implement recent changes, article history
This commit is contained in:
parent
619dc78bac
commit
c39d563dc6
55
DB.go
55
DB.go
@ -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 {
|
||||
|
@ -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
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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}}
|
||||
|
@ -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
48
rHistory.go
Normal 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 "<a href="` + template.HTMLEscapeString(this.opts.ExpectBaseURL+`view/`+url.QueryEscape(articleTitle)) + `">` + template.HTMLEscapeString(articleTitle) + `</a>".</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 »"></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 + `"> <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
63
rRecentChanges.go
Normal 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)) + `">« 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 »</a>`
|
||||
}
|
||||
content += `</td></tr></table>`
|
||||
|
||||
pto.Content = template.HTML(content)
|
||||
this.servePageResponse(w, r, pto)
|
||||
return
|
||||
}
|
Loading…
Reference in New Issue
Block a user