mirror of
https://github.com/Eggbertx/gochan.git
synced 2025-08-05 00:26:23 -07:00
de-deprecate pkg/building
This commit is contained in:
parent
be14983186
commit
e789b08492
5 changed files with 272 additions and 201 deletions
|
@ -7,7 +7,6 @@ import (
|
||||||
"os"
|
"os"
|
||||||
"path"
|
"path"
|
||||||
"strconv"
|
"strconv"
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/gochan-org/gochan/pkg/config"
|
"github.com/gochan-org/gochan/pkg/config"
|
||||||
"github.com/gochan-org/gochan/pkg/gcsql"
|
"github.com/gochan-org/gochan/pkg/gcsql"
|
||||||
|
@ -27,113 +26,111 @@ var (
|
||||||
ErrNoBoardTitle = errors.New("board must have a title before it is built")
|
ErrNoBoardTitle = errors.New("board must have a title before it is built")
|
||||||
)
|
)
|
||||||
|
|
||||||
// BuildBoardPages builds the pages for the board archive.
|
func maxInt(a, b int) int {
|
||||||
// `board` is a Board object representing the board to build archive pages for.
|
if a > b {
|
||||||
// The return value is a string of HTML with debug information from the build process.
|
return a
|
||||||
|
}
|
||||||
|
return b
|
||||||
|
}
|
||||||
|
|
||||||
|
func boolToInt(b bool) int {
|
||||||
|
if b {
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// BuildBoardPages builds the front pages for the given board, and returns any error it encountered.
|
||||||
func BuildBoardPages(board *gcsql.Board) error {
|
func BuildBoardPages(board *gcsql.Board) error {
|
||||||
err := gctemplates.InitTemplates("boardpage")
|
err := gctemplates.InitTemplates("boardpage")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
var currentPageFile *os.File
|
var currentPageFile *os.File
|
||||||
var threadPages [][]interface{}
|
var stickiedThreads []gcsql.Thread
|
||||||
var stickiedThreads []interface{}
|
var nonStickiedThreads []gcsql.Thread
|
||||||
var nonStickiedThreads []interface{}
|
var catalog boardCatalog
|
||||||
var opPosts []gcsql.Post
|
var catalogThreads []catalogThreadData
|
||||||
|
|
||||||
threads, err := gcsql.GetThreadsWithBoardID(board.ID, true)
|
threads, err := board.GetThreads(true)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
gcutil.LogError(err).
|
gcutil.LogError(err).
|
||||||
Int("boardID", board.ID).
|
Int("boardID", board.ID).
|
||||||
Msg("Failed getting OP posts")
|
Msg("Failed getting board threads")
|
||||||
return fmt.Errorf("error getting OP posts for /%s/: %s", board.Dir, err.Error())
|
return fmt.Errorf("error getting OP posts for /%s/: %s", board.Dir, err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get all top level posts for the board
|
for _, thread := range threads {
|
||||||
if opPosts, err = gcsql.GetTopPosts(board.ID); err != nil {
|
catalogThread := catalogThreadData{
|
||||||
gcutil.LogError(err).
|
Locked: boolToInt(thread.Locked),
|
||||||
Str("boardDir", board.Dir).
|
Sticky: boolToInt(thread.Stickied),
|
||||||
Msg("Failed getting OP posts")
|
|
||||||
return fmt.Errorf("error getting OP posts for /%s/: %s", board.Dir, err.Error())
|
|
||||||
}
|
|
||||||
|
|
||||||
// For each top level post, start building a Thread struct
|
|
||||||
for p := range opPosts {
|
|
||||||
op := &opPosts[p]
|
|
||||||
var thread gcsql.Thread
|
|
||||||
var postsInThread []gcsql.Post
|
|
||||||
|
|
||||||
var replyCount, err = gcsql.GetReplyCount(op.ID)
|
|
||||||
if err != nil {
|
|
||||||
gcutil.LogError(err).
|
|
||||||
Str("boardDir", board.Dir).
|
|
||||||
Int("op", op.ID).
|
|
||||||
Msg("Failed getting thread replies")
|
|
||||||
return fmt.Errorf("Error getting replies to /%s/%d: %s", board.Dir, op.ID, err.Error())
|
|
||||||
}
|
}
|
||||||
thread.NumReplies = replyCount
|
if catalogThread.Images, err = thread.GetReplyFileCount(); err != nil {
|
||||||
|
|
||||||
fileCount, err := gcsql.GetReplyFileCount(op.ID)
|
|
||||||
if err != nil {
|
|
||||||
gcutil.LogError(err).
|
gcutil.LogError(err).
|
||||||
Str("boardDir", board.Dir).
|
Str("boardDir", board.Dir).
|
||||||
Int("op", op.ID).
|
Int("threadID", thread.ID).
|
||||||
Msg("Failed getting file count")
|
Msg("Failed getting file count")
|
||||||
return fmt.Errorf("Error getting file count to /%s/%d: %s", board.Dir, op.ID, err.Error())
|
return errors.New("Error getting file count")
|
||||||
}
|
}
|
||||||
thread.NumImages = fileCount
|
|
||||||
|
|
||||||
thread.OP = *op
|
var maxRepliesOnBoardPage int
|
||||||
|
postCfg := config.GetBoardConfig(board.Dir).PostConfig
|
||||||
var numRepliesOnBoardPage int
|
if thread.Stickied {
|
||||||
postCfg := config.GetBoardConfig("").PostConfig
|
|
||||||
if op.Stickied {
|
|
||||||
// If the thread is stickied, limit replies on the archive page to the
|
// If the thread is stickied, limit replies on the archive page to the
|
||||||
// configured value for stickied threads.
|
// configured value for stickied threads.
|
||||||
numRepliesOnBoardPage = postCfg.StickyRepliesOnBoardPage
|
maxRepliesOnBoardPage = postCfg.StickyRepliesOnBoardPage
|
||||||
} else {
|
} else {
|
||||||
// Otherwise, limit the replies to the configured value for normal threads.
|
// Otherwise, limit the replies to the configured value for normal threads.
|
||||||
numRepliesOnBoardPage = postCfg.RepliesOnBoardPage
|
maxRepliesOnBoardPage = postCfg.RepliesOnBoardPage
|
||||||
}
|
}
|
||||||
|
catalogThread.Replies, err = thread.GetReplyCount()
|
||||||
postsInThread, err = gcsql.GetExistingRepliesLimitedRev(op.ID, numRepliesOnBoardPage)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
gcutil.LogError(err).
|
gcutil.LogError(err).
|
||||||
Str("boardDir", board.Dir).
|
Str("boardDir", board.Dir).
|
||||||
Int("op", op.ID).
|
Int("threadID", thread.ID).
|
||||||
Msg("Failed getting thread posts")
|
Msg("Failed getting reply count")
|
||||||
return fmt.Errorf("Error getting posts in /%s/%d: %s", board.Dir, op.ID, err.Error())
|
return errors.New("Error getting reply count: " + err.Error())
|
||||||
|
}
|
||||||
|
catalogThread.posts, err = thread.GetPosts(false, true, maxRepliesOnBoardPage)
|
||||||
|
if err != nil {
|
||||||
|
gcutil.LogError(err).
|
||||||
|
Int("threadid", thread.ID).
|
||||||
|
Str("boardDir", board.Dir).
|
||||||
|
Msg("Failed getting replies")
|
||||||
|
return errors.New("Failed getting replies: " + err.Error())
|
||||||
|
}
|
||||||
|
catalogThread.uploads, err = thread.GetUploads()
|
||||||
|
if err != nil {
|
||||||
|
gcutil.LogError(err).
|
||||||
|
Int("threadid", thread.ID).
|
||||||
|
Str("boardDir", board.Dir).
|
||||||
|
Msg("Failed getting thread uploads")
|
||||||
|
return errors.New("Failed getting thread uploads: " + err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
var reversedPosts []gcsql.Post
|
var imagesOnBoardPage int
|
||||||
for i := len(postsInThread); i > 0; i-- {
|
for _, upload := range catalogThread.uploads {
|
||||||
reversedPosts = append(reversedPosts, postsInThread[i-1])
|
for _, post := range catalogThread.posts {
|
||||||
}
|
if post.ID == upload.PostID {
|
||||||
|
imagesOnBoardPage++
|
||||||
if len(postsInThread) > 0 {
|
|
||||||
// Store the posts to show on board page
|
|
||||||
//thread.BoardReplies = postsInThread
|
|
||||||
thread.BoardReplies = reversedPosts
|
|
||||||
|
|
||||||
// Count number of images on board page
|
|
||||||
imageCount := 0
|
|
||||||
for p := range postsInThread {
|
|
||||||
reply := &postsInThread[p]
|
|
||||||
if reply.Filesize != 0 {
|
|
||||||
imageCount++
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Then calculate number of omitted images.
|
|
||||||
thread.OmittedImages = thread.NumImages - imageCount
|
|
||||||
}
|
}
|
||||||
|
if catalogThread.Replies > maxRepliesOnBoardPage {
|
||||||
|
catalogThread.OmittedPosts = catalogThread.Replies - len(catalogThread.posts)
|
||||||
|
catalogThread.OmittedImages = len(catalogThread.uploads) - imagesOnBoardPage
|
||||||
|
}
|
||||||
|
catalogThread.OmittedPosts = catalogThread.Replies - len(catalogThread.posts)
|
||||||
|
|
||||||
// Add thread struct to appropriate list
|
// Add thread struct to appropriate list
|
||||||
if op.Stickied {
|
if thread.Stickied {
|
||||||
stickiedThreads = append(stickiedThreads, thread)
|
stickiedThreads = append(stickiedThreads, thread)
|
||||||
} else {
|
} else {
|
||||||
nonStickiedThreads = append(nonStickiedThreads, thread)
|
nonStickiedThreads = append(nonStickiedThreads, thread)
|
||||||
}
|
}
|
||||||
|
catalogThreads = append(catalogThreads, catalogThread)
|
||||||
}
|
}
|
||||||
|
|
||||||
criticalCfg := config.GetSystemCriticalConfig()
|
criticalCfg := config.GetSystemCriticalConfig()
|
||||||
gcutil.DeleteMatchingFiles(path.Join(criticalCfg.DocumentRoot, board.Dir), "\\d.html$")
|
gcutil.DeleteMatchingFiles(path.Join(criticalCfg.DocumentRoot, board.Dir), "\\d.html$")
|
||||||
// Order the threads, stickied threads first, then nonstickied threads.
|
// Order the threads, stickied threads first, then nonstickied threads.
|
||||||
|
@ -142,10 +139,11 @@ func BuildBoardPages(board *gcsql.Board) error {
|
||||||
// If there are no posts on the board
|
// If there are no posts on the board
|
||||||
var boardPageFile *os.File
|
var boardPageFile *os.File
|
||||||
if len(threads) == 0 {
|
if len(threads) == 0 {
|
||||||
board.CurrentPage = 1
|
catalog.currentPage = 1
|
||||||
|
|
||||||
// Open 1.html for writing to the first page.
|
// Open 1.html for writing to the first page.
|
||||||
boardPageFile, err = os.OpenFile(path.Join(criticalCfg.DocumentRoot, board.Dir, "1.html"), os.O_CREATE|os.O_RDWR|os.O_TRUNC, 0777)
|
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 {
|
if err != nil {
|
||||||
gcutil.LogError(err).
|
gcutil.LogError(err).
|
||||||
Str("boardDir", board.Dir).
|
Str("boardDir", board.Dir).
|
||||||
|
@ -175,11 +173,10 @@ func BuildBoardPages(board *gcsql.Board) error {
|
||||||
|
|
||||||
// Create the archive pages.
|
// Create the archive pages.
|
||||||
boardCfg := config.GetBoardConfig(board.Dir)
|
boardCfg := config.GetBoardConfig(board.Dir)
|
||||||
threadPages = paginate(boardCfg.ThreadsPerPage, threads)
|
catalog.fillPages(boardCfg.ThreadsPerPage, catalogThreads)
|
||||||
board.NumPages = len(threadPages)
|
|
||||||
|
|
||||||
// Create array of page wrapper objects, and open the file.
|
// Create array of page wrapper objects, and open the file.
|
||||||
var pagesArr boardCatalog
|
var catalogPages boardCatalog
|
||||||
|
|
||||||
catalogJSONFile, err := os.OpenFile(path.Join(criticalCfg.DocumentRoot, board.Dir, "catalog.json"), os.O_CREATE|os.O_RDWR|os.O_TRUNC, 0777)
|
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 {
|
if err != nil {
|
||||||
|
@ -191,11 +188,11 @@ func BuildBoardPages(board *gcsql.Board) error {
|
||||||
}
|
}
|
||||||
defer catalogJSONFile.Close()
|
defer catalogJSONFile.Close()
|
||||||
|
|
||||||
currentBoardPage := board.CurrentPage
|
// currentBoardPage := catalog.currentPage
|
||||||
for _, pageThreads := range threadPages {
|
for _, page := range catalog.pages {
|
||||||
board.CurrentPage++
|
catalog.currentPage++
|
||||||
var currentPageFilepath string
|
var currentPageFilepath string
|
||||||
pageFilename := strconv.Itoa(board.CurrentPage) + ".html"
|
pageFilename := strconv.Itoa(catalog.currentPage) + ".html"
|
||||||
currentPageFilepath = path.Join(criticalCfg.DocumentRoot, board.Dir, pageFilename)
|
currentPageFilepath = path.Join(criticalCfg.DocumentRoot, board.Dir, pageFilename)
|
||||||
currentPageFile, err = os.OpenFile(currentPageFilepath, os.O_CREATE|os.O_RDWR|os.O_TRUNC, 0777)
|
currentPageFile, err = os.OpenFile(currentPageFilepath, os.O_CREATE|os.O_RDWR|os.O_TRUNC, 0777)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -212,11 +209,11 @@ func BuildBoardPages(board *gcsql.Board) error {
|
||||||
"webroot": criticalCfg.WebRoot,
|
"webroot": criticalCfg.WebRoot,
|
||||||
"boards": gcsql.AllBoards,
|
"boards": gcsql.AllBoards,
|
||||||
"sections": gcsql.AllSections,
|
"sections": gcsql.AllSections,
|
||||||
"threads": pageThreads,
|
"threads": page.Threads,
|
||||||
"board": board,
|
"board": board,
|
||||||
"board_config": config.GetBoardConfig(board.Dir),
|
"board_config": boardCfg,
|
||||||
"posts": []interface{}{
|
"posts": []interface{}{
|
||||||
gcsql.Post{BoardID: board.ID},
|
gcsql.Post{},
|
||||||
},
|
},
|
||||||
}, currentPageFile, "text/html"); err != nil {
|
}, currentPageFile, "text/html"); err != nil {
|
||||||
gcutil.LogError(err).
|
gcutil.LogError(err).
|
||||||
|
@ -226,15 +223,15 @@ func BuildBoardPages(board *gcsql.Board) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Collect up threads for this page.
|
// Collect up threads for this page.
|
||||||
pageMap := make(map[string]interface{})
|
page := catalogPage{}
|
||||||
pageMap["page"] = board.CurrentPage
|
page.PageNum = catalog.currentPage
|
||||||
pageMap["threads"] = pageThreads
|
// page.Threads = page.Threads
|
||||||
pagesArr = append(pagesArr, pageMap)
|
catalogPages.pages = append(catalogPages.pages, page)
|
||||||
}
|
}
|
||||||
board.CurrentPage = currentBoardPage
|
// board.CurrentPage = currentBoardPage
|
||||||
|
|
||||||
var catalogJSON []byte
|
var catalogJSON []byte
|
||||||
if catalogJSON, err = json.Marshal(pagesArr); err != nil {
|
if catalogJSON, err = json.Marshal(catalog.pages); err != nil {
|
||||||
gcutil.LogError(err).
|
gcutil.LogError(err).
|
||||||
Str("boardDir", board.Dir).
|
Str("boardDir", board.Dir).
|
||||||
Msg("Failed to marshal to JSON")
|
Msg("Failed to marshal to JSON")
|
||||||
|
@ -258,23 +255,24 @@ func BuildBoards(verbose bool, which ...int) error {
|
||||||
if which == nil {
|
if which == nil {
|
||||||
boards = gcsql.AllBoards
|
boards = gcsql.AllBoards
|
||||||
} else {
|
} else {
|
||||||
for b, id := range which {
|
for _, boardID := range which {
|
||||||
boards = append(boards, gcsql.Board{})
|
board, err := gcsql.GetBoardFromID(boardID)
|
||||||
if err = boards[b].PopulateData(id); err != nil {
|
if err != nil {
|
||||||
gcutil.LogError(err).
|
gcutil.LogError(err).
|
||||||
Int("boardid", id).
|
Int("boardid", boardID).
|
||||||
Msg("Unable to get board information")
|
Msg("Unable to get board information")
|
||||||
return fmt.Errorf("Error getting board information (ID: %d): %s", id, err.Error())
|
return fmt.Errorf("Error getting board information (ID: %d): %s", boardID, err.Error())
|
||||||
}
|
}
|
||||||
|
boards = append(boards, *board)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if len(boards) == 0 {
|
if len(boards) == 0 {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
for b := range boards {
|
for _, board := range boards {
|
||||||
board := &boards[b]
|
// board := &boards[b]
|
||||||
if err = buildBoard(board, false, true); err != nil {
|
if err = buildBoard(&board, true); err != nil {
|
||||||
gcutil.LogError(err).
|
gcutil.LogError(err).
|
||||||
Str("boardDir", board.Dir).
|
Str("boardDir", board.Dir).
|
||||||
Msg("Failed building board")
|
Msg("Failed building board")
|
||||||
|
@ -294,8 +292,8 @@ func BuildCatalog(boardID int) string {
|
||||||
return err.Error()
|
return err.Error()
|
||||||
}
|
}
|
||||||
|
|
||||||
var board gcsql.Board
|
board, err := gcsql.GetBoardFromID(boardID)
|
||||||
if err = board.PopulateData(boardID); err != nil {
|
if err != nil {
|
||||||
gcutil.LogError(err).
|
gcutil.LogError(err).
|
||||||
Int("boardid", boardID).
|
Int("boardid", boardID).
|
||||||
Msg("Unable to get board information")
|
Msg("Unable to get board information")
|
||||||
|
@ -311,12 +309,7 @@ func BuildCatalog(boardID int) string {
|
||||||
return fmt.Sprintf("Failed opening /%s/catalog.html: %s<br/>", board.Dir, err.Error())
|
return fmt.Sprintf("Failed opening /%s/catalog.html: %s<br/>", board.Dir, err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
threadOPs, err := gcsql.GetTopPosts(boardID)
|
threadOPs, err := gcsql.GetBoardTopPosts(boardID, true)
|
||||||
// threadOPs, err := getPostArr(map[string]interface{}{
|
|
||||||
// "boardid": boardID,
|
|
||||||
// "parentid": 0,
|
|
||||||
// "deleted_timestamp": nilTimestamp,
|
|
||||||
// }, "ORDER BY bumped ASC")
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
gcutil.LogError(err).
|
gcutil.LogError(err).
|
||||||
Str("building", "catalog").
|
Str("building", "catalog").
|
||||||
|
@ -341,9 +334,8 @@ func BuildCatalog(boardID int) string {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Build builds the board and its thread files
|
// Build builds the board and its thread files
|
||||||
// if newBoard is true, it adds a row to DBPREFIXboards and fails if it exists
|
|
||||||
// if force is true, it doesn't fail if the directories exist but does fail if it is a file
|
// 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, newBoard, force bool) error {
|
func buildBoard(board *gcsql.Board, force bool) error {
|
||||||
var err error
|
var err error
|
||||||
if board.Dir == "" {
|
if board.Dir == "" {
|
||||||
return ErrNoBoardDir
|
return ErrNoBoardDir
|
||||||
|
@ -352,16 +344,6 @@ func buildBoard(board *gcsql.Board, newBoard, force bool) error {
|
||||||
return ErrNoBoardTitle
|
return ErrNoBoardTitle
|
||||||
}
|
}
|
||||||
|
|
||||||
if newBoard {
|
|
||||||
board.CreatedOn = time.Now()
|
|
||||||
err := gcsql.CreateBoard(board)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
} else if err = board.UpdateID(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
dirPath := board.AbsolutePath()
|
dirPath := board.AbsolutePath()
|
||||||
resPath := board.AbsolutePath("res")
|
resPath := board.AbsolutePath("res")
|
||||||
srcPath := board.AbsolutePath("src")
|
srcPath := board.AbsolutePath("src")
|
||||||
|
|
|
@ -7,6 +7,8 @@ import (
|
||||||
"io"
|
"io"
|
||||||
"os"
|
"os"
|
||||||
"path"
|
"path"
|
||||||
|
"regexp"
|
||||||
|
"strconv"
|
||||||
|
|
||||||
"github.com/gochan-org/gochan/pkg/config"
|
"github.com/gochan-org/gochan/pkg/config"
|
||||||
"github.com/gochan-org/gochan/pkg/gcsql"
|
"github.com/gochan-org/gochan/pkg/gcsql"
|
||||||
|
@ -15,6 +17,64 @@ import (
|
||||||
"github.com/gochan-org/gochan/pkg/serverutil"
|
"github.com/gochan-org/gochan/pkg/serverutil"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
bbcodeTagRE = regexp.MustCompile(`\[/?[^\[\]\s]+\]`)
|
||||||
|
)
|
||||||
|
|
||||||
|
type recentPost struct {
|
||||||
|
Board string
|
||||||
|
URL string
|
||||||
|
ThumbURL string
|
||||||
|
FileDeleted bool
|
||||||
|
MessageSample string
|
||||||
|
}
|
||||||
|
|
||||||
|
func getRecentPosts() ([]recentPost, error) {
|
||||||
|
siteCfg := config.GetSiteConfig()
|
||||||
|
query := `SELECT id, thread_id AS threadid, message_raw,
|
||||||
|
(SELECT dir FROM DBPREFIXboards WHERE id = (
|
||||||
|
SELECT board_id FROM DBPREFIXthreads WHERE id = threadid)
|
||||||
|
) AS board,
|
||||||
|
COALESCE(
|
||||||
|
(SELECT filename FROM DBPREFIXfiles WHERE post_id = DBPREFIXposts.id LIMIT 1),
|
||||||
|
"") AS filename,
|
||||||
|
(SELECT id FROM DBPREFIXposts WHERE is_top_post = TRUE AND thread_id = threadid) AS top_post
|
||||||
|
FROM DBPREFIXposts WHERE is_deleted = FALSE LIMIT ` + strconv.Itoa(siteCfg.MaxRecentPosts)
|
||||||
|
rows, err := gcsql.QuerySQL(query)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer rows.Close()
|
||||||
|
var recentPosts []recentPost
|
||||||
|
|
||||||
|
for rows.Next() {
|
||||||
|
var post recentPost
|
||||||
|
var id, threadID, topPostID string
|
||||||
|
var message, boardDir, filename string
|
||||||
|
err = rows.Scan(&id, &threadID, &message, &boardDir, &filename, &topPostID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if filename == "" && !siteCfg.RecentPostsWithNoFile {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
message = bbcodeTagRE.ReplaceAllString(message, "")
|
||||||
|
if len(message) > 40 {
|
||||||
|
message = message[:37] + "..."
|
||||||
|
}
|
||||||
|
post = recentPost{
|
||||||
|
Board: boardDir,
|
||||||
|
URL: config.WebPath(boardDir, "res", topPostID+".html") + "#" + id,
|
||||||
|
ThumbURL: config.WebPath(boardDir, "thumb", gcutil.GetThumbnailPath("post", filename)),
|
||||||
|
FileDeleted: filename == "deleted",
|
||||||
|
MessageSample: message,
|
||||||
|
}
|
||||||
|
|
||||||
|
recentPosts = append(recentPosts, post)
|
||||||
|
}
|
||||||
|
return recentPosts, nil
|
||||||
|
}
|
||||||
|
|
||||||
// BuildFrontPage builds the front page using templates/front.html
|
// BuildFrontPage builds the front page using templates/front.html
|
||||||
func BuildFrontPage() error {
|
func BuildFrontPage() error {
|
||||||
err := gctemplates.InitTemplates("front")
|
err := gctemplates.InitTemplates("front")
|
||||||
|
@ -25,8 +85,8 @@ func BuildFrontPage() error {
|
||||||
}
|
}
|
||||||
criticalCfg := config.GetSystemCriticalConfig()
|
criticalCfg := config.GetSystemCriticalConfig()
|
||||||
os.Remove(path.Join(criticalCfg.DocumentRoot, "index.html"))
|
os.Remove(path.Join(criticalCfg.DocumentRoot, "index.html"))
|
||||||
frontFile, err := os.OpenFile(path.Join(criticalCfg.DocumentRoot, "index.html"), os.O_CREATE|os.O_RDWR|os.O_TRUNC, 0777)
|
|
||||||
|
|
||||||
|
frontFile, err := os.OpenFile(path.Join(criticalCfg.DocumentRoot, "index.html"), os.O_CREATE|os.O_RDWR|os.O_TRUNC, 0777)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
gcutil.LogError(err).
|
gcutil.LogError(err).
|
||||||
Str("building", "front").Send()
|
Str("building", "front").Send()
|
||||||
|
@ -34,21 +94,15 @@ func BuildFrontPage() error {
|
||||||
}
|
}
|
||||||
defer frontFile.Close()
|
defer frontFile.Close()
|
||||||
|
|
||||||
var recentPostsArr []gcsql.RecentPost
|
var recentPostsArr []recentPost
|
||||||
siteCfg := config.GetSiteConfig()
|
siteCfg := config.GetSiteConfig()
|
||||||
recentPostsArr, err = gcsql.GetRecentPostsGlobal(siteCfg.MaxRecentPosts, !siteCfg.RecentPostsWithNoFile)
|
recentPostsArr, err = getRecentPosts()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
gcutil.LogError(err).
|
gcutil.LogError(err).
|
||||||
Str("building", "recent").Send()
|
Str("building", "recent").Send()
|
||||||
return errors.New("Failed loading recent posts: " + err.Error())
|
return errors.New("Failed loading recent posts: " + err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
for b := range gcsql.AllBoards {
|
|
||||||
if gcsql.AllBoards[b].Section == 0 {
|
|
||||||
gcsql.AllBoards[b].Section = 1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if err = serverutil.MinifyTemplate(gctemplates.FrontPage, map[string]interface{}{
|
if err = serverutil.MinifyTemplate(gctemplates.FrontPage, map[string]interface{}{
|
||||||
"webroot": criticalCfg.WebRoot,
|
"webroot": criticalCfg.WebRoot,
|
||||||
"site_config": siteCfg,
|
"site_config": siteCfg,
|
||||||
|
@ -79,18 +133,8 @@ func BuildBoardListJSON() error {
|
||||||
"boards": {},
|
"boards": {},
|
||||||
}
|
}
|
||||||
|
|
||||||
boardCfg := config.GetBoardConfig("")
|
// TODO: properly check if the board is in a hidden section
|
||||||
// Our cooldowns are site-wide currently.
|
boardsMap["boards"] = gcsql.AllBoards
|
||||||
cooldowns := gcsql.BoardCooldowns{
|
|
||||||
NewThread: boardCfg.NewThreadDelay,
|
|
||||||
Reply: boardCfg.ReplyDelay,
|
|
||||||
ImageReply: boardCfg.ReplyDelay}
|
|
||||||
|
|
||||||
for b := range gcsql.AllBoards {
|
|
||||||
gcsql.AllBoards[b].Cooldowns = cooldowns
|
|
||||||
boardsMap["boards"] = append(boardsMap["boards"], gcsql.AllBoards[b])
|
|
||||||
}
|
|
||||||
|
|
||||||
boardJSON, err := json.Marshal(boardsMap)
|
boardJSON, err := json.Marshal(boardsMap)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
gcutil.LogError(err).Str("building", "boards.json").Send()
|
gcutil.LogError(err).Str("building", "boards.json").Send()
|
||||||
|
@ -166,28 +210,3 @@ func BuildJS() error {
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// paginate returns a 2d array of a specified interface from a 1d array passed in,
|
|
||||||
// with a specified number of values per array in the 2d array.
|
|
||||||
// interfaceLength is the number of interfaces per array in the 2d array (e.g, threads per page)
|
|
||||||
// interf is the array of interfaces to be split up.
|
|
||||||
func paginate(interfaceLength int, interf []interface{}) [][]interface{} {
|
|
||||||
// paginatedInterfaces = the finished interface array
|
|
||||||
// numArrays = the current number of arrays (before remainder overflow)
|
|
||||||
// interfacesRemaining = if greater than 0, these are the remaining interfaces
|
|
||||||
// that will be added to the super-interface
|
|
||||||
|
|
||||||
var paginatedInterfaces [][]interface{}
|
|
||||||
numArrays := len(interf) / interfaceLength
|
|
||||||
interfacesRemaining := len(interf) % interfaceLength
|
|
||||||
currentInterface := 0
|
|
||||||
for l := 0; l < numArrays; l++ {
|
|
||||||
paginatedInterfaces = append(paginatedInterfaces,
|
|
||||||
interf[currentInterface:currentInterface+interfaceLength])
|
|
||||||
currentInterface += interfaceLength
|
|
||||||
}
|
|
||||||
if interfacesRemaining > 0 {
|
|
||||||
paginatedInterfaces = append(paginatedInterfaces, interf[len(interf)-interfacesRemaining:])
|
|
||||||
}
|
|
||||||
return paginatedInterfaces
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,13 +1,17 @@
|
||||||
package building
|
package building
|
||||||
|
|
||||||
|
import "github.com/gochan-org/gochan/pkg/gcsql"
|
||||||
|
|
||||||
type catalogThreadData struct {
|
type catalogThreadData struct {
|
||||||
Replies int `json:"replies"`
|
Replies int `json:"replies"`
|
||||||
Images int `json:"images"`
|
Images int `json:"images"`
|
||||||
OmittedPosts int `json:"omitted_posts"`
|
OmittedPosts int `json:"omitted_posts"` // posts in the thread but not shown on the board page
|
||||||
OmittedImages int `json:"omitted_images"`
|
OmittedImages int `json:"omitted_images"` // uploads in the thread but not shown on the board page
|
||||||
Sticky int `json:"sticky"`
|
Sticky int `json:"sticky"`
|
||||||
Locked int `json:"locked"`
|
Locked int `json:"locked"`
|
||||||
numPages int
|
// numPages int
|
||||||
|
posts []gcsql.Post
|
||||||
|
uploads []gcsql.Upload
|
||||||
}
|
}
|
||||||
|
|
||||||
type catalogPage struct {
|
type catalogPage struct {
|
||||||
|
@ -15,4 +19,51 @@ type catalogPage struct {
|
||||||
Threads []catalogThreadData `json:"threads"`
|
Threads []catalogThreadData `json:"threads"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type boardCatalog []catalogPage
|
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 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
|
||||||
|
// }
|
||||||
|
|
|
@ -2,6 +2,7 @@ package building
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"path"
|
"path"
|
||||||
|
@ -21,11 +22,11 @@ func BuildThreads(all bool, boardid, threadid int) error {
|
||||||
var threads []gcsql.Post
|
var threads []gcsql.Post
|
||||||
var err error
|
var err error
|
||||||
if all {
|
if all {
|
||||||
threads, err = gcsql.GetTopPostsNoSort(boardid)
|
threads, err = gcsql.GetBoardTopPosts(boardid, true)
|
||||||
} else {
|
} else {
|
||||||
var post gcsql.Post
|
var post *gcsql.Post
|
||||||
post, err = gcsql.GetSpecificTopPost(threadid)
|
post, err = gcsql.GetThreadTopPost(threadid)
|
||||||
threads = []gcsql.Post{post}
|
threads = []gcsql.Post{*post}
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -40,26 +41,41 @@ func BuildThreads(all bool, boardid, threadid int) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// BuildThreadPages builds the pages for a thread given by a Post object.
|
// BuildThreadPages builds the pages for a thread given the top post. It fails if op is not the top post
|
||||||
func BuildThreadPages(op *gcsql.Post) error {
|
func BuildThreadPages(op *gcsql.Post) error {
|
||||||
|
if !op.IsTopPost {
|
||||||
|
return gcsql.ErrNotTopPost
|
||||||
|
}
|
||||||
err := gctemplates.InitTemplates("threadpage")
|
err := gctemplates.InitTemplates("threadpage")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
var replies []gcsql.Post
|
|
||||||
var threadPageFile *os.File
|
var threadPageFile *os.File
|
||||||
var board gcsql.Board
|
|
||||||
if err = board.PopulateData(op.BoardID); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
replies, err = gcsql.GetExistingReplies(op.ID)
|
board, err := op.GetBoard()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
gcutil.LogError(err).
|
gcutil.LogError(err).
|
||||||
Str("building", "thread").
|
Str("building", "thread").
|
||||||
Int("threadid", op.ID).Send()
|
Int("postid", op.ID).
|
||||||
return fmt.Errorf("failed building thread %d: %s", op.ID, err.Error())
|
Int("threadid", op.ThreadID).
|
||||||
|
Msg("failed building thread")
|
||||||
|
return errors.New("failed building thread: " + err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
thread, err := gcsql.GetThread(op.ThreadID)
|
||||||
|
if err != nil {
|
||||||
|
gcutil.LogError(err).
|
||||||
|
Str("building", "thread").
|
||||||
|
Int("threadid", op.ThreadID).
|
||||||
|
Msg("Unable to get thread info")
|
||||||
|
return errors.New("unable to get thread info: " + err.Error())
|
||||||
|
}
|
||||||
|
posts, err := thread.GetPosts(false, false, 0)
|
||||||
|
if err != nil {
|
||||||
|
gcutil.LogError(err).
|
||||||
|
Str("building", "thread").
|
||||||
|
Int("threadid", thread.ID).Send()
|
||||||
|
return errors.New("failed building thread: " + err.Error())
|
||||||
}
|
}
|
||||||
criticalCfg := config.GetSystemCriticalConfig()
|
criticalCfg := config.GetSystemCriticalConfig()
|
||||||
os.Remove(path.Join(criticalCfg.DocumentRoot, board.Dir, "res", strconv.Itoa(op.ID)+".html"))
|
os.Remove(path.Join(criticalCfg.DocumentRoot, board.Dir, "res", strconv.Itoa(op.ID)+".html"))
|
||||||
|
@ -82,45 +98,45 @@ func BuildThreadPages(op *gcsql.Post) error {
|
||||||
"board": board,
|
"board": board,
|
||||||
"board_config": config.GetBoardConfig(board.Dir),
|
"board_config": config.GetBoardConfig(board.Dir),
|
||||||
"sections": gcsql.AllSections,
|
"sections": gcsql.AllSections,
|
||||||
"posts": replies,
|
"posts": posts[1:],
|
||||||
"op": op,
|
"op": op,
|
||||||
}, threadPageFile, "text/html"); err != nil {
|
}, threadPageFile, "text/html"); err != nil {
|
||||||
gcutil.LogError(err).
|
gcutil.LogError(err).
|
||||||
Str("building", "thread").
|
Str("building", "thread").
|
||||||
Str("boardDir", board.Dir).
|
Str("boardDir", board.Dir).
|
||||||
Int("threadid", op.ID).
|
Int("threadid", thread.ID).
|
||||||
Msg("Failed building threadpage")
|
Msg("Failed building threadpage")
|
||||||
return fmt.Errorf("failed building /%s/res/%d threadpage: %s", board.Dir, op.ID, err.Error())
|
return fmt.Errorf("failed building /%s/res/%d threadpage: %s", board.Dir, posts[0].ID, err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
// Put together the thread JSON
|
// Put together the thread JSON
|
||||||
threadJSONFile, err := os.OpenFile(path.Join(criticalCfg.DocumentRoot, board.Dir, "res", strconv.Itoa(op.ID)+".json"), os.O_CREATE|os.O_RDWR|os.O_TRUNC, 0777)
|
threadJSONFile, err := os.OpenFile(
|
||||||
|
path.Join(criticalCfg.DocumentRoot, board.Dir, "res", strconv.Itoa(posts[0].ID)+".json"),
|
||||||
|
os.O_CREATE|os.O_RDWR|os.O_TRUNC, 0777)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
gcutil.LogError(err).
|
gcutil.LogError(err).
|
||||||
Str("boardDir", board.Dir).
|
Str("boardDir", board.Dir).
|
||||||
Int("threadid", op.ID).Send()
|
Int("threadid", thread.ID).
|
||||||
return fmt.Errorf("failed opening /%s/res/%d.json: %s", board.Dir, op.ID, err.Error())
|
Int("op", posts[0].ID).Send()
|
||||||
|
return fmt.Errorf("failed opening /%s/res/%d.json: %s", board.Dir, posts[0].ID, err.Error())
|
||||||
}
|
}
|
||||||
defer threadJSONFile.Close()
|
defer threadJSONFile.Close()
|
||||||
|
|
||||||
threadMap := make(map[string][]gcsql.Post)
|
threadMap := make(map[string][]gcsql.Post)
|
||||||
|
|
||||||
// Handle the OP, of type *Post
|
threadMap["posts"] = posts
|
||||||
threadMap["posts"] = []gcsql.Post{*op}
|
|
||||||
|
|
||||||
// Iterate through each reply, which are of type Post
|
|
||||||
threadMap["posts"] = append(threadMap["posts"], replies...)
|
|
||||||
threadJSON, err := json.Marshal(threadMap)
|
threadJSON, err := json.Marshal(threadMap)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
gcutil.LogError(err).Send()
|
gcutil.LogError(err).Send()
|
||||||
return fmt.Errorf("failed to marshal to JSON: %s", err.Error())
|
return errors.New("failed to marshal to JSON: " + err.Error())
|
||||||
}
|
}
|
||||||
if _, err = threadJSONFile.Write(threadJSON); err != nil {
|
if _, err = threadJSONFile.Write(threadJSON); err != nil {
|
||||||
gcutil.LogError(err).
|
gcutil.LogError(err).
|
||||||
Str("boardDir", board.Dir).
|
Str("boardDir", board.Dir).
|
||||||
Int("threadid", op.ID).Send()
|
Int("threadid", thread.ID).
|
||||||
|
Int("op", posts[0].ID).Send()
|
||||||
|
|
||||||
return fmt.Errorf("failed writing /%s/res/%d.json: %s", board.Dir, op.ID, err.Error())
|
return fmt.Errorf("failed writing /%s/res/%d.json: %s", board.Dir, posts[0].ID, err.Error())
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,38 +18,41 @@ func tempCleaner() {
|
||||||
case <-tempCleanerTicker.C:
|
case <-tempCleanerTicker.C:
|
||||||
for p := range gcsql.TempPosts {
|
for p := range gcsql.TempPosts {
|
||||||
post := &gcsql.TempPosts[p]
|
post := &gcsql.TempPosts[p]
|
||||||
if !time.Now().After(post.Timestamp.Add(time.Minute * 5)) {
|
if !time.Now().After(post.CreatedOn.Add(time.Minute * 5)) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
// temporary post is >= 5 minutes, time to prune it
|
// temporary post is >= 5 minutes, time to prune it
|
||||||
gcsql.TempPosts[p] = gcsql.TempPosts[len(gcsql.TempPosts)-1]
|
gcsql.TempPosts[p] = gcsql.TempPosts[len(gcsql.TempPosts)-1]
|
||||||
gcsql.TempPosts = gcsql.TempPosts[:len(gcsql.TempPosts)-1]
|
gcsql.TempPosts = gcsql.TempPosts[:len(gcsql.TempPosts)-1]
|
||||||
if post.FilenameOriginal == "" {
|
upload, err := post.GetUpload()
|
||||||
|
if err != nil {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
var board gcsql.Board
|
if upload.OriginalFilename == "" {
|
||||||
err := board.PopulateData(post.BoardID)
|
continue
|
||||||
|
}
|
||||||
|
board, err := post.GetBoard()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
systemCritical := config.GetSystemCriticalConfig()
|
systemCritical := config.GetSystemCriticalConfig()
|
||||||
fileSrc := path.Join(systemCritical.DocumentRoot, board.Dir, "src", post.FilenameOriginal)
|
fileSrc := path.Join(systemCritical.DocumentRoot, board.Dir, "src", upload.OriginalFilename)
|
||||||
if err = os.Remove(fileSrc); err != nil {
|
if err = os.Remove(fileSrc); err != nil {
|
||||||
gcutil.LogError(err).
|
gcutil.LogError(err).
|
||||||
Str("subject", "tempUpload").
|
Str("subject", "tempUpload").
|
||||||
Str("filePath", fileSrc).Send()
|
Str("filePath", fileSrc).Send()
|
||||||
}
|
}
|
||||||
|
|
||||||
thumbSrc := gcutil.GetThumbnailPath("thread", fileSrc)
|
thumbSrc := upload.ThumbnailPath("thread")
|
||||||
if err = os.Remove(thumbSrc); err != nil {
|
if err = os.Remove(thumbSrc); err != nil {
|
||||||
gcutil.LogError(err).
|
gcutil.LogError(err).
|
||||||
Str("subject", "tempUpload").
|
Str("subject", "tempUpload").
|
||||||
Str("filePath", thumbSrc).Send()
|
Str("filePath", thumbSrc).Send()
|
||||||
}
|
}
|
||||||
|
|
||||||
if post.ParentID == 0 {
|
if post.IsTopPost {
|
||||||
catalogSrc := gcutil.GetThumbnailPath("catalog", fileSrc)
|
catalogSrc := upload.ThumbnailPath("catalog")
|
||||||
if err = os.Remove(catalogSrc); err != nil {
|
if err = os.Remove(catalogSrc); err != nil {
|
||||||
gcutil.LogError(err).
|
gcutil.LogError(err).
|
||||||
Str("subject", "tempUpload").
|
Str("subject", "tempUpload").
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue