Adminlogs: Custom pages, settings, word filters and backups.

WordFilters.Create now returns the ID.
Add the WordFilters.Get method.
Reduce the boilerplate in the backup page and page store.

Sanitise the names and titles in the Page Manager.

Add page_unknown phrase.
Add setting_unknown phrase.
Add panel_logs_administration_action_page_create phrase.
Add panel_logs_administration_action_page_delete phrase.
Add panel_logs_administration_action_page_edit phrase.
Add panel_logs_administration_action_setting_edit phrase.
Add panel_logs_administration_action_word_filter_create phrase.
Add panel_logs_administration_action_word_filter_delete phrase.
Add panel_logs_administration_action_word_filter_edit phrase.
Add panel_logs_administration_action_backup_download phrase.
This commit is contained in:
Azareal 2019-11-09 07:46:50 +10:00
parent 7e1372b0b4
commit ffdc9e7705
8 changed files with 129 additions and 56 deletions

View File

@ -38,12 +38,12 @@ func BlankCustomPage() *CustomPage {
return new(CustomPage)
}
func (page *CustomPage) AddAllowedGroup(gid int) {
page.AllowedGroups = append(page.AllowedGroups, gid)
func (p *CustomPage) AddAllowedGroup(gid int) {
p.AllowedGroups = append(p.AllowedGroups, gid)
}
func (page *CustomPage) getRawAllowedGroups() (rawAllowedGroups string) {
for _, group := range page.AllowedGroups {
func (p *CustomPage) getRawAllowedGroups() (rawAllowedGroups string) {
for _, group := range p.AllowedGroups {
rawAllowedGroups += strconv.Itoa(group) + ","
}
if len(rawAllowedGroups) > 0 {
@ -52,18 +52,17 @@ func (page *CustomPage) getRawAllowedGroups() (rawAllowedGroups string) {
return rawAllowedGroups
}
func (page *CustomPage) Commit() error {
_, err := customPageStmts.update.Exec(page.Name, page.Title, page.Body, page.getRawAllowedGroups(), page.MenuID, page.ID)
Pages.Reload(page.ID)
func (p *CustomPage) Commit() error {
_, err := customPageStmts.update.Exec(p.Name, p.Title, p.Body, p.getRawAllowedGroups(), p.MenuID, p.ID)
Pages.Reload(p.ID)
return err
}
func (page *CustomPage) Create() (int, error) {
res, err := customPageStmts.create.Exec(page.Name, page.Title, page.Body, page.getRawAllowedGroups(), page.MenuID)
func (p *CustomPage) Create() (int, error) {
res, err := customPageStmts.create.Exec(p.Name, p.Title, p.Body, p.getRawAllowedGroups(), p.MenuID)
if err != nil {
return 0, err
}
pid64, err := res.LastInsertId()
return int(pid64), err
}

View File

@ -4,7 +4,7 @@ import (
"database/sql"
"sync/atomic"
"github.com/Azareal/Gosora/query_gen"
qgen "github.com/Azareal/Gosora/query_gen"
)
// TODO: Move some features into methods on this?
@ -19,7 +19,8 @@ var WordFilters WordFilterStore
type WordFilterStore interface {
ReloadAll() error
GetAll() (filters map[int]*WordFilter, err error)
Create(find string, replacement string) error
Get(id int) (*WordFilter, error)
Create(find string, replacement string) (int, error)
Delete(id int) error
Update(id int, find string, replacement string) error
Length() int
@ -31,6 +32,7 @@ type DefaultWordFilterStore struct {
box atomic.Value // An atomic value holding a WordFilterMap
getAll *sql.Stmt
get *sql.Stmt
create *sql.Stmt
delete *sql.Stmt
update *sql.Stmt
@ -41,6 +43,7 @@ func NewDefaultWordFilterStore(acc *qgen.Accumulator) (*DefaultWordFilterStore,
wf := "word_filters"
store := &DefaultWordFilterStore{
getAll: acc.Select(wf).Columns("wfid,find,replacement").Prepare(),
get: acc.Select(wf).Columns("wfid,find,replacement").Where("wfid = ?").Prepare(),
create: acc.Insert(wf).Columns("find,replacement").Fields("?,?").Prepare(),
delete: acc.Delete(wf).Where("wfid = ?").Prepare(),
update: acc.Update(wf).Set("find = ?, replacement = ?").Where("wfid = ?").Prepare(),
@ -93,13 +96,23 @@ func (s *DefaultWordFilterStore) GetAll() (filters map[int]*WordFilter, err erro
return s.box.Load().(map[int]*WordFilter), nil
}
// Create adds a new word filter to the database and refreshes the memory cache
func (s *DefaultWordFilterStore) Create(find string, replace string) error {
_, err := s.create.Exec(find, replace)
if err != nil {
return err
func (s *DefaultWordFilterStore) Get(id int) (*WordFilter, error) {
wf := &WordFilter{ID: id}
err := s.get.QueryRow(id).Scan(&wf.Find, &wf.Replacement)
return wf, err
}
return s.ReloadAll()
// Create adds a new word filter to the database and refreshes the memory cache
func (s *DefaultWordFilterStore) Create(find string, replace string) (int, error) {
res, err := s.create.Exec(find, replace)
if err != nil {
return 0, err
}
id64, err := res.LastInsertId()
if err != nil {
return 0, err
}
return int(id64), s.ReloadAll()
}
// Delete removes a word filter from the database and refreshes the memory cache

View File

@ -1021,6 +1021,8 @@
"topic_unknown":"Unknown",
"group_unknown":"Unknown",
"forum_unknown":"Unknown",
"page_unknown":"Unknown",
"setting_unknown":"unknown",
"panel_logs_administration_head":"Admin Action Logs",
"panel_logs_administration_action_user_edit":"User <a href='%s'>%s</a> was modified by <a href='%s'>%s</a>",
@ -1032,6 +1034,14 @@
"panel_logs_administration_action_forum_create":"Forum <a href='%s'>%s</a> was created by <a href='%s'>%s</a>",
"panel_logs_administration_action_forum_delete":"Forum <a href='%s'>%s</a> was deleted by <a href='%s'>%s</a>",
"panel_logs_administration_action_forum_edit":"Forum <a href='%s'>%s</a> was modified by <a href='%s'>%s</a>",
"panel_logs_administration_action_page_create":"Page <a href='%s'>%s</a> was created by <a href='%s'>%s</a>",
"panel_logs_administration_action_page_delete":"Page <a href='%s'>%s</a> was deleted by <a href='%s'>%s</a>",
"panel_logs_administration_action_page_edit":"Page <a href='%s'>%s</a> was modified by <a href='%s'>%s</a>",
"panel_logs_administration_action_setting_edit":"Setting <a href='%s'>%s</a> was modified by <a href='%s'>%s</a>",
"panel_logs_administration_action_word_filter_create":"Word Filter <a href='%s'>%d</a> was created by <a href='%s'>%s</a>",
"panel_logs_administration_action_word_filter_delete":"Word Filter <a href='%s'>%d</a> was deleted by <a href='%s'>%s</a>",
"panel_logs_administration_action_word_filter_edit":"Word Filter <a href='%s'>%d</a> was modified by <a href='%s'>%s</a>",
"panel_logs_administration_action_backup_download":"A backup was downloaded by <a href='%s'>%s</a>",
"panel_logs_administration_action_unknown":"Unknown action '%s' on elementType '%s' by <a href='%s'>%s</a>",
"panel_logs_administration_no_logs":"There aren't any events logged.",

View File

@ -28,19 +28,24 @@ func Backups(w http.ResponseWriter, r *http.Request, user c.User, backupURL stri
if err != nil {
return c.NotFound(w, r, basePage.Header)
}
w.Header().Set("Content-Length", strconv.FormatInt(info.Size(), 10))
h := w.Header()
h.Set("Content-Length", strconv.FormatInt(info.Size(), 10))
if ext == ".sql" {
// TODO: Change the served filename to gosora_backup_%timestamp%.sql, the time the file was generated, not when it was modified aka what the name of it should be
w.Header().Set("Content-Disposition", "attachment; filename=gosora_backup.sql")
w.Header().Set("Content-Type", "application/sql")
h.Set("Content-Disposition", "attachment; filename=gosora_backup.sql")
h.Set("Content-Type", "application/sql")
} else {
// TODO: Change the served filename to gosora_backup_%timestamp%.zip, the time the file was generated, not when it was modified aka what the name of it should be
w.Header().Set("Content-Disposition", "attachment; filename=gosora_backup.zip")
w.Header().Set("Content-Type", "application/zip")
h.Set("Content-Disposition", "attachment; filename=gosora_backup.zip")
h.Set("Content-Type", "application/zip")
}
// TODO: Fix the problem where non-existent files aren't greeted with custom 404s on ServeFile()'s side
http.ServeFile(w, r, "./backups/"+backupURL)
err = c.AdminLogs.Create("download", 0, "backup", user.LastIP, user.ID)
if err != nil {
return c.InternalError(err, w, r)
}
return nil
}

View File

@ -124,6 +124,26 @@ func adminlogsElementType(action string, elementType string, elementID int, acto
} else {
out = p.GetTmplPhrasef("panel_logs_administration_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)
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)
case "word_filter":
wf, err := c.WordFilters.Get(elementID)
if err != nil {
wf = &c.WordFilter{}
}
out = p.GetTmplPhrasef("panel_logs_administration_action_word_filter_"+action, "/panel/settings/word-filters/", wf.ID, actor.Link, actor.Name)
case "backup":
out = p.GetTmplPhrasef("panel_logs_administration_action_backup_"+action, actor.Link, actor.Name)
}
if out == "" {
out = p.GetTmplPhrasef("panel_logs_administration_action_unknown", action, elementType, actor.Link, actor.Name)

View File

@ -42,24 +42,28 @@ func PagesCreateSubmit(w http.ResponseWriter, r *http.Request, user c.User) c.Ro
return ferr
}
pname := r.PostFormValue("name")
if pname == "" {
name := c.SanitiseSingleLine(r.PostFormValue("name"))
if name == "" {
return c.LocalError("No name was provided for this page", w, r, user)
}
ptitle := r.PostFormValue("title")
if ptitle == "" {
title := c.SanitiseSingleLine(r.PostFormValue("title"))
if title == "" {
return c.LocalError("No title was provided for this page", w, r, user)
}
pbody := r.PostFormValue("body")
if pbody == "" {
body := r.PostFormValue("body")
if body == "" {
return c.LocalError("No body was provided for this page", w, r, user)
}
page := c.BlankCustomPage()
page.Name = pname
page.Title = ptitle
page.Body = pbody
_, err := page.Create()
page.Name = name
page.Title = title
page.Body = body
pid, err := page.Create()
if err != nil {
return c.InternalError(err, w, r)
}
err = c.AdminLogs.Create("create", pid, "page", user.LastIP, user.ID)
if err != nil {
return c.InternalError(err, w, r)
}
@ -102,16 +106,16 @@ func PagesEditSubmit(w http.ResponseWriter, r *http.Request, user c.User, spid s
if err != nil {
return c.LocalError("Page ID needs to be an integer", w, r, user)
}
pname := r.PostFormValue("name")
if pname == "" {
name := c.SanitiseSingleLine(r.PostFormValue("name"))
if name == "" {
return c.LocalError("No name was provided for this page", w, r, user)
}
ptitle := r.PostFormValue("title")
if ptitle == "" {
title := c.SanitiseSingleLine(r.PostFormValue("title"))
if title == "" {
return c.LocalError("No title was provided for this page", w, r, user)
}
pbody := r.PostFormValue("body")
if pbody == "" {
body := r.PostFormValue("body")
if body == "" {
return c.LocalError("No body was provided for this page", w, r, user)
}
@ -119,13 +123,17 @@ func PagesEditSubmit(w http.ResponseWriter, r *http.Request, user c.User, spid s
if err != nil {
return c.NotFound(w, r, nil)
}
page.Name = pname
page.Title = ptitle
page.Body = pbody
page.Name = name
page.Title = title
page.Body = body
err = page.Commit()
if err != nil {
return c.InternalError(err, w, r)
}
err = c.AdminLogs.Create("edit", pid, "page", user.LastIP, user.ID)
if err != nil {
return c.InternalError(err, w, r)
}
http.Redirect(w, r, "/panel/pages/?updated=1", http.StatusSeeOther)
return nil
@ -145,6 +153,10 @@ func PagesDeleteSubmit(w http.ResponseWriter, r *http.Request, user c.User, spid
if err != nil {
return c.InternalError(err, w, r)
}
err = c.AdminLogs.Create("delete", pid, "page", user.LastIP, user.ID)
if err != nil {
return c.InternalError(err, w, r)
}
http.Redirect(w, r, "/panel/pages/?deleted=1", http.StatusSeeOther)
return nil

View File

@ -77,7 +77,6 @@ func SettingEdit(w http.ResponseWriter, r *http.Request, user c.User, sname stri
if err != nil {
return c.LocalError("The value of this setting couldn't be converted to an integer", w, r, user)
}
for index, label := range strings.Split(llist, ",") {
itemList = append(itemList, c.OptionLabel{
Label: label,
@ -94,7 +93,7 @@ func SettingEdit(w http.ResponseWriter, r *http.Request, user c.User, sname stri
return renderTemplate("panel", w, r, basePage.Header, c.Panel{basePage, "", "", "panel_setting", &pi})
}
func SettingEditSubmit(w http.ResponseWriter, r *http.Request, user c.User, sname string) c.RouteError {
func SettingEditSubmit(w http.ResponseWriter, r *http.Request, user c.User, name string) c.RouteError {
headerLite, ferr := c.SimplePanelUserCheck(w, r, &user)
if ferr != nil {
return ferr
@ -103,11 +102,17 @@ func SettingEditSubmit(w http.ResponseWriter, r *http.Request, user c.User, snam
return c.NoPermissions(w, r, user)
}
scontent := c.SanitiseBody(r.PostFormValue("value"))
rerr := headerLite.Settings.Update(sname, scontent)
name = c.SanitiseSingleLine(name)
content := c.SanitiseBody(r.PostFormValue("value"))
rerr := headerLite.Settings.Update(name, content)
if rerr != nil {
return rerr
}
// TODO: Avoid this hack
err := c.AdminLogs.Create(name, 0, "setting", user.LastIP, user.ID)
if err != nil {
return c.InternalError(err, w, r)
}
http.Redirect(w, r, "/panel/settings/", http.StatusSeeOther)
return nil

View File

@ -47,10 +47,14 @@ func WordFiltersCreateSubmit(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"))
err := c.WordFilters.Create(find, replace)
wfid, err := c.WordFilters.Create(find, replace)
if err != nil {
return c.InternalErrorJSQ(err, w, r, js)
}
err = c.AdminLogs.Create("create", wfid, "word_filter", user.LastIP, user.ID)
if err != nil {
return c.InternalError(err, w, r)
}
return successRedirect("/panel/settings/word-filters/", w, r, js)
}
@ -70,7 +74,7 @@ func WordFiltersEdit(w http.ResponseWriter, r *http.Request, user c.User, wfid s
return renderTemplate("panel", w, r, basePage.Header, c.Panel{basePage, "", "", "panel_word_filters_edit", &pi})
}
func WordFiltersEditSubmit(w http.ResponseWriter, r *http.Request, user c.User, wfid string) c.RouteError {
func WordFiltersEditSubmit(w http.ResponseWriter, r *http.Request, user c.User, swfid string) c.RouteError {
_, ferr := c.SimplePanelUserCheck(w, r, &user)
if ferr != nil {
return ferr
@ -80,11 +84,10 @@ func WordFiltersEditSubmit(w http.ResponseWriter, r *http.Request, user c.User,
return c.NoPermissionsJSQ(w, r, user, js)
}
id, err := strconv.Atoi(wfid)
wfid, err := strconv.Atoi(swfid)
if err != nil {
return c.LocalErrorJSQ("The word filter ID must be an integer.", w, r, user, js)
}
find := strings.TrimSpace(r.PostFormValue("find"))
if find == "" {
return c.LocalErrorJSQ("You need to specify what word you want to match", w, r, user, js)
@ -92,35 +95,41 @@ 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"))
err = c.WordFilters.Update(id, find, replace)
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)
if err != nil {
return c.InternalError(err, w, r)
}
http.Redirect(w, r, "/panel/settings/word-filters/", http.StatusSeeOther)
return nil
}
func WordFiltersDeleteSubmit(w http.ResponseWriter, r *http.Request, user c.User, wfid string) c.RouteError {
func WordFiltersDeleteSubmit(w http.ResponseWriter, r *http.Request, user c.User, swfid string) c.RouteError {
_, ferr := c.SimplePanelUserCheck(w, r, &user)
if ferr != nil {
return ferr
}
js := (r.PostFormValue("js") == "1")
if !user.Perms.EditSettings {
return c.NoPermissionsJSQ(w, r, user, js)
}
id, err := strconv.Atoi(wfid)
wfid, err := strconv.Atoi(swfid)
if err != nil {
return c.LocalErrorJSQ("The word filter ID must be an integer.", w, r, user, js)
}
err = c.WordFilters.Delete(id)
err = c.WordFilters.Delete(wfid)
if err == sql.ErrNoRows {
return c.LocalErrorJSQ("This word filter doesn't exist", w, r, user, js)
}
err = c.AdminLogs.Create("delete", wfid, "word_filter", user.LastIP, user.ID)
if err != nil {
return c.InternalError(err, w, r)
}
http.Redirect(w, r, "/panel/settings/word-filters/", http.StatusSeeOther)
return nil