From 1044518e74a47b0f918964c0e8c773b7d669895c Mon Sep 17 00:00:00 2001 From: Azareal Date: Sun, 10 Nov 2019 12:37:53 +1000 Subject: [PATCH] Finish up the adminlogs. Throw an error instead of updating a word filter that doesn't exist. Track a bit of extra data for word filter updates. Shorten the prefixes of the adminlog phrases from panel_logs_administration_ to panel_logs_admin_ Add a bunch of new adminlog phrases. Tweak the word filter phrases. Add the extra column to the moderation_logs and administration_logs tables. You need to run the updater / patcher for this commit. --- cmd/query_gen/tables.go | 3 + common/audit_logs.go | 34 ++++++--- common/extend.go | 10 +-- common/parser.go | 2 +- common/widget.go | 89 +++++++++++----------- common/word_filters.go | 16 ++-- extend/plugin_adventure.go | 6 +- langs/english.json | 50 +++++++----- misc_test.go | 11 +-- patcher/main.go | 4 + patcher/patches.go | 80 ++++++++++--------- routes/panel/logs.go | 38 ++++----- routes/panel/plugins.go | 14 +++- routes/panel/themes.go | 46 +++++++++-- routes/panel/word_filters.go | 16 +++- schema/mssql/query_administration_logs.sql | 3 +- schema/mssql/query_moderation_logs.sql | 3 +- schema/mysql/query_administration_logs.sql | 3 +- schema/mysql/query_moderation_logs.sql | 3 +- schema/pgsql/query_administration_logs.sql | 3 +- schema/pgsql/query_moderation_logs.sql | 3 +- templates/panel_adminlogs.html | 4 +- templates/panel_word_filters.html | 2 +- 23 files changed, 271 insertions(+), 172 deletions(-) diff --git a/cmd/query_gen/tables.go b/cmd/query_gen/tables.go index 5da79925..4b35b0e1 100644 --- a/cmd/query_gen/tables.go +++ b/cmd/query_gen/tables.go @@ -167,6 +167,7 @@ func createTables(adapter qgen.Adapter) (err error) { ) // TODO: Should we add a users prefix to this table to fit the "unofficial convention"? + // TODO: Add an autoincrement key? createTable("emails", "", "", []tC{ tC{"email", "varchar", 200, false, false, ""}, @@ -620,6 +621,7 @@ func createTables(adapter qgen.Adapter) (err error) { tC{"ipaddress", "varchar", 200, false, false, ""}, tC{"actorID", "int", 0, false, false, ""}, // TODO: Make this a foreign key tC{"doneAt", "datetime", 0, false, false, ""}, + tC{"extra", "text", 0, false, false, ""}, }, nil, ) @@ -631,6 +633,7 @@ func createTables(adapter qgen.Adapter) (err error) { tC{"ipaddress", "varchar", 200, false, false, ""}, tC{"actorID", "int", 0, false, false, ""}, // TODO: Make this a foreign key tC{"doneAt", "datetime", 0, false, false, ""}, + tC{"extra", "text", 0, false, false, ""}, }, nil, ) diff --git a/common/audit_logs.go b/common/audit_logs.go index a322129b..21688f88 100644 --- a/common/audit_logs.go +++ b/common/audit_logs.go @@ -4,7 +4,7 @@ import ( "database/sql" "time" - "github.com/Azareal/Gosora/query_gen" + qgen "github.com/Azareal/Gosora/query_gen" ) var ModLogs LogStore @@ -14,15 +14,17 @@ type LogItem struct { Action string ElementID int ElementType string - IP string + IP string ActorID int DoneAt string + Extra string } type LogStore interface { Create(action string, elementID int, elementType string, ip string, actorID int) (err error) + CreateExtra(action string, elementID int, elementType string, ip string, actorID int, extra string) (err error) Count() int - GetOffset(offset int, perPage int) (logs []LogItem, err error) + GetOffset(offset, perPage int) (logs []LogItem, err error) } type SQLModLogStore struct { @@ -34,15 +36,19 @@ type SQLModLogStore struct { func NewModLogStore(acc *qgen.Accumulator) (*SQLModLogStore, error) { ml := "moderation_logs" return &SQLModLogStore{ - create: acc.Insert(ml).Columns("action, elementID, elementType, ipaddress, actorID, doneAt").Fields("?,?,?,?,?,UTC_TIMESTAMP()").Prepare(), + create: acc.Insert(ml).Columns("action, elementID, elementType, ipaddress, actorID, doneAt, extra").Fields("?,?,?,?,?,UTC_TIMESTAMP(),?").Prepare(), count: acc.Count(ml).Prepare(), - getOffset: acc.Select(ml).Columns("action, elementID, elementType, ipaddress, actorID, doneAt").Orderby("doneAt DESC").Limit("?,?").Prepare(), + getOffset: acc.Select(ml).Columns("action, elementID, elementType, ipaddress, actorID, doneAt, extra").Orderby("doneAt DESC").Limit("?,?").Prepare(), }, acc.FirstError() } // TODO: Make a store for this? func (s *SQLModLogStore) Create(action string, elementID int, elementType string, ip string, actorID int) (err error) { - _, err = s.create.Exec(action, elementID, elementType, ip, actorID) + return s.CreateExtra(action, elementID, elementType, ip, actorID, "") +} + +func (s *SQLModLogStore) CreateExtra(action string, elementID int, elementType string, ip string, actorID int, extra string) (err error) { + _, err = s.create.Exec(action, elementID, elementType, ip, actorID, extra) return err } @@ -58,7 +64,7 @@ func buildLogList(rows *sql.Rows) (logs []LogItem, err error) { for rows.Next() { var l LogItem var doneAt time.Time - err := rows.Scan(&l.Action, &l.ElementID, &l.ElementType, &l.IP, &l.ActorID, &doneAt) + err := rows.Scan(&l.Action, &l.ElementID, &l.ElementType, &l.IP, &l.ActorID, &doneAt, &l.Extra) if err != nil { return logs, err } @@ -68,7 +74,7 @@ func buildLogList(rows *sql.Rows) (logs []LogItem, err error) { return logs, rows.Err() } -func (s *SQLModLogStore) GetOffset(offset int, perPage int) (logs []LogItem, err error) { +func (s *SQLModLogStore) GetOffset(offset, perPage int) (logs []LogItem, err error) { rows, err := s.getOffset.Query(offset, perPage) if err != nil { return logs, err @@ -86,15 +92,19 @@ type SQLAdminLogStore struct { func NewAdminLogStore(acc *qgen.Accumulator) (*SQLAdminLogStore, error) { al := "administration_logs" return &SQLAdminLogStore{ - create: acc.Insert(al).Columns("action, elementID, elementType, ipaddress, actorID, doneAt").Fields("?,?,?,?,?,UTC_TIMESTAMP()").Prepare(), + create: acc.Insert(al).Columns("action, elementID, elementType, ipaddress, actorID, doneAt, extra").Fields("?,?,?,?,?,UTC_TIMESTAMP(),?").Prepare(), count: acc.Count(al).Prepare(), - getOffset: acc.Select(al).Columns("action, elementID, elementType, ipaddress, actorID, doneAt").Orderby("doneAt DESC").Limit("?,?").Prepare(), + getOffset: acc.Select(al).Columns("action, elementID, elementType, ipaddress, actorID, doneAt, extra").Orderby("doneAt DESC").Limit("?,?").Prepare(), }, acc.FirstError() } // TODO: Make a store for this? func (s *SQLAdminLogStore) Create(action string, elementID int, elementType string, ip string, actorID int) (err error) { - _, err = s.create.Exec(action, elementID, elementType, ip, actorID) + return s.CreateExtra(action, elementID, elementType, ip, actorID, "") +} + +func (s *SQLAdminLogStore) CreateExtra(action string, elementID int, elementType string, ip string, actorID int, extra string) (err error) { + _, err = s.create.Exec(action, elementID, elementType, ip, actorID, extra) return err } @@ -106,7 +116,7 @@ func (s *SQLAdminLogStore) Count() (count int) { return count } -func (s *SQLAdminLogStore) GetOffset(offset int, perPage int) (logs []LogItem, err error) { +func (s *SQLAdminLogStore) GetOffset(offset, perPage int) (logs []LogItem, err error) { rows, err := s.getOffset.Query(offset, perPage) if err != nil { return logs, err diff --git a/common/extend.go b/common/extend.go index 246b5557..2855e97e 100644 --- a/common/extend.go +++ b/common/extend.go @@ -321,11 +321,11 @@ type Plugin struct { Installable bool Installed bool - Init func(plugin *Plugin) error - Activate func(plugin *Plugin) error - Deactivate func(plugin *Plugin) // TODO: We might want to let this return an error? - Install func(plugin *Plugin) error - Uninstall func(plugin *Plugin) error // TODO: I'm not sure uninstall is implemented + Init func(pl *Plugin) error + Activate func(pl *Plugin) error + Deactivate func(pl *Plugin) // TODO: We might want to let this return an error? + Install func(pl *Plugin) error + Uninstall func(pl *Plugin) error // TODO: I'm not sure uninstall is implemented Hooks map[string]int Data interface{} // Usually used for hosting the VMs / reusable elements of non-native plugins diff --git a/common/parser.go b/common/parser.go index b0c97f97..c58660c6 100644 --- a/common/parser.go +++ b/common/parser.go @@ -470,7 +470,7 @@ func ParseMessage(msg string, sectionID int, sectionType string /*, user User*/) return "" } for _, filter := range wordFilters { - msg = strings.Replace(msg, filter.Find, filter.Replacement, -1) + msg = strings.Replace(msg, filter.Find, filter.Replace, -1) } // Search for URLs, mentions and hashlinks in the messages... diff --git a/common/widget.go b/common/widget.go index 88c33630..fc2cb90a 100644 --- a/common/widget.go +++ b/common/widget.go @@ -50,40 +50,40 @@ type Widget struct { Literal bool TickMask atomic.Value - InitFunc func(widget *Widget, schedule *WidgetScheduler) error - ShutdownFunc func(widget *Widget) error - BuildFunc func(widget *Widget, hvars interface{}) (string, error) - TickFunc func(widget *Widget) error + InitFunc func(w *Widget, schedule *WidgetScheduler) error + ShutdownFunc func(w *Widget) error + BuildFunc func(w *Widget, hvars interface{}) (string, error) + TickFunc func(w *Widget) error } -func (widget *Widget) Delete() error { - _, err := widgetStmts.delete.Exec(widget.ID) +func (w *Widget) Delete() error { + _, err := widgetStmts.delete.Exec(w.ID) if err != nil { return err } // Reload the dock // TODO: Better synchronisation - Widgets.delete(widget.ID) - widgets, err := getDockWidgets(widget.Side) + Widgets.delete(w.ID) + widgets, err := getDockWidgets(w.Side) if err != nil { return err } - setDock(widget.Side, widgets) + setDock(w.Side, widgets) return nil } -func (widget *Widget) Copy() (owidget *Widget) { +func (w *Widget) Copy() (owidget *Widget) { owidget = &Widget{} - *owidget = *widget + *owidget = *w return owidget } // TODO: Test this // TODO: Add support for zone:id. Perhaps, carry a ZoneID property around in *Header? It might allow some weirdness like frontend[5] which matches any zone with an ID of 5 but it would be a tad faster than verifying each zone, although it might be problematic if users end up relying on this behaviour for areas which don't pass IDs to the widgets system but *probably* should // TODO: Add a selector which also matches topics inside a specific forum? -func (widget *Widget) Allowed(zone string, zoneid int) bool { - for _, loc := range strings.Split(widget.Location, "|") { +func (w *Widget) Allowed(zone string, zoneid int) bool { + for _, loc := range strings.Split(w.Location, "|") { if len(loc) == 0 { continue } @@ -107,16 +107,15 @@ func (widget *Widget) Allowed(zone string, zoneid int) bool { } // TODO: Refactor -func (widget *Widget) Build(hvars interface{}) (string, error) { - if widget.Literal { - return widget.Body, nil +func (w *Widget) Build(hvars interface{}) (string, error) { + if w.Literal { + return w.Body, nil } - if widget.BuildFunc != nil { - return widget.BuildFunc(widget, hvars) + if w.BuildFunc != nil { + return w.BuildFunc(w, hvars) } - header := hvars.(*Header) - err := header.Theme.RunTmpl(widget.Body, hvars, header.Writer) + err := header.Theme.RunTmpl(w.Body, hvars, header.Writer) return "", err } @@ -125,40 +124,42 @@ type WidgetEdit struct { Data map[string]string } -func (widget *WidgetEdit) Create() error { - data, err := json.Marshal(widget.Data) +func (w *WidgetEdit) Create() (int, error) { + data, err := json.Marshal(w.Data) + if err != nil { + return 0, err + } + res, err := widgetStmts.create.Exec(w.Position, w.Side, w.Type, w.Enabled, w.Location, data) + if err != nil { + return 0, err + } + + // Reload the dock + widgets, err := getDockWidgets(w.Side) + if err != nil { + return 0, err + } + setDock(w.Side, widgets) + + wid64, err := res.LastInsertId() + return int(wid64), err +} + +func (w *WidgetEdit) Commit() error { + data, err := json.Marshal(w.Data) if err != nil { return err } - _, err = widgetStmts.create.Exec(widget.Position, widget.Side, widget.Type, widget.Enabled, widget.Location, data) + _, err = widgetStmts.update.Exec(w.Position, w.Side, w.Type, w.Enabled, w.Location, data, w.ID) if err != nil { return err } // Reload the dock - widgets, err := getDockWidgets(widget.Side) + widgets, err := getDockWidgets(w.Side) if err != nil { return err } - setDock(widget.Side, widgets) - return nil -} - -func (widget *WidgetEdit) Commit() error { - data, err := json.Marshal(widget.Data) - if err != nil { - return err - } - _, err = widgetStmts.update.Exec(widget.Position, widget.Side, widget.Type, widget.Enabled, widget.Location, data, widget.ID) - if err != nil { - return err - } - - // Reload the dock - widgets, err := getDockWidgets(widget.Side) - if err != nil { - return err - } - setDock(widget.Side, widgets) + setDock(w.Side, widgets) return nil } diff --git a/common/word_filters.go b/common/word_filters.go index 7bd1fcfd..b415786a 100644 --- a/common/word_filters.go +++ b/common/word_filters.go @@ -9,9 +9,15 @@ import ( // TODO: Move some features into methods on this? type WordFilter struct { - ID int - Find string - Replacement string + ID int + Find string + Replace string +} +type WordFilterDiff struct { + BeforeFind string + BeforeReplace string + AfterFind string + AfterReplace string } var WordFilters WordFilterStore @@ -82,7 +88,7 @@ func (s *DefaultWordFilterStore) bypassGetAll() (filters []*WordFilter, err erro for rows.Next() { f := &WordFilter{ID: 0} - err := rows.Scan(&f.ID, &f.Find, &f.Replacement) + err := rows.Scan(&f.ID, &f.Find, &f.Replace) if err != nil { return filters, err } @@ -98,7 +104,7 @@ func (s *DefaultWordFilterStore) GetAll() (filters map[int]*WordFilter, err erro func (s *DefaultWordFilterStore) Get(id int) (*WordFilter, error) { wf := &WordFilter{ID: id} - err := s.get.QueryRow(id).Scan(&wf.Find, &wf.Replacement) + err := s.get.QueryRow(id).Scan(&wf.Find, &wf.Replace) return wf, err } diff --git a/extend/plugin_adventure.go b/extend/plugin_adventure.go index 3356c831..8d63197f 100644 --- a/extend/plugin_adventure.go +++ b/extend/plugin_adventure.go @@ -16,14 +16,14 @@ func init() { }) } -func initAdventure(plugin *c.Plugin) error { +func initAdventure(pl *c.Plugin) error { return nil } // TODO: Change the signature to return an error? -func deactivateAdventure(plugin *c.Plugin) { +func deactivateAdventure(pl *c.Plugin) { } -func installAdventure(plugin *c.Plugin) error { +func installAdventure(pl *c.Plugin) error { return nil } diff --git a/langs/english.json b/langs/english.json index cd4b2ed6..1c1a71ed 100644 --- a/langs/english.json +++ b/langs/english.json @@ -1024,26 +1024,36 @@ "page_unknown":"Unknown", "setting_unknown":"unknown", - "panel_logs_administration_head":"Admin Action Logs", - "panel_logs_administration_action_user_edit":"User %s was modified by %s", - "panel_logs_administration_action_group_create":"Group %s was created by %s", - "panel_logs_administration_action_group_edit":"Group %s was modified by %s", - "panel_logs_administration_action_group_promotion_create":"A group promotion was created by %s", - "panel_logs_administration_action_group_promotion_delete":"A group promotion was deleted by %s", - "panel_logs_administration_action_forum_reorder":"Forums were reordered by %s", - "panel_logs_administration_action_forum_create":"Forum %s was created by %s", - "panel_logs_administration_action_forum_delete":"Forum %s was deleted by %s", - "panel_logs_administration_action_forum_edit":"Forum %s was modified by %s", - "panel_logs_administration_action_page_create":"Page %s was created by %s", - "panel_logs_administration_action_page_delete":"Page %s was deleted by %s", - "panel_logs_administration_action_page_edit":"Page %s was modified by %s", - "panel_logs_administration_action_setting_edit":"Setting %s was modified by %s", - "panel_logs_administration_action_word_filter_create":"Word Filter %d was created by %s", - "panel_logs_administration_action_word_filter_delete":"Word Filter %d was deleted by %s", - "panel_logs_administration_action_word_filter_edit":"Word Filter %d was modified by %s", - "panel_logs_administration_action_backup_download":"A backup was downloaded by %s", - "panel_logs_administration_action_unknown":"Unknown action '%s' on elementType '%s' by %s", - "panel_logs_administration_no_logs":"There aren't any events logged.", + "panel_logs_admin_head":"Admin Action Logs", + "panel_logs_admin_action_user_edit":"User %s was modified by %s", + "panel_logs_admin_action_group_create":"Group %s was created by %s", + "panel_logs_admin_action_group_edit":"Group %s was modified by %s", + "panel_logs_admin_action_group_promotion_create":"A group promotion was created by %s", + "panel_logs_admin_action_group_promotion_delete":"A group promotion was deleted by %s", + "panel_logs_admin_action_forum_reorder":"Forums were reordered by %s", + "panel_logs_admin_action_forum_create":"Forum %s was created by %s", + "panel_logs_admin_action_forum_delete":"Forum %s was deleted by %s", + "panel_logs_admin_action_forum_edit":"Forum %s was modified by %s", + "panel_logs_admin_action_page_create":"Page %s was created by %s", + "panel_logs_admin_action_page_delete":"Page %s was deleted by %s", + "panel_logs_admin_action_page_edit":"Page %s was modified by %s", + "panel_logs_admin_action_setting_edit":"Setting %s was modified by %s", + "panel_logs_admin_action_word_filter_create":"A word filter was created by %s", + "panel_logs_admin_action_word_filter_delete":"A word filter was deleted by %s", + "panel_logs_admin_action_word_filter_edit":"A word filter was modified by %s", + "panel_logs_admin_action_menu_suborder":"Menu #%d was reordered by %s", + "panel_logs_admin_action_menu_item_edit":"Menu item #%d was modified by %s", + "panel_logs_admin_action_menu_item_create":"Menu item #%d was created by %s", + "panel_logs_admin_action_menu_item_delete":"Menu item #%d was deleted by %s", + "panel_logs_admin_action_widget_edit":"Widget #%d was modified by %s", + "panel_logs_admin_action_widget_create":"Widget #%d was created by %s", + "panel_logs_admin_action_widget_delete":"Widget #%d was deleted by %s", + "panel_logs_admin_action_plugin_activate":"The plugin '%s' was activated by %s", + "panel_logs_admin_action_plugin_deactivate":"The plugin '%s' was deactivated by %s", + "panel_logs_admin_action_plugin_install":"The plugin '%s' was installed by %s", + "panel_logs_admin_action_backup_download":"A backup was downloaded by %s", + "panel_logs_admin_action_unknown":"Unknown action '%s' on elementType '%s' by %s", + "panel_logs_admin_no_logs":"There aren't any events logged.", "panel_plugins_head":"Plugins", "panel_plugins_author_prefix":"Author: ", diff --git a/misc_test.go b/misc_test.go index 3e064b88..3efc1c5d 100644 --- a/misc_test.go +++ b/misc_test.go @@ -1355,13 +1355,13 @@ func TestWordFilters(t *testing.T) { filter := filters[1] expect(t, filter.ID == 1, "Word filter ID should be 1") expect(t, filter.Find == "imbecile", "Word filter needle should be imbecile") - expect(t, filter.Replacement == "lovely", "Word filter replacement should be lovely") + expect(t, filter.Replace == "lovely", "Word filter replacement should be lovely") filter, err = c.WordFilters.Get(1) expectNilErr(t, err) expect(t, filter.ID == 1, "Word filter ID should be 1") expect(t, filter.Find == "imbecile", "Word filter needle should be imbecile") - expect(t, filter.Replacement == "lovely", "Word filter replacement should be lovely") + expect(t, filter.Replace == "lovely", "Word filter replacement should be lovely") // Update expectNilErr(t, c.WordFilters.Update(1, "b", "a")) @@ -1376,13 +1376,13 @@ func TestWordFilters(t *testing.T) { filter = filters[1] expect(t, filter.ID == 1, "Word filter ID should be 1") expect(t, filter.Find == "b", "Word filter needle should be b") - expect(t, filter.Replacement == "a", "Word filter replacement should be a") + expect(t, filter.Replace == "a", "Word filter replacement should be a") filter, err = c.WordFilters.Get(1) expectNilErr(t, err) expect(t, filter.ID == 1, "Word filter ID should be 1") expect(t, filter.Find == "b", "Word filter needle should be imbecile") - expect(t, filter.Replacement == "a", "Word filter replacement should be a") + expect(t, filter.Replace == "a", "Word filter replacement should be a") // TODO: Add a test for ParseMessage relating to word filters @@ -1445,8 +1445,9 @@ func TestWidgets(t *testing.T) { widget := &c.Widget{Position: 0, Side: "rightSidebar", Type: "simple", Enabled: true, Location: "global"} ewidget := &c.WidgetEdit{widget, map[string]string{"Name": "Test", "Text": "Testing"}} - err = ewidget.Create() + wid, err := ewidget.Create() expectNilErr(t, err) + expect(t,wid==1,"wid should be 1") // TODO: Do a test for the widget body widget2, err := c.Widgets.Get(1) diff --git a/patcher/main.go b/patcher/main.go index 0f631578..1a38819f 100644 --- a/patcher/main.go +++ b/patcher/main.go @@ -130,8 +130,10 @@ func patcher(scanner *bufio.Scanner) error { } err := patch(scanner) if err != nil { + fmt.Println("Failed to apply patch "+strconv.Itoa(index+1)) return err } + fmt.Println("Applied patch "+strconv.Itoa(index+1)) patched++ } @@ -140,6 +142,8 @@ func patcher(scanner *bufio.Scanner) error { if err != nil { return err } + } else { + fmt.Println("No new patches found.") } return nil diff --git a/patcher/patches.go b/patcher/patches.go index 723f1c76..75dfc161 100644 --- a/patcher/patches.go +++ b/patcher/patches.go @@ -11,6 +11,7 @@ import ( type tblColumn = qgen.DBTableColumn type tC = tblColumn type tblKey = qgen.DBTableKey +type tK = tblKey func init() { addPatch(0, patch0) @@ -40,6 +41,7 @@ func init() { addPatch(24, patch24) addPatch(25, patch25) addPatch(26, patch26) + addPatch(27, patch27) } func patch0(scanner *bufio.Scanner) (err error) { @@ -56,8 +58,8 @@ func patch0(scanner *bufio.Scanner) (err error) { []tC{ tC{"mid", "int", 0, false, true, ""}, }, - []tblKey{ - tblKey{"mid", "primary", "", false}, + []tK{ + tK{"mid", "primary", "", false}, }, )) if err != nil { @@ -83,8 +85,8 @@ func patch0(scanner *bufio.Scanner) (err error) { tC{"staffOnly", "boolean", 0, false, false, "0"}, tC{"adminOnly", "boolean", 0, false, false, "0"}, }, - []tblKey{ - tblKey{"miid", "primary", "", false}, + []tK{ + tK{"miid", "primary", "", false}, }, )) if err != nil { @@ -190,8 +192,8 @@ func patch3(scanner *bufio.Scanner) error { tC{"ipaddress", "varchar", 200, false, false, ""}, tC{"doneAt", "createdAt", 0, false, false, ""}, }, - []tblKey{ - tblKey{"rlid", "primary", "", false}, + []tK{ + tK{"rlid", "primary", "", false}, }, )) } @@ -253,8 +255,8 @@ func patch4(scanner *bufio.Scanner) error { tC{"allowedGroups", "text", 0, false, false, ""}, tC{"menuID", "int", 0, false, false, "-1"}, }, - []tblKey{ - tblKey{"pid", "primary", "", false}, + []tK{ + tK{"pid", "primary", "", false}, }, )) if err != nil { @@ -277,7 +279,7 @@ func patch5(scanner *bufio.Scanner) error { return err } - err = execStmt(qgen.Builder.SimpleUpdate("menu_items", "path = '/user/edit/'", "path = '/user/edit/critical/'")) + err = execStmt(qgen.Builder.SimpleUpdate("menu_items", "path='/user/edit/'", "path='/user/edit/critical/'")) if err != nil { return err } @@ -296,8 +298,8 @@ func patch5(scanner *bufio.Scanner) error { tC{"scratch8", "varchar", 50, false, false, ""}, tC{"createdAt", "createdAt", 0, false, false, ""}, }, - []tblKey{ - tblKey{"uid", "primary", "", false}, + []tK{ + tK{"uid", "primary", "", false}, }, )) if err != nil { @@ -316,8 +318,8 @@ func patch7(scanner *bufio.Scanner) error { []tC{ tC{"uid", "int", 0, false, false, ""}, // TODO: Make this a foreign key }, - []tblKey{ - tblKey{"uid", "primary", "", false}, + []tK{ + tK{"uid", "primary", "", false}, }, )) } @@ -378,8 +380,7 @@ func patch8(scanner *bufio.Scanner) error { return execStmt(qgen.Builder.CreateTable("updates", "", "", []tC{ tC{"dbVersion", "int", 0, false, false, "0"}, - }, - []tblKey{}, + }, nil, )) } @@ -398,8 +399,8 @@ func patch9(scanner *bufio.Scanner) error { tC{"ipaddress", "varchar", 200, false, false, ""}, tC{"doneAt", "createdAt", 0, false, false, ""}, }, - []tblKey{ - tblKey{"lid", "primary", "", false}, + []tK{ + tK{"lid", "primary", "", false}, }, )) } @@ -461,23 +462,21 @@ func patch11(scanner *bufio.Scanner) error { // We could probably do something more efficient, but as there shouldn't be too many sites right now, we can probably cheat a little, otherwise it'll take forever to get things done return acc().Select("topics").Cols("tid").EachInt(func(tid int) error { stid := itoa(tid) - count, err := acc().Count("attachments").Where("originTable = 'topics' and originID = " + stid).Total() + count, err := acc().Count("attachments").Where("originTable='topics' and originID=" + stid).Total() if err != nil { return err } - _, err = acc().Update("topics").Set("attachCount = ?").Where("tid = " + stid).Exec(count) return err }) /*return acc().Select("replies").Cols("rid").EachInt(func(rid int) error { srid := itoa(rid) - count, err := acc().Count("attachments").Where("originTable = 'replies' and originID = " + srid).Total() + count, err := acc().Count("attachments").Where("originTable='replies' and originID=" + srid).Total() if err != nil { return err } - - _, err = acc().Update("replies").Set("attachCount = ?").Where("rid = " + srid).Exec(count) + _, err = acc().Update("replies").Set("attachCount = ?").Where("rid=" + srid).Exec(count) return err })*/ } @@ -519,24 +518,19 @@ func patch12(scanner *bufio.Scanner) error { } func patch13(scanner *bufio.Scanner) error { - err := execStmt(qgen.Builder.AddColumn("widgets", tC{"wid", "int", 0, false, true, ""}, &tblKey{"wid", "primary", "", false})) - if err != nil { - return err - } - - return nil + return execStmt(qgen.Builder.AddColumn("widgets", tC{"wid", "int", 0, false, true, ""}, &tK{"wid", "primary", "", false})) } func patch14(scanner *bufio.Scanner) error { - err := execStmt(qgen.Builder.AddKey("topics", "title", tblKey{"title", "fulltext", "", false})) + err := execStmt(qgen.Builder.AddKey("topics", "title", tK{"title", "fulltext", "", false})) if err != nil { return err } - err = execStmt(qgen.Builder.AddKey("topics", "content", tblKey{"content", "fulltext", "", false})) + err = execStmt(qgen.Builder.AddKey("topics", "content", tK{"content", "fulltext", "", false})) if err != nil { return err } - err = execStmt(qgen.Builder.AddKey("replies", "content", tblKey{"content", "fulltext", "", false})) + err = execStmt(qgen.Builder.AddKey("replies", "content", tK{"content", "fulltext", "", false})) if err != nil { return err } @@ -585,12 +579,10 @@ func patch17(scanner *bufio.Scanner) error { if err != nil { return err } - err = acc().Select("topics").Cols("parentID").Where("tid = ?").QueryRow(tid).Scan(§ionID) if err != nil { return err } - _, err = acc().Update("attachments").Set("sectionID = ?, extra = ?").Where("originTable = 'replies' AND originID = ?").Exec(sectionID, tid, rid) return err }) @@ -616,12 +608,10 @@ func patch20(scanner *bufio.Scanner) error { if err != nil { return err } - err = acc().Select("activity_stream").Cols("asid").Where("asid = ?").QueryRow(asid).Scan(&asid) if err != sql.ErrNoRows { return err } - _, err = acc().Delete("activity_stream_matches").Where("asid = ?").Run(asid) return err }) @@ -673,8 +663,8 @@ func patch23(scanner *bufio.Scanner) error { tC{"lastReplyAt", "datetime", 0, false, false, ""}, tC{"lastReplyBy", "int", 0, false, false, ""}, }, - []tblKey{ - tblKey{"cid", "primary", "", false}, + []tK{ + tK{"cid", "primary", "", false}, }, )) if err != nil { @@ -693,8 +683,8 @@ func patch23(scanner *bufio.Scanner) error { tC{"body", "varchar", 50, false, false, ""}, tC{"post", "varchar", 50, false, false, "''"}, }, - []tblKey{ - tblKey{"pid", "primary", "", false}, + []tK{ + tK{"pid", "primary", "", false}, }, )) if err != nil { @@ -729,8 +719,8 @@ func patch24(scanner *bufio.Scanner) error { tC{"level", "int", 0, false, false, ""}, tC{"minTime", "int", 0, false, false, ""}, // How long someone needs to have been in their current group before being promoted }, - []tblKey{ - tblKey{"pid", "primary", "", false}, + []tK{ + tK{"pid", "primary", "", false}, }, )) } @@ -747,3 +737,11 @@ func patch26(scanner *bufio.Scanner) error { }, nil, )) } + +func patch27(scanner *bufio.Scanner) error { + err := execStmt(qgen.Builder.AddColumn("moderation_logs", tC{"extra", "text", 0, false, false, ""}, nil)) + if err != nil { + return err + } + return execStmt(qgen.Builder.AddColumn("administration_logs", tC{"extra", "text", 0, false, false, ""}, nil)) +} diff --git a/routes/panel/logs.go b/routes/panel/logs.go index 3479a45e..b9a1584c 100644 --- a/routes/panel/logs.go +++ b/routes/panel/logs.go @@ -100,53 +100,59 @@ func modlogsElementType(action string, elementType string, elementID int, actor return out } -func adminlogsElementType(action string, elementType string, elementID int, actor *c.User) (out string) { +func adminlogsElementType(action string, elementType string, elementID int, actor *c.User, extra string) (out string) { switch elementType { // TODO: Record more detail for this, e.g. which field/s was changed case "user": targetUser := handleUnknownUser(c.Users.Get(elementID)) - out = p.GetTmplPhrasef("panel_logs_administration_action_user_"+action, targetUser.Link, targetUser.Name, actor.Link, actor.Name) + out = p.GetTmplPhrasef("panel_logs_admin_action_user_"+action, targetUser.Link, targetUser.Name, actor.Link, actor.Name) case "group": g, err := c.Groups.Get(elementID) if err != nil { g = &c.Group{Name: p.GetTmplPhrase("group_unknown")} } - out = p.GetTmplPhrasef("panel_logs_administration_action_group_"+action, "/panel/groups/edit/"+strconv.Itoa(g.ID), g.Name, actor.Link, actor.Name) + out = p.GetTmplPhrasef("panel_logs_admin_action_group_"+action, "/panel/groups/edit/"+strconv.Itoa(g.ID), g.Name, actor.Link, actor.Name) case "group_promotion": - out = p.GetTmplPhrasef("panel_logs_administration_action_group_promotion_"+action, actor.Link, actor.Name) + out = p.GetTmplPhrasef("panel_logs_admin_action_group_promotion_"+action, actor.Link, actor.Name) case "forum": f, err := c.Forums.Get(elementID) if err != nil { f = &c.Forum{Name: p.GetTmplPhrase("forum_unknown")} } if action == "reorder" { - out = p.GetTmplPhrasef("panel_logs_administration_action_forum_reorder", actor.Link, actor.Name) + out = p.GetTmplPhrasef("panel_logs_admin_action_forum_reorder", actor.Link, actor.Name) } else { - out = p.GetTmplPhrasef("panel_logs_administration_action_forum_"+action, "/panel/forums/edit/"+strconv.Itoa(f.ID), f.Name, actor.Link, actor.Name) + out = p.GetTmplPhrasef("panel_logs_admin_action_forum_"+action, "/panel/forums/edit/"+strconv.Itoa(f.ID), f.Name, actor.Link, actor.Name) } case "page": pp, err := c.Pages.Get(elementID) if err != nil { pp = &c.CustomPage{Name: p.GetTmplPhrase("page_unknown")} } - out = p.GetTmplPhrasef("panel_logs_administration_action_page_"+action, "/panel/pages/edit/"+strconv.Itoa(pp.ID), pp.Name, actor.Link, actor.Name) + out = p.GetTmplPhrasef("panel_logs_admin_action_page_"+action, "/panel/pages/edit/"+strconv.Itoa(pp.ID), pp.Name, actor.Link, actor.Name) case "setting": s, err := c.SettingBox.Load().(c.SettingMap).BypassGet(action) if err != nil { s = &c.Setting{Name: p.GetTmplPhrase("setting_unknown")} } - out = p.GetTmplPhrasef("panel_logs_administration_action_setting_edit", "/panel/settings/edit/"+s.Name, s.Name, actor.Link, actor.Name) + out = p.GetTmplPhrasef("panel_logs_admin_action_setting_edit", "/panel/settings/edit/"+s.Name, s.Name, actor.Link, actor.Name) case "word_filter": - wf, err := c.WordFilters.Get(elementID) - if err != nil { - wf = &c.WordFilter{} + out = p.GetTmplPhrasef("panel_logs_admin_action_word_filter_"+action, actor.Link, actor.Name) + case "menu": + if action == "suborder" { + out = p.GetTmplPhrasef("panel_logs_admin_action_menu_suborder", elementID, actor.Link, actor.Name) } - out = p.GetTmplPhrasef("panel_logs_administration_action_word_filter_"+action, "/panel/settings/word-filters/", wf.ID, actor.Link, actor.Name) + case "menu_item": + out = p.GetTmplPhrasef("panel_logs_admin_action_menu_item_"+action, "/panel/themes/menus/item/edit/"+strconv.Itoa(elementID), elementID, actor.Link, actor.Name) + case "widget": + out = p.GetTmplPhrasef("panel_logs_admin_action_widget_"+action, "/panel/themes/widgets/", elementID, actor.Link, actor.Name) + case "plugin": + out = p.GetTmplPhrasef("panel_logs_admin_action_plugin_"+action, extra, actor.Link, actor.Name) case "backup": - out = p.GetTmplPhrasef("panel_logs_administration_action_backup_"+action, actor.Link, actor.Name) + out = p.GetTmplPhrasef("panel_logs_admin_action_backup_"+action, actor.Link, actor.Name) } if out == "" { - out = p.GetTmplPhrasef("panel_logs_administration_action_unknown", action, elementType, actor.Link, actor.Name) + out = p.GetTmplPhrasef("panel_logs_admin_action_unknown", action, elementType, actor.Link, actor.Name) } return out } @@ -182,8 +188,6 @@ func LogsAdmin(w http.ResponseWriter, r *http.Request, user c.User) c.RouteError if ferr != nil { return ferr } - basePage.AddNotice("Currently under development") - page, _ := strconv.Atoi(r.FormValue("page")) perPage := 12 offset, page, lastPage := c.PageOffset(c.AdminLogs.Count(), page, perPage) @@ -195,7 +199,7 @@ func LogsAdmin(w http.ResponseWriter, r *http.Request, user c.User) c.RouteError llist := make([]c.PageLogItem, len(logs)) for index, log := range logs { actor := handleUnknownUser(c.Users.Get(log.ActorID)) - action := adminlogsElementType(log.Action, log.ElementType, log.ElementID, actor) + action := adminlogsElementType(log.Action, log.ElementType, log.ElementID, actor, log.Extra) llist[index] = c.PageLogItem{Action: template.HTML(action), IP: log.IP, DoneAt: log.DoneAt} } diff --git a/routes/panel/plugins.go b/routes/panel/plugins.go index af6f77e3..b386633f 100644 --- a/routes/panel/plugins.go +++ b/routes/panel/plugins.go @@ -22,7 +22,7 @@ func Plugins(w http.ResponseWriter, r *http.Request, user c.User) c.RouteError { pluginList = append(pluginList, plugin) } - return renderTemplate("panel", w, r, basePage.Header, c.Panel{basePage,"","","panel_plugins", c.PanelPage{basePage, pluginList, nil}}) + return renderTemplate("panel", w, r, basePage.Header, c.Panel{basePage, "", "", "panel_plugins", c.PanelPage{basePage, pluginList, nil}}) } // TODO: Abstract more of the plugin activation / installation / deactivation logic, so we can test all that more reliably and easily @@ -73,6 +73,10 @@ func PluginsActivate(w http.ResponseWriter, r *http.Request, user c.User, uname if err != nil { return c.LocalError(err.Error(), w, r, user) } + err = c.AdminLogs.CreateExtra("activate", 0, "plugin", user.LastIP, user.ID, c.SanitiseSingleLine(plugin.Name)) + if err != nil { + return c.InternalError(err, w, r) + } http.Redirect(w, r, "/panel/plugins/", http.StatusSeeOther) return nil @@ -107,6 +111,10 @@ func PluginsDeactivate(w http.ResponseWriter, r *http.Request, user c.User, unam if plugin.Deactivate != nil { plugin.Deactivate(plugin) } + err = c.AdminLogs.CreateExtra("deactivate", 0, "plugin", user.LastIP, user.ID, c.SanitiseSingleLine(plugin.Name)) + if err != nil { + return c.InternalError(err, w, r) + } http.Redirect(w, r, "/panel/plugins/", http.StatusSeeOther) return nil @@ -173,6 +181,10 @@ func PluginsInstall(w http.ResponseWriter, r *http.Request, user c.User, uname s if err != nil { return c.LocalError(err.Error(), w, r, user) } + err = c.AdminLogs.CreateExtra("install", 0, "plugin", user.LastIP, user.ID, c.SanitiseSingleLine(plugin.Name)) + if err != nil { + return c.InternalError(err, w, r) + } http.Redirect(w, r, "/panel/plugins/", http.StatusSeeOther) return nil diff --git a/routes/panel/themes.go b/routes/panel/themes.go index 4026d270..b1f20190 100644 --- a/routes/panel/themes.go +++ b/routes/panel/themes.go @@ -58,6 +58,10 @@ func ThemesSetDefault(w http.ResponseWriter, r *http.Request, user c.User, uname if err != nil { return c.InternalError(err, w, r) } + err = c.AdminLogs.CreateExtra("set_default", 0, "theme", user.LastIP, user.ID, c.SanitiseSingleLine(theme.Name)) + if err != nil { + return c.InternalError(err, w, r) + } http.Redirect(w, r, "/panel/themes/", http.StatusSeeOther) return nil @@ -173,10 +177,10 @@ func themesMenuItemSetters(r *http.Request, i c.MenuItem) c.MenuItem { i.Aria = getItem("aria") i.Tooltip = getItem("tooltip") i.TmplName = getItem("tmplname") + i.GuestOnly = false switch getItem("permissions") { case "everyone": - i.GuestOnly = false i.MemberOnly = false i.SuperModOnly = false i.AdminOnly = false @@ -186,17 +190,14 @@ func themesMenuItemSetters(r *http.Request, i c.MenuItem) c.MenuItem { i.SuperModOnly = false i.AdminOnly = false case "member-only": - i.GuestOnly = false i.MemberOnly = true i.SuperModOnly = false i.AdminOnly = false case "supermod-only": - i.GuestOnly = false i.MemberOnly = true i.SuperModOnly = true i.AdminOnly = false case "admin-only": - i.GuestOnly = false i.MemberOnly = true i.SuperModOnly = true i.AdminOnly = true @@ -232,6 +233,11 @@ func ThemesMenuItemEditSubmit(w http.ResponseWriter, r *http.Request, user c.Use if err != nil { return c.InternalErrorJSQ(err, w, r, js) } + err = c.AdminLogs.Create("edit", menuItem.ID, "menu_item", user.LastIP, user.ID) + if err != nil { + return c.InternalError(err, w, r) + } + return successRedirect("/panel/themes/menus/item/edit/"+strconv.Itoa(itemID), w, r, js) } @@ -240,11 +246,11 @@ func ThemesMenuItemCreateSubmit(w http.ResponseWriter, r *http.Request, user c.U if ferr != nil { return ferr } - js := r.PostFormValue("js") == "1" if !user.Perms.ManageThemes { return c.NoPermissionsJSQ(w, r, user, js) } + smenuID := r.PostFormValue("mid") if smenuID == "" { return c.LocalErrorJSQ("No menuID provided", w, r, user, js) @@ -260,6 +266,11 @@ func ThemesMenuItemCreateSubmit(w http.ResponseWriter, r *http.Request, user c.U if err != nil { return c.InternalErrorJSQ(err, w, r, js) } + err = c.AdminLogs.Create("create", itemID, "menu_item", user.LastIP, user.ID) + if err != nil { + return c.InternalError(err, w, r) + } + return successRedirect("/panel/themes/menus/item/edit/"+strconv.Itoa(itemID), w, r, js) } @@ -289,6 +300,11 @@ func ThemesMenuItemDeleteSubmit(w http.ResponseWriter, r *http.Request, user c.U if err != nil { return c.InternalErrorJSQ(err, w, r, js) } + err = c.AdminLogs.Create("delete", menuItem.ID, "menu_item", user.LastIP, user.ID) + if err != nil { + return c.InternalError(err, w, r) + } + return successRedirect("/panel/themes/menus/", w, r, js) } @@ -326,6 +342,11 @@ func ThemesMenuItemOrderSubmit(w http.ResponseWriter, r *http.Request, user c.Us } menuHold.UpdateOrder(updateMap) + err = c.AdminLogs.Create("suborder", menuHold.MenuID, "menu", user.LastIP, user.ID) + if err != nil { + return c.InternalError(err, w, r) + } + return successRedirect("/panel/themes/menus/edit/"+strconv.Itoa(mid), w, r, js) } @@ -409,7 +430,6 @@ func ThemesWidgetsEditSubmit(w http.ResponseWriter, r *http.Request, user c.User if err != nil { return c.LocalErrorJSQ(p.GetErrorPhrase("id_must_be_integer"), w, r, user, js) } - widget, err := c.Widgets.Get(wid) if err == sql.ErrNoRows { return c.NotFoundJSQ(w, r, nil, js) @@ -426,6 +446,10 @@ func ThemesWidgetsEditSubmit(w http.ResponseWriter, r *http.Request, user c.User if err != nil { return c.InternalErrorJSQ(err, w, r, js) } + err = c.AdminLogs.Create("edit", widget.ID, "widget", user.LastIP, user.ID) + if err != nil { + return c.InternalError(err, w, r) + } return successRedirect("/panel/themes/widgets/", w, r, js) } @@ -446,10 +470,14 @@ func ThemesWidgetsCreateSubmit(w http.ResponseWriter, r *http.Request, user c.Us return c.LocalErrorJSQ(err.Error(), w, r, user, js) } - err = ewidget.Create() + wid, err := ewidget.Create() if err != nil { return c.InternalErrorJSQ(err, w, r, js) } + err = c.AdminLogs.Create("create", wid, "widget", user.LastIP, user.ID) + if err != nil { + return c.InternalError(err, w, r) + } return successRedirect("/panel/themes/widgets/", w, r, js) } @@ -479,6 +507,10 @@ func ThemesWidgetsDeleteSubmit(w http.ResponseWriter, r *http.Request, user c.Us if err != nil { return c.InternalError(err, w, r) } + err = c.AdminLogs.Create("delete", widget.ID, "widget", user.LastIP, user.ID) + if err != nil { + return c.InternalError(err, w, r) + } return successRedirect("/panel/themes/widgets/", w, r, js) } diff --git a/routes/panel/word_filters.go b/routes/panel/word_filters.go index c022ee06..c74a495f 100644 --- a/routes/panel/word_filters.go +++ b/routes/panel/word_filters.go @@ -2,6 +2,7 @@ package panel import ( "database/sql" + "encoding/json" "net/http" "strconv" "strings" @@ -95,13 +96,24 @@ func WordFiltersEditSubmit(w http.ResponseWriter, r *http.Request, user c.User, // Unlike with find, it's okay if we leave this blank, as this means that the admin wants to remove the word entirely with no replacement replace := strings.TrimSpace(r.PostFormValue("replace")) + wf, err := c.WordFilters.Get(wfid) + if err == sql.ErrNoRows { + return c.LocalErrorJSQ("This word filter doesn't exist.", w, r, user, js) + } else if err != nil { + return c.InternalErrorJSQ(err, w, r, js) + } err = c.WordFilters.Update(wfid, find, replace) if err != nil { return c.InternalErrorJSQ(err, w, r, js) } - err = c.AdminLogs.Create("edit", wfid, "word_filter", user.LastIP, user.ID) + + lBytes, err := json.Marshal(c.WordFilterDiff{wf.Find, wf.Replace, find, replace}) if err != nil { - return c.InternalError(err, w, r) + return c.InternalErrorJSQ(err, w, r, js) + } + err = c.AdminLogs.CreateExtra("edit", wfid, "word_filter", user.LastIP, user.ID, string(lBytes)) + if err != nil { + return c.InternalErrorJSQ(err, w, r, js) } http.Redirect(w, r, "/panel/settings/word-filters/", http.StatusSeeOther) diff --git a/schema/mssql/query_administration_logs.sql b/schema/mssql/query_administration_logs.sql index 57aafd25..92dd4796 100644 --- a/schema/mssql/query_administration_logs.sql +++ b/schema/mssql/query_administration_logs.sql @@ -4,5 +4,6 @@ CREATE TABLE [administration_logs] ( [elementType] nvarchar (100) not null, [ipaddress] nvarchar (200) not null, [actorID] int not null, - [doneAt] datetime not null + [doneAt] datetime not null, + [extra] nvarchar (MAX) not null ); \ No newline at end of file diff --git a/schema/mssql/query_moderation_logs.sql b/schema/mssql/query_moderation_logs.sql index 3a235f33..34431dbf 100644 --- a/schema/mssql/query_moderation_logs.sql +++ b/schema/mssql/query_moderation_logs.sql @@ -4,5 +4,6 @@ CREATE TABLE [moderation_logs] ( [elementType] nvarchar (100) not null, [ipaddress] nvarchar (200) not null, [actorID] int not null, - [doneAt] datetime not null + [doneAt] datetime not null, + [extra] nvarchar (MAX) not null ); \ No newline at end of file diff --git a/schema/mysql/query_administration_logs.sql b/schema/mysql/query_administration_logs.sql index eb4c3cd1..8896b7bb 100644 --- a/schema/mysql/query_administration_logs.sql +++ b/schema/mysql/query_administration_logs.sql @@ -4,5 +4,6 @@ CREATE TABLE `administration_logs` ( `elementType` varchar(100) not null, `ipaddress` varchar(200) not null, `actorID` int not null, - `doneAt` datetime not null + `doneAt` datetime not null, + `extra` text not null ); \ No newline at end of file diff --git a/schema/mysql/query_moderation_logs.sql b/schema/mysql/query_moderation_logs.sql index 5eb65133..daadeb55 100644 --- a/schema/mysql/query_moderation_logs.sql +++ b/schema/mysql/query_moderation_logs.sql @@ -4,5 +4,6 @@ CREATE TABLE `moderation_logs` ( `elementType` varchar(100) not null, `ipaddress` varchar(200) not null, `actorID` int not null, - `doneAt` datetime not null + `doneAt` datetime not null, + `extra` text not null ); \ No newline at end of file diff --git a/schema/pgsql/query_administration_logs.sql b/schema/pgsql/query_administration_logs.sql index 3035a418..4bdec13e 100644 --- a/schema/pgsql/query_administration_logs.sql +++ b/schema/pgsql/query_administration_logs.sql @@ -4,5 +4,6 @@ CREATE TABLE "administration_logs" ( `elementType` varchar (100) not null, `ipaddress` varchar (200) not null, `actorID` int not null, - `doneAt` timestamp not null + `doneAt` timestamp not null, + `extra` text not null ); \ No newline at end of file diff --git a/schema/pgsql/query_moderation_logs.sql b/schema/pgsql/query_moderation_logs.sql index 77250c4b..aac929c0 100644 --- a/schema/pgsql/query_moderation_logs.sql +++ b/schema/pgsql/query_moderation_logs.sql @@ -4,5 +4,6 @@ CREATE TABLE "moderation_logs" ( `elementType` varchar (100) not null, `ipaddress` varchar (200) not null, `actorID` int not null, - `doneAt` timestamp not null + `doneAt` timestamp not null, + `extra` text not null ); \ No newline at end of file diff --git a/templates/panel_adminlogs.html b/templates/panel_adminlogs.html index f9739e91..ca9f5636 100644 --- a/templates/panel_adminlogs.html +++ b/templates/panel_adminlogs.html @@ -1,5 +1,5 @@
-

{{lang "panel_logs_administration_head"}}

+

{{lang "panel_logs_admin_head"}}

{{range .Logs}} @@ -15,7 +15,7 @@
{{else}}
- {{lang "panel_logs_administration_no_logs"}} + {{lang "panel_logs_admin_no_logs"}}
{{end}} diff --git a/templates/panel_word_filters.html b/templates/panel_word_filters.html index 5936312f..abd98515 100644 --- a/templates/panel_word_filters.html +++ b/templates/panel_word_filters.html @@ -6,7 +6,7 @@
{{.Find}} - {{.Replacement}} + {{.Replace}}