mirror of
https://github.com/Eggbertx/gochan.git
synced 2025-08-03 07:36:23 -07:00
HEAVILY simplify template execution
This commit is contained in:
parent
5745428655
commit
ce303c086e
20 changed files with 395 additions and 533 deletions
2
Makefile
2
Makefile
|
@ -3,7 +3,7 @@ GOCHAN_DEBUG=1
|
|||
GOCHAN_VERBOSE=2
|
||||
GOCHAN_VERBOSITY=0 # This is set by "make release/debug/verbose"
|
||||
|
||||
GOCHAN_VERSION=1.9.1
|
||||
GOCHAN_VERSION=1.9.2
|
||||
GOCHAN_BUILDTIME=$(shell date +%y%m%d.%H%M)
|
||||
ifeq ($(GOOS), windows)
|
||||
GOCHAN_BIN=gochan.exe
|
||||
|
|
2
dist.sh
2
dist.sh
|
@ -1,6 +1,6 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
VERSION=1.9.1
|
||||
VERSION=1.9.2
|
||||
GOOS_ORIG=$GOOS
|
||||
|
||||
function copyStuff {
|
||||
|
|
|
@ -7,6 +7,6 @@
|
|||
<h1>404: File not found</h1>
|
||||
<img src="/error/lol 404.gif" border="0" alt="">
|
||||
<p>The requested file could not be found on this server. Are you just typing random stuff in the address bar? If you followed a link from this site here, then post <a href="/site">here</a></p>
|
||||
<hr><address>http://gochan.org powered by Gochan v1.9.1</address>
|
||||
<hr><address>http://gochan.org powered by Gochan v1.9.2</address>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
@ -7,6 +7,6 @@
|
|||
<h1>500: Internal Server error</h1>
|
||||
<img src="/error/derpy server.gif" border="0" alt="">
|
||||
<p>The server encountered an error while trying to serve the page, and we apologize for the inconvenience. The <a href="https://en.wikipedia.org/wiki/Idiot">system administrator</a> will try to fix things as soon has he/she/it can.</p>
|
||||
<hr><address>http://gochan.org powered by Gochan v1.9.1</address>
|
||||
<hr><address>http://gochan.org powered by Gochan v1.9.2</address>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
@ -1,16 +1,17 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"os"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
// set in build.sh via -ldflags
|
||||
// set in Makefile via -ldflags
|
||||
var version string
|
||||
|
||||
// verbose = 0 for no debugging info. Critical errors and general output only
|
||||
// verbose = 1 for non-critical warnings and important info
|
||||
// verbose = 2 for all debugging/benchmarks/warnings
|
||||
// set in build.sh via -ldflags
|
||||
// set in Makefile via -ldflags
|
||||
var verbosity_str string
|
||||
var buildtime_str string // set in build.sh, format: YRMMDD.HHMM
|
||||
|
||||
|
@ -24,7 +25,11 @@ func main() {
|
|||
connectToSQLServer()
|
||||
|
||||
println(0, "Loading and parsing templates...")
|
||||
initTemplates()
|
||||
if err := initTemplates(); err != nil {
|
||||
println(0, err.Error())
|
||||
os.Exit(2)
|
||||
}
|
||||
|
||||
println(0, "Initializing server...")
|
||||
if db != nil {
|
||||
db.Exec("USE `" + config.DBname + "`;")
|
||||
|
|
|
@ -206,7 +206,7 @@ var manage_functions = map[string]ManageFunction{
|
|||
}
|
||||
manageConfigBuffer := bytes.NewBufferString("")
|
||||
|
||||
if err := renderTemplate(manage_config_tmpl, "manage_config", manageConfigBuffer); err != nil {
|
||||
if err := manage_config_tmpl.Execute(manageConfigBuffer, nil); err != nil {
|
||||
html += handleError(1, err.Error())
|
||||
return
|
||||
}
|
||||
|
@ -733,10 +733,10 @@ var manage_functions = map[string]ManageFunction{
|
|||
}
|
||||
all_sections, _ = getSectionArr("")
|
||||
|
||||
if err := renderTemplate(manage_boards_tmpl, "manage_boards", manageBoardsBuffer,
|
||||
&Wrapper{IName: "board", Data: []interface{}{board}},
|
||||
&Wrapper{IName: "section_arr", Data: all_sections},
|
||||
); err != nil {
|
||||
if err := manage_boards_tmpl.Execute(manageBoardsBuffer, map[string]interface{}{
|
||||
"board": []interface{}{board},
|
||||
"section_arr": all_sections,
|
||||
}); err != nil {
|
||||
html += handleError(1, err.Error())
|
||||
return
|
||||
}
|
||||
|
@ -807,8 +807,7 @@ var manage_functions = map[string]ManageFunction{
|
|||
return
|
||||
}
|
||||
|
||||
for _, postInter := range posts {
|
||||
post := postInter.(PostTable)
|
||||
for _, post := range posts {
|
||||
stmt, err := db.Prepare("UPDATE `" + config.DBprefix + "posts` SET `message` = ? WHERE `id` = ? AND `boardid` = ?")
|
||||
if err != nil {
|
||||
html += handleError(1, err.Error()) + "<br />"
|
||||
|
|
307
src/posting.go
307
src/posting.go
|
@ -68,7 +68,6 @@ func buildBoards(all bool, which int) (html string) {
|
|||
// build archive pages for. The return value is a string of HTML with debug information from the build process.
|
||||
func buildBoardPages(board *BoardsTable) (html string) {
|
||||
// start_time := benchmarkTimer("buildBoard"+strconv.Itoa(board.ID), time.Now(), true)
|
||||
var boardinfo_i []interface{}
|
||||
var current_page_file *os.File
|
||||
var threads []interface{}
|
||||
var thread_pages [][]interface{}
|
||||
|
@ -76,14 +75,10 @@ func buildBoardPages(board *BoardsTable) (html string) {
|
|||
var nonstickied_threads []interface{}
|
||||
|
||||
defer func() {
|
||||
// This function cleans up after we return. If there was an error, it prints on the log and the console.
|
||||
if uhoh, ok := recover().(error); ok {
|
||||
errorLog.Print("buildBoardPages failed: " + uhoh.Error())
|
||||
println(0, "buildBoardPages failed: "+uhoh.Error())
|
||||
}
|
||||
if current_page_file != nil {
|
||||
current_page_file.Close()
|
||||
}
|
||||
// Recover and print, log error (if there is one)
|
||||
/* if errmsg, panicked := recover().(error); panicked {
|
||||
handleError(0, "Recovered from panic: "+errmsg.Error())
|
||||
} */
|
||||
}()
|
||||
|
||||
// Check that the board's configured directory is indeed a directory
|
||||
|
@ -109,20 +104,16 @@ func buildBoardPages(board *BoardsTable) (html string) {
|
|||
}, " ORDER BY `bumped` DESC")
|
||||
if err != nil {
|
||||
html += handleError(1, err.Error()) + "<br />"
|
||||
op_posts = nil //make([]interface{}, 0)
|
||||
op_posts = nil
|
||||
return
|
||||
}
|
||||
|
||||
// For each top level post, start building a Thread struct
|
||||
for _, op_post_i := range op_posts {
|
||||
for _, op := range op_posts {
|
||||
var thread Thread
|
||||
var posts_in_thread []interface{}
|
||||
op_post := op_post_i.(PostTable)
|
||||
var posts_in_thread []PostTable
|
||||
thread.IName = "thread"
|
||||
|
||||
// Store the OP post for this thread
|
||||
//op_post := op_post_i.(PostTable)
|
||||
|
||||
// Get the number of replies to this thread.
|
||||
stmt, err := db.Prepare("SELECT COUNT(*) FROM `" + config.DBprefix + "posts` WHERE `boardid` = ? AND `parentid` = ? AND `deleted_timestamp` = ?")
|
||||
if err != nil {
|
||||
|
@ -130,7 +121,7 @@ func buildBoardPages(board *BoardsTable) (html string) {
|
|||
}
|
||||
defer closeStatement(stmt)
|
||||
|
||||
if err = stmt.QueryRow(board.ID, op_post.ID, nil_timestamp).Scan(&thread.NumReplies); err != nil {
|
||||
if err = stmt.QueryRow(board.ID, op.ID, nil_timestamp).Scan(&thread.NumReplies); err != nil {
|
||||
html += err.Error() + "<br />\n"
|
||||
}
|
||||
|
||||
|
@ -139,15 +130,15 @@ func buildBoardPages(board *BoardsTable) (html string) {
|
|||
if err != nil {
|
||||
html += err.Error() + "<br />\n"
|
||||
}
|
||||
if err = stmt.QueryRow(board.ID, op_post.ID, nil_timestamp).Scan(&thread.NumImages); err != nil {
|
||||
if err = stmt.QueryRow(board.ID, op.ID, nil_timestamp).Scan(&thread.NumImages); err != nil {
|
||||
html += err.Error() + "<br />\n"
|
||||
}
|
||||
|
||||
thread.OP = op_post
|
||||
thread.OP = op
|
||||
|
||||
var numRepliesOnBoardPage int
|
||||
|
||||
if op_post.Stickied {
|
||||
if op.Stickied {
|
||||
// If the thread is stickied, limit replies on the archive page to the
|
||||
// configured value for stickied threads.
|
||||
numRepliesOnBoardPage = config.StickyRepliesOnBoardPage
|
||||
|
@ -158,23 +149,27 @@ func buildBoardPages(board *BoardsTable) (html string) {
|
|||
|
||||
posts_in_thread, err = getPostArr(map[string]interface{}{
|
||||
"boardid": board.ID,
|
||||
"parentid": op_post.ID,
|
||||
"parentid": op.ID,
|
||||
"deleted_timestamp": nil_timestamp,
|
||||
}, fmt.Sprintf(" ORDER BY `id` DESC LIMIT %d", numRepliesOnBoardPage))
|
||||
if err != nil {
|
||||
html += err.Error() + "<br />"
|
||||
}
|
||||
|
||||
posts_in_thread = reverse(posts_in_thread)
|
||||
var reversedPosts []PostTable
|
||||
for i := len(posts_in_thread); i > 0; i-- {
|
||||
reversedPosts = append(reversedPosts, posts_in_thread[i-1])
|
||||
}
|
||||
|
||||
if len(posts_in_thread) > 0 {
|
||||
// Store the posts to show on board page
|
||||
thread.BoardReplies = posts_in_thread
|
||||
//thread.BoardReplies = posts_in_thread
|
||||
thread.BoardReplies = reversedPosts
|
||||
|
||||
// Count number of images on board page
|
||||
image_count := 0
|
||||
for _, reply := range posts_in_thread {
|
||||
if reply.(PostTable).Filesize != 0 {
|
||||
if reply.Filesize != 0 {
|
||||
image_count++
|
||||
}
|
||||
}
|
||||
|
@ -183,7 +178,7 @@ func buildBoardPages(board *BoardsTable) (html string) {
|
|||
}
|
||||
|
||||
// Add thread struct to appropriate list
|
||||
if op_post.Stickied {
|
||||
if op.Stickied {
|
||||
stickied_threads = append(stickied_threads, thread)
|
||||
} else {
|
||||
nonstickied_threads = append(nonstickied_threads, thread)
|
||||
|
@ -197,10 +192,7 @@ func buildBoardPages(board *BoardsTable) (html string) {
|
|||
|
||||
// If there are no posts on the board
|
||||
if len(threads) == 0 {
|
||||
board.CurrentPage = 0
|
||||
boardinfo_i = nil
|
||||
boardinfo_i = append(boardinfo_i, board)
|
||||
|
||||
board.CurrentPage = 1
|
||||
// Open board.html for writing to the first page.
|
||||
board_page_file, err := os.OpenFile(path.Join(config.DocumentRoot, board.Dir, "board.html"), os.O_CREATE|os.O_RDWR|os.O_TRUNC, 0777)
|
||||
if err != nil {
|
||||
|
@ -210,30 +202,13 @@ func buildBoardPages(board *BoardsTable) (html string) {
|
|||
|
||||
// Render board page template to the file,
|
||||
// packaging the board/section list, threads, and board info
|
||||
if err = renderTemplate(img_header_tmpl, "img_header", board_page_file,
|
||||
&Wrapper{IName: "boards_", Data: all_boards},
|
||||
&Wrapper{IName: "sections_w", Data: all_sections},
|
||||
&Wrapper{IName: "posts_w", Data: []interface{}{
|
||||
PostTable{BoardID: board.ID},
|
||||
}},
|
||||
&Wrapper{IName: "op", Data: []interface{}{PostTable{}}},
|
||||
&Wrapper{IName: "board", Data: []interface{}{board}},
|
||||
); err != nil {
|
||||
html += handleError(1, fmt.Sprintf("Failed building /%s/res/%d.html: %s", board.Dir, 0, err.Error())) + "<br />"
|
||||
return
|
||||
}
|
||||
|
||||
if err = renderTemplate(img_boardpage_tmpl, "boardpage", board_page_file,
|
||||
&Wrapper{IName: "boards", Data: all_boards},
|
||||
&Wrapper{IName: "sections", Data: all_sections},
|
||||
&Wrapper{IName: "threads", Data: threads},
|
||||
&Wrapper{IName: "boardinfo", Data: boardinfo_i},
|
||||
); err != nil {
|
||||
html += handleError(1, "Failed building /"+board.Dir+"/: "+err.Error()) + "<br />"
|
||||
return
|
||||
}
|
||||
|
||||
if err = renderTemplate(global_footer_tmpl, "global_footer", board_page_file); err != nil {
|
||||
if err = img_boardpage_tmpl.Execute(board_page_file, map[string]interface{}{
|
||||
"config": config,
|
||||
"boards": all_boards,
|
||||
"sections": all_sections,
|
||||
"threads": threads,
|
||||
"board": board,
|
||||
}); err != nil {
|
||||
html += handleError(1, "Failed building /"+board.Dir+"/: "+err.Error()) + "<br />"
|
||||
return
|
||||
}
|
||||
|
@ -244,7 +219,6 @@ func buildBoardPages(board *BoardsTable) (html string) {
|
|||
} else {
|
||||
// Create the archive pages.
|
||||
thread_pages = paginate(config.ThreadsPerPage_img, threads)
|
||||
|
||||
board.NumPages = len(thread_pages) - 1
|
||||
|
||||
// Create array of page wrapper objects, and open the file.
|
||||
|
@ -256,67 +230,49 @@ func buildBoardPages(board *BoardsTable) (html string) {
|
|||
return
|
||||
}
|
||||
defer closeFile(catalog_json_file)
|
||||
|
||||
for page_num, page_threads := range thread_pages {
|
||||
// Package up board info for the template to use.
|
||||
board.CurrentPage = page_num
|
||||
boardinfo_i = nil
|
||||
boardinfo_i = append(boardinfo_i, board)
|
||||
|
||||
// Write to board.html for the first page.
|
||||
currentBoardPage := board.CurrentPage
|
||||
for _, page_threads := range thread_pages {
|
||||
board.CurrentPage++
|
||||
var current_page_filepath string
|
||||
var pageFilename string
|
||||
if board.CurrentPage == 0 {
|
||||
pageFilename = "board.html"
|
||||
} else {
|
||||
pageFilename = strconv.Itoa(page_num) + ".html"
|
||||
}
|
||||
|
||||
pageFilename := strconv.Itoa(board.CurrentPage) + ".html"
|
||||
current_page_filepath = path.Join(config.DocumentRoot, board.Dir, pageFilename)
|
||||
current_page_file, err = os.OpenFile(current_page_filepath, os.O_CREATE|os.O_RDWR|os.O_TRUNC, 0777)
|
||||
if err != nil {
|
||||
html += handleError(1, "Failed opening board page: "+err.Error()) + "<br />"
|
||||
continue
|
||||
}
|
||||
defer closeFile(current_page_file)
|
||||
|
||||
// Render the boardpage template, given boards, sections, threads, and board info
|
||||
if err = renderTemplate(img_header_tmpl, "img_header", current_page_file,
|
||||
&Wrapper{IName: "boards_", Data: all_boards},
|
||||
&Wrapper{IName: "sections_w", Data: all_sections},
|
||||
&Wrapper{IName: "posts_w", Data: []interface{}{
|
||||
// Render the boardpage template, don't forget config
|
||||
if err = img_boardpage_tmpl.Execute(current_page_file, map[string]interface{}{
|
||||
"config": config,
|
||||
"boards": all_boards,
|
||||
"sections": all_sections,
|
||||
"threads": page_threads,
|
||||
"board": board,
|
||||
"posts": []interface{}{
|
||||
PostTable{BoardID: board.ID},
|
||||
}},
|
||||
&Wrapper{IName: "op", Data: []interface{}{PostTable{}}},
|
||||
&Wrapper{IName: "board", Data: []interface{}{board}},
|
||||
); err != nil {
|
||||
html += handleError(1, "Failed building /"+board.Dir+"/ header: "+err.Error()) + "<br />"
|
||||
return
|
||||
}
|
||||
|
||||
if err = renderTemplate(img_boardpage_tmpl, "boardpage", current_page_file,
|
||||
&Wrapper{IName: "boards", Data: all_boards},
|
||||
&Wrapper{IName: "sections", Data: all_sections},
|
||||
&Wrapper{IName: "threads", Data: page_threads},
|
||||
&Wrapper{IName: "boardinfo", Data: boardinfo_i},
|
||||
); err != nil {
|
||||
},
|
||||
}); err != nil {
|
||||
html += handleError(1, "Failed building /"+board.Dir+"/ boardpage: "+err.Error()) + "<br />"
|
||||
return
|
||||
}
|
||||
|
||||
if err = renderTemplate(global_footer_tmpl, "global_footer", current_page_file); err != nil {
|
||||
html += handleError(1, "Failed building /"+board.Dir+"/ footer: "+err.Error())
|
||||
return
|
||||
if board.CurrentPage == 1 {
|
||||
boardPage := path.Join(config.DocumentRoot, board.Dir, "board.html")
|
||||
os.Remove(boardPage)
|
||||
if err = syscall.Symlink(current_page_filepath, boardPage); !os.IsExist(err) && err != nil {
|
||||
html += handleError(1, "Failed building /"+board.Dir+"/: "+err.Error()) + "<br />"
|
||||
}
|
||||
}
|
||||
|
||||
current_page_file.Close()
|
||||
|
||||
// Collect up threads for this page.
|
||||
var page_obj BoardPageJSON
|
||||
page_obj.Page = page_num
|
||||
page_obj.Page = board.CurrentPage
|
||||
|
||||
for _, thread_int := range page_threads {
|
||||
thread := thread_int.(Thread)
|
||||
post_json := makePostJSON(thread.OP.(PostTable), board.Anonymous)
|
||||
post_json := makePostJSON(thread.OP, board.Anonymous)
|
||||
var thread_json ThreadJSON
|
||||
thread_json.PostJSON = &post_json
|
||||
thread_json.Replies = thread.NumReplies
|
||||
|
@ -332,7 +288,7 @@ func buildBoardPages(board *BoardsTable) (html string) {
|
|||
thread_json.OmittedPosts = thread.NumReplies - config.RepliesOnBoardPage
|
||||
}
|
||||
}
|
||||
if thread.OP.(PostTable).Locked {
|
||||
if thread.OP.Locked {
|
||||
thread_json.Locked = 1
|
||||
}
|
||||
page_obj.Threads = append(page_obj.Threads, thread_json)
|
||||
|
@ -340,13 +296,13 @@ func buildBoardPages(board *BoardsTable) (html string) {
|
|||
|
||||
pages_obj = append(pages_obj, page_obj)
|
||||
}
|
||||
board.CurrentPage = currentBoardPage
|
||||
|
||||
catalog_json, err := json.Marshal(pages_obj)
|
||||
if err != nil {
|
||||
html += handleError(1, "Failed to marshal to JSON: "+err.Error()) + "<br />"
|
||||
return
|
||||
}
|
||||
|
||||
if _, err = catalog_json_file.Write(catalog_json); err != nil {
|
||||
html += handleError(1, "Failed writing /"+board.Dir+"/catalog.json: "+err.Error()) + "<br />"
|
||||
return
|
||||
|
@ -369,8 +325,7 @@ func buildThreads(all bool, boardid, threadid int) (html string) {
|
|||
"deleted_timestamp": nil_timestamp,
|
||||
}, "")
|
||||
thread := threads[0]
|
||||
thread_struct := thread.(PostTable)
|
||||
html += buildThreadPages(&thread_struct) + "<br />\n"
|
||||
html += buildThreadPages(&thread) + "<br />\n"
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -382,8 +337,8 @@ func buildThreads(all bool, boardid, threadid int) (html string) {
|
|||
if len(threads) == 0 {
|
||||
return
|
||||
}
|
||||
for _, op_i := range threads {
|
||||
op := op_i.(PostTable)
|
||||
|
||||
for _, op := range threads {
|
||||
html += buildThreadPages(&op) + "<br />\n"
|
||||
}
|
||||
return
|
||||
|
@ -391,33 +346,23 @@ func buildThreads(all bool, boardid, threadid int) (html string) {
|
|||
|
||||
// buildThreadPages builds the pages for a thread given by a PostTable object.
|
||||
func buildThreadPages(op *PostTable) (html string) {
|
||||
var boardDir string
|
||||
var anonymous string
|
||||
var replies []interface{}
|
||||
var replies []PostTable
|
||||
var current_page_file *os.File
|
||||
|
||||
stmt, err := db.Prepare("SELECT `dir`,`anonymous` FROM `" + config.DBprefix + "boards` WHERE `id` = ?")
|
||||
board, err := getBoardFromID(op.BoardID)
|
||||
if err != nil {
|
||||
// possibly a syntax error? This normally shouldn't happen so this might be removed
|
||||
html += handleError(1, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
if err = stmt.QueryRow(op.BoardID).Scan(&boardDir, &anonymous); err != nil {
|
||||
html += handleError(1, "Failed getting board directory and board's anonymous setting from post: "+err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
replies, err = getPostArr(map[string]interface{}{
|
||||
"boardid": op.BoardID,
|
||||
"parentid": op.ID,
|
||||
"deleted_timestamp": nil_timestamp,
|
||||
}, "ORDER BY `ID` ASC")
|
||||
}, "ORDER BY `id` ASC")
|
||||
if err != nil {
|
||||
html += handleError(1, "Error building thread "+strconv.Itoa(op.ID)+":"+err.Error())
|
||||
return
|
||||
}
|
||||
os.Remove(path.Join(config.DocumentRoot, boardDir, "res", strconv.Itoa(op.ID)+".html"))
|
||||
os.Remove(path.Join(config.DocumentRoot, board.Dir, "res", strconv.Itoa(op.ID)+".html"))
|
||||
|
||||
var repliesInterface []interface{}
|
||||
for _, reply := range replies {
|
||||
|
@ -426,47 +371,33 @@ func buildThreadPages(op *PostTable) (html string) {
|
|||
|
||||
//thread_pages := paginate(config.PostsPerThreadPage, replies)
|
||||
thread_pages := paginate(config.PostsPerThreadPage, repliesInterface)
|
||||
for i, _ := range thread_pages {
|
||||
thread_pages[i] = append([]interface{}{op}, thread_pages[i]...)
|
||||
}
|
||||
deleteMatchingFiles(path.Join(config.DocumentRoot, boardDir, "res"), "^"+strconv.Itoa(op.ID)+"p")
|
||||
deleteMatchingFiles(path.Join(config.DocumentRoot, board.Dir, "res"), "^"+strconv.Itoa(op.ID)+"p")
|
||||
|
||||
op.NumPages = len(thread_pages)
|
||||
|
||||
current_page_filepath := path.Join(config.DocumentRoot, boardDir, "res", strconv.Itoa(op.ID)+".html")
|
||||
current_page_filepath := path.Join(config.DocumentRoot, board.Dir, "res", strconv.Itoa(op.ID)+".html")
|
||||
current_page_file, err = os.OpenFile(current_page_filepath, os.O_CREATE|os.O_RDWR|os.O_TRUNC, 0777)
|
||||
if err != nil {
|
||||
html += handleError(1, "Failed opening "+current_page_filepath+": "+err.Error())
|
||||
return
|
||||
}
|
||||
// render main page
|
||||
if err = renderTemplate(img_header_tmpl, "img_header", current_page_file,
|
||||
&Wrapper{IName: "boards_", Data: all_boards},
|
||||
&Wrapper{IName: "sections_w", Data: all_sections},
|
||||
&Wrapper{IName: "posts_w", Data: append([]interface{}{op}, replies...)},
|
||||
); err != nil {
|
||||
html += handleError(1, "Failed building /"+boardDir+"/res/"+strconv.Itoa(op.ID)+" header: "+err.Error()) + "<br />\n"
|
||||
return
|
||||
}
|
||||
|
||||
if err = renderTemplate(img_threadpage_tmpl, "threadpage", current_page_file,
|
||||
&Wrapper{IName: "boards_", Data: all_boards},
|
||||
&Wrapper{IName: "sections_w", Data: all_sections},
|
||||
&Wrapper{IName: "posts_w", Data: append([]interface{}{op}, replies...)},
|
||||
); err != nil {
|
||||
html += handleError(1, "Failed building /%s/res/%d threadpage: ", boardDir, op.ID, err.Error()) + "<br />\n"
|
||||
return
|
||||
}
|
||||
|
||||
if err = renderTemplate(global_footer_tmpl, "global_footer", current_page_file); err != nil {
|
||||
html += handleError(1, "Failed building /%s/res/%d.html: %s", boardDir, op.ID, err.Error())
|
||||
if err = img_threadpage_tmpl.Execute(current_page_file, map[string]interface{}{
|
||||
"config": config,
|
||||
"boards": all_boards,
|
||||
"board": board,
|
||||
"sections": all_sections,
|
||||
"posts": replies,
|
||||
"op": op,
|
||||
}); err != nil {
|
||||
html += handleError(1, "Failed building /%s/res/%d threadpage: ", board.Dir, op.ID, err.Error()) + "<br />\n"
|
||||
return
|
||||
}
|
||||
|
||||
// Put together the thread JSON
|
||||
threadJSONFile, err := os.OpenFile(path.Join(config.DocumentRoot, boardDir, "res", strconv.Itoa(op.ID)+".json"), os.O_CREATE|os.O_RDWR|os.O_TRUNC, 0777)
|
||||
threadJSONFile, err := os.OpenFile(path.Join(config.DocumentRoot, board.Dir, "res", strconv.Itoa(op.ID)+".json"), os.O_CREATE|os.O_RDWR|os.O_TRUNC, 0777)
|
||||
if err != nil {
|
||||
html += handleError(1, "Failed opening /%s/res/%d.json: %s", boardDir, op.ID, err.Error())
|
||||
html += handleError(1, "Failed opening /%s/res/%d.json: %s", board.Dir, op.ID, err.Error())
|
||||
return
|
||||
}
|
||||
defer closeFile(threadJSONFile)
|
||||
|
@ -475,64 +406,51 @@ func buildThreadPages(op *PostTable) (html string) {
|
|||
thread_json_wrapper := new(ThreadJSONWrapper)
|
||||
|
||||
// Handle the OP, of type *PostTable
|
||||
op_post_obj := makePostJSON(*op, anonymous)
|
||||
op_post_obj := makePostJSON(*op, board.Anonymous)
|
||||
thread_json_wrapper.Posts = append(thread_json_wrapper.Posts, op_post_obj)
|
||||
|
||||
// Iterate through each reply, which are of type PostTable
|
||||
for _, post_int := range replies {
|
||||
post := post_int.(PostTable)
|
||||
post_obj := makePostJSON(post, anonymous)
|
||||
thread_json_wrapper.Posts = append(thread_json_wrapper.Posts, post_obj)
|
||||
for _, reply := range replies {
|
||||
postJSON := makePostJSON(reply, board.Anonymous)
|
||||
thread_json_wrapper.Posts = append(thread_json_wrapper.Posts, postJSON)
|
||||
}
|
||||
threadJSON, err := json.Marshal(thread_json_wrapper)
|
||||
|
||||
if err != nil {
|
||||
html += handleError(1, "Failed to marshal to JSON: %s", err.Error()) + "<br />"
|
||||
return
|
||||
}
|
||||
|
||||
if _, err = threadJSONFile.Write(threadJSON); err != nil {
|
||||
html += handleError(1, "Failed writing /%s/res/%d.json: %s", boardDir, op.ID, err.Error()) + "<br />"
|
||||
html += handleError(1, "Failed writing /%s/res/%d.json: %s", board.Dir, op.ID, err.Error()) + "<br />"
|
||||
return
|
||||
}
|
||||
|
||||
success_text := fmt.Sprintf("Built /%s/%d successfully", boardDir, op.ID)
|
||||
success_text := fmt.Sprintf("Built /%s/%d successfully", board.Dir, op.ID)
|
||||
html += success_text + "<br />\n"
|
||||
println(2, success_text)
|
||||
|
||||
for page_num, page_posts := range thread_pages {
|
||||
op.CurrentPage = page_num
|
||||
current_page_filepath := path.Join(config.DocumentRoot, boardDir, "res", strconv.Itoa(op.ID)+"p"+strconv.Itoa(op.CurrentPage+1)+".html")
|
||||
op.CurrentPage = page_num + 1
|
||||
current_page_filepath := path.Join(config.DocumentRoot, board.Dir, "res", strconv.Itoa(op.ID)+"p"+strconv.Itoa(op.CurrentPage)+".html")
|
||||
current_page_file, err = os.OpenFile(current_page_filepath, os.O_CREATE|os.O_RDWR|os.O_TRUNC, 0777)
|
||||
if err != nil {
|
||||
html += handleError(1, "Failed opening "+current_page_filepath+": "+err.Error()) + "<br />\n"
|
||||
return
|
||||
}
|
||||
|
||||
if err = renderTemplate(img_header_tmpl, "img_header", current_page_file,
|
||||
&Wrapper{IName: "boards_", Data: all_boards},
|
||||
&Wrapper{IName: "sections_w", Data: all_sections},
|
||||
&Wrapper{IName: "posts_w", Data: append([]interface{}{op}, replies...)},
|
||||
); err != nil {
|
||||
html += handleError(1, fmt.Sprintf("Failed building /%s/%d: %s", boardDir, op.ID, err.Error()))
|
||||
if err = img_threadpage_tmpl.Execute(current_page_file, map[string]interface{}{
|
||||
"config": config,
|
||||
"boards": all_boards,
|
||||
"board": board,
|
||||
"sections": all_sections,
|
||||
"posts": page_posts,
|
||||
"op": op,
|
||||
}); err != nil {
|
||||
html += handleError(1, "Failed building /%s/%d: %s", board.Dir, op.ID, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
if err = renderTemplate(img_threadpage_tmpl, "threadpage", current_page_file,
|
||||
&Wrapper{IName: "boards_", Data: all_boards},
|
||||
&Wrapper{IName: "sections_w", Data: all_sections},
|
||||
&Wrapper{IName: "posts_w", Data: page_posts},
|
||||
); err != nil {
|
||||
html += handleError(1, "Failed building /%s/%d: %s", boardDir, op.ID, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
if err = renderTemplate(global_footer_tmpl, "global_footer", current_page_file); err != nil {
|
||||
html += handleError(1, "Failed building /%s/%d: %s", boardDir, op.ID, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
success_text := fmt.Sprintf("Built /%s/%dp%d successfully", boardDir, op.ID, op.CurrentPage+1)
|
||||
success_text := fmt.Sprintf("Built /%s/%dp%d successfully", board.Dir, op.ID, op.CurrentPage)
|
||||
html += success_text + "<br />\n"
|
||||
println(2, success_text)
|
||||
}
|
||||
|
@ -556,12 +474,14 @@ func buildFrontPage() (html string) {
|
|||
if err != nil {
|
||||
return handleError(1, "Failed getting front page rows: "+err.Error())
|
||||
}
|
||||
defer closeRows(rows)
|
||||
|
||||
for rows.Next() {
|
||||
frontpage := new(FrontTable)
|
||||
frontpage.IName = "front page"
|
||||
if err = rows.Scan(&frontpage.ID, &frontpage.Page, &frontpage.Order, &frontpage.Subject,
|
||||
&frontpage.Message, &frontpage.Timestamp, &frontpage.Poster, &frontpage.Email); err != nil {
|
||||
return handleError(1, err.Error()) + "<br />"
|
||||
return handleError(1, err.Error())
|
||||
}
|
||||
front_arr = append(front_arr, frontpage)
|
||||
}
|
||||
|
@ -578,32 +498,33 @@ func buildFrontPage() (html string) {
|
|||
"AND `boardid` = `" + config.DBprefix + "boards`.`id` " +
|
||||
"ORDER BY `timestamp` DESC LIMIT ?")
|
||||
if err != nil {
|
||||
return handleError(1, err.Error()) + "<br />"
|
||||
return handleError(1, err.Error())
|
||||
}
|
||||
defer closeStatement(stmt)
|
||||
|
||||
rows, err = stmt.Query(nil_timestamp, config.MaxRecentPosts)
|
||||
if err != nil {
|
||||
return handleError(1, "Failed getting list of recent posts for front page: "+err.Error()) + "<br />"
|
||||
return handleError(1, "Failed getting list of recent posts for front page: "+err.Error())
|
||||
}
|
||||
for rows.Next() {
|
||||
recent_post := new(RecentPost)
|
||||
err = rows.Scan(&recent_post.PostID, &recent_post.ParentID, &recent_post.BoardName, &recent_post.BoardID, &recent_post.Name, &recent_post.Tripcode, &recent_post.Message, &recent_post.Filename, &recent_post.ThumbW, &recent_post.ThumbH)
|
||||
if err != nil {
|
||||
return handleError(1, "Failed getting list of recent posts for front page: "+err.Error()) + "<br />"
|
||||
return handleError(1, "Failed getting list of recent posts for front page: "+err.Error())
|
||||
}
|
||||
recent_posts_arr = append(recent_posts_arr, recent_post)
|
||||
}
|
||||
|
||||
if err = renderTemplate(front_page_tmpl, "frontpage", front_file,
|
||||
&Wrapper{IName: "fronts", Data: front_arr},
|
||||
&Wrapper{IName: "boards", Data: all_boards},
|
||||
&Wrapper{IName: "sections", Data: all_sections},
|
||||
&Wrapper{IName: "recent posts", Data: recent_posts_arr},
|
||||
); err != nil {
|
||||
return handleError(1, "Failed executing front page template: "+err.Error()) + "<br />"
|
||||
if err = front_page_tmpl.Execute(front_file, map[string]interface{}{
|
||||
"config": config,
|
||||
"fronts": front_arr,
|
||||
"boards": all_boards,
|
||||
"sections": all_sections,
|
||||
"recent_posts": recent_posts_arr,
|
||||
}); err != nil {
|
||||
return handleError(1, "Failed executing front page template: "+err.Error())
|
||||
}
|
||||
return "Front page rebuilt successfully.<br />"
|
||||
return "Front page rebuilt successfully."
|
||||
}
|
||||
|
||||
func buildBoardListJSON() (html string) {
|
||||
|
@ -640,8 +561,7 @@ func buildBoardListJSON() (html string) {
|
|||
return "Board list JSON rebuilt successfully.<br />"
|
||||
}
|
||||
|
||||
// bumps the given thread on the given board and returns true if it exists
|
||||
// or false on error
|
||||
// bumps the given thread on the given board and returns true if there were no errors
|
||||
func bumpThread(postID, boardID int) error {
|
||||
stmt, err := db.Prepare("UPDATE `" + config.DBprefix + "posts` SET `bumped` = ? WHERE `id` = ? AND `boardid` = ?")
|
||||
if err != nil {
|
||||
|
@ -649,10 +569,8 @@ func bumpThread(postID, boardID int) error {
|
|||
}
|
||||
defer closeStatement(stmt)
|
||||
|
||||
if _, err = stmt.Exec(time.Now(), postID, boardID); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
_, err = stmt.Exec(time.Now(), postID, boardID)
|
||||
return err
|
||||
}
|
||||
|
||||
// Checks check poster's name/tripcode/file checksum (from PostTable post) for banned status
|
||||
|
@ -1144,9 +1062,6 @@ func makePost(w http.ResponseWriter, r *http.Request, data interface{}) {
|
|||
}
|
||||
}
|
||||
}
|
||||
if post.FilenameOriginal != "" {
|
||||
//post.FilenameOriginal = html.EscapeString(post.FilenameOriginal)
|
||||
}
|
||||
|
||||
if strings.TrimSpace(post.MessageText) == "" && post.Filename == "" {
|
||||
serveErrorPage(w, "Post must contain a message if no image is uploaded.")
|
||||
|
@ -1175,7 +1090,9 @@ func makePost(w http.ResponseWriter, r *http.Request, data interface{}) {
|
|||
var banpage_buffer bytes.Buffer
|
||||
var banpage_html string
|
||||
banpage_buffer.Write([]byte(""))
|
||||
if err = renderTemplate(banpage_tmpl, "banpage", &banpage_buffer, &Wrapper{IName: "bans", Data: isBanned}); err != nil {
|
||||
if err = banpage_tmpl.Execute(&banpage_buffer, map[string]interface{}{
|
||||
"bans": isBanned,
|
||||
}); err != nil {
|
||||
fmt.Fprintf(writer, banpage_html+handleError(1, err.Error())+"\n</body>\n</html>")
|
||||
return
|
||||
}
|
||||
|
|
|
@ -179,6 +179,7 @@ func utilHandler(writer http.ResponseWriter, request *http.Request, data interfa
|
|||
action := request.FormValue("action")
|
||||
password := request.FormValue("password")
|
||||
board := request.FormValue("board")
|
||||
boardid := request.FormValue("boardid")
|
||||
fileOnly := request.FormValue("fileonly") == "on"
|
||||
deleteBtn := request.PostFormValue("delete_btn")
|
||||
reportBtn := request.PostFormValue("report_btn")
|
||||
|
@ -203,7 +204,33 @@ func utilHandler(writer http.ResponseWriter, request *http.Request, data interfa
|
|||
serveErrorPage(writer, "You can only edit one post at a time.")
|
||||
return
|
||||
} else {
|
||||
|
||||
passwordMD5 := md5Sum(password)
|
||||
rank := getStaffRank()
|
||||
if passwordMD5 == "" && rank == 0 {
|
||||
serveErrorPage(writer, "Password required for post editing")
|
||||
return
|
||||
}
|
||||
var post PostTable
|
||||
post.ID, _ = strconv.Atoi(postsArr[0])
|
||||
post.BoardID, _ = strconv.Atoi(boardid)
|
||||
stmt, err := db.Prepare("SELECT `parentid`,` password`,`message_raw` FROM `" + config.DBprefix + "posts` WHERE `id` = ? AND `deleted_timestamp` = ?")
|
||||
if err != nil {
|
||||
serveErrorPage(writer, handleError(1, err.Error()+"\n"))
|
||||
}
|
||||
defer closeStatement(stmt)
|
||||
/* var post_edit_buffer bytes.Buffer
|
||||
if err = renderTemplate(post_edit_tmpl, "post_edit", post_edit_buffer,
|
||||
&Wrapper{IName: "boards_", Data: all_boards},
|
||||
&Wrapper{IName: "sections_w", Data: all_sections},
|
||||
&Wrapper{IName: "posts_w", Data: []interface{}{
|
||||
PostTable{BoardID: board.ID},
|
||||
}},
|
||||
&Wrapper{IName: "op", Data: []interface{}{PostTable{}}},
|
||||
&Wrapper{IName: "board", Data: []interface{}{board}},
|
||||
); err != nil {
|
||||
html += handleError(1, fmt.Sprintf("Failed building /%s/res/%d.html: %s", board.Dir, 0, err.Error())) + "<br />"
|
||||
return
|
||||
} */
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -218,23 +245,19 @@ func utilHandler(writer http.ResponseWriter, request *http.Request, data interfa
|
|||
}
|
||||
|
||||
for _, checkedPostID := range postsArr {
|
||||
// var parentID int
|
||||
// var fileName string
|
||||
var fileType string
|
||||
var thumbType string
|
||||
// var passwordChecksum string
|
||||
// var boardid int
|
||||
var post PostTable
|
||||
post.ID, _ = strconv.Atoi(checkedPostID)
|
||||
post.BoardID, _ = strconv.Atoi(boardid)
|
||||
|
||||
stmt, err := db.Prepare("SELECT `parentID`, `filename`, `password` FROM `" + config.DBprefix + "posts` WHERE `id` = ? AND `deleted_timestamp` = ?")
|
||||
stmt, err := db.Prepare("SELECT `parentid`, `filename`, `password` FROM `" + config.DBprefix + "posts` WHERE `id` = ? AND `boardid` = ? AND `deleted_timestamp` = ?")
|
||||
if err != nil {
|
||||
serveErrorPage(writer, handleError(1, err.Error()+"\n"))
|
||||
}
|
||||
defer closeStatement(stmt)
|
||||
|
||||
err = stmt.QueryRow(&post.ID, nil_timestamp).Scan(&post.ParentID, &post.Filename, &post.Password)
|
||||
|
||||
err = stmt.QueryRow(&post.ID, &post.BoardID, nil_timestamp).Scan(&post.ParentID, &post.Filename, &post.Password)
|
||||
if err == sql.ErrNoRows {
|
||||
//the post has already been deleted
|
||||
writer.Header().Add("refresh", "4;url="+request.Referer())
|
||||
|
@ -279,7 +302,8 @@ func utilHandler(writer http.ResponseWriter, request *http.Request, data interfa
|
|||
_board, _ := getBoardArr(map[string]interface{}{"id": post.BoardID}, "")
|
||||
buildBoardPages(&_board[0])
|
||||
_post, _ := getPostArr(map[string]interface{}{"id": post.ID, "boardid": post.BoardID}, "")
|
||||
postBoard := _post[0].(PostTable)
|
||||
postBoard := _post[0]
|
||||
// postBoard := _post[0].(PostTable)
|
||||
buildThreadPages(&postBoard)
|
||||
|
||||
writer.Header().Add("refresh", "4;url="+request.Referer())
|
||||
|
|
187
src/template.go
187
src/template.go
|
@ -3,21 +3,13 @@ package main
|
|||
import (
|
||||
"fmt"
|
||||
"html"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
"text/template"
|
||||
"time"
|
||||
)
|
||||
|
||||
type FooterData struct {
|
||||
Version string
|
||||
GeneratedTime float32
|
||||
}
|
||||
|
||||
var funcMap = template.FuncMap{
|
||||
"add": func(a, b int) int {
|
||||
return a + b
|
||||
|
@ -40,27 +32,33 @@ var funcMap = template.FuncMap{
|
|||
"gt": func(a int, b int) bool {
|
||||
return a > b
|
||||
},
|
||||
"gte": func(a int, b int) bool {
|
||||
"ge": func(a int, b int) bool {
|
||||
return a >= b
|
||||
},
|
||||
"lt": func(a int, b int) bool {
|
||||
return a < b
|
||||
},
|
||||
"lte": func(a int, b int) bool {
|
||||
"le": func(a int, b int) bool {
|
||||
return a <= b
|
||||
},
|
||||
"makeLoop": func(n int) []struct{} {
|
||||
return make([]struct{}, n)
|
||||
"makeLoop": func(n int, offset int) []int {
|
||||
loopArr := make([]int, n)
|
||||
for i := range loopArr {
|
||||
loopArr[i] = i + offset
|
||||
}
|
||||
return loopArr
|
||||
},
|
||||
"printf": func(v int, format string, a ...interface{}) string {
|
||||
printf(v, format, a...)
|
||||
return ""
|
||||
},
|
||||
"println": func(v int, i ...interface{}) string {
|
||||
println(v, i...)
|
||||
return ""
|
||||
},
|
||||
"stringAppend": func(a, b string) string {
|
||||
return a + b
|
||||
},
|
||||
"stringEq": func(a, b string) bool {
|
||||
return a == b
|
||||
},
|
||||
"stringNeq": func(a, b string) bool {
|
||||
return a != b
|
||||
},
|
||||
"truncateMessage": func(msg string, limit int, max_lines int) string {
|
||||
var truncated bool
|
||||
split := strings.SplitN(msg, "<br />", -1)
|
||||
|
@ -100,36 +98,17 @@ var funcMap = template.FuncMap{
|
|||
"escapeString": func(a string) string {
|
||||
return html.EscapeString(a)
|
||||
},
|
||||
"isNil": func(i interface{}) bool {
|
||||
return i == nil
|
||||
},
|
||||
"intEq": func(a, b int) bool {
|
||||
return a == b
|
||||
},
|
||||
"intToString": func(a int) string {
|
||||
return strconv.Itoa(a)
|
||||
},
|
||||
"intToString": strconv.Itoa,
|
||||
"isStyleDefault_img": func(style string) bool {
|
||||
return style == config.DefaultStyle_img
|
||||
},
|
||||
"isStyleNotDefault_img": func(style string) bool {
|
||||
return style != config.DefaultStyle_img
|
||||
},
|
||||
"getElement": func(in []interface{}, element int) interface{} {
|
||||
if len(in) > element {
|
||||
return in[element]
|
||||
}
|
||||
return nil
|
||||
},
|
||||
"getInterface": func(in []interface{}, index int) interface{} {
|
||||
var nope interface{}
|
||||
if len(in) == 0 {
|
||||
return nope
|
||||
} else if len(in) < index+1 {
|
||||
return nope
|
||||
}
|
||||
return in[index]
|
||||
},
|
||||
"formatTimestamp": func(timestamp time.Time) string {
|
||||
return humanReadableTime(timestamp)
|
||||
},
|
||||
"formatTimestamp": humanReadableTime,
|
||||
"getThreadID": func(post_i interface{}) (thread int) {
|
||||
post := post_i.(PostTable)
|
||||
if post.ParentID == 0 {
|
||||
|
@ -196,121 +175,82 @@ var funcMap = template.FuncMap{
|
|||
}
|
||||
|
||||
var (
|
||||
footer_data = FooterData{version, float32(0)}
|
||||
|
||||
banpage_tmpl *template.Template
|
||||
global_footer_tmpl *template.Template
|
||||
global_header_tmpl *template.Template
|
||||
img_header_tmpl *template.Template
|
||||
img_boardpage_tmpl *template.Template
|
||||
img_threadpage_tmpl *template.Template
|
||||
img_post_form_tmpl *template.Template
|
||||
post_edit_tmpl *template.Template
|
||||
manage_header_tmpl *template.Template
|
||||
manage_boards_tmpl *template.Template
|
||||
manage_config_tmpl *template.Template
|
||||
front_page_tmpl *template.Template
|
||||
)
|
||||
|
||||
func loadTemplate(name string, filename string, before string) (*template.Template, error) {
|
||||
tmplBytes, err := ioutil.ReadFile(config.TemplateDir + "/" + filename)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
func loadTemplate(files ...string) (*template.Template, error) {
|
||||
if len(files) == 0 {
|
||||
return nil, fmt.Errorf("ERROR: no files named in call to loadTemplate")
|
||||
}
|
||||
tmplStr := before + string(tmplBytes)
|
||||
return template.New(name).Funcs(funcMap).Parse(tmplStr)
|
||||
var templates []string
|
||||
for i, file := range files {
|
||||
templates = append(templates, file)
|
||||
files[i] = config.TemplateDir + "/" + files[i]
|
||||
}
|
||||
return template.New(templates[0]).Funcs(funcMap).ParseFiles(files...)
|
||||
}
|
||||
|
||||
func initTemplates() {
|
||||
func templateError(name string, err error) error {
|
||||
return fmt.Errorf("Failed loading template \"" + config.TemplateDir + "/" + name + ": \"" + err.Error())
|
||||
}
|
||||
|
||||
func initTemplates() error {
|
||||
var err error
|
||||
resetBoardSectionArrays()
|
||||
|
||||
banpage_tmpl, err = loadTemplate("banpage_tmpl", "banpage.html",
|
||||
"{{$config := getInterface .Data 0}}"+
|
||||
"{{$ban := getInterface .Data 1}}")
|
||||
banpage_tmpl, err = loadTemplate("banpage.html")
|
||||
if err != nil {
|
||||
println(0, "Failed loading template \""+config.TemplateDir+"/banpage.html: \""+err.Error())
|
||||
os.Exit(2)
|
||||
return templateError("banpage.html", err)
|
||||
}
|
||||
|
||||
global_footer_tmpl, err = loadTemplate("global_footer_tmpl", "global_footer.html", "{{$config := getInterface .Data 0}}")
|
||||
global_header_tmpl, err = loadTemplate("global_header.html")
|
||||
if err != nil {
|
||||
println(0, "Failed loading template \""+config.TemplateDir+"/global_footer.html: \""+err.Error())
|
||||
os.Exit(2)
|
||||
return templateError("global_header.html", err)
|
||||
}
|
||||
|
||||
global_header_tmpl, err = loadTemplate("global_header_tmpl", "global_header.html", "")
|
||||
img_boardpage_tmpl, err = loadTemplate("img_boardpage.html", "img_header.html", "postbox.html", "global_footer.html")
|
||||
if err != nil {
|
||||
println(0, "Failed loading template \""+config.TemplateDir+"/global_header.html: \""+err.Error())
|
||||
os.Exit(2)
|
||||
return templateError("img_boardpage.html", err)
|
||||
}
|
||||
|
||||
img_header_tmpl, err = loadTemplate("img_header_tmpl", "img_header.html",
|
||||
"{{$config := getInterface .Data 0}}"+
|
||||
"{{$board_arr := (getInterface .Data 1).Data}}"+
|
||||
"{{$section_arr := (getInterface .Data 2).Data}}"+
|
||||
"{{$post_arr := (getInterface .Data 3).Data}}"+
|
||||
"{{$op := getElement $post_arr 0}}"+
|
||||
"{{$board := getElement $board_arr (subtract $op.BoardID 1)}}")
|
||||
img_threadpage_tmpl, err = loadTemplate("img_threadpage.html", "img_header.html", "postbox.html", "global_footer.html")
|
||||
if err != nil {
|
||||
println(0, "Failed loading template \""+config.TemplateDir+"/img_header.html: \""+err.Error())
|
||||
os.Exit(2)
|
||||
return templateError("img_threadpage.html", err)
|
||||
}
|
||||
|
||||
img_boardpage_tmpl, err = loadTemplate("img_boardpage_tmpl", "img_boardpage.html",
|
||||
"{{$config := getInterface .Data 0}}"+
|
||||
"{{$board_arr := (getInterface .Data 1).Data}}"+
|
||||
"{{$section_arr := (getInterface .Data 2).Data}}"+
|
||||
"{{$thread_arr := (getInterface .Data 3).Data}}"+
|
||||
"{{$board_info := (getInterface .Data 4).Data}}"+
|
||||
"{{$board := getInterface $board_info 0}}")
|
||||
/* post_edit_tmpl, err = loadTemplate("post_edit_tmpl", "post_edit.html")
|
||||
if err != nil {
|
||||
println(0, "Failed loading template \""+config.TemplateDir+"/img_boardpage.html: \""+err.Error())
|
||||
os.Exit(2)
|
||||
return templateError("img_threadpage.html", err)
|
||||
} */
|
||||
|
||||
manage_header_tmpl, err = loadTemplate("manage_header.html")
|
||||
if err != nil {
|
||||
return templateError("manage_header.html", err)
|
||||
}
|
||||
|
||||
img_threadpage_tmpl, err = loadTemplate("img_threadpage_tmpl", "img_threadpage.html",
|
||||
"{{$config := getInterface .Data 0}}"+
|
||||
"{{$board_arr := (getInterface .Data 1).Data}}"+
|
||||
"{{$section_arr := (getInterface .Data 2).Data}}"+
|
||||
"{{$post_arr := (getInterface .Data 3).Data}}"+
|
||||
"{{$op := getElement $post_arr 0}}"+
|
||||
"{{$board := getElement $board_arr (subtract $op.BoardID 1)}}")
|
||||
manage_boards_tmpl, err = loadTemplate("manage_boards.html")
|
||||
if err != nil {
|
||||
println(0, "Failed loading template \""+config.TemplateDir+"/img_threadpage.html: \""+err.Error())
|
||||
os.Exit(2)
|
||||
return templateError("manage_boards.html", err)
|
||||
}
|
||||
|
||||
manage_header_tmpl, err = loadTemplate("manage_header_tmpl", "manage_header.html", "")
|
||||
manage_config_tmpl, err = loadTemplate("manage_config.html")
|
||||
if err != nil {
|
||||
println(0, "Failed loading template \""+config.TemplateDir+"/manage_header.html: \""+err.Error())
|
||||
os.Exit(2)
|
||||
return templateError("manage_config.html", err)
|
||||
}
|
||||
|
||||
manage_boards_tmpl, err = loadTemplate("manage_boards_tmpl", "manage_boards.html",
|
||||
"{{$config := getInterface .Data 0}}"+
|
||||
"{{$board := getInterface (getInterface .Data 1).Data 0}}"+
|
||||
"{{$section_arr := (getInterface .Data 2).Data}}")
|
||||
front_page_tmpl, err = loadTemplate("front.html", "global_footer.html")
|
||||
if err != nil {
|
||||
println(0, "Failed loading template \""+config.TemplateDir+"/manage_boards.html: \""+err.Error())
|
||||
os.Exit(2)
|
||||
}
|
||||
|
||||
manage_config_tmpl, err = loadTemplate("manage_config_tmpl", "manage_config.html", "{{$config := getInterface .Data 0}}")
|
||||
if err != nil {
|
||||
println(0, "Failed loading template \""+config.TemplateDir+"/manage_config.html: \""+err.Error())
|
||||
os.Exit(2)
|
||||
}
|
||||
|
||||
front_page_tmpl, err = loadTemplate("front_page_tmpl", "front.html",
|
||||
"{{$config := getInterface .Data 0}}"+
|
||||
"{{$page_arr := getInterface .Data 1}}"+
|
||||
"{{$board_arr := getInterface .Data 2}}"+
|
||||
"{{$section_arr := getInterface .Data 3}}"+
|
||||
"{{$recent_posts_arr := getInterface .Data 4}}")
|
||||
if err != nil {
|
||||
println(0, "Failed loading template \""+config.TemplateDir+"/front.html\": "+err.Error())
|
||||
os.Exit(2)
|
||||
return templateError("front.html", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func getStyleLinks(w http.ResponseWriter, stylesheet string) {
|
||||
|
@ -324,14 +264,3 @@ func getStyleLinks(w http.ResponseWriter, stylesheet string) {
|
|||
os.Exit(2)
|
||||
}
|
||||
}
|
||||
|
||||
func renderTemplate(tmpl *template.Template, name string, output io.Writer, wrappers ...*Wrapper) error {
|
||||
var interfaces []interface{}
|
||||
interfaces = append(interfaces, config)
|
||||
|
||||
for _, wrapper := range wrappers {
|
||||
interfaces = append(interfaces, wrapper)
|
||||
}
|
||||
wrapped := &Wrapper{IName: name, Data: interfaces}
|
||||
return tmpl.Execute(output, wrapped)
|
||||
}
|
||||
|
|
|
@ -38,11 +38,11 @@ type RecentPost struct {
|
|||
|
||||
type Thread struct {
|
||||
IName string
|
||||
OP interface{}
|
||||
OP PostTable
|
||||
NumReplies int
|
||||
NumImages int
|
||||
OmittedImages int
|
||||
BoardReplies []interface{}
|
||||
BoardReplies []PostTable
|
||||
Stickied bool
|
||||
ThreadPage int
|
||||
}
|
||||
|
@ -255,11 +255,6 @@ type WordFiltersTable struct {
|
|||
RegEx bool
|
||||
}
|
||||
|
||||
type Wrapper struct {
|
||||
IName string
|
||||
Data []interface{}
|
||||
}
|
||||
|
||||
// Types for the JSON files we generate as a sort of "API"
|
||||
type BoardJSONWrapper struct {
|
||||
Boards []BoardJSON `json:"boards"`
|
||||
|
|
35
src/util.go
35
src/util.go
|
@ -95,9 +95,9 @@ func closeStatement(stmt *sql.Stmt) {
|
|||
}
|
||||
|
||||
/*
|
||||
Deletes files in a folder (root) that match a given regular expression.
|
||||
Returns the number of files that were deleted, and any error encountered.
|
||||
*/
|
||||
* Deletes files in a folder (root) that match a given regular expression.
|
||||
* Returns the number of files that were deleted, and any error encountered.
|
||||
*/
|
||||
func deleteMatchingFiles(root, match string) (filesDeleted int, err error) {
|
||||
files, err := ioutil.ReadDir(root)
|
||||
if err != nil {
|
||||
|
@ -234,8 +234,33 @@ func getBoardArr(parameterList map[string]interface{}, extra string) (boards []B
|
|||
return
|
||||
}
|
||||
|
||||
func getBoardFromID(id int) (*BoardsTable, error) {
|
||||
board := new(BoardsTable)
|
||||
stmt, err := db.Prepare("SELECT `order`,`dir`,`type`,`upload_type`,`title`,`subtitle`,`description`,`section`," +
|
||||
"`max_image_size`,`max_pages`,`locale`,`default_style`,`locked`,`created_on`,`anonymous`,`forced_anon`,`max_age`," +
|
||||
"`autosage_after`,`no_images_after`,`max_message_length`,`embeds_allowed`,`redirect_to_thread`,`require_file`," +
|
||||
"`enable_catalog` FROM `" + config.DBprefix + "boards` WHERE `id` = ?")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer closeStatement(stmt)
|
||||
|
||||
board.ID = id
|
||||
if err = stmt.QueryRow(id).Scan(
|
||||
&board.Order, &board.Dir, &board.Type, &board.UploadType, &board.Title,
|
||||
&board.Subtitle, &board.Description, &board.Section, &board.MaxImageSize,
|
||||
&board.MaxPages, &board.Locale, &board.DefaultStyle, &board.Locked, &board.CreatedOn,
|
||||
&board.Anonymous, &board.ForcedAnon, &board.MaxAge, &board.AutosageAfter,
|
||||
&board.NoImagesAfter, &board.MaxMessageLength, &board.EmbedsAllowed,
|
||||
&board.RedirectToThread, &board.RequireFile, &board.EnableCatalog,
|
||||
); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return board, nil
|
||||
}
|
||||
|
||||
// if parameterList is nil, ignore it and treat extra like a whole SQL query
|
||||
func getPostArr(parameterList map[string]interface{}, extra string) (posts []interface{}, err error) {
|
||||
func getPostArr(parameterList map[string]interface{}, extra string) (posts []PostTable, err error) {
|
||||
queryString := "SELECT * FROM `" + config.DBprefix + "posts` "
|
||||
numKeys := len(parameterList)
|
||||
var parameterValues []interface{}
|
||||
|
@ -353,7 +378,7 @@ func getFileExtension(filename string) (extension string) {
|
|||
return
|
||||
}
|
||||
|
||||
func getFormattedFilesize(size float32) string {
|
||||
func getFormattedFilesize(size int) string {
|
||||
if size < 1000 {
|
||||
return fmt.Sprintf("%fB", size)
|
||||
} else if size <= 100000 {
|
||||
|
|
|
@ -3,12 +3,11 @@
|
|||
<head>
|
||||
<title>Banned</title>
|
||||
<link rel="stylesheet" href="/css/global/front.css" />
|
||||
{{range $i, $style := $config.Styles_img}}
|
||||
<link rel="{{if isStyleNotDefault_img $style}}alternate {{end}}stylesheet" href="/css/{{$style}}/front.css" />{{end}}
|
||||
|
||||
{{range $i, $style := .config.Styles_img}}
|
||||
<link rel="{{if not (isStyleDefault_img $style)}}alternate {{end}}stylesheet" href="/css/{{$style}}/front.css" />{{end}}
|
||||
<script type="text/javascript">
|
||||
var styles = [{{range $i, $style := $config.Styles_img}}{{if gt $i 0}}, {{end}}"{{$style}}"{{end}}];
|
||||
var webroot = "{{$config.SiteWebfolder}}"
|
||||
var styles = [{{range $i, $style := .config.Styles_img}}{{if gt $i 0}}, {{end}}"{{$style}}"{{end}}];
|
||||
var webroot = "{{.config.SiteWebfolder}}"
|
||||
</script>
|
||||
<script type="text/javascript" src="/javascript/jquery-3.3.1.min.js"></script>
|
||||
<script type="text/javascript" src="/javascript/gochan.js"></script>
|
||||
|
@ -16,8 +15,8 @@
|
|||
</head>
|
||||
<body>
|
||||
<div id="top-pane">
|
||||
<span id="site-title">{{$config.SiteName}}</span><br />
|
||||
<span id="site-slogan">{{$config.SiteSlogan}}</span>
|
||||
<span id="site-title">{{.config.SiteName}}</span><br />
|
||||
<span id="site-slogan">{{.config.SiteSlogan}}</span>
|
||||
</div>
|
||||
<br />
|
||||
<br />
|
||||
|
@ -27,29 +26,29 @@
|
|||
</div>
|
||||
<div class="section-body" style="padding-top:8px">
|
||||
<div id="ban-info" style="float:left">
|
||||
{{if stringEq $ban.Boards "*"}}
|
||||
{{if eq .ban.Boards "*"}}
|
||||
You are banned from posting on <b>all boards</b> for the following reason:
|
||||
{{else}}
|
||||
You are banned from posting on <b>{{$ban.Boards}}</b> for the following reason:
|
||||
You are banned from posting on <b>{{.ban.Boards}}</b> for the following reason:
|
||||
{{end}}
|
||||
<br /><br />
|
||||
<b>{{$ban.Message}}</b>
|
||||
<b>{{.ban.Message}}</b>
|
||||
<br /><br />
|
||||
{{$expires_timestamp := formatTimestamp $ban.Expires}}
|
||||
{{$appeal_timestamp := formatTimestamp $ban.AppealAt}}
|
||||
{{if stringEq $expires_timestamp "Mon, January 01, 0001 00:00 AM"}}
|
||||
Your ban was placed on {{formatTimestamp $ban.Timestamp}} and will not expire.<br />
|
||||
{{$expires_timestamp := formatTimestamp .ban.Expires}}
|
||||
{{$appeal_timestamp := formatTimestamp .ban.AppealAt}}
|
||||
{{if eq $expires_timestamp "Mon, January 01, 0001 00:00 AM"}}
|
||||
Your ban was placed on {{formatTimestamp .ban.Timestamp}} and will not expire.<br />
|
||||
{{else}}
|
||||
Your ban was placed on {{formatTimestamp $ban.Timestamp}} and will expire on {{formatTimestamp $ban.Expires}}<br />
|
||||
Your ban was placed on {{formatTimestamp .ban.Timestamp}} and will expire on {{$expires_timestamp}}<br />
|
||||
{{end}}
|
||||
<br />
|
||||
{{if stringEq $appeal_timestamp "Mon, January 01, 0001 00:00 AM"}}
|
||||
{{if eq .appeal_timestamp "Mon, January 01, 0001 00:00 AM"}}
|
||||
You may not appeal this ban.
|
||||
{{else}}
|
||||
You may appeal this ban {{formatTimestamp $ban.AppealAt}}
|
||||
You may appeal this ban {{$appeal_timestamp}}
|
||||
{{end}}
|
||||
<br /><br />
|
||||
Your IP address is {{$ban.IP}}.
|
||||
Your IP address is {{.ban.IP}}.
|
||||
</div>
|
||||
<img id="banpage-image" src="/banned.jpg" style="float:right; margin: 4px 8px 8px 4px"/>
|
||||
</div>
|
||||
|
|
|
@ -2,31 +2,30 @@
|
|||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>{{$config.SiteName}}</title>
|
||||
<title>{{.config.SiteName}}</title>
|
||||
<script type="text/javascript" src="/javascript/jquery-3.3.1.min.js"></script>
|
||||
<script type="text/javascript" src="/javascript/msgpack.js"></script>
|
||||
<script type="text/javascript">
|
||||
var styles = [{{range $ii, $style := $config.Styles_img}}{{if gt $ii 0}}, {{end}}"{{$style}}"{{end}}];
|
||||
var webroot = "{{$config.SiteWebfolder}}"
|
||||
var styles = [{{range $ii, $style := .config.Styles_img}}{{if gt $ii 0}}, {{end}}"{{$style}}"{{end}}];
|
||||
var webroot = "{{.config.SiteWebfolder}}"
|
||||
</script>
|
||||
<script type="text/javascript" src="/javascript/gochan.js"></script>
|
||||
<script type="text/javascript" src="/javascript/manage.js"></script>
|
||||
|
||||
|
||||
<link rel="stylesheet" href="/css/global/front.css" />
|
||||
{{range $i, $style := $config.Styles_img}}
|
||||
<link rel="{{if isStyleNotDefault_img $style}}alternate {{end}}stylesheet" href="/css/{{$style}}/front.css" />{{end}}
|
||||
{{range $i, $style := .config.Styles_img}}
|
||||
<link rel="{{if not (isStyleDefault_img $style)}}alternate {{end}}stylesheet" href="/css/{{$style}}/front.css" />{{end}}
|
||||
<link rel="shortcut icon" href="/favicon.png">
|
||||
</head>
|
||||
<body>
|
||||
<div id="topbar">
|
||||
{{range $i, $board := $board_arr.Data}}
|
||||
{{range $i, $board := .boards}}
|
||||
<a href="/{{$board.Dir}}/" class="topbar-item">/{{$board.Dir}}/</a>
|
||||
{{end}}
|
||||
</div>
|
||||
<div id="top-pane">
|
||||
<span id="site-title">{{$config.SiteName}}</span><br />
|
||||
<span id="site-slogan">{{$config.SiteSlogan}}</span>
|
||||
<span id="site-title">{{.config.SiteName}}</span><br />
|
||||
<span id="site-slogan">{{.config.SiteSlogan}}</span>
|
||||
</div>
|
||||
<div id="main">
|
||||
<div id="tab-bar">
|
||||
|
@ -43,11 +42,9 @@
|
|||
<a href="#faq">FAQ</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div id="first-page" class="page">
|
||||
{{range $ii, $page := $page_arr.Data}}
|
||||
{{if intEq $page.Page 0}}
|
||||
{{range $ii, $page := .page_arr}}
|
||||
{{if eq $page.Page 0}}
|
||||
<div class="section-block">
|
||||
<div class="section-title-block" id="first-page0">
|
||||
<b>{{$page.Subject}}</b> by <a href="mailto:{{$page.Email}}" >{{$page.Poster}}</a><a href="/#first-page{{$ii}}" class="permalink">#</a>
|
||||
|
@ -59,7 +56,6 @@
|
|||
{{end}}
|
||||
{{end}}
|
||||
</div>
|
||||
|
||||
<div id="boards-page" class="page">
|
||||
<div class="section-block">
|
||||
<div class="section-title-block">
|
||||
|
@ -67,19 +63,16 @@
|
|||
</div>
|
||||
<div class="section-body">
|
||||
<ul>
|
||||
{{range $i, $board := $board_arr.Data}}
|
||||
{{if stringEq $board.Dir $config.Modboard}}{{else}}
|
||||
<li><b>/{{$board.Dir}}/</b> {{$board.Description}}</li>
|
||||
{{end}}
|
||||
{{end}}
|
||||
{{range $_, $board := .boards}}
|
||||
{{if eq $board.Dir $.config.Modboard}}{{else}}
|
||||
<li><b>/{{$board.Dir}}/</b> {{$board.Description}}</li>{{end}}{{end}}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="rules-page" class="page">
|
||||
{{range $ii, $page := $page_arr.Data}}
|
||||
{{if intEq $page.Page 1}}
|
||||
{{range $ii, $page := .page_arr}}
|
||||
{{if eq $page.Page 1}}
|
||||
<div class="section-block">
|
||||
<div class="section-title-block" id="first-page0">
|
||||
<b>{{$page.Subject}}</b> by <a href="mailto:{{$page.Email}}" >{{$page.Poster}}</a><a href="/#first-page{{$ii}}" class="permalink">#</a>
|
||||
|
@ -91,10 +84,9 @@
|
|||
{{end}}
|
||||
{{end}}
|
||||
</div>
|
||||
|
||||
<div id="faq-page" class="page">
|
||||
{{range $ii, $page := $page_arr.Data}}
|
||||
{{if intEq $page.Page 2}}
|
||||
{{range $ii, $page := .page_arr.Data}}
|
||||
{{if eq $page.Page 2}}
|
||||
<div class="section-block">
|
||||
<div class="section-title-block" id="first-page0">
|
||||
<b>{{$page.Subject}}</b> by <a href="mailto:{{$page.Email}}" >{{$page.Poster}}</a><a href="/#first-page{{$ii}}" class="permalink">#</a>
|
||||
|
@ -106,16 +98,16 @@
|
|||
{{end}}
|
||||
{{end}}
|
||||
</div>
|
||||
{{if gt $config.MaxRecentPosts 0}}
|
||||
{{if gt .config.MaxRecentPosts 0}}
|
||||
<div id="recent-posts">
|
||||
<div id="recent-posts-header" class="section-title-block"><b>Recent Posts</b></div>
|
||||
{{range $i, $post := $recent_posts_arr.Data}}
|
||||
{{range $i, $post := $.recent_posts}}
|
||||
<div class="section-block">
|
||||
<div class="section-title-block">
|
||||
<span class="section-title"><a href="{{$post.BoardName}}/res/{{if intEq $post.ParentID 0}}{{intToString $post.PostID}}.html{{else}}{{intToString $post.ParentID}}.html#{{intToString $post.PostID}}{{end}}">/{{$post.BoardName}}/</a></span> - {{$appended := stringAppend $post.Name $post.Tripcode}}{{if stringEq $appended ""}}<b>Anonymous</b>{{else}}<b>{{$post.Name}}</b>{{if stringNeq $post.Tripcode ""}}!{{$post.Tripcode}}{{end}}{{end}}
|
||||
<span class="section-title"><a href="{{$post.BoardName}}/res/{{if eq $post.ParentID 0}}{{intToString $post.PostID}}.html{{else}}{{intToString $post.ParentID}}.html#{{intToString $post.PostID}}{{end}}">/{{$post.BoardName}}/</a></span> - {{$appended := stringAppend $post.Name $post.Tripcode}}{{if eq $appended ""}}<b>Anonymous</b>{{else}}<b>{{$post.Name}}</b>{{if ne $post.Tripcode ""}}!{{$post.Tripcode}}{{end}}{{end}}
|
||||
</div>
|
||||
<div class="section-body">
|
||||
{{if stringNeq $post.Filename ""}}<a href="{{$post.BoardName}}/src/{{$post.Filename}}" target="_blank"><img src="{{$post.BoardName}}/thumb/{{getThumbnailFilename $post.Filename}}" alt="post thumbnail"/></a>{{end}}
|
||||
{{if ne $post.Filename ""}}<a href="{{$post.BoardName}}/src/{{$post.Filename}}" target="_blank"><img src="{{$post.BoardName}}/thumb/{{getThumbnailFilename $post.Filename}}" alt="post thumbnail"/></a>{{end}}
|
||||
{{truncateMessage $post.Message 225 12}}
|
||||
</div>
|
||||
</div>
|
||||
|
@ -124,8 +116,8 @@
|
|||
{{end}}
|
||||
</div>
|
||||
<div id="footer">
|
||||
<a href="{{$config.SiteWebfolder}}">Home</a> | <a href="{{$config.SiteWebfolder}}#boards">Boards</a> | <a href="{{$config.SiteWebfolder}}#rules">Rules</a> | <a href="{{$config.SiteWebfolder}}#faq">FAQ</a><br />
|
||||
Powered by <a href="http://github.com/eggbertx/gochan">Gochan {{$config.Version}}</a><br />
|
||||
<a href="{{.config.SiteWebfolder}}">Home</a> | <a href="{{.config.SiteWebfolder}}#boards">Boards</a> | <a href="{{.config.SiteWebfolder}}#rules">Rules</a> | <a href="{{.config.SiteWebfolder}}#faq">FAQ</a><br />
|
||||
Powered by <a href="http://github.com/eggbertx/gochan">Gochan {{.config.Version}}</a><br />
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
<div id="footer">
|
||||
<a href="{{$config.SiteWebfolder}}">Home</a> | <a href="{{$config.SiteWebfolder}}#boards">Boards</a> | <a href="{{$config.SiteWebfolder}}#rules">Rules</a> | <a href="{{$config.SiteWebfolder}}#faq">FAQ</a><br />
|
||||
Powered by <a href="http://github.com/eggbertx/gochan/">Gochan {{$config.Version}}</a><br />
|
||||
<div id="footer">
|
||||
<a href="{{.SiteWebfolder}}">Home</a> | <a href="{{.SiteWebfolder}}#boards">Boards</a> | <a href="{{.SiteWebfolder}}#rules">Rules</a> | <a href="{{.SiteWebfolder}}#faq">FAQ</a><br />
|
||||
Powered by <a href="http://github.com/eggbertx/gochan/">Gochan {{.Version}}</a><br />
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
|
@ -1,37 +1,24 @@
|
|||
{{template "img_header.html" .}}
|
||||
<div id="right-sidelinks">
|
||||
<a href="{{$config.SiteWebfolder}}{{$board.Dir}}/catalog.html">Board catalog</a><br />
|
||||
</div>
|
||||
<div id="postbox-area">
|
||||
<form name="postform" action="/post" method="POST" enctype="multipart/form-data">
|
||||
<input type="hidden" name="threadid" value="0" />
|
||||
<input type="hidden" name="boardid" value="{{$board.ID}}" />
|
||||
<input type="text" name="username" style="display:none" />
|
||||
<table id="postbox-static">
|
||||
<tr><th class="postblock">Name</th><td><input type="text" id="postname" name="postname" maxlength="50" size="28" {{/* value="Name" onFocus="if(this.value=='Name') {this,value= ''}" onBlur="if(this.value == '') {this.value = 'Name'}"*/}}/></td></tr>
|
||||
<tr><th class="postblock">Email</th><td><input type="text" id="postemail" name="postemail" maxlength="50" size="28" /></td></tr>
|
||||
<tr><th class="postblock">Subject</th><td><input type="text" name="postsubject" maxlength="100" size="35" /><input type="submit" value="Post"/></td></tr>
|
||||
<tr><th class="postblock">Message</th><td><textarea rows="4" cols="48" name="postmsg" id="postmsg"></textarea></td></tr>
|
||||
<tr><th class="postblock">File</th><td><input name="imagefile" type="file"><input type="checkbox" id="spoiler" name="spoiler"/><label for="spoiler">Spoiler</label></td></tr>
|
||||
<tr><th class="postblock">Password</th><td><input type="password" id="postpassword" name="postpassword" size="14" /> (for post/file deletion)</td></tr>
|
||||
</table>
|
||||
</form>
|
||||
<a href="{{.config.SiteWebfolder}}{{.board.Dir}}/catalog.html">Board catalog</a><br />
|
||||
</div>
|
||||
{{template "postbox.html" .}}
|
||||
<hr />
|
||||
<div id="content">
|
||||
<form action="/util" method="POST" id="main-form">
|
||||
{{range $t, $thread := $thread_arr}}
|
||||
{{range $t, $thread := .threads}}
|
||||
{{$op := $thread.OP}}
|
||||
<div class="thread">
|
||||
<div class="op-post" id="op{{$op.ID}}">
|
||||
{{if stringNeq $op.Filename ""}}
|
||||
{{if stringNeq $op.Filename "deleted"}}
|
||||
{{if ne $op.Filename ""}}
|
||||
{{if ne $op.Filename "deleted"}}
|
||||
<div class="file-info">File: <a href="src/{{$op.Filename}}" target="_blank">{{$op.Filename}}</a> - ({{formatFilesize $op.Filesize}} , {{$op.ImageW}}x{{$op.ImageH}}, {{$op.FilenameOriginal}})</div>
|
||||
<a class="upload-container" href="{{$config.SiteWebfolder}}{{$board.Dir}}/src/{{$op.Filename}}"><img src="{{$config.SiteWebfolder}}{{$board.Dir}}/thumb/{{imageToThumbnailPath $op.Filename}}" width="{{$op.ThumbW}}" height="{{$op.ThumbH}}" class="upload" /></a>
|
||||
<a class="upload-container" href="{{$.config.SiteWebfolder}}{{$.board.Dir}}/src/{{$op.Filename}}"><img src="{{$.config.SiteWebfolder}}{{$.board.Dir}}/thumb/{{imageToThumbnailPath $op.Filename}}" width="{{$op.ThumbW}}" height="{{$op.ThumbH}}" class="upload" /></a>
|
||||
{{else}}
|
||||
<div class="file-deleted-box" style="text-align:center;">File removed</div>
|
||||
{{end}}
|
||||
{{end}}
|
||||
<input type="checkbox" id="check{{$op.ID}}" name="check{{$op.ID}}" /><label class="post-info" for="check{{$op.ID}}"> <span class="subject">{{$op.Subject}}</span> <span class="postername">{{if stringNeq $op.Email ""}}<a href="mailto:{{$op.Email}}">{{end}}{{if stringNeq $op.Name ""}}{{$op.Name}}{{else}}{{if stringEq $op.Tripcode ""}}{{$board.Anonymous}}{{end}}{{end}}{{if stringNeq $op.Email ""}}</a>{{end}}</span>{{if stringNeq $op.Tripcode ""}}<span class="tripcode">!{{$op.Tripcode}}</span>{{end}} {{formatTimestamp $op.Timestamp}} </label><a href="/{{$board.Dir}}/res/{{$op.ID}}.html#{{$op.ID}}">No.</a> <a href="javascript:quote({{$op.ID}})" class="backlink-click">{{$op.ID}}</a> <span class="post-links"> <span class="thread-ddown">[<a href="javascript:void(0)">▼</a>]</span> <span>[<a href="/{{$board.Dir}}/res/{{$op.ID}}.html">View</a>]</span></span><br />
|
||||
<input type="checkbox" id="check{{$op.ID}}" name="check{{$op.ID}}" /><label class="post-info" for="check{{$op.ID}}"> <span class="subject">{{$op.Subject}}</span> <span class="postername">{{if ne $op.Email ""}}<a href="mailto:{{$op.Email}}">{{end}}{{if ne $op.Name ""}}{{$op.Name}}{{else}}{{if eq $op.Tripcode ""}}{{$.board.Anonymous}}{{end}}{{end}}{{if ne $op.Email ""}}</a>{{end}}</span>{{if ne $op.Tripcode ""}}<span class="tripcode">!{{$op.Tripcode}}</span>{{end}} {{formatTimestamp $op.Timestamp}} </label><a href="/{{$.board.Dir}}/res/{{$op.ID}}.html#{{$op.ID}}">No.</a> <a href="javascript:quote({{$op.ID}})" class="backlink-click">{{$op.ID}}</a> <span class="post-links"> <span class="thread-ddown">[<a href="javascript:void(0)">▼</a>]</span> <span>[<a href="/{{$.board.Dir}}/res/{{$op.ID}}.html">View</a>]</span></span><br />
|
||||
<div class="post-text">{{truncateMessage $op.MessageHTML 2222 18}}</div>
|
||||
{{if gt $thread.NumReplies 3}}
|
||||
<b>{{subtract $thread.NumReplies 3}} post{{if gt $thread.NumReplies 4}}s{{end}} omitted</b>
|
||||
|
@ -41,11 +28,11 @@
|
|||
<div class="reply-container" id="replycontainer{{$reply.ID}}">
|
||||
<a class="anchor" id="{{$reply.ID}}"></a>
|
||||
<div class="reply" id="reply{{$reply.ID}}">
|
||||
<input type="checkbox" id="check{{$reply.ID}}" name="check{{$reply.ID}}" /> <label class="post-info" for="check{{$reply.ID}}"> <span class="subject">{{$reply.Subject}}</span> <span class="postername">{{if stringNeq $reply.Email ""}}<a href="mailto:{{$reply.Email}}">{{end}}{{if stringNeq $reply.Name ""}}{{$reply.Name}}{{else}}{{if stringEq $reply.Tripcode ""}}{{$board.Anonymous}}{{end}}{{end}}{{if stringNeq $reply.Email ""}}</a>{{end}}</span>{{if stringNeq $reply.Tripcode ""}}<span class="tripcode">!{{$reply.Tripcode}}</span>{{end}} {{formatTimestamp $reply.Timestamp}} </label><a href="/{{$board.Dir}}/res/{{$op.ID}}.html#{{$reply.ID}}">No.</a> <a href="javascript:quote({{$reply.ID}})" class="backlink-click">{{$reply.ID}}</a> <span class="post-links"><span class="thread-ddown">[<a href="javascript:void(0)">▼</a>]</span></span><br />
|
||||
{{if stringNeq $reply.Filename ""}}
|
||||
{{if stringNeq $reply.Filename "deleted"}}
|
||||
<input type="checkbox" id="check{{$reply.ID}}" name="check{{$reply.ID}}" /> <label class="post-info" for="check{{$reply.ID}}"> <span class="subject">{{$reply.Subject}}</span> <span class="postername">{{if ne $reply.Email ""}}<a href="mailto:{{$reply.Email}}">{{end}}{{if ne $reply.Name ""}}{{$reply.Name}}{{else}}{{if eq $reply.Tripcode ""}}{{.board.Anonymous}}{{end}}{{end}}{{if eq $reply.Email ""}}</a>{{end}}</span>{{if ne $reply.Tripcode ""}}<span class="tripcode">!{{$reply.Tripcode}}</span>{{end}} {{formatTimestamp $reply.Timestamp}} </label><a href="/{{$.board.Dir}}/res/{{$op.ID}}.html#{{$reply.ID}}">No.</a> <a href="javascript:quote({{$reply.ID}})" class="backlink-click">{{$reply.ID}}</a> <span class="post-links"><span class="thread-ddown">[<a href="javascript:void(0)">▼</a>]</span></span><br />
|
||||
{{if ne $reply.Filename ""}}
|
||||
{{if ne $reply.Filename "deleted"}}
|
||||
<span class="file-info">File: <a href="src/{{$reply.Filename}}" target="_blank">{{$reply.Filename}}</a> - ({{formatFilesize $reply.Filesize}} , {{$reply.ImageW}}x{{$reply.ImageH}}, {{$reply.FilenameOriginal}})</span><br />
|
||||
<a class="upload-container" href="{{$config.SiteWebfolder}}{{$board.Dir}}/src/{{$reply.Filename}}"><img src="{{$config.SiteWebfolder}}{{$board.Dir}}/thumb/{{imageToThumbnailPath $reply.Filename}}" width="{{$reply.ThumbW}}" height="{{$reply.ThumbH}}" class="upload" /></a>
|
||||
<a class="upload-container" href="{{$.config.SiteWebfolder}}{{$.board.Dir}}/src/{{$reply.Filename}}"><img src="{{$.config.SiteWebfolder}}{{$.board.Dir}}/thumb/{{imageToThumbnailPath $reply.Filename}}" width="{{$reply.ThumbW}}" height="{{$reply.ThumbH}}" class="upload" /></a>
|
||||
{{else}}
|
||||
<div class="file-deleted-box" style="text-align:center;">File removed</div>
|
||||
{{end}}{{end}}
|
||||
|
@ -56,7 +43,8 @@
|
|||
<hr />{{end}}
|
||||
<div id="right-bottom-content">
|
||||
<div id="report-delbox">
|
||||
<input type="hidden" name="board" value="{{$board.Dir}}" />
|
||||
<input type="hidden" name="board" value="{{.board.Dir}}" />
|
||||
<input type="hidden" name="boardid" value="{{.board.ID}}" />
|
||||
<label>[<input type="checkbox" name="fileonly"/>File only]</label> <input type="password" size="10" name="password" id="delete-password" /> <input type="submit" name="delete_btn" value="Delete" onclick="return confirm('Are you sure you want to delete these posts?')" /><br />
|
||||
Reason: <input type="text" size="10" name="reason" id="reason" /> <input type="submit" name="report_btn" value="Report" /><br />
|
||||
Edit post <input type="submit" name="edit_btn" value="Edit" />
|
||||
|
@ -65,25 +53,25 @@
|
|||
</form>
|
||||
<div id="left-bottom-content">
|
||||
<table id="pages">
|
||||
<tr><td>{{if gt $board.CurrentPage 1}}
|
||||
<form method="GET" action="{{$config.SiteWebfolder}}{{$board.Dir}}/{{subtract $board.CurrentPage 1}}.html">
|
||||
<tr><td>{{if gt .board.CurrentPage 2}}
|
||||
<form method="GET" action="{{.config.SiteWebfolder}}{{.board.Dir}}/{{subtract .board.CurrentPage 1}}.html">
|
||||
<input type="submit" value="Previous" />
|
||||
</form>
|
||||
{{else if intEq $board.CurrentPage 1}}
|
||||
<form method="GET" action="{{$config.SiteWebfolder}}{{$board.Dir}}/">
|
||||
{{else if intEq .board.CurrentPage 2}}
|
||||
<form method="GET" action="{{.config.SiteWebfolder}}{{.board.Dir}}/board.html">
|
||||
<input type="submit" value="Previous" />
|
||||
</form>
|
||||
{{else}}Previous{{end}}</td>
|
||||
|
||||
<td>[<a href="{{$config.SiteWebfolder}}{{$board.Dir}}/">Index</a>]{{range $i,$_ := makeLoop $board.NumPages}} [{{if eq $i $board.CurrentPage}}<b>{{end}}<a href="{{$config.SiteWebfolder}}{{$board.Dir}}/{{add $i 1}}.html">{{add $i 1}}</a>{{if eq $i $board.CurrentPage}}</b>{{end}}]{{end}}</td>
|
||||
<td>{{if lt $board.CurrentPage $board.NumPages}}
|
||||
<form method="GET" action="{{$config.SiteWebfolder}}{{$board.Dir}}/{{add $board.CurrentPage 1}}.html">
|
||||
<td>{{range $_,$i := makeLoop .board.NumPages 1}} [{{if eq $i $.board.CurrentPage}}<b>{{end}}<a href="{{$.config.SiteWebfolder}}{{$.board.Dir}}/{{if intEq $i 1}}board{{else}}{{$i}}{{end}}.html">{{$i}}</a>{{if eq $i $.board.CurrentPage}}</b>{{end}}]{{end}}</td>
|
||||
<td>{{if lt .board.CurrentPage .board.NumPages}}
|
||||
<form method="GET" action="{{.config.SiteWebfolder}}{{.board.Dir}}/{{add .board.CurrentPage 1}}.html">
|
||||
<input type="submit" value="Next" />
|
||||
</form>
|
||||
{{else}}Next{{end}}</td></tr>
|
||||
</table>
|
||||
<span id="boardmenu-bottom">
|
||||
[{{range $i, $boardlink := $board_arr}} {{if gt $i 0}}/{{end}} <a href="/{{$boardlink.Dir}}/">{{$boardlink.Dir}}</a> {{end}}]
|
||||
[{{range $i, $boardlink := $.boards}}{{if gt $i 0}}/{{end}} <a href="/{{$boardlink.Dir}}/">{{$boardlink.Dir}}</a> {{end}}]
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{{template "global_footer.html" .}}
|
|
@ -2,36 +2,37 @@
|
|||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
{{if stringNeq $op.Subject ""}}
|
||||
<title>/{{$board.Dir}}/ - {{truncateString $op.Subject 20 true}}</title>
|
||||
{{else}}{{if stringNeq $op.MessageHTML ""}}
|
||||
<title>/{{$board.Dir}}/ - {{truncateString $op.MessageText 20 true}}</title>
|
||||
{{end}}{{end}}
|
||||
<title>/{{$board.Dir}}/ - {{$board.Title}}</title>
|
||||
{{with .op.Subject}}
|
||||
{{if ne .op.Subject ""}}
|
||||
<title>/{{.board.Dir}}/ - {{truncateString .op.Subject 20 true}}</title>
|
||||
{{else if ne .op.MessageHTML ""}}
|
||||
<title>/{{.board.Dir}}/ - {{truncateString .op.MessageText 20 true}}</title>
|
||||
{{end}}
|
||||
{{else}}<title>/{{.board.Dir}}/ - {{.board.Title}}</title>{{end}}
|
||||
<title>/{{.board.Dir}}/ - {{.board.Title}}</title>
|
||||
<script type="text/javascript" src="/javascript/jquery-3.3.1.min.js"></script>
|
||||
<script type="text/javascript">
|
||||
var styles = [{{range $ii, $style := $config.Styles_img}}{{if gt $ii 0}}, {{end}}"{{$style}}"{{end}}];
|
||||
var webroot = "{{$config.SiteWebfolder}}";
|
||||
var styles = [{{range $ii, $style := $.config.Styles_img}}{{if gt $ii 0}}, {{end}}"{{$style}}"{{end}}];
|
||||
var webroot = "{{$.config.SiteWebfolder}}";
|
||||
var thread_type = "thread";
|
||||
|
||||
function changePage(sel) {
|
||||
window.location = webroot+"test/res/{{$op.ID}}p"+sel.value+".html";
|
||||
window.location = webroot+"{{$.board.Dir}}/res/{{$.op.ID}}p"+sel.value+".html";
|
||||
}
|
||||
</script>
|
||||
<script type="text/javascript" src="/javascript/gochan.js"></script>
|
||||
<script type="text/javascript" src="/javascript/manage.js"></script>
|
||||
<link rel="stylesheet" href="/css/global/img.css" />
|
||||
{{range $i, $style := $config.Styles_img}}
|
||||
<link rel="{{if isStyleNotDefault_img $style}}alternate {{end}}stylesheet" href="/css/{{$style}}/img.css" />{{end}}
|
||||
{{range $_, $style := .config.Styles_img}}
|
||||
<link rel="{{if not (isStyleDefault_img $style)}}alternate {{end}}stylesheet" href="/css/{{$style}}/img.css" />{{end}}
|
||||
<link rel="shortcut icon" href="/favicon.png" />
|
||||
</head>
|
||||
<body>
|
||||
<div id="topbar">
|
||||
{{range $i, $board := $board_arr}}
|
||||
<a href="/{{$board.Dir}}/" class="topbar-item">/{{$board.Dir}}/</a>
|
||||
{{end}}
|
||||
{{range $i, $board := .boards}}<a href="/{{$board.Dir}}/" class="topbar-item">/{{$board.Dir}}/</a>{{end}}
|
||||
</div>
|
||||
<header>
|
||||
<h1>/{{$board.Dir}}/ - {{$board.Title}}</h1>
|
||||
<div id="board-subtitle">{{$board.Subtitle}}</div>
|
||||
<h1>/{{$.board.Dir}}/ - {{$.board.Title}}</h1>
|
||||
<div id="board-subtitle">{{$.board.Subtitle}}</div>
|
||||
</header>
|
||||
<hr />
|
|
@ -1,5 +1,6 @@
|
|||
{{template "img_header.html" .}}
|
||||
<div id="threadlinks-top">
|
||||
<a href="{{$config.SiteWebfolder}}{{$board.Dir}}/board.html" >Return</a><br />
|
||||
<a href="{{$.config.SiteWebfolder}}{{$.board.Dir}}/board.html" >Return</a><br />
|
||||
<select id="changepage" onchange="changePage(this)">
|
||||
<option value="">Select page...</option>
|
||||
<option value="1">Page 1</option>
|
||||
|
@ -7,62 +8,47 @@
|
|||
<option value="3">Page 3</option>
|
||||
<option value="4">Page 4</option>
|
||||
</select>
|
||||
{{/*}}<a href="{{$op.ID}}-100.html">First 100 posts</a><br />
|
||||
<a href="{{$op.ID}}+50.html">Last 50 posts</a><br />{{*/}}
|
||||
</div>
|
||||
<div id="right-sidelinks">
|
||||
<a href="{{$config.SiteWebfolder}}{{$board.Dir}}/catalog.html">Board catalog</a><br />
|
||||
</div>
|
||||
<div id="postbox-area">
|
||||
<form name="postform" action="/post" method="POST" enctype="multipart/form-data">
|
||||
<input type="hidden" name="threadid" value="{{$op.ID}}" />
|
||||
<input type="hidden" name="boardid" value="{{$op.BoardID}}" />
|
||||
<input type="text" name="username" style="display:none" />
|
||||
<table id="postbox-static">
|
||||
<tr><th class="postblock">Name</th><td><input type="text" id="postname" name="postname" maxlength="50" size="28" {{/* value="Name" onFocus="if(this.value=='Name') {this,value= ''}" onBlur="if(this.value == '') {this.value = 'Name'}"*/}}/></td></tr>
|
||||
<tr><th class="postblock">Email</th><td><input type="text" id="postemail" name="postemail" maxlength="50" size="28" /></td></tr>
|
||||
<tr><th class="postblock">Subject</th><td><input type="text" name="postsubject" maxlength="100" size="35" /><input type="submit" value="Post"/></td></tr>
|
||||
<tr><th class="postblock">Message</th><td><textarea rows="4" cols="48" name="postmsg" id="postmsg"></textarea></td></tr>
|
||||
<tr><th class="postblock">File</th><td><input name="imagefile" type="file"><input type="checkbox" id="spoiler" name="spoiler"/><label for="spoiler">Spoiler</label></td></tr>
|
||||
<tr><th class="postblock">Password</th><td><input type="password" id="postpassword" name="postpassword" size="14" /> (for post/file deletion)</td></tr>
|
||||
</table>
|
||||
</form>
|
||||
<a href="{{$.config.SiteWebfolder}}{{$.board.Dir}}/catalog.html">Board catalog</a><br />
|
||||
</div>
|
||||
{{template "postbox.html" .}}
|
||||
<hr />
|
||||
<div id="content">
|
||||
<form action="/util" method="POST" id="main-form">
|
||||
<div class="thread" id="{{$op.ID}}">
|
||||
<div class="op-post" id="op{{$op.ID}}">
|
||||
{{if stringNeq $op.Filename ""}}
|
||||
{{if stringNeq $op.Filename "deleted"}}
|
||||
<div class="file-info">File: <a href="../src/{{$op.Filename}}" target="_blank">{{$op.Filename}}</a> - ({{formatFilesize $op.Filesize}} , {{$op.ImageW}}x{{$op.ImageH}}, {{$op.FilenameOriginal}})</div>
|
||||
<a class="upload-container" href="{{$config.SiteWebfolder}}{{$board.Dir}}/src/{{$op.Filename}}"><img src="{{$config.SiteWebfolder}}{{$board.Dir}}/thumb/{{imageToThumbnailPath $op.Filename}}" width="{{$op.ThumbW}}" height="{{$op.ThumbH}}" class="upload" /></a>
|
||||
<div class="thread" id="{{$.op.ID}}">
|
||||
<div class="op-post" id="op{{.op.ID}}">
|
||||
{{if ne $.op.Filename ""}}
|
||||
{{if ne $.op.Filename "deleted"}}
|
||||
<div class="file-info">File: <a href="../src/{{.op.Filename}}" target="_blank">{{.op.Filename}}</a> - ({{formatFilesize .op.Filesize}} , {{.op.ImageW}}x{{.op.ImageH}}, {{.op.FilenameOriginal}})</div>
|
||||
<a class="upload-container" href="{{.config.SiteWebfolder}}{{.board.Dir}}/src/{{.op.Filename}}"><img src="{{.config.SiteWebfolder}}{{.board.Dir}}/thumb/{{imageToThumbnailPath .op.Filename}}" width="{{.op.ThumbW}}" height="{{.op.ThumbH}}" class="upload" /></a>
|
||||
{{else}}
|
||||
<div class="file-deleted-box" style="text-align:center;">File removed</div>
|
||||
{{end}}{{end}}
|
||||
<input type="checkbox" id="check{{$op.ID}}" name="check{{$op.ID}}" /><label class="post-info" for="check{{$op.ID}}"> <span class="subject">{{$op.Subject}}</span> <span class="postername">{{if stringNeq $op.Email ""}}<a href="mailto:{{$op.Email}}">{{end}}{{if stringNeq $op.Name ""}}{{$op.Name}}{{else}}{{if stringEq $op.Tripcode ""}}{{$board.Anonymous}}{{end}}{{end}}{{if stringNeq $op.Email ""}}</a>{{end}}</span>{{if stringNeq $op.Tripcode ""}}<span class="tripcode">!{{$op.Tripcode}}</span>{{end}} {{formatTimestamp $op.Timestamp}} </label><a href="/{{$board.Dir}}/res/{{$op.ID}}.html#{{$op.ID}}">No.</a> <a href="javascript:quote({{$op.ID}})" class="backlink-click">{{$op.ID}}</a> <span class="post-links"> <span class="thread-ddown">[<a href="javascript:void(0)">▼</a>]</span></span><br />
|
||||
<div class="post-text">{{$op.MessageHTML}}</div>
|
||||
<input type="checkbox" id="check{{.op.ID}}" name="check{{.op.ID}}" /><label class="post-info" for="check{{.op.ID}}"> <span class="subject">{{.op.Subject}}</span> <span class="postername">{{if ne .op.Email ""}}<a href="mailto:{{.op.Email}}">{{end}}{{if ne .op.Name ""}}{{.op.Name}}{{else}}{{if eq .op.Tripcode ""}}{{.board.Anonymous}}{{end}}{{end}}{{if ne .op.Email ""}}</a>{{end}}</span>{{if ne .op.Tripcode ""}}<span class="tripcode">!{{.op.Tripcode}}</span>{{end}} {{formatTimestamp .op.Timestamp}} </label><a href="/{{.board.Dir}}/res/{{.op.ID}}.html#{{.op.ID}}">No.</a> <a href="javascript:quote({{.op.ID}})" class="backlink-click">{{.op.ID}}</a> <span class="post-links"> <span class="thread-ddown">[<a href="javascript:void(0)">▼</a>]</span></span><br />
|
||||
<div class="post-text">{{.op.MessageHTML}}</div>
|
||||
</div>
|
||||
{{range $reply_num,$reply := $post_arr}}{{if gt $reply_num 0}}
|
||||
{{range $reply_num,$reply := .posts}}
|
||||
<div class="reply-container" id="replycontainer{{$reply.ID}}">
|
||||
<a class="anchor" id="{{$reply.ID}}"></a>
|
||||
<div class="reply" id="reply{{$reply.ID}}">
|
||||
<input type="checkbox" id="check{{$reply.ID}}" name="check{{$reply.ID}}" /> <label class="post-info" for="check{{$reply.ID}}"> <span class="subject">{{$reply.Subject}}</span> <span class="postername">{{if stringNeq $reply.Email ""}}<a href="mailto:{{$reply.Email}}">{{end}}{{if stringNeq $reply.Name ""}}{{$reply.Name}}{{else}}{{if stringEq $reply.Tripcode ""}}{{$board.Anonymous}}{{end}}{{end}}{{if stringNeq $reply.Email ""}}</a>{{end}}</span>{{if stringNeq $reply.Tripcode ""}}<span class="tripcode">!{{$reply.Tripcode}}</span>{{end}} {{formatTimestamp $reply.Timestamp}} </label><a href="/{{$board.Dir}}/res/{{$op.ID}}.html#{{$reply.ID}}">No.</a> <a href="javascript:quote({{$reply.ID}})" class="backlink-click">{{$reply.ID}}</a> <span class="post-links"><span class="thread-ddown">[<a href="javascript:void(0)">▼</a>]</span></span><br />
|
||||
{{if stringNeq $reply.Filename ""}}
|
||||
{{if stringNeq $reply.Filename "deleted"}}
|
||||
<input type="checkbox" id="check{{$reply.ID}}" name="check{{$reply.ID}}" /> <label class="post-info" for="check{{$reply.ID}}"> <span class="subject">{{$reply.Subject}}</span> <span class="postername">{{if ne $reply.Email ""}}<a href="mailto:{{$reply.Email}}">{{end}}{{if ne $reply.Name ""}}{{$reply.Name}}{{else}}{{if eq $reply.Tripcode ""}}{{$.board.Anonymous}}{{end}}{{end}}{{if ne $reply.Email ""}}</a>{{end}}</span>{{if ne $reply.Tripcode ""}}<span class="tripcode">!{{$reply.Tripcode}}</span>{{end}} {{formatTimestamp $reply.Timestamp}} </label><a href="/{{$.board.Dir}}/res/{{$.op.ID}}.html#{{$reply.ID}}">No.</a> <a href="javascript:quote({{$reply.ID}})" class="backlink-click">{{$reply.ID}}</a> <span class="post-links"><span class="thread-ddown">[<a href="javascript:void(0)">▼</a>]</span></span><br />
|
||||
{{if ne $reply.Filename ""}}
|
||||
{{if ne $reply.Filename "deleted"}}
|
||||
<span class="file-info">File: <a href="../src/{{$reply.Filename}}" target="_blank">{{$reply.Filename}}</a> - ({{formatFilesize $reply.Filesize}} , {{$reply.ImageW}}x{{$reply.ImageH}}, {{$reply.FilenameOriginal}})</span><br />
|
||||
<a class="upload-container" href="{{$config.SiteWebfolder}}{{$board.Dir}}/src/{{$reply.Filename}}"><img src="{{$config.SiteWebfolder}}{{$board.Dir}}/thumb/{{imageToThumbnailPath $reply.Filename}}" width="{{$reply.ThumbW}}" height="{{$reply.ThumbH}}" class="upload" /></a>
|
||||
<a class="upload-container" href="{{$.config.SiteWebfolder}}{{$.board.Dir}}/src/{{$reply.Filename}}"><img src="{{$.config.SiteWebfolder}}{{$.board.Dir}}/thumb/{{imageToThumbnailPath $reply.Filename}}" width="{{$reply.ThumbW}}" height="{{$reply.ThumbH}}" class="upload" /></a>
|
||||
{{else}}
|
||||
<div class="file-deleted-box" style="text-align:center;">File removed</div>
|
||||
{{end}}{{end}}
|
||||
<div class="post-text">{{$reply.MessageHTML}}</div>
|
||||
</div>
|
||||
</div>{{end}}{{end}}
|
||||
</div>{{end}}
|
||||
</div>
|
||||
<hr />
|
||||
<div id="right-bottom-content">
|
||||
<div id="report-delbox">
|
||||
<input type="hidden" name="board" value="{{$board.Dir}}" />
|
||||
<input type="hidden" name="board" value="{{.board.Dir}}" />
|
||||
<input type="hidden" name="boardid" value="{{.board.ID}}" />
|
||||
<label>[<input type="checkbox" name="fileonly"/>File only]</label> <input type="password" size="10" name="password" id="delete-password" /> <input type="submit" name="delete_btn" value="Delete" onclick="return confirm('Are you sure you want to delete these posts?')" /><br />
|
||||
Reason: <input type="text" size="10" name="reason" id="reason" /> <input type="submit" name="report_btn" value="Report" /><br />
|
||||
Edit post <input type="submit" name="edit_btn" value="Edit" />
|
||||
|
@ -72,22 +58,25 @@
|
|||
<div id="left-bottom-content">
|
||||
<table id="pages">
|
||||
<tr>
|
||||
<td><a href="{{$config.SiteWebfolder}}{{$board.Dir}}/">Return</a></td>
|
||||
<td>{{if gt $op.CurrentPage 1}}
|
||||
<form method="GET" action="{{$config.SiteWebfolder}}{{$board.Dir}}/res/{{$op.ID}}p{{subtract $op.CurrentPage 1}}.html">
|
||||
<input type="submit" value="Previous" />
|
||||
</form>
|
||||
<td><a href="{{.config.SiteWebfolder}}{{.board.Dir}}/">Return</a></td>
|
||||
<td>{{if gt .op.CurrentPage 1}}
|
||||
<form method="GET" action="{{.config.SiteWebfolder}}{{.board.Dir}}/res/{{.op.ID}}p{{subtract .op.CurrentPage 1}}.html">
|
||||
<input type="submit" value="Previous" />
|
||||
</form>
|
||||
{{else}}Previous{{end}}
|
||||
</td>
|
||||
<td>[<a href="{{$config.SiteWebfolder}}{{$board.Dir}}/res/{{$op.ID}}.html">All</a>]{{range $i,$_ := makeLoop $op.NumPages}} [{{if eq $i (subtract $op.CurrentPage 1)}}<b>{{end}}<a href="{{$config.SiteWebfolder}}{{$board.Dir}}/res/{{$op.ID}}p{{add $i 1}}.html">{{add $i 1}}</a>{{if eq $i (subtract $op.CurrentPage 1)}}</b>{{end}}]{{end}}</td>
|
||||
<td>{{if lt $op.CurrentPage $op.NumPages}}
|
||||
<form method="GET" action="{{$config.SiteWebfolder}}{{$board.Dir}}/res/{{$op.ID}}p{{add $op.CurrentPage 1}}.html">
|
||||
<td>[<a href="{{.config.SiteWebfolder}}{{.board.Dir}}/res/{{.op.ID}}.html">All</a>]
|
||||
{{range $i,$_ := makeLoop .op.NumPages 0}}
|
||||
[{{if eq $i (subtract $.op.CurrentPage 1)}}<b>{{end}}<a href="{{$.config.SiteWebfolder}}{{$.board.Dir}}/res/{{$.op.ID}}p{{add $i 1}}.html">{{add $i 1}}</a>{{if eq $i (subtract $.op.CurrentPage 1)}}</b>{{end}}]{{end}}</td>
|
||||
<td>{{if lt .op.CurrentPage .op.NumPages}}
|
||||
<form method="GET" action="{{.config.SiteWebfolder}}{{.board.Dir}}/res/{{.op.ID}}p{{add .op.CurrentPage 1}}.html">
|
||||
<input type="submit" value="Next" />
|
||||
</form>
|
||||
{{else}}Next{{end}}</td></tr>
|
||||
</table>
|
||||
<span id="boardmenu-bottom">
|
||||
[{{range $i, $boardlink := $board_arr}} {{if gt $i 0}}/{{end}} <a href="/{{$boardlink.Dir}}/">{{$boardlink.Dir}}</a> {{end}}]
|
||||
[{{range $i, $boardlink := .boards}} {{if gt $i 0}}/{{end}} <a href="/{{$boardlink.Dir}}/">{{$boardlink.Dir}}</a> {{end}}]
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{{template "global_footer.html" .}}
|
|
@ -5,7 +5,7 @@
|
|||
<tr><td>Directory</td><td><input type="text" name="dir" value="" /></td></tr>
|
||||
<tr><td>Section</td><td><select name="section" selected="0">
|
||||
<option value="none">Select section...</option>
|
||||
{{range $_, $section := $section_arr}}
|
||||
{{range $_, $section := .section_arr}}
|
||||
<option value="{{$section.ID}}">{{$section.Name}}</option>{{end}}
|
||||
</select></td></tr>
|
||||
<tr><td>Order</td><td><input type="text" name="order" value="0" /></td></tr>
|
||||
|
@ -15,19 +15,18 @@
|
|||
<tr><td>Max image size</td><td><input type="text" name="maximagesize" value="4718592" /></td></tr>
|
||||
<tr><td>Max pages</td><td><input type="text" name="maxpages" value="11" /></td></tr>
|
||||
<tr><td>Default style</td><td><select name="defaultstyle" selected="">
|
||||
{{range $i, $style := $config.Styles_img}}
|
||||
<option value="{{$style}}">{{$style}}</option>{{end}}
|
||||
{{range $_, $style := .config.Styles_img}}<option value="{{$style}}">{{$style}}</option>{{end}}
|
||||
</select></td></tr>
|
||||
<tr><td>Locked</td><td><input type="checkbox" name="locked" {{if $board.Locked}}checked{{end}}/></td></tr>
|
||||
<tr><td>Forced anonymity</td><td><input type="checkbox" name="forcedanon" {{if $board.ForcedAnon}}checked{{end}}/></td></tr>
|
||||
<tr><td>Anonymous name</td><td><input type="text" name="anonymous" value="{{$board.Anonymous}}" /></td></tr>
|
||||
<tr><td>Max age</td><td><input type="text" name="maxage" value="{{$board.MaxAge}}"/></td></tr>
|
||||
<tr><td>Bump limit<sup title="After this many posts, the thread will stop being bumped when a new post is made. ">[?]</sup></td><td><input type="text" name="autosageafter" value="{{$board.AutosageAfter}}"/></td></tr>
|
||||
<tr><td>No images after</td><td><input type="text" name="noimagesafter" value="{{$board.NoImagesAfter}}"/></td></tr>
|
||||
<tr><td>Max message length</td><td><input type="text" name="maxmessagelength" value="{{$board.MaxMessageLength}}"/></td></tr>
|
||||
<tr><td>Embeds allowed</td><td><input type="checkbox" name="embedsallowed" {{if $board.EmbedsAllowed}}checked{{end}}/></td></tr>
|
||||
<tr><td>Redirect to thread<sup title="If checked, posts will redirect to the thread by default without requiring 'noko' in the email">[?]</sup></td><td><input type="checkbox" name="redirecttothread" {{if $board.RedirectToThread}}checked{{end}}/></td></tr>
|
||||
<tr><td>Require an uploaded file</td><td><input type="checkbox" name="require_file" {{if $board.RequireFile}}checked{{end}}/></td></tr>
|
||||
<tr><td>Enable catalog</td><td><input type="checkbox" name="enablecatalog" {{if $board.EnableCatalog}}checked{{end}}/></td></tr>
|
||||
<tr><td>Locked</td><td><input type="checkbox" name="locked" {{if .board.Locked}}checked{{end}}/></td></tr>
|
||||
<tr><td>Forced anonymity</td><td><input type="checkbox" name="forcedanon" {{if .board.ForcedAnon}}checked{{end}}/></td></tr>
|
||||
<tr><td>Anonymous name</td><td><input type="text" name="anonymous" value="{{.board.Anonymous}}" /></td></tr>
|
||||
<tr><td>Max age</td><td><input type="text" name="maxage" value="{{.board.MaxAge}}"/></td></tr>
|
||||
<tr><td>Bump limit<sup title="After this many posts, the thread will stop being bumped when a new post is made. ">[?]</sup></td><td><input type="text" name="autosageafter" value="{{.board.AutosageAfter}}"/></td></tr>
|
||||
<tr><td>No images after</td><td><input type="text" name="noimagesafter" value="{{.board.NoImagesAfter}}"/></td></tr>
|
||||
<tr><td>Max message length</td><td><input type="text" name="maxmessagelength" value="{{.board.MaxMessageLength}}"/></td></tr>
|
||||
<tr><td>Embeds allowed</td><td><input type="checkbox" name="embedsallowed" {{if .board.EmbedsAllowed}}checked{{end}}/></td></tr>
|
||||
<tr><td>Redirect to thread<sup title="If checked, posts will redirect to the thread by default without requiring 'noko' in the email">[?]</sup></td><td><input type="checkbox" name="redirecttothread" {{if .board.RedirectToThread}}checked{{end}}/></td></tr>
|
||||
<tr><td>Require an uploaded file</td><td><input type="checkbox" name="require_file" {{if .board.RequireFile}}checked{{end}}/></td></tr>
|
||||
<tr><td>Enable catalog</td><td><input type="checkbox" name="enablecatalog" {{if .board.EnableCatalog}}checked{{end}}/></td></tr>
|
||||
</table>
|
||||
<input type="submit" /></form>
|
||||
|
|
|
@ -2,21 +2,21 @@
|
|||
<form action="/manage?action=config" method="POST">
|
||||
<table>
|
||||
<tr><th>Name</th><th>Value</th><th>Description</th></tr>
|
||||
<tr><th>DocumentRoot</th><td><input type="text" value="{{$config.DocumentRoot}}"/></td></tr>
|
||||
<tr><th>TemplateDir</th><td><input type="text" value="{{$config.TemplateDir}}" /></td></tr>
|
||||
<tr><th>LogDir</th><td><input type="text" value="{{$config.LogDir}}" /></td></tr>
|
||||
<tr><th>DocumentRoot</th><td><input type="text" value="{{.config.DocumentRoot}}"/></td></tr>
|
||||
<tr><th>TemplateDir</th><td><input type="text" value="{{.config.TemplateDir}}" /></td></tr>
|
||||
<tr><th>LogDir</th><td><input type="text" value="{{.config.LogDir}}" /></td></tr>
|
||||
|
||||
<tr><th>Lockdown</th><td><input type="checkbox" {{if $config.Lockdown}}checked{{end}}/></td></tr>
|
||||
<tr><th>LockdownMessage</th><td><input type="text" value="{{$config.LockdownMessage}}" /></td></tr>
|
||||
<tr><th>Lockdown</th><td><input type="checkbox" {{if .config.Lockdown}}checked{{end}}/></td></tr>
|
||||
<tr><th>LockdownMessage</th><td><input type="text" value="{{.config.LockdownMessage}}" /></td></tr>
|
||||
|
||||
<tr><th>UseSillytags</th><td><input type="checkbox" /></td></tr>
|
||||
<tr><th>Modboard</th><td><input type="text" value="{{$config.Modboard}}"></td></tr>
|
||||
<tr><th>Modboard</th><td><input type="text" value="{{.config.Modboard}}"></td></tr>
|
||||
|
||||
<tr><th>SiteName</th><td><input type="text" value="{{$config.SiteName}}" /></td></tr>
|
||||
<tr><th>SiteSlogan</th><td><input type="text" value="{{$config.SiteSlogan}}" /></td></tr>
|
||||
<tr><th>SiteHeaderURL</th><td><input type="text" value="{{$config.SiteHeaderURL}}" /></td></tr>
|
||||
{{/*<tr><th>SiteWebfolder</th><td><input type="text" value="{{$config.SiteWebFolder}}" /></td></tr>*/}}
|
||||
<tr><th>DomainRegex</th><td><input type="text" value="{{$config.DomainRegex}}" /></td><td>Don't touch this unless you know what you're doing! This could fuck your shit up<br />Default: (https|http)://(.*)/(.*)</td></tr>
|
||||
<tr><th>SiteName</th><td><input type="text" value="{{.config.SiteName}}" /></td></tr>
|
||||
<tr><th>SiteSlogan</th><td><input type="text" value="{{.config.SiteSlogan}}" /></td></tr>
|
||||
<tr><th>SiteHeaderURL</th><td><input type="text" value="{{.config.SiteHeaderURL}}" /></td></tr>
|
||||
{{/*<tr><th>SiteWebfolder</th><td><input type="text" value="{{.config.SiteWebFolder}}" /></td></tr>*/}}
|
||||
<tr><th>DomainRegex</th><td><input type="text" value="{{.config.DomainRegex}}" /></td><td>Don't touch this unless you know what you're doing! This could fuck your shit up<br />Default: (https|http)://(.*)/(.*)</td></tr>
|
||||
</table>
|
||||
<input type="submit" />
|
||||
</form>
|
|
@ -1,7 +1,7 @@
|
|||
<title>Gochan Manage page</title>
|
||||
<link rel="stylesheet" href="/css/global/manage.css" />
|
||||
{{range $i, $style := .Styles_img}}
|
||||
<link rel="{{if isStyleNotDefault_img $style}}alternate {{end}}stylesheet" href="/css/{{$style}}/manage.css" />{{end}}
|
||||
<link rel="{{if not (isStyleDefault_img $style)}}alternate {{end}}stylesheet" href="/css/{{$style}}/manage.css" />{{end}}
|
||||
<link rel="shortcut icon" href="/favicon.png" />
|
||||
<script type="text/javascript">
|
||||
var styles = [{{range $i, $style := .Styles_img}}{{if gt $i 0}}, {{end}}"{{$style}}"{{end}}];
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue