Added the Page Manager for faster and easier custom page creation.
Added the PageStore. Renamed account_own_edit.html to account_own_edit_password.html Renamed custom-page.html to custom_page.html Renamed the pre_render_custom_page hook to pre_render_tmpl_page. Added a new pre_render_custom_page hook, not to be confused with the previous one. Renamed the pre_render_account_own_edit_critical hook to pre_render_account_own_edit_password. Moved the report forum ID into a constant. Renamed todaysReportCount to topicsTopicCountByForum and made it more generic. Renamed panel-menu.html to panel_menu.html Renamed panel-inner-menu.html to panel_inner_menu.html Removed an irrelevant editable_parent in a no results row. Fixed the profile page loading the wrong profile.css Fixed a bug where the last poster avatar would break on the forum page. Added the AddNotice method to *Header. Greatly simplified many of the page struct definitions. Added the ErrorPage page struct and refactored the error pages to use it. Added the BasePanelPage page struct and refactored the panel page structs to use it. Tweaked the DefaultHeader function to set the user on the spot rather than after the fact. Simplified AccountEditAvatarSubmit into a redirect. Add the addElement closure in the control panel dashboard to reduce the amount of complexity. Tweaked LogWarning to better handle nils. Added the account_username phrase. Added the account_avatar phrase. Added the account_email phrase. Added the panel_pages phrase. Added the panel_pages_edit phrase. Added the panel_page_created phrase. Added the panel_page_updated phrase. Added the panel_page_deleted phrase. Added the account_menu_security phrase. Added the panel_menu_pages phrase. Added the panel_pages_head phrase. Added the panel_pages_edit_button_aria phrase. Added the panel_pages_delete_button_aria phrase. Added the panel_pages_no_pages phrase. Added the panel_pages_create_head phrase. Added the panel_pages_create_name phrase. Added the panel_pages_create_name_placeholder phrase. Added the panel_pages_create_title phrase. Added the panel_pages_create_title_placeholder phrase. Added the panel_pages_create_body_placeholder phrase. Added the panel_pages_create_submit_button phrase. Added the panel_pages_edit_head phrase. Added the panel_pages_name phrase. Added the panel_pages_title phrase. Added the panel_pages_edit_update_button phrase. Began work on two-factor authentication. Made more progress with the Nox Theme.
This commit is contained in:
parent
d897e05256
commit
a5f5f4af7e
|
@ -14,6 +14,7 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"../query_gen/lib"
|
"../query_gen/lib"
|
||||||
|
"./gauth"
|
||||||
//"golang.org/x/crypto/argon2"
|
//"golang.org/x/crypto/argon2"
|
||||||
"golang.org/x/crypto/bcrypt"
|
"golang.org/x/crypto/bcrypt"
|
||||||
)
|
)
|
||||||
|
@ -272,3 +273,12 @@ func Argon2GeneratePassword(password string) (hash string, salt string, err erro
|
||||||
return fmt.Sprintf("argon2$%d%d%d%d%s%s", argon2Time, argon2Memory, argon2Threads, argon2KeyLen, salt, hash), string(sbytes), nil
|
return fmt.Sprintf("argon2$%d%d%d%d%s%s", argon2Time, argon2Memory, argon2Threads, argon2KeyLen, salt, hash), string(sbytes), nil
|
||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
// TODO: Not sure if these work, test them with Google Authenticator
|
||||||
|
func GenerateGAuthSecret() (string, error) {
|
||||||
|
return GenerateSafeString(24)
|
||||||
|
}
|
||||||
|
func VerifyGAuthToken(secret string, token string) (bool, error) {
|
||||||
|
trueToken, err := gauth.GetTOTPToken(secret)
|
||||||
|
return trueToken == token, err
|
||||||
|
}
|
||||||
|
|
|
@ -72,18 +72,30 @@ func LogError(err error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func LogWarning(err error) {
|
func LogWarning(err error) {
|
||||||
|
var errmsg string
|
||||||
|
if err == nil {
|
||||||
|
errmsg = "Unknown error"
|
||||||
|
} else {
|
||||||
|
errmsg = err.Error()
|
||||||
|
}
|
||||||
stack := debug.Stack()
|
stack := debug.Stack()
|
||||||
log.Print(err.Error()+"\n", string(stack))
|
log.Print(errmsg+"\n", string(stack))
|
||||||
errorBufferMutex.Lock()
|
errorBufferMutex.Lock()
|
||||||
defer errorBufferMutex.Unlock()
|
defer errorBufferMutex.Unlock()
|
||||||
errorBuffer = append(errorBuffer, ErrorItem{err, stack})
|
errorBuffer = append(errorBuffer, ErrorItem{err, stack})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func errorHeader(w http.ResponseWriter, user User, title string) *Header {
|
||||||
|
header := DefaultHeader(w, user)
|
||||||
|
header.Title = title
|
||||||
|
return header
|
||||||
|
}
|
||||||
|
|
||||||
// TODO: Dump the request?
|
// TODO: Dump the request?
|
||||||
// InternalError is the main function for handling internal errors, while simultaneously printing out a page for the end-user to let them know that *something* has gone wrong
|
// InternalError is the main function for handling internal errors, while simultaneously printing out a page for the end-user to let them know that *something* has gone wrong
|
||||||
// ? - Add a user parameter?
|
// ? - Add a user parameter?
|
||||||
func InternalError(err error, w http.ResponseWriter, r *http.Request) RouteError {
|
func InternalError(err error, w http.ResponseWriter, r *http.Request) RouteError {
|
||||||
pi := Page{"Internal Server Error", GuestUser, DefaultHeader(w), tList, "A problem has occurred in the system."}
|
pi := ErrorPage{errorHeader(w, GuestUser, "Internal Server Error"), "A problem has occurred in the system."}
|
||||||
handleErrorTemplate(w, r, pi)
|
handleErrorTemplate(w, r, pi)
|
||||||
LogError(err)
|
LogError(err)
|
||||||
return HandledRouteError()
|
return HandledRouteError()
|
||||||
|
@ -129,7 +141,7 @@ func SilentInternalErrorXML(err error, w http.ResponseWriter, r *http.Request) R
|
||||||
|
|
||||||
func PreError(errmsg string, w http.ResponseWriter, r *http.Request) RouteError {
|
func PreError(errmsg string, w http.ResponseWriter, r *http.Request) RouteError {
|
||||||
w.WriteHeader(500)
|
w.WriteHeader(500)
|
||||||
pi := Page{"Error", GuestUser, DefaultHeader(w), tList, errmsg}
|
pi := ErrorPage{errorHeader(w, GuestUser, "Error"), errmsg}
|
||||||
handleErrorTemplate(w, r, pi)
|
handleErrorTemplate(w, r, pi)
|
||||||
return HandledRouteError()
|
return HandledRouteError()
|
||||||
}
|
}
|
||||||
|
@ -148,9 +160,10 @@ func PreErrorJSQ(errmsg string, w http.ResponseWriter, r *http.Request, isJs boo
|
||||||
}
|
}
|
||||||
|
|
||||||
// LocalError is an error shown to the end-user when something goes wrong and it's not the software's fault
|
// LocalError is an error shown to the end-user when something goes wrong and it's not the software's fault
|
||||||
|
// TODO: Pass header in for this and similar errors instead of having to pass in both user and w? Would also allow for more stateful things, although this could be a problem
|
||||||
func LocalError(errmsg string, w http.ResponseWriter, r *http.Request, user User) RouteError {
|
func LocalError(errmsg string, w http.ResponseWriter, r *http.Request, user User) RouteError {
|
||||||
w.WriteHeader(500)
|
w.WriteHeader(500)
|
||||||
pi := Page{"Local Error", user, DefaultHeader(w), tList, errmsg}
|
pi := ErrorPage{errorHeader(w, user, "Local Error"), errmsg}
|
||||||
handleErrorTemplate(w, r, pi)
|
handleErrorTemplate(w, r, pi)
|
||||||
return HandledRouteError()
|
return HandledRouteError()
|
||||||
}
|
}
|
||||||
|
@ -172,7 +185,7 @@ func LocalErrorJS(errmsg string, w http.ResponseWriter, r *http.Request) RouteEr
|
||||||
// NoPermissions is an error shown to the end-user when they try to access an area which they aren't authorised to access
|
// NoPermissions is an error shown to the end-user when they try to access an area which they aren't authorised to access
|
||||||
func NoPermissions(w http.ResponseWriter, r *http.Request, user User) RouteError {
|
func NoPermissions(w http.ResponseWriter, r *http.Request, user User) RouteError {
|
||||||
w.WriteHeader(403)
|
w.WriteHeader(403)
|
||||||
pi := Page{"Local Error", user, DefaultHeader(w), tList, "You don't have permission to do that."}
|
pi := ErrorPage{errorHeader(w, user, "Local Error"), "You don't have permission to do that."}
|
||||||
handleErrorTemplate(w, r, pi)
|
handleErrorTemplate(w, r, pi)
|
||||||
return HandledRouteError()
|
return HandledRouteError()
|
||||||
}
|
}
|
||||||
|
@ -193,7 +206,7 @@ func NoPermissionsJS(w http.ResponseWriter, r *http.Request, user User) RouteErr
|
||||||
// ? - Is this actually used? Should it be used? A ban in Gosora should be more of a permission revocation to stop them posting rather than something which spits up an error page, right?
|
// ? - Is this actually used? Should it be used? A ban in Gosora should be more of a permission revocation to stop them posting rather than something which spits up an error page, right?
|
||||||
func Banned(w http.ResponseWriter, r *http.Request, user User) RouteError {
|
func Banned(w http.ResponseWriter, r *http.Request, user User) RouteError {
|
||||||
w.WriteHeader(403)
|
w.WriteHeader(403)
|
||||||
pi := Page{"Banned", user, DefaultHeader(w), tList, "You have been banned from this site."}
|
pi := ErrorPage{errorHeader(w, user, "Banned"), "You have been banned from this site."}
|
||||||
handleErrorTemplate(w, r, pi)
|
handleErrorTemplate(w, r, pi)
|
||||||
return HandledRouteError()
|
return HandledRouteError()
|
||||||
}
|
}
|
||||||
|
@ -225,7 +238,7 @@ func LoginRequiredJSQ(w http.ResponseWriter, r *http.Request, user User, isJs bo
|
||||||
// LoginRequired is an error shown to the end-user when they try to access an area which requires them to login
|
// LoginRequired is an error shown to the end-user when they try to access an area which requires them to login
|
||||||
func LoginRequired(w http.ResponseWriter, r *http.Request, user User) RouteError {
|
func LoginRequired(w http.ResponseWriter, r *http.Request, user User) RouteError {
|
||||||
w.WriteHeader(401)
|
w.WriteHeader(401)
|
||||||
pi := Page{"Local Error", user, DefaultHeader(w), tList, "You need to login to do that."}
|
pi := ErrorPage{errorHeader(w, user, "Local Error"), "You need to login to do that."}
|
||||||
handleErrorTemplate(w, r, pi)
|
handleErrorTemplate(w, r, pi)
|
||||||
return HandledRouteError()
|
return HandledRouteError()
|
||||||
}
|
}
|
||||||
|
@ -241,7 +254,7 @@ func LoginRequiredJS(w http.ResponseWriter, r *http.Request, user User) RouteErr
|
||||||
// ? - Should we add JS and JSQ versions of this?
|
// ? - Should we add JS and JSQ versions of this?
|
||||||
func SecurityError(w http.ResponseWriter, r *http.Request, user User) RouteError {
|
func SecurityError(w http.ResponseWriter, r *http.Request, user User) RouteError {
|
||||||
w.WriteHeader(403)
|
w.WriteHeader(403)
|
||||||
pi := Page{"Security Error", user, DefaultHeader(w), tList, "There was a security issue with your request."}
|
pi := ErrorPage{errorHeader(w, user, "Security Error"), "There was a security issue with your request."}
|
||||||
if RunPreRenderHook("pre_render_security_error", w, r, &user, &pi) {
|
if RunPreRenderHook("pre_render_security_error", w, r, &user, &pi) {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -262,10 +275,10 @@ func NotFound(w http.ResponseWriter, r *http.Request, header *Header) RouteError
|
||||||
// CustomError lets us make custom error types which aren't covered by the generic functions above
|
// CustomError lets us make custom error types which aren't covered by the generic functions above
|
||||||
func CustomError(errmsg string, errcode int, errtitle string, w http.ResponseWriter, r *http.Request, header *Header, user User) RouteError {
|
func CustomError(errmsg string, errcode int, errtitle string, w http.ResponseWriter, r *http.Request, header *Header, user User) RouteError {
|
||||||
if header == nil {
|
if header == nil {
|
||||||
header = DefaultHeader(w)
|
header = DefaultHeader(w, user)
|
||||||
}
|
}
|
||||||
w.WriteHeader(errcode)
|
w.WriteHeader(errcode)
|
||||||
pi := Page{errtitle, user, header, tList, errmsg}
|
pi := ErrorPage{header, errmsg}
|
||||||
handleErrorTemplate(w, r, pi)
|
handleErrorTemplate(w, r, pi)
|
||||||
return HandledRouteError()
|
return HandledRouteError()
|
||||||
}
|
}
|
||||||
|
@ -274,7 +287,7 @@ func CustomError(errmsg string, errcode int, errtitle string, w http.ResponseWri
|
||||||
func CustomErrorJSQ(errmsg string, errcode int, errtitle string, w http.ResponseWriter, r *http.Request, header *Header, user User, isJs bool) RouteError {
|
func CustomErrorJSQ(errmsg string, errcode int, errtitle string, w http.ResponseWriter, r *http.Request, header *Header, user User, isJs bool) RouteError {
|
||||||
if !isJs {
|
if !isJs {
|
||||||
if header == nil {
|
if header == nil {
|
||||||
header = DefaultHeader(w)
|
header = DefaultHeader(w, user)
|
||||||
}
|
}
|
||||||
return CustomError(errmsg, errcode, errtitle, w, r, header, user)
|
return CustomError(errmsg, errcode, errtitle, w, r, header, user)
|
||||||
}
|
}
|
||||||
|
@ -288,7 +301,7 @@ func CustomErrorJS(errmsg string, errcode int, w http.ResponseWriter, r *http.Re
|
||||||
return HandledRouteError()
|
return HandledRouteError()
|
||||||
}
|
}
|
||||||
|
|
||||||
func handleErrorTemplate(w http.ResponseWriter, r *http.Request, pi Page) {
|
func handleErrorTemplate(w http.ResponseWriter, r *http.Request, pi ErrorPage) {
|
||||||
// TODO: What to do about this hook?
|
// TODO: What to do about this hook?
|
||||||
if RunPreRenderHook("pre_render_error", w, r, &pi.Header.CurrentUser, &pi) {
|
if RunPreRenderHook("pre_render_error", w, r, &pi.Header.CurrentUser, &pi) {
|
||||||
return
|
return
|
||||||
|
|
|
@ -89,10 +89,11 @@ var PreRenderHooks = map[string][]func(http.ResponseWriter, *http.Request, *User
|
||||||
"pre_render_view_topic": nil,
|
"pre_render_view_topic": nil,
|
||||||
"pre_render_profile": nil,
|
"pre_render_profile": nil,
|
||||||
"pre_render_custom_page": nil,
|
"pre_render_custom_page": nil,
|
||||||
|
"pre_render_tmpl_page": nil,
|
||||||
"pre_render_overview": nil,
|
"pre_render_overview": nil,
|
||||||
"pre_render_create_topic": nil,
|
"pre_render_create_topic": nil,
|
||||||
|
|
||||||
"pre_render_account_own_edit_critical": nil,
|
"pre_render_account_own_edit_password": nil,
|
||||||
"pre_render_account_own_edit_avatar": nil,
|
"pre_render_account_own_edit_avatar": nil,
|
||||||
"pre_render_account_own_edit_username": nil,
|
"pre_render_account_own_edit_username": nil,
|
||||||
"pre_render_account_own_edit_email": nil,
|
"pre_render_account_own_edit_email": nil,
|
||||||
|
|
|
@ -278,7 +278,7 @@ func (mfs *MemoryForumStore) CacheDelete(id int) {
|
||||||
|
|
||||||
// TODO: Add a hook to allow plugin_guilds to detect when one of it's forums has just been deleted?
|
// TODO: Add a hook to allow plugin_guilds to detect when one of it's forums has just been deleted?
|
||||||
func (mfs *MemoryForumStore) Delete(id int) error {
|
func (mfs *MemoryForumStore) Delete(id int) error {
|
||||||
if id == 1 {
|
if id == ReportForumID {
|
||||||
return errors.New("You cannot delete the Reports forum")
|
return errors.New("You cannot delete the Reports forum")
|
||||||
}
|
}
|
||||||
_, err := mfs.delete.Exec(id)
|
_, err := mfs.delete.Exec(id)
|
||||||
|
|
|
@ -0,0 +1,65 @@
|
||||||
|
// Google Authenticator 2FA
|
||||||
|
// Borrowed from https://github.com/tilaklodha/google-authenticator, as we can't import it as a library as it's in package main
|
||||||
|
package gauth
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"crypto/hmac"
|
||||||
|
"crypto/sha1"
|
||||||
|
"encoding/base32"
|
||||||
|
"encoding/binary"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Append extra 0s if the length of otp is less than 6
|
||||||
|
// If otp is "1234", it will return it as "001234"
|
||||||
|
func prefix0(otp string) string {
|
||||||
|
if len(otp) == 6 {
|
||||||
|
return otp
|
||||||
|
}
|
||||||
|
for i := (6 - len(otp)); i > 0; i-- {
|
||||||
|
otp = "0" + otp
|
||||||
|
}
|
||||||
|
return otp
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetHOTPToken(secret string, interval int64) (string, error) {
|
||||||
|
// Converts secret to base32 Encoding. Base32 encoding desires a 32-character subset of the twenty-six letters A–Z and ten digits 0–9
|
||||||
|
key, err := base32.StdEncoding.DecodeString(strings.ToUpper(secret))
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
bs := make([]byte, 8)
|
||||||
|
binary.BigEndian.PutUint64(bs, uint64(interval))
|
||||||
|
|
||||||
|
// Signing the value using HMAC-SHA1 Algorithm
|
||||||
|
hash := hmac.New(sha1.New, key)
|
||||||
|
hash.Write(bs)
|
||||||
|
h := hash.Sum(nil)
|
||||||
|
|
||||||
|
// We're going to use a subset of the generated hash.
|
||||||
|
// Using the last nibble (half-byte) to choose the index to start from.
|
||||||
|
// This number is always appropriate as it's maximum decimal 15, the hash will have the maximum index 19 (20 bytes of SHA1) and we need 4 bytes.
|
||||||
|
o := (h[19] & 15)
|
||||||
|
|
||||||
|
var header uint32
|
||||||
|
// Get 32 bit chunk from hash starting at the o
|
||||||
|
r := bytes.NewReader(h[o : o+4])
|
||||||
|
err = binary.Read(r, binary.BigEndian, &header)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ignore most significant bits as per RFC 4226.
|
||||||
|
// Takes division from one million to generate a remainder less than < 7 digits
|
||||||
|
h12 := (int(header) & 0x7fffffff) % 1000000
|
||||||
|
return prefix0(strconv.Itoa(int(h12))), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetTOTPToken(secret string) (string, error) {
|
||||||
|
// The TOTP token is just a HOTP token seeded with every 30 seconds.
|
||||||
|
interval := time.Now().Unix() / 30
|
||||||
|
return GetHOTPToken(secret, interval)
|
||||||
|
}
|
|
@ -0,0 +1,175 @@
|
||||||
|
package common
|
||||||
|
|
||||||
|
import (
|
||||||
|
"database/sql"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"../query_gen/lib"
|
||||||
|
)
|
||||||
|
|
||||||
|
type CustomPageStmts struct {
|
||||||
|
update *sql.Stmt
|
||||||
|
create *sql.Stmt
|
||||||
|
}
|
||||||
|
|
||||||
|
var customPageStmts CustomPageStmts
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
DbInits.Add(func(acc *qgen.Accumulator) error {
|
||||||
|
customPageStmts = CustomPageStmts{
|
||||||
|
update: acc.Update("pages").Set("name = ?, title = ?, body = ?, allowedGroups = ?, menuID = ?").Where("pid = ?").Prepare(),
|
||||||
|
create: acc.Insert("pages").Columns("name, title, body, allowedGroups, menuID").Fields("?,?,?,?,?").Prepare(),
|
||||||
|
}
|
||||||
|
return acc.FirstError()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
type CustomPage struct {
|
||||||
|
ID int
|
||||||
|
Name string // TODO: Let admins put pages in "virtual subdirectories"
|
||||||
|
Title string
|
||||||
|
Body string
|
||||||
|
AllowedGroups []int
|
||||||
|
MenuID int
|
||||||
|
}
|
||||||
|
|
||||||
|
func BlankCustomPage() *CustomPage {
|
||||||
|
return new(CustomPage)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (page *CustomPage) AddAllowedGroup(gid int) {
|
||||||
|
page.AllowedGroups = append(page.AllowedGroups, gid)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (page *CustomPage) getRawAllowedGroups() (rawAllowedGroups string) {
|
||||||
|
for _, group := range page.AllowedGroups {
|
||||||
|
rawAllowedGroups += strconv.Itoa(group) + ","
|
||||||
|
}
|
||||||
|
if len(rawAllowedGroups) > 0 {
|
||||||
|
rawAllowedGroups = rawAllowedGroups[:len(rawAllowedGroups)-1]
|
||||||
|
}
|
||||||
|
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)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (page *CustomPage) Create() (int, error) {
|
||||||
|
res, err := customPageStmts.create.Exec(page.Name, page.Title, page.Body, page.getRawAllowedGroups(), page.MenuID)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
pid64, err := res.LastInsertId()
|
||||||
|
return int(pid64), err
|
||||||
|
}
|
||||||
|
|
||||||
|
var Pages PageStore
|
||||||
|
|
||||||
|
// Holds the custom pages, but doesn't include the template pages in /pages/ which are a lot more flexible yet harder to use and which are too risky security-wise to make editable in the Control Panel
|
||||||
|
type PageStore interface {
|
||||||
|
GlobalCount() (pageCount int)
|
||||||
|
Get(id int) (*CustomPage, error)
|
||||||
|
GetByName(name string) (*CustomPage, error)
|
||||||
|
GetOffset(offset int, perPage int) (pages []*CustomPage, err error)
|
||||||
|
Reload(id int) error
|
||||||
|
Delete(id int) error
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Add a cache to this to save on the queries
|
||||||
|
type DefaultPageStore struct {
|
||||||
|
get *sql.Stmt
|
||||||
|
getByName *sql.Stmt
|
||||||
|
getOffset *sql.Stmt
|
||||||
|
count *sql.Stmt
|
||||||
|
delete *sql.Stmt
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewDefaultPageStore(acc *qgen.Accumulator) (*DefaultPageStore, error) {
|
||||||
|
return &DefaultPageStore{
|
||||||
|
get: acc.Select("pages").Columns("name, title, body, allowedGroups, menuID").Where("pid = ?").Prepare(),
|
||||||
|
getByName: acc.Select("pages").Columns("pid, name, title, body, allowedGroups, menuID").Where("name = ?").Prepare(),
|
||||||
|
getOffset: acc.Select("pages").Columns("pid, name, title, body, allowedGroups, menuID").Orderby("pid DESC").Limit("?,?").Prepare(),
|
||||||
|
count: acc.Count("pages").Prepare(),
|
||||||
|
delete: acc.Delete("pages").Where("pid = ?").Prepare(),
|
||||||
|
}, acc.FirstError()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (store *DefaultPageStore) GlobalCount() (pageCount int) {
|
||||||
|
err := store.count.QueryRow().Scan(&pageCount)
|
||||||
|
if err != nil {
|
||||||
|
LogError(err)
|
||||||
|
}
|
||||||
|
return pageCount
|
||||||
|
}
|
||||||
|
|
||||||
|
func (store *DefaultPageStore) parseAllowedGroups(raw string, page *CustomPage) error {
|
||||||
|
if raw == "" {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
for _, sgroup := range strings.Split(raw, ",") {
|
||||||
|
group, err := strconv.Atoi(sgroup)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
page.AddAllowedGroup(group)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (store *DefaultPageStore) Get(id int) (*CustomPage, error) {
|
||||||
|
page := &CustomPage{ID: id}
|
||||||
|
rawAllowedGroups := ""
|
||||||
|
err := store.get.QueryRow(id).Scan(&page.Name, &page.Title, &page.Body, &rawAllowedGroups, &page.MenuID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return page, store.parseAllowedGroups(rawAllowedGroups, page)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (store *DefaultPageStore) GetByName(name string) (*CustomPage, error) {
|
||||||
|
page := BlankCustomPage()
|
||||||
|
rawAllowedGroups := ""
|
||||||
|
err := store.getByName.QueryRow(name).Scan(&page.ID, &page.Name, &page.Title, &page.Body, &rawAllowedGroups, &page.MenuID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return page, store.parseAllowedGroups(rawAllowedGroups, page)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (store *DefaultPageStore) GetOffset(offset int, perPage int) (pages []*CustomPage, err error) {
|
||||||
|
rows, err := store.getOffset.Query(offset, perPage)
|
||||||
|
if err != nil {
|
||||||
|
return pages, err
|
||||||
|
}
|
||||||
|
defer rows.Close()
|
||||||
|
|
||||||
|
for rows.Next() {
|
||||||
|
page := &CustomPage{ID: 0}
|
||||||
|
rawAllowedGroups := ""
|
||||||
|
err := rows.Scan(&page.ID, &page.Name, &page.Title, &page.Body, &rawAllowedGroups, &page.MenuID)
|
||||||
|
if err != nil {
|
||||||
|
return pages, err
|
||||||
|
}
|
||||||
|
err = store.parseAllowedGroups(rawAllowedGroups, page)
|
||||||
|
if err != nil {
|
||||||
|
return pages, err
|
||||||
|
}
|
||||||
|
pages = append(pages, page)
|
||||||
|
}
|
||||||
|
return pages, rows.Err()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Always returns nil as there's currently no cache
|
||||||
|
func (store *DefaultPageStore) Reload(id int) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (store *DefaultPageStore) Delete(id int) error {
|
||||||
|
_, err := store.delete.Exec(id)
|
||||||
|
return err
|
||||||
|
}
|
164
common/pages.go
164
common/pages.go
|
@ -21,8 +21,7 @@ type Header struct {
|
||||||
Themes map[string]*Theme // TODO: Use a slice containing every theme instead of the main map for speed?
|
Themes map[string]*Theme // TODO: Use a slice containing every theme instead of the main map for speed?
|
||||||
Theme *Theme
|
Theme *Theme
|
||||||
//TemplateName string // TODO: Use this to move template calls to the router rather than duplicating them over and over and over?
|
//TemplateName string // TODO: Use this to move template calls to the router rather than duplicating them over and over and over?
|
||||||
// TODO: Use a pointer here
|
CurrentUser User // TODO: Deprecate CurrentUser on the page structs and use a pointer here
|
||||||
CurrentUser User // TODO: Deprecate CurrentUser on the page structs
|
|
||||||
Zone string
|
Zone string
|
||||||
MetaDesc string
|
MetaDesc string
|
||||||
Writer http.ResponseWriter
|
Writer http.ResponseWriter
|
||||||
|
@ -41,6 +40,10 @@ func (header *Header) AddSheet(name string) {
|
||||||
header.Stylesheets = append(header.Stylesheets, name)
|
header.Stylesheets = append(header.Stylesheets, name)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (header *Header) AddNotice(name string) {
|
||||||
|
header.NoticeList = append(header.NoticeList, GetNoticePhrase(name))
|
||||||
|
}
|
||||||
|
|
||||||
// TODO: Add this to routes which don't use templates. E.g. Json APIs.
|
// TODO: Add this to routes which don't use templates. E.g. Json APIs.
|
||||||
type HeaderLite struct {
|
type HeaderLite struct {
|
||||||
Site *site
|
Site *site
|
||||||
|
@ -61,19 +64,31 @@ type ExtData struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
type Page struct {
|
type Page struct {
|
||||||
Title string
|
*Header
|
||||||
CurrentUser User
|
|
||||||
Header *Header
|
|
||||||
ItemList []interface{}
|
ItemList []interface{}
|
||||||
Something interface{}
|
Something interface{}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type SimplePage struct {
|
||||||
|
*Header
|
||||||
|
}
|
||||||
|
|
||||||
|
type ErrorPage struct {
|
||||||
|
*Header
|
||||||
|
Message string
|
||||||
|
}
|
||||||
|
|
||||||
type Paginator struct {
|
type Paginator struct {
|
||||||
PageList []int
|
PageList []int
|
||||||
Page int
|
Page int
|
||||||
LastPage int
|
LastPage int
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type CustomPagePage struct {
|
||||||
|
*Header
|
||||||
|
Page *CustomPage
|
||||||
|
}
|
||||||
|
|
||||||
type TopicPage struct {
|
type TopicPage struct {
|
||||||
*Header
|
*Header
|
||||||
ItemList []ReplyUser
|
ItemList []ReplyUser
|
||||||
|
@ -131,18 +146,22 @@ type PanelStats struct {
|
||||||
Users int
|
Users int
|
||||||
Groups int
|
Groups int
|
||||||
Forums int
|
Forums int
|
||||||
|
Pages int
|
||||||
Settings int
|
Settings int
|
||||||
WordFilters int
|
WordFilters int
|
||||||
Themes int
|
Themes int
|
||||||
Reports int
|
Reports int
|
||||||
}
|
}
|
||||||
|
|
||||||
type PanelPage struct {
|
type BasePanelPage struct {
|
||||||
Title string
|
*Header
|
||||||
CurrentUser User
|
|
||||||
Header *Header
|
|
||||||
Stats PanelStats
|
Stats PanelStats
|
||||||
Zone string
|
Zone string
|
||||||
|
ReportForumID int
|
||||||
|
}
|
||||||
|
|
||||||
|
type PanelPage struct {
|
||||||
|
*BasePanelPage
|
||||||
ItemList []interface{}
|
ItemList []interface{}
|
||||||
Something interface{}
|
Something interface{}
|
||||||
}
|
}
|
||||||
|
@ -158,9 +177,7 @@ type GridElement struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
type PanelDashboardPage struct {
|
type PanelDashboardPage struct {
|
||||||
*Header
|
*BasePanelPage
|
||||||
Stats PanelStats
|
|
||||||
Zone string
|
|
||||||
GridItems []GridElement
|
GridItems []GridElement
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -170,13 +187,22 @@ type PanelSetting struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
type PanelSettingPage struct {
|
type PanelSettingPage struct {
|
||||||
*Header
|
*BasePanelPage
|
||||||
Stats PanelStats
|
|
||||||
Zone string
|
|
||||||
ItemList []OptionLabel
|
ItemList []OptionLabel
|
||||||
Setting *PanelSetting
|
Setting *PanelSetting
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type PanelCustomPagesPage struct {
|
||||||
|
*BasePanelPage
|
||||||
|
ItemList []*CustomPage
|
||||||
|
Paginator
|
||||||
|
}
|
||||||
|
|
||||||
|
type PanelCustomPageEditPage struct {
|
||||||
|
*BasePanelPage
|
||||||
|
Page *CustomPage
|
||||||
|
}
|
||||||
|
|
||||||
type PanelTimeGraph struct {
|
type PanelTimeGraph struct {
|
||||||
Series []int64 // The counts on the left
|
Series []int64 // The counts on the left
|
||||||
Labels []int64 // unixtimes for the bottom, gets converted into 1:00, 2:00, etc. with JS
|
Labels []int64 // unixtimes for the bottom, gets converted into 1:00, 2:00, etc. with JS
|
||||||
|
@ -188,11 +214,7 @@ type PanelAnalyticsItem struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
type PanelAnalyticsPage struct {
|
type PanelAnalyticsPage struct {
|
||||||
Title string
|
*BasePanelPage
|
||||||
CurrentUser User
|
|
||||||
Header *Header
|
|
||||||
Stats PanelStats
|
|
||||||
Zone string
|
|
||||||
PrimaryGraph PanelTimeGraph
|
PrimaryGraph PanelTimeGraph
|
||||||
ViewItems []PanelAnalyticsItem
|
ViewItems []PanelAnalyticsItem
|
||||||
TimeRange string
|
TimeRange string
|
||||||
|
@ -204,11 +226,7 @@ type PanelAnalyticsRoutesItem struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
type PanelAnalyticsRoutesPage struct {
|
type PanelAnalyticsRoutesPage struct {
|
||||||
Title string
|
*BasePanelPage
|
||||||
CurrentUser User
|
|
||||||
Header *Header
|
|
||||||
Stats PanelStats
|
|
||||||
Zone string
|
|
||||||
ItemList []PanelAnalyticsRoutesItem
|
ItemList []PanelAnalyticsRoutesItem
|
||||||
TimeRange string
|
TimeRange string
|
||||||
}
|
}
|
||||||
|
@ -220,21 +238,13 @@ type PanelAnalyticsAgentsItem struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
type PanelAnalyticsAgentsPage struct {
|
type PanelAnalyticsAgentsPage struct {
|
||||||
Title string
|
*BasePanelPage
|
||||||
CurrentUser User
|
|
||||||
Header *Header
|
|
||||||
Stats PanelStats
|
|
||||||
Zone string
|
|
||||||
ItemList []PanelAnalyticsAgentsItem
|
ItemList []PanelAnalyticsAgentsItem
|
||||||
TimeRange string
|
TimeRange string
|
||||||
}
|
}
|
||||||
|
|
||||||
type PanelAnalyticsRoutePage struct {
|
type PanelAnalyticsRoutePage struct {
|
||||||
Title string
|
*BasePanelPage
|
||||||
CurrentUser User
|
|
||||||
Header *Header
|
|
||||||
Stats PanelStats
|
|
||||||
Zone string
|
|
||||||
Route string
|
Route string
|
||||||
PrimaryGraph PanelTimeGraph
|
PrimaryGraph PanelTimeGraph
|
||||||
ViewItems []PanelAnalyticsItem
|
ViewItems []PanelAnalyticsItem
|
||||||
|
@ -242,11 +252,7 @@ type PanelAnalyticsRoutePage struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
type PanelAnalyticsAgentPage struct {
|
type PanelAnalyticsAgentPage struct {
|
||||||
Title string
|
*BasePanelPage
|
||||||
CurrentUser User
|
|
||||||
Header *Header
|
|
||||||
Stats PanelStats
|
|
||||||
Zone string
|
|
||||||
Agent string
|
Agent string
|
||||||
FriendlyAgent string
|
FriendlyAgent string
|
||||||
PrimaryGraph PanelTimeGraph
|
PrimaryGraph PanelTimeGraph
|
||||||
|
@ -254,9 +260,7 @@ type PanelAnalyticsAgentPage struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
type PanelThemesPage struct {
|
type PanelThemesPage struct {
|
||||||
*Header
|
*BasePanelPage
|
||||||
Stats PanelStats
|
|
||||||
Zone string
|
|
||||||
PrimaryThemes []*Theme
|
PrimaryThemes []*Theme
|
||||||
VariantThemes []*Theme
|
VariantThemes []*Theme
|
||||||
}
|
}
|
||||||
|
@ -268,51 +272,35 @@ type PanelMenuListItem struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
type PanelMenuListPage struct {
|
type PanelMenuListPage struct {
|
||||||
*Header
|
*BasePanelPage
|
||||||
Stats PanelStats
|
|
||||||
Zone string
|
|
||||||
ItemList []PanelMenuListItem
|
ItemList []PanelMenuListItem
|
||||||
}
|
}
|
||||||
|
|
||||||
type PanelMenuPage struct {
|
type PanelMenuPage struct {
|
||||||
*Header
|
*BasePanelPage
|
||||||
Stats PanelStats
|
|
||||||
Zone string
|
|
||||||
MenuID int
|
MenuID int
|
||||||
ItemList []MenuItem
|
ItemList []MenuItem
|
||||||
}
|
}
|
||||||
|
|
||||||
type PanelMenuItemPage struct {
|
type PanelMenuItemPage struct {
|
||||||
*Header
|
*BasePanelPage
|
||||||
Stats PanelStats
|
|
||||||
Zone string
|
|
||||||
Item MenuItem
|
Item MenuItem
|
||||||
}
|
}
|
||||||
|
|
||||||
type PanelUserPage struct {
|
type PanelUserPage struct {
|
||||||
*Header
|
*BasePanelPage
|
||||||
Stats PanelStats
|
|
||||||
Zone string
|
|
||||||
ItemList []*User
|
ItemList []*User
|
||||||
Paginator
|
Paginator
|
||||||
}
|
}
|
||||||
|
|
||||||
type PanelGroupPage struct {
|
type PanelGroupPage struct {
|
||||||
Title string
|
*BasePanelPage
|
||||||
CurrentUser User
|
|
||||||
Header *Header
|
|
||||||
Stats PanelStats
|
|
||||||
Zone string
|
|
||||||
ItemList []GroupAdmin
|
ItemList []GroupAdmin
|
||||||
Paginator
|
Paginator
|
||||||
}
|
}
|
||||||
|
|
||||||
type PanelEditGroupPage struct {
|
type PanelEditGroupPage struct {
|
||||||
Title string
|
*BasePanelPage
|
||||||
CurrentUser User
|
|
||||||
Header *Header
|
|
||||||
Stats PanelStats
|
|
||||||
Zone string
|
|
||||||
ID int
|
ID int
|
||||||
Name string
|
Name string
|
||||||
Tag string
|
Tag string
|
||||||
|
@ -326,11 +314,7 @@ type GroupForumPermPreset struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
type PanelEditForumPage struct {
|
type PanelEditForumPage struct {
|
||||||
Title string
|
*BasePanelPage
|
||||||
CurrentUser User
|
|
||||||
Header *Header
|
|
||||||
Stats PanelStats
|
|
||||||
Zone string
|
|
||||||
ID int
|
ID int
|
||||||
Name string
|
Name string
|
||||||
Desc string
|
Desc string
|
||||||
|
@ -346,11 +330,7 @@ type NameLangToggle struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
type PanelEditForumGroupPage struct {
|
type PanelEditForumGroupPage struct {
|
||||||
Title string
|
*BasePanelPage
|
||||||
CurrentUser User
|
|
||||||
Header *Header
|
|
||||||
Stats PanelStats
|
|
||||||
Zone string
|
|
||||||
ForumID int
|
ForumID int
|
||||||
GroupID int
|
GroupID int
|
||||||
Name string
|
Name string
|
||||||
|
@ -361,11 +341,7 @@ type PanelEditForumGroupPage struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
type PanelEditGroupPermsPage struct {
|
type PanelEditGroupPermsPage struct {
|
||||||
Title string
|
*BasePanelPage
|
||||||
CurrentUser User
|
|
||||||
Header *Header
|
|
||||||
Stats PanelStats
|
|
||||||
Zone string
|
|
||||||
ID int
|
ID int
|
||||||
Name string
|
Name string
|
||||||
LocalPerms []NameLangToggle
|
LocalPerms []NameLangToggle
|
||||||
|
@ -381,11 +357,7 @@ type BackupItem struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
type PanelBackupPage struct {
|
type PanelBackupPage struct {
|
||||||
Title string
|
*BasePanelPage
|
||||||
CurrentUser User
|
|
||||||
Header *Header
|
|
||||||
Stats PanelStats
|
|
||||||
Zone string
|
|
||||||
Backups []BackupItem
|
Backups []BackupItem
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -396,11 +368,7 @@ type PageLogItem struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
type PanelLogsPage struct {
|
type PanelLogsPage struct {
|
||||||
Title string
|
*BasePanelPage
|
||||||
CurrentUser User
|
|
||||||
Header *Header
|
|
||||||
Stats PanelStats
|
|
||||||
Zone string
|
|
||||||
Logs []PageLogItem
|
Logs []PageLogItem
|
||||||
Paginator
|
Paginator
|
||||||
}
|
}
|
||||||
|
@ -411,21 +379,13 @@ type PageRegLogItem struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
type PanelRegLogsPage struct {
|
type PanelRegLogsPage struct {
|
||||||
Title string
|
*BasePanelPage
|
||||||
CurrentUser User
|
|
||||||
Header *Header
|
|
||||||
Stats PanelStats
|
|
||||||
Zone string
|
|
||||||
Logs []PageRegLogItem
|
Logs []PageRegLogItem
|
||||||
Paginator
|
Paginator
|
||||||
}
|
}
|
||||||
|
|
||||||
type PanelDebugPage struct {
|
type PanelDebugPage struct {
|
||||||
Title string
|
*BasePanelPage
|
||||||
CurrentUser User
|
|
||||||
Header *Header
|
|
||||||
Stats PanelStats
|
|
||||||
Zone string
|
|
||||||
GoVersion string
|
GoVersion string
|
||||||
DBVersion string
|
DBVersion string
|
||||||
Uptime string
|
Uptime string
|
||||||
|
@ -444,6 +404,6 @@ type AreYouSure struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Write a test for this
|
// TODO: Write a test for this
|
||||||
func DefaultHeader(w http.ResponseWriter) *Header {
|
func DefaultHeader(w http.ResponseWriter, user User) *Header {
|
||||||
return &Header{Site: Site, Theme: Themes[fallbackTheme], CurrentUser: GuestUser, Writer: w}
|
return &Header{Site: Site, Theme: Themes[fallbackTheme], CurrentUser: user, Writer: w}
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,6 +8,10 @@ import (
|
||||||
"../query_gen/lib"
|
"../query_gen/lib"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// TODO: Make the default report forum ID configurable
|
||||||
|
// TODO: Make sure this constant is used everywhere for the report forum ID
|
||||||
|
const ReportForumID = 1
|
||||||
|
|
||||||
var Reports ReportStore
|
var Reports ReportStore
|
||||||
var ErrAlreadyReported = errors.New("This item has already been reported")
|
var ErrAlreadyReported = errors.New("This item has already been reported")
|
||||||
|
|
||||||
|
@ -23,8 +27,8 @@ type DefaultReportStore struct {
|
||||||
|
|
||||||
func NewDefaultReportStore(acc *qgen.Accumulator) (*DefaultReportStore, error) {
|
func NewDefaultReportStore(acc *qgen.Accumulator) (*DefaultReportStore, error) {
|
||||||
return &DefaultReportStore{
|
return &DefaultReportStore{
|
||||||
create: acc.Insert("topics").Columns("title, content, parsed_content, ipaddress, createdAt, lastReplyAt, createdBy, lastReplyBy, data, parentID, css_class").Fields("?,?,?,?,UTC_TIMESTAMP(),UTC_TIMESTAMP(),?,?,?,1,'report'").Prepare(),
|
create: acc.Insert("topics").Columns("title, content, parsed_content, ipaddress, createdAt, lastReplyAt, createdBy, lastReplyBy, data, parentID, css_class").Fields("?,?,?,?,UTC_TIMESTAMP(),UTC_TIMESTAMP(),?,?,?,?,'report'").Prepare(),
|
||||||
exists: acc.Count("topics").Where("data = ? AND data != '' AND parentID = 1").Prepare(),
|
exists: acc.Count("topics").Where("data = ? AND data != '' AND parentID = ?").Prepare(),
|
||||||
}, acc.FirstError()
|
}, acc.FirstError()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -39,7 +43,7 @@ func (store *DefaultReportStore) Create(title string, content string, user *User
|
||||||
return 0, ErrAlreadyReported
|
return 0, ErrAlreadyReported
|
||||||
}
|
}
|
||||||
|
|
||||||
res, err := store.create.Exec(title, content, ParseMessage(content, 0, ""), user.LastIP, user.ID, user.ID, itemType+"_"+strconv.Itoa(itemID))
|
res, err := store.create.Exec(title, content, ParseMessage(content, 0, ""), user.LastIP, user.ID, user.ID, itemType+"_"+strconv.Itoa(itemID), ReportForumID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, err
|
return 0, err
|
||||||
}
|
}
|
||||||
|
@ -49,5 +53,5 @@ func (store *DefaultReportStore) Create(title string, content string, user *User
|
||||||
return 0, err
|
return 0, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return int(lastID), Forums.AddTopic(int(lastID), user.ID, 1)
|
return int(lastID), Forums.AddTopic(int(lastID), user.ID, ReportForumID)
|
||||||
}
|
}
|
||||||
|
|
|
@ -138,14 +138,17 @@ func panelUserCheck(w http.ResponseWriter, r *http.Request, user *User) (header
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: GDPR. Add a global control panel notice warning the admins of staff members who don't have 2FA enabled
|
||||||
stats.Users = Users.GlobalCount()
|
stats.Users = Users.GlobalCount()
|
||||||
stats.Groups = Groups.GlobalCount()
|
stats.Groups = Groups.GlobalCount()
|
||||||
stats.Forums = Forums.GlobalCount() // TODO: Stop it from showing the blanked forums
|
stats.Forums = Forums.GlobalCount() // TODO: Stop it from showing the blanked forums, do we still have those? I think we removed that
|
||||||
|
stats.Pages = Pages.GlobalCount()
|
||||||
stats.Settings = len(header.Settings)
|
stats.Settings = len(header.Settings)
|
||||||
stats.WordFilters = len(WordFilterBox.Load().(WordFilterMap))
|
stats.WordFilters = len(WordFilterBox.Load().(WordFilterMap))
|
||||||
stats.Themes = len(Themes)
|
stats.Themes = len(Themes)
|
||||||
stats.Reports = 0 // TODO: Do the report count. Only show open threads?
|
stats.Reports = 0 // TODO: Do the report count. Only show open threads?
|
||||||
|
|
||||||
|
// TODO: Remove this as it might be counter-productive
|
||||||
pusher, ok := w.(http.Pusher)
|
pusher, ok := w.(http.Pusher)
|
||||||
if ok {
|
if ok {
|
||||||
pusher.Push("/static/"+theme.Name+"/main.css", nil)
|
pusher.Push("/static/"+theme.Name+"/main.css", nil)
|
||||||
|
|
|
@ -105,7 +105,7 @@ var Template_register_handle = func(pi Page, w io.Writer) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// nolint
|
// nolint
|
||||||
var Template_error_handle = func(pi Page, w io.Writer) error {
|
var Template_error_handle = func(pi ErrorPage, w io.Writer) error {
|
||||||
mapping, ok := Themes[DefaultThemeBox.Load().(string)].TemplatesMap["error"]
|
mapping, ok := Themes[DefaultThemeBox.Load().(string)].TemplatesMap["error"]
|
||||||
if !ok {
|
if !ok {
|
||||||
mapping = "error"
|
mapping = "error"
|
||||||
|
@ -229,20 +229,23 @@ func CompileTemplates() error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
loginPage := Page{"Login Page", user, header, tList, nil}
|
header.Title = "Login Page"
|
||||||
|
loginPage := Page{header, tList, nil}
|
||||||
loginTmpl, err := c.Compile("login.html", "templates/", "common.Page", loginPage, varList)
|
loginTmpl, err := c.Compile("login.html", "templates/", "common.Page", loginPage, varList)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
registerPage := Page{"Registration Page", user, header, tList, "nananana"}
|
header.Title = "Registration Page"
|
||||||
|
registerPage := Page{header, tList, "nananana"}
|
||||||
registerTmpl, err := c.Compile("register.html", "templates/", "common.Page", registerPage, varList)
|
registerTmpl, err := c.Compile("register.html", "templates/", "common.Page", registerPage, varList)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
errorPage := Page{"Error", user, header, tList, "A problem has occurred in the system."}
|
header.Title = "Error"
|
||||||
errorTmpl, err := c.Compile("error.html", "templates/", "common.Page", errorPage, varList)
|
errorPage := ErrorPage{header, "A problem has occurred in the system."}
|
||||||
|
errorTmpl, err := c.Compile("error.html", "templates/", "common.ErrorPage", errorPage, varList)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
@ -140,6 +140,15 @@ func (theme *Theme) MapTemplates() {
|
||||||
}
|
}
|
||||||
|
|
||||||
switch dTmplPtr := destTmplPtr.(type) {
|
switch dTmplPtr := destTmplPtr.(type) {
|
||||||
|
case *func(CustomPagePage, io.Writer) error:
|
||||||
|
switch sTmplPtr := sourceTmplPtr.(type) {
|
||||||
|
case *func(CustomPagePage, io.Writer) error:
|
||||||
|
//overridenTemplates[themeTmpl.Name] = d_tmpl_ptr
|
||||||
|
overridenTemplates[themeTmpl.Name] = true
|
||||||
|
*dTmplPtr = *sTmplPtr
|
||||||
|
default:
|
||||||
|
LogError(errors.New("The source and destination templates are incompatible"))
|
||||||
|
}
|
||||||
case *func(TopicPage, io.Writer) error:
|
case *func(TopicPage, io.Writer) error:
|
||||||
switch sTmplPtr := sourceTmplPtr.(type) {
|
switch sTmplPtr := sourceTmplPtr.(type) {
|
||||||
case *func(TopicPage, io.Writer) error:
|
case *func(TopicPage, io.Writer) error:
|
||||||
|
@ -203,6 +212,15 @@ func (theme *Theme) MapTemplates() {
|
||||||
default:
|
default:
|
||||||
LogError(errors.New("The source and destination templates are incompatible"))
|
LogError(errors.New("The source and destination templates are incompatible"))
|
||||||
}
|
}
|
||||||
|
case *func(ErrorPage, io.Writer) error:
|
||||||
|
switch sTmplPtr := sourceTmplPtr.(type) {
|
||||||
|
case *func(ErrorPage, io.Writer) error:
|
||||||
|
//overridenTemplates[themeTmpl.Name] = d_tmpl_ptr
|
||||||
|
overridenTemplates[themeTmpl.Name] = true
|
||||||
|
*dTmplPtr = *sTmplPtr
|
||||||
|
default:
|
||||||
|
LogError(errors.New("The source and destination templates are incompatible"))
|
||||||
|
}
|
||||||
case *func(Page, io.Writer) error:
|
case *func(Page, io.Writer) error:
|
||||||
switch sTmplPtr := sourceTmplPtr.(type) {
|
switch sTmplPtr := sourceTmplPtr.(type) {
|
||||||
case *func(Page, io.Writer) error:
|
case *func(Page, io.Writer) error:
|
||||||
|
|
|
@ -194,6 +194,13 @@ func ResetTemplateOverrides() {
|
||||||
|
|
||||||
// Not really a pointer, more of a function handle, an artifact from one of the earlier versions of themes.go
|
// Not really a pointer, more of a function handle, an artifact from one of the earlier versions of themes.go
|
||||||
switch oPtr := originPointer.(type) {
|
switch oPtr := originPointer.(type) {
|
||||||
|
case func(CustomPagePage, io.Writer) error:
|
||||||
|
switch dPtr := destTmplPtr.(type) {
|
||||||
|
case *func(CustomPagePage, io.Writer) error:
|
||||||
|
*dPtr = oPtr
|
||||||
|
default:
|
||||||
|
LogError(errors.New("The source and destination templates are incompatible"))
|
||||||
|
}
|
||||||
case func(TopicPage, io.Writer) error:
|
case func(TopicPage, io.Writer) error:
|
||||||
switch dPtr := destTmplPtr.(type) {
|
switch dPtr := destTmplPtr.(type) {
|
||||||
case *func(TopicPage, io.Writer) error:
|
case *func(TopicPage, io.Writer) error:
|
||||||
|
@ -243,6 +250,13 @@ func ResetTemplateOverrides() {
|
||||||
default:
|
default:
|
||||||
LogError(errors.New("The source and destination templates are incompatible"))
|
LogError(errors.New("The source and destination templates are incompatible"))
|
||||||
}
|
}
|
||||||
|
case func(ErrorPage, io.Writer) error:
|
||||||
|
switch dPtr := destTmplPtr.(type) {
|
||||||
|
case *func(ErrorPage, io.Writer) error:
|
||||||
|
*dPtr = oPtr
|
||||||
|
default:
|
||||||
|
LogError(errors.New("The source and destination templates are incompatible"))
|
||||||
|
}
|
||||||
case func(Page, io.Writer) error:
|
case func(Page, io.Writer) error:
|
||||||
switch dPtr := destTmplPtr.(type) {
|
switch dPtr := destTmplPtr.(type) {
|
||||||
case *func(Page, io.Writer) error:
|
case *func(Page, io.Writer) error:
|
||||||
|
@ -266,6 +280,9 @@ func ResetTemplateOverrides() {
|
||||||
func RunThemeTemplate(theme string, template string, pi interface{}, w io.Writer) error {
|
func RunThemeTemplate(theme string, template string, pi interface{}, w io.Writer) error {
|
||||||
var getTmpl = GetThemeTemplate(theme, template)
|
var getTmpl = GetThemeTemplate(theme, template)
|
||||||
switch tmplO := getTmpl.(type) {
|
switch tmplO := getTmpl.(type) {
|
||||||
|
case *func(CustomPagePage, io.Writer) error:
|
||||||
|
var tmpl = *tmplO
|
||||||
|
return tmpl(pi.(CustomPagePage), w)
|
||||||
case *func(TopicPage, io.Writer) error:
|
case *func(TopicPage, io.Writer) error:
|
||||||
var tmpl = *tmplO
|
var tmpl = *tmplO
|
||||||
return tmpl(pi.(TopicPage), w)
|
return tmpl(pi.(TopicPage), w)
|
||||||
|
@ -287,9 +304,14 @@ func RunThemeTemplate(theme string, template string, pi interface{}, w io.Writer
|
||||||
case *func(IPSearchPage, io.Writer) error:
|
case *func(IPSearchPage, io.Writer) error:
|
||||||
var tmpl = *tmplO
|
var tmpl = *tmplO
|
||||||
return tmpl(pi.(IPSearchPage), w)
|
return tmpl(pi.(IPSearchPage), w)
|
||||||
|
case *func(ErrorPage, io.Writer) error:
|
||||||
|
var tmpl = *tmplO
|
||||||
|
return tmpl(pi.(ErrorPage), w)
|
||||||
case *func(Page, io.Writer) error:
|
case *func(Page, io.Writer) error:
|
||||||
var tmpl = *tmplO
|
var tmpl = *tmplO
|
||||||
return tmpl(pi.(Page), w)
|
return tmpl(pi.(Page), w)
|
||||||
|
case func(CustomPagePage, io.Writer) error:
|
||||||
|
return tmplO(pi.(CustomPagePage), w)
|
||||||
case func(TopicPage, io.Writer) error:
|
case func(TopicPage, io.Writer) error:
|
||||||
return tmplO(pi.(TopicPage), w)
|
return tmplO(pi.(TopicPage), w)
|
||||||
case func(TopicListPage, io.Writer) error:
|
case func(TopicListPage, io.Writer) error:
|
||||||
|
@ -304,6 +326,8 @@ func RunThemeTemplate(theme string, template string, pi interface{}, w io.Writer
|
||||||
return tmplO(pi.(CreateTopicPage), w)
|
return tmplO(pi.(CreateTopicPage), w)
|
||||||
case func(IPSearchPage, io.Writer) error:
|
case func(IPSearchPage, io.Writer) error:
|
||||||
return tmplO(pi.(IPSearchPage), w)
|
return tmplO(pi.(IPSearchPage), w)
|
||||||
|
case func(ErrorPage, io.Writer) error:
|
||||||
|
return tmplO(pi.(ErrorPage), w)
|
||||||
case func(Page, io.Writer) error:
|
case func(Page, io.Writer) error:
|
||||||
return tmplO(pi.(Page), w)
|
return tmplO(pi.(Page), w)
|
||||||
case string:
|
case string:
|
||||||
|
|
|
@ -0,0 +1,7 @@
|
||||||
|
# Custom Pages
|
||||||
|
|
||||||
|
There are two ways to create custom pages in Gosora, one which requires a lot more technical knowledge than the other. The first is to create the page via the Page Manager in the Control Panel where you'll be able to type whatever you want visible on the page into a little form.
|
||||||
|
|
||||||
|
The second is to create a template file in /pages/ which will be loaded by Gosora and parsed like any template will. You will require knowledge of HTML, and possibly even Go Templates for this option, but it's a lot more flexible than the first option.
|
||||||
|
|
||||||
|
More to come.
|
|
@ -232,13 +232,14 @@ func RouteCreateGuild(w http.ResponseWriter, r *http.Request, user common.User)
|
||||||
if ferr != nil {
|
if ferr != nil {
|
||||||
return ferr
|
return ferr
|
||||||
}
|
}
|
||||||
|
header.Title = "Create Guild"
|
||||||
// TODO: Add an approval queue mode for group creation
|
// TODO: Add an approval queue mode for group creation
|
||||||
if !user.Loggedin || !user.PluginPerms["CreateGuild"] {
|
if !user.Loggedin || !user.PluginPerms["CreateGuild"] {
|
||||||
return common.NoPermissions(w, r, user)
|
return common.NoPermissions(w, r, user)
|
||||||
}
|
}
|
||||||
CommonAreaWidgets(header)
|
CommonAreaWidgets(header)
|
||||||
|
|
||||||
pi := common.Page{"Create Guild", user, header, tList, nil}
|
pi := common.Page{header, tList, nil}
|
||||||
err := common.Templates.ExecuteTemplate(w, "guilds_create_guild.html", pi)
|
err := common.Templates.ExecuteTemplate(w, "guilds_create_guild.html", pi)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return common.InternalError(err, w, r)
|
return common.InternalError(err, w, r)
|
||||||
|
|
|
@ -35,7 +35,7 @@ type Stmts struct {
|
||||||
getActivityCountByWatcher *sql.Stmt
|
getActivityCountByWatcher *sql.Stmt
|
||||||
todaysPostCount *sql.Stmt
|
todaysPostCount *sql.Stmt
|
||||||
todaysTopicCount *sql.Stmt
|
todaysTopicCount *sql.Stmt
|
||||||
todaysReportCount *sql.Stmt
|
todaysTopicCountByForum *sql.Stmt
|
||||||
todaysNewUserCount *sql.Stmt
|
todaysNewUserCount *sql.Stmt
|
||||||
|
|
||||||
Mocks bool
|
Mocks bool
|
||||||
|
|
|
@ -37,7 +37,7 @@ type Stmts struct {
|
||||||
getActivityCountByWatcher *sql.Stmt
|
getActivityCountByWatcher *sql.Stmt
|
||||||
todaysPostCount *sql.Stmt
|
todaysPostCount *sql.Stmt
|
||||||
todaysTopicCount *sql.Stmt
|
todaysTopicCount *sql.Stmt
|
||||||
todaysReportCount *sql.Stmt
|
todaysTopicCountByForum *sql.Stmt
|
||||||
todaysNewUserCount *sql.Stmt
|
todaysNewUserCount *sql.Stmt
|
||||||
|
|
||||||
Mocks bool
|
Mocks bool
|
||||||
|
|
|
@ -28,7 +28,7 @@ type Stmts struct {
|
||||||
getActivityCountByWatcher *sql.Stmt
|
getActivityCountByWatcher *sql.Stmt
|
||||||
todaysPostCount *sql.Stmt
|
todaysPostCount *sql.Stmt
|
||||||
todaysTopicCount *sql.Stmt
|
todaysTopicCount *sql.Stmt
|
||||||
todaysReportCount *sql.Stmt
|
todaysTopicCountByForum *sql.Stmt
|
||||||
todaysNewUserCount *sql.Stmt
|
todaysNewUserCount *sql.Stmt
|
||||||
|
|
||||||
Mocks bool
|
Mocks bool
|
||||||
|
|
588
gen_router.go
588
gen_router.go
File diff suppressed because it is too large
Load Diff
|
@ -81,6 +81,9 @@
|
||||||
"login":"Login",
|
"login":"Login",
|
||||||
"register":"Registration",
|
"register":"Registration",
|
||||||
"ip_search":"IP Search",
|
"ip_search":"IP Search",
|
||||||
|
"account_username":"Edit Username",
|
||||||
|
"account_avatar":"Edit Avatar",
|
||||||
|
"account_email":"Email Manager",
|
||||||
|
|
||||||
"panel_dashboard":"Control Panel Dashboard",
|
"panel_dashboard":"Control Panel Dashboard",
|
||||||
"panel_forums":"Forum Manager",
|
"panel_forums":"Forum Manager",
|
||||||
|
@ -91,6 +94,8 @@
|
||||||
"panel_edit_setting":"Edit Setting",
|
"panel_edit_setting":"Edit Setting",
|
||||||
"panel_word_filters":"Word Filter Manager",
|
"panel_word_filters":"Word Filter Manager",
|
||||||
"panel_edit_word_filter":"Edit Word Filter",
|
"panel_edit_word_filter":"Edit Word Filter",
|
||||||
|
"panel_pages":"Page Manager",
|
||||||
|
"panel_pages_edit":"Page Editor",
|
||||||
"panel_plugins":"Plugin Manager",
|
"panel_plugins":"Plugin Manager",
|
||||||
"panel_users":"User Manager",
|
"panel_users":"User Manager",
|
||||||
"panel_edit_user":"User Editor",
|
"panel_edit_user":"User Editor",
|
||||||
|
@ -248,7 +253,10 @@
|
||||||
"panel_forum_deleted":"The forum was successfully deleted",
|
"panel_forum_deleted":"The forum was successfully deleted",
|
||||||
"panel_forum_updated":"The forum was successfully updated",
|
"panel_forum_updated":"The forum was successfully updated",
|
||||||
"panel_forum_perms_updated":"The forum permissions were successfully updated",
|
"panel_forum_perms_updated":"The forum permissions were successfully updated",
|
||||||
"panel_user_updated":"The user was successfully updated"
|
"panel_user_updated":"The user was successfully updated",
|
||||||
|
"panel_page_created":"The page was successfully created",
|
||||||
|
"panel_page_updated":"The page was successfully updated",
|
||||||
|
"panel_page_deleted":"The page was successfully deleted"
|
||||||
},
|
},
|
||||||
|
|
||||||
"TmplPhrases": {
|
"TmplPhrases": {
|
||||||
|
@ -352,6 +360,7 @@
|
||||||
"account_menu_username":"Username",
|
"account_menu_username":"Username",
|
||||||
"account_menu_password":"Password",
|
"account_menu_password":"Password",
|
||||||
"account_menu_email":"Email",
|
"account_menu_email":"Email",
|
||||||
|
"account_menu_security":"Security",
|
||||||
"account_menu_notifications":"Notifications",
|
"account_menu_notifications":"Notifications",
|
||||||
|
|
||||||
"account_avatar_head":"Edit Avatar",
|
"account_avatar_head":"Edit Avatar",
|
||||||
|
@ -539,6 +548,7 @@
|
||||||
"panel_menu_users":"Users",
|
"panel_menu_users":"Users",
|
||||||
"panel_menu_groups":"Groups",
|
"panel_menu_groups":"Groups",
|
||||||
"panel_menu_forums":"Forums",
|
"panel_menu_forums":"Forums",
|
||||||
|
"panel_menu_pages":"Pages",
|
||||||
"panel_menu_settings":"Settings",
|
"panel_menu_settings":"Settings",
|
||||||
"panel_menu_word_filters":"Word Filters",
|
"panel_menu_word_filters":"Word Filters",
|
||||||
"panel_menu_themes":"Themes",
|
"panel_menu_themes":"Themes",
|
||||||
|
@ -649,6 +659,23 @@
|
||||||
"panel_word_filters_create_replacement_placeholder":"fudge",
|
"panel_word_filters_create_replacement_placeholder":"fudge",
|
||||||
"panel_word_filters_create_create_word_filter_button":"Add Filter",
|
"panel_word_filters_create_create_word_filter_button":"Add Filter",
|
||||||
|
|
||||||
|
"panel_pages_head":"Page Manager",
|
||||||
|
"panel_pages_edit_button_aria":"Edit Page",
|
||||||
|
"panel_pages_delete_button_aria":"Delete Page",
|
||||||
|
"panel_pages_no_pages":"There aren't any pages.",
|
||||||
|
"panel_pages_create_head":"Create Page",
|
||||||
|
"panel_pages_create_name":"Name",
|
||||||
|
"panel_pages_create_name_placeholder":"faq",
|
||||||
|
"panel_pages_create_title":"Title",
|
||||||
|
"panel_pages_create_title_placeholder":"Frequently Asked Questions",
|
||||||
|
"panel_pages_create_body_placeholder":"We understand you have a lot of questions.",
|
||||||
|
"panel_pages_create_submit_button":"Create Page",
|
||||||
|
|
||||||
|
"panel_pages_edit_head":"Page Editor",
|
||||||
|
"panel_pages_name":"Name",
|
||||||
|
"panel_pages_title":"Title",
|
||||||
|
"panel_pages_edit_update_button":"Update Page",
|
||||||
|
|
||||||
"panel_statistics_views_head_suffix":" Views",
|
"panel_statistics_views_head_suffix":" Views",
|
||||||
"panel_statistics_user_agents_head":"User Agents",
|
"panel_statistics_user_agents_head":"User Agents",
|
||||||
"panel_statistics_forums_head":"Forums",
|
"panel_statistics_forums_head":"Forums",
|
||||||
|
|
4
main.go
4
main.go
|
@ -106,6 +106,10 @@ func afterDBInit() (err error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Print("Initialising the stores")
|
log.Print("Initialising the stores")
|
||||||
|
common.Pages, err = common.NewDefaultPageStore(acc)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
common.Reports, err = common.NewDefaultReportStore(acc)
|
common.Reports, err = common.NewDefaultReportStore(acc)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
|
|
@ -5,6 +5,7 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/http/httptest"
|
"net/http/httptest"
|
||||||
"runtime/debug"
|
"runtime/debug"
|
||||||
|
"strconv"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
@ -252,12 +253,12 @@ func userStoreTest(t *testing.T, newUserID int) {
|
||||||
|
|
||||||
expectIntToBeX(t, user.Group, common.Config.DefaultGroup, "Sam should be back in group %d")
|
expectIntToBeX(t, user.Group, common.Config.DefaultGroup, "Sam should be back in group %d")
|
||||||
|
|
||||||
var reportsForumID = 1
|
var reportsForumID = 1 // TODO: Use the constant in common?
|
||||||
var generalForumID = 2
|
var generalForumID = 2
|
||||||
dummyResponseRecorder := httptest.NewRecorder()
|
dummyResponseRecorder := httptest.NewRecorder()
|
||||||
bytesBuffer := bytes.NewBuffer([]byte(""))
|
bytesBuffer := bytes.NewBuffer([]byte(""))
|
||||||
dummyRequest1 := httptest.NewRequest("", "/forum/1", bytesBuffer)
|
dummyRequest1 := httptest.NewRequest("", "/forum/"+strconv.Itoa(reportsForumID), bytesBuffer)
|
||||||
dummyRequest2 := httptest.NewRequest("", "/forum/2", bytesBuffer)
|
dummyRequest2 := httptest.NewRequest("", "/forum/"+strconv.Itoa(generalForumID), bytesBuffer)
|
||||||
|
|
||||||
err = user.ChangeGroup(1)
|
err = user.ChangeGroup(1)
|
||||||
expectNilErr(t, err)
|
expectNilErr(t, err)
|
||||||
|
|
5
mssql.go
5
mssql.go
|
@ -96,8 +96,9 @@ func initMSSQL() (err error) {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Print("Preparing todaysReportCount statement.")
|
log.Print("Preparing todaysTopicCountByForum statement.")
|
||||||
todaysReportCountStmt, err = db.Prepare("select count(*) from topics where createdAt >= DATEADD(DAY, -1, GETUTCDATE()) and parentID = 1")
|
// TODO: Stop hard-coding this query
|
||||||
|
todaysTopicCountByForum, err = db.Prepare("select count(*) from topics where createdAt >= DATEADD(DAY, -1, GETUTCDATE()) and parentID = ?")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
5
mysql.go
5
mysql.go
|
@ -75,8 +75,9 @@ func initMySQL() (err error) {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Print("Preparing todaysReportCount statement.")
|
log.Print("Preparing todaysTopicCountByForum statement.")
|
||||||
stmts.todaysReportCount, err = db.Prepare("select count(*) from topics where createdAt BETWEEN (utc_timestamp() - interval 1 day) and utc_timestamp() and parentID = 1")
|
// TODO: Stop hard-coding this query
|
||||||
|
stmts.todaysTopicCountByForum, err = db.Prepare("select count(*) from topics where createdAt BETWEEN (utc_timestamp() - interval 1 day) and utc_timestamp() and parentID = ?")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
@ -35,11 +35,11 @@ func panelRenderTemplate(tmplName string, w http.ResponseWriter, r *http.Request
|
||||||
}
|
}
|
||||||
|
|
||||||
func routePanelDashboard(w http.ResponseWriter, r *http.Request, user common.User) common.RouteError {
|
func routePanelDashboard(w http.ResponseWriter, r *http.Request, user common.User) common.RouteError {
|
||||||
headerVars, stats, ferr := common.PanelUserCheck(w, r, &user)
|
header, stats, ferr := common.PanelUserCheck(w, r, &user)
|
||||||
if ferr != nil {
|
if ferr != nil {
|
||||||
return ferr
|
return ferr
|
||||||
}
|
}
|
||||||
headerVars.Title = common.GetTitlePhrase("panel_dashboard")
|
header.Title = common.GetTitlePhrase("panel_dashboard")
|
||||||
|
|
||||||
// We won't calculate this on the spot anymore, as the system doesn't seem to like it if we do multiple fetches simultaneously. Should we constantly calculate this on a background thread? Perhaps, the watchdog to scale back heavy features under load? One plus side is that we'd get immediate CPU percentages here instead of waiting it to kick in with WebSockets
|
// We won't calculate this on the spot anymore, as the system doesn't seem to like it if we do multiple fetches simultaneously. Should we constantly calculate this on a background thread? Perhaps, the watchdog to scale back heavy features under load? One plus side is that we'd get immediate CPU percentages here instead of waiting it to kick in with WebSockets
|
||||||
var cpustr = "Unknown"
|
var cpustr = "Unknown"
|
||||||
|
@ -93,8 +93,8 @@ func routePanelDashboard(w http.ResponseWriter, r *http.Request, user common.Use
|
||||||
|
|
||||||
// TODO: Add a stat store for this?
|
// TODO: Add a stat store for this?
|
||||||
var intErr error
|
var intErr error
|
||||||
var extractStat = func(stmt *sql.Stmt) (stat int) {
|
var extractStat = func(stmt *sql.Stmt, args ...interface{}) (stat int) {
|
||||||
err := stmt.QueryRow().Scan(&stat)
|
err := stmt.QueryRow(args...).Scan(&stat)
|
||||||
if err != nil && err != ErrNoRows {
|
if err != nil && err != ErrNoRows {
|
||||||
intErr = err
|
intErr = err
|
||||||
}
|
}
|
||||||
|
@ -109,7 +109,7 @@ func routePanelDashboard(w http.ResponseWriter, r *http.Request, user common.Use
|
||||||
var topicInterval = "day"
|
var topicInterval = "day"
|
||||||
var topicColour = greaterThanSwitch(topicCount, 0, 8)
|
var topicColour = greaterThanSwitch(topicCount, 0, 8)
|
||||||
|
|
||||||
var reportCount = extractStat(stmts.todaysReportCount)
|
var reportCount = extractStat(stmts.todaysTopicCountByForum, common.ReportForumID)
|
||||||
var reportInterval = "week"
|
var reportInterval = "week"
|
||||||
|
|
||||||
var newUserCount = extractStat(stmts.todaysNewUserCount)
|
var newUserCount = extractStat(stmts.todaysNewUserCount)
|
||||||
|
@ -120,11 +120,15 @@ func routePanelDashboard(w http.ResponseWriter, r *http.Request, user common.Use
|
||||||
return common.InternalError(intErr, w, r)
|
return common.InternalError(intErr, w, r)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: Localise these
|
||||||
var gridElements = []common.GridElement{
|
var gridElements = []common.GridElement{
|
||||||
common.GridElement{"dash-version", "v" + version.String(), 0, "grid_istat stat_green", "", "", "Gosora is up-to-date :)"},
|
common.GridElement{"dash-version", "v" + version.String(), 0, "grid_istat stat_green", "", "", "Gosora is up-to-date :)"},
|
||||||
common.GridElement{"dash-cpu", "CPU: " + cpustr, 1, "grid_istat " + cpuColour, "", "", "The global CPU usage of this server"},
|
common.GridElement{"dash-cpu", "CPU: " + cpustr, 1, "grid_istat " + cpuColour, "", "", "The global CPU usage of this server"},
|
||||||
common.GridElement{"dash-ram", "RAM: " + ramstr, 2, "grid_istat " + ramColour, "", "", "The global RAM usage of this server"},
|
common.GridElement{"dash-ram", "RAM: " + ramstr, 2, "grid_istat " + ramColour, "", "", "The global RAM usage of this server"},
|
||||||
}
|
}
|
||||||
|
var addElement = func(element common.GridElement) {
|
||||||
|
gridElements = append(gridElements, element)
|
||||||
|
}
|
||||||
|
|
||||||
if common.EnableWebsockets {
|
if common.EnableWebsockets {
|
||||||
uonline := common.WsHub.UserCount()
|
uonline := common.WsHub.UserCount()
|
||||||
|
@ -140,41 +144,42 @@ func routePanelDashboard(w http.ResponseWriter, r *http.Request, user common.Use
|
||||||
uonline, uunit := common.ConvertFriendlyUnit(uonline)
|
uonline, uunit := common.ConvertFriendlyUnit(uonline)
|
||||||
gonline, gunit := common.ConvertFriendlyUnit(gonline)
|
gonline, gunit := common.ConvertFriendlyUnit(gonline)
|
||||||
|
|
||||||
gridElements = append(gridElements, common.GridElement{"dash-totonline", strconv.Itoa(totonline) + totunit + " online", 3, "grid_stat " + onlineColour, "", "", "The number of people who are currently online"})
|
addElement(common.GridElement{"dash-totonline", strconv.Itoa(totonline) + totunit + " online", 3, "grid_stat " + onlineColour, "", "", "The number of people who are currently online"})
|
||||||
gridElements = append(gridElements, common.GridElement{"dash-gonline", strconv.Itoa(gonline) + gunit + " guests online", 4, "grid_stat " + onlineGuestsColour, "", "", "The number of guests who are currently online"})
|
addElement(common.GridElement{"dash-gonline", strconv.Itoa(gonline) + gunit + " guests online", 4, "grid_stat " + onlineGuestsColour, "", "", "The number of guests who are currently online"})
|
||||||
gridElements = append(gridElements, common.GridElement{"dash-uonline", strconv.Itoa(uonline) + uunit + " users online", 5, "grid_stat " + onlineUsersColour, "", "", "The number of logged-in users who are currently online"})
|
addElement(common.GridElement{"dash-uonline", strconv.Itoa(uonline) + uunit + " users online", 5, "grid_stat " + onlineUsersColour, "", "", "The number of logged-in users who are currently online"})
|
||||||
gridElements = append(gridElements, common.GridElement{"dash-reqs", strconv.Itoa(reqCount) + " reqs / second", 7, "grid_stat grid_end_group " + topicColour, "", "", "The number of requests over the last 24 hours"})
|
addElement(common.GridElement{"dash-reqs", strconv.Itoa(reqCount) + " reqs / second", 7, "grid_stat grid_end_group " + topicColour, "", "", "The number of requests over the last 24 hours"})
|
||||||
}
|
}
|
||||||
|
|
||||||
gridElements = append(gridElements, common.GridElement{"dash-postsperday", strconv.Itoa(postCount) + " posts / " + postInterval, 6, "grid_stat " + postColour, "", "", "The number of new posts over the last 24 hours"})
|
addElement(common.GridElement{"dash-postsperday", strconv.Itoa(postCount) + " posts / " + postInterval, 6, "grid_stat " + postColour, "", "", "The number of new posts over the last 24 hours"})
|
||||||
gridElements = append(gridElements, common.GridElement{"dash-topicsperday", strconv.Itoa(topicCount) + " topics / " + topicInterval, 7, "grid_stat " + topicColour, "", "", "The number of new topics over the last 24 hours"})
|
addElement(common.GridElement{"dash-topicsperday", strconv.Itoa(topicCount) + " topics / " + topicInterval, 7, "grid_stat " + topicColour, "", "", "The number of new topics over the last 24 hours"})
|
||||||
gridElements = append(gridElements, common.GridElement{"dash-totonlineperday", "20 online / day", 8, "grid_stat stat_disabled", "", "", "Coming Soon!" /*, "The people online over the last 24 hours"*/})
|
addElement(common.GridElement{"dash-totonlineperday", "20 online / day", 8, "grid_stat stat_disabled", "", "", "Coming Soon!" /*, "The people online over the last 24 hours"*/})
|
||||||
|
|
||||||
gridElements = append(gridElements, common.GridElement{"dash-searches", "8 searches / week", 9, "grid_stat stat_disabled", "", "", "Coming Soon!" /*"The number of searches over the last 7 days"*/})
|
addElement(common.GridElement{"dash-searches", "8 searches / week", 9, "grid_stat stat_disabled", "", "", "Coming Soon!" /*"The number of searches over the last 7 days"*/})
|
||||||
gridElements = append(gridElements, common.GridElement{"dash-newusers", strconv.Itoa(newUserCount) + " new users / " + newUserInterval, 10, "grid_stat", "", "", "The number of new users over the last 7 days"})
|
addElement(common.GridElement{"dash-newusers", strconv.Itoa(newUserCount) + " new users / " + newUserInterval, 10, "grid_stat", "", "", "The number of new users over the last 7 days"})
|
||||||
gridElements = append(gridElements, common.GridElement{"dash-reports", strconv.Itoa(reportCount) + " reports / " + reportInterval, 11, "grid_stat", "", "", "The number of reports over the last 7 days"})
|
addElement(common.GridElement{"dash-reports", strconv.Itoa(reportCount) + " reports / " + reportInterval, 11, "grid_stat", "", "", "The number of reports over the last 7 days"})
|
||||||
|
|
||||||
if false {
|
if false {
|
||||||
gridElements = append(gridElements, common.GridElement{"dash-minperuser", "2 minutes / user / week", 12, "grid_stat stat_disabled", "", "", "Coming Soon!" /*"The average number of number of minutes spent by each active user over the last 7 days"*/})
|
addElement(common.GridElement{"dash-minperuser", "2 minutes / user / week", 12, "grid_stat stat_disabled", "", "", "Coming Soon!" /*"The average number of number of minutes spent by each active user over the last 7 days"*/})
|
||||||
gridElements = append(gridElements, common.GridElement{"dash-visitorsperweek", "2 visitors / week", 13, "grid_stat stat_disabled", "", "", "Coming Soon!" /*"The number of unique visitors we've had over the last 7 days"*/})
|
addElement(common.GridElement{"dash-visitorsperweek", "2 visitors / week", 13, "grid_stat stat_disabled", "", "", "Coming Soon!" /*"The number of unique visitors we've had over the last 7 days"*/})
|
||||||
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"*/})
|
addElement(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{headerVars, stats, "dashboard", gridElements}
|
pi := common.PanelDashboardPage{&common.BasePanelPage{header, stats, "dashboard", common.ReportForumID}, gridElements}
|
||||||
return panelRenderTemplate("panel_dashboard", w, r, user, &pi)
|
return panelRenderTemplate("panel_dashboard", w, r, user, &pi)
|
||||||
}
|
}
|
||||||
|
|
||||||
func routePanelWordFilters(w http.ResponseWriter, r *http.Request, user common.User) common.RouteError {
|
func routePanelWordFilters(w http.ResponseWriter, r *http.Request, user common.User) common.RouteError {
|
||||||
headerVars, stats, ferr := common.PanelUserCheck(w, r, &user)
|
header, stats, ferr := common.PanelUserCheck(w, r, &user)
|
||||||
if ferr != nil {
|
if ferr != nil {
|
||||||
return ferr
|
return ferr
|
||||||
}
|
}
|
||||||
if !user.Perms.EditSettings {
|
if !user.Perms.EditSettings {
|
||||||
return common.NoPermissions(w, r, user)
|
return common.NoPermissions(w, r, user)
|
||||||
}
|
}
|
||||||
|
header.Title = common.GetTitlePhrase("panel_word_filters")
|
||||||
|
|
||||||
var filterList = common.WordFilterBox.Load().(common.WordFilterMap)
|
var filterList = common.WordFilterBox.Load().(common.WordFilterMap)
|
||||||
pi := common.PanelPage{common.GetTitlePhrase("panel_word_filters"), user, headerVars, stats, "word-filters", tList, filterList}
|
pi := common.PanelPage{&common.BasePanelPage{header, stats, "word-filters", common.ReportForumID}, tList, filterList}
|
||||||
return panelRenderTemplate("panel_word_filters", w, r, user, &pi)
|
return panelRenderTemplate("panel_word_filters", w, r, user, &pi)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -212,17 +217,17 @@ func routePanelWordFiltersCreateSubmit(w http.ResponseWriter, r *http.Request, u
|
||||||
|
|
||||||
// TODO: Implement this as a non-JS fallback
|
// TODO: Implement this as a non-JS fallback
|
||||||
func routePanelWordFiltersEdit(w http.ResponseWriter, r *http.Request, user common.User, wfid string) common.RouteError {
|
func routePanelWordFiltersEdit(w http.ResponseWriter, r *http.Request, user common.User, wfid string) common.RouteError {
|
||||||
headerVars, stats, ferr := common.PanelUserCheck(w, r, &user)
|
header, stats, ferr := common.PanelUserCheck(w, r, &user)
|
||||||
if ferr != nil {
|
if ferr != nil {
|
||||||
return ferr
|
return ferr
|
||||||
}
|
}
|
||||||
if !user.Perms.EditSettings {
|
if !user.Perms.EditSettings {
|
||||||
return common.NoPermissions(w, r, user)
|
return common.NoPermissions(w, r, user)
|
||||||
}
|
}
|
||||||
|
header.Title = common.GetTitlePhrase("panel_edit_word_filter")
|
||||||
_ = wfid
|
_ = wfid
|
||||||
|
|
||||||
pi := common.PanelPage{common.GetTitlePhrase("panel_edit_word_filter"), user, headerVars, stats, "word-filters", tList, nil}
|
pi := common.PanelPage{&common.BasePanelPage{header, stats, "word-filters", common.ReportForumID}, tList, nil}
|
||||||
return panelRenderTemplate("panel_word_filters_edit", w, r, user, &pi)
|
return panelRenderTemplate("panel_word_filters_edit", w, r, user, &pi)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -293,20 +298,21 @@ func routePanelWordFiltersDeleteSubmit(w http.ResponseWriter, r *http.Request, u
|
||||||
}
|
}
|
||||||
|
|
||||||
func routePanelPlugins(w http.ResponseWriter, r *http.Request, user common.User) common.RouteError {
|
func routePanelPlugins(w http.ResponseWriter, r *http.Request, user common.User) common.RouteError {
|
||||||
headerVars, stats, ferr := common.PanelUserCheck(w, r, &user)
|
header, stats, ferr := common.PanelUserCheck(w, r, &user)
|
||||||
if ferr != nil {
|
if ferr != nil {
|
||||||
return ferr
|
return ferr
|
||||||
}
|
}
|
||||||
if !user.Perms.ManagePlugins {
|
if !user.Perms.ManagePlugins {
|
||||||
return common.NoPermissions(w, r, user)
|
return common.NoPermissions(w, r, user)
|
||||||
}
|
}
|
||||||
|
header.Title = common.GetTitlePhrase("panel_plugins")
|
||||||
|
|
||||||
var pluginList []interface{}
|
var pluginList []interface{}
|
||||||
for _, plugin := range common.Plugins {
|
for _, plugin := range common.Plugins {
|
||||||
pluginList = append(pluginList, plugin)
|
pluginList = append(pluginList, plugin)
|
||||||
}
|
}
|
||||||
|
|
||||||
pi := common.PanelPage{common.GetTitlePhrase("panel_plugins"), user, headerVars, stats, "plugins", pluginList, nil}
|
pi := common.PanelPage{&common.BasePanelPage{header, stats, "plugins", common.ReportForumID}, pluginList, nil}
|
||||||
return panelRenderTemplate("panel_plugins", w, r, user, &pi)
|
return panelRenderTemplate("panel_plugins", w, r, user, &pi)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -487,18 +493,19 @@ func routePanelUsers(w http.ResponseWriter, r *http.Request, user common.User) c
|
||||||
}
|
}
|
||||||
|
|
||||||
pageList := common.Paginate(stats.Users, perPage, 5)
|
pageList := common.Paginate(stats.Users, perPage, 5)
|
||||||
pi := common.PanelUserPage{header, stats, "users", users, common.Paginator{pageList, page, lastPage}}
|
pi := common.PanelUserPage{&common.BasePanelPage{header, stats, "users", common.ReportForumID}, users, common.Paginator{pageList, page, lastPage}}
|
||||||
return panelRenderTemplate("panel_users", w, r, user, &pi)
|
return panelRenderTemplate("panel_users", w, r, user, &pi)
|
||||||
}
|
}
|
||||||
|
|
||||||
func routePanelUsersEdit(w http.ResponseWriter, r *http.Request, user common.User, suid string) common.RouteError {
|
func routePanelUsersEdit(w http.ResponseWriter, r *http.Request, user common.User, suid string) common.RouteError {
|
||||||
headerVars, stats, ferr := common.PanelUserCheck(w, r, &user)
|
header, stats, ferr := common.PanelUserCheck(w, r, &user)
|
||||||
if ferr != nil {
|
if ferr != nil {
|
||||||
return ferr
|
return ferr
|
||||||
}
|
}
|
||||||
if !user.Perms.EditUser {
|
if !user.Perms.EditUser {
|
||||||
return common.NoPermissions(w, r, user)
|
return common.NoPermissions(w, r, user)
|
||||||
}
|
}
|
||||||
|
header.Title = common.GetTitlePhrase("panel_edit_user")
|
||||||
|
|
||||||
uid, err := strconv.Atoi(suid)
|
uid, err := strconv.Atoi(suid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -534,10 +541,10 @@ func routePanelUsersEdit(w http.ResponseWriter, r *http.Request, user common.Use
|
||||||
}
|
}
|
||||||
|
|
||||||
if r.FormValue("updated") == "1" {
|
if r.FormValue("updated") == "1" {
|
||||||
headerVars.NoticeList = append(headerVars.NoticeList, common.GetNoticePhrase("panel_user_updated"))
|
header.NoticeList = append(header.NoticeList, common.GetNoticePhrase("panel_user_updated"))
|
||||||
}
|
}
|
||||||
|
|
||||||
pi := common.PanelPage{common.GetTitlePhrase("panel_edit_user"), user, headerVars, stats, "users", groupList, targetUser}
|
pi := common.PanelPage{&common.BasePanelPage{header, stats, "users", common.ReportForumID}, groupList, targetUser}
|
||||||
if common.RunPreRenderHook("pre_render_panel_edit_user", w, r, &user, &pi) {
|
if common.RunPreRenderHook("pre_render_panel_edit_user", w, r, &user, &pi) {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -635,10 +642,11 @@ func routePanelUsersEditSubmit(w http.ResponseWriter, r *http.Request, user comm
|
||||||
}
|
}
|
||||||
|
|
||||||
func routePanelGroups(w http.ResponseWriter, r *http.Request, user common.User) common.RouteError {
|
func routePanelGroups(w http.ResponseWriter, r *http.Request, user common.User) common.RouteError {
|
||||||
headerVars, stats, ferr := common.PanelUserCheck(w, r, &user)
|
header, stats, ferr := common.PanelUserCheck(w, r, &user)
|
||||||
if ferr != nil {
|
if ferr != nil {
|
||||||
return ferr
|
return ferr
|
||||||
}
|
}
|
||||||
|
header.Title = common.GetTitlePhrase("panel_groups")
|
||||||
|
|
||||||
page, _ := strconv.Atoi(r.FormValue("page"))
|
page, _ := strconv.Atoi(r.FormValue("page"))
|
||||||
perPage := 9
|
perPage := 9
|
||||||
|
@ -661,6 +669,7 @@ func routePanelGroups(w http.ResponseWriter, r *http.Request, user common.User)
|
||||||
var canDelete = false
|
var canDelete = false
|
||||||
|
|
||||||
// TODO: Use a switch for this
|
// TODO: Use a switch for this
|
||||||
|
// TODO: Localise this
|
||||||
if group.IsAdmin {
|
if group.IsAdmin {
|
||||||
rank = "Admin"
|
rank = "Admin"
|
||||||
rankClass = "admin"
|
rankClass = "admin"
|
||||||
|
@ -685,18 +694,19 @@ func routePanelGroups(w http.ResponseWriter, r *http.Request, user common.User)
|
||||||
//log.Printf("groupList: %+v\n", groupList)
|
//log.Printf("groupList: %+v\n", groupList)
|
||||||
|
|
||||||
pageList := common.Paginate(stats.Groups, perPage, 5)
|
pageList := common.Paginate(stats.Groups, perPage, 5)
|
||||||
pi := common.PanelGroupPage{common.GetTitlePhrase("panel_groups"), user, headerVars, stats, "groups", groupList, common.Paginator{pageList, page, lastPage}}
|
pi := common.PanelGroupPage{&common.BasePanelPage{header, stats, "groups", common.ReportForumID}, groupList, common.Paginator{pageList, page, lastPage}}
|
||||||
return panelRenderTemplate("panel_groups", w, r, user, &pi)
|
return panelRenderTemplate("panel_groups", w, r, user, &pi)
|
||||||
}
|
}
|
||||||
|
|
||||||
func routePanelGroupsEdit(w http.ResponseWriter, r *http.Request, user common.User, sgid string) common.RouteError {
|
func routePanelGroupsEdit(w http.ResponseWriter, r *http.Request, user common.User, sgid string) common.RouteError {
|
||||||
headerVars, stats, ferr := common.PanelUserCheck(w, r, &user)
|
header, stats, ferr := common.PanelUserCheck(w, r, &user)
|
||||||
if ferr != nil {
|
if ferr != nil {
|
||||||
return ferr
|
return ferr
|
||||||
}
|
}
|
||||||
if !user.Perms.EditGroup {
|
if !user.Perms.EditGroup {
|
||||||
return common.NoPermissions(w, r, user)
|
return common.NoPermissions(w, r, user)
|
||||||
}
|
}
|
||||||
|
header.Title = common.GetTitlePhrase("panel_edit_group")
|
||||||
|
|
||||||
gid, err := strconv.Atoi(sgid)
|
gid, err := strconv.Atoi(sgid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -706,7 +716,7 @@ func routePanelGroupsEdit(w http.ResponseWriter, r *http.Request, user common.Us
|
||||||
group, err := common.Groups.Get(gid)
|
group, err := common.Groups.Get(gid)
|
||||||
if err == ErrNoRows {
|
if err == ErrNoRows {
|
||||||
//log.Print("aaaaa monsters")
|
//log.Print("aaaaa monsters")
|
||||||
return common.NotFound(w, r, headerVars)
|
return common.NotFound(w, r, header)
|
||||||
} else if err != nil {
|
} else if err != nil {
|
||||||
return common.InternalError(err, w, r)
|
return common.InternalError(err, w, r)
|
||||||
}
|
}
|
||||||
|
@ -734,7 +744,7 @@ func routePanelGroupsEdit(w http.ResponseWriter, r *http.Request, user common.Us
|
||||||
|
|
||||||
disableRank := !user.Perms.EditGroupGlobalPerms || (group.ID == 6)
|
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.BasePanelPage{header, stats, "groups", common.ReportForumID}, group.ID, group.Name, group.Tag, rank, disableRank}
|
||||||
if common.RunPreRenderHook("pre_render_panel_edit_group", w, r, &user, &pi) {
|
if common.RunPreRenderHook("pre_render_panel_edit_group", w, r, &user, &pi) {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -746,13 +756,14 @@ func routePanelGroupsEdit(w http.ResponseWriter, r *http.Request, user common.Us
|
||||||
}
|
}
|
||||||
|
|
||||||
func routePanelGroupsEditPerms(w http.ResponseWriter, r *http.Request, user common.User, sgid string) common.RouteError {
|
func routePanelGroupsEditPerms(w http.ResponseWriter, r *http.Request, user common.User, sgid string) common.RouteError {
|
||||||
headerVars, stats, ferr := common.PanelUserCheck(w, r, &user)
|
header, stats, ferr := common.PanelUserCheck(w, r, &user)
|
||||||
if ferr != nil {
|
if ferr != nil {
|
||||||
return ferr
|
return ferr
|
||||||
}
|
}
|
||||||
if !user.Perms.EditGroup {
|
if !user.Perms.EditGroup {
|
||||||
return common.NoPermissions(w, r, user)
|
return common.NoPermissions(w, r, user)
|
||||||
}
|
}
|
||||||
|
header.Title = common.GetTitlePhrase("panel_edit_group")
|
||||||
|
|
||||||
gid, err := strconv.Atoi(sgid)
|
gid, err := strconv.Atoi(sgid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -762,7 +773,7 @@ func routePanelGroupsEditPerms(w http.ResponseWriter, r *http.Request, user comm
|
||||||
group, err := common.Groups.Get(gid)
|
group, err := common.Groups.Get(gid)
|
||||||
if err == ErrNoRows {
|
if err == ErrNoRows {
|
||||||
//log.Print("aaaaa monsters")
|
//log.Print("aaaaa monsters")
|
||||||
return common.NotFound(w, r, headerVars)
|
return common.NotFound(w, r, header)
|
||||||
} else if err != nil {
|
} else if err != nil {
|
||||||
return common.InternalError(err, w, r)
|
return common.InternalError(err, w, r)
|
||||||
}
|
}
|
||||||
|
@ -820,7 +831,7 @@ func routePanelGroupsEditPerms(w http.ResponseWriter, r *http.Request, user comm
|
||||||
addGlobalPerm("ViewIPs", group.Perms.ViewIPs)
|
addGlobalPerm("ViewIPs", group.Perms.ViewIPs)
|
||||||
addGlobalPerm("UploadFiles", group.Perms.UploadFiles)
|
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.BasePanelPage{header, stats, "groups", common.ReportForumID}, group.ID, group.Name, localPerms, globalPerms}
|
||||||
if common.RunPreRenderHook("pre_render_panel_edit_group_perms", w, r, &user, &pi) {
|
if common.RunPreRenderHook("pre_render_panel_edit_group_perms", w, r, &user, &pi) {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -1049,7 +1060,7 @@ func routePanelThemes(w http.ResponseWriter, r *http.Request, user common.User)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pi := common.PanelThemesPage{header, stats, "themes", pThemeList, vThemeList}
|
pi := common.PanelThemesPage{&common.BasePanelPage{header, stats, "themes", common.ReportForumID}, pThemeList, vThemeList}
|
||||||
return panelRenderTemplate("panel_themes", w, r, user, &pi)
|
return panelRenderTemplate("panel_themes", w, r, user, &pi)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1141,7 +1152,7 @@ func routePanelThemesMenus(w http.ResponseWriter, r *http.Request, user common.U
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pi := common.PanelMenuListPage{header, stats, "themes", menuList}
|
pi := common.PanelMenuListPage{&common.BasePanelPage{header, stats, "themes", common.ReportForumID}, menuList}
|
||||||
return panelRenderTemplate("panel_themes_menus", w, r, user, &pi)
|
return panelRenderTemplate("panel_themes_menus", w, r, user, &pi)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1189,7 +1200,7 @@ func routePanelThemesMenusEdit(w http.ResponseWriter, r *http.Request, user comm
|
||||||
menuList = append(menuList, item)
|
menuList = append(menuList, item)
|
||||||
}
|
}
|
||||||
|
|
||||||
pi := common.PanelMenuPage{header, stats, "themes", mid, menuList}
|
pi := common.PanelMenuPage{&common.BasePanelPage{header, stats, "themes", common.ReportForumID}, mid, menuList}
|
||||||
return panelRenderTemplate("panel_themes_menus_items", w, r, user, &pi)
|
return panelRenderTemplate("panel_themes_menus_items", w, r, user, &pi)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1216,7 +1227,7 @@ func routePanelThemesMenuItemEdit(w http.ResponseWriter, r *http.Request, user c
|
||||||
return common.InternalError(err, w, r)
|
return common.InternalError(err, w, r)
|
||||||
}
|
}
|
||||||
|
|
||||||
pi := common.PanelMenuItemPage{header, stats, "themes", menuItem}
|
pi := common.PanelMenuItemPage{&common.BasePanelPage{header, stats, "themes", common.ReportForumID}, menuItem}
|
||||||
return panelRenderTemplate("panel_themes_menus_item_edit", w, r, user, &pi)
|
return panelRenderTemplate("panel_themes_menus_item_edit", w, r, user, &pi)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1152,7 +1152,7 @@ type Stmts struct {
|
||||||
getActivityCountByWatcher *sql.Stmt
|
getActivityCountByWatcher *sql.Stmt
|
||||||
todaysPostCount *sql.Stmt
|
todaysPostCount *sql.Stmt
|
||||||
todaysTopicCount *sql.Stmt
|
todaysTopicCount *sql.Stmt
|
||||||
todaysReportCount *sql.Stmt
|
todaysTopicCountByForum *sql.Stmt
|
||||||
todaysNewUserCount *sql.Stmt
|
todaysNewUserCount *sql.Stmt
|
||||||
|
|
||||||
Mocks bool
|
Mocks bool
|
||||||
|
|
|
@ -737,7 +737,7 @@ type Stmts struct {
|
||||||
getActivityCountByWatcher *sql.Stmt
|
getActivityCountByWatcher *sql.Stmt
|
||||||
todaysPostCount *sql.Stmt
|
todaysPostCount *sql.Stmt
|
||||||
todaysTopicCount *sql.Stmt
|
todaysTopicCount *sql.Stmt
|
||||||
todaysReportCount *sql.Stmt
|
todaysTopicCountByForum *sql.Stmt
|
||||||
todaysNewUserCount *sql.Stmt
|
todaysNewUserCount *sql.Stmt
|
||||||
|
|
||||||
Mocks bool
|
Mocks bool
|
||||||
|
|
|
@ -440,7 +440,7 @@ type Stmts struct {
|
||||||
getActivityCountByWatcher *sql.Stmt
|
getActivityCountByWatcher *sql.Stmt
|
||||||
todaysPostCount *sql.Stmt
|
todaysPostCount *sql.Stmt
|
||||||
todaysTopicCount *sql.Stmt
|
todaysTopicCount *sql.Stmt
|
||||||
todaysReportCount *sql.Stmt
|
todaysTopicCountByForum *sql.Stmt
|
||||||
todaysNewUserCount *sql.Stmt
|
todaysNewUserCount *sql.Stmt
|
||||||
|
|
||||||
Mocks bool
|
Mocks bool
|
||||||
|
|
|
@ -62,6 +62,26 @@ func createTables(adapter qgen.Adapter) error {
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
|
/*
|
||||||
|
qgen.Install.CreateTable("users_2fa_keys", "utf8mb4", "utf8mb4_general_ci",
|
||||||
|
[]qgen.DBTableColumn{
|
||||||
|
qgen.DBTableColumn{"uid", "int", 0, false, false, ""},
|
||||||
|
qgen.DBTableColumn{"secret", "varchar", 100, false, false, ""},
|
||||||
|
qgen.DBTableColumn{"scratch1", "varchar", 50, false, false, ""},
|
||||||
|
qgen.DBTableColumn{"scratch2", "varchar", 50, false, false, ""},
|
||||||
|
qgen.DBTableColumn{"scratch3", "varchar", 50, false, false, ""},
|
||||||
|
qgen.DBTableColumn{"scratch4", "varchar", 50, false, false, ""},
|
||||||
|
qgen.DBTableColumn{"scratch5", "varchar", 50, false, false, ""},
|
||||||
|
qgen.DBTableColumn{"scratch6", "varchar", 50, false, false, ""},
|
||||||
|
qgen.DBTableColumn{"scratch7", "varchar", 50, false, false, ""},
|
||||||
|
qgen.DBTableColumn{"scratch8", "varchar", 50, false, false, ""},
|
||||||
|
},
|
||||||
|
[]qgen.DBTableKey{
|
||||||
|
qgen.DBTableKey{"uid", "primary"},
|
||||||
|
},
|
||||||
|
)
|
||||||
|
*/
|
||||||
|
|
||||||
// What should we do about global penalties? Put them on the users table for speed? Or keep them here?
|
// What should we do about global penalties? Put them on the users table for speed? Or keep them here?
|
||||||
// Should we add IP Penalties? No, that's a stupid idea, just implement IP Bans properly. What about shadowbans?
|
// Should we add IP Penalties? No, that's a stupid idea, just implement IP Bans properly. What about shadowbans?
|
||||||
// TODO: Perm overrides
|
// TODO: Perm overrides
|
||||||
|
|
|
@ -151,6 +151,12 @@ func buildPanelRoutes() {
|
||||||
Action("routePanelWordFiltersEditSubmit", "/panel/settings/word-filters/edit/submit/", "extraData"),
|
Action("routePanelWordFiltersEditSubmit", "/panel/settings/word-filters/edit/submit/", "extraData"),
|
||||||
Action("routePanelWordFiltersDeleteSubmit", "/panel/settings/word-filters/delete/submit/", "extraData"),
|
Action("routePanelWordFiltersDeleteSubmit", "/panel/settings/word-filters/delete/submit/", "extraData"),
|
||||||
|
|
||||||
|
View("panel.Pages", "/panel/pages/").Before("AdminOnly"),
|
||||||
|
Action("panel.PagesCreateSubmit", "/panel/pages/create/submit/").Before("AdminOnly"),
|
||||||
|
View("panel.PagesEdit", "/panel/pages/edit/", "extraData").Before("AdminOnly"),
|
||||||
|
Action("panel.PagesEditSubmit", "/panel/pages/edit/submit/", "extraData").Before("AdminOnly"),
|
||||||
|
Action("panel.PagesDeleteSubmit", "/panel/pages/delete/submit/", "extraData").Before("AdminOnly"),
|
||||||
|
|
||||||
View("routePanelThemes", "/panel/themes/"),
|
View("routePanelThemes", "/panel/themes/"),
|
||||||
Action("routePanelThemesSetDefault", "/panel/themes/default/", "extraData"),
|
Action("routePanelThemesSetDefault", "/panel/themes/default/", "extraData"),
|
||||||
View("routePanelThemesMenus", "/panel/themes/menus/"),
|
View("routePanelThemesMenus", "/panel/themes/menus/"),
|
||||||
|
|
|
@ -27,7 +27,8 @@ func AccountLogin(w http.ResponseWriter, r *http.Request, user common.User) comm
|
||||||
if user.Loggedin {
|
if user.Loggedin {
|
||||||
return common.LocalError("You're already logged in.", w, r, user)
|
return common.LocalError("You're already logged in.", w, r, user)
|
||||||
}
|
}
|
||||||
pi := common.Page{common.GetTitlePhrase("login"), user, header, tList, nil}
|
header.Title = common.GetTitlePhrase("login")
|
||||||
|
pi := common.Page{header, tList, nil}
|
||||||
if common.RunPreRenderHook("pre_render_login", w, r, &user, &pi) {
|
if common.RunPreRenderHook("pre_render_login", w, r, &user, &pi) {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -92,11 +93,13 @@ func AccountRegister(w http.ResponseWriter, r *http.Request, user common.User) c
|
||||||
if user.Loggedin {
|
if user.Loggedin {
|
||||||
return common.LocalError("You're already logged in.", w, r, user)
|
return common.LocalError("You're already logged in.", w, r, user)
|
||||||
}
|
}
|
||||||
|
header.Title = common.GetTitlePhrase("register")
|
||||||
|
|
||||||
h := sha256.New()
|
h := sha256.New()
|
||||||
h.Write([]byte(common.JSTokenBox.Load().(string)))
|
h.Write([]byte(common.JSTokenBox.Load().(string)))
|
||||||
h.Write([]byte(user.LastIP))
|
h.Write([]byte(user.LastIP))
|
||||||
jsToken := hex.EncodeToString(h.Sum(nil))
|
jsToken := hex.EncodeToString(h.Sum(nil))
|
||||||
pi := common.Page{common.GetTitlePhrase("register"), user, header, tList, jsToken}
|
pi := common.Page{header, tList, jsToken}
|
||||||
if common.RunPreRenderHook("pre_render_register", w, r, &user, &pi) {
|
if common.RunPreRenderHook("pre_render_register", w, r, &user, &pi) {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -230,23 +233,27 @@ func AccountRegisterSubmit(w http.ResponseWriter, r *http.Request, user common.U
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: Rename this
|
||||||
func AccountEditCritical(w http.ResponseWriter, r *http.Request, user common.User) common.RouteError {
|
func AccountEditCritical(w http.ResponseWriter, r *http.Request, user common.User) common.RouteError {
|
||||||
header, ferr := common.UserCheck(w, r, &user)
|
header, ferr := common.UserCheck(w, r, &user)
|
||||||
if ferr != nil {
|
if ferr != nil {
|
||||||
return ferr
|
return ferr
|
||||||
}
|
}
|
||||||
|
// TODO: Add a phrase for this
|
||||||
|
header.Title = "Edit Password"
|
||||||
|
|
||||||
pi := common.Page{"Edit Password", user, header, tList, nil}
|
pi := common.Page{header, tList, nil}
|
||||||
if common.RunPreRenderHook("pre_render_account_own_edit_critical", w, r, &user, &pi) {
|
if common.RunPreRenderHook("pre_render_account_own_edit_password", w, r, &user, &pi) {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
err := common.Templates.ExecuteTemplate(w, "account_own_edit.html", pi)
|
err := common.Templates.ExecuteTemplate(w, "account_own_edit_password.html", pi)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return common.InternalError(err, w, r)
|
return common.InternalError(err, w, r)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: Rename this
|
||||||
func AccountEditCriticalSubmit(w http.ResponseWriter, r *http.Request, user common.User) common.RouteError {
|
func AccountEditCriticalSubmit(w http.ResponseWriter, r *http.Request, user common.User) common.RouteError {
|
||||||
_, ferr := common.SimpleUserCheck(w, r, &user)
|
_, ferr := common.SimpleUserCheck(w, r, &user)
|
||||||
if ferr != nil {
|
if ferr != nil {
|
||||||
|
@ -285,12 +292,16 @@ func AccountEditCriticalSubmit(w http.ResponseWriter, r *http.Request, user comm
|
||||||
}
|
}
|
||||||
|
|
||||||
func AccountEditAvatar(w http.ResponseWriter, r *http.Request, user common.User) common.RouteError {
|
func AccountEditAvatar(w http.ResponseWriter, r *http.Request, user common.User) common.RouteError {
|
||||||
headerVars, ferr := common.UserCheck(w, r, &user)
|
header, ferr := common.UserCheck(w, r, &user)
|
||||||
if ferr != nil {
|
if ferr != nil {
|
||||||
return ferr
|
return ferr
|
||||||
}
|
}
|
||||||
|
header.Title = common.GetTitlePhrase("account_avatar")
|
||||||
|
if r.FormValue("updated") == "1" {
|
||||||
|
header.NoticeList = append(header.NoticeList, common.GetNoticePhrase("account_avatar_updated"))
|
||||||
|
}
|
||||||
|
|
||||||
pi := common.Page{"Edit Avatar", user, headerVars, tList, nil}
|
pi := common.Page{header, tList, nil}
|
||||||
if common.RunPreRenderHook("pre_render_account_own_edit_avatar", w, r, &user, &pi) {
|
if common.RunPreRenderHook("pre_render_account_own_edit_avatar", w, r, &user, &pi) {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -302,7 +313,7 @@ func AccountEditAvatar(w http.ResponseWriter, r *http.Request, user common.User)
|
||||||
}
|
}
|
||||||
|
|
||||||
func AccountEditAvatarSubmit(w http.ResponseWriter, r *http.Request, user common.User) common.RouteError {
|
func AccountEditAvatarSubmit(w http.ResponseWriter, r *http.Request, user common.User) common.RouteError {
|
||||||
headerVars, ferr := common.UserCheck(w, r, &user)
|
_, ferr := common.SimpleUserCheck(w, r, &user)
|
||||||
if ferr != nil {
|
if ferr != nil {
|
||||||
return ferr
|
return ferr
|
||||||
}
|
}
|
||||||
|
@ -366,30 +377,21 @@ func AccountEditAvatarSubmit(w http.ResponseWriter, r *http.Request, user common
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return common.InternalError(err, w, r)
|
return common.InternalError(err, w, r)
|
||||||
}
|
}
|
||||||
user.Avatar = "/uploads/avatar_" + strconv.Itoa(user.ID) + "." + ext
|
http.Redirect(w, r, "/user/edit/avatar/?updated=1", http.StatusSeeOther)
|
||||||
headerVars.NoticeList = append(headerVars.NoticeList, common.GetNoticePhrase("account_avatar_updated"))
|
|
||||||
|
|
||||||
pi := common.Page{"Edit Avatar", user, headerVars, tList, nil}
|
|
||||||
if common.RunPreRenderHook("pre_render_account_own_edit_avatar", w, r, &user, &pi) {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
err = common.Templates.ExecuteTemplate(w, "account_own_edit_avatar.html", pi)
|
|
||||||
if err != nil {
|
|
||||||
return common.InternalError(err, w, r)
|
|
||||||
}
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func AccountEditUsername(w http.ResponseWriter, r *http.Request, user common.User) common.RouteError {
|
func AccountEditUsername(w http.ResponseWriter, r *http.Request, user common.User) common.RouteError {
|
||||||
headerVars, ferr := common.UserCheck(w, r, &user)
|
header, ferr := common.UserCheck(w, r, &user)
|
||||||
if ferr != nil {
|
if ferr != nil {
|
||||||
return ferr
|
return ferr
|
||||||
}
|
}
|
||||||
|
header.Title = common.GetTitlePhrase("account_username")
|
||||||
if r.FormValue("updated") == "1" {
|
if r.FormValue("updated") == "1" {
|
||||||
headerVars.NoticeList = append(headerVars.NoticeList, common.GetNoticePhrase("account_username_updated"))
|
header.NoticeList = append(header.NoticeList, common.GetNoticePhrase("account_username_updated"))
|
||||||
}
|
}
|
||||||
|
|
||||||
pi := common.Page{"Edit Username", user, headerVars, tList, user.Name}
|
pi := common.Page{header, tList, user.Name}
|
||||||
if common.RunPreRenderHook("pre_render_account_own_edit_username", w, r, &user, &pi) {
|
if common.RunPreRenderHook("pre_render_account_own_edit_username", w, r, &user, &pi) {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -421,8 +423,7 @@ func AccountEditEmail(w http.ResponseWriter, r *http.Request, user common.User)
|
||||||
if ferr != nil {
|
if ferr != nil {
|
||||||
return ferr
|
return ferr
|
||||||
}
|
}
|
||||||
// TODO: Add a phrase for this
|
header.Title = common.GetTitlePhrase("account_email")
|
||||||
header.Title = "Email Manager"
|
|
||||||
|
|
||||||
emails, err := common.Emails.GetEmailsByUser(&user)
|
emails, err := common.Emails.GetEmailsByUser(&user)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -457,9 +458,9 @@ func AccountEditEmail(w http.ResponseWriter, r *http.Request, user common.User)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Do a session check on this?
|
// TODO: Should we make this an AnonAction so someone can do this without being logged in?
|
||||||
func AccountEditEmailTokenSubmit(w http.ResponseWriter, r *http.Request, user common.User, token string) common.RouteError {
|
func AccountEditEmailTokenSubmit(w http.ResponseWriter, r *http.Request, user common.User, token string) common.RouteError {
|
||||||
headerVars, ferr := common.UserCheck(w, r, &user)
|
header, ferr := common.UserCheck(w, r, &user)
|
||||||
if ferr != nil {
|
if ferr != nil {
|
||||||
return ferr
|
return ferr
|
||||||
}
|
}
|
||||||
|
@ -492,7 +493,7 @@ func AccountEditEmailTokenSubmit(w http.ResponseWriter, r *http.Request, user co
|
||||||
}
|
}
|
||||||
|
|
||||||
// If Email Activation is on, then activate the account while we're here
|
// If Email Activation is on, then activate the account while we're here
|
||||||
if headerVars.Settings["activation_type"] == 2 {
|
if header.Settings["activation_type"] == 2 {
|
||||||
err = user.Activate()
|
err = user.Activate()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return common.InternalError(err, w, r)
|
return common.InternalError(err, w, r)
|
||||||
|
|
|
@ -52,9 +52,10 @@ func Overview(w http.ResponseWriter, r *http.Request, user common.User) common.R
|
||||||
if ferr != nil {
|
if ferr != nil {
|
||||||
return ferr
|
return ferr
|
||||||
}
|
}
|
||||||
|
header.Title = common.GetTitlePhrase("overview")
|
||||||
header.Zone = "overview"
|
header.Zone = "overview"
|
||||||
|
|
||||||
pi := common.Page{common.GetTitlePhrase("overview"), user, header, tList, nil}
|
pi := common.Page{header, tList, nil}
|
||||||
if common.RunPreRenderHook("pre_render_overview", w, r, &user, &pi) {
|
if common.RunPreRenderHook("pre_render_overview", w, r, &user, &pi) {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -70,20 +71,38 @@ func CustomPage(w http.ResponseWriter, r *http.Request, user common.User, name s
|
||||||
if ferr != nil {
|
if ferr != nil {
|
||||||
return ferr
|
return ferr
|
||||||
}
|
}
|
||||||
|
header.Title = common.GetTitlePhrase("page")
|
||||||
header.Zone = "custom_page"
|
header.Zone = "custom_page"
|
||||||
|
|
||||||
|
name = common.SanitiseSingleLine(name)
|
||||||
|
page, err := common.Pages.GetByName(name)
|
||||||
|
if err == nil {
|
||||||
|
header.Title = page.Title
|
||||||
|
pi := common.CustomPagePage{header, page}
|
||||||
|
if common.RunPreRenderHook("pre_render_custom_page", w, r, &user, &pi) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
err := common.RunThemeTemplate(header.Theme.Name, "custom_page", pi, w)
|
||||||
|
if err != nil {
|
||||||
|
return common.InternalError(err, w, r)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
} else if err != sql.ErrNoRows {
|
||||||
|
return common.InternalError(err, w, r)
|
||||||
|
}
|
||||||
|
|
||||||
// ! Is this safe?
|
// ! Is this safe?
|
||||||
if common.Templates.Lookup("page_"+name+".html") == nil {
|
if common.Templates.Lookup("page_"+name+".html") == nil {
|
||||||
return common.NotFound(w, r, header)
|
return common.NotFound(w, r, header)
|
||||||
}
|
}
|
||||||
|
|
||||||
pi := common.Page{common.GetTitlePhrase("page"), user, header, tList, nil}
|
pi := common.Page{header, tList, nil}
|
||||||
// TODO: Pass the page name to the pre-render hook?
|
// TODO: Pass the page name to the pre-render hook?
|
||||||
if common.RunPreRenderHook("pre_render_custom_page", w, r, &user, &pi) {
|
if common.RunPreRenderHook("pre_render_tmpl_page", w, r, &user, &pi) {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
err := common.Templates.ExecuteTemplate(w, "page_"+name+".html", pi)
|
err = common.Templates.ExecuteTemplate(w, "page_"+name+".html", pi)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return common.InternalError(err, w, r)
|
return common.InternalError(err, w, r)
|
||||||
}
|
}
|
||||||
|
@ -96,7 +115,7 @@ type AttachmentStmts struct {
|
||||||
|
|
||||||
var attachmentStmts AttachmentStmts
|
var attachmentStmts AttachmentStmts
|
||||||
|
|
||||||
// TODO: Move these DbInits into a TopicList abstraction
|
// TODO: Abstract this with an attachment store
|
||||||
func init() {
|
func init() {
|
||||||
common.DbInits.Add(func(acc *qgen.Accumulator) error {
|
common.DbInits.Add(func(acc *qgen.Accumulator) error {
|
||||||
attachmentStmts = AttachmentStmts{
|
attachmentStmts = AttachmentStmts{
|
||||||
|
|
|
@ -107,14 +107,25 @@ func analyticsRowsToViewMap(rows *sql.Rows, labelList []int64, viewMap map[int64
|
||||||
return viewMap, rows.Err()
|
return viewMap, rows.Err()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func PreAnalyticsDetail(w http.ResponseWriter, r *http.Request, user *common.User) (*common.BasePanelPage, common.RouteError) {
|
||||||
|
header, stats, ferr := common.PanelUserCheck(w, r, user)
|
||||||
|
if ferr != nil {
|
||||||
|
return nil, ferr
|
||||||
|
}
|
||||||
|
|
||||||
|
header.Title = common.GetTitlePhrase("panel_analytics")
|
||||||
|
header.AddSheet("chartist/chartist.min.css")
|
||||||
|
header.AddScript("chartist/chartist.min.js")
|
||||||
|
header.AddScript("analytics.js")
|
||||||
|
|
||||||
|
return &common.BasePanelPage{header, stats, "analytics", common.ReportForumID}, nil
|
||||||
|
}
|
||||||
|
|
||||||
func AnalyticsViews(w http.ResponseWriter, r *http.Request, user common.User) common.RouteError {
|
func AnalyticsViews(w http.ResponseWriter, r *http.Request, user common.User) common.RouteError {
|
||||||
headerVars, stats, ferr := common.PanelUserCheck(w, r, &user)
|
basePage, ferr := PreAnalyticsDetail(w, r, &user)
|
||||||
if ferr != nil {
|
if ferr != nil {
|
||||||
return ferr
|
return ferr
|
||||||
}
|
}
|
||||||
headerVars.AddSheet("chartist/chartist.min.css")
|
|
||||||
headerVars.AddScript("chartist/chartist.min.js")
|
|
||||||
headerVars.AddScript("analytics.js")
|
|
||||||
|
|
||||||
timeRange, err := analyticsTimeRange(r.FormValue("timeRange"))
|
timeRange, err := analyticsTimeRange(r.FormValue("timeRange"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -143,18 +154,15 @@ func AnalyticsViews(w http.ResponseWriter, r *http.Request, user common.User) co
|
||||||
graph := common.PanelTimeGraph{Series: viewList, Labels: labelList}
|
graph := common.PanelTimeGraph{Series: viewList, Labels: labelList}
|
||||||
common.DebugLogf("graph: %+v\n", graph)
|
common.DebugLogf("graph: %+v\n", graph)
|
||||||
|
|
||||||
pi := common.PanelAnalyticsPage{common.GetTitlePhrase("panel_analytics"), user, headerVars, stats, "analytics", graph, viewItems, timeRange.Range}
|
pi := common.PanelAnalyticsPage{basePage, graph, viewItems, timeRange.Range}
|
||||||
return panelRenderTemplate("panel_analytics_views", w, r, user, &pi)
|
return panelRenderTemplate("panel_analytics_views", w, r, user, &pi)
|
||||||
}
|
}
|
||||||
|
|
||||||
func AnalyticsRouteViews(w http.ResponseWriter, r *http.Request, user common.User, route string) common.RouteError {
|
func AnalyticsRouteViews(w http.ResponseWriter, r *http.Request, user common.User, route string) common.RouteError {
|
||||||
headerVars, stats, ferr := common.PanelUserCheck(w, r, &user)
|
basePage, ferr := PreAnalyticsDetail(w, r, &user)
|
||||||
if ferr != nil {
|
if ferr != nil {
|
||||||
return ferr
|
return ferr
|
||||||
}
|
}
|
||||||
headerVars.AddSheet("chartist/chartist.min.css")
|
|
||||||
headerVars.AddScript("chartist/chartist.min.js")
|
|
||||||
headerVars.AddScript("analytics.js")
|
|
||||||
|
|
||||||
timeRange, err := analyticsTimeRange(r.FormValue("timeRange"))
|
timeRange, err := analyticsTimeRange(r.FormValue("timeRange"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -184,18 +192,15 @@ func AnalyticsRouteViews(w http.ResponseWriter, r *http.Request, user common.Use
|
||||||
graph := common.PanelTimeGraph{Series: viewList, Labels: labelList}
|
graph := common.PanelTimeGraph{Series: viewList, Labels: labelList}
|
||||||
common.DebugLogf("graph: %+v\n", graph)
|
common.DebugLogf("graph: %+v\n", graph)
|
||||||
|
|
||||||
pi := common.PanelAnalyticsRoutePage{common.GetTitlePhrase("panel_analytics"), user, headerVars, stats, "analytics", common.SanitiseSingleLine(route), graph, viewItems, timeRange.Range}
|
pi := common.PanelAnalyticsRoutePage{basePage, common.SanitiseSingleLine(route), graph, viewItems, timeRange.Range}
|
||||||
return panelRenderTemplate("panel_analytics_route_views", w, r, user, &pi)
|
return panelRenderTemplate("panel_analytics_route_views", w, r, user, &pi)
|
||||||
}
|
}
|
||||||
|
|
||||||
func AnalyticsAgentViews(w http.ResponseWriter, r *http.Request, user common.User, agent string) common.RouteError {
|
func AnalyticsAgentViews(w http.ResponseWriter, r *http.Request, user common.User, agent string) common.RouteError {
|
||||||
headerVars, stats, ferr := common.PanelUserCheck(w, r, &user)
|
basePage, ferr := PreAnalyticsDetail(w, r, &user)
|
||||||
if ferr != nil {
|
if ferr != nil {
|
||||||
return ferr
|
return ferr
|
||||||
}
|
}
|
||||||
headerVars.AddSheet("chartist/chartist.min.css")
|
|
||||||
headerVars.AddScript("chartist/chartist.min.js")
|
|
||||||
headerVars.AddScript("analytics.js")
|
|
||||||
|
|
||||||
timeRange, err := analyticsTimeRange(r.FormValue("timeRange"))
|
timeRange, err := analyticsTimeRange(r.FormValue("timeRange"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -231,19 +236,15 @@ func AnalyticsAgentViews(w http.ResponseWriter, r *http.Request, user common.Use
|
||||||
friendlyAgent = agent
|
friendlyAgent = agent
|
||||||
}
|
}
|
||||||
|
|
||||||
pi := common.PanelAnalyticsAgentPage{common.GetTitlePhrase("panel_analytics"), user, headerVars, stats, "analytics", agent, friendlyAgent, graph, timeRange.Range}
|
pi := common.PanelAnalyticsAgentPage{basePage, agent, friendlyAgent, graph, timeRange.Range}
|
||||||
return panelRenderTemplate("panel_analytics_agent_views", w, r, user, &pi)
|
return panelRenderTemplate("panel_analytics_agent_views", w, r, user, &pi)
|
||||||
}
|
}
|
||||||
|
|
||||||
func AnalyticsForumViews(w http.ResponseWriter, r *http.Request, user common.User, sfid string) common.RouteError {
|
func AnalyticsForumViews(w http.ResponseWriter, r *http.Request, user common.User, sfid string) common.RouteError {
|
||||||
headerVars, stats, ferr := common.PanelUserCheck(w, r, &user)
|
basePage, ferr := PreAnalyticsDetail(w, r, &user)
|
||||||
if ferr != nil {
|
if ferr != nil {
|
||||||
return ferr
|
return ferr
|
||||||
}
|
}
|
||||||
headerVars.AddSheet("chartist/chartist.min.css")
|
|
||||||
headerVars.AddScript("chartist/chartist.min.js")
|
|
||||||
headerVars.AddScript("analytics.js")
|
|
||||||
|
|
||||||
timeRange, err := analyticsTimeRange(r.FormValue("timeRange"))
|
timeRange, err := analyticsTimeRange(r.FormValue("timeRange"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return common.LocalError(err.Error(), w, r, user)
|
return common.LocalError(err.Error(), w, r, user)
|
||||||
|
@ -280,18 +281,15 @@ func AnalyticsForumViews(w http.ResponseWriter, r *http.Request, user common.Use
|
||||||
return common.InternalError(err, w, r)
|
return common.InternalError(err, w, r)
|
||||||
}
|
}
|
||||||
|
|
||||||
pi := common.PanelAnalyticsAgentPage{common.GetTitlePhrase("panel_analytics"), user, headerVars, stats, "analytics", sfid, forum.Name, graph, timeRange.Range}
|
pi := common.PanelAnalyticsAgentPage{basePage, sfid, forum.Name, graph, timeRange.Range}
|
||||||
return panelRenderTemplate("panel_analytics_forum_views", w, r, user, &pi)
|
return panelRenderTemplate("panel_analytics_forum_views", w, r, user, &pi)
|
||||||
}
|
}
|
||||||
|
|
||||||
func AnalyticsSystemViews(w http.ResponseWriter, r *http.Request, user common.User, system string) common.RouteError {
|
func AnalyticsSystemViews(w http.ResponseWriter, r *http.Request, user common.User, system string) common.RouteError {
|
||||||
headerVars, stats, ferr := common.PanelUserCheck(w, r, &user)
|
basePage, ferr := PreAnalyticsDetail(w, r, &user)
|
||||||
if ferr != nil {
|
if ferr != nil {
|
||||||
return ferr
|
return ferr
|
||||||
}
|
}
|
||||||
headerVars.AddSheet("chartist/chartist.min.css")
|
|
||||||
headerVars.AddScript("chartist/chartist.min.js")
|
|
||||||
headerVars.AddScript("analytics.js")
|
|
||||||
|
|
||||||
timeRange, err := analyticsTimeRange(r.FormValue("timeRange"))
|
timeRange, err := analyticsTimeRange(r.FormValue("timeRange"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -325,19 +323,15 @@ func AnalyticsSystemViews(w http.ResponseWriter, r *http.Request, user common.Us
|
||||||
friendlySystem = system
|
friendlySystem = system
|
||||||
}
|
}
|
||||||
|
|
||||||
pi := common.PanelAnalyticsAgentPage{common.GetTitlePhrase("panel_analytics"), user, headerVars, stats, "analytics", system, friendlySystem, graph, timeRange.Range}
|
pi := common.PanelAnalyticsAgentPage{basePage, system, friendlySystem, graph, timeRange.Range}
|
||||||
return panelRenderTemplate("panel_analytics_system_views", w, r, user, &pi)
|
return panelRenderTemplate("panel_analytics_system_views", w, r, user, &pi)
|
||||||
}
|
}
|
||||||
|
|
||||||
func AnalyticsLanguageViews(w http.ResponseWriter, r *http.Request, user common.User, lang string) common.RouteError {
|
func AnalyticsLanguageViews(w http.ResponseWriter, r *http.Request, user common.User, lang string) common.RouteError {
|
||||||
headerVars, stats, ferr := common.PanelUserCheck(w, r, &user)
|
basePage, ferr := PreAnalyticsDetail(w, r, &user)
|
||||||
if ferr != nil {
|
if ferr != nil {
|
||||||
return ferr
|
return ferr
|
||||||
}
|
}
|
||||||
headerVars.AddSheet("chartist/chartist.min.css")
|
|
||||||
headerVars.AddScript("chartist/chartist.min.js")
|
|
||||||
headerVars.AddScript("analytics.js")
|
|
||||||
|
|
||||||
timeRange, err := analyticsTimeRange(r.FormValue("timeRange"))
|
timeRange, err := analyticsTimeRange(r.FormValue("timeRange"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return common.LocalError(err.Error(), w, r, user)
|
return common.LocalError(err.Error(), w, r, user)
|
||||||
|
@ -370,19 +364,15 @@ func AnalyticsLanguageViews(w http.ResponseWriter, r *http.Request, user common.
|
||||||
friendlyLang = lang
|
friendlyLang = lang
|
||||||
}
|
}
|
||||||
|
|
||||||
pi := common.PanelAnalyticsAgentPage{common.GetTitlePhrase("panel_analytics"), user, headerVars, stats, "analytics", lang, friendlyLang, graph, timeRange.Range}
|
pi := common.PanelAnalyticsAgentPage{basePage, lang, friendlyLang, graph, timeRange.Range}
|
||||||
return panelRenderTemplate("panel_analytics_lang_views", w, r, user, &pi)
|
return panelRenderTemplate("panel_analytics_lang_views", w, r, user, &pi)
|
||||||
}
|
}
|
||||||
|
|
||||||
func AnalyticsReferrerViews(w http.ResponseWriter, r *http.Request, user common.User, domain string) common.RouteError {
|
func AnalyticsReferrerViews(w http.ResponseWriter, r *http.Request, user common.User, domain string) common.RouteError {
|
||||||
headerVars, stats, ferr := common.PanelUserCheck(w, r, &user)
|
basePage, ferr := PreAnalyticsDetail(w, r, &user)
|
||||||
if ferr != nil {
|
if ferr != nil {
|
||||||
return ferr
|
return ferr
|
||||||
}
|
}
|
||||||
headerVars.AddSheet("chartist/chartist.min.css")
|
|
||||||
headerVars.AddScript("chartist/chartist.min.js")
|
|
||||||
headerVars.AddScript("analytics.js")
|
|
||||||
|
|
||||||
timeRange, err := analyticsTimeRange(r.FormValue("timeRange"))
|
timeRange, err := analyticsTimeRange(r.FormValue("timeRange"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return common.LocalError(err.Error(), w, r, user)
|
return common.LocalError(err.Error(), w, r, user)
|
||||||
|
@ -409,19 +399,15 @@ func AnalyticsReferrerViews(w http.ResponseWriter, r *http.Request, user common.
|
||||||
graph := common.PanelTimeGraph{Series: viewList, Labels: labelList}
|
graph := common.PanelTimeGraph{Series: viewList, Labels: labelList}
|
||||||
common.DebugLogf("graph: %+v\n", graph)
|
common.DebugLogf("graph: %+v\n", graph)
|
||||||
|
|
||||||
pi := common.PanelAnalyticsAgentPage{common.GetTitlePhrase("panel_analytics"), user, headerVars, stats, "analytics", common.SanitiseSingleLine(domain), "", graph, timeRange.Range}
|
pi := common.PanelAnalyticsAgentPage{basePage, common.SanitiseSingleLine(domain), "", graph, timeRange.Range}
|
||||||
return panelRenderTemplate("panel_analytics_referrer_views", w, r, user, &pi)
|
return panelRenderTemplate("panel_analytics_referrer_views", w, r, user, &pi)
|
||||||
}
|
}
|
||||||
|
|
||||||
func AnalyticsTopics(w http.ResponseWriter, r *http.Request, user common.User) common.RouteError {
|
func AnalyticsTopics(w http.ResponseWriter, r *http.Request, user common.User) common.RouteError {
|
||||||
headerVars, stats, ferr := common.PanelUserCheck(w, r, &user)
|
basePage, ferr := PreAnalyticsDetail(w, r, &user)
|
||||||
if ferr != nil {
|
if ferr != nil {
|
||||||
return ferr
|
return ferr
|
||||||
}
|
}
|
||||||
headerVars.AddSheet("chartist/chartist.min.css")
|
|
||||||
headerVars.AddScript("chartist/chartist.min.js")
|
|
||||||
headerVars.AddScript("analytics.js")
|
|
||||||
|
|
||||||
timeRange, err := analyticsTimeRange(r.FormValue("timeRange"))
|
timeRange, err := analyticsTimeRange(r.FormValue("timeRange"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return common.LocalError(err.Error(), w, r, user)
|
return common.LocalError(err.Error(), w, r, user)
|
||||||
|
@ -449,19 +435,15 @@ func AnalyticsTopics(w http.ResponseWriter, r *http.Request, user common.User) c
|
||||||
graph := common.PanelTimeGraph{Series: viewList, Labels: labelList}
|
graph := common.PanelTimeGraph{Series: viewList, Labels: labelList}
|
||||||
common.DebugLogf("graph: %+v\n", graph)
|
common.DebugLogf("graph: %+v\n", graph)
|
||||||
|
|
||||||
pi := common.PanelAnalyticsPage{common.GetTitlePhrase("panel_analytics"), user, headerVars, stats, "analytics", graph, viewItems, timeRange.Range}
|
pi := common.PanelAnalyticsPage{basePage, graph, viewItems, timeRange.Range}
|
||||||
return panelRenderTemplate("panel_analytics_topics", w, r, user, &pi)
|
return panelRenderTemplate("panel_analytics_topics", w, r, user, &pi)
|
||||||
}
|
}
|
||||||
|
|
||||||
func AnalyticsPosts(w http.ResponseWriter, r *http.Request, user common.User) common.RouteError {
|
func AnalyticsPosts(w http.ResponseWriter, r *http.Request, user common.User) common.RouteError {
|
||||||
headerVars, stats, ferr := common.PanelUserCheck(w, r, &user)
|
basePage, ferr := PreAnalyticsDetail(w, r, &user)
|
||||||
if ferr != nil {
|
if ferr != nil {
|
||||||
return ferr
|
return ferr
|
||||||
}
|
}
|
||||||
headerVars.AddSheet("chartist/chartist.min.css")
|
|
||||||
headerVars.AddScript("chartist/chartist.min.js")
|
|
||||||
headerVars.AddScript("analytics.js")
|
|
||||||
|
|
||||||
timeRange, err := analyticsTimeRange(r.FormValue("timeRange"))
|
timeRange, err := analyticsTimeRange(r.FormValue("timeRange"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return common.LocalError(err.Error(), w, r, user)
|
return common.LocalError(err.Error(), w, r, user)
|
||||||
|
@ -489,7 +471,7 @@ func AnalyticsPosts(w http.ResponseWriter, r *http.Request, user common.User) co
|
||||||
graph := common.PanelTimeGraph{Series: viewList, Labels: labelList}
|
graph := common.PanelTimeGraph{Series: viewList, Labels: labelList}
|
||||||
common.DebugLogf("graph: %+v\n", graph)
|
common.DebugLogf("graph: %+v\n", graph)
|
||||||
|
|
||||||
pi := common.PanelAnalyticsPage{common.GetTitlePhrase("panel_analytics"), user, headerVars, stats, "analytics", graph, viewItems, timeRange.Range}
|
pi := common.PanelAnalyticsPage{basePage, graph, viewItems, timeRange.Range}
|
||||||
return panelRenderTemplate("panel_analytics_posts", w, r, user, &pi)
|
return panelRenderTemplate("panel_analytics_posts", w, r, user, &pi)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -515,10 +497,11 @@ func analyticsRowsToNameMap(rows *sql.Rows) (map[string]int, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func AnalyticsForums(w http.ResponseWriter, r *http.Request, user common.User) common.RouteError {
|
func AnalyticsForums(w http.ResponseWriter, r *http.Request, user common.User) common.RouteError {
|
||||||
headerVars, stats, ferr := common.PanelUserCheck(w, r, &user)
|
header, stats, ferr := common.PanelUserCheck(w, r, &user)
|
||||||
if ferr != nil {
|
if ferr != nil {
|
||||||
return ferr
|
return ferr
|
||||||
}
|
}
|
||||||
|
header.Title = common.GetTitlePhrase("panel_analytics")
|
||||||
|
|
||||||
timeRange, err := analyticsTimeRange(r.FormValue("timeRange"))
|
timeRange, err := analyticsTimeRange(r.FormValue("timeRange"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -554,15 +537,17 @@ func AnalyticsForums(w http.ResponseWriter, r *http.Request, user common.User) c
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pi := common.PanelAnalyticsAgentsPage{common.GetTitlePhrase("panel_analytics"), user, headerVars, stats, "analytics", forumItems, timeRange.Range}
|
pi := common.PanelAnalyticsAgentsPage{&common.BasePanelPage{header, stats, "analytics", common.ReportForumID}, forumItems, timeRange.Range}
|
||||||
return panelRenderTemplate("panel_analytics_forums", w, r, user, &pi)
|
return panelRenderTemplate("panel_analytics_forums", w, r, user, &pi)
|
||||||
}
|
}
|
||||||
|
|
||||||
func AnalyticsRoutes(w http.ResponseWriter, r *http.Request, user common.User) common.RouteError {
|
func AnalyticsRoutes(w http.ResponseWriter, r *http.Request, user common.User) common.RouteError {
|
||||||
headerVars, stats, ferr := common.PanelUserCheck(w, r, &user)
|
header, stats, ferr := common.PanelUserCheck(w, r, &user)
|
||||||
if ferr != nil {
|
if ferr != nil {
|
||||||
return ferr
|
return ferr
|
||||||
}
|
}
|
||||||
|
header.Title = common.GetTitlePhrase("panel_analytics")
|
||||||
|
|
||||||
timeRange, err := analyticsTimeRange(r.FormValue("timeRange"))
|
timeRange, err := analyticsTimeRange(r.FormValue("timeRange"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return common.LocalError(err.Error(), w, r, user)
|
return common.LocalError(err.Error(), w, r, user)
|
||||||
|
@ -588,15 +573,17 @@ func AnalyticsRoutes(w http.ResponseWriter, r *http.Request, user common.User) c
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pi := common.PanelAnalyticsRoutesPage{common.GetTitlePhrase("panel_analytics"), user, headerVars, stats, "analytics", routeItems, timeRange.Range}
|
pi := common.PanelAnalyticsRoutesPage{&common.BasePanelPage{header, stats, "analytics", common.ReportForumID}, routeItems, timeRange.Range}
|
||||||
return panelRenderTemplate("panel_analytics_routes", w, r, user, &pi)
|
return panelRenderTemplate("panel_analytics_routes", w, r, user, &pi)
|
||||||
}
|
}
|
||||||
|
|
||||||
func AnalyticsAgents(w http.ResponseWriter, r *http.Request, user common.User) common.RouteError {
|
func AnalyticsAgents(w http.ResponseWriter, r *http.Request, user common.User) common.RouteError {
|
||||||
headerVars, stats, ferr := common.PanelUserCheck(w, r, &user)
|
header, stats, ferr := common.PanelUserCheck(w, r, &user)
|
||||||
if ferr != nil {
|
if ferr != nil {
|
||||||
return ferr
|
return ferr
|
||||||
}
|
}
|
||||||
|
header.Title = common.GetTitlePhrase("panel_analytics")
|
||||||
|
|
||||||
timeRange, err := analyticsTimeRange(r.FormValue("timeRange"))
|
timeRange, err := analyticsTimeRange(r.FormValue("timeRange"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return common.LocalError(err.Error(), w, r, user)
|
return common.LocalError(err.Error(), w, r, user)
|
||||||
|
@ -627,15 +614,17 @@ func AnalyticsAgents(w http.ResponseWriter, r *http.Request, user common.User) c
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pi := common.PanelAnalyticsAgentsPage{common.GetTitlePhrase("panel_analytics"), user, headerVars, stats, "analytics", agentItems, timeRange.Range}
|
pi := common.PanelAnalyticsAgentsPage{&common.BasePanelPage{header, stats, "analytics", common.ReportForumID}, agentItems, timeRange.Range}
|
||||||
return panelRenderTemplate("panel_analytics_agents", w, r, user, &pi)
|
return panelRenderTemplate("panel_analytics_agents", w, r, user, &pi)
|
||||||
}
|
}
|
||||||
|
|
||||||
func AnalyticsSystems(w http.ResponseWriter, r *http.Request, user common.User) common.RouteError {
|
func AnalyticsSystems(w http.ResponseWriter, r *http.Request, user common.User) common.RouteError {
|
||||||
headerVars, stats, ferr := common.PanelUserCheck(w, r, &user)
|
header, stats, ferr := common.PanelUserCheck(w, r, &user)
|
||||||
if ferr != nil {
|
if ferr != nil {
|
||||||
return ferr
|
return ferr
|
||||||
}
|
}
|
||||||
|
header.Title = common.GetTitlePhrase("panel_analytics")
|
||||||
|
|
||||||
timeRange, err := analyticsTimeRange(r.FormValue("timeRange"))
|
timeRange, err := analyticsTimeRange(r.FormValue("timeRange"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return common.LocalError(err.Error(), w, r, user)
|
return common.LocalError(err.Error(), w, r, user)
|
||||||
|
@ -666,15 +655,17 @@ func AnalyticsSystems(w http.ResponseWriter, r *http.Request, user common.User)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pi := common.PanelAnalyticsAgentsPage{common.GetTitlePhrase("panel_analytics"), user, headerVars, stats, "analytics", systemItems, timeRange.Range}
|
pi := common.PanelAnalyticsAgentsPage{&common.BasePanelPage{header, stats, "analytics", common.ReportForumID}, systemItems, timeRange.Range}
|
||||||
return panelRenderTemplate("panel_analytics_systems", w, r, user, &pi)
|
return panelRenderTemplate("panel_analytics_systems", w, r, user, &pi)
|
||||||
}
|
}
|
||||||
|
|
||||||
func AnalyticsLanguages(w http.ResponseWriter, r *http.Request, user common.User) common.RouteError {
|
func AnalyticsLanguages(w http.ResponseWriter, r *http.Request, user common.User) common.RouteError {
|
||||||
headerVars, stats, ferr := common.PanelUserCheck(w, r, &user)
|
header, stats, ferr := common.PanelUserCheck(w, r, &user)
|
||||||
if ferr != nil {
|
if ferr != nil {
|
||||||
return ferr
|
return ferr
|
||||||
}
|
}
|
||||||
|
header.Title = common.GetTitlePhrase("panel_analytics")
|
||||||
|
|
||||||
timeRange, err := analyticsTimeRange(r.FormValue("timeRange"))
|
timeRange, err := analyticsTimeRange(r.FormValue("timeRange"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return common.LocalError(err.Error(), w, r, user)
|
return common.LocalError(err.Error(), w, r, user)
|
||||||
|
@ -706,15 +697,17 @@ func AnalyticsLanguages(w http.ResponseWriter, r *http.Request, user common.User
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pi := common.PanelAnalyticsAgentsPage{common.GetTitlePhrase("panel_analytics"), user, headerVars, stats, "analytics", langItems, timeRange.Range}
|
pi := common.PanelAnalyticsAgentsPage{&common.BasePanelPage{header, stats, "analytics", common.ReportForumID}, langItems, timeRange.Range}
|
||||||
return panelRenderTemplate("panel_analytics_langs", w, r, user, &pi)
|
return panelRenderTemplate("panel_analytics_langs", w, r, user, &pi)
|
||||||
}
|
}
|
||||||
|
|
||||||
func AnalyticsReferrers(w http.ResponseWriter, r *http.Request, user common.User) common.RouteError {
|
func AnalyticsReferrers(w http.ResponseWriter, r *http.Request, user common.User) common.RouteError {
|
||||||
headerVars, stats, ferr := common.PanelUserCheck(w, r, &user)
|
header, stats, ferr := common.PanelUserCheck(w, r, &user)
|
||||||
if ferr != nil {
|
if ferr != nil {
|
||||||
return ferr
|
return ferr
|
||||||
}
|
}
|
||||||
|
header.Title = common.GetTitlePhrase("panel_analytics")
|
||||||
|
|
||||||
timeRange, err := analyticsTimeRange(r.FormValue("timeRange"))
|
timeRange, err := analyticsTimeRange(r.FormValue("timeRange"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return common.LocalError(err.Error(), w, r, user)
|
return common.LocalError(err.Error(), w, r, user)
|
||||||
|
@ -740,6 +733,6 @@ func AnalyticsReferrers(w http.ResponseWriter, r *http.Request, user common.User
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pi := common.PanelAnalyticsAgentsPage{common.GetTitlePhrase("panel_analytics"), user, headerVars, stats, "analytics", refItems, timeRange.Range}
|
pi := common.PanelAnalyticsAgentsPage{&common.BasePanelPage{header, stats, "analytics", common.ReportForumID}, refItems, timeRange.Range}
|
||||||
return panelRenderTemplate("panel_analytics_referrers", w, r, user, &pi)
|
return panelRenderTemplate("panel_analytics_referrers", w, r, user, &pi)
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,10 +11,11 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
func Backups(w http.ResponseWriter, r *http.Request, user common.User, backupURL string) common.RouteError {
|
func Backups(w http.ResponseWriter, r *http.Request, user common.User, backupURL string) common.RouteError {
|
||||||
headerVars, stats, ferr := common.PanelUserCheck(w, r, &user)
|
header, stats, ferr := common.PanelUserCheck(w, r, &user)
|
||||||
if ferr != nil {
|
if ferr != nil {
|
||||||
return ferr
|
return ferr
|
||||||
}
|
}
|
||||||
|
header.Title = common.GetTitlePhrase("panel_backups")
|
||||||
|
|
||||||
if backupURL != "" {
|
if backupURL != "" {
|
||||||
// We don't want them trying to break out of this directory, it shouldn't hurt since it's a super admin, but it's always good to practice good security hygiene, especially if this is one of many instances on a managed server not controlled by the superadmin/s
|
// We don't want them trying to break out of this directory, it shouldn't hurt since it's a super admin, but it's always good to practice good security hygiene, especially if this is one of many instances on a managed server not controlled by the superadmin/s
|
||||||
|
@ -24,7 +25,7 @@ func Backups(w http.ResponseWriter, r *http.Request, user common.User, backupURL
|
||||||
if ext == ".sql" {
|
if ext == ".sql" {
|
||||||
info, err := os.Stat("./backups/" + backupURL)
|
info, err := os.Stat("./backups/" + backupURL)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return common.NotFound(w, r, headerVars)
|
return common.NotFound(w, r, header)
|
||||||
}
|
}
|
||||||
// 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
|
// 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-Disposition", "attachment; filename=gosora_backup.sql")
|
||||||
|
@ -33,7 +34,7 @@ func Backups(w http.ResponseWriter, r *http.Request, user common.User, backupURL
|
||||||
http.ServeFile(w, r, "./backups/"+backupURL)
|
http.ServeFile(w, r, "./backups/"+backupURL)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
return common.NotFound(w, r, headerVars)
|
return common.NotFound(w, r, header)
|
||||||
}
|
}
|
||||||
|
|
||||||
var backupList []common.BackupItem
|
var backupList []common.BackupItem
|
||||||
|
@ -49,6 +50,6 @@ func Backups(w http.ResponseWriter, r *http.Request, user common.User, backupURL
|
||||||
backupList = append(backupList, common.BackupItem{backupFile.Name(), backupFile.ModTime()})
|
backupList = append(backupList, common.BackupItem{backupFile.Name(), backupFile.ModTime()})
|
||||||
}
|
}
|
||||||
|
|
||||||
pi := common.PanelBackupPage{common.GetTitlePhrase("panel_backups"), user, headerVars, stats, "backups", backupList}
|
pi := common.PanelBackupPage{&common.BasePanelPage{header, stats, "backups", common.ReportForumID}, backupList}
|
||||||
return panelRenderTemplate("panel_backups", w, r, user, &pi)
|
return panelRenderTemplate("panel_backups", w, r, user, &pi)
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,10 +11,11 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
func Debug(w http.ResponseWriter, r *http.Request, user common.User) common.RouteError {
|
func Debug(w http.ResponseWriter, r *http.Request, user common.User) common.RouteError {
|
||||||
headerVars, stats, ferr := common.PanelUserCheck(w, r, &user)
|
header, stats, ferr := common.PanelUserCheck(w, r, &user)
|
||||||
if ferr != nil {
|
if ferr != nil {
|
||||||
return ferr
|
return ferr
|
||||||
}
|
}
|
||||||
|
header.Title = common.GetTitlePhrase("panel_debug")
|
||||||
|
|
||||||
goVersion := runtime.Version()
|
goVersion := runtime.Version()
|
||||||
dbVersion := qgen.Builder.DbVersion()
|
dbVersion := qgen.Builder.DbVersion()
|
||||||
|
@ -37,6 +38,6 @@ func Debug(w http.ResponseWriter, r *http.Request, user common.User) common.Rout
|
||||||
// Disk I/O?
|
// Disk I/O?
|
||||||
// TODO: Fetch the adapter from Builder rather than getting it from a global?
|
// TODO: Fetch the adapter from Builder rather than getting it from a global?
|
||||||
|
|
||||||
pi := common.PanelDebugPage{common.GetTitlePhrase("panel_debug"), user, headerVars, stats, "debug", goVersion, dbVersion, uptime, openConnCount, qgen.Builder.GetAdapter().GetName()}
|
pi := common.PanelDebugPage{&common.BasePanelPage{header, stats, "debug", common.ReportForumID}, goVersion, dbVersion, uptime, openConnCount, qgen.Builder.GetAdapter().GetName()}
|
||||||
return panelRenderTemplate("panel_debug", w, r, user, &pi)
|
return panelRenderTemplate("panel_debug", w, r, user, &pi)
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,13 +11,14 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
func Forums(w http.ResponseWriter, r *http.Request, user common.User) common.RouteError {
|
func Forums(w http.ResponseWriter, r *http.Request, user common.User) common.RouteError {
|
||||||
headerVars, stats, ferr := common.PanelUserCheck(w, r, &user)
|
header, stats, ferr := common.PanelUserCheck(w, r, &user)
|
||||||
if ferr != nil {
|
if ferr != nil {
|
||||||
return ferr
|
return ferr
|
||||||
}
|
}
|
||||||
if !user.Perms.ManageForums {
|
if !user.Perms.ManageForums {
|
||||||
return common.NoPermissions(w, r, user)
|
return common.NoPermissions(w, r, user)
|
||||||
}
|
}
|
||||||
|
header.Title = common.GetTitlePhrase("panel_forums")
|
||||||
|
|
||||||
// TODO: Paginate this?
|
// TODO: Paginate this?
|
||||||
var forumList []interface{}
|
var forumList []interface{}
|
||||||
|
@ -38,14 +39,14 @@ func Forums(w http.ResponseWriter, r *http.Request, user common.User) common.Rou
|
||||||
}
|
}
|
||||||
|
|
||||||
if r.FormValue("created") == "1" {
|
if r.FormValue("created") == "1" {
|
||||||
headerVars.NoticeList = append(headerVars.NoticeList, common.GetNoticePhrase("panel_forum_created"))
|
header.NoticeList = append(header.NoticeList, common.GetNoticePhrase("panel_forum_created"))
|
||||||
} else if r.FormValue("deleted") == "1" {
|
} else if r.FormValue("deleted") == "1" {
|
||||||
headerVars.NoticeList = append(headerVars.NoticeList, common.GetNoticePhrase("panel_forum_deleted"))
|
header.NoticeList = append(header.NoticeList, common.GetNoticePhrase("panel_forum_deleted"))
|
||||||
} else if r.FormValue("updated") == "1" {
|
} else if r.FormValue("updated") == "1" {
|
||||||
headerVars.NoticeList = append(headerVars.NoticeList, common.GetNoticePhrase("panel_forum_updated"))
|
header.NoticeList = append(header.NoticeList, common.GetNoticePhrase("panel_forum_updated"))
|
||||||
}
|
}
|
||||||
|
|
||||||
pi := common.PanelPage{common.GetTitlePhrase("panel_forums"), user, headerVars, stats, "forums", forumList, nil}
|
pi := common.PanelPage{&common.BasePanelPage{header, stats, "forums", common.ReportForumID}, forumList, nil}
|
||||||
return panelRenderTemplate("panel_forums", w, r, user, &pi)
|
return panelRenderTemplate("panel_forums", w, r, user, &pi)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -75,13 +76,14 @@ func ForumsCreateSubmit(w http.ResponseWriter, r *http.Request, user common.User
|
||||||
|
|
||||||
// TODO: Revamp this
|
// TODO: Revamp this
|
||||||
func ForumsDelete(w http.ResponseWriter, r *http.Request, user common.User, sfid string) common.RouteError {
|
func ForumsDelete(w http.ResponseWriter, r *http.Request, user common.User, sfid string) common.RouteError {
|
||||||
headerVars, stats, ferr := common.PanelUserCheck(w, r, &user)
|
header, stats, ferr := common.PanelUserCheck(w, r, &user)
|
||||||
if ferr != nil {
|
if ferr != nil {
|
||||||
return ferr
|
return ferr
|
||||||
}
|
}
|
||||||
if !user.Perms.ManageForums {
|
if !user.Perms.ManageForums {
|
||||||
return common.NoPermissions(w, r, user)
|
return common.NoPermissions(w, r, user)
|
||||||
}
|
}
|
||||||
|
header.Title = common.GetTitlePhrase("panel_delete_forum")
|
||||||
|
|
||||||
fid, err := strconv.Atoi(sfid)
|
fid, err := strconv.Atoi(sfid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -99,7 +101,7 @@ func ForumsDelete(w http.ResponseWriter, r *http.Request, user common.User, sfid
|
||||||
confirmMsg := "Are you sure you want to delete the '" + forum.Name + "' forum?"
|
confirmMsg := "Are you sure you want to delete the '" + forum.Name + "' forum?"
|
||||||
yousure := common.AreYouSure{"/panel/forums/delete/submit/" + strconv.Itoa(fid), confirmMsg}
|
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.BasePanelPage{header, stats, "forums", common.ReportForumID}, tList, yousure}
|
||||||
if common.RunPreRenderHook("pre_render_panel_delete_forum", w, r, &user, &pi) {
|
if common.RunPreRenderHook("pre_render_panel_delete_forum", w, r, &user, &pi) {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -136,13 +138,14 @@ func ForumsDeleteSubmit(w http.ResponseWriter, r *http.Request, user common.User
|
||||||
}
|
}
|
||||||
|
|
||||||
func ForumsEdit(w http.ResponseWriter, r *http.Request, user common.User, sfid string) common.RouteError {
|
func ForumsEdit(w http.ResponseWriter, r *http.Request, user common.User, sfid string) common.RouteError {
|
||||||
headerVars, stats, ferr := common.PanelUserCheck(w, r, &user)
|
header, stats, ferr := common.PanelUserCheck(w, r, &user)
|
||||||
if ferr != nil {
|
if ferr != nil {
|
||||||
return ferr
|
return ferr
|
||||||
}
|
}
|
||||||
if !user.Perms.ManageForums {
|
if !user.Perms.ManageForums {
|
||||||
return common.NoPermissions(w, r, user)
|
return common.NoPermissions(w, r, user)
|
||||||
}
|
}
|
||||||
|
header.Title = common.GetTitlePhrase("panel_edit_forum")
|
||||||
|
|
||||||
fid, err := strconv.Atoi(sfid)
|
fid, err := strconv.Atoi(sfid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -180,10 +183,10 @@ func ForumsEdit(w http.ResponseWriter, r *http.Request, user common.User, sfid s
|
||||||
}
|
}
|
||||||
|
|
||||||
if r.FormValue("updated") == "1" {
|
if r.FormValue("updated") == "1" {
|
||||||
headerVars.NoticeList = append(headerVars.NoticeList, common.GetNoticePhrase("panel_forum_updated"))
|
header.NoticeList = append(header.NoticeList, common.GetNoticePhrase("panel_forum_updated"))
|
||||||
}
|
}
|
||||||
|
|
||||||
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.BasePanelPage{header, stats, "forums", common.ReportForumID}, forum.ID, forum.Name, forum.Desc, forum.Active, forum.Preset, gplist}
|
||||||
if common.RunPreRenderHook("pre_render_panel_edit_forum", w, r, &user, &pi) {
|
if common.RunPreRenderHook("pre_render_panel_edit_forum", w, r, &user, &pi) {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -294,13 +297,14 @@ func forumPermsExtractDash(paramList string) (fid int, gid int, err error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func ForumsEditPermsAdvance(w http.ResponseWriter, r *http.Request, user common.User, paramList string) common.RouteError {
|
func ForumsEditPermsAdvance(w http.ResponseWriter, r *http.Request, user common.User, paramList string) common.RouteError {
|
||||||
headerVars, stats, ferr := common.PanelUserCheck(w, r, &user)
|
header, stats, ferr := common.PanelUserCheck(w, r, &user)
|
||||||
if ferr != nil {
|
if ferr != nil {
|
||||||
return ferr
|
return ferr
|
||||||
}
|
}
|
||||||
if !user.Perms.ManageForums {
|
if !user.Perms.ManageForums {
|
||||||
return common.NoPermissions(w, r, user)
|
return common.NoPermissions(w, r, user)
|
||||||
}
|
}
|
||||||
|
header.Title = common.GetTitlePhrase("panel_edit_forum")
|
||||||
|
|
||||||
fid, gid, err := forumPermsExtractDash(paramList)
|
fid, gid, err := forumPermsExtractDash(paramList)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -346,10 +350,10 @@ func ForumsEditPermsAdvance(w http.ResponseWriter, r *http.Request, user common.
|
||||||
addNameLangToggle("MoveTopic", forumPerms.MoveTopic)
|
addNameLangToggle("MoveTopic", forumPerms.MoveTopic)
|
||||||
|
|
||||||
if r.FormValue("updated") == "1" {
|
if r.FormValue("updated") == "1" {
|
||||||
headerVars.NoticeList = append(headerVars.NoticeList, common.GetNoticePhrase("panel_forums_perms_updated"))
|
header.NoticeList = append(header.NoticeList, common.GetNoticePhrase("panel_forums_perms_updated"))
|
||||||
}
|
}
|
||||||
|
|
||||||
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.BasePanelPage{header, stats, "forums", common.ReportForumID}, forum.ID, gid, forum.Name, forum.Desc, forum.Active, forum.Preset, formattedPermList}
|
||||||
if common.RunPreRenderHook("pre_render_panel_edit_forum", w, r, &user, &pi) {
|
if common.RunPreRenderHook("pre_render_panel_edit_forum", w, r, &user, &pi) {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,10 +11,11 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
func LogsRegs(w http.ResponseWriter, r *http.Request, user common.User) common.RouteError {
|
func LogsRegs(w http.ResponseWriter, r *http.Request, user common.User) common.RouteError {
|
||||||
headerVars, stats, ferr := common.PanelUserCheck(w, r, &user)
|
header, stats, ferr := common.PanelUserCheck(w, r, &user)
|
||||||
if ferr != nil {
|
if ferr != nil {
|
||||||
return ferr
|
return ferr
|
||||||
}
|
}
|
||||||
|
header.Title = common.GetTitlePhrase("panel_registration_logs")
|
||||||
|
|
||||||
logCount := common.RegLogs.GlobalCount()
|
logCount := common.RegLogs.GlobalCount()
|
||||||
page, _ := strconv.Atoi(r.FormValue("page"))
|
page, _ := strconv.Atoi(r.FormValue("page"))
|
||||||
|
@ -31,7 +32,7 @@ func LogsRegs(w http.ResponseWriter, r *http.Request, user common.User) common.R
|
||||||
}
|
}
|
||||||
|
|
||||||
pageList := common.Paginate(logCount, perPage, 5)
|
pageList := common.Paginate(logCount, perPage, 5)
|
||||||
pi := common.PanelRegLogsPage{common.GetTitlePhrase("panel_registration_logs"), user, headerVars, stats, "logs", llist, common.Paginator{pageList, page, lastPage}}
|
pi := common.PanelRegLogsPage{&common.BasePanelPage{header, stats, "logs", common.ReportForumID}, llist, common.Paginator{pageList, page, lastPage}}
|
||||||
return panelRenderTemplate("panel_reglogs", w, r, user, &pi)
|
return panelRenderTemplate("panel_reglogs", w, r, user, &pi)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -101,10 +102,11 @@ func modlogsElementType(action string, elementType string, elementID int, actor
|
||||||
}
|
}
|
||||||
|
|
||||||
func LogsMod(w http.ResponseWriter, r *http.Request, user common.User) common.RouteError {
|
func LogsMod(w http.ResponseWriter, r *http.Request, user common.User) common.RouteError {
|
||||||
headerVars, stats, ferr := common.PanelUserCheck(w, r, &user)
|
header, stats, ferr := common.PanelUserCheck(w, r, &user)
|
||||||
if ferr != nil {
|
if ferr != nil {
|
||||||
return ferr
|
return ferr
|
||||||
}
|
}
|
||||||
|
header.Title = common.GetTitlePhrase("panel_mod_logs")
|
||||||
|
|
||||||
logCount := common.ModLogs.GlobalCount()
|
logCount := common.ModLogs.GlobalCount()
|
||||||
page, _ := strconv.Atoi(r.FormValue("page"))
|
page, _ := strconv.Atoi(r.FormValue("page"))
|
||||||
|
@ -123,15 +125,16 @@ func LogsMod(w http.ResponseWriter, r *http.Request, user common.User) common.Ro
|
||||||
}
|
}
|
||||||
|
|
||||||
pageList := common.Paginate(logCount, perPage, 5)
|
pageList := common.Paginate(logCount, perPage, 5)
|
||||||
pi := common.PanelLogsPage{common.GetTitlePhrase("panel_mod_logs"), user, headerVars, stats, "logs", llist, common.Paginator{pageList, page, lastPage}}
|
pi := common.PanelLogsPage{&common.BasePanelPage{header, stats, "logs", common.ReportForumID}, llist, common.Paginator{pageList, page, lastPage}}
|
||||||
return panelRenderTemplate("panel_modlogs", w, r, user, &pi)
|
return panelRenderTemplate("panel_modlogs", w, r, user, &pi)
|
||||||
}
|
}
|
||||||
|
|
||||||
func LogsAdmin(w http.ResponseWriter, r *http.Request, user common.User) common.RouteError {
|
func LogsAdmin(w http.ResponseWriter, r *http.Request, user common.User) common.RouteError {
|
||||||
headerVars, stats, ferr := common.PanelUserCheck(w, r, &user)
|
header, stats, ferr := common.PanelUserCheck(w, r, &user)
|
||||||
if ferr != nil {
|
if ferr != nil {
|
||||||
return ferr
|
return ferr
|
||||||
}
|
}
|
||||||
|
header.Title = common.GetTitlePhrase("panel_admin_logs")
|
||||||
|
|
||||||
logCount := common.ModLogs.GlobalCount()
|
logCount := common.ModLogs.GlobalCount()
|
||||||
page, _ := strconv.Atoi(r.FormValue("page"))
|
page, _ := strconv.Atoi(r.FormValue("page"))
|
||||||
|
@ -150,6 +153,6 @@ func LogsAdmin(w http.ResponseWriter, r *http.Request, user common.User) common.
|
||||||
}
|
}
|
||||||
|
|
||||||
pageList := common.Paginate(logCount, perPage, 5)
|
pageList := common.Paginate(logCount, perPage, 5)
|
||||||
pi := common.PanelLogsPage{common.GetTitlePhrase("panel_admin_logs"), user, headerVars, stats, "logs", llist, common.Paginator{pageList, page, lastPage}}
|
pi := common.PanelLogsPage{&common.BasePanelPage{header, stats, "logs", common.ReportForumID}, llist, common.Paginator{pageList, page, lastPage}}
|
||||||
return panelRenderTemplate("panel_adminlogs", w, r, user, &pi)
|
return panelRenderTemplate("panel_adminlogs", w, r, user, &pi)
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,156 @@
|
||||||
|
package panel
|
||||||
|
|
||||||
|
import (
|
||||||
|
"database/sql"
|
||||||
|
"net/http"
|
||||||
|
"strconv"
|
||||||
|
|
||||||
|
"../../common"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Pages(w http.ResponseWriter, r *http.Request, user common.User) common.RouteError {
|
||||||
|
header, stats, ferr := common.PanelUserCheck(w, r, &user)
|
||||||
|
if ferr != nil {
|
||||||
|
return ferr
|
||||||
|
}
|
||||||
|
header.Title = common.GetTitlePhrase("panel_pages")
|
||||||
|
|
||||||
|
if r.FormValue("created") == "1" {
|
||||||
|
header.AddNotice("panel_page_created")
|
||||||
|
} else if r.FormValue("deleted") == "1" {
|
||||||
|
header.AddNotice("panel_page_deleted")
|
||||||
|
}
|
||||||
|
|
||||||
|
pageCount := common.Pages.GlobalCount()
|
||||||
|
page, _ := strconv.Atoi(r.FormValue("page"))
|
||||||
|
perPage := 10
|
||||||
|
offset, page, lastPage := common.PageOffset(pageCount, page, perPage)
|
||||||
|
|
||||||
|
cPages, err := common.Pages.GetOffset(offset, perPage)
|
||||||
|
if err != nil {
|
||||||
|
return common.InternalError(err, w, r)
|
||||||
|
}
|
||||||
|
|
||||||
|
pageList := common.Paginate(pageCount, perPage, 5)
|
||||||
|
pi := common.PanelCustomPagesPage{&common.BasePanelPage{header, stats, "pages", common.ReportForumID}, cPages, common.Paginator{pageList, page, lastPage}}
|
||||||
|
return panelRenderTemplate("panel_pages", w, r, user, &pi)
|
||||||
|
}
|
||||||
|
|
||||||
|
func PagesCreateSubmit(w http.ResponseWriter, r *http.Request, user common.User) common.RouteError {
|
||||||
|
_, ferr := common.SimplePanelUserCheck(w, r, &user)
|
||||||
|
if ferr != nil {
|
||||||
|
return ferr
|
||||||
|
}
|
||||||
|
|
||||||
|
pname := r.PostFormValue("name")
|
||||||
|
if pname == "" {
|
||||||
|
return common.LocalError("No name was provided for this page", w, r, user)
|
||||||
|
}
|
||||||
|
ptitle := r.PostFormValue("title")
|
||||||
|
if ptitle == "" {
|
||||||
|
return common.LocalError("No title was provided for this page", w, r, user)
|
||||||
|
}
|
||||||
|
pbody := r.PostFormValue("body")
|
||||||
|
if pbody == "" {
|
||||||
|
return common.LocalError("No body was provided for this page", w, r, user)
|
||||||
|
}
|
||||||
|
|
||||||
|
page := common.BlankCustomPage()
|
||||||
|
page.Name = pname
|
||||||
|
page.Title = ptitle
|
||||||
|
page.Body = pbody
|
||||||
|
_, err := page.Create()
|
||||||
|
if err != nil {
|
||||||
|
return common.InternalError(err, w, r)
|
||||||
|
}
|
||||||
|
|
||||||
|
http.Redirect(w, r, "/panel/pages/?created=1", http.StatusSeeOther)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func PagesEdit(w http.ResponseWriter, r *http.Request, user common.User, spid string) common.RouteError {
|
||||||
|
header, stats, ferr := common.PanelUserCheck(w, r, &user)
|
||||||
|
if ferr != nil {
|
||||||
|
return ferr
|
||||||
|
}
|
||||||
|
header.Title = common.GetTitlePhrase("panel_pages_edit")
|
||||||
|
|
||||||
|
if r.FormValue("updated") == "1" {
|
||||||
|
header.AddNotice("panel_page_updated")
|
||||||
|
}
|
||||||
|
|
||||||
|
pid, err := strconv.Atoi(spid)
|
||||||
|
if err != nil {
|
||||||
|
return common.LocalError("Page ID needs to be an integer", w, r, user)
|
||||||
|
}
|
||||||
|
|
||||||
|
page, err := common.Pages.Get(pid)
|
||||||
|
if err == sql.ErrNoRows {
|
||||||
|
return common.NotFound(w, r, header)
|
||||||
|
} else if err != nil {
|
||||||
|
return common.InternalError(err, w, r)
|
||||||
|
}
|
||||||
|
|
||||||
|
pi := common.PanelCustomPageEditPage{&common.BasePanelPage{header, stats, "pages", common.ReportForumID}, page}
|
||||||
|
return panelRenderTemplate("panel_pages_edit", w, r, user, &pi)
|
||||||
|
}
|
||||||
|
|
||||||
|
func PagesEditSubmit(w http.ResponseWriter, r *http.Request, user common.User, spid string) common.RouteError {
|
||||||
|
_, ferr := common.SimplePanelUserCheck(w, r, &user)
|
||||||
|
if ferr != nil {
|
||||||
|
return ferr
|
||||||
|
}
|
||||||
|
|
||||||
|
pid, err := strconv.Atoi(spid)
|
||||||
|
if err != nil {
|
||||||
|
return common.LocalError("Page ID needs to be an integer", w, r, user)
|
||||||
|
}
|
||||||
|
|
||||||
|
pname := r.PostFormValue("name")
|
||||||
|
if pname == "" {
|
||||||
|
return common.LocalError("No name was provided for this page", w, r, user)
|
||||||
|
}
|
||||||
|
ptitle := r.PostFormValue("title")
|
||||||
|
if ptitle == "" {
|
||||||
|
return common.LocalError("No title was provided for this page", w, r, user)
|
||||||
|
}
|
||||||
|
pbody := r.PostFormValue("body")
|
||||||
|
if pbody == "" {
|
||||||
|
return common.LocalError("No body was provided for this page", w, r, user)
|
||||||
|
}
|
||||||
|
|
||||||
|
page, err := common.Pages.Get(pid)
|
||||||
|
if err != nil {
|
||||||
|
return common.NotFound(w, r, nil)
|
||||||
|
}
|
||||||
|
page.Name = pname
|
||||||
|
page.Title = ptitle
|
||||||
|
page.Body = pbody
|
||||||
|
err = page.Commit()
|
||||||
|
if err != nil {
|
||||||
|
return common.InternalError(err, w, r)
|
||||||
|
}
|
||||||
|
|
||||||
|
http.Redirect(w, r, "/panel/pages/?updated=1", http.StatusSeeOther)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func PagesDeleteSubmit(w http.ResponseWriter, r *http.Request, user common.User, spid string) common.RouteError {
|
||||||
|
_, ferr := common.SimplePanelUserCheck(w, r, &user)
|
||||||
|
if ferr != nil {
|
||||||
|
return ferr
|
||||||
|
}
|
||||||
|
|
||||||
|
pid, err := strconv.Atoi(spid)
|
||||||
|
if err != nil {
|
||||||
|
return common.LocalError("Page ID needs to be an integer", w, r, user)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = common.Pages.Delete(pid)
|
||||||
|
if err != nil {
|
||||||
|
return common.InternalError(err, w, r)
|
||||||
|
}
|
||||||
|
|
||||||
|
http.Redirect(w, r, "/panel/pages/?deleted=1", http.StatusSeeOther)
|
||||||
|
return nil
|
||||||
|
}
|
|
@ -18,6 +18,7 @@ func Settings(w http.ResponseWriter, r *http.Request, user common.User) common.R
|
||||||
if !user.Perms.EditSettings {
|
if !user.Perms.EditSettings {
|
||||||
return common.NoPermissions(w, r, user)
|
return common.NoPermissions(w, r, user)
|
||||||
}
|
}
|
||||||
|
header.Title = common.GetTitlePhrase("panel_settings")
|
||||||
|
|
||||||
settings, err := header.Settings.BypassGetAll()
|
settings, err := header.Settings.BypassGetAll()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -48,7 +49,7 @@ func Settings(w http.ResponseWriter, r *http.Request, user common.User) common.R
|
||||||
settingList = append(settingList, &common.PanelSetting{setting, common.GetSettingPhrase(setting.Name)})
|
settingList = append(settingList, &common.PanelSetting{setting, common.GetSettingPhrase(setting.Name)})
|
||||||
}
|
}
|
||||||
|
|
||||||
pi := common.PanelPage{common.GetTitlePhrase("panel_settings"), user, header, stats, "settings", tList, settingList}
|
pi := common.PanelPage{&common.BasePanelPage{header, stats, "settings", common.ReportForumID}, tList, settingList}
|
||||||
return panelRenderTemplate("panel_settings", w, r, user, &pi)
|
return panelRenderTemplate("panel_settings", w, r, user, &pi)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -90,7 +91,7 @@ func SettingEdit(w http.ResponseWriter, r *http.Request, user common.User, sname
|
||||||
}
|
}
|
||||||
|
|
||||||
pSetting := &common.PanelSetting{setting, common.GetSettingPhrase(setting.Name)}
|
pSetting := &common.PanelSetting{setting, common.GetSettingPhrase(setting.Name)}
|
||||||
pi := common.PanelSettingPage{header, stats, "settings", itemList, pSetting}
|
pi := common.PanelSettingPage{&common.BasePanelPage{header, stats, "settings", common.ReportForumID}, itemList, pSetting}
|
||||||
return panelRenderTemplate("panel_setting", w, r, user, &pi)
|
return panelRenderTemplate("panel_setting", w, r, user, &pi)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -33,7 +33,7 @@ func ViewProfile(w http.ResponseWriter, r *http.Request, user common.User) commo
|
||||||
return ferr
|
return ferr
|
||||||
}
|
}
|
||||||
// TODO: Preload this?
|
// TODO: Preload this?
|
||||||
header.AddScript("profile.css")
|
header.AddSheet(header.Theme.Name + "/profile.css")
|
||||||
|
|
||||||
var err error
|
var err error
|
||||||
var replyCreatedAt time.Time
|
var replyCreatedAt time.Time
|
||||||
|
|
|
@ -22,6 +22,7 @@ func ReportSubmit(w http.ResponseWriter, r *http.Request, user common.User, site
|
||||||
}
|
}
|
||||||
itemType := r.FormValue("type")
|
itemType := r.FormValue("type")
|
||||||
|
|
||||||
|
// TODO: Localise these titles and bodies
|
||||||
var title, content string
|
var title, content string
|
||||||
if itemType == "reply" {
|
if itemType == "reply" {
|
||||||
reply, err := common.Rstore.Get(itemID)
|
reply, err := common.Rstore.Get(itemID)
|
||||||
|
|
|
@ -1,8 +0,0 @@
|
||||||
{{template "header.html" . }}
|
|
||||||
<main>
|
|
||||||
<div class="rowblock">
|
|
||||||
<div class="rowitem">{{.Title}}</div>
|
|
||||||
</div>
|
|
||||||
<div class="rowblock">{{.Something}}</div>
|
|
||||||
</main>
|
|
||||||
{{template "footer.html" . }}
|
|
|
@ -0,0 +1,10 @@
|
||||||
|
{{template "header.html" . }}
|
||||||
|
<main>
|
||||||
|
<div class="rowblock rowhead">
|
||||||
|
<div class="rowitem"><h1>{{.Page.Title}}</h1></div>
|
||||||
|
</div>
|
||||||
|
<div class="rowblock">
|
||||||
|
<div class="rowitem passive rowmsg">{{.Page.Body}}</div>
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
|
{{template "footer.html" . }}
|
|
@ -4,7 +4,7 @@
|
||||||
<div class="rowitem"><h1>{{lang "error_head"}}</h1></div>
|
<div class="rowitem"><h1>{{lang "error_head"}}</h1></div>
|
||||||
</div>
|
</div>
|
||||||
<div class="rowblock">
|
<div class="rowblock">
|
||||||
<div class="rowitem passive rowmsg">{{.Something}}</div>
|
<div class="rowitem passive rowmsg">{{.Message}}</div>
|
||||||
</div>
|
</div>
|
||||||
</main>
|
</main>
|
||||||
{{template "footer.html" . }}
|
{{template "footer.html" . }}
|
||||||
|
|
|
@ -101,12 +101,14 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="rowitem topic_right passive datarow {{if .Sticky}}topic_sticky{{else if .IsClosed}}topic_closed{{end}}">
|
<div class="rowitem topic_right passive datarow {{if .Sticky}}topic_sticky{{else if .IsClosed}}topic_closed{{end}}">
|
||||||
|
<div class="topic_right_inside">
|
||||||
<a href="{{.LastUser.Link}}"><img src="{{.LastUser.Avatar}}" height="64" alt="{{.LastUser.Name}}'s Avatar" title="{{.LastUser.Name}}'s Avatar" /></a>
|
<a href="{{.LastUser.Link}}"><img src="{{.LastUser.Avatar}}" height="64" alt="{{.LastUser.Name}}'s Avatar" title="{{.LastUser.Name}}'s Avatar" /></a>
|
||||||
<span>
|
<span>
|
||||||
<a href="{{.LastUser.Link}}" class="lastName" style="font-size: 14px;" title="{{.LastUser.Name}}">{{.LastUser.Name}}</a><br>
|
<a href="{{.LastUser.Link}}" class="lastName" style="font-size: 14px;" title="{{.LastUser.Name}}">{{.LastUser.Name}}</a><br>
|
||||||
<span class="rowsmall lastReplyAt">{{.RelativeLastReplyAt}}</span>
|
<span class="rowsmall lastReplyAt">{{.RelativeLastReplyAt}}</span>
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
</div>{{else}}<div class="rowitem passive rowmsg">{{lang "forum_no_topics"}}{{if .CurrentUser.Perms.CreateTopic}} <a href="/topics/create/{{.Forum.ID}}">{{lang "forum_start_one"}}</a>{{end}}</div>{{end}}
|
</div>{{else}}<div class="rowitem passive rowmsg">{{lang "forum_no_topics"}}{{if .CurrentUser.Perms.CreateTopic}} <a href="/topics/create/{{.Forum.ID}}">{{lang "forum_start_one"}}</a>{{end}}</div>{{end}}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{{template "header.html" . }}
|
{{template "header.html" . }}
|
||||||
<div class="colstack panel_stack">
|
<div class="colstack panel_stack">
|
||||||
{{template "panel-menu.html" . }}
|
{{template "panel_menu.html" . }}
|
||||||
|
|
||||||
<main class="colstack_right">
|
<main class="colstack_right">
|
||||||
<div class="colstack_item colstack_head">
|
<div class="colstack_item colstack_head">
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
{{template "header.html" . }}
|
{{template "header.html" . }}
|
||||||
|
|
||||||
<div class="colstack panel_stack">
|
<div class="colstack panel_stack">
|
||||||
{{template "panel-menu.html" . }}
|
{{template "panel_menu.html" . }}
|
||||||
<script type="text/javascript">
|
<script type="text/javascript">
|
||||||
var formVars = {'perm_preset': ['can_moderate','can_post','read_only','no_access','default','custom']};
|
var formVars = {'perm_preset': ['can_moderate','can_post','read_only','no_access','default','custom']};
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -9,7 +9,7 @@
|
||||||
<div class="rowitem passive"><a>Promotions</a></div>
|
<div class="rowitem passive"><a>Promotions</a></div>
|
||||||
<div class="rowitem passive"><a href="/panel/groups/edit/perms/{{.ID}}">Permissions</a></div>
|
<div class="rowitem passive"><a href="/panel/groups/edit/perms/{{.ID}}">Permissions</a></div>
|
||||||
</div>
|
</div>
|
||||||
{{template "panel-inner-menu.html" . }}
|
{{template "panel_inner_menu.html" . }}
|
||||||
</nav>
|
</nav>
|
||||||
<main class="colstack_right">
|
<main class="colstack_right">
|
||||||
<div class="colstack_item colstack_head">
|
<div class="colstack_item colstack_head">
|
||||||
|
|
|
@ -9,7 +9,7 @@
|
||||||
<div class="rowitem passive"><a>{{lang "panel_group_menu_promotions"}}</a></div>
|
<div class="rowitem passive"><a>{{lang "panel_group_menu_promotions"}}</a></div>
|
||||||
<div class="rowitem passive"><a href="/panel/groups/edit/perms/{{.ID}}">{{lang "panel_group_menu_permissions"}}</a></div>
|
<div class="rowitem passive"><a href="/panel/groups/edit/perms/{{.ID}}">{{lang "panel_group_menu_permissions"}}</a></div>
|
||||||
</div>
|
</div>
|
||||||
{{template "panel-inner-menu.html" . }}
|
{{template "panel_inner_menu.html" . }}
|
||||||
</nav>
|
</nav>
|
||||||
<main class="colstack_right">
|
<main class="colstack_right">
|
||||||
<div class="colstack_item colstack_head">
|
<div class="colstack_item colstack_head">
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
{{template "header.html" . }}
|
{{template "header.html" . }}
|
||||||
<div class="colstack panel_stack">
|
<div class="colstack panel_stack">
|
||||||
|
|
||||||
{{template "panel-menu.html" . }}
|
{{template "panel_menu.html" . }}
|
||||||
<main class="colstack_right">
|
<main class="colstack_right">
|
||||||
<div class="colstack_item colstack_head">
|
<div class="colstack_item colstack_head">
|
||||||
<div class="rowitem"><h1>{{lang "panel_user_head"}}</h1></div>
|
<div class="rowitem"><h1>{{lang "panel_user_head"}}</h1></div>
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{{template "header.html" . }}
|
{{template "header.html" . }}
|
||||||
<div class="colstack panel_stack">
|
<div class="colstack panel_stack">
|
||||||
{{template "panel-menu.html" . }}
|
{{template "panel_menu.html" . }}
|
||||||
<main class="colstack_right">
|
<main class="colstack_right">
|
||||||
<div class="colstack_item colstack_head">
|
<div class="colstack_item colstack_head">
|
||||||
<div class="rowitem"><h1>{{lang "panel_logs_administration_head"}}</h1></div>
|
<div class="rowitem"><h1>{{lang "panel_logs_administration_head"}}</h1></div>
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{{template "header.html" . }}
|
{{template "header.html" . }}
|
||||||
<div class="colstack panel_stack">
|
<div class="colstack panel_stack">
|
||||||
{{template "panel-menu.html" . }}
|
{{template "panel_menu.html" . }}
|
||||||
<main id="panel_analytics_right" class="colstack_right">
|
<main id="panel_analytics_right" class="colstack_right">
|
||||||
<form id="timeRangeForm" name="timeRangeForm" action="/panel/analytics/agent/{{.Agent}}" method="get">
|
<form id="timeRangeForm" name="timeRangeForm" action="/panel/analytics/agent/{{.Agent}}" method="get">
|
||||||
<div class="colstack_item colstack_head">
|
<div class="colstack_item colstack_head">
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{{template "header.html" . }}
|
{{template "header.html" . }}
|
||||||
<div class="colstack panel_stack">
|
<div class="colstack panel_stack">
|
||||||
{{template "panel-menu.html" . }}
|
{{template "panel_menu.html" . }}
|
||||||
<main id="panel_analytics_right" class="colstack_right">
|
<main id="panel_analytics_right" class="colstack_right">
|
||||||
<form id="timeRangeForm" name="timeRangeForm" action="/panel/analytics/agents/" method="get">
|
<form id="timeRangeForm" name="timeRangeForm" action="/panel/analytics/agents/" method="get">
|
||||||
<div class="colstack_item colstack_head">
|
<div class="colstack_item colstack_head">
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{{template "header.html" . }}
|
{{template "header.html" . }}
|
||||||
<div class="colstack panel_stack">
|
<div class="colstack panel_stack">
|
||||||
{{template "panel-menu.html" . }}
|
{{template "panel_menu.html" . }}
|
||||||
<main id="panel_analytics_right" class="colstack_right">
|
<main id="panel_analytics_right" class="colstack_right">
|
||||||
<form id="timeRangeForm" name="timeRangeForm" action="/panel/analytics/forum/{{.Agent}}" method="get">
|
<form id="timeRangeForm" name="timeRangeForm" action="/panel/analytics/forum/{{.Agent}}" method="get">
|
||||||
<div class="colstack_item colstack_head">
|
<div class="colstack_item colstack_head">
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{{template "header.html" . }}
|
{{template "header.html" . }}
|
||||||
<div class="colstack panel_stack">
|
<div class="colstack panel_stack">
|
||||||
{{template "panel-menu.html" . }}
|
{{template "panel_menu.html" . }}
|
||||||
<main id="panel_analytics_right" class="colstack_right">
|
<main id="panel_analytics_right" class="colstack_right">
|
||||||
<form id="timeRangeForm" name="timeRangeForm" action="/panel/analytics/forums/" method="get">
|
<form id="timeRangeForm" name="timeRangeForm" action="/panel/analytics/forums/" method="get">
|
||||||
<div class="colstack_item colstack_head">
|
<div class="colstack_item colstack_head">
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{{template "header.html" . }}
|
{{template "header.html" . }}
|
||||||
<div class="colstack panel_stack">
|
<div class="colstack panel_stack">
|
||||||
{{template "panel-menu.html" . }}
|
{{template "panel_menu.html" . }}
|
||||||
<main id="panel_analytics_right" class="colstack_right">
|
<main id="panel_analytics_right" class="colstack_right">
|
||||||
<form id="timeRangeForm" name="timeRangeForm" action="/panel/analytics/lang/{{.Agent}}" method="get">
|
<form id="timeRangeForm" name="timeRangeForm" action="/panel/analytics/lang/{{.Agent}}" method="get">
|
||||||
<div class="colstack_item colstack_head">
|
<div class="colstack_item colstack_head">
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{{template "header.html" . }}
|
{{template "header.html" . }}
|
||||||
<div class="colstack panel_stack">
|
<div class="colstack panel_stack">
|
||||||
{{template "panel-menu.html" . }}
|
{{template "panel_menu.html" . }}
|
||||||
<main id="panel_analytics_right" class="colstack_right">
|
<main id="panel_analytics_right" class="colstack_right">
|
||||||
<form id="timeRangeForm" name="timeRangeForm" action="/panel/analytics/langs/" method="get">
|
<form id="timeRangeForm" name="timeRangeForm" action="/panel/analytics/langs/" method="get">
|
||||||
<div class="colstack_item colstack_head">
|
<div class="colstack_item colstack_head">
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{{template "header.html" . }}
|
{{template "header.html" . }}
|
||||||
<div class="colstack panel_stack">
|
<div class="colstack panel_stack">
|
||||||
{{template "panel-menu.html" . }}
|
{{template "panel_menu.html" . }}
|
||||||
<main id="panel_analytics_right" class="colstack_right">
|
<main id="panel_analytics_right" class="colstack_right">
|
||||||
<form id="timeRangeForm" name="timeRangeForm" action="/panel/analytics/posts/" method="get">
|
<form id="timeRangeForm" name="timeRangeForm" action="/panel/analytics/posts/" method="get">
|
||||||
<div class="colstack_item colstack_head">
|
<div class="colstack_item colstack_head">
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{{template "header.html" . }}
|
{{template "header.html" . }}
|
||||||
<div class="colstack panel_stack">
|
<div class="colstack panel_stack">
|
||||||
{{template "panel-menu.html" . }}
|
{{template "panel_menu.html" . }}
|
||||||
<main id="panel_analytics_right" class="colstack_right">
|
<main id="panel_analytics_right" class="colstack_right">
|
||||||
<form id="timeRangeForm" name="timeRangeForm" action="/panel/analytics/referrer/{{.Agent}}" method="get">
|
<form id="timeRangeForm" name="timeRangeForm" action="/panel/analytics/referrer/{{.Agent}}" method="get">
|
||||||
<div class="colstack_item colstack_head">
|
<div class="colstack_item colstack_head">
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{{template "header.html" . }}
|
{{template "header.html" . }}
|
||||||
<div class="colstack panel_stack">
|
<div class="colstack panel_stack">
|
||||||
{{template "panel-menu.html" . }}
|
{{template "panel_menu.html" . }}
|
||||||
<main id="panel_analytics_right" class="colstack_right">
|
<main id="panel_analytics_right" class="colstack_right">
|
||||||
<form id="timeRangeForm" name="timeRangeForm" action="/panel/analytics/referrers/" method="get">
|
<form id="timeRangeForm" name="timeRangeForm" action="/panel/analytics/referrers/" method="get">
|
||||||
<div class="colstack_item colstack_head">
|
<div class="colstack_item colstack_head">
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{{template "header.html" . }}
|
{{template "header.html" . }}
|
||||||
<div class="colstack panel_stack">
|
<div class="colstack panel_stack">
|
||||||
{{template "panel-menu.html" . }}
|
{{template "panel_menu.html" . }}
|
||||||
<main id="panel_analytics_right" class="colstack_right">
|
<main id="panel_analytics_right" class="colstack_right">
|
||||||
<form id="timeRangeForm" name="timeRangeForm" action="/panel/analytics/route/{{.Route}}" method="get">
|
<form id="timeRangeForm" name="timeRangeForm" action="/panel/analytics/route/{{.Route}}" method="get">
|
||||||
<div class="colstack_item colstack_head">
|
<div class="colstack_item colstack_head">
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{{template "header.html" . }}
|
{{template "header.html" . }}
|
||||||
<div class="colstack panel_stack">
|
<div class="colstack panel_stack">
|
||||||
{{template "panel-menu.html" . }}
|
{{template "panel_menu.html" . }}
|
||||||
<main id="panel_analytics_right" class="colstack_right">
|
<main id="panel_analytics_right" class="colstack_right">
|
||||||
<form id="timeRangeForm" name="timeRangeForm" action="/panel/analytics/routes/" method="get">
|
<form id="timeRangeForm" name="timeRangeForm" action="/panel/analytics/routes/" method="get">
|
||||||
<div class="colstack_item colstack_head">
|
<div class="colstack_item colstack_head">
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{{template "header.html" . }}
|
{{template "header.html" . }}
|
||||||
<div class="colstack panel_stack">
|
<div class="colstack panel_stack">
|
||||||
{{template "panel-menu.html" . }}
|
{{template "panel_menu.html" . }}
|
||||||
<main id="panel_analytics_right" class="colstack_right">
|
<main id="panel_analytics_right" class="colstack_right">
|
||||||
<form id="timeRangeForm" name="timeRangeForm" action="/panel/analytics/system/{{.Agent}}" method="get">
|
<form id="timeRangeForm" name="timeRangeForm" action="/panel/analytics/system/{{.Agent}}" method="get">
|
||||||
<div class="colstack_item colstack_head">
|
<div class="colstack_item colstack_head">
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{{template "header.html" . }}
|
{{template "header.html" . }}
|
||||||
<div class="colstack panel_stack">
|
<div class="colstack panel_stack">
|
||||||
{{template "panel-menu.html" . }}
|
{{template "panel_menu.html" . }}
|
||||||
<main id="panel_analytics_right" class="colstack_right">
|
<main id="panel_analytics_right" class="colstack_right">
|
||||||
<form id="timeRangeForm" name="timeRangeForm" action="/panel/analytics/systems/" method="get">
|
<form id="timeRangeForm" name="timeRangeForm" action="/panel/analytics/systems/" method="get">
|
||||||
<div class="colstack_item colstack_head">
|
<div class="colstack_item colstack_head">
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{{template "header.html" . }}
|
{{template "header.html" . }}
|
||||||
<div class="colstack panel_stack">
|
<div class="colstack panel_stack">
|
||||||
{{template "panel-menu.html" . }}
|
{{template "panel_menu.html" . }}
|
||||||
<main id="panel_analytics_right" class="colstack_right">
|
<main id="panel_analytics_right" class="colstack_right">
|
||||||
<form id="timeRangeForm" name="timeRangeForm" action="/panel/analytics/topics/" method="get">
|
<form id="timeRangeForm" name="timeRangeForm" action="/panel/analytics/topics/" method="get">
|
||||||
<div class="colstack_item colstack_head">
|
<div class="colstack_item colstack_head">
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{{template "header.html" . }}
|
{{template "header.html" . }}
|
||||||
<div class="colstack panel_stack">
|
<div class="colstack panel_stack">
|
||||||
{{template "panel-menu.html" . }}
|
{{template "panel_menu.html" . }}
|
||||||
<main id="panel_analytics_right" class="colstack_right">
|
<main id="panel_analytics_right" class="colstack_right">
|
||||||
<form id="timeRangeForm" name="timeRangeForm" action="/panel/analytics/views/" method="get">
|
<form id="timeRangeForm" name="timeRangeForm" action="/panel/analytics/views/" method="get">
|
||||||
<div class="colstack_item colstack_head">
|
<div class="colstack_item colstack_head">
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{{template "header.html" . }}
|
{{template "header.html" . }}
|
||||||
<div class="colstack panel_stack">
|
<div class="colstack panel_stack">
|
||||||
{{template "panel-menu.html" . }}
|
{{template "panel_menu.html" . }}
|
||||||
<main class="colstack_right">
|
<main class="colstack_right">
|
||||||
<div class="colstack_item colstack_head">
|
<div class="colstack_item colstack_head">
|
||||||
<div class="rowitem"><h1>{{lang "panel_backups_head"}}</h1></div>
|
<div class="rowitem"><h1>{{lang "panel_backups_head"}}</h1></div>
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{{template "header.html" . }}
|
{{template "header.html" . }}
|
||||||
<div class="colstack panel_stack">
|
<div class="colstack panel_stack">
|
||||||
{{template "panel-menu.html" . }}
|
{{template "panel_menu.html" . }}
|
||||||
<main id="panel_dashboard_right" class="colstack_right">
|
<main id="panel_dashboard_right" class="colstack_right">
|
||||||
<div class="colstack_item colstack_head">
|
<div class="colstack_item colstack_head">
|
||||||
<div class="rowitem"><a>{{lang "panel_dashboard_head"}}</a></div>
|
<div class="rowitem"><a>{{lang "panel_dashboard_head"}}</a></div>
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{{template "header.html" . }}
|
{{template "header.html" . }}
|
||||||
<div class="colstack panel_stack">
|
<div class="colstack panel_stack">
|
||||||
{{template "panel-menu.html" . }}
|
{{template "panel_menu.html" . }}
|
||||||
<main id="panel_dashboard_right" class="colstack_right">
|
<main id="panel_dashboard_right" class="colstack_right">
|
||||||
<div class="colstack_item colstack_head">
|
<div class="colstack_item colstack_head">
|
||||||
<div class="rowitem"><a>{{lang "panel_debug_head"}}</a></div>
|
<div class="rowitem"><a>{{lang "panel_debug_head"}}</a></div>
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
{{template "header.html" . }}
|
{{template "header.html" . }}
|
||||||
|
|
||||||
<div class="colstack panel_stack">
|
<div class="colstack panel_stack">
|
||||||
{{template "panel-menu.html" . }}
|
{{template "panel_menu.html" . }}
|
||||||
<script>var formVars = {
|
<script>var formVars = {
|
||||||
'forum_active': ['Hide','Show'],
|
'forum_active': ['Hide','Show'],
|
||||||
'forum_preset': ['all','announce','members','staff','admins','archive','custom']};
|
'forum_preset': ['all','announce','members','staff','admins','archive','custom']};
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
{{template "header.html" . }}
|
{{template "header.html" . }}
|
||||||
<div class="colstack panel_stack">
|
<div class="colstack panel_stack">
|
||||||
|
|
||||||
{{template "panel-menu.html" . }}
|
{{template "panel_menu.html" . }}
|
||||||
<main class="colstack_right">
|
<main class="colstack_right">
|
||||||
<div class="colstack_item colstack_head">
|
<div class="colstack_item colstack_head">
|
||||||
<div class="rowitem"><h1>{{lang "panel_groups_head"}}</h1></div>
|
<div class="rowitem"><h1>{{lang "panel_groups_head"}}</h1></div>
|
||||||
|
|
|
@ -11,6 +11,9 @@
|
||||||
{{if .CurrentUser.Perms.ManageForums}}<div class="rowitem passive">
|
{{if .CurrentUser.Perms.ManageForums}}<div class="rowitem passive">
|
||||||
<a href="/panel/forums/">{{lang "panel_menu_forums"}}</a> <a class="menu_stats" href="#">({{.Stats.Forums}})</a>
|
<a href="/panel/forums/">{{lang "panel_menu_forums"}}</a> <a class="menu_stats" href="#">({{.Stats.Forums}})</a>
|
||||||
</div>{{end}}
|
</div>{{end}}
|
||||||
|
<div class="rowitem passive">
|
||||||
|
<a href="/panel/pages/">{{lang "panel_menu_pages"}}</a> <a class="menu_stats" href="#">({{.Stats.Pages}})</a>
|
||||||
|
</div>
|
||||||
{{if .CurrentUser.Perms.EditSettings}}<div class="rowitem passive">
|
{{if .CurrentUser.Perms.EditSettings}}<div class="rowitem passive">
|
||||||
<a href="/panel/settings/">{{lang "panel_menu_settings"}}</a> <a class="menu_stats" href="#">({{.Stats.Settings}})</a>
|
<a href="/panel/settings/">{{lang "panel_menu_settings"}}</a> <a class="menu_stats" href="#">({{.Stats.Settings}})</a>
|
||||||
</div>
|
</div>
|
||||||
|
@ -61,7 +64,7 @@
|
||||||
</div>
|
</div>
|
||||||
{{end}}
|
{{end}}
|
||||||
<div class="rowitem passive">
|
<div class="rowitem passive">
|
||||||
<a href="/forum/1">{{lang "panel_menu_reports"}}</a> <a class="menu_stats" href="#">({{.Stats.Reports}})</a>
|
<a href="/forum/{{.ReportForumID}}">{{lang "panel_menu_reports"}}</a> <a class="menu_stats" href="#">({{.Stats.Reports}})</a>
|
||||||
</div>
|
</div>
|
||||||
<div class="rowitem passive">
|
<div class="rowitem passive">
|
||||||
<a href="/panel/logs/mod/">{{lang "panel_menu_logs"}}</a>
|
<a href="/panel/logs/mod/">{{lang "panel_menu_logs"}}</a>
|
|
@ -1 +1 @@
|
||||||
<nav class="colstack_left" aria-label="The control panel menu">{{template "panel-inner-menu.html" . }}</nav>
|
<nav class="colstack_left" aria-label="The control panel menu">{{template "panel_inner_menu.html" . }}</nav>
|
|
@ -1,6 +1,6 @@
|
||||||
{{template "header.html" . }}
|
{{template "header.html" . }}
|
||||||
<div class="colstack panel_stack">
|
<div class="colstack panel_stack">
|
||||||
{{template "panel-menu.html" . }}
|
{{template "panel_menu.html" . }}
|
||||||
<main class="colstack_right">
|
<main class="colstack_right">
|
||||||
<div class="colstack_item colstack_head">
|
<div class="colstack_item colstack_head">
|
||||||
<div class="rowitem"><h1>{{lang "panel_logs_moderation_head"}}</h1></div>
|
<div class="rowitem"><h1>{{lang "panel_logs_moderation_head"}}</h1></div>
|
||||||
|
|
|
@ -0,0 +1,49 @@
|
||||||
|
{{template "header.html" . }}
|
||||||
|
<div id="panel_page_list" class="colstack panel_stack">
|
||||||
|
{{template "panel_menu.html" . }}
|
||||||
|
<main class="colstack_right">
|
||||||
|
<div class="colstack_item colstack_head">
|
||||||
|
<div class="rowitem"><h1>{{lang "panel_pages_head"}}</h1></div>
|
||||||
|
</div>
|
||||||
|
<div id="panel_settings" class="colstack_item rowlist">
|
||||||
|
{{range .ItemList}}
|
||||||
|
<div class="rowitem panel_compactrow">
|
||||||
|
<a href="/panel/pages/edit/{{.ID}}" class="panel_upshift">{{.Title}}</a> <a href="/pages/{{.Name}}">[{{.Name}}]</a>
|
||||||
|
<span class="panel_buttons">
|
||||||
|
<a href="/panel/pages/edit/{{.ID}}" class="panel_tag panel_right_button edit_button" aria-label="{{lang "panel_pages_edit_button_aria"}}"></a>
|
||||||
|
<a href="/panel/pages/delete/submit/{{.ID}}?session={{$.CurrentUser.Session}}" class="panel_tag panel_right_button delete_button" aria-label="{{lang "panel_pages_delete_button_aria"}}"></a>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
{{else}}
|
||||||
|
<div class="rowitem rowmsg">
|
||||||
|
<a>{{lang "panel_pages_no_pages"}}</a>
|
||||||
|
</div>
|
||||||
|
{{end}}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="colstack_item colstack_head">
|
||||||
|
<div class="rowitem"><h1>{{lang "panel_pages_create_head"}}</h1></div>
|
||||||
|
</div>
|
||||||
|
<div class="colstack_item the_form">
|
||||||
|
<form action="/panel/pages/create/submit/?session={{.CurrentUser.Session}}" method="post">
|
||||||
|
<div class="formrow">
|
||||||
|
<div class="formitem formlabel"><a>{{lang "panel_pages_create_name"}}</a></div>
|
||||||
|
<div class="formitem"><input name="name" type="text" placeholder="{{lang "panel_pages_create_name_placeholder"}}" /></div>
|
||||||
|
</div>
|
||||||
|
<div class="formrow">
|
||||||
|
<div class="formitem formlabel"><a>{{lang "panel_pages_create_title"}}</a></div>
|
||||||
|
<div class="formitem"><input name="title" type="text" placeholder="{{lang "panel_pages_create_title_placeholder"}}" /></div>
|
||||||
|
</div>
|
||||||
|
<div class="formrow">
|
||||||
|
<div class="formitem">
|
||||||
|
<textarea name="body" placeholder="{{lang "panel_pages_create_body_placeholder"}}"></textarea>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="formrow">
|
||||||
|
<div class="formitem"><button name="panel-button" class="formbutton form_middle_button">{{lang "panel_pages_create_submit_button"}}</button></div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
|
</div>
|
||||||
|
{{template "footer.html" . }}
|
|
@ -0,0 +1,31 @@
|
||||||
|
{{template "header.html" . }}
|
||||||
|
<div id="panel_page_edit" class="colstack panel_stack">
|
||||||
|
{{template "panel_menu.html" . }}
|
||||||
|
<main class="colstack_right">
|
||||||
|
<div class="colstack_item colstack_head">
|
||||||
|
<div class="rowitem"><h1>{{lang "panel_pages_edit_head"}}</h1></div>
|
||||||
|
</div>
|
||||||
|
<form action="/panel/pages/edit/submit/{{.Page.ID}}?session={{.CurrentUser.Session}}" method="post">
|
||||||
|
<div id="panel_page_edit_item" class="colstack_item">
|
||||||
|
<div class="formrow">
|
||||||
|
<div class="formitem formlabel"><a>{{lang "panel_pages_name"}}</a></div>
|
||||||
|
<div class="formitem"><input name="name" type="text" value="{{.Page.Name}}" /></div>
|
||||||
|
</div>
|
||||||
|
<div class="formrow">
|
||||||
|
<div class="formitem formlabel"><a>{{lang "panel_pages_title"}}</a></div>
|
||||||
|
<div class="formitem"><input name="title" type="text" value="{{.Page.Title}}" /></div>
|
||||||
|
</div>
|
||||||
|
<div class="formrow">
|
||||||
|
<div class="formitem">
|
||||||
|
<textarea name="body">{{.Page.Body}}</textarea>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="formrow">
|
||||||
|
<div class="formitem"><button name="panel-button" class="formbutton">{{lang "panel_pages_edit_update_button"}}</button></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</main>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
{{template "footer.html" . }}
|
|
@ -1,7 +1,7 @@
|
||||||
{{template "header.html" . }}
|
{{template "header.html" . }}
|
||||||
<div class="colstack panel_stack">
|
<div class="colstack panel_stack">
|
||||||
|
|
||||||
{{template "panel-menu.html" . }}
|
{{template "panel_menu.html" . }}
|
||||||
<main class="colstack_right">
|
<main class="colstack_right">
|
||||||
<div class="colstack_item colstack_head">
|
<div class="colstack_item colstack_head">
|
||||||
<div class="rowitem"><h1>{{lang "panel_plugins_head"}}</h1></div>
|
<div class="rowitem"><h1>{{lang "panel_plugins_head"}}</h1></div>
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{{template "header.html" . }}
|
{{template "header.html" . }}
|
||||||
<div class="colstack panel_stack">
|
<div class="colstack panel_stack">
|
||||||
{{template "panel-menu.html" . }}
|
{{template "panel_menu.html" . }}
|
||||||
<main class="colstack_right">
|
<main class="colstack_right">
|
||||||
<div class="colstack_item colstack_head">
|
<div class="colstack_item colstack_head">
|
||||||
<div class="rowitem"><h1>{{lang "panel_logs_registration_head"}}</h1></div>
|
<div class="rowitem"><h1>{{lang "panel_logs_registration_head"}}</h1></div>
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
{{template "header.html" . }}
|
{{template "header.html" . }}
|
||||||
<div class="colstack panel_stack">
|
<div class="colstack panel_stack">
|
||||||
|
|
||||||
{{template "panel-menu.html" . }}
|
{{template "panel_menu.html" . }}
|
||||||
<main class="colstack_right">
|
<main class="colstack_right">
|
||||||
<div class="colstack_item colstack_head">
|
<div class="colstack_item colstack_head">
|
||||||
<div class="rowitem"><h1>{{.Setting.FriendlyName}}</h1></div>
|
<div class="rowitem"><h1>{{.Setting.FriendlyName}}</h1></div>
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{{template "header.html" . }}
|
{{template "header.html" . }}
|
||||||
<div class="colstack panel_stack">
|
<div class="colstack panel_stack">
|
||||||
{{template "panel-menu.html" . }}
|
{{template "panel_menu.html" . }}
|
||||||
<main class="colstack_right">
|
<main class="colstack_right">
|
||||||
<div class="colstack_item colstack_head">
|
<div class="colstack_item colstack_head">
|
||||||
<div class="rowitem"><h1>{{lang "panel_settings_head"}}</h1></div>
|
<div class="rowitem"><h1>{{lang "panel_settings_head"}}</h1></div>
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{{template "header.html" . }}
|
{{template "header.html" . }}
|
||||||
<div class="colstack panel_stack">
|
<div class="colstack panel_stack">
|
||||||
{{template "panel-menu.html" . }}
|
{{template "panel_menu.html" . }}
|
||||||
{{/** Stop inlining this x.x **/}}
|
{{/** Stop inlining this x.x **/}}
|
||||||
<style type="text/css">
|
<style type="text/css">
|
||||||
.rowitem::after {
|
.rowitem::after {
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{{template "header.html" . }}
|
{{template "header.html" . }}
|
||||||
<div class="colstack panel_stack">
|
<div class="colstack panel_stack">
|
||||||
{{template "panel-menu.html" . }}
|
{{template "panel_menu.html" . }}
|
||||||
<main class="colstack_right">
|
<main class="colstack_right">
|
||||||
<div class="colstack_item colstack_head">
|
<div class="colstack_item colstack_head">
|
||||||
<div class="rowitem"><h1>{{lang "panel_themes_menus_head"}}</h1></div>
|
<div class="rowitem"><h1>{{lang "panel_themes_menus_head"}}</h1></div>
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
<div class="colstack panel_stack">
|
<div class="colstack panel_stack">
|
||||||
{{/** TODO: Set the order based on the order here **/}}
|
{{/** TODO: Set the order based on the order here **/}}
|
||||||
{{/** TODO: Write the backend code and JS code for saving this menu **/}}
|
{{/** TODO: Write the backend code and JS code for saving this menu **/}}
|
||||||
{{template "panel-menu.html" . }}
|
{{template "panel_menu.html" . }}
|
||||||
<main class="colstack_right">
|
<main class="colstack_right">
|
||||||
<div class="colstack_item colstack_head">
|
<div class="colstack_item colstack_head">
|
||||||
<div class="rowitem"><h1>{{lang "panel_themes_menus_edit_head"}}</h1></div>
|
<div class="rowitem"><h1>{{lang "panel_themes_menus_edit_head"}}</h1></div>
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{{template "header.html" . }}
|
{{template "header.html" . }}
|
||||||
<div class="colstack panel_stack">
|
<div class="colstack panel_stack">
|
||||||
{{template "panel-menu.html" . }}
|
{{template "panel_menu.html" . }}
|
||||||
<main class="colstack_right">
|
<main class="colstack_right">
|
||||||
<div class="colstack_item colstack_head">
|
<div class="colstack_item colstack_head">
|
||||||
<div class="rowitem"><h1>{{lang "panel_themes_menus_head"}}</h1></div>
|
<div class="rowitem"><h1>{{lang "panel_themes_menus_head"}}</h1></div>
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
{{template "header.html" . }}
|
{{template "header.html" . }}
|
||||||
<div class="colstack panel_stack">
|
<div class="colstack panel_stack">
|
||||||
|
|
||||||
{{template "panel-menu.html" . }}
|
{{template "panel_menu.html" . }}
|
||||||
<main class="colstack_right">
|
<main class="colstack_right">
|
||||||
<div class="colstack_item colstack_head">
|
<div class="colstack_item colstack_head">
|
||||||
<div class="rowitem"><h1>{{lang "panel_users_head"}}</h1></div>
|
<div class="rowitem"><h1>{{lang "panel_users_head"}}</h1></div>
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
{{template "header.html" . }}
|
{{template "header.html" . }}
|
||||||
<div class="colstack panel_stack">
|
<div class="colstack panel_stack">
|
||||||
|
|
||||||
{{template "panel-menu.html" . }}
|
{{template "panel_menu.html" . }}
|
||||||
<main class="colstack_right">
|
<main class="colstack_right">
|
||||||
<div class="colstack_item colstack_head">
|
<div class="colstack_item colstack_head">
|
||||||
<div class="rowitem"><h1>{{lang "panel_word_filters_head"}}</h1></div>
|
<div class="rowitem"><h1>{{lang "panel_word_filters_head"}}</h1></div>
|
||||||
|
@ -19,7 +19,7 @@
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
{{else}}
|
{{else}}
|
||||||
<div class="rowitem editable_parent rowmsg">
|
<div class="rowitem rowmsg">
|
||||||
<a>{{lang "panel_word_filters_no_filters"}}</a>
|
<a>{{lang "panel_word_filters_no_filters"}}</a>
|
||||||
</div>
|
</div>
|
||||||
{{end}}
|
{{end}}
|
||||||
|
|
|
@ -148,10 +148,13 @@
|
||||||
padding-bottom: 4px;
|
padding-bottom: 4px;
|
||||||
margin-bottom: 6px;
|
margin-bottom: 6px;
|
||||||
}
|
}
|
||||||
#panel_setting textarea {
|
#panel_setting textarea, #panel_page_list textarea, #panel_page_edit textarea {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 80px;
|
height: 80px;
|
||||||
}
|
}
|
||||||
|
#panel_page_list textarea, #panel_page_edit textarea {
|
||||||
|
margin-top: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
#forum_quick_perms .formitem {
|
#forum_quick_perms .formitem {
|
||||||
display: flex;
|
display: flex;
|
||||||
|
|
|
@ -9,6 +9,7 @@ body {
|
||||||
margin: 0px;
|
margin: 0px;
|
||||||
padding: 0px;
|
padding: 0px;
|
||||||
color: #AAAAAA;
|
color: #AAAAAA;
|
||||||
|
background-color: var(--darkest-background);
|
||||||
font-family: "Segoe UI";
|
font-family: "Segoe UI";
|
||||||
}
|
}
|
||||||
a {
|
a {
|
||||||
|
@ -18,7 +19,6 @@ a {
|
||||||
|
|
||||||
nav.nav {
|
nav.nav {
|
||||||
background: var(--darkest-background);
|
background: var(--darkest-background);
|
||||||
border-bottom: 1px solid #444444;
|
|
||||||
width: calc(100% - 200px);
|
width: calc(100% - 200px);
|
||||||
float: left;
|
float: left;
|
||||||
}
|
}
|
||||||
|
@ -60,7 +60,6 @@ li a {
|
||||||
float: left;
|
float: left;
|
||||||
width: 200px;
|
width: 200px;
|
||||||
background-color: var(--darkest-background);
|
background-color: var(--darkest-background);
|
||||||
border-bottom: 1px solid #444444;
|
|
||||||
padding-top: 12px;
|
padding-top: 12px;
|
||||||
padding-bottom: 12px;
|
padding-bottom: 12px;
|
||||||
padding-right: 12px;
|
padding-right: 12px;
|
||||||
|
@ -69,10 +68,9 @@ li a {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
background-color: #333333;
|
background-color: #333333;
|
||||||
border: 1px solid #444444;
|
padding-top: 11px;
|
||||||
padding-top: 10px;
|
padding-bottom: 11px;
|
||||||
padding-bottom: 10px;
|
padding-left: 12px;
|
||||||
padding-left: 10px;
|
|
||||||
}
|
}
|
||||||
.user_box img {
|
.user_box img {
|
||||||
display: block;
|
display: block;
|
||||||
|
@ -110,7 +108,6 @@ li a {
|
||||||
}
|
}
|
||||||
.rowblock:not(.topic_list):not(.rowhead):not(.opthead) .rowitem {
|
.rowblock:not(.topic_list):not(.rowhead):not(.opthead) .rowitem {
|
||||||
background-color: #444444;
|
background-color: #444444;
|
||||||
border-color: #555555;
|
|
||||||
display: flex;
|
display: flex;
|
||||||
padding: 12px;
|
padding: 12px;
|
||||||
margin-left: 12px;
|
margin-left: 12px;
|
||||||
|
@ -150,7 +147,6 @@ h1, h3 {
|
||||||
}
|
}
|
||||||
.topic_row {
|
.topic_row {
|
||||||
background-color: #444444;
|
background-color: #444444;
|
||||||
border-color: #555555;
|
|
||||||
display: flex;
|
display: flex;
|
||||||
}
|
}
|
||||||
.topic_left, .topic_right, .topic_middle {
|
.topic_left, .topic_right, .topic_middle {
|
||||||
|
@ -261,8 +257,13 @@ h1, h3 {
|
||||||
width: 36px;
|
width: 36px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.colstack {
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
|
||||||
.footer .widget {
|
.footer .widget {
|
||||||
padding: 12px;
|
padding: 12px;
|
||||||
|
border-bottom: 1px solid #555555;
|
||||||
}
|
}
|
||||||
#poweredByHolder {
|
#poweredByHolder {
|
||||||
display: flex;
|
display: flex;
|
||||||
|
@ -275,7 +276,6 @@ h1, h3 {
|
||||||
}
|
}
|
||||||
.footer .widget, #poweredByHolder {
|
.footer .widget, #poweredByHolder {
|
||||||
background-color: #444444;
|
background-color: #444444;
|
||||||
border-top: 1px solid #555555;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@media(min-width: 1010px) {
|
@media(min-width: 1010px) {
|
||||||
|
@ -286,8 +286,6 @@ h1, h3 {
|
||||||
width: 1000px;
|
width: 1000px;
|
||||||
margin-left: auto;
|
margin-left: auto;
|
||||||
margin-right: auto;
|
margin-right: auto;
|
||||||
border-left: 1px solid #444444;
|
|
||||||
border-right: 1px solid #444444;
|
|
||||||
}
|
}
|
||||||
.footBlock {
|
.footBlock {
|
||||||
display: flex;
|
display: flex;
|
||||||
|
@ -299,10 +297,6 @@ h1, h3 {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
}
|
}
|
||||||
.footer .widget, #poweredByHolder {
|
|
||||||
border-left: 1px solid #555555;
|
|
||||||
border-right: 1px solid #555555;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@media(min-width: 1330px) {
|
@media(min-width: 1330px) {
|
||||||
|
|
|
@ -0,0 +1,40 @@
|
||||||
|
#back {
|
||||||
|
padding: 0px;
|
||||||
|
}
|
||||||
|
#back, .footer .widget, #poweredByHolder {
|
||||||
|
border: none;
|
||||||
|
}
|
||||||
|
.left_of_nav, .nav, .right_of_nav, .sidebar {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
.footer .widget, #poweredByHolder {
|
||||||
|
background-color: #393939;
|
||||||
|
}
|
||||||
|
|
||||||
|
.colstack_left {
|
||||||
|
width: 25%;
|
||||||
|
padding-top: 12px;
|
||||||
|
padding-right: 24px;
|
||||||
|
padding-bottom: 24px;
|
||||||
|
padding-left: 24px;
|
||||||
|
}
|
||||||
|
.colstack_left .colstack_head {
|
||||||
|
font-size: 19px;
|
||||||
|
margin-bottom: 4px;
|
||||||
|
}
|
||||||
|
.colstack_left .colstack_head:not(:first-child) {
|
||||||
|
margin-top: 8px;
|
||||||
|
}
|
||||||
|
.rowmenu {
|
||||||
|
margin-bottom: 2px;
|
||||||
|
font-size: 17px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.colstack_right {
|
||||||
|
background-color: #444444;
|
||||||
|
width: 75%;
|
||||||
|
padding-top: 12px;
|
||||||
|
padding-right: 24px;
|
||||||
|
padding-bottom: 24px;
|
||||||
|
padding-left: 24px;
|
||||||
|
}
|
|
@ -0,0 +1,7 @@
|
||||||
|
.avatar, .userbit img {
|
||||||
|
width: 40px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.footer .widget, .sidebar {
|
||||||
|
display: none;
|
||||||
|
}
|
Loading…
Reference in New Issue