forked from mirror/oddmu
Errors in p.save aren't logged, so the saveHandler must log them. Errors in p.notify are logged, so the saveHandler must not log them. That's because in this situation, the errors are not directly related to page saving (which is what the saveHandler does). However, the user must still be made aware of the error, so a HTTP error page is shown. When backup fails (since it now returns an error), save does not proceed. The helper functions in notify.go using c as the variable for the changes page, the index page, or a hashtag page now all use p.
188 lines
6.0 KiB
Go
188 lines
6.0 KiB
Go
package main
|
|
|
|
import (
|
|
"log"
|
|
"path"
|
|
"regexp"
|
|
"strings"
|
|
"time"
|
|
)
|
|
|
|
// notify adds a link to the "changes" page, the "index" page, as well as to all the existing hashtag pages. The link to
|
|
// the "index" page is only added if the page being edited is a blog page for the current year. The link to existing
|
|
// hashtag pages is only added for blog pages. If the "changes" page does not exist, it is created. If the hashtag page
|
|
// does not exist, it is not. Hashtag pages are considered optional. If the page that's being edited is in a
|
|
// subdirectory, then the "changes", "index" and hashtag pages of that particular subdirectory are affected. Every
|
|
// subdirectory is treated like a potentially independent wiki. Errors are logged before being returned because the
|
|
// error messages are confusing from the point of view of the saveHandler.
|
|
func (p *Page) notify() error {
|
|
p.handleTitle(false)
|
|
if p.Title == "" {
|
|
p.Title = p.Name
|
|
}
|
|
esc := nameEscape(path.Base(p.Name))
|
|
link := "* [" + p.Title + "](" + esc + ")\n"
|
|
re := regexp.MustCompile(`(?m)^\* \[[^\]]+\]\(` + esc + `\)\n`)
|
|
dir := path.Dir(p.Name)
|
|
err := addLinkWithDate(path.Join(dir, "changes"), link, re)
|
|
if err != nil {
|
|
log.Printf("Updating changes in %s failed: %s", dir, err)
|
|
return err
|
|
}
|
|
if p.isBlog() {
|
|
// Add to the index only if the blog post is for the current year
|
|
if strings.HasPrefix(path.Base(p.Name), time.Now().Format("2006")) {
|
|
err := addLink(path.Join(dir, "index"), true, link, re)
|
|
if err != nil {
|
|
log.Printf("Updating index in %s failed: %s", dir, err)
|
|
return err
|
|
}
|
|
}
|
|
p.renderHtml() // to set hashtags
|
|
for _, hashtag := range p.Hashtags {
|
|
err := addLink(path.Join(dir, hashtag), false, link, re)
|
|
if err != nil {
|
|
log.Printf("Updating hashtag %s in %s failed: %s", hashtag, dir, err)
|
|
return err
|
|
}
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// addLinkWithDate adds the link to a page, with date header for today. If a match already exists, it is removed. If
|
|
// this leaves a date header without any links, it is removed as well. If a list is found, the link is added at the top
|
|
// of the list. Lists must use the asterisk, not the minus character.
|
|
func addLinkWithDate(name, link string, re *regexp.Regexp) error {
|
|
date := time.Now().Format(time.DateOnly)
|
|
org := ""
|
|
p, err := loadPage(name)
|
|
if err != nil {
|
|
// create a new page
|
|
p = &Page{Name: name, Body: []byte("# Changes\n\n## " + date + "\n" + link)}
|
|
} else {
|
|
org = string(p.Body)
|
|
// remove the old match, if one exists
|
|
loc := re.FindIndex(p.Body)
|
|
if loc != nil {
|
|
r := p.Body[:loc[0]]
|
|
if loc[1] < len(p.Body) {
|
|
r = append(r, p.Body[loc[1]:]...)
|
|
}
|
|
p.Body = r
|
|
if loc[0] >= 14 && len(p.Body) >= loc[0]+15 {
|
|
// remove the preceding date if there are now two dates following each other
|
|
re := regexp.MustCompile(`(?m)^## (\d\d\d\d-\d\d-\d\d)\n\n## (\d\d\d\d-\d\d-\d\d)\n`)
|
|
if re.Match(p.Body[loc[0]-14 : loc[0]+15]) {
|
|
p.Body = append(p.Body[0:loc[0]-14], p.Body[loc[0]+1:]...)
|
|
}
|
|
} else if len(p.Body) == loc[0] {
|
|
// remove a trailing date
|
|
re := regexp.MustCompile(`## (\d\d\d\d-\d\d-\d\d)\n`)
|
|
if re.Match(p.Body[loc[0]-14 : loc[0]]) {
|
|
p.Body = p.Body[0 : loc[0]-14]
|
|
}
|
|
}
|
|
}
|
|
// locate the beginning of the list to insert the line
|
|
re := regexp.MustCompile(`(?m)^\* \[[^\]]+\]\([^\)]+\)\n`)
|
|
loc = re.FindIndex(p.Body)
|
|
if loc == nil {
|
|
// if no list was found, use the end of the page
|
|
loc = []int{len(p.Body)}
|
|
}
|
|
// start with new page content
|
|
r := []byte("")
|
|
// check if there is a date right before the insertion point
|
|
addDate := true
|
|
if loc[0] >= 14 {
|
|
re := regexp.MustCompile(`(?m)^## (\d\d\d\d-\d\d-\d\d)\n`)
|
|
m := re.Find(p.Body[loc[0]-14 : loc[0]])
|
|
if m == nil {
|
|
// not a date: insert date, don't move insertion point
|
|
} else if string(p.Body[loc[0]-11:loc[0]-1]) == date {
|
|
// if the date is our date, don't add it, don't move insertion point
|
|
addDate = false
|
|
} else {
|
|
// if the date is not out date, move the insertion point
|
|
loc[0] -= 14
|
|
}
|
|
}
|
|
// append up to the insertion point
|
|
r = append(r, p.Body[:loc[0]]...)
|
|
// append date, if necessary
|
|
if addDate {
|
|
// ensure paragraph break
|
|
if len(r) > 0 && r[len(r)-1] != '\n' {
|
|
r = append(r, '\n')
|
|
}
|
|
if len(r) > 1 && r[len(r)-2] != '\n' {
|
|
r = append(r, '\n')
|
|
}
|
|
r = append(r, []byte("## ")...)
|
|
r = append(r, []byte(date)...)
|
|
r = append(r, '\n')
|
|
}
|
|
// append link
|
|
r = append(r, []byte(link)...)
|
|
// if we just added a date, add an empty line after the single-element list
|
|
if len(p.Body) > loc[0] && p.Body[loc[0]] != '*' {
|
|
r = append(r, '\n')
|
|
}
|
|
// append the rest
|
|
r = append(r, p.Body[loc[0]:]...)
|
|
p.Body = r
|
|
}
|
|
// only save if something changed
|
|
if string(p.Body) != org {
|
|
return p.save()
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// addLink adds a link to a named page, if the page exists and doesn't contain the link. If the link exists but with a
|
|
// different title, the title is fixed.
|
|
func addLink(name string, mandatory bool, link string, re *regexp.Regexp) error {
|
|
p, err := loadPage(name)
|
|
if err != nil {
|
|
if mandatory {
|
|
p = &Page{Name: name, Body: []byte(link)}
|
|
return p.save()
|
|
} else {
|
|
// Skip non-existing files: no error
|
|
return nil
|
|
}
|
|
}
|
|
org := string(p.Body)
|
|
// if a link exists, that's the place to insert the new link (in which case loc[0] and loc[1] differ)
|
|
loc := re.FindIndex(p.Body)
|
|
// if no link exists, find a good place to insert it
|
|
if loc == nil {
|
|
// locate the beginning of the list to insert the line
|
|
re = regexp.MustCompile(`(?m)^\* \[[^\]]+\]\([^\)]+\)\n`)
|
|
loc = re.FindIndex(p.Body)
|
|
if loc == nil {
|
|
// if no list was found, use the end of the page
|
|
m := len(p.Body)
|
|
loc = []int{m, m}
|
|
} else {
|
|
// if a list item was found, use just the beginning as insertion point
|
|
loc[1] = loc[0]
|
|
}
|
|
}
|
|
// start with new page content
|
|
r := []byte("")
|
|
// append up to the insertion point
|
|
r = append(r, p.Body[:loc[0]]...)
|
|
// append link
|
|
r = append(r, []byte(link)...)
|
|
// append the rest
|
|
r = append(r, p.Body[loc[1]:]...)
|
|
p.Body = r
|
|
// only save if something changed
|
|
if string(p.Body) != org {
|
|
return p.save()
|
|
}
|
|
return nil
|
|
}
|