Static CSS files are now processed by the template system. This will be most likely be superseded by a more sophisticated system in the future.
Added the authentication interface and associated struct to better organise / escapsulate the authentication logic. Added the LogError function. We'll put together a custom logger at some point which will supersede this. Reorganised things a little. Moved two queries into the query generator and inlined four with the query builder. Added SimpleInsertLeftJoin to the query generator. Added SimpleInsertSelect, SimpleInsertLeftJoin, and SimpleInsertInnerJoin to the query builder. Removed the undocumented password reset mechanism for security reasons. We have a proper interface for this in the control panel. Made one of the template compiler errors less cryptic. Fixed the CSS on widget_simple. Fixed a glitch in the weak password detector regarding unique character detection. Moved the responsive CSS in media.partial.css for Tempra Conflux, and Tempra Simple. Added the CreateUser method to the UserStore. Users are no longer logged out on all devices when logging out. Took the first step towards SEO URLs.
This commit is contained in:
parent
1cc1e47582
commit
21d623cba4
|
@ -0,0 +1,117 @@
|
|||
/* Work in progress */
|
||||
package main
|
||||
|
||||
import "log"
|
||||
import "errors"
|
||||
import "strconv"
|
||||
import "net/http"
|
||||
import "database/sql"
|
||||
|
||||
import "./query_gen/lib"
|
||||
import "golang.org/x/crypto/bcrypt"
|
||||
|
||||
var auth Auth
|
||||
var ErrMismatchedHashAndPassword = bcrypt.ErrMismatchedHashAndPassword
|
||||
|
||||
type Auth interface
|
||||
{
|
||||
Authenticate(username string, password string) (int,error)
|
||||
Logout(w http.ResponseWriter, uid int)
|
||||
ForceLogout(uid int) error
|
||||
SetCookies(w http.ResponseWriter, uid int, session string)
|
||||
CreateSession(uid int) (string, error)
|
||||
}
|
||||
|
||||
type DefaultAuth struct
|
||||
{
|
||||
login *sql.Stmt
|
||||
logout *sql.Stmt
|
||||
}
|
||||
|
||||
func NewDefaultAuth() *DefaultAuth {
|
||||
login_stmt, err := qgen.Builder.SimpleSelect("users","uid, name, password, salt","name = ?","","")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
logout_stmt, err := qgen.Builder.SimpleUpdate("users","session = ''","uid = ?")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
return &DefaultAuth{
|
||||
login: login_stmt,
|
||||
logout: logout_stmt,
|
||||
}
|
||||
}
|
||||
|
||||
func (auth *DefaultAuth) Authenticate(username string, password string) (uid int, err error) {
|
||||
var real_password, salt string
|
||||
err = auth.login.QueryRow(username).Scan(&uid, &username, &real_password, &salt)
|
||||
if err == sql.ErrNoRows {
|
||||
return 0, errors.New("We couldn't find an account with that username.")
|
||||
} else if err != nil {
|
||||
LogError(err)
|
||||
return 0, errors.New("There was a glitch in the system. Please contact the system administrator.")
|
||||
}
|
||||
|
||||
if salt == "" {
|
||||
// Send an email to admin for this?
|
||||
LogError(errors.New("Missing salt for user #" + strconv.Itoa(uid) + ". Potential security breach."))
|
||||
return 0, errors.New("There was a glitch in the system. Please contact the system administrator.")
|
||||
}
|
||||
|
||||
err = CheckPassword(real_password,password,salt)
|
||||
if err == ErrMismatchedHashAndPassword {
|
||||
return 0, errors.New("That's not the correct password.")
|
||||
} else if err != nil {
|
||||
LogError(err)
|
||||
return 0, errors.New("There was a glitch in the system. Please contact the system administrator.")
|
||||
}
|
||||
|
||||
return uid, nil
|
||||
}
|
||||
|
||||
func (auth *DefaultAuth) ForceLogout(uid int) error {
|
||||
_, err := auth.logout.Exec(uid)
|
||||
if err != nil {
|
||||
LogError(err)
|
||||
return errors.New("There was a glitch in the system. Please contact the system administrator.")
|
||||
}
|
||||
|
||||
// Flush the user out of the cache and reload
|
||||
err = users.Load(uid)
|
||||
if err != nil {
|
||||
return errors.New("Your account no longer exists!")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (auth *DefaultAuth) Logout(w http.ResponseWriter, _ int) {
|
||||
cookie := http.Cookie{Name:"uid",Value:"",Path:"/",MaxAge: year}
|
||||
http.SetCookie(w,&cookie)
|
||||
cookie = http.Cookie{Name:"session",Value:"",Path:"/",MaxAge: year}
|
||||
http.SetCookie(w,&cookie)
|
||||
}
|
||||
|
||||
func (auth *DefaultAuth) SetCookies(w http.ResponseWriter, uid int, session string) {
|
||||
cookie := http.Cookie{Name: "uid",Value: strconv.Itoa(uid),Path: "/",MaxAge: year}
|
||||
http.SetCookie(w,&cookie)
|
||||
cookie = http.Cookie{Name: "session",Value: session,Path: "/",MaxAge: year}
|
||||
http.SetCookie(w,&cookie)
|
||||
}
|
||||
|
||||
func(auth *DefaultAuth) CreateSession(uid int) (string, error) {
|
||||
session, err := GenerateSafeString(sessionLength)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
_, err = update_session_stmt.Exec(session, uid)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
// Reload the user data
|
||||
_ = users.Load(uid)
|
||||
return session, nil
|
||||
}
|
|
@ -26,6 +26,10 @@ func init_errors() error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func LogError(err error) {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
func InternalError(err error, w http.ResponseWriter, r *http.Request) {
|
||||
w.Write(error_internal)
|
||||
log.Fatal(err)
|
||||
|
|
98
files.go
98
files.go
|
@ -1,16 +1,17 @@
|
|||
package main
|
||||
|
||||
import "log"
|
||||
import "bytes"
|
||||
import "strings"
|
||||
import "mime"
|
||||
//import "errors"
|
||||
import "os"
|
||||
//import "io"
|
||||
import "io/ioutil"
|
||||
import "path/filepath"
|
||||
import "net/http"
|
||||
import "compress/gzip"
|
||||
import (
|
||||
"log"
|
||||
"bytes"
|
||||
"strings"
|
||||
"mime"
|
||||
//"errors"
|
||||
"os"
|
||||
"io/ioutil"
|
||||
"path/filepath"
|
||||
"net/http"
|
||||
"compress/gzip"
|
||||
)
|
||||
|
||||
type SFile struct
|
||||
{
|
||||
|
@ -24,42 +25,39 @@ type SFile struct
|
|||
FormattedModTime string
|
||||
}
|
||||
|
||||
/*func (r SFile) Read(b []byte) (n int, err error) {
|
||||
n = 0
|
||||
if r.Pos > r.Length {
|
||||
return n, io.EOF
|
||||
}
|
||||
|
||||
size := cap(b)
|
||||
if size > 0 {
|
||||
for n < size {
|
||||
b[n] = r.Data[r.Pos]
|
||||
n++
|
||||
if r.Pos == r.Length {
|
||||
break
|
||||
}
|
||||
r.Pos++
|
||||
}
|
||||
}
|
||||
return n, nil
|
||||
type CssData struct
|
||||
{
|
||||
ComingSoon string
|
||||
}
|
||||
|
||||
func (r SFile) Seek(offset int64, whence int) (int64, error) {
|
||||
if offset < 0 {
|
||||
return 0, errors.New("negative position")
|
||||
func init_static_files() {
|
||||
log.Print("Loading the static files.")
|
||||
err := filepath.Walk("./public", func(path string, f os.FileInfo, err error) error {
|
||||
if f.IsDir() {
|
||||
return nil
|
||||
}
|
||||
|
||||
path = strings.Replace(path,"\\","/",-1)
|
||||
data, err := ioutil.ReadFile(path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
path = strings.TrimPrefix(path,"public/")
|
||||
var ext string = filepath.Ext("/public/" + path)
|
||||
gzip_data := compress_bytes_gzip(data)
|
||||
|
||||
static_files["/static/" + path] = SFile{data,gzip_data,0,int64(len(data)),int64(len(gzip_data)),mime.TypeByExtension(ext),f,f.ModTime().UTC().Format(http.TimeFormat)}
|
||||
|
||||
if debug {
|
||||
log.Print("Added the '" + path + "' static file.")
|
||||
}
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
switch whence {
|
||||
case 0:
|
||||
r.Pos = offset
|
||||
case 1:
|
||||
r.Pos += offset
|
||||
case 2:
|
||||
r.Pos = r.Length + offset
|
||||
default:
|
||||
return 0, errors.New("invalid whence")
|
||||
}
|
||||
return r.Pos, nil
|
||||
}*/
|
||||
}
|
||||
|
||||
func add_static_file(path string, prefix string) error {
|
||||
data, err := ioutil.ReadFile(path)
|
||||
|
@ -74,20 +72,22 @@ func add_static_file(path string, prefix string) error {
|
|||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
|
||||
var ext string = filepath.Ext(path)
|
||||
path = strings.TrimPrefix(path, prefix)
|
||||
gzip_data := compress_bytes_gzip(data)
|
||||
|
||||
static_files["/static" + path] = SFile{data,gzip_data,0,int64(len(data)),int64(len(gzip_data)),mime.TypeByExtension(ext),f,f.ModTime().UTC().Format(http.TimeFormat)}
|
||||
|
||||
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)}
|
||||
return nil
|
||||
}
|
||||
|
||||
func compress_bytes_gzip(in []byte) []byte {
|
||||
var buff bytes.Buffer
|
||||
gz := gzip.NewWriter(&buff)
|
||||
gz := gzip.NewWriter(&buff)
|
||||
gz.Write(in)
|
||||
gz.Close()
|
||||
return buff.Bytes()
|
||||
|
|
42
gen_mysql.go
42
gen_mysql.go
|
@ -9,9 +9,7 @@ import "database/sql"
|
|||
var get_user_stmt *sql.Stmt
|
||||
var get_reply_stmt *sql.Stmt
|
||||
var get_user_reply_stmt *sql.Stmt
|
||||
var login_stmt *sql.Stmt
|
||||
var get_password_stmt *sql.Stmt
|
||||
var username_exists_stmt *sql.Stmt
|
||||
var get_settings_stmt *sql.Stmt
|
||||
var get_setting_stmt *sql.Stmt
|
||||
var get_full_setting_stmt *sql.Stmt
|
||||
|
@ -56,7 +54,6 @@ var create_action_reply_stmt *sql.Stmt
|
|||
var create_like_stmt *sql.Stmt
|
||||
var add_activity_stmt *sql.Stmt
|
||||
var notify_one_stmt *sql.Stmt
|
||||
var register_stmt *sql.Stmt
|
||||
var add_email_stmt *sql.Stmt
|
||||
var create_profile_reply_stmt *sql.Stmt
|
||||
var add_subscription_stmt *sql.Stmt
|
||||
|
@ -81,7 +78,6 @@ var stick_topic_stmt *sql.Stmt
|
|||
var unstick_topic_stmt *sql.Stmt
|
||||
var update_last_ip_stmt *sql.Stmt
|
||||
var update_session_stmt *sql.Stmt
|
||||
var logout_stmt *sql.Stmt
|
||||
var set_password_stmt *sql.Stmt
|
||||
var set_avatar_stmt *sql.Stmt
|
||||
var set_username_stmt *sql.Stmt
|
||||
|
@ -111,6 +107,8 @@ var delete_profile_reply_stmt *sql.Stmt
|
|||
var delete_forum_perms_by_forum_stmt *sql.Stmt
|
||||
var report_exists_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
|
||||
var notify_watchers_stmt *sql.Stmt
|
||||
|
||||
func gen_mysql() (err error) {
|
||||
|
@ -136,24 +134,12 @@ func gen_mysql() (err error) {
|
|||
return err
|
||||
}
|
||||
|
||||
log.Print("Preparing login statement.")
|
||||
login_stmt, err = db.Prepare("SELECT `uid`,`name`,`password`,`salt` FROM `users` WHERE `name` = ?")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
log.Print("Preparing get_password statement.")
|
||||
get_password_stmt, err = db.Prepare("SELECT `password`,`salt` FROM `users` WHERE `uid` = ?")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
log.Print("Preparing username_exists statement.")
|
||||
username_exists_stmt, err = db.Prepare("SELECT `name` FROM `users` WHERE `name` = ?")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
log.Print("Preparing get_settings statement.")
|
||||
get_settings_stmt, err = db.Prepare("SELECT `name`,`content`,`type` FROM `settings`")
|
||||
if err != nil {
|
||||
|
@ -418,12 +404,6 @@ func gen_mysql() (err error) {
|
|||
return err
|
||||
}
|
||||
|
||||
log.Print("Preparing register statement.")
|
||||
register_stmt, err = db.Prepare("INSERT INTO `users`(`name`,`email`,`password`,`salt`,`group`,`is_super_admin`,`session`,`active`,`message`) VALUES (?,?,?,?,?,0,?,?,'')")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
log.Print("Preparing add_email statement.")
|
||||
add_email_stmt, err = db.Prepare("INSERT INTO `emails`(`email`,`uid`,`validated`,`token`) VALUES (?,?,?,?)")
|
||||
if err != nil {
|
||||
|
@ -568,12 +548,6 @@ func gen_mysql() (err error) {
|
|||
return err
|
||||
}
|
||||
|
||||
log.Print("Preparing logout statement.")
|
||||
logout_stmt, err = db.Prepare("UPDATE `users` SET `session` = '' WHERE `uid` = ?")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
log.Print("Preparing set_password statement.")
|
||||
set_password_stmt, err = db.Prepare("UPDATE `users` SET `password` = ?,`salt` = ? WHERE `uid` = ?")
|
||||
if err != nil {
|
||||
|
@ -748,6 +722,18 @@ func gen_mysql() (err error) {
|
|||
return err
|
||||
}
|
||||
|
||||
log.Print("Preparing add_forum_perms_to_forum_staff statement.")
|
||||
add_forum_perms_to_forum_staff_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` = 0 AND `is_mod` = 1")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
log.Print("Preparing add_forum_perms_to_forum_members statement.")
|
||||
add_forum_perms_to_forum_members_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` = 0 AND `is_mod` = 0 AND `is_banned` = 0")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
log.Print("Preparing notify_watchers statement.")
|
||||
notify_watchers_stmt, err = db.Prepare("INSERT INTO `activity_stream_matches`(`watcher`,`asid`) SELECT `activity_subscriptions`.`user`, `activity_stream`.`asid` FROM `activity_stream` INNER JOIN `activity_subscriptions` ON `activity_subscriptions`.`targetType` = `activity_stream`.`elementType` AND `activity_subscriptions`.`targetID` = `activity_stream`.`elementID` AND `activity_subscriptions`.`user` != `activity_stream`.`actor` WHERE `asid` = ?")
|
||||
if err != nil {
|
||||
|
|
42
main.go
42
main.go
|
@ -5,13 +5,8 @@ import (
|
|||
"net/http"
|
||||
"fmt"
|
||||
"log"
|
||||
"mime"
|
||||
"time"
|
||||
"strings"
|
||||
"path/filepath"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"html/template"
|
||||
//"runtime/pprof"
|
||||
)
|
||||
|
@ -26,9 +21,6 @@ const kilobyte int = 1024
|
|||
const megabyte int = kilobyte * 1024
|
||||
const gigabyte int = megabyte * 1024
|
||||
const terabyte int = gigabyte * 1024
|
||||
//const thousand int = 1000
|
||||
//const million int = 1_000_000
|
||||
//const billion int = 1_000_000_000
|
||||
const saltLength int = 32
|
||||
const sessionLength int = 80
|
||||
var enable_websockets bool = false // Don't change this, the value is overwritten by an initialiser
|
||||
|
@ -59,8 +51,8 @@ func compile_templates() {
|
|||
NoticeList:[]string{"test"},
|
||||
Stylesheets:[]string{"panel"},
|
||||
Scripts:[]string{"whatever"},
|
||||
Sidebars:HeaderSidebars{
|
||||
Left: template.HTML("lalala"),
|
||||
Widgets:PageWidgets{
|
||||
LeftSidebar: template.HTML("lalala"),
|
||||
},
|
||||
}
|
||||
|
||||
|
@ -142,33 +134,6 @@ func init_templates() {
|
|||
template.Must(templates.ParseGlob("pages/*"))
|
||||
}
|
||||
|
||||
func init_static_files() {
|
||||
log.Print("Loading the static files.")
|
||||
err := filepath.Walk("./public", func(path string, f os.FileInfo, err error) error {
|
||||
if f.IsDir() {
|
||||
return nil
|
||||
}
|
||||
|
||||
path = strings.Replace(path,"\\","/",-1)
|
||||
data, err := ioutil.ReadFile(path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
path = strings.TrimPrefix(path,"public/")
|
||||
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)}
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func main(){
|
||||
//if profiling {
|
||||
// f, err := os.Create("startup_cpu.prof")
|
||||
|
@ -215,6 +180,9 @@ func main(){
|
|||
log.Fatal(err)
|
||||
}
|
||||
|
||||
log.Print("Initialising the authentication system")
|
||||
auth = NewDefaultAuth()
|
||||
|
||||
log.Print("Initialising the router")
|
||||
router := NewGenRouter(http.FileServer(http.Dir("./uploads")))
|
||||
///router.HandleFunc("/static/", route_static)
|
||||
|
|
15
mysql.go
15
mysql.go
|
@ -9,9 +9,6 @@ import "./query_gen/lib"
|
|||
|
||||
var get_activity_feed_by_watcher_stmt *sql.Stmt
|
||||
var get_activity_count_by_watcher_stmt *sql.Stmt
|
||||
var add_forum_perms_to_forum_staff_stmt *sql.Stmt
|
||||
var add_forum_perms_to_forum_members_stmt *sql.Stmt
|
||||
var update_forum_perms_for_group_stmt *sql.Stmt
|
||||
var todays_post_count_stmt *sql.Stmt
|
||||
var todays_topic_count_stmt *sql.Stmt
|
||||
var todays_report_count_stmt *sql.Stmt
|
||||
|
@ -65,18 +62,6 @@ func _init_database() (err error) {
|
|||
return err
|
||||
}
|
||||
|
||||
log.Print("Preparing add_forum_perms_to_forum_staff statement.")
|
||||
add_forum_perms_to_forum_staff_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 = 0 AND is_mod = 1")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
log.Print("Preparing add_forum_perms_to_forum_members statement.")
|
||||
add_forum_perms_to_forum_members_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 = 0 AND is_mod = 0 AND is_banned = 0")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
log.Print("Preparing todays_post_count statement.")
|
||||
todays_post_count_stmt, err = db.Prepare("select count(*) from replies where createdAt BETWEEN (now() - interval 1 day) and now()")
|
||||
if err != nil {
|
||||
|
|
8
pages.go
8
pages.go
|
@ -13,13 +13,13 @@ type HeaderVars struct
|
|||
NoticeList []string
|
||||
Scripts []string
|
||||
Stylesheets []string
|
||||
Sidebars HeaderSidebars
|
||||
Widgets PageWidgets
|
||||
}
|
||||
|
||||
type HeaderSidebars struct
|
||||
type PageWidgets struct
|
||||
{
|
||||
Left template.HTML
|
||||
Right template.HTML
|
||||
LeftSidebar template.HTML
|
||||
RightSidebar template.HTML
|
||||
}
|
||||
|
||||
type ExtData struct
|
||||
|
|
|
@ -1466,6 +1466,7 @@ func route_panel_themes_default(w http.ResponseWriter, r *http.Request, uname st
|
|||
}
|
||||
|
||||
var isDefault bool
|
||||
fmt.Println("uname",uname)
|
||||
err := is_theme_default_stmt.QueryRow(uname).Scan(&isDefault)
|
||||
if err != nil && err != sql.ErrNoRows {
|
||||
InternalError(err,w,r)
|
||||
|
@ -1474,6 +1475,7 @@ func route_panel_themes_default(w http.ResponseWriter, r *http.Request, uname st
|
|||
|
||||
has_theme := err != sql.ErrNoRows
|
||||
if has_theme {
|
||||
fmt.Println("isDefault",isDefault)
|
||||
if isDefault {
|
||||
LocalError("The theme is already active",w,r,user)
|
||||
return
|
||||
|
@ -1511,7 +1513,7 @@ func route_panel_themes_default(w http.ResponseWriter, r *http.Request, uname st
|
|||
|
||||
defaultTheme = uname
|
||||
reset_template_overrides()
|
||||
add_theme_static_files(uname)
|
||||
add_theme_static_files(themes[uname])
|
||||
map_theme_templates(theme)
|
||||
|
||||
http.Redirect(w,r,"/panel/themes/",http.StatusSeeOther)
|
||||
|
|
|
@ -64,6 +64,30 @@ func (build *builder) SimpleInsert(table string, columns string, fields string)
|
|||
return build.conn.Prepare(res)
|
||||
}
|
||||
|
||||
func (build *builder) SimpleInsertSelect(ins DB_Insert, sel DB_Select) (stmt *sql.Stmt, err error) {
|
||||
res, err := build.adapter.SimpleInsertSelect("_builder", ins, sel)
|
||||
if err != nil {
|
||||
return stmt, err
|
||||
}
|
||||
return build.conn.Prepare(res)
|
||||
}
|
||||
|
||||
func (build *builder) SimpleInsertLeftJoin(ins DB_Insert, sel DB_Join) (stmt *sql.Stmt, err error) {
|
||||
res, err := build.adapter.SimpleInsertLeftJoin("_builder", ins, sel)
|
||||
if err != nil {
|
||||
return stmt, err
|
||||
}
|
||||
return build.conn.Prepare(res)
|
||||
}
|
||||
|
||||
func (build *builder) SimpleInsertInnerJoin(ins DB_Insert, sel DB_Join) (stmt *sql.Stmt, err error) {
|
||||
res, err := build.adapter.SimpleInsertInnerJoin("_builder", ins, sel)
|
||||
if err != nil {
|
||||
return stmt, err
|
||||
}
|
||||
return build.conn.Prepare(res)
|
||||
}
|
||||
|
||||
func (build *builder) SimpleUpdate(table string, set string, where string) (stmt *sql.Stmt, err error) {
|
||||
res, err := build.adapter.SimpleUpdate("_builder", table, set, where)
|
||||
if err != nil {
|
||||
|
|
|
@ -452,6 +452,7 @@ func (adapter *Mysql_Adapter) SimpleInnerJoin(name string, table1 string, table2
|
|||
|
||||
func (adapter *Mysql_Adapter) SimpleInsertSelect(name string, ins DB_Insert, sel DB_Select) (string, error) {
|
||||
/* Insert Portion */
|
||||
|
||||
var querystr string = "INSERT INTO `" + ins.Table + "`("
|
||||
|
||||
// Escape the column names, just in case we've used a reserved keyword
|
||||
|
@ -523,8 +524,94 @@ func (adapter *Mysql_Adapter) SimpleInsertSelect(name string, ins DB_Insert, sel
|
|||
return querystr, nil
|
||||
}
|
||||
|
||||
func (adapter *Mysql_Adapter) SimpleInsertLeftJoin(name string, ins DB_Insert, sel DB_Join) (string, error) {
|
||||
/* Insert Portion */
|
||||
|
||||
var querystr string = "INSERT INTO `" + ins.Table + "`("
|
||||
|
||||
// Escape the column names, just in case we've used a reserved keyword
|
||||
for _, column := range _process_columns(ins.Columns) {
|
||||
if column.Type == "function" {
|
||||
querystr += column.Left + ","
|
||||
} else {
|
||||
querystr += "`" + column.Left + "`,"
|
||||
}
|
||||
}
|
||||
querystr = querystr[0:len(querystr) - 1] + ") SELECT"
|
||||
|
||||
/* Select Portion */
|
||||
|
||||
for _, column := range _process_columns(sel.Columns) {
|
||||
var source, alias string
|
||||
|
||||
// Escape the column names, just in case we've used a reserved keyword
|
||||
if column.Table != "" {
|
||||
source = "`" + column.Table + "`.`" + column.Left + "`"
|
||||
} else if column.Type == "function" {
|
||||
source = column.Left
|
||||
} else {
|
||||
source = "`" + column.Left + "`"
|
||||
}
|
||||
|
||||
if column.Alias != "" {
|
||||
alias = " AS `" + column.Alias + "`"
|
||||
}
|
||||
querystr += " " + source + alias + ","
|
||||
}
|
||||
querystr = querystr[0:len(querystr) - 1]
|
||||
|
||||
querystr += " FROM `" + sel.Table1 + "` LEFT JOIN `" + sel.Table2 + "` ON "
|
||||
for _, joiner := range _process_joiner(sel.Joiners) {
|
||||
querystr += "`" + joiner.LeftTable + "`.`" + joiner.LeftColumn + "` " + joiner.Operator + " `" + joiner.RightTable + "`.`" + joiner.RightColumn + "` AND "
|
||||
}
|
||||
querystr = querystr[0:len(querystr) - 4]
|
||||
|
||||
// Add support for BETWEEN x.x
|
||||
if len(sel.Where) != 0 {
|
||||
querystr += " WHERE"
|
||||
for _, loc := range _process_where(sel.Where) {
|
||||
for _, token := range loc.Expr {
|
||||
switch(token.Type) {
|
||||
case "function","operator","number","substitute":
|
||||
querystr += " " + token.Contents + ""
|
||||
case "column":
|
||||
halves := strings.Split(token.Contents,".")
|
||||
if len(halves) == 2 {
|
||||
querystr += " `" + halves[0] + "`.`" + halves[1] + "`"
|
||||
} else {
|
||||
querystr += " `" + token.Contents + "`"
|
||||
}
|
||||
case "string":
|
||||
querystr += " '" + token.Contents + "'"
|
||||
default:
|
||||
panic("This token doesn't exist o_o")
|
||||
}
|
||||
}
|
||||
querystr += " AND"
|
||||
}
|
||||
querystr = querystr[0:len(querystr) - 4]
|
||||
}
|
||||
|
||||
if len(sel.Orderby) != 0 {
|
||||
querystr += " ORDER BY "
|
||||
for _, column := range _process_orderby(sel.Orderby) {
|
||||
querystr += column.Column + " " + strings.ToUpper(column.Order) + ","
|
||||
}
|
||||
querystr = querystr[0:len(querystr) - 1]
|
||||
}
|
||||
|
||||
if sel.Limit != "" {
|
||||
querystr += " LIMIT " + sel.Limit
|
||||
}
|
||||
|
||||
querystr = strings.TrimSpace(querystr)
|
||||
adapter.push_statement(name,querystr)
|
||||
return querystr, nil
|
||||
}
|
||||
|
||||
func (adapter *Mysql_Adapter) SimpleInsertInnerJoin(name string, ins DB_Insert, sel DB_Join) (string, error) {
|
||||
/* Insert Portion */
|
||||
|
||||
var querystr string = "INSERT INTO `" + ins.Table + "`("
|
||||
|
||||
// Escape the column names, just in case we've used a reserved keyword
|
||||
|
|
|
@ -33,24 +33,6 @@ type DB_Insert struct
|
|||
Fields string
|
||||
}
|
||||
|
||||
/*type DB_Select struct
|
||||
{
|
||||
Name string
|
||||
Table string
|
||||
Columns []DB_Column
|
||||
Where []DB_Where
|
||||
Orderby []DB_Order
|
||||
Limit DB_Limit
|
||||
}
|
||||
|
||||
type DB_Insert struct
|
||||
{
|
||||
Name string
|
||||
Table string
|
||||
Columns []DB_Column
|
||||
Fields []DB_Field
|
||||
}*/
|
||||
|
||||
type DB_Column struct
|
||||
{
|
||||
Table string
|
||||
|
@ -111,6 +93,7 @@ type DB_Adapter interface {
|
|||
SimpleLeftJoin(string,string,string,string,string,string,string,string) (string, error)
|
||||
SimpleInnerJoin(string,string,string,string,string,string,string,string) (string, error)
|
||||
SimpleInsertSelect(string,DB_Insert,DB_Select) (string,error)
|
||||
SimpleInsertLeftJoin(string,DB_Insert,DB_Join) (string,error)
|
||||
SimpleInsertInnerJoin(string,DB_Insert,DB_Join) (string,error)
|
||||
SimpleCount(string,string,string,string) (string, error)
|
||||
Write() error
|
||||
|
|
|
@ -81,12 +81,8 @@ func write_selects(adapter qgen.DB_Adapter) error {
|
|||
adapter.SimpleSelect("get_reply","replies","tid, content, createdBy, createdAt, lastEdit, lastEditBy, ipaddress, likeCount","rid = ?","","")
|
||||
|
||||
adapter.SimpleSelect("get_user_reply","users_replies","uid, content, createdBy, createdAt, lastEdit, lastEditBy, ipaddress","rid = ?","","")
|
||||
|
||||
adapter.SimpleSelect("login","users","uid, name, password, salt","name = ?","","")
|
||||
|
||||
adapter.SimpleSelect("get_password","users","password,salt","uid = ?","","")
|
||||
|
||||
adapter.SimpleSelect("username_exists","users","name","name = ?","","")
|
||||
adapter.SimpleSelect("get_password","users","password,salt","uid = ?","","")
|
||||
|
||||
|
||||
adapter.SimpleSelect("get_settings","settings","name, content, type","","","")
|
||||
|
@ -189,10 +185,6 @@ func write_inserts(adapter qgen.DB_Adapter) error {
|
|||
|
||||
adapter.SimpleInsert("notify_one","activity_stream_matches","watcher,asid","?,?")
|
||||
|
||||
// Add an admin version of register_stmt with more flexibility?
|
||||
// create_account_stmt, err = db.Prepare("INSERT INTO
|
||||
adapter.SimpleInsert("register","users","name, email, password, salt, group, is_super_admin, session, active, message","?,?,?,?,?,0,?,?,''")
|
||||
|
||||
adapter.SimpleInsert("add_email","emails","email, uid, validated, token","?,?,?,?")
|
||||
|
||||
adapter.SimpleInsert("create_profile_reply","users_replies","uid, content, parsed_content, createdAt, createdBy, ipaddress","?,?,?,NOW(),?,?")
|
||||
|
@ -249,8 +241,6 @@ func write_updates(adapter qgen.DB_Adapter) error {
|
|||
adapter.SimpleUpdate("update_last_ip","users","last_ip = ?","uid = ?")
|
||||
|
||||
adapter.SimpleUpdate("update_session","users","session = ?","uid = ?")
|
||||
|
||||
adapter.SimpleUpdate("logout","users","session = ''","uid = ?")
|
||||
|
||||
adapter.SimpleUpdate("set_password","users","password = ?, salt = ?","uid = ?")
|
||||
|
||||
|
@ -327,6 +317,16 @@ func write_insert_selects(adapter qgen.DB_Adapter) error {
|
|||
qgen.DB_Select{"users_groups","gid, ? AS fid, ? AS preset, ? AS permissions","is_admin = 1","",""},
|
||||
)
|
||||
|
||||
adapter.SimpleInsertSelect("add_forum_perms_to_forum_staff",
|
||||
qgen.DB_Insert{"forums_permissions","gid,fid,preset,permissions",""},
|
||||
qgen.DB_Select{"users_groups","gid, ? AS fid, ? AS preset, ? AS permissions","is_admin = 0 AND is_mod = 1","",""},
|
||||
)
|
||||
|
||||
adapter.SimpleInsertSelect("add_forum_perms_to_forum_members",
|
||||
qgen.DB_Insert{"forums_permissions","gid,fid,preset,permissions",""},
|
||||
qgen.DB_Select{"users_groups","gid, ? AS fid, ? AS preset, ? AS permissions","is_admin = 0 AND is_mod = 0 AND is_banned = 0","",""},
|
||||
)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
162
routes.go
162
routes.go
|
@ -190,7 +190,13 @@ func route_topics(w http.ResponseWriter, r *http.Request){
|
|||
|
||||
func route_forum(w http.ResponseWriter, r *http.Request, sfid string){
|
||||
page, _ := strconv.Atoi(r.FormValue("page"))
|
||||
fid, err := strconv.Atoi(sfid)
|
||||
|
||||
// SEO URLs...
|
||||
halves := strings.Split(sfid,".")
|
||||
if len(halves) < 2 {
|
||||
halves = append(halves,halves[0])
|
||||
}
|
||||
fid, err := strconv.Atoi(halves[1])
|
||||
if err != nil {
|
||||
PreError("The provided ForumID is not a valid number.",w,r)
|
||||
return
|
||||
|
@ -1238,18 +1244,7 @@ func route_account_own_edit_critical_submit(w http.ResponseWriter, r *http.Reque
|
|||
SetPassword(user.ID, new_password)
|
||||
|
||||
// Log the user out as a safety precaution
|
||||
_, err = logout_stmt.Exec(user.ID)
|
||||
if err != nil {
|
||||
InternalError(err,w,r)
|
||||
return
|
||||
}
|
||||
|
||||
// Reload the user data
|
||||
err = users.Load(user.ID)
|
||||
if err != nil {
|
||||
LocalError("Your account no longer exists!",w,r,user)
|
||||
return
|
||||
}
|
||||
auth.ForceLogout(user.ID)
|
||||
|
||||
headerVars.NoticeList = append(headerVars.NoticeList,"Your password was successfully updated")
|
||||
pi := Page{"Edit Password",user,headerVars,tList,nil}
|
||||
|
@ -1542,18 +1537,7 @@ func route_logout(w http.ResponseWriter, r *http.Request) {
|
|||
LocalError("You can't logout without logging in first.",w,r,user)
|
||||
return
|
||||
}
|
||||
|
||||
_, err := logout_stmt.Exec(user.ID)
|
||||
if err != nil {
|
||||
InternalError(err,w,r)
|
||||
return
|
||||
}
|
||||
|
||||
err = users.Load(user.ID)
|
||||
if err != nil {
|
||||
LocalError("Your account doesn't exist!",w,r,user)
|
||||
return
|
||||
}
|
||||
auth.Logout(w, user.ID)
|
||||
http.Redirect(w,r, "/", http.StatusSeeOther)
|
||||
}
|
||||
|
||||
|
@ -1585,79 +1569,24 @@ func route_login_submit(w http.ResponseWriter, r *http.Request) {
|
|||
return
|
||||
}
|
||||
|
||||
var uid int
|
||||
var real_password, salt, session string
|
||||
username := html.EscapeString(r.PostFormValue("username"))
|
||||
password := r.PostFormValue("password")
|
||||
|
||||
err = login_stmt.QueryRow(username).Scan(&uid, &username, &real_password, &salt)
|
||||
if err == sql.ErrNoRows {
|
||||
LocalError("That username doesn't exist.",w,r,user)
|
||||
return
|
||||
} else if err != nil {
|
||||
InternalError(err,w,r)
|
||||
uid, err := auth.Authenticate(html.EscapeString(r.PostFormValue("username")), r.PostFormValue("password"))
|
||||
if err != nil {
|
||||
LocalError(err.Error(),w,r,user)
|
||||
return
|
||||
}
|
||||
|
||||
// Admin password reset mechanism...
|
||||
if salt == "" {
|
||||
if password != real_password {
|
||||
LocalError("That's not the correct password.",w,r,user)
|
||||
return
|
||||
}
|
||||
|
||||
// Re-encrypt the password
|
||||
SetPassword(uid, real_password)
|
||||
|
||||
// Fe-fetch the user data...
|
||||
err = login_stmt.QueryRow(username).Scan(&uid, &username, &real_password, &salt)
|
||||
if err == sql.ErrNoRows {
|
||||
LocalError("That username doesn't exist anymore.",w,r,user)
|
||||
return
|
||||
} else if err != nil {
|
||||
var session string
|
||||
if user.Session == "" {
|
||||
session, err = auth.CreateSession(uid)
|
||||
if err != nil {
|
||||
InternalError(err,w,r)
|
||||
return
|
||||
}
|
||||
} else {
|
||||
session = user.Session
|
||||
}
|
||||
|
||||
password = password + salt
|
||||
if err != nil {
|
||||
InternalError(err,w,r)
|
||||
return
|
||||
}
|
||||
|
||||
err = bcrypt.CompareHashAndPassword([]byte(real_password), []byte(password))
|
||||
if err == bcrypt.ErrMismatchedHashAndPassword {
|
||||
LocalError("That's not the correct password.",w,r,user)
|
||||
return
|
||||
} else if err != nil {
|
||||
InternalError(err,w,r)
|
||||
return
|
||||
}
|
||||
|
||||
session, err = GenerateSafeString(sessionLength)
|
||||
if err != nil {
|
||||
InternalError(err,w,r)
|
||||
return
|
||||
}
|
||||
|
||||
_, err = update_session_stmt.Exec(session, uid)
|
||||
if err != nil {
|
||||
InternalError(err,w,r)
|
||||
return
|
||||
}
|
||||
|
||||
// Reload the user data
|
||||
err = users.Load(uid)
|
||||
if err != nil {
|
||||
LocalError("Your account no longer exists!",w,r,user)
|
||||
return
|
||||
}
|
||||
|
||||
cookie := http.Cookie{Name:"uid",Value:strconv.Itoa(uid),Path:"/",MaxAge:year}
|
||||
http.SetCookie(w,&cookie)
|
||||
cookie = http.Cookie{Name:"session",Value:session,Path:"/",MaxAge:year}
|
||||
http.SetCookie(w,&cookie)
|
||||
auth.SetCookies(w,uid,session)
|
||||
http.Redirect(w,r,"/",http.StatusSeeOther)
|
||||
}
|
||||
|
||||
|
@ -1726,34 +1655,6 @@ func route_register_submit(w http.ResponseWriter, r *http.Request) {
|
|||
return
|
||||
}
|
||||
|
||||
// Is this username already taken..?
|
||||
err = username_exists_stmt.QueryRow(username).Scan(&username)
|
||||
if err != nil && err != sql.ErrNoRows {
|
||||
InternalError(err,w,r)
|
||||
return
|
||||
} else if err != sql.ErrNoRows {
|
||||
LocalError("This username isn't available. Try another.",w,r,user)
|
||||
return
|
||||
}
|
||||
|
||||
salt, err := GenerateSafeString(saltLength)
|
||||
if err != nil {
|
||||
InternalError(err,w,r)
|
||||
return
|
||||
}
|
||||
session, err := GenerateSafeString(sessionLength)
|
||||
if err != nil {
|
||||
InternalError(err,w,r)
|
||||
return
|
||||
}
|
||||
|
||||
password = password + salt
|
||||
hashed_password, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost)
|
||||
if err != nil {
|
||||
InternalError(err,w,r)
|
||||
return
|
||||
}
|
||||
|
||||
var active, group int
|
||||
switch settings["activation_type"] {
|
||||
case 1: // Activate All
|
||||
|
@ -1763,13 +1664,11 @@ func route_register_submit(w http.ResponseWriter, r *http.Request) {
|
|||
group = activation_group
|
||||
}
|
||||
|
||||
res, err := register_stmt.Exec(username,email,string(hashed_password),salt,group,session,active)
|
||||
if err != nil {
|
||||
InternalError(err,w,r)
|
||||
uid, err := users.CreateUser(username, password, email, group, active)
|
||||
if err == err_account_exists {
|
||||
LocalError("This username isn't available. Try another.",w,r,user)
|
||||
return
|
||||
}
|
||||
lastId, err := res.LastInsertId()
|
||||
if err != nil {
|
||||
} else if err != nil {
|
||||
InternalError(err,w,r)
|
||||
return
|
||||
}
|
||||
|
@ -1781,7 +1680,7 @@ func route_register_submit(w http.ResponseWriter, r *http.Request) {
|
|||
InternalError(err,w,r)
|
||||
return
|
||||
}
|
||||
_, err = add_email_stmt.Exec(email, lastId, 0, token)
|
||||
_, err = add_email_stmt.Exec(email, uid, 0, token)
|
||||
if err != nil {
|
||||
InternalError(err,w,r)
|
||||
return
|
||||
|
@ -1793,11 +1692,14 @@ func route_register_submit(w http.ResponseWriter, r *http.Request) {
|
|||
}
|
||||
}
|
||||
|
||||
cookie := http.Cookie{Name: "uid",Value: strconv.FormatInt(lastId, 10),Path: "/",MaxAge: year}
|
||||
http.SetCookie(w,&cookie)
|
||||
cookie = http.Cookie{Name: "session",Value: session,Path: "/",MaxAge: year}
|
||||
http.SetCookie(w,&cookie)
|
||||
http.Redirect(w,r, "/", http.StatusSeeOther)
|
||||
session, err := auth.CreateSession(uid)
|
||||
if err != nil {
|
||||
InternalError(err,w,r)
|
||||
return
|
||||
}
|
||||
|
||||
auth.SetCookies(w,uid,session)
|
||||
http.Redirect(w,r,"/",http.StatusSeeOther)
|
||||
}
|
||||
|
||||
var phrase_login_alerts []byte = []byte(`{"msgs":[{"msg":"Login to see your alerts","path":"/accounts/login"}]}`)
|
||||
|
|
|
@ -51,7 +51,7 @@ w.Write(menu_6)
|
|||
}
|
||||
w.Write(menu_7)
|
||||
w.Write(header_9)
|
||||
if tmpl_forum_vars.Header.Sidebars.Right != "" {
|
||||
if tmpl_forum_vars.Header.Widgets.RightSidebar != "" {
|
||||
w.Write(header_10)
|
||||
}
|
||||
w.Write(header_11)
|
||||
|
@ -137,9 +137,9 @@ w.Write(forum_31)
|
|||
}
|
||||
w.Write(forum_32)
|
||||
w.Write(footer_0)
|
||||
if tmpl_forum_vars.Header.Sidebars.Right != "" {
|
||||
if tmpl_forum_vars.Header.Widgets.RightSidebar != "" {
|
||||
w.Write(footer_1)
|
||||
w.Write([]byte(string(tmpl_forum_vars.Header.Sidebars.Right)))
|
||||
w.Write([]byte(string(tmpl_forum_vars.Header.Widgets.RightSidebar)))
|
||||
w.Write(footer_2)
|
||||
}
|
||||
w.Write(footer_3)
|
||||
|
|
|
@ -51,7 +51,7 @@ w.Write(menu_6)
|
|||
}
|
||||
w.Write(menu_7)
|
||||
w.Write(header_9)
|
||||
if tmpl_forums_vars.Header.Sidebars.Right != "" {
|
||||
if tmpl_forums_vars.Header.Widgets.RightSidebar != "" {
|
||||
w.Write(header_10)
|
||||
}
|
||||
w.Write(header_11)
|
||||
|
@ -110,9 +110,9 @@ w.Write(forums_20)
|
|||
}
|
||||
w.Write(forums_21)
|
||||
w.Write(footer_0)
|
||||
if tmpl_forums_vars.Header.Sidebars.Right != "" {
|
||||
if tmpl_forums_vars.Header.Widgets.RightSidebar != "" {
|
||||
w.Write(footer_1)
|
||||
w.Write([]byte(string(tmpl_forums_vars.Header.Sidebars.Right)))
|
||||
w.Write([]byte(string(tmpl_forums_vars.Header.Widgets.RightSidebar)))
|
||||
w.Write(footer_2)
|
||||
}
|
||||
w.Write(footer_3)
|
||||
|
|
|
@ -51,7 +51,7 @@ w.Write(menu_6)
|
|||
}
|
||||
w.Write(menu_7)
|
||||
w.Write(header_9)
|
||||
if tmpl_profile_vars.Header.Sidebars.Right != "" {
|
||||
if tmpl_profile_vars.Header.Widgets.RightSidebar != "" {
|
||||
w.Write(header_10)
|
||||
}
|
||||
w.Write(header_11)
|
||||
|
@ -143,9 +143,9 @@ w.Write(profile_37)
|
|||
}
|
||||
w.Write(profile_38)
|
||||
w.Write(footer_0)
|
||||
if tmpl_profile_vars.Header.Sidebars.Right != "" {
|
||||
if tmpl_profile_vars.Header.Widgets.RightSidebar != "" {
|
||||
w.Write(footer_1)
|
||||
w.Write([]byte(string(tmpl_profile_vars.Header.Sidebars.Right)))
|
||||
w.Write([]byte(string(tmpl_profile_vars.Header.Widgets.RightSidebar)))
|
||||
w.Write(footer_2)
|
||||
}
|
||||
w.Write(footer_3)
|
||||
|
|
|
@ -51,7 +51,7 @@ w.Write(menu_6)
|
|||
}
|
||||
w.Write(menu_7)
|
||||
w.Write(header_9)
|
||||
if tmpl_topic_vars.Header.Sidebars.Right != "" {
|
||||
if tmpl_topic_vars.Header.Widgets.RightSidebar != "" {
|
||||
w.Write(header_10)
|
||||
}
|
||||
w.Write(header_11)
|
||||
|
@ -251,9 +251,9 @@ w.Write([]byte(strconv.Itoa(tmpl_topic_vars.Topic.ID)))
|
|||
w.Write(topic_83)
|
||||
}
|
||||
w.Write(footer_0)
|
||||
if tmpl_topic_vars.Header.Sidebars.Right != "" {
|
||||
if tmpl_topic_vars.Header.Widgets.RightSidebar != "" {
|
||||
w.Write(footer_1)
|
||||
w.Write([]byte(string(tmpl_topic_vars.Header.Sidebars.Right)))
|
||||
w.Write([]byte(string(tmpl_topic_vars.Header.Widgets.RightSidebar)))
|
||||
w.Write(footer_2)
|
||||
}
|
||||
w.Write(footer_3)
|
||||
|
|
|
@ -51,7 +51,7 @@ w.Write(menu_6)
|
|||
}
|
||||
w.Write(menu_7)
|
||||
w.Write(header_9)
|
||||
if tmpl_topic_alt_vars.Header.Sidebars.Right != "" {
|
||||
if tmpl_topic_alt_vars.Header.Widgets.RightSidebar != "" {
|
||||
w.Write(header_10)
|
||||
}
|
||||
w.Write(header_11)
|
||||
|
@ -257,9 +257,9 @@ w.Write([]byte(strconv.Itoa(tmpl_topic_alt_vars.Topic.ID)))
|
|||
w.Write(topic_alt_87)
|
||||
}
|
||||
w.Write(footer_0)
|
||||
if tmpl_topic_alt_vars.Header.Sidebars.Right != "" {
|
||||
if tmpl_topic_alt_vars.Header.Widgets.RightSidebar != "" {
|
||||
w.Write(footer_1)
|
||||
w.Write([]byte(string(tmpl_topic_alt_vars.Header.Sidebars.Right)))
|
||||
w.Write([]byte(string(tmpl_topic_alt_vars.Header.Widgets.RightSidebar)))
|
||||
w.Write(footer_2)
|
||||
}
|
||||
w.Write(footer_3)
|
||||
|
|
|
@ -51,7 +51,7 @@ w.Write(menu_6)
|
|||
}
|
||||
w.Write(menu_7)
|
||||
w.Write(header_9)
|
||||
if tmpl_topics_vars.Header.Sidebars.Right != "" {
|
||||
if tmpl_topics_vars.Header.Widgets.RightSidebar != "" {
|
||||
w.Write(header_10)
|
||||
}
|
||||
w.Write(header_11)
|
||||
|
@ -113,9 +113,9 @@ w.Write(topics_21)
|
|||
}
|
||||
w.Write(topics_22)
|
||||
w.Write(footer_0)
|
||||
if tmpl_topics_vars.Header.Sidebars.Right != "" {
|
||||
if tmpl_topics_vars.Header.Widgets.RightSidebar != "" {
|
||||
w.Write(footer_1)
|
||||
w.Write([]byte(string(tmpl_topics_vars.Header.Sidebars.Right)))
|
||||
w.Write([]byte(string(tmpl_topics_vars.Header.Widgets.RightSidebar)))
|
||||
w.Write(footer_2)
|
||||
}
|
||||
w.Write(footer_3)
|
||||
|
|
|
@ -887,6 +887,9 @@ func (c *CTemplateSet) compile_varsub(varname string, val reflect.Value) string
|
|||
case reflect.Int64:
|
||||
return "w.Write([]byte(strconv.FormatInt(" + varname + ", 10)))"
|
||||
default:
|
||||
if !val.IsValid() {
|
||||
panic(varname + "^\n" + "Invalid value. Maybe, it doesn't exist?")
|
||||
}
|
||||
fmt.Println("Unknown Variable Name:",varname)
|
||||
fmt.Println("Unknown Kind:",val.Kind())
|
||||
fmt.Println("Unknown Type:",val.Type().Name())
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
</div>
|
||||
{{if .Header.Sidebars.Right}}<div class="sidebar">{{.Header.Sidebars.Right}}</div>{{end}}
|
||||
{{if .Header.Widgets.RightSidebar}}<div class="sidebar">{{.Header.Widgets.RightSidebar}}</div>{{end}}
|
||||
<div style="clear: both;"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -18,5 +18,5 @@
|
|||
<body>
|
||||
<div class="container">
|
||||
{{template "menu.html" .}}
|
||||
<div id="back"><div id="main" {{if .Header.Sidebars.Right}}class="shrink_main"{{end}}>
|
||||
<div id="back"><div id="main" {{if .Header.Widgets.RightSidebar}}class="shrink_main"{{end}}>
|
||||
{{range .Header.NoticeList}}<div class="alert">{{.}}</div>{{end}}
|
||||
|
|
|
@ -1,5 +1,15 @@
|
|||
{{template "header.html" . }}
|
||||
{{template "panel-menu.html" . }}
|
||||
<div class="colstack_left">
|
||||
<div class="colstack_item colstack_head">
|
||||
<div class="rowitem rowhead"><a href="/panel/themes/">Theme Manager</a></div>
|
||||
</div>
|
||||
<div class="colstack_item">
|
||||
<div class="rowitem passive"><a href="#">Widgets</a></div>
|
||||
</div>
|
||||
{{template "panel-inner-menu.html" . }}
|
||||
</div>
|
||||
|
||||
<!-- Stop inlining this x.x -->
|
||||
<style type="text/css">
|
||||
.rowitem::after {
|
||||
content: "";
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<div class="rowblock">
|
||||
<div class="rowitem">{{.Name}}</div>
|
||||
<div class="rowitem rowhead">{{.Name}}</div>
|
||||
</div>
|
||||
<div class="rowblock">
|
||||
<div class="rowitem">{{.Text}}</div>
|
||||
|
|
46
themes.go
46
themes.go
|
@ -6,12 +6,14 @@ import (
|
|||
"log"
|
||||
"io"
|
||||
"os"
|
||||
"bytes"
|
||||
"strings"
|
||||
"mime"
|
||||
"io/ioutil"
|
||||
"path/filepath"
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
"text/template"
|
||||
)
|
||||
|
||||
var defaultTheme string
|
||||
|
@ -37,6 +39,7 @@ type Theme struct
|
|||
Templates []TemplateMapping
|
||||
TemplatesMap map[string]string
|
||||
Resources []ThemeResource
|
||||
ResourceTemplates *template.Template
|
||||
|
||||
// This variable should only be set and unset by the system, not the theme meta file
|
||||
Active bool
|
||||
|
@ -89,11 +92,14 @@ func LoadThemes() error {
|
|||
}
|
||||
}
|
||||
|
||||
theme.ResourceTemplates = template.New("")
|
||||
template.Must(theme.ResourceTemplates.ParseGlob("./themes/" + uname + "/public/*.css"))
|
||||
|
||||
if defaultThemeSwitch {
|
||||
log.Print("Loading the theme '" + theme.Name + "'")
|
||||
theme.Active = true
|
||||
defaultTheme = uname
|
||||
add_theme_static_files(uname)
|
||||
add_theme_static_files(theme)
|
||||
map_theme_templates(theme)
|
||||
} else {
|
||||
theme.Active = false
|
||||
|
@ -147,32 +153,46 @@ func init_themes() {
|
|||
}
|
||||
}
|
||||
|
||||
func add_theme_static_files(themeName string) {
|
||||
err := filepath.Walk("./themes/" + themeName + "/public", func(path string, f os.FileInfo, err error) error {
|
||||
func add_theme_static_files(theme Theme) {
|
||||
err := filepath.Walk("./themes/" + theme.Name + "/public", func(path string, f os.FileInfo, err error) error {
|
||||
if debug {
|
||||
log.Print("Attempting to add static file '" + path + "' for default theme '" + theme.Name + "'")
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if f.IsDir() {
|
||||
return nil
|
||||
}
|
||||
|
||||
path = strings.Replace(path,"\\","/",-1)
|
||||
|
||||
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")
|
||||
if debug {
|
||||
log.Print("Added the '" + path + "' static file for default theme " + themeName + ".")
|
||||
var ext string = filepath.Ext(path)
|
||||
//log.Print("path ",path)
|
||||
//log.Print("ext ",ext)
|
||||
if ext == ".css" {
|
||||
var b bytes.Buffer
|
||||
var pieces []string = strings.Split(path,"/")
|
||||
var filename string = pieces[len(pieces) - 1]
|
||||
//log.Print("filename ", filename)
|
||||
err = theme.ResourceTemplates.ExecuteTemplate(&b,filename, CssData{ComingSoon:"We don't have any data to pass you yet!"})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
data = b.Bytes()
|
||||
}
|
||||
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)}
|
||||
path = strings.TrimPrefix(path,"themes/" + theme.Name + "/public")
|
||||
gzip_data := compress_bytes_gzip(data)
|
||||
static_files["/static" + path] = SFile{data,gzip_data,0,int64(len(data)),int64(len(gzip_data)),mime.TypeByExtension(ext),f,f.ModTime().UTC().Format(http.TimeFormat)}
|
||||
|
||||
if debug {
|
||||
log.Print("Added the '" + path + "' static file for default theme " + theme.Name + ".")
|
||||
}
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
/* Sample CSS file. Doesn't do anything. */
|
|
@ -527,132 +527,4 @@ button.username {
|
|||
top: -2px;
|
||||
}
|
||||
|
||||
/* The Media Queries */
|
||||
@media (max-width: 880px) {
|
||||
li {
|
||||
height: 29px;
|
||||
font-size: 15px;
|
||||
padding-left: 9px;
|
||||
padding-top: 6px;
|
||||
padding-bottom: 6px;
|
||||
}
|
||||
ul {
|
||||
height: 30px;
|
||||
margin-top: 8px;
|
||||
}
|
||||
.menu_left, .menu_right { padding-right: 9px; }
|
||||
.menu_alerts {
|
||||
padding-left: 7px;
|
||||
padding-right: 7px;
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
body {
|
||||
padding-left: 4px;
|
||||
padding-right: 4px;
|
||||
margin: 0px !important;
|
||||
width: 100% !important;
|
||||
height: 100% !important;
|
||||
overflow-x: hidden;
|
||||
}
|
||||
.container { width: auto; }
|
||||
.selectedAlert .alertList { top: 37px; right: 4px; }
|
||||
}
|
||||
|
||||
@media (max-width: 680px) {
|
||||
li {
|
||||
padding-left: 5px;
|
||||
padding-top: 3px;
|
||||
padding-bottom: 2px;
|
||||
height: 25px;
|
||||
}
|
||||
li a { font-size: 14px; }
|
||||
ul { height: 26px; }
|
||||
.menu_left, .menu_right { padding-right: 7px; }
|
||||
.menu_create_topic, .hide_on_mobile { display: none; }
|
||||
|
||||
.menu_alerts {
|
||||
padding-left: 4px;
|
||||
padding-right: 4px;
|
||||
font-size: 16px;
|
||||
padding-top: 1px;
|
||||
}
|
||||
.selectedAlert .alertList { top: 33px; }
|
||||
|
||||
.prev_button, .next_button { top: auto;bottom: 5px; }
|
||||
.colstack_grid { grid-template-columns: none; grid-gap: 8px; }
|
||||
.grid_istat { margin-bottom: 0px; }
|
||||
}
|
||||
|
||||
@media (max-width: 470px) {
|
||||
ul { margin-bottom: 10px; }
|
||||
.menu_overview, .menu_profile, .hide_on_micro { display: none; }
|
||||
.selectedAlert .alertList {
|
||||
width: 156px;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
.alertItem.withAvatar {
|
||||
background-size: 36px;
|
||||
text-align: center;
|
||||
padding-left: 42px;
|
||||
height: 46px;
|
||||
}
|
||||
.alertItem { padding: 8px; }
|
||||
.alertItem .text {
|
||||
height: 30px;
|
||||
font-size: 10px;
|
||||
font-weight: bold;
|
||||
white-space: normal;
|
||||
}
|
||||
.post_container { overflow: visible !important; }
|
||||
|
||||
.post_item {
|
||||
background-position: 0px 2px !important;
|
||||
background-size: 64px 64px !important;
|
||||
padding-left: 2px !important;
|
||||
min-height: 96px;
|
||||
position: relative !important;
|
||||
}
|
||||
.post_item > .user_content {
|
||||
margin-left: 75px !important;
|
||||
width: 100% !important;
|
||||
}
|
||||
.post_item > .mod_button {
|
||||
float: right !important;
|
||||
margin-left: 2px !important;
|
||||
position: relative;
|
||||
top: -14px;
|
||||
}
|
||||
.post_item > .real_username {
|
||||
position: absolute;
|
||||
top: 70px;
|
||||
float: left;
|
||||
margin-top: 0px;
|
||||
padding-top: 3px !important;
|
||||
margin-right: 2px;
|
||||
width: 60px;
|
||||
font-size: 15px;
|
||||
}
|
||||
.userinfo { width: 70px; }
|
||||
.userinfo .avatar_item {
|
||||
background-size: 64px;
|
||||
width: 64px;
|
||||
min-height: 64px;
|
||||
}
|
||||
.content_container {
|
||||
margin-left: 73px;
|
||||
min-height: inherit !important;
|
||||
}
|
||||
.user_content { min-height: 76.5px !important; font-size: 15px; }
|
||||
.user_content.nobuttons { min-height: 100.5px !important; }
|
||||
.the_name { font-size: 15px; }
|
||||
.post_tag { font-size: 12px; }
|
||||
.rowtopic { font-size: 14px; }
|
||||
|
||||
.container { width: 100% !important; }
|
||||
}
|
||||
|
||||
@media (max-width: 330px) {
|
||||
li { padding-left: 6px; }
|
||||
.menu_left { padding-right: 6px; }
|
||||
}
|
||||
{{template "media.partial.css" }}
|
||||
|
|
|
@ -0,0 +1,143 @@
|
|||
/* The Media Queries */
|
||||
|
||||
@media(min-width: 881px) {
|
||||
.shrink_main {
|
||||
float: left;
|
||||
width: calc(75% - 12px);
|
||||
}
|
||||
.sidebar {
|
||||
float: left;
|
||||
width: 25%;
|
||||
margin-left: 12px;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 880px) {
|
||||
li {
|
||||
height: 29px;
|
||||
font-size: 15px;
|
||||
padding-left: 9px;
|
||||
padding-top: 6px;
|
||||
padding-bottom: 6px;
|
||||
}
|
||||
ul {
|
||||
height: 30px;
|
||||
margin-top: 8px;
|
||||
}
|
||||
.menu_left, .menu_right { padding-right: 9px; }
|
||||
.menu_alerts {
|
||||
padding-left: 7px;
|
||||
padding-right: 7px;
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
body {
|
||||
padding-left: 4px;
|
||||
padding-right: 4px;
|
||||
margin: 0px !important;
|
||||
width: 100% !important;
|
||||
height: 100% !important;
|
||||
overflow-x: hidden;
|
||||
}
|
||||
.container { width: auto; }
|
||||
.sidebar { display: none; }
|
||||
.selectedAlert .alertList { top: 37px; right: 4px; }
|
||||
}
|
||||
|
||||
@media (max-width: 680px) {
|
||||
li {
|
||||
padding-left: 5px;
|
||||
padding-top: 3px;
|
||||
padding-bottom: 2px;
|
||||
height: 25px;
|
||||
}
|
||||
li a { font-size: 14px; }
|
||||
ul { height: 26px; }
|
||||
.menu_left, .menu_right { padding-right: 7px; }
|
||||
.menu_create_topic, .hide_on_mobile { display: none; }
|
||||
|
||||
.menu_alerts {
|
||||
padding-left: 4px;
|
||||
padding-right: 4px;
|
||||
font-size: 16px;
|
||||
padding-top: 1px;
|
||||
}
|
||||
.selectedAlert .alertList { top: 33px; }
|
||||
|
||||
.prev_button, .next_button { top: auto;bottom: 5px; }
|
||||
.colstack_grid { grid-template-columns: none; grid-gap: 8px; }
|
||||
.grid_istat { margin-bottom: 0px; }
|
||||
}
|
||||
|
||||
@media (max-width: 470px) {
|
||||
ul { margin-bottom: 10px; }
|
||||
.menu_overview, .menu_profile, .hide_on_micro { display: none; }
|
||||
.selectedAlert .alertList {
|
||||
width: 156px;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
.alertItem.withAvatar {
|
||||
background-size: 36px;
|
||||
text-align: center;
|
||||
padding-left: 42px;
|
||||
height: 46px;
|
||||
}
|
||||
.alertItem { padding: 8px; }
|
||||
.alertItem .text {
|
||||
height: 30px;
|
||||
font-size: 10px;
|
||||
font-weight: bold;
|
||||
white-space: normal;
|
||||
}
|
||||
.post_container { overflow: visible !important; }
|
||||
|
||||
.post_item {
|
||||
background-position: 0px 2px !important;
|
||||
background-size: 64px 64px !important;
|
||||
padding-left: 2px !important;
|
||||
min-height: 96px;
|
||||
position: relative !important;
|
||||
}
|
||||
.post_item > .user_content {
|
||||
margin-left: 75px !important;
|
||||
width: 100% !important;
|
||||
}
|
||||
.post_item > .mod_button {
|
||||
float: right !important;
|
||||
margin-left: 2px !important;
|
||||
position: relative;
|
||||
top: -14px;
|
||||
}
|
||||
.post_item > .real_username {
|
||||
position: absolute;
|
||||
top: 70px;
|
||||
float: left;
|
||||
margin-top: 0px;
|
||||
padding-top: 3px !important;
|
||||
margin-right: 2px;
|
||||
width: 60px;
|
||||
font-size: 15px;
|
||||
}
|
||||
.userinfo { width: 70px; }
|
||||
.userinfo .avatar_item {
|
||||
background-size: 64px;
|
||||
width: 64px;
|
||||
min-height: 64px;
|
||||
}
|
||||
.content_container {
|
||||
margin-left: 73px;
|
||||
min-height: inherit !important;
|
||||
}
|
||||
.user_content { min-height: 76.5px !important; font-size: 15px; }
|
||||
.user_content.nobuttons { min-height: 100.5px !important; }
|
||||
.the_name { font-size: 15px; }
|
||||
.post_tag { font-size: 12px; }
|
||||
.rowtopic { font-size: 14px; }
|
||||
|
||||
.container { width: 100% !important; }
|
||||
}
|
||||
|
||||
@media (max-width: 330px) {
|
||||
li { padding-left: 6px; }
|
||||
.menu_left { padding-right: 6px; }
|
||||
}
|
|
@ -6,6 +6,7 @@
|
|||
"FullImage": "tempra-conflux.png",
|
||||
"MobileFriendly": true,
|
||||
"URL": "github.com/Azareal/Gosora",
|
||||
"Sidebars":"right",
|
||||
"Templates": [
|
||||
{
|
||||
"Name": "topic",
|
||||
|
|
|
@ -481,9 +481,17 @@ button.username {
|
|||
font-size: 12px;
|
||||
}
|
||||
|
||||
@media(max-width: 1300px) {
|
||||
.theme_row {
|
||||
background-image: none !important;
|
||||
/* Media Queries */
|
||||
|
||||
@media(min-width: 881px) {
|
||||
.shrink_main {
|
||||
float: left;
|
||||
width: calc(75% - 12px);
|
||||
}
|
||||
.sidebar {
|
||||
float: left;
|
||||
width: 25%;
|
||||
margin-left: 12px;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -516,6 +524,7 @@ button.username {
|
|||
overflow-x: hidden;
|
||||
}
|
||||
.container { width: auto; }
|
||||
.sidebar { display: none; }
|
||||
.selectedAlert .alertList { top: 37px; right: 4px; }
|
||||
}
|
||||
|
||||
|
|
|
@ -65,3 +65,9 @@
|
|||
.perm_preset_can_moderate:before { content: "Can Moderate"; color: darkblue; }
|
||||
.perm_preset_custom:before { content: "Custom"; color: black; }
|
||||
.perm_preset_default:before { content: "Default"; }
|
||||
|
||||
@media(max-width: 1300px) {
|
||||
.theme_row {
|
||||
background-image: none !important;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,5 +6,6 @@
|
|||
"FullImage": "tempra-cursive.png",
|
||||
"ForkOf": "tempra-simple",
|
||||
"MobileFriendly": true,
|
||||
"URL": "github.com/Azareal/Gosora"
|
||||
"URL": "github.com/Azareal/Gosora",
|
||||
"Sidebars":"right"
|
||||
}
|
||||
|
|
|
@ -451,165 +451,4 @@ button.username { position: relative; top: -0.25px; }
|
|||
top: -2px;
|
||||
}
|
||||
|
||||
@media(min-width: 881px) {
|
||||
.shrink_main {
|
||||
float: left;
|
||||
width: calc(75% - 12px);
|
||||
}
|
||||
.sidebar {
|
||||
float: left;
|
||||
width: 25%;
|
||||
margin-left: 12px;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 880px) {
|
||||
li {
|
||||
height: 29px;
|
||||
font-size: 15px;
|
||||
padding-left: 9px;
|
||||
padding-top: 6px;
|
||||
padding-bottom: 6px;
|
||||
}
|
||||
ul {
|
||||
height: 30px;
|
||||
margin-top: 8px;
|
||||
}
|
||||
.menu_left, .menu_right { padding-right: 9px; }
|
||||
.menu_alerts {
|
||||
padding-left: 7px;
|
||||
padding-right: 7px;
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
body {
|
||||
padding-left: 4px;
|
||||
padding-right: 4px;
|
||||
margin: 0px !important;
|
||||
width: 100% !important;
|
||||
height: 100% !important;
|
||||
overflow-x: hidden;
|
||||
}
|
||||
.container { width: auto; }
|
||||
.sidebar { display: none; }
|
||||
.selectedAlert .alertList { top: 37px; right: 4px; }
|
||||
}
|
||||
|
||||
@media (max-width: 680px) {
|
||||
li {
|
||||
padding-left: 5px;
|
||||
padding-top: 3px;
|
||||
padding-bottom: 2px;
|
||||
height: 25px;
|
||||
}
|
||||
li a { font-size: 14px; }
|
||||
ul { height: 26px; }
|
||||
.menu_left, .menu_right { padding-right: 7px; }
|
||||
.menu_create_topic { display: none; }
|
||||
|
||||
.menu_alerts {
|
||||
padding-left: 4px;
|
||||
padding-right: 4px;
|
||||
font-size: 16px;
|
||||
padding-top: 1px;
|
||||
}
|
||||
.selectedAlert .alertList { top: 33px; }
|
||||
|
||||
.hide_on_mobile { display: none !important; }
|
||||
.prev_button, .next_button { top: auto; bottom: 5px; }
|
||||
.colstack_grid { grid-template-columns: none; grid-gap: 8px; }
|
||||
.grid_istat { margin-bottom: 0px; }
|
||||
}
|
||||
|
||||
@media (max-width: 470px) {
|
||||
.menu_overview, .menu_profile { display: none; }
|
||||
.selectedAlert .alertList {
|
||||
width: 135px;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
.selectedAlert.hasAvatars .alertList { width: calc(100% - 8px); }
|
||||
.alertItem.withAvatar {
|
||||
background-size: 36px;
|
||||
text-align: right;
|
||||
padding-left: 10px;
|
||||
height: 46px;
|
||||
}
|
||||
.hasAvatars > .alertList > .alertItem.withAvatar {
|
||||
background-size: 46px;
|
||||
text-align: inherit;
|
||||
padding-left: 56px;
|
||||
height: 42px;
|
||||
}
|
||||
.alertItem { padding: 8px; }
|
||||
.hasAvatars > .alertList > .alertItem { padding-top: 11px; }
|
||||
.selectedAlert:not(.hasAvatars) > .alertList > .alertItem.withAvatar .text {
|
||||
width: calc(100% - 20px);
|
||||
height: 30px;
|
||||
white-space: normal;
|
||||
}
|
||||
.selectedAlert:not(.hasAvatars) > .alertList > .alertItem .text {
|
||||
font-size: 10px;
|
||||
font-weight: bold;
|
||||
margin-left: 0px;
|
||||
}
|
||||
.alertActive {
|
||||
opacity: 0.7;
|
||||
}
|
||||
|
||||
.hide_on_micro { display: none !important; }
|
||||
.post_container { overflow: visible !important; }
|
||||
.post_item:not(.action_item) {
|
||||
background-position: 0px 2px !important;
|
||||
background-size: 64px auto !important;
|
||||
padding-left: 2px !important;
|
||||
min-height: 96px;
|
||||
position: relative !important;
|
||||
}
|
||||
.post_item > .user_content {
|
||||
margin-left: 75px !important;
|
||||
width: 100% !important;
|
||||
min-height: 45px;
|
||||
}
|
||||
.post_item > .controls > .mod_button {
|
||||
float: right !important;
|
||||
margin-left: 2px !important;
|
||||
margin-right: 3px;
|
||||
}
|
||||
.post_item > .controls > .mod_button > button {
|
||||
opacity: 1;
|
||||
padding-left: 3px;
|
||||
padding-right: 3px;
|
||||
}
|
||||
.post_item > .controls > .real_username {
|
||||
margin-top: 0px;
|
||||
margin-right: 0px;
|
||||
font-size: 15px;
|
||||
color: black;
|
||||
max-width: 61px;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
.post_item > .controls {
|
||||
margin-top: 0px;
|
||||
margin-left: 74px;
|
||||
width: calc(100% - 74px);
|
||||
}
|
||||
.rowtopic {
|
||||
font-size: 14px;
|
||||
}
|
||||
.container { width: 100% !important; }
|
||||
}
|
||||
|
||||
@media (max-width: 330px) {
|
||||
li { padding-left: 6px; }
|
||||
.menu_left { padding-right: 6px; }
|
||||
.post_item > .controls > .real_username {
|
||||
display: inline-block;
|
||||
overflow: hidden;
|
||||
margin-right: -3px;
|
||||
text-overflow: clip;
|
||||
max-width: 84px;
|
||||
}
|
||||
.post_item > .controls { margin-left: 72px; }
|
||||
.top_post > .post_item > .controls > .real_username { max-width: 57px; }
|
||||
.top_post > .post_item { padding-right: 4px; }
|
||||
}
|
||||
{{template "media.partial.css" }}
|
||||
|
|
|
@ -0,0 +1,162 @@
|
|||
@media(min-width: 881px) {
|
||||
.shrink_main {
|
||||
float: left;
|
||||
width: calc(75% - 12px);
|
||||
}
|
||||
.sidebar {
|
||||
float: left;
|
||||
width: 25%;
|
||||
margin-left: 12px;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 880px) {
|
||||
li {
|
||||
height: 29px;
|
||||
font-size: 15px;
|
||||
padding-left: 9px;
|
||||
padding-top: 6px;
|
||||
padding-bottom: 6px;
|
||||
}
|
||||
ul {
|
||||
height: 30px;
|
||||
margin-top: 8px;
|
||||
}
|
||||
.menu_left, .menu_right { padding-right: 9px; }
|
||||
.menu_alerts {
|
||||
padding-left: 7px;
|
||||
padding-right: 7px;
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
body {
|
||||
padding-left: 4px;
|
||||
padding-right: 4px;
|
||||
margin: 0px !important;
|
||||
width: 100% !important;
|
||||
height: 100% !important;
|
||||
overflow-x: hidden;
|
||||
}
|
||||
.container { width: auto; }
|
||||
.sidebar { display: none; }
|
||||
.selectedAlert .alertList { top: 37px; right: 4px; }
|
||||
}
|
||||
|
||||
@media (max-width: 680px) {
|
||||
li {
|
||||
padding-left: 5px;
|
||||
padding-top: 3px;
|
||||
padding-bottom: 2px;
|
||||
height: 25px;
|
||||
}
|
||||
li a { font-size: 14px; }
|
||||
ul { height: 26px; }
|
||||
.menu_left, .menu_right { padding-right: 7px; }
|
||||
.menu_create_topic { display: none; }
|
||||
|
||||
.menu_alerts {
|
||||
padding-left: 4px;
|
||||
padding-right: 4px;
|
||||
font-size: 16px;
|
||||
padding-top: 1px;
|
||||
}
|
||||
.selectedAlert .alertList { top: 33px; }
|
||||
|
||||
.hide_on_mobile { display: none !important; }
|
||||
.prev_button, .next_button { top: auto; bottom: 5px; }
|
||||
.colstack_grid { grid-template-columns: none; grid-gap: 8px; }
|
||||
.grid_istat { margin-bottom: 0px; }
|
||||
}
|
||||
|
||||
@media (max-width: 470px) {
|
||||
.menu_overview, .menu_profile { display: none; }
|
||||
.selectedAlert .alertList {
|
||||
width: 135px;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
.selectedAlert.hasAvatars .alertList { width: calc(100% - 8px); }
|
||||
.alertItem.withAvatar {
|
||||
background-size: 36px;
|
||||
text-align: right;
|
||||
padding-left: 10px;
|
||||
height: 46px;
|
||||
}
|
||||
.hasAvatars > .alertList > .alertItem.withAvatar {
|
||||
background-size: 46px;
|
||||
text-align: inherit;
|
||||
padding-left: 56px;
|
||||
height: 42px;
|
||||
}
|
||||
.alertItem { padding: 8px; }
|
||||
.hasAvatars > .alertList > .alertItem { padding-top: 11px; }
|
||||
.selectedAlert:not(.hasAvatars) > .alertList > .alertItem.withAvatar .text {
|
||||
width: calc(100% - 20px);
|
||||
height: 30px;
|
||||
white-space: normal;
|
||||
}
|
||||
.selectedAlert:not(.hasAvatars) > .alertList > .alertItem .text {
|
||||
font-size: 10px;
|
||||
font-weight: bold;
|
||||
margin-left: 0px;
|
||||
}
|
||||
.alertActive {
|
||||
opacity: 0.7;
|
||||
}
|
||||
|
||||
.hide_on_micro { display: none !important; }
|
||||
.post_container { overflow: visible !important; }
|
||||
.post_item:not(.action_item) {
|
||||
background-position: 0px 2px !important;
|
||||
background-size: 64px auto !important;
|
||||
padding-left: 2px !important;
|
||||
min-height: 96px;
|
||||
position: relative !important;
|
||||
}
|
||||
.post_item > .user_content {
|
||||
margin-left: 75px !important;
|
||||
width: 100% !important;
|
||||
min-height: 45px;
|
||||
}
|
||||
.post_item > .controls > .mod_button {
|
||||
float: right !important;
|
||||
margin-left: 2px !important;
|
||||
margin-right: 3px;
|
||||
}
|
||||
.post_item > .controls > .mod_button > button {
|
||||
opacity: 1;
|
||||
padding-left: 3px;
|
||||
padding-right: 3px;
|
||||
}
|
||||
.post_item > .controls > .real_username {
|
||||
margin-top: 0px;
|
||||
margin-right: 0px;
|
||||
font-size: 15px;
|
||||
color: black;
|
||||
max-width: 61px;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
.post_item > .controls {
|
||||
margin-top: 0px;
|
||||
margin-left: 74px;
|
||||
width: calc(100% - 74px);
|
||||
}
|
||||
.rowtopic {
|
||||
font-size: 14px;
|
||||
}
|
||||
.container { width: 100% !important; }
|
||||
}
|
||||
|
||||
@media (max-width: 330px) {
|
||||
li { padding-left: 6px; }
|
||||
.menu_left { padding-right: 6px; }
|
||||
.post_item > .controls > .real_username {
|
||||
display: inline-block;
|
||||
overflow: hidden;
|
||||
margin-right: -3px;
|
||||
text-overflow: clip;
|
||||
max-width: 84px;
|
||||
}
|
||||
.post_item > .controls { margin-left: 72px; }
|
||||
.top_post > .post_item > .controls > .real_username { max-width: 57px; }
|
||||
.top_post > .post_item { padding-right: 4px; }
|
||||
}
|
|
@ -6,7 +6,7 @@
|
|||
"FullImage": "tempra-simple.png",
|
||||
"MobileFriendly": true,
|
||||
"URL": "github.com/Azareal/Gosora",
|
||||
"Sidebars":"false",
|
||||
"Sidebars":"right",
|
||||
"Resources": [
|
||||
{
|
||||
"Name": "sample.css",
|
||||
|
|
15
user.go
15
user.go
|
@ -52,6 +52,10 @@ type Email struct
|
|||
Token string
|
||||
}
|
||||
|
||||
func CheckPassword(real_password string, password string, salt string) (err error) {
|
||||
return bcrypt.CompareHashAndPassword([]byte(real_password), []byte(password + salt))
|
||||
}
|
||||
|
||||
func SetPassword(uid int, password string) (error) {
|
||||
salt, err := GenerateSafeString(saltLength)
|
||||
if err != nil {
|
||||
|
@ -72,12 +76,13 @@ func SetPassword(uid int, password string) (error) {
|
|||
}
|
||||
|
||||
func SendValidationEmail(username string, email string, token string) bool {
|
||||
var schema string
|
||||
var schema string = "http"
|
||||
if enable_ssl {
|
||||
schema = "s"
|
||||
schema += "s"
|
||||
}
|
||||
|
||||
subject := "Validate Your Email @ " + site_name
|
||||
msg := "Dear " + username + ", following your registration on our forums, we ask you to validate your email, so that we can confirm that this email actually belongs to you.\n\nClick on the following link to do so. http" + schema + "://" + site_url + "/user/edit/token/" + token + "\n\nIf you haven't created an account here, then please feel free to ignore this email.\nWe're sorry for the inconvenience this may have caused."
|
||||
msg := "Dear " + username + ", following your registration on our forums, we ask you to validate your email, so that we can confirm that this email actually belongs to you.\n\nClick on the following link to do so. " + schema + "://" + site_url + "/user/edit/token/" + token + "\n\nIf you haven't created an account here, then please feel free to ignore this email.\nWe're sorry for the inconvenience this may have caused."
|
||||
return SendEmail(email, subject, msg)
|
||||
}
|
||||
|
||||
|
@ -207,13 +212,11 @@ func SessionCheck(w http.ResponseWriter, r *http.Request) (user User, headerVars
|
|||
if len(docks.RightSidebar) != 0 {
|
||||
var sbody string
|
||||
for _, widget := range docks.RightSidebar {
|
||||
//fmt.Println("widget",widget)
|
||||
if widget.Enabled && widget.Location == "global" {
|
||||
sbody += widget.Body
|
||||
//fmt.Println("sbody",sbody)
|
||||
}
|
||||
}
|
||||
headerVars.Sidebars.Right = template.HTML(sbody)
|
||||
headerVars.Widgets.RightSidebar = template.HTML(sbody)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
100
user_store.go
100
user_store.go
|
@ -2,12 +2,15 @@ package main
|
|||
|
||||
import "log"
|
||||
import "sync"
|
||||
import "errors"
|
||||
import "strings"
|
||||
import "strconv"
|
||||
import "database/sql"
|
||||
import "./query_gen/lib"
|
||||
import "golang.org/x/crypto/bcrypt"
|
||||
|
||||
var users UserStore
|
||||
var err_account_exists = errors.New("This username is already in use.")
|
||||
|
||||
type UserStore interface {
|
||||
Load(id int) error
|
||||
|
@ -22,6 +25,7 @@ type UserStore interface {
|
|||
//GetConn() interface{}
|
||||
Remove(id int) error
|
||||
RemoveUnsafe(id int) error
|
||||
CreateUser(username string, password string, email string, group int, active int) (int, error)
|
||||
GetLength() int
|
||||
GetCapacity() int
|
||||
}
|
||||
|
@ -31,18 +35,35 @@ type MemoryUserStore struct {
|
|||
length int
|
||||
capacity int
|
||||
get *sql.Stmt
|
||||
register *sql.Stmt
|
||||
username_exists *sql.Stmt
|
||||
sync.RWMutex
|
||||
}
|
||||
|
||||
func NewMemoryUserStore(capacity int) *MemoryUserStore {
|
||||
stmt, err := qgen.Builder.SimpleSelect("users","name, group, is_super_admin, session, email, avatar, message, url_prefix, url_name, level, score, last_ip","uid = ?","","")
|
||||
get_stmt, err := qgen.Builder.SimpleSelect("users","name, group, is_super_admin, session, email, avatar, message, url_prefix, url_name, level, score, last_ip","uid = ?","","")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
// Add an admin version of register_stmt with more flexibility?
|
||||
// create_account_stmt, err = db.Prepare("INSERT INTO
|
||||
register_stmt, err := qgen.Builder.SimpleInsert("users","name, email, password, salt, group, is_super_admin, session, active, message","?,?,?,?,?,0,'',?,''")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
username_exists_stmt, err := qgen.Builder.SimpleSelect("users","name","name = ?","","")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
return &MemoryUserStore{
|
||||
items:make(map[int]*User),
|
||||
capacity:capacity,
|
||||
get:stmt,
|
||||
get:get_stmt,
|
||||
register:register_stmt,
|
||||
username_exists:username_exists_stmt,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -178,6 +199,32 @@ func (sus *MemoryUserStore) RemoveUnsafe(id int) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (sus *MemoryUserStore) CreateUser(username string, password string, email string, group int, active int) (int, error) {
|
||||
// Is this username already taken..?
|
||||
err := sus.username_exists.QueryRow(username).Scan(&username)
|
||||
if err != sql.ErrNoRows {
|
||||
return 0, err_account_exists
|
||||
}
|
||||
|
||||
salt, err := GenerateSafeString(saltLength)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
hashed_password, err := bcrypt.GenerateFromPassword([]byte(password + salt), bcrypt.DefaultCost)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
res, err := sus.register.Exec(username,email,string(hashed_password),salt,group,active)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
lastId, err := res.LastInsertId()
|
||||
return int(lastId), err
|
||||
}
|
||||
|
||||
func (sus *MemoryUserStore) GetLength() int {
|
||||
return sus.length
|
||||
}
|
||||
|
@ -192,14 +239,33 @@ func (sus *MemoryUserStore) GetCapacity() int {
|
|||
|
||||
type SqlUserStore struct {
|
||||
get *sql.Stmt
|
||||
register *sql.Stmt
|
||||
username_exists *sql.Stmt
|
||||
}
|
||||
|
||||
func NewSqlUserStore() *SqlUserStore {
|
||||
stmt, err := qgen.Builder.SimpleSelect("users","name, group, is_super_admin, session, email, avatar, message, url_prefix, url_name, level, score, last_ip","uid = ?","","")
|
||||
get_stmt, err := qgen.Builder.SimpleSelect("users","name, group, is_super_admin, session, email, avatar, message, url_prefix, url_name, level, score, last_ip","uid = ?","","")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
return &SqlUserStore{stmt}
|
||||
|
||||
// Add an admin version of register_stmt with more flexibility?
|
||||
// create_account_stmt, err = db.Prepare("INSERT INTO
|
||||
register_stmt, err := qgen.Builder.SimpleInsert("users","name, email, password, salt, group, is_super_admin, session, active, message","?,?,?,?,?,0,'',?,''")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
username_exists_stmt, err := qgen.Builder.SimpleSelect("users","name","name = ?","","")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
return &SqlUserStore{
|
||||
get:get_stmt,
|
||||
register:register_stmt,
|
||||
username_exists:username_exists_stmt,
|
||||
}
|
||||
}
|
||||
|
||||
func (sus *SqlUserStore) Get(id int) (*User, error) {
|
||||
|
@ -273,6 +339,32 @@ func (sus *SqlUserStore) Load(id int) error {
|
|||
return err
|
||||
}
|
||||
|
||||
func (sus *SqlUserStore) CreateUser(username string, password string, email string, group int, active int) (int, error) {
|
||||
// Is this username already taken..?
|
||||
err := sus.username_exists.QueryRow(username).Scan(&username)
|
||||
if err != sql.ErrNoRows {
|
||||
return 0, err_account_exists
|
||||
}
|
||||
|
||||
salt, err := GenerateSafeString(saltLength)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
hashed_password, err := bcrypt.GenerateFromPassword([]byte(password + salt), bcrypt.DefaultCost)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
res, err := sus.register.Exec(username,email,string(hashed_password),salt,group,active)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
lastId, err := res.LastInsertId()
|
||||
return int(lastId), err
|
||||
}
|
||||
|
||||
// Placeholder methods, the actual queries are done elsewhere
|
||||
func (sus *SqlUserStore) Set(item *User) error {
|
||||
return nil
|
||||
|
|
2
utils.go
2
utils.go
|
@ -213,7 +213,7 @@ func weak_password(password string) error {
|
|||
if lower == 0 {
|
||||
return errors.New("You don't have any lowercase characters in your password.")
|
||||
}
|
||||
if (len(password) / 2) < len(charMap) {
|
||||
if (len(password) / 2) > len(charMap) {
|
||||
return errors.New("You don't have enough unique characters in your password.")
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue