db: plumbing to handle deletions, restructure Article/Title structs
This commit is contained in:
parent
2c5f1e9244
commit
6e63285a54
56
DB.go
56
DB.go
@ -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
|
||||
}
|
||||
|
@ -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/") {
|
||||
|
4
rDiff.go
4
rDiff.go
@ -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>`,
|
||||
)
|
||||
|
@ -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 »"></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>` +
|
||||
|
@ -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
|
||||
|
@ -33,7 +33,7 @@ func (this *WikiServer) routeModify(w http.ResponseWriter, r *http.Request, arti
|
||||
baseRev = 0
|
||||
} else {
|
||||
pageTitleHTML = `Editing article "<a href="` + template.HTMLEscapeString(this.opts.ExpectBaseURL+`view/`+url.PathEscape(articleTitle)) + `">` + template.HTMLEscapeString(articleTitle) + `</a>"`
|
||||
baseRev = a.ID
|
||||
baseRev = a.ArticleID
|
||||
existingBody = string(a.Body)
|
||||
}
|
||||
|
||||
|
10
rRSS.go
10
rRSS.go
@ -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>
|
||||
`
|
||||
|
@ -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> ` +
|
||||
`<a href="` + template.HTMLEscapeString(this.opts.ExpectBaseURL+`archive/`+fmt.Sprintf("%d", rev.ArticleID)) + `">rev</a> ` +
|
||||
diffHtml +
|
||||
`</td>` +
|
||||
`</td>` +
|
||||
|
Loading…
Reference in New Issue
Block a user