@@ -851,77 +871,77 @@ var topics_16 = []byte(`
`)
-var topics_17 = []byte(`
+var topics_20 = []byte(`
`)
-var topics_18 = []byte(`
+var topics_21 = []byte(`
diff --git a/common/extend.go b/common/extend.go index 2b9896ea..ae2f0428 100644 --- a/common/extend.go +++ b/common/extend.go @@ -112,7 +112,7 @@ var PreRenderHooks = map[string][]func(http.ResponseWriter, *http.Request, *User "pre_render_panel_edit_group": nil, "pre_render_panel_edit_group_perms": nil, "pre_render_panel_themes": nil, - "pre_render_panel_mod_log": nil, + "pre_render_panel_modlogs": nil, "pre_render_error": nil, // Note: This hook isn't run for a few errors whose templates are computed at startup and reused, such as InternalError. This hook is also not available in JS mode. "pre_render_security_error": nil, diff --git a/common/tasks.go b/common/tasks.go index aaf36dc3..c7c616b6 100644 --- a/common/tasks.go +++ b/common/tasks.go @@ -19,6 +19,7 @@ type TaskStmts struct { getSync *sql.Stmt } +var ScheduledHalfSecondTasks []func() error var ScheduledSecondTasks []func() error var ScheduledFifteenMinuteTasks []func() error var ShutdownTasks []func() error @@ -37,6 +38,11 @@ func init() { }) } +// AddScheduledHalfSecondTask is not concurrency safe +func AddScheduledHalfSecondTask(task func() error) { + ScheduledHalfSecondTasks = append(ScheduledHalfSecondTasks, task) +} + // AddScheduledSecondTask is not concurrency safe func AddScheduledSecondTask(task func() error) { ScheduledSecondTasks = append(ScheduledSecondTasks, task) diff --git a/gen_mssql.go b/gen_mssql.go index 9ab0b781..881f07f6 100644 --- a/gen_mssql.go +++ b/gen_mssql.go @@ -15,6 +15,7 @@ type Stmts struct { isThemeDefault *sql.Stmt getModlogs *sql.Stmt getModlogsOffset *sql.Stmt + getAdminlogsOffset *sql.Stmt getReplyTID *sql.Stmt getTopicFID *sql.Stmt getUserReplyUID *sql.Stmt @@ -122,6 +123,13 @@ func _gen_mssql() (err error) { return err } + log.Print("Preparing getAdminlogsOffset statement.") + stmts.getAdminlogsOffset, err = db.Prepare("SELECT [action],[elementID],[elementType],[ipaddress],[actorID],[doneAt] FROM [administration_logs] ORDER BY doneAt DESC OFFSET ?1 ROWS FETCH NEXT ?2 ROWS ONLY") + if err != nil { + log.Print("Bad Query: ","SELECT [action],[elementID],[elementType],[ipaddress],[actorID],[doneAt] FROM [administration_logs] ORDER BY doneAt DESC OFFSET ?1 ROWS FETCH NEXT ?2 ROWS ONLY") + return err + } + log.Print("Preparing getReplyTID statement.") stmts.getReplyTID, err = db.Prepare("SELECT [tid] FROM [replies] WHERE [rid] = ?1") if err != nil { diff --git a/gen_mysql.go b/gen_mysql.go index b771e4fc..caffba4d 100644 --- a/gen_mysql.go +++ b/gen_mysql.go @@ -17,6 +17,7 @@ type Stmts struct { isThemeDefault *sql.Stmt getModlogs *sql.Stmt getModlogsOffset *sql.Stmt + getAdminlogsOffset *sql.Stmt getReplyTID *sql.Stmt getTopicFID *sql.Stmt getUserReplyUID *sql.Stmt @@ -118,6 +119,12 @@ func _gen_mysql() (err error) { return err } + log.Print("Preparing getAdminlogsOffset statement.") + stmts.getAdminlogsOffset, err = db.Prepare("SELECT `action`,`elementID`,`elementType`,`ipaddress`,`actorID`,`doneAt` FROM `administration_logs` ORDER BY doneAt DESC LIMIT ?,?") + if err != nil { + return err + } + log.Print("Preparing getReplyTID statement.") stmts.getReplyTID, err = db.Prepare("SELECT `tid` FROM `replies` WHERE `rid` = ?") if err != nil { diff --git a/gen_router.go b/gen_router.go index d888058f..0477f5d3 100644 --- a/gen_router.go +++ b/gen_router.go @@ -233,6 +233,9 @@ var agentMapEnum = map[string]int{ "bing": 9, "baidu": 10, "duckduckgo": 11, + "discord": 12, + "lynx": 13, + "blank": 14, } var reverseAgentMapEnum = map[int]string{ 0: "unknown", @@ -247,6 +250,9 @@ var reverseAgentMapEnum = map[int]string{ 9: "bing", 10: "baidu", 11: "duckduckgo", + 12: "discord", + 13: "lynx", + 14: "blank", } // TODO: Stop spilling these into the package scope? @@ -344,7 +350,7 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) { // Track the user agents. Unfortunately, everyone pretends to be Mozilla, so this'll be a little less efficient than I would like. // TODO: Add a setting to disable this? // TODO: Use a more efficient detector instead of smashing every possible combination in - ua := strings.TrimSuffix(strings.TrimPrefix(req.UserAgent(),"Mozilla/5.0 ")," Safari/537.36") // Noise, no one's going to be running this and it complicates implementing an efficient UA parser, particularly the more efficient right-to-left one I have in mind + ua := strings.TrimSpace(strings.TrimSuffix(strings.TrimPrefix(req.UserAgent(),"Mozilla/5.0 ")," Safari/537.36")) // Noise, no one's going to be running this and it complicates implementing an efficient UA parser, particularly the more efficient right-to-left one I have in mind switch { case strings.Contains(ua,"Google"): common.AgentViewCounter.Bump(7) @@ -368,6 +374,12 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) { common.AgentViewCounter.Bump(10) case strings.Contains(ua,"DuckDuckBot"): common.AgentViewCounter.Bump(11) + case strings.Contains(ua,"Discordbot"): + common.AgentViewCounter.Bump(12) + case strings.Contains(ua,"Lynx"): + common.AgentViewCounter.Bump(13) + case ua == "": + common.AgentViewCounter.Bump(14) default: common.AgentViewCounter.Bump(0) if common.Dev.DebugMode { diff --git a/langs/english.json b/langs/english.json index 676c94b6..18c0cf03 100644 --- a/langs/english.json +++ b/langs/english.json @@ -69,23 +69,24 @@ "register":"Registration", "ip-search":"IP Search", - "panel-dashboard":"Control Panel Dashboard", - "panel-forums":"Forum Manager", - "panel-delete-forum":"Delete Forum", - "panel-edit-forum":"Forum Editor", - "panel-analytics":"Analytics", - "panel-settings":"Setting Manager", - "panel-edit-setting":"Edit Setting", - "panel-word-filters":"Word Filter Manager", - "panel-edit-word-filter":"Edit Word Filter", - "panel-plugins":"Plugin Manager", - "panel-users":"User Manager", - "panel-edit-user":"User Editor", - "panel-groups":"Group Manager", - "panel-edit-group":"Group Editor", - "panel-themes":"Theme Manager", - "panel-backups":"Backups", - "panel-mod-logs":"Moderation Logs", - "panel-debug":"Debug" + "panel_dashboard":"Control Panel Dashboard", + "panel_forums":"Forum Manager", + "panel_delete_forum":"Delete Forum", + "panel_edit_forum":"Forum Editor", + "panel_analytics":"Analytics", + "panel_settings":"Setting Manager", + "panel_edit_setting":"Edit Setting", + "panel_word_filters":"Word Filter Manager", + "panel_edit_word_filter":"Edit Word Filter", + "panel_plugins":"Plugin Manager", + "panel_users":"User Manager", + "panel_edit_user":"User Editor", + "panel_groups":"Group Manager", + "panel_edit_group":"Group Editor", + "panel_themes":"Theme Manager", + "panel_backups":"Backups", + "panel_mod_logs":"Moderation Logs", + "panel_admin_logs":"Administration Logs", + "panel_debug":"Debug" } } \ No newline at end of file diff --git a/main.go b/main.go index ca50706d..1cccba3d 100644 --- a/main.go +++ b/main.go @@ -246,13 +246,18 @@ func main() { } } - // Run this goroutine once a second + // Run this goroutine once every half second + halfSecondTicker := time.NewTicker(time.Second / 2) secondTicker := time.NewTicker(1 * time.Second) fifteenMinuteTicker := time.NewTicker(15 * time.Minute) //hourTicker := time.NewTicker(1 * time.Hour) go func() { for { select { + case <-halfSecondTicker.C: + // TODO: Add a plugin hook here + runTasks(common.ScheduledHalfSecondTasks) + // TODO: Add a plugin hook here case <-secondTicker.C: // TODO: Add a plugin hook here runTasks(common.ScheduledSecondTasks) @@ -291,13 +296,14 @@ func main() { } }() + // TODO: Move these routes into the new routes list log.Print("Initialising the router") router = NewGenRouter(http.FileServer(http.Dir("./uploads"))) router.HandleFunc("/topic/create/submit/", routeTopicCreateSubmit) router.HandleFunc("/topic/", routeTopicID) router.HandleFunc("/reply/create/", routeCreateReply) - //router.HandleFunc("/reply/edit/", routeReplyEdit) - //router.HandleFunc("/reply/delete/", routeReplyDelete) + //router.HandleFunc("/reply/edit/", routeReplyEdit) // No js fallback + //router.HandleFunc("/reply/delete/", routeReplyDelete) // No js confirmation page? We could have a confirmation modal for the JS case router.HandleFunc("/reply/edit/submit/", routeReplyEditSubmit) router.HandleFunc("/reply/delete/submit/", routeReplyDeleteSubmit) router.HandleFunc("/reply/like/submit/", routeReplyLikeSubmit) @@ -307,6 +313,7 @@ func main() { router.HandleFunc("/topic/unstick/submit/", routeUnstickTopic) router.HandleFunc("/topic/lock/submit/", routeLockTopic) router.HandleFunc("/topic/unlock/submit/", routeUnlockTopic) + router.HandleFunc("/topic/move/submit/", routeMoveTopic) router.HandleFunc("/topic/like/submit/", routeLikeTopic) // Accounts diff --git a/mod_routes.go b/mod_routes.go index 2c223c59..e163099a 100644 --- a/mod_routes.go +++ b/mod_routes.go @@ -312,6 +312,48 @@ func routeUnlockTopic(w http.ResponseWriter, r *http.Request, user common.User) return nil } +func routeMoveTopic(w http.ResponseWriter, r *http.Request, user common.User) common.RouteError { + return common.NoPermissions(w, r, user) + + tid, err := strconv.Atoi(r.URL.Path[len("/topic/move/submit/"):]) + if err != nil { + return common.PreError("The provided TopicID is not a valid number.", w, r) + } + + topic, err := common.Topics.Get(tid) + if err == ErrNoRows { + return common.PreError("The topic you tried to move doesn't exist.", w, r) + } else if err != nil { + return common.InternalError(err, w, r) + } + + // TODO: Add hooks to make use of headerLite + _, ferr := common.SimpleForumUserCheck(w, r, &user, topic.ParentID) + if ferr != nil { + return ferr + } + if !user.Perms.ViewTopic { // TODO: MoveTopic permission? + return common.NoPermissions(w, r, user) + } + + err = topic.Unlock() + if err != nil { + return common.InternalError(err, w, r) + } + + err = common.ModLogs.Create("move", tid, "topic", user.LastIP, user.ID) + if err != nil { + return common.InternalError(err, w, r) + } + err = topic.CreateActionReply("move", user.LastIP, user) + if err != nil { + return common.InternalError(err, w, r) + } + + http.Redirect(w, r, "/topic/"+strconv.Itoa(tid), http.StatusSeeOther) + return nil +} + // TODO: Disable stat updates in posts handled by plugin_guilds // TODO: Update the stats after edits so that we don't under or over decrement stats during deletes func routeReplyEditSubmit(w http.ResponseWriter, r *http.Request, user common.User) common.RouteError { diff --git a/panel_routes.go b/panel_routes.go index 2607ab67..b02bab95 100644 --- a/panel_routes.go +++ b/panel_routes.go @@ -30,20 +30,18 @@ func panelSuccessRedirect(dest string, w http.ResponseWriter, r *http.Request, i } return nil } - -// TODO: Implement this properly -/*func panelRenderTemplate(tmplName string, w http.ResponseWriter, r *http.Request, user common.User, pi interface{}) common.RouteError { +func panelRenderTemplate(tmplName string, w http.ResponseWriter, r *http.Request, user common.User, pi interface{}) common.RouteError { if common.PreRenderHooks["pre_render_"+tmplName] != nil { - if common.RunPreRenderHook("pre_render_"+tmplName, w, r, &user, &pi) { + if common.RunPreRenderHook("pre_render_"+tmplName, w, r, &user, pi) { return nil } } - err = common.Templates.ExecuteTemplate(w, tmplName+".html", pi) + err := common.Templates.ExecuteTemplate(w, tmplName+".html", pi) if err != nil { return common.InternalError(err, w, r) } return nil -}*/ +} func routePanel(w http.ResponseWriter, r *http.Request, user common.User) common.RouteError { headerVars, stats, ferr := common.PanelUserCheck(w, r, &user) @@ -170,18 +168,8 @@ func routePanel(w http.ResponseWriter, r *http.Request, user common.User) common gridElements = append(gridElements, common.GridElement{"dash-postsperuser", "5 posts / user / week", 14, "grid_stat stat_disabled", "", "", "Coming Soon!" /*"The average number of posts made by each active user over the past week"*/}) } - pi := common.PanelDashboardPage{common.GetTitlePhrase("panel-dashboard"), user, headerVars, stats, "dashboard", gridElements} - if common.PreRenderHooks["pre_render_panel_dashboard"] != nil { - if common.RunPreRenderHook("pre_render_panel_dashboard", w, r, &user, &pi) { - return nil - } - } - err = common.Templates.ExecuteTemplate(w, "panel_dashboard.html", pi) - if err != nil { - return common.InternalError(err, w, r) - } - return nil - //return panelRenderTemplate("panel_dashboard",w,r,user,pi) + pi := common.PanelDashboardPage{common.GetTitlePhrase("panel_dashboard"), user, headerVars, stats, "dashboard", gridElements} + return panelRenderTemplate("panel_dashboard", w, r, user, &pi) } func routePanelForums(w http.ResponseWriter, r *http.Request, user common.User) common.RouteError { @@ -210,18 +198,8 @@ func routePanelForums(w http.ResponseWriter, r *http.Request, user common.User) forumList = append(forumList, fadmin) } } - pi := common.PanelPage{common.GetTitlePhrase("panel-forums"), user, headerVars, stats, "forums", forumList, nil} - if common.PreRenderHooks["pre_render_panel_forums"] != nil { - if common.RunPreRenderHook("pre_render_panel_forums", w, r, &user, &pi) { - return nil - } - } - err = common.Templates.ExecuteTemplate(w, "panel-forums.html", pi) - if err != nil { - return common.InternalError(err, w, r) - } - - return nil + pi := common.PanelPage{common.GetTitlePhrase("panel_forums"), user, headerVars, stats, "forums", forumList, nil} + return panelRenderTemplate("panel_forums", w, r, user, &pi) } func routePanelForumsCreateSubmit(w http.ResponseWriter, r *http.Request, user common.User) common.RouteError { @@ -274,7 +252,7 @@ func routePanelForumsDelete(w http.ResponseWriter, r *http.Request, user common. confirmMsg := "Are you sure you want to delete the '" + forum.Name + "' forum?" yousure := common.AreYouSure{"/panel/forums/delete/submit/" + strconv.Itoa(fid), confirmMsg} - pi := common.PanelPage{common.GetTitlePhrase("panel-delete-forum"), user, headerVars, stats, "forums", tList, yousure} + pi := common.PanelPage{common.GetTitlePhrase("panel_delete_forum"), user, headerVars, stats, "forums", tList, yousure} if common.PreRenderHooks["pre_render_panel_delete_forum"] != nil { if common.RunPreRenderHook("pre_render_panel_delete_forum", w, r, &user, &pi) { return nil @@ -351,7 +329,7 @@ func routePanelForumsEdit(w http.ResponseWriter, r *http.Request, user common.Us gplist = append(gplist, common.GroupForumPermPreset{group, common.ForumPermsToGroupForumPreset(group.Forums[fid])}) } - pi := common.PanelEditForumPage{common.GetTitlePhrase("panel-edit-forum"), user, headerVars, stats, "forums", forum.ID, forum.Name, forum.Desc, forum.Active, forum.Preset, gplist} + pi := common.PanelEditForumPage{common.GetTitlePhrase("panel_edit_forum"), user, headerVars, stats, "forums", forum.ID, forum.Name, forum.Desc, forum.Active, forum.Preset, gplist} if common.PreRenderHooks["pre_render_panel_edit_forum"] != nil { if common.RunPreRenderHook("pre_render_panel_edit_forum", w, r, &user, &pi) { return nil @@ -513,7 +491,7 @@ func routePanelForumsEditPermsAdvance(w http.ResponseWriter, r *http.Request, us addNameLangToggle("PinTopic", forumPerms.PinTopic) addNameLangToggle("CloseTopic", forumPerms.CloseTopic) - pi := common.PanelEditForumGroupPage{common.GetTitlePhrase("panel-edit-forum"), user, headerVars, stats, "forums", forum.ID, gid, forum.Name, forum.Desc, forum.Active, forum.Preset, formattedPermList} + pi := common.PanelEditForumGroupPage{common.GetTitlePhrase("panel_edit_forum"), user, headerVars, stats, "forums", forum.ID, gid, forum.Name, forum.Desc, forum.Active, forum.Preset, formattedPermList} if common.PreRenderHooks["pre_render_panel_edit_forum"] != nil { if common.RunPreRenderHook("pre_render_panel_edit_forum", w, r, &user, &pi) { return nil @@ -596,6 +574,12 @@ func routePanelAnalyticsViews(w http.ResponseWriter, r *http.Request, user commo var timeRange = "six-hours" switch r.FormValue("timeRange") { + case "two-days": // Two days is experimental + timeQuantity = 2 + timeUnit = "day" + timeSlices = 24 + sliceWidth = 60 * 60 * 2 + timeRange = "two-days" case "one-day": timeQuantity = 1 timeUnit = "day" @@ -668,7 +652,7 @@ func routePanelAnalyticsViews(w http.ResponseWriter, r *http.Request, user commo graph := common.PanelTimeGraph{Series: viewList, Labels: labelList} log.Printf("graph: %+v\n", graph) - pi := common.PanelAnalyticsPage{common.GetTitlePhrase("panel-analytics"), user, headerVars, stats, "analytics", graph, viewItems, timeRange} + pi := common.PanelAnalyticsPage{common.GetTitlePhrase("panel_analytics"), user, headerVars, stats, "analytics", graph, viewItems, timeRange} if common.PreRenderHooks["pre_render_panel_analytics"] != nil { if common.RunPreRenderHook("pre_render_panel_analytics", w, r, &user, &pi) { return nil @@ -696,6 +680,12 @@ func routePanelAnalyticsRouteViews(w http.ResponseWriter, r *http.Request, user var timeRange = "six-hours" switch r.FormValue("timeRange") { + case "two-days": // Two days is experimental + timeQuantity = 2 + timeUnit = "day" + timeSlices = 24 + sliceWidth = 60 * 60 * 2 + timeRange = "two-days" case "one-day": timeQuantity = 1 timeUnit = "day" @@ -767,17 +757,8 @@ func routePanelAnalyticsRouteViews(w http.ResponseWriter, r *http.Request, user graph := common.PanelTimeGraph{Series: viewList, Labels: labelList} log.Printf("graph: %+v\n", graph) - pi := common.PanelAnalyticsRoutePage{common.GetTitlePhrase("panel-analytics"), user, headerVars, stats, "analytics", html.EscapeString(route), graph, timeRange} - if common.PreRenderHooks["pre_render_panel_analytics_route_views"] != nil { - if common.RunPreRenderHook("pre_render_panel_analytics_route_views", w, r, &user, &pi) { - return nil - } - } - err = common.Templates.ExecuteTemplate(w, "panel-analytics-route-views.html", pi) - if err != nil { - return common.InternalError(err, w, r) - } - return nil + pi := common.PanelAnalyticsRoutePage{common.GetTitlePhrase("panel_analytics"), user, headerVars, stats, "analytics", html.EscapeString(route), graph, timeRange} + return panelRenderTemplate("panel_analytics_route_views", w, r, user, &pi) } func routePanelAnalyticsAgentViews(w http.ResponseWriter, r *http.Request, user common.User, agent string) common.RouteError { @@ -795,6 +776,12 @@ func routePanelAnalyticsAgentViews(w http.ResponseWriter, r *http.Request, user var timeRange = "six-hours" switch r.FormValue("timeRange") { + case "two-days": // Two days is experimental + timeQuantity = 2 + timeUnit = "day" + timeSlices = 24 + sliceWidth = 60 * 60 * 2 + timeRange = "two-days" case "one-day": timeQuantity = 1 timeUnit = "day" @@ -866,17 +853,8 @@ func routePanelAnalyticsAgentViews(w http.ResponseWriter, r *http.Request, user graph := common.PanelTimeGraph{Series: viewList, Labels: labelList} log.Printf("graph: %+v\n", graph) - pi := common.PanelAnalyticsAgentPage{common.GetTitlePhrase("panel-analytics"), user, headerVars, stats, "analytics", html.EscapeString(agent), graph, timeRange} - if common.PreRenderHooks["pre_render_panel_analytics_agent_views"] != nil { - if common.RunPreRenderHook("pre_render_panel_analytics_agent_views", w, r, &user, &pi) { - return nil - } - } - err = common.Templates.ExecuteTemplate(w, "panel-analytics-agent-views.html", pi) - if err != nil { - return common.InternalError(err, w, r) - } - return nil + pi := common.PanelAnalyticsAgentPage{common.GetTitlePhrase("panel_analytics"), user, headerVars, stats, "analytics", html.EscapeString(agent), graph, timeRange} + return panelRenderTemplate("panel_analytics_agent_views", w, r, user, &pi) } func routePanelAnalyticsRoutes(w http.ResponseWriter, r *http.Request, user common.User) common.RouteError { @@ -919,17 +897,8 @@ func routePanelAnalyticsRoutes(w http.ResponseWriter, r *http.Request, user comm }) } - pi := common.PanelAnalyticsRoutesPage{common.GetTitlePhrase("panel-analytics"), user, headerVars, stats, "analytics", routeItems} - if common.PreRenderHooks["pre_render_panel_analytics_routes"] != nil { - if common.RunPreRenderHook("pre_render_panel_analytics_routes", w, r, &user, &pi) { - return nil - } - } - err = common.Templates.ExecuteTemplate(w, "panel-analytics-routes.html", pi) - if err != nil { - return common.InternalError(err, w, r) - } - return nil + pi := common.PanelAnalyticsRoutesPage{common.GetTitlePhrase("panel_analytics"), user, headerVars, stats, "analytics", routeItems} + return panelRenderTemplate("panel_analytics_routes", w, r, user, &pi) } func routePanelAnalyticsAgents(w http.ResponseWriter, r *http.Request, user common.User) common.RouteError { @@ -972,17 +941,8 @@ func routePanelAnalyticsAgents(w http.ResponseWriter, r *http.Request, user comm }) } - pi := common.PanelAnalyticsAgentsPage{common.GetTitlePhrase("panel-analytics"), user, headerVars, stats, "analytics", agentItems} - if common.PreRenderHooks["pre_render_panel_analytics_agents"] != nil { - if common.RunPreRenderHook("pre_render_panel_analytics_agents", w, r, &user, &pi) { - return nil - } - } - err = common.Templates.ExecuteTemplate(w, "panel-analytics-agents.html", pi) - if err != nil { - return common.InternalError(err, w, r) - } - return nil + pi := common.PanelAnalyticsAgentsPage{common.GetTitlePhrase("panel_analytics"), user, headerVars, stats, "analytics", agentItems} + return panelRenderTemplate("panel_analytics_agents", w, r, user, &pi) } func routePanelSettings(w http.ResponseWriter, r *http.Request, user common.User) common.RouteError { @@ -1021,17 +981,8 @@ func routePanelSettings(w http.ResponseWriter, r *http.Request, user common.User settingList[setting.Name] = setting.Content } - pi := common.PanelPage{common.GetTitlePhrase("panel-settings"), user, headerVars, stats, "settings", tList, settingList} - if common.PreRenderHooks["pre_render_panel_settings"] != nil { - if common.RunPreRenderHook("pre_render_panel_settings", w, r, &user, &pi) { - return nil - } - } - err = common.Templates.ExecuteTemplate(w, "panel-settings.html", pi) - if err != nil { - return common.InternalError(err, w, r) - } - return nil + pi := common.PanelPage{common.GetTitlePhrase("panel_settings"), user, headerVars, stats, "settings", tList, settingList} + return panelRenderTemplate("panel_settings", w, r, user, &pi) } func routePanelSettingEdit(w http.ResponseWriter, r *http.Request, user common.User, sname string) common.RouteError { @@ -1067,17 +1018,8 @@ func routePanelSettingEdit(w http.ResponseWriter, r *http.Request, user common.U } } - pi := common.PanelPage{common.GetTitlePhrase("panel-edit-setting"), user, headerVars, stats, "settings", itemList, setting} - if common.PreRenderHooks["pre_render_panel_setting"] != nil { - if common.RunPreRenderHook("pre_render_panel_setting", w, r, &user, &pi) { - return nil - } - } - err = common.Templates.ExecuteTemplate(w, "panel-setting.html", pi) - if err != nil { - return common.InternalError(err, w, r) - } - return nil + pi := common.PanelPage{common.GetTitlePhrase("panel_edit_setting"), user, headerVars, stats, "settings", itemList, setting} + return panelRenderTemplate("panel_setting", w, r, user, &pi) } func routePanelSettingEditSubmit(w http.ResponseWriter, r *http.Request, user common.User, sname string) common.RouteError { @@ -1112,17 +1054,8 @@ func routePanelWordFilters(w http.ResponseWriter, r *http.Request, user common.U } var filterList = common.WordFilterBox.Load().(common.WordFilterMap) - pi := common.PanelPage{common.GetTitlePhrase("panel-word-filters"), user, headerVars, stats, "word-filters", tList, filterList} - if common.PreRenderHooks["pre_render_panel_word_filters"] != nil { - if common.RunPreRenderHook("pre_render_panel_word_filters", w, r, &user, &pi) { - return nil - } - } - err := common.Templates.ExecuteTemplate(w, "panel-word-filters.html", pi) - if err != nil { - return common.InternalError(err, w, r) - } - return nil + pi := common.PanelPage{common.GetTitlePhrase("panel_word_filters"), user, headerVars, stats, "word-filters", tList, filterList} + return panelRenderTemplate("panel_word_filters", w, r, user, &pi) } func routePanelWordFiltersCreate(w http.ResponseWriter, r *http.Request, user common.User) common.RouteError { @@ -1156,6 +1089,7 @@ func routePanelWordFiltersCreate(w http.ResponseWriter, r *http.Request, user co return panelSuccessRedirect("/panel/settings/word-filters/", w, r, isJs) } +// TODO: Implement this as a non-JS fallback func routePanelWordFiltersEdit(w http.ResponseWriter, r *http.Request, user common.User, wfid string) common.RouteError { headerVars, stats, ferr := common.PanelUserCheck(w, r, &user) if ferr != nil { @@ -1167,17 +1101,8 @@ func routePanelWordFiltersEdit(w http.ResponseWriter, r *http.Request, user comm _ = wfid - pi := common.PanelPage{common.GetTitlePhrase("panel-edit-word-filter"), user, headerVars, stats, "word-filters", tList, nil} - if common.PreRenderHooks["pre_render_panel_word_filters_edit"] != nil { - if common.RunPreRenderHook("pre_render_panel_word_filters_edit", w, r, &user, &pi) { - return nil - } - } - err := common.Templates.ExecuteTemplate(w, "panel-word-filters-edit.html", pi) - if err != nil { - return common.InternalError(err, w, r) - } - return nil + pi := common.PanelPage{common.GetTitlePhrase("panel_edit_word_filter"), user, headerVars, stats, "word-filters", tList, nil} + return panelRenderTemplate("panel_word_filters_edit", w, r, user, &pi) } func routePanelWordFiltersEditSubmit(w http.ResponseWriter, r *http.Request, user common.User, wfid string) common.RouteError { @@ -1260,17 +1185,8 @@ func routePanelPlugins(w http.ResponseWriter, r *http.Request, user common.User) pluginList = append(pluginList, plugin) } - pi := common.PanelPage{common.GetTitlePhrase("panel-plugins"), user, headerVars, stats, "plugins", pluginList, nil} - if common.PreRenderHooks["pre_render_panel_plugins"] != nil { - if common.RunPreRenderHook("pre_render_panel_plugins", w, r, &user, &pi) { - return nil - } - } - err := common.Templates.ExecuteTemplate(w, "panel-plugins.html", pi) - if err != nil { - return common.InternalError(err, w, r) - } - return nil + pi := common.PanelPage{common.GetTitlePhrase("panel_plugins"), user, headerVars, stats, "plugins", pluginList, nil} + return panelRenderTemplate("panel_plugins", w, r, user, &pi) } func routePanelPluginsActivate(w http.ResponseWriter, r *http.Request, user common.User, uname string) common.RouteError { @@ -1481,17 +1397,8 @@ func routePanelUsers(w http.ResponseWriter, r *http.Request, user common.User) c } pageList := common.Paginate(stats.Users, perPage, 5) - pi := common.PanelUserPage{common.GetTitlePhrase("panel-users"), user, headerVars, stats, "users", userList, pageList, page, lastPage} - if common.PreRenderHooks["pre_render_panel_users"] != nil { - if common.RunPreRenderHook("pre_render_panel_users", w, r, &user, &pi) { - return nil - } - } - err = common.Templates.ExecuteTemplate(w, "panel-users.html", pi) - if err != nil { - return common.InternalError(err, w, r) - } - return nil + pi := common.PanelUserPage{common.GetTitlePhrase("panel_users"), user, headerVars, stats, "users", userList, pageList, page, lastPage} + return panelRenderTemplate("panel_users", w, r, user, &pi) } func routePanelUsersEdit(w http.ResponseWriter, r *http.Request, user common.User, suid string) common.RouteError { @@ -1536,7 +1443,7 @@ func routePanelUsersEdit(w http.ResponseWriter, r *http.Request, user common.Use groupList = append(groupList, group) } - pi := common.PanelPage{common.GetTitlePhrase("panel-edit-user"), user, headerVars, stats, "users", groupList, targetUser} + pi := common.PanelPage{common.GetTitlePhrase("panel_edit_user"), user, headerVars, stats, "users", groupList, targetUser} if common.PreRenderHooks["pre_render_panel_edit_user"] != nil { if common.RunPreRenderHook("pre_render_panel_edit_user", w, r, &user, &pi) { return nil @@ -1679,18 +1586,8 @@ func routePanelGroups(w http.ResponseWriter, r *http.Request, user common.User) //log.Printf("groupList: %+v\n", groupList) pageList := common.Paginate(stats.Groups, perPage, 5) - pi := common.PanelGroupPage{common.GetTitlePhrase("panel-groups"), user, headerVars, stats, "groups", groupList, pageList, page, lastPage} - if common.PreRenderHooks["pre_render_panel_groups"] != nil { - if common.RunPreRenderHook("pre_render_panel_groups", w, r, &user, &pi) { - return nil - } - } - - err := common.Templates.ExecuteTemplate(w, "panel-groups.html", pi) - if err != nil { - return common.InternalError(err, w, r) - } - return nil + pi := common.PanelGroupPage{common.GetTitlePhrase("panel_groups"), user, headerVars, stats, "groups", groupList, pageList, page, lastPage} + return panelRenderTemplate("panel_groups", w, r, user, &pi) } func routePanelGroupsEdit(w http.ResponseWriter, r *http.Request, user common.User, sgid string) common.RouteError { @@ -1738,7 +1635,7 @@ func routePanelGroupsEdit(w http.ResponseWriter, r *http.Request, user common.Us disableRank := !user.Perms.EditGroupGlobalPerms || (group.ID == 6) - pi := common.PanelEditGroupPage{common.GetTitlePhrase("panel-edit-group"), user, headerVars, stats, "groups", group.ID, group.Name, group.Tag, rank, disableRank} + pi := common.PanelEditGroupPage{common.GetTitlePhrase("panel_edit_group"), user, headerVars, stats, "groups", group.ID, group.Name, group.Tag, rank, disableRank} if common.PreRenderHooks["pre_render_panel_edit_group"] != nil { if common.RunPreRenderHook("pre_render_panel_edit_group", w, r, &user, &pi) { return nil @@ -1825,7 +1722,7 @@ func routePanelGroupsEditPerms(w http.ResponseWriter, r *http.Request, user comm addGlobalPerm("ViewIPs", group.Perms.ViewIPs) addGlobalPerm("UploadFiles", group.Perms.UploadFiles) - pi := common.PanelEditGroupPermsPage{common.GetTitlePhrase("panel-edit-group"), user, headerVars, stats, "groups", group.ID, group.Name, localPerms, globalPerms} + pi := common.PanelEditGroupPermsPage{common.GetTitlePhrase("panel_edit_group"), user, headerVars, stats, "groups", group.ID, group.Name, localPerms, globalPerms} if common.PreRenderHooks["pre_render_panel_edit_group_perms"] != nil { if common.RunPreRenderHook("pre_render_panel_edit_group_perms", w, r, &user, &pi) { return nil @@ -2055,17 +1952,8 @@ func routePanelThemes(w http.ResponseWriter, r *http.Request, user common.User) } - pi := common.PanelThemesPage{common.GetTitlePhrase("panel-themes"), user, headerVars, stats, "themes", pThemeList, vThemeList} - if common.PreRenderHooks["pre_render_panel_themes"] != nil { - if common.RunPreRenderHook("pre_render_panel_themes", w, r, &user, &pi) { - return nil - } - } - err := common.Templates.ExecuteTemplate(w, "panel-themes.html", pi) - if err != nil { - return common.InternalError(err, w, r) - } - return nil + pi := common.PanelThemesPage{common.GetTitlePhrase("panel_themes"), user, headerVars, stats, "themes", pThemeList, vThemeList} + return panelRenderTemplate("panel_themes", w, r, user, &pi) } func routePanelThemesSetDefault(w http.ResponseWriter, r *http.Request, user common.User, uname string) common.RouteError { @@ -2172,12 +2060,8 @@ func routePanelBackups(w http.ResponseWriter, r *http.Request, user common.User, backupList = append(backupList, common.BackupItem{backupFile.Name(), backupFile.ModTime()}) } - pi := common.PanelBackupPage{common.GetTitlePhrase("panel-backups"), user, headerVars, stats, "backups", backupList} - err = common.Templates.ExecuteTemplate(w, "panel-backups.html", pi) - if err != nil { - return common.InternalError(err, w, r) - } - return nil + pi := common.PanelBackupPage{common.GetTitlePhrase("panel_backups"), user, headerVars, stats, "backups", backupList} + return panelRenderTemplate("panel_backups", w, r, user, &pi) } // TODO: Log errors when something really screwy is going on? @@ -2270,17 +2154,48 @@ func routePanelLogsMod(w http.ResponseWriter, r *http.Request, user common.User) } pageList := common.Paginate(logCount, perPage, 5) - pi := common.PanelLogsPage{common.GetTitlePhrase("panel-mod-logs"), user, headerVars, stats, "logs", logs, pageList, page, lastPage} - if common.PreRenderHooks["pre_render_panel_mod_log"] != nil { - if common.RunPreRenderHook("pre_render_panel_mod_log", w, r, &user, &pi) { - return nil - } + pi := common.PanelLogsPage{common.GetTitlePhrase("panel_mod_logs"), user, headerVars, stats, "logs", logs, pageList, page, lastPage} + return panelRenderTemplate("panel_modlogs", w, r, user, &pi) +} + +func routePanelLogsAdmin(w http.ResponseWriter, r *http.Request, user common.User) common.RouteError { + headerVars, stats, ferr := common.PanelUserCheck(w, r, &user) + if ferr != nil { + return ferr } - err = common.Templates.ExecuteTemplate(w, "panel-modlogs.html", pi) + + logCount := common.ModLogs.GlobalCount() + page, _ := strconv.Atoi(r.FormValue("page")) + perPage := 10 + offset, page, lastPage := common.PageOffset(logCount, page, perPage) + + rows, err := stmts.getAdminlogsOffset.Query(offset, perPage) if err != nil { return common.InternalError(err, w, r) } - return nil + defer rows.Close() + + var logs []common.LogItem + var action, elementType, ipaddress, doneAt string + var elementID, actorID int + for rows.Next() { + err := rows.Scan(&action, &elementID, &elementType, &ipaddress, &actorID, &doneAt) + if err != nil { + return common.InternalError(err, w, r) + } + + actor := handleUnknownUser(common.Users.Get(actorID)) + action = modlogsElementType(action, elementType, elementID, actor) + logs = append(logs, common.LogItem{Action: template.HTML(action), IPAddress: ipaddress, DoneAt: doneAt}) + } + err = rows.Err() + if err != nil { + return common.InternalError(err, w, r) + } + + pageList := common.Paginate(logCount, perPage, 5) + pi := common.PanelLogsPage{common.GetTitlePhrase("panel_admin_logs"), user, headerVars, stats, "logs", logs, pageList, page, lastPage} + return panelRenderTemplate("panel_adminlogs", w, r, user, &pi) } func routePanelDebug(w http.ResponseWriter, r *http.Request, user common.User) common.RouteError { @@ -2294,10 +2209,6 @@ func routePanelDebug(w http.ResponseWriter, r *http.Request, user common.User) c openConnCount := dbStats.OpenConnections // Disk I/O? - pi := common.PanelDebugPage{common.GetTitlePhrase("panel-debug"), user, headerVars, stats, "debug", uptime, openConnCount, dbAdapter} - err := common.Templates.ExecuteTemplate(w, "panel-debug.html", pi) - if err != nil { - return common.InternalError(err, w, r) - } - return nil + pi := common.PanelDebugPage{common.GetTitlePhrase("panel_debug"), user, headerVars, stats, "debug", uptime, openConnCount, dbAdapter} + return panelRenderTemplate("panel_debug", w, r, user, &pi) } diff --git a/public/global.js b/public/global.js index 7f757eb4..90f061ec 100644 --- a/public/global.js +++ b/public/global.js @@ -506,6 +506,14 @@ $(document).ready(function(){ let optionNode = selectNode.options[selectNode.selectedIndex]; let action = optionNode.getAttribute("val"); //console.log("action",action); + + // Handle these specially + switch(action) { + case "move": + console.log("move action"); + $("#mod_topic_mover").removeClass("auto_hide"); + return; + } let url = "/topic/"+action+"/submit/"; //console.log("JSON.stringify(selectedTopics) ", JSON.stringify(selectedTopics)); diff --git a/query_gen/main.go b/query_gen/main.go index 487ebdb2..01fe681c 100644 --- a/query_gen/main.go +++ b/query_gen/main.go @@ -230,6 +230,8 @@ func writeSelects(adapter qgen.Adapter) error { build.Select("getModlogsOffset").Table("moderation_logs").Columns("action, elementID, elementType, ipaddress, actorID, doneAt").Orderby("doneAt DESC").Limit("?,?").Parse() + build.Select("getAdminlogsOffset").Table("administration_logs").Columns("action, elementID, elementType, ipaddress, actorID, doneAt").Orderby("doneAt DESC").Limit("?,?").Parse() + build.Select("getReplyTID").Table("replies").Columns("tid").Where("rid = ?").Parse() build.Select("getTopicFID").Table("topics").Columns("parentID").Where("tid = ?").Parse() diff --git a/router_gen/main.go b/router_gen/main.go index 45f8d96e..85ac2a46 100644 --- a/router_gen/main.go +++ b/router_gen/main.go @@ -171,6 +171,9 @@ func main() { "bing", "baidu", "duckduckgo", + "discord", + "lynx", + "blank", } tmplVars.AllAgentMap = make(map[string]int) @@ -307,7 +310,7 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) { // Track the user agents. Unfortunately, everyone pretends to be Mozilla, so this'll be a little less efficient than I would like. // TODO: Add a setting to disable this? // TODO: Use a more efficient detector instead of smashing every possible combination in - ua := strings.TrimSuffix(strings.TrimPrefix(req.UserAgent(),"Mozilla/5.0 ")," Safari/537.36") // Noise, no one's going to be running this and it complicates implementing an efficient UA parser, particularly the more efficient right-to-left one I have in mind + ua := strings.TrimSpace(strings.TrimSuffix(strings.TrimPrefix(req.UserAgent(),"Mozilla/5.0 ")," Safari/537.36")) // Noise, no one's going to be running this and it complicates implementing an efficient UA parser, particularly the more efficient right-to-left one I have in mind switch { case strings.Contains(ua,"Google"): common.AgentViewCounter.Bump({{.AllAgentMap.googlebot}}) @@ -331,6 +334,12 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) { common.AgentViewCounter.Bump({{.AllAgentMap.baidu}}) case strings.Contains(ua,"DuckDuckBot"): common.AgentViewCounter.Bump({{.AllAgentMap.duckduckgo}}) + case strings.Contains(ua,"Discordbot"): + common.AgentViewCounter.Bump({{.AllAgentMap.discord}}) + case strings.Contains(ua,"Lynx"): + common.AgentViewCounter.Bump({{.AllAgentMap.lynx}}) + case ua == "": + common.AgentViewCounter.Bump({{.AllAgentMap.blank}}) default: common.AgentViewCounter.Bump({{.AllAgentMap.unknown}}) if common.Dev.DebugMode { diff --git a/template_list.go b/template_list.go index c553f709..7618b42e 100644 --- a/template_list.go +++ b/template_list.go @@ -811,6 +811,7 @@ var topics_8 = []byte(` @@ -819,21 +820,40 @@ var topics_8 = []byte(` `) var topics_9 = []byte(` +