From e38daba7adb27f3e83596f794d072e8d107d3838 Mon Sep 17 00:00:00 2001 From: Timur Ismagilov Date: Wed, 22 Jun 2022 00:39:56 +0300 Subject: [PATCH] Interwiki: Add /interwiki/add-entry and the related stuff --- interwiki/interwiki.go | 59 ++++++++++++++++++++++++++++------ interwiki/map.go | 15 ++++----- interwiki/view_name_taken.html | 43 +++++++++++++++++++++++++ interwiki/web.go | 38 +++++++++++++++++++++- 4 files changed, 135 insertions(+), 20 deletions(-) create mode 100644 interwiki/view_name_taken.html diff --git a/interwiki/interwiki.go b/interwiki/interwiki.go index a1fc69c..519a5d6 100644 --- a/interwiki/interwiki.go +++ b/interwiki/interwiki.go @@ -3,11 +3,13 @@ package interwiki import ( "encoding/json" + "errors" "github.com/bouncepaw/mycomarkup/v5/options" "github.com/bouncepaw/mycorrhiza/files" "github.com/bouncepaw/mycorrhiza/util" "log" "os" + "sync" ) func Init() { @@ -20,21 +22,47 @@ func Init() { for _, wiki := range record { wiki := wiki // This line is required wiki.canonize() - theMap.list = append(theMap.list, &wiki) - for _, name := range append(wiki.Aliases, wiki.Name) { - if _, found := theMap.byName[name]; found { - log.Fatalf("There are multiple uses of the same name โ€˜%sโ€™\n", name) - } else { - theMap.byName[name] = &wiki - } + if err := addEntry(&wiki); err != nil { + log.Fatalln(err.Error()) } } - log.Printf("Loaded %d interwiki entries\n", len(theMap.list)) + log.Printf("Loaded %d interwiki entries\n", len(listOfEntries)) +} + +func areNamesFree(names []string) (bool, string) { + for _, name := range names { + if _, found := entriesByName[name]; found { + return false, name + } + } + return true, "" +} + +var mutex sync.Mutex + +func addEntry(wiki *Wiki) error { + mutex.Lock() + defer mutex.Unlock() + + var ( + names = append(wiki.Aliases, wiki.Name) + ok, name = areNamesFree(names) + ) + if !ok { + log.Printf("There are multiple uses of the same name โ€˜%sโ€™\n", name) + return errors.New(name) + } + + listOfEntries = append(listOfEntries, wiki) + for _, name := range names { + entriesByName[name] = wiki + } + return nil } func HrefLinkFormatFor(prefix string) (string, options.InterwikiError) { prefix = util.CanonicalName(prefix) - if wiki, ok := theMap.byName[prefix]; ok { + if wiki, ok := entriesByName[prefix]; ok { return wiki.LinkHrefFormat, options.Ok } return "", options.UnknownPrefix @@ -42,7 +70,7 @@ func HrefLinkFormatFor(prefix string) (string, options.InterwikiError) { func ImgSrcFormatFor(prefix string) (string, options.InterwikiError) { prefix = util.CanonicalName(prefix) - if wiki, ok := theMap.byName[prefix]; ok { + if wiki, ok := entriesByName[prefix]; ok { return wiki.ImgSrcFormat, options.Ok } return "", options.UnknownPrefix @@ -66,3 +94,14 @@ func readInterwiki() ([]Wiki, error) { } return record, nil } + +func saveInterwikiJson() { + // Trust me, wiki crashing when an admin takes an administrative action totally makes sense. + if data, err := json.MarshalIndent(listOfEntries, "", "\t"); err != nil { + log.Fatalln(err) + } else if err = os.WriteFile(files.InterwikiJSON(), data, 0666); err != nil { + log.Fatalln(err) + } else { + log.Println("Saved interwiki.json") + } +} diff --git a/interwiki/map.go b/interwiki/map.go index 989788a..98dad8f 100644 --- a/interwiki/map.go +++ b/interwiki/map.go @@ -1,14 +1,11 @@ package interwiki -// Map is an interwiki map -type Map struct { - list []*Wiki - byName map[string]*Wiki -} - -var theMap Map +var ( + listOfEntries []*Wiki + entriesByName map[string]*Wiki +) func init() { - theMap.list = []*Wiki{} - theMap.byName = map[string]*Wiki{} + listOfEntries = []*Wiki{} + entriesByName = map[string]*Wiki{} } diff --git a/interwiki/view_name_taken.html b/interwiki/view_name_taken.html new file mode 100644 index 0000000..40b55cf --- /dev/null +++ b/interwiki/view_name_taken.html @@ -0,0 +1,43 @@ +{{define "body"}} +
+

{{block "heading" .}}Name taken{{end}}

+

{{block "tip" .TakenName}}Name {{.}} is already taken, please choose a different one.{{end}}

+
+

+ + +

+

+ + +

+

+ + +

+

+ + +

+

+ + +

+

+ + +

+ + {{template "cancel"}} +
+
+{{end}} \ No newline at end of file diff --git a/interwiki/web.go b/interwiki/web.go index ceb5f74..63d9843 100644 --- a/interwiki/web.go +++ b/interwiki/web.go @@ -5,6 +5,7 @@ import ( "github.com/bouncepaw/mycorrhiza/viewutil" "github.com/gorilla/mux" "net/http" + "strings" ) var ( @@ -12,11 +13,46 @@ var ( fs embed.FS ruTranslation = `` chainInterwiki viewutil.Chain + chainNameTaken viewutil.Chain ) func InitHandlers(rtr *mux.Router) { chainInterwiki = viewutil.CopyEnRuWith(fs, "view_interwiki.html", ruTranslation) + chainNameTaken = viewutil.CopyEnRuWith(fs, "view_name_taken.html", ruTranslation) rtr.HandleFunc("/interwiki", handlerInterwiki) + rtr.HandleFunc("/interwiki/add-entry", handlerAddEntry).Methods(http.MethodPost) +} + +func handlerAddEntry(w http.ResponseWriter, rq *http.Request) { + wiki := Wiki{ + Name: rq.PostFormValue("name"), + Aliases: strings.Split(rq.PostFormValue("aliases"), ","), + URL: rq.PostFormValue("url"), + LinkHrefFormat: rq.PostFormValue("link-href-format"), + ImgSrcFormat: rq.PostFormValue("img-src-format"), + Engine: WikiEngine(rq.PostFormValue("engine")), + } + wiki.canonize() + if err := addEntry(&wiki); err != nil { + viewNameTaken(viewutil.MetaFrom(w, rq), &wiki, err.Error()) + return + } + saveInterwikiJson() + http.Redirect(w, rq, "/interwiki", http.StatusSeeOther) +} + +type nameTakenData struct { + *viewutil.BaseData + *Wiki + TakenName string +} + +func viewNameTaken(meta viewutil.Meta, wiki *Wiki, takenName string) { + viewutil.ExecutePage(meta, chainNameTaken, nameTakenData{ + BaseData: &viewutil.BaseData{}, + Wiki: wiki, + TakenName: takenName, + }) } func handlerInterwiki(w http.ResponseWriter, rq *http.Request) { @@ -33,7 +69,7 @@ type interwikiData struct { func viewInterwiki(meta viewutil.Meta) { viewutil.ExecutePage(meta, chainInterwiki, interwikiData{ BaseData: &viewutil.BaseData{}, - Entries: theMap.list, + Entries: listOfEntries, CanEdit: meta.U.Group == "admin", Error: "", })