1
0
Fork 0
mirror of https://github.com/Eggbertx/gochan.git synced 2025-08-26 10:36:23 -07:00
gochan/pkg/building/catalog.go

127 lines
3.9 KiB
Go

package building
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/server/serverutil"
)
type catalogThreadData struct {
*Post
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
Stickied int `json:"sticky"`
Locked int `json:"closed"`
Posts []*Post `json:"-"`
uploads []gcsql.Upload
}
type catalogPage struct {
PageNum int `json:"page"`
Threads []catalogThreadData `json:"threads"`
}
type boardCatalog struct {
pages []catalogPage // this array gets marshalled, not the boardCatalog object
numPages int
currentPage int
}
// fillPages fills the catalog's pages array with pages of the specified size, with the remainder
// on the last page
func (catalog *boardCatalog) fillPages(threadsPerPage int, threads []catalogThreadData) {
catalog.pages = []catalogPage{} // clear the array if it isn't already
catalog.numPages = len(threads) / threadsPerPage
remainder := len(threads) % threadsPerPage
currentThreadIndex := 0
var i int
for i = 0; i < catalog.numPages; i++ {
catalog.pages = append(catalog.pages,
catalogPage{
PageNum: i + 1,
Threads: threads[currentThreadIndex : currentThreadIndex+threadsPerPage],
},
)
currentThreadIndex += threadsPerPage
}
if remainder > 0 {
catalog.pages = append(catalog.pages,
catalogPage{
PageNum: i + 1,
Threads: threads[len(threads)-remainder:],
},
)
}
}
func getBoardTopPosts(board string) ([]*Post, error) {
const query = `SELECT id, thread_id, ip, name, tripcode, email, subject, created_on, last_modified, parent_id,
last_bump, message, message_raw, board_id, dir, original_filename, filename, checksum, filesize, tw, th,
width, height, locked, stickied, cyclical, flag, country, is_deleted
FROM DBPREFIXv_building_posts WHERE id = parent_id AND dir = ?`
var posts []*Post
err := QueryPosts(query, []any{board}, func(p *Post) error {
posts = append(posts, p)
return nil
})
return posts, err
}
// 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(gctemplates.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, config.NormalFileMode)
if err != nil {
errEv.Err(err).Caller().Send()
return fmt.Errorf("failed opening /%s/catalog.html: %w", board.Dir, err)
}
if err = config.TakeOwnershipOfFile(catalogFile); err != nil {
errEv.Err(err).Caller().Send()
return fmt.Errorf("failed taking ownership of /%s/catalog.html", board.Dir)
}
threadOPs, err := getBoardTopPosts(board.Dir)
if err != nil {
errEv.Err(err).Caller().Send()
return fmt.Errorf("failed building catalog for /%s/", board.Dir)
}
boardConfig := config.GetBoardConfig(board.Dir)
if err = serverutil.MinifyTemplate(gctemplates.Catalog, map[string]any{
"boards": gcsql.AllBoards,
"board": board,
"boardConfig": boardConfig,
"sections": gcsql.AllSections,
"threads": threadOPs,
}, catalogFile, "text/html"); err != nil {
errEv.Err(err).Caller().Send()
return fmt.Errorf("failed building catalog for /%s/", board.Dir)
}
return catalogFile.Close()
}