mirror of
https://github.com/osmarks/mycorrhiza.git
synced 2024-12-13 05:50:27 +00:00
Split module hyphae into two modules
This commit is contained in:
parent
345efef35a
commit
0467f3a773
@ -8,6 +8,7 @@ import (
|
||||
"github.com/bouncepaw/mycorrhiza/history"
|
||||
"github.com/bouncepaw/mycorrhiza/hyphae"
|
||||
"github.com/bouncepaw/mycorrhiza/markup"
|
||||
"github.com/bouncepaw/mycorrhiza/shroom"
|
||||
"github.com/bouncepaw/mycorrhiza/templates"
|
||||
"github.com/bouncepaw/mycorrhiza/user"
|
||||
"github.com/bouncepaw/mycorrhiza/util"
|
||||
@ -29,7 +30,7 @@ func init() {
|
||||
|
||||
func factoryHandlerAsker(
|
||||
actionPath string,
|
||||
asker func(*hyphae.Hypha, *user.User) (error, string),
|
||||
asker func(*user.User, *hyphae.Hypha) (error, string),
|
||||
succTitleTemplate string,
|
||||
succPageTemplate func(*http.Request, string, bool) string,
|
||||
) func(http.ResponseWriter, *http.Request) {
|
||||
@ -40,7 +41,7 @@ func factoryHandlerAsker(
|
||||
h = hyphae.ByName(hyphaName)
|
||||
u = user.FromRequest(rq)
|
||||
)
|
||||
if err, errtitle := asker(h, u); err != nil {
|
||||
if err, errtitle := asker(u, h); err != nil {
|
||||
HttpErr(
|
||||
w,
|
||||
http.StatusInternalServerError,
|
||||
@ -60,27 +61,21 @@ func factoryHandlerAsker(
|
||||
|
||||
var handlerUnattachAsk = factoryHandlerAsker(
|
||||
"unattach-ask",
|
||||
func(h *hyphae.Hypha, u *user.User) (error, string) {
|
||||
return h.CanUnattach(u)
|
||||
},
|
||||
shroom.CanUnattach,
|
||||
"Unattach %s?",
|
||||
templates.UnattachAskHTML,
|
||||
)
|
||||
|
||||
var handlerDeleteAsk = factoryHandlerAsker(
|
||||
"delete-ask",
|
||||
func(h *hyphae.Hypha, u *user.User) (error, string) {
|
||||
return h.CanDelete(u)
|
||||
},
|
||||
shroom.CanDelete,
|
||||
"Delete %s?",
|
||||
templates.DeleteAskHTML,
|
||||
)
|
||||
|
||||
var handlerRenameAsk = factoryHandlerAsker(
|
||||
"rename-ask",
|
||||
func(h *hyphae.Hypha, u *user.User) (error, string) {
|
||||
return h.CanRename(u)
|
||||
},
|
||||
shroom.CanRename,
|
||||
"Rename %s?",
|
||||
templates.RenameAskHTML,
|
||||
)
|
||||
@ -109,14 +104,14 @@ func factoryHandlerConfirmer(
|
||||
var handlerUnattachConfirm = factoryHandlerConfirmer(
|
||||
"unattach-confirm",
|
||||
func(h *hyphae.Hypha, u *user.User, _ *http.Request) (*history.HistoryOp, string) {
|
||||
return h.UnattachHypha(u)
|
||||
return shroom.UnattachHypha(u, h)
|
||||
},
|
||||
)
|
||||
|
||||
var handlerDeleteConfirm = factoryHandlerConfirmer(
|
||||
"delete-confirm",
|
||||
func(h *hyphae.Hypha, u *user.User, _ *http.Request) (*history.HistoryOp, string) {
|
||||
return h.DeleteHypha(u)
|
||||
return shroom.DeleteHypha(u, h)
|
||||
},
|
||||
)
|
||||
|
||||
@ -128,7 +123,7 @@ var handlerRenameConfirm = factoryHandlerConfirmer(
|
||||
recursive = rq.PostFormValue("recursive") == "true"
|
||||
newHypha = hyphae.ByName(newName)
|
||||
)
|
||||
return oldHypha.RenameHypha(newHypha, recursive, u)
|
||||
return shroom.RenameHypha(oldHypha, newHypha, recursive, u)
|
||||
},
|
||||
)
|
||||
|
||||
@ -143,14 +138,14 @@ func handlerEdit(w http.ResponseWriter, rq *http.Request) {
|
||||
err error
|
||||
u = user.FromRequest(rq)
|
||||
)
|
||||
if err, errtitle := h.CanEdit(u); err != nil {
|
||||
if err, errtitle := shroom.CanEdit(u, h); err != nil {
|
||||
HttpErr(w, http.StatusInternalServerError, hyphaName,
|
||||
errtitle,
|
||||
err.Error())
|
||||
return
|
||||
}
|
||||
if h.Exists {
|
||||
textAreaFill, err = h.FetchTextPart()
|
||||
textAreaFill, err = shroom.FetchTextPart(h)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
HttpErr(w, http.StatusInternalServerError, hyphaName,
|
||||
@ -183,7 +178,7 @@ func handlerUploadText(w http.ResponseWriter, rq *http.Request) {
|
||||
)
|
||||
|
||||
if action != "Preview" {
|
||||
hop, errtitle = h.UploadText([]byte(textData), u)
|
||||
hop, errtitle = shroom.UploadText(h, []byte(textData), u)
|
||||
}
|
||||
|
||||
if hop.HasErrors() {
|
||||
@ -220,7 +215,12 @@ func handlerUploadBinary(w http.ResponseWriter, rq *http.Request) {
|
||||
u = user.FromRequest(rq)
|
||||
file, handler, err = rq.FormFile("binary")
|
||||
)
|
||||
if err, errtitle := h.CanAttach(err, u); err != nil {
|
||||
if err != nil {
|
||||
HttpErr(w, http.StatusInternalServerError, hyphaName,
|
||||
"Error",
|
||||
err.Error())
|
||||
}
|
||||
if err, errtitle := shroom.CanAttach(u, h); err != nil {
|
||||
HttpErr(w, http.StatusInternalServerError, hyphaName,
|
||||
errtitle,
|
||||
err.Error())
|
||||
@ -237,7 +237,7 @@ func handlerUploadBinary(w http.ResponseWriter, rq *http.Request) {
|
||||
}
|
||||
var (
|
||||
mime = handler.Header.Get("Content-Type")
|
||||
hop, errtitle = h.UploadBinary(mime, file, u)
|
||||
hop, errtitle = shroom.UploadBinary(h, mime, file, u)
|
||||
)
|
||||
|
||||
if hop.HasErrors() {
|
||||
|
@ -13,6 +13,7 @@ import (
|
||||
"github.com/bouncepaw/mycorrhiza/hyphae"
|
||||
"github.com/bouncepaw/mycorrhiza/markup"
|
||||
"github.com/bouncepaw/mycorrhiza/mimetype"
|
||||
"github.com/bouncepaw/mycorrhiza/shroom"
|
||||
"github.com/bouncepaw/mycorrhiza/templates"
|
||||
"github.com/bouncepaw/mycorrhiza/tree"
|
||||
"github.com/bouncepaw/mycorrhiza/user"
|
||||
@ -100,7 +101,7 @@ func handlerHypha(w http.ResponseWriter, rq *http.Request) {
|
||||
openGraph = md.OpenGraphHTML()
|
||||
}
|
||||
if !os.IsNotExist(errB) {
|
||||
contents = h.BinaryHtmlBlock() + contents
|
||||
contents = shroom.BinaryHtmlBlock(h) + contents
|
||||
}
|
||||
}
|
||||
treeHTML, subhyphaeHTML, prevHypha, nextHypha := tree.Tree(hyphaName)
|
||||
@ -112,7 +113,7 @@ func handlerHypha(w http.ResponseWriter, rq *http.Request) {
|
||||
contents,
|
||||
treeHTML,
|
||||
subhyphaeHTML,
|
||||
h.BackLinkEntriesHTML(),
|
||||
shroom.BackLinkEntriesHTML(h),
|
||||
prevHypha, nextHypha,
|
||||
hasAmnt),
|
||||
u,
|
||||
|
@ -1,72 +0,0 @@
|
||||
package hyphae
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
|
||||
"github.com/bouncepaw/mycorrhiza/link"
|
||||
"github.com/bouncepaw/mycorrhiza/markup"
|
||||
"github.com/bouncepaw/mycorrhiza/util"
|
||||
)
|
||||
|
||||
func (h *Hypha) BackLinkEntriesHTML() (html string) {
|
||||
for _, backlinkHypha := range h.BackLinks {
|
||||
_ = link.Link{}
|
||||
html += fmt.Sprintf(`<li class="backlinks__entry">
|
||||
<a class="backlinks__link" href="/hypha/%s">%s</a>`, backlinkHypha.Name, util.BeautifulName(backlinkHypha.Name))
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (h *Hypha) outlinksThis(oh *Hypha) bool {
|
||||
for _, outlink := range h.OutLinks {
|
||||
if outlink == oh {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (h *Hypha) backlinkedBy(oh *Hypha) bool {
|
||||
for _, backlink := range h.BackLinks {
|
||||
if backlink == oh {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// FindAllBacklinks iterates over all hyphae that have text parts, sets their outlinks and then sets backlinks.
|
||||
func FindAllBacklinks() {
|
||||
for h := range FilterTextHyphae(YieldExistingHyphae()) {
|
||||
findBacklinkWorker(h)
|
||||
}
|
||||
}
|
||||
|
||||
func findBacklinkWorker(h *Hypha) {
|
||||
h.Lock()
|
||||
defer h.Unlock()
|
||||
|
||||
textContents, err := ioutil.ReadFile(h.TextPath)
|
||||
if err == nil {
|
||||
for outlink := range markup.Doc(h.Name, string(textContents)).OutLinks() {
|
||||
outlink := outlink
|
||||
outlinkHypha := ByName(outlink)
|
||||
if outlinkHypha == h {
|
||||
continue
|
||||
}
|
||||
|
||||
outlinkHypha.Lock()
|
||||
if !outlinkHypha.backlinkedBy(h) {
|
||||
outlinkHypha.BackLinks = append(outlinkHypha.BackLinks, h)
|
||||
outlinkHypha.InsertIfNewKeepExistence()
|
||||
}
|
||||
outlinkHypha.Unlock()
|
||||
|
||||
// Insert outlinkHypha if unique
|
||||
if !h.outlinksThis(outlinkHypha) {
|
||||
h.OutLinks = append(h.OutLinks, outlinkHypha)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -10,21 +10,21 @@ var count = struct {
|
||||
sync.Mutex
|
||||
}{}
|
||||
|
||||
// Set the value of hyphae count to zero.
|
||||
// Set the value of hyphae count to zero. Use when reloading hyphae.
|
||||
func ResetCount() {
|
||||
count.Lock()
|
||||
count.value = 0
|
||||
count.Unlock()
|
||||
count.Lock()
|
||||
count.value = 0
|
||||
count.Unlock()
|
||||
}
|
||||
|
||||
// Increment the value of hyphae count.
|
||||
// Increment the value of the hyphae counter. Use when creating new hyphae or loading hyphae from disk.
|
||||
func IncrementCount() {
|
||||
count.Lock()
|
||||
count.value++
|
||||
count.Unlock()
|
||||
}
|
||||
|
||||
// Decrement the value of hyphae count.
|
||||
// Decrement the value of the hyphae counter. Use when deleting existing hyphae.
|
||||
func DecrementCount() {
|
||||
count.Lock()
|
||||
count.value--
|
||||
@ -33,6 +33,5 @@ func DecrementCount() {
|
||||
|
||||
// Count how many hyphae there are.
|
||||
func Count() int {
|
||||
// it is concurrent-safe to not lock here, right?
|
||||
return count.value
|
||||
}
|
||||
|
@ -1,50 +0,0 @@
|
||||
package hyphae
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"log"
|
||||
|
||||
"github.com/bouncepaw/mycorrhiza/history"
|
||||
"github.com/bouncepaw/mycorrhiza/user"
|
||||
)
|
||||
|
||||
func rejectDeleteLog(h *Hypha, u *user.User, errmsg string) {
|
||||
log.Printf("Reject delete ‘%s’ by @%s: %s\n", h.Name, u.Name, errmsg)
|
||||
}
|
||||
|
||||
// CanDelete checks if given user can delete given hypha.
|
||||
func (h *Hypha) CanDelete(u *user.User) (err error, errtitle string) {
|
||||
// First, check if can unattach at all
|
||||
if !u.CanProceed("delete-confirm") {
|
||||
rejectDeleteLog(h, u, "no rights")
|
||||
return errors.New("Not enough rights to delete, you must be a moderator"), "Not enough rights"
|
||||
}
|
||||
|
||||
if !h.Exists {
|
||||
rejectDeleteLog(h, u, "does not exist")
|
||||
return errors.New("Cannot delete this hypha because it does not exist"), "Does not exist"
|
||||
}
|
||||
|
||||
return nil, ""
|
||||
}
|
||||
|
||||
// DeleteHypha deletes hypha and makes a history record about that.
|
||||
func (h *Hypha) DeleteHypha(u *user.User) (hop *history.HistoryOp, errtitle string) {
|
||||
hop = history.Operation(history.TypeDeleteHypha)
|
||||
|
||||
if err, errtitle := h.CanDelete(u); errtitle != "" {
|
||||
hop.WithError(err)
|
||||
return hop, errtitle
|
||||
}
|
||||
|
||||
hop.
|
||||
WithFilesRemoved(h.TextPath, h.BinaryPath).
|
||||
WithMsg(fmt.Sprintf("Delete ‘%s’", h.Name)).
|
||||
WithUser(u).
|
||||
Apply()
|
||||
if len(hop.Errs) == 0 {
|
||||
h.delete()
|
||||
}
|
||||
return hop, ""
|
||||
}
|
117
hyphae/hyphae.go
117
hyphae/hyphae.go
@ -1,44 +1,12 @@
|
||||
// The `hyphae` package is for the Hypha type, hypha storage and stuff like that. It shall not depend on mycorrhiza modules other than util.
|
||||
package hyphae
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"log"
|
||||
"regexp"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/bouncepaw/mycorrhiza/markup"
|
||||
"github.com/bouncepaw/mycorrhiza/util"
|
||||
)
|
||||
|
||||
func init() {
|
||||
markup.HyphaExists = func(hyphaName string) bool {
|
||||
return ByName(hyphaName).Exists
|
||||
}
|
||||
markup.HyphaAccess = func(hyphaName string) (rawText, binaryBlock string, err error) {
|
||||
if h := ByName(hyphaName); h.Exists {
|
||||
rawText, err = h.FetchTextPart()
|
||||
if h.BinaryPath != "" {
|
||||
binaryBlock = h.BinaryHtmlBlock()
|
||||
}
|
||||
} else {
|
||||
err = errors.New("Hypha " + hyphaName + " does not exist")
|
||||
}
|
||||
return
|
||||
}
|
||||
markup.HyphaIterate = func(λ func(string)) {
|
||||
for h := range YieldExistingHyphae() {
|
||||
λ(h.Name)
|
||||
}
|
||||
}
|
||||
markup.HyphaImageForOG = func(hyphaName string) string {
|
||||
if h := ByName(hyphaName); h.Exists && h.BinaryPath != "" {
|
||||
return util.URL + "/binary/" + hyphaName
|
||||
}
|
||||
return util.URL + "/favicon.ico"
|
||||
}
|
||||
}
|
||||
|
||||
// HyphaPattern is a pattern which all hyphae must match.
|
||||
var HyphaPattern = regexp.MustCompile(`[^?!:#@><*|"\'&%{}]+`)
|
||||
|
||||
@ -56,57 +24,6 @@ type Hypha struct {
|
||||
var byNames = make(map[string]*Hypha)
|
||||
var byNamesMutex = sync.Mutex{}
|
||||
|
||||
// YieldExistingHyphae iterates over all hyphae and yields all existing ones.
|
||||
func YieldExistingHyphae() chan *Hypha {
|
||||
ch := make(chan *Hypha)
|
||||
go func() {
|
||||
for _, h := range byNames {
|
||||
if h.Exists {
|
||||
ch <- h
|
||||
}
|
||||
}
|
||||
close(ch)
|
||||
}()
|
||||
return ch
|
||||
}
|
||||
|
||||
// FilterTextHyphae filters the source channel and yields only those hyphae than have text parts.
|
||||
func FilterTextHyphae(src chan *Hypha) chan *Hypha {
|
||||
sink := make(chan *Hypha)
|
||||
go func() {
|
||||
for h := range src {
|
||||
if h.TextPath != "" {
|
||||
sink <- h
|
||||
}
|
||||
}
|
||||
close(sink)
|
||||
}()
|
||||
return sink
|
||||
}
|
||||
|
||||
// Subhyphae returns slice of subhyphae.
|
||||
func (h *Hypha) Subhyphae() []*Hypha {
|
||||
hyphae := []*Hypha{}
|
||||
for subh := range YieldExistingHyphae() {
|
||||
if strings.HasPrefix(subh.Name, h.Name+"/") {
|
||||
hyphae = append(hyphae, subh)
|
||||
}
|
||||
}
|
||||
return hyphae
|
||||
}
|
||||
|
||||
// AreFreeNames checks if all given `hyphaNames` are not taken.
|
||||
func AreFreeNames(hyphaNames ...string) (firstFailure string, ok bool) {
|
||||
for h := range YieldExistingHyphae() {
|
||||
for _, hn := range hyphaNames {
|
||||
if hn == h.Name {
|
||||
return hn, false
|
||||
}
|
||||
}
|
||||
}
|
||||
return "", true
|
||||
}
|
||||
|
||||
// EmptyHypha returns an empty hypha struct with given name.
|
||||
func EmptyHypha(hyphaName string) *Hypha {
|
||||
return &Hypha{
|
||||
@ -164,7 +81,7 @@ func (h *Hypha) InsertIfNewKeepExistence() {
|
||||
}
|
||||
}
|
||||
|
||||
func (h *Hypha) delete() {
|
||||
func (h *Hypha) Delete() {
|
||||
byNamesMutex.Lock()
|
||||
h.Lock()
|
||||
delete(byNames, h.Name)
|
||||
@ -173,7 +90,7 @@ func (h *Hypha) delete() {
|
||||
h.Unlock()
|
||||
}
|
||||
|
||||
func (h *Hypha) renameTo(newName string) {
|
||||
func (h *Hypha) RenameTo(newName string) {
|
||||
byNamesMutex.Lock()
|
||||
h.Lock()
|
||||
delete(byNames, h.Name)
|
||||
@ -195,3 +112,31 @@ func (h *Hypha) MergeIn(oh *Hypha) {
|
||||
h.BinaryPath = oh.BinaryPath
|
||||
}
|
||||
}
|
||||
|
||||
// Link related stuff:
|
||||
|
||||
func (h *Hypha) AddOutLink(oh *Hypha) (added bool) {
|
||||
h.Lock()
|
||||
defer h.Unlock()
|
||||
|
||||
for _, outlink := range h.OutLinks {
|
||||
if outlink == oh {
|
||||
return false
|
||||
}
|
||||
}
|
||||
h.OutLinks = append(h.OutLinks, oh)
|
||||
return true
|
||||
}
|
||||
|
||||
func (h *Hypha) AddBackLink(bh *Hypha) (added bool) {
|
||||
h.Lock()
|
||||
defer h.Unlock()
|
||||
|
||||
for _, backlink := range h.BackLinks {
|
||||
if backlink == h {
|
||||
return false
|
||||
}
|
||||
}
|
||||
h.BackLinks = append(h.BackLinks, bh)
|
||||
return true
|
||||
}
|
||||
|
57
hyphae/iterators.go
Normal file
57
hyphae/iterators.go
Normal file
@ -0,0 +1,57 @@
|
||||
// File `iterators.go` contains stuff that iterates over hyphae.
|
||||
package hyphae
|
||||
|
||||
import (
|
||||
"strings"
|
||||
)
|
||||
|
||||
// YieldExistingHyphae iterates over all hyphae and yields all existing ones.
|
||||
func YieldExistingHyphae() chan *Hypha {
|
||||
ch := make(chan *Hypha)
|
||||
go func() {
|
||||
for _, h := range byNames {
|
||||
if h.Exists {
|
||||
ch <- h
|
||||
}
|
||||
}
|
||||
close(ch)
|
||||
}()
|
||||
return ch
|
||||
}
|
||||
|
||||
// FilterTextHyphae filters the source channel and yields only those hyphae than have text parts.
|
||||
func FilterTextHyphae(src chan *Hypha) chan *Hypha {
|
||||
sink := make(chan *Hypha)
|
||||
go func() {
|
||||
for h := range src {
|
||||
if h.TextPath != "" {
|
||||
sink <- h
|
||||
}
|
||||
}
|
||||
close(sink)
|
||||
}()
|
||||
return sink
|
||||
}
|
||||
|
||||
// Subhyphae returns slice of subhyphae.
|
||||
func (h *Hypha) Subhyphae() []*Hypha {
|
||||
hyphae := []*Hypha{}
|
||||
for subh := range YieldExistingHyphae() {
|
||||
if strings.HasPrefix(subh.Name, h.Name+"/") {
|
||||
hyphae = append(hyphae, subh)
|
||||
}
|
||||
}
|
||||
return hyphae
|
||||
}
|
||||
|
||||
// AreFreeNames checks if all given `hyphaNames` are not taken. If they are not taken, `ok` is true. If not, `firstFailure` is the name of the first met hypha that is not free.
|
||||
func AreFreeNames(hyphaNames ...string) (firstFailure string, ok bool) {
|
||||
for h := range YieldExistingHyphae() {
|
||||
for _, hn := range hyphaNames {
|
||||
if hn == h.Name {
|
||||
return hn, false
|
||||
}
|
||||
}
|
||||
}
|
||||
return "", true
|
||||
}
|
@ -1,64 +0,0 @@
|
||||
package hyphae
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"log"
|
||||
|
||||
"github.com/bouncepaw/mycorrhiza/history"
|
||||
"github.com/bouncepaw/mycorrhiza/user"
|
||||
)
|
||||
|
||||
func rejectUnattachLog(h *Hypha, u *user.User, errmsg string) {
|
||||
log.Printf("Reject unattach ‘%s’ by @%s: %s\n", h.Name, u.Name, errmsg)
|
||||
}
|
||||
|
||||
// CanUnattach checks if given user can unattach given hypha. If they can, `errtitle` is an empty string and `err` is nil. If they cannot, `errtitle` is not an empty string, and `err` is an error.
|
||||
func (h *Hypha) CanUnattach(u *user.User) (err error, errtitle string) {
|
||||
if !u.CanProceed("unattach-confirm") {
|
||||
rejectUnattachLog(h, u, "no rights")
|
||||
return errors.New("Not enough rights to unattach, you must be a trusted editor"), "Not enough rights"
|
||||
}
|
||||
|
||||
if !h.Exists {
|
||||
rejectUnattachLog(h, u, "does not exist")
|
||||
return errors.New("Cannot unattach this hypha because it does not exist"), "Does not exist"
|
||||
}
|
||||
|
||||
if h.BinaryPath == "" {
|
||||
rejectUnattachLog(h, u, "no amnt")
|
||||
return errors.New("Cannot unattach this hypha because it has no attachment"), "No attachment"
|
||||
}
|
||||
|
||||
return nil, ""
|
||||
}
|
||||
|
||||
// UnattachHypha unattaches hypha and makes a history record about that.
|
||||
func (h *Hypha) UnattachHypha(u *user.User) (hop *history.HistoryOp, errtitle string) {
|
||||
hop = history.Operation(history.TypeUnattachHypha)
|
||||
|
||||
if err, errtitle := h.CanUnattach(u); errtitle != "" {
|
||||
hop.WithError(err)
|
||||
return hop, errtitle
|
||||
}
|
||||
|
||||
hop.
|
||||
WithFilesRemoved(h.BinaryPath).
|
||||
WithMsg(fmt.Sprintf("Unattach ‘%s’", h.Name)).
|
||||
WithUser(u).
|
||||
Apply()
|
||||
|
||||
if len(hop.Errs) > 0 {
|
||||
rejectUnattachLog(h, u, "fail")
|
||||
return hop.WithError(errors.New(fmt.Sprintf("Could not unattach this hypha due to internal server errors: <code>%v</code>", hop.Errs))), "Error"
|
||||
}
|
||||
|
||||
if h.BinaryPath != "" {
|
||||
h.BinaryPath = ""
|
||||
}
|
||||
// If nothing is left of the hypha
|
||||
if h.TextPath == "" {
|
||||
h.delete()
|
||||
}
|
||||
return hop, ""
|
||||
}
|
122
hyphae/upload.go
122
hyphae/upload.go
@ -1,122 +0,0 @@
|
||||
package hyphae
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"mime/multipart"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/bouncepaw/mycorrhiza/history"
|
||||
"github.com/bouncepaw/mycorrhiza/mimetype"
|
||||
"github.com/bouncepaw/mycorrhiza/user"
|
||||
"github.com/bouncepaw/mycorrhiza/util"
|
||||
)
|
||||
|
||||
func rejectEditLog(h *Hypha, u *user.User, errmsg string) {
|
||||
log.Printf("Reject edit ‘%s’ by @%s: %s\n", h.Name, u.Name, errmsg)
|
||||
}
|
||||
|
||||
func rejectAttachLog(h *Hypha, u *user.User, errmsg string) {
|
||||
log.Printf("Reject attach ‘%s’ by @%s: %s\n", h.Name, u.Name, errmsg)
|
||||
}
|
||||
|
||||
func (h *Hypha) CanEdit(u *user.User) (err error, errtitle string) {
|
||||
if !u.CanProceed("edit") {
|
||||
rejectEditLog(h, u, "no rights")
|
||||
return errors.New("You must be an editor to edit pages."), "Not enough rights"
|
||||
}
|
||||
return nil, ""
|
||||
}
|
||||
|
||||
func (h *Hypha) CanUploadThat(data []byte, u *user.User) (err error, errtitle string) {
|
||||
if len(data) == 0 {
|
||||
return errors.New("No text data passed"), "Empty"
|
||||
}
|
||||
return nil, ""
|
||||
}
|
||||
|
||||
func (h *Hypha) UploadText(textData []byte, u *user.User) (hop *history.HistoryOp, errtitle string) {
|
||||
hop = history.Operation(history.TypeEditText)
|
||||
if h.Exists {
|
||||
hop.WithMsg(fmt.Sprintf("Edit ‘%s’", h.Name))
|
||||
} else {
|
||||
hop.WithMsg(fmt.Sprintf("Create ‘%s’", h.Name))
|
||||
}
|
||||
|
||||
if err, errtitle := h.CanEdit(u); err != nil {
|
||||
return hop.WithError(err), errtitle
|
||||
}
|
||||
if err, errtitle := h.CanUploadThat(textData, u); err != nil {
|
||||
return hop.WithError(err), errtitle
|
||||
}
|
||||
|
||||
return h.uploadHelp(hop, ".myco", textData, u)
|
||||
}
|
||||
|
||||
func (h *Hypha) CanAttach(err error, u *user.User) (error, string) {
|
||||
if !u.CanProceed("upload-binary") {
|
||||
rejectAttachLog(h, u, "no rights")
|
||||
return errors.New("You must be an editor to upload attachments."), "Not enough rights"
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
rejectAttachLog(h, u, err.Error())
|
||||
return errors.New("No binary data passed"), err.Error()
|
||||
}
|
||||
return nil, ""
|
||||
}
|
||||
|
||||
func (h *Hypha) UploadBinary(mime string, file multipart.File, u *user.User) (*history.HistoryOp, string) {
|
||||
var (
|
||||
hop = history.Operation(history.TypeEditBinary).WithMsg(fmt.Sprintf("Upload binary part for ‘%s’ with type ‘%s’", h.Name, mime))
|
||||
data, err = ioutil.ReadAll(file)
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
return hop.WithError(err), err.Error()
|
||||
}
|
||||
if err, errtitle := h.CanEdit(u); err != nil {
|
||||
return hop.WithError(err), errtitle
|
||||
}
|
||||
if err, errtitle := h.CanUploadThat(data, u); err != nil {
|
||||
return hop.WithError(err), errtitle
|
||||
}
|
||||
|
||||
return h.uploadHelp(hop, mimetype.ToExtension(mime), data, u)
|
||||
}
|
||||
|
||||
// uploadHelp is a helper function for UploadText and UploadBinary
|
||||
func (h *Hypha) uploadHelp(hop *history.HistoryOp, ext string, data []byte, u *user.User) (*history.HistoryOp, string) {
|
||||
var (
|
||||
fullPath = filepath.Join(util.WikiDir, h.Name+ext)
|
||||
originalFullPath = &h.TextPath
|
||||
)
|
||||
if hop.Type == history.TypeEditBinary {
|
||||
originalFullPath = &h.BinaryPath
|
||||
}
|
||||
|
||||
if err := os.MkdirAll(filepath.Dir(fullPath), 0777); err != nil {
|
||||
return hop.WithError(err), err.Error()
|
||||
}
|
||||
|
||||
if err := ioutil.WriteFile(fullPath, data, 0644); err != nil {
|
||||
return hop.WithError(err), err.Error()
|
||||
}
|
||||
|
||||
if h.Exists && *originalFullPath != fullPath && *originalFullPath != "" {
|
||||
if err := history.Rename(*originalFullPath, fullPath); err != nil {
|
||||
return hop.WithError(err), err.Error()
|
||||
}
|
||||
log.Println("Move", *originalFullPath, "to", fullPath)
|
||||
}
|
||||
|
||||
h.InsertIfNew()
|
||||
if h.Exists && h.TextPath != "" && hop.Type == history.TypeEditText && !history.FileChanged(fullPath) {
|
||||
return hop.Abort(), "No changes"
|
||||
}
|
||||
*originalFullPath = fullPath
|
||||
return hop.WithFiles(fullPath).WithUser(u).Apply(), ""
|
||||
}
|
7
main.go
7
main.go
@ -15,6 +15,7 @@ import (
|
||||
"github.com/bouncepaw/mycorrhiza/history"
|
||||
"github.com/bouncepaw/mycorrhiza/hyphae"
|
||||
"github.com/bouncepaw/mycorrhiza/mimetype"
|
||||
"github.com/bouncepaw/mycorrhiza/shroom"
|
||||
"github.com/bouncepaw/mycorrhiza/templates"
|
||||
"github.com/bouncepaw/mycorrhiza/user"
|
||||
"github.com/bouncepaw/mycorrhiza/util"
|
||||
@ -85,7 +86,7 @@ func handlerUpdateHeaderLinks(w http.ResponseWriter, rq *http.Request) {
|
||||
log.Println("Rejected", rq.URL)
|
||||
return
|
||||
}
|
||||
hyphae.SetHeaderLinks()
|
||||
shroom.SetHeaderLinks()
|
||||
http.Redirect(w, rq, "/", http.StatusSeeOther)
|
||||
}
|
||||
|
||||
@ -175,11 +176,11 @@ func main() {
|
||||
log.Println("Wiki storage directory is", WikiDir)
|
||||
hyphae.Index(WikiDir)
|
||||
log.Println("Indexed", hyphae.Count(), "hyphae")
|
||||
hyphae.FindAllBacklinks()
|
||||
shroom.FindAllBacklinks()
|
||||
log.Println("Found all backlinks")
|
||||
|
||||
history.Start(WikiDir)
|
||||
hyphae.SetHeaderLinks()
|
||||
shroom.SetHeaderLinks()
|
||||
|
||||
go handleGemini()
|
||||
|
||||
|
55
shroom/backlink.go
Normal file
55
shroom/backlink.go
Normal file
@ -0,0 +1,55 @@
|
||||
package shroom
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"sync"
|
||||
|
||||
"github.com/bouncepaw/mycorrhiza/hyphae"
|
||||
"github.com/bouncepaw/mycorrhiza/link"
|
||||
"github.com/bouncepaw/mycorrhiza/markup"
|
||||
"github.com/bouncepaw/mycorrhiza/util"
|
||||
)
|
||||
|
||||
func BackLinkEntriesHTML(h *hyphae.Hypha) (html string) {
|
||||
for _, backlinkHypha := range h.BackLinks {
|
||||
_ = link.Link{}
|
||||
html += fmt.Sprintf(`<li class="backlinks__entry">
|
||||
<a class="backlinks__link" href="/hypha/%s">%s</a>`, backlinkHypha.Name, util.BeautifulName(backlinkHypha.Name))
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// FindAllBacklinks iterates over all hyphae that have text parts, sets their outlinks and then sets backlinks.
|
||||
func FindAllBacklinks() {
|
||||
for h := range hyphae.FilterTextHyphae(hyphae.YieldExistingHyphae()) {
|
||||
findBacklinkWorker(h)
|
||||
}
|
||||
}
|
||||
|
||||
func findBacklinkWorker(h *hyphae.Hypha) {
|
||||
var (
|
||||
wg sync.WaitGroup
|
||||
textContents, err = ioutil.ReadFile(h.TextPath)
|
||||
)
|
||||
if err == nil {
|
||||
for outlink := range markup.Doc(h.Name, string(textContents)).OutLinks() {
|
||||
go func() {
|
||||
wg.Add(1)
|
||||
outlinkHypha := hyphae.ByName(outlink)
|
||||
if outlinkHypha == h {
|
||||
return
|
||||
}
|
||||
|
||||
outlinkHypha.AddBackLink(h)
|
||||
outlinkHypha.InsertIfNewKeepExistence()
|
||||
h.AddOutLink(outlinkHypha)
|
||||
wg.Done()
|
||||
}()
|
||||
}
|
||||
wg.Wait()
|
||||
} else {
|
||||
log.Println("Error when reading text contents of ‘%s’: %s", h.Name, err.Error())
|
||||
}
|
||||
}
|
92
shroom/can.go
Normal file
92
shroom/can.go
Normal file
@ -0,0 +1,92 @@
|
||||
package shroom
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"github.com/bouncepaw/mycorrhiza/hyphae"
|
||||
"github.com/bouncepaw/mycorrhiza/user"
|
||||
)
|
||||
|
||||
func canFactory(
|
||||
rejectLogger func(*hyphae.Hypha, *user.User, string),
|
||||
action string,
|
||||
dispatcher func(*hyphae.Hypha, *user.User) (string, string),
|
||||
noRightsMsg string,
|
||||
notExistsMsg string,
|
||||
careAboutExistince bool,
|
||||
) func(*user.User, *hyphae.Hypha) (error, string) {
|
||||
return func(u *user.User, h *hyphae.Hypha) (error, string) {
|
||||
if !u.CanProceed(action) {
|
||||
rejectLogger(h, u, "no rights")
|
||||
return errors.New(noRightsMsg), "Not enough rights"
|
||||
}
|
||||
|
||||
if careAboutExistince && !h.Exists {
|
||||
rejectLogger(h, u, "does not exist")
|
||||
return errors.New(notExistsMsg), "Does not exist"
|
||||
}
|
||||
|
||||
if dispatcher == nil {
|
||||
return nil, ""
|
||||
}
|
||||
errmsg, errtitle := dispatcher(h, u)
|
||||
if errtitle == "" {
|
||||
return nil, ""
|
||||
}
|
||||
return errors.New(errmsg), errtitle
|
||||
}
|
||||
}
|
||||
|
||||
var (
|
||||
CanDelete = canFactory(
|
||||
rejectDeleteLog,
|
||||
"delete-confirm",
|
||||
nil,
|
||||
"Not enough rights to delete, you must be a moderator",
|
||||
"Cannot delete this hypha because it does not exist",
|
||||
true,
|
||||
)
|
||||
|
||||
CanRename = canFactory(
|
||||
rejectRenameLog,
|
||||
"rename-confirm",
|
||||
nil,
|
||||
"Not enough rights to rename, you must be a trusted editor",
|
||||
"Cannot rename this hypha because it does not exist",
|
||||
true,
|
||||
)
|
||||
|
||||
CanUnattach = canFactory(
|
||||
rejectUnattachLog,
|
||||
"unattach-confirm",
|
||||
func(h *hyphae.Hypha, u *user.User) (errmsg, errtitle string) {
|
||||
if h.BinaryPath == "" {
|
||||
rejectUnattachLog(h, u, "no amnt")
|
||||
return "Cannot unattach this hypha because it has no attachment", "No attachment"
|
||||
}
|
||||
|
||||
return "", ""
|
||||
},
|
||||
"Not enough rights to unattach, you must be a trusted editor",
|
||||
"Cannot unattach this hypha because it does not exist",
|
||||
true,
|
||||
)
|
||||
|
||||
CanEdit = canFactory(
|
||||
rejectEditLog,
|
||||
"upload-text",
|
||||
nil,
|
||||
"You must be an editor to edit a hypha",
|
||||
"You cannot edit a hypha that does not exist",
|
||||
false,
|
||||
)
|
||||
|
||||
CanAttach = canFactory(
|
||||
rejectAttachLog,
|
||||
"upload-binary",
|
||||
nil,
|
||||
"You must be an editor to attach a hypha",
|
||||
"You cannot attach a hypha that does not exist",
|
||||
false,
|
||||
)
|
||||
)
|
29
shroom/delete.go
Normal file
29
shroom/delete.go
Normal file
@ -0,0 +1,29 @@
|
||||
package shroom
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/bouncepaw/mycorrhiza/history"
|
||||
"github.com/bouncepaw/mycorrhiza/hyphae"
|
||||
"github.com/bouncepaw/mycorrhiza/user"
|
||||
)
|
||||
|
||||
// DeleteHypha deletes hypha and makes a history record about that.
|
||||
func DeleteHypha(u *user.User, h *hyphae.Hypha) (hop *history.HistoryOp, errtitle string) {
|
||||
hop = history.Operation(history.TypeDeleteHypha)
|
||||
|
||||
if err, errtitle := CanDelete(u, h); errtitle != "" {
|
||||
hop.WithError(err).Abort()
|
||||
return hop, errtitle
|
||||
}
|
||||
|
||||
hop.
|
||||
WithFilesRemoved(h.TextPath, h.BinaryPath).
|
||||
WithMsg(fmt.Sprintf("Delete ‘%s’", h.Name)).
|
||||
WithUser(u).
|
||||
Apply()
|
||||
if !hop.HasErrors() {
|
||||
h.Delete()
|
||||
}
|
||||
return hop, ""
|
||||
}
|
37
shroom/init.go
Normal file
37
shroom/init.go
Normal file
@ -0,0 +1,37 @@
|
||||
package shroom
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"github.com/bouncepaw/mycorrhiza/hyphae"
|
||||
"github.com/bouncepaw/mycorrhiza/markup"
|
||||
"github.com/bouncepaw/mycorrhiza/util"
|
||||
)
|
||||
|
||||
func init() {
|
||||
markup.HyphaExists = func(hyphaName string) bool {
|
||||
return hyphae.ByName(hyphaName).Exists
|
||||
}
|
||||
markup.HyphaAccess = func(hyphaName string) (rawText, binaryBlock string, err error) {
|
||||
if h := hyphae.ByName(hyphaName); h.Exists {
|
||||
rawText, err = FetchTextPart(h)
|
||||
if h.BinaryPath != "" {
|
||||
binaryBlock = BinaryHtmlBlock(h)
|
||||
}
|
||||
} else {
|
||||
err = errors.New("Hypha " + hyphaName + " does not exist")
|
||||
}
|
||||
return
|
||||
}
|
||||
markup.HyphaIterate = func(λ func(string)) {
|
||||
for h := range hyphae.YieldExistingHyphae() {
|
||||
λ(h.Name)
|
||||
}
|
||||
}
|
||||
markup.HyphaImageForOG = func(hyphaName string) string {
|
||||
if h := hyphae.ByName(hyphaName); h.Exists && h.BinaryPath != "" {
|
||||
return util.URL + "/binary/" + hyphaName
|
||||
}
|
||||
return util.URL + "/favicon.ico"
|
||||
}
|
||||
}
|
24
shroom/log.go
Normal file
24
shroom/log.go
Normal file
@ -0,0 +1,24 @@
|
||||
package shroom
|
||||
|
||||
import (
|
||||
"log"
|
||||
|
||||
"github.com/bouncepaw/mycorrhiza/hyphae"
|
||||
"github.com/bouncepaw/mycorrhiza/user"
|
||||
)
|
||||
|
||||
func rejectDeleteLog(h *hyphae.Hypha, u *user.User, errmsg string) {
|
||||
log.Printf("Reject delete ‘%s’ by @%s: %s\n", h.Name, u.Name, errmsg)
|
||||
}
|
||||
func rejectRenameLog(h *hyphae.Hypha, u *user.User, errmsg string) {
|
||||
log.Printf("Reject rename ‘%s’ by @%s: %s\n", h.Name, u.Name, errmsg)
|
||||
}
|
||||
func rejectUnattachLog(h *hyphae.Hypha, u *user.User, errmsg string) {
|
||||
log.Printf("Reject unattach ‘%s’ by @%s: %s\n", h.Name, u.Name, errmsg)
|
||||
}
|
||||
func rejectEditLog(h *hyphae.Hypha, u *user.User, errmsg string) {
|
||||
log.Printf("Reject edit ‘%s’ by @%s: %s\n", h.Name, u.Name, errmsg)
|
||||
}
|
||||
func rejectAttachLog(h *hyphae.Hypha, u *user.User, errmsg string) {
|
||||
log.Printf("Reject attach ‘%s’ by @%s: %s\n", h.Name, u.Name, errmsg)
|
||||
}
|
@ -1,35 +1,17 @@
|
||||
package hyphae
|
||||
package shroom
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"log"
|
||||
"regexp"
|
||||
|
||||
"github.com/bouncepaw/mycorrhiza/history"
|
||||
"github.com/bouncepaw/mycorrhiza/hyphae"
|
||||
"github.com/bouncepaw/mycorrhiza/user"
|
||||
"github.com/bouncepaw/mycorrhiza/util"
|
||||
)
|
||||
|
||||
func rejectRenameLog(h *Hypha, u *user.User, errmsg string) {
|
||||
log.Printf("Reject rename ‘%s’ by @%s: %s\n", h.Name, u.Name, errmsg)
|
||||
}
|
||||
|
||||
func (h *Hypha) CanRename(u *user.User) (err error, errtitle string) {
|
||||
if !u.CanProceed("rename-confirm") {
|
||||
rejectRenameLog(h, u, "no rights")
|
||||
return errors.New("Not enough rights to rename, you must be a trusted editor"), "Not enough rights"
|
||||
}
|
||||
|
||||
if !h.Exists {
|
||||
rejectRenameLog(h, u, "does not exist")
|
||||
return errors.New("Cannot rename this hypha because it does not exist"), "Does not exist"
|
||||
}
|
||||
|
||||
return nil, ""
|
||||
}
|
||||
|
||||
func canRenameThisToThat(oh *Hypha, nh *Hypha, u *user.User) (err error, errtitle string) {
|
||||
func canRenameThisToThat(oh *hyphae.Hypha, nh *hyphae.Hypha, u *user.User) (err error, errtitle string) {
|
||||
if nh.Exists {
|
||||
rejectRenameLog(oh, u, fmt.Sprintf("name ‘%s’ taken already", nh.Name))
|
||||
return errors.New(fmt.Sprintf("Hypha named <a href='/hypha/%[1]s'>%[1]s</a> already exists, cannot rename", nh.Name)), "Name taken"
|
||||
@ -40,7 +22,7 @@ func canRenameThisToThat(oh *Hypha, nh *Hypha, u *user.User) (err error, errtitl
|
||||
return errors.New("No new name is given"), "No name given"
|
||||
}
|
||||
|
||||
if !HyphaPattern.MatchString(nh.Name) {
|
||||
if !hyphae.HyphaPattern.MatchString(nh.Name) {
|
||||
rejectRenameLog(oh, u, fmt.Sprintf("new name ‘%s’ invalid", nh.Name))
|
||||
return errors.New("Invalid new name. Names cannot contain characters <code>^?!:#@><*|\"\\'&%</code>"), "Invalid name"
|
||||
}
|
||||
@ -49,12 +31,12 @@ func canRenameThisToThat(oh *Hypha, nh *Hypha, u *user.User) (err error, errtitl
|
||||
}
|
||||
|
||||
// RenameHypha renames hypha from old name `hyphaName` to `newName` and makes a history record about that. If `recursive` is `true`, its subhyphae will be renamed the same way.
|
||||
func (h *Hypha) RenameHypha(newHypha *Hypha, recursive bool, u *user.User) (hop *history.HistoryOp, errtitle string) {
|
||||
func RenameHypha(h *hyphae.Hypha, newHypha *hyphae.Hypha, recursive bool, u *user.User) (hop *history.HistoryOp, errtitle string) {
|
||||
newHypha.Lock()
|
||||
defer newHypha.Unlock()
|
||||
hop = history.Operation(history.TypeRenameHypha)
|
||||
|
||||
if err, errtitle := h.CanRename(u); errtitle != "" {
|
||||
if err, errtitle := CanRename(u, h); errtitle != "" {
|
||||
hop.WithError(err)
|
||||
return hop, errtitle
|
||||
}
|
||||
@ -85,7 +67,7 @@ func (h *Hypha) RenameHypha(newHypha *Hypha, recursive bool, u *user.User) (hop
|
||||
Apply()
|
||||
if len(hop.Errs) == 0 {
|
||||
for _, h := range hyphaeToRename {
|
||||
h.renameTo(replaceName(h.Name))
|
||||
h.RenameTo(replaceName(h.Name))
|
||||
h.Lock()
|
||||
h.TextPath = replaceName(h.TextPath)
|
||||
h.BinaryPath = replaceName(h.BinaryPath)
|
||||
@ -95,15 +77,15 @@ func (h *Hypha) RenameHypha(newHypha *Hypha, recursive bool, u *user.User) (hop
|
||||
return hop, ""
|
||||
}
|
||||
|
||||
func findHyphaeToRename(superhypha *Hypha, recursive bool) []*Hypha {
|
||||
hyphae := []*Hypha{superhypha}
|
||||
func findHyphaeToRename(superhypha *hyphae.Hypha, recursive bool) []*hyphae.Hypha {
|
||||
hyphae := []*hyphae.Hypha{superhypha}
|
||||
if recursive {
|
||||
hyphae = append(hyphae, superhypha.Subhyphae()...)
|
||||
}
|
||||
return hyphae
|
||||
}
|
||||
|
||||
func renamingPairs(hyphaeToRename []*Hypha, replaceName func(string) string) (map[string]string, error) {
|
||||
func renamingPairs(hyphaeToRename []*hyphae.Hypha, replaceName func(string) string) (map[string]string, error) {
|
||||
renameMap := make(map[string]string)
|
||||
newNames := make([]string, len(hyphaeToRename))
|
||||
for _, h := range hyphaeToRename {
|
||||
@ -117,7 +99,7 @@ func renamingPairs(hyphaeToRename []*Hypha, replaceName func(string) string) (ma
|
||||
}
|
||||
h.RUnlock()
|
||||
}
|
||||
if firstFailure, ok := AreFreeNames(newNames...); !ok {
|
||||
if firstFailure, ok := hyphae.AreFreeNames(newNames...); !ok {
|
||||
return nil, errors.New("Hypha " + firstFailure + " already exists")
|
||||
}
|
||||
return renameMap, nil
|
40
shroom/unattach.go
Normal file
40
shroom/unattach.go
Normal file
@ -0,0 +1,40 @@
|
||||
package shroom
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/bouncepaw/mycorrhiza/history"
|
||||
"github.com/bouncepaw/mycorrhiza/hyphae"
|
||||
"github.com/bouncepaw/mycorrhiza/user"
|
||||
)
|
||||
|
||||
// UnattachHypha unattaches hypha and makes a history record about that.
|
||||
func UnattachHypha(u *user.User, h *hyphae.Hypha) (hop *history.HistoryOp, errtitle string) {
|
||||
hop = history.Operation(history.TypeUnattachHypha)
|
||||
|
||||
if err, errtitle := CanUnattach(u, h); errtitle != "" {
|
||||
hop.WithError(err).Abort()
|
||||
return hop, errtitle
|
||||
}
|
||||
|
||||
hop.
|
||||
WithFilesRemoved(h.BinaryPath).
|
||||
WithMsg(fmt.Sprintf("Unattach ‘%s’", h.Name)).
|
||||
WithUser(u).
|
||||
Apply()
|
||||
|
||||
if len(hop.Errs) > 0 {
|
||||
rejectUnattachLog(h, u, "fail")
|
||||
return hop.WithError(errors.New(fmt.Sprintf("Could not unattach this hypha due to internal server errors: <code>%v</code>", hop.Errs))), "Error"
|
||||
}
|
||||
|
||||
if h.BinaryPath != "" {
|
||||
h.BinaryPath = ""
|
||||
}
|
||||
// If nothing is left of the hypha
|
||||
if h.TextPath == "" {
|
||||
h.Delete()
|
||||
}
|
||||
return hop, ""
|
||||
}
|
87
shroom/upload.go
Normal file
87
shroom/upload.go
Normal file
@ -0,0 +1,87 @@
|
||||
package shroom
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"mime/multipart"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/bouncepaw/mycorrhiza/history"
|
||||
"github.com/bouncepaw/mycorrhiza/hyphae"
|
||||
"github.com/bouncepaw/mycorrhiza/mimetype"
|
||||
"github.com/bouncepaw/mycorrhiza/user"
|
||||
"github.com/bouncepaw/mycorrhiza/util"
|
||||
)
|
||||
|
||||
func UploadText(h *hyphae.Hypha, data []byte, u *user.User) (hop *history.HistoryOp, errtitle string) {
|
||||
hop = history.Operation(history.TypeEditText)
|
||||
if h.Exists {
|
||||
hop.WithMsg(fmt.Sprintf("Edit ‘%s’", h.Name))
|
||||
} else {
|
||||
hop.WithMsg(fmt.Sprintf("Create ‘%s’", h.Name))
|
||||
}
|
||||
|
||||
if err, errtitle := CanEdit(u, h); err != nil {
|
||||
return hop.WithError(err), errtitle
|
||||
}
|
||||
if len(data) == 0 {
|
||||
return hop.WithError(errors.New("No data passed")), "Empty"
|
||||
}
|
||||
|
||||
return uploadHelp(h, hop, ".myco", data, u)
|
||||
}
|
||||
|
||||
func UploadBinary(h *hyphae.Hypha, mime string, file multipart.File, u *user.User) (*history.HistoryOp, string) {
|
||||
var (
|
||||
hop = history.Operation(history.TypeEditBinary).WithMsg(fmt.Sprintf("Upload binary part for ‘%s’ with type ‘%s’", h.Name, mime))
|
||||
data, err = ioutil.ReadAll(file)
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
return hop.WithError(err), err.Error()
|
||||
}
|
||||
if err, errtitle := CanAttach(u, h); err != nil {
|
||||
return hop.WithError(err), errtitle
|
||||
}
|
||||
if len(data) == 0 {
|
||||
return hop.WithError(errors.New("No data passed")), "Empty"
|
||||
}
|
||||
|
||||
return uploadHelp(h, hop, mimetype.ToExtension(mime), data, u)
|
||||
}
|
||||
|
||||
// uploadHelp is a helper function for UploadText and UploadBinary
|
||||
func uploadHelp(h *hyphae.Hypha, hop *history.HistoryOp, ext string, data []byte, u *user.User) (*history.HistoryOp, string) {
|
||||
var (
|
||||
fullPath = filepath.Join(util.WikiDir, h.Name+ext)
|
||||
originalFullPath = &h.TextPath
|
||||
)
|
||||
if hop.Type == history.TypeEditBinary {
|
||||
originalFullPath = &h.BinaryPath
|
||||
}
|
||||
|
||||
if err := os.MkdirAll(filepath.Dir(fullPath), 0777); err != nil {
|
||||
return hop.WithError(err), err.Error()
|
||||
}
|
||||
|
||||
if err := ioutil.WriteFile(fullPath, data, 0644); err != nil {
|
||||
return hop.WithError(err), err.Error()
|
||||
}
|
||||
|
||||
if h.Exists && *originalFullPath != fullPath && *originalFullPath != "" {
|
||||
if err := history.Rename(*originalFullPath, fullPath); err != nil {
|
||||
return hop.WithError(err), err.Error()
|
||||
}
|
||||
log.Println("Move", *originalFullPath, "to", fullPath)
|
||||
}
|
||||
|
||||
h.InsertIfNew()
|
||||
if h.Exists && h.TextPath != "" && hop.Type == history.TypeEditText && !history.FileChanged(fullPath) {
|
||||
return hop.Abort(), "No changes"
|
||||
}
|
||||
*originalFullPath = fullPath
|
||||
return hop.WithFiles(fullPath).WithUser(u).Apply(), ""
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
package hyphae
|
||||
package shroom
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
@ -6,12 +6,13 @@ import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/bouncepaw/mycorrhiza/hyphae"
|
||||
"github.com/bouncepaw/mycorrhiza/markup"
|
||||
"github.com/bouncepaw/mycorrhiza/util"
|
||||
)
|
||||
|
||||
// FetchTextPart tries to read text file of the given hypha. If there is no file, empty string is returned.
|
||||
func (h *Hypha) FetchTextPart() (string, error) {
|
||||
func FetchTextPart(h *hyphae.Hypha) (string, error) {
|
||||
if h.TextPath == "" {
|
||||
return "", nil
|
||||
}
|
||||
@ -25,7 +26,7 @@ func (h *Hypha) FetchTextPart() (string, error) {
|
||||
}
|
||||
|
||||
// binaryHtmlBlock creates an html block for binary part of the hypha.
|
||||
func (h *Hypha) BinaryHtmlBlock() string {
|
||||
func BinaryHtmlBlock(h *hyphae.Hypha) string {
|
||||
switch filepath.Ext(h.BinaryPath) {
|
||||
case ".jpg", ".gif", ".png", ".webp", ".svg", ".ico":
|
||||
return fmt.Sprintf(`
|
||||
@ -35,7 +36,7 @@ func (h *Hypha) BinaryHtmlBlock() string {
|
||||
case ".ogg", ".webm", ".mp4":
|
||||
return fmt.Sprintf(`
|
||||
<div class="binary-container binary-container_with-video">
|
||||
<video>
|
||||
<video controls>
|
||||
<source src="/binary/%[1]s"/>
|
||||
<p>Your browser does not support video. <a href="/binary/%[1]s">Download video</a></p>
|
||||
</video>
|
||||
@ -43,7 +44,7 @@ func (h *Hypha) BinaryHtmlBlock() string {
|
||||
case ".mp3":
|
||||
return fmt.Sprintf(`
|
||||
<div class="binary-container binary-container_with-audio">
|
||||
<audio>
|
||||
<audio controls>
|
||||
<source src="/binary/%[1]s"/>
|
||||
<p>Your browser does not support audio. <a href="/binary/%[1]s">Download audio</a></p>
|
||||
</audio>
|
||||
@ -58,7 +59,7 @@ func (h *Hypha) BinaryHtmlBlock() string {
|
||||
}
|
||||
|
||||
func SetHeaderLinks() {
|
||||
if userLinksHypha := ByName(util.HeaderLinksHypha); !userLinksHypha.Exists {
|
||||
if userLinksHypha := hyphae.ByName(util.HeaderLinksHypha); !userLinksHypha.Exists {
|
||||
util.SetDefaultHeaderLinks()
|
||||
} else {
|
||||
contents, err := ioutil.ReadFile(userLinksHypha.TextPath)
|
Loading…
Reference in New Issue
Block a user