1
0
Fork 0
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:
Joshua Merrell 2018-04-06 01:03:57 -07:00
parent 5745428655
commit ce303c086e
20 changed files with 395 additions and 533 deletions

View file

@ -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

View file

@ -1,6 +1,6 @@
#!/usr/bin/env bash
VERSION=1.9.1
VERSION=1.9.2
GOOS_ORIG=$GOOS
function copyStuff {

View file

@ -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>

View file

@ -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>

View file

@ -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 + "`;")

View file

@ -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 />"

View file

@ -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
}

View file

@ -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())

View file

@ -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)
}

View file

@ -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"`

View file

@ -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 {

View file

@ -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>

View file

@ -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>

View file

@ -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>

View file

@ -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)">&#9660;</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)">&#9660;</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)">&#9660;</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)">&#9660;</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" .}}

View file

@ -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 />

View file

@ -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)">&#9660;</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)">&#9660;</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)">&#9660;</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)">&#9660;</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" .}}

View file

@ -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>

View file

@ -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>

View file

@ -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}}];