Added the Control Panel Dashboard.
Bumped the version requirements upto Go 1.8 Added gopsutil as a dependency. Added the minify_templates and super_debug config.go options. Cleaned up the console. config.go is a little more organised now.
This commit is contained in:
parent
cac3ffe982
commit
fab2db0936
18
README.md
18
README.md
|
@ -110,13 +110,17 @@ We're looking for ways to clean-up the plugin system so that all of them (except
|
|||
|
||||
# Dependencies
|
||||
|
||||
* Go 1.7
|
||||
* Go 1.8
|
||||
|
||||
* MariaDB
|
||||
* MariaDB (or any other MySQL compatible database engine)
|
||||
|
||||
* github.com/go-sql-driver/mysql
|
||||
* github.com/go-sql-driver/mysql For interfacing with MariaDB.
|
||||
|
||||
* golang.org/x/crypto/bcrypt
|
||||
* golang.org/x/crypto/bcrypt For hashing passwords.
|
||||
|
||||
* github.com/shirou/gopsutil For pulling information on CPU and memory usage.
|
||||
|
||||
* github.com/StackExchange/wmi Dependency for gopsutil on Windows.
|
||||
|
||||
# Bundled Plugins
|
||||
|
||||
|
@ -127,3 +131,9 @@ There are several plugins which are bundled with the software by default. These
|
|||
* BBCode - A plugin in early development for converting BBCode Tags into HTML.
|
||||
|
||||
* Markdown - An extremely simple plugin for converting Markdown into HTML.
|
||||
|
||||
# Developers
|
||||
|
||||
There are a few things you'll need to know before running the more developer oriented features like the tests or the benchmarks.
|
||||
|
||||
The benchmarks are currently being rewritten as they're currently extremely serial which can lead to severe slow-downs when run on a home computer due to the benchmarks being run on the one core everything else is being run on (Browser, OS, etc.) and the tests not taking parallelism into account.
|
||||
|
|
37
config.go
37
config.go
|
@ -1,5 +1,13 @@
|
|||
package main
|
||||
|
||||
// Site Info
|
||||
var site_name = "Test Install" // Should be a setting in the database
|
||||
var site_url = "localhost:8080"
|
||||
var server_port = "8080"
|
||||
var enable_ssl = false
|
||||
var ssl_privkey = ""
|
||||
var ssl_fullchain = ""
|
||||
|
||||
// Database details
|
||||
var dbhost = "localhost"
|
||||
var dbuser = "root"
|
||||
|
@ -10,35 +18,32 @@ var dbport = "3306" // You probably won't need to change this
|
|||
// Limiters
|
||||
var max_request_size = 5 * megabyte
|
||||
|
||||
// Misc
|
||||
// Caching
|
||||
var cache_topicuser = CACHE_STATIC
|
||||
var user_cache_capacity = 100 // The max number of users held in memory
|
||||
var topic_cache_capacity = 100 // The max number of topics held in memory
|
||||
var default_route = route_topics
|
||||
var default_group = 3 // Should be a setting
|
||||
var activation_group = 5 // Should be a setting
|
||||
var staff_css = " background-color: #ffeaff;"
|
||||
var uncategorised_forum_visible = true
|
||||
var enable_emails = false
|
||||
var site_name = "Test Install" // Should be a setting
|
||||
|
||||
var site_email = "" // Should be a setting
|
||||
// Email
|
||||
var site_email = "" // Should be a setting in the database
|
||||
var smtp_server = ""
|
||||
var smtp_username = ""
|
||||
var smtp_password = ""
|
||||
var smtp_port = "25"
|
||||
var enable_emails = false
|
||||
|
||||
// Misc
|
||||
var default_route = route_topics
|
||||
var default_group = 3 // Should be a setting in the database
|
||||
var activation_group = 5 // Should be a setting in the database
|
||||
var staff_css = " background-color: #ffeaff;"
|
||||
var uncategorised_forum_visible = true
|
||||
var minify_templates = true
|
||||
|
||||
//var noavatar = "https://api.adorable.io/avatars/{width}/{id}@{site_url}.png"
|
||||
var noavatar = "https://api.adorable.io/avatars/285/{id}@" + site_url + ".png"
|
||||
var items_per_page = 25
|
||||
|
||||
var site_url = "localhost:8080"
|
||||
var server_port = "8080"
|
||||
var enable_ssl = false
|
||||
var ssl_privkey = ""
|
||||
var ssl_fullchain = ""
|
||||
|
||||
// Developer flags
|
||||
var debug = false
|
||||
var super_debug = false
|
||||
var profiling = false
|
||||
|
||||
|
|
5
files.go
5
files.go
|
@ -75,9 +75,10 @@ func add_static_file(path string, prefix string) error {
|
|||
return err
|
||||
}
|
||||
|
||||
log.Print("Adding the '" + path + "' static file")
|
||||
path = strings.TrimPrefix(path, prefix)
|
||||
log.Print("Added the '" + path + "' static file")
|
||||
if debug {
|
||||
log.Print("Added the '" + path + "' static file")
|
||||
}
|
||||
gzip_data := compress_bytes_gzip(data)
|
||||
|
||||
static_files["/static" + path] = SFile{data,gzip_data,0,int64(len(data)),int64(len(gzip_data)),mime.TypeByExtension(filepath.Ext(prefix + path)),f,f.ModTime().UTC().Format(http.TimeFormat)}
|
||||
|
|
|
@ -2,6 +2,8 @@ echo "Installing the MySQL Driver"
|
|||
go get -u github.com/go-sql-driver/mysql
|
||||
echo "Installing bcrypt"
|
||||
go get -u golang.org/x/crypto/bcrypt
|
||||
echo "Installing gopsutil"
|
||||
go get -u github.com/shirou/gopsutil
|
||||
|
||||
echo "Preparing the installer"
|
||||
go generate
|
||||
|
|
12
install.bat
12
install.bat
|
@ -1,5 +1,5 @@
|
|||
@echo off
|
||||
echo Installing the dependencies
|
||||
echo Installing dependencies
|
||||
go get -u github.com/go-sql-driver/mysql
|
||||
if %errorlevel% neq 0 (
|
||||
pause
|
||||
|
@ -10,6 +10,16 @@ if %errorlevel% neq 0 (
|
|||
pause
|
||||
exit /b %errorlevel%
|
||||
)
|
||||
go get -u github.com/StackExchange/wmi
|
||||
if %errorlevel% neq 0 (
|
||||
pause
|
||||
exit /b %errorlevel%
|
||||
)
|
||||
go get -u github.com/shirou/gopsutil
|
||||
if %errorlevel% neq 0 (
|
||||
pause
|
||||
exit /b %errorlevel%
|
||||
)
|
||||
|
||||
echo Preparing the installer
|
||||
go generate
|
||||
|
|
|
@ -138,6 +138,14 @@ func main() {
|
|||
|
||||
configContents := []byte(`package main
|
||||
|
||||
// Site Info
|
||||
var site_name = "` + site_name + `" // Should be a setting in the database
|
||||
var site_url = "` + site_url + `"
|
||||
var server_port = "` + server_port + `"
|
||||
var enable_ssl = false
|
||||
var ssl_privkey = ""
|
||||
var ssl_fullchain = ""
|
||||
|
||||
// Database details
|
||||
var dbhost = "` + db_host + `"
|
||||
var dbuser = "` + db_username + `"
|
||||
|
@ -148,36 +156,34 @@ var dbport = "` + db_port + `" // You probably won't need to change this
|
|||
// Limiters
|
||||
var max_request_size = 5 * megabyte
|
||||
|
||||
// Misc
|
||||
// Caching
|
||||
var cache_topicuser = CACHE_STATIC
|
||||
var user_cache_capacity = 100 // The max number of users held in memory
|
||||
var topic_cache_capacity = 100 // The max number of topics held in memory
|
||||
var default_route = route_topics
|
||||
var default_group = 3 // Should be a setting
|
||||
var activation_group = 5 // Should be a setting
|
||||
var staff_css = " background-color: #ffeaff;"
|
||||
var uncategorised_forum_visible = true
|
||||
var enable_emails = false
|
||||
var site_name = "` + site_name + `" // Should be a setting
|
||||
|
||||
var site_email = "" // Should be a setting
|
||||
// Email
|
||||
var site_email = "" // Should be a setting in the database
|
||||
var smtp_server = ""
|
||||
var smtp_username = ""
|
||||
var smtp_password = ""
|
||||
var smtp_port = "25"
|
||||
var enable_emails = false
|
||||
|
||||
// Misc
|
||||
var default_route = route_topics
|
||||
var default_group = 3 // Should be a setting in the database
|
||||
var activation_group = 5 // Should be a setting in the database
|
||||
var staff_css = " background-color: #ffeaff;"
|
||||
var uncategorised_forum_visible = true
|
||||
var minify_templates = true
|
||||
|
||||
//var noavatar = "https://api.adorable.io/avatars/{width}/{id}@{site_url}.png"
|
||||
var noavatar = "https://api.adorable.io/avatars/285/{id}@" + site_url + ".png"
|
||||
var items_per_page = 25
|
||||
|
||||
var site_url = "` + site_url + `"
|
||||
var server_port = "` + server_port + `"
|
||||
var enable_ssl = false
|
||||
var ssl_privkey = ""
|
||||
var ssl_fullchain = ""
|
||||
|
||||
// Developer flag
|
||||
var debug = false
|
||||
var debug = true
|
||||
var super_debug = false
|
||||
var profiling = false
|
||||
`)
|
||||
|
||||
|
|
34
main.go
34
main.go
|
@ -1,8 +1,9 @@
|
|||
/* Copyright Azareal 2016 - 2017 */
|
||||
/* Copyright Azareal 2016 - 2018 */
|
||||
package main
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"fmt"
|
||||
"log"
|
||||
"mime"
|
||||
"strings"
|
||||
|
@ -14,15 +15,18 @@ import (
|
|||
//"runtime/pprof"
|
||||
)
|
||||
|
||||
var version Version = Version{Major:0,Minor:1,Patch:0,Tag:"dev"}
|
||||
|
||||
const hour int = 60 * 60
|
||||
const day int = hour * 24
|
||||
const month int = day * 30
|
||||
const year int = day * 365
|
||||
const kilobyte int = 1024
|
||||
const megabyte int = kilobyte * 1024
|
||||
const gigabyte int = megabyte * 1024
|
||||
const terabyte int = gigabyte * 1024
|
||||
const saltLength int = 32
|
||||
const sessionLength int = 80
|
||||
var nogrouplog bool = false // This is mainly for benchmarks, as we don't want a lot of information getting in the way of the results
|
||||
|
||||
var templates = template.New("")
|
||||
var no_css_tmpl = template.CSS("")
|
||||
|
@ -106,19 +110,14 @@ func init_templates() {
|
|||
compile_templates()
|
||||
|
||||
// Filler functions for now...
|
||||
filler_func := func(in interface{}, in2 interface{})interface{} {
|
||||
return 1
|
||||
}
|
||||
fmap := make(map[string]interface{})
|
||||
fmap["add"] = func(in interface{}, in2 interface{})interface{} {
|
||||
return 1
|
||||
}
|
||||
fmap["subtract"] = func(in interface{}, in2 interface{})interface{} {
|
||||
return 1
|
||||
}
|
||||
fmap["multiply"] = func(in interface{}, in2 interface{})interface{} {
|
||||
return 1
|
||||
}
|
||||
fmap["divide"] = func(in interface{}, in2 interface{})interface{} {
|
||||
return 1
|
||||
}
|
||||
fmap["add"] = filler_func
|
||||
fmap["subtract"] = filler_func
|
||||
fmap["multiply"] = filler_func
|
||||
fmap["divide"] = filler_func
|
||||
|
||||
// The interpreted templates...
|
||||
templates.Funcs(fmap)
|
||||
|
@ -140,7 +139,9 @@ func init_static_files() {
|
|||
}
|
||||
|
||||
path = strings.TrimPrefix(path,"public/")
|
||||
log.Print("Added the '" + path + "' static file.")
|
||||
if debug {
|
||||
log.Print("Added the '" + path + "' static file.")
|
||||
}
|
||||
gzip_data := compress_bytes_gzip(data)
|
||||
|
||||
static_files["/static/" + path] = SFile{data,gzip_data,0,int64(len(data)),int64(len(gzip_data)),mime.TypeByExtension(filepath.Ext("/public/" + path)),f,f.ModTime().UTC().Format(http.TimeFormat)}
|
||||
|
@ -160,6 +161,9 @@ func main(){
|
|||
// pprof.StartCPUProfile(f)
|
||||
//}
|
||||
|
||||
log.Print("Running Gosora v" + version.String())
|
||||
fmt.Println("")
|
||||
|
||||
init_themes()
|
||||
err := init_database()
|
||||
if err != nil {
|
||||
|
|
27
mysql.go
27
mysql.go
|
@ -633,7 +633,7 @@ func init_database() (err error) {
|
|||
|
||||
// Ugh, you really shouldn't physically delete these items, it makes a big mess of things
|
||||
if group.ID != i {
|
||||
fmt.Println("Stop physically deleting groups. You are messing up the IDs. Use the Group Manager or delete_group() instead x.x")
|
||||
log.Print("Stop physically deleting groups. You are messing up the IDs. Use the Group Manager or delete_group() instead x.x")
|
||||
fill_group_id_gap(i, group.ID)
|
||||
}
|
||||
|
||||
|
@ -641,8 +641,8 @@ func init_database() (err error) {
|
|||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !nogrouplog {
|
||||
fmt.Println(group.Name + ": ")
|
||||
if debug {
|
||||
log.Print(group.Name + ": ")
|
||||
fmt.Printf("%+v\n", group.Perms)
|
||||
}
|
||||
|
||||
|
@ -679,7 +679,7 @@ func init_database() (err error) {
|
|||
|
||||
// Ugh, you really shouldn't physically delete these items, it makes a big mess of things
|
||||
if forum.ID != i {
|
||||
fmt.Println("Stop physically deleting forums. You are messing up the IDs. Use the Forum Manager or delete_forum() instead x.x")
|
||||
log.Print("Stop physically deleting forums. You are messing up the IDs. Use the Forum Manager or delete_forum() instead x.x")
|
||||
fill_forum_id_gap(i, forum.ID)
|
||||
}
|
||||
|
||||
|
@ -693,7 +693,13 @@ func init_database() (err error) {
|
|||
forum.LastTopicTime = ""
|
||||
}*/
|
||||
|
||||
log.Print("Adding the " + forum.Name + " forum")
|
||||
if forum.Name == "" {
|
||||
if debug {
|
||||
log.Print("Adding a placeholder forum")
|
||||
}
|
||||
} else {
|
||||
log.Print("Adding the " + forum.Name + " forum")
|
||||
}
|
||||
forums = append(forums,forum)
|
||||
}
|
||||
err = rows.Err()
|
||||
|
@ -712,12 +718,13 @@ func init_database() (err error) {
|
|||
}
|
||||
defer rows.Close()
|
||||
|
||||
if debug {
|
||||
log.Print("Adding the forum permissions")
|
||||
}
|
||||
// Temporarily store the forum perms in a map before transferring it to a much faster slice
|
||||
log.Print("Adding the forum permissions")
|
||||
forum_perms = make(map[int]map[int]ForumPerms)
|
||||
for rows.Next() {
|
||||
var gid int
|
||||
var fid int
|
||||
var gid, fid int
|
||||
var perms []byte
|
||||
var pperms ForumPerms
|
||||
err := rows.Scan(&gid, &fid, &perms)
|
||||
|
@ -737,7 +744,9 @@ func init_database() (err error) {
|
|||
forum_perms[gid][fid] = pperms
|
||||
}
|
||||
for gid, _ := range groups {
|
||||
log.Print("Adding the forum permissions for Group #" + strconv.Itoa(gid) + " - " + groups[gid].Name)
|
||||
if debug {
|
||||
log.Print("Adding the forum permissions for Group #" + strconv.Itoa(gid) + " - " + groups[gid].Name)
|
||||
}
|
||||
//groups[gid].Forums = append(groups[gid].Forums,BlankForumPerms) // GID 0. I sometimes wish MySQL's AUTO_INCREMENT would start at zero
|
||||
for fid, _ := range forums {
|
||||
forum_perm, ok := forum_perms[gid][fid]
|
||||
|
|
19
pages.go
19
pages.go
|
@ -77,6 +77,25 @@ type CreateTopicPage struct
|
|||
ExtData interface{}
|
||||
}
|
||||
|
||||
type GridElement struct
|
||||
{
|
||||
Body string
|
||||
Order int // For future use
|
||||
Class string
|
||||
Background string
|
||||
TextColour string
|
||||
Note string
|
||||
}
|
||||
|
||||
type PanelDashboardPage struct
|
||||
{
|
||||
Title string
|
||||
CurrentUser User
|
||||
NoticeList []string
|
||||
GridItems []GridElement
|
||||
ExtData interface{}
|
||||
}
|
||||
|
||||
type ThemesPage struct
|
||||
{
|
||||
Title string
|
||||
|
|
160
panel_routes.go
160
panel_routes.go
|
@ -1,15 +1,23 @@
|
|||
package main
|
||||
|
||||
import "log"
|
||||
import "fmt"
|
||||
import "strings"
|
||||
import "strconv"
|
||||
import "html"
|
||||
import "encoding/json"
|
||||
import "net/http"
|
||||
import "html/template"
|
||||
import "database/sql"
|
||||
import (
|
||||
"log"
|
||||
"fmt"
|
||||
"errors"
|
||||
"strings"
|
||||
"strconv"
|
||||
"html"
|
||||
"time"
|
||||
"runtime"
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
"html/template"
|
||||
"database/sql"
|
||||
)
|
||||
|
||||
import _ "github.com/go-sql-driver/mysql"
|
||||
import "github.com/shirou/gopsutil/cpu"
|
||||
import "github.com/shirou/gopsutil/mem"
|
||||
|
||||
func route_panel(w http.ResponseWriter, r *http.Request){
|
||||
user, noticeList, ok := SessionCheck(w,r)
|
||||
|
@ -20,7 +28,137 @@ func route_panel(w http.ResponseWriter, r *http.Request){
|
|||
NoPermissions(w,r,user)
|
||||
return
|
||||
}
|
||||
pi := Page{"Control Panel Dashboard",user,noticeList,tList,nil}
|
||||
|
||||
var cpustr, cpuColour string
|
||||
perc2, err := cpu.Percent(time.Duration(time.Second),true)
|
||||
if err != nil {
|
||||
cpustr = "Unknown"
|
||||
} else {
|
||||
/*cpures, _ := cpu.Times(true)
|
||||
totcpu := cpures[0].Idle + cpures[0].System + cpures[0].User
|
||||
fmt.Println("System",cpures[0].System)
|
||||
fmt.Println("User",cpures[0].User)
|
||||
fmt.Println("Usage",cpures[0].System + cpures[0].User)
|
||||
fmt.Println("Idle",cpures[0].Idle)
|
||||
fmt.Println("Gap",totcpu - (cpures[0].System + cpures[0].User))
|
||||
perc := ((cpures[0].System + + cpures[0].User) * 100) / totcpu
|
||||
fmt.Println("Perc",perc)
|
||||
fmt.Println("Perc2",perc2)*/
|
||||
calcperc := int(perc2[0]) / runtime.NumCPU()
|
||||
cpustr = strconv.Itoa(calcperc)
|
||||
if calcperc < 25 {
|
||||
cpuColour = "stat_green"
|
||||
} else if calcperc < 75 {
|
||||
cpuColour = "stat_orange"
|
||||
} else {
|
||||
cpuColour = "stat_red"
|
||||
}
|
||||
}
|
||||
|
||||
var ramstr, ramColour string
|
||||
memres, err := mem.VirtualMemory()
|
||||
if err != nil {
|
||||
ramstr = "Unknown"
|
||||
} else {
|
||||
total_count, total_unit := convert_byte_unit(float64(memres.Total))
|
||||
used_count := convert_byte_in_unit(float64(memres.Total - memres.Available),total_unit)
|
||||
|
||||
// Round totals with .9s up, it's how most people see it anyway. Floats are notoriously imprecise, so do it off 0.85
|
||||
//fmt.Println(used_count)
|
||||
var totstr string
|
||||
if (total_count - float64(int(total_count))) > 0.85 {
|
||||
used_count += 1.0 - (total_count - float64(int(total_count)))
|
||||
totstr = strconv.Itoa(int(total_count) + 1)
|
||||
} else {
|
||||
totstr = fmt.Sprintf("%.1f",total_count)
|
||||
}
|
||||
//fmt.Println(used_count)
|
||||
|
||||
if used_count > total_count {
|
||||
used_count = total_count
|
||||
}
|
||||
ramstr = fmt.Sprintf("%.1f",used_count) + " / " + totstr + total_unit
|
||||
|
||||
ramperc := ((memres.Total - memres.Available) * 100) / memres.Total
|
||||
//fmt.Println(ramperc)
|
||||
if ramperc < 50 {
|
||||
ramColour = "stat_green"
|
||||
} else if ramperc < 75 {
|
||||
ramColour = "stat_orange"
|
||||
} else {
|
||||
ramColour = "stat_red"
|
||||
}
|
||||
}
|
||||
|
||||
var postCount int
|
||||
err = db.QueryRow("select count(*) from replies where createdAt BETWEEN (now() - interval 1 day) and now()").Scan(&postCount)
|
||||
if err != nil && err != sql.ErrNoRows {
|
||||
InternalError(err,w,r)
|
||||
return
|
||||
}
|
||||
var postInterval string = "day"
|
||||
|
||||
var postColour string
|
||||
if postCount > 10 {
|
||||
postColour = "stat_green"
|
||||
} else if postCount > 0 {
|
||||
postColour = "stat_orange"
|
||||
} else {
|
||||
postColour = "stat_red"
|
||||
}
|
||||
|
||||
var topicCount int
|
||||
err = db.QueryRow("select count(*) from topics where createdAt BETWEEN (now() - interval 1 day) and now()").Scan(&topicCount)
|
||||
if err != nil && err != sql.ErrNoRows {
|
||||
InternalError(err,w,r)
|
||||
return
|
||||
}
|
||||
var topicInterval string = "day"
|
||||
|
||||
var topicColour string
|
||||
if topicCount > 10 {
|
||||
topicColour = "stat_green"
|
||||
} else if topicCount > 0 {
|
||||
topicColour = "stat_orange"
|
||||
} else {
|
||||
topicColour = "stat_red"
|
||||
}
|
||||
|
||||
var reportCount int
|
||||
err = db.QueryRow("select count(*) from topics where createdAt BETWEEN (now() - interval 1 day) and now() and parentID = 1").Scan(&reportCount)
|
||||
if err != nil && err != sql.ErrNoRows {
|
||||
InternalError(err,w,r)
|
||||
return
|
||||
}
|
||||
var reportInterval string = "week"
|
||||
|
||||
var newUserCount int
|
||||
err = db.QueryRow("select count(*) from users where createdAt BETWEEN (now() - interval 1 day) and now()").Scan(&newUserCount)
|
||||
if err != nil && err != sql.ErrNoRows {
|
||||
InternalError(err,w,r)
|
||||
return
|
||||
}
|
||||
var newUserInterval string = "week"
|
||||
|
||||
var gridElements []GridElement = []GridElement{
|
||||
GridElement{"v" + version.String(),0,"grid_istat stat_green","","","Gosora is up-to-date :)"},
|
||||
GridElement{"CPU: " + cpustr + "%",1,"grid_istat " + cpuColour,"","","The global CPU usage of this server"},
|
||||
GridElement{"RAM: " + ramstr,2,"grid_istat " + ramColour,"","","The global RAM usage of this server"},
|
||||
|
||||
GridElement{strconv.Itoa(postCount) + " posts / " + postInterval,3,"grid_stat " + postColour,"","","The number of new posts over the last 24 hours"},
|
||||
GridElement{strconv.Itoa(topicCount) + " topics / " + topicInterval,4,"grid_stat " + topicColour,"","","The number of new topics over the last 24 hours"},
|
||||
GridElement{"20 online / day",5,"grid_stat stat_disabled","","","Coming Soon!"/*"The people online over the last 24 hours"*/},
|
||||
|
||||
GridElement{"8 searches / week",6,"grid_stat stat_disabled","","","Coming Soon!"/*"The number of searches over the last 7 days"*/},
|
||||
GridElement{strconv.Itoa(newUserCount) + " new users / " + newUserInterval,7,"grid_stat","","","The number of new users over the last 7 days"},
|
||||
GridElement{strconv.Itoa(reportCount) + " reports / " + reportInterval,8,"grid_stat","","","The number of reports over the last 7 days"},
|
||||
|
||||
GridElement{"2 minutes / user / week",9,"grid_stat stat_disabled","","","Coming Soon!"/*"The average number of number of minutes spent by each active user over the last 7 days"*/},
|
||||
GridElement{"2 visitors / week",10,"grid_stat stat_disabled","","","Coming Soon!"/*"The number of unique visitors we've had over the last 7 days"*/},
|
||||
GridElement{"5 posts / user / week",11,"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,noticeList,gridElements,nil}
|
||||
templates.ExecuteTemplate(w,"panel-dashboard.html",pi)
|
||||
}
|
||||
|
||||
|
@ -1261,7 +1399,7 @@ func route_panel_themes_default(w http.ResponseWriter, r *http.Request, uname st
|
|||
|
||||
dTheme, ok := themes[defaultTheme]
|
||||
if !ok {
|
||||
log.Fatal("The default theme is missing")
|
||||
InternalError(errors.New("The default theme is missing"),w,r)
|
||||
return
|
||||
}
|
||||
dTheme.Active = false
|
||||
|
|
|
@ -27,11 +27,7 @@ func init() {
|
|||
func parseSetting(sname string, scontent string, stype string, constraint string) string {
|
||||
var err error
|
||||
if stype == "bool" {
|
||||
if scontent == "1" {
|
||||
settings[sname] = true
|
||||
} else {
|
||||
settings[sname] = false
|
||||
}
|
||||
settings[sname] = (scontent == "1")
|
||||
} else if stype == "int" {
|
||||
settings[sname], err = strconv.Atoi(scontent)
|
||||
if err != nil {
|
||||
|
|
113
templates.go
113
templates.go
|
@ -1,4 +1,5 @@
|
|||
package main
|
||||
|
||||
import "log"
|
||||
import "fmt"
|
||||
import "bytes"
|
||||
|
@ -90,7 +91,7 @@ func (c *CTemplateSet) compile_template(name string, dir string, expects string,
|
|||
}
|
||||
|
||||
content := string(res)
|
||||
if !debug {
|
||||
if minify_templates {
|
||||
content = minify(content)
|
||||
}
|
||||
|
||||
|
@ -100,7 +101,7 @@ func (c *CTemplateSet) compile_template(name string, dir string, expects string,
|
|||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
if debug {
|
||||
if super_debug {
|
||||
fmt.Println(name)
|
||||
}
|
||||
|
||||
|
@ -110,7 +111,7 @@ func (c *CTemplateSet) compile_template(name string, dir string, expects string,
|
|||
c.tlist[fname] = tree
|
||||
varholder := "tmpl_" + fname + "_vars"
|
||||
|
||||
if debug {
|
||||
if super_debug {
|
||||
fmt.Println(c.tlist)
|
||||
}
|
||||
c.localVars = make(map[string]map[string]VarItemReflect)
|
||||
|
@ -123,13 +124,13 @@ func (c *CTemplateSet) compile_template(name string, dir string, expects string,
|
|||
c.FragmentCursor[fname] = 0
|
||||
|
||||
subtree := c.tlist[fname]
|
||||
if debug {
|
||||
if super_debug {
|
||||
fmt.Println(subtree.Root)
|
||||
}
|
||||
|
||||
treeLength := len(subtree.Root.Nodes)
|
||||
for index, node := range subtree.Root.Nodes {
|
||||
if debug {
|
||||
if super_debug {
|
||||
fmt.Println("Node: " + node.String())
|
||||
}
|
||||
|
||||
|
@ -165,12 +166,14 @@ w.Write([]byte(`," + ",-1)
|
|||
//whitespace_writes := regexp.MustCompile(`(?s)w.Write\(\[\]byte\(`+spstr+`\)\)`)
|
||||
//fout = whitespace_writes.ReplaceAllString(fout,"")
|
||||
|
||||
for index, count := range c.stats {
|
||||
fmt.Println(index + ": " + strconv.Itoa(count))
|
||||
}
|
||||
fmt.Println(" ")
|
||||
|
||||
if debug {
|
||||
for index, count := range c.stats {
|
||||
fmt.Println(index + ": " + strconv.Itoa(count))
|
||||
}
|
||||
fmt.Println(" ")
|
||||
}
|
||||
|
||||
if super_debug {
|
||||
fmt.Println("Output!")
|
||||
fmt.Println(fout)
|
||||
}
|
||||
|
@ -180,7 +183,7 @@ w.Write([]byte(`," + ",-1)
|
|||
func (c *CTemplateSet) compile_switch(varholder string, holdreflect reflect.Value, template_name string, node interface{}) (out string) {
|
||||
switch node := node.(type) {
|
||||
case *parse.ActionNode:
|
||||
if debug {
|
||||
if super_debug {
|
||||
fmt.Println("Action Node")
|
||||
}
|
||||
|
||||
|
@ -192,14 +195,14 @@ func (c *CTemplateSet) compile_switch(varholder string, holdreflect reflect.Valu
|
|||
}
|
||||
return out
|
||||
case *parse.IfNode:
|
||||
if debug {
|
||||
if super_debug {
|
||||
fmt.Println("If Node: ")
|
||||
fmt.Println(node.Pipe)
|
||||
}
|
||||
|
||||
var expr string
|
||||
for _, cmd := range node.Pipe.Cmds {
|
||||
if debug {
|
||||
if super_debug {
|
||||
fmt.Println("If Node Bit: ")
|
||||
fmt.Println(cmd)
|
||||
fmt.Println(reflect.ValueOf(cmd).Type().Name())
|
||||
|
@ -211,18 +214,18 @@ func (c *CTemplateSet) compile_switch(varholder string, holdreflect reflect.Valu
|
|||
c.currentNode = parse.NodeList
|
||||
c.nextNode = -1
|
||||
if node.ElseList == nil {
|
||||
if debug {
|
||||
if super_debug {
|
||||
fmt.Println("Branch 1")
|
||||
}
|
||||
return "if " + expr + " {\n" + c.compile_switch(varholder, holdreflect, template_name, node.List) + "}\n"
|
||||
} else {
|
||||
if debug {
|
||||
if super_debug {
|
||||
fmt.Println("Branch 2")
|
||||
}
|
||||
return "if " + expr + " {\n" + c.compile_switch(varholder, holdreflect, template_name, node.List) + "} else {\n" + c.compile_switch(varholder, holdreflect, template_name, node.ElseList) + "}\n"
|
||||
}
|
||||
case *parse.ListNode:
|
||||
if debug {
|
||||
if super_debug {
|
||||
fmt.Println("List Node")
|
||||
}
|
||||
for _, subnode := range node.Nodes {
|
||||
|
@ -230,21 +233,21 @@ func (c *CTemplateSet) compile_switch(varholder string, holdreflect reflect.Valu
|
|||
}
|
||||
return out
|
||||
case *parse.RangeNode:
|
||||
if debug {
|
||||
if super_debug {
|
||||
fmt.Println("Range Node!")
|
||||
fmt.Println(node.Pipe)
|
||||
}
|
||||
|
||||
var outVal reflect.Value
|
||||
for _, cmd := range node.Pipe.Cmds {
|
||||
if debug {
|
||||
if super_debug {
|
||||
fmt.Println("Range Bit: ")
|
||||
fmt.Println(cmd)
|
||||
}
|
||||
out, outVal = c.compile_reflectswitch(varholder, holdreflect, template_name, cmd)
|
||||
}
|
||||
|
||||
if debug {
|
||||
if super_debug {
|
||||
fmt.Println("Returned: ")
|
||||
fmt.Println(out)
|
||||
fmt.Println("Range Kind Switch!")
|
||||
|
@ -277,7 +280,7 @@ func (c *CTemplateSet) compile_switch(varholder string, holdreflect reflect.Valu
|
|||
}
|
||||
return out
|
||||
case *parse.TemplateNode:
|
||||
if debug {
|
||||
if super_debug {
|
||||
fmt.Println("Template Node")
|
||||
}
|
||||
return c.compile_subtemplate(varholder, holdreflect, node)
|
||||
|
@ -309,7 +312,7 @@ func (c *CTemplateSet) compile_subswitch(varholder string, holdreflect reflect.V
|
|||
firstWord := node.Args[0]
|
||||
switch n := firstWord.(type) {
|
||||
case *parse.FieldNode:
|
||||
if debug {
|
||||
if super_debug {
|
||||
fmt.Println("Field Node: ")
|
||||
fmt.Println(n.Ident)
|
||||
}
|
||||
|
@ -324,7 +327,7 @@ func (c *CTemplateSet) compile_subswitch(varholder string, holdreflect reflect.V
|
|||
}
|
||||
|
||||
for _, id := range n.Ident {
|
||||
if debug {
|
||||
if super_debug {
|
||||
fmt.Println("Data Kind: ")
|
||||
fmt.Println(cur.Kind().String())
|
||||
fmt.Println("Field Bit: ")
|
||||
|
@ -346,7 +349,7 @@ func (c *CTemplateSet) compile_subswitch(varholder string, holdreflect reflect.V
|
|||
} else {
|
||||
varbit += "." + id
|
||||
}
|
||||
if debug {
|
||||
if super_debug {
|
||||
fmt.Println("End Cycle")
|
||||
}
|
||||
}
|
||||
|
@ -359,7 +362,7 @@ func (c *CTemplateSet) compile_subswitch(varholder string, holdreflect reflect.V
|
|||
}
|
||||
return out
|
||||
case *parse.DotNode:
|
||||
if debug {
|
||||
if super_debug {
|
||||
fmt.Println("Dot Node")
|
||||
fmt.Println(node.String())
|
||||
}
|
||||
|
@ -367,7 +370,7 @@ func (c *CTemplateSet) compile_subswitch(varholder string, holdreflect reflect.V
|
|||
case *parse.NilNode:
|
||||
panic("Nil is not a command x.x")
|
||||
case *parse.VariableNode:
|
||||
if debug {
|
||||
if super_debug {
|
||||
fmt.Println("Variable Node")
|
||||
fmt.Println(n.String())
|
||||
fmt.Println(n.Ident)
|
||||
|
@ -377,7 +380,7 @@ func (c *CTemplateSet) compile_subswitch(varholder string, holdreflect reflect.V
|
|||
case *parse.StringNode:
|
||||
return n.Quoted
|
||||
case *parse.IdentifierNode:
|
||||
if debug {
|
||||
if super_debug {
|
||||
fmt.Println("Identifier Node: ")
|
||||
fmt.Println(node)
|
||||
fmt.Println(node.Args)
|
||||
|
@ -397,7 +400,7 @@ func (c *CTemplateSet) compile_varswitch(varholder string, holdreflect reflect.V
|
|||
firstWord := node.Args[0]
|
||||
switch n := firstWord.(type) {
|
||||
case *parse.FieldNode:
|
||||
if debug {
|
||||
if super_debug {
|
||||
fmt.Println("Field Node: ")
|
||||
fmt.Println(n.Ident)
|
||||
|
||||
|
@ -410,14 +413,14 @@ func (c *CTemplateSet) compile_varswitch(varholder string, holdreflect reflect.V
|
|||
/* Use reflect to determine if the field is for a method, otherwise assume it's a variable. Coming Soon. */
|
||||
return c.compile_boolsub(n.String(), varholder, template_name, holdreflect)
|
||||
case *parse.ChainNode:
|
||||
if debug {
|
||||
if super_debug {
|
||||
fmt.Println("Chain Node: ")
|
||||
fmt.Println(n.Node)
|
||||
fmt.Println(node.Args)
|
||||
}
|
||||
break
|
||||
case *parse.IdentifierNode:
|
||||
if debug {
|
||||
if super_debug {
|
||||
fmt.Println("Identifier Node: ")
|
||||
fmt.Println(node)
|
||||
fmt.Println(node.Args)
|
||||
|
@ -426,7 +429,7 @@ func (c *CTemplateSet) compile_varswitch(varholder string, holdreflect reflect.V
|
|||
case *parse.DotNode:
|
||||
return varholder
|
||||
case *parse.VariableNode:
|
||||
if debug {
|
||||
if super_debug {
|
||||
fmt.Println("Variable Node")
|
||||
fmt.Println(n.String())
|
||||
fmt.Println(n.Ident)
|
||||
|
@ -436,7 +439,7 @@ func (c *CTemplateSet) compile_varswitch(varholder string, holdreflect reflect.V
|
|||
case *parse.NilNode:
|
||||
panic("Nil is not a command x.x")
|
||||
case *parse.PipeNode:
|
||||
if debug {
|
||||
if super_debug {
|
||||
fmt.Println("Pipe Node!")
|
||||
fmt.Println(n)
|
||||
fmt.Println("Args: ")
|
||||
|
@ -444,7 +447,7 @@ func (c *CTemplateSet) compile_varswitch(varholder string, holdreflect reflect.V
|
|||
}
|
||||
out += c.compile_identswitch_n(varholder, holdreflect, template_name, node)
|
||||
|
||||
if debug {
|
||||
if super_debug {
|
||||
fmt.Println("Out: ")
|
||||
fmt.Println(out)
|
||||
}
|
||||
|
@ -467,7 +470,7 @@ func (c *CTemplateSet) compile_identswitch_n(varholder string, holdreflect refle
|
|||
func (c *CTemplateSet) compile_identswitch(varholder string, holdreflect reflect.Value, template_name string, node *parse.CommandNode) (out string, val reflect.Value) {
|
||||
ArgLoop:
|
||||
for pos, id := range node.Args {
|
||||
if debug {
|
||||
if super_debug {
|
||||
fmt.Println(id)
|
||||
}
|
||||
|
||||
|
@ -480,42 +483,42 @@ func (c *CTemplateSet) compile_identswitch(varholder string, holdreflect reflect
|
|||
out += " && "
|
||||
case "le":
|
||||
out += c.compile_if_varsub_n(node.Args[pos + 1].String(), varholder, template_name, holdreflect) + " <= " + c.compile_if_varsub_n(node.Args[pos + 2].String(), varholder, template_name, holdreflect)
|
||||
if debug {
|
||||
if super_debug {
|
||||
fmt.Println(node.Args[pos + 1])
|
||||
fmt.Println(node.Args[pos + 2])
|
||||
}
|
||||
break ArgLoop
|
||||
case "lt":
|
||||
out += c.compile_if_varsub_n(node.Args[pos + 1].String(), varholder, template_name, holdreflect) + " < " + c.compile_if_varsub_n(node.Args[pos + 2].String(), varholder, template_name, holdreflect)
|
||||
if debug {
|
||||
if super_debug {
|
||||
fmt.Println(node.Args[pos + 1])
|
||||
fmt.Println(node.Args[pos + 2])
|
||||
}
|
||||
break ArgLoop
|
||||
case "gt":
|
||||
out += c.compile_if_varsub_n(node.Args[pos + 1].String(), varholder, template_name, holdreflect) + " > " + c.compile_if_varsub_n(node.Args[pos + 2].String(), varholder, template_name, holdreflect)
|
||||
if debug {
|
||||
if super_debug {
|
||||
fmt.Println(node.Args[pos + 1])
|
||||
fmt.Println(node.Args[pos + 2])
|
||||
}
|
||||
break ArgLoop
|
||||
case "ge":
|
||||
out += c.compile_if_varsub_n(node.Args[pos + 1].String(), varholder, template_name, holdreflect) + " >= " + c.compile_if_varsub_n(node.Args[pos + 2].String(), varholder, template_name, holdreflect)
|
||||
if debug {
|
||||
if super_debug {
|
||||
fmt.Println(node.Args[pos + 1])
|
||||
fmt.Println(node.Args[pos + 2])
|
||||
}
|
||||
break ArgLoop
|
||||
case "eq":
|
||||
out += c.compile_if_varsub_n(node.Args[pos + 1].String(), varholder, template_name, holdreflect) + " == " + c.compile_if_varsub_n(node.Args[pos + 2].String(), varholder, template_name, holdreflect)
|
||||
if debug {
|
||||
if super_debug {
|
||||
fmt.Println(node.Args[pos + 1])
|
||||
fmt.Println(node.Args[pos + 2])
|
||||
}
|
||||
break ArgLoop
|
||||
case "ne":
|
||||
out += c.compile_if_varsub_n(node.Args[pos + 1].String(), varholder, template_name, holdreflect) + " != " + c.compile_if_varsub_n(node.Args[pos + 2].String(), varholder, template_name, holdreflect)
|
||||
if debug {
|
||||
if super_debug {
|
||||
fmt.Println(node.Args[pos + 1])
|
||||
fmt.Println(node.Args[pos + 2])
|
||||
}
|
||||
|
@ -534,7 +537,7 @@ func (c *CTemplateSet) compile_identswitch(varholder string, holdreflect reflect
|
|||
}
|
||||
|
||||
out += param1 + " + " + param2
|
||||
if debug {
|
||||
if super_debug {
|
||||
fmt.Println("add")
|
||||
fmt.Println(node.Args[pos + 1])
|
||||
fmt.Println(node.Args[pos + 2])
|
||||
|
@ -554,7 +557,7 @@ func (c *CTemplateSet) compile_identswitch(varholder string, holdreflect reflect
|
|||
}
|
||||
|
||||
out += param1 + " - " + param2
|
||||
if debug {
|
||||
if super_debug {
|
||||
fmt.Println("subtract")
|
||||
fmt.Println(node.Args[pos + 1])
|
||||
fmt.Println(node.Args[pos + 2])
|
||||
|
@ -574,7 +577,7 @@ func (c *CTemplateSet) compile_identswitch(varholder string, holdreflect reflect
|
|||
}
|
||||
|
||||
out += param1 + " / " + param2
|
||||
if debug {
|
||||
if super_debug {
|
||||
fmt.Println("divide")
|
||||
fmt.Println(node.Args[pos + 1])
|
||||
fmt.Println(node.Args[pos + 2])
|
||||
|
@ -594,14 +597,14 @@ func (c *CTemplateSet) compile_identswitch(varholder string, holdreflect reflect
|
|||
}
|
||||
|
||||
out += param1 + " * " + param2
|
||||
if debug {
|
||||
if super_debug {
|
||||
fmt.Println("multiply")
|
||||
fmt.Println(node.Args[pos + 1])
|
||||
fmt.Println(node.Args[pos + 2])
|
||||
}
|
||||
break ArgLoop
|
||||
default:
|
||||
if debug {
|
||||
if super_debug {
|
||||
fmt.Println("Variable!")
|
||||
}
|
||||
out += c.compile_if_varsub_n(id.String(), varholder, template_name, holdreflect)
|
||||
|
@ -614,7 +617,7 @@ func (c *CTemplateSet) compile_reflectswitch(varholder string, holdreflect refle
|
|||
firstWord := node.Args[0]
|
||||
switch n := firstWord.(type) {
|
||||
case *parse.FieldNode:
|
||||
if debug {
|
||||
if super_debug {
|
||||
fmt.Println("Field Node: ")
|
||||
fmt.Println(n.Ident)
|
||||
|
||||
|
@ -626,7 +629,7 @@ func (c *CTemplateSet) compile_reflectswitch(varholder string, holdreflect refle
|
|||
/* Use reflect to determine if the field is for a method, otherwise assume it's a variable. Coming Soon. */
|
||||
return c.compile_if_varsub(n.String(), varholder, template_name, holdreflect)
|
||||
case *parse.ChainNode:
|
||||
if debug {
|
||||
if super_debug {
|
||||
fmt.Println("Chain Node: ")
|
||||
fmt.Println(n.Node)
|
||||
fmt.Println(node.Args)
|
||||
|
@ -676,7 +679,7 @@ func (c *CTemplateSet) compile_if_varsub(varname string, varholder string, templ
|
|||
}
|
||||
bits[0] = strings.TrimPrefix(bits[0],"$")
|
||||
|
||||
if debug {
|
||||
if super_debug {
|
||||
fmt.Println("Cur Kind: ")
|
||||
fmt.Println(cur.Kind())
|
||||
fmt.Println("Cur Type: ")
|
||||
|
@ -684,7 +687,7 @@ func (c *CTemplateSet) compile_if_varsub(varname string, varholder string, templ
|
|||
}
|
||||
|
||||
for _, bit := range bits {
|
||||
if debug {
|
||||
if super_debug {
|
||||
fmt.Println("Variable Field!")
|
||||
fmt.Println(bit)
|
||||
}
|
||||
|
@ -701,7 +704,7 @@ func (c *CTemplateSet) compile_if_varsub(varname string, varholder string, templ
|
|||
out += "." + bit
|
||||
}
|
||||
|
||||
if debug {
|
||||
if super_debug {
|
||||
fmt.Println("Data Kind: ")
|
||||
fmt.Println(cur.Kind())
|
||||
fmt.Println("Data Type: ")
|
||||
|
@ -709,7 +712,7 @@ func (c *CTemplateSet) compile_if_varsub(varname string, varholder string, templ
|
|||
}
|
||||
}
|
||||
|
||||
if debug {
|
||||
if super_debug {
|
||||
fmt.Println("Out Value: ")
|
||||
fmt.Println(out)
|
||||
fmt.Println("Out Kind: ")
|
||||
|
@ -724,7 +727,7 @@ func (c *CTemplateSet) compile_if_varsub(varname string, varholder string, templ
|
|||
}
|
||||
}
|
||||
|
||||
if debug {
|
||||
if super_debug {
|
||||
fmt.Println("Out Value: ")
|
||||
fmt.Println(out)
|
||||
fmt.Println("Out Kind: ")
|
||||
|
@ -802,7 +805,7 @@ func (c *CTemplateSet) compile_varsub(varname string, val reflect.Value) string
|
|||
}
|
||||
|
||||
func (c *CTemplateSet) compile_subtemplate(pvarholder string, pholdreflect reflect.Value, node *parse.TemplateNode) (out string) {
|
||||
if debug {
|
||||
if super_debug {
|
||||
fmt.Println("Template Node: " + node.Name)
|
||||
}
|
||||
|
||||
|
@ -832,7 +835,7 @@ func (c *CTemplateSet) compile_subtemplate(pvarholder string, pholdreflect refle
|
|||
}
|
||||
|
||||
content := string(res)
|
||||
if !debug {
|
||||
if minify_templates {
|
||||
content = minify(content)
|
||||
}
|
||||
|
||||
|
@ -845,7 +848,7 @@ func (c *CTemplateSet) compile_subtemplate(pvarholder string, pholdreflect refle
|
|||
|
||||
c.tlist[fname] = tree
|
||||
subtree := c.tlist[fname]
|
||||
if debug {
|
||||
if super_debug {
|
||||
fmt.Println(subtree.Root)
|
||||
}
|
||||
|
||||
|
@ -855,7 +858,7 @@ func (c *CTemplateSet) compile_subtemplate(pvarholder string, pholdreflect refle
|
|||
|
||||
treeLength := len(subtree.Root.Nodes)
|
||||
for index, node := range subtree.Root.Nodes {
|
||||
if debug {
|
||||
if super_debug {
|
||||
fmt.Println("Node: " + node.String())
|
||||
}
|
||||
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
{{template "header.html" . }}
|
||||
{{template "panel-menu.html" . }}
|
||||
<div class="colstack_right">
|
||||
<div class="colstack_item colstack_head">
|
||||
<div class="rowitem rowhead"><a>Dashboard</a></div>
|
||||
</div>
|
||||
<div class="colstack_item">
|
||||
<div class="rowitem passive">Coming Soon...</div>
|
||||
<div class="colstack_grid">
|
||||
{{range .GridItems}}
|
||||
<div class="grid_item {{.Class}}" title="{{.Note}}" style="{{if .TextColour}}color: {{.TextColour}};{{end}}
|
||||
{{if .Background}}background-color: {{.Background}};{{end}}">{{.Body}}</div>
|
||||
{{end}}
|
||||
</div>
|
||||
</div>
|
||||
{{template "footer.html" . }}
|
|
@ -20,7 +20,7 @@
|
|||
<a style="font-size: 17px;">{{.Action}}</a><br />
|
||||
<small style="margin-left: 2px;">IP: {{.IPAddress}}</small>
|
||||
<span style="float: right;">
|
||||
<span style="font-size: 16px;position:relative;top: -2px;">{{.DoneAt}}</span>
|
||||
<span style="font-size: 16px;">{{.DoneAt}}</span>
|
||||
</span>
|
||||
</div>
|
||||
{{end}}
|
||||
|
|
13
themes.go
13
themes.go
|
@ -77,7 +77,9 @@ func init_themes() {
|
|||
theme.Active = false // Set this to false, just in case someone explicitly overrode this value in the JSON file
|
||||
|
||||
if theme.FullImage != "" {
|
||||
log.Print("Adding theme image")
|
||||
if debug {
|
||||
log.Print("Adding theme image")
|
||||
}
|
||||
err = add_static_file("./themes/" + themeName + "/" + theme.FullImage, "./themes/" + themeName)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
|
@ -98,14 +100,19 @@ func add_theme_static_files(themeName string) {
|
|||
}
|
||||
path = strings.Replace(path,"\\","/",-1)
|
||||
|
||||
log.Print("Attempting to add static file '" + path + "' for default theme '" + themeName + "'")
|
||||
if debug {
|
||||
log.Print("Attempting to add static file '" + path + "' for default theme '" + themeName + "'")
|
||||
}
|
||||
|
||||
data, err := ioutil.ReadFile(path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
path = strings.TrimPrefix(path,"themes/" + themeName + "/public")
|
||||
log.Print("Added the '" + path + "' static file for default theme " + themeName + ".")
|
||||
if debug {
|
||||
log.Print("Added the '" + path + "' static file for default theme " + themeName + ".")
|
||||
}
|
||||
gzip_data := compress_bytes_gzip(data)
|
||||
|
||||
static_files["/static" + path] = SFile{data,gzip_data,0,int64(len(data)),int64(len(gzip_data)),mime.TypeByExtension(filepath.Ext("/themes/" + themeName + "/public" + path)),f,f.ModTime().UTC().Format(http.TimeFormat)}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
/* AtomBB Cosmo Port. Copyright Azareal 2017 */
|
||||
/* AtomBB Cosmo Port. Copyright Azareal 2017 - 2018 */
|
||||
/* I'm currently converting the CSS over. Don't use this yet! */
|
||||
|
||||
* {
|
||||
|
@ -261,7 +261,7 @@ hr { color: silver; border: 1px solid silver; }
|
|||
.colblock_left:first-of-type { margin-top: 8px; }
|
||||
.colblock_right:first-of-type { margin-top: 8px; }
|
||||
|
||||
/* The new method of doing columns layouts, colblock is now deprecated */
|
||||
/* The new method of doing columns layouts, colblock is now deprecated :( */
|
||||
.colstack_left
|
||||
{
|
||||
float: left;
|
||||
|
@ -289,6 +289,39 @@ hr { color: silver; border: 1px solid silver; }
|
|||
.colstack_left:empty { display: none; }
|
||||
.colstack_right:empty { display: none; }
|
||||
|
||||
.colstack_grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
/*grid-gap: 15px;*/
|
||||
grid-gap: 12px;
|
||||
margin-left: 5px;
|
||||
}
|
||||
.grid_item {
|
||||
border: 1px solid #ccc;
|
||||
word-wrap: break-word;
|
||||
background-color: white;
|
||||
width: 100%;
|
||||
overflow: hidden;
|
||||
}
|
||||
.grid_stat, .grid_istat {
|
||||
/*padding-top: 15px;*/
|
||||
text-align: center;
|
||||
/*padding-bottom: 15px;
|
||||
font-size: 20px;*/
|
||||
padding-top: 12px;
|
||||
padding-bottom: 12px;
|
||||
font-size: 16px;
|
||||
}
|
||||
.grid_istat {
|
||||
/*margin-bottom: 10px;*/
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
.stat_green { background-color: lightgreen; border-color: green; }
|
||||
.stat_orange { background-color: #ffe4b3; border-color: orange; }
|
||||
.stat_red { background-color: #ffb2b2; border-color: red; }
|
||||
.stat_disabled { background-color: lightgray; border-color: gray; }
|
||||
|
||||
.colitem
|
||||
{
|
||||
padding-left: 8px;
|
||||
|
|
|
@ -196,13 +196,8 @@ hr { color: silver; border: 1px solid silver; }
|
|||
border-left: 1px solid black;
|
||||
border-right: 1px solid black;
|
||||
}
|
||||
.rowblock:first-of-type {
|
||||
margin-top: 8px;
|
||||
}
|
||||
.rowitem:not(:last-child)
|
||||
{
|
||||
border-bottom: 1px dotted #ccc;
|
||||
}
|
||||
.rowblock:first-of-type { margin-top: 8px; }
|
||||
.rowitem:not(:last-child) { border-bottom: 1px dotted #ccc; }
|
||||
|
||||
.rowhead, .colhead {
|
||||
background: #ce2424;
|
||||
|
@ -283,6 +278,39 @@ hr { color: silver; border: 1px solid silver; }
|
|||
.colstack_left:empty { display: none; }
|
||||
.colstack_right:empty { display: none; }
|
||||
|
||||
.colstack_grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
/*grid-gap: 15px;*/
|
||||
grid-gap: 12px;
|
||||
margin-left: 5px;
|
||||
}
|
||||
.grid_item {
|
||||
border: 1px solid #ccc;
|
||||
word-wrap: break-word;
|
||||
background-color: white;
|
||||
width: 100%;
|
||||
overflow: hidden;
|
||||
}
|
||||
.grid_stat, .grid_istat {
|
||||
/*padding-top: 15px;*/
|
||||
text-align: center;
|
||||
/*padding-bottom: 15px;
|
||||
font-size: 20px;*/
|
||||
padding-top: 12px;
|
||||
padding-bottom: 12px;
|
||||
font-size: 16px;
|
||||
}
|
||||
.grid_istat {
|
||||
/*margin-bottom: 10px;*/
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
.stat_green { background-color: lightgreen; border-color: green; }
|
||||
.stat_orange { background-color: #ffe4b3; border-color: orange; }
|
||||
.stat_red { background-color: #ffb2b2; border-color: red; }
|
||||
.stat_disabled { background-color: lightgray; border-color: gray; }
|
||||
|
||||
.colitem
|
||||
{
|
||||
padding-left: 8px;
|
||||
|
@ -302,10 +330,7 @@ hr { color: silver; border: 1px solid silver; }
|
|||
text-decoration: none;
|
||||
color: black;
|
||||
}
|
||||
.colitem a:hover
|
||||
{
|
||||
color: silver;
|
||||
}
|
||||
.colitem a:hover { color: silver; }
|
||||
|
||||
.col_left
|
||||
{
|
||||
|
|
|
@ -178,7 +178,7 @@ li a
|
|||
.colblock_left:empty { display: none; }
|
||||
.colblock_right:empty { display: none; }
|
||||
|
||||
/* The new method of doing columns layouts, colblock is now deprecated */
|
||||
/* The new method of doing columns layouts, colblock is now deprecated :( */
|
||||
.colstack_left
|
||||
{
|
||||
float: left;
|
||||
|
@ -205,6 +205,39 @@ li a
|
|||
.colstack_left:empty { display: none; }
|
||||
.colstack_right:empty { display: none; }
|
||||
|
||||
.colstack_grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
/*grid-gap: 15px;*/
|
||||
grid-gap: 12px;
|
||||
margin-left: 5px;
|
||||
}
|
||||
.grid_item {
|
||||
border: 1px solid #ccc;
|
||||
word-wrap: break-word;
|
||||
background-color: white;
|
||||
width: 100%;
|
||||
overflow: hidden;
|
||||
}
|
||||
.grid_stat, .grid_istat {
|
||||
/*padding-top: 15px;*/
|
||||
text-align: center;
|
||||
/*padding-bottom: 15px;
|
||||
font-size: 20px;*/
|
||||
padding-top: 12px;
|
||||
padding-bottom: 12px;
|
||||
font-size: 16px;
|
||||
}
|
||||
.grid_istat {
|
||||
/*margin-bottom: 10px;*/
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
.stat_green { background-color: lightgreen; border-color: green; }
|
||||
.stat_orange { background-color: #ffe4b3; border-color: orange; }
|
||||
.stat_red { background-color: #ffb2b2; border-color: red; }
|
||||
.stat_disabled { background-color: lightgray; border-color: gray; }
|
||||
|
||||
.rowitem
|
||||
{
|
||||
width: 100%;
|
||||
|
|
|
@ -12,7 +12,7 @@ body
|
|||
|
||||
/* Patch for Edge */
|
||||
@supports (-ms-ime-align:auto) {
|
||||
.user_content { font-family: Segoe UI Emoji, arial; }
|
||||
.user_content { font-family: Segoe UI Emoji, arial; }
|
||||
}
|
||||
|
||||
ul
|
||||
|
@ -167,7 +167,7 @@ li a
|
|||
.colblock_left:empty { display: none; }
|
||||
.colblock_right:empty { display: none; }
|
||||
|
||||
/* The new method of doing columns layouts, colblock is now deprecated */
|
||||
/* The new method of doing columns layouts, colblock is now deprecated :( */
|
||||
.colstack_left
|
||||
{
|
||||
float: left;
|
||||
|
@ -194,6 +194,39 @@ li a
|
|||
.colstack_left:empty { display: none; }
|
||||
.colstack_right:empty { display: none; }
|
||||
|
||||
.colstack_grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
/*grid-gap: 15px;*/
|
||||
grid-gap: 12px;
|
||||
margin-left: 5px;
|
||||
}
|
||||
.grid_item {
|
||||
border: 1px solid #ccc;
|
||||
word-wrap: break-word;
|
||||
background-color: white;
|
||||
width: 100%;
|
||||
overflow: hidden;
|
||||
}
|
||||
.grid_stat, .grid_istat {
|
||||
/*padding-top: 15px;*/
|
||||
text-align: center;
|
||||
/*padding-bottom: 15px;
|
||||
font-size: 20px;*/
|
||||
padding-top: 12px;
|
||||
padding-bottom: 12px;
|
||||
font-size: 16px;
|
||||
}
|
||||
.grid_istat {
|
||||
/*margin-bottom: 10px;*/
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
.stat_green { background-color: lightgreen; border-color: lightgreen; }
|
||||
.stat_orange { background-color: #ffe4b3; border-color: #ffe4b3; }
|
||||
.stat_red { background-color: #ffb2b2; border-color: #ffb2b2; }
|
||||
.stat_disabled { background-color: lightgray; border-color: lightgray; }
|
||||
|
||||
.rowhead { font-family: cursive; }
|
||||
.rowitem
|
||||
{
|
||||
|
|
|
@ -12,7 +12,7 @@ body
|
|||
|
||||
/* Patch for Edge */
|
||||
@supports (-ms-ime-align:auto) {
|
||||
.user_content { font-family: Segoe UI Emoji, arial; }
|
||||
.user_content { font-family: Segoe UI Emoji, arial; }
|
||||
}
|
||||
|
||||
/*.move_left{float: left;position: relative;left: 50%;}
|
||||
|
@ -167,7 +167,7 @@ li a
|
|||
.colblock_left:empty { display: none; }
|
||||
.colblock_right:empty { display: none; }
|
||||
|
||||
/* The new method of doing columns layouts, colblock is now deprecated */
|
||||
/* The new method of doing columns layouts, colblock is now deprecated :( */
|
||||
.colstack_left
|
||||
{
|
||||
float: left;
|
||||
|
@ -194,6 +194,39 @@ li a
|
|||
.colstack_left:empty { display: none; }
|
||||
.colstack_right:empty { display: none; }
|
||||
|
||||
.colstack_grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
/*grid-gap: 15px;*/
|
||||
grid-gap: 12px;
|
||||
margin-left: 5px;
|
||||
}
|
||||
.grid_item {
|
||||
border: 1px solid #ccc;
|
||||
word-wrap: break-word;
|
||||
background-color: white;
|
||||
width: 100%;
|
||||
overflow: hidden;
|
||||
}
|
||||
.grid_stat, .grid_istat {
|
||||
/*padding-top: 15px;*/
|
||||
text-align: center;
|
||||
/*padding-bottom: 15px;
|
||||
font-size: 20px;*/
|
||||
padding-top: 12px;
|
||||
padding-bottom: 12px;
|
||||
font-size: 16px;
|
||||
}
|
||||
.grid_istat {
|
||||
/*margin-bottom: 10px;*/
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
.stat_green { background-color: lightgreen; border-color: lightgreen; }
|
||||
.stat_orange { background-color: #ffe4b3; border-color: #ffe4b3; }
|
||||
.stat_red { background-color: #ffb2b2; border-color: #ffb2b2; }
|
||||
.stat_disabled { background-color: lightgray; border-color: lightgray; }
|
||||
|
||||
.rowitem
|
||||
{
|
||||
width: 100%;
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
echo "Updating the MySQL Driver"
|
||||
go get -u github.com/go-sql-driver/mysql
|
||||
echo "Updating bcrypt"
|
||||
go get -u golang.org/x/crypto/bcrypt
|
||||
go get -u golang.org/x/crypto/bcrypt
|
||||
echo "Updating gopsutil"
|
||||
go get -u github.com/shirou/gopsutil
|
||||
|
|
|
@ -13,5 +13,19 @@ if %errorlevel% neq 0 (
|
|||
exit /b %errorlevel%
|
||||
)
|
||||
|
||||
echo Updating wmi (dependency for gopsutil)
|
||||
go get -u github.com/StackExchange/wmi
|
||||
if %errorlevel% neq 0 (
|
||||
pause
|
||||
exit /b %errorlevel%
|
||||
)
|
||||
|
||||
echo Updating gopsutil
|
||||
go get -u github.com/shirou/gopsutil
|
||||
if %errorlevel% neq 0 (
|
||||
pause
|
||||
exit /b %errorlevel%
|
||||
)
|
||||
|
||||
echo The dependencies were successfully updated
|
||||
pause
|
47
utils.go
47
utils.go
|
@ -6,10 +6,31 @@ import "os"
|
|||
import "math"
|
||||
import "strings"
|
||||
import "unicode"
|
||||
import "strconv"
|
||||
import "encoding/base64"
|
||||
import "crypto/rand"
|
||||
import "net/smtp"
|
||||
|
||||
type Version struct
|
||||
{
|
||||
Major int
|
||||
Minor int
|
||||
Patch int
|
||||
Tag string
|
||||
TagID int
|
||||
}
|
||||
|
||||
func (version *Version) String() (out string) {
|
||||
out = strconv.Itoa(version.Major) + "." + strconv.Itoa(version.Minor) + "." + strconv.Itoa(version.Patch)
|
||||
if version.Tag != "" {
|
||||
out += "-" + version.Tag
|
||||
if version.TagID != 0 {
|
||||
out += strconv.Itoa(version.TagID)
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Generate a cryptographically secure set of random bytes..
|
||||
func GenerateSafeString(length int) (string, error) {
|
||||
rb := make([]byte,length)
|
||||
|
@ -66,6 +87,32 @@ func relative_time(in string) (string, error) {
|
|||
}
|
||||
}
|
||||
|
||||
func convert_byte_unit(bytes float64) (float64,string) {
|
||||
switch
|
||||
{
|
||||
case bytes >= float64(terabyte): return bytes / float64(terabyte), "TB"
|
||||
case bytes >= float64(gigabyte): return bytes / float64(gigabyte), "GB"
|
||||
case bytes >= float64(megabyte): return bytes / float64(megabyte), "MB"
|
||||
case bytes >= float64(kilobyte): return bytes / float64(kilobyte), "KB"
|
||||
default: return bytes, " bytes"
|
||||
}
|
||||
}
|
||||
|
||||
func convert_byte_in_unit(bytes float64,unit string) (count float64) {
|
||||
switch(unit) {
|
||||
case "TB": count = bytes / float64(terabyte)
|
||||
case "GB": count = bytes / float64(gigabyte)
|
||||
case "MB": count = bytes / float64(megabyte)
|
||||
case "KB": count = bytes / float64(kilobyte)
|
||||
default: count = 0.1
|
||||
}
|
||||
|
||||
if count < 0.1 {
|
||||
count = 0.1
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func SendEmail(email string, subject string, msg string) (res bool) {
|
||||
// This hook is useful for plugin_sendmail or for testing tools. Possibly to hook it into some sort of mail server?
|
||||
if vhooks["email_send_intercept"] != nil {
|
||||
|
|
Loading…
Reference in New Issue