mirror of
https://github.com/Eggbertx/gochan.git
synced 2025-09-16 07:56:24 -07:00
More abstraction to make SQL queries simpler, initialsetupdb.sql is now called on every startup
This commit is contained in:
parent
6a4443dbc6
commit
9597760442
13 changed files with 313 additions and 322 deletions
|
@ -1,5 +1,4 @@
|
|||
-- Initial setup file for Gochan
|
||||
-- Deleted after setup is finished
|
||||
|
||||
-- Turn off warnings in case tables are already there.
|
||||
SET sql_notes=0;
|
||||
|
@ -200,11 +199,11 @@ CREATE TABLE IF NOT EXISTS `DBPREFIXstaff` (
|
|||
PRIMARY KEY (`id`)
|
||||
) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4;
|
||||
|
||||
# create a temp table with the same columns as the posts table to be stored in memory
|
||||
# This is currently not used, and commented out.
|
||||
#CREATE TABLE IF NOT EXISTS `DBPREFIXtempposts` SELECT * FROM DBPREFIXposts;
|
||||
#ALTER TABLE `DBPREFIXtempposts` CHANGE `message` `message` VARCHAR(1024);
|
||||
#ALTER TABLE `DBPREFIXtempposts` ENGINE=MEMORY;
|
||||
-- create a temp table with the same columns as the posts table to be stored in memory
|
||||
-- This is currently not used, and commented out.
|
||||
-- CREATE TABLE IF NOT EXISTS `DBPREFIXtempposts` SELECT * FROM DBPREFIXposts;
|
||||
-- ALTER TABLE `DBPREFIXtempposts` CHANGE `message` `message` VARCHAR(1024);
|
||||
-- ALTER TABLE `DBPREFIXtempposts` ENGINE=MEMORY;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `DBPREFIXwordfilters` (
|
||||
`id` SMALLINT UNSIGNED NOT NULL AUTO_INCREMENT,
|
||||
|
|
|
@ -1,2 +0,0 @@
|
|||
This file is in here to keep the log directory around, as gochan expects the log
|
||||
directory to exist before it runs.
|
|
@ -16,7 +16,11 @@ var verbosityString string
|
|||
var buildtimeString string // set in Makefile, format: YRMMDD.HHMM
|
||||
|
||||
func main() {
|
||||
defer db.Close()
|
||||
defer func() {
|
||||
if db != nil {
|
||||
_ = db.Close()
|
||||
}
|
||||
}()
|
||||
initConfig()
|
||||
config.Verbosity, _ = strconv.Atoi(verbosityString)
|
||||
config.Version = version
|
||||
|
@ -26,7 +30,7 @@ func main() {
|
|||
|
||||
println(0, "Loading and parsing templates...")
|
||||
if err := initTemplates(); err != nil {
|
||||
println(0, err.Error())
|
||||
handleError(0, customError(err))
|
||||
os.Exit(2)
|
||||
}
|
||||
|
||||
|
@ -34,7 +38,8 @@ func main() {
|
|||
if db != nil {
|
||||
_, err := db.Exec("USE `" + config.DBname + "`")
|
||||
if err != nil {
|
||||
println(0, customError(err))
|
||||
handleError(0, customError(err))
|
||||
os.Exit(2)
|
||||
}
|
||||
}
|
||||
initServer()
|
||||
|
|
193
src/manage.go
193
src/manage.go
|
@ -87,19 +87,24 @@ func getCurrentStaff() (string, error) {
|
|||
}
|
||||
key = sessionCookie.Value
|
||||
|
||||
row := db.QueryRow("SELECT `data` FROM `"+config.DBprefix+"sessions` WHERE `key` = ?", key)
|
||||
current_session := new(SessionsTable)
|
||||
|
||||
if err := row.Scan(¤t_session.Data); err != nil {
|
||||
if err := queryRowSQL(
|
||||
"SELECT `data` FROM `"+config.DBprefix+"sessions` WHERE `key` = ?",
|
||||
[]interface{}{key},
|
||||
[]interface{}{¤t_session.Data},
|
||||
); err != nil {
|
||||
return "", err
|
||||
}
|
||||
return current_session.Data, nil
|
||||
}
|
||||
|
||||
func getStaff(name string) (*StaffTable, error) {
|
||||
row := db.QueryRow("SELECT * FROM `"+config.DBprefix+"staff` WHERE `username` = ?", name)
|
||||
staff_obj := new(StaffTable)
|
||||
err := row.Scan(&staff_obj.ID, &staff_obj.Username, &staff_obj.PasswordChecksum, &staff_obj.Salt, &staff_obj.Rank, &staff_obj.Boards, &staff_obj.AddedOn, &staff_obj.LastActive)
|
||||
err := queryRowSQL(
|
||||
"SELECT * FROM `"+config.DBprefix+"staff` WHERE `username` = ?",
|
||||
[]interface{}{name},
|
||||
[]interface{}{&staff_obj.ID, &staff_obj.Username, &staff_obj.PasswordChecksum, &staff_obj.Salt, &staff_obj.Rank, &staff_obj.Boards, &staff_obj.AddedOn, &staff_obj.LastActive},
|
||||
)
|
||||
return staff_obj, err
|
||||
}
|
||||
|
||||
|
@ -124,7 +129,7 @@ func getStaffRank() int {
|
|||
func createSession(key string, username string, password string, request *http.Request, writer *http.ResponseWriter) int {
|
||||
//returns 0 for successful, 1 for password mismatch, and 2 for other
|
||||
domain := request.Host
|
||||
|
||||
var err error
|
||||
chopPortNumRegex := regexp.MustCompile("(.+|\\w+):(\\d+)$")
|
||||
domain = chopPortNumRegex.Split(domain, -1)[0]
|
||||
|
||||
|
@ -146,13 +151,17 @@ func createSession(key string, username string, password string, request *http.R
|
|||
// successful login, add cookie that expires in one month
|
||||
cookie := &http.Cookie{Name: "sessiondata", Value: key, Path: "/", Domain: domain, Expires: time.Now().Add(time.Duration(time.Hour * 730))}
|
||||
http.SetCookie(*writer, cookie)
|
||||
_, err := db.Exec("INSERT INTO `" + config.DBprefix + "sessions` (`key`, `data`, `expires`) VALUES('" + key + "','" + username + "', '" + getSpecificSQLDateTime(time.Now().Add(time.Duration(time.Hour*730))) + "')")
|
||||
if err != nil {
|
||||
if _, err = execSQL(
|
||||
"INSERT INTO `"+config.DBprefix+"sessions` (`key`, `data`, `expires`) VALUES(?,?,?)",
|
||||
key, username, getSpecificSQLDateTime(time.Now().Add(time.Duration(time.Hour*730))),
|
||||
); err != nil {
|
||||
handleError(1, customError(err))
|
||||
return 2
|
||||
}
|
||||
_, err = db.Exec("UPDATE `"+config.DBprefix+"staff` SET `last_active` = ? WHERE `username` = ?", getSQLDateTime(), username)
|
||||
if err != nil {
|
||||
|
||||
if _, err = execSQL(
|
||||
"UPDATE `"+config.DBprefix+"staff` SET `last_active` = ? WHERE `username` = ?", getSQLDateTime(), username,
|
||||
); err != nil {
|
||||
handleError(1, customError(err))
|
||||
}
|
||||
return 0
|
||||
|
@ -165,10 +174,12 @@ var manage_functions = map[string]ManageFunction{
|
|||
Permissions: 3,
|
||||
Callback: func() (html string) {
|
||||
html = "<h2>Cleanup</h2><br />"
|
||||
var err error
|
||||
if request.FormValue("run") == "Run Cleanup" {
|
||||
html += "Removing deleted posts from the database.<hr />"
|
||||
_, err := db.Exec("DELETE FROM `" + config.DBprefix + "posts` WHERE `deleted_timestamp` = '" + nilTimestamp + "'")
|
||||
if err != nil {
|
||||
if _, err = execSQL(
|
||||
"DELETE FROM `"+config.DBprefix+"posts` WHERE `deleted_timestamp` = ?", nilTimestamp,
|
||||
); err != nil {
|
||||
html += "<tr><td>" + handleError(1, err.Error()) + "</td></tr></table>"
|
||||
return
|
||||
}
|
||||
|
@ -176,7 +187,8 @@ var manage_functions = map[string]ManageFunction{
|
|||
// TODO: remove orphaned uploads
|
||||
|
||||
html += "Optimizing all tables in database.<hr />"
|
||||
tableRows, tablesErr := db.Query("SHOW TABLES")
|
||||
tableRows, tablesErr := querySQL("SHOW TABLES")
|
||||
defer closeRows(tableRows)
|
||||
if tablesErr != nil {
|
||||
html += "<tr><td>" + tablesErr.Error() + "</td></tr></table>"
|
||||
return
|
||||
|
@ -185,8 +197,7 @@ var manage_functions = map[string]ManageFunction{
|
|||
for tableRows.Next() {
|
||||
var table string
|
||||
tableRows.Scan(&table)
|
||||
_, err = db.Exec("OPTIMIZE TABLE " + table)
|
||||
if err != nil {
|
||||
if _, err := execSQL("OPTIMIZE TABLE `" + table + "`"); err != nil {
|
||||
html += handleError(1, err.Error()) + "<br />"
|
||||
return
|
||||
}
|
||||
|
@ -230,7 +241,8 @@ var manage_functions = map[string]ManageFunction{
|
|||
Permissions: 3,
|
||||
Callback: func() (html string) {
|
||||
html = "<img src=\"/css/purge.jpg\" />"
|
||||
rows, err := db.Query("SELECT `dir` FROM `" + config.DBprefix + "boards`")
|
||||
rows, err := querySQL("SELECT `dir` FROM `" + config.DBprefix + "boards`")
|
||||
defer closeRows(rows)
|
||||
if err != nil {
|
||||
html += err.Error()
|
||||
handleError(1, customError(err))
|
||||
|
@ -264,16 +276,20 @@ var manage_functions = map[string]ManageFunction{
|
|||
return
|
||||
}
|
||||
}
|
||||
if _, err = db.Exec("TRUNCATE `" + config.DBprefix + "posts`"); err != nil {
|
||||
if _, err = execSQL("TRUNCATE `" + config.DBprefix + "posts`"); err != nil {
|
||||
html += err.Error() + "<br />"
|
||||
handleError(1, customError(err))
|
||||
return
|
||||
}
|
||||
|
||||
if _, err = execSQL("ALTER TABLE `" + config.DBprefix + "posts` AUTO_INCREMENT = 1"); err != nil {
|
||||
html += err.Error() + "<br />"
|
||||
handleError(1, customError(err))
|
||||
return
|
||||
}
|
||||
db.Exec("ALTER TABLE `" + config.DBprefix + "posts` AUTO_INCREMENT = 1")
|
||||
html += "<br />Everything purged, rebuilding all<br />" +
|
||||
buildBoards(true, 0) + "<hr />\n" +
|
||||
buildFrontPage()
|
||||
|
||||
return
|
||||
}},
|
||||
"executesql": {
|
||||
|
@ -283,8 +299,7 @@ var manage_functions = map[string]ManageFunction{
|
|||
html = "<h1>Execute SQL statement(s)</h1><form method = \"POST\" action=\"/manage?action=executesql\">\n<textarea name=\"sql\" id=\"sql-statement\">" + statement + "</textarea>\n<input type=\"submit\" />\n</form>"
|
||||
if statement != "" {
|
||||
html += "<hr />"
|
||||
_, sqlerr := db.Exec(statement)
|
||||
if sqlerr != nil {
|
||||
if _, sqlerr := execSQL(statement); sqlerr != nil {
|
||||
html += handleError(1, sqlerr.Error())
|
||||
} else {
|
||||
html += "Statement esecuted successfully."
|
||||
|
@ -349,7 +364,8 @@ var manage_functions = map[string]ManageFunction{
|
|||
Callback: func() (html string) {
|
||||
html = "<h1>Announcements</h1><br />"
|
||||
|
||||
rows, err := db.Query("SELECT `subject`,`message`,`poster`,`timestamp` FROM `" + config.DBprefix + "announcements` ORDER BY `id` DESC")
|
||||
rows, err := querySQL("SELECT `subject`,`message`,`poster`,`timestamp` FROM `" + config.DBprefix + "announcements` ORDER BY `id` DESC")
|
||||
defer closeRows(rows)
|
||||
if err != nil {
|
||||
html += handleError(1, err.Error())
|
||||
return
|
||||
|
@ -399,7 +415,8 @@ var manage_functions = map[string]ManageFunction{
|
|||
boards_list_html := " <span style=\"font-weight: bold;\">Boards: </span><br />\n" +
|
||||
" <label>All boards <input type=\"checkbox\" id=\"allboards\" /></label> overrides individual board selection<br />\n"
|
||||
|
||||
rows, err := db.Query("SELECT `dir` FROM `" + config.DBprefix + "boards`")
|
||||
rows, err := querySQL("SELECT `dir` FROM `" + config.DBprefix + "boards`")
|
||||
defer closeRows(rows)
|
||||
if err != nil {
|
||||
html += "<hr />" + handleError(1, err.Error())
|
||||
return
|
||||
|
@ -451,7 +468,7 @@ var manage_functions = map[string]ManageFunction{
|
|||
"<input type=\"submit\" name=\"ban-both-button\" value=\"Ban both\" /></form>\n</br />" +
|
||||
"<h2>Banned IPs</h2>\n"
|
||||
|
||||
rows, err = db.Query("SELECT * FROM `" + config.DBprefix + "banlist`")
|
||||
rows, err = querySQL("SELECT * FROM `" + config.DBprefix + "banlist`")
|
||||
if err != nil {
|
||||
html += "</table><br />" + handleError(1, err.Error())
|
||||
return
|
||||
|
@ -502,9 +519,11 @@ var manage_functions = map[string]ManageFunction{
|
|||
html = "nobody;0;"
|
||||
return
|
||||
}
|
||||
row := db.QueryRow("SELECT `rank`,`boards` FROM `"+config.DBprefix+"staff` WHERE `username` = ?", current_staff)
|
||||
staff := new(StaffTable)
|
||||
if err = row.Scan(&staff.Rank, &staff.Boards); err != nil {
|
||||
if err := queryRowSQL("SELECT `rank`,`boards` FROM `"+config.DBprefix+"staff` WHERE `username` = ?",
|
||||
[]interface{}{current_staff},
|
||||
[]interface{}{&staff.Rank, &staff.Boards},
|
||||
); err != nil {
|
||||
html += handleError(1, "Error getting staff list: "+err.Error())
|
||||
return
|
||||
}
|
||||
|
@ -619,33 +638,26 @@ var manage_functions = map[string]ManageFunction{
|
|||
board_creation_status = handleError(1, "ERROR: directory /"+config.DocumentRoot+"/"+board.Dir+"/src/ already exists!")
|
||||
break
|
||||
}
|
||||
stmt, err := db.Prepare(
|
||||
"INSERT INTO `" + config.DBprefix + "boards` (`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`) " +
|
||||
"VALUES(?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)")
|
||||
if err != nil {
|
||||
do = ""
|
||||
board_creation_status = handleError(1, err.Error())
|
||||
break
|
||||
}
|
||||
defer closeStatement(stmt)
|
||||
|
||||
boardCreationTimestamp := getSpecificSQLDateTime(board.CreatedOn)
|
||||
|
||||
if _, err = stmt.Exec(
|
||||
&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, &boardCreationTimestamp, &board.Anonymous,
|
||||
&board.ForcedAnon, &board.MaxAge, &board.AutosageAfter,
|
||||
&board.NoImagesAfter, &board.MaxMessageLength, &board.EmbedsAllowed,
|
||||
&board.RedirectToThread, &board.RequireFile, &board.EnableCatalog,
|
||||
if _, err := execSQL(
|
||||
"INSERT INTO `"+config.DBprefix+"boards` (`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`) "+
|
||||
"VALUES(?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)",
|
||||
[]interface{}{
|
||||
&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, &boardCreationTimestamp, &board.Anonymous,
|
||||
&board.ForcedAnon, &board.MaxAge, &board.AutosageAfter,
|
||||
&board.NoImagesAfter, &board.MaxMessageLength, &board.EmbedsAllowed,
|
||||
&board.RedirectToThread, &board.RequireFile, &board.EnableCatalog,
|
||||
},
|
||||
); err != nil {
|
||||
do = ""
|
||||
board_creation_status = customError(err)
|
||||
handleError(1, "Error creating board: "+board_creation_status)
|
||||
board_creation_status = handleError(1, "Error creating board: "+customError(err))
|
||||
break
|
||||
} else {
|
||||
board_creation_status = "Board created successfully"
|
||||
println(2, board_creation_status)
|
||||
|
@ -661,7 +673,7 @@ var manage_functions = map[string]ManageFunction{
|
|||
// resetBoardSectionArrays()
|
||||
default:
|
||||
// put the default column values in the text boxes
|
||||
rows, err = db.Query("SELECT `column_name`,`columnDefault` FROM `information_schema`.`columns` WHERE `table_name` = '" + config.DBprefix + "boards'")
|
||||
rows, err = querySQL("SELECT `column_name`,`columnDefault` FROM `information_schema`.`columns` WHERE `table_name` = '" + config.DBprefix + "boards'")
|
||||
if err != nil {
|
||||
html += handleError(1, "Error getting column names from boards table:"+err.Error())
|
||||
return
|
||||
|
@ -726,12 +738,12 @@ var manage_functions = map[string]ManageFunction{
|
|||
}
|
||||
|
||||
html = "<h1>Manage boards</h1>\n<form action=\"/manage?action=boards\" method=\"POST\">\n<input type=\"hidden\" name=\"do\" value=\"existing\" /><select name=\"boardselect\">\n<option>Select board...</option>\n"
|
||||
rows, err = db.Query("SELECT `dir` FROM `" + config.DBprefix + "boards`")
|
||||
rows, err = querySQL("SELECT `dir` FROM `" + config.DBprefix + "boards`")
|
||||
defer closeRows(rows)
|
||||
if err != nil {
|
||||
html += handleError(1, err.Error())
|
||||
return
|
||||
}
|
||||
defer closeRows(rows)
|
||||
|
||||
for rows.Next() {
|
||||
var boardDir string
|
||||
|
@ -745,7 +757,7 @@ var manage_functions = map[string]ManageFunction{
|
|||
manageBoardsBuffer := bytes.NewBufferString("")
|
||||
allSections, _ = getSectionArr("")
|
||||
if len(allSections) == 0 {
|
||||
db.Exec("INSERT INTO `" + config.DBprefix + "sections` (`hidden`,`name`,`abbreviation`) VALUES(0,'Main','main')")
|
||||
execSQL("INSERT INTO `" + config.DBprefix + "sections` (`hidden`,`name`,`abbreviation`) VALUES(0,'Main','main')")
|
||||
}
|
||||
allSections, _ = getSectionArr("")
|
||||
|
||||
|
@ -824,14 +836,13 @@ var manage_functions = map[string]ManageFunction{
|
|||
}
|
||||
|
||||
for _, post := range posts {
|
||||
stmt, err := db.Prepare("UPDATE `" + config.DBprefix + "posts` SET `message` = ? WHERE `id` = ? AND `boardid` = ?")
|
||||
_, err = execSQL("UPDATE `"+config.DBprefix+"posts` SET `message` = ? WHERE `id` = ? AND `boardid` = ?",
|
||||
formatMessage(post.MessageText), post.ID, post.BoardID,
|
||||
)
|
||||
if err != nil {
|
||||
html += handleError(1, err.Error()) + "<br />"
|
||||
return
|
||||
}
|
||||
defer closeStatement(stmt)
|
||||
|
||||
stmt.Exec(formatMessage(post.MessageText), post.ID, post.BoardID)
|
||||
}
|
||||
html += "Done reparsing HTML<hr />" +
|
||||
buildFrontPage() + "<hr />\n" +
|
||||
|
@ -847,27 +858,30 @@ var manage_functions = map[string]ManageFunction{
|
|||
limit = "50"
|
||||
}
|
||||
html = "<h1>Recent posts</h1>\nLimit by: <select id=\"limit\"><option>25</option><option>50</option><option>100</option><option>200</option></select>\n<br />\n<table width=\"100%%d\" border=\"1\">\n<colgroup><col width=\"25%%\" /><col width=\"50%%\" /><col width=\"17%%\" /></colgroup><tr><th></th><th>Message</th><th>Time</th></tr>"
|
||||
rows, err := db.Query("SELECT `" + config.DBprefix + "boards`.`dir` AS `boardname`, " +
|
||||
"`" + config.DBprefix + "posts`.`boardid` AS boardid, " +
|
||||
"`" + config.DBprefix + "posts`.`id` AS id, " +
|
||||
"`" + config.DBprefix + "posts`. " +
|
||||
"`parentid` AS parentid, " +
|
||||
"`" + config.DBprefix + "posts`. " +
|
||||
"`message` AS message, " +
|
||||
"`" + config.DBprefix + "posts`. " +
|
||||
"`ip` AS ip, " +
|
||||
"`" + config.DBprefix + "posts`. " +
|
||||
"`timestamp` AS timestamp " +
|
||||
"FROM `" + config.DBprefix + "posts`, `" + config.DBprefix + "boards` " +
|
||||
"WHERE `reviewed` = 0 " +
|
||||
"AND `" + config.DBprefix + "posts`.`deleted_timestamp` = \"" + nilTimestamp + "\" " +
|
||||
"AND `boardid` = `" + config.DBprefix + "boards`.`id` " +
|
||||
"ORDER BY `timestamp` DESC LIMIT " + limit + "")
|
||||
rows, err := querySQL(
|
||||
"SELECT `"+config.DBprefix+"boards`.`dir` AS `boardname`, "+
|
||||
"`"+config.DBprefix+"posts`.`boardid` AS boardid, "+
|
||||
"`"+config.DBprefix+"posts`.`id` AS id, "+
|
||||
"`"+config.DBprefix+"posts`. "+
|
||||
"`parentid` AS parentid, "+
|
||||
"`"+config.DBprefix+"posts`. "+
|
||||
"`message` AS message, "+
|
||||
"`"+config.DBprefix+"posts`. "+
|
||||
"`ip` AS ip, "+
|
||||
"`"+config.DBprefix+"posts`. "+
|
||||
"`timestamp` AS timestamp "+
|
||||
"FROM `"+config.DBprefix+"posts`, `"+config.DBprefix+"boards` "+
|
||||
"WHERE `reviewed` = 0 "+
|
||||
"AND `"+config.DBprefix+"posts`.`deleted_timestamp` = ? "+
|
||||
"AND `boardid` = `"+config.DBprefix+"boards`.`id` "+
|
||||
"ORDER BY `timestamp` DESC LIMIT ?",
|
||||
nilTimestamp, limit,
|
||||
)
|
||||
defer closeRows(rows)
|
||||
if err != nil {
|
||||
html += "<tr><td>" + handleError(1, err.Error()) + "</td></tr></table>"
|
||||
return
|
||||
}
|
||||
defer closeRows(rows)
|
||||
|
||||
for rows.Next() {
|
||||
recentpost := new(RecentPost)
|
||||
|
@ -891,16 +905,16 @@ var manage_functions = map[string]ManageFunction{
|
|||
"staff": {
|
||||
Permissions: 3,
|
||||
Callback: func() (html string) {
|
||||
//do := request.FormValue("do")
|
||||
do := request.FormValue("do")
|
||||
html = "<h1>Staff</h1><br />\n" +
|
||||
"<table id=\"stafftable\" border=\"1\">\n" +
|
||||
"<tr><td><b>Username</b></td><td><b>Rank</b></td><td><b>Boards</b></td><td><b>Added on</b></td><td><b>Action</b></td></tr>\n"
|
||||
rows, err := db.Query("SELECT `username`,`rank`,`boards`,`added_on` FROM `" + config.DBprefix + "staff`")
|
||||
rows, err := querySQL("SELECT `username`,`rank`,`boards`,`added_on` FROM `" + config.DBprefix + "staff`")
|
||||
defer closeRows(rows)
|
||||
if err != nil {
|
||||
html += "<tr><td>" + handleError(1, err.Error()) + "</td></tr></table>"
|
||||
return
|
||||
}
|
||||
defer closeRows(rows)
|
||||
|
||||
iter := 1
|
||||
for rows.Next() {
|
||||
|
@ -910,28 +924,21 @@ var manage_functions = map[string]ManageFunction{
|
|||
return err.Error()
|
||||
}
|
||||
|
||||
if request.FormValue("do") == "add" {
|
||||
if do == "add" {
|
||||
newUsername := request.FormValue("username")
|
||||
newPassword := request.FormValue("password")
|
||||
newRank := request.FormValue("rank")
|
||||
stmt, err := db.Prepare("INSERT INTO `" + config.DBprefix + "staff` (`username`, `password_checksum`, `rank`) VALUES(?,?,?)")
|
||||
if err != nil {
|
||||
if _, err := execSQL("INSERT INTO `"+config.DBprefix+"staff` (`username`, `password_checksum`, `rank`) VALUES(?,?,?)",
|
||||
&newUsername, bcryptSum(newPassword), &newRank,
|
||||
); err != nil {
|
||||
serveErrorPage(writer, handleError(1, err.Error()))
|
||||
}
|
||||
defer closeStatement(stmt)
|
||||
|
||||
if _, err = stmt.Exec(&newUsername, bcryptSum(newPassword), &newRank); err != nil {
|
||||
} else if do == "del" && request.FormValue("username") != "" {
|
||||
if _, err = execSQL("DELETE FROM `"+config.DBprefix+"staff` WHERE `username` = ?",
|
||||
request.FormValue("username"),
|
||||
); err != nil {
|
||||
serveErrorPage(writer, handleError(1, err.Error()))
|
||||
}
|
||||
|
||||
} else if request.FormValue("do") == "del" && request.FormValue("username") != "" {
|
||||
stmt, err := db.Prepare("DELETE FROM `" + config.DBprefix + "staff` WHERE `username` = ?")
|
||||
if err != nil {
|
||||
serveErrorPage(writer, handleError(1, err.Error()))
|
||||
}
|
||||
defer closeStatement(stmt)
|
||||
|
||||
_, err = stmt.Exec(request.FormValue("username"))
|
||||
}
|
||||
|
||||
var rank string
|
||||
|
|
177
src/posting.go
177
src/posting.go
|
@ -115,22 +115,18 @@ func buildBoardPages(board *BoardsTable) (html string) {
|
|||
thread.IName = "thread"
|
||||
|
||||
// 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 {
|
||||
html += err.Error() + "<br />\n"
|
||||
}
|
||||
defer closeStatement(stmt)
|
||||
|
||||
if err = stmt.QueryRow(board.ID, op.ID, nilTimestamp).Scan(&thread.NumReplies); err != nil {
|
||||
if err = queryRowSQL("SELECT COUNT(*) FROM `"+config.DBprefix+"posts` WHERE `boardid` = ? AND `parentid` = ? AND `deleted_timestamp` = ?",
|
||||
[]interface{}{board.ID, op.ID, nilTimestamp},
|
||||
[]interface{}{&thread.NumReplies},
|
||||
); err != nil {
|
||||
html += err.Error() + "<br />\n"
|
||||
}
|
||||
|
||||
// Get the number of image replies in this thread
|
||||
stmt, err = db.Prepare("SELECT COUNT(*) FROM `" + config.DBprefix + "posts` WHERE `boardid` = ? AND `parentid` = ? AND `deleted_timestamp` = ? AND `filesize` <> 0")
|
||||
if err != nil {
|
||||
html += err.Error() + "<br />\n"
|
||||
}
|
||||
if err = stmt.QueryRow(board.ID, op.ID, nilTimestamp).Scan(&thread.NumImages); err != nil {
|
||||
if err = queryRowSQL("SELECT COUNT(*) FROM `"+config.DBprefix+"posts` WHERE `boardid` = ? AND `parentid` = ? AND `deleted_timestamp` = ? AND `filesize` <> 0",
|
||||
[]interface{}{board.ID, op.ID, nilTimestamp},
|
||||
[]interface{}{&thread.NumImages},
|
||||
); err != nil {
|
||||
html += err.Error() + "<br />\n"
|
||||
}
|
||||
|
||||
|
@ -225,11 +221,12 @@ func buildBoardPages(board *BoardsTable) (html string) {
|
|||
var pages_obj []BoardPageJSON
|
||||
|
||||
catalog_json_file, err := os.OpenFile(path.Join(config.DocumentRoot, board.Dir, "catalog.json"), os.O_CREATE|os.O_RDWR|os.O_TRUNC, 0600)
|
||||
defer closeFile(catalog_json_file)
|
||||
if err != nil {
|
||||
html += handleError(1, "Failed opening /"+board.Dir+"/catalog.json: "+err.Error())
|
||||
return
|
||||
}
|
||||
defer closeFile(catalog_json_file)
|
||||
|
||||
currentBoardPage := board.CurrentPage
|
||||
for _, page_threads := range thread_pages {
|
||||
board.CurrentPage++
|
||||
|
@ -237,11 +234,11 @@ func buildBoardPages(board *BoardsTable) (html 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, 0600)
|
||||
defer closeFile(current_page_file)
|
||||
if err != nil {
|
||||
html += handleError(1, "Failed opening board page: "+err.Error()) + "<br />"
|
||||
continue
|
||||
}
|
||||
defer closeFile(current_page_file)
|
||||
|
||||
// Render the boardpage template, don't forget config
|
||||
if err = img_boardpage_tmpl.Execute(current_page_file, map[string]interface{}{
|
||||
|
@ -396,11 +393,11 @@ func buildThreadPages(op *PostTable) (html string) {
|
|||
|
||||
// Put together the thread JSON
|
||||
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, 0600)
|
||||
defer closeFile(threadJSONFile)
|
||||
if err != nil {
|
||||
html += handleError(1, "Failed opening /%s/res/%d.json: %s", board.Dir, op.ID, err.Error())
|
||||
return
|
||||
}
|
||||
defer closeFile(threadJSONFile)
|
||||
|
||||
// Create the wrapper object
|
||||
thread_json_wrapper := new(ThreadJSONWrapper)
|
||||
|
@ -464,17 +461,17 @@ func buildFrontPage() (html string) {
|
|||
|
||||
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, 0600)
|
||||
defer closeFile(front_file)
|
||||
if err != nil {
|
||||
return handleError(1, "Failed opening front page for writing: "+err.Error()) + "<br />\n"
|
||||
}
|
||||
defer closeFile(front_file)
|
||||
|
||||
// get front pages
|
||||
rows, err := db.Query("SELECT * FROM `" + config.DBprefix + "frontpage`")
|
||||
rows, err := querySQL("SELECT * FROM `" + config.DBprefix + "frontpage`")
|
||||
defer closeRows(rows)
|
||||
if err != nil {
|
||||
return handleError(1, "Failed getting front page rows: "+err.Error())
|
||||
}
|
||||
defer closeRows(rows)
|
||||
|
||||
for rows.Next() {
|
||||
frontpage := new(FrontTable)
|
||||
|
@ -487,25 +484,23 @@ func buildFrontPage() (html string) {
|
|||
}
|
||||
|
||||
// get recent posts
|
||||
stmt, err := db.Prepare(
|
||||
"SELECT `" + config.DBprefix + "posts`.`id`, " +
|
||||
"`" + config.DBprefix + "posts`.`parentid`, " +
|
||||
"`" + config.DBprefix + "boards`.`dir` AS boardname, " +
|
||||
"`" + config.DBprefix + "posts`.`boardid` AS boardid, " +
|
||||
"`name`, `tripcode`, `message`, `filename`, `thumb_w`, `thumb_h` " +
|
||||
"FROM `" + config.DBprefix + "posts`, `" + config.DBprefix + "boards` " +
|
||||
"WHERE `" + config.DBprefix + "posts`.`deleted_timestamp` = ? " +
|
||||
"AND `boardid` = `" + config.DBprefix + "boards`.`id` " +
|
||||
"ORDER BY `timestamp` DESC LIMIT ?")
|
||||
rows, err = querySQL(
|
||||
"SELECT `"+config.DBprefix+"posts`.`id`, "+
|
||||
"`"+config.DBprefix+"posts`.`parentid`, "+
|
||||
"`"+config.DBprefix+"boards`.`dir` AS boardname, "+
|
||||
"`"+config.DBprefix+"posts`.`boardid` AS boardid, "+
|
||||
"`name`, `tripcode`, `message`, `filename`, `thumb_w`, `thumb_h` "+
|
||||
"FROM `"+config.DBprefix+"posts`, `"+config.DBprefix+"boards` "+
|
||||
"WHERE `"+config.DBprefix+"posts`.`deleted_timestamp` = ? "+
|
||||
"AND `boardid` = `"+config.DBprefix+"boards`.`id` "+
|
||||
"ORDER BY `timestamp` DESC LIMIT ?",
|
||||
nilTimestamp, config.MaxRecentPosts,
|
||||
)
|
||||
defer closeRows(rows)
|
||||
if err != nil {
|
||||
return handleError(1, err.Error())
|
||||
}
|
||||
defer closeStatement(stmt)
|
||||
|
||||
rows, err = stmt.Query(nilTimestamp, config.MaxRecentPosts)
|
||||
if err != nil {
|
||||
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)
|
||||
|
@ -529,10 +524,10 @@ func buildFrontPage() (html string) {
|
|||
|
||||
func buildBoardListJSON() (html string) {
|
||||
board_list_file, err := os.OpenFile(path.Join(config.DocumentRoot, "boards.json"), os.O_CREATE|os.O_RDWR|os.O_TRUNC, 0600)
|
||||
defer closeFile(board_list_file)
|
||||
if err != nil {
|
||||
return handleError(1, "Failed opening board.json for writing: "+err.Error()) + "<br />\n"
|
||||
}
|
||||
defer closeFile(board_list_file)
|
||||
|
||||
board_list_wrapper := new(BoardJSONWrapper)
|
||||
|
||||
|
@ -563,13 +558,10 @@ func buildBoardListJSON() (html string) {
|
|||
|
||||
// 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 {
|
||||
return err
|
||||
}
|
||||
defer closeStatement(stmt)
|
||||
_, err := execSQL("UPDATE `"+config.DBprefix+"posts` SET `bumped` = ? WHERE `id` = ? AND `boardid` = ?",
|
||||
[]interface{}{time.Now(), postID, boardID},
|
||||
)
|
||||
|
||||
_, err = stmt.Exec(time.Now(), postID, boardID)
|
||||
return err
|
||||
}
|
||||
|
||||
|
@ -581,51 +573,44 @@ func checkBannedStatus(post *PostTable, writer *http.ResponseWriter) ([]interfac
|
|||
var interfaces []interface{}
|
||||
// var count int
|
||||
// var search string
|
||||
stmt, err := db.Prepare("SELECT `ip`, `name`, `tripcode`, `message`, `boards`, `timestamp`, `expires`, `appeal_at` FROM `" + config.DBprefix + "banlist` WHERE `ip` = ?")
|
||||
if err != nil {
|
||||
err := queryRowSQL("SELECT `ip`, `name`, `tripcode`, `message`, `boards`, `timestamp`, `expires`, `appeal_at` FROM `"+config.DBprefix+"banlist` WHERE `ip` = ?",
|
||||
[]interface{}{&post.IP},
|
||||
[]interface{}{&ban_entry.IP, &ban_entry.Name, &ban_entry.Tripcode, &ban_entry.Message, &ban_entry.Boards, &ban_entry.Timestamp, &ban_entry.Expires, &ban_entry.AppealAt},
|
||||
)
|
||||
if err == sql.ErrNoRows {
|
||||
// the user isn't banned
|
||||
// We don't need to return err because it isn't necessary
|
||||
return interfaces, nil
|
||||
} else if err != nil {
|
||||
handleError(1, "Error checking banned status: "+err.Error())
|
||||
return interfaces, err
|
||||
}
|
||||
defer closeStatement(stmt)
|
||||
|
||||
err = stmt.QueryRow(&post.IP).Scan(&ban_entry.IP, &ban_entry.Name, &ban_entry.Tripcode, &ban_entry.Message, &ban_entry.Boards, &ban_entry.Timestamp, &ban_entry.Expires, &ban_entry.AppealAt)
|
||||
if err != nil {
|
||||
if err == sql.ErrNoRows {
|
||||
// the user isn't banned
|
||||
// We don't need to return err because it isn't necessary
|
||||
return interfaces, nil
|
||||
}
|
||||
return interfaces, err // something went wrong
|
||||
} else {
|
||||
isExpired = ban_entry.Expires.After(time.Now()) == false
|
||||
if isExpired {
|
||||
// if it is expired, send a message saying that it's expired, but still post
|
||||
println(1, "expired")
|
||||
return interfaces, nil
|
||||
}
|
||||
// the user's IP is in the banlist. Check if the ban has expired
|
||||
if getSpecificSQLDateTime(ban_entry.Expires) == "0001-01-01 00:00:00" || ban_entry.Expires.After(time.Now()) {
|
||||
// for some funky reason, Go's MySQL driver seems to not like getting a supposedly nil timestamp as an ACTUAL nil timestamp
|
||||
// so we're just going to wing it and cheat. Of course if they change that, we're kind of hosed.
|
||||
|
||||
var interfaces []interface{}
|
||||
interfaces = append(interfaces, config)
|
||||
interfaces = append(interfaces, ban_entry)
|
||||
return interfaces, nil
|
||||
}
|
||||
isExpired = ban_entry.Expires.After(time.Now()) == false
|
||||
if isExpired {
|
||||
// if it is expired, send a message saying that it's expired, but still post
|
||||
println(1, "expired")
|
||||
return interfaces, nil
|
||||
}
|
||||
// the user's IP is in the banlist. Check if the ban has expired
|
||||
if getSpecificSQLDateTime(ban_entry.Expires) == "0001-01-01 00:00:00" || ban_entry.Expires.After(time.Now()) {
|
||||
// for some funky reason, Go's MySQL driver seems to not like getting a supposedly nil timestamp as an ACTUAL nil timestamp
|
||||
// so we're just going to wing it and cheat. Of course if they change that, we're kind of hosed.
|
||||
|
||||
return []interface{}{config, ban_entry}, nil
|
||||
}
|
||||
return interfaces, nil
|
||||
}
|
||||
|
||||
func sinceLastPost(post *PostTable) int {
|
||||
var lastPostTime time.Time
|
||||
if err := db.QueryRow("SELECT `timestamp` FROM `" + config.DBprefix + "posts` WHERE `ip` = '" + post.IP + "' ORDER BY `timestamp` DESC LIMIT 1").Scan(&lastPostTime); err == sql.ErrNoRows {
|
||||
if err := queryRowSQL("SELECT `timestamp` FROM `"+config.DBprefix+"posts` WHERE `ip` = '?' ORDER BY `timestamp` DESC LIMIT 1",
|
||||
[]interface{}{post.IP},
|
||||
[]interface{}{&lastPostTime},
|
||||
); err == sql.ErrNoRows {
|
||||
// no posts by that IP.
|
||||
return -1
|
||||
} else {
|
||||
return int(time.Since(lastPostTime).Seconds())
|
||||
}
|
||||
return -1
|
||||
return int(time.Since(lastPostTime).Seconds())
|
||||
}
|
||||
|
||||
func createImageThumbnail(image_obj image.Image, size string) image.Image {
|
||||
|
@ -738,13 +723,7 @@ func insertPost(post PostTable, bump bool) (sql.Result, error) {
|
|||
}
|
||||
insertValues += " ? )"
|
||||
|
||||
stmt, err := db.Prepare(insertString + insertValues)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer closeStatement(stmt)
|
||||
|
||||
result, err = stmt.Exec(
|
||||
result, err := execSQL(insertString+insertValues,
|
||||
post.BoardID, post.ParentID, post.Name, post.Tripcode,
|
||||
post.Email, post.Subject, post.MessageHTML, post.MessageText,
|
||||
post.Password, post.Filename, post.FilenameOriginal,
|
||||
|
@ -753,6 +732,7 @@ func insertPost(post PostTable, bump bool) (sql.Result, error) {
|
|||
post.Autosage, post.PosterAuthority, post.DeletedTimestamp,
|
||||
post.Bumped, post.Stickied, post.Locked, post.Reviewed, post.Sillytag,
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
return result, err
|
||||
}
|
||||
|
@ -821,16 +801,11 @@ func makePost(w http.ResponseWriter, r *http.Request, data interface{}) {
|
|||
post.Subject = request.FormValue("postsubject")
|
||||
post.MessageText = strings.Trim(request.FormValue("postmsg"), "\r\n")
|
||||
|
||||
stmt, err := db.Prepare("SELECT `max_message_length` from `" + config.DBprefix + "boards` WHERE `id` = ?")
|
||||
if err != nil {
|
||||
serveErrorPage(w, "Error getting board info.")
|
||||
errorLog.Print("Error getting board info: " + err.Error())
|
||||
return
|
||||
}
|
||||
defer closeStatement(stmt)
|
||||
|
||||
if err = stmt.QueryRow(post.BoardID).Scan(&maxMessageLength); err != nil {
|
||||
serveErrorPage(w, handleError(1, "Requested board does not exist."))
|
||||
if err := queryRowSQL("SELECT `max_message_length` from `"+config.DBprefix+"boards` WHERE `id` = ?",
|
||||
[]interface{}{post.BoardID},
|
||||
[]interface{}{&maxMessageLength},
|
||||
); err != nil {
|
||||
serveErrorPage(w, handleError(0, "Error getting board info: "+err.Error()))
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -921,14 +896,10 @@ func makePost(w http.ResponseWriter, r *http.Request, data interface{}) {
|
|||
post.FileChecksum = fmt.Sprintf("%x", md5.Sum(data))
|
||||
|
||||
var allowsVids bool
|
||||
vidStmt, err := db.Prepare("SELECT `embeds_allowed` FROM `" + config.DBprefix + "boards` WHERE `id` = ? LIMIT 1")
|
||||
if err != nil {
|
||||
serveErrorPage(w, handleError(1, "Couldn't get board info: "+err.Error()))
|
||||
return
|
||||
}
|
||||
defer closeStatement(vidStmt)
|
||||
|
||||
if err = vidStmt.QueryRow(post.BoardID).Scan(&allowsVids); err != nil {
|
||||
if err = queryRowSQL("SELECT `embeds_allowed` FROM `"+config.DBprefix+"boards` WHERE `id` = ? LIMIT 1",
|
||||
[]interface{}{post.BoardID},
|
||||
[]interface{}{&allowsVids},
|
||||
); err != nil {
|
||||
serveErrorPage(w, handleError(1, "Couldn't get board info: "+err.Error()))
|
||||
return
|
||||
}
|
||||
|
@ -1142,9 +1113,13 @@ func formatMessage(message string) string {
|
|||
// the link is in fact, a valid int
|
||||
var boardDir string
|
||||
var linkParent int
|
||||
stmt, err := db.Prepare("SELECT `dir`,`parentid` FROM " + config.DBprefix + "posts," + config.DBprefix + "boards WHERE " + config.DBprefix + "posts.id = ?")
|
||||
handleError(1, customError(err))
|
||||
stmt.QueryRow(word[8:]).Scan(&boardDir, &linkParent)
|
||||
|
||||
if err = queryRowSQL("SELECT `dir`,`parentid` FROM "+config.DBprefix+"posts,"+config.DBprefix+"boards WHERE "+config.DBprefix+"posts.id = ?",
|
||||
[]interface{}{word[8:]},
|
||||
[]interface{}{&boardDir, &linkParent},
|
||||
); err != nil {
|
||||
handleError(1, customError(err))
|
||||
}
|
||||
|
||||
// get post board dir
|
||||
if boardDir == "" {
|
||||
|
|
|
@ -94,10 +94,9 @@ func serveNotFound(writer http.ResponseWriter, request *http.Request) {
|
|||
writer.WriteHeader(404)
|
||||
errorPage, err := ioutil.ReadFile(config.DocumentRoot + "/error/404.html")
|
||||
if err != nil {
|
||||
writer.Write([]byte("Requested page not found, and 404 error page not found"))
|
||||
_, _ = writer.Write([]byte("Requested page not found, and 404 error page not found"))
|
||||
} else {
|
||||
writer.Write(errorPage)
|
||||
|
||||
_, _ = writer.Write(errorPage)
|
||||
}
|
||||
errorLog.Print("Error: 404 Not Found from " + getRealIP(request) + " @ " + request.RequestURI)
|
||||
}
|
||||
|
@ -121,7 +120,7 @@ func (s GochanServer) ServeHTTP(writer http.ResponseWriter, request *http.Reques
|
|||
serveNotFound(writer, request)
|
||||
return
|
||||
}
|
||||
writer.Write(fb)
|
||||
_, _ = writer.Write(fb)
|
||||
}
|
||||
|
||||
func initServer() {
|
||||
|
@ -143,7 +142,7 @@ func initServer() {
|
|||
|
||||
testfunc := func(writer http.ResponseWriter, response *http.Request, data interface{}) {
|
||||
if writer != nil {
|
||||
writer.Write([]byte("hahahaha"))
|
||||
_, _ = writer.Write([]byte("hahahaha"))
|
||||
}
|
||||
}
|
||||
server.AddNamespace("example", testfunc)
|
||||
|
@ -254,29 +253,29 @@ func utilHandler(writer http.ResponseWriter, request *http.Request, data interfa
|
|||
var fileType string
|
||||
var thumbType string
|
||||
var post PostTable
|
||||
var err error
|
||||
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 `boardid` = ? AND `deleted_timestamp` = ?")
|
||||
if err != nil {
|
||||
serveErrorPage(writer, handleError(1, err.Error()+"\n"))
|
||||
}
|
||||
defer closeStatement(stmt)
|
||||
|
||||
err = stmt.QueryRow(&post.ID, &post.BoardID, nilTimestamp).Scan(&post.ParentID, &post.Filename, &post.Password)
|
||||
if err == sql.ErrNoRows {
|
||||
if err = queryRowSQL(
|
||||
"SELECT `parentid`, `filename`, `password` FROM `"+config.DBprefix+"posts` WHERE `id` = ? AND `boardid` = ? AND `deleted_timestamp` = ?",
|
||||
[]interface{}{&post.ID, &post.BoardID, nilTimestamp},
|
||||
[]interface{}{&post.ParentID, &post.Filename, &post.Password},
|
||||
); err == sql.ErrNoRows {
|
||||
//the post has already been deleted
|
||||
writer.Header().Add("refresh", "4;url="+request.Referer())
|
||||
fmt.Fprintf(writer, "%d has already been deleted or is a post in a deleted thread.\n<br />", post.ID)
|
||||
continue
|
||||
}
|
||||
if err != nil {
|
||||
serveErrorPage(writer, err.Error())
|
||||
} else if err != nil {
|
||||
serveErrorPage(writer, handleError(1, err.Error()+"\n"))
|
||||
return
|
||||
}
|
||||
|
||||
err = db.QueryRow("SELECT `id` FROM `" + config.DBprefix + "boards` WHERE `dir` = '" + board + "'").Scan(&post.BoardID)
|
||||
if err != nil {
|
||||
if err = queryRowSQL(
|
||||
"SELECT `id` FROM `"+config.DBprefix+"boards` WHERE `dir` = ?",
|
||||
[]interface{}{board},
|
||||
[]interface{}{&post.BoardID},
|
||||
); err != nil {
|
||||
serveErrorPage(writer, err.Error())
|
||||
return
|
||||
}
|
||||
|
@ -299,8 +298,10 @@ func utilHandler(writer http.ResponseWriter, request *http.Request, data interfa
|
|||
os.Remove(path.Join(config.DocumentRoot, board, "/thumb/"+fileName+"t."+thumbType))
|
||||
os.Remove(path.Join(config.DocumentRoot, board, "/thumb/"+fileName+"c."+thumbType))
|
||||
|
||||
_, err = db.Exec("UPDATE `" + config.DBprefix + "posts` SET `filename` = 'deleted' WHERE `id` = " + strconv.Itoa(post.ID) + " AND `boardid` = " + strconv.Itoa(post.BoardID))
|
||||
if err != nil {
|
||||
if _, err = execSQL(
|
||||
"UPDATE `"+config.DBprefix+"posts` SET `filename` = 'deleted' WHERE `id` = ? AND `boardid` = ?",
|
||||
post.ID, post.BoardID,
|
||||
); err != nil {
|
||||
serveErrorPage(writer, err.Error())
|
||||
return
|
||||
}
|
||||
|
@ -309,14 +310,18 @@ func utilHandler(writer http.ResponseWriter, request *http.Request, data interfa
|
|||
buildBoardPages(&_board[0])
|
||||
_post, _ := getPostArr(map[string]interface{}{"id": post.ID, "boardid": post.BoardID}, "")
|
||||
postBoard := _post[0]
|
||||
// postBoard := _post[0].(PostTable)
|
||||
buildThreadPages(&postBoard)
|
||||
|
||||
writer.Header().Add("refresh", "4;url="+request.Referer())
|
||||
fmt.Fprintf(writer, "Attached image from %d deleted successfully<br />\n<meta http-equiv=\"refresh\" content=\"1;url=/"+board+"/\">", post.ID)
|
||||
} else {
|
||||
// delete the post
|
||||
_, err = db.Exec("UPDATE `" + config.DBprefix + "posts` SET `deleted_timestamp` = '" + getSQLDateTime() + "' WHERE `id` = " + strconv.Itoa(post.ID))
|
||||
if _, err = execSQL(
|
||||
"UPDATE `"+config.DBprefix+"posts` SET `deleted_timestamp` = ? WHERE `id` = ?",
|
||||
getSQLDateTime(), post.ID,
|
||||
); err != nil {
|
||||
serveErrorPage(writer, err.Error())
|
||||
}
|
||||
if post.ParentID == 0 {
|
||||
os.Remove(path.Join(config.DocumentRoot, board, "/res/"+strconv.Itoa(post.ID)+".html"))
|
||||
} else {
|
||||
|
@ -325,23 +330,30 @@ func utilHandler(writer http.ResponseWriter, request *http.Request, data interfa
|
|||
}
|
||||
|
||||
// if the deleted post is actually a thread, delete its posts
|
||||
_, err = db.Exec("UPDATE `" + config.DBprefix + "posts` SET `deleted_timestamp` = '" + getSQLDateTime() + "' WHERE `parentID` = " + strconv.Itoa(post.ID))
|
||||
if err != nil {
|
||||
if _, err = execSQL("UPDATE `"+config.DBprefix+"posts` SET `deleted_timestamp` = ? WHERE `parentID` = ?",
|
||||
getSQLDateTime(), post.ID,
|
||||
); err != nil {
|
||||
serveErrorPage(writer, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
// delete the file
|
||||
var deletedFilename string
|
||||
err = db.QueryRow("SELECT `filename` FROM `" + config.DBprefix + "posts` WHERE `id` = " + strconv.Itoa(post.ID) + " AND `filename` != ''").Scan(&deletedFilename)
|
||||
if err == nil {
|
||||
if err = queryRowSQL(
|
||||
"SELECT `filename` FROM `"+config.DBprefix+"posts` WHERE `id` = ? AND `filename` != ''",
|
||||
[]interface{}{post.ID},
|
||||
[]interface{}{&deletedFilename},
|
||||
); err == nil {
|
||||
os.Remove(path.Join(config.DocumentRoot, board, "/src/", deletedFilename))
|
||||
os.Remove(path.Join(config.DocumentRoot, board, "/thumb/", strings.Replace(deletedFilename, ".", "t.", -1)))
|
||||
os.Remove(path.Join(config.DocumentRoot, board, "/thumb/", strings.Replace(deletedFilename, ".", "c.", -1)))
|
||||
}
|
||||
|
||||
err = db.QueryRow("SELECT `filename` FROM `" + config.DBprefix + "posts` WHERE `parentID` = " + strconv.Itoa(post.ID) + " AND `filename` != ''").Scan(&deletedFilename)
|
||||
if err == nil {
|
||||
if err = queryRowSQL(
|
||||
"SELECT `filename` FROM `"+config.DBprefix+"posts` WHERE `parentID` = ? AND `filename` != ''",
|
||||
[]interface{}{post.ID},
|
||||
[]interface{}{&deletedFilename},
|
||||
); err == nil {
|
||||
os.Remove(path.Join(config.DocumentRoot, board, "/src/", deletedFilename))
|
||||
os.Remove(path.Join(config.DocumentRoot, board, "/thumb/", strings.Replace(deletedFilename, ".", "t.", -1)))
|
||||
os.Remove(path.Join(config.DocumentRoot, board, "/thumb/", strings.Replace(deletedFilename, ".", "c.", -1)))
|
||||
|
|
66
src/sql.go
66
src/sql.go
|
@ -17,8 +17,7 @@ const (
|
|||
)
|
||||
|
||||
var (
|
||||
db *sql.DB
|
||||
dbConnected = false
|
||||
db *sql.DB
|
||||
)
|
||||
|
||||
func connectToSQLServer() {
|
||||
|
@ -31,45 +30,25 @@ func connectToSQLServer() {
|
|||
os.Exit(2)
|
||||
}
|
||||
|
||||
// get the number of tables in the database. If the number > 1, we can assume that initial setup has already been run
|
||||
var numRows int
|
||||
err = db.QueryRow("SELECT COUNT(*) FROM information_schema.tables WHERE table_schema = ?", config.DBname).Scan(&numRows)
|
||||
if err == sql.ErrNoRows {
|
||||
numRows = 0
|
||||
} else if err != nil {
|
||||
printf(0, "Failed retrieving list of tables in database: ")
|
||||
handleError(0, customError(err))
|
||||
os.Exit(2)
|
||||
}
|
||||
// Detect that there are at least the number of tables that we are setting up.
|
||||
// If there are fewer than that, then we either half-way set up, or there's other tables in our database.
|
||||
if numRows >= 16 {
|
||||
// the initial setup has already been run
|
||||
needsInitialSetup = false
|
||||
dbConnected = true
|
||||
println(0, "complete.")
|
||||
return
|
||||
}
|
||||
|
||||
// check if initialsetupdb.sql still exists
|
||||
if _, err = os.Stat("initialsetupdb.sql"); err != nil {
|
||||
println(0, "Initial setup file (initialsetupdb.sql) missing. Please reinstall gochan")
|
||||
errorLog.Fatal("Initial setup file (initialsetupdb.sql) missing. Please reinstall gochan")
|
||||
handleError(0, "Initial setup file (initialsetupdb.sql) missing. Please reinstall gochan")
|
||||
os.Exit(2)
|
||||
}
|
||||
|
||||
// read the initial setup sql file into a string
|
||||
initialSQLBytes, err := ioutil.ReadFile("initialsetupdb.sql")
|
||||
if err != nil {
|
||||
printf(0, "failed: ")
|
||||
print(0, "failed: ")
|
||||
handleError(0, customError(err))
|
||||
os.Exit(2)
|
||||
}
|
||||
initialSQLStr := string(initialSQLBytes)
|
||||
|
||||
printf(0, "Starting initial setup...")
|
||||
initialSQLStr = strings.Replace(initialSQLStr, "DBNAME", config.DBname, -1)
|
||||
initialSQLStr = strings.Replace(initialSQLStr, "DBPREFIX", config.DBprefix, -1)
|
||||
initialSQLStr += "\nINSERT INTO `" + config.DBname + "`.`" + config.DBprefix + "staff` (`username`, `password_checksum`, `salt`, `rank`) VALUES ('admin', '" + bcryptSum("password") + "', 'abc', 3);"
|
||||
print(0, "Starting initial setup...")
|
||||
initialSQLStr += "\nINSERT INTO `DBNAME`.`DBPREFIXstaff` (`username`, `password_checksum`, `salt`, `rank`) VALUES ('admin', '" + bcryptSum("password") + "', 'abc', 3);"
|
||||
initialSQLStr = strings.NewReplacer("DBNAME", config.DBname, "DBPREFIX", config.DBprefix).Replace(initialSQLStr)
|
||||
|
||||
initialSQLArr := strings.Split(initialSQLStr, ";")
|
||||
|
||||
for _, statement := range initialSQLArr {
|
||||
|
@ -82,9 +61,34 @@ func connectToSQLServer() {
|
|||
}
|
||||
}
|
||||
println(0, "complete.")
|
||||
needsInitialSetup = false
|
||||
dbConnected = true
|
||||
}
|
||||
|
||||
func execSQL(query string, values ...interface{}) (sql.Result, error) {
|
||||
stmt, err := db.Prepare(query)
|
||||
defer closeStatement(stmt)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return stmt.Exec(values...)
|
||||
}
|
||||
|
||||
func queryRowSQL(query string, values []interface{}, out []interface{}) error {
|
||||
stmt, err := db.Prepare(query)
|
||||
defer closeStatement(stmt)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = stmt.QueryRow(values...).Scan(out...)
|
||||
return err
|
||||
}
|
||||
|
||||
func querySQL(query string, a ...interface{}) (*sql.Rows, error) {
|
||||
stmt, err := db.Prepare(query)
|
||||
defer closeStatement(stmt)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return stmt.Query(a...)
|
||||
}
|
||||
|
||||
func getSQLDateTime() string {
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"html"
|
||||
"net/http"
|
||||
|
@ -189,7 +190,7 @@ var (
|
|||
|
||||
func loadTemplate(files ...string) (*template.Template, error) {
|
||||
if len(files) == 0 {
|
||||
return nil, fmt.Errorf("ERROR: no files named in call to loadTemplate")
|
||||
return nil, errors.New("ERROR: no files named in call to loadTemplate")
|
||||
}
|
||||
var templates []string
|
||||
for i, file := range files {
|
||||
|
@ -200,7 +201,7 @@ func loadTemplate(files ...string) (*template.Template, error) {
|
|||
}
|
||||
|
||||
func templateError(name string, err error) error {
|
||||
return fmt.Errorf("Failed loading template \"" + config.TemplateDir + "/" + name + ": \"" + err.Error())
|
||||
return errors.New("Failed loading template \"" + config.TemplateDir + "/" + name + ": \"" + err.Error())
|
||||
}
|
||||
|
||||
func initTemplates() error {
|
||||
|
@ -260,7 +261,7 @@ func getStyleLinks(w http.ResponseWriter, stylesheet string) {
|
|||
}
|
||||
|
||||
if err := manage_header_tmpl.Execute(w, config); err != nil {
|
||||
println(0, err.Error())
|
||||
handleError(0, customError(err))
|
||||
os.Exit(2)
|
||||
}
|
||||
}
|
||||
|
|
13
src/types.go
13
src/types.go
|
@ -12,13 +12,12 @@ import (
|
|||
)
|
||||
|
||||
var (
|
||||
needsInitialSetup = true
|
||||
config GochanConfig
|
||||
accessLog *log.Logger
|
||||
errorLog *log.Logger
|
||||
modLog *log.Logger
|
||||
readBannedIPs []string
|
||||
bbcompiler bbcode.Compiler
|
||||
config GochanConfig
|
||||
accessLog *log.Logger
|
||||
errorLog *log.Logger
|
||||
modLog *log.Logger
|
||||
readBannedIPs []string
|
||||
bbcompiler bbcode.Compiler
|
||||
)
|
||||
|
||||
type RecentPost struct {
|
||||
|
|
80
src/util.go
80
src/util.go
|
@ -185,14 +185,13 @@ func getBoardArr(parameterList map[string]interface{}, extra string) (boards []B
|
|||
queryString += fmt.Sprintf(" %s ORDER BY `order`", extra)
|
||||
printf(2, "queryString@getBoardArr: %s\n", queryString)
|
||||
|
||||
stmt, err := db.Prepare(queryString)
|
||||
rows, err := querySQL(queryString, parameterValues...)
|
||||
defer closeRows(rows)
|
||||
if err != nil {
|
||||
errorLog.Print(err.Error())
|
||||
handleError(0, "error getting board list: %s", customError(err))
|
||||
return
|
||||
}
|
||||
defer closeStatement(stmt)
|
||||
|
||||
rows, err := stmt.Query(parameterValues...)
|
||||
// For each row in the results from the database, populate a new BoardsTable instance,
|
||||
// then append it to the boards array we are going to return
|
||||
for rows.Next() {
|
||||
|
@ -225,8 +224,7 @@ func getBoardArr(parameterList map[string]interface{}, extra string) (boards []B
|
|||
&board.RequireFile,
|
||||
&board.EnableCatalog,
|
||||
); err != nil {
|
||||
errorLog.Print(err.Error())
|
||||
println(0, err.Error())
|
||||
handleError(0, customError(err))
|
||||
return
|
||||
}
|
||||
boards = append(boards, *board)
|
||||
|
@ -236,27 +234,24 @@ func getBoardArr(parameterList map[string]interface{}, extra string) (boards []B
|
|||
|
||||
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)
|
||||
err := queryRowSQL(
|
||||
"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` = ?",
|
||||
[]interface{}{id},
|
||||
[]interface{}{
|
||||
&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,
|
||||
},
|
||||
)
|
||||
|
||||
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
|
||||
return board, err
|
||||
}
|
||||
|
||||
// if parameterList is nil, ignore it and treat extra like a whole SQL query
|
||||
|
@ -281,19 +276,12 @@ func getPostArr(parameterList map[string]interface{}, extra string) (posts []Pos
|
|||
queryString += " " + extra // " ORDER BY `order`"
|
||||
printf(2, "queryString@getPostArr queryString: %s\n", queryString)
|
||||
|
||||
stmt, err := db.Prepare(queryString)
|
||||
if err != nil {
|
||||
errorLog.Print(err.Error())
|
||||
return
|
||||
}
|
||||
defer closeStatement(stmt)
|
||||
|
||||
rows, err := stmt.Query(parameterValues...)
|
||||
if err != nil {
|
||||
handleError(1, "Error in getPostArr: "+err.Error())
|
||||
return
|
||||
}
|
||||
rows, err := querySQL(queryString, parameterValues...)
|
||||
defer closeRows(rows)
|
||||
if err != nil {
|
||||
handleError(1, customError(err))
|
||||
return
|
||||
}
|
||||
|
||||
// For each row in the results from the database, populate a new PostTable instance,
|
||||
// then append it to the posts array we are going to return
|
||||
|
@ -307,8 +295,7 @@ func getPostArr(parameterList map[string]interface{}, extra string) (posts []Pos
|
|||
&post.Autosage, &post.PosterAuthority, &post.DeletedTimestamp, &post.Bumped,
|
||||
&post.Stickied, &post.Locked, &post.Reviewed, &post.Sillytag,
|
||||
); err != nil {
|
||||
errorLog.Print(err.Error())
|
||||
println(0, err.Error())
|
||||
handleError(0, customError(err))
|
||||
return
|
||||
}
|
||||
posts = append(posts, post)
|
||||
|
@ -316,23 +303,24 @@ func getPostArr(parameterList map[string]interface{}, extra string) (posts []Pos
|
|||
return
|
||||
}
|
||||
|
||||
// TODO: replace where with a map[string]interface{} like getBoardsArr()
|
||||
func getSectionArr(where string) (sections []interface{}, err error) {
|
||||
if where == "" {
|
||||
where = "1"
|
||||
}
|
||||
rows, err := db.Query("SELECT * FROM `" + config.DBprefix + "sections` WHERE " + where + " ORDER BY `order`;")
|
||||
rows, err := querySQL("SELECT * FROM `" + config.DBprefix + "sections` WHERE " + where + " ORDER BY `order`")
|
||||
defer closeRows(rows)
|
||||
if err != nil {
|
||||
errorLog.Print(err.Error())
|
||||
return
|
||||
}
|
||||
defer closeRows(rows)
|
||||
|
||||
for rows.Next() {
|
||||
section := new(BoardSectionsTable)
|
||||
section.IName = "section"
|
||||
|
||||
if err = rows.Scan(§ion.ID, §ion.Order, §ion.Hidden, §ion.Name, §ion.Abbreviation); err != nil {
|
||||
errorLog.Print(err.Error())
|
||||
handleError(1, customError(err))
|
||||
return
|
||||
}
|
||||
sections = append(sections, section)
|
||||
|
@ -370,7 +358,7 @@ func generateSalt() string {
|
|||
}
|
||||
|
||||
func getFileExtension(filename string) (extension string) {
|
||||
if strings.Index(filename, ".") == -1 {
|
||||
if !strings.Contains(filename, ".") {
|
||||
extension = ""
|
||||
} else {
|
||||
extension = filename[strings.LastIndex(filename, ".")+1:]
|
||||
|
@ -544,7 +532,11 @@ func checkPostForSpam(userIP string, userAgent string, referrer string,
|
|||
handleError(1, err.Error())
|
||||
return "other_failure"
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
defer func() {
|
||||
if resp != nil && resp.Body != nil {
|
||||
resp.Body.Close()
|
||||
}
|
||||
}()
|
||||
body, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
handleError(1, err.Error())
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<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 />
|
||||
<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>
|
||||
|
|
|
@ -13,7 +13,7 @@
|
|||
{{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" alt="" /></a>
|
||||
{{else}}
|
||||
<div class="file-deleted-box" style="text-align:center;">File removed</div>
|
||||
{{end}}
|
||||
|
@ -32,7 +32,7 @@
|
|||
{{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" alt=""/></a>
|
||||
{{else}}
|
||||
<div class="file-deleted-box" style="text-align:center;">File removed</div>
|
||||
{{end}}{{end}}
|
||||
|
|
|
@ -10,7 +10,6 @@
|
|||
<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}}];
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue