1
0
Fork 0
mirror of https://github.com/Eggbertx/gochan.git synced 2025-09-16 12:06:23 -07:00

Make board pages, thread pages, and catalogs render properly

This commit is contained in:
Eggbertx 2022-11-28 16:15:20 -08:00
parent 29c7493715
commit 8bc5e2148b
8 changed files with 151 additions and 136 deletions

View file

@ -35,8 +35,13 @@ func boolToInt(b bool) int {
// BuildBoardPages builds the front pages for the given board, and returns any error it encountered.
func BuildBoardPages(board *gcsql.Board) error {
errEv := gcutil.LogError(nil).
Int("boardID", board.ID).
Str("boardDir", board.Dir)
defer errEv.Discard()
err := gctemplates.InitTemplates("boardpage")
if err != nil {
errEv.Err(err).Caller().Msg("unable to initialize boardpage template")
return err
}
var currentPageFile *os.File
@ -47,8 +52,7 @@ func BuildBoardPages(board *gcsql.Board) error {
threads, err := board.GetThreads(true)
if err != nil {
gcutil.LogError(err).
Int("boardID", board.ID).
errEv.Err(err).
Caller().Msg("Failed getting board threads")
return fmt.Errorf("error getting OP posts for /%s/: %s", board.Dir, err.Error())
}
@ -58,10 +62,9 @@ func BuildBoardPages(board *gcsql.Board) error {
Locked: boolToInt(thread.Locked),
Sticky: boolToInt(thread.Stickied),
}
errEv.Int("threadID", thread.ID)
if catalogThread.Images, err = thread.GetReplyFileCount(); err != nil {
gcutil.LogError(err).
Str("boardDir", board.Dir).
Int("threadID", thread.ID).
errEv.Err(err).
Caller().Msg("Failed getting file count")
return err
}
@ -78,42 +81,43 @@ func BuildBoardPages(board *gcsql.Board) error {
}
catalogThread.Replies, err = thread.GetReplyCount()
if err != nil {
gcutil.LogError(err).
Str("boardDir", board.Dir).
Int("threadID", thread.ID).
errEv.Err(err).
Caller().Msg("Failed getting reply count")
return errors.New("Error getting reply count: " + err.Error())
}
catalogThread.posts, err = thread.GetPosts(false, true, maxRepliesOnBoardPage)
catalogThread.Posts, err = getThreadPosts(&thread)
if err != nil {
gcutil.LogError(err).
Int("threadid", thread.ID).
Str("boardDir", board.Dir).
Msg("Failed getting replies")
errEv.Err(err).
Caller().Msg("Failed getting replies")
return errors.New("Failed getting replies: " + err.Error())
}
if len(catalogThread.Posts) > maxRepliesOnBoardPage {
op := catalogThread.Posts[0]
replies := catalogThread.Posts[len(catalogThread.Posts)-maxRepliesOnBoardPage:]
catalogThread.Posts = []Post{op}
catalogThread.Posts = append(catalogThread.Posts, replies...)
}
catalogThread.uploads, err = thread.GetUploads()
if err != nil {
gcutil.LogError(err).
Int("threadid", thread.ID).
Str("boardDir", board.Dir).
errEv.Err(err).
Caller().Msg("Failed getting thread uploads")
return errors.New("Failed getting thread uploads: " + err.Error())
}
var imagesOnBoardPage int
for _, upload := range catalogThread.uploads {
for _, post := range catalogThread.posts {
for _, post := range catalogThread.Posts {
if post.ID == upload.PostID {
imagesOnBoardPage++
}
}
}
if catalogThread.Replies > maxRepliesOnBoardPage {
catalogThread.OmittedPosts = catalogThread.Replies - len(catalogThread.posts)
catalogThread.OmittedPosts = catalogThread.Replies - len(catalogThread.Posts)
catalogThread.OmittedImages = len(catalogThread.uploads) - imagesOnBoardPage
}
catalogThread.OmittedPosts = catalogThread.Replies - len(catalogThread.posts)
catalogThread.OmittedPosts = catalogThread.Replies - len(catalogThread.Posts)
// Add thread struct to appropriate list
if thread.Stickied {
@ -131,6 +135,7 @@ func BuildBoardPages(board *gcsql.Board) error {
// If there are no posts on the board
var boardPageFile *os.File
boardConfig := config.GetBoardConfig(board.Dir)
if len(threads) == 0 {
catalog.currentPage = 1
@ -138,14 +143,12 @@ func BuildBoardPages(board *gcsql.Board) error {
boardPageFile, err = os.OpenFile(path.Join(criticalCfg.DocumentRoot, board.Dir, "1.html"),
os.O_CREATE|os.O_RDWR|os.O_TRUNC, 0777)
if err != nil {
gcutil.LogError(err).
Str("boardDir", board.Dir).
errEv.Err(err).
Str("page", "board.html").
Caller().Msg("Failed getting board page")
return fmt.Errorf("failed opening /%s/board.html: %s", board.Dir, err.Error())
}
boardConfig := config.GetBoardConfig(board.Dir)
defer boardPageFile.Close()
// Render board page template to the file,
// packaging the board/section list, threads, and board info
if err = serverutil.MinifyTemplate(gctemplates.BoardPage, map[string]interface{}{
@ -153,12 +156,12 @@ func BuildBoardPages(board *gcsql.Board) error {
"boards": gcsql.AllBoards,
"sections": gcsql.AllSections,
"threads": threads,
"numPages": len(threads) / boardConfig.ThreadsPerPage,
"numPages": 1,
"currentPage": 1,
"board": board,
"board_config": boardConfig,
}, boardPageFile, "text/html"); err != nil {
gcutil.LogError(err).
Str("boardDir", board.Dir).
errEv.Err(err).
Str("page", "board.html").
Caller().Msg("Failed building board")
return fmt.Errorf("failed building /%s/: %s", board.Dir, err.Error())
@ -173,17 +176,16 @@ func BuildBoardPages(board *gcsql.Board) error {
// Create array of page wrapper objects, and open the file.
var catalogPages boardCatalog
// catalog JSON file is built with the pages because pages are recorded in the JSON file
catalogJSONFile, err := os.OpenFile(path.Join(criticalCfg.DocumentRoot, board.Dir, "catalog.json"), os.O_CREATE|os.O_RDWR|os.O_TRUNC, 0777)
if err != nil {
gcutil.LogError(err).
errEv.Err(err).
Str("subject", "catalog.json").
Str("boardDir", board.Dir).
Msg("Failed opening catalog.json")
Caller().Msg("Failed opening catalog.json")
return fmt.Errorf("failed opening /%s/catalog.json: %s", board.Dir, err.Error())
}
defer catalogJSONFile.Close()
// currentBoardPage := catalog.currentPage
for _, page := range catalog.pages {
catalog.currentPage++
var currentPageFilepath string
@ -191,10 +193,9 @@ func BuildBoardPages(board *gcsql.Board) error {
currentPageFilepath = path.Join(criticalCfg.DocumentRoot, board.Dir, pageFilename)
currentPageFile, err = os.OpenFile(currentPageFilepath, os.O_CREATE|os.O_RDWR|os.O_TRUNC, 0777)
if err != nil {
gcutil.LogError(err).
Str("boardDir", board.Dir).
errEv.Err(err).
Str("page", pageFilename).
Msg("Failed getting board page")
Caller().Msg("Failed getting board page")
continue
}
defer currentPageFile.Close()
@ -205,37 +206,34 @@ func BuildBoardPages(board *gcsql.Board) error {
"boards": gcsql.AllBoards,
"sections": gcsql.AllSections,
"threads": page.Threads,
"numPages": len(threads) / boardConfig.ThreadsPerPage,
"currentPage": catalog.currentPage,
"board": board,
"board_config": boardCfg,
"posts": []interface{}{
gcsql.Post{},
},
}, currentPageFile, "text/html"); err != nil {
gcutil.LogError(err).
Str("boardDir", board.Dir).
Msg("Failed building boardpage")
return fmt.Errorf("Failed building /%s/ boardpage: %s", board.Dir, err.Error())
errEv.Err(err).
Caller().Send()
return fmt.Errorf("failed building /%s/ boardpage: %s", board.Dir, err.Error())
}
// Collect up threads for this page.
page := catalogPage{}
page.PageNum = catalog.currentPage
// page.Threads = page.Threads
catalogPages.pages = append(catalogPages.pages, page)
}
// board.CurrentPage = currentBoardPage
var catalogJSON []byte
if catalogJSON, err = json.Marshal(catalog.pages); err != nil {
gcutil.LogError(err).
Str("boardDir", board.Dir).
Msg("Failed to marshal to JSON")
errEv.Err(err).
Caller().Send()
return errors.New("failed to marshal to JSON: " + err.Error())
}
if _, err = catalogJSONFile.Write(catalogJSON); err != nil {
gcutil.LogError(err).
Str("boardDir", board.Dir).
Msg("Failed writing catalog.json")
errEv.Err(err).
Caller().Msg("Failed writing catalog.json")
return fmt.Errorf("failed writing /%s/catalog.json: %s", board.Dir, err.Error())
}
return nil
@ -256,7 +254,7 @@ func BuildBoards(verbose bool, which ...int) error {
gcutil.LogError(err).
Int("boardid", boardID).
Caller().Msg("Unable to get board information")
return fmt.Errorf("Error getting board information (ID: %d): %s", boardID, err.Error())
return fmt.Errorf("unable to get board information (ID: %d): %s", boardID, err.Error())
}
boards = append(boards, *board)
}
@ -277,53 +275,6 @@ func BuildBoards(verbose bool, which ...int) error {
return nil
}
// BuildCatalog builds the catalog for a board with a given id
func BuildCatalog(boardID int) error {
errEv := gcutil.LogError(nil).
Str("building", "catalog").
Int("boardID", boardID)
err := gctemplates.InitTemplates("catalog")
if err != nil {
errEv.Err(err).Send()
return err
}
board, err := gcsql.GetBoardFromID(boardID)
if err != nil {
errEv.Err(err).
Caller().Msg("Unable to get board information")
return err
}
errEv.Str("boardDir", board.Dir)
criticalCfg := config.GetSystemCriticalConfig()
catalogPath := path.Join(criticalCfg.DocumentRoot, board.Dir, "catalog.html")
catalogFile, err := os.OpenFile(catalogPath, os.O_CREATE|os.O_RDWR|os.O_TRUNC, 0777)
if err != nil {
errEv.Err(err).Caller().Send()
return fmt.Errorf("failed opening /%s/catalog.html: %s<br/>", board.Dir, err.Error())
}
threadOPs, err := getBoardTopPosts(boardID)
if err != nil {
errEv.Err(err).Caller().Send()
return fmt.Errorf("failed building catalog for /%s/: %s<br/>", board.Dir, err.Error())
}
boardConfig := config.GetBoardConfig(board.Dir)
if err = serverutil.MinifyTemplate(gctemplates.Catalog, map[string]interface{}{
"boards": gcsql.AllBoards,
"webroot": criticalCfg.WebRoot,
"board": board,
"board_config": boardConfig,
"sections": gcsql.AllSections,
"threads": threadOPs,
}, catalogFile, "text/html"); err != nil {
errEv.Err(err).Caller().Send()
return fmt.Errorf("failed building catalog for /%s/: %s", board.Dir, err.Error())
}
return nil
}
// Build builds the board and its thread files
// if force is true, it doesn't fail if the directories exist but does fail if it is a file
func buildBoard(board *gcsql.Board, force bool) error {

View file

@ -1,17 +1,26 @@
package building
import "github.com/gochan-org/gochan/pkg/gcsql"
import (
"fmt"
"os"
"path"
"github.com/gochan-org/gochan/pkg/config"
"github.com/gochan-org/gochan/pkg/gcsql"
"github.com/gochan-org/gochan/pkg/gctemplates"
"github.com/gochan-org/gochan/pkg/gcutil"
"github.com/gochan-org/gochan/pkg/serverutil"
)
type catalogThreadData struct {
Replies int `json:"replies"`
Images int `json:"images"`
OmittedPosts int `json:"omitted_posts"` // posts in the thread but not shown on the board page
OmittedImages int `json:"omitted_images"` // uploads in the thread but not shown on the board page
Sticky int `json:"sticky"`
Locked int `json:"locked"`
// numPages int
posts []gcsql.Post
uploads []gcsql.Upload
Replies int `json:"replies"`
Images int `json:"images"`
OmittedPosts int `json:"omitted_posts"` // posts in the thread but not shown on the board page
OmittedImages int `json:"omitted_images"` // uploads in the thread but not shown on the board page
Sticky int `json:"sticky"`
Locked int `json:"locked"`
Posts []Post `json:"-"`
uploads []gcsql.Upload
}
type catalogPage struct {
@ -52,18 +61,49 @@ func (catalog *boardCatalog) fillPages(threadsPerPage int, threads []catalogThre
}
}
// func paginateBoards(threadsPerPage int, threads []catalogThreadData) [][]catalogThreadData {
// var paginatedThreads [][]catalogThreadData
// numArrays := len(threads) / threadsPerPage
// remainder := len(threads) % threadsPerPage
// currentIndex := 0
// for l := 0; l < numArrays; l++ {
// paginatedThreads = append(paginatedThreads,
// threads[currentIndex:currentIndex+threadsPerPage])
// currentIndex += threadsPerPage
// }
// if remainder > 0 {
// paginatedThreads = append(paginatedThreads, threads[len(threads)-remainder:])
// }
// return paginatedThreads
// }
// BuildCatalog builds the catalog for a board with a given id
func BuildCatalog(boardID int) error {
errEv := gcutil.LogError(nil).
Str("building", "catalog").
Int("boardID", boardID)
err := gctemplates.InitTemplates("catalog")
if err != nil {
errEv.Err(err).Send()
return err
}
board, err := gcsql.GetBoardFromID(boardID)
if err != nil {
errEv.Err(err).
Caller().Msg("Unable to get board information")
return err
}
errEv.Str("boardDir", board.Dir)
criticalCfg := config.GetSystemCriticalConfig()
catalogPath := path.Join(criticalCfg.DocumentRoot, board.Dir, "catalog.html")
catalogFile, err := os.OpenFile(catalogPath, os.O_CREATE|os.O_RDWR|os.O_TRUNC, 0777)
if err != nil {
errEv.Err(err).Caller().Send()
return fmt.Errorf("failed opening /%s/catalog.html: %s<br/>", board.Dir, err.Error())
}
threadOPs, err := getBoardTopPosts(boardID)
if err != nil {
errEv.Err(err).Caller().Send()
return fmt.Errorf("failed building catalog for /%s/: %s<br/>", board.Dir, err.Error())
}
boardConfig := config.GetBoardConfig(board.Dir)
if err = serverutil.MinifyTemplate(gctemplates.Catalog, map[string]interface{}{
"boards": gcsql.AllBoards,
"webroot": criticalCfg.WebRoot,
"board": board,
"board_config": boardConfig,
"sections": gcsql.AllSections,
"threads": threadOPs,
}, catalogFile, "text/html"); err != nil {
errEv.Err(err).Caller().Send()
return fmt.Errorf("failed building catalog for /%s/: %s", board.Dir, err.Error())
}
return nil
}

View file

@ -33,6 +33,16 @@ const (
LEFT JOIN DBPREFIXfiles ON DBPREFIXfiles.post_id = DBPREFIXposts.id WHERE is_deleted = FALSE`
)
func truncateString(msg string, limit int, ellipsis bool) string {
if len(msg) > limit {
if ellipsis {
return msg[:limit] + "..."
}
return msg[:limit]
}
return msg
}
type Post struct {
ID int `json:"no"`
ParentID int `json:"resto"`
@ -60,6 +70,18 @@ type Post struct {
LastModified string `json:"last_modified"`
}
func (p Post) TitleText() string {
title := "/" + p.BoardDir + "/ - "
if p.Subject != "" {
title += truncateString(p.Subject, 20, true)
} else if p.Message != "" {
title += truncateString(bbcodeTagRE.ReplaceAllString(p.MessageRaw, ""), 20, true)
} else {
title += "#" + strconv.Itoa(p.ID)
}
return title
}
func (p Post) WebPath() string {
threadID := p.ParentID
if threadID == 0 {
@ -102,7 +124,7 @@ func GetBuildablePost(id int, boardid int) (*Post, error) {
}
func getBoardTopPosts(boardID int) ([]Post, error) {
const query = "SELECT * FROM (" + postQueryBase + ") p WHERE boardid = ?"
const query = "SELECT * FROM (" + postQueryBase + " AND is_top_post) p WHERE boardid = ?"
rows, err := gcsql.QuerySQL(query, boardID)
if err != nil {
return nil, err
@ -124,7 +146,7 @@ func getBoardTopPosts(boardID int) ([]Post, error) {
if err != nil {
return nil, err
}
post.IsTopPost = post.ParentID == 0
post.IsTopPost = post.ParentID == 0 || post.ParentID == post.ID
posts = append(posts, post)
}
return posts, nil
@ -150,7 +172,7 @@ func getThreadPosts(thread *gcsql.Thread) ([]Post, error) {
if err != nil {
return nil, err
}
post.IsTopPost = post.ParentID == 0
post.IsTopPost = post.ParentID == 0 || post.ParentID == post.ID
posts = append(posts, post)
}
return posts, nil

View file

@ -245,7 +245,7 @@ func (board *Board) Delete() error {
}
func (board *Board) GetThreads(onlyNotDeleted bool) ([]Thread, error) {
query := selectThreadsBaseSQL + " WHERE id = ?"
query := selectThreadsBaseSQL + " WHERE board_id = ?"
if onlyNotDeleted {
query += " AND is_deleted = FALSE"
}

View file

@ -18,7 +18,7 @@ const (
SELECT thread_id FROM DBPREFIXposts WHERE id = ?))`
selectPostsBaseSQL = `SELECT
id, thread_id, is_top_post, ip, created_on, name, tripcode, is_role_signature,
email, subject, message, message_raw, password, deleted_at, is_deleted, banned_message
email, subject, message, message_raw, password, deleted_at, is_deleted, COALESCE(banned_message,'') AS banned_message
FROM DBPREFIXposts `
)

View file

@ -75,7 +75,7 @@ func GetThreadsWithBoardID(boardID int, onlyNotDeleted bool) ([]Thread, error) {
func GetThreadReplyCountFromOP(opID int) (int, error) {
const query = `SELECT COUNT(*) FROM DBPREFIXposts WHERE thread_id = (
SELECT thread_id FROM DBPREFIXposts WHERE id = ?) AND is_deleted = 0`
SELECT thread_id FROM DBPREFIXposts WHERE id = ?) AND is_deleted = FALSE AND is_top_post = FALSE`
var num int
err := QueryRowSQL(query, interfaceSlice(opID), interfaceSlice(&num))
return num, err

View file

@ -12,14 +12,16 @@
{{- template "postbox.html" . -}}<hr />
<form action="{{.webroot}}util" method="POST" id="main-form">
{{$global := .}}
{{- range $t, $thread := .threads}}{{$op := $thread.OP}}
{{- range $t, $thread := .threads}}{{$op := index $thread.Posts 0}}
<div class="thread">
{{- template "post.html" map "global" $global "board" $.board "post" $op "is_board_page" true -}}
{{- if gt $thread.NumReplies 3 -}}
<b>{{subtract $thread.NumReplies 3}} post{{if gt $thread.NumReplies 4}}s{{end}} omitted</b><br />
{{- if gt $thread.Replies 3 -}}
<b>{{subtract $thread.Replies 3}} post{{if gt $thread.Replies 4}}s{{end}} omitted</b><br />
{{- end -}}
{{- range $reply_num,$reply := $thread.BoardReplies -}}
{{- template "post.html" map "global" $global "board" $.board "post" $reply -}}
{{- range $r,$reply := $thread.Posts -}}
{{if gt $r 0}}
{{- template "post.html" map "global" $global "board" $.board "post" $reply -}}
{{end}}
{{- end -}}
</div><hr />
{{- end}}
@ -38,20 +40,20 @@
<a href="#">Scroll to top</a><br/>
<table id="pages">
<tr>
{{/*<td>{{if gt .board.CurrentPage 1}}
{{/*<td>{{if gt $.currentPage 1}}
<form method="GET" action='{{.board.PagePath "prev"}}'>
<input type="submit" value="Previous" />
</form>
{{- else}}Previous{{end}}</td> */}}
<td>{{range $_,$i := makeLoop .numPages 1 -}}
{{- if eq $.board.CurrentPage $i -}}
{{- if eq $.currentPage $i -}}
[<b>{{$i}}</b>]
{{- else -}}
[<a href="{{$.board.PagePath $i }}">{{$i}}</a>]
{{- end -}}
{{- end}}</td>
{{/*
<td>{{if lt .board.CurrentPage .numPages}}
<td>{{if lt $.currentPage .numPages}}
<form method="GET" action="{{.board.PagePath `next` }}">
<input type="submit" value="Next" />
</form>

View file

@ -5,10 +5,10 @@
<meta name="viewport" content="width=device-width, initial-scale=1.0">
{{with .board -}}
{{with $.op -}}
{{if ne $.op.Subject "" -}}<title>/{{$.board.Dir}}/ - {{truncateString $.op.Subject 20 true}}</title>
{{- else if ne $.op.Message "" -}}<title>/{{$.board.Dir}}/ - {{truncateString $.op.MessageRaw 20 true}}</title>
{{- else}}<title>/{{$.board.Dir}}/ - #{{$.op.ID}}</title>{{end}}
{{- else}}<title>/{{$.board.Dir}}/ - {{$.board.Title}}</title>{{end}}
<title>{{$.op.TitleText}}</title>
{{- else}}
<title>/{{$.board.Dir}}/ - {{$.board.Title}}</title>
{{end}}
{{- else}}<title>{{with $.page_title}}{{$.page_title}} - {{end}}{{.site_config.SiteName}}</title>{{end}}
<link rel="stylesheet" href="{{.webroot}}css/global.css" />
<link id="theme" rel="stylesheet" href="{{.webroot}}css/{{.board_config.DefaultStyle}}" />