Compare commits
43 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| db659236bf | |||
| 817b1690e7 | |||
| 5b533f7b40 | |||
| 51aae382b7 | |||
| cfc0107bef | |||
| e997e1b08a | |||
| c830c2b4dd | |||
| fdb854e6c7 | |||
| f934c2917f | |||
| 1bfefdccb3 | |||
| 9ca58bc16c | |||
| 122acf6999 | |||
| f627946c0d | |||
| 5b42685956 | |||
| 90fedf86d9 | |||
| a9a6b51a3f | |||
| 9687f90cf5 | |||
| 5cc93387e7 | |||
| fc57e4d8f3 | |||
| 2bc26c5966 | |||
| f5767db840 | |||
| edf88d1f31 | |||
| 262c3ba903 | |||
| a260d102ee | |||
| 179617d058 | |||
| 5347efb51a | |||
| e3cee5b94c | |||
| e4cf02cde7 | |||
| 06e5b4ddf9 | |||
| dbf5e1b246 | |||
| fbad854279 | |||
| d78429129f | |||
| d937ea6562 | |||
| 884acd3040 | |||
| e515d73052 | |||
| c87eaa637a | |||
| 043720a086 | |||
| a12af6967c | |||
| 9792c262a2 | |||
| 6d9079e1dc | |||
| 1c4505a2d9 | |||
| 11a4f97212 | |||
| 74b3124997 |
5
.gitignore
vendored
5
.gitignore
vendored
@@ -6,5 +6,8 @@ build/
|
||||
cmd/yatwiki-server/yatwiki-server
|
||||
|
||||
# Development db files
|
||||
cmd/yatwiki-server/wiki.db
|
||||
cmd/yatwiki-server/*.db
|
||||
cmd/yatwiki-server/config.json
|
||||
|
||||
# Vendor
|
||||
vendor/
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package yatwiki3
|
||||
package yatwiki
|
||||
|
||||
import (
|
||||
"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
|
||||
}
|
||||
|
||||
func Author(r *http.Request) string {
|
||||
func Author(r *http.Request, trustXForwardedFor bool) string {
|
||||
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]
|
||||
}
|
||||
|
||||
16
DB.go
16
DB.go
@@ -1,8 +1,9 @@
|
||||
package yatwiki3
|
||||
package yatwiki
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
_ "github.com/mattn/go-sqlite3"
|
||||
@@ -91,7 +92,7 @@ func (this *WikiDB) GetRevision(revId int) (*Article, error) {
|
||||
}
|
||||
|
||||
func (this *WikiDB) GetLatestVersion(title string) (*Article, error) {
|
||||
row := this.db.QueryRow(`SELECT articles.* FROM articles WHERE article = (SELECT id FROM titles WHERE title = ?) ORDER BY modified DESC LIMIT 1`, title)
|
||||
row := this.db.QueryRow(`SELECT articles.* FROM articles WHERE article = (SELECT id FROM titles WHERE title = ?) ORDER BY modified DESC LIMIT 1`, this.normaliseTitle(title))
|
||||
return this.parseArticle(row)
|
||||
}
|
||||
|
||||
@@ -103,6 +104,10 @@ func (aae ArticleAlteredError) Error() string {
|
||||
return fmt.Sprintf("Warning: Your changes were not based on the most recent version of the page (r%d ≠ r%d). No changes were saved.", aae.got, aae.expected)
|
||||
}
|
||||
|
||||
func (this *WikiDB) normaliseTitle(title string) string {
|
||||
return strings.ToLower(strings.Trim(title, " \r\n\t"))
|
||||
}
|
||||
|
||||
func (this *WikiDB) SaveArticle(title, author, body string, expectBaseRev int64) error {
|
||||
isNewArticle := false
|
||||
a, err := this.GetLatestVersion(title)
|
||||
@@ -125,7 +130,7 @@ func (this *WikiDB) SaveArticle(title, author, body string, expectBaseRev int64)
|
||||
|
||||
var titleId int64
|
||||
if isNewArticle {
|
||||
titleInsert, err := this.db.Exec(`INSERT INTO titles (title) VALUES (?)`, title)
|
||||
titleInsert, err := this.db.Exec(`INSERT INTO titles (title) VALUES (?)`, this.normaliseTitle(title))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -147,7 +152,10 @@ func (this *WikiDB) SaveArticle(title, author, body string, expectBaseRev int64)
|
||||
}
|
||||
|
||||
func (this *WikiDB) GetRevisionHistory(title string) ([]Article, error) {
|
||||
rows, err := this.db.Query(`SELECT articles.id, articles.modified, articles.author FROM articles WHERE article = (SELECT id FROM titles WHERE title = ?) ORDER BY modified DESC`, title)
|
||||
rows, err := this.db.Query(
|
||||
`SELECT articles.id, articles.modified, articles.author FROM articles WHERE article = (SELECT id FROM titles WHERE title = ?) ORDER BY modified DESC`,
|
||||
this.normaliseTitle(title),
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
21
Gopkg.lock
generated
Normal file
21
Gopkg.lock
generated
Normal file
@@ -0,0 +1,21 @@
|
||||
# This file is autogenerated, do not edit; changes may be undone by the next 'dep ensure'.
|
||||
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/mattn/go-sqlite3"
|
||||
packages = ["."]
|
||||
revision = "5160b48509cf5c877bc22c11c373f8c7738cdb38"
|
||||
version = "v1.3.0"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "golang.org/x/net"
|
||||
packages = ["context"]
|
||||
revision = "c73622c77280266305273cb545f54516ced95b93"
|
||||
|
||||
[solve-meta]
|
||||
analyzer-name = "dep"
|
||||
analyzer-version = 1
|
||||
inputs-digest = "a1f2d643f8c1770c92ee1759184a0c7004af5672869db579328d05bb7cfd6bef"
|
||||
solver-name = "gps-cdcl"
|
||||
solver-version = 1
|
||||
26
Gopkg.toml
Normal file
26
Gopkg.toml
Normal file
@@ -0,0 +1,26 @@
|
||||
|
||||
# Gopkg.toml example
|
||||
#
|
||||
# Refer to https://github.com/golang/dep/blob/master/docs/Gopkg.toml.md
|
||||
# for detailed Gopkg.toml documentation.
|
||||
#
|
||||
# required = ["github.com/user/thing/cmd/thing"]
|
||||
# ignored = ["github.com/user/project/pkgX", "bitbucket.org/user/project/pkgA/pkgY"]
|
||||
#
|
||||
# [[constraint]]
|
||||
# name = "github.com/user/project"
|
||||
# version = "1.0.0"
|
||||
#
|
||||
# [[constraint]]
|
||||
# name = "github.com/user/project2"
|
||||
# branch = "dev"
|
||||
# source = "github.com/myfork/project2"
|
||||
#
|
||||
# [[override]]
|
||||
# name = "github.com/x/y"
|
||||
# version = "2.4.0"
|
||||
|
||||
|
||||
[[constraint]]
|
||||
name = "github.com/mattn/go-sqlite3"
|
||||
version = "1.3.0"
|
||||
23
Makefile
23
Makefile
@@ -2,20 +2,24 @@
|
||||
# Makefile for YATWiki3
|
||||
#
|
||||
|
||||
VERSION:=3.0
|
||||
VERSION:=3.2.0
|
||||
|
||||
SOURCES:=Makefile \
|
||||
static \
|
||||
cmd $(wildcard cmd/yatwiki-server/*.go) \
|
||||
Gopkg.lock Gopkg.toml \
|
||||
$(wildcard *.go)
|
||||
|
||||
GOFLAGS := -ldflags='-s -w' -gcflags='-trimpath=$(GOPATH)' -asmflags='-trimpath=$(GOPATH)'
|
||||
GOFLAGS:=-a \
|
||||
-ldflags "-s -w -X code.ivysaur.me/yatwiki.SERVER_HEADER=YATWiki/$(VERSION)" \
|
||||
-gcflags '-trimpath=$(GOPATH)' \
|
||||
-asmflags '-trimpath=$(GOPATH)'
|
||||
|
||||
#
|
||||
# Phony targets
|
||||
#
|
||||
|
||||
.PHONY: all dist clean
|
||||
.PHONY: all dist clean deps
|
||||
|
||||
all: build/linux64/yatwiki-server build/win32/yatwiki-server.exe
|
||||
|
||||
@@ -25,16 +29,21 @@ dist: \
|
||||
_dist/yatwiki-$(VERSION)-src.zip
|
||||
|
||||
clean:
|
||||
if [ -f ./staticResources.go ] ; then rm ./staticResources.go ; fi
|
||||
if [ -d ./build ] ; then rm -r ./build ; fi
|
||||
if [ -f ./yatwiki ] ; then rm ./yatwiki ; fi
|
||||
rm -f ./staticResources.go
|
||||
rm -fr ./build
|
||||
rm -f ./yatwiki
|
||||
|
||||
deps:
|
||||
go get -u github.com/jteeuwen/go-bindata/...
|
||||
go get -u github.com/golang/dep/cmd/dep
|
||||
dep ensure
|
||||
|
||||
#
|
||||
# Generated files
|
||||
#
|
||||
|
||||
staticResources.go: static/ static/*
|
||||
go-bindata -o staticResources.go -prefix static -pkg yatwiki3 static
|
||||
go-bindata -o staticResources.go -prefix static -pkg yatwiki static
|
||||
|
||||
|
||||
#
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package yatwiki3
|
||||
package yatwiki
|
||||
|
||||
import (
|
||||
"time"
|
||||
@@ -13,6 +13,7 @@ type ServerOptions struct {
|
||||
DBFilePath string
|
||||
FaviconFilePath string
|
||||
AllowDBDownload bool
|
||||
TrustXForwardedFor bool // Introduced in 3.0.1 - default false
|
||||
RecentChanges int
|
||||
RecentChangesRSS int
|
||||
GzipCompressionLevel int
|
||||
@@ -20,6 +21,8 @@ type ServerOptions struct {
|
||||
ExternalBaseURL string
|
||||
DeclareRSSLanguage string
|
||||
DeclareRSSEmail string
|
||||
ContentedServer string
|
||||
ContentedBBCodeTag string
|
||||
}
|
||||
|
||||
func DefaultOptions() *ServerOptions {
|
||||
@@ -32,6 +35,7 @@ func DefaultOptions() *ServerOptions {
|
||||
DBFilePath: "wiki.db",
|
||||
FaviconFilePath: "", // no favicon
|
||||
AllowDBDownload: true,
|
||||
TrustXForwardedFor: false,
|
||||
RecentChanges: 20,
|
||||
RecentChangesRSS: 10,
|
||||
GzipCompressionLevel: 9,
|
||||
@@ -39,5 +43,7 @@ func DefaultOptions() *ServerOptions {
|
||||
ExternalBaseURL: "http://127.0.0.1/",
|
||||
DeclareRSSLanguage: "en-GB",
|
||||
DeclareRSSEmail: `nobody@example.com`,
|
||||
ContentedServer: "",
|
||||
ContentedBBCodeTag: "",
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package yatwiki3
|
||||
package yatwiki
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
@@ -14,6 +14,8 @@ import (
|
||||
"time"
|
||||
)
|
||||
|
||||
var SERVER_HEADER string = "YATWiki/0.0.0-devel"
|
||||
|
||||
type WikiServer struct {
|
||||
db *WikiDB
|
||||
opts *ServerOptions
|
||||
@@ -37,7 +39,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 {
|
||||
return nil, err
|
||||
}
|
||||
@@ -68,12 +76,16 @@ func NewWikiServer(opts *ServerOptions) (*WikiServer, error) {
|
||||
return &ws, nil
|
||||
}
|
||||
|
||||
func (this *WikiServer) GetBBCodeRenderer() *BBCodeRenderer {
|
||||
return NewBBCodeRenderer(this.opts.ExpectBaseURL, this.opts.ContentedServer, this.opts.ContentedBBCodeTag)
|
||||
}
|
||||
|
||||
func (this *WikiServer) Close() {
|
||||
this.db.Close()
|
||||
}
|
||||
|
||||
func (this *WikiServer) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Set("Server", "YATWiki3")
|
||||
w.Header().Set("Server", SERVER_HEADER)
|
||||
|
||||
if len(this.bans) > 0 {
|
||||
remoteIP := RemoteAddrToIPAddress(r.RemoteAddr)
|
||||
@@ -104,9 +116,13 @@ func (this *WikiServer) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
w.Write(content)
|
||||
return
|
||||
|
||||
} else if remainingPath == "favicon.ico" && len(this.opts.FaviconFilePath) > 0 {
|
||||
w.Header().Set("Content-Type", "image/x-icon")
|
||||
http.ServeFile(w, r, this.opts.FaviconFilePath)
|
||||
} else if remainingPath == "favicon.ico" {
|
||||
if len(this.opts.FaviconFilePath) > 0 {
|
||||
w.Header().Set("Content-Type", "image/x-icon")
|
||||
http.ServeFile(w, r, this.opts.FaviconFilePath)
|
||||
} else {
|
||||
http.Error(w, "Not found", 404)
|
||||
}
|
||||
return
|
||||
|
||||
} else if remainingPath == "download-database" {
|
||||
@@ -128,7 +144,7 @@ func (this *WikiServer) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
|
||||
} 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
|
||||
|
||||
} else if remainingPath == "random" {
|
||||
@@ -139,11 +155,11 @@ func (this *WikiServer) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
} else if strings.HasPrefix(remainingPath, "view/") {
|
||||
articleTitle, err := url.QueryUnescape(remainingPath[len("view/"):])
|
||||
articleTitle, err := url.PathUnescape(remainingPath[len("view/"):])
|
||||
if err != nil {
|
||||
this.serveErrorMessage(w, err)
|
||||
return
|
||||
@@ -152,7 +168,7 @@ func (this *WikiServer) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
|
||||
} else if strings.HasPrefix(remainingPath, "modify/") {
|
||||
articleTitle, err := url.QueryUnescape(remainingPath[len("modify/"):])
|
||||
articleTitle, err := url.PathUnescape(remainingPath[len("modify/"):])
|
||||
if err != nil {
|
||||
this.serveErrorMessage(w, err)
|
||||
return
|
||||
@@ -161,7 +177,7 @@ func (this *WikiServer) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
|
||||
} else if strings.HasPrefix(remainingPath, "history/") {
|
||||
articleTitle, err := url.QueryUnescape(remainingPath[len("history/"):])
|
||||
articleTitle, err := url.PathUnescape(remainingPath[len("history/"):])
|
||||
if err != nil {
|
||||
this.serveErrorMessage(w, err)
|
||||
return
|
||||
@@ -255,13 +271,13 @@ func (this *WikiServer) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
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 {
|
||||
this.serveErrorMessage(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
this.serveRedirect(w, this.opts.ExpectBaseURL+`view/`+url.QueryEscape(title))
|
||||
this.serveRedirect(w, this.opts.ExpectBaseURL+`view/`+url.PathEscape(title))
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
A semi-anonymous wiki for use in trusted environments.
|
||||
|
||||
As of the 20150901 release, a desktop version is available for Windows (based on PHPDesktop).
|
||||
For the 20150901 release, a desktop version is available for Windows (based on PHPDesktop).
|
||||
|
||||
As of the 3.0 release, YATWiki is now a standalone server instead of a PHP script.
|
||||
|
||||
@@ -14,6 +14,7 @@ As of the 3.0 release, YATWiki is now a standalone server instead of a PHP scrip
|
||||
- IP-based ban system
|
||||
- Article index, random article, download database backup
|
||||
- Source code highlighting (thanks [url=https://github.com/isagalaev/highlight.js]highlight.js[/url])
|
||||
- Optional integration with `contented` for file/image uploads
|
||||
|
||||
Written in Golang, PHP
|
||||
|
||||
@@ -28,9 +29,45 @@ You can start YATWiki by running the binary. A default configuration file and da
|
||||
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=
|
||||
|
||||
2017-07-11 v3.0
|
||||
2017-11-18 3.2.0
|
||||
- Feature: Add new ContentedBBCodeTag option to choose a BBCode tag for mini thumbnails (requires `contented` >= 1.2.0)
|
||||
- Feature: Replace menu image with SVG, for high-DPI screens
|
||||
- Feature: Hover over timestamps to display in more detail
|
||||
- Feature: Link to diff pages directly from the Recent Changes page
|
||||
- Fix some cosmetic issues with the Recent Changes page
|
||||
|
||||
2017-10-29 3.1.3
|
||||
- Fix one more case of article title normalisation
|
||||
|
||||
2017-10-29 3.1.2
|
||||
- Lock dependency versions
|
||||
- Enhancement: Advertise build number in Server headers
|
||||
- Fix a regression in 3.x series with not normalising article titles
|
||||
- Fix server response if favicon is not configured
|
||||
|
||||
2017-10-15 3.1.1
|
||||
- Update `contented` integration (requires `contented` >= 1.1.0)
|
||||
|
||||
2017-10-08 3.1.0
|
||||
- Feature: Support content upload to a `contented` server
|
||||
|
||||
2017-08-11 3.0.2
|
||||
- Fix an issue with XSS prevention for web browsers other than Chrome
|
||||
|
||||
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.
|
||||
- Enhancement: Standalone binary server
|
||||
- Enhancement: No longer requires cookies for error messages
|
||||
@@ -43,11 +80,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 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
|
||||
- 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
|
||||
- Fix an issue with noncompliant HTML when comparing diffs
|
||||
|
||||
|
||||
24
bbcode.go
24
bbcode.go
@@ -1,7 +1,8 @@
|
||||
package yatwiki3
|
||||
package yatwiki
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"html"
|
||||
"html/template"
|
||||
"net/url"
|
||||
"regexp"
|
||||
@@ -13,13 +14,17 @@ type BBCodeRenderer struct {
|
||||
baseUrl string
|
||||
CodePresent bool
|
||||
DynamicContentWarning string
|
||||
ContentedURL string
|
||||
ContentedTag string
|
||||
}
|
||||
|
||||
func NewBBCodeRenderer(baseUrl string) *BBCodeRenderer {
|
||||
func NewBBCodeRenderer(baseUrl, ContentedURL, ContentedTag string) *BBCodeRenderer {
|
||||
return &BBCodeRenderer{
|
||||
baseUrl: baseUrl,
|
||||
CodePresent: false,
|
||||
DynamicContentWarning: `⚠ run dynamic content`,
|
||||
ContentedURL: ContentedURL,
|
||||
ContentedTag: ContentedTag,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -45,10 +50,10 @@ func (this *BBCodeRenderer) bbcode(data string) string {
|
||||
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)\[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 {
|
||||
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\]`),
|
||||
@@ -61,6 +66,15 @@ func (this *BBCodeRenderer) bbcode(data string) string {
|
||||
}},
|
||||
}
|
||||
|
||||
if len(this.ContentedTag) > 0 {
|
||||
s_to_r = append(s_to_r,
|
||||
pregReplaceRule{regexp.MustCompile(`(?si)\[` + regexp.QuoteMeta(this.ContentedTag) + `\](.*?)\[/` + regexp.QuoteMeta(this.ContentedTag) + `\]`),
|
||||
`<a href="` + html.EscapeString(this.ContentedURL) + `p/${1}"><img class="imgur" alt="" src="` + html.EscapeString(this.ContentedURL) + `thumb/s/${1}" ></a>`,
|
||||
nil,
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
for _, prr := range s_to_r {
|
||||
|
||||
for prr.match.MatchString(data) { // repeat until all recursive replacements are consumed
|
||||
@@ -177,7 +191,7 @@ func (this *BBCodeRenderer) displayfmt(s string) string {
|
||||
}
|
||||
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>`
|
||||
hpos = epos + 7
|
||||
|
||||
@@ -8,7 +8,7 @@ import (
|
||||
"net/http"
|
||||
"os"
|
||||
|
||||
"code.ivysaur.me/yatwiki3"
|
||||
"code.ivysaur.me/yatwiki"
|
||||
)
|
||||
|
||||
func main() {
|
||||
@@ -17,13 +17,13 @@ func main() {
|
||||
configPath := flag.String("config", "config.json", "Configuration file")
|
||||
flag.Parse()
|
||||
|
||||
opts := yatwiki3.ServerOptions{}
|
||||
opts := yatwiki.ServerOptions{}
|
||||
|
||||
cfg, err := ioutil.ReadFile(*configPath)
|
||||
|
||||
if err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
opts = *yatwiki3.DefaultOptions()
|
||||
opts = *yatwiki.DefaultOptions()
|
||||
if cfg, err := json.MarshalIndent(opts, "", "\t"); err == nil {
|
||||
err := ioutil.WriteFile(*configPath, cfg, 0644)
|
||||
if err != nil {
|
||||
@@ -43,7 +43,7 @@ func main() {
|
||||
}
|
||||
}
|
||||
|
||||
ws, err := yatwiki3.NewWikiServer(&opts)
|
||||
ws, err := yatwiki.NewWikiServer(&opts)
|
||||
if err != nil {
|
||||
fmt.Fprintln(os.Stderr, err.Error())
|
||||
os.Exit(1)
|
||||
|
||||
@@ -4,7 +4,7 @@ import (
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"code.ivysaur.me/yatwiki3/diff"
|
||||
"code.ivysaur.me/yatwiki/diff"
|
||||
)
|
||||
|
||||
func TestDiff(t *testing.T) {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package yatwiki3
|
||||
package yatwiki
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package yatwiki3
|
||||
package yatwiki
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
@@ -17,7 +17,9 @@ type pageTemplateOptions struct {
|
||||
LoadCodeResources bool
|
||||
DefaultPage string
|
||||
AllowDownload bool
|
||||
SessionMessage template.HTML
|
||||
SessionMessage string
|
||||
PageNotExistsError bool
|
||||
PageNotExistsTarget string
|
||||
}
|
||||
|
||||
func DefaultPageTemplateOptions(opts *ServerOptions) *pageTemplateOptions {
|
||||
@@ -85,26 +87,56 @@ function els(e,s){ // no js exec in innerHTML
|
||||
</head>
|
||||
<body>
|
||||
<div class="header">
|
||||
<a href="{{.BaseURL}}view/{{.DefaultPage | urlquery}}" 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="{{.BaseURL}}modify/{{.NewArticleTitle | urlquery}}" title="New Page"><div class="sprite nw"></div></a>
|
||||
<a href="{{.BaseURL}}view/{{.DefaultPage | pathcomponent}}" title="Home"><div class="sprite">
|
||||
<svg viewBox="0 0 24 24">
|
||||
<path d="M10,20V14H14V20H19V12H22L12,3L2,12H5V20H10Z" />
|
||||
</svg>
|
||||
</div></a>
|
||||
<a href="javascript:;" onclick="tid('spm');tid('tr1');tid('tr2');" title="Menu"><div class="sprite">
|
||||
<svg viewBox="0 0 24 24">
|
||||
<path d="M12,17.27L18.18,21L16.54,13.97L22,9.24L14.81,8.62L12,2L9.19,8.62L2,9.24L7.45,13.97L5.82,21L12,17.27Z" />
|
||||
</svg>
|
||||
</div></a>
|
||||
<a href="{{.BaseURL}}modify/{{.NewArticleTitle | pathcomponent}}" title="New Page"><div class="sprite">
|
||||
<svg viewBox="0 0 24 24">
|
||||
<path d="M13,9V3.5L18.5,9M6,2C4.89,2 4,2.89 4,4V20A2,2 0 0,0 6,22H18A2,2 0 0,0 20,20V8L14,2H6Z" />
|
||||
</svg>
|
||||
</div></a>
|
||||
{{if .CurrentPageIsArticle }}
|
||||
<div class="sep"></div>
|
||||
<a href="{{.BaseURL}}history/{{.CurrentPageName | urlquery}}" 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}}history/{{.CurrentPageName | pathcomponent}}" title="Page History"><div class="sprite">
|
||||
<svg viewBox="0 0 24 24">
|
||||
<path d="M15,13H16.5V15.82L18.94,17.23L18.19,18.53L15,16.69V13M19,8H5V19H9.67C9.24,18.09 9,17.07 9,16A7,7 0 0,1 16,9C17.07,9 18.09,9.24 19,9.67V8M5,21C3.89,21 3,20.1 3,19V5C3,3.89 3.89,3 5,3H6V1H8V3H16V1H18V3H19A2,2 0 0,1 21,5V11.1C22.24,12.36 23,14.09 23,16A7,7 0 0,1 16,23C14.09,23 12.36,22.24 11.1,21H5M16,11.15A4.85,4.85 0 0,0 11.15,16C11.15,18.68 13.32,20.85 16,20.85A4.85,4.85 0 0,0 20.85,16C20.85,13.32 18.68,11.15 16,11.15Z" />
|
||||
</svg>
|
||||
</div></a>
|
||||
<a href="{{.BaseURL}}modify/{{.CurrentPageName | pathcomponent}}" title="Modify Page"><div class="sprite">
|
||||
<svg viewBox="0 0 24 24">
|
||||
<path d="M20.71,7.04C21.1,6.65 21.1,6 20.71,5.63L18.37,3.29C18,2.9 17.35,2.9 16.96,3.29L15.12,5.12L18.87,8.87M3,17.25V21H6.75L17.81,9.93L14.06,6.18L3,17.25Z" />
|
||||
</svg>
|
||||
</div></a>
|
||||
{{end}}
|
||||
</div>
|
||||
<div id="tr1" style="display:none;"></div>
|
||||
<div id="tr2" style="display:none;"></div>
|
||||
<div class="ddmenu" id="spm" style="display:none;">
|
||||
<a href="{{.BaseURL}}recent/1"><div class="sprite no"></div> Recent Changes</a>
|
||||
<a href="{{.BaseURL}}random"><div class="sprite rn"></div> Random Page</a>
|
||||
<a href="{{.BaseURL}}index"><div class="sprite no"></div> Article Index</a>
|
||||
<a href="{{.BaseURL}}recent/1"><div class="sprite"></div> Recent Changes</a>
|
||||
<a href="{{.BaseURL}}random"><div class="sprite">
|
||||
<svg viewBox="0 0 24 24">
|
||||
<path d="M12,18A6,6 0 0,1 6,12C6,11 6.25,10.03 6.7,9.2L5.24,7.74C4.46,8.97 4,10.43 4,12A8,8 0 0,0 12,20V23L16,19L12,15M12,4V1L8,5L12,9V6A6,6 0 0,1 18,12C18,13 17.75,13.97 17.3,14.8L18.76,16.26C19.54,15.03 20,13.57 20,12A8,8 0 0,0 12,4Z" />
|
||||
</svg>
|
||||
</div> Random Page</a>
|
||||
<a href="{{.BaseURL}}index"><div class="sprite"></div> Article Index</a>
|
||||
{{if .AllowDownload}}
|
||||
<a href="{{.BaseURL}}download-database" download><div class="sprite no"></div> Download DB backup</a>
|
||||
<a href="{{.BaseURL}}download-database" download><div class="sprite"></div> Download DB backup</a>
|
||||
{{end}}
|
||||
</div>
|
||||
<div class="content">
|
||||
{{if .PageNotExistsError}}
|
||||
<div class="info">
|
||||
No such article exists.
|
||||
<a href="{{.BaseURL}}modify/{{.PageNotExistsTarget | pathcomponent}}">Click here</a> to create it.
|
||||
</div>
|
||||
{{end}}
|
||||
{{if len .SessionMessage}}
|
||||
<div class="info">{{.SessionMessage}}</div>
|
||||
{{end}}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package yatwiki3
|
||||
package yatwiki
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
@@ -25,12 +25,12 @@ func (this *WikiServer) routeArchive(w http.ResponseWriter, r *http.Request, rev
|
||||
pto.CurrentPageName = a.Title
|
||||
pto.CurrentPageIsArticle = true
|
||||
|
||||
bcr := NewBBCodeRenderer(this.opts.ExpectBaseURL)
|
||||
bcr := this.GetBBCodeRenderer()
|
||||
pto.Content = template.HTML(
|
||||
`<div class="info">`+
|
||||
`You are viewing specific revision of this page, last modified `+
|
||||
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>`,
|
||||
) + bcr.RenderHTML(string(a.Body))
|
||||
pto.LoadCodeResources = bcr.CodePresent
|
||||
|
||||
4
rDiff.go
4
rDiff.go
@@ -1,4 +1,4 @@
|
||||
package yatwiki3
|
||||
package yatwiki
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
@@ -8,7 +8,7 @@ import (
|
||||
"html/template"
|
||||
"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) {
|
||||
|
||||
35
rErrors.go
35
rErrors.go
@@ -1,4 +1,4 @@
|
||||
package yatwiki3
|
||||
package yatwiki
|
||||
|
||||
import (
|
||||
"html/template"
|
||||
@@ -8,12 +8,8 @@ import (
|
||||
"time"
|
||||
)
|
||||
|
||||
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.`)
|
||||
}
|
||||
|
||||
func (this *WikiServer) serveErrorMessage(w http.ResponseWriter, message error) {
|
||||
this.serveErrorHTMLMessage(w, template.HTML(template.HTMLEscapeString(message.Error())))
|
||||
func (this *WikiServer) serveErrorMessage(w http.ResponseWriter, err error) {
|
||||
this.serveErrorText(w, err.Error())
|
||||
}
|
||||
|
||||
func (this *WikiServer) serveInternalError(w http.ResponseWriter, r *http.Request, e error) {
|
||||
@@ -21,8 +17,12 @@ func (this *WikiServer) serveInternalError(w http.ResponseWriter, r *http.Reques
|
||||
http.Error(w, "An internal error occurred. Please ask an administrator to check the log file.", 500)
|
||||
}
|
||||
|
||||
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)))
|
||||
func (this *WikiServer) serveErrorText(w http.ResponseWriter, msg string) {
|
||||
this.serveRedirect(w, this.opts.ExpectBaseURL+"view/"+url.PathEscape(this.opts.DefaultPage)+"?error="+url.QueryEscape(msg))
|
||||
}
|
||||
|
||||
func (this *WikiServer) serveNoSuchArticle(w http.ResponseWriter, lookingFor string) {
|
||||
this.serveRedirect(w, this.opts.ExpectBaseURL+"view/"+url.PathEscape(this.opts.DefaultPage)+"?notfound="+url.QueryEscape(lookingFor))
|
||||
}
|
||||
|
||||
func (this *WikiServer) serveRedirect(w http.ResponseWriter, location string) {
|
||||
@@ -32,7 +32,14 @@ func (this *WikiServer) serveRedirect(w http.ResponseWriter, location string) {
|
||||
|
||||
func (this *WikiServer) servePageResponse(w http.ResponseWriter, r *http.Request, pto *pageTemplateOptions) {
|
||||
w.WriteHeader(200)
|
||||
pto.SessionMessage = template.HTML(r.URL.Query().Get("error")) // FIXME reflected XSS (although Chrome automatically blocks it..)
|
||||
|
||||
if noSuchArticleTarget, ok := r.URL.Query()["notfound"]; ok {
|
||||
pto.PageNotExistsError = true
|
||||
pto.PageNotExistsTarget = noSuchArticleTarget[0]
|
||||
|
||||
} else {
|
||||
pto.SessionMessage = r.URL.Query().Get("error")
|
||||
}
|
||||
|
||||
err := this.pageTmp.Execute(w, pto)
|
||||
if err != nil {
|
||||
@@ -40,11 +47,13 @@ func (this *WikiServer) servePageResponse(w http.ResponseWriter, r *http.Request
|
||||
}
|
||||
}
|
||||
|
||||
func (this *WikiServer) formatTimestamp(m int64) string {
|
||||
func (this *WikiServer) formatTimestamp(m int64) template.HTML {
|
||||
// TODO add a more detailed timestamp on hover
|
||||
return template.HTMLEscapeString(time.Unix(m, 0).In(this.loc).Format(this.opts.DateFormat))
|
||||
dt := time.Unix(m, 0).In(this.loc)
|
||||
|
||||
return template.HTML(`<span title="` + template.HTMLEscapeString(dt.Format(time.RFC3339)) + `">` + template.HTMLEscapeString(dt.Format(this.opts.DateFormat)) + `</span>`)
|
||||
}
|
||||
|
||||
func (this *WikiServer) viewLink(articleTitle string) template.HTML {
|
||||
return template.HTML(`"<a href="` + template.HTMLEscapeString(this.opts.ExpectBaseURL+`view/`+url.QueryEscape(articleTitle)) + `">` + template.HTMLEscapeString(articleTitle) + `</a>"`)
|
||||
return template.HTML(`"<a href="` + template.HTMLEscapeString(this.opts.ExpectBaseURL+`view/`+url.PathEscape(articleTitle)) + `">` + template.HTMLEscapeString(articleTitle) + `</a>"`)
|
||||
}
|
||||
|
||||
@@ -1,13 +1,15 @@
|
||||
package yatwiki3
|
||||
package yatwiki
|
||||
|
||||
import (
|
||||
"html/template"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
func (this *WikiServer) routeFormatting(w http.ResponseWriter, r *http.Request) {
|
||||
pto := DefaultPageTemplateOptions(this.opts)
|
||||
pto.CurrentPageName = "Formatting help"
|
||||
pto.Content = `
|
||||
|
||||
content := `
|
||||
<h2>Formatting help</h2><br><br>
|
||||
<ul>
|
||||
<li>[h]header[/h]</li>
|
||||
@@ -21,9 +23,20 @@ func (this *WikiServer) routeFormatting(w http.ResponseWriter, r *http.Request)
|
||||
<li>[article=page name]title[/article] or [rev=id]title[/rev]</li>
|
||||
<li>[img]image-url[/img]</li>
|
||||
<li>[imgur]asdf.jpg[/imgur]</li>
|
||||
`
|
||||
if len(this.opts.ContentedBBCodeTag) > 0 {
|
||||
content += `
|
||||
<li>[` + this.opts.ContentedBBCodeTag + `]abc[/` + this.opts.ContentedBBCodeTag + `]</li>
|
||||
`
|
||||
}
|
||||
|
||||
content += `
|
||||
<li>[code]fixed width[/code]</li>
|
||||
<li>[section=header]content[/section]</li>
|
||||
<li>[html]raw html[/html]</li>
|
||||
</ul>`
|
||||
|
||||
pto.Content = template.HTML(content)
|
||||
|
||||
this.servePageResponse(w, r, pto)
|
||||
}
|
||||
|
||||
13
rHistory.go
13
rHistory.go
@@ -1,4 +1,4 @@
|
||||
package yatwiki3
|
||||
package yatwiki
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
@@ -12,7 +12,7 @@ func (this *WikiServer) routeHistory(w http.ResponseWriter, r *http.Request, art
|
||||
revs, err := this.db.GetRevisionHistory(articleTitle)
|
||||
if err != nil {
|
||||
if err == sql.ErrNoRows {
|
||||
this.serveErrorHTMLMessage(w, this.noSuchArticleError(articleTitle))
|
||||
this.serveNoSuchArticle(w, articleTitle)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -20,12 +20,17 @@ func (this *WikiServer) routeHistory(w http.ResponseWriter, r *http.Request, art
|
||||
return
|
||||
}
|
||||
|
||||
if len(revs) == 0 {
|
||||
this.serveNoSuchArticle(w, articleTitle)
|
||||
return
|
||||
}
|
||||
|
||||
pto := DefaultPageTemplateOptions(this.opts)
|
||||
pto.CurrentPageName = articleTitle
|
||||
pto.CurrentPageIsArticle = true
|
||||
|
||||
content := `<h2>Page History</h2><br>` +
|
||||
`<em>There have been ` + fmt.Sprintf("%d", len(revs)) + ` edits to the page "<a href="` + template.HTMLEscapeString(this.opts.ExpectBaseURL+`view/`+url.QueryEscape(articleTitle)) + `">` + template.HTMLEscapeString(articleTitle) + `</a>".</em>` +
|
||||
`<em>There have been ` + fmt.Sprintf("%d", len(revs)) + ` edits to the page "<a href="` + template.HTMLEscapeString(this.opts.ExpectBaseURL+`view/`+url.PathEscape(articleTitle)) + `">` + template.HTMLEscapeString(articleTitle) + `</a>".</em>` +
|
||||
`<br><br>` +
|
||||
`<form method="GET" action="` + template.HTMLEscapeString(this.opts.ExpectBaseURL+`diff`) + `">` +
|
||||
`<table>`
|
||||
@@ -34,7 +39,7 @@ func (this *WikiServer) routeHistory(w http.ResponseWriter, r *http.Request, art
|
||||
for _, rev := range revs {
|
||||
revIdStr := fmt.Sprintf("%d", rev.ID)
|
||||
content += `<tr>` +
|
||||
`<td><a href="` + template.HTMLEscapeString(this.opts.ExpectBaseURL+`archive/`+revIdStr) + `">` + this.formatTimestamp(rev.Modified) + `</a></td>` +
|
||||
`<td><a href="` + template.HTMLEscapeString(this.opts.ExpectBaseURL+`archive/`+revIdStr) + `">` + string(this.formatTimestamp(rev.Modified)) + `</a></td>` +
|
||||
`<td>` + template.HTMLEscapeString(rev.Author) + `</td>` +
|
||||
`<td><input type="radio" name="t" value="` + revIdStr + `"> <input type="radio" name="f" value="` + revIdStr + `"></td>` +
|
||||
`</tr>`
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package yatwiki3
|
||||
package yatwiki
|
||||
|
||||
import (
|
||||
"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))
|
||||
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>`
|
||||
|
||||
|
||||
22
rModify.go
22
rModify.go
@@ -1,4 +1,4 @@
|
||||
package yatwiki3
|
||||
package yatwiki
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
@@ -32,7 +32,7 @@ func (this *WikiServer) routeModify(w http.ResponseWriter, r *http.Request, arti
|
||||
pageTitleHTML = `Creating new article`
|
||||
baseRev = 0
|
||||
} else {
|
||||
pageTitleHTML = `Editing article "<a href="` + template.HTMLEscapeString(this.opts.ExpectBaseURL+`view/`+url.QueryEscape(articleTitle)) + `">` + template.HTMLEscapeString(articleTitle) + `</a>"`
|
||||
pageTitleHTML = `Editing article "<a href="` + template.HTMLEscapeString(this.opts.ExpectBaseURL+`view/`+url.PathEscape(articleTitle)) + `">` + template.HTMLEscapeString(articleTitle) + `</a>"`
|
||||
baseRev = a.ID
|
||||
existingBody = string(a.Body)
|
||||
}
|
||||
@@ -49,6 +49,24 @@ func (this *WikiServer) routeModify(w http.ResponseWriter, r *http.Request, arti
|
||||
</label>
|
||||
<input type="submit" value="Save »">
|
||||
| <a href="` + template.HTMLEscapeString(this.opts.ExpectBaseURL+`formatting`) + `" target="_blank">formatting help</a>
|
||||
`
|
||||
if len(this.opts.ContentedServer) > 0 {
|
||||
content += `
|
||||
<script type="text/javascript" src="` + this.opts.ContentedServer + `sdk.js"></script>
|
||||
| <a href="javascript:;" id="open-contented-uploader">upload...</a>
|
||||
<script type="text/javascript">
|
||||
document.getElementById("open-contented-uploader").addEventListener("click", function() {
|
||||
contented.init("#contentctr", function(items) {
|
||||
for (var i = 0; i < items.length; ++i) {
|
||||
$("#contentctr textarea").append(" " + contented.getPreviewURL(items[i]) + " ");
|
||||
}
|
||||
});
|
||||
});
|
||||
</script>
|
||||
`
|
||||
}
|
||||
|
||||
content += `
|
||||
</div>
|
||||
<div id="contentctr"><textarea name="content">` + template.HTMLEscapeString(existingBody) + `</textarea></div>
|
||||
</form>
|
||||
|
||||
4
rRSS.go
4
rRSS.go
@@ -1,4 +1,4 @@
|
||||
package yatwiki3
|
||||
package yatwiki
|
||||
|
||||
import (
|
||||
"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>
|
||||
<pubDate>` + template.HTMLEscapeString(time.Unix(a.Modified, 0).In(this.loc).Format(time.RFC1123Z)) + `</pubDate>
|
||||
<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>
|
||||
|
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package yatwiki3
|
||||
package yatwiki
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package yatwiki3
|
||||
package yatwiki
|
||||
|
||||
import (
|
||||
"errors"
|
||||
@@ -37,25 +37,41 @@ func (this *WikiServer) routeRecentChanges(w http.ResponseWriter, r *http.Reques
|
||||
pto.CurrentPageName = "Recent Changes"
|
||||
|
||||
content := `<h2>Recent Changes</h2><br>` +
|
||||
`<em>Showing up to ` + fmt.Sprintf("%d", this.opts.RecentChanges) + ` changes.</em><br>` +
|
||||
`<table>`
|
||||
`<em>Showing up to ` + fmt.Sprintf("%d", this.opts.RecentChanges) + ` changes.</em><br><br>` +
|
||||
`<div style="display:inline-block;">` +
|
||||
`<table class="ti">` +
|
||||
`<tr><td>Page</td><td>Actions</td><td>Time</td><td>Author</td></tr>`
|
||||
for _, rev := range recents {
|
||||
|
||||
diffHtml := ""
|
||||
diffRev, err := this.db.GetNextOldestRevision(int(rev.ID))
|
||||
if err != nil {
|
||||
diffHtml = `[new]`
|
||||
} else {
|
||||
diffHtml = `<a href="` + template.HTMLEscapeString(this.opts.ExpectBaseURL+`diff/`+fmt.Sprintf("%d/%d", diffRev, rev.ID)) + `">diff</a>`
|
||||
}
|
||||
|
||||
content += `<tr>` +
|
||||
`<td><a href="` + template.HTMLEscapeString(this.opts.ExpectBaseURL+`view/`+url.QueryEscape(rev.Title)) + `">` + template.HTMLEscapeString(rev.Title) + `</a>` +
|
||||
` [<a href="` + template.HTMLEscapeString(this.opts.ExpectBaseURL+`archive/`+fmt.Sprintf("%d", rev.ID)) + `">a</a>]` +
|
||||
`<td><a href="` + template.HTMLEscapeString(this.opts.ExpectBaseURL+`view/`+url.PathEscape(rev.Title)) + `">` + template.HTMLEscapeString(rev.Title) + `</a></td>` +
|
||||
`<td>` +
|
||||
`<a href="` + template.HTMLEscapeString(this.opts.ExpectBaseURL+`archive/`+fmt.Sprintf("%d", rev.ID)) + `">rev</a> ` +
|
||||
diffHtml +
|
||||
`</td>` +
|
||||
`<td>` + this.formatTimestamp(rev.Modified) + ` by ` + template.HTMLEscapeString(rev.Author) + `</td>` +
|
||||
`</td>` +
|
||||
`<td>` + string(this.formatTimestamp(rev.Modified)) + `</td>` +
|
||||
`<td>` + template.HTMLEscapeString(rev.Author) + `</td>` +
|
||||
`</tr>`
|
||||
}
|
||||
content += `<tr><td>`
|
||||
content += `</table>`
|
||||
|
||||
if pageNum > 1 {
|
||||
content += `<a href="` + template.HTMLEscapeString(this.opts.ExpectBaseURL+`recent/`+fmt.Sprintf("%d", pageNum-1)) + `">« Newer</a>`
|
||||
content += `<span style="float:left;"><a href="` + template.HTMLEscapeString(this.opts.ExpectBaseURL+`recent/`+fmt.Sprintf("%d", pageNum-1)) + `">« Newer</a></span>`
|
||||
}
|
||||
content += `</td><td></td><td style="text-align:right;">`
|
||||
|
||||
if pageNum < maxPage {
|
||||
content += `<a href="` + template.HTMLEscapeString(this.opts.ExpectBaseURL+`recent/`+fmt.Sprintf("%d", pageNum+1)) + `">Older »</a>`
|
||||
content += `<span style="float:right;"><a href="` + template.HTMLEscapeString(this.opts.ExpectBaseURL+`recent/`+fmt.Sprintf("%d", pageNum+1)) + `">Older »</a></span>`
|
||||
}
|
||||
content += `</td></tr></table>`
|
||||
content += `</div>`
|
||||
|
||||
pto.Content = template.HTML(content)
|
||||
this.servePageResponse(w, r, pto)
|
||||
|
||||
8
rView.go
8
rView.go
@@ -1,4 +1,4 @@
|
||||
package yatwiki3
|
||||
package yatwiki
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
@@ -15,11 +15,11 @@ func (this *WikiServer) routeView(w http.ResponseWriter, r *http.Request, articl
|
||||
// If this was an old link, it might not be present.
|
||||
// Redirect if possible
|
||||
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
|
||||
}
|
||||
|
||||
this.serveErrorHTMLMessage(w, this.noSuchArticleError(articleTitle))
|
||||
this.serveNoSuchArticle(w, articleTitle)
|
||||
return
|
||||
}
|
||||
this.serveErrorMessage(w, err)
|
||||
@@ -30,7 +30,7 @@ func (this *WikiServer) routeView(w http.ResponseWriter, r *http.Request, articl
|
||||
pto.CurrentPageName = articleTitle
|
||||
pto.CurrentPageIsArticle = true
|
||||
|
||||
bcr := NewBBCodeRenderer(this.opts.ExpectBaseURL)
|
||||
bcr := this.GetBBCodeRenderer()
|
||||
pto.Content = bcr.RenderHTML(string(a.Body))
|
||||
pto.LoadCodeResources = bcr.CodePresent
|
||||
|
||||
|
||||
@@ -112,16 +112,6 @@ fieldset legend {
|
||||
display:inline-block;
|
||||
width:16px;height:16px;
|
||||
vertical-align:text-bottom;
|
||||
background-repeat:no-repeat;
|
||||
background-image: url(data:image/png;base64,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);
|
||||
}
|
||||
.sprite.hm { background-position:0px 0px;}
|
||||
.sprite.hs { background-position:0px -16px;}
|
||||
.sprite.sp { background-position:0px -32px;}
|
||||
.sprite.nw { background-position:-16px 0px;}
|
||||
.sprite.ed { background-position:-16px -16px;}
|
||||
.sprite.rn { background-position:-16px -32px;}
|
||||
.sprite.no {
|
||||
background:none;
|
||||
}
|
||||
.sep {
|
||||
|
||||
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user