From bf2af0ae9658efccd4612f85d874cd03f8b1d646 Mon Sep 17 00:00:00 2001 From: Azareal Date: Thu, 27 Dec 2018 19:12:30 +1000 Subject: [PATCH] Laid the foundations for better reply attachments. The attachment manager introduced in the previous patch is now properly localised. Repurposed AttachmentStore.CountInTopic as a more general CountIn method. Added an & entity in attachment URLs so that the characters don't get mutated into something weird. Tried to make the linebreaks a little glitchy in the inline editor, we have a better solution in a mind soon! Fixed a bug where replies used .ContentHTML instead of .Content which led to a lot of HTML getting in the way of reply edits. Fixed a bug where reply attachments used the topicID rather than the replyID for their originID entries. Fixed a bug where the topic attachment counts weren't getting incremented. Added the topic.select_button_test, topic.copy_button_test and topic.upload_button_test phrases. Added the attachCount column to the replies table. This commit requires you to run the patcher / updater. --- cmd/query_gen/tables.go | 1 + common/attachments.go | 34 ++++++++++++++-------------- common/parser.go | 2 +- langs/english.json | 4 ++++ patcher/patches.go | 41 ++++++++++++++++++++++++++++++++-- public/global.js | 25 +++++++++++++-------- routes/reply.go | 37 ++++++++++++++++++++++-------- routes/topic.go | 10 +++++++-- schema/mssql/query_replies.sql | 1 + schema/mysql/query_replies.sql | 1 + schema/pgsql/query_replies.sql | 1 + templates/topic_alt.html | 10 ++++----- templates/topic_alt_posts.html | 2 +- themes/nox/public/main.css | 22 ++++++++++++++---- 14 files changed, 141 insertions(+), 50 deletions(-) diff --git a/cmd/query_gen/tables.go b/cmd/query_gen/tables.go index ff17b372..123f4472 100644 --- a/cmd/query_gen/tables.go +++ b/cmd/query_gen/tables.go @@ -247,6 +247,7 @@ func createTables(adapter qgen.Adapter) error { tblColumn{"lastUpdated", "datetime", 0, false, false, ""}, tblColumn{"ipaddress", "varchar", 200, false, false, "0.0.0.0.0"}, tblColumn{"likeCount", "int", 0, false, false, "0"}, + tblColumn{"attachCount", "int", 0, false, false, "0"}, tblColumn{"words", "int", 0, false, false, "1"}, // ? - replies has a default of 1 and topics has 0? why? tblColumn{"actionType", "varchar", 20, false, false, "''"}, tblColumn{"poll", "int", 0, false, false, "0"}, diff --git a/common/attachments.go b/common/attachments.go index 35574e8d..1c9a63da 100644 --- a/common/attachments.go +++ b/common/attachments.go @@ -26,31 +26,31 @@ type AttachmentStore interface { MiniTopicGet(id int) (alist []*MiniAttachment, err error) Add(sectionID int, sectionTable string, originID int, originTable string, uploadedBy int, path string) (int, error) GlobalCount() int - CountInTopic(tid int) int + CountIn(originTable string, oid int) int CountInPath(path string) int Delete(aid int) error } type DefaultAttachmentStore struct { - get *sql.Stmt - getByTopic *sql.Stmt - add *sql.Stmt - count *sql.Stmt - countInTopic *sql.Stmt - countInPath *sql.Stmt - delete *sql.Stmt + get *sql.Stmt + getByTopic *sql.Stmt + add *sql.Stmt + count *sql.Stmt + countIn *sql.Stmt + countInPath *sql.Stmt + delete *sql.Stmt } func NewDefaultAttachmentStore() (*DefaultAttachmentStore, error) { acc := qgen.NewAcc() return &DefaultAttachmentStore{ - get: acc.Select("attachments").Columns("originID, sectionID, uploadedBy, path").Where("attachID = ?").Prepare(), - getByTopic: acc.Select("attachments").Columns("attachID, sectionID, uploadedBy, path").Where("originTable = 'topics' AND originID = ?").Prepare(), - add: acc.Insert("attachments").Columns("sectionID, sectionTable, originID, originTable, uploadedBy, path").Fields("?,?,?,?,?,?").Prepare(), - count: acc.Count("attachments").Prepare(), - countInTopic: acc.Count("attachments").Where("originTable = 'topics' and originID = ?").Prepare(), - countInPath: acc.Count("attachments").Where("path = ?").Prepare(), - delete: acc.Delete("attachments").Where("attachID = ?").Prepare(), + get: acc.Select("attachments").Columns("originID, sectionID, uploadedBy, path").Where("attachID = ?").Prepare(), + getByTopic: acc.Select("attachments").Columns("attachID, sectionID, uploadedBy, path").Where("originTable = 'topics' AND originID = ?").Prepare(), + add: acc.Insert("attachments").Columns("sectionID, sectionTable, originID, originTable, uploadedBy, path").Fields("?,?,?,?,?,?").Prepare(), + count: acc.Count("attachments").Prepare(), + countIn: acc.Count("attachments").Where("originTable = ? and originID = ?").Prepare(), + countInPath: acc.Count("attachments").Where("path = ?").Prepare(), + delete: acc.Delete("attachments").Where("attachID = ?").Prepare(), }, acc.FirstError() } @@ -107,8 +107,8 @@ func (store *DefaultAttachmentStore) GlobalCount() (count int) { return count } -func (store *DefaultAttachmentStore) CountInTopic(tid int) (count int) { - err := store.countInTopic.QueryRow(tid).Scan(&count) +func (store *DefaultAttachmentStore) CountIn(originTable string, oid int) (count int) { + err := store.countIn.QueryRow(originTable, oid).Scan(&count) if err != nil { LogError(err) } diff --git a/common/parser.go b/common/parser.go index 412e4c33..799f5ec9 100644 --- a/common/parser.go +++ b/common/parser.go @@ -587,7 +587,7 @@ func ParseMessage(msg string, sectionID int, sectionType string /*, user User*/) // TODO: Reduce the amount of code duplication if media.Type == "attach" { - addImage(media.URL + "?sectionID=" + strconv.Itoa(sectionID) + "§ionType=" + sectionType) + addImage(media.URL + "?sectionID=" + strconv.Itoa(sectionID) + "&sectionType=" + sectionType) continue } else if media.Type == "image" { addImage(media.URL) diff --git a/langs/english.json b/langs/english.json index 6ff4ecfa..163465b6 100644 --- a/langs/english.json +++ b/langs/english.json @@ -376,6 +376,10 @@ "topic.report_button_text":"Report", "topic.flag_button_text":"Flag", + "topic.select_button_text":"Select", + "topic.copy_button_text":"Copy", + "topic.upload_button_text":"Upload", + "panel_rank_admins":"Admins", "panel_rank_mods":"Mods", "panel_rank_banned":"Banned", diff --git a/patcher/patches.go b/patcher/patches.go index 54a4f79e..b3a81f44 100644 --- a/patcher/patches.go +++ b/patcher/patches.go @@ -22,6 +22,7 @@ func init() { addPatch(8, patch8) addPatch(9, patch9) addPatch(10, patch10) + addPatch(11, patch11) } func patch0(scanner *bufio.Scanner) (err error) { @@ -29,7 +30,6 @@ func patch0(scanner *bufio.Scanner) (err error) { if err != nil { return err } - err = execStmt(qgen.Builder.DropTable("menu_items")) if err != nil { return err @@ -400,7 +400,6 @@ func patch10(scanner *bufio.Scanner) error { return err } - // We could probably do something more efficient, but as there shouldn't be too many sites right now, we can probably cheat a little, otherwise it'll take forever to get things done err = acc().Select("topics").Cols("tid").EachInt(func(tid int) error { stid := itoa(tid) @@ -430,3 +429,41 @@ func patch10(scanner *bufio.Scanner) error { _, err = acc().Insert("updates").Columns("dbVersion").Fields("0").Exec() return err } + +func patch11(scanner *bufio.Scanner) error { + err := execStmt(qgen.Builder.AddColumn("replies", tblColumn{"attachCount", "int", 0, false, false, "0"})) + if err != nil { + return err + } + + // Attachments for replies got the topicID rather than the replyID for a while in error, so we want to separate these out + _, err = acc().Update("attachments").Set("originTable = 'freplies'").Where("originTable = 'replies'").Exec() + if err != nil { + return err + } + + // We could probably do something more efficient, but as there shouldn't be too many sites right now, we can probably cheat a little, otherwise it'll take forever to get things done + return acc().Select("topics").Cols("tid").EachInt(func(tid int) error { + stid := itoa(tid) + + count, err := acc().Count("attachments").Where("originTable = 'topics' and originID = " + stid).Total() + if err != nil { + return err + } + + _, err = acc().Update("topics").Set("attachCount = ?").Where("tid = " + stid).Exec(count) + return err + }) + + /*return acc().Select("replies").Cols("rid").EachInt(func(rid int) error { + srid := itoa(rid) + + count, err := acc().Count("attachments").Where("originTable = 'replies' and originID = " + srid).Total() + if err != nil { + return err + } + + _, err = acc().Update("replies").Set("attachCount = ?").Where("rid = " + srid).Exec(count) + return err + })*/ +} diff --git a/public/global.js b/public/global.js index b93b380e..7d86b796 100644 --- a/public/global.js +++ b/public/global.js @@ -380,7 +380,7 @@ function mainInit(){ $(".topic_name").html(topicNameInput); $(".topic_name").attr(topicNameInput); let topicContentInput = $('.topic_content_input').val(); - $(".topic_content").html(topicContentInput.replace(/(\n)+/g,"
")); + $(".topic_content").html(topicContentInput.replace("\n","

")); let topicStatusInput = $('.topic_status_input').val(); $(".topic_status_e:not(.open_edit)").html(topicStatusInput); @@ -410,18 +410,25 @@ function mainInit(){ $(".edit_item").click(function(event){ event.preventDefault(); - let blockParent = $(this).closest('.editable_parent'); - let block = blockParent.find('.editable_block').eq(0); - block.html("
"); + let blockParent = this.closest('.editable_parent'); + let srcNode = blockParent.querySelector(".edit_source"); + let block = blockParent.querySelector('.editable_block'); + block.classList.add("in_edit"); + let source = ""; + if(srcNode!=null) source = srcNode.innerText; + else source = block.innerHTML; + // TODO: Add a client template for this + block.innerHTML = "
"; $(".submit_edit").click(function(event){ event.preventDefault(); - let blockParent = $(this).closest('.editable_parent'); - let block = blockParent.find('.editable_block').eq(0); - let newContent = block.find('textarea').eq(0).val(); - block.html(newContent); + block.classList.remove("in_edit"); + let newContent = block.querySelector('textarea').value; + block.innerHTML = newContent.replace("\n","

"); + if(srcNode!=null) srcNode.innerText = newContent; - var formAction = $(this).closest('a').attr("href"); + let formAction = this.closest('a').getAttribute("href"); + // TODO: Bounce the parsed post back and set innerHTML to it? $.ajax({ url: formAction, type: "POST", error: ajaxError, dataType: "json", data: { isJs: "1", edit_item: newContent } }); }); diff --git a/routes/reply.go b/routes/reply.go index b570223a..42a8a9ba 100644 --- a/routes/reply.go +++ b/routes/reply.go @@ -8,8 +8,26 @@ import ( "github.com/Azareal/Gosora/common" "github.com/Azareal/Gosora/common/counters" + "github.com/Azareal/Gosora/query_gen" ) +type ReplyStmts struct { + updateAttachs *sql.Stmt +} + +var replyStmts ReplyStmts + +// TODO: Move this statement somewhere else +func init() { + common.DbInits.Add(func(acc *qgen.Accumulator) error { + replyStmts = ReplyStmts{ + // TODO: Less race-y attachment count updates + updateAttachs: acc.Update("replies").Set("attachCount = ?").Where("rid = ?").Prepare(), + } + return acc.FirstError() + }) +} + func CreateReplySubmit(w http.ResponseWriter, r *http.Request, user common.User) common.RouteError { tid, err := strconv.Atoi(r.PostFormValue("tid")) if err != nil { @@ -35,15 +53,6 @@ func CreateReplySubmit(w http.ResponseWriter, r *http.Request, user common.User) return common.NoPermissions(w, r, user) } - // Handle the file attachments - // TODO: Stop duplicating this code - if user.Perms.UploadFiles { - _, rerr := uploadAttachment(w, r, user, topic.ParentID, "forums", tid, "replies") - if rerr != nil { - return rerr - } - } - content := common.PreparseMessage(r.PostFormValue("reply-content")) // TODO: Fully parse the post and put that in the parsed column rid, err := common.Rstore.Create(topic, content, user.LastIP, user.ID) @@ -55,6 +64,16 @@ func CreateReplySubmit(w http.ResponseWriter, r *http.Request, user common.User) if err != nil { return common.LocalError("Unable to load the reply", w, r, user) } + + // Handle the file attachments + // TODO: Stop duplicating this code + if user.Perms.UploadFiles { + _, rerr := uploadAttachment(w, r, user, topic.ParentID, "forums", rid, "replies") + if rerr != nil { + return rerr + } + } + if r.PostFormValue("has_poll") == "1" { var maxPollOptions = 10 var pollInputItems = make(map[int]string) diff --git a/routes/topic.go b/routes/topic.go index c352961a..920bcf3b 100644 --- a/routes/topic.go +++ b/routes/topic.go @@ -599,6 +599,7 @@ func deleteAttachment(w http.ResponseWriter, r *http.Request, user common.User, // TODO: Stop duplicating this code // TODO: Use a transaction here +// TODO: Move this function to neutral ground func uploadAttachment(w http.ResponseWriter, r *http.Request, user common.User, sid int, sectionTable string, oid int, originTable string) (pathMap map[string]string, rerr common.RouteError) { pathMap = make(map[string]string) files, rerr := uploadFilesWithHash(w, r, user, "./attachs/") @@ -619,9 +620,9 @@ func uploadAttachment(w http.ResponseWriter, r *http.Request, user common.User, pathMap[filename] = strconv.Itoa(aid) } - switch sectionTable { + switch originTable { case "topics": - _, err = topicStmts.updateAttachs.Exec(common.Attachments.CountInTopic(oid), oid) + _, err = topicStmts.updateAttachs.Exec(common.Attachments.CountIn(originTable,oid), oid) if err != nil { return nil, common.InternalError(err, w, r) } @@ -629,6 +630,11 @@ func uploadAttachment(w http.ResponseWriter, r *http.Request, user common.User, if err != nil { return nil, common.InternalError(err, w, r) } + case "replies": + _, err = replyStmts.updateAttachs.Exec(common.Attachments.CountIn(originTable,oid), oid) + if err != nil { + return nil, common.InternalError(err, w, r) + } } } diff --git a/schema/mssql/query_replies.sql b/schema/mssql/query_replies.sql index aa74cc37..96b5decc 100644 --- a/schema/mssql/query_replies.sql +++ b/schema/mssql/query_replies.sql @@ -10,6 +10,7 @@ CREATE TABLE [replies] ( [lastUpdated] datetime not null, [ipaddress] nvarchar (200) DEFAULT '0.0.0.0.0' not null, [likeCount] int DEFAULT 0 not null, + [attachCount] int DEFAULT 0 not null, [words] int DEFAULT 1 not null, [actionType] nvarchar (20) DEFAULT '' not null, [poll] int DEFAULT 0 not null, diff --git a/schema/mysql/query_replies.sql b/schema/mysql/query_replies.sql index 636b46d5..65dbe737 100644 --- a/schema/mysql/query_replies.sql +++ b/schema/mysql/query_replies.sql @@ -10,6 +10,7 @@ CREATE TABLE `replies` ( `lastUpdated` datetime not null, `ipaddress` varchar(200) DEFAULT '0.0.0.0.0' not null, `likeCount` int DEFAULT 0 not null, + `attachCount` int DEFAULT 0 not null, `words` int DEFAULT 1 not null, `actionType` varchar(20) DEFAULT '' not null, `poll` int DEFAULT 0 not null, diff --git a/schema/pgsql/query_replies.sql b/schema/pgsql/query_replies.sql index 6af1fbae..d15948e3 100644 --- a/schema/pgsql/query_replies.sql +++ b/schema/pgsql/query_replies.sql @@ -10,6 +10,7 @@ CREATE TABLE "replies" ( `lastUpdated` timestamp not null, `ipaddress` varchar (200) DEFAULT '0.0.0.0.0' not null, `likeCount` int DEFAULT 0 not null, + `attachCount` int DEFAULT 0 not null, `words` int DEFAULT 1 not null, `actionType` varchar (20) DEFAULT '' not null, `poll` int DEFAULT 0 not null, diff --git a/templates/topic_alt.html b/templates/topic_alt.html index 3a192f79..e990c1b7 100644 --- a/templates/topic_alt.html +++ b/templates/topic_alt.html @@ -82,17 +82,17 @@ {{if .Topic.Attachments}}
{{range .Topic.Attachments}}
- {{if .Image}}{{end}} + {{if .Image}}{{end}} {{.Path}} - - + +
{{end}}
{{if .CurrentUser.Perms.UploadFiles}} - {{end}} - + {{end}} +
{{end}} diff --git a/templates/topic_alt_posts.html b/templates/topic_alt_posts.html index 6d77cd7f..008455b2 100644 --- a/templates/topic_alt_posts.html +++ b/templates/topic_alt_posts.html @@ -11,7 +11,7 @@ {{.ActionType}} {{else}} - {{/** TODO: We might end up with
s in the inline editor, fix this **/}} +
{{.Content}}
{{.ContentHtml}}
diff --git a/themes/nox/public/main.css b/themes/nox/public/main.css index e66b9589..df827d81 100644 --- a/themes/nox/public/main.css +++ b/themes/nox/public/main.css @@ -710,6 +710,23 @@ button, .formbutton, .panel_right_button:not(.has_inner_button) { border-radius: 3px; padding: 16px; } +.user_content.in_edit { + padding: 0px; + background: none; +} +.user_content textarea { + resize: vertical; + height: 150px; + width: 100% !important; + padding: 16px; +} +.user_content.in_edit a { + display: flex; + background-color: #444444; + border-radius: 4px; + margin-top: 4px; /*8 without
*/ + padding: 6px; +} .post_item .button_container { display: flex; margin-top: 8px; @@ -1014,10 +1031,7 @@ input[type=checkbox]:checked + label .sel { } @media(max-width: 500px) { - .sidebar { - display: none; - } - .topic_view_count { + .sidebar, .topic_view_count { display: none; }