Renamed the pre_render_panel_mod_log pre-render hook to pre_render_panel_modlogs.

Added the half-second task type, you'll why later ;)
Reduced the amount of code duplication in the panel routes (saved a hundred lines).
Added the two day time range option for graphs.
We now track the discord, lynx and blank user agents.
Renamed some template files for consistency and to help stamp out some duplicate code.

Began work on topic move.
This commit is contained in:
Azareal 2018-01-11 08:03:17 +00:00
parent 5f5994726e
commit 5ba7aa74f7
34 changed files with 414 additions and 335 deletions

View File

@ -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,

View File

@ -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)

View File

@ -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 {

View File

@ -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 {

View File

@ -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 {

View File

@ -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"
}
}

13
main.go
View File

@ -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

View File

@ -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 {

View File

@ -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)
}

View File

@ -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));

View File

@ -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()

View File

@ -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 {

View File

@ -811,6 +811,7 @@ var topics_8 = []byte(`
<select class="mod_floater_options">
<option val="delete">Delete them</option>
<option val="lock">Lock them</option>
<option val="move">Move them</option>
</select>
<button class="mod_floater_submit">Run</button>
</div>
@ -819,21 +820,40 @@ var topics_8 = []byte(`
`)
var topics_9 = []byte(`
<div id="mod_topic_mover" class="modal_pane auto_hide">
<form action="/topic/move/submit/" method="post">
<div class="pane_header">
<h3>Move Topics (3)</h3>
</div>
<div class="pane_body">
<div class="pane_table">
<div class="pane_row"></div>
`)
var topics_10 = []byte(`<div class="pane_row">`)
var topics_11 = []byte(`</div>`)
var topics_12 = []byte(`
</div>
</div>
<div class="pane_buttons">
<button>Move Topics</button>
</div>
</form>
</div>
<div class="rowblock topic_create_form quick_create_form" style="display: none;" aria-label="Quick Topic Form">
<form name="topic_create_form_form" id="topic_create_form_form" enctype="multipart/form-data" action="/topic/create/submit/" method="post"></form>
<img class="little_row_avatar" src="`)
var topics_10 = []byte(`" height="64" alt="Your Avatar" title="Your Avatar" />
var topics_13 = []byte(`" height="64" alt="Your Avatar" title="Your Avatar" />
<div class="main_form">
<div class="topic_meta">
<div class="formrow topic_board_row real_first_child">
<div class="formitem"><select form="topic_create_form_form" id="topic_board_input" name="topic-board">
`)
var topics_11 = []byte(`<option `)
var topics_12 = []byte(`selected`)
var topics_13 = []byte(` value="`)
var topics_14 = []byte(`">`)
var topics_15 = []byte(`</option>`)
var topics_16 = []byte(`
var topics_14 = []byte(`<option `)
var topics_15 = []byte(`selected`)
var topics_16 = []byte(` value="`)
var topics_17 = []byte(`">`)
var topics_18 = []byte(`</option>`)
var topics_19 = []byte(`
</select></div>
</div>
<div class="formrow topic_name_row">
@ -851,77 +871,77 @@ var topics_16 = []byte(`
<div class="formitem">
<button form="topic_create_form_form" class="formbutton">Create Topic</button>
`)
var topics_17 = []byte(`
var topics_20 = []byte(`
<input name="upload_files" form="topic_create_form_form" id="upload_files" multiple type="file" style="display: none;" />
<label for="upload_files" class="formbutton add_file_button">Add File</label>
<div id="upload_file_dock"></div>`)
var topics_18 = []byte(`
var topics_21 = []byte(`
<button class="formbutton close_form">Cancel</button>
</div>
</div>
</div>
</div>
`)
var topics_19 = []byte(`
var topics_22 = []byte(`
<div id="topic_list" class="rowblock topic_list" aria-label="A list containing topics from every forum">
`)
var topics_20 = []byte(`<div class="topic_row" data-tid="`)
var topics_21 = []byte(`">
<div class="rowitem topic_left passive datarow `)
var topics_22 = []byte(`topic_sticky`)
var topics_23 = []byte(`topic_closed`)
var topics_23 = []byte(`<div class="topic_row" data-tid="`)
var topics_24 = []byte(`">
<div class="rowitem topic_left passive datarow `)
var topics_25 = []byte(`topic_sticky`)
var topics_26 = []byte(`topic_closed`)
var topics_27 = []byte(`">
<span class="selector"></span>
<a href="`)
var topics_25 = []byte(`"><img src="`)
var topics_26 = []byte(`" height="64" alt="`)
var topics_27 = []byte(`'s Avatar" title="`)
var topics_28 = []byte(`'s Avatar" /></a>
var topics_28 = []byte(`"><img src="`)
var topics_29 = []byte(`" height="64" alt="`)
var topics_30 = []byte(`'s Avatar" title="`)
var topics_31 = []byte(`'s Avatar" /></a>
<span class="topic_inner_left">
<a class="rowtopic" href="`)
var topics_29 = []byte(`" itemprop="itemListElement"><span>`)
var topics_30 = []byte(`</span></a> `)
var topics_31 = []byte(`<a class="rowsmall parent_forum" href="`)
var topics_32 = []byte(`">`)
var topics_33 = []byte(`</a>`)
var topics_34 = []byte(`
<br /><a class="rowsmall starter" href="`)
var topics_32 = []byte(`" itemprop="itemListElement"><span>`)
var topics_33 = []byte(`</span></a> `)
var topics_34 = []byte(`<a class="rowsmall parent_forum" href="`)
var topics_35 = []byte(`">`)
var topics_36 = []byte(`</a>
var topics_36 = []byte(`</a>`)
var topics_37 = []byte(`
<br /><a class="rowsmall starter" href="`)
var topics_38 = []byte(`">`)
var topics_39 = []byte(`</a>
`)
var topics_37 = []byte(`<span class="rowsmall topic_status_e topic_status_closed" title="Status: Closed"> | &#x1F512;&#xFE0E</span>`)
var topics_38 = []byte(`<span class="rowsmall topic_status_e topic_status_sticky" title="Status: Pinned"> | &#x1F4CD;&#xFE0E</span>`)
var topics_39 = []byte(`
var topics_40 = []byte(`<span class="rowsmall topic_status_e topic_status_closed" title="Status: Closed"> | &#x1F512;&#xFE0E</span>`)
var topics_41 = []byte(`<span class="rowsmall topic_status_e topic_status_sticky" title="Status: Pinned"> | &#x1F4CD;&#xFE0E</span>`)
var topics_42 = []byte(`
</span>
<span class="topic_inner_right rowsmall" style="float: right;">
<span class="replyCount">`)
var topics_40 = []byte(`</span><br />
var topics_43 = []byte(`</span><br />
<span class="likeCount">`)
var topics_41 = []byte(`</span>
var topics_44 = []byte(`</span>
</span>
</div>
<div class="rowitem topic_right passive datarow `)
var topics_42 = []byte(`topic_sticky`)
var topics_43 = []byte(`topic_closed`)
var topics_44 = []byte(`">
var topics_45 = []byte(`topic_sticky`)
var topics_46 = []byte(`topic_closed`)
var topics_47 = []byte(`">
<a href="`)
var topics_45 = []byte(`"><img src="`)
var topics_46 = []byte(`" height="64" alt="`)
var topics_47 = []byte(`'s Avatar" title="`)
var topics_48 = []byte(`'s Avatar" /></a>
var topics_48 = []byte(`"><img src="`)
var topics_49 = []byte(`" height="64" alt="`)
var topics_50 = []byte(`'s Avatar" title="`)
var topics_51 = []byte(`'s Avatar" /></a>
<span>
<a href="`)
var topics_49 = []byte(`" class="lastName" style="font-size: 14px;">`)
var topics_50 = []byte(`</a><br>
var topics_52 = []byte(`" class="lastName" style="font-size: 14px;">`)
var topics_53 = []byte(`</a><br>
<span class="rowsmall lastReplyAt">`)
var topics_51 = []byte(`</span>
var topics_54 = []byte(`</span>
</span>
</div>
</div>`)
var topics_52 = []byte(`<div class="rowitem passive">There aren't any topics yet.`)
var topics_53 = []byte(` <a href="/topics/create/">Start one?</a>`)
var topics_54 = []byte(`</div>`)
var topics_55 = []byte(`
var topics_55 = []byte(`<div class="rowitem passive">There aren't any topics yet.`)
var topics_56 = []byte(` <a href="/topics/create/">Start one?</a>`)
var topics_57 = []byte(`</div>`)
var topics_58 = []byte(`
</div>
</main>
@ -973,6 +993,7 @@ var forum_18 = []byte(`
<select class="mod_floater_options">
<option val="delete">Delete them</option>
<option val="lock">Lock them</option>
<option val="move">Move them</option>
</select>
<button>Run</button>
</div>

View File

@ -3,9 +3,9 @@
// Code generated by Gosora. More below:
/* This file was automatically generated by the software. Please don't edit it as your changes may be overwritten at any moment. */
package main
import "net/http"
import "./common"
import "strconv"
import "net/http"
// nolint
func init() {
@ -99,108 +99,116 @@ if tmpl_topics_vars.CurrentUser.ID != 0 {
w.Write(topics_8)
if len(tmpl_topics_vars.ForumList) != 0 {
w.Write(topics_9)
w.Write([]byte(tmpl_topics_vars.CurrentUser.Avatar))
w.Write(topics_10)
if len(tmpl_topics_vars.ForumList) != 0 {
for _, item := range tmpl_topics_vars.ForumList {
w.Write(topics_11)
if item.ID == tmpl_topics_vars.DefaultForum {
w.Write(topics_12)
}
w.Write(topics_13)
w.Write([]byte(strconv.Itoa(item.ID)))
w.Write(topics_14)
w.Write(topics_10)
w.Write([]byte(item.Name))
w.Write(topics_11)
}
}
w.Write(topics_12)
w.Write([]byte(tmpl_topics_vars.CurrentUser.Avatar))
w.Write(topics_13)
if len(tmpl_topics_vars.ForumList) != 0 {
for _, item := range tmpl_topics_vars.ForumList {
w.Write(topics_14)
if item.ID == tmpl_topics_vars.DefaultForum {
w.Write(topics_15)
}
}
w.Write(topics_16)
if tmpl_topics_vars.CurrentUser.Perms.UploadFiles {
w.Write([]byte(strconv.Itoa(item.ID)))
w.Write(topics_17)
}
w.Write([]byte(item.Name))
w.Write(topics_18)
}
}
w.Write(topics_19)
if tmpl_topics_vars.CurrentUser.Perms.UploadFiles {
w.Write(topics_20)
}
w.Write(topics_21)
}
}
w.Write(topics_22)
if len(tmpl_topics_vars.TopicList) != 0 {
for _, item := range tmpl_topics_vars.TopicList {
w.Write(topics_20)
w.Write([]byte(strconv.Itoa(item.ID)))
w.Write(topics_21)
if item.Sticky {
w.Write(topics_22)
} else {
if item.IsClosed {
w.Write(topics_23)
}
}
w.Write([]byte(strconv.Itoa(item.ID)))
w.Write(topics_24)
w.Write([]byte(item.Creator.Link))
if item.Sticky {
w.Write(topics_25)
w.Write([]byte(item.Creator.Avatar))
w.Write(topics_26)
w.Write([]byte(item.Creator.Name))
w.Write(topics_27)
w.Write([]byte(item.Creator.Name))
w.Write(topics_28)
w.Write([]byte(item.Link))
w.Write(topics_29)
w.Write([]byte(item.Title))
w.Write(topics_30)
if item.ForumName != "" {
w.Write(topics_31)
w.Write([]byte(item.ForumLink))
w.Write(topics_32)
w.Write([]byte(item.ForumName))
w.Write(topics_33)
}
w.Write(topics_34)
w.Write([]byte(item.Creator.Link))
w.Write(topics_35)
w.Write([]byte(item.Creator.Name))
w.Write(topics_36)
if item.IsClosed {
w.Write(topics_37)
}
if item.Sticky {
w.Write(topics_38)
}
w.Write(topics_39)
w.Write([]byte(strconv.Itoa(item.PostCount)))
w.Write(topics_40)
w.Write([]byte(strconv.Itoa(item.LikeCount)))
w.Write(topics_41)
if item.Sticky {
w.Write(topics_42)
} else {
if item.IsClosed {
w.Write(topics_26)
}
}
w.Write(topics_27)
w.Write([]byte(item.Creator.Link))
w.Write(topics_28)
w.Write([]byte(item.Creator.Avatar))
w.Write(topics_29)
w.Write([]byte(item.Creator.Name))
w.Write(topics_30)
w.Write([]byte(item.Creator.Name))
w.Write(topics_31)
w.Write([]byte(item.Link))
w.Write(topics_32)
w.Write([]byte(item.Title))
w.Write(topics_33)
if item.ForumName != "" {
w.Write(topics_34)
w.Write([]byte(item.ForumLink))
w.Write(topics_35)
w.Write([]byte(item.ForumName))
w.Write(topics_36)
}
w.Write(topics_37)
w.Write([]byte(item.Creator.Link))
w.Write(topics_38)
w.Write([]byte(item.Creator.Name))
w.Write(topics_39)
if item.IsClosed {
w.Write(topics_40)
}
if item.Sticky {
w.Write(topics_41)
}
w.Write(topics_42)
w.Write([]byte(strconv.Itoa(item.PostCount)))
w.Write(topics_43)
}
}
w.Write([]byte(strconv.Itoa(item.LikeCount)))
w.Write(topics_44)
w.Write([]byte(item.LastUser.Link))
if item.Sticky {
w.Write(topics_45)
w.Write([]byte(item.LastUser.Avatar))
} else {
if item.IsClosed {
w.Write(topics_46)
w.Write([]byte(item.LastUser.Name))
}
}
w.Write(topics_47)
w.Write([]byte(item.LastUser.Name))
w.Write(topics_48)
w.Write([]byte(item.LastUser.Link))
w.Write(topics_48)
w.Write([]byte(item.LastUser.Avatar))
w.Write(topics_49)
w.Write([]byte(item.LastUser.Name))
w.Write(topics_50)
w.Write([]byte(item.RelativeLastReplyAt))
w.Write([]byte(item.LastUser.Name))
w.Write(topics_51)
}
} else {
w.Write([]byte(item.LastUser.Link))
w.Write(topics_52)
if tmpl_topics_vars.CurrentUser.Perms.CreateTopic {
w.Write([]byte(item.LastUser.Name))
w.Write(topics_53)
}
w.Write([]byte(item.RelativeLastReplyAt))
w.Write(topics_54)
}
} else {
w.Write(topics_55)
if tmpl_topics_vars.CurrentUser.Perms.CreateTopic {
w.Write(topics_56)
}
w.Write(topics_57)
}
w.Write(topics_58)
w.Write(footer_0)
w.Write([]byte(common.BuildWidget("footer",tmpl_topics_vars.Header)))
w.Write(footer_1)

View File

@ -31,6 +31,7 @@
<select class="mod_floater_options">
<option val="delete">Delete them</option>
<option val="lock">Lock them</option>
<option val="move">Move them</option>
</select>
<button>Run</button>
</div>

View File

@ -7,6 +7,7 @@
<div class="rowitem">
<a>Views</a>
<select class="timeRangeSelector to_right" name="timeRange">
<option val="two-days"{{if eq .TimeRange "two-days"}} selected{{end}}>2 days</option>
<option val="one-day"{{if eq .TimeRange "one-day"}} selected{{end}}>1 day</option>
<option val="twelve-hours"{{if eq .TimeRange "twelve-hours"}} selected{{end}}>12 hours</option>
<option val="six-hours"{{if eq .TimeRange "six-hours"}} selected{{end}}>6 hours</option>

View File

@ -7,6 +7,7 @@
<div class="rowitem">
<a>{{.Agent}} Views</a>
<select class="timeRangeSelector to_right" name="timeRange">
<option val="two-days"{{if eq .TimeRange "two-days"}} selected{{end}}>2 days</option>
<option val="one-day"{{if eq .TimeRange "one-day"}} selected{{end}}>1 day</option>
<option val="twelve-hours"{{if eq .TimeRange "twelve-hours"}} selected{{end}}>12 hours</option>
<option val="six-hours"{{if eq .TimeRange "six-hours"}} selected{{end}}>6 hours</option>

View File

@ -7,6 +7,7 @@
<div class="rowitem">
<a>{{.Route}} Views</a>
<select class="timeRangeSelector to_right" name="timeRange">
<option val="two-days"{{if eq .TimeRange "two-days"}} selected{{end}}>2 days</option>
<option val="one-day"{{if eq .TimeRange "one-day"}} selected{{end}}>1 day</option>
<option val="twelve-hours"{{if eq .TimeRange "twelve-hours"}} selected{{end}}>12 hours</option>
<option val="six-hours"{{if eq .TimeRange "six-hours"}} selected{{end}}>6 hours</option>

View File

@ -17,6 +17,7 @@
</div>
{{if ne .CurrentUser.ID 0}}
{{/** TODO: Hide these from unauthorised users? **/}}
<div class="mod_floater auto_hide">
<form method="post">
<div class="mod_floater_head">
@ -26,6 +27,7 @@
<select class="mod_floater_options">
<option val="delete">Delete them</option>
<option val="lock">Lock them</option>
<option val="move">Move them</option>
</select>
<button class="mod_floater_submit">Run</button>
</div>
@ -33,6 +35,24 @@
</div>
{{if .ForumList}}
{{/** TODO: Have a seperate forum list for moving topics? Maybe an AJAX forum search compatible with plugin_guilds? **/}}
{{/** TODO: Add ARIA attributes for this **/}}
<div id="mod_topic_mover" class="modal_pane auto_hide">
<form action="/topic/move/submit/" method="post">
<div class="pane_header">
<h3>Move Topics (3)</h3>
</div>
<div class="pane_body">
<div class="pane_table">
<div class="pane_row"></div>
{{range .ForumList}}<div class="pane_row">{{.Name}}</div>{{end}}
</div>
</div>
<div class="pane_buttons">
<button>Move Topics</button>
</div>
</form>
</div>
<div class="rowblock topic_create_form quick_create_form" style="display: none;" aria-label="Quick Topic Form">
<form name="topic_create_form_form" id="topic_create_form_form" enctype="multipart/form-data" action="/topic/create/submit/" method="post"></form>
<img class="little_row_avatar" src="{{.CurrentUser.Avatar}}" height="64" alt="Your Avatar" title="Your Avatar" />

View File

@ -220,16 +220,18 @@ ul {
font-size: 20px;
font-weight: normal;
color: var(--primary-text-color);
-webkit-margin-before: 0;
-webkit-margin-after: 0;
margin-block-start: 0;
margin-block-end: 0;
display: inline-block;
}
.colstack_head a h1 {
font-size: 16px;
color: var(--primary-link-color);
}
h1, h3 {
-webkit-margin-before: 0;
-webkit-margin-after: 0;
margin-block-start: 0;
margin-block-end: 0;
}
.colstack {
display: flex;
@ -261,9 +263,9 @@ ul {
position: fixed;
bottom: 15px;
right: 15px;
background-color: var(--inverse-primary-text-color);
width: 200px;
height: 115px;
background-color: var(--inverse-primary-text-color);
border: 1px solid var(--header-border-color);
border-bottom: 2px solid var(--header-border-color);
z-index: 9999;
@ -308,6 +310,17 @@ ul {
margin-top: -2px;
}
.modal_pane {
position: fixed;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
background-color: var(--inverse-primary-text-color);
border: 1px solid var(--header-border-color);
border-bottom: 2px solid var(--header-border-color);
padding: 8px;
}
@keyframes fadein {
from { opacity: 0; }
to { opacity: 1; }

View File

@ -174,7 +174,7 @@
stroke: hsl(359,98%,23%) !important;
}
.ct-point:hover {
stroke: hsl(359,98%,20%) !important;
stroke: hsl(359,98%,30%) !important;
}
.timeRangeSelector {
margin-top: -5px;