diff --git a/common/pages.go b/common/pages.go index dd456963..aef699a1 100644 --- a/common/pages.go +++ b/common/pages.go @@ -202,20 +202,25 @@ type TopicListSort struct { Ascending bool } +type QuickTools struct { + CanDelete bool + CanLock bool + CanMove bool +} + type TopicListPage struct { *Header - TopicList []*TopicsRow + TopicList []TopicsRowMut ForumList []Forum DefaultForum int Sort TopicListSort - CanLock bool - CanMove bool + QuickTools Paginator } type ForumPage struct { *Header - ItemList []*TopicsRow + ItemList []TopicsRowMut Forum *Forum CanLock bool CanMove bool diff --git a/common/template_init.go b/common/template_init.go index 871390ae..7e56e8c9 100644 --- a/common/template_init.go +++ b/common/template_init.go @@ -226,9 +226,9 @@ func compileCommons(c *tmpl.CTemplateSet, head, head2 *Header, forumList []Forum return head2 }*/ - var topicsList []*TopicsRow - topicsList = append(topicsList, &TopicsRow{1, "topic-title", "Topic Title", "The topic content.", 1, false, false, now, now, user3.ID, 1, 1, "", "::1", 1, 0, 1, 1, 0, "classname", 0, "", user2, "", 0, user3, "General", "/forum/general.2", nil}) - topicListPage := TopicListPage{htitle("Topic List"), topicsList, forumList, Config.DefaultForum, TopicListSort{"lastupdated", false}, false, false, Paginator{[]int{1}, 1, 1}} + var topicsList []TopicsRowMut + topicsList = append(topicsList, TopicsRowMut{&TopicsRow{1, "topic-title", "Topic Title", "The topic content.", 1, false, false, now, now, user3.ID, 1, 1, "", "::1", 1, 0, 1, 1, 0, "classname", 0, "", user2, "", 0, user3, "General", "/forum/general.2", nil}, false}) + topicListPage := TopicListPage{htitle("Topic List"), topicsList, forumList, Config.DefaultForum, TopicListSort{"lastupdated", false}, QuickTools{false, false, false}, Paginator{[]int{1}, 1, 1}} o.Add("topics", "c.TopicListPage", topicListPage) o.Add("topics_mini", "c.TopicListPage", topicListPage) @@ -309,9 +309,9 @@ func compileTemplates(wg *sync.WaitGroup, c *tmpl.CTemplateSet, themeName string ppage := ProfilePage{htitle("User 526"), replyList, *user, 0, 0, false, false, false, false} // TODO: Use the score from user to generate the currentScore and nextScore t.Add("profile", "c.ProfilePage", ppage) - var topicsList []*TopicsRow - topicsList = append(topicsList, &TopicsRow{1, "topic-title", "Topic Title", "The topic content.", 1, false, false, now, now, user3.ID, 1, 1, "", "127.0.0.1", 1, 0, 1, 1, 0, "classname", 0, "", user2, "", 0, user3, "General", "/forum/general.2", nil}) - topicListPage := TopicListPage{htitle("Topic List"), topicsList, forumList, Config.DefaultForum, TopicListSort{"lastupdated", false}, false, false, Paginator{[]int{1}, 1, 1}} + var topicsList []TopicsRowMut + topicsList = append(topicsList, TopicsRowMut{&TopicsRow{1, "topic-title", "Topic Title", "The topic content.", 1, false, false, now, now, user3.ID, 1, 1, "", "127.0.0.1", 1, 0, 1, 1, 0, "classname", 0, "", user2, "", 0, user3, "General", "/forum/general.2", nil}, false}) + topicListPage := TopicListPage{htitle("Topic List"), topicsList, forumList, Config.DefaultForum, TopicListSort{"lastupdated", false}, QuickTools{false, false, false}, Paginator{[]int{1}, 1, 1}} forumItem := BlankForum(1, "general-forum.1", "General Forum", "Where the general stuff happens", true, "all", 0, "", 0) forumPage := ForumPage{htitle("General Forum"), topicsList, forumItem, false, false, Paginator{[]int{1}, 1, 1}} @@ -538,8 +538,8 @@ func compileJSTemplates(wg *sync.WaitGroup, c *tmpl.CTemplateSet, themeName stri t := TItemHold(make(map[string]TItem)) - topicsRow := &TopicsRow{1, "topic-title", "Topic Title", "The topic content.", 1, false, false, now, now, user3.ID, 1, 1, "", "::1", 1, 0, 1, 0, 1, "classname", 0, "", user2, "", 0, user3, "General", "/forum/general.2", nil} - t.AddStd("topics_topic", "c.TopicsRow", topicsRow) + topicsRow := TopicsRowMut{&TopicsRow{1, "topic-title", "Topic Title", "The topic content.", 1, false, false, now, now, user3.ID, 1, 1, "", "::1", 1, 0, 1, 0, 1, "classname", 0, "", user2, "", 0, user3, "General", "/forum/general.2", nil}, false} + t.AddStd("topics_topic", "c.TopicsRowMut", topicsRow) poll := Poll{ID: 1, Type: 0, Options: map[int]string{0: "Nothing", 1: "Something"}, Results: map[int]int{0: 5, 1: 2}, QuickOptions: []PollOption{ PollOption{0, "Nothing"}, diff --git a/common/topic.go b/common/topic.go index 437c4079..69a8238c 100644 --- a/common/topic.go +++ b/common/topic.go @@ -94,6 +94,11 @@ type TopicUser struct { Deletable bool } +type TopicsRowMut struct { + *TopicsRow + CanMod bool +} + // TODO: Embed TopicUser to simplify this structure and it's related logic? type TopicsRow struct { ID int @@ -151,11 +156,17 @@ type WsTopicsRow struct { LastUser *WsJSONUser ForumName string ForumLink string + CanMod bool } // TODO: Can we get the client side to render the relative times instead? func (r *TopicsRow) WebSockets() *WsTopicsRow { - return &WsTopicsRow{r.ID, r.Link, r.Title, r.CreatedBy, r.IsClosed, r.Sticky, r.CreatedAt, r.LastReplyAt, RelativeTime(r.LastReplyAt), r.LastReplyBy, r.LastReplyID, r.ParentID, r.ViewCount, r.PostCount, r.LikeCount, r.AttachCount, r.ClassName, r.Creator.WebSockets(), r.LastUser.WebSockets(), r.ForumName, r.ForumLink} + return &WsTopicsRow{r.ID, r.Link, r.Title, r.CreatedBy, r.IsClosed, r.Sticky, r.CreatedAt, r.LastReplyAt, RelativeTime(r.LastReplyAt), r.LastReplyBy, r.LastReplyID, r.ParentID, r.ViewCount, r.PostCount, r.LikeCount, r.AttachCount, r.ClassName, r.Creator.WebSockets(), r.LastUser.WebSockets(), r.ForumName, r.ForumLink, false} +} + +// TODO: Can we get the client side to render the relative times instead? +func (r *TopicsRow) WebSockets2(canMod bool) *WsTopicsRow { + return &WsTopicsRow{r.ID, r.Link, r.Title, r.CreatedBy, r.IsClosed, r.Sticky, r.CreatedAt, r.LastReplyAt, RelativeTime(r.LastReplyAt), r.LastReplyBy, r.LastReplyID, r.ParentID, r.ViewCount, r.PostCount, r.LikeCount, r.AttachCount, r.ClassName, r.Creator.WebSockets(), r.LastUser.WebSockets(), r.ForumName, r.ForumLink, canMod} } // TODO: Stop relying on so many struct types? diff --git a/common/ws_hub.go b/common/ws_hub.go index 9edb1d15..07236ae4 100644 --- a/common/ws_hub.go +++ b/common/ws_hub.go @@ -151,6 +151,7 @@ func wsTopicListTick(h *WsHubImpl) error { } canSeeRenders := make(map[string][]byte) + canSeeLists := make(map[string][]*WsTopicsRow) for name, canSee := range canSeeMap { topicList, forumList, _, err := TopicList.GetListByCanSee(canSee, 1, 0, nil) if err != nil { @@ -180,6 +181,7 @@ func wsTopicListTick(h *WsHubImpl) error { for i, topicRow := range topicList { wsTopicList[i] = topicRow.WebSockets() } + canSeeLists[name] = wsTopicList outBytes, err := json.Marshal(&WsTopicList{wsTopicList, 0, tickStart.Unix()}) if err != nil { @@ -191,17 +193,94 @@ func wsTopicListTick(h *WsHubImpl) error { // TODO: Use MessagePack for additional speed? //fmt.Println("writing to the clients") for _, wsUser := range currentWatchers { - group := groups[wsUser.User.Group] + u := wsUser.User + group := groups[u.Group] canSee := make([]byte, len(group.CanSee)) for i, item := range group.CanSee { canSee[i] = byte(item) } + sCanSee := string(canSee) + l := canSeeLists[sCanSee] + + // TODO: Optimise this away for guests? + anyMod, anyLock, anyMove, allMod := false, false, false, true + var modSet map[int]int + if u.IsSuperAdmin { + anyMod = true + anyLock = true + anyMove = true + } else { + modSet = make(map[int]int, len(l)) + for i, t := range l { + // TODO: Abstract this? + fp, err := FPStore.Get(t.ParentID, u.Group) + if err == ErrNoRows { + fp = BlankForumPerms() + } else if err != nil { + return err + } + var ccanMod, ccanLock, ccanMove bool + if fp.Overrides { + ccanLock = fp.CloseTopic + ccanMove = fp.MoveTopic + ccanMod = t.CreatedBy == u.ID || fp.DeleteTopic || ccanLock || ccanMove + } else { + ccanLock = u.Perms.CloseTopic + ccanMove = u.Perms.MoveTopic + ccanMod = t.CreatedBy == u.ID || u.Perms.DeleteTopic || ccanLock || ccanMove + } + if ccanLock { + anyLock = true + } + if ccanMove { + anyMove = true + } + if ccanMod { + anyMod = true + } else { + allMod = false + } + var v int + if ccanMod { + v = 1 + } + modSet[i] = v + } + } //fmt.Println("writing to user #", wsUser.User.ID) - outBytes := canSeeRenders[string(canSee)] + outBytes := canSeeRenders[sCanSee] //fmt.Println("outBytes: ", string(outBytes)) - err := wsUser.WriteToPageBytes(outBytes, "/topics/") - if err == ErrNoneOnPage { + //fmt.Println("outBytes[:len(outBytes)-1]: ", string(outBytes[:len(outBytes)-1])) + //e := wsUser.WriteToPageBytes(outBytes, "/topics/") + //e := wsUser.WriteToPageBytesMulti([][]byte{outBytes[:len(outBytes)-1], []byte(`,"mod":1}`)}, "/topics/") + var e error + if !anyMod { + e = wsUser.WriteToPageBytes(outBytes, "/topics/") + } else { + var lm []byte + if anyLock && anyMove { + lm = []byte(`,"lock":1,"move":1}`) + } else if anyLock { + lm = []byte(`,"lock":1}`) + } else if anyMove { + lm = []byte(`,"move":1}`) + } else { + lm = []byte("}") + } + if allMod { + e = wsUser.WriteToPageBytesMulti([][]byte{outBytes[:len(outBytes)-1], []byte(`,"mod":1`), lm}, "/topics/") + } else { + // TODO: Temporary and inefficient + mBytes, err := json.Marshal(modSet) + if err != nil { + return err + } + e = wsUser.WriteToPageBytesMulti([][]byte{outBytes[:len(outBytes)-1], []byte(`,"mod":`), mBytes, lm}, "/topics/") + } + } + + if e == ErrNoneOnPage { //fmt.Printf("werr for #%d: %s\n", wsUser.User.ID, err) wsUser.FinalizePage("/topics/", func() { topicListMutex.Lock() diff --git a/public/global.js b/public/global.js index 5ebcb208..bab9ae4d 100644 --- a/public/global.js +++ b/public/global.js @@ -221,8 +221,8 @@ function runWebSockets(resume=false) { if(window.location.protocol == "https:") s = "s"; conn = new WebSocket("ws"+s+"://" + document.location.host + "/ws/"); - conn.onerror = err => { - console.log(err); + conn.onerror = e => { + console.log(e); } // TODO: Sync alerts, topic list, etc. @@ -297,11 +297,26 @@ function runWebSockets(resume=false) { } else if("Topics" in data) { console.log("topic in data"); console.log("data",data); + // TODO: Handle desyncs more gracefully? + // TODO: Send less unneccessary data? let topic = data.Topics[0]; if(topic===undefined){ console.log("empty topic list"); return; } + if("mod" in data) { + topic.CanMod = data.mod==1 || data.mod[0]==1; + if(data.lock==1) { + $(".val_lock").each(function(){ + this.classList.remove("auto_hide"); + }); + } + if(data.move==1) { + $(".val_move").each(function(){ + this.classList.remove("auto_hide"); + }); + } + } // TODO: Fix the data race where the function hasn't been loaded yet let renTopic = Tmpl_topics_topic(topic); $(".topic_row[data-tid='"+topic.ID+"']").addClass("ajax_topic_dupe"); @@ -313,7 +328,7 @@ function runWebSockets(resume=false) { moreTopicCount++; let blocks = document.getElementsByClassName("more_topic_block_initial"); - for(let i=0; i < blocks.length; i++) { + for(let i=0; i -1)) throw("This file doesn't have an extension"); + if(!name.indexOf('.') > -1) throw("This file doesn't have an extension"); return name.split('.').pop(); } @@ -439,9 +454,9 @@ function mainInit(){ ev.preventDefault(); let blocks = document.getElementsByClassName("more_topic_block_active"); for(let i=0; i { addHook("end_bind_topic", () => { + let changeListener = (files,handler) => { + if(files!=null) { + files.removeEventListener("change", handler, false); + files.addEventListener("change", handler, false); + } + }; let uploadFiles = document.getElementById("upload_files"); - if(uploadFiles!=null) { - uploadFiles.removeEventListener("change", uploadAttachHandler, false); - uploadFiles.addEventListener("change", uploadAttachHandler, false); - } + changeListener(uploadFiles,uploadAttachHandler); let uploadFilesOp = document.getElementById("upload_files_op"); - if(uploadFilesOp!=null) { - uploadFilesOp.removeEventListener("change", uploadAttachHandler2, false); - uploadFilesOp.addEventListener("change", uploadAttachHandler2, false); - } + changeListener(uploadFilesOp,uploadAttachHandler2); bindAttachManager(); function bindAttachItems() { @@ -212,73 +212,6 @@ var imageExts = ["png","jpg","jpe","jpeg","jif","jfi","jfif","svg","bmp","gif"," bindAttachItems(); bindAttachManager(); }); - - $(".moderate_link").unbind("click"); - $(".mod_floater_submit").unbind("click"); - $(".moderate_link").click(ev => { - ev.preventDefault(); - $(".pre_opt").removeClass("auto_hide"); - $(".moderate_link").addClass("moderate_open"); - $("#topicsItemList,#forumItemList").addClass("topics_moderate"); - $(".topic_row").each(function(){ - $(this).click(function(){ - selectedTopics.push(parseInt($(this).attr("data-tid"),10)); - if(selectedTopics.length==1) { - var msg = phraseBox["topic_list"]["topic_list.what_to_do_single"]; - } else { - var msg = "What do you want to do with these "+selectedTopics.length+" topics?"; - } - $(".mod_floater_head span").html(msg); - $(this).addClass("topic_selected"); - $(".mod_floater").removeClass("auto_hide"); - }); - }); - - let bulkActionSender = (action,selectedTopics,fragBit) => { - $.ajax({ - url: "/topic/"+action+"/submit/"+fragBit+"?s="+me.User.S, - type: "POST", - data: JSON.stringify(selectedTopics), - contentType: "application/json", - error: ajaxError, - success: () => { - window.location.reload(); - } - }); - }; - // TODO: Should we unbind this here to avoid binding multiple listeners to this accidentally? - $(".mod_floater_submit").click(function(ev){ - ev.preventDefault(); - let selectNode = this.form.querySelector(".mod_floater_options"); - let optionNode = selectNode.options[selectNode.selectedIndex]; - let action = optionNode.getAttribute("value"); - - // Handle these specially - switch(action) { - case "move": - console.log("move action"); - let modTopicMover = $("#mod_topic_mover"); - $("#mod_topic_mover").removeClass("auto_hide"); - $("#mod_topic_mover .pane_row").click(function(){ - modTopicMover.find(".pane_row").removeClass("pane_selected"); - let fid = this.getAttribute("data-fid"); - if(fid==null) return; - this.classList.add("pane_selected"); - console.log("fid",fid); - forumToMoveTo = fid; - - $("#mover_submit").unbind("click"); - $("#mover_submit").click(ev => { - ev.preventDefault(); - bulkActionSender("move",selectedTopics,forumToMoveTo); - }); - }); - return; - } - - bulkActionSender(action,selectedTopics,""); - }); - }); function addPollInput() { console.log("clicked on pollinputinput"); @@ -306,4 +239,74 @@ var imageExts = ["png","jpg","jpe","jpeg","jif","jfi","jfif","svg","bmp","gif"," }); }); }); + //addInitHook("after_init_bind_page", () => { + addHook("end_bind_page", () => { + $(".moderate_link").unbind("click"); + $(".mod_floater_submit").unbind("click"); + $(".moderate_link").click(ev => { + ev.preventDefault(); + $(".pre_opt").removeClass("auto_hide"); + $(".moderate_link").addClass("moderate_open"); + $("#topicsItemList,#forumItemList").addClass("topics_moderate"); + $(".topic_row").each(function(){ + $(this).click(function(){ + if(!this.classList.contains("can_mod") || this.classList.contains("topic_selected")) return; + selectedTopics.push(parseInt($(this).attr("data-tid"),10)); + if(selectedTopics.length==1) { + var msg = phraseBox["topic_list"]["topic_list.what_to_do_single"]; + } else { + var msg = "What do you want to do with these "+selectedTopics.length+" topics?"; + } + $(".mod_floater_head span").html(msg); + $(this).addClass("topic_selected"); + $(".mod_floater").removeClass("auto_hide"); + }); + }); + + let bulkActionSender = (action,selectedTopics,fragBit) => { + $.ajax({ + url: "/topic/"+action+"/submit/"+fragBit+"?s="+me.User.S, + type: "POST", + data: JSON.stringify(selectedTopics), + contentType: "application/json", + error: ajaxError, + success: () => { + window.location.reload(); + } + }); + }; + // TODO: Should we unbind this here to avoid binding multiple listeners to this accidentally? + $(".mod_floater_submit").click(function(ev){ + ev.preventDefault(); + let selectNode = this.form.querySelector(".mod_floater_options"); + let optionNode = selectNode.options[selectNode.selectedIndex]; + let action = optionNode.getAttribute("value"); + + // Handle these specially + switch(action) { + case "move": + console.log("move action"); + let modTopicMover = $("#mod_topic_mover"); + $("#mod_topic_mover").removeClass("auto_hide"); + $("#mod_topic_mover .pane_row").click(function(){ + modTopicMover.find(".pane_row").removeClass("pane_selected"); + let fid = this.getAttribute("data-fid"); + if(fid==null) return; + this.classList.add("pane_selected"); + console.log("fid",fid); + forumToMoveTo = fid; + + $("#mover_submit").unbind("click"); + $("#mover_submit").click(ev => { + ev.preventDefault(); + bulkActionSender("move",selectedTopics,forumToMoveTo); + }); + }); + return; + } + + bulkActionSender(action,selectedTopics,""); + }); + }); + }); })() \ No newline at end of file diff --git a/routes/forum.go b/routes/forum.go index d7be46bd..b4c38b79 100644 --- a/routes/forum.go +++ b/routes/forum.go @@ -54,8 +54,14 @@ func ViewForum(w http.ResponseWriter, r *http.Request, u *c.User, h *c.Header, s return nil } + topicList2 := make([]c.TopicsRowMut, len(topicList)) + canMod := u.Perms.CloseTopic || u.Perms.MoveTopic + for i, t := range topicList { + topicList2[i] = c.TopicsRowMut{t, t.CreatedBy == u.ID || canMod} + } + //pageList := c.Paginate(page, lastPage, 5) - pi := c.ForumPage{h, topicList, forum, u.Perms.CloseTopic, u.Perms.MoveTopic, pagi} + pi := c.ForumPage{h, topicList2, forum, u.Perms.CloseTopic, u.Perms.MoveTopic, pagi} tmpl := forum.Tmpl if tmpl == "" { ferr = renderTemplate("forum", w, r, h, pi) diff --git a/routes/forum_list.go b/routes/forum_list.go index 3f9df081..0a278aa1 100644 --- a/routes/forum_list.go +++ b/routes/forum_list.go @@ -8,12 +8,12 @@ import ( "github.com/Azareal/Gosora/common/phrases" ) -func ForumList(w http.ResponseWriter, r *http.Request, user *c.User, h *c.Header) c.RouteError { - /*skip, rerr := h.Hooks.VhookSkippable("route_forum_list_start", w, r, user, h) +func ForumList(w http.ResponseWriter, r *http.Request, u *c.User, h *c.Header) c.RouteError { + /*skip, rerr := h.Hooks.VhookSkippable("route_forum_list_start", w, r, u, h) if skip || rerr != nil { return rerr }*/ - skip, rerr := c.H_route_forum_list_start_hook(h.Hooks, w, r, user, h) + skip, rerr := c.H_route_forum_list_start_hook(h.Hooks, w, r, u, h) if skip || rerr != nil { return rerr } @@ -24,18 +24,18 @@ func ForumList(w http.ResponseWriter, r *http.Request, user *c.User, h *c.Header var err error var canSee []int - if user.IsSuperAdmin { + if u.IsSuperAdmin { canSee, err = c.Forums.GetAllVisibleIDs() if err != nil { return c.InternalError(err, w, r) } } else { - group, err := c.Groups.Get(user.Group) + g, err := c.Groups.Get(u.Group) if err != nil { - log.Printf("Group #%d doesn't exist despite being used by c.User #%d", user.Group, user.ID) - return c.LocalError("Something weird happened", w, r, user) + log.Printf("Group #%d doesn't exist despite being used by c.User #%d", u.Group, u.ID) + return c.LocalError("Something weird happened", w, r, u) } - canSee = group.CanSee + canSee = g.CanSee } var forumList []c.Forum diff --git a/routes/panel/forums.go b/routes/panel/forums.go index f916d42c..2d1657c8 100644 --- a/routes/panel/forums.go +++ b/routes/panel/forums.go @@ -11,13 +11,13 @@ import ( p "github.com/Azareal/Gosora/common/phrases" ) -func Forums(w http.ResponseWriter, r *http.Request, user *c.User) c.RouteError { - basePage, ferr := buildBasePage(w, r, user, "forums", "forums") +func Forums(w http.ResponseWriter, r *http.Request, u *c.User) c.RouteError { + basePage, ferr := buildBasePage(w, r, u, "forums", "forums") if ferr != nil { return ferr } - if !user.Perms.ManageForums { - return c.NoPermissions(w, r, user) + if !u.Perms.ManageForums { + return c.NoPermissions(w, r, u) } basePage.Header.AddScript("Sortable-1.4.0/Sortable.min.js") basePage.Header.AddScriptAsync("panel_forums.js") @@ -52,26 +52,26 @@ func Forums(w http.ResponseWriter, r *http.Request, user *c.User) c.RouteError { return renderTemplate("panel", w, r, basePage.Header, c.Panel{basePage, "", "", "panel_forums", &pi}) } -func ForumsCreateSubmit(w http.ResponseWriter, r *http.Request, user *c.User) c.RouteError { - _, ferr := c.SimplePanelUserCheck(w, r, user) +func ForumsCreateSubmit(w http.ResponseWriter, r *http.Request, u *c.User) c.RouteError { + _, ferr := c.SimplePanelUserCheck(w, r, u) if ferr != nil { return ferr } - if !user.Perms.ManageForums { - return c.NoPermissions(w, r, user) + if !u.Perms.ManageForums { + return c.NoPermissions(w, r, u) } - fname := r.PostFormValue("name") - fdesc := r.PostFormValue("desc") - fpreset := c.StripInvalidPreset(r.PostFormValue("preset")) + name := r.PostFormValue("name") + desc := r.PostFormValue("desc") + preset := c.StripInvalidPreset(r.PostFormValue("preset")) factive := r.PostFormValue("active") active := (factive == "on" || factive == "1") - fid, err := c.Forums.Create(fname, fdesc, active, fpreset) + fid, err := c.Forums.Create(name, desc, active, preset) if err != nil { return c.InternalError(err, w, r) } - err = c.AdminLogs.Create("create", fid, "forum", user.GetIP(), user.ID) + err = c.AdminLogs.Create("create", fid, "forum", u.GetIP(), u.ID) if err != nil { return c.InternalError(err, w, r) } @@ -81,22 +81,22 @@ func ForumsCreateSubmit(w http.ResponseWriter, r *http.Request, user *c.User) c. } // TODO: Revamp this -func ForumsDelete(w http.ResponseWriter, r *http.Request, user *c.User, sfid string) c.RouteError { - basePage, ferr := buildBasePage(w, r, user, "delete_forum", "forums") +func ForumsDelete(w http.ResponseWriter, r *http.Request, u *c.User, sfid string) c.RouteError { + basePage, ferr := buildBasePage(w, r, u, "delete_forum", "forums") if ferr != nil { return ferr } - if !user.Perms.ManageForums { - return c.NoPermissions(w, r, user) + if !u.Perms.ManageForums { + return c.NoPermissions(w, r, u) } fid, err := strconv.Atoi(sfid) if err != nil { - return c.LocalError("The provided Forum ID is not a valid number.", w, r, user) + return c.LocalError("The provided Forum ID is not a valid number.", w, r, u) } forum, err := c.Forums.Get(fid) if err == sql.ErrNoRows { - return c.LocalError("The forum you're trying to delete doesn't exist.", w, r, user) + return c.LocalError("The forum you're trying to delete doesn't exist.", w, r, u) } else if err != nil { return c.InternalError(err, w, r) } @@ -105,32 +105,32 @@ func ForumsDelete(w http.ResponseWriter, r *http.Request, user *c.User, sfid str youSure := c.AreYouSure{"/panel/forums/delete/submit/" + strconv.Itoa(fid), confirmMsg} pi := c.PanelPage{basePage, tList, youSure} - if c.RunPreRenderHook("pre_render_panel_delete_forum", w, r, user, &pi) { + if c.RunPreRenderHook("pre_render_panel_delete_forum", w, r, u, &pi) { return nil } return renderTemplate("panel_are_you_sure", w, r, basePage.Header, &pi) } -func ForumsDeleteSubmit(w http.ResponseWriter, r *http.Request, user *c.User, sfid string) c.RouteError { - _, ferr := c.SimplePanelUserCheck(w, r, user) +func ForumsDeleteSubmit(w http.ResponseWriter, r *http.Request, u *c.User, sfid string) c.RouteError { + _, ferr := c.SimplePanelUserCheck(w, r, u) if ferr != nil { return ferr } - if !user.Perms.ManageForums { - return c.NoPermissions(w, r, user) + if !u.Perms.ManageForums { + return c.NoPermissions(w, r, u) } fid, err := strconv.Atoi(sfid) if err != nil { - return c.LocalError("The provided Forum ID is not a valid number.", w, r, user) + return c.LocalError("The provided Forum ID is not a valid number.", w, r, u) } err = c.Forums.Delete(fid) if err == sql.ErrNoRows { - return c.LocalError("The forum you're trying to delete doesn't exist.", w, r, user) + return c.LocalError("The forum you're trying to delete doesn't exist.", w, r, u) } else if err != nil { return c.InternalError(err, w, r) } - err = c.AdminLogs.Create("delete", fid, "forum", user.GetIP(), user.ID) + err = c.AdminLogs.Create("delete", fid, "forum", u.GetIP(), u.ID) if err != nil { return c.InternalError(err, w, r) } @@ -139,14 +139,14 @@ func ForumsDeleteSubmit(w http.ResponseWriter, r *http.Request, user *c.User, sf return nil } -func ForumsOrderSubmit(w http.ResponseWriter, r *http.Request, user *c.User) c.RouteError { - _, ferr := c.SimplePanelUserCheck(w, r, user) +func ForumsOrderSubmit(w http.ResponseWriter, r *http.Request, u *c.User) c.RouteError { + _, ferr := c.SimplePanelUserCheck(w, r, u) if ferr != nil { return ferr } js := r.PostFormValue("js") == "1" - if !user.Perms.ManageForums { - return c.NoPermissionsJSQ(w, r, user, js) + if !u.Perms.ManageForums { + return c.NoPermissionsJSQ(w, r, u, js) } sitems := strings.TrimSuffix(strings.TrimPrefix(r.PostFormValue("items"), "{"), "}") //fmt.Printf("sitems: %+v\n", sitems) @@ -155,13 +155,13 @@ func ForumsOrderSubmit(w http.ResponseWriter, r *http.Request, user *c.User) c.R for index, sfid := range strings.Split(sitems, ",") { fid, err := strconv.Atoi(sfid) if err != nil { - return c.LocalErrorJSQ("Invalid integer in forum list", w, r, user, js) + return c.LocalErrorJSQ("Invalid integer in forum list", w, r, u, js) } updateMap[fid] = index } c.Forums.UpdateOrder(updateMap) - err := c.AdminLogs.Create("reorder", 0, "forum", user.GetIP(), user.ID) + err := c.AdminLogs.Create("reorder", 0, "forum", u.GetIP(), u.ID) if err != nil { return c.InternalErrorJSQ(err, w, r, js) } @@ -169,13 +169,13 @@ func ForumsOrderSubmit(w http.ResponseWriter, r *http.Request, user *c.User) c.R return successRedirect("/panel/forums/", w, r, js) } -func ForumsEdit(w http.ResponseWriter, r *http.Request, user *c.User, sfid string) c.RouteError { - basePage, ferr := buildBasePage(w, r, user, "edit_forum", "forums") +func ForumsEdit(w http.ResponseWriter, r *http.Request, u *c.User, sfid string) c.RouteError { + basePage, ferr := buildBasePage(w, r, u, "edit_forum", "forums") if ferr != nil { return ferr } - if !user.Perms.ManageForums { - return c.NoPermissions(w, r, user) + if !u.Perms.ManageForums { + return c.NoPermissions(w, r, u) } fid, err := strconv.Atoi(sfid) @@ -186,7 +186,7 @@ func ForumsEdit(w http.ResponseWriter, r *http.Request, user *c.User, sfid strin forum, err := c.Forums.Get(fid) if err == sql.ErrNoRows { - return c.LocalError("The forum you're trying to edit doesn't exist.", w, r, user) + return c.LocalError("The forum you're trying to edit doesn't exist.", w, r, u) } else if err != nil { return c.InternalError(err, w, r) } @@ -222,30 +222,30 @@ func ForumsEdit(w http.ResponseWriter, r *http.Request, user *c.User, sfid strin return renderTemplate("panel", w, r, basePage.Header, c.Panel{basePage, "", "", "panel_forum_edit", &pi}) } -func ForumsEditSubmit(w http.ResponseWriter, r *http.Request, user *c.User, sfid string) c.RouteError { - _, ferr := c.SimplePanelUserCheck(w, r, user) +func ForumsEditSubmit(w http.ResponseWriter, r *http.Request, u *c.User, sfid string) c.RouteError { + _, ferr := c.SimplePanelUserCheck(w, r, u) if ferr != nil { return ferr } - if !user.Perms.ManageForums { - return c.NoPermissions(w, r, user) + if !u.Perms.ManageForums { + return c.NoPermissions(w, r, u) } js := r.PostFormValue("js") == "1" fid, err := strconv.Atoi(sfid) if err != nil { - return c.LocalErrorJSQ("The provided Forum ID is not a valid number.", w, r, user, js) + return c.LocalErrorJSQ("The provided Forum ID is not a valid number.", w, r, u, js) } forum, err := c.Forums.Get(fid) if err == sql.ErrNoRows { - return c.LocalErrorJSQ("The forum you're trying to edit doesn't exist.", w, r, user, js) + return c.LocalErrorJSQ("The forum you're trying to edit doesn't exist.", w, r, u, js) } else if err != nil { return c.InternalErrorJSQ(err, w, r, js) } - fname := r.PostFormValue("forum_name") - fdesc := r.PostFormValue("forum_desc") - fpreset := c.StripInvalidPreset(r.PostFormValue("forum_preset")) + name := r.PostFormValue("forum_name") + desc := r.PostFormValue("forum_desc") + preset := c.StripInvalidPreset(r.PostFormValue("forum_preset")) factive := r.PostFormValue("forum_active") active := false @@ -255,11 +255,11 @@ func ForumsEditSubmit(w http.ResponseWriter, r *http.Request, user *c.User, sfid active = true } - err = forum.Update(fname, fdesc, active, fpreset) + err = forum.Update(name, desc, active, preset) if err != nil { return c.InternalErrorJSQ(err, w, r, js) } - err = c.AdminLogs.Create("edit", fid, "forum", user.GetIP(), user.ID) + err = c.AdminLogs.Create("edit", fid, "forum", u.GetIP(), u.ID) if err != nil { return c.InternalError(err, w, r) } @@ -268,29 +268,28 @@ func ForumsEditSubmit(w http.ResponseWriter, r *http.Request, user *c.User, sfid return successRedirect("/panel/forums/", w, r, js) } -func ForumsEditPermsSubmit(w http.ResponseWriter, r *http.Request, user *c.User, sfid string) c.RouteError { - _, ferr := c.SimplePanelUserCheck(w, r, user) +func ForumsEditPermsSubmit(w http.ResponseWriter, r *http.Request, u *c.User, sfid string) c.RouteError { + _, ferr := c.SimplePanelUserCheck(w, r, u) if ferr != nil { return ferr } - if !user.Perms.ManageForums { - return c.NoPermissions(w, r, user) + if !u.Perms.ManageForums { + return c.NoPermissions(w, r, u) } js := r.PostFormValue("js") == "1" fid, err := strconv.Atoi(sfid) if err != nil { - return c.LocalErrorJSQ("The provided Forum ID is not a valid number.", w, r, user, js) + return c.LocalErrorJSQ("The provided Forum ID is not a valid number.", w, r, u, js) } - gid, err := strconv.Atoi(r.PostFormValue("gid")) if err != nil { - return c.LocalErrorJSQ("Invalid Group ID", w, r, user, js) + return c.LocalErrorJSQ("Invalid Group ID", w, r, u, js) } forum, err := c.Forums.Get(fid) if err == sql.ErrNoRows { - return c.LocalErrorJSQ("This forum doesn't exist", w, r, user, js) + return c.LocalErrorJSQ("This forum doesn't exist", w, r, u, js) } else if err != nil { return c.InternalErrorJSQ(err, w, r, js) } @@ -298,9 +297,9 @@ func ForumsEditPermsSubmit(w http.ResponseWriter, r *http.Request, user *c.User, permPreset := c.StripInvalidGroupForumPreset(r.PostFormValue("perm_preset")) err = forum.SetPreset(permPreset, gid) if err != nil { - return c.LocalErrorJSQ(err.Error(), w, r, user, js) + return c.LocalErrorJSQ(err.Error(), w, r, u, js) } - err = c.AdminLogs.Create("edit", fid, "forum", user.GetIP(), user.ID) + err = c.AdminLogs.Create("edit", fid, "forum", u.GetIP(), u.ID) if err != nil { return c.InternalError(err, w, r) } @@ -328,23 +327,23 @@ func forumPermsExtractDash(paramList string) (fid, gid int, err error) { return fid, gid, err } -func ForumsEditPermsAdvance(w http.ResponseWriter, r *http.Request, user *c.User, paramList string) c.RouteError { - basePage, ferr := buildBasePage(w, r, user, "edit_forum", "forums") +func ForumsEditPermsAdvance(w http.ResponseWriter, r *http.Request, u *c.User, paramList string) c.RouteError { + basePage, ferr := buildBasePage(w, r, u, "edit_forum", "forums") if ferr != nil { return ferr } - if !user.Perms.ManageForums { - return c.NoPermissions(w, r, user) + if !u.Perms.ManageForums { + return c.NoPermissions(w, r, u) } fid, gid, err := forumPermsExtractDash(paramList) if err != nil { - return c.LocalError(err.Error(), w, r, user) + return c.LocalError(err.Error(), w, r, u) } f, err := c.Forums.Get(fid) if err == sql.ErrNoRows { - return c.LocalError("The forum you're trying to edit doesn't exist.", w, r, user) + return c.LocalError("The forum you're trying to edit doesn't exist.", w, r, u) } else if err != nil { return c.InternalError(err, w, r) } @@ -386,24 +385,24 @@ func ForumsEditPermsAdvance(w http.ResponseWriter, r *http.Request, user *c.User return renderTemplate("panel", w, r, basePage.Header, c.Panel{basePage, "", "", "panel_forum_edit_perms", &pi}) } -func ForumsEditPermsAdvanceSubmit(w http.ResponseWriter, r *http.Request, user *c.User, paramList string) c.RouteError { - _, ferr := c.SimplePanelUserCheck(w, r, user) +func ForumsEditPermsAdvanceSubmit(w http.ResponseWriter, r *http.Request, u *c.User, paramList string) c.RouteError { + _, ferr := c.SimplePanelUserCheck(w, r, u) if ferr != nil { return ferr } - if !user.Perms.ManageForums { - return c.NoPermissions(w, r, user) + if !u.Perms.ManageForums { + return c.NoPermissions(w, r, u) } js := r.PostFormValue("js") == "1" fid, gid, err := forumPermsExtractDash(paramList) if err != nil { - return c.LocalError(err.Error(), w, r, user) + return c.LocalError(err.Error(), w, r, u) } forum, err := c.Forums.Get(fid) if err == sql.ErrNoRows { - return c.LocalError("The forum you're trying to edit doesn't exist.", w, r, user) + return c.LocalError("The forum you're trying to edit doesn't exist.", w, r, u) } else if err != nil { return c.InternalError(err, w, r) } @@ -435,9 +434,9 @@ func ForumsEditPermsAdvanceSubmit(w http.ResponseWriter, r *http.Request, user * err = forum.SetPerms(&fp, "custom", gid) if err != nil { - return c.LocalErrorJSQ(err.Error(), w, r, user, js) + return c.LocalErrorJSQ(err.Error(), w, r, u, js) } - err = c.AdminLogs.Create("edit", fid, "forum", user.GetIP(), user.ID) + err = c.AdminLogs.Create("edit", fid, "forum", u.GetIP(), u.ID) if err != nil { return c.InternalError(err, w, r) } diff --git a/routes/topic_list.go b/routes/topic_list.go index 0133187f..f2bd4b74 100644 --- a/routes/topic_list.go +++ b/routes/topic_list.go @@ -19,6 +19,21 @@ func wsTopicList(topicList []*c.TopicsRow, lastPage int) *c.WsTopicList { return &c.WsTopicList{wsTopicList, lastPage, 0} } +func wsTopicList2(topicList []*c.TopicsRow, u *c.User, fps map[int]c.QuickTools, lastPage int) *c.WsTopicList { + wsTopicList := make([]*c.WsTopicsRow, len(topicList)) + for i, t := range topicList { + var canMod bool + if fps == nil { + canMod = true + } else { + quickTools := fps[t.ParentID] + canMod = t.CreatedBy == u.ID || quickTools.CanDelete || quickTools.CanLock || quickTools.CanMove + } + wsTopicList[i] = t.WebSockets2(canMod) + } + return &c.WsTopicList{wsTopicList, lastPage, 0} +} + func TopicList(w http.ResponseWriter, r *http.Request, u *c.User, h *c.Header) c.RouteError { /*skip, rerr := h.Hooks.VhookSkippable("route_topic_list_start", w, r, u, h) if skip || rerr != nil { @@ -79,7 +94,7 @@ func TopicListCommon(w http.ResponseWriter, r *http.Request, user *c.User, h *c. var topicList []*c.TopicsRow var forumList []c.Forum var pagi c.Paginator - var canLock, ccanLock, canMove, ccanMove bool + var canDelete, ccanDelete, canLock, ccanLock, canMove, ccanMove bool q := r.FormValue("q") if q != "" && c.RepliesSearch != nil { var canSee []int @@ -152,34 +167,45 @@ func TopicListCommon(w http.ResponseWriter, r *http.Request, user *c.User, h *c. // TODO: De-dupe this logic in common/topic_list.go? //var sb strings.Builder + fps := make(map[int]c.QuickTools) for _, t := range topicList { //c.BuildTopicURLSb(&sb, c.NameToSlug(t.Title), t.ID) //t.Link = sb.String() //sb.Reset() t.Link = c.BuildTopicURL(c.NameToSlug(t.Title), t.ID) // TODO: Pass forum to something like t.Forum and use that instead of these two properties? Could be more flexible. - forum := c.Forums.DirtyGet(t.ParentID) - t.ForumName = forum.Name - t.ForumLink = forum.Link + f := c.Forums.DirtyGet(t.ParentID) + t.ForumName = f.Name + t.ForumLink = f.Link - fp, err := c.FPStore.Get(forum.ID, user.Group) - if err == c.ErrNoRows { - fp = c.BlankForumPerms() - } else if err != nil { - return c.InternalError(err, w, r) - } - if fp.Overrides && !user.IsSuperAdmin { - ccanLock = fp.CloseTopic - ccanMove = fp.MoveTopic - } else { - ccanLock = user.Perms.CloseTopic - ccanMove = user.Perms.MoveTopic - } - if ccanLock { - canLock = true - } - if ccanMove { - canMove = true + _, ok := fps[f.ID] + if !ok { + // TODO: Abstract this? + fp, err := c.FPStore.Get(f.ID, user.Group) + if err == c.ErrNoRows { + fp = c.BlankForumPerms() + } else if err != nil { + return c.InternalError(err, w, r) + } + if fp.Overrides && !user.IsSuperAdmin { + ccanDelete = fp.DeleteTopic + ccanLock = fp.CloseTopic + ccanMove = fp.MoveTopic + } else { + ccanDelete = user.Perms.DeleteTopic + ccanLock = user.Perms.CloseTopic + ccanMove = user.Perms.MoveTopic + } + if ccanDelete { + canDelete = true + } + if ccanLock { + canLock = true + } + if ccanMove { + canMove = true + } + fps[f.ID] = c.QuickTools{ccanDelete, ccanLock, ccanMove} } // TODO: Create a specialised function with a bit less overhead for getting the last page for a post count @@ -192,7 +218,7 @@ func TopicListCommon(w http.ResponseWriter, r *http.Request, user *c.User, h *c. // TODO: Reduce the amount of boilerplate here if r.FormValue("js") == "1" { - outBytes, err := wsTopicList(topicList, pagi.LastPage).MarshalJSON() + outBytes, err := wsTopicList2(topicList, user, fps, pagi.LastPage).MarshalJSON() if err != nil { return c.InternalError(err, w, r) } @@ -200,37 +226,57 @@ func TopicListCommon(w http.ResponseWriter, r *http.Request, user *c.User, h *c. return nil } + topicList2 := make([]c.TopicsRowMut, len(topicList)) + for i, t := range topicList { + var canMod bool + if fps == nil { + canMod = true + } else { + quickTools := fps[t.ParentID] + canMod = t.CreatedBy == user.ID || quickTools.CanDelete || quickTools.CanLock || quickTools.CanMove + } + topicList2[i] = c.TopicsRowMut{t, canMod} + } + h.Title = phrases.GetTitlePhrase("topics_search") - pi := c.TopicListPage{h, topicList, forumList, c.Config.DefaultForum, c.TopicListSort{torder, false}, canLock, canMove, pagi} + pi := c.TopicListPage{h, topicList2, forumList, c.Config.DefaultForum, c.TopicListSort{torder, false}, c.QuickTools{canDelete, canLock, canMove}, pagi} return renderTemplate("topics", w, r, h, pi) } // TODO: Pass a struct back rather than passing back so many variables + var fps map[int]c.QuickTools if user.IsSuperAdmin { topicList, forumList, pagi, err = c.TopicList.GetList(page, tsorder, fids) canLock, canMove = true, true } else { topicList, forumList, pagi, err = c.TopicList.GetListByGroup(group, page, tsorder, fids) - for _, forum := range forumList { - fp, err := c.FPStore.Get(forum.ID, user.Group) + fps = make(map[int]c.QuickTools) + for _, f := range forumList { + fp, err := c.FPStore.Get(f.ID, user.Group) if err == c.ErrNoRows { fp = c.BlankForumPerms() } else if err != nil { return c.InternalError(err, w, r) } if fp.Overrides { + ccanDelete = fp.DeleteTopic ccanLock = fp.CloseTopic ccanMove = fp.MoveTopic } else { + ccanDelete = user.Perms.DeleteTopic ccanLock = user.Perms.CloseTopic ccanMove = user.Perms.MoveTopic } + if ccanDelete { + canDelete = true + } if ccanLock { canLock = true } if ccanMove { canMove = true } + fps[f.ID] = c.QuickTools{ccanDelete, ccanLock, ccanMove} } } if err != nil { @@ -239,7 +285,7 @@ func TopicListCommon(w http.ResponseWriter, r *http.Request, user *c.User, h *c. // TODO: Reduce the amount of boilerplate here if r.FormValue("js") == "1" { - outBytes, err := wsTopicList(topicList, pagi.LastPage).MarshalJSON() + outBytes, err := wsTopicList2(topicList, user, fps, pagi.LastPage).MarshalJSON() if err != nil { return c.InternalError(err, w, r) } @@ -247,7 +293,19 @@ func TopicListCommon(w http.ResponseWriter, r *http.Request, user *c.User, h *c. return nil } - pi := c.TopicListPage{h, topicList, forumList, c.Config.DefaultForum, c.TopicListSort{torder, false}, canLock, canMove, pagi} + topicList2 := make([]c.TopicsRowMut, len(topicList)) + for i, t := range topicList { + var canMod bool + if fps == nil { + canMod = true + } else { + quickTools := fps[t.ParentID] + canMod = t.CreatedBy == user.ID || quickTools.CanDelete || quickTools.CanLock || quickTools.CanMove + } + topicList2[i] = c.TopicsRowMut{t, canMod} + } + + pi := c.TopicListPage{h, topicList2, forumList, c.Config.DefaultForum, c.TopicListSort{torder, false}, c.QuickTools{canDelete, canLock, canMove}, pagi} if r.FormValue("i") == "1" { return renderTemplate("topics_mini", w, r, h, pi) } diff --git a/run-nowebsockets.bat b/run-nowebsockets.bat index 4af4b0d7..4588c8d4 100644 --- a/run-nowebsockets.bat +++ b/run-nowebsockets.bat @@ -41,6 +41,9 @@ if %errorlevel% neq 0 ( exit /b %errorlevel% ) +echo Generating the JSON handlers +easyjson -pkg common + echo Building the hook generator go build -tags hookgen -ldflags="-s -w" "./cmd/hook_gen" if %errorlevel% neq 0 ( @@ -54,9 +57,6 @@ if %errorlevel% neq 0 ( exit /b %errorlevel% ) -echo Generating the JSON handlers -easyjson -pkg common - echo Building the query generator go build -ldflags="-s -w" "./cmd/query_gen" if %errorlevel% neq 0 ( diff --git a/run_mssql.bat b/run_mssql.bat index d6cab5af..8e66cc02 100644 --- a/run_mssql.bat +++ b/run_mssql.bat @@ -41,6 +41,9 @@ if %errorlevel% neq 0 ( exit /b %errorlevel% ) +echo Generating the JSON handlers +easyjson -pkg common + echo Building the hook generator go build -tags hookgen -ldflags="-s -w" "./cmd/hook_gen" if %errorlevel% neq 0 ( @@ -54,9 +57,6 @@ if %errorlevel% neq 0 ( exit /b %errorlevel% ) -echo Generating the JSON handlers -easyjson -pkg common - echo Building the query generator go build -ldflags="-s -w" "./cmd/query_gen" if %errorlevel% neq 0 ( diff --git a/run_tests.bat b/run_tests.bat index 0b392d64..3b1899e0 100644 --- a/run_tests.bat +++ b/run_tests.bat @@ -41,6 +41,9 @@ if %errorlevel% neq 0 ( exit /b %errorlevel% ) +echo Generating the JSON handlers +easyjson -pkg common + echo Building the hook generator go build -tags hookgen -ldflags="-s -w" "./cmd/hook_gen" if %errorlevel% neq 0 ( @@ -54,9 +57,6 @@ if %errorlevel% neq 0 ( exit /b %errorlevel% ) -echo Generating the JSON handlers -easyjson -pkg common - echo Building the query generator go build -ldflags="-s -w" "./cmd/query_gen" if %errorlevel% neq 0 ( diff --git a/run_tests_mssql.bat b/run_tests_mssql.bat index 12aba2bb..e6717abd 100644 --- a/run_tests_mssql.bat +++ b/run_tests_mssql.bat @@ -41,6 +41,9 @@ if %errorlevel% neq 0 ( exit /b %errorlevel% ) +echo Generating the JSON handlers +easyjson -pkg common + echo Building the hook generator go build -tags hookgen -ldflags="-s -w" "./cmd/hook_gen" if %errorlevel% neq 0 ( @@ -54,9 +57,6 @@ if %errorlevel% neq 0 ( exit /b %errorlevel% ) -echo Generating the JSON handlers -easyjson -pkg common - echo Building the query generator go build -ldflags="-s -w" "./cmd/query_gen" if %errorlevel% neq 0 ( diff --git a/templates/forum.html b/templates/forum.html index aad56ad1..60e5b3e8 100644 --- a/templates/forum.html +++ b/templates/forum.html @@ -47,7 +47,7 @@ {{end}} {{end}}
- {{range .ItemList}}
+ {{range .ItemList}}
diff --git a/templates/panel_pages.html b/templates/panel_pages.html index fbd169f1..69a30f9d 100644 --- a/templates/panel_pages.html +++ b/templates/panel_pages.html @@ -1,13 +1,13 @@

{{lang "panel_pages_head"}}

-
+
{{range .ItemList}} {{else}} @@ -21,22 +21,22 @@

{{lang "panel_pages_create_head"}}

-
+
- +
-
+
\ No newline at end of file diff --git a/templates/panel_themes_menus_item_edit.html b/templates/panel_themes_menus_item_edit.html index fd20c05a..2f4deb04 100644 --- a/templates/panel_themes_menus_item_edit.html +++ b/templates/panel_themes_menus_item_edit.html @@ -3,20 +3,20 @@

{{lang "panel_themes_menus_edit_head"}}

-
-
+ +
{{/** TODO: Let an admin move a menu item from one menu to another? **/}}
- +
diff --git a/templates/topics_inner.html b/templates/topics_inner.html index 22a2a9f5..251b2f3c 100644 --- a/templates/topics_inner.html +++ b/templates/topics_inner.html @@ -30,7 +30,7 @@
{{if .CurrentUser.Loggedin}} - {{template "topics_mod_floater.html" .}} + {{template "topics_mod_floater.html" . }} {{if .ForumList}} {{/** TODO: Have a seperate forum list for moving topics? Maybe an AJAX forum search compatible with plugin_guilds? **/}} @@ -52,7 +52,7 @@
-
+
{{lang
diff --git a/templates/topics_mod_floater.html b/templates/topics_mod_floater.html index c9ed277c..f06d07e3 100644 --- a/templates/topics_mod_floater.html +++ b/templates/topics_mod_floater.html @@ -6,9 +6,9 @@
diff --git a/templates/topics_topic.html b/templates/topics_topic.html index 26eb7fff..421c3e45 100644 --- a/templates/topics_topic.html +++ b/templates/topics_topic.html @@ -1,4 +1,4 @@ -
+
diff --git a/themes/cosora/public/main.css b/themes/cosora/public/main.css index 9ad41254..e52bc2eb 100644 --- a/themes/cosora/public/main.css +++ b/themes/cosora/public/main.css @@ -907,7 +907,11 @@ textarea { .topic_sticky .topic_left, .topic_sticky .topic_right { border-bottom: 2px solid hsl(51, 60%, 70%); } -.topics_moderate .topic_row:hover .topic_left, .topics_moderate .topic_row:hover .topic_right { +.topics_moderate .topic_row:not(.can_mod) .topic_left, +.topics_moderate .topic_row:not(.can_mod) .topic_right { + background-color: #EEEEEE; +} +.topics_moderate .can_mod:hover .topic_left, .topics_moderate .can_mod:hover .topic_right { background-color: hsl(81, 60%, 97%); } .topic_selected .topic_left, .topic_selected .topic_right { diff --git a/themes/nox/overrides/topics_topic.html b/themes/nox/overrides/topics_topic.html index 639ceb5b..3a3f6c8f 100644 --- a/themes/nox/overrides/topics_topic.html +++ b/themes/nox/overrides/topics_topic.html @@ -1,4 +1,4 @@ -
+
diff --git a/themes/nox/public/main.css b/themes/nox/public/main.css index 7110bdad..00459ba9 100644 --- a/themes/nox/public/main.css +++ b/themes/nox/public/main.css @@ -228,7 +228,10 @@ li a { .sidebar .rowblock:not(.topic_list):not(.rowhead):not(.opthead) .rowitem, .sidebar .search { margin-left: 12px; } -.topics_moderate .topic_row:hover { +.topics_moderate .can_mod { + background-color: #4d4d4d; +} +.topics_moderate .can_mod:hover { background-color: rgb(78, 78, 98); } .widget_search:first-child { @@ -552,7 +555,7 @@ h2 { .topic_closed { background-color: #4b4b4b; } -.topic_selected { +.topic_row.topic_selected { background-color: rgb(68, 68, 88); } .new_item .topic_left { diff --git a/themes/shadow/public/main.css b/themes/shadow/public/main.css index 3651ca2f..cbec7cb9 100644 --- a/themes/shadow/public/main.css +++ b/themes/shadow/public/main.css @@ -855,12 +855,18 @@ input[type=checkbox]:checked + label.poll_option_label .sel { .topic_list .topic_row { display: flex; } -.topics_moderate .topic_row:hover .rowitem { - background-color: hsla(0, 0%, 27%, 1); +.topics_moderate .topic_row:not(.can_mod) .rowitem { + background-color: hsla(0, 0%, 22%, 1); } -.topic_selected .rowitem { +.topics_moderate .can_mod .rowitem { + background-color: hsla(0, 0%, 25%, 1); +} +.topics_moderate .can_mod:hover .rowitem { background-color: hsla(0, 0%, 29%, 1); } +.topic_row.topic_selected .rowitem { + background-color: hsla(0, 0%, 31%, 1); +} /* Temporary hack, so that I don't break the topic lists of the other themes */ .topic_list .topic_inner_right { display: none;