implement recent changes, article history

This commit is contained in:
mappu 2017-07-09 18:05:03 +12:00
parent 619dc78bac
commit c39d563dc6
7 changed files with 183 additions and 14 deletions

55
DB.go
View File

@ -78,6 +78,7 @@ type Article struct {
Modified int64
Body []byte
Author string
Title string
}
func (this *Article) FillModifiedTimestamp() {
@ -89,17 +90,12 @@ func (this *Article) FillAuthor(r *http.Request) {
this.Author = r.RemoteAddr + "-" + hex.EncodeToString(userAgentHash[:])[:6]
}
type ArticleWithTitle struct {
Article
Title string
}
func (this *WikiDB) GetArticleById(articleId int) (*Article, error) {
row := this.db.QueryRow(`SELECT articles.* FROM articles WHERE id = ?`, articleId)
return this.parseArticle(row)
}
func (this *WikiDB) GetRevision(revId int) (*ArticleWithTitle, error) {
func (this *WikiDB) GetRevision(revId int) (*Article, error) {
row := this.db.QueryRow(`SELECT articles.*, titles.title FROM articles JOIN titles ON articles.article=titles.id WHERE articles.id = ?`, revId)
return this.parseArticleWithTitle(row)
}
@ -109,6 +105,47 @@ func (this *WikiDB) GetLatestVersion(title string) (*Article, error) {
return this.parseArticle(row)
}
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)
if err != nil {
return nil, err
}
defer rows.Close()
ret := make([]Article, 0)
for rows.Next() {
a := Article{}
err := rows.Scan(&a.ID, &a.Modified, &a.Author)
if err != nil {
return nil, err
}
ret = append(ret, a)
}
return ret, nil
}
func (this *WikiDB) GetRecentChanges(offset int, limit int) ([]Article, error) {
rows, err := this.db.Query(
`SELECT articles.id, articles.modified, articles.author, titles.title FROM articles JOIN titles ON articles.article=titles.id ORDER BY modified DESC ` +
fmt.Sprintf(`LIMIT %d OFFSET %d`, limit, offset),
)
if err != nil {
return nil, err
}
defer rows.Close()
ret := make([]Article, 0, limit)
for rows.Next() {
a := Article{}
err := rows.Scan(&a.ID, &a.Modified, &a.Author, &a.Title)
if err != nil {
return nil, err
}
ret = append(ret, a)
}
return ret, nil
}
func (this *WikiDB) TotalRevisions() (int64, error) {
row := this.db.QueryRow(`SELECT COUNT(*) c FROM articles`)
var ret int64
@ -125,8 +162,8 @@ func (this *WikiDB) ListTitles() ([]string, error) {
if err != nil {
return nil, err
}
defer rows.Close()
ret := make([]string, 0)
for rows.Next() {
var title string
@ -173,8 +210,8 @@ func (this *WikiDB) parseArticle(row *sql.Row) (*Article, error) {
return &a, nil
}
func (this *WikiDB) parseArticleWithTitle(row *sql.Row) (*ArticleWithTitle, error) {
a := ArticleWithTitle{}
func (this *WikiDB) parseArticleWithTitle(row *sql.Row) (*Article, error) {
a := Article{}
var gzBody []byte
err := row.Scan(&a.ID, &a.TitleID, &a.Modified, &gzBody, &a.Author, &a.Title)
if err != nil {

View File

@ -1,10 +1,6 @@
Save changes
Article history
Recent history
Diffs
RSS

View File

@ -93,6 +93,15 @@ func (this *WikiServer) ServeHTTP(w http.ResponseWriter, r *http.Request) {
this.routeModify(w, r, articleTitle)
return
} else if strings.HasPrefix(r.URL.Path, this.opts.ExpectBaseURL+"history/") {
articleTitle, err := url.QueryUnescape(r.URL.Path[len(this.opts.ExpectBaseURL+"history/"):])
if err != nil {
this.serveErrorMessage(w, err)
return
}
this.routeHistory(w, r, articleTitle)
return
} else if strings.HasPrefix(r.URL.Path, this.opts.ExpectBaseURL+"raw/") {
revId, err := strconv.Atoi(r.URL.Path[len(this.opts.ExpectBaseURL+"raw/"):])
if err != nil {
@ -113,6 +122,16 @@ func (this *WikiServer) ServeHTTP(w http.ResponseWriter, r *http.Request) {
this.routeArchive(w, r, revId)
return
} else if strings.HasPrefix(r.URL.Path, this.opts.ExpectBaseURL+"recent/") {
pageNum, err := strconv.Atoi(r.URL.Path[len(this.opts.ExpectBaseURL+"recent/"):])
if err != nil {
this.serveErrorMessage(w, err)
return
}
this.routeRecentChanges(w, r, pageNum)
return
}
}

View File

@ -97,7 +97,7 @@ function els(e,s){ // no js exec in innerHTML
<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"><div class="sprite no"></div> Recent Changes</a>
<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>
{{if .AllowDownload}}

View File

@ -5,6 +5,7 @@ import (
"log"
"net/http"
"net/url"
"time"
)
func (this *WikiServer) noSuchArticleError(title string) template.HTML {
@ -38,3 +39,8 @@ func (this *WikiServer) servePageResponse(w http.ResponseWriter, r *http.Request
log.Println(err.Error())
}
}
func (this *WikiServer) formatTimestamp(m int64) string {
// TODO add a more detailed timestamp on hover
return template.HTMLEscapeString(time.Unix(m, 0).In(this.loc).Format(this.opts.DateFormat))
}

48
rHistory.go Normal file
View File

@ -0,0 +1,48 @@
package yatwiki3
import (
"database/sql"
"fmt"
"html/template"
"net/http"
"net/url"
)
func (this *WikiServer) routeHistory(w http.ResponseWriter, r *http.Request, articleTitle string) {
revs, err := this.db.GetRevisionHistory(articleTitle)
if err != nil {
if err == sql.ErrNoRows {
this.serveErrorHTMLMessage(w, this.noSuchArticleError(articleTitle))
return
}
this.serveErrorMessage(w, err)
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 &quot;<a href="` + template.HTMLEscapeString(this.opts.ExpectBaseURL+`view/`+url.QueryEscape(articleTitle)) + `">` + template.HTMLEscapeString(articleTitle) + `</a>&quot;.</em>` +
`<br><br>` +
`<form method="GET" action="` + template.HTMLEscapeString(this.opts.ExpectBaseURL+`diff`) + `">` +
`<table>`
compareRow := `<tr><td colspan="2"></td><td><input type="submit" value="Compare Selected &raquo;"></td></tr>`
content += compareRow
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>` + template.HTMLEscapeString(rev.Author) + `</td>` +
`<td><input type="radio" name="t" value="` + revIdStr + `">&nbsp;<input type="radio" name="f" value="` + revIdStr + `"></td>` +
`</tr>`
}
content += compareRow
content += `</table></form>`
pto.Content = template.HTML(content)
this.servePageResponse(w, r, pto)
return
}

63
rRecentChanges.go Normal file
View File

@ -0,0 +1,63 @@
package yatwiki3
import (
"errors"
"fmt"
"html/template"
"math"
"net/http"
"net/url"
)
func (this *WikiServer) routeRecentChanges(w http.ResponseWriter, r *http.Request, pageNum int) {
totalEdits, err := this.db.TotalRevisions()
if err != nil {
this.serveInternalError(w, r, err)
return
}
minPage := 1
maxPage := int(math.Ceil(float64(totalEdits) / float64(this.opts.RecentChanges)))
if pageNum < minPage || pageNum > maxPage {
this.serveErrorMessage(w, errors.New("Invalid page."))
return
}
recents, err := this.db.GetRecentChanges((pageNum-1)*this.opts.RecentChanges, this.opts.RecentChanges)
if err != nil {
this.serveErrorMessage(w, err)
return
}
//
pto := DefaultPageTemplateOptions(this.opts)
pto.CurrentPageName = "Recent Changes"
content := `<h2>Recent Changes</h2><br>` +
`<em>Showing up to ` + fmt.Sprintf("%d", this.opts.RecentChanges) + ` changes.</em><br>` +
`<table>`
for _, rev := range recents {
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>` +
`<td>` + this.formatTimestamp(rev.Modified) + ` by ` + template.HTMLEscapeString(rev.Author) + `</td>` +
`</tr>`
}
content += `<tr><td>`
if pageNum > 1 {
content += `<a href="` + template.HTMLEscapeString(this.opts.ExpectBaseURL+`recent/`+fmt.Sprintf("%d", pageNum-1)) + `">&laquo; Newer</a>`
}
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 &raquo;</a>`
}
content += `</td></tr></table>`
pto.Content = template.HTML(content)
this.servePageResponse(w, r, pto)
return
}