Move a lot of files into internal dir

Outside of it are web and stuff that needs further refactoring
This commit is contained in:
Timur Ismagilov 2024-06-04 23:26:49 +03:00
parent 5522542a7f
commit 4c31c8cc32
93 changed files with 513 additions and 518 deletions

View File

@ -1,5 +1,5 @@
{% import "net/http" %}
{% import "github.com/bouncepaw/mycorrhiza/cfg" %}
{% import "github.com/bouncepaw/mycorrhiza/internal/cfg" %}
{% import "github.com/bouncepaw/mycorrhiza/l18n" %}
{% func Register(rq *http.Request) %}

View File

@ -8,7 +8,7 @@ package auth
import "net/http"
//line auth/auth.qtpl:2
import "github.com/bouncepaw/mycorrhiza/cfg"
import "github.com/bouncepaw/mycorrhiza/internal/cfg"
//line auth/auth.qtpl:3
import "github.com/bouncepaw/mycorrhiza/l18n"

View File

@ -2,13 +2,14 @@ package categories
import (
"encoding/json"
"github.com/bouncepaw/mycorrhiza/files"
"github.com/bouncepaw/mycorrhiza/util"
"golang.org/x/exp/slices"
"log"
"os"
"sort"
"sync"
"github.com/bouncepaw/mycorrhiza/internal/files"
"github.com/bouncepaw/mycorrhiza/util"
)
var categoryToHyphae = map[string]*categoryNode{}

View File

@ -1,9 +1,9 @@
package categories
import (
"github.com/bouncepaw/mycorrhiza/user"
"github.com/bouncepaw/mycorrhiza/internal/user"
"github.com/bouncepaw/mycorrhiza/util"
"github.com/bouncepaw/mycorrhiza/viewutil"
"github.com/bouncepaw/mycorrhiza/web/viewutil"
"github.com/gorilla/mux"
"io"
"log"

View File

@ -2,7 +2,7 @@ package categories
import (
"embed"
"github.com/bouncepaw/mycorrhiza/viewutil"
viewutil2 "github.com/bouncepaw/mycorrhiza/web/viewutil"
"log"
"sort"
"strings"
@ -31,14 +31,14 @@ const ruTranslation = `
var (
//go:embed *.html
fs embed.FS
viewListChain, viewPageChain, viewCardChain, viewEditChain viewutil.Chain
viewListChain, viewPageChain, viewCardChain, viewEditChain viewutil2.Chain
)
func prepareViews() {
viewCardChain = viewutil.CopyEnRuWith(fs, "view_card.html", ruTranslation)
viewListChain = viewutil.CopyEnRuWith(fs, "view_list.html", ruTranslation)
viewPageChain = viewutil.CopyEnRuWith(fs, "view_page.html", ruTranslation)
viewEditChain = viewutil.CopyEnRuWith(fs, "view_edit.html", ruTranslation)
viewCardChain = viewutil2.CopyEnRuWith(fs, "view_card.html", ruTranslation)
viewListChain = viewutil2.CopyEnRuWith(fs, "view_list.html", ruTranslation)
viewPageChain = viewutil2.CopyEnRuWith(fs, "view_page.html", ruTranslation)
viewEditChain = viewutil2.CopyEnRuWith(fs, "view_edit.html", ruTranslation)
}
type cardData struct {
@ -48,7 +48,7 @@ type cardData struct {
}
// CategoryCard is the little sidebar that is shown nearby the hypha view.
func CategoryCard(meta viewutil.Meta, hyphaName string) string {
func CategoryCard(meta viewutil2.Meta, hyphaName string) string {
var buf strings.Builder
err := viewCardChain.Get(meta).ExecuteTemplate(&buf, "category card", cardData{
HyphaName: hyphaName,
@ -62,15 +62,15 @@ func CategoryCard(meta viewutil.Meta, hyphaName string) string {
}
type catData struct {
*viewutil.BaseData
*viewutil2.BaseData
CatName string
Hyphae []string
GivenPermissionToModify bool
}
func categoryEdit(meta viewutil.Meta, catName string) {
viewutil.ExecutePage(meta, viewEditChain, catData{
BaseData: &viewutil.BaseData{
func categoryEdit(meta viewutil2.Meta, catName string) {
viewutil2.ExecutePage(meta, viewEditChain, catData{
BaseData: &viewutil2.BaseData{
Addr: "/edit-category/" + catName,
},
CatName: catName,
@ -79,9 +79,9 @@ func categoryEdit(meta viewutil.Meta, catName string) {
})
}
func categoryPage(meta viewutil.Meta, catName string) {
viewutil.ExecutePage(meta, viewPageChain, catData{
BaseData: &viewutil.BaseData{
func categoryPage(meta viewutil2.Meta, catName string) {
viewutil2.ExecutePage(meta, viewPageChain, catData{
BaseData: &viewutil2.BaseData{
Addr: "/category/" + catName,
},
CatName: catName,
@ -91,15 +91,15 @@ func categoryPage(meta viewutil.Meta, catName string) {
}
type listData struct {
*viewutil.BaseData
*viewutil2.BaseData
Categories []string
}
func categoryList(meta viewutil.Meta) {
func categoryList(meta viewutil2.Meta) {
cats := listOfCategories()
sort.Strings(cats)
viewutil.ExecutePage(meta, viewListChain, listData{
BaseData: &viewutil.BaseData{
viewutil2.ExecutePage(meta, viewListChain, listData{
BaseData: &viewutil2.BaseData{
Addr: "/category",
},
Categories: cats,

14
flag.go
View File

@ -12,17 +12,17 @@ import (
"golang.org/x/term"
"github.com/bouncepaw/mycorrhiza/cfg"
"github.com/bouncepaw/mycorrhiza/files"
"github.com/bouncepaw/mycorrhiza/user"
"github.com/bouncepaw/mycorrhiza/version"
"github.com/bouncepaw/mycorrhiza/internal/cfg"
"github.com/bouncepaw/mycorrhiza/internal/files"
user2 "github.com/bouncepaw/mycorrhiza/internal/user"
"github.com/bouncepaw/mycorrhiza/internal/version"
)
// CLI options are read and parsed here.
// printHelp prints the help message.
func printHelp() {
fmt.Fprintf(
_, _ = fmt.Fprintf(
flag.CommandLine.Output(),
"Usage: %s WIKI_PATH\n",
os.Args[0],
@ -70,13 +70,13 @@ func createAdminCommand(name string) {
}
cfg.UseAuth = true
cfg.AllowRegistration = true
user.InitUserDatabase()
user2.InitUserDatabase()
password, err := askPass("Password")
if err != nil {
log.Fatal(err)
}
if err := user.Register(name, password, "admin", "local", true); err != nil {
if err := user2.Register(name, password, "admin", "local", true); err != nil {
log.Fatal(err)
}
}

View File

@ -4,7 +4,7 @@ package help
import (
"git.sr.ht/~bouncepaw/mycomarkup/v5"
"github.com/bouncepaw/mycorrhiza/mycoopts"
"github.com/bouncepaw/mycorrhiza/viewutil"
viewutil2 "github.com/bouncepaw/mycorrhiza/web/viewutil"
"github.com/gorilla/mux"
"io"
"net/http"
@ -14,7 +14,7 @@ import (
)
var (
chain viewutil.Chain
chain viewutil2.Chain
ruTranslation = `
{{define "title"}}Справка{{end}}
{{define "entry not found"}}Статья не найдена{{end}}
@ -46,14 +46,14 @@ var (
func InitHandlers(r *mux.Router) {
r.PathPrefix("/help").HandlerFunc(handlerHelp)
chain = viewutil.CopyEnRuWith(fs, "view_help.html", ruTranslation)
chain = viewutil2.CopyEnRuWith(fs, "view_help.html", ruTranslation)
}
// handlerHelp gets the appropriate documentation or tells you where you (personally) have failed.
func handlerHelp(w http.ResponseWriter, rq *http.Request) {
// See the history of this file to resurrect the old algorithm that supported multiple languages
var (
meta = viewutil.MetaFrom(w, rq)
meta = viewutil2.MetaFrom(w, rq)
articlePath = strings.TrimPrefix(strings.TrimPrefix(rq.URL.Path, "/help/"), "/help")
lang = "en"
)
@ -88,14 +88,14 @@ func handlerHelp(w http.ResponseWriter, rq *http.Request) {
}
type helpData struct {
*viewutil.BaseData
*viewutil2.BaseData
ContentsHTML string
Lang string
}
func viewHelp(meta viewutil.Meta, lang, contentsHTML, articlePath string) {
viewutil.ExecutePage(meta, chain, helpData{
BaseData: &viewutil.BaseData{
func viewHelp(meta viewutil2.Meta, lang, contentsHTML, articlePath string) {
viewutil2.ExecutePage(meta, chain, helpData{
BaseData: &viewutil2.BaseData{
Addr: "/help/" + articlePath,
},
ContentsHTML: contentsHTML,

View File

@ -3,12 +3,11 @@ package history
import (
"errors"
"fmt"
"github.com/bouncepaw/mycorrhiza/internal/cfg"
"net/url"
"strings"
"time"
"github.com/bouncepaw/mycorrhiza/cfg"
"github.com/gorilla/feeds"
)

View File

@ -9,7 +9,7 @@ import (
"path/filepath"
"regexp"
"github.com/bouncepaw/mycorrhiza/files"
"github.com/bouncepaw/mycorrhiza/internal/files"
"github.com/bouncepaw/mycorrhiza/util"
)

View File

@ -4,12 +4,12 @@ package histweb
import (
"embed"
"fmt"
"github.com/bouncepaw/mycorrhiza/cfg"
"github.com/bouncepaw/mycorrhiza/files"
"github.com/bouncepaw/mycorrhiza/history"
"github.com/bouncepaw/mycorrhiza/hyphae"
"github.com/bouncepaw/mycorrhiza/internal/cfg"
"github.com/bouncepaw/mycorrhiza/internal/files"
hyphae2 "github.com/bouncepaw/mycorrhiza/internal/hyphae"
"github.com/bouncepaw/mycorrhiza/util"
"github.com/bouncepaw/mycorrhiza/viewutil"
viewutil2 "github.com/bouncepaw/mycorrhiza/web/viewutil"
"github.com/gorilla/mux"
"html/template"
"log"
@ -30,9 +30,9 @@ func InitHandlers(rtr *mux.Router) {
rtr.HandleFunc("/recent-changes-atom", handlerRecentChangesAtom)
rtr.HandleFunc("/recent-changes-json", handlerRecentChangesJSON)
chainPrimitiveDiff = viewutil.CopyEnRuWith(fs, "view_primitive_diff.html", ruTranslation)
chainRecentChanges = viewutil.CopyEnRuWith(fs, "view_recent_changes.html", ruTranslation)
chainHistory = viewutil.CopyEnRuWith(fs, "view_history.html", ruTranslation)
chainPrimitiveDiff = viewutil2.CopyEnRuWith(fs, "view_primitive_diff.html", ruTranslation)
chainRecentChanges = viewutil2.CopyEnRuWith(fs, "view_recent_changes.html", ruTranslation)
chainHistory = viewutil2.CopyEnRuWith(fs, "view_history.html", ruTranslation)
}
func handlerPrimitiveDiff(w http.ResponseWriter, rq *http.Request) {
@ -45,12 +45,12 @@ func handlerPrimitiveDiff(w http.ResponseWriter, rq *http.Request) {
}
var (
mycoFilePath string
h = hyphae.ByName(util.CanonicalName(slug))
h = hyphae2.ByName(util.CanonicalName(slug))
)
switch h := h.(type) {
case hyphae.ExistingHypha:
case hyphae2.ExistingHypha:
mycoFilePath = h.TextFilePath()
case *hyphae.EmptyHypha:
case *hyphae2.EmptyHypha:
mycoFilePath = filepath.Join(files.HyphaeDir(), h.CanonicalName()+".myco")
}
text, err := history.PrimitiveDiffAtRevision(mycoFilePath, revHash)
@ -58,7 +58,7 @@ func handlerPrimitiveDiff(w http.ResponseWriter, rq *http.Request) {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
primitiveDiff(viewutil.MetaFrom(w, rq), h, revHash, text)
primitiveDiff(viewutil2.MetaFrom(w, rq), h, revHash, text)
}
// handlerRecentChanges displays the /recent-changes/ page.
@ -68,7 +68,7 @@ func handlerRecentChanges(w http.ResponseWriter, rq *http.Request) {
if editCount > 100 {
return
}
recentChanges(viewutil.MetaFrom(w, rq), editCount, history.RecentChanges(editCount))
recentChanges(viewutil2.MetaFrom(w, rq), editCount, history.RecentChanges(editCount))
}
// handlerHistory lists all revisions of a hypha.
@ -83,7 +83,7 @@ func handlerHistory(w http.ResponseWriter, rq *http.Request) {
}
log.Println("Found", len(revs), "revisions for", hyphaName)
historyView(viewutil.MetaFrom(w, rq), hyphaName, list)
historyView(viewutil2.MetaFrom(w, rq), hyphaName, list)
}
// genericHandlerOfFeeds is a helper function for the web feed handlers.
@ -135,20 +135,20 @@ var (
{{define "n recent changes"}}{{.}} свеж{{if eq . 1}}ая правка{{else if le . 4}}их правок{{else}}их правок{{end}}{{end}}
{{define "recent empty"}}Правки не найдены.{{end}}
`
chainPrimitiveDiff, chainRecentChanges, chainHistory viewutil.Chain
chainPrimitiveDiff, chainRecentChanges, chainHistory viewutil2.Chain
)
type recentChangesData struct {
*viewutil.BaseData
*viewutil2.BaseData
EditCount int
Changes []history.Revision
UserHypha string
Stops []int
}
func recentChanges(meta viewutil.Meta, editCount int, changes []history.Revision) {
viewutil.ExecutePage(meta, chainRecentChanges, recentChangesData{
BaseData: &viewutil.BaseData{},
func recentChanges(meta viewutil2.Meta, editCount int, changes []history.Revision) {
viewutil2.ExecutePage(meta, chainRecentChanges, recentChangesData{
BaseData: &viewutil2.BaseData{},
EditCount: editCount,
Changes: changes,
UserHypha: cfg.UserHypha,
@ -157,13 +157,13 @@ func recentChanges(meta viewutil.Meta, editCount int, changes []history.Revision
}
type primitiveDiffData struct {
*viewutil.BaseData
*viewutil2.BaseData
HyphaName string
Hash string
Text template.HTML
}
func primitiveDiff(meta viewutil.Meta, h hyphae.Hypha, hash, text string) {
func primitiveDiff(meta viewutil2.Meta, h hyphae2.Hypha, hash, text string) {
hunks := history.SplitPrimitiveDiff(text)
if len(hunks) > 0 {
var buf strings.Builder
@ -198,8 +198,8 @@ func primitiveDiff(meta viewutil.Meta, h hyphae.Hypha, hash, text string) {
text = fmt.Sprintf(
`<pre class="codeblock"><code>%s</code></pre>`, text)
}
viewutil.ExecutePage(meta, chainPrimitiveDiff, primitiveDiffData{
BaseData: &viewutil.BaseData{},
viewutil2.ExecutePage(meta, chainPrimitiveDiff, primitiveDiffData{
BaseData: &viewutil2.BaseData{},
HyphaName: h.CanonicalName(),
Hash: hash,
Text: template.HTML(text),
@ -207,14 +207,14 @@ func primitiveDiff(meta viewutil.Meta, h hyphae.Hypha, hash, text string) {
}
type historyData struct {
*viewutil.BaseData
*viewutil2.BaseData
HyphaName string
Contents string
}
func historyView(meta viewutil.Meta, hyphaName, contents string) {
viewutil.ExecutePage(meta, chainHistory, historyData{
BaseData: &viewutil.BaseData{
func historyView(meta viewutil2.Meta, hyphaName, contents string) {
viewutil2.ExecutePage(meta, chainHistory, historyData{
BaseData: &viewutil2.BaseData{
Addr: "/history/" + util.CanonicalName(hyphaName),
},
HyphaName: hyphaName,

View File

@ -64,7 +64,7 @@
</p>
<p>
<img class="icon" width="20" height="20" src="/static/icon/feed.svg" aria-hidden="true" alt="RSS icon">
<img class="icon" width="20" height="20" src="/web/static/icon/feed.svg" aria-hidden="true" alt="RSS icon">
{{block "subscribe via" .}}Subscribe via <a href="/recent-changes-rss">RSS</a>, <a href="/recent-changes-atom">Atom</a> or <a href="/recent-changes-json">JSON feed</a>.{{end}}
</p>
</main>

View File

@ -4,11 +4,11 @@ package history
// Things related to writing history.
import (
"fmt"
"github.com/bouncepaw/mycorrhiza/internal/user"
"os"
"path/filepath"
"sync"
"github.com/bouncepaw/mycorrhiza/user"
"github.com/bouncepaw/mycorrhiza/util"
)

View File

@ -8,7 +8,7 @@ import (
"strings"
"time"
"github.com/bouncepaw/mycorrhiza/files"
"github.com/bouncepaw/mycorrhiza/internal/files"
)
// Revision represents a revision, duh. Hash is usually short. Username is extracted from email.

View File

@ -1,5 +1,5 @@
{% import "fmt" %}
{% import "github.com/bouncepaw/mycorrhiza/cfg" %}
{% import "github.com/bouncepaw/mycorrhiza/internal/cfg" %}
HyphaeLinksHTML returns a comma-separated list of hyphae that were affected by this revision as HTML string.
{% func (rev Revision) HyphaeLinksHTML() %}

View File

@ -8,7 +8,7 @@ package history
import "fmt"
//line history/view.qtpl:2
import "github.com/bouncepaw/mycorrhiza/cfg"
import "github.com/bouncepaw/mycorrhiza/internal/cfg"
// HyphaeLinksHTML returns a comma-separated list of hyphae that were affected by this revision as HTML string.

View File

@ -1,14 +1,13 @@
package main
import (
"github.com/bouncepaw/mycorrhiza/internal/cfg"
"log"
"net"
"net/http"
"os"
"strings"
"time"
"github.com/bouncepaw/mycorrhiza/cfg"
)
func serveHTTP(handler http.Handler) {

View File

@ -2,13 +2,12 @@ package hypview
import (
"embed"
"github.com/bouncepaw/mycorrhiza/backlinks"
"github.com/bouncepaw/mycorrhiza/internal/backlinks"
"github.com/bouncepaw/mycorrhiza/internal/cfg"
viewutil2 "github.com/bouncepaw/mycorrhiza/web/viewutil"
"html/template"
"log"
"strings"
"github.com/bouncepaw/mycorrhiza/cfg"
"github.com/bouncepaw/mycorrhiza/viewutil"
)
var (
@ -77,25 +76,25 @@ var (
{{define "remove media from [[x]]?"}}Убрать медиа у <a href="/hypha/{{.MatchedHyphaName}}">{{beautifulName .MatchedHyphaName}}</a>?{{end}}
{{define "remove media for real?"}}Вы точно хотите убрать медиа у гифы «{{beautifulName .MatchedHyphaName}}»?{{end}}
`
chainNaviTitle viewutil.Chain
chainEditHypha viewutil.Chain
chainEmptyHypha viewutil.Chain
chainDeleteHypha viewutil.Chain
chainRenameHypha viewutil.Chain
chainRemoveMedia viewutil.Chain
chainNaviTitle viewutil2.Chain
chainEditHypha viewutil2.Chain
chainEmptyHypha viewutil2.Chain
chainDeleteHypha viewutil2.Chain
chainRenameHypha viewutil2.Chain
chainRemoveMedia viewutil2.Chain
)
func Init() {
chainNaviTitle = viewutil.CopyEnRuWith(fs, "view_navititle.html", "")
chainEditHypha = viewutil.CopyEnRuWith(fs, "view_edit.html", ruTranslation)
chainEmptyHypha = viewutil.CopyEnRuWith(fs, "view_empty_hypha.html", ruTranslation)
chainDeleteHypha = viewutil.CopyEnRuWith(fs, "view_delete.html", ruTranslation)
chainRenameHypha = viewutil.CopyEnRuWith(fs, "view_rename.html", ruTranslation)
chainRemoveMedia = viewutil.CopyEnRuWith(fs, "view_remove_media.html", ruTranslation)
chainNaviTitle = viewutil2.CopyEnRuWith(fs, "view_navititle.html", "")
chainEditHypha = viewutil2.CopyEnRuWith(fs, "view_edit.html", ruTranslation)
chainEmptyHypha = viewutil2.CopyEnRuWith(fs, "view_empty_hypha.html", ruTranslation)
chainDeleteHypha = viewutil2.CopyEnRuWith(fs, "view_delete.html", ruTranslation)
chainRenameHypha = viewutil2.CopyEnRuWith(fs, "view_rename.html", ruTranslation)
chainRemoveMedia = viewutil2.CopyEnRuWith(fs, "view_remove_media.html", ruTranslation)
}
type editData struct {
*viewutil.BaseData
*viewutil2.BaseData
HyphaName string
IsNew bool
Content string
@ -103,9 +102,9 @@ type editData struct {
Preview template.HTML
}
func EditHypha(meta viewutil.Meta, hyphaName string, isNew bool, content string, message string, preview template.HTML) {
viewutil.ExecutePage(meta, chainEditHypha, editData{
BaseData: &viewutil.BaseData{
func EditHypha(meta viewutil2.Meta, hyphaName string, isNew bool, content string, message string, preview template.HTML) {
viewutil2.ExecutePage(meta, chainEditHypha, editData{
BaseData: &viewutil2.BaseData{
Addr: "/edit/" + hyphaName,
EditScripts: cfg.EditScripts,
},
@ -118,14 +117,14 @@ func EditHypha(meta viewutil.Meta, hyphaName string, isNew bool, content string,
}
type renameData struct {
*viewutil.BaseData
*viewutil2.BaseData
HyphaName string
LeaveRedirectionDefault bool
}
func RenameHypha(meta viewutil.Meta, hyphaName string) {
viewutil.ExecutePage(meta, chainRenameHypha, renameData{
BaseData: &viewutil.BaseData{
func RenameHypha(meta viewutil2.Meta, hyphaName string) {
viewutil2.ExecutePage(meta, chainRenameHypha, renameData{
BaseData: &viewutil2.BaseData{
Addr: "/rename/" + hyphaName,
},
HyphaName: hyphaName,
@ -134,22 +133,22 @@ func RenameHypha(meta viewutil.Meta, hyphaName string) {
}
type deleteRemoveMediaData struct {
*viewutil.BaseData
*viewutil2.BaseData
HyphaName string
}
func DeleteHypha(meta viewutil.Meta, hyphaName string) {
viewutil.ExecutePage(meta, chainDeleteHypha, deleteRemoveMediaData{
BaseData: &viewutil.BaseData{
func DeleteHypha(meta viewutil2.Meta, hyphaName string) {
viewutil2.ExecutePage(meta, chainDeleteHypha, deleteRemoveMediaData{
BaseData: &viewutil2.BaseData{
Addr: "/delete/" + hyphaName,
},
HyphaName: hyphaName,
})
}
func RemoveMedia(meta viewutil.Meta, hyphaName string) {
viewutil.ExecutePage(meta, chainRemoveMedia, deleteRemoveMediaData{
BaseData: &viewutil.BaseData{
func RemoveMedia(meta viewutil2.Meta, hyphaName string) {
viewutil2.ExecutePage(meta, chainRemoveMedia, deleteRemoveMediaData{
BaseData: &viewutil2.BaseData{
Addr: "/remove-media/" + hyphaName,
},
HyphaName: hyphaName,
@ -157,13 +156,13 @@ func RemoveMedia(meta viewutil.Meta, hyphaName string) {
}
type emptyHyphaData struct {
Meta viewutil.Meta
Meta viewutil2.Meta
HyphaName string
AllowRegistration bool
UseAuth bool
}
func EmptyHypha(meta viewutil.Meta, hyphaName string) string {
func EmptyHypha(meta viewutil2.Meta, hyphaName string) string {
var buf strings.Builder
if err := chainEmptyHypha.Get(meta).ExecuteTemplate(&buf, "empty hypha card", emptyHyphaData{
Meta: meta,
@ -183,7 +182,7 @@ type naviTitleData struct {
HomeHypha string
}
func NaviTitle(meta viewutil.Meta, hyphaName string) string {
func NaviTitle(meta viewutil2.Meta, hyphaName string) string {
parts, partsWithParents := naviTitleify(hyphaName)
var buf strings.Builder
err := chainNaviTitle.Get(meta).ExecuteTemplate(&buf, "navititle", naviTitleData{

View File

@ -1,9 +1,9 @@
{% import "github.com/bouncepaw/mycorrhiza/backlinks" %}
{% import "github.com/bouncepaw/mycorrhiza/cfg" %}
{% import "github.com/bouncepaw/mycorrhiza/hyphae" %}
{% import "github.com/bouncepaw/mycorrhiza/user" %}
{% import "github.com/bouncepaw/mycorrhiza/internal/backlinks" %}
{% import "github.com/bouncepaw/mycorrhiza/internal/cfg" %}
{% import "github.com/bouncepaw/mycorrhiza/internal/hyphae" %}
{% import "github.com/bouncepaw/mycorrhiza/internal/user" %}
{% import "github.com/bouncepaw/mycorrhiza/util" %}
{% import "github.com/bouncepaw/mycorrhiza/viewutil" %}
{% import "github.com/bouncepaw/mycorrhiza/web/viewutil" %}
{% func hyphaInfoEntry(h hyphae.Hypha, u *user.User, action string, hasToExist bool, displayText string) %}
{% code flag := true %}

View File

@ -5,22 +5,22 @@
package hypview
//line hypview/nav.qtpl:1
import "github.com/bouncepaw/mycorrhiza/backlinks"
import "github.com/bouncepaw/mycorrhiza/internal/backlinks"
//line hypview/nav.qtpl:2
import "github.com/bouncepaw/mycorrhiza/cfg"
import "github.com/bouncepaw/mycorrhiza/internal/cfg"
//line hypview/nav.qtpl:3
import "github.com/bouncepaw/mycorrhiza/hyphae"
import "github.com/bouncepaw/mycorrhiza/internal/hyphae"
//line hypview/nav.qtpl:4
import "github.com/bouncepaw/mycorrhiza/user"
import "github.com/bouncepaw/mycorrhiza/internal/user"
//line hypview/nav.qtpl:5
import "github.com/bouncepaw/mycorrhiza/util"
//line hypview/nav.qtpl:6
import "github.com/bouncepaw/mycorrhiza/viewutil"
import "github.com/bouncepaw/mycorrhiza/web/viewutil"
//line hypview/nav.qtpl:8
import (

View File

@ -3,15 +3,15 @@
{% import "path" %}
{% import "os" %}
{% import "github.com/bouncepaw/mycorrhiza/cfg" %}
{% import "github.com/bouncepaw/mycorrhiza/hyphae" %}
{% import "github.com/bouncepaw/mycorrhiza/internal/cfg" %}
{% import "github.com/bouncepaw/mycorrhiza/internal/hyphae" %}
{% import "github.com/bouncepaw/mycorrhiza/categories" %}
{% import "github.com/bouncepaw/mycorrhiza/l18n" %}
{% import "github.com/bouncepaw/mycorrhiza/mimetype" %}
{% import "github.com/bouncepaw/mycorrhiza/internal/mimetype" %}
{% import "github.com/bouncepaw/mycorrhiza/tree" %}
{% import "github.com/bouncepaw/mycorrhiza/user" %}
{% import "github.com/bouncepaw/mycorrhiza/internal/user" %}
{% import "github.com/bouncepaw/mycorrhiza/util" %}
{% import "github.com/bouncepaw/mycorrhiza/viewutil" %}
{% import "github.com/bouncepaw/mycorrhiza/web/viewutil" %}
{% func MediaMenu(rq *http.Request, h hyphae.Hypha, u *user.User) %}
{% code

View File

@ -17,10 +17,10 @@ import "path"
import "os"
//line hypview/readers.qtpl:6
import "github.com/bouncepaw/mycorrhiza/cfg"
import "github.com/bouncepaw/mycorrhiza/internal/cfg"
//line hypview/readers.qtpl:7
import "github.com/bouncepaw/mycorrhiza/hyphae"
import "github.com/bouncepaw/mycorrhiza/internal/hyphae"
//line hypview/readers.qtpl:8
import "github.com/bouncepaw/mycorrhiza/categories"
@ -29,19 +29,19 @@ import "github.com/bouncepaw/mycorrhiza/categories"
import "github.com/bouncepaw/mycorrhiza/l18n"
//line hypview/readers.qtpl:10
import "github.com/bouncepaw/mycorrhiza/mimetype"
import "github.com/bouncepaw/mycorrhiza/internal/mimetype"
//line hypview/readers.qtpl:11
import "github.com/bouncepaw/mycorrhiza/tree"
//line hypview/readers.qtpl:12
import "github.com/bouncepaw/mycorrhiza/user"
import "github.com/bouncepaw/mycorrhiza/internal/user"
//line hypview/readers.qtpl:13
import "github.com/bouncepaw/mycorrhiza/util"
//line hypview/readers.qtpl:14
import "github.com/bouncepaw/mycorrhiza/viewutil"
import "github.com/bouncepaw/mycorrhiza/web/viewutil"
//line hypview/readers.qtpl:16
import (

View File

@ -39,7 +39,7 @@
{{end}}
</section>
</aside>
<script src="/static/toolbar.js"></script>
<script src="/web/static/toolbar.js"></script>
{{end}}
{{define "editing hypha"}}Edit {{beautifulName .}}{{end}}
@ -98,7 +98,7 @@
{{end}}
</main>
{{template "toolbar" .}}
<script src="/static/editor.js"></script>
<script src="/web/static/editor.js"></script>
{{range .EditScripts}}
<script src="{{.}}"></script>
{{end}}

View File

@ -2,11 +2,11 @@
package backlinks
import (
hyphae2 "github.com/bouncepaw/mycorrhiza/internal/hyphae"
"log"
"os"
"sort"
"github.com/bouncepaw/mycorrhiza/hyphae"
"github.com/bouncepaw/mycorrhiza/util"
)
@ -14,7 +14,7 @@ import (
func yieldHyphaBacklinks(hyphaName string) <-chan string {
hyphaName = util.CanonicalName(hyphaName)
out := make(chan string)
sorted := hyphae.PathographicSort(out)
sorted := hyphae2.PathographicSort(out)
go func() {
backlinks, exists := backlinkIndex[hyphaName]
if exists {
@ -43,7 +43,7 @@ var backlinkIndex = make(map[string]linkSet)
// IndexBacklinks traverses all text hyphae, extracts links from them and forms an initial index. Call it when indexing and reindexing hyphae.
func IndexBacklinks() {
// It is safe to ignore the mutex, because there is only one worker.
for h := range hyphae.FilterHyphaeWithText(hyphae.YieldExistingHyphae()) {
for h := range hyphae2.FilterHyphaeWithText(hyphae2.YieldExistingHyphae()) {
foundLinks := extractHyphaLinksFromContent(h.CanonicalName(), fetchText(h))
for _, link := range foundLinks {
if _, exists := backlinkIndex[link]; !exists {
@ -72,7 +72,7 @@ func BacklinksFor(hyphaName string) []string {
func Orphans() []string {
var orphans []string
for h := range hyphae.YieldExistingHyphae() {
for h := range hyphae2.YieldExistingHyphae() {
if BacklinksCount(h.CanonicalName()) == 0 {
orphans = append(orphans, h.CanonicalName())
}
@ -92,14 +92,14 @@ func toLinkSet(xs []string) linkSet {
return result
}
func fetchText(h hyphae.Hypha) string {
func fetchText(h hyphae2.Hypha) string {
var path string
switch h := h.(type) {
case *hyphae.EmptyHypha:
case *hyphae2.EmptyHypha:
return ""
case *hyphae.TextualHypha:
case *hyphae2.TextualHypha:
path = h.TextFilePath()
case *hyphae.MediaHypha:
case *hyphae2.MediaHypha:
if !h.HasTextFile() {
return ""
}

View File

@ -5,7 +5,7 @@ import (
"git.sr.ht/~bouncepaw/mycomarkup/v5/links"
"git.sr.ht/~bouncepaw/mycomarkup/v5/mycocontext"
"git.sr.ht/~bouncepaw/mycomarkup/v5/tools"
"github.com/bouncepaw/mycorrhiza/hyphae"
"github.com/bouncepaw/mycorrhiza/internal/hyphae"
"github.com/bouncepaw/mycorrhiza/mycoopts"
)

View File

@ -2,12 +2,11 @@
package files
import (
"github.com/bouncepaw/mycorrhiza/internal/cfg"
"github.com/bouncepaw/mycorrhiza/web/static"
"io"
"os"
"path/filepath"
"github.com/bouncepaw/mycorrhiza/cfg"
"github.com/bouncepaw/mycorrhiza/static"
)
var paths struct {

View File

@ -1,11 +1,10 @@
package hyphae
import (
"github.com/bouncepaw/mycorrhiza/internal/mimetype"
"log"
"os"
"path/filepath"
"github.com/bouncepaw/mycorrhiza/mimetype"
)
// Index finds all hypha files in the full `path` and saves them to the hypha storage.

View File

@ -1,9 +1,10 @@
package hyphae
import (
"github.com/bouncepaw/mycorrhiza/files"
"path/filepath"
"sync"
"github.com/bouncepaw/mycorrhiza/internal/files"
)
type MediaHypha struct {

View File

@ -2,7 +2,7 @@ package migration
import (
"git.sr.ht/~bouncepaw/mycomarkup/v5/tools"
"github.com/bouncepaw/mycorrhiza/files"
"github.com/bouncepaw/mycorrhiza/internal/files"
"io/ioutil"
"log"
"os"

View File

@ -8,13 +8,14 @@
package migration
import (
"github.com/bouncepaw/mycorrhiza/history"
"github.com/bouncepaw/mycorrhiza/hyphae"
"github.com/bouncepaw/mycorrhiza/user"
"github.com/bouncepaw/mycorrhiza/internal/user"
"io"
"log"
"os"
"strings"
"github.com/bouncepaw/mycorrhiza/history"
"github.com/bouncepaw/mycorrhiza/internal/hyphae"
)
func genericLineMigrator(

View File

@ -2,7 +2,7 @@ package migration
import (
"git.sr.ht/~bouncepaw/mycomarkup/v5/tools"
"github.com/bouncepaw/mycorrhiza/files"
"github.com/bouncepaw/mycorrhiza/internal/files"
"io/ioutil"
"log"
"os"

View File

@ -2,23 +2,23 @@ package shroom
import (
"errors"
hyphae2 "github.com/bouncepaw/mycorrhiza/internal/hyphae"
"github.com/bouncepaw/mycorrhiza/internal/user"
"github.com/bouncepaw/mycorrhiza/hyphae"
"github.com/bouncepaw/mycorrhiza/l18n"
"github.com/bouncepaw/mycorrhiza/user"
)
// TODO: get rid of this abomination
func canFactory(
rejectLogger func(hyphae.Hypha, *user.User, string),
rejectLogger func(hyphae2.Hypha, *user.User, string),
action string,
dispatcher func(hyphae.Hypha, *user.User, *l18n.Localizer) (string, string),
dispatcher func(hyphae2.Hypha, *user.User, *l18n.Localizer) (string, string),
noRightsMsg string,
notExistsMsg string,
mustExist bool,
) func(*user.User, hyphae.Hypha, *l18n.Localizer) error {
return func(u *user.User, h hyphae.Hypha, lc *l18n.Localizer) error {
) func(*user.User, hyphae2.Hypha, *l18n.Localizer) error {
return func(u *user.User, h hyphae2.Hypha, lc *l18n.Localizer) error {
if !u.CanProceed(action) {
rejectLogger(h, u, "no rights")
return errors.New(noRightsMsg)
@ -26,7 +26,7 @@ func canFactory(
if mustExist {
switch h.(type) {
case *hyphae.EmptyHypha:
case *hyphae2.EmptyHypha:
rejectLogger(h, u, "does not exist")
return errors.New(notExistsMsg)
}

View File

@ -2,29 +2,29 @@ package shroom
import (
"fmt"
"github.com/bouncepaw/mycorrhiza/backlinks"
"github.com/bouncepaw/mycorrhiza/categories"
"github.com/bouncepaw/mycorrhiza/history"
"github.com/bouncepaw/mycorrhiza/hyphae"
"github.com/bouncepaw/mycorrhiza/user"
"github.com/bouncepaw/mycorrhiza/internal/backlinks"
hyphae2 "github.com/bouncepaw/mycorrhiza/internal/hyphae"
"github.com/bouncepaw/mycorrhiza/internal/user"
)
// Delete deletes the hypha and makes a history record about that.
func Delete(u *user.User, h hyphae.ExistingHypha) error {
func Delete(u *user.User, h hyphae2.ExistingHypha) error {
hop := history.
Operation(history.TypeDeleteHypha).
WithMsg(fmt.Sprintf("Delete %s", h.CanonicalName())).
WithUser(u)
originalText, _ := hyphae.FetchMycomarkupFile(h)
originalText, _ := hyphae2.FetchMycomarkupFile(h)
switch h := h.(type) {
case *hyphae.MediaHypha:
case *hyphae2.MediaHypha:
if h.HasTextFile() {
hop.WithFilesRemoved(h.MediaFilePath(), h.TextFilePath())
} else {
hop.WithFilesRemoved(h.MediaFilePath())
}
case *hyphae.TextualHypha:
case *hyphae2.TextualHypha:
hop.WithFilesRemoved(h.TextFilePath())
}
if hop.Apply().HasErrors() {
@ -32,6 +32,6 @@ func Delete(u *user.User, h hyphae.ExistingHypha) error {
}
backlinks.UpdateBacklinksAfterDelete(h, originalText)
categories.RemoveHyphaFromAllCategories(h.CanonicalName())
hyphae.DeleteHypha(h)
hyphae2.DeleteHypha(h)
return nil
}

View File

@ -4,19 +4,19 @@ import (
"git.sr.ht/~bouncepaw/mycomarkup/v5"
"git.sr.ht/~bouncepaw/mycomarkup/v5/blocks"
"git.sr.ht/~bouncepaw/mycomarkup/v5/mycocontext"
"github.com/bouncepaw/mycorrhiza/cfg"
"github.com/bouncepaw/mycorrhiza/hyphae"
"github.com/bouncepaw/mycorrhiza/internal/cfg"
hyphae2 "github.com/bouncepaw/mycorrhiza/internal/hyphae"
"github.com/bouncepaw/mycorrhiza/mycoopts"
"github.com/bouncepaw/mycorrhiza/viewutil"
"github.com/bouncepaw/mycorrhiza/web/viewutil"
"os"
)
// SetHeaderLinks initializes header links by reading the configured hypha, if there is any, or resorting to default values.
func SetHeaderLinks() {
switch userLinksHypha := hyphae.ByName(cfg.HeaderLinksHypha).(type) {
case *hyphae.EmptyHypha:
switch userLinksHypha := hyphae2.ByName(cfg.HeaderLinksHypha).(type) {
case *hyphae2.EmptyHypha:
setDefaultHeaderLinks()
case hyphae.ExistingHypha:
case hyphae2.ExistingHypha:
contents, err := os.ReadFile(userLinksHypha.TextFilePath())
if err != nil || len(contents) == 0 {
setDefaultHeaderLinks()

View File

@ -1,10 +1,9 @@
package shroom
import (
"github.com/bouncepaw/mycorrhiza/internal/hyphae"
"github.com/bouncepaw/mycorrhiza/internal/user"
"log"
"github.com/bouncepaw/mycorrhiza/hyphae"
"github.com/bouncepaw/mycorrhiza/user"
)
func rejectRenameLog(h hyphae.Hypha, u *user.User, errmsg string) {

View File

@ -3,36 +3,36 @@ package shroom
import (
"errors"
"fmt"
"github.com/bouncepaw/mycorrhiza/backlinks"
"github.com/bouncepaw/mycorrhiza/categories"
"github.com/bouncepaw/mycorrhiza/cfg"
"github.com/bouncepaw/mycorrhiza/files"
"github.com/bouncepaw/mycorrhiza/internal/backlinks"
"github.com/bouncepaw/mycorrhiza/internal/cfg"
"github.com/bouncepaw/mycorrhiza/internal/files"
hyphae2 "github.com/bouncepaw/mycorrhiza/internal/hyphae"
"github.com/bouncepaw/mycorrhiza/internal/user"
"path"
"path/filepath"
"regexp"
"strings"
"github.com/bouncepaw/mycorrhiza/history"
"github.com/bouncepaw/mycorrhiza/hyphae"
"github.com/bouncepaw/mycorrhiza/user"
"github.com/bouncepaw/mycorrhiza/util"
)
// Rename renames the old hypha to the new name and makes a history record about that. Call if and only if the user has the permission to rename.
func Rename(oldHypha hyphae.ExistingHypha, newName string, recursive bool, leaveRedirections bool, u *user.User) error {
func Rename(oldHypha hyphae2.ExistingHypha, newName string, recursive bool, leaveRedirections bool, u *user.User) error {
// * bouncepaw hates this function and related renaming functions
if newName == "" {
rejectRenameLog(oldHypha, u, "no new name given")
return errors.New("ui.rename_noname_tip")
}
if !hyphae.IsValidName(newName) {
if !hyphae2.IsValidName(newName) {
rejectRenameLog(oldHypha, u, fmt.Sprintf("new name %s invalid", newName))
return errors.New("ui.rename_badname_tip") // FIXME: There is a bug related to this.
}
switch targetHypha := hyphae.ByName(newName); targetHypha.(type) {
case hyphae.ExistingHypha:
switch targetHypha := hyphae2.ByName(newName); targetHypha.(type) {
case hyphae2.ExistingHypha:
if targetHypha.CanonicalName() == oldHypha.CanonicalName() {
return nil
}
@ -81,7 +81,7 @@ func Rename(oldHypha hyphae.ExistingHypha, newName string, recursive bool, leave
oldName = h.CanonicalName()
newName = re.ReplaceAllString(oldName, newName)
)
hyphae.RenameHyphaTo(h, newName, replaceName)
hyphae2.RenameHyphaTo(h, newName, replaceName)
backlinks.UpdateBacklinksAfterRename(h, oldName)
categories.RenameHyphaInAllCategories(oldName, newName)
if leaveRedirections {
@ -104,12 +104,12 @@ const redirectionTemplate = `=> %[1]s | 👁️➡️ %[2]s
func leaveRedirection(oldName, newName string, hop *history.Op) error {
var (
text = fmt.Sprintf(redirectionTemplate, newName, util.BeautifulName(newName))
emptyHypha = hyphae.ByName(oldName)
emptyHypha = hyphae2.ByName(oldName)
)
switch emptyHypha := emptyHypha.(type) {
case *hyphae.EmptyHypha:
h := hyphae.ExtendEmptyToTextual(emptyHypha, filepath.Join(files.HyphaeDir(), oldName+".myco"))
hyphae.Insert(h)
case *hyphae2.EmptyHypha:
h := hyphae2.ExtendEmptyToTextual(emptyHypha, filepath.Join(files.HyphaeDir(), oldName+".myco"))
hyphae2.Insert(h)
categories.AddHyphaToCategory(oldName, cfg.RedirectionCategory)
defer backlinks.UpdateBacklinksAfterEdit(h, "")
return writeTextToDisk(h, []byte(text), hop)
@ -118,15 +118,15 @@ func leaveRedirection(oldName, newName string, hop *history.Op) error {
}
}
func findHyphaeToRename(superhypha hyphae.ExistingHypha, recursive bool) []hyphae.ExistingHypha {
hyphaList := []hyphae.ExistingHypha{superhypha}
func findHyphaeToRename(superhypha hyphae2.ExistingHypha, recursive bool) []hyphae2.ExistingHypha {
hyphaList := []hyphae2.ExistingHypha{superhypha}
if recursive {
hyphaList = append(hyphaList, hyphae.Subhyphae(superhypha)...)
hyphaList = append(hyphaList, hyphae2.Subhyphae(superhypha)...)
}
return hyphaList
}
func renamingPairs(hyphaeToRename []hyphae.ExistingHypha, replaceName func(string) string) (map[string]string, error) {
func renamingPairs(hyphaeToRename []hyphae2.ExistingHypha, replaceName func(string) string) (map[string]string, error) {
var (
renameMap = make(map[string]string)
newNames = make([]string, len(hyphaeToRename))
@ -138,12 +138,12 @@ func renamingPairs(hyphaeToRename []hyphae.ExistingHypha, replaceName func(strin
renameMap[h.TextFilePath()] = replaceName(h.TextFilePath())
}
switch h := h.(type) {
case *hyphae.MediaHypha:
case *hyphae2.MediaHypha:
renameMap[h.MediaFilePath()] = replaceName(h.MediaFilePath())
}
h.Unlock()
}
if firstFailure, ok := hyphae.AreFreeNames(newNames...); !ok {
if firstFailure, ok := hyphae2.AreFreeNames(newNames...); !ok {
return nil, errors.New("Hypha " + firstFailure + " already exists")
}
return renameMap, nil

View File

@ -1,9 +1,9 @@
package shroom
import (
"github.com/bouncepaw/mycorrhiza/internal/hyphae"
"strings"
"github.com/bouncepaw/mycorrhiza/hyphae"
"github.com/bouncepaw/mycorrhiza/util"
)

View File

@ -2,14 +2,14 @@ package shroom
import (
"fmt"
hyphae2 "github.com/bouncepaw/mycorrhiza/internal/hyphae"
"github.com/bouncepaw/mycorrhiza/internal/user"
"github.com/bouncepaw/mycorrhiza/history"
"github.com/bouncepaw/mycorrhiza/hyphae"
"github.com/bouncepaw/mycorrhiza/user"
)
// RemoveMedia removes media from the media hypha and makes a history record about that. If it only had media, the hypha will be deleted. If it also had text, the hypha will become textual.
func RemoveMedia(u *user.User, h *hyphae.MediaHypha) error {
func RemoveMedia(u *user.User, h *hyphae2.MediaHypha) error {
hop := history.
Operation(history.TypeRemoveMedia).
WithFilesRemoved(h.MediaFilePath()).
@ -24,9 +24,9 @@ func RemoveMedia(u *user.User, h *hyphae.MediaHypha) error {
}
if h.HasTextFile() {
hyphae.Insert(hyphae.ShrinkMediaToTextual(h))
hyphae2.Insert(hyphae2.ShrinkMediaToTextual(h))
} else {
hyphae.DeleteHypha(h)
hyphae2.DeleteHypha(h)
}
return nil
}

View File

@ -4,12 +4,12 @@ import (
"bytes"
"errors"
"fmt"
"github.com/bouncepaw/mycorrhiza/backlinks"
"github.com/bouncepaw/mycorrhiza/files"
"github.com/bouncepaw/mycorrhiza/history"
"github.com/bouncepaw/mycorrhiza/hyphae"
"github.com/bouncepaw/mycorrhiza/mimetype"
"github.com/bouncepaw/mycorrhiza/user"
"github.com/bouncepaw/mycorrhiza/internal/backlinks"
"github.com/bouncepaw/mycorrhiza/internal/files"
hyphae2 "github.com/bouncepaw/mycorrhiza/internal/hyphae"
"github.com/bouncepaw/mycorrhiza/internal/mimetype"
"github.com/bouncepaw/mycorrhiza/internal/user"
"io"
"log"
"mime/multipart"
@ -17,10 +17,10 @@ import (
"path/filepath"
)
func historyMessageForTextUpload(h hyphae.Hypha, userMessage string) string {
func historyMessageForTextUpload(h hyphae2.Hypha, userMessage string) string {
var verb string
switch h.(type) {
case *hyphae.EmptyHypha:
case *hyphae2.EmptyHypha:
verb = "Create"
default:
verb = "Edit"
@ -32,8 +32,8 @@ func historyMessageForTextUpload(h hyphae.Hypha, userMessage string) string {
return fmt.Sprintf("%s %s: %s", verb, h.CanonicalName(), userMessage)
}
func writeTextToDisk(h hyphae.ExistingHypha, data []byte, hop *history.Op) error {
if err := hyphae.WriteToMycoFile(h, data); err != nil {
func writeTextToDisk(h hyphae2.ExistingHypha, data []byte, hop *history.Op) error {
if err := hyphae2.WriteToMycoFile(h, data); err != nil {
return err
}
hop.WithFiles(h.TextFilePath())
@ -42,7 +42,7 @@ func writeTextToDisk(h hyphae.ExistingHypha, data []byte, hop *history.Op) error
}
// UploadText edits the hypha's text part and makes a history record about that.
func UploadText(h hyphae.Hypha, data []byte, userMessage string, u *user.User) error {
func UploadText(h hyphae2.Hypha, data []byte, userMessage string, u *user.User) error {
hop := history.
Operation(history.TypeEditText).
WithMsg(historyMessageForTextUpload(h, userMessage)).
@ -56,13 +56,13 @@ func UploadText(h hyphae.Hypha, data []byte, userMessage string, u *user.User) e
}
// Hypha name exploit check
if !hyphae.IsValidName(h.CanonicalName()) {
if !hyphae2.IsValidName(h.CanonicalName()) {
// We check for the name only. I suppose the filepath would be valid as well.
hop.Abort()
return errors.New("invalid hypha name")
}
oldText, err := hyphae.FetchMycomarkupFile(h)
oldText, err := hyphae2.FetchMycomarkupFile(h)
if err != nil {
hop.Abort()
return err
@ -77,8 +77,8 @@ func UploadText(h hyphae.Hypha, data []byte, userMessage string, u *user.User) e
// At this point, we have a savable user-generated Mycomarkup document. Gotta save it.
switch h := h.(type) {
case *hyphae.EmptyHypha:
H := hyphae.ExtendEmptyToTextual(h, filepath.Join(files.HyphaeDir(), h.CanonicalName()+".myco"))
case *hyphae2.EmptyHypha:
H := hyphae2.ExtendEmptyToTextual(h, filepath.Join(files.HyphaeDir(), h.CanonicalName()+".myco"))
err := writeTextToDisk(H, data, hop)
if err != nil {
@ -86,9 +86,9 @@ func UploadText(h hyphae.Hypha, data []byte, userMessage string, u *user.User) e
return err
}
hyphae.Insert(H)
hyphae2.Insert(H)
backlinks.UpdateBacklinksAfterEdit(H, "")
case *hyphae.MediaHypha:
case *hyphae2.MediaHypha:
// TODO: that []byte(...) part should be removed
if bytes.Equal(data, []byte(oldText)) {
// No changes! Just like cancel button
@ -103,8 +103,8 @@ func UploadText(h hyphae.Hypha, data []byte, userMessage string, u *user.User) e
}
backlinks.UpdateBacklinksAfterEdit(h, oldText)
case *hyphae.TextualHypha:
oldText, err := hyphae.FetchMycomarkupFile(h)
case *hyphae2.TextualHypha:
oldText, err := hyphae2.FetchMycomarkupFile(h)
if err != nil {
hop.Abort()
return err
@ -130,12 +130,12 @@ func UploadText(h hyphae.Hypha, data []byte, userMessage string, u *user.User) e
return nil
}
func historyMessageForMediaUpload(h hyphae.Hypha, mime string) string {
func historyMessageForMediaUpload(h hyphae2.Hypha, mime string) string {
return fmt.Sprintf("Upload media for %s with type %s", h.CanonicalName(), mime)
}
// writeMediaToDisk saves the given data with the given mime type for the given hypha to the disk and returns the path to the saved file and an error, if any.
func writeMediaToDisk(h hyphae.Hypha, mime string, data []byte) (string, error) {
func writeMediaToDisk(h hyphae2.Hypha, mime string, data []byte) (string, error) {
var (
ext = mimetype.ToExtension(mime)
// That's where the file will go
@ -153,7 +153,7 @@ func writeMediaToDisk(h hyphae.Hypha, mime string, data []byte) (string, error)
}
// UploadBinary edits the hypha's media part and makes a history record about that.
func UploadBinary(h hyphae.Hypha, mime string, file multipart.File, u *user.User) error {
func UploadBinary(h hyphae2.Hypha, mime string, file multipart.File, u *user.User) error {
// Privilege check
if !u.CanProceed("upload-binary") {
@ -162,7 +162,7 @@ func UploadBinary(h hyphae.Hypha, mime string, file multipart.File, u *user.User
}
// Hypha name exploit check
if !hyphae.IsValidName(h.CanonicalName()) {
if !hyphae2.IsValidName(h.CanonicalName()) {
// We check for the name only. I suppose the filepath would be valid as well.
return errors.New("invalid hypha name")
}
@ -185,12 +185,12 @@ func UploadBinary(h hyphae.Hypha, mime string, file multipart.File, u *user.User
}
switch h := h.(type) {
case *hyphae.EmptyHypha:
H := hyphae.ExtendEmptyToMedia(h, uploadedFilePath)
hyphae.Insert(H)
case *hyphae.TextualHypha:
hyphae.Insert(hyphae.ExtendTextualToMedia(h, uploadedFilePath))
case *hyphae.MediaHypha: // If this is not the first media the hypha gets
case *hyphae2.EmptyHypha:
H := hyphae2.ExtendEmptyToMedia(h, uploadedFilePath)
hyphae2.Insert(H)
case *hyphae2.TextualHypha:
hyphae2.Insert(hyphae2.ExtendTextualToMedia(h, uploadedFilePath))
case *hyphae2.MediaHypha: // If this is not the first media the hypha gets
prevFilePath := h.MediaFilePath()
if prevFilePath != uploadedFilePath {
if err := history.Rename(prevFilePath, uploadedFilePath); err != nil {

View File

@ -3,11 +3,11 @@ package user
import (
"encoding/json"
"errors"
"github.com/bouncepaw/mycorrhiza/internal/cfg"
"log"
"os"
"github.com/bouncepaw/mycorrhiza/cfg"
"github.com/bouncepaw/mycorrhiza/files"
"github.com/bouncepaw/mycorrhiza/internal/files"
"github.com/bouncepaw/mycorrhiza/util"
)

View File

@ -6,6 +6,7 @@ import (
"encoding/hex"
"errors"
"fmt"
"github.com/bouncepaw/mycorrhiza/internal/cfg"
"log"
"net/http"
"sort"
@ -14,7 +15,6 @@ import (
"golang.org/x/crypto/bcrypt"
"github.com/bouncepaw/mycorrhiza/cfg"
"github.com/bouncepaw/mycorrhiza/util"
)

View File

@ -2,12 +2,12 @@ package user
import (
"fmt"
"github.com/bouncepaw/mycorrhiza/internal/cfg"
"net/http"
"strings"
"sync"
"time"
"github.com/bouncepaw/mycorrhiza/cfg"
"golang.org/x/crypto/bcrypt"
)

View File

@ -5,7 +5,7 @@ import (
"encoding/json"
"errors"
"git.sr.ht/~bouncepaw/mycomarkup/v5/options"
"github.com/bouncepaw/mycorrhiza/files"
"github.com/bouncepaw/mycorrhiza/internal/files"
"github.com/bouncepaw/mycorrhiza/util"
"log"
"os"

View File

@ -2,7 +2,7 @@ package interwiki
import (
"embed"
"github.com/bouncepaw/mycorrhiza/viewutil"
viewutil2 "github.com/bouncepaw/mycorrhiza/web/viewutil"
"github.com/gorilla/mux"
"log"
"net/http"
@ -29,13 +29,13 @@ var (
{{define "edit separately."}}Изменяйте записи по отдельности.{{end}}
{{define "add interwiki entry"}}Добавить запись в интеркарту{{end}}
`
chainInterwiki viewutil.Chain
chainNameTaken viewutil.Chain
chainInterwiki viewutil2.Chain
chainNameTaken viewutil2.Chain
)
func InitHandlers(rtr *mux.Router) {
chainInterwiki = viewutil.CopyEnRuWith(fs, "view_interwiki.html", ruTranslation)
chainNameTaken = viewutil.CopyEnRuWith(fs, "view_name_taken.html", ruTranslation)
chainInterwiki = viewutil2.CopyEnRuWith(fs, "view_interwiki.html", ruTranslation)
chainNameTaken = viewutil2.CopyEnRuWith(fs, "view_name_taken.html", ruTranslation)
rtr.HandleFunc("/interwiki", handlerInterwiki)
rtr.HandleFunc("/interwiki/add-entry", handlerAddEntry).Methods(http.MethodPost)
rtr.HandleFunc("/interwiki/modify-entry/{target}", handlerModifyEntry).Methods(http.MethodPost)
@ -64,13 +64,13 @@ func handlerModifyEntry(w http.ResponseWriter, rq *http.Request) {
if oldData, ok = entriesByName[name]; !ok {
log.Printf("Could not modify interwiki entry %s because it does not exist", name)
viewutil.HandlerNotFound(w, rq)
viewutil2.HandlerNotFound(w, rq)
return
}
if err := replaceEntry(oldData, &newData); err != nil {
log.Printf("Could not modify interwiki entry %s because one of the proposed aliases/name is taken\n", name)
viewNameTaken(viewutil.MetaFrom(w, rq), oldData, err.Error(), "modify-entry/"+name)
viewNameTaken(viewutil2.MetaFrom(w, rq), oldData, err.Error(), "modify-entry/"+name)
return
}
@ -82,7 +82,7 @@ func handlerModifyEntry(w http.ResponseWriter, rq *http.Request) {
func handlerAddEntry(w http.ResponseWriter, rq *http.Request) {
wiki := readInterwikiEntryFromRequest(rq)
if err := addEntry(&wiki); err != nil {
viewNameTaken(viewutil.MetaFrom(w, rq), &wiki, err.Error(), "add-entry")
viewNameTaken(viewutil2.MetaFrom(w, rq), &wiki, err.Error(), "add-entry")
return
}
saveInterwikiJson()
@ -90,15 +90,15 @@ func handlerAddEntry(w http.ResponseWriter, rq *http.Request) {
}
type nameTakenData struct {
*viewutil.BaseData
*viewutil2.BaseData
*Wiki
TakenName string
Action string
}
func viewNameTaken(meta viewutil.Meta, wiki *Wiki, takenName, action string) {
viewutil.ExecutePage(meta, chainNameTaken, nameTakenData{
BaseData: &viewutil.BaseData{},
func viewNameTaken(meta viewutil2.Meta, wiki *Wiki, takenName, action string) {
viewutil2.ExecutePage(meta, chainNameTaken, nameTakenData{
BaseData: &viewutil2.BaseData{},
Wiki: wiki,
TakenName: takenName,
Action: action,
@ -106,19 +106,19 @@ func viewNameTaken(meta viewutil.Meta, wiki *Wiki, takenName, action string) {
}
func handlerInterwiki(w http.ResponseWriter, rq *http.Request) {
viewInterwiki(viewutil.MetaFrom(w, rq))
viewInterwiki(viewutil2.MetaFrom(w, rq))
}
type interwikiData struct {
*viewutil.BaseData
*viewutil2.BaseData
Entries []*Wiki
CanEdit bool
Error string
}
func viewInterwiki(meta viewutil.Meta) {
viewutil.ExecutePage(meta, chainInterwiki, interwikiData{
BaseData: &viewutil.BaseData{},
func viewInterwiki(meta viewutil2.Meta) {
viewutil2.ExecutePage(meta, chainInterwiki, interwikiData{
BaseData: &viewutil2.BaseData{},
Entries: listOfEntries,
CanEdit: meta.U.Group == "admin",
Error: "",

32
main.go
View File

@ -8,23 +8,23 @@
package main
import (
"github.com/bouncepaw/mycorrhiza/backlinks"
"github.com/bouncepaw/mycorrhiza/categories"
"github.com/bouncepaw/mycorrhiza/interwiki"
"github.com/bouncepaw/mycorrhiza/migration"
"github.com/bouncepaw/mycorrhiza/version"
"github.com/bouncepaw/mycorrhiza/viewutil"
"log"
"os"
"github.com/bouncepaw/mycorrhiza/cfg"
"github.com/bouncepaw/mycorrhiza/files"
"github.com/bouncepaw/mycorrhiza/categories"
"github.com/bouncepaw/mycorrhiza/history"
"github.com/bouncepaw/mycorrhiza/hyphae"
"github.com/bouncepaw/mycorrhiza/shroom"
"github.com/bouncepaw/mycorrhiza/static"
"github.com/bouncepaw/mycorrhiza/user"
"github.com/bouncepaw/mycorrhiza/internal/backlinks"
"github.com/bouncepaw/mycorrhiza/internal/cfg"
"github.com/bouncepaw/mycorrhiza/internal/files"
"github.com/bouncepaw/mycorrhiza/internal/hyphae"
migration2 "github.com/bouncepaw/mycorrhiza/internal/migration"
"github.com/bouncepaw/mycorrhiza/internal/shroom"
user2 "github.com/bouncepaw/mycorrhiza/internal/user"
"github.com/bouncepaw/mycorrhiza/internal/version"
"github.com/bouncepaw/mycorrhiza/interwiki"
"github.com/bouncepaw/mycorrhiza/web"
"github.com/bouncepaw/mycorrhiza/web/static"
"github.com/bouncepaw/mycorrhiza/web/viewutil"
)
func main() {
@ -49,11 +49,11 @@ func main() {
hyphae.Index(files.HyphaeDir())
backlinks.IndexBacklinks()
go backlinks.RunBacklinksConveyor()
user.InitUserDatabase()
user2.InitUserDatabase()
history.Start()
history.InitGitRepo()
migration.MigrateRocketsMaybe()
migration.MigrateHeadingsMaybe()
migration2.MigrateRocketsMaybe()
migration2.MigrateHeadingsMaybe()
shroom.SetHeaderLinks()
categories.Init()
interwiki.Init()
@ -61,7 +61,7 @@ func main() {
// Static files:
static.InitFS(files.StaticFiles())
if !user.HasAnyAdmins() {
if !user2.HasAnyAdmins() {
log.Println("Your wiki has no admin yet. Run Mycorrhiza with -create-admin <username> option to create an admin.")
}

View File

@ -1,10 +1,10 @@
package misc
import (
"github.com/bouncepaw/mycorrhiza/cfg"
"github.com/bouncepaw/mycorrhiza/internal/cfg"
"github.com/bouncepaw/mycorrhiza/internal/user"
"github.com/bouncepaw/mycorrhiza/internal/version"
"github.com/bouncepaw/mycorrhiza/l18n"
"github.com/bouncepaw/mycorrhiza/user"
"github.com/bouncepaw/mycorrhiza/version"
"log"
"strings"
"text/template" // sic!

View File

@ -2,6 +2,13 @@
package misc
import (
"github.com/bouncepaw/mycorrhiza/internal/backlinks"
"github.com/bouncepaw/mycorrhiza/internal/cfg"
hyphae2 "github.com/bouncepaw/mycorrhiza/internal/hyphae"
shroom2 "github.com/bouncepaw/mycorrhiza/internal/shroom"
"github.com/bouncepaw/mycorrhiza/internal/user"
"github.com/bouncepaw/mycorrhiza/web/static"
viewutil2 "github.com/bouncepaw/mycorrhiza/web/viewutil"
"io"
"log"
"math/rand"
@ -11,16 +18,9 @@ import (
"github.com/gorilla/mux"
"github.com/bouncepaw/mycorrhiza/backlinks"
"github.com/bouncepaw/mycorrhiza/cfg"
"github.com/bouncepaw/mycorrhiza/files"
"github.com/bouncepaw/mycorrhiza/hyphae"
"github.com/bouncepaw/mycorrhiza/internal/files"
"github.com/bouncepaw/mycorrhiza/l18n"
"github.com/bouncepaw/mycorrhiza/shroom"
"github.com/bouncepaw/mycorrhiza/static"
"github.com/bouncepaw/mycorrhiza/user"
"github.com/bouncepaw/mycorrhiza/util"
"github.com/bouncepaw/mycorrhiza/viewutil"
)
func InitAssetHandlers(rtr *mux.Router) {
@ -49,22 +49,22 @@ func handlerList(w http.ResponseWriter, rq *http.Request) {
// TODO: make this more effective, there are too many loops and vars
var (
hyphaNames = make(chan string)
sortedHypha = hyphae.PathographicSort(hyphaNames)
sortedHypha = hyphae2.PathographicSort(hyphaNames)
entries []listDatum
)
for hypha := range hyphae.YieldExistingHyphae() {
for hypha := range hyphae2.YieldExistingHyphae() {
hyphaNames <- hypha.CanonicalName()
}
close(hyphaNames)
for hyphaName := range sortedHypha {
switch h := hyphae.ByName(hyphaName).(type) {
case *hyphae.TextualHypha:
switch h := hyphae2.ByName(hyphaName).(type) {
case *hyphae2.TextualHypha:
entries = append(entries, listDatum{h.CanonicalName(), ""})
case *hyphae.MediaHypha:
case *hyphae2.MediaHypha:
entries = append(entries, listDatum{h.CanonicalName(), filepath.Ext(h.MediaFilePath())[1:]})
}
}
viewList(viewutil.MetaFrom(w, rq), entries)
viewList(viewutil2.MetaFrom(w, rq), entries)
}
// handlerReindex reindexes all hyphae by checking the wiki storage directory anew.
@ -72,13 +72,13 @@ func handlerReindex(w http.ResponseWriter, rq *http.Request) {
util.PrepareRq(rq)
if ok := user.CanProceed(rq, "reindex"); !ok {
var lc = l18n.FromRequest(rq)
viewutil.HttpErr(viewutil.MetaFrom(w, rq), http.StatusForbidden, cfg.HomeHypha, lc.Get("ui.reindex_no_rights"))
viewutil2.HttpErr(viewutil2.MetaFrom(w, rq), http.StatusForbidden, cfg.HomeHypha, lc.Get("ui.reindex_no_rights"))
log.Println("Rejected", rq.URL)
return
}
hyphae.ResetCount()
hyphae2.ResetCount()
log.Println("Reindexing hyphae in", files.HyphaeDir())
hyphae.Index(files.HyphaeDir())
hyphae2.Index(files.HyphaeDir())
backlinks.IndexBacklinks()
http.Redirect(w, rq, "/", http.StatusSeeOther)
}
@ -88,11 +88,11 @@ func handlerUpdateHeaderLinks(w http.ResponseWriter, rq *http.Request) {
util.PrepareRq(rq)
if ok := user.CanProceed(rq, "update-header-links"); !ok {
var lc = l18n.FromRequest(rq)
viewutil.HttpErr(viewutil.MetaFrom(w, rq), http.StatusForbidden, cfg.HomeHypha, lc.Get("ui.header_no_rights"))
viewutil2.HttpErr(viewutil2.MetaFrom(w, rq), http.StatusForbidden, cfg.HomeHypha, lc.Get("ui.header_no_rights"))
log.Println("Rejected", rq.URL)
return
}
shroom.SetHeaderLinks()
shroom2.SetHeaderLinks()
http.Redirect(w, rq, "/", http.StatusSeeOther)
}
@ -101,15 +101,15 @@ func handlerRandom(w http.ResponseWriter, rq *http.Request) {
util.PrepareRq(rq)
var (
randomHyphaName string
amountOfHyphae = hyphae.Count()
amountOfHyphae = hyphae2.Count()
)
if amountOfHyphae == 0 {
var lc = l18n.FromRequest(rq)
viewutil.HttpErr(viewutil.MetaFrom(w, rq), http.StatusNotFound, cfg.HomeHypha, lc.Get("ui.random_no_hyphae_tip"))
viewutil2.HttpErr(viewutil2.MetaFrom(w, rq), http.StatusNotFound, cfg.HomeHypha, lc.Get("ui.random_no_hyphae_tip"))
return
}
i := rand.Intn(amountOfHyphae)
for h := range hyphae.YieldExistingHyphae() {
for h := range hyphae2.YieldExistingHyphae() {
if i == 0 {
randomHyphaName = h.CanonicalName()
}
@ -126,8 +126,8 @@ func handlerAbout(w http.ResponseWriter, rq *http.Request) {
lc = l18n.FromRequest(rq)
title = lc.Get("ui.about_title", &l18n.Replacements{"name": cfg.WikiName})
)
_, err := io.WriteString(w, viewutil.Base(
viewutil.MetaFrom(w, rq),
_, err := io.WriteString(w, viewutil2.Base(
viewutil2.MetaFrom(w, rq),
title,
AboutHTML(lc),
map[string]string{},
@ -174,12 +174,12 @@ func handlerTitleSearch(w http.ResponseWriter, rq *http.Request) {
var (
query = rq.FormValue("q")
hyphaName = util.CanonicalName(query)
_, nameFree = hyphae.AreFreeNames(hyphaName)
_, nameFree = hyphae2.AreFreeNames(hyphaName)
results []string
)
for hyphaName := range shroom.YieldHyphaNamesContainingString(query) {
for hyphaName := range shroom2.YieldHyphaNamesContainingString(query) {
results = append(results, hyphaName)
}
w.WriteHeader(http.StatusOK)
viewTitleSearch(viewutil.MetaFrom(w, rq), query, hyphaName, !nameFree, results)
viewTitleSearch(viewutil2.MetaFrom(w, rq), query, hyphaName, !nameFree, results)
}

View File

@ -2,14 +2,14 @@ package misc
import (
"embed"
"github.com/bouncepaw/mycorrhiza/hyphae"
"github.com/bouncepaw/mycorrhiza/viewutil"
"github.com/bouncepaw/mycorrhiza/internal/hyphae"
viewutil2 "github.com/bouncepaw/mycorrhiza/web/viewutil"
)
var (
//go:embed *html
fs embed.FS
chainList, chainTitleSearch viewutil.Chain
chainList, chainTitleSearch viewutil2.Chain
ruTranslation = `
{{define "list of hyphae"}}Список гиф{{end}}
{{define "search:"}}Поиск: {{.}}{{end}}
@ -21,8 +21,8 @@ var (
)
func initViews() {
chainList = viewutil.CopyEnRuWith(fs, "view_list.html", ruTranslation)
chainTitleSearch = viewutil.CopyEnRuWith(fs, "view_title_search.html", ruTranslation)
chainList = viewutil2.CopyEnRuWith(fs, "view_list.html", ruTranslation)
chainTitleSearch = viewutil2.CopyEnRuWith(fs, "view_title_search.html", ruTranslation)
}
type listDatum struct {
@ -31,30 +31,30 @@ type listDatum struct {
}
type listData struct {
*viewutil.BaseData
*viewutil2.BaseData
Entries []listDatum
HyphaCount int
}
func viewList(meta viewutil.Meta, entries []listDatum) {
viewutil.ExecutePage(meta, chainList, listData{
BaseData: &viewutil.BaseData{},
func viewList(meta viewutil2.Meta, entries []listDatum) {
viewutil2.ExecutePage(meta, chainList, listData{
BaseData: &viewutil2.BaseData{},
Entries: entries,
HyphaCount: hyphae.Count(),
})
}
type titleSearchData struct {
*viewutil.BaseData
*viewutil2.BaseData
Query string
Results []string
MatchedHyphaName string
HasExactMatch bool
}
func viewTitleSearch(meta viewutil.Meta, query string, hyphaName string, hasExactMatch bool, results []string) {
viewutil.ExecutePage(meta, chainTitleSearch, titleSearchData{
BaseData: &viewutil.BaseData{},
func viewTitleSearch(meta viewutil2.Meta, query string, hyphaName string, hasExactMatch bool, results []string) {
viewutil2.ExecutePage(meta, chainTitleSearch, titleSearchData{
BaseData: &viewutil2.BaseData{},
Query: query,
Results: results,
MatchedHyphaName: hyphaName,

View File

@ -3,8 +3,8 @@ package mycoopts
import (
"errors"
"git.sr.ht/~bouncepaw/mycomarkup/v5/options"
"github.com/bouncepaw/mycorrhiza/cfg"
"github.com/bouncepaw/mycorrhiza/hyphae"
"github.com/bouncepaw/mycorrhiza/internal/cfg"
hyphae2 "github.com/bouncepaw/mycorrhiza/internal/hyphae"
"github.com/bouncepaw/mycorrhiza/interwiki"
"github.com/bouncepaw/mycorrhiza/util"
)
@ -17,26 +17,26 @@ func MarkupOptions(hyphaName string) options.Options {
RedLinksSupported: true,
InterwikiSupported: true,
HyphaExists: func(hyphaName string) bool {
switch hyphae.ByName(hyphaName).(type) {
case *hyphae.EmptyHypha:
switch hyphae2.ByName(hyphaName).(type) {
case *hyphae2.EmptyHypha:
return false
default:
return true
}
},
IterateHyphaNamesWith: func(λ func(string)) {
for h := range hyphae.YieldExistingHyphae() {
for h := range hyphae2.YieldExistingHyphae() {
λ(h.CanonicalName())
}
},
HyphaHTMLData: func(hyphaName string) (rawText, binaryBlock string, err error) {
switch h := hyphae.ByName(hyphaName).(type) {
case *hyphae.EmptyHypha:
switch h := hyphae2.ByName(hyphaName).(type) {
case *hyphae2.EmptyHypha:
err = errors.New("Hypha " + hyphaName + " does not exist")
case *hyphae.TextualHypha:
rawText, err = hyphae.FetchMycomarkupFile(h)
case *hyphae.MediaHypha:
rawText, err = hyphae.FetchMycomarkupFile(h)
case *hyphae2.TextualHypha:
rawText, err = hyphae2.FetchMycomarkupFile(h)
case *hyphae2.MediaHypha:
rawText, err = hyphae2.FetchMycomarkupFile(h)
binaryBlock = mediaRaw(h)
}
return

View File

@ -1,6 +1,6 @@
{% import "path/filepath" %}
{% import "github.com/bouncepaw/mycorrhiza/hyphae" %}
{% import "github.com/bouncepaw/mycorrhiza/internal/hyphae" %}
{% import "github.com/bouncepaw/mycorrhiza/l18n" %}
{% func mediaRaw(h *hyphae.MediaHypha) %}{%= Media(h, l18n.New("en", "en")) %}{% endfunc %}

View File

@ -8,7 +8,7 @@ package mycoopts
import "path/filepath"
//line mycoopts/view.qtpl:3
import "github.com/bouncepaw/mycorrhiza/hyphae"
import "github.com/bouncepaw/mycorrhiza/internal/hyphae"
//line mycoopts/view.qtpl:4
import "github.com/bouncepaw/mycorrhiza/l18n"

View File

@ -1,7 +1,7 @@
package tree
import (
"github.com/bouncepaw/mycorrhiza/hyphae"
"github.com/bouncepaw/mycorrhiza/internal/hyphae"
"path"
"sort"
"strings"

View File

@ -3,13 +3,14 @@ package util
import (
"crypto/rand"
"encoding/hex"
"github.com/bouncepaw/mycorrhiza/files"
"log"
"net/http"
"strings"
"github.com/bouncepaw/mycorrhiza/internal/cfg"
"github.com/bouncepaw/mycorrhiza/internal/files"
"git.sr.ht/~bouncepaw/mycomarkup/v5/util"
"github.com/bouncepaw/mycorrhiza/cfg"
)
// PrepareRq strips the trailing / in rq.URL.Path. In the future it might do more stuff for making all request structs uniform.

View File

@ -2,10 +2,10 @@ package web
import (
"fmt"
"github.com/bouncepaw/mycorrhiza/cfg"
"github.com/bouncepaw/mycorrhiza/user"
"github.com/bouncepaw/mycorrhiza/internal/cfg"
user2 "github.com/bouncepaw/mycorrhiza/internal/user"
"github.com/bouncepaw/mycorrhiza/util"
"github.com/bouncepaw/mycorrhiza/viewutil"
viewutil2 "github.com/bouncepaw/mycorrhiza/web/viewutil"
"github.com/gorilla/mux"
"log"
"mime"
@ -52,53 +52,53 @@ const adminTranslationRu = `
{{define "delete user warning"}}Вы уверены, что хотите удалить этого пользователя из базы данных? Это действие нельзя отменить.{{end}}
`
func viewPanel(meta viewutil.Meta) {
viewutil.ExecutePage(meta, panelChain, &viewutil.BaseData{})
func viewPanel(meta viewutil2.Meta) {
viewutil2.ExecutePage(meta, panelChain, &viewutil2.BaseData{})
}
type listData struct {
*viewutil.BaseData
*viewutil2.BaseData
UserHypha string
Users []*user.User
Users []*user2.User
}
func viewList(meta viewutil.Meta, users []*user.User) {
viewutil.ExecutePage(meta, listChain, listData{
BaseData: &viewutil.BaseData{},
func viewList(meta viewutil2.Meta, users []*user2.User) {
viewutil2.ExecutePage(meta, listChain, listData{
BaseData: &viewutil2.BaseData{},
UserHypha: cfg.UserHypha,
Users: users,
})
}
type newUserData struct {
*viewutil.BaseData
*viewutil2.BaseData
Form util.FormData
}
func viewNewUser(meta viewutil.Meta, form util.FormData) {
viewutil.ExecutePage(meta, newUserChain, newUserData{
BaseData: &viewutil.BaseData{},
func viewNewUser(meta viewutil2.Meta, form util.FormData) {
viewutil2.ExecutePage(meta, newUserChain, newUserData{
BaseData: &viewutil2.BaseData{},
Form: form,
})
}
type editDeleteUserData struct {
*viewutil.BaseData
*viewutil2.BaseData
Form util.FormData
U *user.User
U *user2.User
}
func viewEditUser(meta viewutil.Meta, form util.FormData, u *user.User) {
viewutil.ExecutePage(meta, editUserChain, editDeleteUserData{
BaseData: &viewutil.BaseData{},
func viewEditUser(meta viewutil2.Meta, form util.FormData, u *user2.User) {
viewutil2.ExecutePage(meta, editUserChain, editDeleteUserData{
BaseData: &viewutil2.BaseData{},
Form: form,
U: u,
})
}
func viewDeleteUser(meta viewutil.Meta, form util.FormData, u *user.User) {
viewutil.ExecutePage(meta, deleteUserChain, editDeleteUserData{
BaseData: &viewutil.BaseData{},
func viewDeleteUser(meta viewutil2.Meta, form util.FormData, u *user2.User) {
viewutil2.ExecutePage(meta, deleteUserChain, editDeleteUserData{
BaseData: &viewutil2.BaseData{},
Form: form,
U: u,
})
@ -108,12 +108,12 @@ func viewDeleteUser(meta viewutil.Meta, form util.FormData, u *user.User) {
func handlerAdmin(w http.ResponseWriter, rq *http.Request) {
w.Header().Set("Content-Type", "text/html;charset=utf-8")
w.WriteHeader(http.StatusOK)
viewPanel(viewutil.MetaFrom(w, rq))
viewPanel(viewutil2.MetaFrom(w, rq))
}
// handlerAdminShutdown kills the wiki.
func handlerAdminShutdown(w http.ResponseWriter, rq *http.Request) {
if user.CanProceed(rq, "admin/shutdown") {
if user2.CanProceed(rq, "admin/shutdown") {
log.Println("An admin commanded the wiki to shutdown")
os.Exit(0)
}
@ -121,7 +121,7 @@ func handlerAdminShutdown(w http.ResponseWriter, rq *http.Request) {
// handlerAdminReindexUsers reinitialises the user system.
func handlerAdminReindexUsers(w http.ResponseWriter, rq *http.Request) {
user.ReadUsersFromFilesystem()
user2.ReadUsersFromFilesystem()
redirectTo := rq.Referer()
if redirectTo == "" {
redirectTo = "/hypha/" + cfg.UserHypha
@ -131,8 +131,8 @@ func handlerAdminReindexUsers(w http.ResponseWriter, rq *http.Request) {
func handlerAdminUsers(w http.ResponseWriter, rq *http.Request) {
// Get a sorted list of users
var users []*user.User
for u := range user.YieldUsers() {
var users []*user2.User
for u := range user2.YieldUsers() {
users = append(users, u)
}
@ -140,12 +140,12 @@ func handlerAdminUsers(w http.ResponseWriter, rq *http.Request) {
less := users[i].RegisteredAt.Before(users[j].RegisteredAt)
return less
})
viewList(viewutil.MetaFrom(w, rq), users)
viewList(viewutil2.MetaFrom(w, rq), users)
}
func handlerAdminUserEdit(w http.ResponseWriter, rq *http.Request) {
vars := mux.Vars(rq)
u := user.ByName(vars["username"])
u := user2.ByName(vars["username"])
if u == nil {
util.HTTP404Page(w, "404 page not found")
return
@ -157,9 +157,9 @@ func handlerAdminUserEdit(w http.ResponseWriter, rq *http.Request) {
oldGroup := u.Group
newGroup := f.Get("group")
if user.ValidGroup(newGroup) {
if user2.ValidGroup(newGroup) {
u.Group = newGroup
if err := user.SaveUserDatabase(); err != nil {
if err := user2.SaveUserDatabase(); err != nil {
u.Group = oldGroup
log.Println(err)
f = f.WithError(err)
@ -179,12 +179,12 @@ func handlerAdminUserEdit(w http.ResponseWriter, rq *http.Request) {
}
w.Header().Set("Content-Type", mime.TypeByExtension(".html"))
viewEditUser(viewutil.MetaFrom(w, rq), f, u)
viewEditUser(viewutil2.MetaFrom(w, rq), f, u)
}
func handlerAdminUserChangePassword(w http.ResponseWriter, rq *http.Request) {
vars := mux.Vars(rq)
u := user.ByName(vars["username"])
u := user2.ByName(vars["username"])
if u == nil {
util.HTTP404Page(w, "404 page not found")
return
@ -204,7 +204,7 @@ func handlerAdminUserChangePassword(w http.ResponseWriter, rq *http.Request) {
if err := u.ChangePassword(password); err != nil {
f = f.WithError(err)
} else {
if err := user.SaveUserDatabase(); err != nil {
if err := user2.SaveUserDatabase(); err != nil {
u.Password = previousPassword
f = f.WithError(err)
} else {
@ -222,12 +222,12 @@ func handlerAdminUserChangePassword(w http.ResponseWriter, rq *http.Request) {
}
w.Header().Set("Content-Type", mime.TypeByExtension(".html"))
viewEditUser(viewutil.MetaFrom(w, rq), f, u)
viewEditUser(viewutil2.MetaFrom(w, rq), f, u)
}
func handlerAdminUserDelete(w http.ResponseWriter, rq *http.Request) {
vars := mux.Vars(rq)
u := user.ByName(vars["username"])
u := user2.ByName(vars["username"])
if u == nil {
util.HTTP404Page(w, "404 page not found")
return
@ -236,7 +236,7 @@ func handlerAdminUserDelete(w http.ResponseWriter, rq *http.Request) {
f := util.NewFormData()
if rq.Method == http.MethodPost {
f = f.WithError(user.DeleteUser(u.Name))
f = f.WithError(user2.DeleteUser(u.Name))
if !f.HasError() {
http.Redirect(w, rq, "/admin/users/", http.StatusSeeOther)
} else {
@ -248,23 +248,23 @@ func handlerAdminUserDelete(w http.ResponseWriter, rq *http.Request) {
w.WriteHeader(http.StatusBadRequest)
}
w.Header().Set("Content-Type", mime.TypeByExtension(".html"))
viewDeleteUser(viewutil.MetaFrom(w, rq), f, u)
viewDeleteUser(viewutil2.MetaFrom(w, rq), f, u)
}
func handlerAdminUserNew(w http.ResponseWriter, rq *http.Request) {
if rq.Method == http.MethodGet {
w.Header().Set("Content-Type", mime.TypeByExtension(".html"))
viewNewUser(viewutil.MetaFrom(w, rq), util.NewFormData())
viewNewUser(viewutil2.MetaFrom(w, rq), util.NewFormData())
} else if rq.Method == http.MethodPost {
// Create a user
f := util.FormDataFromRequest(rq, []string{"name", "password", "group"})
err := user.Register(f.Get("name"), f.Get("password"), f.Get("group"), "local", true)
err := user2.Register(f.Get("name"), f.Get("password"), f.Get("group"), "local", true)
if err != nil {
w.WriteHeader(http.StatusBadRequest)
w.Header().Set("Content-Type", mime.TypeByExtension(".html"))
viewNewUser(viewutil.MetaFrom(w, rq), f.WithError(err))
viewNewUser(viewutil2.MetaFrom(w, rq), f.WithError(err))
} else {
http.Redirect(w, rq, "/admin/users/", http.StatusSeeOther)
}

View File

@ -2,22 +2,21 @@ package web
import (
"git.sr.ht/~bouncepaw/mycomarkup/v5"
hyphae2 "github.com/bouncepaw/mycorrhiza/internal/hyphae"
shroom2 "github.com/bouncepaw/mycorrhiza/internal/shroom"
"github.com/bouncepaw/mycorrhiza/internal/user"
viewutil2 "github.com/bouncepaw/mycorrhiza/web/viewutil"
"html/template"
"log"
"net/http"
"git.sr.ht/~bouncepaw/mycomarkup/v5/mycocontext"
"github.com/bouncepaw/mycorrhiza/hypview"
"github.com/bouncepaw/mycorrhiza/mycoopts"
"github.com/bouncepaw/mycorrhiza/viewutil"
"git.sr.ht/~bouncepaw/mycomarkup/v5/mycocontext"
"github.com/gorilla/mux"
"github.com/bouncepaw/mycorrhiza/hyphae"
"github.com/bouncepaw/mycorrhiza/l18n"
"github.com/bouncepaw/mycorrhiza/shroom"
"github.com/bouncepaw/mycorrhiza/user"
"github.com/bouncepaw/mycorrhiza/util"
)
@ -36,24 +35,24 @@ func handlerRemoveMedia(w http.ResponseWriter, rq *http.Request) {
util.PrepareRq(rq)
var (
u = user.FromRequest(rq)
h = hyphae.ByName(util.HyphaNameFromRq(rq, "remove-media"))
meta = viewutil.MetaFrom(w, rq)
h = hyphae2.ByName(util.HyphaNameFromRq(rq, "remove-media"))
meta = viewutil2.MetaFrom(w, rq)
)
if !u.CanProceed("remove-media") {
viewutil.HttpErr(meta, http.StatusForbidden, h.CanonicalName(), "no rights")
viewutil2.HttpErr(meta, http.StatusForbidden, h.CanonicalName(), "no rights")
return
}
if rq.Method == "GET" {
hypview.RemoveMedia(viewutil.MetaFrom(w, rq), h.CanonicalName())
hypview.RemoveMedia(viewutil2.MetaFrom(w, rq), h.CanonicalName())
return
}
switch h := h.(type) {
case *hyphae.EmptyHypha, *hyphae.TextualHypha:
viewutil.HttpErr(meta, http.StatusForbidden, h.CanonicalName(), "no media to remove")
case *hyphae2.EmptyHypha, *hyphae2.TextualHypha:
viewutil2.HttpErr(meta, http.StatusForbidden, h.CanonicalName(), "no media to remove")
return
case *hyphae.MediaHypha:
if err := shroom.RemoveMedia(u, h); err != nil {
viewutil.HttpErr(meta, http.StatusInternalServerError, h.CanonicalName(), err.Error())
case *hyphae2.MediaHypha:
if err := shroom2.RemoveMedia(u, h); err != nil {
viewutil2.HttpErr(meta, http.StatusInternalServerError, h.CanonicalName(), err.Error())
return
}
}
@ -64,21 +63,21 @@ func handlerDelete(w http.ResponseWriter, rq *http.Request) {
util.PrepareRq(rq)
var (
u = user.FromRequest(rq)
h = hyphae.ByName(util.HyphaNameFromRq(rq, "delete"))
meta = viewutil.MetaFrom(w, rq)
h = hyphae2.ByName(util.HyphaNameFromRq(rq, "delete"))
meta = viewutil2.MetaFrom(w, rq)
)
if !u.CanProceed("delete") {
log.Printf("%s has no rights to delete %s\n", u.Name, h.CanonicalName())
viewutil.HttpErr(meta, http.StatusForbidden, h.CanonicalName(), "No rights")
viewutil2.HttpErr(meta, http.StatusForbidden, h.CanonicalName(), "No rights")
return
}
switch h.(type) {
case *hyphae.EmptyHypha:
case *hyphae2.EmptyHypha:
log.Printf("%s tries to delete empty hypha %s\n", u.Name, h.CanonicalName())
// TODO: localize
viewutil.HttpErr(meta, http.StatusForbidden, h.CanonicalName(), "Cannot delete an empty hypha")
viewutil2.HttpErr(meta, http.StatusForbidden, h.CanonicalName(), "Cannot delete an empty hypha")
return
}
@ -87,9 +86,9 @@ func handlerDelete(w http.ResponseWriter, rq *http.Request) {
return
}
if err := shroom.Delete(u, h.(hyphae.ExistingHypha)); err != nil {
if err := shroom2.Delete(u, h.(hyphae2.ExistingHypha)); err != nil {
log.Println(err)
viewutil.HttpErr(meta, http.StatusInternalServerError, h.CanonicalName(), err.Error())
viewutil2.HttpErr(meta, http.StatusInternalServerError, h.CanonicalName(), err.Error())
return
}
http.Redirect(w, rq, "/hypha/"+h.CanonicalName(), http.StatusSeeOther)
@ -100,25 +99,25 @@ func handlerRename(w http.ResponseWriter, rq *http.Request) {
var (
u = user.FromRequest(rq)
lc = l18n.FromRequest(rq)
h = hyphae.ByName(util.HyphaNameFromRq(rq, "rename"))
meta = viewutil.MetaFrom(w, rq)
h = hyphae2.ByName(util.HyphaNameFromRq(rq, "rename"))
meta = viewutil2.MetaFrom(w, rq)
)
switch h.(type) {
case *hyphae.EmptyHypha:
case *hyphae2.EmptyHypha:
log.Printf("%s tries to rename empty hypha %s", u.Name, h.CanonicalName())
viewutil.HttpErr(meta, http.StatusForbidden, h.CanonicalName(), "Cannot rename an empty hypha") // TODO: localize
viewutil2.HttpErr(meta, http.StatusForbidden, h.CanonicalName(), "Cannot rename an empty hypha") // TODO: localize
return
}
if !u.CanProceed("rename") {
log.Printf("%s has no rights to rename %s\n", u.Name, h.CanonicalName())
viewutil.HttpErr(meta, http.StatusForbidden, h.CanonicalName(), "No rights")
viewutil2.HttpErr(meta, http.StatusForbidden, h.CanonicalName(), "No rights")
return
}
var (
oldHypha = h.(hyphae.ExistingHypha)
oldHypha = h.(hyphae2.ExistingHypha)
newName = util.CanonicalName(rq.PostFormValue("new-name"))
recursive = rq.PostFormValue("recursive") == "true"
leaveRedirections = rq.PostFormValue("redirection") == "true"
@ -129,9 +128,9 @@ func handlerRename(w http.ResponseWriter, rq *http.Request) {
return
}
if err := shroom.Rename(oldHypha, newName, recursive, leaveRedirections, u); err != nil {
if err := shroom2.Rename(oldHypha, newName, recursive, leaveRedirections, u); err != nil {
log.Printf("%s tries to rename %s: %s", u.Name, oldHypha.CanonicalName(), err.Error())
viewutil.HttpErr(meta, http.StatusForbidden, oldHypha.CanonicalName(), lc.Get(err.Error())) // TODO: localize
viewutil2.HttpErr(meta, http.StatusForbidden, oldHypha.CanonicalName(), lc.Get(err.Error())) // TODO: localize
return
}
http.Redirect(w, rq, "/hypha/"+newName, http.StatusSeeOther)
@ -143,29 +142,29 @@ func handlerEdit(w http.ResponseWriter, rq *http.Request) {
var (
u = user.FromRequest(rq)
lc = l18n.FromRequest(rq)
meta = viewutil.MetaFrom(w, rq)
meta = viewutil2.MetaFrom(w, rq)
hyphaName = util.HyphaNameFromRq(rq, "edit")
h = hyphae.ByName(hyphaName)
h = hyphae2.ByName(hyphaName)
isNew bool
content string
err error
)
if err := shroom.CanEdit(u, h, lc); err != nil {
viewutil.HttpErr(meta, http.StatusInternalServerError, hyphaName, err.Error())
if err := shroom2.CanEdit(u, h, lc); err != nil {
viewutil2.HttpErr(meta, http.StatusInternalServerError, hyphaName, err.Error())
return
}
switch h.(type) {
case *hyphae.EmptyHypha:
case *hyphae2.EmptyHypha:
isNew = true
default:
content, err = hyphae.FetchMycomarkupFile(h)
content, err = hyphae2.FetchMycomarkupFile(h)
if err != nil {
log.Println(err)
viewutil.HttpErr(meta, http.StatusInternalServerError, hyphaName, lc.Get("ui.error_text_fetch"))
viewutil2.HttpErr(meta, http.StatusInternalServerError, hyphaName, lc.Get("ui.error_text_fetch"))
return
}
}
@ -177,11 +176,11 @@ func handlerUploadText(w http.ResponseWriter, rq *http.Request) {
util.PrepareRq(rq)
var (
u = user.FromRequest(rq)
meta = viewutil.MetaFrom(w, rq)
meta = viewutil2.MetaFrom(w, rq)
hyphaName = util.HyphaNameFromRq(rq, "upload-text")
h = hyphae.ByName(hyphaName)
_, isNew = h.(*hyphae.EmptyHypha)
h = hyphae2.ByName(hyphaName)
_, isNew = h.(*hyphae2.EmptyHypha)
textData = rq.PostFormValue("text")
action = rq.PostFormValue("action")
@ -195,8 +194,8 @@ func handlerUploadText(w http.ResponseWriter, rq *http.Request) {
return
}
if err := shroom.UploadText(h, []byte(textData), message, u); err != nil {
viewutil.HttpErr(meta, http.StatusForbidden, hyphaName, err.Error())
if err := shroom2.UploadText(h, []byte(textData), message, u); err != nil {
viewutil2.HttpErr(meta, http.StatusForbidden, hyphaName, err.Error())
return
}
http.Redirect(w, rq, "/hypha/"+hyphaName, http.StatusSeeOther)
@ -208,17 +207,17 @@ func handlerUploadBinary(w http.ResponseWriter, rq *http.Request) {
rq.ParseMultipartForm(10 << 20) // Set upload limit
var (
hyphaName = util.HyphaNameFromRq(rq, "upload-binary")
h = hyphae.ByName(hyphaName)
h = hyphae2.ByName(hyphaName)
u = user.FromRequest(rq)
lc = l18n.FromRequest(rq)
file, handler, err = rq.FormFile("binary")
meta = viewutil.MetaFrom(w, rq)
meta = viewutil2.MetaFrom(w, rq)
)
if err != nil {
viewutil.HttpErr(meta, http.StatusInternalServerError, hyphaName, err.Error())
viewutil2.HttpErr(meta, http.StatusInternalServerError, hyphaName, err.Error())
}
if err := shroom.CanAttach(u, h, lc); err != nil {
viewutil.HttpErr(meta, http.StatusInternalServerError, hyphaName, err.Error())
if err := shroom2.CanAttach(u, h, lc); err != nil {
viewutil2.HttpErr(meta, http.StatusInternalServerError, hyphaName, err.Error())
}
// If file is not passed:
@ -235,8 +234,8 @@ func handlerUploadBinary(w http.ResponseWriter, rq *http.Request) {
mime = handler.Header.Get("Content-Type")
)
if err := shroom.UploadBinary(h, mime, file, u); err != nil {
viewutil.HttpErr(meta, http.StatusInternalServerError, hyphaName, err.Error())
if err := shroom2.UploadBinary(h, mime, file, u); err != nil {
viewutil2.HttpErr(meta, http.StatusInternalServerError, hyphaName, err.Error())
return
}
http.Redirect(w, rq, "/hypha/"+hyphaName, http.StatusSeeOther)

View File

@ -10,8 +10,8 @@
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>{{block "title" .}}{{end}}</title>
<link rel="icon" href="/static/favicon.ico">
<link rel="stylesheet" href="/static/style.css">
<link rel="icon" href="/web/static/favicon.ico">
<link rel="stylesheet" href="/web/static/style.css">
{{range .HeadElements}}{{.}}{{end}}
</head>
<body data-rrh-addr="{{if .Addr}}{{.Addr}}{{else}}{{.Meta.Addr}}{{end}}"{{range $key, $value := .BodyAttributes}} data-rrh-{{$key}}="{{$value}}"{{end}}>
@ -45,9 +45,9 @@
</nav>
</header>
{{block "body" .}}{{end}}
<script src="/static/common.js"></script>
<script src="/static/shortcuts.js"></script>
<script src="/static/view.js"></script>
<script src="/web/static/common.js"></script>
<script src="/web/static/shortcuts.js"></script>
<script src="/web/static/view.js"></script>
{{range .CommonScripts}}
<script src="{{.}}"></script>
{{end}}

View File

@ -3,12 +3,11 @@ package newtmpl
import (
"embed"
"fmt"
"github.com/bouncepaw/mycorrhiza/cfg"
"github.com/bouncepaw/mycorrhiza/internal/cfg"
"github.com/bouncepaw/mycorrhiza/util"
viewutil2 "github.com/bouncepaw/mycorrhiza/web/viewutil"
"html/template"
"strings"
"github.com/bouncepaw/mycorrhiza/viewutil"
)
//go:embed *.html
@ -85,13 +84,13 @@ func translationsIntoTemplates(m map[string]string) string {
return sb.String()
}
func (p *Page) RenderTo(meta viewutil.Meta, data map[string]any) error {
func (p *Page) RenderTo(meta viewutil2.Meta, data map[string]any) error {
data["Meta"] = meta
data["HeadElements"] = meta.HeadElements
data["BodyAttributes"] = meta.BodyAttributes
data["CommonScripts"] = cfg.CommonScripts
data["EditScripts"] = cfg.EditScripts
data["HeaderLinks"] = viewutil.HeaderLinks
data["HeaderLinks"] = viewutil2.HeaderLinks
tmpl := p.TemplateEnglish
if meta.LocaleIsRussian() {

View File

@ -2,8 +2,8 @@ package web
import (
"embed"
"github.com/bouncepaw/mycorrhiza/viewutil"
"github.com/bouncepaw/mycorrhiza/web/newtmpl"
viewutil2 "github.com/bouncepaw/mycorrhiza/web/viewutil"
)
//go:embed views/*.html
@ -11,15 +11,15 @@ var fs embed.FS
var pageOrphans, pageBacklinks, pageUserList, pageChangePassword *newtmpl.Page
var panelChain, listChain, newUserChain, editUserChain, deleteUserChain viewutil.Chain
var panelChain, listChain, newUserChain, editUserChain, deleteUserChain viewutil2.Chain
func initPages() {
panelChain = viewutil.CopyEnRuWith(fs, "views/admin-panel.html", adminTranslationRu)
listChain = viewutil.CopyEnRuWith(fs, "views/admin-user-list.html", adminTranslationRu)
newUserChain = viewutil.CopyEnRuWith(fs, "views/admin-new-user.html", adminTranslationRu)
editUserChain = viewutil.CopyEnRuWith(fs, "views/admin-edit-user.html", adminTranslationRu)
deleteUserChain = viewutil.CopyEnRuWith(fs, "views/admin-delete-user.html", adminTranslationRu)
panelChain = viewutil2.CopyEnRuWith(fs, "views/admin-panel.html", adminTranslationRu)
listChain = viewutil2.CopyEnRuWith(fs, "views/admin-user-list.html", adminTranslationRu)
newUserChain = viewutil2.CopyEnRuWith(fs, "views/admin-new-user.html", adminTranslationRu)
editUserChain = viewutil2.CopyEnRuWith(fs, "views/admin-edit-user.html", adminTranslationRu)
deleteUserChain = viewutil2.CopyEnRuWith(fs, "views/admin-delete-user.html", adminTranslationRu)
pageOrphans = newtmpl.NewPage(fs, "views/orphans.html", map[string]string{
"orphaned hyphae": "Гифы-сироты",

View File

@ -2,19 +2,18 @@ package web
import (
"fmt"
user2 "github.com/bouncepaw/mycorrhiza/internal/user"
"github.com/bouncepaw/mycorrhiza/util"
"github.com/bouncepaw/mycorrhiza/viewutil"
"github.com/bouncepaw/mycorrhiza/web/viewutil"
"mime"
"net/http"
"reflect"
"github.com/bouncepaw/mycorrhiza/user"
)
func handlerUserChangePassword(w http.ResponseWriter, rq *http.Request) {
u := user.FromRequest(rq)
u := user2.FromRequest(rq)
// TODO: is there a better way?
if reflect.DeepEqual(u, user.EmptyUser()) || u == nil {
if reflect.DeepEqual(u, user2.EmptyUser()) || u == nil {
util.HTTP404Page(w, "404 page not found")
return
}
@ -22,7 +21,7 @@ func handlerUserChangePassword(w http.ResponseWriter, rq *http.Request) {
f := util.FormDataFromRequest(rq, []string{"current_password", "password", "password_confirm"})
currentPassword := f.Get("current_password")
if user.CredentialsOK(u.Name, currentPassword) {
if user2.CredentialsOK(u.Name, currentPassword) {
password := f.Get("password")
passwordConfirm := f.Get("password_confirm")
// server side validation
@ -35,7 +34,7 @@ func handlerUserChangePassword(w http.ResponseWriter, rq *http.Request) {
if err := u.ChangePassword(password); err != nil {
f = f.WithError(err)
} else {
if err := user.SaveUserDatabase(); err != nil {
if err := user2.SaveUserDatabase(); err != nil {
u.Password = previousPassword
f = f.WithError(err)
} else {

View File

@ -3,12 +3,15 @@ package web
import (
"fmt"
"git.sr.ht/~bouncepaw/mycomarkup/v5"
"github.com/bouncepaw/mycorrhiza/backlinks"
"github.com/bouncepaw/mycorrhiza/categories"
"github.com/bouncepaw/mycorrhiza/files"
views2 "github.com/bouncepaw/mycorrhiza/hypview"
"github.com/bouncepaw/mycorrhiza/internal/backlinks"
"github.com/bouncepaw/mycorrhiza/internal/files"
hyphae2 "github.com/bouncepaw/mycorrhiza/internal/hyphae"
"github.com/bouncepaw/mycorrhiza/internal/mimetype"
"github.com/bouncepaw/mycorrhiza/internal/user"
"github.com/bouncepaw/mycorrhiza/mycoopts"
"github.com/bouncepaw/mycorrhiza/viewutil"
viewutil2 "github.com/bouncepaw/mycorrhiza/web/viewutil"
"io"
"log"
"net/http"
@ -22,10 +25,7 @@ import (
"git.sr.ht/~bouncepaw/mycomarkup/v5/mycocontext"
"git.sr.ht/~bouncepaw/mycomarkup/v5/tools"
"github.com/bouncepaw/mycorrhiza/history"
"github.com/bouncepaw/mycorrhiza/hyphae"
"github.com/bouncepaw/mycorrhiza/l18n"
"github.com/bouncepaw/mycorrhiza/mimetype"
"github.com/bouncepaw/mycorrhiza/user"
"github.com/bouncepaw/mycorrhiza/util"
)
@ -59,13 +59,13 @@ func handlerMedia(w http.ResponseWriter, rq *http.Request) {
util.PrepareRq(rq)
var (
hyphaName = util.HyphaNameFromRq(rq, "media")
h = hyphae.ByName(hyphaName)
h = hyphae2.ByName(hyphaName)
u = user.FromRequest(rq)
lc = l18n.FromRequest(rq)
)
util.HTTP200Page(w,
viewutil.Base(
viewutil.MetaFrom(w, rq),
viewutil2.Base(
viewutil2.MetaFrom(w, rq),
lc.Get("ui.media_title", &l18n.Replacements{"name": util.BeautifulName(hyphaName)}),
views2.MediaMenu(rq, h, u),
map[string]string{},
@ -85,11 +85,11 @@ func handlerRevisionText(w http.ResponseWriter, rq *http.Request) {
}
var (
hyphaName = util.CanonicalName(slug)
h = hyphae.ByName(hyphaName)
h = hyphae2.ByName(hyphaName)
)
w.Header().Set("Content-Type", "text/plain; charset=utf-8")
switch h := h.(type) {
case *hyphae.EmptyHypha:
case *hyphae2.EmptyHypha:
var mycoFilePath = filepath.Join(files.HyphaeDir(), h.CanonicalName()+".myco")
var textContents, err = history.FileAtRevision(mycoFilePath, revHash)
@ -102,7 +102,7 @@ func handlerRevisionText(w http.ResponseWriter, rq *http.Request) {
log.Printf("Serving text of %s from %s at revision %s\n", hyphaName, mycoFilePath, revHash)
w.WriteHeader(http.StatusOK)
_, _ = io.WriteString(w, textContents)
case hyphae.ExistingHypha:
case hyphae2.ExistingHypha:
if !h.HasTextFile() {
log.Printf(`Media hypha %s has no text`)
w.WriteHeader(http.StatusNotFound)
@ -133,16 +133,16 @@ func handlerRevision(w http.ResponseWriter, rq *http.Request) {
}
var (
hyphaName = util.CanonicalName(slug)
h = hyphae.ByName(hyphaName)
h = hyphae2.ByName(hyphaName)
contents = fmt.Sprintf(`<p>%s</p>`, lc.Get("ui.revision_no_text"))
textContents string
err error
mycoFilePath string
)
switch h := h.(type) {
case hyphae.ExistingHypha:
case hyphae2.ExistingHypha:
mycoFilePath = h.TextFilePath()
case *hyphae.EmptyHypha:
case *hyphae2.EmptyHypha:
mycoFilePath = filepath.Join(files.HyphaeDir(), h.CanonicalName()+".myco")
}
textContents, err = history.FileAtRevision(mycoFilePath, revHash)
@ -152,7 +152,7 @@ func handlerRevision(w http.ResponseWriter, rq *http.Request) {
}
page := views2.Revision(
viewutil.MetaFrom(w, rq),
viewutil2.MetaFrom(w, rq),
h,
contents,
revHash,
@ -161,8 +161,8 @@ func handlerRevision(w http.ResponseWriter, rq *http.Request) {
w.WriteHeader(http.StatusOK)
_, _ = fmt.Fprint(
w,
viewutil.Base(
viewutil.MetaFrom(w, rq),
viewutil2.Base(
viewutil2.MetaFrom(w, rq),
lc.Get("ui.revision_title", &l18n.Replacements{"name": util.BeautifulName(hyphaName), "rev": revHash}),
page,
map[string]string{},
@ -174,8 +174,8 @@ func handlerRevision(w http.ResponseWriter, rq *http.Request) {
func handlerText(w http.ResponseWriter, rq *http.Request) {
util.PrepareRq(rq)
hyphaName := util.HyphaNameFromRq(rq, "text")
switch h := hyphae.ByName(hyphaName).(type) {
case hyphae.ExistingHypha:
switch h := hyphae2.ByName(hyphaName).(type) {
case hyphae2.ExistingHypha:
log.Println("Serving", h.TextFilePath())
w.Header().Set("Content-Type", "text/plain; charset=utf-8")
http.ServeFile(w, rq, h.TextFilePath())
@ -186,12 +186,12 @@ func handlerText(w http.ResponseWriter, rq *http.Request) {
func handlerBinary(w http.ResponseWriter, rq *http.Request) {
util.PrepareRq(rq)
hyphaName := util.HyphaNameFromRq(rq, "binary")
switch h := hyphae.ByName(hyphaName).(type) {
case *hyphae.EmptyHypha:
case *hyphae.TextualHypha:
switch h := hyphae2.ByName(hyphaName).(type) {
case *hyphae2.EmptyHypha:
case *hyphae2.TextualHypha:
w.WriteHeader(http.StatusNotFound)
log.Printf("Textual hypha %s has no media, cannot serve\n", h.CanonicalName())
case *hyphae.MediaHypha:
case *hyphae2.MediaHypha:
log.Println("Serving", h.MediaFilePath())
w.Header().Set("Content-Type", mimetype.FromExtension(filepath.Ext(h.MediaFilePath())))
http.ServeFile(w, rq, h.MediaFilePath())
@ -203,22 +203,22 @@ func handlerHypha(w http.ResponseWriter, rq *http.Request) {
util.PrepareRq(rq)
var (
hyphaName = util.HyphaNameFromRq(rq, "page", "hypha")
h = hyphae.ByName(hyphaName)
h = hyphae2.ByName(hyphaName)
contents string
openGraph string
lc = l18n.FromRequest(rq)
)
switch h := h.(type) {
case *hyphae.EmptyHypha:
case *hyphae2.EmptyHypha:
util.HTTP404Page(w,
viewutil.Base(
viewutil.MetaFrom(w, rq),
viewutil2.Base(
viewutil2.MetaFrom(w, rq),
util.BeautifulName(hyphaName),
views2.Hypha(viewutil.MetaFrom(w, rq), h, contents),
views2.Hypha(viewutil2.MetaFrom(w, rq), h, contents),
map[string]string{},
openGraph))
case hyphae.ExistingHypha:
case hyphae2.ExistingHypha:
fileContentsT, errT := os.ReadFile(h.TextFilePath())
if errT == nil {
ctx, _ := mycocontext.ContextFromStringInput(string(fileContentsT), mycoopts.MarkupOptions(hyphaName))
@ -228,17 +228,17 @@ func handlerHypha(w http.ResponseWriter, rq *http.Request) {
openGraph = getOpenGraph()
}
switch h := h.(type) {
case *hyphae.MediaHypha:
case *hyphae2.MediaHypha:
contents = mycoopts.Media(h, lc) + contents
}
category_list := ":" + strings.Join(categories.CategoriesWithHypha(h.CanonicalName()), ":") + ":"
util.HTTP200Page(w,
viewutil.Base(
viewutil.MetaFrom(w, rq),
viewutil2.Base(
viewutil2.MetaFrom(w, rq),
util.BeautifulName(hyphaName),
views2.Hypha(viewutil.MetaFrom(w, rq), h, contents),
views2.Hypha(viewutil2.MetaFrom(w, rq), h, contents),
map[string]string{"cats": category_list},
openGraph))
}
@ -248,7 +248,7 @@ func handlerHypha(w http.ResponseWriter, rq *http.Request) {
func handlerBacklinks(w http.ResponseWriter, rq *http.Request) {
hyphaName := util.HyphaNameFromRq(rq, "backlinks")
_ = pageBacklinks.RenderTo(viewutil.MetaFrom(w, rq),
_ = pageBacklinks.RenderTo(viewutil2.MetaFrom(w, rq),
map[string]any{
"Addr": "/backlinks/" + hyphaName,
"HyphaName": hyphaName,
@ -257,7 +257,7 @@ func handlerBacklinks(w http.ResponseWriter, rq *http.Request) {
}
func handlerOrphans(w http.ResponseWriter, rq *http.Request) {
_ = pageOrphans.RenderTo(viewutil.MetaFrom(w, rq),
_ = pageOrphans.RenderTo(viewutil2.MetaFrom(w, rq),
map[string]any{
"Addr": "/orphans",
"Orphans": backlinks.Orphans(),

View File

@ -108,11 +108,11 @@ main h1:not(.navi-title) {font-size:1.7rem;}
blockquote { margin: 0; padding-left: .75rem; }
.wikilink_external::before { display: inline-block; width: 18px; height: 16px; vertical-align: sub; }
/* .wikilink_external { padding-left: 16px; } */
.wikilink_gopher::before { content: url("/static/icon/gopher-proto.svg"); }
.wikilink_http::before, .wikilink_https::before { content: url("/static/icon/http-proto.svg"); }
.wikilink_gopher::before { content: url("/web/staticatic/icon/gopher-proto.svg"); }
.wikilink_http::before, .wikilink_https::before { content: url("/web/staticatic/icon/http-proto.svg"); }
/* .wikilink_https { background: transparent url("/static/icon/http-proto.svg") center left no-repeat; } */
.wikilink_gemini::before { content: url("/static/icon/gemini-proto.svg"); }
.wikilink_mailto::before { content: url("/static/icon/mailto-proto.svg"); }
.wikilink_gemini::before { content: url("/web/staticatic/icon/gemini-proto.svg"); }
.wikilink_mailto::before { content: url("/web/staticatic/icon/mailto-proto.svg"); }
article { overflow-wrap: break-word; word-wrap: break-word; word-break: break-word; line-height: 150%; }
main h1 { margin: .5rem 0 0 0; }
@ -356,7 +356,7 @@ kbd {
margin: 0;
padding: 8px;
border: none;
background: url(/static/icon/x.svg) no-repeat 8px 8px / 16px 16px;
background: url(/web/static/icon/x.svg) no-repeat 8px 8px / 16px 16px;
width: 32px;
height: 32px;
cursor: pointer;

View File

Before

Width:  |  Height:  |  Size: 531 B

After

Width:  |  Height:  |  Size: 531 B

View File

Before

Width:  |  Height:  |  Size: 473 B

After

Width:  |  Height:  |  Size: 473 B

View File

Before

Width:  |  Height:  |  Size: 951 B

After

Width:  |  Height:  |  Size: 951 B

View File

Before

Width:  |  Height:  |  Size: 627 B

After

Width:  |  Height:  |  Size: 627 B

View File

Before

Width:  |  Height:  |  Size: 261 B

After

Width:  |  Height:  |  Size: 261 B

View File

Before

Width:  |  Height:  |  Size: 888 B

After

Width:  |  Height:  |  Size: 888 B

View File

Before

Width:  |  Height:  |  Size: 139 B

After

Width:  |  Height:  |  Size: 139 B

View File

@ -10,8 +10,8 @@
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>{{block "title" .}}{{end}}</title>
<link rel="icon" href="/static/favicon.ico">
<link rel="stylesheet" href="/static/style.css">
<link rel="icon" href="/web/static/favicon.ico">
<link rel="stylesheet" href="/web/static/style.css">
{{range .HeadElements}}{{.}}{{end}}
</head>
<body data-rrh-addr="{{if .Addr}}{{.Addr}}{{else}}{{.Meta.Addr}}{{end}}"{{range $key, $value := .BodyAttributes}} data-rrh-{{$key}}="{{$value}}"{{end}}>
@ -45,9 +45,9 @@
</nav>
</header>
{{block "body" .}}{{end}}
<script src="/static/common.js"></script>
<script src="/static/shortcuts.js"></script>
<script src="/static/view.js"></script>
<script src="/web/static/common.js"></script>
<script src="/web/static/shortcuts.js"></script>
<script src="/web/static/view.js"></script>
{{range .CommonScripts}}
<script src="{{.}}"></script>
{{end}}

View File

@ -1,8 +1,8 @@
package viewutil
import (
user2 "github.com/bouncepaw/mycorrhiza/internal/user"
"github.com/bouncepaw/mycorrhiza/l18n"
"github.com/bouncepaw/mycorrhiza/user"
"io"
"net/http"
)
@ -10,7 +10,7 @@ import (
// Meta is a bundle of common stuffs used by views, templates.
type Meta struct {
Lc *l18n.Localizer
U *user.User
U *user2.User
W io.Writer
Addr string
@ -23,7 +23,7 @@ type Meta struct {
func MetaFrom(w http.ResponseWriter, rq *http.Request) Meta {
return Meta{
Lc: l18n.FromRequest(rq),
U: user.FromRequest(rq),
U: user2.FromRequest(rq),
W: w,
Addr: rq.URL.Path,
}

View File

@ -4,12 +4,12 @@ package viewutil
import (
"embed"
"fmt"
"github.com/bouncepaw/mycorrhiza/internal/cfg"
"io/fs"
"log"
"strings"
"text/template" // TODO: save the world
"github.com/bouncepaw/mycorrhiza/cfg"
"github.com/bouncepaw/mycorrhiza/util"
)

View File

@ -4,8 +4,10 @@ package web
import (
"errors"
"fmt"
"github.com/bouncepaw/mycorrhiza/internal/cfg"
user2 "github.com/bouncepaw/mycorrhiza/internal/user"
"github.com/bouncepaw/mycorrhiza/l18n"
"github.com/bouncepaw/mycorrhiza/viewutil"
viewutil2 "github.com/bouncepaw/mycorrhiza/web/viewutil"
"io"
"log"
"mime"
@ -22,8 +24,6 @@ import (
"github.com/bouncepaw/mycorrhiza/misc"
"github.com/gorilla/mux"
"github.com/bouncepaw/mycorrhiza/cfg"
"github.com/bouncepaw/mycorrhiza/user"
"github.com/bouncepaw/mycorrhiza/util"
)
@ -63,7 +63,7 @@ func Handler() http.Handler {
r := router.PathPrefix("").Subrouter()
r.Use(func(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, rq *http.Request) {
user := user.FromRequest(rq)
user := user2.FromRequest(rq)
if !user.ShowLockMaybe(w, rq) {
next.ServeHTTP(w, rq)
}
@ -117,7 +117,7 @@ func Handler() http.Handler {
func groupMiddleware(group string) func(http.Handler) http.Handler {
return func(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, rq *http.Request) {
if cfg.UseAuth && user.CanProceed(rq, group) {
if cfg.UseAuth && user2.CanProceed(rq, group) {
next.ServeHTTP(w, rq)
return
}
@ -133,8 +133,8 @@ func groupMiddleware(group string) func(http.Handler) http.Handler {
// Auth
func handlerUserList(w http.ResponseWriter, rq *http.Request) {
admins, moderators, editors, readers := user.UsersInGroups()
_ = pageUserList.RenderTo(viewutil.MetaFrom(w, rq),
admins, moderators, editors, readers := user2.UsersInGroups()
_ = pageUserList.RenderTo(viewutil2.MetaFrom(w, rq),
map[string]any{
"Admins": admins,
"Moderators": moderators,
@ -154,8 +154,8 @@ func handlerRegister(w http.ResponseWriter, rq *http.Request) {
if rq.Method == http.MethodGet {
_, _ = io.WriteString(
w,
viewutil.Base(
viewutil.MetaFrom(w, rq),
viewutil2.Base(
viewutil2.MetaFrom(w, rq),
lc.Get("auth.register_title"),
auth.Register(rq),
map[string]string{},
@ -167,7 +167,7 @@ func handlerRegister(w http.ResponseWriter, rq *http.Request) {
var (
username = rq.PostFormValue("username")
password = rq.PostFormValue("password")
err = user.Register(username, password, "editor", "local", false)
err = user2.Register(username, password, "editor", "local", false)
)
if err != nil {
log.Printf("Failed to register %s: %s", username, err.Error())
@ -175,8 +175,8 @@ func handlerRegister(w http.ResponseWriter, rq *http.Request) {
w.WriteHeader(http.StatusBadRequest)
_, _ = io.WriteString(
w,
viewutil.Base(
viewutil.MetaFrom(w, rq),
viewutil2.Base(
viewutil2.MetaFrom(w, rq),
lc.Get("auth.register_title"),
fmt.Sprintf(
`<main class="main-width"><p>%s</p><p><a href="/register">%s<a></p></main>`,
@ -190,7 +190,7 @@ func handlerRegister(w http.ResponseWriter, rq *http.Request) {
}
log.Printf("Successfully registered %s", username)
if err := user.LoginDataHTTP(w, username, password); err != nil {
if err := user2.LoginDataHTTP(w, username, password); err != nil {
return
}
http.Redirect(w, rq, "/"+rq.URL.RawQuery, http.StatusSeeOther)
@ -200,7 +200,7 @@ func handlerRegister(w http.ResponseWriter, rq *http.Request) {
func handlerLogout(w http.ResponseWriter, rq *http.Request) {
if rq.Method == http.MethodGet {
var (
u = user.FromRequest(rq)
u = user2.FromRequest(rq)
can = u != nil
lc = l18n.FromRequest(rq)
)
@ -214,10 +214,10 @@ func handlerLogout(w http.ResponseWriter, rq *http.Request) {
}
_, _ = io.WriteString(
w,
viewutil.Base(viewutil.MetaFrom(w, rq), lc.Get("auth.logout_title"), auth.Logout(can, lc), map[string]string{}),
viewutil2.Base(viewutil2.MetaFrom(w, rq), lc.Get("auth.logout_title"), auth.Logout(can, lc), map[string]string{}),
)
} else if rq.Method == http.MethodPost {
user.LogoutFromRequest(w, rq)
user2.LogoutFromRequest(w, rq)
http.Redirect(w, rq, "/", http.StatusSeeOther)
}
}
@ -230,8 +230,8 @@ func handlerLogin(w http.ResponseWriter, rq *http.Request) {
w.WriteHeader(http.StatusOK)
_, _ = io.WriteString(
w,
viewutil.Base(
viewutil.MetaFrom(w, rq),
viewutil2.Base(
viewutil2.MetaFrom(w, rq),
lc.Get("auth.login_title"),
auth.Login(lc),
map[string]string{},
@ -241,12 +241,12 @@ func handlerLogin(w http.ResponseWriter, rq *http.Request) {
var (
username = util.CanonicalName(rq.PostFormValue("username"))
password = rq.PostFormValue("password")
err = user.LoginDataHTTP(w, username, password)
err = user2.LoginDataHTTP(w, username, password)
)
if err != nil {
w.Header().Set("Content-Type", "text/html;charset=utf-8")
w.WriteHeader(http.StatusInternalServerError)
_, _ = io.WriteString(w, viewutil.Base(viewutil.MetaFrom(w, rq), err.Error(), auth.LoginError(err.Error(), lc), map[string]string{}))
_, _ = io.WriteString(w, viewutil2.Base(viewutil2.MetaFrom(w, rq), err.Error(), auth.LoginError(err.Error(), lc), map[string]string{}))
return
}
http.Redirect(w, rq, "/", http.StatusSeeOther)
@ -261,8 +261,8 @@ func handlerTelegramLogin(w http.ResponseWriter, rq *http.Request) {
var (
values = rq.URL.Query()
username = strings.ToLower(values.Get("username"))
seemsValid = user.TelegramAuthParamsAreValid(values)
err = user.Register(
seemsValid = user2.TelegramAuthParamsAreValid(values)
err = user2.Register(
username,
"", // Password matters not
"editor",
@ -272,7 +272,7 @@ func handlerTelegramLogin(w http.ResponseWriter, rq *http.Request) {
)
// If registering a user via Telegram failed, because a Telegram user with this name
// has already registered, then everything is actually ok!
if user.HasUsername(username) && user.ByName(username).Source == "telegram" {
if user2.HasUsername(username) && user2.ByName(username).Source == "telegram" {
err = nil
}
@ -285,8 +285,8 @@ func handlerTelegramLogin(w http.ResponseWriter, rq *http.Request) {
w.WriteHeader(http.StatusBadRequest)
_, _ = io.WriteString(
w,
viewutil.Base(
viewutil.MetaFrom(w, rq),
viewutil2.Base(
viewutil2.MetaFrom(w, rq),
lc.Get("ui.error"),
fmt.Sprintf(
`<main class="main-width"><p>%s</p><p>%s</p><p><a href="/login">%s<a></p></main>`,
@ -300,14 +300,14 @@ func handlerTelegramLogin(w http.ResponseWriter, rq *http.Request) {
return
}
errmsg := user.LoginDataHTTP(w, username, "")
errmsg := user2.LoginDataHTTP(w, username, "")
if errmsg != nil {
log.Printf("Failed to login %s using Telegram: %s", username, err.Error())
w.WriteHeader(http.StatusBadRequest)
_, _ = io.WriteString(
w,
viewutil.Base(
viewutil.MetaFrom(w, rq),
viewutil2.Base(
viewutil2.MetaFrom(w, rq),
"Error",
fmt.Sprintf(
`<main class="main-width"><p>%s</p><p>%s</p><p><a href="/login">%s<a></p></main>`,