mirror of
https://github.com/osmarks/mycorrhiza.git
synced 2025-01-19 15:12:49 +00:00
commit
54fdb29ddf
@ -2,6 +2,8 @@
|
|||||||
|
|
||||||
<img src="https://mycorrhiza.wiki/binary/release/1.4/screenshot" alt="A screenshot of mycorrhiza.wiki's home page in the Safari browser" width="600">
|
<img src="https://mycorrhiza.wiki/binary/release/1.4/screenshot" alt="A screenshot of mycorrhiza.wiki's home page in the Safari browser" width="600">
|
||||||
|
|
||||||
|
[![Go Report Card](https://goreportcard.com/badge/github.com/bouncepaw/mycorrhiza)](https://goreportcard.com/report/github.com/bouncepaw/mycorrhiza)
|
||||||
|
|
||||||
**Mycorrhiza Wiki** is a lightweight file-system wiki engine that uses Git for keeping history.
|
**Mycorrhiza Wiki** is a lightweight file-system wiki engine that uses Git for keeping history.
|
||||||
|
|
||||||
[👉 Main wiki](https://mycorrhiza.wiki)
|
[👉 Main wiki](https://mycorrhiza.wiki)
|
||||||
|
1
flag.go
1
flag.go
@ -28,6 +28,7 @@ func printHelp() {
|
|||||||
flag.PrintDefaults()
|
flag.PrintDefaults()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// CreateUserCommand is parameters for admin creation flag. Only a single parameter, though.
|
||||||
type CreateUserCommand struct {
|
type CreateUserCommand struct {
|
||||||
name string
|
name string
|
||||||
}
|
}
|
||||||
|
@ -33,6 +33,7 @@ func Start() {
|
|||||||
gitpath = path
|
gitpath = path
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// InitGitRepo checks a Git repository and initializes it if necessary.
|
||||||
func InitGitRepo() {
|
func InitGitRepo() {
|
||||||
// Detect if the Git repo directory is a Git repository
|
// Detect if the Git repo directory is a Git repository
|
||||||
isGitRepo := true
|
isGitRepo := true
|
||||||
@ -144,7 +145,7 @@ func (rev *Revision) textDiff() (diff string) {
|
|||||||
for _, filename := range filenames {
|
for _, filename := range filenames {
|
||||||
text, err := PrimitiveDiffAtRevision(filename, rev.Hash)
|
text, err := PrimitiveDiffAtRevision(filename, rev.Hash)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
diff += "\nAn error has occured with " + filename + "\n"
|
diff += "\nAn error has occurred with " + filename + "\n"
|
||||||
}
|
}
|
||||||
diff += text + "\n"
|
diff += text + "\n"
|
||||||
}
|
}
|
||||||
|
@ -52,18 +52,22 @@ func recentChangesFeed() *feeds.Feed {
|
|||||||
return feed
|
return feed
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// RecentChangesRSS creates recent changes feed in RSS format.
|
||||||
func RecentChangesRSS() (string, error) {
|
func RecentChangesRSS() (string, error) {
|
||||||
return recentChangesFeed().ToRss()
|
return recentChangesFeed().ToRss()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// RecentChangesAtom creates recent changes feed in Atom format.
|
||||||
func RecentChangesAtom() (string, error) {
|
func RecentChangesAtom() (string, error) {
|
||||||
return recentChangesFeed().ToAtom()
|
return recentChangesFeed().ToAtom()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// RecentChangesJSON creates recent changes feed in JSON format.
|
||||||
func RecentChangesJSON() (string, error) {
|
func RecentChangesJSON() (string, error) {
|
||||||
return recentChangesFeed().ToJSON()
|
return recentChangesFeed().ToJSON()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// RecentChanges gathers an arbitrary number of latest changes in form of revisions slice.
|
||||||
func RecentChanges(n int) []Revision {
|
func RecentChanges(n int) []Revision {
|
||||||
var (
|
var (
|
||||||
out, err = silentGitsh(
|
out, err = silentGitsh(
|
||||||
@ -110,8 +114,8 @@ func Revisions(hyphaName string) ([]Revision, error) {
|
|||||||
return revs, err
|
return revs, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// HistoryWithRevisions returns an html representation of `revs` that is meant to be inserted in a history page.
|
// WithRevisions returns an html representation of `revs` that is meant to be inserted in a history page.
|
||||||
func HistoryWithRevisions(hyphaName string, revs []Revision) (html string) {
|
func WithRevisions(hyphaName string, revs []Revision) (html string) {
|
||||||
var (
|
var (
|
||||||
currentYear int
|
currentYear int
|
||||||
currentMonth time.Month
|
currentMonth time.Month
|
||||||
|
@ -19,16 +19,22 @@ var gitMutex = sync.Mutex{}
|
|||||||
type OpType int
|
type OpType int
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
// TypeNone represents an empty operation. Not to be used in practice.
|
||||||
TypeNone OpType = iota
|
TypeNone OpType = iota
|
||||||
|
// TypeEditText represents an edit of hypha text part.
|
||||||
TypeEditText
|
TypeEditText
|
||||||
|
// TypeEditBinary represents an addition or replacement of hypha attachment.
|
||||||
TypeEditBinary
|
TypeEditBinary
|
||||||
|
// TypeDeleteHypha represents a hypha deletion
|
||||||
TypeDeleteHypha
|
TypeDeleteHypha
|
||||||
|
// TypeRenameHypha represents a hypha renaming
|
||||||
TypeRenameHypha
|
TypeRenameHypha
|
||||||
|
// TypeUnattachHypha represents a hypha attachment deletion
|
||||||
TypeUnattachHypha
|
TypeUnattachHypha
|
||||||
)
|
)
|
||||||
|
|
||||||
// HistoryOp is an object representing a history operation.
|
// Op is an object representing a history operation.
|
||||||
type HistoryOp struct {
|
type Op struct {
|
||||||
// All errors are appended here.
|
// All errors are appended here.
|
||||||
Errs []error
|
Errs []error
|
||||||
Type OpType
|
Type OpType
|
||||||
@ -38,9 +44,9 @@ type HistoryOp struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Operation is a constructor of a history operation.
|
// Operation is a constructor of a history operation.
|
||||||
func Operation(opType OpType) *HistoryOp {
|
func Operation(opType OpType) *Op {
|
||||||
gitMutex.Lock()
|
gitMutex.Lock()
|
||||||
hop := &HistoryOp{
|
hop := &Op{
|
||||||
Errs: []error{},
|
Errs: []error{},
|
||||||
name: "anon",
|
name: "anon",
|
||||||
email: "anon@mycorrhiza",
|
email: "anon@mycorrhiza",
|
||||||
@ -50,7 +56,7 @@ func Operation(opType OpType) *HistoryOp {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// git operation maker helper
|
// git operation maker helper
|
||||||
func (hop *HistoryOp) gitop(args ...string) *HistoryOp {
|
func (hop *Op) gitop(args ...string) *Op {
|
||||||
out, err := gitsh(args...)
|
out, err := gitsh(args...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Println("out:", out.String())
|
fmt.Println("out:", out.String())
|
||||||
@ -60,17 +66,18 @@ func (hop *HistoryOp) gitop(args ...string) *HistoryOp {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// WithErr appends the `err` to the list of errors.
|
// WithErr appends the `err` to the list of errors.
|
||||||
func (hop *HistoryOp) WithErr(err error) *HistoryOp {
|
func (hop *Op) WithErr(err error) *Op {
|
||||||
hop.Errs = append(hop.Errs, err)
|
hop.Errs = append(hop.Errs, err)
|
||||||
return hop
|
return hop
|
||||||
}
|
}
|
||||||
|
|
||||||
func (hop *HistoryOp) WithErrAbort(err error) *HistoryOp {
|
// WithErrAbort appends the `err` to the list of errors and immediately aborts the operation.
|
||||||
|
func (hop *Op) WithErrAbort(err error) *Op {
|
||||||
return hop.WithErr(err).Abort()
|
return hop.WithErr(err).Abort()
|
||||||
}
|
}
|
||||||
|
|
||||||
// WithFilesRemoved git-rm-s all passed `paths`. Paths can be rooted or not. Paths that are empty strings are ignored.
|
// WithFilesRemoved git-rm-s all passed `paths`. Paths can be rooted or not. Paths that are empty strings are ignored.
|
||||||
func (hop *HistoryOp) WithFilesRemoved(paths ...string) *HistoryOp {
|
func (hop *Op) WithFilesRemoved(paths ...string) *Op {
|
||||||
args := []string{"rm", "--quiet", "--"}
|
args := []string{"rm", "--quiet", "--"}
|
||||||
for _, path := range paths {
|
for _, path := range paths {
|
||||||
if path != "" {
|
if path != "" {
|
||||||
@ -81,7 +88,7 @@ func (hop *HistoryOp) WithFilesRemoved(paths ...string) *HistoryOp {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// WithFilesRenamed git-mv-s all passed keys of `pairs` to values of `pairs`. Paths can be rooted ot not. Empty keys are ignored.
|
// WithFilesRenamed git-mv-s all passed keys of `pairs` to values of `pairs`. Paths can be rooted ot not. Empty keys are ignored.
|
||||||
func (hop *HistoryOp) WithFilesRenamed(pairs map[string]string) *HistoryOp {
|
func (hop *Op) WithFilesRenamed(pairs map[string]string) *Op {
|
||||||
for from, to := range pairs {
|
for from, to := range pairs {
|
||||||
if from != "" {
|
if from != "" {
|
||||||
if err := os.MkdirAll(filepath.Dir(to), 0777); err != nil {
|
if err := os.MkdirAll(filepath.Dir(to), 0777); err != nil {
|
||||||
@ -97,7 +104,7 @@ func (hop *HistoryOp) WithFilesRenamed(pairs map[string]string) *HistoryOp {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// WithFiles stages all passed `paths`. Paths can be rooted or not.
|
// WithFiles stages all passed `paths`. Paths can be rooted or not.
|
||||||
func (hop *HistoryOp) WithFiles(paths ...string) *HistoryOp {
|
func (hop *Op) WithFiles(paths ...string) *Op {
|
||||||
for i, path := range paths {
|
for i, path := range paths {
|
||||||
paths[i] = util.ShorterPath(path)
|
paths[i] = util.ShorterPath(path)
|
||||||
}
|
}
|
||||||
@ -106,7 +113,7 @@ func (hop *HistoryOp) WithFiles(paths ...string) *HistoryOp {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Apply applies history operation by doing the commit.
|
// Apply applies history operation by doing the commit.
|
||||||
func (hop *HistoryOp) Apply() *HistoryOp {
|
func (hop *Op) Apply() *Op {
|
||||||
hop.gitop(
|
hop.gitop(
|
||||||
"commit",
|
"commit",
|
||||||
"--author='"+hop.name+" <"+hop.email+">'",
|
"--author='"+hop.name+" <"+hop.email+">'",
|
||||||
@ -117,13 +124,13 @@ func (hop *HistoryOp) Apply() *HistoryOp {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Abort aborts the history operation.
|
// Abort aborts the history operation.
|
||||||
func (hop *HistoryOp) Abort() *HistoryOp {
|
func (hop *Op) Abort() *Op {
|
||||||
gitMutex.Unlock()
|
gitMutex.Unlock()
|
||||||
return hop
|
return hop
|
||||||
}
|
}
|
||||||
|
|
||||||
// WithMsg sets what message will be used for the future commit. If user message exceeds one line, it is stripped down.
|
// WithMsg sets what message will be used for the future commit. If user message exceeds one line, it is stripped down.
|
||||||
func (hop *HistoryOp) WithMsg(userMsg string) *HistoryOp {
|
func (hop *Op) WithMsg(userMsg string) *Op {
|
||||||
for _, ch := range userMsg {
|
for _, ch := range userMsg {
|
||||||
if ch == '\r' || ch == '\n' {
|
if ch == '\r' || ch == '\n' {
|
||||||
break
|
break
|
||||||
@ -134,7 +141,7 @@ func (hop *HistoryOp) WithMsg(userMsg string) *HistoryOp {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// WithUser sets a user for the commit.
|
// WithUser sets a user for the commit.
|
||||||
func (hop *HistoryOp) WithUser(u *user.User) *HistoryOp {
|
func (hop *Op) WithUser(u *user.User) *Op {
|
||||||
if u.Group != "anon" {
|
if u.Group != "anon" {
|
||||||
hop.name = u.Name
|
hop.name = u.Name
|
||||||
hop.email = u.Name + "@mycorrhiza"
|
hop.email = u.Name + "@mycorrhiza"
|
||||||
@ -142,10 +149,12 @@ func (hop *HistoryOp) WithUser(u *user.User) *HistoryOp {
|
|||||||
return hop
|
return hop
|
||||||
}
|
}
|
||||||
|
|
||||||
func (hop *HistoryOp) HasErrors() bool {
|
// HasErrors checks whether operation has errors appended.
|
||||||
|
func (hop *Op) HasErrors() bool {
|
||||||
return len(hop.Errs) > 0
|
return len(hop.Errs) > 0
|
||||||
}
|
}
|
||||||
|
|
||||||
func (hop *HistoryOp) FirstErrorText() string {
|
// FirstErrorText extracts first error appended to the operation.
|
||||||
|
func (hop *Op) FirstErrorText() string {
|
||||||
return hop.Errs[0].Error()
|
return hop.Errs[0].Error()
|
||||||
}
|
}
|
||||||
|
@ -38,12 +38,14 @@ type BacklinkIndexOperation interface {
|
|||||||
Apply()
|
Apply()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// BacklinkIndexEdit contains data for backlink index update after a hypha edit
|
||||||
type BacklinkIndexEdit struct {
|
type BacklinkIndexEdit struct {
|
||||||
Name string
|
Name string
|
||||||
OldLinks []string
|
OldLinks []string
|
||||||
NewLinks []string
|
NewLinks []string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Apply changes backlink index respective to the operation data
|
||||||
func (op BacklinkIndexEdit) Apply() {
|
func (op BacklinkIndexEdit) Apply() {
|
||||||
oldLinks := toLinkSet(op.OldLinks)
|
oldLinks := toLinkSet(op.OldLinks)
|
||||||
newLinks := toLinkSet(op.NewLinks)
|
newLinks := toLinkSet(op.NewLinks)
|
||||||
@ -62,11 +64,13 @@ func (op BacklinkIndexEdit) Apply() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// BacklinkIndexDeletion contains data for backlink index update after a hypha deletion
|
||||||
type BacklinkIndexDeletion struct {
|
type BacklinkIndexDeletion struct {
|
||||||
Name string
|
Name string
|
||||||
Links []string
|
Links []string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Apply changes backlink index respective to the operation data
|
||||||
func (op BacklinkIndexDeletion) Apply() {
|
func (op BacklinkIndexDeletion) Apply() {
|
||||||
for _, link := range op.Links {
|
for _, link := range op.Links {
|
||||||
if lSet, exists := backlinkIndex[link]; exists {
|
if lSet, exists := backlinkIndex[link]; exists {
|
||||||
@ -75,12 +79,14 @@ func (op BacklinkIndexDeletion) Apply() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// BacklinkIndexRenaming contains data for backlink index update after a hypha renaming
|
||||||
type BacklinkIndexRenaming struct {
|
type BacklinkIndexRenaming struct {
|
||||||
OldName string
|
OldName string
|
||||||
NewName string
|
NewName string
|
||||||
Links []string
|
Links []string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Apply changes backlink index respective to the operation data
|
||||||
func (op BacklinkIndexRenaming) Apply() {
|
func (op BacklinkIndexRenaming) Apply() {
|
||||||
for _, link := range op.Links {
|
for _, link := range op.Links {
|
||||||
if lSet, exists := backlinkIndex[link]; exists {
|
if lSet, exists := backlinkIndex[link]; exists {
|
||||||
|
@ -10,21 +10,21 @@ var count = struct {
|
|||||||
sync.Mutex
|
sync.Mutex
|
||||||
}{}
|
}{}
|
||||||
|
|
||||||
// Set the value of hyphae count to zero. Use when reloading hyphae.
|
// ResetCount sets the value of hyphae count to zero. Use when reloading hyphae.
|
||||||
func ResetCount() {
|
func ResetCount() {
|
||||||
count.Lock()
|
count.Lock()
|
||||||
count.value = 0
|
count.value = 0
|
||||||
count.Unlock()
|
count.Unlock()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Increment the value of the hyphae counter. Use when creating new hyphae or loading hyphae from disk.
|
// IncrementCount increments the value of the hyphae counter. Use when creating new hyphae or loading hyphae from disk.
|
||||||
func IncrementCount() {
|
func IncrementCount() {
|
||||||
count.Lock()
|
count.Lock()
|
||||||
count.value++
|
count.value++
|
||||||
count.Unlock()
|
count.Unlock()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Decrement the value of the hyphae counter. Use when deleting existing hyphae.
|
// DecrementCount decrements the value of the hyphae counter. Use when deleting existing hyphae.
|
||||||
func DecrementCount() {
|
func DecrementCount() {
|
||||||
count.Lock()
|
count.Lock()
|
||||||
count.value--
|
count.value--
|
||||||
|
@ -12,6 +12,7 @@ import (
|
|||||||
// HyphaPattern is a pattern which all hyphae must match.
|
// HyphaPattern is a pattern which all hyphae must match.
|
||||||
var HyphaPattern = regexp.MustCompile(`[^?!:#@><*|"'&%{}]+`)
|
var HyphaPattern = regexp.MustCompile(`[^?!:#@><*|"'&%{}]+`)
|
||||||
|
|
||||||
|
// Hypha keeps vital information about a hypha
|
||||||
type Hypha struct {
|
type Hypha struct {
|
||||||
sync.RWMutex
|
sync.RWMutex
|
||||||
|
|
||||||
@ -79,6 +80,7 @@ func (h *Hypha) Insert() (justRecorded bool) {
|
|||||||
return !recorded
|
return !recorded
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// InsertIfNew checks whether hypha exists and returns `true` if it didn't and has been created.
|
||||||
func (h *Hypha) InsertIfNew() (justRecorded bool) {
|
func (h *Hypha) InsertIfNew() (justRecorded bool) {
|
||||||
if !h.Exists {
|
if !h.Exists {
|
||||||
return h.Insert()
|
return h.Insert()
|
||||||
@ -86,6 +88,7 @@ func (h *Hypha) InsertIfNew() (justRecorded bool) {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Delete removes a hypha from the storage.
|
||||||
func (h *Hypha) Delete() {
|
func (h *Hypha) Delete() {
|
||||||
byNamesMutex.Lock()
|
byNamesMutex.Lock()
|
||||||
h.Lock()
|
h.Lock()
|
||||||
@ -95,6 +98,7 @@ func (h *Hypha) Delete() {
|
|||||||
h.Unlock()
|
h.Unlock()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// RenameTo renames a hypha and performs respective changes in the storage.
|
||||||
func (h *Hypha) RenameTo(newName string) {
|
func (h *Hypha) RenameTo(newName string) {
|
||||||
byNamesMutex.Lock()
|
byNamesMutex.Lock()
|
||||||
h.Lock()
|
h.Lock()
|
||||||
|
@ -11,6 +11,7 @@ type Iteration struct {
|
|||||||
checks []func(h *Hypha) CheckResult
|
checks []func(h *Hypha) CheckResult
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NewIteration constructs an iteration without checks.
|
||||||
func NewIteration() *Iteration {
|
func NewIteration() *Iteration {
|
||||||
return &Iteration{
|
return &Iteration{
|
||||||
iterator: YieldExistingHyphae,
|
iterator: YieldExistingHyphae,
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
// File `iterators.go` contains stuff that iterates over hyphae.
|
|
||||||
package hyphae
|
package hyphae
|
||||||
|
|
||||||
|
// File `iterators.go` contains stuff that iterates over hyphae.
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"sort"
|
"sort"
|
||||||
"strings"
|
"strings"
|
||||||
|
@ -14,7 +14,7 @@ var locales = language.NewMatcher([]language.Tag{
|
|||||||
language.Make("ru"),
|
language.Make("ru"),
|
||||||
})
|
})
|
||||||
|
|
||||||
// GetLocalizer takes a HTTP request and picks the most appropriate localizer (with English fallback)
|
// FromRequest takes a HTTP request and picks the most appropriate localizer (with English fallback)
|
||||||
func FromRequest(r *http.Request) *Localizer {
|
func FromRequest(r *http.Request) *Localizer {
|
||||||
t, _, _ := language.ParseAcceptLanguage(r.Header.Get("Accept-Language"))
|
t, _, _ := language.ParseAcceptLanguage(r.Header.Get("Accept-Language"))
|
||||||
tag, _, _ := locales.Match(t...)
|
tag, _, _ := locales.Match(t...)
|
||||||
@ -91,7 +91,7 @@ func getLocalizationKey(locale string, key string) string {
|
|||||||
|
|
||||||
/* chekoopa: Missing translation features:
|
/* chekoopa: Missing translation features:
|
||||||
- history records (they use Git description, the possible solution is to parse and translate)
|
- history records (they use Git description, the possible solution is to parse and translate)
|
||||||
- history dates (HistoryWithRevisions doesn't consider locale, Monday package is bad idea)
|
- history dates (history.WithRevisions doesn't consider locale, Monday package is bad idea)
|
||||||
- probably error messages (which are scattered across the code)
|
- probably error messages (which are scattered across the code)
|
||||||
- default top bar (it is static from one-shot cfg.SetDefaultHeaderLinks, but it is possible to track default-ness in templates)
|
- default top bar (it is static from one-shot cfg.SetDefaultHeaderLinks, but it is possible to track default-ness in templates)
|
||||||
- alt solution is implementing "special" links
|
- alt solution is implementing "special" links
|
||||||
|
@ -14,29 +14,30 @@ func canFactory(
|
|||||||
noRightsMsg string,
|
noRightsMsg string,
|
||||||
notExistsMsg string,
|
notExistsMsg string,
|
||||||
careAboutExistence bool,
|
careAboutExistence bool,
|
||||||
) func(*user.User, *hyphae.Hypha) (error, string) {
|
) func(*user.User, *hyphae.Hypha) (string, error) {
|
||||||
return func(u *user.User, h *hyphae.Hypha) (error, string) {
|
return func(u *user.User, h *hyphae.Hypha) (string, error) {
|
||||||
if !u.CanProceed(action) {
|
if !u.CanProceed(action) {
|
||||||
rejectLogger(h, u, "no rights")
|
rejectLogger(h, u, "no rights")
|
||||||
return errors.New(noRightsMsg), "Not enough rights"
|
return "Not enough rights", errors.New(noRightsMsg)
|
||||||
}
|
}
|
||||||
|
|
||||||
if careAboutExistence && !h.Exists {
|
if careAboutExistence && !h.Exists {
|
||||||
rejectLogger(h, u, "does not exist")
|
rejectLogger(h, u, "does not exist")
|
||||||
return errors.New(notExistsMsg), "Does not exist"
|
return "Does not exist", errors.New(notExistsMsg)
|
||||||
}
|
}
|
||||||
|
|
||||||
if dispatcher == nil {
|
if dispatcher == nil {
|
||||||
return nil, ""
|
return "", nil
|
||||||
}
|
}
|
||||||
errmsg, errtitle := dispatcher(h, u)
|
errmsg, errtitle := dispatcher(h, u)
|
||||||
if errtitle == "" {
|
if errtitle == "" {
|
||||||
return nil, ""
|
return "", nil
|
||||||
}
|
}
|
||||||
return errors.New(errmsg), errtitle
|
return errtitle, errors.New(errmsg)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// CanDelete and etc are hyphae operation checkers based on user rights and hyphae existence.
|
||||||
var (
|
var (
|
||||||
CanDelete = canFactory(
|
CanDelete = canFactory(
|
||||||
rejectDeleteLog,
|
rejectDeleteLog,
|
||||||
|
@ -9,10 +9,10 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
// DeleteHypha deletes hypha and makes a history record about that.
|
// DeleteHypha deletes hypha and makes a history record about that.
|
||||||
func DeleteHypha(u *user.User, h *hyphae.Hypha) (hop *history.HistoryOp, errtitle string) {
|
func DeleteHypha(u *user.User, h *hyphae.Hypha) (hop *history.Op, errtitle string) {
|
||||||
hop = history.Operation(history.TypeDeleteHypha)
|
hop = history.Operation(history.TypeDeleteHypha)
|
||||||
|
|
||||||
if err, errtitle := CanDelete(u, h); errtitle != "" {
|
if errtitle, err := CanDelete(u, h); errtitle != "" {
|
||||||
hop.WithErrAbort(err)
|
hop.WithErrAbort(err)
|
||||||
return hop, errtitle
|
return hop, errtitle
|
||||||
}
|
}
|
||||||
|
@ -11,36 +11,36 @@ import (
|
|||||||
"github.com/bouncepaw/mycorrhiza/util"
|
"github.com/bouncepaw/mycorrhiza/util"
|
||||||
)
|
)
|
||||||
|
|
||||||
func canRenameThisToThat(oh *hyphae.Hypha, nh *hyphae.Hypha, u *user.User) (err error, errtitle string) {
|
func canRenameThisToThat(oh *hyphae.Hypha, nh *hyphae.Hypha, u *user.User) (errtitle string, err error) {
|
||||||
if nh.Exists {
|
if nh.Exists {
|
||||||
rejectRenameLog(oh, u, fmt.Sprintf("name ‘%s’ taken already", nh.Name))
|
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"
|
return "Name taken", fmt.Errorf("Hypha named <a href='/hypha/%[1]s'>%[1]s</a> already exists, cannot rename", nh.Name)
|
||||||
}
|
}
|
||||||
|
|
||||||
if nh.Name == "" {
|
if nh.Name == "" {
|
||||||
rejectRenameLog(oh, u, "no new name given")
|
rejectRenameLog(oh, u, "no new name given")
|
||||||
return errors.New("No new name is given"), "No name given"
|
return "No name given", errors.New("No new name is given")
|
||||||
}
|
}
|
||||||
|
|
||||||
if !hyphae.HyphaPattern.MatchString(nh.Name) {
|
if !hyphae.HyphaPattern.MatchString(nh.Name) {
|
||||||
rejectRenameLog(oh, u, fmt.Sprintf("new name ‘%s’ invalid", 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"
|
return "Invalid name", errors.New("Invalid new name. Names cannot contain characters <code>^?!:#@><*|\"\\'&%</code>")
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil, ""
|
return "", nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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.
|
// 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 RenameHypha(h *hyphae.Hypha, newHypha *hyphae.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.Op, errtitle string) {
|
||||||
newHypha.Lock()
|
newHypha.Lock()
|
||||||
defer newHypha.Unlock()
|
defer newHypha.Unlock()
|
||||||
hop = history.Operation(history.TypeRenameHypha)
|
hop = history.Operation(history.TypeRenameHypha)
|
||||||
|
|
||||||
if err, errtitle := CanRename(u, h); errtitle != "" {
|
if errtitle, err := CanRename(u, h); errtitle != "" {
|
||||||
hop.WithErrAbort(err)
|
hop.WithErrAbort(err)
|
||||||
return hop, errtitle
|
return hop, errtitle
|
||||||
}
|
}
|
||||||
if err, errtitle := canRenameThisToThat(h, newHypha, u); errtitle != "" {
|
if errtitle, err := canRenameThisToThat(h, newHypha, u); errtitle != "" {
|
||||||
hop.WithErrAbort(err)
|
hop.WithErrAbort(err)
|
||||||
return hop, errtitle
|
return hop, errtitle
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
package shroom
|
package shroom
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"github.com/bouncepaw/mycorrhiza/history"
|
"github.com/bouncepaw/mycorrhiza/history"
|
||||||
@ -10,10 +9,10 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
// UnattachHypha unattaches hypha and makes a history record about that.
|
// UnattachHypha unattaches hypha and makes a history record about that.
|
||||||
func UnattachHypha(u *user.User, h *hyphae.Hypha) (hop *history.HistoryOp, errtitle string) {
|
func UnattachHypha(u *user.User, h *hyphae.Hypha) (hop *history.Op, errtitle string) {
|
||||||
hop = history.Operation(history.TypeUnattachHypha)
|
hop = history.Operation(history.TypeUnattachHypha)
|
||||||
|
|
||||||
if err, errtitle := CanUnattach(u, h); errtitle != "" {
|
if errtitle, err := CanUnattach(u, h); errtitle != "" {
|
||||||
hop.WithErrAbort(err)
|
hop.WithErrAbort(err)
|
||||||
return hop, errtitle
|
return hop, errtitle
|
||||||
}
|
}
|
||||||
@ -27,7 +26,7 @@ func UnattachHypha(u *user.User, h *hyphae.Hypha) (hop *history.HistoryOp, errti
|
|||||||
if len(hop.Errs) > 0 {
|
if len(hop.Errs) > 0 {
|
||||||
rejectUnattachLog(h, u, "fail")
|
rejectUnattachLog(h, u, "fail")
|
||||||
// FIXME: something may be wrong here
|
// FIXME: something may be wrong here
|
||||||
return hop.WithErrAbort(errors.New(fmt.Sprintf("Could not unattach this hypha due to internal server errors: <code>%v</code>", hop.Errs))), "Error"
|
return hop.WithErrAbort(fmt.Errorf("Could not unattach this hypha due to internal server errors: <code>%v</code>", hop.Errs)), "Error"
|
||||||
}
|
}
|
||||||
|
|
||||||
if h.BinaryPath != "" {
|
if h.BinaryPath != "" {
|
||||||
|
@ -18,7 +18,8 @@ import (
|
|||||||
"github.com/bouncepaw/mycorrhiza/user"
|
"github.com/bouncepaw/mycorrhiza/user"
|
||||||
)
|
)
|
||||||
|
|
||||||
func UploadText(h *hyphae.Hypha, data []byte, message string, u *user.User) (hop *history.HistoryOp, errtitle string) {
|
// UploadText edits a hypha' text part and makes a history record about that.
|
||||||
|
func UploadText(h *hyphae.Hypha, data []byte, message string, u *user.User) (hop *history.Op, errtitle string) {
|
||||||
hop = history.Operation(history.TypeEditText)
|
hop = history.Operation(history.TypeEditText)
|
||||||
var action string
|
var action string
|
||||||
if h.Exists {
|
if h.Exists {
|
||||||
@ -33,7 +34,7 @@ func UploadText(h *hyphae.Hypha, data []byte, message string, u *user.User) (hop
|
|||||||
hop.WithMsg(fmt.Sprintf("%s ‘%s’: %s", action, h.Name, message))
|
hop.WithMsg(fmt.Sprintf("%s ‘%s’: %s", action, h.Name, message))
|
||||||
}
|
}
|
||||||
|
|
||||||
if err, errtitle := CanEdit(u, h); err != nil {
|
if errtitle, err := CanEdit(u, h); err != nil {
|
||||||
return hop.WithErrAbort(err), errtitle
|
return hop.WithErrAbort(err), errtitle
|
||||||
}
|
}
|
||||||
if len(bytes.TrimSpace(data)) == 0 && h.BinaryPath == "" {
|
if len(bytes.TrimSpace(data)) == 0 && h.BinaryPath == "" {
|
||||||
@ -43,7 +44,8 @@ func UploadText(h *hyphae.Hypha, data []byte, message string, u *user.User) (hop
|
|||||||
return uploadHelp(h, hop, ".myco", data, u)
|
return uploadHelp(h, hop, ".myco", data, u)
|
||||||
}
|
}
|
||||||
|
|
||||||
func UploadBinary(h *hyphae.Hypha, mime string, file multipart.File, u *user.User) (*history.HistoryOp, string) {
|
// UploadBinary edits a hypha' attachment and makes a history record about that.
|
||||||
|
func UploadBinary(h *hyphae.Hypha, mime string, file multipart.File, u *user.User) (*history.Op, string) {
|
||||||
var (
|
var (
|
||||||
hop = history.Operation(history.TypeEditBinary).WithMsg(fmt.Sprintf("Upload attachment for ‘%s’ with type ‘%s’", h.Name, mime))
|
hop = history.Operation(history.TypeEditBinary).WithMsg(fmt.Sprintf("Upload attachment for ‘%s’ with type ‘%s’", h.Name, mime))
|
||||||
data, err = io.ReadAll(file)
|
data, err = io.ReadAll(file)
|
||||||
@ -52,7 +54,7 @@ func UploadBinary(h *hyphae.Hypha, mime string, file multipart.File, u *user.Use
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return hop.WithErrAbort(err), err.Error()
|
return hop.WithErrAbort(err), err.Error()
|
||||||
}
|
}
|
||||||
if err, errtitle := CanAttach(u, h); err != nil {
|
if errtitle, err := CanAttach(u, h); err != nil {
|
||||||
return hop.WithErrAbort(err), errtitle
|
return hop.WithErrAbort(err), errtitle
|
||||||
}
|
}
|
||||||
if len(data) == 0 {
|
if len(data) == 0 {
|
||||||
@ -63,7 +65,7 @@ func UploadBinary(h *hyphae.Hypha, mime string, file multipart.File, u *user.Use
|
|||||||
}
|
}
|
||||||
|
|
||||||
// uploadHelp is a helper function for UploadText and UploadBinary
|
// 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) {
|
func uploadHelp(h *hyphae.Hypha, hop *history.Op, ext string, data []byte, u *user.User) (*history.Op, string) {
|
||||||
var (
|
var (
|
||||||
fullPath = filepath.Join(files.HyphaeDir(), h.Name+ext)
|
fullPath = filepath.Join(files.HyphaeDir(), h.Name+ext)
|
||||||
originalFullPath = &h.TextPath
|
originalFullPath = &h.TextPath
|
||||||
|
@ -21,6 +21,7 @@ func FetchTextPart(h *hyphae.Hypha) (string, error) {
|
|||||||
return string(text), nil
|
return string(text), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SetHeaderLinks initializes header links by reading the configured hypha, if there is any, or resorting to default values.
|
||||||
func SetHeaderLinks() {
|
func SetHeaderLinks() {
|
||||||
if userLinksHypha := hyphae.ByName(cfg.HeaderLinksHypha); !userLinksHypha.Exists {
|
if userLinksHypha := hyphae.ByName(cfg.HeaderLinksHypha); !userLinksHypha.Exists {
|
||||||
cfg.SetDefaultHeaderLinks()
|
cfg.SetDefaultHeaderLinks()
|
||||||
|
@ -137,14 +137,14 @@ func figureOutChildren(hyphaName string, subhyphaePool map[string]bool, exists b
|
|||||||
nestLevel = strings.Count(hyphaName, "/")
|
nestLevel = strings.Count(hyphaName, "/")
|
||||||
adopted = make([]child, 0)
|
adopted = make([]child, 0)
|
||||||
)
|
)
|
||||||
for subhyphaName, _ := range subhyphaePool {
|
for subhyphaName := range subhyphaePool {
|
||||||
subnestLevel := strings.Count(subhyphaName, "/")
|
subnestLevel := strings.Count(subhyphaName, "/")
|
||||||
if subnestLevel-1 == nestLevel && path.Dir(subhyphaName) == hyphaName {
|
if subnestLevel-1 == nestLevel && path.Dir(subhyphaName) == hyphaName {
|
||||||
delete(subhyphaePool, subhyphaName)
|
delete(subhyphaePool, subhyphaName)
|
||||||
adopted = append(adopted, figureOutChildren(subhyphaName, subhyphaePool, true))
|
adopted = append(adopted, figureOutChildren(subhyphaName, subhyphaePool, true))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for descName, _ := range subhyphaePool {
|
for descName := range subhyphaePool {
|
||||||
if strings.HasPrefix(descName, hyphaName) {
|
if strings.HasPrefix(descName, hyphaName) {
|
||||||
var (
|
var (
|
||||||
rawSubPath = strings.TrimPrefix(descName, hyphaName)[1:]
|
rawSubPath = strings.TrimPrefix(descName, hyphaName)[1:]
|
||||||
|
@ -76,6 +76,7 @@ func readTokensToUsers() {
|
|||||||
log.Println("Found", len(tmp), "active sessions")
|
log.Println("Found", len(tmp), "active sessions")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SaveUserDatabase stores current user credentials into JSON file by configured path.
|
||||||
func SaveUserDatabase() error {
|
func SaveUserDatabase() error {
|
||||||
return dumpUserCredentials()
|
return dumpUserCredentials()
|
||||||
}
|
}
|
||||||
|
@ -27,7 +27,7 @@ func FromRequest(rq *http.Request) *User {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return EmptyUser()
|
return EmptyUser()
|
||||||
}
|
}
|
||||||
return UserByToken(cookie.Value)
|
return ByToken(cookie.Value)
|
||||||
}
|
}
|
||||||
|
|
||||||
// LogoutFromRequest logs the user in `rq` out and rewrites the cookie in `w`.
|
// LogoutFromRequest logs the user in `rq` out and rewrites the cookie in `w`.
|
||||||
@ -108,9 +108,9 @@ func AddSession(username string) (string, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// A handy cookie constructor
|
// A handy cookie constructor
|
||||||
func cookie(name_suffix, val string, t time.Time) *http.Cookie {
|
func cookie(nameSuffix, val string, t time.Time) *http.Cookie {
|
||||||
return &http.Cookie{
|
return &http.Cookie{
|
||||||
Name: "mycorrhiza_" + name_suffix,
|
Name: "mycorrhiza_" + nameSuffix,
|
||||||
Value: val,
|
Value: val,
|
||||||
Expires: t,
|
Expires: t,
|
||||||
Path: "/",
|
Path: "/",
|
||||||
|
@ -9,7 +9,7 @@ import (
|
|||||||
"golang.org/x/crypto/bcrypt"
|
"golang.org/x/crypto/bcrypt"
|
||||||
)
|
)
|
||||||
|
|
||||||
// User is a user.
|
// User is a user (duh).
|
||||||
type User struct {
|
type User struct {
|
||||||
// Name is a username. It must follow hypha naming rules.
|
// Name is a username. It must follow hypha naming rules.
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
@ -59,6 +59,7 @@ var groupRight = map[string]int{
|
|||||||
"admin": 4,
|
"admin": 4,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ValidGroup checks whether provided user group name exists.
|
||||||
func ValidGroup(group string) bool {
|
func ValidGroup(group string) bool {
|
||||||
for _, grp := range groups {
|
for _, grp := range groups {
|
||||||
if grp == group {
|
if grp == group {
|
||||||
@ -68,10 +69,12 @@ func ValidGroup(group string) bool {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ValidSource checks whether provided user source name exists.
|
||||||
func ValidSource(source string) bool {
|
func ValidSource(source string) bool {
|
||||||
return source == "local" || source == "telegram"
|
return source == "local" || source == "telegram"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// EmptyUser constructs an anonymous user.
|
||||||
func EmptyUser() *User {
|
func EmptyUser() *User {
|
||||||
return &User{
|
return &User{
|
||||||
Name: "anon",
|
Name: "anon",
|
||||||
@ -81,6 +84,7 @@ func EmptyUser() *User {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// CanProceed checks whether user has rights to visit the provided path (and perform an action).
|
||||||
func (user *User) CanProceed(route string) bool {
|
func (user *User) CanProceed(route string) bool {
|
||||||
if !cfg.UseAuth {
|
if !cfg.UseAuth {
|
||||||
return true
|
return true
|
||||||
|
@ -5,6 +5,7 @@ import "sync"
|
|||||||
var users sync.Map
|
var users sync.Map
|
||||||
var tokens sync.Map
|
var tokens sync.Map
|
||||||
|
|
||||||
|
// YieldUsers creates a channel which iterates existing users.
|
||||||
func YieldUsers() chan *User {
|
func YieldUsers() chan *User {
|
||||||
ch := make(chan *User)
|
ch := make(chan *User)
|
||||||
go func(ch chan *User) {
|
go func(ch chan *User) {
|
||||||
@ -17,6 +18,7 @@ func YieldUsers() chan *User {
|
|||||||
return ch
|
return ch
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ListUsersWithGroup returns a slice with users of desired group.
|
||||||
func ListUsersWithGroup(group string) []string {
|
func ListUsersWithGroup(group string) []string {
|
||||||
filtered := []string{}
|
filtered := []string{}
|
||||||
for u := range YieldUsers() {
|
for u := range YieldUsers() {
|
||||||
@ -27,6 +29,7 @@ func ListUsersWithGroup(group string) []string {
|
|||||||
return filtered
|
return filtered
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Count returns total users count
|
||||||
func Count() (i uint64) {
|
func Count() (i uint64) {
|
||||||
users.Range(func(k, v interface{}) bool {
|
users.Range(func(k, v interface{}) bool {
|
||||||
i++
|
i++
|
||||||
@ -35,24 +38,29 @@ func Count() (i uint64) {
|
|||||||
return i
|
return i
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// HasUsername checks whether the desired user exists
|
||||||
func HasUsername(username string) bool {
|
func HasUsername(username string) bool {
|
||||||
_, has := users.Load(username)
|
_, has := users.Load(username)
|
||||||
return has
|
return has
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// CredentialsOK checks whether a correct user-password pair is provided
|
||||||
func CredentialsOK(username, password string) bool {
|
func CredentialsOK(username, password string) bool {
|
||||||
return UserByName(username).isCorrectPassword(password)
|
return ByName(username).isCorrectPassword(password)
|
||||||
}
|
}
|
||||||
|
|
||||||
func UserByToken(token string) *User {
|
// ByToken finds a user by provided session token
|
||||||
|
func ByToken(token string) *User {
|
||||||
|
// TODO: Needs more session data -- chekoopa
|
||||||
if usernameUntyped, ok := tokens.Load(token); ok {
|
if usernameUntyped, ok := tokens.Load(token); ok {
|
||||||
username := usernameUntyped.(string)
|
username := usernameUntyped.(string)
|
||||||
return UserByName(username)
|
return ByName(username)
|
||||||
}
|
}
|
||||||
return EmptyUser()
|
return EmptyUser()
|
||||||
}
|
}
|
||||||
|
|
||||||
func UserByName(username string) *User {
|
// ByName finds a user by one's username
|
||||||
|
func ByName(username string) *User {
|
||||||
if userUntyped, ok := users.Load(username); ok {
|
if userUntyped, ok := users.Load(username); ok {
|
||||||
user := userUntyped.(*User)
|
user := userUntyped.(*User)
|
||||||
return user
|
return user
|
||||||
@ -60,6 +68,7 @@ func UserByName(username string) *User {
|
|||||||
return EmptyUser()
|
return EmptyUser()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// DeleteUser removes a user by one's name and saves user database.
|
||||||
func DeleteUser(name string) error {
|
func DeleteUser(name string) error {
|
||||||
user, loaded := users.LoadAndDelete(name)
|
user, loaded := users.LoadAndDelete(name)
|
||||||
if loaded {
|
if loaded {
|
||||||
|
@ -111,6 +111,7 @@ type FormData struct {
|
|||||||
fields map[string]string
|
fields map[string]string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NewFormData constructs empty form data instance.
|
||||||
func NewFormData() FormData {
|
func NewFormData() FormData {
|
||||||
return FormData{
|
return FormData{
|
||||||
err: nil,
|
err: nil,
|
||||||
@ -118,6 +119,7 @@ func NewFormData() FormData {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// FormDataFromRequest extracts a form data from request, using a set of keys.
|
||||||
func FormDataFromRequest(r *http.Request, keys []string) FormData {
|
func FormDataFromRequest(r *http.Request, keys []string) FormData {
|
||||||
formData := NewFormData()
|
formData := NewFormData()
|
||||||
for _, key := range keys {
|
for _, key := range keys {
|
||||||
@ -126,10 +128,12 @@ func FormDataFromRequest(r *http.Request, keys []string) FormData {
|
|||||||
return formData
|
return formData
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// HasError is true if there is indeed an error.
|
||||||
func (f FormData) HasError() bool {
|
func (f FormData) HasError() bool {
|
||||||
return f.err != nil
|
return f.err != nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Error returns an error text or empty string, if there are no errors in form data.
|
||||||
func (f FormData) Error() string {
|
func (f FormData) Error() string {
|
||||||
if f.err == nil {
|
if f.err == nil {
|
||||||
return ""
|
return ""
|
||||||
@ -137,15 +141,18 @@ func (f FormData) Error() string {
|
|||||||
return f.err.Error()
|
return f.err.Error()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// WithError puts an error into form data and returns itself.
|
||||||
func (f FormData) WithError(err error) FormData {
|
func (f FormData) WithError(err error) FormData {
|
||||||
f.err = err
|
f.err = err
|
||||||
return f
|
return f
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Get accesses form data with a key
|
||||||
func (f FormData) Get(key string) string {
|
func (f FormData) Get(key string) string {
|
||||||
return f.fields[key]
|
return f.fields[key]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Put writes a form value for provided key
|
||||||
func (f FormData) Put(key, value string) {
|
func (f FormData) Put(key, value string) {
|
||||||
f.fields[key] = value
|
f.fields[key] = value
|
||||||
}
|
}
|
||||||
|
@ -79,7 +79,7 @@ func handlerAdminUsers(w http.ResponseWriter, rq *http.Request) {
|
|||||||
|
|
||||||
func handlerAdminUserEdit(w http.ResponseWriter, rq *http.Request) {
|
func handlerAdminUserEdit(w http.ResponseWriter, rq *http.Request) {
|
||||||
vars := mux.Vars(rq)
|
vars := mux.Vars(rq)
|
||||||
u := user.UserByName(vars["username"])
|
u := user.ByName(vars["username"])
|
||||||
if u == nil {
|
if u == nil {
|
||||||
util.HTTP404Page(w, "404 page not found")
|
util.HTTP404Page(w, "404 page not found")
|
||||||
return
|
return
|
||||||
@ -121,7 +121,7 @@ func handlerAdminUserEdit(w http.ResponseWriter, rq *http.Request) {
|
|||||||
|
|
||||||
func handlerAdminUserDelete(w http.ResponseWriter, rq *http.Request) {
|
func handlerAdminUserDelete(w http.ResponseWriter, rq *http.Request) {
|
||||||
vars := mux.Vars(rq)
|
vars := mux.Vars(rq)
|
||||||
u := user.UserByName(vars["username"])
|
u := user.ByName(vars["username"])
|
||||||
if u == nil {
|
if u == nil {
|
||||||
util.HTTP404Page(w, "404 page not found")
|
util.HTTP404Page(w, "404 page not found")
|
||||||
return
|
return
|
||||||
|
@ -143,7 +143,7 @@ func handlerTelegramLogin(w http.ResponseWriter, rq *http.Request) {
|
|||||||
false,
|
false,
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
if user.HasUsername(username) && user.UserByName(username).Source == "telegram" {
|
if user.HasUsername(username) && user.ByName(username).Source == "telegram" {
|
||||||
// Problems is something we put blankets on.
|
// Problems is something we put blankets on.
|
||||||
err = nil
|
err = nil
|
||||||
}
|
}
|
||||||
|
@ -36,7 +36,7 @@ func handlerHistory(w http.ResponseWriter, rq *http.Request) {
|
|||||||
// History can be found for files that do not exist anymore.
|
// History can be found for files that do not exist anymore.
|
||||||
revs, err := history.Revisions(hyphaName)
|
revs, err := history.Revisions(hyphaName)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
list = history.HistoryWithRevisions(hyphaName, revs)
|
list = history.WithRevisions(hyphaName, revs)
|
||||||
}
|
}
|
||||||
log.Println("Found", len(revs), "revisions for", hyphaName)
|
log.Println("Found", len(revs), "revisions for", hyphaName)
|
||||||
|
|
||||||
|
@ -35,7 +35,7 @@ func initMutators(r *mux.Router) {
|
|||||||
|
|
||||||
func factoryHandlerAsker(
|
func factoryHandlerAsker(
|
||||||
actionPath string,
|
actionPath string,
|
||||||
asker func(*user.User, *hyphae.Hypha) (error, string),
|
asker func(*user.User, *hyphae.Hypha) (string, error),
|
||||||
succTitleKey string,
|
succTitleKey string,
|
||||||
succPageTemplate func(*http.Request, string, bool) string,
|
succPageTemplate func(*http.Request, string, bool) string,
|
||||||
) func(http.ResponseWriter, *http.Request) {
|
) func(http.ResponseWriter, *http.Request) {
|
||||||
@ -47,7 +47,7 @@ func factoryHandlerAsker(
|
|||||||
u = user.FromRequest(rq)
|
u = user.FromRequest(rq)
|
||||||
lc = l18n.FromRequest(rq)
|
lc = l18n.FromRequest(rq)
|
||||||
)
|
)
|
||||||
if err, errtitle := asker(u, h); err != nil {
|
if errtitle, err := asker(u, h); err != nil {
|
||||||
httpErr(
|
httpErr(
|
||||||
w,
|
w,
|
||||||
lc,
|
lc,
|
||||||
@ -90,7 +90,7 @@ var handlerRenameAsk = factoryHandlerAsker(
|
|||||||
|
|
||||||
func factoryHandlerConfirmer(
|
func factoryHandlerConfirmer(
|
||||||
actionPath string,
|
actionPath string,
|
||||||
confirmer func(*hyphae.Hypha, *user.User, *http.Request) (*history.HistoryOp, string),
|
confirmer func(*hyphae.Hypha, *user.User, *http.Request) (*history.Op, string),
|
||||||
) func(http.ResponseWriter, *http.Request) {
|
) func(http.ResponseWriter, *http.Request) {
|
||||||
return func(w http.ResponseWriter, rq *http.Request) {
|
return func(w http.ResponseWriter, rq *http.Request) {
|
||||||
util.PrepareRq(rq)
|
util.PrepareRq(rq)
|
||||||
@ -112,14 +112,14 @@ func factoryHandlerConfirmer(
|
|||||||
|
|
||||||
var handlerUnattachConfirm = factoryHandlerConfirmer(
|
var handlerUnattachConfirm = factoryHandlerConfirmer(
|
||||||
"unattach-confirm",
|
"unattach-confirm",
|
||||||
func(h *hyphae.Hypha, u *user.User, _ *http.Request) (*history.HistoryOp, string) {
|
func(h *hyphae.Hypha, u *user.User, _ *http.Request) (*history.Op, string) {
|
||||||
return shroom.UnattachHypha(u, h)
|
return shroom.UnattachHypha(u, h)
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
var handlerDeleteConfirm = factoryHandlerConfirmer(
|
var handlerDeleteConfirm = factoryHandlerConfirmer(
|
||||||
"delete-confirm",
|
"delete-confirm",
|
||||||
func(h *hyphae.Hypha, u *user.User, _ *http.Request) (*history.HistoryOp, string) {
|
func(h *hyphae.Hypha, u *user.User, _ *http.Request) (*history.Op, string) {
|
||||||
return shroom.DeleteHypha(u, h)
|
return shroom.DeleteHypha(u, h)
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
@ -158,7 +158,7 @@ func handlerEdit(w http.ResponseWriter, rq *http.Request) {
|
|||||||
u = user.FromRequest(rq)
|
u = user.FromRequest(rq)
|
||||||
lc = l18n.FromRequest(rq)
|
lc = l18n.FromRequest(rq)
|
||||||
)
|
)
|
||||||
if err, errtitle := shroom.CanEdit(u, h); err != nil {
|
if errtitle, err := shroom.CanEdit(u, h); err != nil {
|
||||||
httpErr(w, lc, http.StatusInternalServerError, hyphaName,
|
httpErr(w, lc, http.StatusInternalServerError, hyphaName,
|
||||||
errtitle,
|
errtitle,
|
||||||
err.Error())
|
err.Error())
|
||||||
@ -196,7 +196,7 @@ func handlerUploadText(w http.ResponseWriter, rq *http.Request) {
|
|||||||
message = rq.PostFormValue("message")
|
message = rq.PostFormValue("message")
|
||||||
u = user.FromRequest(rq)
|
u = user.FromRequest(rq)
|
||||||
lc = l18n.FromRequest(rq)
|
lc = l18n.FromRequest(rq)
|
||||||
hop *history.HistoryOp
|
hop *history.Op
|
||||||
errtitle string
|
errtitle string
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -247,7 +247,7 @@ func handlerUploadBinary(w http.ResponseWriter, rq *http.Request) {
|
|||||||
lc.Get("ui.error"),
|
lc.Get("ui.error"),
|
||||||
err.Error())
|
err.Error())
|
||||||
}
|
}
|
||||||
if err, errtitle := shroom.CanAttach(u, h); err != nil {
|
if errtitle, err := shroom.CanAttach(u, h); err != nil {
|
||||||
httpErr(w, lc, http.StatusInternalServerError, hyphaName,
|
httpErr(w, lc, http.StatusInternalServerError, hyphaName,
|
||||||
errtitle,
|
errtitle,
|
||||||
err.Error())
|
err.Error())
|
||||||
|
@ -55,10 +55,10 @@ func handlerAttachment(w http.ResponseWriter, rq *http.Request) {
|
|||||||
func handlerPrimitiveDiff(w http.ResponseWriter, rq *http.Request) {
|
func handlerPrimitiveDiff(w http.ResponseWriter, rq *http.Request) {
|
||||||
util.PrepareRq(rq)
|
util.PrepareRq(rq)
|
||||||
var (
|
var (
|
||||||
shorterUrl = strings.TrimPrefix(rq.URL.Path, "/primitive-diff/")
|
shorterURL = strings.TrimPrefix(rq.URL.Path, "/primitive-diff/")
|
||||||
firstSlashIndex = strings.IndexRune(shorterUrl, '/')
|
firstSlashIndex = strings.IndexRune(shorterURL, '/')
|
||||||
revHash = shorterUrl[:firstSlashIndex]
|
revHash = shorterURL[:firstSlashIndex]
|
||||||
hyphaName = util.CanonicalName(shorterUrl[firstSlashIndex+1:])
|
hyphaName = util.CanonicalName(shorterURL[firstSlashIndex+1:])
|
||||||
h = hyphae.ByName(hyphaName)
|
h = hyphae.ByName(hyphaName)
|
||||||
u = user.FromRequest(rq)
|
u = user.FromRequest(rq)
|
||||||
lc = l18n.FromRequest(rq)
|
lc = l18n.FromRequest(rq)
|
||||||
@ -77,10 +77,10 @@ func handlerPrimitiveDiff(w http.ResponseWriter, rq *http.Request) {
|
|||||||
func handlerRevisionText(w http.ResponseWriter, rq *http.Request) {
|
func handlerRevisionText(w http.ResponseWriter, rq *http.Request) {
|
||||||
util.PrepareRq(rq)
|
util.PrepareRq(rq)
|
||||||
var (
|
var (
|
||||||
shorterUrl = strings.TrimPrefix(rq.URL.Path, "/rev-text/")
|
shorterURL = strings.TrimPrefix(rq.URL.Path, "/rev-text/")
|
||||||
firstSlashIndex = strings.IndexRune(shorterUrl, '/')
|
firstSlashIndex = strings.IndexRune(shorterURL, '/')
|
||||||
revHash = shorterUrl[:firstSlashIndex]
|
revHash = shorterURL[:firstSlashIndex]
|
||||||
hyphaName = util.CanonicalName(shorterUrl[firstSlashIndex+1:])
|
hyphaName = util.CanonicalName(shorterURL[firstSlashIndex+1:])
|
||||||
h = hyphae.ByName(hyphaName)
|
h = hyphae.ByName(hyphaName)
|
||||||
textContents, err = history.FileAtRevision(h.TextPartPath(), revHash)
|
textContents, err = history.FileAtRevision(h.TextPartPath(), revHash)
|
||||||
)
|
)
|
||||||
@ -101,10 +101,10 @@ func handlerRevision(w http.ResponseWriter, rq *http.Request) {
|
|||||||
util.PrepareRq(rq)
|
util.PrepareRq(rq)
|
||||||
var (
|
var (
|
||||||
lc = l18n.FromRequest(rq)
|
lc = l18n.FromRequest(rq)
|
||||||
shorterUrl = strings.TrimPrefix(rq.URL.Path, "/rev/")
|
shorterURL = strings.TrimPrefix(rq.URL.Path, "/rev/")
|
||||||
firstSlashIndex = strings.IndexRune(shorterUrl, '/')
|
firstSlashIndex = strings.IndexRune(shorterURL, '/')
|
||||||
revHash = shorterUrl[:firstSlashIndex]
|
revHash = shorterURL[:firstSlashIndex]
|
||||||
hyphaName = util.CanonicalName(shorterUrl[firstSlashIndex+1:])
|
hyphaName = util.CanonicalName(shorterURL[firstSlashIndex+1:])
|
||||||
h = hyphae.ByName(hyphaName)
|
h = hyphae.ByName(hyphaName)
|
||||||
contents = fmt.Sprintf(`<p>%s</p>`, lc.Get("ui.revision_no_text"))
|
contents = fmt.Sprintf(`<p>%s</p>`, lc.Get("ui.revision_no_text"))
|
||||||
textContents, err = history.FileAtRevision(h.TextPartPath(), revHash)
|
textContents, err = history.FileAtRevision(h.TextPartPath(), revHash)
|
||||||
|
@ -74,6 +74,7 @@ func handlerRobotsTxt(w http.ResponseWriter, rq *http.Request) {
|
|||||||
file.Close()
|
file.Close()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Handler initializes
|
||||||
func Handler() http.Handler {
|
func Handler() http.Handler {
|
||||||
router := mux.NewRouter()
|
router := mux.NewRouter()
|
||||||
router.Use(func(next http.Handler) http.Handler {
|
router.Use(func(next http.Handler) http.Handler {
|
||||||
|
Loading…
Reference in New Issue
Block a user