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:
parent
7e1372b0b4
commit
ffdc9e7705
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
// 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)
|
||||
func (s *DefaultWordFilterStore) Create(find string, replace string) (int, error) {
|
||||
res, err := s.create.Exec(find, replace)
|
||||
if err != nil {
|
||||
return err
|
||||
return 0, err
|
||||
}
|
||||
return s.ReloadAll()
|
||||
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
|
||||
|
|
|
@ -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.",
|
||||
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue