Moving the files into subpackages 1/?
Moved more routes into the router generator. Renamed a few of the routes. Refactored the router generator. Fixed PreRoute. Began work on letting plugins plug into the template transpiler.
This commit is contained in:
parent
f8e892da20
commit
20bb909c54
|
@ -0,0 +1,6 @@
|
|||
package common
|
||||
|
||||
import "database/sql"
|
||||
|
||||
// ErrNoRows is an alias of sql.ErrNoRows, just in case we end up with non-database/sql datastores
|
||||
var ErrNoRows = sql.ErrNoRows
|
|
@ -1,4 +1,4 @@
|
|||
package main
|
||||
package common
|
||||
|
||||
import "log"
|
||||
|
||||
|
@ -14,6 +14,9 @@ var errorBuffer []error
|
|||
//var notfoundCountPerSecond int
|
||||
//var nopermsCountPerSecond int
|
||||
|
||||
// A blank list to fill out that parameter in Page for routes which don't use it
|
||||
var tList []interface{}
|
||||
|
||||
// WIP, a new system to propagate errors up from routes
|
||||
type RouteError interface {
|
||||
Type() string
|
||||
|
@ -82,7 +85,7 @@ func InternalError(err error, w http.ResponseWriter, r *http.Request) RouteError
|
|||
|
||||
// TODO: Centralise the user struct somewhere else
|
||||
user := User{0, "guest", "Guest", "", 0, false, false, false, false, false, false, GuestPerms, nil, "", false, "", "", "", "", "", 0, 0, "0.0.0.0.0", 0}
|
||||
pi := Page{"Internal Server Error", user, getDefaultHeaderVar(), tList, "A problem has occurred in the system."}
|
||||
pi := Page{"Internal Server Error", user, DefaultHeaderVar(), tList, "A problem has occurred in the system."}
|
||||
err = templates.ExecuteTemplate(w, "error.html", pi)
|
||||
if err != nil {
|
||||
log.Print(err)
|
||||
|
@ -120,7 +123,7 @@ func InternalErrorJS(err error, w http.ResponseWriter, r *http.Request) RouteErr
|
|||
// 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 {
|
||||
w.WriteHeader(401)
|
||||
pi := Page{"Local Error", user, getDefaultHeaderVar(), tList, "You need to login to do that."}
|
||||
pi := Page{"Local Error", user, DefaultHeaderVar(), tList, "You need to login to do that."}
|
||||
if preRenderHooks["pre_render_error"] != nil {
|
||||
if runPreRenderHook("pre_render_error", w, r, &user, &pi) {
|
||||
return nil
|
||||
|
@ -136,7 +139,7 @@ func LoginRequired(w http.ResponseWriter, r *http.Request, user User) RouteError
|
|||
func PreError(errmsg string, w http.ResponseWriter, r *http.Request) RouteError {
|
||||
w.WriteHeader(500)
|
||||
user := User{ID: 0, Group: 6, Perms: GuestPerms}
|
||||
pi := Page{"Error", user, getDefaultHeaderVar(), tList, errmsg}
|
||||
pi := Page{"Error", user, DefaultHeaderVar(), tList, errmsg}
|
||||
if preRenderHooks["pre_render_error"] != nil {
|
||||
if runPreRenderHook("pre_render_error", w, r, &user, &pi) {
|
||||
return nil
|
||||
|
@ -165,7 +168,7 @@ 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
|
||||
func LocalError(errmsg string, w http.ResponseWriter, r *http.Request, user User) RouteError {
|
||||
w.WriteHeader(500)
|
||||
pi := Page{"Local Error", user, getDefaultHeaderVar(), tList, errmsg}
|
||||
pi := Page{"Local Error", user, DefaultHeaderVar(), tList, errmsg}
|
||||
if preRenderHooks["pre_render_error"] != nil {
|
||||
if runPreRenderHook("pre_render_error", w, r, &user, &pi) {
|
||||
return nil
|
||||
|
@ -195,7 +198,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
|
||||
func NoPermissions(w http.ResponseWriter, r *http.Request, user User) RouteError {
|
||||
w.WriteHeader(403)
|
||||
pi := Page{"Local Error", user, getDefaultHeaderVar(), tList, "You don't have permission to do that."}
|
||||
pi := Page{"Local Error", user, DefaultHeaderVar(), tList, "You don't have permission to do that."}
|
||||
// TODO: What to do about this hook?
|
||||
if preRenderHooks["pre_render_error"] != nil {
|
||||
if runPreRenderHook("pre_render_error", w, r, &user, &pi) {
|
||||
|
@ -225,7 +228,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?
|
||||
func Banned(w http.ResponseWriter, r *http.Request, user User) RouteError {
|
||||
w.WriteHeader(403)
|
||||
pi := Page{"Banned", user, getDefaultHeaderVar(), tList, "You have been banned from this site."}
|
||||
pi := Page{"Banned", user, DefaultHeaderVar(), tList, "You have been banned from this site."}
|
||||
if preRenderHooks["pre_render_error"] != nil {
|
||||
if runPreRenderHook("pre_render_error", w, r, &user, &pi) {
|
||||
return nil
|
||||
|
@ -257,7 +260,7 @@ func BannedJS(w http.ResponseWriter, r *http.Request, user User) RouteError {
|
|||
func LoginRequiredJSQ(w http.ResponseWriter, r *http.Request, user User, isJs bool) RouteError {
|
||||
w.WriteHeader(401)
|
||||
if !isJs {
|
||||
pi := Page{"Local Error", user, getDefaultHeaderVar(), tList, "You need to login to do that."}
|
||||
pi := Page{"Local Error", user, DefaultHeaderVar(), tList, "You need to login to do that."}
|
||||
if preRenderHooks["pre_render_error"] != nil {
|
||||
if runPreRenderHook("pre_render_error", w, r, &user, &pi) {
|
||||
return nil
|
||||
|
@ -277,7 +280,7 @@ func LoginRequiredJSQ(w http.ResponseWriter, r *http.Request, user User, isJs bo
|
|||
// ? - Should we add JS and JSQ versions of this?
|
||||
func SecurityError(w http.ResponseWriter, r *http.Request, user User) RouteError {
|
||||
w.WriteHeader(403)
|
||||
pi := Page{"Security Error", user, getDefaultHeaderVar(), tList, "There was a security issue with your request."}
|
||||
pi := Page{"Security Error", user, DefaultHeaderVar(), tList, "There was a security issue with your request."}
|
||||
if preRenderHooks["pre_render_security_error"] != nil {
|
||||
if runPreRenderHook("pre_render_security_error", w, r, &user, &pi) {
|
||||
return nil
|
||||
|
@ -297,7 +300,7 @@ func NotFound(w http.ResponseWriter, r *http.Request) RouteError {
|
|||
w.WriteHeader(404)
|
||||
// TODO: Centralise the user struct somewhere else
|
||||
user := User{0, "guest", "Guest", "", 0, false, false, false, false, false, false, GuestPerms, nil, "", false, "", "", "", "", "", 0, 0, "0.0.0.0.0", 0}
|
||||
pi := Page{"Not Found", user, getDefaultHeaderVar(), tList, "The requested page doesn't exist."}
|
||||
pi := Page{"Not Found", user, DefaultHeaderVar(), tList, "The requested page doesn't exist."}
|
||||
err := templates.ExecuteTemplate(w, "error.html", pi)
|
||||
if err != nil {
|
||||
LogError(err)
|
||||
|
@ -308,7 +311,7 @@ func NotFound(w http.ResponseWriter, r *http.Request) RouteError {
|
|||
// 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, user User) RouteError {
|
||||
w.WriteHeader(errcode)
|
||||
pi := Page{errtitle, user, getDefaultHeaderVar(), tList, errmsg}
|
||||
pi := Page{errtitle, user, DefaultHeaderVar(), tList, errmsg}
|
||||
if preRenderHooks["pre_render_error"] != nil {
|
||||
if runPreRenderHook("pre_render_error", w, r, &user, &pi) {
|
||||
return nil
|
|
@ -4,14 +4,18 @@
|
|||
* Copyright Azareal 2016 - 2018
|
||||
*
|
||||
*/
|
||||
package main
|
||||
package common
|
||||
|
||||
import (
|
||||
"log"
|
||||
"net/http"
|
||||
|
||||
"../query_gen/lib"
|
||||
)
|
||||
|
||||
var plugins = make(map[string]*Plugin)
|
||||
type PluginList map[string]*Plugin
|
||||
|
||||
var Plugins PluginList = make(map[string]*Plugin)
|
||||
|
||||
// Hooks with a single argument. Is this redundant? Might be useful for inlining, as variadics aren't inlined? Are closures even inlined to begin with?
|
||||
var hooks = map[string][]func(interface{}) interface{}{
|
||||
|
@ -138,12 +142,16 @@ func initExtend() (err error) {
|
|||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return LoadPlugins()
|
||||
return Plugins.Load()
|
||||
}
|
||||
|
||||
// LoadPlugins polls the database to see which plugins have been activated and which have been installed
|
||||
func LoadPlugins() error {
|
||||
rows, err := stmts.getPlugins.Query()
|
||||
func (plugins PluginList) Load() error {
|
||||
getPlugins, err := qgen.Builder.SimpleSelect("plugins", "uname, active, installed", "", "", "")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
rows, err := getPlugins.Query()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -278,12 +286,12 @@ func (plugin *Plugin) RemoveHook(name string, handler interface{}) {
|
|||
var pluginsInited = false
|
||||
|
||||
func initPlugins() {
|
||||
for name, body := range plugins {
|
||||
for name, body := range Plugins {
|
||||
log.Print("Added plugin " + name)
|
||||
if body.Active {
|
||||
log.Print("Initialised plugin " + name)
|
||||
if plugins[name].Init != nil {
|
||||
err := plugins[name].Init()
|
||||
if Plugins[name].Init != nil {
|
||||
err := Plugins[name].Init()
|
||||
if err != nil {
|
||||
log.Print(err)
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
package main
|
||||
package common
|
||||
|
||||
//import "fmt"
|
||||
import (
|
|
@ -4,7 +4,7 @@
|
|||
* Copyright Azareal 2017 - 2018
|
||||
*
|
||||
*/
|
||||
package main
|
||||
package common
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
|
@ -14,7 +14,7 @@ import (
|
|||
"sync"
|
||||
"sync/atomic"
|
||||
|
||||
"./query_gen/lib"
|
||||
"../query_gen/lib"
|
||||
)
|
||||
|
||||
var forumUpdateMutex sync.Mutex
|
|
@ -1,4 +1,4 @@
|
|||
package main
|
||||
package common
|
||||
|
||||
var blankGroup = Group{ID: 0, Name: ""}
|
||||
|
|
@ -0,0 +1,286 @@
|
|||
package common
|
||||
|
||||
import (
|
||||
"html/template"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
type HeaderVars struct {
|
||||
NoticeList []string
|
||||
Scripts []string
|
||||
Stylesheets []string
|
||||
Widgets PageWidgets
|
||||
Site *Site
|
||||
Settings map[string]interface{}
|
||||
Themes map[string]Theme // TODO: Use a slice containing every theme instead of the main map for speed
|
||||
ThemeName string
|
||||
//TemplateName string // TODO: Use this to move template calls to the router rather than duplicating them over and over and over?
|
||||
ExtData ExtData
|
||||
}
|
||||
|
||||
// TODO: Add this to routes which don't use templates. E.g. Json APIs.
|
||||
type HeaderLite struct {
|
||||
Site *Site
|
||||
Settings SettingMap
|
||||
ExtData ExtData
|
||||
}
|
||||
|
||||
type PageWidgets struct {
|
||||
LeftSidebar template.HTML
|
||||
RightSidebar template.HTML
|
||||
}
|
||||
|
||||
// TODO: Add a ExtDataHolder interface with methods for manipulating the contents?
|
||||
// ? - Could we use a sync.Map instead?
|
||||
type ExtData struct {
|
||||
items map[string]interface{} // Key: pluginname
|
||||
sync.RWMutex
|
||||
}
|
||||
|
||||
type Page struct {
|
||||
Title string
|
||||
CurrentUser User
|
||||
Header *HeaderVars
|
||||
ItemList []interface{}
|
||||
Something interface{}
|
||||
}
|
||||
|
||||
type TopicPage struct {
|
||||
Title string
|
||||
CurrentUser User
|
||||
Header *HeaderVars
|
||||
ItemList []ReplyUser
|
||||
Topic TopicUser
|
||||
Page int
|
||||
LastPage int
|
||||
}
|
||||
|
||||
type TopicsPage struct {
|
||||
Title string
|
||||
CurrentUser User
|
||||
Header *HeaderVars
|
||||
TopicList []*TopicsRow
|
||||
ForumList []Forum
|
||||
DefaultForum int
|
||||
}
|
||||
|
||||
type ForumPage struct {
|
||||
Title string
|
||||
CurrentUser User
|
||||
Header *HeaderVars
|
||||
ItemList []*TopicsRow
|
||||
Forum *Forum
|
||||
Page int
|
||||
LastPage int
|
||||
}
|
||||
|
||||
type ForumsPage struct {
|
||||
Title string
|
||||
CurrentUser User
|
||||
Header *HeaderVars
|
||||
ItemList []Forum
|
||||
}
|
||||
|
||||
type ProfilePage struct {
|
||||
Title string
|
||||
CurrentUser User
|
||||
Header *HeaderVars
|
||||
ItemList []ReplyUser
|
||||
ProfileOwner User
|
||||
}
|
||||
|
||||
type CreateTopicPage struct {
|
||||
Title string
|
||||
CurrentUser User
|
||||
Header *HeaderVars
|
||||
ItemList []Forum
|
||||
FID int
|
||||
}
|
||||
|
||||
type IPSearchPage struct {
|
||||
Title string
|
||||
CurrentUser User
|
||||
Header *HeaderVars
|
||||
ItemList map[int]*User
|
||||
IP string
|
||||
}
|
||||
|
||||
type PanelStats struct {
|
||||
Users int
|
||||
Groups int
|
||||
Forums int
|
||||
Settings int
|
||||
WordFilters int
|
||||
Themes int
|
||||
Reports int
|
||||
}
|
||||
|
||||
type PanelPage struct {
|
||||
Title string
|
||||
CurrentUser User
|
||||
Header *HeaderVars
|
||||
Stats PanelStats
|
||||
ItemList []interface{}
|
||||
Something interface{}
|
||||
}
|
||||
|
||||
type GridElement struct {
|
||||
ID string
|
||||
Body string
|
||||
Order int // For future use
|
||||
Class string
|
||||
Background string
|
||||
TextColour string
|
||||
Note string
|
||||
}
|
||||
|
||||
type PanelDashboardPage struct {
|
||||
Title string
|
||||
CurrentUser User
|
||||
Header *HeaderVars
|
||||
Stats PanelStats
|
||||
GridItems []GridElement
|
||||
}
|
||||
|
||||
type PanelThemesPage struct {
|
||||
Title string
|
||||
CurrentUser User
|
||||
Header *HeaderVars
|
||||
Stats PanelStats
|
||||
PrimaryThemes []Theme
|
||||
VariantThemes []Theme
|
||||
}
|
||||
|
||||
type PanelUserPage struct {
|
||||
Title string
|
||||
CurrentUser User
|
||||
Header *HeaderVars
|
||||
Stats PanelStats
|
||||
ItemList []User
|
||||
PageList []int
|
||||
Page int
|
||||
LastPage int
|
||||
}
|
||||
|
||||
type PanelGroupPage struct {
|
||||
Title string
|
||||
CurrentUser User
|
||||
Header *HeaderVars
|
||||
Stats PanelStats
|
||||
ItemList []GroupAdmin
|
||||
PageList []int
|
||||
Page int
|
||||
LastPage int
|
||||
}
|
||||
|
||||
type PanelEditGroupPage struct {
|
||||
Title string
|
||||
CurrentUser User
|
||||
Header *HeaderVars
|
||||
Stats PanelStats
|
||||
ID int
|
||||
Name string
|
||||
Tag string
|
||||
Rank string
|
||||
DisableRank bool
|
||||
}
|
||||
|
||||
type GroupForumPermPreset struct {
|
||||
Group *Group
|
||||
Preset string
|
||||
}
|
||||
|
||||
type PanelEditForumPage struct {
|
||||
Title string
|
||||
CurrentUser User
|
||||
Header *HeaderVars
|
||||
Stats PanelStats
|
||||
ID int
|
||||
Name string
|
||||
Desc string
|
||||
Active bool
|
||||
Preset string
|
||||
Groups []GroupForumPermPreset
|
||||
}
|
||||
|
||||
/*type NameLangPair struct {
|
||||
Name string
|
||||
LangStr string
|
||||
}*/
|
||||
|
||||
type NameLangToggle struct {
|
||||
Name string
|
||||
LangStr string
|
||||
Toggle bool
|
||||
}
|
||||
|
||||
type PanelEditGroupPermsPage struct {
|
||||
Title string
|
||||
CurrentUser User
|
||||
Header *HeaderVars
|
||||
Stats PanelStats
|
||||
ID int
|
||||
Name string
|
||||
LocalPerms []NameLangToggle
|
||||
GlobalPerms []NameLangToggle
|
||||
}
|
||||
|
||||
type backupItem struct {
|
||||
SQLURL string
|
||||
|
||||
// TODO: Add an easier to parse format here for Gosora to be able to more easily reimport portions of the dump and to strip unnecessary data (e.g. table defs and parsed post data)
|
||||
|
||||
Timestamp time.Time
|
||||
}
|
||||
|
||||
type PanelBackupPage struct {
|
||||
Title string
|
||||
CurrentUser User
|
||||
Header *HeaderVars
|
||||
Stats PanelStats
|
||||
Backups []backupItem
|
||||
}
|
||||
|
||||
type logItem struct {
|
||||
Action template.HTML
|
||||
IPAddress string
|
||||
DoneAt string
|
||||
}
|
||||
|
||||
type PanelLogsPage struct {
|
||||
Title string
|
||||
CurrentUser User
|
||||
Header *HeaderVars
|
||||
Stats PanelStats
|
||||
Logs []logItem
|
||||
PageList []int
|
||||
Page int
|
||||
LastPage int
|
||||
}
|
||||
|
||||
type PanelDebugPage struct {
|
||||
Title string
|
||||
CurrentUser User
|
||||
Header *HeaderVars
|
||||
Stats PanelStats
|
||||
Uptime string
|
||||
OpenConns int
|
||||
DBAdapter string
|
||||
}
|
||||
|
||||
type PageSimple struct {
|
||||
Title string
|
||||
Something interface{}
|
||||
}
|
||||
|
||||
type AreYouSure struct {
|
||||
URL string
|
||||
Message string
|
||||
}
|
||||
|
||||
// This is mostly for errors.go, please create *HeaderVars on the spot instead of relying on this or the atomic store underlying it, if possible
|
||||
// TODO: Write a test for this
|
||||
func DefaultHeaderVar() *HeaderVars {
|
||||
return &HeaderVars{Site: site, ThemeName: fallbackTheme}
|
||||
}
|
|
@ -1,12 +1,10 @@
|
|||
package main
|
||||
package common
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"encoding/json"
|
||||
"log"
|
||||
"sync"
|
||||
|
||||
"./query_gen/lib"
|
||||
)
|
||||
|
||||
// TODO: Refactor the perms system
|
|
@ -1,4 +1,4 @@
|
|||
package main
|
||||
package common
|
||||
|
||||
import (
|
||||
"encoding/json"
|
|
@ -1,9 +1,7 @@
|
|||
package main
|
||||
package common
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
|
||||
"./query_gen/lib"
|
||||
)
|
||||
|
||||
var prstore ProfileReplyStore
|
|
@ -4,7 +4,7 @@
|
|||
* Copyright Azareal 2016 - 2018
|
||||
*
|
||||
*/
|
||||
package main
|
||||
package common
|
||||
|
||||
import (
|
||||
"errors"
|
|
@ -1,7 +1,7 @@
|
|||
package main
|
||||
package common
|
||||
|
||||
import "database/sql"
|
||||
import "./query_gen/lib"
|
||||
import "../query_gen/lib"
|
||||
|
||||
var rstore ReplyStore
|
||||
|
|
@ -1,13 +1,13 @@
|
|||
package main
|
||||
package common
|
||||
|
||||
import "strconv"
|
||||
import "strings"
|
||||
import "sync/atomic"
|
||||
|
||||
// SettingBox is a map type specifically for holding the various settings admins set to toggle features on and off or to otherwise alter Gosora's behaviour from the Control Panel
|
||||
type SettingBox map[string]interface{}
|
||||
// SettingMap is a map type specifically for holding the various settings admins set to toggle features on and off or to otherwise alter Gosora's behaviour from the Control Panel
|
||||
type SettingMap map[string]interface{}
|
||||
|
||||
var settingBox atomic.Value // An atomic value pointing to a SettingBox
|
||||
var SettingBox atomic.Value // An atomic value pointing to a SettingBox
|
||||
|
||||
type OptionLabel struct {
|
||||
Label string
|
||||
|
@ -23,7 +23,7 @@ type Setting struct {
|
|||
}
|
||||
|
||||
func init() {
|
||||
settingBox.Store(SettingBox(make(map[string]interface{})))
|
||||
SettingBox.Store(SettingMap(make(map[string]interface{})))
|
||||
}
|
||||
|
||||
func LoadSettings() error {
|
||||
|
@ -33,7 +33,7 @@ func LoadSettings() error {
|
|||
}
|
||||
defer rows.Close()
|
||||
|
||||
var sBox = SettingBox(make(map[string]interface{}))
|
||||
var sBox = SettingMap(make(map[string]interface{}))
|
||||
var sname, scontent, stype, sconstraints string
|
||||
for rows.Next() {
|
||||
err = rows.Scan(&sname, &scontent, &stype, &sconstraints)
|
||||
|
@ -50,12 +50,12 @@ func LoadSettings() error {
|
|||
return err
|
||||
}
|
||||
|
||||
settingBox.Store(sBox)
|
||||
SettingBox.Store(sBox)
|
||||
return nil
|
||||
}
|
||||
|
||||
// TODO: Add better support for HTML attributes (html-attribute). E.g. Meta descriptions.
|
||||
func (sBox SettingBox) ParseSetting(sname string, scontent string, stype string, constraint string) string {
|
||||
func (sBox SettingMap) ParseSetting(sname string, scontent string, stype string, constraint string) string {
|
||||
var err error
|
||||
var ssBox = map[string]interface{}(sBox)
|
||||
if stype == "bool" {
|
|
@ -1,4 +1,4 @@
|
|||
package main
|
||||
package common
|
||||
|
||||
import (
|
||||
"errors"
|
|
@ -1,4 +1,4 @@
|
|||
package main
|
||||
package common
|
||||
|
||||
import (
|
||||
"html/template"
|
||||
|
@ -8,6 +8,15 @@ import (
|
|||
)
|
||||
|
||||
var templates = template.New("")
|
||||
var prebuildTmplList []func(*User, *HeaderVars) CTmpl
|
||||
|
||||
type CTmpl struct {
|
||||
Name string
|
||||
Filename string
|
||||
Path string
|
||||
StructName string
|
||||
Data interface{}
|
||||
}
|
||||
|
||||
// nolint
|
||||
func interpreted_topic_template(pi TopicPage, w http.ResponseWriter) error {
|
||||
|
@ -150,6 +159,17 @@ func compileTemplates() error {
|
|||
return err
|
||||
}
|
||||
|
||||
// Let plugins register their own templates
|
||||
for _, tmplfunc := range prebuildTmplList {
|
||||
tmpl := tmplfunc(user, headerVars)
|
||||
varList = make(map[string]VarItem)
|
||||
compiledTmpl, err := c.compileTemplate(tmpl.Filename, tmpl.Path, tmpl.StructName, tmpl.Data, varList)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
go writeTemplate(tmpl.Name, compiledTmpl)
|
||||
}
|
||||
|
||||
log.Print("Writing the templates")
|
||||
go writeTemplate("topic", topicIDTmpl)
|
||||
go writeTemplate("topic_alt", topicIDAltTmpl)
|
|
@ -1,5 +1,5 @@
|
|||
/* Copyright Azareal 2016 - 2018 */
|
||||
package main
|
||||
package common
|
||||
|
||||
import (
|
||||
//"fmt"
|
||||
|
@ -19,11 +19,10 @@ import (
|
|||
"text/template"
|
||||
)
|
||||
|
||||
//var themes = make(map[string]Theme)
|
||||
type ThemeList map[string]Theme
|
||||
|
||||
var themes ThemeList = make(map[string]Theme)
|
||||
var defaultThemeBox atomic.Value
|
||||
//var themes ThemeList = make(map[string]Theme)
|
||||
var DefaultThemeBox atomic.Value
|
||||
var changeDefaultThemeMutex sync.Mutex
|
||||
|
||||
// TODO: Use this when the default theme doesn't exist
|
|
@ -4,7 +4,7 @@
|
|||
* Copyright Azareal 2017 - 2018
|
||||
*
|
||||
*/
|
||||
package main
|
||||
package common
|
||||
|
||||
//import "fmt"
|
||||
import (
|
|
@ -4,7 +4,7 @@
|
|||
* Copyright Azareal 2017 - 2018
|
||||
*
|
||||
*/
|
||||
package main
|
||||
package common
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
|
@ -13,7 +13,7 @@ import (
|
|||
"sync"
|
||||
"sync/atomic"
|
||||
|
||||
"./query_gen/lib"
|
||||
"../query_gen/lib"
|
||||
)
|
||||
|
||||
// TODO: Add the watchdog goroutine
|
|
@ -4,7 +4,7 @@
|
|||
* Copyright Azareal 2017 - 2018
|
||||
*
|
||||
*/
|
||||
package main
|
||||
package common
|
||||
|
||||
import (
|
||||
//"log"
|
||||
|
@ -15,14 +15,13 @@ import (
|
|||
"strings"
|
||||
"time"
|
||||
|
||||
"./query_gen/lib"
|
||||
"golang.org/x/crypto/bcrypt"
|
||||
)
|
||||
|
||||
// TODO: Replace any literals with this
|
||||
var banGroup = 4
|
||||
var BanGroup = 4
|
||||
|
||||
var guestUser = User{ID: 0, Link: "#", Group: 6, Perms: GuestPerms}
|
||||
var GuestUser = User{ID: 0, Link: "#", Group: 6, Perms: GuestPerms}
|
||||
|
||||
//func(real_password string, password string, salt string) (err error)
|
||||
var CheckPassword = BcryptCheckPassword
|
|
@ -1,4 +1,4 @@
|
|||
package main
|
||||
package common
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
|
@ -8,7 +8,6 @@ import (
|
|||
"sync"
|
||||
"sync/atomic"
|
||||
|
||||
"./query_gen/lib"
|
||||
"golang.org/x/crypto/bcrypt"
|
||||
)
|
||||
|
|
@ -4,7 +4,7 @@
|
|||
* Copyright Azareal 2017 - 2018
|
||||
*
|
||||
*/
|
||||
package main
|
||||
package common
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
|
@ -0,0 +1,536 @@
|
|||
package guilds
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"database/sql"
|
||||
"errors"
|
||||
"html"
|
||||
"html/template"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"../../../common"
|
||||
)
|
||||
|
||||
var ListStmt *sql.Stmt
|
||||
var MemberListStmt *sql.Stmt
|
||||
var MemberListJoinStmt *sql.Stmt
|
||||
var GetMemberStmt *sql.Stmt
|
||||
var GetGuildStmt *sql.Stmt
|
||||
var CreateGuildStmt *sql.Stmt
|
||||
var AttachForumStmt *sql.Stmt
|
||||
var UnattachForumStmt *sql.Stmt
|
||||
var AddMemberStmt *sql.Stmt
|
||||
|
||||
// Guild is a struct representing a guild
|
||||
type Guild struct {
|
||||
ID int
|
||||
Link string
|
||||
Name string
|
||||
Desc string
|
||||
Active bool
|
||||
Privacy int /* 0: Public, 1: Protected, 2: Private */
|
||||
|
||||
// Who should be able to accept applications and create invites? Mods+ or just admins? Mods is a good start, we can ponder over whether we should make this more flexible in the future.
|
||||
Joinable int /* 0: Private, 1: Anyone can join, 2: Applications, 3: Invite-only */
|
||||
|
||||
MemberCount int
|
||||
Owner int
|
||||
Backdrop string
|
||||
CreatedAt string
|
||||
LastUpdateTime string
|
||||
|
||||
MainForumID int
|
||||
MainForum *common.Forum
|
||||
Forums []*common.Forum
|
||||
ExtData ExtData
|
||||
}
|
||||
|
||||
type Page struct {
|
||||
Title string
|
||||
CurrentUser User
|
||||
Header *common.HeaderVars
|
||||
ItemList []*TopicsRow
|
||||
Forum *commmon.Forum
|
||||
Guild *Guild
|
||||
Page int
|
||||
LastPage int
|
||||
}
|
||||
|
||||
// ListPage is a page struct for constructing a list of every guild
|
||||
type ListPage struct {
|
||||
Title string
|
||||
CurrentUser User
|
||||
Header *HeaderVars
|
||||
GuildList []*Guild
|
||||
}
|
||||
|
||||
type MemberListPage struct {
|
||||
Title string
|
||||
CurrentUser User
|
||||
Header *HeaderVars
|
||||
ItemList []GuildMember
|
||||
Guild *Guild
|
||||
Page int
|
||||
LastPage int
|
||||
}
|
||||
|
||||
// Member is a struct representing a specific member of a guild, not to be confused with the global User struct.
|
||||
type Member struct {
|
||||
Link string
|
||||
Rank int /* 0: Member. 1: Mod. 2: Admin. */
|
||||
RankString string /* Member, Mod, Admin, Owner */
|
||||
PostCount int
|
||||
JoinedAt string
|
||||
Offline bool // TODO: Need to track the online states of members when WebSockets are enabled
|
||||
|
||||
User User
|
||||
}
|
||||
|
||||
func PrebuildTmplList(user *User, headerVars *HeaderVars) CTmpl {
|
||||
var guildList = []*Guild{
|
||||
&Guild{
|
||||
ID: 1,
|
||||
Name: "lol",
|
||||
Link: guildsBuildGuildURL(nameToSlug("lol"), 1),
|
||||
Desc: "A group for people who like to laugh",
|
||||
Active: true,
|
||||
MemberCount: 1,
|
||||
Owner: 1,
|
||||
CreatedAt: "date",
|
||||
LastUpdateTime: "date",
|
||||
MainForumID: 1,
|
||||
MainForum: fstore.DirtyGet(1),
|
||||
Forums: []*Forum{fstore.DirtyGet(1)},
|
||||
},
|
||||
}
|
||||
listPage := ListPage{"Guild List", user, headerVars, guildList}
|
||||
return CTmpl{"guilds-guild-list", "guilds_guild_list", "templates/", "guilds.ListPage", listPage}
|
||||
}
|
||||
|
||||
// TODO: Do this properly via the widget system
|
||||
func CommonAreaWidgets(headerVars *HeaderVars) {
|
||||
// TODO: Hot Groups? Featured Groups? Official Groups?
|
||||
var b bytes.Buffer
|
||||
var menu = WidgetMenu{"Guilds", []WidgetMenuItem{
|
||||
WidgetMenuItem{"Create Guild", "/guild/create/", false},
|
||||
}}
|
||||
|
||||
err := templates.ExecuteTemplate(&b, "widget_menu.html", menu)
|
||||
if err != nil {
|
||||
LogError(err)
|
||||
return
|
||||
}
|
||||
|
||||
if themes[headerVars.ThemeName].Sidebars == "left" {
|
||||
headerVars.Widgets.LeftSidebar = template.HTML(string(b.Bytes()))
|
||||
} else if themes[headerVars.ThemeName].Sidebars == "right" || themes[headerVars.ThemeName].Sidebars == "both" {
|
||||
headerVars.Widgets.RightSidebar = template.HTML(string(b.Bytes()))
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Do this properly via the widget system
|
||||
// TODO: Make a better more customisable group widget system
|
||||
func GuildWidgets(headerVars *HeaderVars, guildItem *Guild) (success bool) {
|
||||
return false // Disabled until the next commit
|
||||
|
||||
/*var b bytes.Buffer
|
||||
var menu WidgetMenu = WidgetMenu{"Guild Options", []WidgetMenuItem{
|
||||
WidgetMenuItem{"Join", "/guild/join/" + strconv.Itoa(guildItem.ID), false},
|
||||
WidgetMenuItem{"Members", "/guild/members/" + strconv.Itoa(guildItem.ID), false},
|
||||
}}
|
||||
|
||||
err := templates.ExecuteTemplate(&b, "widget_menu.html", menu)
|
||||
if err != nil {
|
||||
LogError(err)
|
||||
return false
|
||||
}
|
||||
|
||||
if themes[headerVars.ThemeName].Sidebars == "left" {
|
||||
headerVars.Widgets.LeftSidebar = template.HTML(string(b.Bytes()))
|
||||
} else if themes[headerVars.ThemeName].Sidebars == "right" || themes[headerVars.ThemeName].Sidebars == "both" {
|
||||
headerVars.Widgets.RightSidebar = template.HTML(string(b.Bytes()))
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
return true*/
|
||||
}
|
||||
|
||||
/*
|
||||
Custom Pages
|
||||
*/
|
||||
|
||||
func routeGuildList(w http.ResponseWriter, r *http.Request, user User) RouteError {
|
||||
headerVars, ferr := UserCheck(w, r, &user)
|
||||
if ferr != nil {
|
||||
return ferr
|
||||
}
|
||||
guildsCommonAreaWidgets(headerVars)
|
||||
|
||||
rows, err := guildsListStmt.Query()
|
||||
if err != nil && err != ErrNoRows {
|
||||
return InternalError(err, w, r)
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
var guildList []*Guild
|
||||
for rows.Next() {
|
||||
guildItem := &Guild{ID: 0}
|
||||
err := rows.Scan(&guildItem.ID, &guildItem.Name, &guildItem.Desc, &guildItem.Active, &guildItem.Privacy, &guildItem.Joinable, &guildItem.Owner, &guildItem.MemberCount, &guildItem.CreatedAt, &guildItem.LastUpdateTime)
|
||||
if err != nil {
|
||||
return InternalError(err, w, r)
|
||||
}
|
||||
guildItem.Link = guildsBuildGuildURL(nameToSlug(guildItem.Name), guildItem.ID)
|
||||
guildList = append(guildList, guildItem)
|
||||
}
|
||||
err = rows.Err()
|
||||
if err != nil {
|
||||
return InternalError(err, w, r)
|
||||
}
|
||||
|
||||
pi := GuildListPage{"Guild List", user, headerVars, guildList}
|
||||
err = RunThemeTemplate(headerVars.ThemeName, "guilds_guild_list", pi, w)
|
||||
if err != nil {
|
||||
return InternalError(err, w, r)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func GetGuild(guildID int) (guildItem *Guild, err error) {
|
||||
guildItem = &Guild{ID: guildID}
|
||||
err = guildsGetGuildStmt.QueryRow(guildID).Scan(&guildItem.Name, &guildItem.Desc, &guildItem.Active, &guildItem.Privacy, &guildItem.Joinable, &guildItem.Owner, &guildItem.MemberCount, &guildItem.MainForumID, &guildItem.Backdrop, &guildItem.CreatedAt, &guildItem.LastUpdateTime)
|
||||
return guildItem, err
|
||||
}
|
||||
|
||||
func middleViewGuild(w http.ResponseWriter, r *http.Request, user User) RouteError {
|
||||
// SEO URLs...
|
||||
halves := strings.Split(r.URL.Path[len("/guild/"):], ".")
|
||||
if len(halves) < 2 {
|
||||
halves = append(halves, halves[0])
|
||||
}
|
||||
guildID, err := strconv.Atoi(halves[1])
|
||||
if err != nil {
|
||||
return PreError("Not a valid guild ID", w, r)
|
||||
}
|
||||
|
||||
guildItem, err := guildsGetGuild(guildID)
|
||||
if err != nil {
|
||||
return LocalError("Bad guild", w, r, user)
|
||||
}
|
||||
if !guildItem.Active {
|
||||
return NotFound(w, r)
|
||||
}
|
||||
|
||||
// Re-route the request to routeForums
|
||||
var ctx = context.WithValue(r.Context(), "guilds_current_guild", guildItem)
|
||||
return routeForum(w, r.WithContext(ctx), user, strconv.Itoa(guildItem.MainForumID))
|
||||
}
|
||||
|
||||
func CreateGuild(w http.ResponseWriter, r *http.Request, user User) RouteError {
|
||||
headerVars, ferr := UserCheck(w, r, &user)
|
||||
if ferr != nil {
|
||||
return ferr
|
||||
}
|
||||
// TODO: Add an approval queue mode for group creation
|
||||
if !user.Loggedin || !user.PluginPerms["CreateGuild"] {
|
||||
return NoPermissions(w, r, user)
|
||||
}
|
||||
guildsCommonAreaWidgets(headerVars)
|
||||
|
||||
pi := Page{"Create Guild", user, headerVars, tList, nil}
|
||||
err := templates.ExecuteTemplate(w, "guilds_create_guild.html", pi)
|
||||
if err != nil {
|
||||
return InternalError(err, w, r)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func CreateGuildSubmit(w http.ResponseWriter, r *http.Request, user User) RouteError {
|
||||
// TODO: Add an approval queue mode for group creation
|
||||
if !user.Loggedin || !user.PluginPerms["CreateGuild"] {
|
||||
return NoPermissions(w, r, user)
|
||||
}
|
||||
|
||||
var guildActive = true
|
||||
var guildName = html.EscapeString(r.PostFormValue("group_name"))
|
||||
var guildDesc = html.EscapeString(r.PostFormValue("group_desc"))
|
||||
var gprivacy = r.PostFormValue("group_privacy")
|
||||
|
||||
var guildPrivacy int
|
||||
switch gprivacy {
|
||||
case "0":
|
||||
guildPrivacy = 0 // Public
|
||||
case "1":
|
||||
guildPrivacy = 1 // Protected
|
||||
case "2":
|
||||
guildPrivacy = 2 // private
|
||||
default:
|
||||
guildPrivacy = 0
|
||||
}
|
||||
|
||||
// Create the backing forum
|
||||
fid, err := fstore.Create(guildName, "", true, "")
|
||||
if err != nil {
|
||||
return InternalError(err, w, r)
|
||||
}
|
||||
|
||||
res, err := guildsCreateGuildStmt.Exec(guildName, guildDesc, guildActive, guildPrivacy, user.ID, fid)
|
||||
if err != nil {
|
||||
return InternalError(err, w, r)
|
||||
}
|
||||
lastID, err := res.LastInsertId()
|
||||
if err != nil {
|
||||
return InternalError(err, w, r)
|
||||
}
|
||||
|
||||
// Add the main backing forum to the forum list
|
||||
err = guildsAttachForum(int(lastID), fid)
|
||||
if err != nil {
|
||||
return InternalError(err, w, r)
|
||||
}
|
||||
|
||||
_, err = guildsAddMemberStmt.Exec(lastID, user.ID, 2)
|
||||
if err != nil {
|
||||
return InternalError(err, w, r)
|
||||
}
|
||||
|
||||
http.Redirect(w, r, guildsBuildGuildURL(nameToSlug(guildName), int(lastID)), http.StatusSeeOther)
|
||||
return nil
|
||||
}
|
||||
|
||||
func MemberList(w http.ResponseWriter, r *http.Request, user User) RouteError {
|
||||
headerVars, ferr := UserCheck(w, r, &user)
|
||||
if ferr != nil {
|
||||
return ferr
|
||||
}
|
||||
|
||||
// SEO URLs...
|
||||
halves := strings.Split(r.URL.Path[len("/guild/members/"):], ".")
|
||||
if len(halves) < 2 {
|
||||
halves = append(halves, halves[0])
|
||||
}
|
||||
guildID, err := strconv.Atoi(halves[1])
|
||||
if err != nil {
|
||||
return PreError("Not a valid group ID", w, r)
|
||||
}
|
||||
|
||||
var guildItem = &Guild{ID: guildID}
|
||||
var mainForum int // Unused
|
||||
err = guildsGetGuildStmt.QueryRow(guildID).Scan(&guildItem.Name, &guildItem.Desc, &guildItem.Active, &guildItem.Privacy, &guildItem.Joinable, &guildItem.Owner, &guildItem.MemberCount, &mainForum, &guildItem.Backdrop, &guildItem.CreatedAt, &guildItem.LastUpdateTime)
|
||||
if err != nil {
|
||||
return LocalError("Bad group", w, r, user)
|
||||
}
|
||||
guildItem.Link = guildsBuildGuildURL(nameToSlug(guildItem.Name), guildItem.ID)
|
||||
|
||||
guildsGuildWidgets(headerVars, guildItem)
|
||||
|
||||
rows, err := guildsMemberListJoinStmt.Query(guildID)
|
||||
if err != nil && err != ErrNoRows {
|
||||
return InternalError(err, w, r)
|
||||
}
|
||||
|
||||
var guildMembers []GuildMember
|
||||
for rows.Next() {
|
||||
guildMember := GuildMember{PostCount: 0}
|
||||
err := rows.Scan(&guildMember.User.ID, &guildMember.Rank, &guildMember.PostCount, &guildMember.JoinedAt, &guildMember.User.Name, &guildMember.User.Avatar)
|
||||
if err != nil {
|
||||
return InternalError(err, w, r)
|
||||
}
|
||||
guildMember.Link = buildProfileURL(nameToSlug(guildMember.User.Name), guildMember.User.ID)
|
||||
if guildMember.User.Avatar != "" {
|
||||
if guildMember.User.Avatar[0] == '.' {
|
||||
guildMember.User.Avatar = "/uploads/avatar_" + strconv.Itoa(guildMember.User.ID) + guildMember.User.Avatar
|
||||
}
|
||||
} else {
|
||||
guildMember.User.Avatar = strings.Replace(config.Noavatar, "{id}", strconv.Itoa(guildMember.User.ID), 1)
|
||||
}
|
||||
guildMember.JoinedAt, _ = relativeTimeFromString(guildMember.JoinedAt)
|
||||
if guildItem.Owner == guildMember.User.ID {
|
||||
guildMember.RankString = "Owner"
|
||||
} else {
|
||||
switch guildMember.Rank {
|
||||
case 0:
|
||||
guildMember.RankString = "Member"
|
||||
case 1:
|
||||
guildMember.RankString = "Mod"
|
||||
case 2:
|
||||
guildMember.RankString = "Admin"
|
||||
}
|
||||
}
|
||||
guildMembers = append(guildMembers, guildMember)
|
||||
}
|
||||
err = rows.Err()
|
||||
if err != nil {
|
||||
return InternalError(err, w, r)
|
||||
}
|
||||
rows.Close()
|
||||
|
||||
pi := GuildMemberListPage{"Guild Member List", user, headerVars, guildMembers, guildItem, 0, 0}
|
||||
// A plugin with plugins. Pluginception!
|
||||
if preRenderHooks["pre_render_guilds_member_list"] != nil {
|
||||
if runPreRenderHook("pre_render_guilds_member_list", w, r, &user, &pi) {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
err = RunThemeTemplate(headerVars.ThemeName, "guilds_member_list", pi, w)
|
||||
if err != nil {
|
||||
return InternalError(err, w, r)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func AttachForum(guildID int, fid int) error {
|
||||
_, err := guildsAttachForumStmt.Exec(guildID, fid)
|
||||
return err
|
||||
}
|
||||
|
||||
func UnattachForum(fid int) error {
|
||||
_, err := guildsAttachForumStmt.Exec(fid)
|
||||
return err
|
||||
}
|
||||
|
||||
func BuildGuildURL(slug string, id int) string {
|
||||
if slug == "" {
|
||||
return "/guild/" + slug + "." + strconv.Itoa(id)
|
||||
}
|
||||
return "/guild/" + strconv.Itoa(id)
|
||||
}
|
||||
|
||||
/*
|
||||
Hooks
|
||||
*/
|
||||
|
||||
// TODO: Prebuild this template
|
||||
func PreRenderViewForum(w http.ResponseWriter, r *http.Request, user *User, data interface{}) (halt bool) {
|
||||
pi := data.(*ForumPage)
|
||||
if pi.Header.ExtData.items != nil {
|
||||
if guildData, ok := pi.Header.ExtData.items["guilds_current_group"]; ok {
|
||||
guildItem := guildData.(*Guild)
|
||||
|
||||
guildpi := GuildPage{pi.Title, pi.CurrentUser, pi.Header, pi.ItemList, pi.Forum, guildItem, pi.Page, pi.LastPage}
|
||||
err := templates.ExecuteTemplate(w, "guilds_view_guild.html", guildpi)
|
||||
if err != nil {
|
||||
LogError(err)
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func TrowAssign(args ...interface{}) interface{} {
|
||||
var forum = args[1].(*Forum)
|
||||
if forum.ParentType == "guild" {
|
||||
var topicItem = args[0].(*TopicsRow)
|
||||
topicItem.ForumLink = "/guild/" + strings.TrimPrefix(topicItem.ForumLink, getForumURLPrefix())
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// TODO: It would be nice, if you could select one of the boards in the group from that drop-down rather than just the one you got linked from
|
||||
func TopicCreatePreLoop(args ...interface{}) interface{} {
|
||||
var fid = args[2].(int)
|
||||
if fstore.DirtyGet(fid).ParentType == "guild" {
|
||||
var strictmode = args[5].(*bool)
|
||||
*strictmode = true
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// TODO: Add privacy options
|
||||
// TODO: Add support for multiple boards and add per-board simplified permissions
|
||||
// TODO: Take isJs into account for routes which expect JSON responses
|
||||
func ForumCheck(args ...interface{}) (skip bool, rerr RouteError) {
|
||||
var r = args[1].(*http.Request)
|
||||
var fid = args[3].(*int)
|
||||
var forum = fstore.DirtyGet(*fid)
|
||||
|
||||
if forum.ParentType == "guild" {
|
||||
var err error
|
||||
var w = args[0].(http.ResponseWriter)
|
||||
guildItem, ok := r.Context().Value("guilds_current_group").(*Guild)
|
||||
if !ok {
|
||||
guildItem, err = guildsGetGuild(forum.ParentID)
|
||||
if err != nil {
|
||||
return true, InternalError(errors.New("Unable to find the parent group for a forum"), w, r)
|
||||
}
|
||||
if !guildItem.Active {
|
||||
return true, NotFound(w, r)
|
||||
}
|
||||
r = r.WithContext(context.WithValue(r.Context(), "guilds_current_group", guildItem))
|
||||
}
|
||||
|
||||
var user = args[2].(*User)
|
||||
var rank int
|
||||
var posts int
|
||||
var joinedAt string
|
||||
|
||||
// TODO: Group privacy settings. For now, groups are all globally visible
|
||||
|
||||
// Clear the default group permissions
|
||||
// TODO: Do this more efficiently, doing it quick and dirty for now to get this out quickly
|
||||
overrideForumPerms(&user.Perms, false)
|
||||
user.Perms.ViewTopic = true
|
||||
|
||||
err = guildsGetMemberStmt.QueryRow(guildItem.ID, user.ID).Scan(&rank, &posts, &joinedAt)
|
||||
if err != nil && err != ErrNoRows {
|
||||
return true, InternalError(err, w, r)
|
||||
} else if err != nil {
|
||||
// TODO: Should we let admins / guests into public groups?
|
||||
return true, LocalError("You're not part of this group!", w, r, *user)
|
||||
}
|
||||
|
||||
// TODO: Implement bans properly by adding the Local Ban API in the next commit
|
||||
// TODO: How does this even work? Refactor it along with the rest of this plugin!
|
||||
if rank < 0 {
|
||||
return true, LocalError("You've been banned from this group!", w, r, *user)
|
||||
}
|
||||
|
||||
// Basic permissions for members, more complicated permissions coming in the next commit!
|
||||
if guildItem.Owner == user.ID {
|
||||
overrideForumPerms(&user.Perms, true)
|
||||
} else if rank == 0 {
|
||||
user.Perms.LikeItem = true
|
||||
user.Perms.CreateTopic = true
|
||||
user.Perms.CreateReply = true
|
||||
} else {
|
||||
overrideForumPerms(&user.Perms, true)
|
||||
}
|
||||
return true, nil
|
||||
}
|
||||
|
||||
return false, nil
|
||||
}
|
||||
|
||||
// TODO: Override redirects? I don't think this is needed quite yet
|
||||
|
||||
func Widgets(args ...interface{}) interface{} {
|
||||
var zone = args[0].(string)
|
||||
var headerVars = args[2].(*HeaderVars)
|
||||
var request = args[3].(*http.Request)
|
||||
|
||||
if zone != "view_forum" {
|
||||
return false
|
||||
}
|
||||
|
||||
var forum = args[1].(*Forum)
|
||||
if forum.ParentType == "guild" {
|
||||
// This is why I hate using contexts, all the daisy chains and interface casts x.x
|
||||
guildItem, ok := request.Context().Value("guilds_current_group").(*Guild)
|
||||
if !ok {
|
||||
LogError(errors.New("Unable to find a parent group in the context data"))
|
||||
return false
|
||||
}
|
||||
|
||||
if headerVars.ExtData.items == nil {
|
||||
headerVars.ExtData.items = make(map[string]interface{})
|
||||
}
|
||||
headerVars.ExtData.items["guilds_current_group"] = guildItem
|
||||
|
||||
return guildsGuildWidgets(headerVars, guildItem)
|
||||
}
|
||||
return false
|
||||
}
|
|
@ -266,15 +266,21 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
|||
return
|
||||
}
|
||||
|
||||
err = routeAccountOwnEditCritical(w,req,user)
|
||||
err = routeAccountEditCritical(w,req,user)
|
||||
case "/user/edit/critical/submit/":
|
||||
err = NoSessionMismatch(w,req,user)
|
||||
if err != nil {
|
||||
router.handleError(err,w,req,user)
|
||||
return
|
||||
}
|
||||
|
||||
err = MemberOnly(w,req,user)
|
||||
if err != nil {
|
||||
router.handleError(err,w,req,user)
|
||||
return
|
||||
}
|
||||
|
||||
err = routeAccountOwnEditCriticalSubmit(w,req,user)
|
||||
err = routeAccountEditCriticalSubmit(w,req,user)
|
||||
case "/user/edit/avatar/":
|
||||
err = MemberOnly(w,req,user)
|
||||
if err != nil {
|
||||
|
@ -282,7 +288,7 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
|||
return
|
||||
}
|
||||
|
||||
err = routeAccountOwnEditAvatar(w,req,user)
|
||||
err = routeAccountEditAvatar(w,req,user)
|
||||
case "/user/edit/avatar/submit/":
|
||||
err = MemberOnly(w,req,user)
|
||||
if err != nil {
|
||||
|
@ -290,7 +296,7 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
|||
return
|
||||
}
|
||||
|
||||
err = routeAccountOwnEditAvatarSubmit(w,req,user)
|
||||
err = routeAccountEditAvatarSubmit(w,req,user)
|
||||
case "/user/edit/username/":
|
||||
err = MemberOnly(w,req,user)
|
||||
if err != nil {
|
||||
|
@ -298,7 +304,7 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
|||
return
|
||||
}
|
||||
|
||||
err = routeAccountOwnEditUsername(w,req,user)
|
||||
err = routeAccountEditUsername(w,req,user)
|
||||
case "/user/edit/username/submit/":
|
||||
err = MemberOnly(w,req,user)
|
||||
if err != nil {
|
||||
|
@ -306,7 +312,7 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
|||
return
|
||||
}
|
||||
|
||||
err = routeAccountOwnEditUsernameSubmit(w,req,user)
|
||||
err = routeAccountEditUsernameSubmit(w,req,user)
|
||||
case "/user/edit/email/":
|
||||
err = MemberOnly(w,req,user)
|
||||
if err != nil {
|
||||
|
@ -314,7 +320,7 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
|||
return
|
||||
}
|
||||
|
||||
err = routeAccountOwnEditEmail(w,req,user)
|
||||
err = routeAccountEditEmail(w,req,user)
|
||||
case "/user/edit/token/":
|
||||
err = MemberOnly(w,req,user)
|
||||
if err != nil {
|
||||
|
@ -322,7 +328,7 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
|||
return
|
||||
}
|
||||
|
||||
err = routeAccountOwnEditEmailTokenSubmit(w,req,user,extra_data)
|
||||
err = routeAccountEditEmailTokenSubmit(w,req,user,extra_data)
|
||||
default:
|
||||
req.URL.Path += extra_data
|
||||
err = routeProfile(w,req,user)
|
||||
|
@ -330,6 +336,44 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
|||
if err != nil {
|
||||
router.handleError(err,w,req,user)
|
||||
}
|
||||
case "/users":
|
||||
err = MemberOnly(w,req,user)
|
||||
if err != nil {
|
||||
router.handleError(err,w,req,user)
|
||||
return
|
||||
}
|
||||
|
||||
switch(req.URL.Path) {
|
||||
case "/users/ban/submit/":
|
||||
err = NoSessionMismatch(w,req,user)
|
||||
if err != nil {
|
||||
router.handleError(err,w,req,user)
|
||||
return
|
||||
}
|
||||
|
||||
err = routeBanSubmit(w,req,user)
|
||||
case "/users/unban/":
|
||||
err = NoSessionMismatch(w,req,user)
|
||||
if err != nil {
|
||||
router.handleError(err,w,req,user)
|
||||
return
|
||||
}
|
||||
|
||||
err = routeUnban(w,req,user)
|
||||
case "/users/activate/":
|
||||
err = NoSessionMismatch(w,req,user)
|
||||
if err != nil {
|
||||
router.handleError(err,w,req,user)
|
||||
return
|
||||
}
|
||||
|
||||
err = routeActivate(w,req,user)
|
||||
case "/users/ips/":
|
||||
err = routeIps(w,req,user)
|
||||
}
|
||||
if err != nil {
|
||||
router.handleError(err,w,req,user)
|
||||
}
|
||||
case "/uploads":
|
||||
if extra_data == "" {
|
||||
NotFound(w,req)
|
||||
|
|
58
main.go
58
main.go
|
@ -228,12 +228,6 @@ func main() {
|
|||
|
||||
log.Print("Initialising the router")
|
||||
router = NewGenRouter(http.FileServer(http.Dir("./uploads")))
|
||||
////router.HandleFunc("/static/", routeStatic)
|
||||
////router.HandleFunc("/overview/", routeOverview)
|
||||
////router.HandleFunc("/topics/create/", routeTopicCreate)
|
||||
////router.HandleFunc("/topics/", routeTopics)
|
||||
////router.HandleFunc("/forums/", routeForums)
|
||||
////router.HandleFunc("/forum/", routeForum)
|
||||
router.HandleFunc("/topic/create/submit/", routeTopicCreateSubmit)
|
||||
router.HandleFunc("/topic/", routeTopicID)
|
||||
router.HandleFunc("/reply/create/", routeCreateReply)
|
||||
|
@ -242,7 +236,6 @@ func main() {
|
|||
router.HandleFunc("/reply/edit/submit/", routeReplyEditSubmit)
|
||||
router.HandleFunc("/reply/delete/submit/", routeReplyDeleteSubmit)
|
||||
router.HandleFunc("/reply/like/submit/", routeReplyLikeSubmit)
|
||||
///router.HandleFunc("/report/submit/", route_report_submit)
|
||||
router.HandleFunc("/topic/edit/submit/", routeEditTopic)
|
||||
router.HandleFunc("/topic/delete/submit/", routeDeleteTopic)
|
||||
router.HandleFunc("/topic/stick/submit/", routeStickTopic)
|
||||
|
@ -260,65 +253,14 @@ func main() {
|
|||
router.HandleFunc("/accounts/logout/", routeLogout)
|
||||
router.HandleFunc("/accounts/login/submit/", routeLoginSubmit)
|
||||
router.HandleFunc("/accounts/create/submit/", routeRegisterSubmit)
|
||||
|
||||
//router.HandleFunc("/accounts/list/", routeLogin) // Redirect /accounts/ and /user/ to here.. // Get a list of all of the accounts on the forum
|
||||
//router.HandleFunc("/accounts/create/full/", routeLogout) // Advanced account creator for admins?
|
||||
//router.HandleFunc("/user/edit/", routeLogout)
|
||||
////router.HandleFunc("/user/edit/critical/", routeAccountOwnEditCritical) // Password & Email
|
||||
////router.HandleFunc("/user/edit/critical/submit/", routeAccountOwnEditCriticalSubmit)
|
||||
////router.HandleFunc("/user/edit/avatar/", routeAccountOwnEditAvatar)
|
||||
////router.HandleFunc("/user/edit/avatar/submit/", routeAccountOwnEditAvatarSubmit)
|
||||
////router.HandleFunc("/user/edit/username/", routeAccountOwnEditUsername)
|
||||
////router.HandleFunc("/user/edit/username/submit/", routeAccountOwnEditUsernameSubmit)
|
||||
////router.HandleFunc("/user/edit/email/", routeAccountOwnEditEmail)
|
||||
////router.HandleFunc("/user/edit/token/", routeAccountOwnEditEmailTokenSubmit)
|
||||
////router.HandleFunc("/user/", routeProfile)
|
||||
|
||||
// TODO: Move these into /user/?
|
||||
router.HandleFunc("/profile/reply/create/", routeProfileReplyCreate)
|
||||
router.HandleFunc("/profile/reply/edit/submit/", routeProfileReplyEditSubmit)
|
||||
router.HandleFunc("/profile/reply/delete/submit/", routeProfileReplyDeleteSubmit)
|
||||
|
||||
//router.HandleFunc("/user/edit/submit/", routeLogout) // routeLogout? what on earth? o.o
|
||||
//router.HandleFunc("/users/ban/", routeBan)
|
||||
router.HandleFunc("/users/ban/submit/", routeBanSubmit)
|
||||
router.HandleFunc("/users/unban/", routeUnban)
|
||||
router.HandleFunc("/users/activate/", routeActivate)
|
||||
router.HandleFunc("/users/ips/", routeIps)
|
||||
|
||||
// The Control Panel
|
||||
// TODO: Rename the commented route handlers to the new camelCase format :'(
|
||||
////router.HandleFunc("/panel/", routePanel)
|
||||
////router.HandleFunc("/panel/forums/", routePanelForums)
|
||||
////router.HandleFunc("/panel/forums/create/", routePanelForumsCreateSubmit)
|
||||
////router.HandleFunc("/panel/forums/delete/", routePanelForumsDelete)
|
||||
////router.HandleFunc("/panel/forums/delete/submit/", routePanelForumsDeleteSubmit)
|
||||
////router.HandleFunc("/panel/forums/edit/", routePanelForumsEdit)
|
||||
////router.HandleFunc("/panel/forums/edit/submit/", routePanelForumsEditSubmit)
|
||||
////router.HandleFunc("/panel/forums/edit/perms/submit/", routePanelForumsEditPermsSubmit)
|
||||
////router.HandleFunc("/panel/settings/", routePanelSettings)
|
||||
////router.HandleFunc("/panel/settings/edit/", routePanelSetting)
|
||||
////router.HandleFunc("/panel/settings/edit/submit/", routePanelSettingEdit)
|
||||
///router.HandleFunc("/panel/themes/", route_panel_themes)
|
||||
///router.HandleFunc("/panel/themes/default/", route_panel_themes_default)
|
||||
///router.HandleFunc("/panel/plugins/", route_panel_plugins)
|
||||
///router.HandleFunc("/panel/plugins/activate/", route_panel_plugins_activate)
|
||||
///router.HandleFunc("/panel/plugins/deactivate/", route_panel_plugins_deactivate)
|
||||
///router.HandleFunc("/panel/users/", route_panel_users)
|
||||
///router.HandleFunc("/panel/users/edit/", route_panel_users_edit)
|
||||
///router.HandleFunc("/panel/users/edit/submit/", route_panel_users_edit_submit)
|
||||
///router.HandleFunc("/panel/groups/", route_panel_groups)
|
||||
///router.HandleFunc("/panel/groups/edit/", route_panel_groups_edit)
|
||||
///router.HandleFunc("/panel/groups/edit/perms/", route_panel_groups_edit_perms)
|
||||
///router.HandleFunc("/panel/groups/edit/submit/", route_panel_groups_edit_submit)
|
||||
///router.HandleFunc("/panel/groups/edit/perms/submit/", route_panel_groups_edit_perms_submit)
|
||||
///router.HandleFunc("/panel/groups/create/", route_panel_groups_create_submit)
|
||||
///router.HandleFunc("/panel/logs/mod/", route_panel_logs_mod)
|
||||
///router.HandleFunc("/panel/debug/", route_panel_debug)
|
||||
|
||||
////router.HandleFunc("/api/", routeAPI)
|
||||
//router.HandleFunc("/exit/", routeExit)
|
||||
////router.HandleFunc("/", config.DefaultRoute)
|
||||
router.HandleFunc("/ws/", routeWebsockets)
|
||||
|
||||
log.Print("Initialising the plugins")
|
||||
|
|
|
@ -96,7 +96,7 @@ func routeTopicCreate(w http.ResponseWriter, r *http.Request, user User, sfid st
|
|||
}
|
||||
}
|
||||
|
||||
err = template_create_topic_handle(ctpage, w)
|
||||
err = RunThemeTemplate(headerVars.ThemeName, "create-topic", ctpage, w)
|
||||
if err != nil {
|
||||
return InternalError(err, w, r)
|
||||
}
|
||||
|
@ -630,7 +630,7 @@ func routeReportSubmit(w http.ResponseWriter, r *http.Request, user User, sitemI
|
|||
return nil
|
||||
}
|
||||
|
||||
func routeAccountOwnEditCritical(w http.ResponseWriter, r *http.Request, user User) RouteError {
|
||||
func routeAccountEditCritical(w http.ResponseWriter, r *http.Request, user User) RouteError {
|
||||
headerVars, ferr := UserCheck(w, r, &user)
|
||||
if ferr != nil {
|
||||
return ferr
|
||||
|
@ -649,23 +649,18 @@ func routeAccountOwnEditCritical(w http.ResponseWriter, r *http.Request, user Us
|
|||
return nil
|
||||
}
|
||||
|
||||
func routeAccountOwnEditCriticalSubmit(w http.ResponseWriter, r *http.Request, user User) RouteError {
|
||||
func routeAccountEditCriticalSubmit(w http.ResponseWriter, r *http.Request, user User) RouteError {
|
||||
headerVars, ferr := UserCheck(w, r, &user)
|
||||
if ferr != nil {
|
||||
return ferr
|
||||
}
|
||||
|
||||
err := r.ParseForm()
|
||||
if err != nil {
|
||||
return LocalError("Bad Form", w, r, user)
|
||||
}
|
||||
|
||||
var realPassword, salt string
|
||||
currentPassword := r.PostFormValue("account-current-password")
|
||||
newPassword := r.PostFormValue("account-new-password")
|
||||
confirmPassword := r.PostFormValue("account-confirm-password")
|
||||
|
||||
err = stmts.getPassword.QueryRow(user.ID).Scan(&realPassword, &salt)
|
||||
err := stmts.getPassword.QueryRow(user.ID).Scan(&realPassword, &salt)
|
||||
if err == ErrNoRows {
|
||||
return LocalError("Your account no longer exists.", w, r, user)
|
||||
} else if err != nil {
|
||||
|
@ -700,7 +695,7 @@ func routeAccountOwnEditCriticalSubmit(w http.ResponseWriter, r *http.Request, u
|
|||
return nil
|
||||
}
|
||||
|
||||
func routeAccountOwnEditAvatar(w http.ResponseWriter, r *http.Request, user User) RouteError {
|
||||
func routeAccountEditAvatar(w http.ResponseWriter, r *http.Request, user User) RouteError {
|
||||
headerVars, ferr := UserCheck(w, r, &user)
|
||||
if ferr != nil {
|
||||
return ferr
|
||||
|
@ -719,7 +714,7 @@ func routeAccountOwnEditAvatar(w http.ResponseWriter, r *http.Request, user User
|
|||
return nil
|
||||
}
|
||||
|
||||
func routeAccountOwnEditAvatarSubmit(w http.ResponseWriter, r *http.Request, user User) RouteError {
|
||||
func routeAccountEditAvatarSubmit(w http.ResponseWriter, r *http.Request, user User) RouteError {
|
||||
if r.ContentLength > int64(config.MaxRequestSize) {
|
||||
size, unit := convertByteUnit(float64(config.MaxRequestSize))
|
||||
return CustomError("Your avatar's too big. Avatars must be smaller than "+strconv.Itoa(int(size))+unit, http.StatusExpectationFailed, "Error", w, r, user)
|
||||
|
@ -805,7 +800,7 @@ func routeAccountOwnEditAvatarSubmit(w http.ResponseWriter, r *http.Request, use
|
|||
return nil
|
||||
}
|
||||
|
||||
func routeAccountOwnEditUsername(w http.ResponseWriter, r *http.Request, user User) RouteError {
|
||||
func routeAccountEditUsername(w http.ResponseWriter, r *http.Request, user User) RouteError {
|
||||
headerVars, ferr := UserCheck(w, r, &user)
|
||||
if ferr != nil {
|
||||
return ferr
|
||||
|
@ -824,18 +819,14 @@ func routeAccountOwnEditUsername(w http.ResponseWriter, r *http.Request, user Us
|
|||
return nil
|
||||
}
|
||||
|
||||
func routeAccountOwnEditUsernameSubmit(w http.ResponseWriter, r *http.Request, user User) RouteError {
|
||||
func routeAccountEditUsernameSubmit(w http.ResponseWriter, r *http.Request, user User) RouteError {
|
||||
headerVars, ferr := UserCheck(w, r, &user)
|
||||
if ferr != nil {
|
||||
return ferr
|
||||
}
|
||||
err := r.ParseForm()
|
||||
if err != nil {
|
||||
return LocalError("Bad Form", w, r, user)
|
||||
}
|
||||
|
||||
newUsername := html.EscapeString(r.PostFormValue("account-new-username"))
|
||||
err = user.ChangeName(newUsername)
|
||||
err := user.ChangeName(newUsername)
|
||||
if err != nil {
|
||||
return LocalError("Unable to change the username. Does someone else already have this name?", w, r, user)
|
||||
}
|
||||
|
@ -855,7 +846,7 @@ func routeAccountOwnEditUsernameSubmit(w http.ResponseWriter, r *http.Request, u
|
|||
return nil
|
||||
}
|
||||
|
||||
func routeAccountOwnEditEmail(w http.ResponseWriter, r *http.Request, user User) RouteError {
|
||||
func routeAccountEditEmail(w http.ResponseWriter, r *http.Request, user User) RouteError {
|
||||
headerVars, ferr := UserCheck(w, r, &user)
|
||||
if ferr != nil {
|
||||
return ferr
|
||||
|
@ -910,7 +901,8 @@ func routeAccountOwnEditEmail(w http.ResponseWriter, r *http.Request, user User)
|
|||
return nil
|
||||
}
|
||||
|
||||
func routeAccountOwnEditEmailTokenSubmit(w http.ResponseWriter, r *http.Request, user User, token string) RouteError {
|
||||
// TODO: Do a session check on this?
|
||||
func routeAccountEditEmailTokenSubmit(w http.ResponseWriter, r *http.Request, user User, token string) RouteError {
|
||||
headerVars, ferr := UserCheck(w, r, &user)
|
||||
if ferr != nil {
|
||||
return ferr
|
||||
|
|
|
@ -603,9 +603,6 @@ func routeBanSubmit(w http.ResponseWriter, r *http.Request, user User) RouteErro
|
|||
if !user.Perms.BanUsers {
|
||||
return NoPermissions(w, r, user)
|
||||
}
|
||||
if r.FormValue("session") != user.Session {
|
||||
return SecurityError(w, r, user)
|
||||
}
|
||||
|
||||
uid, err := strconv.Atoi(r.URL.Path[len("/users/ban/submit/"):])
|
||||
if err != nil {
|
||||
|
@ -679,9 +676,6 @@ func routeUnban(w http.ResponseWriter, r *http.Request, user User) RouteError {
|
|||
if !user.Perms.BanUsers {
|
||||
return NoPermissions(w, r, user)
|
||||
}
|
||||
if r.FormValue("session") != user.Session {
|
||||
return SecurityError(w, r, user)
|
||||
}
|
||||
|
||||
uid, err := strconv.Atoi(r.URL.Path[len("/users/unban/"):])
|
||||
if err != nil {
|
||||
|
@ -721,9 +715,6 @@ func routeActivate(w http.ResponseWriter, r *http.Request, user User) RouteError
|
|||
if !user.Perms.ActivateUsers {
|
||||
return NoPermissions(w, r, user)
|
||||
}
|
||||
if r.FormValue("session") != user.Session {
|
||||
return SecurityError(w, r, user)
|
||||
}
|
||||
|
||||
uid, err := strconv.Atoi(r.URL.Path[len("/users/activate/"):])
|
||||
if err != nil {
|
||||
|
|
279
pages.go
279
pages.go
|
@ -3,287 +3,12 @@ package main
|
|||
import (
|
||||
//"fmt"
|
||||
"bytes"
|
||||
"html/template"
|
||||
"net/url"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
type HeaderVars struct {
|
||||
NoticeList []string
|
||||
Scripts []string
|
||||
Stylesheets []string
|
||||
Widgets PageWidgets
|
||||
Site *Site
|
||||
Settings map[string]interface{}
|
||||
Themes map[string]Theme // TODO: Use a slice containing every theme instead of the main map for speed
|
||||
ThemeName string
|
||||
ExtData ExtData
|
||||
}
|
||||
|
||||
// TODO: Add this to routes which don't use templates. E.g. Json APIs.
|
||||
type HeaderLite struct {
|
||||
Site *Site
|
||||
Settings SettingBox
|
||||
ExtData ExtData
|
||||
}
|
||||
|
||||
type PageWidgets struct {
|
||||
LeftSidebar template.HTML
|
||||
RightSidebar template.HTML
|
||||
}
|
||||
|
||||
// TODO: Add a ExtDataHolder interface with methods for manipulating the contents?
|
||||
// ? - Could we use a sync.Map instead?
|
||||
type ExtData struct {
|
||||
items map[string]interface{} // Key: pluginname
|
||||
sync.RWMutex
|
||||
}
|
||||
|
||||
type Page struct {
|
||||
Title string
|
||||
CurrentUser User
|
||||
Header *HeaderVars
|
||||
ItemList []interface{}
|
||||
Something interface{}
|
||||
}
|
||||
|
||||
type TopicPage struct {
|
||||
Title string
|
||||
CurrentUser User
|
||||
Header *HeaderVars
|
||||
ItemList []ReplyUser
|
||||
Topic TopicUser
|
||||
Page int
|
||||
LastPage int
|
||||
}
|
||||
|
||||
type TopicsPage struct {
|
||||
Title string
|
||||
CurrentUser User
|
||||
Header *HeaderVars
|
||||
TopicList []*TopicsRow
|
||||
ForumList []Forum
|
||||
DefaultForum int
|
||||
}
|
||||
|
||||
type ForumPage struct {
|
||||
Title string
|
||||
CurrentUser User
|
||||
Header *HeaderVars
|
||||
ItemList []*TopicsRow
|
||||
Forum *Forum
|
||||
Page int
|
||||
LastPage int
|
||||
}
|
||||
|
||||
type ForumsPage struct {
|
||||
Title string
|
||||
CurrentUser User
|
||||
Header *HeaderVars
|
||||
ItemList []Forum
|
||||
}
|
||||
|
||||
type ProfilePage struct {
|
||||
Title string
|
||||
CurrentUser User
|
||||
Header *HeaderVars
|
||||
ItemList []ReplyUser
|
||||
ProfileOwner User
|
||||
}
|
||||
|
||||
type CreateTopicPage struct {
|
||||
Title string
|
||||
CurrentUser User
|
||||
Header *HeaderVars
|
||||
ItemList []Forum
|
||||
FID int
|
||||
}
|
||||
|
||||
type IPSearchPage struct {
|
||||
Title string
|
||||
CurrentUser User
|
||||
Header *HeaderVars
|
||||
ItemList map[int]*User
|
||||
IP string
|
||||
}
|
||||
|
||||
type PanelStats struct {
|
||||
Users int
|
||||
Groups int
|
||||
Forums int
|
||||
Settings int
|
||||
WordFilters int
|
||||
Themes int
|
||||
Reports int
|
||||
}
|
||||
|
||||
type PanelPage struct {
|
||||
Title string
|
||||
CurrentUser User
|
||||
Header *HeaderVars
|
||||
Stats PanelStats
|
||||
ItemList []interface{}
|
||||
Something interface{}
|
||||
}
|
||||
|
||||
type GridElement struct {
|
||||
ID string
|
||||
Body string
|
||||
Order int // For future use
|
||||
Class string
|
||||
Background string
|
||||
TextColour string
|
||||
Note string
|
||||
}
|
||||
|
||||
type PanelDashboardPage struct {
|
||||
Title string
|
||||
CurrentUser User
|
||||
Header *HeaderVars
|
||||
Stats PanelStats
|
||||
GridItems []GridElement
|
||||
}
|
||||
|
||||
type PanelThemesPage struct {
|
||||
Title string
|
||||
CurrentUser User
|
||||
Header *HeaderVars
|
||||
Stats PanelStats
|
||||
PrimaryThemes []Theme
|
||||
VariantThemes []Theme
|
||||
}
|
||||
|
||||
type PanelUserPage struct {
|
||||
Title string
|
||||
CurrentUser User
|
||||
Header *HeaderVars
|
||||
Stats PanelStats
|
||||
ItemList []User
|
||||
PageList []int
|
||||
Page int
|
||||
LastPage int
|
||||
}
|
||||
|
||||
type PanelGroupPage struct {
|
||||
Title string
|
||||
CurrentUser User
|
||||
Header *HeaderVars
|
||||
Stats PanelStats
|
||||
ItemList []GroupAdmin
|
||||
PageList []int
|
||||
Page int
|
||||
LastPage int
|
||||
}
|
||||
|
||||
type PanelEditGroupPage struct {
|
||||
Title string
|
||||
CurrentUser User
|
||||
Header *HeaderVars
|
||||
Stats PanelStats
|
||||
ID int
|
||||
Name string
|
||||
Tag string
|
||||
Rank string
|
||||
DisableRank bool
|
||||
}
|
||||
|
||||
type GroupForumPermPreset struct {
|
||||
Group *Group
|
||||
Preset string
|
||||
}
|
||||
|
||||
type PanelEditForumPage struct {
|
||||
Title string
|
||||
CurrentUser User
|
||||
Header *HeaderVars
|
||||
Stats PanelStats
|
||||
ID int
|
||||
Name string
|
||||
Desc string
|
||||
Active bool
|
||||
Preset string
|
||||
Groups []GroupForumPermPreset
|
||||
}
|
||||
|
||||
/*type NameLangPair struct {
|
||||
Name string
|
||||
LangStr string
|
||||
}*/
|
||||
|
||||
type NameLangToggle struct {
|
||||
Name string
|
||||
LangStr string
|
||||
Toggle bool
|
||||
}
|
||||
|
||||
type PanelEditGroupPermsPage struct {
|
||||
Title string
|
||||
CurrentUser User
|
||||
Header *HeaderVars
|
||||
Stats PanelStats
|
||||
ID int
|
||||
Name string
|
||||
LocalPerms []NameLangToggle
|
||||
GlobalPerms []NameLangToggle
|
||||
}
|
||||
|
||||
type backupItem struct {
|
||||
SQLURL string
|
||||
|
||||
// TODO: Add an easier to parse format here for Gosora to be able to more easily reimport portions of the dump and to strip unnecessary data (e.g. table defs and parsed post data)
|
||||
|
||||
Timestamp time.Time
|
||||
}
|
||||
|
||||
type PanelBackupPage struct {
|
||||
Title string
|
||||
CurrentUser User
|
||||
Header *HeaderVars
|
||||
Stats PanelStats
|
||||
Backups []backupItem
|
||||
}
|
||||
|
||||
type logItem struct {
|
||||
Action template.HTML
|
||||
IPAddress string
|
||||
DoneAt string
|
||||
}
|
||||
|
||||
type PanelLogsPage struct {
|
||||
Title string
|
||||
CurrentUser User
|
||||
Header *HeaderVars
|
||||
Stats PanelStats
|
||||
Logs []logItem
|
||||
PageList []int
|
||||
Page int
|
||||
LastPage int
|
||||
}
|
||||
|
||||
type PanelDebugPage struct {
|
||||
Title string
|
||||
CurrentUser User
|
||||
Header *HeaderVars
|
||||
Stats PanelStats
|
||||
Uptime string
|
||||
OpenConns int
|
||||
DBAdapter string
|
||||
}
|
||||
|
||||
type PageSimple struct {
|
||||
Title string
|
||||
Something interface{}
|
||||
}
|
||||
|
||||
type AreYouSure struct {
|
||||
URL string
|
||||
Message string
|
||||
}
|
||||
|
||||
var spaceGap = []byte(" ")
|
||||
var httpProtBytes = []byte("http://")
|
||||
var invalidURL = []byte("<span style='color: red;'>[Invalid URL]</span>")
|
||||
|
@ -300,11 +25,11 @@ var urlClose = []byte("</a>")
|
|||
var imageOpen = []byte("<a href=\"")
|
||||
var imageOpen2 = []byte("\"><img src='")
|
||||
var imageClose = []byte("' class='postImage' /></a>")
|
||||
var urlpattern = `(?s)([ {1}])((http|https|ftp|mailto)*)(:{??)\/\/([\.a-zA-Z\/]+)([ {1}])`
|
||||
var urlPattern = `(?s)([ {1}])((http|https|ftp|mailto)*)(:{??)\/\/([\.a-zA-Z\/]+)([ {1}])`
|
||||
var urlReg *regexp.Regexp
|
||||
|
||||
func init() {
|
||||
urlReg = regexp.MustCompile(urlpattern)
|
||||
urlReg = regexp.MustCompile(urlPattern)
|
||||
}
|
||||
|
||||
// TODO: Write a test for this
|
||||
|
|
582
plugin_guilds.go
582
plugin_guilds.go
|
@ -2,149 +2,69 @@ package main
|
|||
|
||||
import (
|
||||
//"fmt"
|
||||
"bytes"
|
||||
"context"
|
||||
"database/sql"
|
||||
"errors"
|
||||
"html"
|
||||
"html/template"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"./extend/guilds/lib"
|
||||
"./query_gen/lib"
|
||||
)
|
||||
|
||||
var guildsListStmt *sql.Stmt
|
||||
var guildsMemberListStmt *sql.Stmt
|
||||
var guildsMemberListJoinStmt *sql.Stmt
|
||||
var guildsGetMemberStmt *sql.Stmt
|
||||
var guildsGetGuildStmt *sql.Stmt
|
||||
var guildsCreateGuildStmt *sql.Stmt
|
||||
var guildsAttachForumStmt *sql.Stmt
|
||||
var guildsUnattachForumStmt *sql.Stmt
|
||||
var guildsAddMemberStmt *sql.Stmt
|
||||
|
||||
// TODO: Add a better way of splitting up giant plugins like this
|
||||
|
||||
// Guild is a struct representing a guild
|
||||
type Guild struct {
|
||||
ID int
|
||||
Link string
|
||||
Name string
|
||||
Desc string
|
||||
Active bool
|
||||
Privacy int /* 0: Public, 1: Protected, 2: Private */
|
||||
|
||||
// Who should be able to accept applications and create invites? Mods+ or just admins? Mods is a good start, we can ponder over whether we should make this more flexible in the future.
|
||||
Joinable int /* 0: Private, 1: Anyone can join, 2: Applications, 3: Invite-only */
|
||||
|
||||
MemberCount int
|
||||
Owner int
|
||||
Backdrop string
|
||||
CreatedAt string
|
||||
LastUpdateTime string
|
||||
|
||||
MainForumID int
|
||||
MainForum *Forum
|
||||
Forums []*Forum
|
||||
ExtData ExtData
|
||||
}
|
||||
|
||||
type GuildPage struct {
|
||||
Title string
|
||||
CurrentUser User
|
||||
Header *HeaderVars
|
||||
ItemList []*TopicsRow
|
||||
Forum *Forum
|
||||
Guild *Guild
|
||||
Page int
|
||||
LastPage int
|
||||
}
|
||||
|
||||
// GuildListPage is a page struct for constructing a list of every guild
|
||||
type GuildListPage struct {
|
||||
Title string
|
||||
CurrentUser User
|
||||
Header *HeaderVars
|
||||
GuildList []*Guild
|
||||
}
|
||||
|
||||
type GuildMemberListPage struct {
|
||||
Title string
|
||||
CurrentUser User
|
||||
Header *HeaderVars
|
||||
ItemList []GuildMember
|
||||
Guild *Guild
|
||||
Page int
|
||||
LastPage int
|
||||
}
|
||||
|
||||
// GuildMember is a struct representing a specific member of a guild, not to be confused with the global User struct.
|
||||
type GuildMember struct {
|
||||
Link string
|
||||
Rank int /* 0: Member. 1: Mod. 2: Admin. */
|
||||
RankString string /* Member, Mod, Admin, Owner */
|
||||
PostCount int
|
||||
JoinedAt string
|
||||
Offline bool // TODO: Need to track the online states of members when WebSockets are enabled
|
||||
|
||||
User User
|
||||
}
|
||||
|
||||
// TODO: Add a plugin interface instead of having a bunch of argument to AddPlugin?
|
||||
func init() {
|
||||
plugins["guilds"] = NewPlugin("guilds", "Guilds", "Azareal", "http://github.com/Azareal", "", "", "", initGuilds, nil, deactivateGuilds, installGuilds, nil)
|
||||
|
||||
// TODO: Is it possible to avoid doing this when the plugin isn't activated?
|
||||
prebuildTmplList = append(prebuildTmplList, guilds.PrebuildTmplList)
|
||||
}
|
||||
|
||||
func initGuilds() (err error) {
|
||||
plugins["guilds"].AddHook("intercept_build_widgets", guildsWidgets)
|
||||
plugins["guilds"].AddHook("trow_assign", guildsTrowAssign)
|
||||
plugins["guilds"].AddHook("topic_create_pre_loop", guildsTopicCreatePreLoop)
|
||||
plugins["guilds"].AddHook("pre_render_view_forum", guildsPreRenderViewForum)
|
||||
plugins["guilds"].AddHook("simple_forum_check_pre_perms", guildsForumCheck)
|
||||
plugins["guilds"].AddHook("forum_check_pre_perms", guildsForumCheck)
|
||||
plugins["guilds"].AddHook("intercept_build_widgets", guilds.Widgets)
|
||||
plugins["guilds"].AddHook("trow_assign", guilds.TrowAssign)
|
||||
plugins["guilds"].AddHook("topic_create_pre_loop", guilds.TopicCreatePreLoop)
|
||||
plugins["guilds"].AddHook("pre_render_view_forum", guilds.PreRenderViewForum)
|
||||
plugins["guilds"].AddHook("simple_forum_check_pre_perms", guilds.ForumCheck)
|
||||
plugins["guilds"].AddHook("forum_check_pre_perms", guilds.ForumCheck)
|
||||
// TODO: Auto-grant this perm to admins upon installation?
|
||||
registerPluginPerm("CreateGuild")
|
||||
router.HandleFunc("/guilds/", guildsGuildList)
|
||||
router.HandleFunc("/guild/", guildsViewGuild)
|
||||
router.HandleFunc("/guild/create/", guildsCreateGuild)
|
||||
router.HandleFunc("/guild/create/submit/", guildsCreateGuildSubmit)
|
||||
router.HandleFunc("/guild/members/", guildsMemberList)
|
||||
router.HandleFunc("/guilds/", guilds.GuildList)
|
||||
router.HandleFunc("/guild/", guilds.ViewGuild)
|
||||
router.HandleFunc("/guild/create/", guilds.CreateGuild)
|
||||
router.HandleFunc("/guild/create/submit/", guilds.CreateGuildSubmit)
|
||||
router.HandleFunc("/guild/members/", guilds.MemberList)
|
||||
|
||||
guildsListStmt, err = qgen.Builder.SimpleSelect("guilds", "guildID, name, desc, active, privacy, joinable, owner, memberCount, createdAt, lastUpdateTime", "", "", "")
|
||||
guilds.ListStmt, err = qgen.Builder.SimpleSelect("guilds", "guildID, name, desc, active, privacy, joinable, owner, memberCount, createdAt, lastUpdateTime", "", "", "")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
guildsGetGuildStmt, err = qgen.Builder.SimpleSelect("guilds", "name, desc, active, privacy, joinable, owner, memberCount, mainForum, backdrop, createdAt, lastUpdateTime", "guildID = ?", "", "")
|
||||
guilds.GetGuildStmt, err = qgen.Builder.SimpleSelect("guilds", "name, desc, active, privacy, joinable, owner, memberCount, mainForum, backdrop, createdAt, lastUpdateTime", "guildID = ?", "", "")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
guildsMemberListStmt, err = qgen.Builder.SimpleSelect("guilds_members", "guildID, uid, rank, posts, joinedAt", "", "", "")
|
||||
guilds.MemberListStmt, err = qgen.Builder.SimpleSelect("guilds_members", "guildID, uid, rank, posts, joinedAt", "", "", "")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
guildsMemberListJoinStmt, err = qgen.Builder.SimpleLeftJoin("guilds_members", "users", "users.uid, guilds_members.rank, guilds_members.posts, guilds_members.joinedAt, users.name, users.avatar", "guilds_members.uid = users.uid", "guilds_members.guildID = ?", "guilds_members.rank DESC, guilds_members.joinedat ASC", "")
|
||||
guilds.MemberListJoinStmt, err = qgen.Builder.SimpleLeftJoin("guilds_members", "users", "users.uid, guilds_members.rank, guilds_members.posts, guilds_members.joinedAt, users.name, users.avatar", "guilds_members.uid = users.uid", "guilds_members.guildID = ?", "guilds_members.rank DESC, guilds_members.joinedat ASC", "")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
guildsGetMemberStmt, err = qgen.Builder.SimpleSelect("guilds_members", "rank, posts, joinedAt", "guildID = ? AND uid = ?", "", "")
|
||||
guilds.GetMemberStmt, err = qgen.Builder.SimpleSelect("guilds_members", "rank, posts, joinedAt", "guildID = ? AND uid = ?", "", "")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
guildsCreateGuildStmt, err = qgen.Builder.SimpleInsert("guilds", "name, desc, active, privacy, joinable, owner, memberCount, mainForum, backdrop, createdAt, lastUpdateTime", "?,?,?,?,1,?,1,?,'',UTC_TIMESTAMP(),UTC_TIMESTAMP()")
|
||||
guilds.CreateGuildStmt, err = qgen.Builder.SimpleInsert("guilds", "name, desc, active, privacy, joinable, owner, memberCount, mainForum, backdrop, createdAt, lastUpdateTime", "?,?,?,?,1,?,1,?,'',UTC_TIMESTAMP(),UTC_TIMESTAMP()")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
guildsAttachForumStmt, err = qgen.Builder.SimpleUpdate("forums", "parentID = ?, parentType = 'guild'", "fid = ?")
|
||||
guilds.AttachForumStmt, err = qgen.Builder.SimpleUpdate("forums", "parentID = ?, parentType = 'guild'", "fid = ?")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
guildsUnattachForumStmt, err = qgen.Builder.SimpleUpdate("forums", "parentID = 0, parentType = ''", "fid = ?")
|
||||
guilds.UnattachForumStmt, err = qgen.Builder.SimpleUpdate("forums", "parentID = 0, parentType = ''", "fid = ?")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
guildsAddMemberStmt, err = qgen.Builder.SimpleInsert("guilds_members", "guildID, uid, rank, posts, joinedAt", "?,?,?,0,UTC_TIMESTAMP()")
|
||||
guilds.AddMemberStmt, err = qgen.Builder.SimpleInsert("guilds_members", "guildID, uid, rank, posts, joinedAt", "?,?,?,0,UTC_TIMESTAMP()")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -153,26 +73,26 @@ func initGuilds() (err error) {
|
|||
}
|
||||
|
||||
func deactivateGuilds() {
|
||||
plugins["guilds"].RemoveHook("intercept_build_widgets", guildsWidgets)
|
||||
plugins["guilds"].RemoveHook("trow_assign", guildsTrowAssign)
|
||||
plugins["guilds"].RemoveHook("topic_create_pre_loop", guildsTopicCreatePreLoop)
|
||||
plugins["guilds"].RemoveHook("pre_render_view_forum", guildsPreRenderViewForum)
|
||||
plugins["guilds"].RemoveHook("simple_forum_check_pre_perms", guildsForumCheck)
|
||||
plugins["guilds"].RemoveHook("forum_check_pre_perms", guildsForumCheck)
|
||||
plugins["guilds"].RemoveHook("intercept_build_widgets", guilds.Widgets)
|
||||
plugins["guilds"].RemoveHook("trow_assign", guilds.TrowAssign)
|
||||
plugins["guilds"].RemoveHook("topic_create_pre_loop", guilds.TopicCreatePreLoop)
|
||||
plugins["guilds"].RemoveHook("pre_render_view_forum", guilds.PreRenderViewForum)
|
||||
plugins["guilds"].RemoveHook("simple_forum_check_pre_perms", guilds.ForumCheck)
|
||||
plugins["guilds"].RemoveHook("forum_check_pre_perms", guilds.ForumCheck)
|
||||
deregisterPluginPerm("CreateGuild")
|
||||
_ = router.RemoveFunc("/guilds/")
|
||||
_ = router.RemoveFunc("/guild/")
|
||||
_ = router.RemoveFunc("/guild/create/")
|
||||
_ = router.RemoveFunc("/guild/create/submit/")
|
||||
_ = guildsListStmt.Close()
|
||||
_ = guildsMemberListStmt.Close()
|
||||
_ = guildsMemberListJoinStmt.Close()
|
||||
_ = guildsGetMemberStmt.Close()
|
||||
_ = guildsGetGuildStmt.Close()
|
||||
_ = guildsCreateGuildStmt.Close()
|
||||
_ = guildsAttachForumStmt.Close()
|
||||
_ = guildsUnattachForumStmt.Close()
|
||||
_ = guildsAddMemberStmt.Close()
|
||||
_ = guilds.ListStmt.Close()
|
||||
_ = guilds.MemberListStmt.Close()
|
||||
_ = guilds.MemberListJoinStmt.Close()
|
||||
_ = guilds.GetMemberStmt.Close()
|
||||
_ = guilds.GetGuildStmt.Close()
|
||||
_ = guilds.CreateGuildStmt.Close()
|
||||
_ = guilds.AttachForumStmt.Close()
|
||||
_ = guilds.UnattachForumStmt.Close()
|
||||
_ = guilds.AddMemberStmt.Close()
|
||||
}
|
||||
|
||||
// TODO: Stop accessing the query builder directly and add a feature in Gosora which is more easily reversed, if an error comes up during the installation process
|
||||
|
@ -228,427 +148,3 @@ func installGuilds() error {
|
|||
func uninstallGuilds() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// TODO: Do this properly via the widget system
|
||||
func guildsCommonAreaWidgets(headerVars *HeaderVars) {
|
||||
// TODO: Hot Groups? Featured Groups? Official Groups?
|
||||
var b bytes.Buffer
|
||||
var menu = WidgetMenu{"Guilds", []WidgetMenuItem{
|
||||
WidgetMenuItem{"Create Guild", "/guild/create/", false},
|
||||
}}
|
||||
|
||||
err := templates.ExecuteTemplate(&b, "widget_menu.html", menu)
|
||||
if err != nil {
|
||||
LogError(err)
|
||||
return
|
||||
}
|
||||
|
||||
if themes[headerVars.ThemeName].Sidebars == "left" {
|
||||
headerVars.Widgets.LeftSidebar = template.HTML(string(b.Bytes()))
|
||||
} else if themes[headerVars.ThemeName].Sidebars == "right" || themes[headerVars.ThemeName].Sidebars == "both" {
|
||||
headerVars.Widgets.RightSidebar = template.HTML(string(b.Bytes()))
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Do this properly via the widget system
|
||||
// TODO: Make a better more customisable group widget system
|
||||
func guildsGuildWidgets(headerVars *HeaderVars, guildItem *Guild) (success bool) {
|
||||
return false // Disabled until the next commit
|
||||
|
||||
/*var b bytes.Buffer
|
||||
var menu WidgetMenu = WidgetMenu{"Guild Options", []WidgetMenuItem{
|
||||
WidgetMenuItem{"Join", "/guild/join/" + strconv.Itoa(guildItem.ID), false},
|
||||
WidgetMenuItem{"Members", "/guild/members/" + strconv.Itoa(guildItem.ID), false},
|
||||
}}
|
||||
|
||||
err := templates.ExecuteTemplate(&b, "widget_menu.html", menu)
|
||||
if err != nil {
|
||||
LogError(err)
|
||||
return false
|
||||
}
|
||||
|
||||
if themes[headerVars.ThemeName].Sidebars == "left" {
|
||||
headerVars.Widgets.LeftSidebar = template.HTML(string(b.Bytes()))
|
||||
} else if themes[headerVars.ThemeName].Sidebars == "right" || themes[headerVars.ThemeName].Sidebars == "both" {
|
||||
headerVars.Widgets.RightSidebar = template.HTML(string(b.Bytes()))
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
return true*/
|
||||
}
|
||||
|
||||
/*
|
||||
Custom Pages
|
||||
*/
|
||||
|
||||
func guildsGuildList(w http.ResponseWriter, r *http.Request, user User) RouteError {
|
||||
headerVars, ferr := UserCheck(w, r, &user)
|
||||
if ferr != nil {
|
||||
return ferr
|
||||
}
|
||||
guildsCommonAreaWidgets(headerVars)
|
||||
|
||||
rows, err := guildsListStmt.Query()
|
||||
if err != nil && err != ErrNoRows {
|
||||
return InternalError(err, w, r)
|
||||
}
|
||||
|
||||
var guildList []*Guild
|
||||
for rows.Next() {
|
||||
guildItem := &Guild{ID: 0}
|
||||
err := rows.Scan(&guildItem.ID, &guildItem.Name, &guildItem.Desc, &guildItem.Active, &guildItem.Privacy, &guildItem.Joinable, &guildItem.Owner, &guildItem.MemberCount, &guildItem.CreatedAt, &guildItem.LastUpdateTime)
|
||||
if err != nil {
|
||||
return InternalError(err, w, r)
|
||||
}
|
||||
guildItem.Link = guildsBuildGuildURL(nameToSlug(guildItem.Name), guildItem.ID)
|
||||
guildList = append(guildList, guildItem)
|
||||
}
|
||||
err = rows.Err()
|
||||
if err != nil {
|
||||
return InternalError(err, w, r)
|
||||
}
|
||||
rows.Close()
|
||||
|
||||
pi := GuildListPage{"Guild List", user, headerVars, guildList}
|
||||
err = templates.ExecuteTemplate(w, "guilds_guild_list.html", pi)
|
||||
if err != nil {
|
||||
return InternalError(err, w, r)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func guildsGetGuild(guildID int) (guildItem *Guild, err error) {
|
||||
guildItem = &Guild{ID: guildID}
|
||||
err = guildsGetGuildStmt.QueryRow(guildID).Scan(&guildItem.Name, &guildItem.Desc, &guildItem.Active, &guildItem.Privacy, &guildItem.Joinable, &guildItem.Owner, &guildItem.MemberCount, &guildItem.MainForumID, &guildItem.Backdrop, &guildItem.CreatedAt, &guildItem.LastUpdateTime)
|
||||
return guildItem, err
|
||||
}
|
||||
|
||||
func guildsViewGuild(w http.ResponseWriter, r *http.Request, user User) RouteError {
|
||||
// SEO URLs...
|
||||
halves := strings.Split(r.URL.Path[len("/guild/"):], ".")
|
||||
if len(halves) < 2 {
|
||||
halves = append(halves, halves[0])
|
||||
}
|
||||
guildID, err := strconv.Atoi(halves[1])
|
||||
if err != nil {
|
||||
return PreError("Not a valid guild ID", w, r)
|
||||
}
|
||||
|
||||
guildItem, err := guildsGetGuild(guildID)
|
||||
if err != nil {
|
||||
return LocalError("Bad guild", w, r, user)
|
||||
}
|
||||
if !guildItem.Active {
|
||||
return NotFound(w, r)
|
||||
}
|
||||
|
||||
// Re-route the request to routeForums
|
||||
var ctx = context.WithValue(r.Context(), "guilds_current_guild", guildItem)
|
||||
return routeForum(w, r.WithContext(ctx), user, strconv.Itoa(guildItem.MainForumID))
|
||||
}
|
||||
|
||||
func guildsCreateGuild(w http.ResponseWriter, r *http.Request, user User) RouteError {
|
||||
headerVars, ferr := UserCheck(w, r, &user)
|
||||
if ferr != nil {
|
||||
return ferr
|
||||
}
|
||||
// TODO: Add an approval queue mode for group creation
|
||||
if !user.Loggedin || !user.PluginPerms["CreateGuild"] {
|
||||
return NoPermissions(w, r, user)
|
||||
}
|
||||
guildsCommonAreaWidgets(headerVars)
|
||||
|
||||
pi := Page{"Create Guild", user, headerVars, tList, nil}
|
||||
err := templates.ExecuteTemplate(w, "guilds_create_guild.html", pi)
|
||||
if err != nil {
|
||||
return InternalError(err, w, r)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func guildsCreateGuildSubmit(w http.ResponseWriter, r *http.Request, user User) RouteError {
|
||||
// TODO: Add an approval queue mode for group creation
|
||||
if !user.Loggedin || !user.PluginPerms["CreateGuild"] {
|
||||
return NoPermissions(w, r, user)
|
||||
}
|
||||
|
||||
var guildActive = true
|
||||
var guildName = html.EscapeString(r.PostFormValue("group_name"))
|
||||
var guildDesc = html.EscapeString(r.PostFormValue("group_desc"))
|
||||
var gprivacy = r.PostFormValue("group_privacy")
|
||||
|
||||
var guildPrivacy int
|
||||
switch gprivacy {
|
||||
case "0":
|
||||
guildPrivacy = 0 // Public
|
||||
case "1":
|
||||
guildPrivacy = 1 // Protected
|
||||
case "2":
|
||||
guildPrivacy = 2 // private
|
||||
default:
|
||||
guildPrivacy = 0
|
||||
}
|
||||
|
||||
// Create the backing forum
|
||||
fid, err := fstore.Create(guildName, "", true, "")
|
||||
if err != nil {
|
||||
return InternalError(err, w, r)
|
||||
}
|
||||
|
||||
res, err := guildsCreateGuildStmt.Exec(guildName, guildDesc, guildActive, guildPrivacy, user.ID, fid)
|
||||
if err != nil {
|
||||
return InternalError(err, w, r)
|
||||
}
|
||||
lastID, err := res.LastInsertId()
|
||||
if err != nil {
|
||||
return InternalError(err, w, r)
|
||||
}
|
||||
|
||||
// Add the main backing forum to the forum list
|
||||
err = guildsAttachForum(int(lastID), fid)
|
||||
if err != nil {
|
||||
return InternalError(err, w, r)
|
||||
}
|
||||
|
||||
_, err = guildsAddMemberStmt.Exec(lastID, user.ID, 2)
|
||||
if err != nil {
|
||||
return InternalError(err, w, r)
|
||||
}
|
||||
|
||||
http.Redirect(w, r, guildsBuildGuildURL(nameToSlug(guildName), int(lastID)), http.StatusSeeOther)
|
||||
return nil
|
||||
}
|
||||
|
||||
func guildsMemberList(w http.ResponseWriter, r *http.Request, user User) RouteError {
|
||||
headerVars, ferr := UserCheck(w, r, &user)
|
||||
if ferr != nil {
|
||||
return ferr
|
||||
}
|
||||
|
||||
// SEO URLs...
|
||||
halves := strings.Split(r.URL.Path[len("/guild/members/"):], ".")
|
||||
if len(halves) < 2 {
|
||||
halves = append(halves, halves[0])
|
||||
}
|
||||
guildID, err := strconv.Atoi(halves[1])
|
||||
if err != nil {
|
||||
return PreError("Not a valid group ID", w, r)
|
||||
}
|
||||
|
||||
var guildItem = &Guild{ID: guildID}
|
||||
var mainForum int // Unused
|
||||
err = guildsGetGuildStmt.QueryRow(guildID).Scan(&guildItem.Name, &guildItem.Desc, &guildItem.Active, &guildItem.Privacy, &guildItem.Joinable, &guildItem.Owner, &guildItem.MemberCount, &mainForum, &guildItem.Backdrop, &guildItem.CreatedAt, &guildItem.LastUpdateTime)
|
||||
if err != nil {
|
||||
return LocalError("Bad group", w, r, user)
|
||||
}
|
||||
guildItem.Link = guildsBuildGuildURL(nameToSlug(guildItem.Name), guildItem.ID)
|
||||
|
||||
guildsGuildWidgets(headerVars, guildItem)
|
||||
|
||||
rows, err := guildsMemberListJoinStmt.Query(guildID)
|
||||
if err != nil && err != ErrNoRows {
|
||||
return InternalError(err, w, r)
|
||||
}
|
||||
|
||||
var guildMembers []GuildMember
|
||||
for rows.Next() {
|
||||
guildMember := GuildMember{PostCount: 0}
|
||||
err := rows.Scan(&guildMember.User.ID, &guildMember.Rank, &guildMember.PostCount, &guildMember.JoinedAt, &guildMember.User.Name, &guildMember.User.Avatar)
|
||||
if err != nil {
|
||||
return InternalError(err, w, r)
|
||||
}
|
||||
guildMember.Link = buildProfileURL(nameToSlug(guildMember.User.Name), guildMember.User.ID)
|
||||
if guildMember.User.Avatar != "" {
|
||||
if guildMember.User.Avatar[0] == '.' {
|
||||
guildMember.User.Avatar = "/uploads/avatar_" + strconv.Itoa(guildMember.User.ID) + guildMember.User.Avatar
|
||||
}
|
||||
} else {
|
||||
guildMember.User.Avatar = strings.Replace(config.Noavatar, "{id}", strconv.Itoa(guildMember.User.ID), 1)
|
||||
}
|
||||
guildMember.JoinedAt, _ = relativeTimeFromString(guildMember.JoinedAt)
|
||||
if guildItem.Owner == guildMember.User.ID {
|
||||
guildMember.RankString = "Owner"
|
||||
} else {
|
||||
switch guildMember.Rank {
|
||||
case 0:
|
||||
guildMember.RankString = "Member"
|
||||
case 1:
|
||||
guildMember.RankString = "Mod"
|
||||
case 2:
|
||||
guildMember.RankString = "Admin"
|
||||
}
|
||||
}
|
||||
guildMembers = append(guildMembers, guildMember)
|
||||
}
|
||||
err = rows.Err()
|
||||
if err != nil {
|
||||
return InternalError(err, w, r)
|
||||
}
|
||||
rows.Close()
|
||||
|
||||
pi := GuildMemberListPage{"Guild Member List", user, headerVars, guildMembers, guildItem, 0, 0}
|
||||
// A plugin with plugins. Pluginception!
|
||||
if preRenderHooks["pre_render_guilds_member_list"] != nil {
|
||||
if runPreRenderHook("pre_render_guilds_member_list", w, r, &user, &pi) {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
err = templates.ExecuteTemplate(w, "guilds_member_list.html", pi)
|
||||
if err != nil {
|
||||
return InternalError(err, w, r)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func guildsAttachForum(guildID int, fid int) error {
|
||||
_, err := guildsAttachForumStmt.Exec(guildID, fid)
|
||||
return err
|
||||
}
|
||||
|
||||
func guildsUnattachForum(fid int) error {
|
||||
_, err := guildsAttachForumStmt.Exec(fid)
|
||||
return err
|
||||
}
|
||||
|
||||
func guildsBuildGuildURL(slug string, id int) string {
|
||||
if slug == "" {
|
||||
return "/guild/" + slug + "." + strconv.Itoa(id)
|
||||
}
|
||||
return "/guild/" + strconv.Itoa(id)
|
||||
}
|
||||
|
||||
/*
|
||||
Hooks
|
||||
*/
|
||||
|
||||
func guildsPreRenderViewForum(w http.ResponseWriter, r *http.Request, user *User, data interface{}) (halt bool) {
|
||||
pi := data.(*ForumPage)
|
||||
if pi.Header.ExtData.items != nil {
|
||||
if guildData, ok := pi.Header.ExtData.items["guilds_current_group"]; ok {
|
||||
guildItem := guildData.(*Guild)
|
||||
|
||||
guildpi := GuildPage{pi.Title, pi.CurrentUser, pi.Header, pi.ItemList, pi.Forum, guildItem, pi.Page, pi.LastPage}
|
||||
err := templates.ExecuteTemplate(w, "guilds_view_guild.html", guildpi)
|
||||
if err != nil {
|
||||
LogError(err)
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func guildsTrowAssign(args ...interface{}) interface{} {
|
||||
var forum = args[1].(*Forum)
|
||||
if forum.ParentType == "guild" {
|
||||
var topicItem = args[0].(*TopicsRow)
|
||||
topicItem.ForumLink = "/guild/" + strings.TrimPrefix(topicItem.ForumLink, getForumURLPrefix())
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// TODO: It would be nice, if you could select one of the boards in the group from that drop-down rather than just the one you got linked from
|
||||
func guildsTopicCreatePreLoop(args ...interface{}) interface{} {
|
||||
var fid = args[2].(int)
|
||||
if fstore.DirtyGet(fid).ParentType == "guild" {
|
||||
var strictmode = args[5].(*bool)
|
||||
*strictmode = true
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// TODO: Add privacy options
|
||||
// TODO: Add support for multiple boards and add per-board simplified permissions
|
||||
// TODO: Take isJs into account for routes which expect JSON responses
|
||||
func guildsForumCheck(args ...interface{}) (skip bool, rerr RouteError) {
|
||||
var r = args[1].(*http.Request)
|
||||
var fid = args[3].(*int)
|
||||
var forum = fstore.DirtyGet(*fid)
|
||||
|
||||
if forum.ParentType == "guild" {
|
||||
var err error
|
||||
var w = args[0].(http.ResponseWriter)
|
||||
guildItem, ok := r.Context().Value("guilds_current_group").(*Guild)
|
||||
if !ok {
|
||||
guildItem, err = guildsGetGuild(forum.ParentID)
|
||||
if err != nil {
|
||||
return true, InternalError(errors.New("Unable to find the parent group for a forum"), w, r)
|
||||
}
|
||||
if !guildItem.Active {
|
||||
return true, NotFound(w, r)
|
||||
}
|
||||
r = r.WithContext(context.WithValue(r.Context(), "guilds_current_group", guildItem))
|
||||
}
|
||||
|
||||
var user = args[2].(*User)
|
||||
var rank int
|
||||
var posts int
|
||||
var joinedAt string
|
||||
|
||||
// TODO: Group privacy settings. For now, groups are all globally visible
|
||||
|
||||
// Clear the default group permissions
|
||||
// TODO: Do this more efficiently, doing it quick and dirty for now to get this out quickly
|
||||
overrideForumPerms(&user.Perms, false)
|
||||
user.Perms.ViewTopic = true
|
||||
|
||||
err = guildsGetMemberStmt.QueryRow(guildItem.ID, user.ID).Scan(&rank, &posts, &joinedAt)
|
||||
if err != nil && err != ErrNoRows {
|
||||
return true, InternalError(err, w, r)
|
||||
} else if err != nil {
|
||||
// TODO: Should we let admins / guests into public groups?
|
||||
return true, LocalError("You're not part of this group!", w, r, *user)
|
||||
}
|
||||
|
||||
// TODO: Implement bans properly by adding the Local Ban API in the next commit
|
||||
// TODO: How does this even work? Refactor it along with the rest of this plugin!
|
||||
if rank < 0 {
|
||||
return true, LocalError("You've been banned from this group!", w, r, *user)
|
||||
}
|
||||
|
||||
// Basic permissions for members, more complicated permissions coming in the next commit!
|
||||
if guildItem.Owner == user.ID {
|
||||
overrideForumPerms(&user.Perms, true)
|
||||
} else if rank == 0 {
|
||||
user.Perms.LikeItem = true
|
||||
user.Perms.CreateTopic = true
|
||||
user.Perms.CreateReply = true
|
||||
} else {
|
||||
overrideForumPerms(&user.Perms, true)
|
||||
}
|
||||
return true, nil
|
||||
}
|
||||
|
||||
return false, nil
|
||||
}
|
||||
|
||||
// TODO: Override redirects? I don't think this is needed quite yet
|
||||
|
||||
func guildsWidgets(args ...interface{}) interface{} {
|
||||
var zone = args[0].(string)
|
||||
var headerVars = args[2].(*HeaderVars)
|
||||
var request = args[3].(*http.Request)
|
||||
|
||||
if zone != "view_forum" {
|
||||
return false
|
||||
}
|
||||
|
||||
var forum = args[1].(*Forum)
|
||||
if forum.ParentType == "guild" {
|
||||
// This is why I hate using contexts, all the daisy chains and interface casts x.x
|
||||
guildItem, ok := request.Context().Value("guilds_current_group").(*Guild)
|
||||
if !ok {
|
||||
LogError(errors.New("Unable to find a parent group in the context data"))
|
||||
return false
|
||||
}
|
||||
|
||||
if headerVars.ExtData.items == nil {
|
||||
headerVars.ExtData.items = make(map[string]interface{})
|
||||
}
|
||||
headerVars.ExtData.items["guilds_current_group"] = guildItem
|
||||
|
||||
return guildsGuildWidgets(headerVars, guildItem)
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
|
|
@ -232,8 +232,6 @@ func writeSelects(adapter qgen.DB_Adapter) error {
|
|||
|
||||
adapter.SimpleSelect("getFullSettings", "settings", "name, content, type, constraints", "", "", "")
|
||||
|
||||
adapter.SimpleSelect("getPlugins", "plugins", "uname, active, installed", "", "", "")
|
||||
|
||||
adapter.SimpleSelect("getThemes", "themes", "uname, default", "", "", "")
|
||||
|
||||
adapter.SimpleSelect("getWidgets", "widgets", "position, side, type, active, location, data", "", "position ASC", "")
|
||||
|
|
|
@ -30,12 +30,17 @@ func inStringList(needle string, list []string) bool {
|
|||
return false
|
||||
}
|
||||
|
||||
func (group *RouteGroup) Before(line string, literal ...bool) *RouteGroup {
|
||||
var litItem bool
|
||||
if len(literal) > 0 {
|
||||
litItem = literal[0]
|
||||
func (group *RouteGroup) Before(lines ...string) *RouteGroup {
|
||||
for _, line := range lines {
|
||||
group.RunBefore = append(group.RunBefore, Runnable{line, false})
|
||||
}
|
||||
return group
|
||||
}
|
||||
|
||||
func (group *RouteGroup) LitBefore(lines ...string) *RouteGroup {
|
||||
for _, line := range lines {
|
||||
group.RunBefore = append(group.RunBefore, Runnable{line, true})
|
||||
}
|
||||
group.RunBefore = append(group.RunBefore, Runnable{line, litItem})
|
||||
return group
|
||||
}
|
||||
|
||||
|
|
|
@ -16,12 +16,17 @@ func addRoute(route *RouteImpl) {
|
|||
routeList = append(routeList, route)
|
||||
}
|
||||
|
||||
func (route *RouteImpl) Before(item string, literal ...bool) *RouteImpl {
|
||||
var litItem bool
|
||||
if len(literal) > 0 {
|
||||
litItem = literal[0]
|
||||
func (route *RouteImpl) Before(items ...string) *RouteImpl {
|
||||
for _, item := range items {
|
||||
route.RunBefore = append(route.RunBefore, Runnable{item, false})
|
||||
}
|
||||
return route
|
||||
}
|
||||
|
||||
func (route *RouteImpl) LitBefore(items ...string) *RouteImpl {
|
||||
for _, item := range items {
|
||||
route.RunBefore = append(route.RunBefore, Runnable{item, true})
|
||||
}
|
||||
route.RunBefore = append(route.RunBefore, Runnable{item, litItem})
|
||||
return route
|
||||
}
|
||||
|
||||
|
|
|
@ -4,13 +4,20 @@ type RouteSubset struct {
|
|||
RouteList []*RouteImpl
|
||||
}
|
||||
|
||||
func (set *RouteSubset) Before(line string, literal ...bool) *RouteSubset {
|
||||
var litItem bool
|
||||
if len(literal) > 0 {
|
||||
litItem = literal[0]
|
||||
}
|
||||
func (set *RouteSubset) Before(lines ...string) *RouteSubset {
|
||||
for _, line := range lines {
|
||||
for _, route := range set.RouteList {
|
||||
route.RunBefore = append(route.RunBefore, Runnable{line, litItem})
|
||||
route.RunBefore = append(route.RunBefore, Runnable{line, false})
|
||||
}
|
||||
}
|
||||
return set
|
||||
}
|
||||
|
||||
func (set *RouteSubset) LitBefore(lines ...string) *RouteSubset {
|
||||
for _, line := range lines {
|
||||
for _, route := range set.RouteList {
|
||||
route.RunBefore = append(route.RunBefore, Runnable{line, true})
|
||||
}
|
||||
}
|
||||
return set
|
||||
}
|
||||
|
|
|
@ -17,7 +17,7 @@ func routes() {
|
|||
// TODO: Reduce the number of Befores. With a new method, perhaps?
|
||||
reportGroup := newRouteGroup("/report/",
|
||||
Route("routeReportSubmit", "/report/submit/", "extra_data"),
|
||||
).Before("MemberOnly").Before("NoBanned").Before("NoSessionMismatch")
|
||||
).Before("MemberOnly", "NoBanned", "NoSessionMismatch")
|
||||
addRouteGroup(reportGroup)
|
||||
|
||||
topicGroup := newRouteGroup("/topics/",
|
||||
|
@ -34,17 +34,27 @@ func routes() {
|
|||
func buildUserRoutes() {
|
||||
userGroup := newRouteGroup("/user/")
|
||||
userGroup.Routes(
|
||||
Route("routeProfile", "/user/").Before("req.URL.Path += extra_data", true),
|
||||
Route("routeAccountOwnEditCritical", "/user/edit/critical/"),
|
||||
Route("routeAccountOwnEditCriticalSubmit", "/user/edit/critical/submit/"),
|
||||
Route("routeAccountOwnEditAvatar", "/user/edit/avatar/"),
|
||||
Route("routeAccountOwnEditAvatarSubmit", "/user/edit/avatar/submit/"),
|
||||
Route("routeAccountOwnEditUsername", "/user/edit/username/"),
|
||||
Route("routeAccountOwnEditUsernameSubmit", "/user/edit/username/submit/"),
|
||||
Route("routeAccountOwnEditEmail", "/user/edit/email/"),
|
||||
Route("routeAccountOwnEditEmailTokenSubmit", "/user/edit/token/", "extra_data"),
|
||||
Route("routeProfile", "/user/").LitBefore("req.URL.Path += extra_data"),
|
||||
Route("routeAccountEditCritical", "/user/edit/critical/"),
|
||||
Route("routeAccountEditCriticalSubmit", "/user/edit/critical/submit/").Before("NoSessionMismatch"), // TODO: Full test this
|
||||
Route("routeAccountEditAvatar", "/user/edit/avatar/"),
|
||||
Route("routeAccountEditAvatarSubmit", "/user/edit/avatar/submit/"),
|
||||
Route("routeAccountEditUsername", "/user/edit/username/"),
|
||||
Route("routeAccountEditUsernameSubmit", "/user/edit/username/submit/"), // TODO: Full test this
|
||||
Route("routeAccountEditEmail", "/user/edit/email/"),
|
||||
Route("routeAccountEditEmailTokenSubmit", "/user/edit/token/", "extra_data"),
|
||||
).Not("/user/").Before("MemberOnly")
|
||||
addRouteGroup(userGroup)
|
||||
|
||||
// TODO: Auto test and manual test these routes
|
||||
userGroup = newRouteGroup("/users/").Before("MemberOnly")
|
||||
userGroup.Routes(
|
||||
Route("routeBanSubmit", "/users/ban/submit/"),
|
||||
Route("routeUnban", "/users/unban/"),
|
||||
Route("routeActivate", "/users/activate/"),
|
||||
Route("routeIps", "/users/ips/"),
|
||||
).Not("/users/ips/").Before("NoSessionMismatch")
|
||||
addRouteGroup(userGroup)
|
||||
}
|
||||
|
||||
func buildPanelRoutes() {
|
||||
|
|
10
routes.go
10
routes.go
|
@ -17,6 +17,7 @@ import (
|
|||
"strings"
|
||||
"time"
|
||||
|
||||
"./common"
|
||||
"./query_gen/lib"
|
||||
)
|
||||
|
||||
|
@ -102,7 +103,7 @@ func routeOverview(w http.ResponseWriter, r *http.Request, user User) RouteError
|
|||
}
|
||||
BuildWidgets("overview", nil, headerVars, r)
|
||||
|
||||
pi := Page{"Overview", user, headerVars, tList, nil}
|
||||
pi := common.Page{"Overview", user, headerVars, tList, nil}
|
||||
if preRenderHooks["pre_render_overview"] != nil {
|
||||
if runPreRenderHook("pre_render_overview", w, r, &user, &pi) {
|
||||
return nil
|
||||
|
@ -128,7 +129,7 @@ func routeCustomPage(w http.ResponseWriter, r *http.Request, user User) RouteErr
|
|||
}
|
||||
BuildWidgets("custom_page", name, headerVars, r)
|
||||
|
||||
pi := Page{"Page", user, headerVars, tList, nil}
|
||||
pi := common.Page{"Page", user, headerVars, tList, nil}
|
||||
if preRenderHooks["pre_render_custom_page"] != nil {
|
||||
if runPreRenderHook("pre_render_custom_page", w, r, &user, &pi) {
|
||||
return nil
|
||||
|
@ -252,10 +253,7 @@ func routeTopics(w http.ResponseWriter, r *http.Request, user User) RouteError {
|
|||
topicItem.ForumName = forum.Name
|
||||
topicItem.ForumLink = forum.Link
|
||||
|
||||
/*topicItem.CreatedAt, err = relativeTimeFromString(topicItem.CreatedAt)
|
||||
if err != nil {
|
||||
replyItem.CreatedAt = ""
|
||||
}*/
|
||||
//topicItem.CreatedAt = relativeTime(topicItem.CreatedAt)
|
||||
topicItem.RelativeLastReplyAt = relativeTime(topicItem.LastReplyAt)
|
||||
|
||||
if vhooks["topics_topic_row_assign"] != nil {
|
||||
|
|
|
@ -7,6 +7,8 @@ import (
|
|||
"net"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"./common"
|
||||
)
|
||||
|
||||
// nolint
|
||||
|
@ -14,23 +16,17 @@ var PreRoute func(http.ResponseWriter, *http.Request) (User, bool) = preRoute
|
|||
|
||||
// TODO: Come up with a better middleware solution
|
||||
// nolint We need these types so people can tell what they are without scrolling to the bottom of the file
|
||||
var PanelUserCheck func(http.ResponseWriter, *http.Request, *User) (*HeaderVars, PanelStats, RouteError) = panelUserCheck
|
||||
var SimplePanelUserCheck func(http.ResponseWriter, *http.Request, *User) (*HeaderLite, RouteError) = simplePanelUserCheck
|
||||
var SimpleForumUserCheck func(w http.ResponseWriter, r *http.Request, user *User, fid int) (headerLite *HeaderLite, err RouteError) = simpleForumUserCheck
|
||||
var ForumUserCheck func(w http.ResponseWriter, r *http.Request, user *User, fid int) (headerVars *HeaderVars, err RouteError) = forumUserCheck
|
||||
var MemberCheck func(w http.ResponseWriter, r *http.Request, user *User) (headerVars *HeaderVars, err RouteError) = memberCheck
|
||||
var SimpleUserCheck func(w http.ResponseWriter, r *http.Request, user *User) (headerLite *HeaderLite, err RouteError) = simpleUserCheck
|
||||
var UserCheck func(w http.ResponseWriter, r *http.Request, user *User) (headerVars *HeaderVars, err RouteError) = userCheck
|
||||
|
||||
// This is mostly for errors.go, please create *HeaderVars on the spot instead of relying on this or the atomic store underlying it, if possible
|
||||
// TODO: Write a test for this
|
||||
func getDefaultHeaderVar() *HeaderVars {
|
||||
return &HeaderVars{Site: site, ThemeName: fallbackTheme}
|
||||
}
|
||||
var PanelUserCheck func(http.ResponseWriter, *http.Request, *User) (*common.HeaderVars, common.PanelStats, RouteError) = panelUserCheck
|
||||
var SimplePanelUserCheck func(http.ResponseWriter, *http.Request, *User) (*common.HeaderLite, RouteError) = simplePanelUserCheck
|
||||
var SimpleForumUserCheck func(w http.ResponseWriter, r *http.Request, user *User, fid int) (headerLite *common.HeaderLite, err RouteError) = simpleForumUserCheck
|
||||
var ForumUserCheck func(w http.ResponseWriter, r *http.Request, user *User, fid int) (headerVars *common.HeaderVars, err RouteError) = forumUserCheck
|
||||
var MemberCheck func(w http.ResponseWriter, r *http.Request, user *User) (headerVars *common.HeaderVars, err RouteError) = memberCheck
|
||||
var UserCheck func(w http.ResponseWriter, r *http.Request, user *User) (headerVars *common.HeaderVars, err RouteError) = userCheck
|
||||
var UserCheck func(w http.ResponseWriter, r *http.Request, user *User) (headerVars *common.HeaderVars, err RouteError) = userCheck
|
||||
|
||||
// TODO: Support for left sidebars and sidebars on both sides
|
||||
// http.Request is for context.Context middleware. Mostly for plugin_guilds right now
|
||||
func BuildWidgets(zone string, data interface{}, headerVars *HeaderVars, r *http.Request) {
|
||||
func BuildWidgets(zone string, data interface{}, headerVars *common.HeaderVars, r *http.Request) {
|
||||
if vhooks["intercept_build_widgets"] != nil {
|
||||
if runVhook("intercept_build_widgets", zone, data, headerVars, r).(bool) {
|
||||
return
|
||||
|
@ -53,7 +49,7 @@ func BuildWidgets(zone string, data interface{}, headerVars *HeaderVars, r *http
|
|||
}
|
||||
}
|
||||
|
||||
func simpleForumUserCheck(w http.ResponseWriter, r *http.Request, user *User, fid int) (headerLite *HeaderLite, rerr RouteError) {
|
||||
func simpleForumUserCheck(w http.ResponseWriter, r *http.Request, user *User, fid int) (headerLite *common.HeaderLite, rerr RouteError) {
|
||||
if !fstore.Exists(fid) {
|
||||
return nil, PreError("The target forum doesn't exist.", w, r)
|
||||
}
|
||||
|
@ -77,7 +73,7 @@ func simpleForumUserCheck(w http.ResponseWriter, r *http.Request, user *User, fi
|
|||
return headerLite, nil
|
||||
}
|
||||
|
||||
func forumUserCheck(w http.ResponseWriter, r *http.Request, user *User, fid int) (headerVars *HeaderVars, rerr RouteError) {
|
||||
func forumUserCheck(w http.ResponseWriter, r *http.Request, user *User, fid int) (headerVars *common.HeaderVars, rerr RouteError) {
|
||||
headerVars, rerr = UserCheck(w, r, user)
|
||||
if rerr != nil {
|
||||
return headerVars, rerr
|
||||
|
@ -130,7 +126,7 @@ func cascadeForumPerms(fperms ForumPerms, user *User) {
|
|||
|
||||
// Even if they have the right permissions, the control panel is only open to supermods+. There are many areas without subpermissions which assume that the current user is a supermod+ and admins are extremely unlikely to give these permissions to someone who isn't at-least a supermod to begin with
|
||||
// TODO: Do a panel specific theme?
|
||||
func panelUserCheck(w http.ResponseWriter, r *http.Request, user *User) (headerVars *HeaderVars, stats PanelStats, rerr RouteError) {
|
||||
func panelUserCheck(w http.ResponseWriter, r *http.Request, user *User) (headerVars *common.HeaderVars, stats common.PanelStats, rerr RouteError) {
|
||||
var themeName = defaultThemeBox.Load().(string)
|
||||
|
||||
cookie, err := r.Cookie("current_theme")
|
||||
|
@ -192,7 +188,7 @@ func panelUserCheck(w http.ResponseWriter, r *http.Request, user *User) (headerV
|
|||
return headerVars, stats, nil
|
||||
}
|
||||
|
||||
func simplePanelUserCheck(w http.ResponseWriter, r *http.Request, user *User) (headerLite *HeaderLite, rerr RouteError) {
|
||||
func simplePanelUserCheck(w http.ResponseWriter, r *http.Request, user *User) (headerLite *common.HeaderLite, rerr RouteError) {
|
||||
return &HeaderLite{
|
||||
Site: site,
|
||||
Settings: settingBox.Load().(SettingBox),
|
||||
|
@ -200,7 +196,7 @@ func simplePanelUserCheck(w http.ResponseWriter, r *http.Request, user *User) (h
|
|||
}
|
||||
|
||||
// TODO: Add this to the member routes
|
||||
func memberCheck(w http.ResponseWriter, r *http.Request, user *User) (headerVars *HeaderVars, rerr RouteError) {
|
||||
func memberCheck(w http.ResponseWriter, r *http.Request, user *User) (headerVars *common.HeaderVars, rerr RouteError) {
|
||||
headerVars, rerr = UserCheck(w, r, user)
|
||||
if !user.Loggedin {
|
||||
return headerVars, NoPermissions(w, r, *user)
|
||||
|
@ -209,7 +205,7 @@ func memberCheck(w http.ResponseWriter, r *http.Request, user *User) (headerVars
|
|||
}
|
||||
|
||||
// SimpleUserCheck is back from the grave, yay :D
|
||||
func simpleUserCheck(w http.ResponseWriter, r *http.Request, user *User) (headerLite *HeaderLite, rerr RouteError) {
|
||||
func simpleUserCheck(w http.ResponseWriter, r *http.Request, user *User) (headerLite *common.HeaderLite, rerr RouteError) {
|
||||
headerLite = &HeaderLite{
|
||||
Site: site,
|
||||
Settings: settingBox.Load().(SettingBox),
|
||||
|
@ -218,7 +214,7 @@ func simpleUserCheck(w http.ResponseWriter, r *http.Request, user *User) (header
|
|||
}
|
||||
|
||||
// TODO: Add the ability for admins to restrict certain themes to certain groups?
|
||||
func userCheck(w http.ResponseWriter, r *http.Request, user *User) (headerVars *HeaderVars, rerr RouteError) {
|
||||
func userCheck(w http.ResponseWriter, r *http.Request, user *User) (headerVars *common.HeaderVars, rerr RouteError) {
|
||||
var themeName = defaultThemeBox.Load().(string)
|
||||
|
||||
cookie, err := r.Cookie("current_theme")
|
||||
|
@ -270,7 +266,6 @@ func userCheck(w http.ResponseWriter, r *http.Request, user *User) (headerVars *
|
|||
}
|
||||
|
||||
func preRoute(w http.ResponseWriter, r *http.Request) (User, bool) {
|
||||
user, ok := func(w http.ResponseWriter, r *http.Request) (User, bool) {
|
||||
user, halt := auth.SessionCheck(w, r)
|
||||
if halt {
|
||||
return *user, false
|
||||
|
@ -279,31 +274,29 @@ func preRoute(w http.ResponseWriter, r *http.Request) (User, bool) {
|
|||
return *user, true
|
||||
}
|
||||
|
||||
h := w.Header()
|
||||
h.Set("X-Frame-Options", "deny")
|
||||
//h.Set("X-XSS-Protection", "1")
|
||||
// TODO: Set the content policy header
|
||||
return *user, true
|
||||
}(w, r)
|
||||
if !ok {
|
||||
return user, false
|
||||
}
|
||||
var usercpy = *user
|
||||
|
||||
// TODO: WIP. Refactor this to eliminate the unnecessary query
|
||||
host, _, err := net.SplitHostPort(r.RemoteAddr)
|
||||
if err != nil {
|
||||
PreError("Bad IP", w, r)
|
||||
return user, false
|
||||
return usercpy, false
|
||||
}
|
||||
if host != user.LastIP {
|
||||
_, err = stmts.updateLastIP.Exec(host, user.ID)
|
||||
if host != usercpy.LastIP {
|
||||
_, err = stmts.updateLastIP.Exec(host, usercpy.ID)
|
||||
if err != nil {
|
||||
InternalError(err, w, r)
|
||||
return user, false
|
||||
return usercpy, false
|
||||
}
|
||||
user.LastIP = host
|
||||
usercpy.LastIP = host
|
||||
}
|
||||
return user, ok
|
||||
|
||||
h := w.Header()
|
||||
h.Set("X-Frame-Options", "deny")
|
||||
//h.Set("X-XSS-Protection", "1")
|
||||
// TODO: Set the content policy header
|
||||
|
||||
return usercpy, true
|
||||
}
|
||||
|
||||
// SuperModeOnly makes sure that only super mods or higher can access the panel routes
|
||||
|
|
Loading…
Reference in New Issue