mirror of
https://github.com/Eggbertx/gochan.git
synced 2025-08-02 15:06:23 -07:00
Start making templates more readable
Also change most remaining underscore var names to camelCase
This commit is contained in:
parent
6a96420e3e
commit
267a5eeb6f
16 changed files with 446 additions and 465 deletions
221
src/building.go
221
src/building.go
|
@ -21,8 +21,8 @@ func buildFrontPage() string {
|
||||||
var recentPostsArr []interface{}
|
var recentPostsArr []interface{}
|
||||||
|
|
||||||
os.Remove(path.Join(config.DocumentRoot, "index.html"))
|
os.Remove(path.Join(config.DocumentRoot, "index.html"))
|
||||||
front_file, err := os.OpenFile(path.Join(config.DocumentRoot, "index.html"), os.O_CREATE|os.O_RDWR|os.O_TRUNC, 0777)
|
frontFile, err := os.OpenFile(path.Join(config.DocumentRoot, "index.html"), os.O_CREATE|os.O_RDWR|os.O_TRUNC, 0777)
|
||||||
defer closeHandle(front_file)
|
defer closeHandle(frontFile)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return handleError(1, "Failed opening front page for writing: "+err.Error()) + "<br />\n"
|
return handleError(1, "Failed opening front page for writing: "+err.Error()) + "<br />\n"
|
||||||
}
|
}
|
||||||
|
@ -71,7 +71,7 @@ func buildFrontPage() string {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if err = frontPageTmpl.Execute(front_file, map[string]interface{}{
|
if err = frontPageTmpl.Execute(frontFile, map[string]interface{}{
|
||||||
"config": config,
|
"config": config,
|
||||||
"sections": allSections,
|
"sections": allSections,
|
||||||
"boards": allBoards,
|
"boards": allBoards,
|
||||||
|
@ -119,27 +119,28 @@ func buildBoardPages(board *Board) (html string) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err.Error()
|
return err.Error()
|
||||||
}
|
}
|
||||||
start_time := benchmarkTimer("buildBoard"+strconv.Itoa(board.ID), time.Now(), true)
|
startTime := benchmarkTimer("buildBoard"+strconv.Itoa(board.ID), time.Now(), true)
|
||||||
var current_page_file *os.File
|
var currentPageFile *os.File
|
||||||
var threads []interface{}
|
var threads []interface{}
|
||||||
var thread_pages [][]interface{}
|
var threadPages [][]interface{}
|
||||||
var stickied_threads []interface{}
|
var stickiedThreads []interface{}
|
||||||
var nonstickied_threads []interface{}
|
var nonStickiedThreads []interface{}
|
||||||
|
|
||||||
|
var opPosts []Post
|
||||||
|
|
||||||
// Get all top level posts for the board.
|
// Get all top level posts for the board.
|
||||||
op_posts, err := getPostArr(map[string]interface{}{
|
if opPosts, err = getPostArr(map[string]interface{}{
|
||||||
"boardid": board.ID,
|
"boardid": board.ID,
|
||||||
"parentid": 0,
|
"parentid": 0,
|
||||||
"deleted_timestamp": nilTimestamp,
|
"deleted_timestamp": nilTimestamp,
|
||||||
}, " ORDER BY bumped DESC")
|
}, " ORDER BY bumped DESC"); err != nil {
|
||||||
if err != nil {
|
|
||||||
html += handleError(1, "Error getting OP posts for /%s/: %s", board.Dir, err.Error()) + "<br />\n"
|
html += handleError(1, "Error getting OP posts for /%s/: %s", board.Dir, err.Error()) + "<br />\n"
|
||||||
op_posts = nil
|
opPosts = nil
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// For each top level post, start building a Thread struct
|
// For each top level post, start building a Thread struct
|
||||||
for _, op := range op_posts {
|
for _, op := range opPosts {
|
||||||
var thread Thread
|
var thread Thread
|
||||||
var postsInThread []Post
|
var postsInThread []Post
|
||||||
|
|
||||||
|
@ -201,43 +202,42 @@ func buildBoardPages(board *Board) (html string) {
|
||||||
thread.BoardReplies = reversedPosts
|
thread.BoardReplies = reversedPosts
|
||||||
|
|
||||||
// Count number of images on board page
|
// Count number of images on board page
|
||||||
image_count := 0
|
imageCount := 0
|
||||||
for _, reply := range postsInThread {
|
for _, reply := range postsInThread {
|
||||||
if reply.Filesize != 0 {
|
if reply.Filesize != 0 {
|
||||||
image_count++
|
imageCount++
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Then calculate number of omitted images.
|
// Then calculate number of omitted images.
|
||||||
thread.OmittedImages = thread.NumImages - image_count
|
thread.OmittedImages = thread.NumImages - imageCount
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add thread struct to appropriate list
|
// Add thread struct to appropriate list
|
||||||
if op.Stickied {
|
if op.Stickied {
|
||||||
stickied_threads = append(stickied_threads, thread)
|
stickiedThreads = append(stickiedThreads, thread)
|
||||||
} else {
|
} else {
|
||||||
nonstickied_threads = append(nonstickied_threads, thread)
|
nonStickiedThreads = append(nonStickiedThreads, thread)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
num, _ := deleteMatchingFiles(path.Join(config.DocumentRoot, board.Dir), "\\d.html$")
|
num, _ := deleteMatchingFiles(path.Join(config.DocumentRoot, board.Dir), "\\d.html$")
|
||||||
printf(2, "Number of files deleted: %d\n", num)
|
printf(2, "Number of files deleted: %d\n", num)
|
||||||
// Order the threads, stickied threads first, then nonstickied threads.
|
// Order the threads, stickied threads first, then nonstickied threads.
|
||||||
threads = append(stickied_threads, nonstickied_threads...)
|
threads = append(stickiedThreads, nonStickiedThreads...)
|
||||||
|
|
||||||
// If there are no posts on the board
|
// If there are no posts on the board
|
||||||
if len(threads) == 0 {
|
if len(threads) == 0 {
|
||||||
board.CurrentPage = 1
|
board.CurrentPage = 1
|
||||||
// Open board.html for writing to the first page.
|
// 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)
|
boardPageFile, err := os.OpenFile(path.Join(config.DocumentRoot, board.Dir, "board.html"), os.O_CREATE|os.O_RDWR|os.O_TRUNC, 0777)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
html += handleError(1,
|
html += handleError(1, "Failed opening /%s/board.html: %s", board.Dir, err.Error()) + "<br />"
|
||||||
"Failed opening /%s/board.html: %s", board.Dir, err.Error()) + "<br />"
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Render board page template to the file,
|
// Render board page template to the file,
|
||||||
// packaging the board/section list, threads, and board info
|
// packaging the board/section list, threads, and board info
|
||||||
if err = boardpageTmpl.Execute(board_page_file, map[string]interface{}{
|
if err = boardpageTmpl.Execute(boardPageFile, map[string]interface{}{
|
||||||
"config": config,
|
"config": config,
|
||||||
"boards": allBoards,
|
"boards": allBoards,
|
||||||
"sections": allSections,
|
"sections": allSections,
|
||||||
|
@ -249,79 +249,80 @@ func buildBoardPages(board *Board) (html string) {
|
||||||
}
|
}
|
||||||
|
|
||||||
html += "/" + board.Dir + "/ built successfully, no threads to build.\n"
|
html += "/" + board.Dir + "/ built successfully, no threads to build.\n"
|
||||||
benchmarkTimer("buildBoard"+strconv.Itoa(board.ID), start_time, false)
|
benchmarkTimer("buildBoard"+strconv.Itoa(board.ID), startTime, false)
|
||||||
return
|
return
|
||||||
} else {
|
|
||||||
// Create the archive pages.
|
|
||||||
thread_pages = paginate(config.ThreadsPerPage, threads)
|
|
||||||
board.NumPages = len(thread_pages) - 1
|
|
||||||
|
|
||||||
// Create array of page wrapper objects, and open the file.
|
|
||||||
pagesArr := make([]map[string]interface{}, board.NumPages)
|
|
||||||
|
|
||||||
catalog_json_file, err := os.OpenFile(path.Join(config.DocumentRoot, board.Dir, "catalog.json"), os.O_CREATE|os.O_RDWR|os.O_TRUNC, 0777)
|
|
||||||
defer closeHandle(catalog_json_file)
|
|
||||||
if err != nil {
|
|
||||||
html += handleError(1, "Failed opening /"+board.Dir+"/catalog.json: "+err.Error())
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
currentBoardPage := board.CurrentPage
|
|
||||||
for _, page_threads := range thread_pages {
|
|
||||||
board.CurrentPage++
|
|
||||||
var current_page_filepath string
|
|
||||||
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)
|
|
||||||
defer closeHandle(current_page_file)
|
|
||||||
if err != nil {
|
|
||||||
html += handleError(1, "Failed opening board page: "+err.Error()) + "<br />"
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
// Render the boardpage template, don't forget config
|
|
||||||
if err = boardpageTmpl.Execute(current_page_file, map[string]interface{}{
|
|
||||||
"config": config,
|
|
||||||
"boards": allBoards,
|
|
||||||
"sections": allSections,
|
|
||||||
"threads": page_threads,
|
|
||||||
"board": board,
|
|
||||||
"posts": []interface{}{
|
|
||||||
Post{BoardID: board.ID},
|
|
||||||
},
|
|
||||||
}); err != nil {
|
|
||||||
html += handleError(1, "Failed building /"+board.Dir+"/ boardpage: "+err.Error()) + "<br />"
|
|
||||||
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 />"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Collect up threads for this page.
|
|
||||||
pageMap := make(map[string]interface{})
|
|
||||||
pageMap["page"] = board.CurrentPage
|
|
||||||
pageMap["threads"] = page_threads
|
|
||||||
pagesArr = append(pagesArr, pageMap)
|
|
||||||
}
|
|
||||||
board.CurrentPage = currentBoardPage
|
|
||||||
|
|
||||||
catalog_json, err := json.Marshal(pagesArr)
|
|
||||||
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
|
|
||||||
}
|
|
||||||
html += "/" + board.Dir + "/ built successfully.\n"
|
|
||||||
}
|
}
|
||||||
benchmarkTimer("buildBoard"+strconv.Itoa(board.ID), start_time, false)
|
|
||||||
|
// Create the archive pages.
|
||||||
|
threadPages = paginate(config.ThreadsPerPage, threads)
|
||||||
|
board.NumPages = len(threadPages) - 1
|
||||||
|
|
||||||
|
// Create array of page wrapper objects, and open the file.
|
||||||
|
pagesArr := make([]map[string]interface{}, board.NumPages)
|
||||||
|
|
||||||
|
catalogJSONFile, err := os.OpenFile(path.Join(config.DocumentRoot, board.Dir, "catalog.json"), os.O_CREATE|os.O_RDWR|os.O_TRUNC, 0777)
|
||||||
|
defer closeHandle(catalogJSONFile)
|
||||||
|
if err != nil {
|
||||||
|
html += handleError(1, "Failed opening /"+board.Dir+"/catalog.json: "+err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
currentBoardPage := board.CurrentPage
|
||||||
|
for _, pageThreads := range threadPages {
|
||||||
|
board.CurrentPage++
|
||||||
|
var currentPageFilepath string
|
||||||
|
pageFilename := strconv.Itoa(board.CurrentPage) + ".html"
|
||||||
|
currentPageFilepath = path.Join(config.DocumentRoot, board.Dir, pageFilename)
|
||||||
|
currentPageFile, err = os.OpenFile(currentPageFilepath, os.O_CREATE|os.O_RDWR|os.O_TRUNC, 0777)
|
||||||
|
defer closeHandle(currentPageFile)
|
||||||
|
if err != nil {
|
||||||
|
html += handleError(1, "Failed opening board page: "+err.Error()) + "<br />"
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// Render the boardpage template, don't forget config
|
||||||
|
if err = boardpageTmpl.Execute(currentPageFile, map[string]interface{}{
|
||||||
|
"config": config,
|
||||||
|
"boards": allBoards,
|
||||||
|
"sections": allSections,
|
||||||
|
"threads": pageThreads,
|
||||||
|
"board": board,
|
||||||
|
"posts": []interface{}{
|
||||||
|
Post{BoardID: board.ID},
|
||||||
|
},
|
||||||
|
}); err != nil {
|
||||||
|
html += handleError(1, "Failed building /"+board.Dir+"/ boardpage: "+err.Error()) + "<br />"
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if board.CurrentPage == 1 {
|
||||||
|
boardPage := path.Join(config.DocumentRoot, board.Dir, "board.html")
|
||||||
|
os.Remove(boardPage)
|
||||||
|
if err = syscall.Symlink(currentPageFilepath, boardPage); !os.IsExist(err) && err != nil {
|
||||||
|
html += handleError(1, "Failed building /"+board.Dir+"/: "+err.Error()) + "<br />"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Collect up threads for this page.
|
||||||
|
pageMap := make(map[string]interface{})
|
||||||
|
pageMap["page"] = board.CurrentPage
|
||||||
|
pageMap["threads"] = pageThreads
|
||||||
|
pagesArr = append(pagesArr, pageMap)
|
||||||
|
}
|
||||||
|
board.CurrentPage = currentBoardPage
|
||||||
|
|
||||||
|
catalogJSON, err := json.Marshal(pagesArr)
|
||||||
|
if err != nil {
|
||||||
|
html += handleError(1, "Failed to marshal to JSON: "+err.Error()) + "<br />"
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if _, err = catalogJSONFile.Write(catalogJSON); err != nil {
|
||||||
|
html += handleError(1, "Failed writing /"+board.Dir+"/catalog.json: "+err.Error()) + "<br />"
|
||||||
|
return
|
||||||
|
}
|
||||||
|
html += "/" + board.Dir + "/ built successfully.\n"
|
||||||
|
|
||||||
|
benchmarkTimer("buildBoard"+strconv.Itoa(board.ID), startTime, false)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -435,7 +436,7 @@ func buildThreadPages(op *Post) (html string) {
|
||||||
}
|
}
|
||||||
|
|
||||||
var replies []Post
|
var replies []Post
|
||||||
var current_page_file *os.File
|
var currentPageFile *os.File
|
||||||
var board *Board
|
var board *Board
|
||||||
if board, err = getBoardFromID(op.BoardID); err != nil {
|
if board, err = getBoardFromID(op.BoardID); err != nil {
|
||||||
html += handleError(1, err.Error())
|
html += handleError(1, err.Error())
|
||||||
|
@ -457,20 +458,20 @@ func buildThreadPages(op *Post) (html string) {
|
||||||
repliesInterface = append(repliesInterface, reply)
|
repliesInterface = append(repliesInterface, reply)
|
||||||
}
|
}
|
||||||
|
|
||||||
thread_pages := paginate(config.PostsPerThreadPage, repliesInterface)
|
threadPages := paginate(config.PostsPerThreadPage, repliesInterface)
|
||||||
deleteMatchingFiles(path.Join(config.DocumentRoot, board.Dir, "res"), "^"+strconv.Itoa(op.ID)+"p")
|
deleteMatchingFiles(path.Join(config.DocumentRoot, board.Dir, "res"), "^"+strconv.Itoa(op.ID)+"p")
|
||||||
|
|
||||||
op.NumPages = len(thread_pages)
|
op.NumPages = len(threadPages)
|
||||||
|
|
||||||
current_page_filepath := path.Join(config.DocumentRoot, board.Dir, "res", strconv.Itoa(op.ID)+".html")
|
currentPageFilepath := 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)
|
currentPageFile, err = os.OpenFile(currentPageFilepath, os.O_CREATE|os.O_RDWR|os.O_TRUNC, 0777)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
html += handleError(1, "Failed opening "+current_page_filepath+": "+err.Error())
|
html += handleError(1, "Failed opening "+currentPageFilepath+": "+err.Error())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// render main page
|
// render main page
|
||||||
if err = threadpageTmpl.Execute(current_page_file, map[string]interface{}{
|
if err = threadpageTmpl.Execute(currentPageFile, map[string]interface{}{
|
||||||
"config": config,
|
"config": config,
|
||||||
"boards": allBoards,
|
"boards": allBoards,
|
||||||
"board": board,
|
"board": board,
|
||||||
|
@ -496,9 +497,7 @@ func buildThreadPages(op *Post) (html string) {
|
||||||
threadMap["posts"] = []Post{*op}
|
threadMap["posts"] = []Post{*op}
|
||||||
|
|
||||||
// Iterate through each reply, which are of type Post
|
// Iterate through each reply, which are of type Post
|
||||||
for _, reply := range replies {
|
threadMap["posts"] = append(threadMap["posts"], replies...)
|
||||||
threadMap["posts"] = append(threadMap["posts"], reply)
|
|
||||||
}
|
|
||||||
threadJSON, err := json.Marshal(threadMap)
|
threadJSON, err := json.Marshal(threadMap)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
html += handleError(1, "Failed to marshal to JSON: %s", err.Error()) + "<br />"
|
html += handleError(1, "Failed to marshal to JSON: %s", err.Error()) + "<br />"
|
||||||
|
@ -512,21 +511,21 @@ func buildThreadPages(op *Post) (html string) {
|
||||||
|
|
||||||
html += fmt.Sprintf("Built /%s/%d successfully", board.Dir, op.ID)
|
html += fmt.Sprintf("Built /%s/%d successfully", board.Dir, op.ID)
|
||||||
|
|
||||||
for page_num, page_posts := range thread_pages {
|
for pageNum, pagePosts := range threadPages {
|
||||||
op.CurrentPage = page_num + 1
|
op.CurrentPage = pageNum + 1
|
||||||
current_page_filepath := path.Join(config.DocumentRoot, board.Dir, "res", strconv.Itoa(op.ID)+"p"+strconv.Itoa(op.CurrentPage)+".html")
|
currentPageFilepath := 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)
|
currentPageFile, err = os.OpenFile(currentPageFilepath, os.O_CREATE|os.O_RDWR|os.O_TRUNC, 0777)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
html += handleError(1, "<br />Failed opening "+current_page_filepath+": "+err.Error()) + "<br />\n"
|
html += handleError(1, "<br />Failed opening "+currentPageFilepath+": "+err.Error()) + "<br />\n"
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if err = threadpageTmpl.Execute(current_page_file, map[string]interface{}{
|
if err = threadpageTmpl.Execute(currentPageFile, map[string]interface{}{
|
||||||
"config": config,
|
"config": config,
|
||||||
"boards": allBoards,
|
"boards": allBoards,
|
||||||
"board": board,
|
"board": board,
|
||||||
"sections": allSections,
|
"sections": allSections,
|
||||||
"posts": page_posts,
|
"posts": pagePosts,
|
||||||
"op": op,
|
"op": op,
|
||||||
}); err != nil {
|
}); err != nil {
|
||||||
html += handleError(1, "<br />Failed building /%s/%d: %s", board.Dir, op.ID, err.Error())
|
html += handleError(1, "<br />Failed building /%s/%d: %s", board.Dir, op.ID, err.Error())
|
||||||
|
|
|
@ -37,7 +37,7 @@ func main() {
|
||||||
go tempCleaner()
|
go tempCleaner()
|
||||||
|
|
||||||
sc := make(chan os.Signal, 1)
|
sc := make(chan os.Signal, 1)
|
||||||
signal.Notify(sc, syscall.SIGINT, syscall.SIGTERM, os.Interrupt, os.Kill)
|
signal.Notify(sc, syscall.SIGINT, syscall.SIGTERM, os.Interrupt)
|
||||||
go func() {
|
go func() {
|
||||||
initServer()
|
initServer()
|
||||||
}()
|
}()
|
||||||
|
|
193
src/manage.go
193
src/manage.go
|
@ -54,13 +54,13 @@ func callManageFunction(writer http.ResponseWriter, request *http.Request) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, ok := manage_functions[action]; ok {
|
if _, ok := manageFunctions[action]; ok {
|
||||||
if staffRank >= manage_functions[action].Permissions {
|
if staffRank >= manageFunctions[action].Permissions {
|
||||||
managePageBuffer.Write([]byte(manage_functions[action].Callback(writer, request)))
|
managePageBuffer.Write([]byte(manageFunctions[action].Callback(writer, request)))
|
||||||
} else if staffRank == 0 && manage_functions[action].Permissions == 0 {
|
} else if staffRank == 0 && manageFunctions[action].Permissions == 0 {
|
||||||
managePageBuffer.Write([]byte(manage_functions[action].Callback(writer, request)))
|
managePageBuffer.Write([]byte(manageFunctions[action].Callback(writer, request)))
|
||||||
} else if staffRank == 0 {
|
} else if staffRank == 0 {
|
||||||
managePageBuffer.Write([]byte(manage_functions["login"].Callback(writer, request)))
|
managePageBuffer.Write([]byte(manageFunctions["login"].Callback(writer, request)))
|
||||||
} else {
|
} else {
|
||||||
managePageBuffer.Write([]byte(action + " is undefined."))
|
managePageBuffer.Write([]byte(action + " is undefined."))
|
||||||
}
|
}
|
||||||
|
@ -71,11 +71,7 @@ func callManageFunction(writer http.ResponseWriter, request *http.Request) {
|
||||||
managePageBuffer.Write([]byte("\n</body>\n</html>"))
|
managePageBuffer.Write([]byte("\n</body>\n</html>"))
|
||||||
}
|
}
|
||||||
|
|
||||||
/* extension := getFileExtension(request.URL.Path)
|
writer.Write(managePageBuffer.Bytes())
|
||||||
if extension == "" {
|
|
||||||
writer.Header().Add("Cache-Control", "max-age=5, must-revalidate")
|
|
||||||
} */
|
|
||||||
fmt.Fprintf(writer, managePageBuffer.String())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func getCurrentStaff(request *http.Request) (string, error) {
|
func getCurrentStaff(request *http.Request) (string, error) {
|
||||||
|
@ -148,40 +144,40 @@ func createSession(key string, username string, password string, request *http.R
|
||||||
if err != nil {
|
if err != nil {
|
||||||
handleError(1, customError(err))
|
handleError(1, customError(err))
|
||||||
return 1
|
return 1
|
||||||
} else {
|
|
||||||
success := bcrypt.CompareHashAndPassword([]byte(staff.PasswordChecksum), []byte(password))
|
|
||||||
if success == bcrypt.ErrMismatchedHashAndPassword {
|
|
||||||
// password mismatch
|
|
||||||
modLog.Print("Failed login (password mismatch) from " + request.RemoteAddr + " at " + getSQLDateTime())
|
|
||||||
return 1
|
|
||||||
} else {
|
|
||||||
// successful login, add cookie that expires in one month
|
|
||||||
http.SetCookie(writer, &http.Cookie{
|
|
||||||
Name: "sessiondata",
|
|
||||||
Value: key,
|
|
||||||
Path: "/",
|
|
||||||
Domain: domain,
|
|
||||||
MaxAge: 60 * 60 * 24 * 7,
|
|
||||||
})
|
|
||||||
|
|
||||||
if _, err = execSQL(
|
|
||||||
"INSERT INTO "+config.DBprefix+"sessions (name,sessiondata,expires) VALUES(?,?,?)",
|
|
||||||
key, username, getSpecificSQLDateTime(time.Now().Add(time.Duration(time.Hour*730)))); err != nil {
|
|
||||||
handleError(0, customError(err))
|
|
||||||
return 2
|
|
||||||
}
|
|
||||||
|
|
||||||
if _, err = execSQL(
|
|
||||||
"UPDATE "+config.DBprefix+"staff SET last_active = ? WHERE username = ?", getSQLDateTime(), username,
|
|
||||||
); err != nil {
|
|
||||||
handleError(1, customError(err))
|
|
||||||
}
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
success := bcrypt.CompareHashAndPassword([]byte(staff.PasswordChecksum), []byte(password))
|
||||||
|
if success == bcrypt.ErrMismatchedHashAndPassword {
|
||||||
|
// password mismatch
|
||||||
|
modLog.Print("Failed login (password mismatch) from " + request.RemoteAddr + " at " + getSQLDateTime())
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
// successful login, add cookie that expires in one month
|
||||||
|
http.SetCookie(writer, &http.Cookie{
|
||||||
|
Name: "sessiondata",
|
||||||
|
Value: key,
|
||||||
|
Path: "/",
|
||||||
|
Domain: domain,
|
||||||
|
MaxAge: 60 * 60 * 24 * 7,
|
||||||
|
})
|
||||||
|
|
||||||
|
if _, err = execSQL(
|
||||||
|
"INSERT INTO "+config.DBprefix+"sessions (name,sessiondata,expires) VALUES(?,?,?)",
|
||||||
|
key, username, getSpecificSQLDateTime(time.Now().Add(time.Duration(time.Hour*730)))); err != nil {
|
||||||
|
handleError(0, customError(err))
|
||||||
|
return 2
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err = execSQL(
|
||||||
|
"UPDATE "+config.DBprefix+"staff SET last_active = ? WHERE username = ?", getSQLDateTime(), username,
|
||||||
|
); err != nil {
|
||||||
|
handleError(1, customError(err))
|
||||||
|
}
|
||||||
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
var manage_functions = map[string]ManageFunction{
|
var manageFunctions = map[string]ManageFunction{
|
||||||
"cleanup": {
|
"cleanup": {
|
||||||
Permissions: 3,
|
Permissions: 3,
|
||||||
Callback: func(writer http.ResponseWriter, request *http.Request) (html string) {
|
Callback: func(writer http.ResponseWriter, request *http.Request) (html string) {
|
||||||
|
@ -433,68 +429,68 @@ var manage_functions = map[string]ManageFunction{
|
||||||
html += manageConfigBuffer.String()
|
html += manageConfigBuffer.String()
|
||||||
return
|
return
|
||||||
}},
|
}},
|
||||||
"purgeeverything": {
|
/*"purgeeverything": {
|
||||||
Permissions: 3,
|
Permissions: 3,
|
||||||
Callback: func(writer http.ResponseWriter, request *http.Request) (html string) {
|
Callback: func(writer http.ResponseWriter, request *http.Request) (html string) {
|
||||||
html = "<img src=\"/css/purge.jpg\" />"
|
html = "<img src=\"/css/purge.jpg\" />"
|
||||||
rows, err := querySQL("SELECT dir FROM " + config.DBprefix + "boards")
|
rows, err := querySQL("SELECT dir FROM " + config.DBprefix + "boards")
|
||||||
defer closeHandle(rows)
|
defer closeHandle(rows)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
html += err.Error()
|
||||||
|
handleError(1, customError(err))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var board string
|
||||||
|
for rows.Next() {
|
||||||
|
if err = rows.Scan(&board); err != nil {
|
||||||
html += err.Error()
|
html += err.Error()
|
||||||
handleError(1, customError(err))
|
handleError(1, customError(err))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
var board string
|
if _, err = deleteMatchingFiles(path.Join(config.DocumentRoot, board), ".html"); err != nil {
|
||||||
for rows.Next() {
|
html += err.Error()
|
||||||
if err = rows.Scan(&board); err != nil {
|
handleError(1, customError(err))
|
||||||
html += err.Error()
|
|
||||||
handleError(1, customError(err))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if _, err = deleteMatchingFiles(path.Join(config.DocumentRoot, board), ".html"); err != nil {
|
|
||||||
html += err.Error()
|
|
||||||
handleError(1, customError(err))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if _, err = deleteMatchingFiles(path.Join(config.DocumentRoot, board, "res"), ".*"); err != nil {
|
|
||||||
html += err.Error()
|
|
||||||
handleError(1, customError(err))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if _, err = deleteMatchingFiles(path.Join(config.DocumentRoot, board, "src"), ".*"); err != nil {
|
|
||||||
html += err.Error()
|
|
||||||
handleError(1, customError(err))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if _, err = deleteMatchingFiles(path.Join(config.DocumentRoot, board, "thumb"), ".*"); err != nil {
|
|
||||||
html += err.Error()
|
|
||||||
handleError(1, customError(err))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
truncateSQL := "TRUNCATE " + config.DBprefix + "posts"
|
|
||||||
var values []interface{} // only used for SQLite since it doesn't have a proper TRUNCATE
|
|
||||||
if config.DBtype == "sqlite3" {
|
|
||||||
truncateSQL = "DELETE FROM " + config.DBprefix + "posts; DELETE FROM sqlite_sequence WHERE name = ?;"
|
|
||||||
values = append(values, config.DBprefix+"posts")
|
|
||||||
} else if config.DBtype == "postgres" {
|
|
||||||
truncateSQL += " RESTART IDENTITY"
|
|
||||||
}
|
|
||||||
if _, err = execSQL(truncateSQL, values...); err != nil {
|
|
||||||
html += handleError(0, err.Error()) + "<br />\n"
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
if _, err = deleteMatchingFiles(path.Join(config.DocumentRoot, board, "res"), ".*"); err != nil {
|
||||||
if _, err = execSQL("ALTER TABLE `" + config.DBprefix + "posts` AUTO_INCREMENT = 1"); err != nil {
|
html += err.Error()
|
||||||
html += handleError(0, err.Error()) + "<br />\n"
|
handleError(1, customError(err))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
html += "<br />Everything purged, rebuilding all<br />" +
|
if _, err = deleteMatchingFiles(path.Join(config.DocumentRoot, board, "src"), ".*"); err != nil {
|
||||||
buildBoards() + "<hr />\n" +
|
html += err.Error()
|
||||||
buildFrontPage()
|
handleError(1, customError(err))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if _, err = deleteMatchingFiles(path.Join(config.DocumentRoot, board, "thumb"), ".*"); err != nil {
|
||||||
|
html += err.Error()
|
||||||
|
handleError(1, customError(err))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
truncateSQL := "TRUNCATE " + config.DBprefix + "posts"
|
||||||
|
var values []interface{} // only used for SQLite since it doesn't have a proper TRUNCATE
|
||||||
|
if config.DBtype == "sqlite3" {
|
||||||
|
truncateSQL = "DELETE FROM " + config.DBprefix + "posts; DELETE FROM sqlite_sequence WHERE name = ?;"
|
||||||
|
values = append(values, config.DBprefix+"posts")
|
||||||
|
} else if config.DBtype == "postgres" {
|
||||||
|
truncateSQL += " RESTART IDENTITY"
|
||||||
|
}
|
||||||
|
if _, err = execSQL(truncateSQL, values...); err != nil {
|
||||||
|
html += handleError(0, err.Error()) + "<br />\n"
|
||||||
return
|
return
|
||||||
}},
|
}
|
||||||
|
|
||||||
|
if _, err = execSQL("ALTER TABLE `" + config.DBprefix + "posts` AUTO_INCREMENT = 1"); err != nil {
|
||||||
|
html += handleError(0, err.Error()) + "<br />\n"
|
||||||
|
return
|
||||||
|
}
|
||||||
|
html += "<br />Everything purged, rebuilding all<br />" +
|
||||||
|
buildBoards() + "<hr />\n" +
|
||||||
|
buildFrontPage()
|
||||||
|
return
|
||||||
|
}},*/
|
||||||
"executesql": {
|
"executesql": {
|
||||||
Permissions: 3,
|
Permissions: 3,
|
||||||
Callback: func(writer http.ResponseWriter, request *http.Request) (html string) {
|
Callback: func(writer http.ResponseWriter, request *http.Request) (html string) {
|
||||||
|
@ -837,7 +833,6 @@ var manage_functions = map[string]ManageFunction{
|
||||||
println(2, "Boards rebuilt successfully")
|
println(2, "Boards rebuilt successfully")
|
||||||
done = true
|
done = true
|
||||||
}
|
}
|
||||||
break
|
|
||||||
case do == "del":
|
case do == "del":
|
||||||
// resetBoardSectionArrays()
|
// resetBoardSectionArrays()
|
||||||
case do == "edit":
|
case do == "edit":
|
||||||
|
@ -877,7 +872,11 @@ var manage_functions = map[string]ManageFunction{
|
||||||
manageBoardsBuffer := bytes.NewBufferString("")
|
manageBoardsBuffer := bytes.NewBufferString("")
|
||||||
allSections, _ = getSectionArr("")
|
allSections, _ = getSectionArr("")
|
||||||
if len(allSections) == 0 {
|
if len(allSections) == 0 {
|
||||||
execSQL("INSERT INTO " + config.DBprefix + "sections (hidden,name,abbreviation) VALUES(0,'Main','main')")
|
if _, err = execSQL(
|
||||||
|
"INSERT INTO " + config.DBprefix + "sections (hidden,name,abbreviation) VALUES(0,'Main','main')",
|
||||||
|
); err != nil {
|
||||||
|
html += handleError(1, err.Error())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
allSections, _ = getSectionArr("")
|
allSections, _ = getSectionArr("")
|
||||||
|
|
||||||
|
|
|
@ -36,7 +36,6 @@ var (
|
||||||
allBoards []Board
|
allBoards []Board
|
||||||
tempPosts []Post
|
tempPosts []Post
|
||||||
tempCleanerTicker *time.Ticker
|
tempCleanerTicker *time.Ticker
|
||||||
tempCleanerQuit = make(chan struct{})
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// bumps the given thread on the given board and returns true if there were no errors
|
// bumps the given thread on the given board and returns true if there were no errors
|
||||||
|
@ -130,7 +129,7 @@ func sinceLastPost(post *Post) int {
|
||||||
return int(time.Since(lastPostTime).Seconds())
|
return int(time.Since(lastPostTime).Seconds())
|
||||||
}
|
}
|
||||||
|
|
||||||
func createImageThumbnail(image_obj image.Image, size string) image.Image {
|
func createImageThumbnail(imageObj image.Image, size string) image.Image {
|
||||||
var thumbWidth int
|
var thumbWidth int
|
||||||
var thumbHeight int
|
var thumbHeight int
|
||||||
|
|
||||||
|
@ -145,14 +144,14 @@ func createImageThumbnail(image_obj image.Image, size string) image.Image {
|
||||||
thumbWidth = config.ThumbWidth_catalog
|
thumbWidth = config.ThumbWidth_catalog
|
||||||
thumbHeight = config.ThumbHeight_catalog
|
thumbHeight = config.ThumbHeight_catalog
|
||||||
}
|
}
|
||||||
old_rect := image_obj.Bounds()
|
oldRect := imageObj.Bounds()
|
||||||
if thumbWidth >= old_rect.Max.X && thumbHeight >= old_rect.Max.Y {
|
if thumbWidth >= oldRect.Max.X && thumbHeight >= oldRect.Max.Y {
|
||||||
return image_obj
|
return imageObj
|
||||||
}
|
}
|
||||||
|
|
||||||
thumbW, thumbH := getThumbnailSize(old_rect.Max.X, old_rect.Max.Y, size)
|
thumbW, thumbH := getThumbnailSize(oldRect.Max.X, oldRect.Max.Y, size)
|
||||||
image_obj = imaging.Resize(image_obj, thumbW, thumbH, imaging.CatmullRom) // resize to 600x400 px using CatmullRom cubic filter
|
imageObj = imaging.Resize(imageObj, thumbW, thumbH, imaging.CatmullRom) // resize to 600x400 px using CatmullRom cubic filter
|
||||||
return image_obj
|
return imageObj
|
||||||
}
|
}
|
||||||
|
|
||||||
func createVideoThumbnail(video, thumb string, size int) error {
|
func createVideoThumbnail(video, thumb string, size int) error {
|
||||||
|
@ -588,10 +587,10 @@ func makePost(writer http.ResponseWriter, request *http.Request) {
|
||||||
if err = banpageTmpl.Execute(&banpageBuffer, map[string]interface{}{
|
if err = banpageTmpl.Execute(&banpageBuffer, map[string]interface{}{
|
||||||
"config": config, "ban": banStatus, "banBoards": boards[post.BoardID-1].Dir,
|
"config": config, "ban": banStatus, "banBoards": boards[post.BoardID-1].Dir,
|
||||||
}); err != nil {
|
}); err != nil {
|
||||||
fmt.Fprintf(writer, handleError(1, err.Error()))
|
writer.Write([]byte(handleError(1, err.Error())))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
fmt.Fprintf(writer, banpageBuffer.String())
|
writer.Write(banpageBuffer.Bytes())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -758,5 +757,5 @@ func banHandler(writer http.ResponseWriter, request *http.Request) {
|
||||||
fmt.Fprintf(writer, handleError(1, err.Error())+"\n</body>\n</html>")
|
fmt.Fprintf(writer, handleError(1, err.Error())+"\n</body>\n</html>")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
fmt.Fprintf(writer, banpageBuffer.String())
|
writer.Write(banpageBuffer.Bytes())
|
||||||
}
|
}
|
||||||
|
|
|
@ -35,59 +35,60 @@ func (s GochanServer) serveFile(writer http.ResponseWriter, request *http.Reques
|
||||||
// the requested path isn't a file or directory, 404
|
// the requested path isn't a file or directory, 404
|
||||||
serveNotFound(writer, request)
|
serveNotFound(writer, request)
|
||||||
return
|
return
|
||||||
} else {
|
|
||||||
//the file exists, or there is a folder here
|
|
||||||
if results.IsDir() {
|
|
||||||
//check to see if one of the specified index pages exists
|
|
||||||
for _, value := range config.FirstPage {
|
|
||||||
newPath := path.Join(filePath, value)
|
|
||||||
_, err := os.Stat(newPath)
|
|
||||||
if err == nil {
|
|
||||||
filePath = newPath
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
//the file exists, and is not a folder
|
|
||||||
extension := strings.ToLower(getFileExtension(request.URL.Path))
|
|
||||||
switch extension {
|
|
||||||
case "png":
|
|
||||||
writer.Header().Add("Content-Type", "image/png")
|
|
||||||
writer.Header().Add("Cache-Control", "max-age=86400")
|
|
||||||
case "gif":
|
|
||||||
writer.Header().Add("Content-Type", "image/gif")
|
|
||||||
writer.Header().Add("Cache-Control", "max-age=86400")
|
|
||||||
case "jpg":
|
|
||||||
fallthrough
|
|
||||||
case "jpeg":
|
|
||||||
writer.Header().Add("Content-Type", "image/jpeg")
|
|
||||||
writer.Header().Add("Cache-Control", "max-age=86400")
|
|
||||||
case "css":
|
|
||||||
writer.Header().Add("Content-Type", "text/css")
|
|
||||||
writer.Header().Add("Cache-Control", "max-age=43200")
|
|
||||||
case "js":
|
|
||||||
writer.Header().Add("Content-Type", "text/javascript")
|
|
||||||
writer.Header().Add("Cache-Control", "max-age=43200")
|
|
||||||
case "json":
|
|
||||||
writer.Header().Add("Content-Type", "application/json")
|
|
||||||
writer.Header().Add("Cache-Control", "max-age=5, must-revalidate")
|
|
||||||
case "webm":
|
|
||||||
writer.Header().Add("Content-Type", "video/webm")
|
|
||||||
writer.Header().Add("Cache-Control", "max-age=86400")
|
|
||||||
case "htm":
|
|
||||||
fallthrough
|
|
||||||
case "html":
|
|
||||||
writer.Header().Add("Content-Type", "text/html")
|
|
||||||
writer.Header().Add("Cache-Control", "max-age=5, must-revalidate")
|
|
||||||
}
|
|
||||||
accessLog.Print("Success: 200 from " + getRealIP(request) + " @ " + request.URL.Path)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//the file exists, or there is a folder here
|
||||||
|
if results.IsDir() {
|
||||||
|
//check to see if one of the specified index pages exists
|
||||||
|
for _, value := range config.FirstPage {
|
||||||
|
newPath := path.Join(filePath, value)
|
||||||
|
_, err := os.Stat(newPath)
|
||||||
|
if err == nil {
|
||||||
|
filePath = newPath
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
//the file exists, and is not a folder
|
||||||
|
extension := strings.ToLower(getFileExtension(request.URL.Path))
|
||||||
|
switch extension {
|
||||||
|
case "png":
|
||||||
|
writer.Header().Add("Content-Type", "image/png")
|
||||||
|
writer.Header().Add("Cache-Control", "max-age=86400")
|
||||||
|
case "gif":
|
||||||
|
writer.Header().Add("Content-Type", "image/gif")
|
||||||
|
writer.Header().Add("Cache-Control", "max-age=86400")
|
||||||
|
case "jpg":
|
||||||
|
fallthrough
|
||||||
|
case "jpeg":
|
||||||
|
writer.Header().Add("Content-Type", "image/jpeg")
|
||||||
|
writer.Header().Add("Cache-Control", "max-age=86400")
|
||||||
|
case "css":
|
||||||
|
writer.Header().Add("Content-Type", "text/css")
|
||||||
|
writer.Header().Add("Cache-Control", "max-age=43200")
|
||||||
|
case "js":
|
||||||
|
writer.Header().Add("Content-Type", "text/javascript")
|
||||||
|
writer.Header().Add("Cache-Control", "max-age=43200")
|
||||||
|
case "json":
|
||||||
|
writer.Header().Add("Content-Type", "application/json")
|
||||||
|
writer.Header().Add("Cache-Control", "max-age=5, must-revalidate")
|
||||||
|
case "webm":
|
||||||
|
writer.Header().Add("Content-Type", "video/webm")
|
||||||
|
writer.Header().Add("Cache-Control", "max-age=86400")
|
||||||
|
case "htm":
|
||||||
|
fallthrough
|
||||||
|
case "html":
|
||||||
|
writer.Header().Add("Content-Type", "text/html")
|
||||||
|
writer.Header().Add("Cache-Control", "max-age=5, must-revalidate")
|
||||||
|
}
|
||||||
|
accessLog.Print("Success: 200 from " + getRealIP(request) + " @ " + request.URL.Path)
|
||||||
|
}
|
||||||
|
|
||||||
// serve the index page
|
// serve the index page
|
||||||
writer.Header().Add("Cache-Control", "max-age=5, must-revalidate")
|
writer.Header().Add("Cache-Control", "max-age=5, must-revalidate")
|
||||||
fileBytes, _ = ioutil.ReadFile(filePath)
|
fileBytes, _ = ioutil.ReadFile(filePath)
|
||||||
writer.Header().Add("Cache-Control", "max-age=86400")
|
writer.Header().Add("Cache-Control", "max-age=86400")
|
||||||
_, _ = writer.Write(fileBytes)
|
writer.Write(fileBytes)
|
||||||
}
|
}
|
||||||
|
|
||||||
func serveNotFound(writer http.ResponseWriter, request *http.Request) {
|
func serveNotFound(writer http.ResponseWriter, request *http.Request) {
|
||||||
|
|
17
src/sql.go
17
src/sql.go
|
@ -52,7 +52,6 @@ func connectToSQLServer() {
|
||||||
os.Exit(2)
|
os.Exit(2)
|
||||||
}
|
}
|
||||||
|
|
||||||
nullTime, _ = time.Parse("2006-01-02 15:04:05", nilTimestamp)
|
|
||||||
if db, err = sql.Open(config.DBtype, connStr); err != nil {
|
if db, err = sql.Open(config.DBtype, connStr); err != nil {
|
||||||
handleError(0, "Failed to connect to the database: %s\n", customError(err))
|
handleError(0, "Failed to connect to the database: %s\n", customError(err))
|
||||||
os.Exit(2)
|
os.Exit(2)
|
||||||
|
@ -69,8 +68,11 @@ func connectToSQLServer() {
|
||||||
}
|
}
|
||||||
|
|
||||||
var sqlVersionStr string
|
var sqlVersionStr string
|
||||||
err = queryRowSQL("SELECT value FROM "+config.DBprefix+"info WHERE name = 'version'",
|
if err = queryRowSQL("SELECT value FROM "+config.DBprefix+"info WHERE name = 'version'",
|
||||||
[]interface{}{}, []interface{}{&sqlVersionStr})
|
[]interface{}{}, []interface{}{&sqlVersionStr}); err != nil {
|
||||||
|
handleError(0, "failed: %s\n", customError(err))
|
||||||
|
os.Exit(2)
|
||||||
|
}
|
||||||
var numBoards, numStaff int
|
var numBoards, numStaff int
|
||||||
rows, err := querySQL("SELECT COUNT(*) FROM " + config.DBprefix + "boards UNION ALL SELECT COUNT(*) FROM " + config.DBprefix + "staff")
|
rows, err := querySQL("SELECT COUNT(*) FROM " + config.DBprefix + "boards UNION ALL SELECT COUNT(*) FROM " + config.DBprefix + "staff")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -106,7 +108,11 @@ func connectToSQLServer() {
|
||||||
buildFrontPage()
|
buildFrontPage()
|
||||||
buildBoardListJSON()
|
buildBoardListJSON()
|
||||||
buildBoards()
|
buildBoards()
|
||||||
_, err = execSQL("INSERT INTO "+config.DBprefix+"info (name,value) VALUES('version',?)", versionStr)
|
if _, err = execSQL(
|
||||||
|
"INSERT INTO "+config.DBprefix+"info (name,value) VALUES('version',?)",
|
||||||
|
versionStr); err != nil {
|
||||||
|
handleError(0, "failed: %s\n", err.Error())
|
||||||
|
}
|
||||||
return
|
return
|
||||||
} else if err != nil {
|
} else if err != nil {
|
||||||
handleError(0, "failed: %s\n", customError(err))
|
handleError(0, "failed: %s\n", customError(err))
|
||||||
|
@ -146,7 +152,6 @@ func initDB(initFile string) error {
|
||||||
if statement != "" && statement != " " {
|
if statement != "" && statement != " " {
|
||||||
if _, err := db.Exec(statement + ";"); err != nil {
|
if _, err := db.Exec(statement + ";"); err != nil {
|
||||||
panic("Error with SQL statement:" + statement)
|
panic("Error with SQL statement:" + statement)
|
||||||
return err
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -184,7 +189,6 @@ func prepareSQL(query string) (*sql.Stmt, error) {
|
||||||
fallthrough
|
fallthrough
|
||||||
case "sqlite3":
|
case "sqlite3":
|
||||||
preparedStr = query
|
preparedStr = query
|
||||||
break
|
|
||||||
case "postgres":
|
case "postgres":
|
||||||
arr := strings.Split(query, "?")
|
arr := strings.Split(query, "?")
|
||||||
for i := range arr {
|
for i := range arr {
|
||||||
|
@ -194,7 +198,6 @@ func prepareSQL(query string) (*sql.Stmt, error) {
|
||||||
arr[i] += fmt.Sprintf("$%d", i+1)
|
arr[i] += fmt.Sprintf("$%d", i+1)
|
||||||
}
|
}
|
||||||
preparedStr = strings.Join(arr, "")
|
preparedStr = strings.Join(arr, "")
|
||||||
break
|
|
||||||
}
|
}
|
||||||
stmt, err := db.Prepare(preparedStr)
|
stmt, err := db.Prepare(preparedStr)
|
||||||
return stmt, sqlVersionErr(err)
|
return stmt, sqlVersionErr(err)
|
||||||
|
|
|
@ -4,6 +4,7 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"html"
|
"html"
|
||||||
"os"
|
"os"
|
||||||
|
"path"
|
||||||
"reflect"
|
"reflect"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
@ -61,10 +62,10 @@ var funcMap = template.FuncMap{
|
||||||
"escapeString": func(a string) string {
|
"escapeString": func(a string) string {
|
||||||
return html.EscapeString(a)
|
return html.EscapeString(a)
|
||||||
},
|
},
|
||||||
"formatFilesize": func(size_int int) string {
|
"formatFilesize": func(sizeInt int) string {
|
||||||
size := float32(size_int)
|
size := float32(sizeInt)
|
||||||
if size < 1000 {
|
if size < 1000 {
|
||||||
return fmt.Sprintf("%d B", size_int)
|
return fmt.Sprintf("%d B", sizeInt)
|
||||||
} else if size <= 100000 {
|
} else if size <= 100000 {
|
||||||
return fmt.Sprintf("%0.1f KB", size/1024)
|
return fmt.Sprintf("%0.1f KB", size/1024)
|
||||||
} else if size <= 100000000 {
|
} else if size <= 100000000 {
|
||||||
|
@ -88,12 +89,12 @@ var funcMap = template.FuncMap{
|
||||||
}
|
}
|
||||||
return appended
|
return appended
|
||||||
},
|
},
|
||||||
"truncateMessage": func(msg string, limit int, max_lines int) string {
|
"truncateMessage": func(msg string, limit int, maxLines int) string {
|
||||||
var truncated bool
|
var truncated bool
|
||||||
split := strings.SplitN(msg, "<br />", -1)
|
split := strings.SplitN(msg, "<br />", -1)
|
||||||
|
|
||||||
if len(split) > max_lines {
|
if len(split) > maxLines {
|
||||||
split = split[:max_lines]
|
split = split[:maxLines]
|
||||||
msg = strings.Join(split, "<br />")
|
msg = strings.Join(split, "<br />")
|
||||||
truncated = true
|
truncated = true
|
||||||
}
|
}
|
||||||
|
@ -143,8 +144,8 @@ var funcMap = template.FuncMap{
|
||||||
"getCatalogThumbnail": func(img string) string {
|
"getCatalogThumbnail": func(img string) string {
|
||||||
return getThumbnailPath("catalog", img)
|
return getThumbnailPath("catalog", img)
|
||||||
},
|
},
|
||||||
"getThreadID": func(post_i interface{}) (thread int) {
|
"getThreadID": func(postInterface interface{}) (thread int) {
|
||||||
post, ok := post_i.(Post)
|
post, ok := postInterface.(Post)
|
||||||
if !ok {
|
if !ok {
|
||||||
thread = 0
|
thread = 0
|
||||||
} else if post.ParentID == 0 {
|
} else if post.ParentID == 0 {
|
||||||
|
@ -154,20 +155,20 @@ var funcMap = template.FuncMap{
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
},
|
},
|
||||||
"getPostURL": func(post_i interface{}, typeOf string, withDomain bool) (postURL string) {
|
"getPostURL": func(postInterface interface{}, typeOf string, withDomain bool) (postURL string) {
|
||||||
if withDomain {
|
if withDomain {
|
||||||
postURL = config.SiteDomain
|
postURL = config.SiteDomain
|
||||||
}
|
}
|
||||||
postURL += config.SiteWebfolder
|
postURL += config.SiteWebfolder
|
||||||
|
|
||||||
if typeOf == "recent" {
|
if typeOf == "recent" {
|
||||||
post, ok := post_i.(*RecentPost)
|
post, ok := postInterface.(*RecentPost)
|
||||||
if !ok {
|
if !ok {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
postURL = post.GetURL(withDomain)
|
postURL = post.GetURL(withDomain)
|
||||||
} else {
|
} else {
|
||||||
post, ok := post_i.(*Post)
|
post, ok := postInterface.(*Post)
|
||||||
if !ok {
|
if !ok {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -297,7 +298,6 @@ var (
|
||||||
frontPageTmpl *template.Template
|
frontPageTmpl *template.Template
|
||||||
boardpageTmpl *template.Template
|
boardpageTmpl *template.Template
|
||||||
threadpageTmpl *template.Template
|
threadpageTmpl *template.Template
|
||||||
postFormTmpl *template.Template
|
|
||||||
postEditTmpl *template.Template
|
postEditTmpl *template.Template
|
||||||
manageBansTmpl *template.Template
|
manageBansTmpl *template.Template
|
||||||
manageBoardsTmpl *template.Template
|
manageBoardsTmpl *template.Template
|
||||||
|
@ -310,10 +310,12 @@ func loadTemplate(files ...string) (*template.Template, error) {
|
||||||
var templates []string
|
var templates []string
|
||||||
for i, file := range files {
|
for i, file := range files {
|
||||||
templates = append(templates, file)
|
templates = append(templates, file)
|
||||||
if _, err := os.Stat(config.TemplateDir + "/override/" + file); !os.IsNotExist(err) {
|
tmplPath := path.Join(config.TemplateDir, "override", file)
|
||||||
files[i] = config.TemplateDir + "/override/" + files[i]
|
|
||||||
|
if _, err := os.Stat(tmplPath); !os.IsNotExist(err) {
|
||||||
|
files[i] = tmplPath
|
||||||
} else {
|
} else {
|
||||||
files[i] = config.TemplateDir + "/" + files[i]
|
files[i] = path.Join(config.TemplateDir, file)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -85,9 +85,7 @@ type BanAppeal struct {
|
||||||
|
|
||||||
func (a *BanAppeal) GetBan() (BanInfo, error) {
|
func (a *BanAppeal) GetBan() (BanInfo, error) {
|
||||||
var ban BanInfo
|
var ban BanInfo
|
||||||
var err error
|
err := queryRowSQL("SELECT * FROM "+config.DBprefix+"banlist WHERE id = ? LIMIT 1",
|
||||||
|
|
||||||
err = queryRowSQL("SELECT * FROM "+config.DBprefix+"banlist WHERE id = ? LIMIT 1",
|
|
||||||
[]interface{}{a.ID}, []interface{}{
|
[]interface{}{a.ID}, []interface{}{
|
||||||
&ban.ID, &ban.AllowRead, &ban.IP, &ban.Name, &ban.NameIsRegex, &ban.SilentBan,
|
&ban.ID, &ban.AllowRead, &ban.IP, &ban.Name, &ban.NameIsRegex, &ban.SilentBan,
|
||||||
&ban.Boards, &ban.Staff, &ban.Timestamp, &ban.Expires, &ban.Permaban, &ban.Reason,
|
&ban.Boards, &ban.Staff, &ban.Timestamp, &ban.Expires, &ban.Permaban, &ban.Reason,
|
||||||
|
|
44
src/util.go
44
src/util.go
|
@ -25,7 +25,6 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
nullTime time.Time
|
|
||||||
errEmptyDurationString = errors.New("Empty Duration string")
|
errEmptyDurationString = errors.New("Empty Duration string")
|
||||||
errInvalidDurationString = errors.New("Invalid Duration string")
|
errInvalidDurationString = errors.New("Invalid Duration string")
|
||||||
durationRegexp = regexp.MustCompile(`^((\d+)\s?ye?a?r?s?)?\s?((\d+)\s?mon?t?h?s?)?\s?((\d+)\s?we?e?k?s?)?\s?((\d+)\s?da?y?s?)?\s?((\d+)\s?ho?u?r?s?)?\s?((\d+)\s?mi?n?u?t?e?s?)?\s?((\d+)\s?s?e?c?o?n?d?s?)?$`)
|
durationRegexp = regexp.MustCompile(`^((\d+)\s?ye?a?r?s?)?\s?((\d+)\s?mon?t?h?s?)?\s?((\d+)\s?we?e?k?s?)?\s?((\d+)\s?da?y?s?)?\s?((\d+)\s?ho?u?r?s?)?\s?((\d+)\s?mi?n?u?t?e?s?)?\s?((\d+)\s?s?e?c?o?n?d?s?)?$`)
|
||||||
|
@ -409,9 +408,7 @@ func resetBoardSectionArrays() {
|
||||||
allSections = nil
|
allSections = nil
|
||||||
|
|
||||||
allBoardsArr, _ := getBoardArr(nil, "")
|
allBoardsArr, _ := getBoardArr(nil, "")
|
||||||
for _, b := range allBoardsArr {
|
allBoards = append(allBoards, allBoardsArr...)
|
||||||
allBoards = append(allBoards, b)
|
|
||||||
}
|
|
||||||
|
|
||||||
allSectionsArr, _ := getSectionArr("")
|
allSectionsArr, _ := getSectionArr("")
|
||||||
allSections = append(allSections, allSectionsArr...)
|
allSections = append(allSections, allSectionsArr...)
|
||||||
|
@ -426,20 +423,6 @@ func searchStrings(item string, arr []string, permissive bool) int {
|
||||||
return -1
|
return -1
|
||||||
}
|
}
|
||||||
|
|
||||||
func bToI(b bool) int {
|
|
||||||
if b {
|
|
||||||
return 1
|
|
||||||
}
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
func bToA(b bool) string {
|
|
||||||
if b {
|
|
||||||
return "1"
|
|
||||||
}
|
|
||||||
return "0"
|
|
||||||
}
|
|
||||||
|
|
||||||
// Checks the validity of the Akismet API key given in the config file.
|
// Checks the validity of the Akismet API key given in the config file.
|
||||||
func checkAkismetAPIKey(key string) error {
|
func checkAkismetAPIKey(key string) error {
|
||||||
if key == "" {
|
if key == "" {
|
||||||
|
@ -536,11 +519,6 @@ func marshalJSON(tag string, data interface{}, indent bool) (string, error) {
|
||||||
return string(jsonBytes), err
|
return string(jsonBytes), err
|
||||||
}
|
}
|
||||||
|
|
||||||
func jsonError(err string) string {
|
|
||||||
errJSON, _ := marshalJSON("error", err, false)
|
|
||||||
return errJSON
|
|
||||||
}
|
|
||||||
|
|
||||||
func limitArraySize(arr []string, maxSize int) []string {
|
func limitArraySize(arr []string, maxSize int) []string {
|
||||||
if maxSize > len(arr)-1 || maxSize < 0 {
|
if maxSize > len(arr)-1 || maxSize < 0 {
|
||||||
return arr
|
return arr
|
||||||
|
@ -559,26 +537,6 @@ func numReplies(boardid, threadid int) int {
|
||||||
return num
|
return num
|
||||||
}
|
}
|
||||||
|
|
||||||
func ipMatch(newIP, existingIP string) bool {
|
|
||||||
if newIP == existingIP {
|
|
||||||
// both are single IPs and are the same
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
wildcardIndex := strings.Index(existingIP, "*")
|
|
||||||
if wildcardIndex < 0 {
|
|
||||||
// single (or invalid) and they don't match
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
ipRegexStr := existingIP[0:wildcardIndex]
|
|
||||||
ipRegexStr = strings.Replace(ipRegexStr, ".", "\\.", -1) + ".*"
|
|
||||||
ipRegex, err := regexp.Compile(ipRegexStr)
|
|
||||||
if err != nil {
|
|
||||||
// this shouldn't happen unless you enter an invalid IP in the db
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
return ipRegex.MatchString(newIP)
|
|
||||||
}
|
|
||||||
|
|
||||||
// based on TinyBoard's parse_time function
|
// based on TinyBoard's parse_time function
|
||||||
func parseDurationString(str string) (time.Duration, error) {
|
func parseDurationString(str string) (time.Duration, error) {
|
||||||
if str == "" {
|
if str == "" {
|
||||||
|
|
|
@ -1,82 +1,85 @@
|
||||||
{{template "page_header.html" .}}
|
{{template "page_header.html" .}}
|
||||||
<header>
|
<header>
|
||||||
<h1 id="board-title">/{{$.board.Dir}}/ - {{$.board.Title}}</h1>
|
<h1 id="board-title">/{{$.board.Dir}}/ - {{$.board.Title}}</h1>
|
||||||
<span id="board-subtitle">{{$.board.Subtitle}}</span>
|
<span id="board-subtitle">{{$.board.Subtitle}}</span>
|
||||||
</header>
|
</header><hr />
|
||||||
<hr />
|
<div id="right-sidelinks">
|
||||||
<div id="right-sidelinks">
|
<a href="{{.config.SiteWebfolder}}{{.board.Dir}}/catalog.html">Board catalog</a><br />
|
||||||
<a href="{{.config.SiteWebfolder}}{{.board.Dir}}/catalog.html">Board catalog</a><br />
|
</div>
|
||||||
</div>
|
{{- template "postbox.html" .}}
|
||||||
{{template "postbox.html" .}}
|
<hr />
|
||||||
<hr />
|
<div id="content">
|
||||||
<div id="content">
|
<form action="/util" method="POST" id="main-form">
|
||||||
<form action="/util" method="POST" id="main-form">
|
{{- range $t, $thread := .threads}}{{$op := $thread.OP}}
|
||||||
{{range $t, $thread := .threads}}
|
<div class="thread">
|
||||||
{{$op := $thread.OP}}
|
<div class="op-post" id="op{{$op.ID}}">
|
||||||
<div class="thread">
|
{{- if ne $op.Filename "" -}}
|
||||||
<div class="op-post" id="op{{$op.ID}}">
|
{{- if ne $op.Filename "deleted"}}
|
||||||
{{if ne $op.Filename ""}}
|
<div class="file-info">File: <a href="{{$.config.SiteWebfolder}}{{$.board.Dir}}/src/{{$op.Filename}}" target="_blank">{{$op.Filename}}</a> - ({{formatFilesize $op.Filesize}} , {{$op.ImageW}}x{{$op.ImageH}}, {{$op.FilenameOriginal}})</div>
|
||||||
{{if ne $op.Filename "deleted"}}
|
<a class="upload-container" href="{{$.config.SiteWebfolder}}{{$.board.Dir}}/src/{{$op.Filename}}"><img src="{{$.config.SiteWebfolder}}{{$.board.Dir}}/thumb/{{getThreadThumbnail $op.Filename}}" alt="{{$.config.SiteWebfolder}}{{$.board.Dir}}/src/{{$op.Filename}}" width="{{$op.ThumbW}}" height="{{$op.ThumbH}}" class="upload" /></a>
|
||||||
<div class="file-info">File: <a href="{{$.config.SiteWebfolder}}{{$.board.Dir}}/src/{{$op.Filename}}" target="_blank">{{$op.Filename}}</a> - ({{formatFilesize $op.Filesize}} , {{$op.ImageW}}x{{$op.ImageH}}, {{$op.FilenameOriginal}})</div>
|
{{else}}
|
||||||
<a class="upload-container" href="{{$.config.SiteWebfolder}}{{$.board.Dir}}/src/{{$op.Filename}}"><img src="{{$.config.SiteWebfolder}}{{$.board.Dir}}/thumb/{{getThreadThumbnail $op.Filename}}" alt="{{$.config.SiteWebfolder}}{{$.board.Dir}}/src/{{$op.Filename}}" width="{{$op.ThumbW}}" height="{{$op.ThumbH}}" class="upload" /></a>
|
<div class="file-deleted-box" style="text-align:center;">File removed</div>
|
||||||
{{else}}
|
{{end}}
|
||||||
<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 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="{{$.config.SiteWebfolder}}{{$.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="{{$.config.SiteWebfolder}}{{$.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>
|
||||||
|
{{end}}
|
||||||
|
</div>
|
||||||
|
{{- range $reply_num,$reply := $thread.BoardReplies}}
|
||||||
|
<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 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="{{$.config.SiteWebfolder}}{{$.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="{{$.config.SiteWebfolder}}{{$.board.Dir}}/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/{{getThreadThumbnail $reply.Filename}}" alt="{{$.config.SiteWebfolder}}{{$.board.Dir}}/src/{{$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}}
|
||||||
<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="{{$.config.SiteWebfolder}}{{$.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="{{$.config.SiteWebfolder}}{{$.board.Dir}}/res/{{$op.ID}}.html">View</a>]</span></span><br />
|
{{end -}}
|
||||||
<div class="post-text">{{truncateMessage $op.MessageHTML 2222 18}}</div>
|
<div class="post-text">{{$reply.MessageHTML}}</div>
|
||||||
{{if gt $thread.NumReplies 3}}
|
|
||||||
<b>{{subtract $thread.NumReplies 3}} post{{if gt $thread.NumReplies 4}}s{{end}} omitted</b>
|
|
||||||
{{end}}
|
|
||||||
</div>
|
|
||||||
{{range $reply_num,$reply := $thread.BoardReplies}}
|
|
||||||
<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 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="{{$.config.SiteWebfolder}}{{$.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="{{$.config.SiteWebfolder}}{{$.board.Dir}}/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/{{getThreadThumbnail $reply.Filename}}" alt="{{$.config.SiteWebfolder}}{{$.board.Dir}}/src/{{$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}}
|
|
||||||
</div>
|
|
||||||
<hr />{{end}}
|
|
||||||
<div id="right-bottom-content">
|
|
||||||
<div id="report-delbox">
|
|
||||||
<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" />
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
{{end -}}
|
||||||
<div id="left-bottom-content">
|
</div><hr />
|
||||||
<table id="pages">
|
{{- end}}
|
||||||
<tr><td>{{if gt .board.CurrentPage 2}}
|
<div id="right-bottom-content">
|
||||||
<form method="GET" action="{{.config.SiteWebfolder}}{{.board.Dir}}/{{subtract .board.CurrentPage 1}}.html">
|
<div id="report-delbox">
|
||||||
<input type="submit" value="Previous" />
|
<input type="hidden" name="board" value="{{.board.Dir}}" />
|
||||||
</form>
|
<input type="hidden" name="boardid" value="{{.board.ID}}" />
|
||||||
{{else if intEq .board.CurrentPage 2}}
|
<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 />
|
||||||
<form method="GET" action="{{.config.SiteWebfolder}}{{.board.Dir}}/board.html">
|
Reason: <input type="text" size="10" name="reason" id="reason" /> <input type="submit" name="report_btn" value="Report" /><br />
|
||||||
<input type="submit" value="Previous" />
|
Edit post <input type="submit" name="edit_btn" value="Edit" />
|
||||||
</form>
|
</div>
|
||||||
{{else}}Previous{{end}}</td>
|
|
||||||
<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 := $.boards}}{{if gt $i 0}}/{{end}} <a href="{{$.config.SiteWebfolder}}{{$boardlink.Dir}}/">{{$boardlink.Dir}}</a> {{end}}]
|
|
||||||
</span>
|
|
||||||
</div>
|
</div>
|
||||||
|
</form>
|
||||||
|
<div id="left-bottom-content">
|
||||||
|
<table id="pages">
|
||||||
|
<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 2}}
|
||||||
|
<form method="GET" action="{{.config.SiteWebfolder}}{{.board.Dir}}/board.html">
|
||||||
|
<input type="submit" value="Previous" />
|
||||||
|
</form>
|
||||||
|
{{else}}Previous{{end}}</td>
|
||||||
|
<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 := $.boards}}{{if gt $i 0}}/{{end}} <a href="{{$.config.SiteWebfolder}}{{$boardlink.Dir}}/">{{$boardlink.Dir}}</a> {{end}}]
|
||||||
|
</span>
|
||||||
</div>
|
</div>
|
||||||
{{template "page_footer.html" .}}
|
</div>
|
||||||
|
{{template "page_footer.html" .}}
|
||||||
|
|
|
@ -2,9 +2,11 @@
|
||||||
This will be used for storing configuration-dependent JS variables,
|
This will be used for storing configuration-dependent JS variables,
|
||||||
instead of loading them on every HTML page.
|
instead of loading them on every HTML page.
|
||||||
*/ -}}
|
*/ -}}
|
||||||
var styles = [{{range $ii, $style := .Styles -}}
|
var styles = [
|
||||||
{{if gt $ii 0}}, {{end -}}
|
{{- range $ii, $style := .Styles -}}
|
||||||
{Name: "{{js $style.Name}}", Filename: "{{js $style.Filename}}"}
|
{{if gt $ii 0}},{{end -}}
|
||||||
{{- end}}];
|
{Name: "{{js $style.Name}}", Filename: "{{js $style.Filename}}"}
|
||||||
|
{{- end -}}
|
||||||
|
];
|
||||||
var defaultStyle = "{{.DefaultStyle}}";
|
var defaultStyle = "{{.DefaultStyle}}";
|
||||||
var webroot = "{{.SiteWebfolder}}";
|
var webroot = "{{.SiteWebfolder}}";
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
{{template "page_header.html" .}}
|
{{- template "page_header.html" .}}
|
||||||
<div id="top-pane">
|
<div id="top-pane">
|
||||||
<span id="site-title">{{.config.SiteName}}</span><br />
|
<span id="site-title">{{.config.SiteName}}</span><br />
|
||||||
<span id="site-slogan">{{.config.SiteSlogan}}</span>
|
<span id="site-slogan">{{.config.SiteSlogan}}</span>
|
||||||
|
@ -12,26 +12,37 @@
|
||||||
<div class="section-block">
|
<div class="section-block">
|
||||||
<div class="section-title-block"><b>Boards</b></div>
|
<div class="section-title-block"><b>Boards</b></div>
|
||||||
<div class="section-body">
|
<div class="section-body">
|
||||||
{{range $_, $section := .sections}}{{if not $section.Hidden}}<ul style="float:left; list-style: none">
|
{{- range $_, $section := .sections -}}
|
||||||
<li style="text-align: center; font-weight: bold"><b><u>{{$section.Name}}</u></b></li>
|
{{if not $section.Hidden}}
|
||||||
{{range $_, $board := $.boards}}{{if and (eq $board.Section $section.ID) (ne $board.Dir $.config.Modboard)}}
|
<ul style="float:left; list-style: none">
|
||||||
<li><a href="{{$.config.SiteWebfolder}}{{$board.Dir}}/" title="{{$board.Description}}">/{{$board.Dir}}/</a> — {{$board.Title}}</li>
|
<li style="text-align: center; font-weight: bold"><b><u>{{$section.Name}}</u></b></li>
|
||||||
{{end}}{{end}}
|
{{range $_, $board := $.boards}}
|
||||||
</ul>{{end}}{{end}}
|
{{if and (eq $board.Section $section.ID) (ne $board.Dir $.config.Modboard)}}
|
||||||
|
<li><a href="{{$.config.SiteWebfolder}}{{$board.Dir}}/" title="{{$board.Description}}">/{{$board.Dir}}/</a> — {{$board.Title}}</li>
|
||||||
|
{{end}}
|
||||||
|
{{end}}
|
||||||
|
</ul>
|
||||||
|
{{end}}
|
||||||
|
{{end}}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{{if gt .config.MaxRecentPosts 0}}
|
{{- if gt .config.MaxRecentPosts 0}}
|
||||||
<div class="section-block">
|
<div class="section-block">
|
||||||
<div class="section-title-block"><b>Recent Posts</b></div>
|
<div class="section-title-block"><b>Recent Posts</b></div>
|
||||||
<div class="section-body">
|
<div class="section-body">
|
||||||
<div id="recent-posts">
|
<div id="recent-posts">
|
||||||
{{range $i, $post := $.recent_posts}}{{$postURL := getPostURL $post "recent" false}}
|
{{- range $i, $post := $.recent_posts}}{{$postURL := getPostURL $post "recent" false}}
|
||||||
<div class="recent-post">
|
<div class="recent-post">
|
||||||
{{if and (ne $post.Filename "deleted") (ne $post.Filename "")}}<a href="{{$postURL}}" class="front-reply" target="_blank"><img src="{{$.config.SiteWebfolder}}{{$post.BoardName}}/thumb/{{getThreadThumbnail $post.Filename}}" alt="post thumbnail"/></a><br />
|
{{if and (ne $post.Filename "deleted") (ne $post.Filename "") -}}
|
||||||
{{else}}<div class="file-deleted-box" style="text-align:center; float:none;"><a href="{{$postURL}}" class="front-reply" target="_blank">No file</a></div>{{end}}<br />
|
<a href="{{$postURL}}" class="front-reply" target="_blank"><img src="{{$.config.SiteWebfolder}}{{$post.BoardName}}/thumb/{{getThreadThumbnail $post.Filename}}" alt="post thumbnail"/></a><br />
|
||||||
<a href="{{$.config.SiteWebfolder}}{{$post.BoardName}}/">/{{$post.BoardName}}/</a>
|
{{else}}
|
||||||
<hr />
|
<div class="file-deleted-box" style="text-align:center; float:none;"><a href="{{$postURL}}" class="front-reply" target="_blank">No file</a></div>
|
||||||
{{truncateMessage (stripHTML $post.Message) 40 4}}</div>{{end}}</div>
|
{{- end}}<br />
|
||||||
</div>{{end}}</div>
|
<a href="{{$.config.SiteWebfolder}}{{$post.BoardName}}/">/{{$post.BoardName}}/</a><hr />
|
||||||
|
{{truncateMessage (stripHTML $post.Message) 40 4}}
|
||||||
|
</div>{{end}}
|
||||||
|
</div>
|
||||||
|
</div>{{end}}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{{template "page_footer.html" .}}
|
{{template "page_footer.html" .}}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
<div id="footer">
|
<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 />
|
<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 {{version}}</a><br />
|
Powered by <a href="http://github.com/eggbertx/gochan/">Gochan {{version}}</a><br />
|
||||||
</div>
|
</div>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
|
@ -3,13 +3,13 @@
|
||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
{{with .board}}
|
{{with .board -}}
|
||||||
{{with $.op}}
|
{{with $.op -}}
|
||||||
{{if ne $.op.Subject ""}}<title>/{{$.board.Dir}}/ - {{truncateString $.op.Subject 20 true}}</title>
|
{{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>
|
{{- else if ne $.op.MessageHTML "" -}}<title>/{{$.board.Dir}}/ - {{truncateString $.op.MessageText 20 true}}</title>
|
||||||
{{else}}<title>/{{$.board.Dir}}/ - #{{$.op.ID}}</title>{{end}}
|
{{- else}}<title>/{{$.board.Dir}}/ - #{{$.op.ID}}</title>{{end}}
|
||||||
{{else}}<title>/{{$.board.Dir}}/ - {{$.board.Title}}</title>{{end}}
|
{{- else}}<title>/{{$.board.Dir}}/ - {{$.board.Title}}</title>{{end}}
|
||||||
{{else}}<title>{{.config.SiteName}}</title>{{end}}
|
{{- else}}<title>{{.config.SiteName}}</title>{{end}}
|
||||||
<link rel="stylesheet" href="{{.config.SiteWebfolder}}css/global.css" />
|
<link rel="stylesheet" href="{{.config.SiteWebfolder}}css/global.css" />
|
||||||
<link id="theme" rel="stylesheet" href="{{.config.SiteWebfolder}}css/{{.config.DefaultStyle}}" />
|
<link id="theme" rel="stylesheet" href="{{.config.SiteWebfolder}}css/{{.config.DefaultStyle}}" />
|
||||||
<link rel="shortcut icon" href="{{.config.SiteWebfolder}}favicon.png">
|
<link rel="shortcut icon" href="{{.config.SiteWebfolder}}favicon.png">
|
||||||
|
@ -19,6 +19,6 @@
|
||||||
<script type="text/javascript" src="{{$.config.SiteWebfolder}}javascript/manage.js"></script>
|
<script type="text/javascript" src="{{$.config.SiteWebfolder}}javascript/manage.js"></script>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div id="topbar">
|
<div id="topbar">
|
||||||
{{range $i, $board := .boards}}<a href="{{$.config.SiteWebfolder}}{{$board.Dir}}/" class="topbar-item">/{{$board.Dir}}/</a>{{end}}
|
{{range $i, $board := .boards}}<a href="{{$.config.SiteWebfolder}}{{$board.Dir}}/" class="topbar-item">/{{$board.Dir}}/</a>{{end}}
|
||||||
</div>
|
</div>
|
|
@ -1,10 +1,13 @@
|
||||||
{{define "postbox.html"}}
|
{{define "postbox.html"}}
|
||||||
<div id="postbox-area">
|
<div id="postbox-area">
|
||||||
<form id="postform" name="postform" action="/post" method="POST" enctype="multipart/form-data">
|
<form id="postform" name="postform" action="/post" method="POST" enctype="multipart/form-data">
|
||||||
{{with .op}}<input type="hidden" name="threadid" value="{{$.op.ID}}" />
|
{{- with .op}}
|
||||||
|
<input type="hidden" name="threadid" value="{{$.op.ID}}" />
|
||||||
<input type="hidden" name="boardid" value="{{$.board.ID}}" />
|
<input type="hidden" name="boardid" value="{{$.board.ID}}" />
|
||||||
{{else}}<input type="hidden" name="threadid" value="0" />
|
{{- else -}}
|
||||||
<input type="hidden" name="boardid" value="{{$.board.ID}}" />{{end}}
|
<input type="hidden" name="threadid" value="0" />
|
||||||
|
<input type="hidden" name="boardid" value="{{$.board.ID}}" />
|
||||||
|
{{- end}}
|
||||||
<table id="postbox-static">
|
<table id="postbox-static">
|
||||||
<tr><th class="postblock">Name</th><td><input type="text" name="postname" maxlength="100" size="28" /></td></tr>
|
<tr><th class="postblock">Name</th><td><input type="text" name="postname" maxlength="100" size="28" /></td></tr>
|
||||||
<tr><th class="postblock">Email</th><td><input type="text" name="postemail" maxlength="100" size="28" /></td></tr>
|
<tr><th class="postblock">Email</th><td><input type="text" name="postemail" maxlength="100" size="28" /></td></tr>
|
||||||
|
@ -13,8 +16,7 @@
|
||||||
<input type="submit" value="{{with .op}}Reply{{else}}Post{{end}}"/></td></tr>
|
<input type="submit" value="{{with .op}}Reply{{else}}Post{{end}}"/></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">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">File</th><td><input name="imagefile" type="file"><input type="checkbox" id="spoiler" name="spoiler"/><label for="spoiler">Spoiler</label></td></tr>
|
||||||
<input type="password" name="dummy2" style="display:none"/>
|
|
||||||
<tr><th class="postblock">Password</th><td><input type="password" id="postpassword" name="postpassword" size="14" /> (for post/file deletion)</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>
|
</table><input type="password" name="dummy2" style="display:none"/>
|
||||||
</form>
|
</form>
|
||||||
</div>{{end}}
|
</div>{{end}}
|
|
@ -2,8 +2,7 @@
|
||||||
<header>
|
<header>
|
||||||
<h1 id="board-title">/{{$.board.Dir}}/ - {{$.board.Title}}</h1>
|
<h1 id="board-title">/{{$.board.Dir}}/ - {{$.board.Title}}</h1>
|
||||||
<span id="board-subtitle">{{$.board.Subtitle}}</span>
|
<span id="board-subtitle">{{$.board.Subtitle}}</span>
|
||||||
</header>
|
</header><hr />
|
||||||
<hr />
|
|
||||||
<div id="threadlinks-top">
|
<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)">
|
<select id="changepage" onchange="changePage(this)">
|
||||||
|
@ -17,23 +16,23 @@
|
||||||
<div id="right-sidelinks">
|
<div id="right-sidelinks">
|
||||||
<a href="{{$.config.SiteWebfolder}}{{$.board.Dir}}/catalog.html">Board catalog</a><br />
|
<a href="{{$.config.SiteWebfolder}}{{$.board.Dir}}/catalog.html">Board catalog</a><br />
|
||||||
</div>
|
</div>
|
||||||
{{template "postbox.html" .}}
|
{{template "postbox.html" .}}<hr />
|
||||||
<hr />
|
|
||||||
<div id="content">
|
<div id="content">
|
||||||
<form action="/util" method="POST" id="main-form">
|
<form action="/util" method="POST" id="main-form">
|
||||||
<div class="thread" id="{{$.op.ID}}">
|
<div class="thread" id="{{$.op.ID}}">
|
||||||
<div class="op-post" id="op{{.op.ID}}">
|
<div class="op-post" id="op{{.op.ID}}">
|
||||||
{{if ne $.op.Filename ""}}
|
{{if ne $.op.Filename ""}}
|
||||||
{{if ne $.op.Filename "deleted"}}
|
{{- 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>
|
<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/{{getThreadThumbnail .op.Filename}}" alt="{{$.config.SiteWebfolder}}{{$.board.Dir}}/src/{{.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/{{getThreadThumbnail .op.Filename}}" alt="{{$.config.SiteWebfolder}}{{$.board.Dir}}/src/{{.op.Filename}}" width="{{.op.ThumbW}}" height="{{.op.ThumbH}}" class="upload" /></a>
|
||||||
{{else}}
|
{{- else}}
|
||||||
<div class="file-deleted-box" style="text-align:center;">File removed</div>
|
<div class="file-deleted-box" style="text-align:center;">File removed</div>
|
||||||
{{end}}{{end}}
|
{{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 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="{{$.config.SiteWebfolder}}{{.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 />
|
<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="{{$.config.SiteWebfolder}}{{.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 class="post-text">{{.op.MessageHTML}}</div>
|
||||||
</div>
|
</div>
|
||||||
{{range $reply_num,$reply := .posts}}
|
{{range $reply_num,$reply := .posts -}}
|
||||||
<div class="reply-container" id="replycontainer{{$reply.ID}}">
|
<div class="reply-container" id="replycontainer{{$reply.ID}}">
|
||||||
<a class="anchor" id="{{$reply.ID}}"></a>
|
<a class="anchor" id="{{$reply.ID}}"></a>
|
||||||
<div class="reply" id="reply{{$reply.ID}}">
|
<div class="reply" id="reply{{$reply.ID}}">
|
||||||
|
@ -47,9 +46,9 @@
|
||||||
{{end}}{{end}}
|
{{end}}{{end}}
|
||||||
<div class="post-text">{{$reply.MessageHTML}}</div>
|
<div class="post-text">{{$reply.MessageHTML}}</div>
|
||||||
</div>
|
</div>
|
||||||
</div>{{end}}
|
</div>
|
||||||
</div>
|
{{end}}
|
||||||
<hr />
|
</div><hr />
|
||||||
<div id="right-bottom-content">
|
<div id="right-bottom-content">
|
||||||
<div id="report-delbox">
|
<div id="report-delbox">
|
||||||
<input type="hidden" name="board" value="{{.board.Dir}}" />
|
<input type="hidden" name="board" value="{{.board.Dir}}" />
|
||||||
|
@ -64,15 +63,18 @@
|
||||||
<table id="pages">
|
<table id="pages">
|
||||||
<tr>
|
<tr>
|
||||||
<td><a href="{{.config.SiteWebfolder}}{{.board.Dir}}/">Return</a></td>
|
<td><a href="{{.config.SiteWebfolder}}{{.board.Dir}}/">Return</a></td>
|
||||||
<td>{{if gt .op.CurrentPage 1}}
|
<td>{{- if gt .op.CurrentPage 1 -}}
|
||||||
<form method="GET" action="{{.config.SiteWebfolder}}{{.board.Dir}}/res/{{.op.ID}}p{{subtract .op.CurrentPage 1}}.html">
|
<form method="GET" action="{{.config.SiteWebfolder}}{{.board.Dir}}/res/{{.op.ID}}p{{subtract .op.CurrentPage 1}}.html">
|
||||||
<input type="submit" value="Previous" />
|
<input type="submit" value="Previous" />
|
||||||
</form>
|
</form>
|
||||||
{{else}}Previous{{end}}
|
{{- else}}Previous{{end -}}
|
||||||
</td>
|
</td>
|
||||||
<td>[<a href="{{.config.SiteWebfolder}}{{.board.Dir}}/res/{{.op.ID}}.html">All</a>]
|
<td>[<a href="{{.config.SiteWebfolder}}{{.board.Dir}}/res/{{.op.ID}}.html">All</a>]
|
||||||
{{range $i,$_ := makeLoop .op.NumPages 0}}
|
{{- 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>
|
[{{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}}
|
<td>{{if lt .op.CurrentPage .op.NumPages}}
|
||||||
<form method="GET" action="{{.config.SiteWebfolder}}{{.board.Dir}}/res/{{.op.ID}}p{{add .op.CurrentPage 1}}.html">
|
<form method="GET" action="{{.config.SiteWebfolder}}{{.board.Dir}}/res/{{.op.ID}}p{{add .op.CurrentPage 1}}.html">
|
||||||
<input type="submit" value="Next" />
|
<input type="submit" value="Next" />
|
||||||
|
@ -80,7 +82,9 @@
|
||||||
{{else}}Next{{end}}</td></tr>
|
{{else}}Next{{end}}</td></tr>
|
||||||
</table>
|
</table>
|
||||||
<span id="boardmenu-bottom">
|
<span id="boardmenu-bottom">
|
||||||
[{{range $i, $boardlink := .boards}} {{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>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue