14 Commits

24 changed files with 92 additions and 58 deletions

2
.gitignore vendored
View File

@@ -6,5 +6,5 @@ build/
cmd/yatwiki-server/yatwiki-server cmd/yatwiki-server/yatwiki-server
# Development db files # Development db files
cmd/yatwiki-server/wiki.db cmd/yatwiki-server/*.db
cmd/yatwiki-server/config.json cmd/yatwiki-server/config.json

View File

@@ -1,4 +1,4 @@
package yatwiki3 package yatwiki
import ( import (
"crypto/md5" "crypto/md5"
@@ -11,8 +11,16 @@ func RemoteAddrToIPAddress(remoteAddr string) string {
return strings.TrimRight(strings.TrimRight(remoteAddr, `0123456789`), `:`) // trim trailing port; IPv4 and IPv6-safe return strings.TrimRight(strings.TrimRight(remoteAddr, `0123456789`), `:`) // trim trailing port; IPv4 and IPv6-safe
} }
func Author(r *http.Request) string { func Author(r *http.Request, trustXForwardedFor bool) string {
userAgentHash := md5.Sum([]byte(r.UserAgent())) userAgentHash := md5.Sum([]byte(r.UserAgent()))
return RemoteAddrToIPAddress(r.RemoteAddr) + "-" + hex.EncodeToString(userAgentHash[:])[:6] ipAddress := RemoteAddrToIPAddress(r.RemoteAddr)
if trustXForwardedFor {
if xff := r.Header.Get("X-Forwarded-For"); len(xff) > 0 {
ipAddress = xff
}
}
return ipAddress + "-" + hex.EncodeToString(userAgentHash[:])[:6]
} }

2
DB.go
View File

@@ -1,4 +1,4 @@
package yatwiki3 package yatwiki
import ( import (
"database/sql" "database/sql"

View File

@@ -2,7 +2,7 @@
# Makefile for YATWiki3 # Makefile for YATWiki3
# #
VERSION:=3.0 VERSION:=3.0.1
SOURCES:=Makefile \ SOURCES:=Makefile \
static \ static \
@@ -34,7 +34,7 @@ clean:
# #
staticResources.go: static/ static/* staticResources.go: static/ static/*
go-bindata -o staticResources.go -prefix static -pkg yatwiki3 static go-bindata -o staticResources.go -prefix static -pkg yatwiki static
# #

View File

@@ -1,4 +1,4 @@
package yatwiki3 package yatwiki
import ( import (
"time" "time"
@@ -13,6 +13,7 @@ type ServerOptions struct {
DBFilePath string DBFilePath string
FaviconFilePath string FaviconFilePath string
AllowDBDownload bool AllowDBDownload bool
TrustXForwardedFor bool // Introduced in 3.0.1 - default false
RecentChanges int RecentChanges int
RecentChangesRSS int RecentChangesRSS int
GzipCompressionLevel int GzipCompressionLevel int
@@ -32,6 +33,7 @@ func DefaultOptions() *ServerOptions {
DBFilePath: "wiki.db", DBFilePath: "wiki.db",
FaviconFilePath: "", // no favicon FaviconFilePath: "", // no favicon
AllowDBDownload: true, AllowDBDownload: true,
TrustXForwardedFor: false,
RecentChanges: 20, RecentChanges: 20,
RecentChangesRSS: 10, RecentChangesRSS: 10,
GzipCompressionLevel: 9, GzipCompressionLevel: 9,

View File

@@ -1,4 +1,4 @@
package yatwiki3 package yatwiki
import ( import (
"database/sql" "database/sql"
@@ -37,7 +37,13 @@ func NewWikiServer(opts *ServerOptions) (*WikiServer, error) {
} }
} }
tmpl, err := template.New("yatwiki/page").Parse(pageTemplate) tmpl := template.New("yatwiki/page")
tmpl.Funcs(map[string]interface{}{
"pathcomponent": func(s string) string {
return url.PathEscape(s)
},
})
_, err = tmpl.Parse(pageTemplate)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@@ -128,7 +134,7 @@ func (this *WikiServer) ServeHTTP(w http.ResponseWriter, r *http.Request) {
return return
} else if remainingPath == "" { } else if remainingPath == "" {
this.serveRedirect(w, this.opts.ExpectBaseURL+`view/`+url.QueryEscape(this.opts.DefaultPage)) this.serveRedirect(w, this.opts.ExpectBaseURL+`view/`+url.PathEscape(this.opts.DefaultPage))
return return
} else if remainingPath == "random" { } else if remainingPath == "random" {
@@ -139,11 +145,11 @@ func (this *WikiServer) ServeHTTP(w http.ResponseWriter, r *http.Request) {
} }
chosenArticle := titles[rand.Intn(len(titles))] chosenArticle := titles[rand.Intn(len(titles))]
this.serveRedirect(w, this.opts.ExpectBaseURL+`view/`+url.QueryEscape(chosenArticle)) this.serveRedirect(w, this.opts.ExpectBaseURL+`view/`+url.PathEscape(chosenArticle))
return return
} else if strings.HasPrefix(remainingPath, "view/") { } else if strings.HasPrefix(remainingPath, "view/") {
articleTitle, err := url.QueryUnescape(remainingPath[len("view/"):]) articleTitle, err := url.PathUnescape(remainingPath[len("view/"):])
if err != nil { if err != nil {
this.serveErrorMessage(w, err) this.serveErrorMessage(w, err)
return return
@@ -152,7 +158,7 @@ func (this *WikiServer) ServeHTTP(w http.ResponseWriter, r *http.Request) {
return return
} else if strings.HasPrefix(remainingPath, "modify/") { } else if strings.HasPrefix(remainingPath, "modify/") {
articleTitle, err := url.QueryUnescape(remainingPath[len("modify/"):]) articleTitle, err := url.PathUnescape(remainingPath[len("modify/"):])
if err != nil { if err != nil {
this.serveErrorMessage(w, err) this.serveErrorMessage(w, err)
return return
@@ -161,7 +167,7 @@ func (this *WikiServer) ServeHTTP(w http.ResponseWriter, r *http.Request) {
return return
} else if strings.HasPrefix(remainingPath, "history/") { } else if strings.HasPrefix(remainingPath, "history/") {
articleTitle, err := url.QueryUnescape(remainingPath[len("history/"):]) articleTitle, err := url.PathUnescape(remainingPath[len("history/"):])
if err != nil { if err != nil {
this.serveErrorMessage(w, err) this.serveErrorMessage(w, err)
return return
@@ -255,13 +261,13 @@ func (this *WikiServer) ServeHTTP(w http.ResponseWriter, r *http.Request) {
return return
} }
err = this.db.SaveArticle(title, Author(r), body, int64(expectRev)) err = this.db.SaveArticle(title, Author(r, this.opts.TrustXForwardedFor), body, int64(expectRev))
if err != nil { if err != nil {
this.serveErrorMessage(w, err) this.serveErrorMessage(w, err)
return return
} }
this.serveRedirect(w, this.opts.ExpectBaseURL+`view/`+url.QueryEscape(title)) this.serveRedirect(w, this.opts.ExpectBaseURL+`view/`+url.PathEscape(title))
return return
} }

View File

@@ -28,9 +28,20 @@ You can start YATWiki by running the binary. A default configuration file and da
Bind address (default "127.0.0.1:80") Bind address (default "127.0.0.1:80")
` `
=GO GET=
This package can be installed via go get: `go get code.ivysaur.me/yatwiki`
[go-get]code.ivysaur.me/yatwiki git https://git.ivysaur.me/code.ivysaur.me/yatwiki.git[/go-get]
=CHANGELOG= =CHANGELOG=
2017-07-11 v3.0 2017-08-11 3.0.1
- Feature: New `TrustXForwardedFor` config option for usage behind reverse proxies
- Fix an issue with article titles containing `+`
- Fix an issue with `[html]` tags
- Fix an issue with viewing history for unknown articles
2017-07-11 3.0
- YATWiki was rewritten in Go. - YATWiki was rewritten in Go.
- Enhancement: Standalone binary server - Enhancement: Standalone binary server
- Enhancement: No longer requires cookies for error messages - Enhancement: No longer requires cookies for error messages
@@ -43,11 +54,13 @@ You can start YATWiki by running the binary. A default configuration file and da
- Fix a number of issues with handling of base URLs in links - Fix a number of issues with handling of base URLs in links
- Fix a cosmetic issue with file caching for CSS content - Fix a cosmetic issue with file caching for CSS content
2016-11-16 (no public release) 2016-11-16 20161116
- (no public release)
- Enhancement: Always open the formatting help in a new tab - Enhancement: Always open the formatting help in a new tab
- Fix a cosmetic issue with display of backslash characters caused by Meiryo font - Fix a cosmetic issue with display of backslash characters caused by Meiryo font
2016-08-24 (no public release) 2016-08-24 20160824
- (no public release)
- Feature: Add Compare button to both top and bottom of article revision list - Feature: Add Compare button to both top and bottom of article revision list
- Fix an issue with noncompliant HTML when comparing diffs - Fix an issue with noncompliant HTML when comparing diffs

View File

@@ -1,4 +1,4 @@
package yatwiki3 package yatwiki
import ( import (
"encoding/json" "encoding/json"
@@ -45,10 +45,10 @@ func (this *BBCodeRenderer) bbcode(data string) string {
pregReplaceRule{regexp.MustCompile(`(?si)\[\*\]`), `</li><li>`, nil}, pregReplaceRule{regexp.MustCompile(`(?si)\[\*\]`), `</li><li>`, nil},
pregReplaceRule{regexp.MustCompile(`(?si)\[url=(.*?)\](.*?)\[/url\]`), `<a rel="noreferrer" href="$1">$2</a>`, nil}, pregReplaceRule{regexp.MustCompile(`(?si)\[url=(.*?)\](.*?)\[/url\]`), `<a rel="noreferrer" href="$1">$2</a>`, nil},
pregReplaceRule{regexp.MustCompile(`(?si)\[article=(.*?)\](.*?)\[/article\]`), "", func(m []string) string { pregReplaceRule{regexp.MustCompile(`(?si)\[article=(.*?)\](.*?)\[/article\]`), "", func(m []string) string {
return `<a href="` + template.HTMLEscapeString(this.baseUrl+`view/`+url.QueryEscape(m[1])) + `">` + m[2] + `</a>` return `<a href="` + template.HTMLEscapeString(this.baseUrl+`view/`+url.PathEscape(m[1])) + `">` + m[2] + `</a>`
}}, }},
pregReplaceRule{regexp.MustCompile(`(?si)\[rev=(.*?)\](.*?)\[/rev\]`), "", func(m []string) string { pregReplaceRule{regexp.MustCompile(`(?si)\[rev=(.*?)\](.*?)\[/rev\]`), "", func(m []string) string {
return `<a href="` + template.HTMLEscapeString(this.baseUrl+`archive/`+url.QueryEscape(m[1])) + `">` + m[2] + `</a>` return `<a href="` + template.HTMLEscapeString(this.baseUrl+`archive/`+url.PathEscape(m[1])) + `">` + m[2] + `</a>`
}}, }},
pregReplaceRule{regexp.MustCompile(`(?si)\[imgur\](.*?)\.(...)\[/imgur\]`), pregReplaceRule{regexp.MustCompile(`(?si)\[imgur\](.*?)\.(...)\[/imgur\]`),
@@ -177,7 +177,7 @@ func (this *BBCodeRenderer) displayfmt(s string) string {
} }
epos += spos epos += spos
jsonInnerContent, _ := json.Marshal(s[spos : epos-spos]) jsonInnerContent, _ := json.Marshal(s[spos:epos])
ret += `<div class="html"><a href="javascript:;" onclick="` + template.HTMLEscapeString(`els(this, `+string(jsonInnerContent)+`);`) + `">` + this.DynamicContentWarning + `</a></div>` ret += `<div class="html"><a href="javascript:;" onclick="` + template.HTMLEscapeString(`els(this, `+string(jsonInnerContent)+`);`) + `">` + this.DynamicContentWarning + `</a></div>`
hpos = epos + 7 hpos = epos + 7

View File

@@ -8,7 +8,7 @@ import (
"net/http" "net/http"
"os" "os"
"code.ivysaur.me/yatwiki3" "code.ivysaur.me/yatwiki"
) )
func main() { func main() {
@@ -17,13 +17,13 @@ func main() {
configPath := flag.String("config", "config.json", "Configuration file") configPath := flag.String("config", "config.json", "Configuration file")
flag.Parse() flag.Parse()
opts := yatwiki3.ServerOptions{} opts := yatwiki.ServerOptions{}
cfg, err := ioutil.ReadFile(*configPath) cfg, err := ioutil.ReadFile(*configPath)
if err != nil { if err != nil {
if os.IsNotExist(err) { if os.IsNotExist(err) {
opts = *yatwiki3.DefaultOptions() opts = *yatwiki.DefaultOptions()
if cfg, err := json.MarshalIndent(opts, "", "\t"); err == nil { if cfg, err := json.MarshalIndent(opts, "", "\t"); err == nil {
err := ioutil.WriteFile(*configPath, cfg, 0644) err := ioutil.WriteFile(*configPath, cfg, 0644)
if err != nil { if err != nil {
@@ -43,7 +43,7 @@ func main() {
} }
} }
ws, err := yatwiki3.NewWikiServer(&opts) ws, err := yatwiki.NewWikiServer(&opts)
if err != nil { if err != nil {
fmt.Fprintln(os.Stderr, err.Error()) fmt.Fprintln(os.Stderr, err.Error())
os.Exit(1) os.Exit(1)

View File

@@ -1,4 +1,4 @@
package yatwiki3 package yatwiki
import ( import (
"bytes" "bytes"

View File

@@ -1,4 +1,4 @@
package yatwiki3 package yatwiki
import ( import (
"fmt" "fmt"
@@ -85,13 +85,13 @@ function els(e,s){ // no js exec in innerHTML
</head> </head>
<body> <body>
<div class="header"> <div class="header">
<a href="{{.BaseURL}}view/{{.DefaultPage | urlquery}}" title="Home"><div class="sprite hm"></div></a> <a href="{{.BaseURL}}view/{{.DefaultPage | pathcomponent}}" title="Home"><div class="sprite hm"></div></a>
<a href="javascript:;" onclick="tid('spm');tid('tr1');tid('tr2');" title="Menu"><div class="sprite sp"></div></a> <a href="javascript:;" onclick="tid('spm');tid('tr1');tid('tr2');" title="Menu"><div class="sprite sp"></div></a>
<a href="{{.BaseURL}}modify/{{.NewArticleTitle | urlquery}}" title="New Page"><div class="sprite nw"></div></a> <a href="{{.BaseURL}}modify/{{.NewArticleTitle | pathcomponent}}" title="New Page"><div class="sprite nw"></div></a>
{{if .CurrentPageIsArticle }} {{if .CurrentPageIsArticle }}
<div class="sep"></div> <div class="sep"></div>
<a href="{{.BaseURL}}history/{{.CurrentPageName | urlquery}}" title="Page History"><div class="sprite hs"></div></a> <a href="{{.BaseURL}}history/{{.CurrentPageName | pathcomponent}}" title="Page History"><div class="sprite hs"></div></a>
<a href="{{.BaseURL}}modify/{{.CurrentPageName | urlquery}}" title="Modify Page"><div class="sprite ed"></div></a> <a href="{{.BaseURL}}modify/{{.CurrentPageName | pathcomponent}}" title="Modify Page"><div class="sprite ed"></div></a>
{{end}} {{end}}
</div> </div>
<div id="tr1" style="display:none;"></div> <div id="tr1" style="display:none;"></div>

View File

@@ -1,4 +1,4 @@
package yatwiki3 package yatwiki
import ( import (
"database/sql" "database/sql"
@@ -30,7 +30,7 @@ func (this *WikiServer) routeArchive(w http.ResponseWriter, r *http.Request, rev
`<div class="info">`+ `<div class="info">`+
`You are viewing specific revision of this page, last modified `+ `You are viewing specific revision of this page, last modified `+
time.Unix(a.Modified, 0).In(this.loc).Format(this.opts.DateFormat)+`. `+ time.Unix(a.Modified, 0).In(this.loc).Format(this.opts.DateFormat)+`. `+
`Click <a href="`+template.HTMLEscapeString(this.opts.ExpectBaseURL+`view/`+url.QueryEscape(a.Title))+`">here</a> to see the latest revision.`+ `Click <a href="`+template.HTMLEscapeString(this.opts.ExpectBaseURL+`view/`+url.PathEscape(a.Title))+`">here</a> to see the latest revision.`+
`</div>`, `</div>`,
) + bcr.RenderHTML(string(a.Body)) ) + bcr.RenderHTML(string(a.Body))
pto.LoadCodeResources = bcr.CodePresent pto.LoadCodeResources = bcr.CodePresent

View File

@@ -1,4 +1,4 @@
package yatwiki3 package yatwiki
import ( import (
"bytes" "bytes"
@@ -8,7 +8,7 @@ import (
"html/template" "html/template"
"net/http" "net/http"
"code.ivysaur.me/yatwiki3/diff" "code.ivysaur.me/yatwiki/diff"
) )
func (this *WikiServer) routeDiff(w http.ResponseWriter, r *http.Request, oldRev, newRev int) { func (this *WikiServer) routeDiff(w http.ResponseWriter, r *http.Request, oldRev, newRev int) {

View File

@@ -1,4 +1,4 @@
package yatwiki3 package yatwiki
import ( import (
"html/template" "html/template"
@@ -9,7 +9,7 @@ import (
) )
func (this *WikiServer) noSuchArticleError(title string) template.HTML { func (this *WikiServer) noSuchArticleError(title string) template.HTML {
return template.HTML(`No such article exists. <a href="` + this.opts.ExpectBaseURL + `modify/` + template.HTMLEscapeString(url.QueryEscape(title)) + `">Click here</a> to create it.`) return template.HTML(`No such article exists. <a href="` + this.opts.ExpectBaseURL + `modify/` + template.HTMLEscapeString(url.PathEscape(title)) + `">Click here</a> to create it.`)
} }
func (this *WikiServer) serveErrorMessage(w http.ResponseWriter, message error) { func (this *WikiServer) serveErrorMessage(w http.ResponseWriter, message error) {
@@ -22,7 +22,7 @@ func (this *WikiServer) serveInternalError(w http.ResponseWriter, r *http.Reques
} }
func (this *WikiServer) serveErrorHTMLMessage(w http.ResponseWriter, msg template.HTML) { func (this *WikiServer) serveErrorHTMLMessage(w http.ResponseWriter, msg template.HTML) {
this.serveRedirect(w, this.opts.ExpectBaseURL+"view/"+url.QueryEscape(this.opts.DefaultPage)+"?error="+url.QueryEscape(string(msg))) this.serveRedirect(w, this.opts.ExpectBaseURL+"view/"+url.PathEscape(this.opts.DefaultPage)+"?error="+url.QueryEscape(string(msg)))
} }
func (this *WikiServer) serveRedirect(w http.ResponseWriter, location string) { func (this *WikiServer) serveRedirect(w http.ResponseWriter, location string) {
@@ -46,5 +46,5 @@ func (this *WikiServer) formatTimestamp(m int64) string {
} }
func (this *WikiServer) viewLink(articleTitle string) template.HTML { func (this *WikiServer) viewLink(articleTitle string) template.HTML {
return template.HTML(`&quot;<a href="` + template.HTMLEscapeString(this.opts.ExpectBaseURL+`view/`+url.QueryEscape(articleTitle)) + `">` + template.HTMLEscapeString(articleTitle) + `</a>&quot;`) return template.HTML(`&quot;<a href="` + template.HTMLEscapeString(this.opts.ExpectBaseURL+`view/`+url.PathEscape(articleTitle)) + `">` + template.HTMLEscapeString(articleTitle) + `</a>&quot;`)
} }

View File

@@ -1,4 +1,4 @@
package yatwiki3 package yatwiki
import ( import (
"net/http" "net/http"

View File

@@ -1,4 +1,4 @@
package yatwiki3 package yatwiki
import ( import (
"database/sql" "database/sql"
@@ -20,12 +20,17 @@ func (this *WikiServer) routeHistory(w http.ResponseWriter, r *http.Request, art
return return
} }
if len(revs) == 0 {
this.serveErrorHTMLMessage(w, this.noSuchArticleError(articleTitle))
return
}
pto := DefaultPageTemplateOptions(this.opts) pto := DefaultPageTemplateOptions(this.opts)
pto.CurrentPageName = articleTitle pto.CurrentPageName = articleTitle
pto.CurrentPageIsArticle = true pto.CurrentPageIsArticle = true
content := `<h2>Page History</h2><br>` + content := `<h2>Page History</h2><br>` +
`<em>There have been ` + fmt.Sprintf("%d", len(revs)) + ` edits to the page &quot;<a href="` + template.HTMLEscapeString(this.opts.ExpectBaseURL+`view/`+url.QueryEscape(articleTitle)) + `">` + template.HTMLEscapeString(articleTitle) + `</a>&quot;.</em>` + `<em>There have been ` + fmt.Sprintf("%d", len(revs)) + ` edits to the page &quot;<a href="` + template.HTMLEscapeString(this.opts.ExpectBaseURL+`view/`+url.PathEscape(articleTitle)) + `">` + template.HTMLEscapeString(articleTitle) + `</a>&quot;.</em>` +
`<br><br>` + `<br><br>` +
`<form method="GET" action="` + template.HTMLEscapeString(this.opts.ExpectBaseURL+`diff`) + `">` + `<form method="GET" action="` + template.HTMLEscapeString(this.opts.ExpectBaseURL+`diff`) + `">` +
`<table>` `<table>`

View File

@@ -1,4 +1,4 @@
package yatwiki3 package yatwiki
import ( import (
"fmt" "fmt"
@@ -22,7 +22,7 @@ func (this *WikiServer) routeIndex(w http.ResponseWriter, r *http.Request) {
content := fmt.Sprintf(`<h2>Article Index</h2><br><em>There are %d edits to %d pages.</em><br><br><ul>`, totalRevs, len(titles)) content := fmt.Sprintf(`<h2>Article Index</h2><br><em>There are %d edits to %d pages.</em><br><br><ul>`, totalRevs, len(titles))
for _, title := range titles { for _, title := range titles {
content += `<li><a href="` + template.HTMLEscapeString(this.opts.ExpectBaseURL+`view/`+url.QueryEscape(title)) + `">` + template.HTMLEscapeString(title) + `</a></li>` content += `<li><a href="` + template.HTMLEscapeString(this.opts.ExpectBaseURL+`view/`+url.PathEscape(title)) + `">` + template.HTMLEscapeString(title) + `</a></li>`
} }
content += `</ul>` content += `</ul>`

View File

@@ -1,4 +1,4 @@
package yatwiki3 package yatwiki
import ( import (
"database/sql" "database/sql"
@@ -32,7 +32,7 @@ func (this *WikiServer) routeModify(w http.ResponseWriter, r *http.Request, arti
pageTitleHTML = `Creating new article` pageTitleHTML = `Creating new article`
baseRev = 0 baseRev = 0
} else { } else {
pageTitleHTML = `Editing article &quot;<a href="` + template.HTMLEscapeString(this.opts.ExpectBaseURL+`view/`+url.QueryEscape(articleTitle)) + `">` + template.HTMLEscapeString(articleTitle) + `</a>&quot;` pageTitleHTML = `Editing article &quot;<a href="` + template.HTMLEscapeString(this.opts.ExpectBaseURL+`view/`+url.PathEscape(articleTitle)) + `">` + template.HTMLEscapeString(articleTitle) + `</a>&quot;`
baseRev = a.ID baseRev = a.ID
existingBody = string(a.Body) existingBody = string(a.Body)
} }

View File

@@ -1,4 +1,4 @@
package yatwiki3 package yatwiki
import ( import (
"fmt" "fmt"
@@ -33,7 +33,7 @@ func (this *WikiServer) routeRecentChangesRSS(w http.ResponseWriter, r *http.Req
<author>` + template.HTMLEscapeString(this.opts.DeclareRSSEmail+` (`+this.opts.PageTitle+` `+a.Author+`)`) + `</author> <author>` + template.HTMLEscapeString(this.opts.DeclareRSSEmail+` (`+this.opts.PageTitle+` `+a.Author+`)`) + `</author>
<pubDate>` + template.HTMLEscapeString(time.Unix(a.Modified, 0).In(this.loc).Format(time.RFC1123Z)) + `</pubDate> <pubDate>` + template.HTMLEscapeString(time.Unix(a.Modified, 0).In(this.loc).Format(time.RFC1123Z)) + `</pubDate>
<description>` + template.HTMLEscapeString(` <description>` + template.HTMLEscapeString(`
<a href="`+template.HTMLEscapeString(this.opts.ExternalBaseURL+`view/`+url.QueryEscape(a.Title))+`">latest version</a> <a href="`+template.HTMLEscapeString(this.opts.ExternalBaseURL+`view/`+url.PathEscape(a.Title))+`">latest version</a>
| |
<a href="`+template.HTMLEscapeString(this.opts.ExternalBaseURL+`archive/`+fmt.Sprintf("%d", a.ID))+`">revision `+fmt.Sprintf("%d", a.ID)+`</a> <a href="`+template.HTMLEscapeString(this.opts.ExternalBaseURL+`archive/`+fmt.Sprintf("%d", a.ID))+`">revision `+fmt.Sprintf("%d", a.ID)+`</a>
| |

View File

@@ -1,4 +1,4 @@
package yatwiki3 package yatwiki
import ( import (
"database/sql" "database/sql"

View File

@@ -1,4 +1,4 @@
package yatwiki3 package yatwiki
import ( import (
"errors" "errors"
@@ -41,7 +41,7 @@ func (this *WikiServer) routeRecentChanges(w http.ResponseWriter, r *http.Reques
`<table>` `<table>`
for _, rev := range recents { for _, rev := range recents {
content += `<tr>` + content += `<tr>` +
`<td><a href="` + template.HTMLEscapeString(this.opts.ExpectBaseURL+`view/`+url.QueryEscape(rev.Title)) + `">` + template.HTMLEscapeString(rev.Title) + `</a>` + `<td><a href="` + template.HTMLEscapeString(this.opts.ExpectBaseURL+`view/`+url.PathEscape(rev.Title)) + `">` + template.HTMLEscapeString(rev.Title) + `</a>` +
` [<a href="` + template.HTMLEscapeString(this.opts.ExpectBaseURL+`archive/`+fmt.Sprintf("%d", rev.ID)) + `">a</a>]` + ` [<a href="` + template.HTMLEscapeString(this.opts.ExpectBaseURL+`archive/`+fmt.Sprintf("%d", rev.ID)) + `">a</a>]` +
`</td>` + `</td>` +
`<td>` + this.formatTimestamp(rev.Modified) + ` by ` + template.HTMLEscapeString(rev.Author) + `</td>` + `<td>` + this.formatTimestamp(rev.Modified) + ` by ` + template.HTMLEscapeString(rev.Author) + `</td>` +

View File

@@ -1,4 +1,4 @@
package yatwiki3 package yatwiki
import ( import (
"database/sql" "database/sql"
@@ -15,7 +15,7 @@ func (this *WikiServer) routeView(w http.ResponseWriter, r *http.Request, articl
// If this was an old link, it might not be present. // If this was an old link, it might not be present.
// Redirect if possible // Redirect if possible
if len(articleTitle) > 0 && articleTitle[len(articleTitle)-1] == '/' { if len(articleTitle) > 0 && articleTitle[len(articleTitle)-1] == '/' {
this.serveRedirect(w, this.opts.ExpectBaseURL+"view/"+url.QueryEscape(articleTitle[0:len(articleTitle)-1])) this.serveRedirect(w, this.opts.ExpectBaseURL+"view/"+url.PathEscape(articleTitle[0:len(articleTitle)-1]))
return return
} }

View File

@@ -1,4 +1,4 @@
package yatwiki3 package yatwiki
import ( import (
"regexp" "regexp"

View File

@@ -4,7 +4,7 @@
// static/wiki.css // static/wiki.css
// DO NOT EDIT! // DO NOT EDIT!
package yatwiki3 package yatwiki
import ( import (
"bytes" "bytes"