Added the Panel Debug page.
Added the mini stats for the various parts of the Control Panel in the Control Panel Menu. Fixed a crash bug in the router. The basic arithmetic template functions now work for the interpreted templates. Tweaked the connection pool to hopefully ease the number of goroutines fighting over a database connection. The Group Manager is now paginated. panel.css is now pushed by HTTP/2 Push. Fixed an issue with the padding on /forum/ and /topics/ for Shadow. Fixed a bug in the login system forcefully overriding sessions on login. All admin logins are now logged. Refactored the client-side alert loading logic. Added SimpleCount to the Query Builder.
This commit is contained in:
parent
0955a6eda2
commit
3e4cfa8888
@ -6,6 +6,7 @@ import "database/sql"
|
||||
|
||||
var db *sql.DB
|
||||
var db_version string
|
||||
var db_adapter string
|
||||
|
||||
var ErrNoRows = sql.ErrNoRows
|
||||
|
||||
|
@ -6,6 +6,9 @@ import "bytes"
|
||||
import "net/http"
|
||||
import "runtime/debug"
|
||||
|
||||
//var notfound_count_per_second int
|
||||
//var noperms_count_per_second int
|
||||
|
||||
var error_internal []byte
|
||||
var error_notfound []byte
|
||||
func init_errors() error {
|
||||
|
@ -35,6 +35,8 @@ type ForumStore interface
|
||||
//GetChildren(parentID int, parentType string) ([]*Forum,error)
|
||||
//GetFirstChild(parentID int, parentType string) (*Forum,error)
|
||||
CreateForum(forum_name string, forum_desc string, active bool, preset string) (int, error)
|
||||
|
||||
GetGlobalCount() int
|
||||
}
|
||||
|
||||
type StaticForumStore struct
|
||||
@ -45,6 +47,7 @@ type StaticForumStore struct
|
||||
|
||||
get *sql.Stmt
|
||||
get_all *sql.Stmt
|
||||
forum_count *sql.Stmt
|
||||
}
|
||||
|
||||
func NewStaticForumStore() *StaticForumStore {
|
||||
@ -56,9 +59,14 @@ func NewStaticForumStore() *StaticForumStore {
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
forum_count_stmt, err := qgen.Builder.SimpleCount("forums","name != ''","")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
return &StaticForumStore{
|
||||
get: get_stmt,
|
||||
get_all: get_all_stmt,
|
||||
forum_count: forum_count_stmt,
|
||||
}
|
||||
}
|
||||
|
||||
@ -322,6 +330,17 @@ func (sfs *StaticForumStore) fill_forum_id_gap(biggerID int, smallerID int) {
|
||||
}
|
||||
}
|
||||
|
||||
// Return the total number of forums
|
||||
// TO-DO: Get the total count of forums in the forum store minus the blanked forums rather than doing a heavy query for this?
|
||||
func (sfs *StaticForumStore) GetGlobalCount() int {
|
||||
var fcount int
|
||||
err := sfs.forum_count.QueryRow().Scan(&fcount)
|
||||
if err != nil {
|
||||
LogError(err)
|
||||
}
|
||||
return fcount
|
||||
}
|
||||
|
||||
// TO-DO: Work on MapForumStore
|
||||
|
||||
// TO-DO: Work on SqlForumStore
|
||||
|
@ -108,6 +108,7 @@ var delete_topic_stmt *sql.Stmt
|
||||
var delete_profile_reply_stmt *sql.Stmt
|
||||
var delete_forum_perms_by_forum_stmt *sql.Stmt
|
||||
var report_exists_stmt *sql.Stmt
|
||||
var group_count_stmt *sql.Stmt
|
||||
var add_forum_perms_to_forum_admins_stmt *sql.Stmt
|
||||
var add_forum_perms_to_forum_staff_stmt *sql.Stmt
|
||||
var add_forum_perms_to_forum_members_stmt *sql.Stmt
|
||||
@ -724,6 +725,12 @@ func _gen_mysql() (err error) {
|
||||
return err
|
||||
}
|
||||
|
||||
log.Print("Preparing group_count statement.")
|
||||
group_count_stmt, err = db.Prepare("SELECT COUNT(*) AS `count` FROM `users_groups`")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
log.Print("Preparing add_forum_perms_to_forum_admins statement.")
|
||||
add_forum_perms_to_forum_admins_stmt, err = db.Prepare("INSERT INTO `forums_permissions`(`gid`,`fid`,`preset`,`permissions`) SELECT `gid`, ? AS `fid`, ? AS `preset`, ? AS `permissions` FROM `users_groups` WHERE `is_admin` = 1")
|
||||
if err != nil {
|
||||
|
@ -50,7 +50,7 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
||||
// default_route(w,req)
|
||||
// return
|
||||
//}
|
||||
if req.URL.Path[0] != '/' {
|
||||
if len(req.URL.Path) == 0 || req.URL.Path[0] != '/' {
|
||||
w.WriteHeader(405)
|
||||
w.Write([]byte(""))
|
||||
return
|
||||
@ -198,6 +198,9 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
||||
case "/panel/logs/mod/":
|
||||
route_panel_logs_mod(w,req,user)
|
||||
return
|
||||
case "/panel/debug/":
|
||||
route_panel_debug(w,req,user)
|
||||
return
|
||||
default:
|
||||
route_panel(w,req,user)
|
||||
return
|
||||
|
@ -145,6 +145,7 @@ site.Url = "` + site_url + `"
|
||||
site.Port = "` + server_port + `"
|
||||
site.EnableSsl = false
|
||||
site.EnableEmails = false
|
||||
site.HasProxy = false // Cloudflare counts as this, if it's sitting in the middle
|
||||
config.SslPrivkey = ""
|
||||
config.SslFullchain = ""
|
||||
|
||||
|
61
main.go
61
main.go
@ -210,15 +210,59 @@ func init_templates() {
|
||||
}
|
||||
compile_templates()
|
||||
|
||||
// Filler functions for now...
|
||||
filler_func := func(in interface{}, in2 interface{})interface{} {
|
||||
return 1
|
||||
}
|
||||
// TO-DO: Add support for 64-bit integers
|
||||
// TO-DO: Add support for floats
|
||||
fmap := make(map[string]interface{})
|
||||
fmap["add"] = filler_func
|
||||
fmap["subtract"] = filler_func
|
||||
fmap["multiply"] = filler_func
|
||||
fmap["divide"] = filler_func
|
||||
fmap["add"] = func(left interface{}, right interface{})interface{} {
|
||||
var left_int int
|
||||
var right_int int
|
||||
switch left := left.(type) {
|
||||
case uint, uint8, uint16, int, int32: left_int = left.(int)
|
||||
}
|
||||
switch right := right.(type) {
|
||||
case uint, uint8, uint16, int, int32: right_int = right.(int)
|
||||
}
|
||||
return left_int + right_int
|
||||
}
|
||||
|
||||
fmap["subtract"] = func(left interface{}, right interface{})interface{} {
|
||||
var left_int int
|
||||
var right_int int
|
||||
switch left := left.(type) {
|
||||
case uint, uint8, uint16, int, int32: left_int = left.(int)
|
||||
}
|
||||
switch right := right.(type) {
|
||||
case uint, uint8, uint16, int, int32: right_int = right.(int)
|
||||
}
|
||||
return left_int - right_int
|
||||
}
|
||||
|
||||
fmap["multiply"] = func(left interface{}, right interface{})interface{} {
|
||||
var left_int int
|
||||
var right_int int
|
||||
switch left := left.(type) {
|
||||
case uint, uint8, uint16, int, int32: left_int = left.(int)
|
||||
}
|
||||
switch right := right.(type) {
|
||||
case uint, uint8, uint16, int, int32: right_int = right.(int)
|
||||
}
|
||||
return left_int * right_int
|
||||
}
|
||||
|
||||
fmap["divide"] = func(left interface{}, right interface{})interface{} {
|
||||
var left_int int
|
||||
var right_int int
|
||||
switch left := left.(type) {
|
||||
case uint, uint8, uint16, int, int32: left_int = left.(int)
|
||||
}
|
||||
switch right := right.(type) {
|
||||
case uint, uint8, uint16, int, int32: right_int = right.(int)
|
||||
}
|
||||
if left_int == 0 || right_int == 0 {
|
||||
return 0
|
||||
}
|
||||
return left_int / right_int
|
||||
}
|
||||
|
||||
// The interpreted templates...
|
||||
if dev.DebugMode {
|
||||
@ -377,6 +421,7 @@ func main(){
|
||||
///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/", route_api)
|
||||
//router.HandleFunc("/exit/", route_exit)
|
||||
|
9
mysql.go
9
mysql.go
@ -4,6 +4,7 @@
|
||||
package main
|
||||
|
||||
import "log"
|
||||
//import "time"
|
||||
import "database/sql"
|
||||
import _ "github.com/go-sql-driver/mysql"
|
||||
import "./query_gen/lib"
|
||||
@ -16,6 +17,10 @@ var todays_topic_count_stmt *sql.Stmt
|
||||
var todays_report_count_stmt *sql.Stmt
|
||||
var todays_newuser_count_stmt *sql.Stmt
|
||||
|
||||
func init() {
|
||||
db_adapter = "mysql"
|
||||
}
|
||||
|
||||
func _init_database() (err error) {
|
||||
var _dbpassword string
|
||||
if(db_config.Password != ""){
|
||||
@ -39,6 +44,10 @@ func _init_database() (err error) {
|
||||
|
||||
// Set the number of max open connections
|
||||
db.SetMaxOpenConns(64)
|
||||
db.SetMaxIdleConns(32)
|
||||
|
||||
// Only hold connections open for five seconds to avoid accumulating a large number of stale connections
|
||||
//db.SetConnMaxLifetime(5 * time.Second)
|
||||
|
||||
// Build the generated prepared statements, we are going to slowly move the queries over to the query generator rather than writing them all by hand, this'll make it easier for us to implement database adapters for other databases like PostgreSQL, MSSQL, SQlite, etc.
|
||||
err = _gen_mysql()
|
||||
|
99
pages.go
99
pages.go
@ -106,6 +106,39 @@ type CreateTopicPage struct
|
||||
ExtData ExtData
|
||||
}
|
||||
|
||||
type PanelStats struct
|
||||
{
|
||||
Users int
|
||||
Groups int
|
||||
Forums int
|
||||
Settings int
|
||||
Themes int
|
||||
Reports int
|
||||
}
|
||||
|
||||
type PanelPage struct
|
||||
{
|
||||
Title string
|
||||
CurrentUser User
|
||||
Header HeaderVars
|
||||
Stats PanelStats
|
||||
ItemList []interface{}
|
||||
Something interface{}
|
||||
}
|
||||
|
||||
type PanelGroupPage struct
|
||||
{
|
||||
Title string
|
||||
CurrentUser User
|
||||
Header HeaderVars
|
||||
Stats PanelStats
|
||||
ItemList []GroupAdmin
|
||||
PageList []int
|
||||
Page int
|
||||
LastPage int
|
||||
ExtData ExtData
|
||||
}
|
||||
|
||||
type GridElement struct
|
||||
{
|
||||
ID string
|
||||
@ -122,25 +155,28 @@ type PanelDashboardPage struct
|
||||
Title string
|
||||
CurrentUser User
|
||||
Header HeaderVars
|
||||
Stats PanelStats
|
||||
GridItems []GridElement
|
||||
ExtData ExtData
|
||||
}
|
||||
|
||||
type ThemesPage struct
|
||||
type PanelThemesPage struct
|
||||
{
|
||||
Title string
|
||||
CurrentUser User
|
||||
Header HeaderVars
|
||||
Stats PanelStats
|
||||
PrimaryThemes []Theme
|
||||
VariantThemes []Theme
|
||||
ExtData ExtData
|
||||
}
|
||||
|
||||
type EditGroupPage struct
|
||||
type PanelEditGroupPage struct
|
||||
{
|
||||
Title string
|
||||
CurrentUser User
|
||||
Header HeaderVars
|
||||
Stats PanelStats
|
||||
ID int
|
||||
Name string
|
||||
Tag string
|
||||
@ -155,11 +191,12 @@ type GroupForumPermPreset struct
|
||||
Preset string
|
||||
}
|
||||
|
||||
type EditForumPage struct
|
||||
type PanelEditForumPage struct
|
||||
{
|
||||
Title string
|
||||
CurrentUser User
|
||||
Header HeaderVars
|
||||
Stats PanelStats
|
||||
ID int
|
||||
Name string
|
||||
Desc string
|
||||
@ -182,11 +219,12 @@ type NameLangToggle struct
|
||||
Toggle bool
|
||||
}
|
||||
|
||||
type EditGroupPermsPage struct
|
||||
type PanelEditGroupPermsPage struct
|
||||
{
|
||||
Title string
|
||||
CurrentUser User
|
||||
Header HeaderVars
|
||||
Stats PanelStats
|
||||
ID int
|
||||
Name string
|
||||
LocalPerms []NameLangToggle
|
||||
@ -200,15 +238,28 @@ type Log struct {
|
||||
DoneAt string
|
||||
}
|
||||
|
||||
type LogsPage struct
|
||||
type PanelLogsPage struct
|
||||
{
|
||||
Title string
|
||||
CurrentUser User
|
||||
Header HeaderVars
|
||||
Stats PanelStats
|
||||
Logs []Log
|
||||
ExtData ExtData
|
||||
}
|
||||
|
||||
type PanelDebugPage struct
|
||||
{
|
||||
Title string
|
||||
CurrentUser User
|
||||
Header HeaderVars
|
||||
Stats PanelStats
|
||||
Uptime string
|
||||
OpenConns int
|
||||
DBAdapter string
|
||||
ExtData ExtData
|
||||
}
|
||||
|
||||
type PageSimple struct
|
||||
{
|
||||
Title string
|
||||
@ -747,3 +798,41 @@ func coerce_int_bytes(data []byte) (res int, length int) {
|
||||
}
|
||||
return conv, i
|
||||
}
|
||||
|
||||
// TO-DO: Write tests for this
|
||||
func paginate(count int, per_page int, maxPages int) []int {
|
||||
if count < per_page {
|
||||
return []int{1}
|
||||
}
|
||||
|
||||
var page int
|
||||
var out []int
|
||||
for current := 0; current < count; current += per_page {
|
||||
page++
|
||||
out = append(out,page)
|
||||
if len(out) >= maxPages {
|
||||
break
|
||||
}
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
// TO-DO: Write tests for this
|
||||
func page_offset(count int, page int, perPage int) (int, int, int) {
|
||||
var offset int
|
||||
lastPage := int(count / perPage) + 1
|
||||
if page > 1 {
|
||||
offset = (perPage * page) - perPage
|
||||
} else if page == -1 {
|
||||
page = lastPage
|
||||
offset = (perPage * page) - perPage
|
||||
} else {
|
||||
page = 1
|
||||
}
|
||||
|
||||
// We don't want the offset to overflow the slices, if everything's in memory
|
||||
if offset >= (count - 1) {
|
||||
offset = 0
|
||||
}
|
||||
return offset, page, lastPage
|
||||
}
|
||||
|
@ -15,7 +15,7 @@ import (
|
||||
)
|
||||
|
||||
func route_panel(w http.ResponseWriter, r *http.Request, user User){
|
||||
headerVars, ok := PanelSessionCheck(w,r,&user)
|
||||
headerVars, stats, ok := PanelSessionCheck(w,r,&user)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
@ -168,7 +168,7 @@ func route_panel(w http.ResponseWriter, r *http.Request, user User){
|
||||
gridElements = append(gridElements, GridElement{"dash-visitorsperweek","2 visitors / week",13,"grid_stat stat_disabled","","","Coming Soon!"/*"The number of unique visitors we've had over the last 7 days"*/})
|
||||
gridElements = append(gridElements, GridElement{"dash-postsperuser","5 posts / user / week",14,"grid_stat stat_disabled","","","Coming Soon!"/*"The average number of posts made by each active user over the past week"*/})
|
||||
|
||||
pi := PanelDashboardPage{"Control Panel Dashboard",user,headerVars,gridElements,extData}
|
||||
pi := PanelDashboardPage{"Control Panel Dashboard",user,headerVars,stats,gridElements,extData}
|
||||
if pre_render_hooks["pre_render_panel_dashboard"] != nil {
|
||||
if run_pre_render_hook("pre_render_panel_dashboard", w, r, &user, &pi) {
|
||||
return
|
||||
@ -181,7 +181,7 @@ func route_panel(w http.ResponseWriter, r *http.Request, user User){
|
||||
}
|
||||
|
||||
func route_panel_forums(w http.ResponseWriter, r *http.Request, user User){
|
||||
headerVars, ok := PanelSessionCheck(w,r,&user)
|
||||
headerVars, stats, ok := PanelSessionCheck(w,r,&user)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
@ -206,7 +206,7 @@ func route_panel_forums(w http.ResponseWriter, r *http.Request, user User){
|
||||
forumList = append(forumList,fadmin)
|
||||
}
|
||||
}
|
||||
pi := Page{"Forum Manager",user,headerVars,forumList,nil}
|
||||
pi := PanelPage{"Forum Manager",user,headerVars,stats,forumList,nil}
|
||||
if pre_render_hooks["pre_render_panel_forums"] != nil {
|
||||
if run_pre_render_hook("pre_render_panel_forums", w, r, &user, &pi) {
|
||||
return
|
||||
@ -254,7 +254,7 @@ func route_panel_forums_create_submit(w http.ResponseWriter, r *http.Request, us
|
||||
}
|
||||
|
||||
func route_panel_forums_delete(w http.ResponseWriter, r *http.Request, user User, sfid string){
|
||||
headerVars, ok := PanelSessionCheck(w,r,&user)
|
||||
headerVars, stats, ok := PanelSessionCheck(w,r,&user)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
@ -285,7 +285,7 @@ func route_panel_forums_delete(w http.ResponseWriter, r *http.Request, user User
|
||||
confirm_msg := "Are you sure you want to delete the '" + forum.Name + "' forum?"
|
||||
yousure := AreYouSure{"/panel/forums/delete/submit/" + strconv.Itoa(fid),confirm_msg}
|
||||
|
||||
pi := Page{"Delete Forum",user,headerVars,tList,yousure}
|
||||
pi := PanelPage{"Delete Forum",user,headerVars,stats,tList,yousure}
|
||||
if pre_render_hooks["pre_render_panel_delete_forum"] != nil {
|
||||
if run_pre_render_hook("pre_render_panel_delete_forum", w, r, &user, &pi) {
|
||||
return
|
||||
@ -330,7 +330,7 @@ func route_panel_forums_delete_submit(w http.ResponseWriter, r *http.Request, us
|
||||
}
|
||||
|
||||
func route_panel_forums_edit(w http.ResponseWriter, r *http.Request, user User, sfid string) {
|
||||
headerVars, ok := PanelSessionCheck(w,r,&user)
|
||||
headerVars, stats, ok := PanelSessionCheck(w,r,&user)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
@ -367,7 +367,7 @@ func route_panel_forums_edit(w http.ResponseWriter, r *http.Request, user User,
|
||||
gplist = append(gplist,GroupForumPermPreset{group,forum_perms_to_group_forum_preset(group.Forums[fid])})
|
||||
}
|
||||
|
||||
pi := EditForumPage{"Forum Editor",user,headerVars,forum.ID,forum.Name,forum.Desc,forum.Active,forum.Preset,gplist,extData}
|
||||
pi := PanelEditForumPage{"Forum Editor",user,headerVars,stats,forum.ID,forum.Name,forum.Desc,forum.Active,forum.Preset,gplist,extData}
|
||||
if pre_render_hooks["pre_render_panel_edit_forum"] != nil {
|
||||
if run_pre_render_hook("pre_render_panel_edit_forum", w, r, &user, &pi) {
|
||||
return
|
||||
@ -548,7 +548,7 @@ func route_panel_forums_edit_perms_submit(w http.ResponseWriter, r *http.Request
|
||||
}
|
||||
|
||||
func route_panel_settings(w http.ResponseWriter, r *http.Request, user User){
|
||||
headerVars, ok := PanelSessionCheck(w,r,&user)
|
||||
headerVars, stats, ok := PanelSessionCheck(w,r,&user)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
@ -597,7 +597,7 @@ func route_panel_settings(w http.ResponseWriter, r *http.Request, user User){
|
||||
return
|
||||
}
|
||||
|
||||
pi := Page{"Setting Manager",user,headerVars,tList,settingList}
|
||||
pi := PanelPage{"Setting Manager",user,headerVars,stats,tList,settingList}
|
||||
if pre_render_hooks["pre_render_panel_settings"] != nil {
|
||||
if run_pre_render_hook("pre_render_panel_settings", w, r, &user, &pi) {
|
||||
return
|
||||
@ -610,7 +610,7 @@ func route_panel_settings(w http.ResponseWriter, r *http.Request, user User){
|
||||
}
|
||||
|
||||
func route_panel_setting(w http.ResponseWriter, r *http.Request, user User, sname string){
|
||||
headerVars, ok := PanelSessionCheck(w,r,&user)
|
||||
headerVars, stats, ok := PanelSessionCheck(w,r,&user)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
@ -653,7 +653,7 @@ func route_panel_setting(w http.ResponseWriter, r *http.Request, user User, snam
|
||||
}
|
||||
}
|
||||
|
||||
pi := Page{"Edit Setting",user,headerVars,itemList,setting}
|
||||
pi := PanelPage{"Edit Setting",user,headerVars,stats,itemList,setting}
|
||||
if pre_render_hooks["pre_render_panel_setting"] != nil {
|
||||
if run_pre_render_hook("pre_render_panel_setting", w, r, &user, &pi) {
|
||||
return
|
||||
@ -720,7 +720,7 @@ func route_panel_setting_edit(w http.ResponseWriter, r *http.Request, user User,
|
||||
}
|
||||
|
||||
func route_panel_plugins(w http.ResponseWriter, r *http.Request, user User){
|
||||
headerVars, ok := PanelSessionCheck(w,r,&user)
|
||||
headerVars, stats, ok := PanelSessionCheck(w,r,&user)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
@ -736,7 +736,7 @@ func route_panel_plugins(w http.ResponseWriter, r *http.Request, user User){
|
||||
pluginList = append(pluginList,plugin)
|
||||
}
|
||||
|
||||
pi := Page{"Plugin Manager",user,headerVars,pluginList,nil}
|
||||
pi := PanelPage{"Plugin Manager",user,headerVars,stats,pluginList,nil}
|
||||
if pre_render_hooks["pre_render_panel_plugins"] != nil {
|
||||
if run_pre_render_hook("pre_render_panel_plugins", w, r, &user, &pi) {
|
||||
return
|
||||
@ -926,7 +926,6 @@ func route_panel_plugins_install(w http.ResponseWriter, r *http.Request, user Us
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if has_plugin {
|
||||
_, err = update_plugin_install_stmt.Exec(1,uname)
|
||||
if err != nil {
|
||||
@ -960,7 +959,7 @@ func route_panel_plugins_install(w http.ResponseWriter, r *http.Request, user Us
|
||||
}
|
||||
|
||||
func route_panel_users(w http.ResponseWriter, r *http.Request, user User){
|
||||
headerVars, ok := PanelSessionCheck(w,r,&user)
|
||||
headerVars, stats, ok := PanelSessionCheck(w,r,&user)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
@ -1004,7 +1003,7 @@ func route_panel_users(w http.ResponseWriter, r *http.Request, user User){
|
||||
return
|
||||
}
|
||||
|
||||
pi := Page{"User Manager",user,headerVars,userList,nil}
|
||||
pi := PanelPage{"User Manager",user,headerVars,stats,userList,nil}
|
||||
if pre_render_hooks["pre_render_panel_users"] != nil {
|
||||
if run_pre_render_hook("pre_render_panel_users", w, r, &user, &pi) {
|
||||
return
|
||||
@ -1017,7 +1016,7 @@ func route_panel_users(w http.ResponseWriter, r *http.Request, user User){
|
||||
}
|
||||
|
||||
func route_panel_users_edit(w http.ResponseWriter, r *http.Request, user User, suid string){
|
||||
headerVars, ok := PanelSessionCheck(w,r,&user)
|
||||
headerVars, stats, ok := PanelSessionCheck(w,r,&user)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
@ -1058,7 +1057,7 @@ func route_panel_users_edit(w http.ResponseWriter, r *http.Request, user User, s
|
||||
groupList = append(groupList,group)
|
||||
}
|
||||
|
||||
pi := Page{"User Editor",user,headerVars,groupList,targetUser}
|
||||
pi := PanelPage{"User Editor",user,headerVars,stats,groupList,targetUser}
|
||||
if pre_render_hooks["pre_render_panel_edit_user"] != nil {
|
||||
if run_pre_render_hook("pre_render_panel_edit_user", w, r, &user, &pi) {
|
||||
return
|
||||
@ -1166,13 +1165,25 @@ func route_panel_users_edit_submit(w http.ResponseWriter, r *http.Request, user
|
||||
}
|
||||
|
||||
func route_panel_groups(w http.ResponseWriter, r *http.Request, user User){
|
||||
headerVars, ok := PanelSessionCheck(w,r,&user)
|
||||
headerVars, stats, ok := PanelSessionCheck(w,r,&user)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
var groupList []interface{}
|
||||
for _, group := range groups[1:] {
|
||||
page, _ := strconv.Atoi(r.FormValue("page"))
|
||||
perPage := 9
|
||||
offset, page, lastPage := page_offset(stats.Groups, page, perPage)
|
||||
|
||||
// Skip the System group
|
||||
offset++
|
||||
|
||||
var count int
|
||||
var groupList []GroupAdmin
|
||||
for _, group := range groups[offset:] {
|
||||
if count == perPage {
|
||||
break
|
||||
}
|
||||
|
||||
var rank string
|
||||
var rank_class string
|
||||
var can_edit bool
|
||||
@ -1197,10 +1208,12 @@ func route_panel_groups(w http.ResponseWriter, r *http.Request, user User){
|
||||
|
||||
can_edit = user.Perms.EditGroup && (!group.Is_Admin || user.Perms.EditGroupAdmin) && (!group.Is_Mod || user.Perms.EditGroupSuperMod)
|
||||
groupList = append(groupList, GroupAdmin{group.ID,group.Name,rank,rank_class,can_edit,can_delete})
|
||||
count++
|
||||
}
|
||||
//log.Printf("groupList: %+v\n", groupList)
|
||||
|
||||
pi := Page{"Group Manager",user,headerVars,groupList,nil}
|
||||
pageList := paginate(stats.Groups, perPage, 5)
|
||||
pi := PanelGroupPage{"Group Manager",user,headerVars,stats,groupList,pageList,page,lastPage,extData}
|
||||
if pre_render_hooks["pre_render_panel_groups"] != nil {
|
||||
if run_pre_render_hook("pre_render_panel_groups", w, r, &user, &pi) {
|
||||
return
|
||||
@ -1214,7 +1227,7 @@ func route_panel_groups(w http.ResponseWriter, r *http.Request, user User){
|
||||
}
|
||||
|
||||
func route_panel_groups_edit(w http.ResponseWriter, r *http.Request, user User, sgid string){
|
||||
headerVars, ok := PanelSessionCheck(w,r,&user)
|
||||
headerVars, stats, ok := PanelSessionCheck(w,r,&user)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
@ -1260,7 +1273,7 @@ func route_panel_groups_edit(w http.ResponseWriter, r *http.Request, user User,
|
||||
|
||||
disable_rank := !user.Perms.EditGroupGlobalPerms || (group.ID == 6)
|
||||
|
||||
pi := EditGroupPage{"Group Editor",user,headerVars,group.ID,group.Name,group.Tag,rank,disable_rank,extData}
|
||||
pi := PanelEditGroupPage{"Group Editor",user,headerVars,stats,group.ID,group.Name,group.Tag,rank,disable_rank,extData}
|
||||
if pre_render_hooks["pre_render_panel_edit_group"] != nil {
|
||||
if run_pre_render_hook("pre_render_panel_edit_group", w, r, &user, &pi) {
|
||||
return
|
||||
@ -1273,7 +1286,7 @@ func route_panel_groups_edit(w http.ResponseWriter, r *http.Request, user User,
|
||||
}
|
||||
|
||||
func route_panel_groups_edit_perms(w http.ResponseWriter, r *http.Request, user User, sgid string){
|
||||
headerVars, ok := PanelSessionCheck(w,r,&user)
|
||||
headerVars, stats, ok := PanelSessionCheck(w,r,&user)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
@ -1338,7 +1351,7 @@ func route_panel_groups_edit_perms(w http.ResponseWriter, r *http.Request, user
|
||||
globalPerms = append(globalPerms, NameLangToggle{"ViewAdminLogs",GetGlobalPermPhrase("ViewAdminLogs"),group.Perms.ViewAdminLogs})
|
||||
globalPerms = append(globalPerms, NameLangToggle{"ViewIPs",GetGlobalPermPhrase("ViewIPs"),group.Perms.ViewIPs})
|
||||
|
||||
pi := EditGroupPermsPage{"Group Editor",user,headerVars,group.ID,group.Name,localPerms,globalPerms,extData}
|
||||
pi := PanelEditGroupPermsPage{"Group Editor",user,headerVars,stats,group.ID,group.Name,localPerms,globalPerms,extData}
|
||||
if pre_render_hooks["pre_render_panel_edit_group_perms"] != nil {
|
||||
if run_pre_render_hook("pre_render_panel_edit_group_perms", w, r, &user, &pi) {
|
||||
return
|
||||
@ -1609,7 +1622,7 @@ func route_panel_groups_create_submit(w http.ResponseWriter, r *http.Request, us
|
||||
}
|
||||
|
||||
func route_panel_themes(w http.ResponseWriter, r *http.Request, user User){
|
||||
headerVars, ok := PanelSessionCheck(w,r,&user)
|
||||
headerVars, stats, ok := PanelSessionCheck(w,r,&user)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
@ -1631,7 +1644,7 @@ func route_panel_themes(w http.ResponseWriter, r *http.Request, user User){
|
||||
|
||||
}
|
||||
|
||||
pi := ThemesPage{"Theme Manager",user,headerVars,pThemeList,vThemeList,extData}
|
||||
pi := PanelThemesPage{"Theme Manager",user,headerVars,stats,pThemeList,vThemeList,extData}
|
||||
if pre_render_hooks["pre_render_panel_themes"] != nil {
|
||||
if run_pre_render_hook("pre_render_panel_themes", w, r, &user, &pi) {
|
||||
return
|
||||
@ -1722,7 +1735,7 @@ func route_panel_themes_default(w http.ResponseWriter, r *http.Request, user Use
|
||||
}
|
||||
|
||||
func route_panel_logs_mod(w http.ResponseWriter, r *http.Request, user User){
|
||||
headerVars, ok := PanelSessionCheck(w,r,&user)
|
||||
headerVars, stats, ok := PanelSessionCheck(w,r,&user)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
@ -1813,7 +1826,7 @@ func route_panel_logs_mod(w http.ResponseWriter, r *http.Request, user User){
|
||||
return
|
||||
}
|
||||
|
||||
pi := LogsPage{"Moderation Logs",user,headerVars,logs,extData}
|
||||
pi := PanelLogsPage{"Moderation Logs",user,headerVars,stats,logs,extData}
|
||||
if pre_render_hooks["pre_render_panel_mod_log"] != nil {
|
||||
if run_pre_render_hook("pre_render_panel_mod_log", w, r, &user, &pi) {
|
||||
return
|
||||
@ -1821,6 +1834,28 @@ func route_panel_logs_mod(w http.ResponseWriter, r *http.Request, user User){
|
||||
}
|
||||
err = templates.ExecuteTemplate(w,"panel-modlogs.html",pi)
|
||||
if err != nil {
|
||||
log.Print(err)
|
||||
InternalError(err,w)
|
||||
}
|
||||
}
|
||||
|
||||
func route_panel_debug(w http.ResponseWriter, r *http.Request, user User) {
|
||||
headerVars, stats, ok := PanelSessionCheck(w,r,&user)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
if !user.Is_Admin {
|
||||
NoPermissions(w,r,user)
|
||||
return
|
||||
}
|
||||
|
||||
uptime := "..."
|
||||
db_stats := db.Stats()
|
||||
open_conn_count := db_stats.OpenConnections
|
||||
// Disk I/O?
|
||||
|
||||
pi := PanelDebugPage{"Debug",user,headerVars,stats,uptime,open_conn_count,db_adapter,extData}
|
||||
err := templates.ExecuteTemplate(w,"panel-debug.html",pi)
|
||||
if err != nil {
|
||||
InternalError(err,w)
|
||||
}
|
||||
}
|
||||
|
9
pgsql.go
9
pgsql.go
@ -5,6 +5,7 @@
|
||||
package main
|
||||
|
||||
import "strings"
|
||||
//import "time"
|
||||
import "database/sql"
|
||||
import _ "github.com/lib/pq"
|
||||
import "./query_gen/lib"
|
||||
@ -18,6 +19,10 @@ var todays_topic_count_stmt *sql.Stmt
|
||||
var todays_report_count_stmt *sql.Stmt
|
||||
var todays_newuser_count_stmt *sql.Stmt
|
||||
|
||||
func init() {
|
||||
db_adapter = "pgsql"
|
||||
}
|
||||
|
||||
func _init_database() (err error) {
|
||||
// TO-DO: Investigate connect_timeout to see what it does exactly and whether it's relevant to us
|
||||
var _dbpassword string
|
||||
@ -40,6 +45,10 @@ func _init_database() (err error) {
|
||||
|
||||
// Set the number of max open connections. How many do we need? Might need to do some tests.
|
||||
db.SetMaxOpenConns(64)
|
||||
db.SetMaxIdleConns(32)
|
||||
|
||||
// Only hold connections open for five seconds to avoid accumulating a large number of stale connections
|
||||
//db.SetConnMaxLifetime(5 * time.Second)
|
||||
|
||||
err = _gen_pgsql()
|
||||
if err != nil {
|
||||
|
@ -13,14 +13,16 @@ function post_link(event)
|
||||
|
||||
function load_alerts(menu_alerts)
|
||||
{
|
||||
menu_alerts.find(".alert_counter").text("");
|
||||
var alertListNode = menu_alerts.getElementsByClassName("alertList")[0];
|
||||
var alertCounterNode = menu_alerts.getElementsByClassName("alert_counter")[0];
|
||||
alertCounterNode.textContent = "";
|
||||
$.ajax({
|
||||
type: 'get',
|
||||
dataType: 'json',
|
||||
url:'/api/?action=get&module=alerts&format=json',
|
||||
success: function(data) {
|
||||
if("errmsg" in data) {
|
||||
menu_alerts.find(".alertList").html("<div class='alertItem'>"+data.errmsg+"</div>");
|
||||
alertListNode.innerHTML = "<div class='alertItem'>"+data.errmsg+"</div>";
|
||||
return;
|
||||
}
|
||||
|
||||
@ -55,8 +57,13 @@ function load_alerts(menu_alerts)
|
||||
//menu_alerts.removeClass("hasAvatars");
|
||||
//if(anyAvatar) menu_alerts.addClass("hasAvatars");
|
||||
}
|
||||
menu_alerts.find(".alertList").html(alist);
|
||||
if(data.msgCount != 0) menu_alerts.find(".alert_counter").text(data.msgCount);
|
||||
alertListNode.innerHTML = alist;
|
||||
if(data.msgCount != 0) {
|
||||
alertCounterNode.textContent = data.msgCount;
|
||||
menu_alerts.classList.add("has_alerts");
|
||||
} else {
|
||||
menu_alerts.classList.remove("has_alerts");
|
||||
}
|
||||
alertCount = data.msgCount;
|
||||
},
|
||||
error: function(magic,theStatus,error) {
|
||||
@ -69,36 +76,36 @@ function load_alerts(menu_alerts)
|
||||
console.log(magic.responseText);
|
||||
console.log(err);
|
||||
}
|
||||
menu_alerts.find(".alertList").html("<div class='alertItem'>"+errtxt+"</div>");
|
||||
alertListNode.innerHTML = "<div class='alertItem'>"+errtxt+"</div>";
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
$(document).ready(function(){
|
||||
function SplitN(data,ch,n) {
|
||||
var out = [];
|
||||
if(data.length == 0) return out;
|
||||
function SplitN(data,ch,n) {
|
||||
var out = [];
|
||||
if(data.length == 0) return out;
|
||||
|
||||
var lastIndex = 0;
|
||||
var j = 0;
|
||||
var lastN = 1;
|
||||
for(var i = 0; i < data.length; i++) {
|
||||
if(data[i] == ch) {
|
||||
out[j++] = data.substring(lastIndex,i);
|
||||
lastIndex = i;
|
||||
if(lastN == n) break;
|
||||
lastN++;
|
||||
}
|
||||
var lastIndex = 0;
|
||||
var j = 0;
|
||||
var lastN = 1;
|
||||
for(var i = 0; i < data.length; i++) {
|
||||
if(data[i] == ch) {
|
||||
out[j++] = data.substring(lastIndex,i);
|
||||
lastIndex = i;
|
||||
if(lastN == n) break;
|
||||
lastN++;
|
||||
}
|
||||
if(data.length > lastIndex) out[out.length - 1] += data.substring(lastIndex);
|
||||
return out;
|
||||
}
|
||||
if(data.length > lastIndex) out[out.length - 1] += data.substring(lastIndex);
|
||||
return out;
|
||||
}
|
||||
|
||||
$(document).ready(function(){
|
||||
if(window["WebSocket"]) {
|
||||
if(window.location.protocol == "https:")
|
||||
conn = new WebSocket("wss://" + document.location.host + "/ws/");
|
||||
else conn = new WebSocket("ws://" + document.location.host + "/ws/");
|
||||
|
||||
|
||||
conn.onopen = function() {
|
||||
conn.send("page " + document.location.pathname + '\r');
|
||||
}
|
||||
@ -342,14 +349,15 @@ $(document).ready(function(){
|
||||
}
|
||||
});
|
||||
|
||||
$(".menu_alerts").ready(function(){
|
||||
load_alerts($(this));
|
||||
});
|
||||
var alert_menu_list = document.getElementsByClassName("menu_alerts");
|
||||
for(var i = 0; i < alert_menu_list.length; i++) {
|
||||
load_alerts(alert_menu_list[i]);
|
||||
}
|
||||
|
||||
$(".menu_alerts").click(function(event) {
|
||||
event.stopPropagation();
|
||||
if($(this).hasClass("selectedAlert")) return;
|
||||
if(!conn) load_alerts($(this));
|
||||
if(!conn) load_alerts(this);
|
||||
this.className += " selectedAlert";
|
||||
document.getElementById("back").className += " alertActive"
|
||||
});
|
||||
|
@ -1,7 +1,7 @@
|
||||
/* WIP Under Construction */
|
||||
package qgen
|
||||
|
||||
//import "fmt"
|
||||
//import "log"
|
||||
import "database/sql"
|
||||
|
||||
var Builder *builder
|
||||
@ -38,12 +38,21 @@ func (build *builder) SimpleSelect(table string, columns string, where string, o
|
||||
return build.conn.Prepare(res)
|
||||
}
|
||||
|
||||
func (build *builder) SimpleCount(table string, where string, limit string) (stmt *sql.Stmt, err error) {
|
||||
res, err := build.adapter.SimpleCount("_builder", table, where, limit)
|
||||
if err != nil {
|
||||
return stmt, err
|
||||
}
|
||||
//log.Print("res",res)
|
||||
return build.conn.Prepare(res)
|
||||
}
|
||||
|
||||
func (build *builder) SimpleLeftJoin(table1 string, table2 string, columns string, joiners string, where string, orderby string, limit string) (stmt *sql.Stmt, err error) {
|
||||
res, err := build.adapter.SimpleLeftJoin("_builder", table1, table2, columns, joiners, where, orderby, limit)
|
||||
if err != nil {
|
||||
return stmt, err
|
||||
}
|
||||
//fmt.Println("res",res)
|
||||
//log.Print("res",res)
|
||||
return build.conn.Prepare(res)
|
||||
}
|
||||
|
||||
@ -52,7 +61,7 @@ func (build *builder) SimpleInnerJoin(table1 string, table2 string, columns stri
|
||||
if err != nil {
|
||||
return stmt, err
|
||||
}
|
||||
//fmt.Println("res",res)
|
||||
//log.Print("res",res)
|
||||
return build.conn.Prepare(res)
|
||||
}
|
||||
|
||||
|
@ -393,6 +393,8 @@ func write_deletes(adapter qgen.DB_Adapter) error {
|
||||
func write_simple_counts(adapter qgen.DB_Adapter) error {
|
||||
adapter.SimpleCount("report_exists","topics","data = ? AND data != '' AND parentID = 1","")
|
||||
|
||||
adapter.SimpleCount("group_count","users_groups","","")
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -31,7 +31,7 @@ func (router *Router) HandleFunc(pattern string, handle func(http.ResponseWriter
|
||||
}
|
||||
|
||||
func (router *Router) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
||||
if req.URL.Path[0] != '/' {
|
||||
if len(req.URL.Path) == 0 || req.URL.Path[0] != '/' {
|
||||
w.WriteHeader(405)
|
||||
w.Write([]byte(""))
|
||||
return
|
||||
|
@ -127,7 +127,7 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
||||
// default_route(w,req)
|
||||
// return
|
||||
//}
|
||||
if req.URL.Path[0] != '/' {
|
||||
if len(req.URL.Path) == 0 || req.URL.Path[0] != '/' {
|
||||
w.WriteHeader(405)
|
||||
w.Write([]byte(""))
|
||||
return
|
||||
|
@ -75,5 +75,6 @@ func routes() {
|
||||
Route{"route_panel_groups_create_submit","/panel/groups/create/","",[]string{}},
|
||||
|
||||
Route{"route_panel_logs_mod","/panel/logs/mod/","",[]string{}},
|
||||
Route{"route_panel_debug","/panel/debug/","",[]string{}},
|
||||
)
|
||||
}
|
||||
|
23
routes.go
23
routes.go
@ -62,6 +62,9 @@ func route_static(w http.ResponseWriter, r *http.Request){
|
||||
if strings.Contains(r.Header.Get("Accept-Encoding"),"gzip") {
|
||||
h.Set("Content-Encoding","gzip")
|
||||
h.Set("Content-Length", strconv.FormatInt(file.GzipLength, 10))
|
||||
if site.HasProxy {
|
||||
h.Set("Vary","Accept-Encoding")
|
||||
}
|
||||
io.Copy(w, bytes.NewReader(file.GzipData)) // Use w.Write instead?
|
||||
} else {
|
||||
h.Set("Content-Length", strconv.FormatInt(file.Length, 10)) // Avoid doing a type conversion every time?
|
||||
@ -139,6 +142,7 @@ func route_custom_page(w http.ResponseWriter, r *http.Request, user User){
|
||||
}
|
||||
}
|
||||
|
||||
// TO-DO: Paginate this
|
||||
func route_topics(w http.ResponseWriter, r *http.Request, user User){
|
||||
headerVars, ok := SessionCheck(w,r,&user)
|
||||
if !ok {
|
||||
@ -1726,6 +1730,9 @@ func route_login(w http.ResponseWriter, r *http.Request, user User) {
|
||||
templates.ExecuteTemplate(w,"login.html",pi)
|
||||
}
|
||||
|
||||
// TO-DO: Log failed attempted logins?
|
||||
// TO-DO: Lock IPS out if they have too many failed attempts?
|
||||
// TO-DO: Log unusual countries in comparison to the country a user usually logs in from? Alert the user about this?
|
||||
func route_login_submit(w http.ResponseWriter, r *http.Request, user User) {
|
||||
if user.Loggedin {
|
||||
LocalError("You're already logged in.",w,r,user)
|
||||
@ -1743,6 +1750,13 @@ func route_login_submit(w http.ResponseWriter, r *http.Request, user User) {
|
||||
return
|
||||
}
|
||||
|
||||
userPtr, err := users.CascadeGet(uid)
|
||||
if err != nil {
|
||||
LocalError("Bad account",w,r,user)
|
||||
return
|
||||
}
|
||||
user = *userPtr
|
||||
|
||||
var session string
|
||||
if user.Session == "" {
|
||||
session, err = auth.CreateSession(uid)
|
||||
@ -1755,6 +1769,15 @@ func route_login_submit(w http.ResponseWriter, r *http.Request, user User) {
|
||||
}
|
||||
|
||||
auth.SetCookies(w,uid,session)
|
||||
if user.Is_Admin {
|
||||
// Is this error check reundant? We already check for the error in PreRoute for the same IP
|
||||
host, _, err := net.SplitHostPort(r.RemoteAddr)
|
||||
if err != nil {
|
||||
InternalError(err,w)
|
||||
return
|
||||
}
|
||||
log.Print("#" + strconv.Itoa(uid) + " has logged in with IP " + host)
|
||||
}
|
||||
http.Redirect(w,r,"/",http.StatusSeeOther)
|
||||
}
|
||||
|
||||
|
1
site.go
1
site.go
@ -15,6 +15,7 @@ type Site struct
|
||||
Port string
|
||||
EnableSsl bool
|
||||
EnableEmails bool
|
||||
HasProxy bool
|
||||
}
|
||||
|
||||
type DB_Config struct
|
||||
|
17
templates/panel-debug.html
Normal file
17
templates/panel-debug.html
Normal file
@ -0,0 +1,17 @@
|
||||
{{template "header.html" . }}
|
||||
{{template "panel-menu.html" . }}
|
||||
<div id="panel_dashboard_right" class="colstack_right">
|
||||
<div class="colstack_item colstack_head">
|
||||
<div class="rowitem"><a>Debug</a></div>
|
||||
</div>
|
||||
<div id="panel_debug" class="colstack_grid">
|
||||
<div class="grid_item grid_stat">Uptime</div>
|
||||
<div class="grid_item grid_stat">Open DB Conns</div>
|
||||
<div class="grid_item grid_stat">Adapter</div>
|
||||
|
||||
<div class="grid_item grid_stat">{{.Uptime}}</div>
|
||||
<div class="grid_item grid_stat">{{.OpenConns}}</div>
|
||||
<div class="grid_item grid_stat">{{.DBAdapter}}</div>
|
||||
</div>
|
||||
</div>
|
||||
{{template "footer.html" . }}
|
@ -18,6 +18,15 @@
|
||||
</div>
|
||||
{{end}}
|
||||
</div>
|
||||
{{if gt .LastPage 1}}
|
||||
<div class="pageset">
|
||||
{{if gt .Page 1}}<div class="pageitem"><a href="?page={{subtract .Page 1}}">Prev</a></div>{{end}}
|
||||
{{range .PageList}}
|
||||
<div class="pageitem"><a href="?page={{.}}">{{.}}</a></div>
|
||||
{{end}}
|
||||
{{if ne .LastPage .Page}}<div class="pageitem"><a href="?page={{add .Page 1}}">Next</a></div>{{end}}
|
||||
</div>
|
||||
{{end}}
|
||||
<div class="colstack_item colstack_head">
|
||||
<div class="rowitem"><a>Create Group</a></div>
|
||||
</div>
|
||||
|
@ -2,12 +2,36 @@
|
||||
<div class="rowitem"><a href="/panel/">Control Panel</a></div>
|
||||
</div>
|
||||
<div class="colstack_item rowmenu">
|
||||
<div class="rowitem passive"><a href="/panel/users/">Users</a></div>
|
||||
<div class="rowitem passive"><a href="/panel/groups/">Groups</a></div>
|
||||
{{if .CurrentUser.Perms.ManageForums}}<div class="rowitem passive"><a href="/panel/forums/">Forums</a></div>{{end}}
|
||||
{{if .CurrentUser.Perms.EditSettings}}<div class="rowitem passive"><a href="/panel/settings/">Settings</a></div>{{end}}
|
||||
{{if .CurrentUser.Perms.ManageThemes}}<div class="rowitem passive"><a href="/panel/themes/">Themes</a></div>{{end}}
|
||||
{{if .CurrentUser.Perms.ManagePlugins}}<div class="rowitem passive"><a href="/panel/plugins/">Plugins</a></div>{{end}}
|
||||
<div class="rowitem passive"><a href="/forum/1">Reports</a></div>
|
||||
<div class="rowitem passive"><a href="/panel/logs/mod/">Logs</a></div>
|
||||
<div class="rowitem passive">
|
||||
<a href="/panel/users/">Users</a> <a class="menu_stats" href="#">({{.Stats.Users}})</a>
|
||||
</div>
|
||||
<div class="rowitem passive">
|
||||
<a href="/panel/groups/">Groups</a> <a class="menu_stats" href="#">({{.Stats.Groups}})</a>
|
||||
</div>
|
||||
{{if .CurrentUser.Perms.ManageForums}}<div class="rowitem passive">
|
||||
<a href="/panel/forums/">Forums</a> <a class="menu_stats" href="#">({{.Stats.Forums}})</a>
|
||||
</div>{{end}}
|
||||
{{if .CurrentUser.Perms.EditSettings}}<div class="rowitem passive">
|
||||
<a href="/panel/settings/">Settings</a> <a class="menu_stats" href="#">({{.Stats.Settings}})</a>
|
||||
</div>{{end}}
|
||||
{{if .CurrentUser.Perms.ManageThemes}}<div class="rowitem passive">
|
||||
<a href="/panel/themes/">Themes</a> <a class="menu_stats" href="#">({{.Stats.Themes}})</a>
|
||||
</div>{{end}}
|
||||
<div class="rowitem passive">
|
||||
<a href="/forum/1">Reports</a> <a class="menu_stats" href="#">({{.Stats.Reports}})</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="colstack_item colstack_head">
|
||||
<div class="rowitem"><a href="#">System</a></div>
|
||||
</div>
|
||||
<div class="colstack_item rowmenu">
|
||||
{{if .CurrentUser.Perms.ManagePlugins}}<div class="rowitem passive">
|
||||
<a href="/panel/plugins/">Plugins</a>
|
||||
</div>{{end}}
|
||||
<div class="rowitem passive">
|
||||
<a href="/panel/logs/mod/">Logs</a>
|
||||
</div>
|
||||
{{if .CurrentUser.Is_Admin}}<div class="rowitem passive">
|
||||
<a href="/panel/debug/">Debug</a>
|
||||
</div>{{end}}
|
||||
</div>
|
||||
|
@ -330,6 +330,23 @@ textarea.large {
|
||||
font-size: 13px;
|
||||
padding: 10px;
|
||||
}
|
||||
.menu_stats {
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
/* Mini paginators aka panel paginators */
|
||||
.pageset {
|
||||
margin-top: 4px;
|
||||
clear: both;
|
||||
height: 32px;
|
||||
}
|
||||
.pageitem {
|
||||
background-color: rgb(61,61,61);
|
||||
padding: 10px;
|
||||
margin-right: 4px;
|
||||
font-size: 13px;
|
||||
float: left;
|
||||
}
|
||||
|
||||
.rowlist.bgavatars .rowitem {
|
||||
background-repeat: no-repeat;
|
||||
@ -429,6 +446,9 @@ input, select, textarea {
|
||||
margin-left: 8px;
|
||||
width: 108px;
|
||||
}
|
||||
.topic_list .rowitem:last-child {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
/* Profiles */
|
||||
#profile_left_lane {
|
||||
|
21
user.go
21
user.go
@ -14,7 +14,7 @@ import (
|
||||
var guest_user User = User{ID:0,Link:"#",Group:6,Perms:GuestPerms}
|
||||
|
||||
var PreRoute func(http.ResponseWriter, *http.Request) (User,bool) = _pre_route
|
||||
var PanelSessionCheck func(http.ResponseWriter, *http.Request, *User) (HeaderVars,bool) = _panel_session_check
|
||||
var PanelSessionCheck func(http.ResponseWriter, *http.Request, *User) (HeaderVars,PanelStats,bool) = _panel_session_check
|
||||
var SimplePanelSessionCheck func(http.ResponseWriter, *http.Request, *User) bool = _simple_panel_session_check
|
||||
var SimpleForumSessionCheck func(w http.ResponseWriter, r *http.Request, user *User, fid int) (success bool) = _simple_forum_session_check
|
||||
var ForumSessionCheck func(w http.ResponseWriter, r *http.Request, user *User, fid int) (headerVars HeaderVars, success bool) = _forum_session_check
|
||||
@ -208,11 +208,11 @@ func _forum_session_check(w http.ResponseWriter, r *http.Request, user *User, fi
|
||||
}
|
||||
|
||||
// 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
|
||||
func _panel_session_check(w http.ResponseWriter, r *http.Request, user *User) (headerVars HeaderVars, success bool) {
|
||||
func _panel_session_check(w http.ResponseWriter, r *http.Request, user *User) (headerVars HeaderVars, stats PanelStats, success bool) {
|
||||
headerVars.Site = site
|
||||
if !user.Is_Super_Mod {
|
||||
NoPermissions(w,r,*user)
|
||||
return headerVars, false
|
||||
return headerVars, stats, false
|
||||
}
|
||||
|
||||
headerVars.Stylesheets = append(headerVars.Stylesheets,"panel.css")
|
||||
@ -233,9 +233,22 @@ func _panel_session_check(w http.ResponseWriter, r *http.Request, user *User) (h
|
||||
}
|
||||
}
|
||||
|
||||
err := group_count_stmt.QueryRow().Scan(&stats.Groups)
|
||||
if err != nil {
|
||||
InternalError(err,w)
|
||||
return headerVars, stats, false
|
||||
}
|
||||
|
||||
stats.Users = users.GetGlobalCount()
|
||||
stats.Forums = fstore.GetGlobalCount() // TO-DO: Stop it from showing the blanked forums
|
||||
stats.Settings = len(settings) // TO-DO: IS this racey?
|
||||
stats.Themes = len(themes)
|
||||
stats.Reports = 0 // TO-DO: Do the report count. Only show open threads?
|
||||
|
||||
pusher, ok := w.(http.Pusher)
|
||||
if ok {
|
||||
pusher.Push("/static/main.css", nil)
|
||||
pusher.Push("/static/panel.css", nil)
|
||||
pusher.Push("/static/global.js", nil)
|
||||
pusher.Push("/static/jquery-3.1.1.min.js", nil)
|
||||
// TO-DO: Push the theme CSS files
|
||||
@ -243,7 +256,7 @@ func _panel_session_check(w http.ResponseWriter, r *http.Request, user *User) (h
|
||||
// TO-DO: Push avatars?
|
||||
}
|
||||
|
||||
return headerVars, true
|
||||
return headerVars, stats, true
|
||||
}
|
||||
func _simple_panel_session_check(w http.ResponseWriter, r *http.Request, user *User) (success bool) {
|
||||
if !user.Is_Super_Mod {
|
||||
|
@ -32,6 +32,7 @@ type UserStore interface {
|
||||
CreateUser(username string, password string, email string, group int, active int) (int, error)
|
||||
GetLength() int
|
||||
GetCapacity() int
|
||||
GetGlobalCount() int
|
||||
}
|
||||
|
||||
type MemoryUserStore struct {
|
||||
@ -41,6 +42,7 @@ type MemoryUserStore struct {
|
||||
get *sql.Stmt
|
||||
register *sql.Stmt
|
||||
username_exists *sql.Stmt
|
||||
user_count *sql.Stmt
|
||||
sync.RWMutex
|
||||
}
|
||||
|
||||
@ -62,12 +64,18 @@ func NewMemoryUserStore(capacity int) *MemoryUserStore {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
user_count_stmt, err := qgen.Builder.SimpleCount("users","","")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
return &MemoryUserStore{
|
||||
items:make(map[int]*User),
|
||||
capacity:capacity,
|
||||
get:get_stmt,
|
||||
register:register_stmt,
|
||||
username_exists:username_exists_stmt,
|
||||
user_count:user_count_stmt,
|
||||
}
|
||||
}
|
||||
|
||||
@ -352,10 +360,21 @@ func (sus *MemoryUserStore) GetCapacity() int {
|
||||
return sus.capacity
|
||||
}
|
||||
|
||||
// Return the total number of users registered on the forums
|
||||
func (sus *MemoryUserStore) GetGlobalCount() int {
|
||||
var ucount int
|
||||
err := sus.user_count.QueryRow().Scan(&ucount)
|
||||
if err != nil {
|
||||
LogError(err)
|
||||
}
|
||||
return ucount
|
||||
}
|
||||
|
||||
type SqlUserStore struct {
|
||||
get *sql.Stmt
|
||||
register *sql.Stmt
|
||||
username_exists *sql.Stmt
|
||||
user_count *sql.Stmt
|
||||
}
|
||||
|
||||
func NewSqlUserStore() *SqlUserStore {
|
||||
@ -376,10 +395,16 @@ func NewSqlUserStore() *SqlUserStore {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
user_count_stmt, err := qgen.Builder.SimpleCount("users","","")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
return &SqlUserStore{
|
||||
get:get_stmt,
|
||||
register:register_stmt,
|
||||
username_exists:username_exists_stmt,
|
||||
get: get_stmt,
|
||||
register: register_stmt,
|
||||
username_exists: username_exists_stmt,
|
||||
user_count: user_count_stmt,
|
||||
}
|
||||
}
|
||||
|
||||
@ -551,6 +576,20 @@ func (sus *SqlUserStore) GetCapacity() int {
|
||||
return 0
|
||||
}
|
||||
|
||||
// Return the total number of users registered on the forums
|
||||
func (sus *SqlUserStore) GetLength() int {
|
||||
return 0 // Return the total number of users registered on the forums?
|
||||
var ucount int
|
||||
err := sus.user_count.QueryRow().Scan(&ucount)
|
||||
if err != nil {
|
||||
LogError(err)
|
||||
}
|
||||
return ucount
|
||||
}
|
||||
func (sus *SqlUserStore) GetGlobalCount() int {
|
||||
var ucount int
|
||||
err := sus.user_count.QueryRow().Scan(&ucount)
|
||||
if err != nil {
|
||||
LogError(err)
|
||||
}
|
||||
return ucount
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user