Added support for desktop notifications.

The alert list is now visible on the Shadow Theme.

The User Manager is now paginated.
The Moderation Logs are now paginated.
The Group Manager is now paginated.
The alert counter no longer shows up as undefined on Edge.
Added a cache control header to the static files.
Fixed a few bits of mangled HTML.
Fixed the Forum Manager CSS for the Shadow Theme.
This commit is contained in:
Azareal 2017-08-17 12:13:49 +01:00
parent 3e4cfa8888
commit ca9c755a47
28 changed files with 202 additions and 45 deletions

View File

@ -3,14 +3,18 @@ package main
import "fmt" import "fmt"
import "log" import "log"
import "bytes" import "bytes"
import "sync"
import "net/http" import "net/http"
import "runtime/debug" import "runtime/debug"
// TO-DO: Use the error_buffer variable to construct the system log in the Control Panel. Should we log errors caused by users too? Or just collect statistics on those or do nothing? Intercept recover()? Could we intercept the logger instead here? We might get too much information, if we intercept the logger, maybe make it part of the Debug page?
var error_buffer_mutex sync.RWMutex
var error_buffer []error
//var notfound_count_per_second int //var notfound_count_per_second int
//var noperms_count_per_second int //var noperms_count_per_second int
var error_internal []byte var error_internal []byte
var error_notfound []byte var error_notfound []byte
func init_errors() error { func init_errors() error {
var b bytes.Buffer var b bytes.Buffer
user := User{0,"guest","Guest","",0,false,false,false,false,false,false,GuestPerms,nil,"",false,"","","","","",0,0,"0.0.0.0.0"} user := User{0,"guest","Guest","",0,false,false,false,false,false,false,GuestPerms,nil,"",false,"","","","","",0,0,"0.0.0.0.0"}
@ -34,6 +38,9 @@ func init_errors() error {
func LogError(err error) { func LogError(err error) {
log.Print(err) log.Print(err)
debug.PrintStack() debug.PrintStack()
error_buffer_mutex.Lock()
defer error_buffer_mutex.Unlock()
error_buffer = append(error_buffer,err)
log.Fatal("") log.Fatal("")
} }
@ -41,6 +48,9 @@ func InternalError(err error, w http.ResponseWriter) {
w.Write(error_internal) w.Write(error_internal)
log.Print(err) log.Print(err)
debug.PrintStack() debug.PrintStack()
error_buffer_mutex.Lock()
defer error_buffer_mutex.Unlock()
error_buffer = append(error_buffer,err)
log.Fatal("") log.Fatal("")
} }
@ -53,12 +63,18 @@ func InternalErrorJSQ(err error, w http.ResponseWriter, r *http.Request, is_js s
} }
log.Print(err) log.Print(err)
debug.PrintStack() debug.PrintStack()
error_buffer_mutex.Lock()
defer error_buffer_mutex.Unlock()
error_buffer = append(error_buffer,err)
log.Fatal("") log.Fatal("")
} }
func InternalErrorJS(err error, w http.ResponseWriter, r *http.Request) { func InternalErrorJS(err error, w http.ResponseWriter, r *http.Request) {
w.WriteHeader(500) w.WriteHeader(500)
w.Write([]byte(`{"errmsg":"A problem has occured in the system."}`)) w.Write([]byte(`{"errmsg":"A problem has occured in the system."}`))
error_buffer_mutex.Lock()
defer error_buffer_mutex.Unlock()
error_buffer = append(error_buffer,err)
log.Fatal(err) log.Fatal(err)
} }

View File

@ -23,8 +23,10 @@ var get_themes_stmt *sql.Stmt
var get_widgets_stmt *sql.Stmt var get_widgets_stmt *sql.Stmt
var is_plugin_active_stmt *sql.Stmt var is_plugin_active_stmt *sql.Stmt
var get_users_stmt *sql.Stmt var get_users_stmt *sql.Stmt
var get_users_offset_stmt *sql.Stmt
var is_theme_default_stmt *sql.Stmt var is_theme_default_stmt *sql.Stmt
var get_modlogs_stmt *sql.Stmt var get_modlogs_stmt *sql.Stmt
var get_modlogs_offset_stmt *sql.Stmt
var get_reply_tid_stmt *sql.Stmt var get_reply_tid_stmt *sql.Stmt
var get_topic_fid_stmt *sql.Stmt var get_topic_fid_stmt *sql.Stmt
var get_user_reply_uid_stmt *sql.Stmt var get_user_reply_uid_stmt *sql.Stmt
@ -109,6 +111,7 @@ var delete_profile_reply_stmt *sql.Stmt
var delete_forum_perms_by_forum_stmt *sql.Stmt var delete_forum_perms_by_forum_stmt *sql.Stmt
var report_exists_stmt *sql.Stmt var report_exists_stmt *sql.Stmt
var group_count_stmt *sql.Stmt var group_count_stmt *sql.Stmt
var modlog_count_stmt *sql.Stmt
var add_forum_perms_to_forum_admins_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_staff_stmt *sql.Stmt
var add_forum_perms_to_forum_members_stmt *sql.Stmt var add_forum_perms_to_forum_members_stmt *sql.Stmt
@ -215,6 +218,12 @@ func _gen_mysql() (err error) {
return err return err
} }
log.Print("Preparing get_users_offset statement.")
get_users_offset_stmt, err = db.Prepare("SELECT `uid`,`name`,`group`,`active`,`is_super_admin`,`avatar` FROM `users` LIMIT ?,?")
if err != nil {
return err
}
log.Print("Preparing is_theme_default statement.") log.Print("Preparing is_theme_default statement.")
is_theme_default_stmt, err = db.Prepare("SELECT `default` FROM `themes` WHERE `uname` = ?") is_theme_default_stmt, err = db.Prepare("SELECT `default` FROM `themes` WHERE `uname` = ?")
if err != nil { if err != nil {
@ -227,6 +236,12 @@ func _gen_mysql() (err error) {
return err return err
} }
log.Print("Preparing get_modlogs_offset statement.")
get_modlogs_offset_stmt, err = db.Prepare("SELECT `action`,`elementID`,`elementType`,`ipaddress`,`actorID`,`doneAt` FROM `moderation_logs` LIMIT ?,?")
if err != nil {
return err
}
log.Print("Preparing get_reply_tid statement.") log.Print("Preparing get_reply_tid statement.")
get_reply_tid_stmt, err = db.Prepare("SELECT `tid` FROM `replies` WHERE `rid` = ?") get_reply_tid_stmt, err = db.Prepare("SELECT `tid` FROM `replies` WHERE `rid` = ?")
if err != nil { if err != nil {
@ -731,6 +746,12 @@ func _gen_mysql() (err error) {
return err return err
} }
log.Print("Preparing modlog_count statement.")
modlog_count_stmt, err = db.Prepare("SELECT COUNT(*) AS `count` FROM `moderation_logs`")
if err != nil {
return err
}
log.Print("Preparing add_forum_perms_to_forum_admins statement.") 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") 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 { if err != nil {

View File

@ -190,6 +190,8 @@ INSERT INTO settings(`name`,`content`,`type`) VALUES ('url_tags','1','bool');
INSERT INTO settings(`name`,`content`,`type`,`constraints`) VALUES ('activation_type','1','list','1-3'); INSERT INTO settings(`name`,`content`,`type`,`constraints`) VALUES ('activation_type','1','list','1-3');
INSERT INTO settings(`name`,`content`,`type`) VALUES ('bigpost_min_words','250','int'); INSERT INTO settings(`name`,`content`,`type`) VALUES ('bigpost_min_words','250','int');
INSERT INTO settings(`name`,`content`,`type`) VALUES ('megapost_min_words','1000','int'); INSERT INTO settings(`name`,`content`,`type`) VALUES ('megapost_min_words','1000','int');
/* TO-DO: Implement the html-attribute setting type before deploying this */
/*INSERT INTO settings(`name`,`content`,`type`) VALUES ('meta_desc','','html-attribute');*/
INSERT INTO themes(`uname`,`default`) VALUES ('tempra-simple',1); INSERT INTO themes(`uname`,`default`) VALUES ('tempra-simple',1);
INSERT INTO emails(`email`,`uid`,`validated`) VALUES ('admin@localhost',1,1); INSERT INTO emails(`email`,`uid`,`validated`) VALUES ('admin@localhost',1,1);

View File

@ -126,19 +126,6 @@ type PanelPage struct
Something 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 type GridElement struct
{ {
ID string ID string
@ -171,6 +158,32 @@ type PanelThemesPage struct
ExtData ExtData ExtData ExtData
} }
type PanelUserPage struct
{
Title string
CurrentUser User
Header HeaderVars
Stats PanelStats
ItemList []User
PageList []int
Page int
LastPage int
ExtData ExtData
}
type PanelGroupPage struct
{
Title string
CurrentUser User
Header HeaderVars
Stats PanelStats
ItemList []GroupAdmin
PageList []int
Page int
LastPage int
ExtData ExtData
}
type PanelEditGroupPage struct type PanelEditGroupPage struct
{ {
Title string Title string
@ -245,6 +258,9 @@ type PanelLogsPage struct
Header HeaderVars Header HeaderVars
Stats PanelStats Stats PanelStats
Logs []Log Logs []Log
PageList []int
Page int
LastPage int
ExtData ExtData ExtData ExtData
} }

View File

@ -964,15 +964,19 @@ func route_panel_users(w http.ResponseWriter, r *http.Request, user User){
return return
} }
var userList []interface{} page, _ := strconv.Atoi(r.FormValue("page"))
rows, err := get_users_stmt.Query() perPage := 10
offset, page, lastPage := page_offset(stats.Users, page, perPage)
var userList []User
rows, err := get_users_offset_stmt.Query(offset,perPage)
if err != nil { if err != nil {
InternalError(err,w) InternalError(err,w)
return return
} }
defer rows.Close() defer rows.Close()
// TO-DO: Add a UserStore method for iterating over global users // TO-DO: Add a UserStore method for iterating over global users and global user offsets
for rows.Next() { for rows.Next() {
puser := User{ID: 0,} puser := User{ID: 0,}
err := rows.Scan(&puser.ID, &puser.Name, &puser.Group, &puser.Active, &puser.Is_Super_Admin, &puser.Avatar) err := rows.Scan(&puser.ID, &puser.Name, &puser.Group, &puser.Active, &puser.Is_Super_Admin, &puser.Avatar)
@ -1003,7 +1007,8 @@ func route_panel_users(w http.ResponseWriter, r *http.Request, user User){
return return
} }
pi := PanelPage{"User Manager",user,headerVars,stats,userList,nil} pageList := paginate(stats.Users, perPage, 5)
pi := PanelUserPage{"User Manager",user,headerVars,stats,userList,pageList,page,lastPage,extData}
if pre_render_hooks["pre_render_panel_users"] != nil { if pre_render_hooks["pre_render_panel_users"] != nil {
if run_pre_render_hook("pre_render_panel_users", w, r, &user, &pi) { if run_pre_render_hook("pre_render_panel_users", w, r, &user, &pi) {
return return
@ -1740,7 +1745,18 @@ func route_panel_logs_mod(w http.ResponseWriter, r *http.Request, user User){
return return
} }
rows, err := get_modlogs_stmt.Query() var logCount int
err := modlog_count_stmt.QueryRow().Scan(&logCount)
if err != nil {
InternalError(err,w)
return
}
page, _ := strconv.Atoi(r.FormValue("page"))
perPage := 10
offset, page, lastPage := page_offset(logCount, page, perPage)
rows, err := get_modlogs_offset_stmt.Query(offset,perPage)
if err != nil { if err != nil {
InternalError(err,w) InternalError(err,w)
return return
@ -1826,7 +1842,8 @@ func route_panel_logs_mod(w http.ResponseWriter, r *http.Request, user User){
return return
} }
pi := PanelLogsPage{"Moderation Logs",user,headerVars,stats,logs,extData} pageList := paginate(logCount, perPage, 5)
pi := PanelLogsPage{"Moderation Logs",user,headerVars,stats,logs,pageList,page,lastPage,extData}
if pre_render_hooks["pre_render_panel_mod_log"] != nil { if pre_render_hooks["pre_render_panel_mod_log"] != nil {
if run_pre_render_hook("pre_render_panel_mod_log", w, r, &user, &pi) { if run_pre_render_hook("pre_render_panel_mod_log", w, r, &user, &pi) {
return return

View File

@ -15,7 +15,7 @@ function load_alerts(menu_alerts)
{ {
var alertListNode = menu_alerts.getElementsByClassName("alertList")[0]; var alertListNode = menu_alerts.getElementsByClassName("alertList")[0];
var alertCounterNode = menu_alerts.getElementsByClassName("alert_counter")[0]; var alertCounterNode = menu_alerts.getElementsByClassName("alert_counter")[0];
alertCounterNode.textContent = ""; alertCounterNode.textContent = "0";
$.ajax({ $.ajax({
type: 'get', type: 'get',
dataType: 'json', dataType: 'json',
@ -58,7 +58,7 @@ function load_alerts(menu_alerts)
//if(anyAvatar) menu_alerts.addClass("hasAvatars"); //if(anyAvatar) menu_alerts.addClass("hasAvatars");
} }
alertListNode.innerHTML = alist; alertListNode.innerHTML = alist;
if(data.msgCount != 0) { if(data.msgCount != 0 && data.msgCount != undefined) {
alertCounterNode.textContent = data.msgCount; alertCounterNode.textContent = data.msgCount;
menu_alerts.classList.add("has_alerts"); menu_alerts.classList.add("has_alerts");
} else { } else {
@ -108,6 +108,8 @@ $(document).ready(function(){
conn.onopen = function() { conn.onopen = function() {
conn.send("page " + document.location.pathname + '\r'); conn.send("page " + document.location.pathname + '\r');
// TO-DO: Don't ask again, if it's denied. We could have a setting in the UCP which automatically requests this when someone flips desktop notifications on
Notification.requestPermission();
} }
conn.onclose = function() { conn.onclose = function() {
conn = false; conn = false;
@ -141,6 +143,16 @@ $(document).ready(function(){
//console.log(alist); //console.log(alist);
$("#general_alerts").find(".alertList").html(alist); // Add support for other alert feeds like PM Alerts $("#general_alerts").find(".alertList").html(alist); // Add support for other alert feeds like PM Alerts
$("#general_alerts").find(".alert_counter").text(alertCount); $("#general_alerts").find(".alert_counter").text(alertCount);
// TO-DO: Add some sort of notification queue to avoid flooding the end-user with notices?
// TO-DO: Use the site name instead of "Something Happened"
if(Notification.permission === "granted") {
var n = new Notification("Something Happened",{
body: msg,
icon: data.avatar,
});
setTimeout(n.close.bind(n), 8000);
}
} }
} }

View File

@ -192,10 +192,14 @@ func write_selects(adapter qgen.DB_Adapter) error {
adapter.SimpleSelect("get_users","users","uid, name, group, active, is_super_admin, avatar","","","") adapter.SimpleSelect("get_users","users","uid, name, group, active, is_super_admin, avatar","","","")
adapter.SimpleSelect("get_users_offset","users","uid, name, group, active, is_super_admin, avatar","","","?,?")
adapter.SimpleSelect("is_theme_default","themes","default","uname = ?","","") adapter.SimpleSelect("is_theme_default","themes","default","uname = ?","","")
adapter.SimpleSelect("get_modlogs","moderation_logs","action, elementID, elementType, ipaddress, actorID, doneAt","","","") adapter.SimpleSelect("get_modlogs","moderation_logs","action, elementID, elementType, ipaddress, actorID, doneAt","","","")
adapter.SimpleSelect("get_modlogs_offset","moderation_logs","action, elementID, elementType, ipaddress, actorID, doneAt","","","?,?")
adapter.SimpleSelect("get_reply_tid","replies","tid","rid = ?","","") adapter.SimpleSelect("get_reply_tid","replies","tid","rid = ?","","")
adapter.SimpleSelect("get_topic_fid","topics","parentID","tid = ?","","") adapter.SimpleSelect("get_topic_fid","topics","parentID","tid = ?","","")
@ -395,6 +399,8 @@ func write_simple_counts(adapter qgen.DB_Adapter) error {
adapter.SimpleCount("group_count","users_groups","","") adapter.SimpleCount("group_count","users_groups","","")
adapter.SimpleCount("modlog_count","moderation_logs","","")
return nil return nil
} }

View File

@ -24,6 +24,7 @@ var nList []string
var hvars HeaderVars var hvars HeaderVars
var extData ExtData var extData ExtData
var success_json_bytes []byte = []byte(`{"success":"1"}`) var success_json_bytes []byte = []byte(`{"success":"1"}`)
var cacheControlMaxAge string = "max-age=" + strconv.Itoa(day)
func init() { func init() {
hvars.Site = site hvars.Site = site
@ -57,14 +58,14 @@ func route_static(w http.ResponseWriter, r *http.Request){
h := w.Header() h := w.Header()
h.Set("Last-Modified", file.FormattedModTime) h.Set("Last-Modified", file.FormattedModTime)
h.Set("Content-Type", file.Mimetype) h.Set("Content-Type", file.Mimetype)
//Cache-Control: max-age=31536000
h.Set("Cache-Control", cacheControlMaxAge)
h.Set("Vary","Accept-Encoding")
//http.ServeContent(w,r,r.URL.Path,file.Info.ModTime(),file) //http.ServeContent(w,r,r.URL.Path,file.Info.ModTime(),file)
//w.Write(file.Data) //w.Write(file.Data)
if strings.Contains(r.Header.Get("Accept-Encoding"),"gzip") { if strings.Contains(r.Header.Get("Accept-Encoding"),"gzip") {
h.Set("Content-Encoding","gzip") h.Set("Content-Encoding","gzip")
h.Set("Content-Length", strconv.FormatInt(file.GzipLength, 10)) 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? io.Copy(w, bytes.NewReader(file.GzipData)) // Use w.Write instead?
} else { } else {
h.Set("Content-Length", strconv.FormatInt(file.Length, 10)) // Avoid doing a type conversion every time? h.Set("Content-Length", strconv.FormatInt(file.Length, 10)) // Avoid doing a type conversion every time?

View File

@ -50,6 +50,7 @@ func LoadSettings() error {
return nil return nil
} }
// TO-DO: Add better support for HTML attributes (html-attribute). E.g. Meta descriptions.
func parseSetting(sname string, scontent string, stype string, constraint string) string { func parseSetting(sname string, scontent string, stype string, constraint string) string {
var err error var err error
if stype == "bool" { if stype == "bool" {

View File

@ -149,7 +149,7 @@ var topic_40 []byte = []byte(`' style="font-weight:normal;" title="Unpin Topic">
var topic_41 []byte = []byte(`<a href='/topic/stick/submit/`) var topic_41 []byte = []byte(`<a href='/topic/stick/submit/`)
var topic_42 []byte = []byte(`' class="mod_button" style="font-weight:normal;" title="Pin Topic"><button class="username pin_label"></button></a>`) var topic_42 []byte = []byte(`' class="mod_button" style="font-weight:normal;" title="Pin Topic"><button class="username pin_label"></button></a>`)
var topic_43 []byte = []byte(` var topic_43 []byte = []byte(`
<a class="mod_button" href="/report/submit/`) <a href="/report/submit/`)
var topic_44 []byte = []byte(`?session=`) var topic_44 []byte = []byte(`?session=`)
var topic_45 []byte = []byte(`&type=topic" class="mod_button report_item" style="font-weight:normal;" title="Flag Topic"><button class="username flag_label"></button></a> var topic_45 []byte = []byte(`&type=topic" class="mod_button report_item" style="font-weight:normal;" title="Flag Topic"><button class="username flag_label"></button></a>
@ -200,7 +200,7 @@ var topic_71 []byte = []byte(`" class="mod_button" title="Edit Reply"><button cl
var topic_72 []byte = []byte(`<a href="/reply/delete/submit/`) var topic_72 []byte = []byte(`<a href="/reply/delete/submit/`)
var topic_73 []byte = []byte(`" class="mod_button" title="Delete Reply"><button class="username delete_item trash_label"></button></a>`) var topic_73 []byte = []byte(`" class="mod_button" title="Delete Reply"><button class="username delete_item trash_label"></button></a>`)
var topic_74 []byte = []byte(` var topic_74 []byte = []byte(`
<a class="mod_button" href="/report/submit/`) <a href="/report/submit/`)
var topic_75 []byte = []byte(`?session=`) var topic_75 []byte = []byte(`?session=`)
var topic_76 []byte = []byte(`&type=reply" class="mod_button report_item" title="Flag Reply"><button class="username report_item flag_label"></button></a> var topic_76 []byte = []byte(`&type=reply" class="mod_button report_item" title="Flag Reply"><button class="username report_item flag_label"></button></a>
@ -210,8 +210,8 @@ var topic_78 []byte = []byte(`</a><a class="username hide_on_micro like_count_la
var topic_79 []byte = []byte(`<a class="username hide_on_micro user_tag">`) var topic_79 []byte = []byte(`<a class="username hide_on_micro user_tag">`)
var topic_80 []byte = []byte(`</a>`) var topic_80 []byte = []byte(`</a>`)
var topic_81 []byte = []byte(`<a class="username hide_on_micro level">`) var topic_81 []byte = []byte(`<a class="username hide_on_micro level">`)
var topic_82 []byte = []byte(`</a><a class="username hide_on_micro level_label" style="float:right;" title="Level">`) var topic_82 []byte = []byte(`</a><a class="username hide_on_micro level_label" style="float:right;" title="Level"></a>`)
var topic_83 []byte = []byte(`</a> var topic_83 []byte = []byte(`
</span> </span>
</div> </div>

View File

@ -6,7 +6,6 @@
<div class="colstack_item rowmenu"> <div class="colstack_item rowmenu">
<div class="rowitem passive"><a href="/panel/logs/mod/">Moderation Logs</a></div> <div class="rowitem passive"><a href="/panel/logs/mod/">Moderation Logs</a></div>
{{if .CurrentUser.Perms.ViewAdminLogs}}<div class="rowitem passive"><a>Administration Logs</a></div>{{end}} {{if .CurrentUser.Perms.ViewAdminLogs}}<div class="rowitem passive"><a>Administration Logs</a></div>{{end}}
{{if .CurrentUser.Perms.ViewAdminLogs}}<div class="rowitem passive"><a>System Logs</a></div>{{end}}
</div> </div>
{{template "panel-inner-menu.html" . }} {{template "panel-inner-menu.html" . }}
</div> </div>

View File

@ -11,7 +11,7 @@
</div> </div>
<div id="panel_forums" class="colstack_item rowlist"> <div id="panel_forums" class="colstack_item rowlist">
{{range .ItemList}} {{range .ItemList}}
<div class="rowitem editable_parent" style="{{if eq .ID 1}}border-bottom-style:solid;{{end}}"> <div class="rowitem editable_parent{{if eq .ID 1}} builtin_forum_divider{{end}}">
<span class="panel_floater"> <span class="panel_floater">
<span data-field="forum_active" data-type="list" class="panel_tag editable_block forum_active {{if .Active}}forum_active_Show" data-value="1{{else}}forum_active_Hide" data-value="0{{end}}" title="Hidden"></span> <span data-field="forum_active" data-type="list" class="panel_tag editable_block forum_active {{if .Active}}forum_active_Show" data-value="1{{else}}forum_active_Hide" data-value="0{{end}}" title="Hidden"></span>
<span data-field="forum_preset" data-type="list" data-value="{{.Preset}}" class="panel_tag editable_block forum_preset forum_preset_{{.Preset}}" title="{{.PresetLang}}"></span> <span data-field="forum_preset" data-type="list" data-value="{{.Preset}}" class="panel_tag editable_block forum_preset forum_preset_{{.Preset}}" title="{{.PresetLang}}"></span>
@ -24,7 +24,8 @@
</span> </span>
</span> </span>
<span style="float: left;"> <span style="float: left;">
<a data-field="forum_name" data-type="text" class="editable_block forum_name" style="{{if not .Active}}color:#707070;{{end}}">{{.Name}}</a> {{/** TO-DO: Make sure the forum_active_name class is set and unset when the activity status of this forum is changed **/}}
<a data-field="forum_name" data-type="text" class="editable_block forum_name{{if not .Active}} forum_active_name{{end}}">{{.Name}}</a>
</span> </span>
<br /><span data-field="forum_desc" data-type="text" class="editable_block forum_desc rowsmall">{{.Desc}}</span> <br /><span data-field="forum_desc" data-type="text" class="editable_block forum_desc rowsmall">{{.Desc}}</span>
<div style="clear: both;"></div> <div style="clear: both;"></div>

View File

@ -6,7 +6,6 @@
<div class="colstack_item rowmenu"> <div class="colstack_item rowmenu">
<div class="rowitem passive"><a href="/panel/logs/mod/">Moderation Logs</a></div> <div class="rowitem passive"><a href="/panel/logs/mod/">Moderation Logs</a></div>
{{if .CurrentUser.Perms.ViewAdminLogs}}<div class="rowitem passive"><a>Administration Logs</a></div>{{end}} {{if .CurrentUser.Perms.ViewAdminLogs}}<div class="rowitem passive"><a>Administration Logs</a></div>{{end}}
{{if .CurrentUser.Perms.ViewAdminLogs}}<div class="rowitem passive"><a>System Logs</a></div>{{end}}
</div> </div>
{{template "panel-inner-menu.html" . }} {{template "panel-inner-menu.html" . }}
</div> </div>
@ -28,5 +27,14 @@
</div> </div>
{{end}} {{end}}
</div> </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> </div>
{{template "footer.html" . }} {{template "footer.html" . }}

View File

@ -19,5 +19,14 @@
</div> </div>
{{end}} {{end}}
</div> </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> </div>
{{template "footer.html" . }} {{template "footer.html" . }}

View File

@ -39,7 +39,7 @@
{{if .CurrentUser.Perms.DeleteTopic}}<a href='/topic/delete/submit/{{.Topic.ID}}' class="mod_button" style="font-weight:normal;" title="Delete Topic"><button class="username trash_label"></button></a>{{end}} {{if .CurrentUser.Perms.DeleteTopic}}<a href='/topic/delete/submit/{{.Topic.ID}}' class="mod_button" style="font-weight:normal;" title="Delete Topic"><button class="username trash_label"></button></a>{{end}}
{{if .CurrentUser.Perms.PinTopic}}{{if .Topic.Sticky}}<a class="mod_button" href='/topic/unstick/submit/{{.Topic.ID}}' style="font-weight:normal;" title="Unpin Topic"><button class="username unpin_label"></button></a>{{else}}<a href='/topic/stick/submit/{{.Topic.ID}}' class="mod_button" style="font-weight:normal;" title="Pin Topic"><button class="username pin_label"></button></a>{{end}}{{end}} {{if .CurrentUser.Perms.PinTopic}}{{if .Topic.Sticky}}<a class="mod_button" href='/topic/unstick/submit/{{.Topic.ID}}' style="font-weight:normal;" title="Unpin Topic"><button class="username unpin_label"></button></a>{{else}}<a href='/topic/stick/submit/{{.Topic.ID}}' class="mod_button" style="font-weight:normal;" title="Pin Topic"><button class="username pin_label"></button></a>{{end}}{{end}}
<a class="mod_button" href="/report/submit/{{.Topic.ID}}?session={{.CurrentUser.Session}}&type=topic" class="mod_button report_item" style="font-weight:normal;" title="Flag Topic"><button class="username flag_label"></button></a> <a href="/report/submit/{{.Topic.ID}}?session={{.CurrentUser.Session}}&type=topic" class="mod_button report_item" style="font-weight:normal;" title="Flag Topic"><button class="username flag_label"></button></a>
{{if .Topic.LikeCount}}<a class="username hide_on_micro like_count">{{.Topic.LikeCount}}</a><a class="username hide_on_micro like_count_label" title="Like Count"></a>{{end}} {{if .Topic.LikeCount}}<a class="username hide_on_micro like_count">{{.Topic.LikeCount}}</a><a class="username hide_on_micro like_count_label" title="Like Count"></a>{{end}}
@ -65,11 +65,11 @@
{{if $.CurrentUser.Perms.EditReply}}<a href="/reply/edit/submit/{{.ID}}" class="mod_button" title="Edit Reply"><button class="username edit_item edit_label"></button></a>{{end}} {{if $.CurrentUser.Perms.EditReply}}<a href="/reply/edit/submit/{{.ID}}" class="mod_button" title="Edit Reply"><button class="username edit_item edit_label"></button></a>{{end}}
{{if $.CurrentUser.Perms.DeleteReply}}<a href="/reply/delete/submit/{{.ID}}" class="mod_button" title="Delete Reply"><button class="username delete_item trash_label"></button></a>{{end}} {{if $.CurrentUser.Perms.DeleteReply}}<a href="/reply/delete/submit/{{.ID}}" class="mod_button" title="Delete Reply"><button class="username delete_item trash_label"></button></a>{{end}}
<a class="mod_button" href="/report/submit/{{.ID}}?session={{$.CurrentUser.Session}}&type=reply" class="mod_button report_item" title="Flag Reply"><button class="username report_item flag_label"></button></a> <a href="/report/submit/{{.ID}}?session={{$.CurrentUser.Session}}&type=reply" class="mod_button report_item" title="Flag Reply"><button class="username report_item flag_label"></button></a>
{{if .LikeCount}}<a class="username hide_on_micro like_count">{{.LikeCount}}</a><a class="username hide_on_micro like_count_label" title="Like Count"></a>{{end}} {{if .LikeCount}}<a class="username hide_on_micro like_count">{{.LikeCount}}</a><a class="username hide_on_micro like_count_label" title="Like Count"></a>{{end}}
{{if .Tag}}<a class="username hide_on_micro user_tag">{{.Tag}}</a>{{else}}<a class="username hide_on_micro level">{{.Level}}</a><a class="username hide_on_micro level_label" style="float:right;" title="Level">{{end}}</a> {{if .Tag}}<a class="username hide_on_micro user_tag">{{.Tag}}</a>{{else}}<a class="username hide_on_micro level">{{.Level}}</a><a class="username hide_on_micro level_label" style="float:right;" title="Level"></a>{{end}}
</span> </span>
</div> </div>

View File

@ -60,6 +60,8 @@
.forum_active_Hide:before { content: "Hidden"; } .forum_active_Hide:before { content: "Hidden"; }
.forum_active_Hide + .forum_preset:before { content: " | "; } .forum_active_Hide + .forum_preset:before { content: " | "; }
.forum_active_Show { display: none !important; } .forum_active_Show { display: none !important; }
.forum_active_name { color: #707070; }
.builtin_forum_divider { border-bottom-style: solid; }
.perm_preset_no_access:before { content: "No Access"; color: maroon; } .perm_preset_no_access:before { content: "No Access"; color: maroon; }
.perm_preset_read_only:before { content: "Read Only"; color: green; } .perm_preset_read_only:before { content: "Read Only"; color: green; }

View File

@ -60,6 +60,8 @@
.forum_active_Hide:before { content: "Hidden"; } .forum_active_Hide:before { content: "Hidden"; }
.forum_active_Hide + .forum_preset:before { content: " | "; } .forum_active_Hide + .forum_preset:before { content: " | "; }
.forum_active_Show { display: none !important; } .forum_active_Show { display: none !important; }
.forum_active_name { color: #707070; }
.builtin_forum_divider { border-bottom-style: solid; }
.perm_preset_no_access:before { content: "No Access"; color: maroon; } .perm_preset_no_access:before { content: "No Access"; color: maroon; }
.perm_preset_read_only:before { content: "Read Only"; color: green; } .perm_preset_read_only:before { content: "Read Only"; color: green; }

View File

@ -86,8 +86,27 @@ li {
} }
.selectedAlert .alertList { .selectedAlert .alertList {
/* Coming Soon. Hidden so it doesn't distract me while I design this theme */ display: block;
display: none; position: absolute;
top: 44px;
float: left;
width: 200px;
z-index: 50;
right: 15%;
font-size: 13px;
background-color: #333333;
}
.alertItem {
margin-bottom: 2px;
}
.alertItem.withAvatar {
height: 40px;
background-size: 48px;
background-repeat: no-repeat;
background-color: rgb(61,61,61);
padding-left: 56px;
padding-top: 8px;
} }
a { a {
@ -142,19 +161,13 @@ a {
float: left; float: left;
width: calc(70% - 24px); width: calc(70% - 24px);
} }
.colstack_left:empty, .colstack_right:empty { .colstack_left:empty, .colstack_right:empty, .show_on_edit {
display: none;
}
.show_on_edit {
display: none; display: none;
} }
.topic_sticky_head { .topic_sticky_head {
} }
.topic_closed_head { .topic_closed_head {
} }
.post_item { .post_item {
@ -585,6 +598,22 @@ input, select, textarea {
display: none; display: none;
} }
.selectedAlert .alertList {
right: 10px;
top: 42px;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.alertItem.withAvatar {
height: 28px;
background-size: 38px;
padding-left: 46px;
padding-top: 10px;
overflow: hidden;
text-overflow: ellipsis;
}
#back { #back {
width: calc(100% - 20px); width: calc(100% - 20px);
} }

View File

@ -22,3 +22,10 @@
#forum_quick_perms .edit_fields { #forum_quick_perms .edit_fields {
float: right; float: right;
} }
.forum_active_name {
color: rgb(200,200,200);
}
.builtin_forum_divider {
margin-bottom: 5px;
}

View File

@ -70,6 +70,8 @@
.forum_active_Hide:before { content: "Hidden"; } .forum_active_Hide:before { content: "Hidden"; }
.forum_active_Hide + .forum_preset:before { content: " | "; } .forum_active_Hide + .forum_preset:before { content: " | "; }
.forum_active_Show { display: none !important; } .forum_active_Show { display: none !important; }
.forum_active_name { color: #707070; }
.builtin_forum_divider { border-bottom-style: solid; }
.perm_preset_no_access:before { content: "No Access"; color: maroon; } .perm_preset_no_access:before { content: "No Access"; color: maroon; }
.perm_preset_read_only:before { content: "Read Only"; color: green; } .perm_preset_read_only:before { content: "Read Only"; color: green; }

View File

@ -58,6 +58,8 @@
.forum_active_Hide:before { content: "Hidden"; } .forum_active_Hide:before { content: "Hidden"; }
.forum_active_Hide + .forum_preset:before { content: " | "; } .forum_active_Hide + .forum_preset:before { content: " | "; }
.forum_active_Show { display: none !important; } .forum_active_Show { display: none !important; }
.forum_active_name { color: #707070; }
.builtin_forum_divider { border-bottom-style: solid; }
.perm_preset_no_access:before { content: "No Access"; color: maroon; } .perm_preset_no_access:before { content: "No Access"; color: maroon; }
.perm_preset_read_only:before { content: "Read Only"; color: green; } .perm_preset_read_only:before { content: "Read Only"; color: green; }

View File

@ -62,6 +62,8 @@
.forum_preset_all, .forum_preset_custom, .forum_preset_ { display: none !important; } .forum_preset_all, .forum_preset_custom, .forum_preset_ { display: none !important; }
.forum_active_Hide:before { content: "🕵️"; } .forum_active_Hide:before { content: "🕵️"; }
.forum_active_Show { display: none !important; } .forum_active_Show { display: none !important; }
.forum_active_name { color: #707070; }
.builtin_forum_divider { border-bottom-style: solid; }
.perm_preset_no_access:before { content: "No Access"; color: maroon; } .perm_preset_no_access:before { content: "No Access"; color: maroon; }
.perm_preset_read_only:before { content: "Read Only"; color: green; } .perm_preset_read_only:before { content: "Read Only"; color: green; }

View File

@ -324,6 +324,8 @@ func _pre_route(w http.ResponseWriter, r *http.Request) (User,bool) {
} }
user.Last_IP = host user.Last_IP = host
} }
// TO-DO: Set the X-Frame-Options header
return *user, true return *user, true
} }