db: plumbing to handle deletions, restructure Article/Title structs

This commit is contained in:
mappu 2018-04-02 17:41:47 +12:00
parent 2c5f1e9244
commit 6e63285a54
8 changed files with 54 additions and 34 deletions

56
DB.go
View File

@ -117,13 +117,18 @@ func (this *WikiDB) assertSchema() error {
return nil
}
type TitleInfo struct {
TitleID int64
Title string
IsDeleted bool
}
type Article struct {
ID int64
TitleID int64
Modified int64
Body []byte
Author string
Title string
TitleInfo
ArticleID int64
Modified int64
Body []byte
Author string
}
func (this *WikiDB) GetArticleById(articleId int) (*Article, error) {
@ -132,7 +137,7 @@ func (this *WikiDB) GetArticleById(articleId int) (*Article, 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)
row := this.db.QueryRow(`SELECT articles.*, titles.title, titles.is_deleted FROM articles JOIN titles ON articles.article=titles.id WHERE articles.id = ?`, revId)
return this.parseArticleWithTitle(row)
}
@ -164,8 +169,8 @@ func (this *WikiDB) SaveArticle(title, author, body string, expectBaseRev int64)
}
}
if !isNewArticle && a.ID != expectBaseRev {
return ArticleAlteredError{got: expectBaseRev, expected: a.ID}
if !isNewArticle && a.ArticleID != expectBaseRev {
return ArticleAlteredError{got: expectBaseRev, expected: a.ArticleID}
}
zBody, err := gzdeflate([]byte(body), this.compressionLevel)
@ -193,6 +198,13 @@ func (this *WikiDB) SaveArticle(title, author, body string, expectBaseRev int64)
return err
}
// Update is-deleted flag
isDeleted := 0
if body == bbcodeIsDeleted {
isDeleted = 1
}
_, err = this.db.Exec(`UPDATE titles SET is_deleted = ? WHERE id = ?`, isDeleted, titleId)
return nil
}
@ -209,7 +221,7 @@ func (this *WikiDB) GetRevisionHistory(title string) ([]Article, error) {
ret := make([]Article, 0)
for rows.Next() {
a := Article{}
err := rows.Scan(&a.ID, &a.Modified, &a.Author)
err := rows.Scan(&a.ArticleID, &a.Modified, &a.Author)
if err != nil {
return nil, err
}
@ -235,7 +247,7 @@ func (this *WikiDB) GetNextOldestRevision(revision int) (int, error) {
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 ` +
`SELECT articles.id, articles.modified, articles.author, titles.title, titles.is_deleted FROM articles JOIN titles ON articles.article=titles.id ORDER BY modified DESC ` +
fmt.Sprintf(`LIMIT %d OFFSET %d`, limit, offset),
)
if err != nil {
@ -246,7 +258,7 @@ func (this *WikiDB) GetRecentChanges(offset int, limit int) ([]Article, error) {
ret := make([]Article, 0, limit)
for rows.Next() {
a := Article{}
err := rows.Scan(&a.ID, &a.Modified, &a.Author, &a.Title)
err := rows.Scan(&a.ArticleID, &a.Modified, &a.Author, &a.Title, &a.IsDeleted)
if err != nil {
return nil, err
}
@ -266,17 +278,23 @@ func (this *WikiDB) TotalRevisions() (int64, error) {
return ret, nil
}
func (this *WikiDB) ListTitles() ([]string, error) {
rows, err := this.db.Query(`SELECT title FROM titles ORDER BY title ASC`)
func (this *WikiDB) ListTitles(includeDeleted bool) ([]TitleInfo, error) {
stmt := `SELECT id, title, is_deleted FROM titles`
if !includeDeleted {
stmt += ` WHERE is_deleted = 0`
}
stmt += ` ORDER BY title ASC`
rows, err := this.db.Query(stmt)
if err != nil {
return nil, err
}
defer rows.Close()
ret := make([]string, 0)
ret := make([]TitleInfo, 0)
for rows.Next() {
var title string
err = rows.Scan(&title)
var title TitleInfo
err = rows.Scan(&title.TitleID, &title.Title, &title.IsDeleted)
if err != nil {
return nil, err
}
@ -289,7 +307,7 @@ func (this *WikiDB) ListTitles() ([]string, error) {
func (this *WikiDB) parseArticle(row *sql.Row) (*Article, error) {
a := Article{}
var gzBody []byte
err := row.Scan(&a.ID, &a.TitleID, &a.Modified, &gzBody, &a.Author)
err := row.Scan(&a.ArticleID, &a.TitleID, &a.Modified, &gzBody, &a.Author)
if err != nil {
return nil, err
}
@ -307,7 +325,7 @@ func (this *WikiDB) parseArticle(row *sql.Row) (*Article, error) {
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)
err := row.Scan(&a.ArticleID, &a.TitleID, &a.Modified, &gzBody, &a.Author, &a.Title, &a.IsDeleted)
if err != nil {
return nil, err
}

View File

@ -148,14 +148,14 @@ func (this *WikiServer) ServeHTTP(w http.ResponseWriter, r *http.Request) {
return
} else if remainingPath == "random" {
titles, err := this.db.ListTitles()
titles, err := this.db.ListTitles(false) // "Random page" mode does not include deleted pages
if err != nil {
this.serveInternalError(w, r, err)
return
}
chosenArticle := titles[rand.Intn(len(titles))]
this.serveRedirect(w, this.opts.ExpectBaseURL+`view/`+url.PathEscape(chosenArticle))
this.serveRedirect(w, this.opts.ExpectBaseURL+`view/`+url.PathEscape(chosenArticle.Title))
return
} else if strings.HasPrefix(remainingPath, "view/") {

View File

@ -53,9 +53,9 @@ func (this *WikiServer) routeDiff(w http.ResponseWriter, r *http.Request, oldRev
pto.Content = template.HTML(
`<h2>` +
`Comparing ` + string(this.viewLink(oa.Title)) + ` versions ` +
`<a href="` + template.HTMLEscapeString(this.opts.ExpectBaseURL+`archive/`+fmt.Sprintf("%d", oa.ID)) + `">r` + fmt.Sprintf("%d", oa.ID) + `</a>` +
`<a href="` + template.HTMLEscapeString(this.opts.ExpectBaseURL+`archive/`+fmt.Sprintf("%d", oa.ArticleID)) + `">r` + fmt.Sprintf("%d", oa.ArticleID) + `</a>` +
` - ` +
`<a href="` + template.HTMLEscapeString(this.opts.ExpectBaseURL+`archive/`+fmt.Sprintf("%d", na.ID)) + `">r` + fmt.Sprintf("%d", na.ID) + `</a>` +
`<a href="` + template.HTMLEscapeString(this.opts.ExpectBaseURL+`archive/`+fmt.Sprintf("%d", na.ArticleID)) + `">r` + fmt.Sprintf("%d", na.ArticleID) + `</a>` +
`</h2>` +
`<pre>` + string(b.Bytes()) + `</pre>`,
)

View File

@ -37,7 +37,7 @@ func (this *WikiServer) routeHistory(w http.ResponseWriter, r *http.Request, art
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)
revIdStr := fmt.Sprintf("%d", rev.ArticleID)
content += `<tr>` +
`<td><a href="` + template.HTMLEscapeString(this.opts.ExpectBaseURL+`archive/`+revIdStr) + `">` + string(this.formatTimestamp(rev.Modified)) + `</a></td>` +
`<td>` + template.HTMLEscapeString(rev.Author) + `</td>` +

View File

@ -8,7 +8,7 @@ import (
)
func (this *WikiServer) routeIndex(w http.ResponseWriter, r *http.Request) {
titles, err := this.db.ListTitles()
titles, err := this.db.ListTitles(true) // Always load deleted pages, even if we don't display them in the list
if err != nil {
this.serveInternalError(w, r, err)
return

View File

@ -33,7 +33,7 @@ func (this *WikiServer) routeModify(w http.ResponseWriter, r *http.Request, arti
baseRev = 0
} else {
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.ArticleID
existingBody = string(a.Body)
}

10
rRSS.go
View File

@ -27,17 +27,17 @@ func (this *WikiServer) routeRecentChangesRSS(w http.ResponseWriter, r *http.Req
for _, a := range recents {
content += `
<item>
<title>` + template.HTMLEscapeString(a.Title+` (r`+fmt.Sprintf("%d", a.ID)+`)`) + `</title>
<link>` + template.HTMLEscapeString(this.opts.ExternalBaseURL+`archive/`+fmt.Sprintf("%d", a.ID)) + `</link>
<guid>` + template.HTMLEscapeString(this.opts.ExternalBaseURL+`archive/`+fmt.Sprintf("%d", a.ID)) + `</guid>
<title>` + template.HTMLEscapeString(a.Title+` (r`+fmt.Sprintf("%d", a.ArticleID)+`)`) + `</title>
<link>` + template.HTMLEscapeString(this.opts.ExternalBaseURL+`archive/`+fmt.Sprintf("%d", a.ArticleID)) + `</link>
<guid>` + template.HTMLEscapeString(this.opts.ExternalBaseURL+`archive/`+fmt.Sprintf("%d", a.ArticleID)) + `</guid>
<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.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.ArticleID))+`">revision `+fmt.Sprintf("%d", a.ArticleID)+`</a>
|
<a href="`+template.HTMLEscapeString(this.opts.ExternalBaseURL+`diff/parent/`+fmt.Sprintf("%d", a.ID))+`">diff to previous</a>
<a href="`+template.HTMLEscapeString(this.opts.ExternalBaseURL+`diff/parent/`+fmt.Sprintf("%d", a.ArticleID))+`">diff to previous</a>
`) + `</description>
</item>
`

View File

@ -44,17 +44,19 @@ func (this *WikiServer) routeRecentChanges(w http.ResponseWriter, r *http.Reques
for _, rev := range recents {
diffHtml := ""
diffRev, err := this.db.GetNextOldestRevision(int(rev.ID))
diffRev, err := this.db.GetNextOldestRevision(int(rev.ArticleID))
if err != nil {
diffHtml = `[new]`
} else {
diffHtml = `<a href="` + template.HTMLEscapeString(this.opts.ExpectBaseURL+`diff/`+fmt.Sprintf("%d/%d", diffRev, rev.ID)) + `">diff</a>`
diffHtml = `<a href="` + template.HTMLEscapeString(this.opts.ExpectBaseURL+`diff/`+fmt.Sprintf("%d/%d", diffRev, rev.ArticleID)) + `">diff</a>`
}
}
content += `<tr>` +
`<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> &nbsp; ` +
`<a href="` + template.HTMLEscapeString(this.opts.ExpectBaseURL+`archive/`+fmt.Sprintf("%d", rev.ArticleID)) + `">rev</a> &nbsp; ` +
diffHtml +
`</td>` +
`</td>` +