diff --git a/.gitignore b/.gitignore
index f2b3649c..705c6af2 100644
--- a/.gitignore
+++ b/.gitignore
@@ -8,6 +8,7 @@ brun.bat
uploads/avatar_*
uploads/socialgroup_*
bin/*
+out/*
*.exe
*.exe~
*.prof
diff --git a/auth.go b/auth.go
index 5896fb83..45b18224 100644
--- a/auth.go
+++ b/auth.go
@@ -92,10 +92,10 @@ func (auth *DefaultAuth) ForceLogout(uid int) error {
return errors.New("There was a glitch in the system. Please contact your local administrator.")
}
- // Flush the user out of the cache and reload
- err = users.Reload(uid)
- if err != nil {
- return errors.New("Your account no longer exists.")
+ // Flush the user out of the cache
+ ucache, ok := users.(UserCache)
+ if ok {
+ ucache.CacheRemove(uid)
}
return nil
@@ -167,7 +167,10 @@ func (auth *DefaultAuth) CreateSession(uid int) (session string, err error) {
return "", err
}
- // Reload the user data
- _ = users.Reload(uid)
+ // Flush the user data from the cache
+ ucache, ok := users.(UserCache)
+ if ok {
+ ucache.CacheRemove(uid)
+ }
return session, nil
}
diff --git a/errors.go b/errors.go
index 4ca0aa29..dc501904 100644
--- a/errors.go
+++ b/errors.go
@@ -1,40 +1,18 @@
package main
-import "fmt"
import "log"
-import "bytes"
+
import "sync"
import "net/http"
import "runtime/debug"
// TODO: Use the error_buffer variable to construct the system log in the Control Panel. Should we log errors caused by users too? Or just collect statistics on those or do nothing? Intercept recover()? Could we intercept the logger instead here? We might get too much information, if we intercept the logger, maybe make it part of the Debug page?
+// ? - Should we pass HeaderVars / HeaderLite rather than forcing the errors to pull the global HeaderVars instance?
var errorBufferMutex sync.RWMutex
var errorBuffer []error
//var notfoundCountPerSecond int
//var nopermsCountPerSecond int
-var errorInternal []byte
-var errorNotfound []byte
-
-func initErrors() error {
- var b bytes.Buffer
- user := User{0, "guest", "Guest", "", 0, false, false, false, false, false, false, GuestPerms, nil, "", false, "", "", "", "", "", 0, 0, "0.0.0.0.0", 0}
- pi := Page{"Internal Server Error", user, hvars, tList, "A problem has occurred in the system."}
- err := templates.ExecuteTemplate(&b, "error.html", pi)
- if err != nil {
- return err
- }
- errorInternal = b.Bytes()
-
- b.Reset()
- pi = Page{"Not Found", user, hvars, tList, "The requested page doesn't exist."}
- err = templates.ExecuteTemplate(&b, "error.html", pi)
- if err != nil {
- return err
- }
- errorNotfound = b.Bytes()
- return nil
-}
// LogError logs internal handler errors which can't be handled with InternalError() as a wrapper for log.Fatal(), we might do more with it in the future
func LogError(err error) {
@@ -47,10 +25,19 @@ func LogError(err error) {
}
// InternalError is the main function for handling internal errors, while simultaneously printing out a page for the end-user to let them know that *something* has gone wrong
+// ? - Add a user parameter?
func InternalError(err error, w http.ResponseWriter) {
- _, _ = w.Write(errorInternal)
log.Print(err)
debug.PrintStack()
+
+ // TODO: Centralise the user struct somewhere else
+ user := User{0, "guest", "Guest", "", 0, false, false, false, false, false, false, GuestPerms, nil, "", false, "", "", "", "", "", 0, 0, "0.0.0.0.0", 0}
+ pi := Page{"Internal Server Error", user, getDefaultHeaderVar(), tList, "A problem has occurred in the system."}
+ err = templates.ExecuteTemplate(w, "error.html", pi)
+ if err != nil {
+ log.Print(err)
+ }
+
errorBufferMutex.Lock()
defer errorBufferMutex.Unlock()
errorBuffer = append(errorBuffer, err)
@@ -58,22 +45,17 @@ func InternalError(err error, w http.ResponseWriter) {
}
// InternalErrorJSQ is the JSON "maybe" version of InternalError which can handle both JSON and normal requests
+// ? - Add a user parameter?
func InternalErrorJSQ(err error, w http.ResponseWriter, r *http.Request, isJs bool) {
- w.WriteHeader(500)
if !isJs {
- _, _ = w.Write(errorInternal)
+ InternalError(err, w)
} else {
- _, _ = w.Write([]byte(`{"errmsg":"A problem has occured in the system."}`))
+ InternalErrorJS(err, w, r)
}
- log.Print(err)
- debug.PrintStack()
- errorBufferMutex.Lock()
- defer errorBufferMutex.Unlock()
- errorBuffer = append(errorBuffer, err)
- log.Fatal("")
}
// InternalErrorJS is the JSON version of InternalError on routes we know will only be requested via JSON. E.g. An API.
+// ? - Add a user parameter?
func InternalErrorJS(err error, w http.ResponseWriter, r *http.Request) {
w.WriteHeader(500)
_, _ = w.Write([]byte(`{"errmsg":"A problem has occured in the system."}`))
@@ -87,35 +69,31 @@ func InternalErrorJS(err error, w http.ResponseWriter, r *http.Request) {
// LoginRequired is an error shown to the end-user when they try to access an area which requires them to login
func LoginRequired(w http.ResponseWriter, r *http.Request, user User) {
w.WriteHeader(401)
- pi := Page{"Local Error", user, hvars, tList, "You need to login to do that."}
+ pi := Page{"Local Error", user, getDefaultHeaderVar(), tList, "You need to login to do that."}
if preRenderHooks["pre_render_error"] != nil {
if runPreRenderHook("pre_render_error", w, r, &user, &pi) {
return
}
}
- var b bytes.Buffer
- err := templates.ExecuteTemplate(&b, "error.html", pi)
+ err := templates.ExecuteTemplate(w, "error.html", pi)
if err != nil {
LogError(err)
}
- fmt.Fprintln(w, b.String())
}
func PreError(errmsg string, w http.ResponseWriter, r *http.Request) {
w.WriteHeader(500)
user := User{ID: 0, Group: 6, Perms: GuestPerms}
- pi := Page{"Error", user, hvars, tList, errmsg}
+ pi := Page{"Error", user, getDefaultHeaderVar(), tList, errmsg}
if preRenderHooks["pre_render_error"] != nil {
if runPreRenderHook("pre_render_error", w, r, &user, &pi) {
return
}
}
- var b bytes.Buffer
- err := templates.ExecuteTemplate(&b, "error.html", pi)
+ err := templates.ExecuteTemplate(w, "error.html", pi)
if err != nil {
LogError(err)
}
- fmt.Fprintln(w, b.String())
}
func PreErrorJS(errmsg string, w http.ResponseWriter, r *http.Request) {
@@ -124,60 +102,33 @@ func PreErrorJS(errmsg string, w http.ResponseWriter, r *http.Request) {
}
func PreErrorJSQ(errmsg string, w http.ResponseWriter, r *http.Request, isJs bool) {
- w.WriteHeader(500)
if !isJs {
- user := User{ID: 0, Group: 6, Perms: GuestPerms}
- pi := Page{"Local Error", user, hvars, tList, errmsg}
- if preRenderHooks["pre_render_error"] != nil {
- if runPreRenderHook("pre_render_error", w, r, &user, &pi) {
- return
- }
- }
- var b bytes.Buffer
- err := templates.ExecuteTemplate(&b, "error.html", pi)
- if err != nil {
- LogError(err)
- }
- fmt.Fprintln(w, b.String())
+ PreError(errmsg, w, r)
} else {
- _, _ = w.Write([]byte(`{"errmsg":"` + errmsg + `"}`))
+ PreErrorJS(errmsg, w, r)
}
}
// LocalError is an error shown to the end-user when something goes wrong and it's not the software's fault
func LocalError(errmsg string, w http.ResponseWriter, r *http.Request, user User) {
w.WriteHeader(500)
- pi := Page{"Local Error", user, hvars, tList, errmsg}
+ pi := Page{"Local Error", user, getDefaultHeaderVar(), tList, errmsg}
if preRenderHooks["pre_render_error"] != nil {
if runPreRenderHook("pre_render_error", w, r, &user, &pi) {
return
}
}
- var b bytes.Buffer
- err := templates.ExecuteTemplate(&b, "error.html", pi)
+ err := templates.ExecuteTemplate(w, "error.html", pi)
if err != nil {
LogError(err)
}
- fmt.Fprintln(w, b.String())
}
func LocalErrorJSQ(errmsg string, w http.ResponseWriter, r *http.Request, user User, isJs bool) {
- w.WriteHeader(500)
if !isJs {
- pi := Page{"Local Error", user, hvars, tList, errmsg}
- if preRenderHooks["pre_render_error"] != nil {
- if runPreRenderHook("pre_render_error", w, r, &user, &pi) {
- return
- }
- }
- var b bytes.Buffer
- err := templates.ExecuteTemplate(&b, "error.html", pi)
- if err != nil {
- LogError(err)
- }
- fmt.Fprintln(w, b.String())
+ LocalError(errmsg, w, r, user)
} else {
- _, _ = w.Write([]byte(`{"errmsg":"` + errmsg + `"}`))
+ LocalErrorJS(errmsg, w, r)
}
}
@@ -189,156 +140,135 @@ func LocalErrorJS(errmsg string, w http.ResponseWriter, r *http.Request) {
// NoPermissions is an error shown to the end-user when they try to access an area which they aren't authorised to access
func NoPermissions(w http.ResponseWriter, r *http.Request, user User) {
w.WriteHeader(403)
- pi := Page{"Local Error", user, hvars, tList, "You don't have permission to do that."}
+ pi := Page{"Local Error", user, getDefaultHeaderVar(), tList, "You don't have permission to do that."}
if preRenderHooks["pre_render_error"] != nil {
if runPreRenderHook("pre_render_error", w, r, &user, &pi) {
return
}
}
- var b bytes.Buffer
- err := templates.ExecuteTemplate(&b, "error.html", pi)
+ err := templates.ExecuteTemplate(w, "error.html", pi)
if err != nil {
LogError(err)
}
- errpage := b.String()
- fmt.Fprintln(w, errpage)
}
func NoPermissionsJSQ(w http.ResponseWriter, r *http.Request, user User, isJs bool) {
- w.WriteHeader(403)
if !isJs {
- pi := Page{"Local Error", user, hvars, tList, "You don't have permission to do that."}
- if preRenderHooks["pre_render_error"] != nil {
- if runPreRenderHook("pre_render_error", w, r, &user, &pi) {
- return
- }
- }
- var b bytes.Buffer
- err := templates.ExecuteTemplate(&b, "error.html", pi)
- if err != nil {
- LogError(err)
- }
- fmt.Fprintln(w, b.String())
+ NoPermissions(w, r, user)
} else {
- _, _ = w.Write([]byte(`{"errmsg":"You don't have permission to do that."}`))
+ NoPermissionsJS(w, r, user)
}
}
+func NoPermissionsJS(w http.ResponseWriter, r *http.Request, user User) {
+ w.WriteHeader(403)
+ _, _ = w.Write([]byte(`{"errmsg":"You don't have permission to do that."}`))
+}
+
// ? - Is this actually used? Should it be used? A ban in Gosora should be more of a permission revocation to stop them posting rather than something which spits up an error page, right?
func Banned(w http.ResponseWriter, r *http.Request, user User) {
w.WriteHeader(403)
- pi := Page{"Banned", user, hvars, tList, "You have been banned from this site."}
+ pi := Page{"Banned", user, getDefaultHeaderVar(), tList, "You have been banned from this site."}
if preRenderHooks["pre_render_error"] != nil {
if runPreRenderHook("pre_render_error", w, r, &user, &pi) {
return
}
}
- var b bytes.Buffer
- err := templates.ExecuteTemplate(&b, "error.html", pi)
+ err := templates.ExecuteTemplate(w, "error.html", pi)
if err != nil {
LogError(err)
}
- fmt.Fprintln(w, b.String())
}
// nolint
// BannedJSQ is the version of the banned error page which handles both JavaScript requests and normal page loads
func BannedJSQ(w http.ResponseWriter, r *http.Request, user User, isJs bool) {
- w.WriteHeader(403)
if !isJs {
- pi := Page{"Banned", user, hvars, tList, "You have been banned from this site."}
- if preRenderHooks["pre_render_error"] != nil {
- if runPreRenderHook("pre_render_error", w, r, &user, &pi) {
- return
- }
- }
- var b bytes.Buffer
- err := templates.ExecuteTemplate(&b, "error.html", pi)
- if err != nil {
- LogError(err)
- }
- fmt.Fprintln(w, b.String())
+ Banned(w, r, user)
} else {
- _, _ = w.Write([]byte(`{"errmsg":"You have been banned from this site."}`))
+ BannedJS(w, r, user)
}
}
+func BannedJS(w http.ResponseWriter, r *http.Request, user User) {
+ w.WriteHeader(403)
+ _, _ = w.Write([]byte(`{"errmsg":"You have been banned from this site."}`))
+}
+
// nolint
func LoginRequiredJSQ(w http.ResponseWriter, r *http.Request, user User, isJs bool) {
w.WriteHeader(401)
if !isJs {
- pi := Page{"Local Error", user, hvars, tList, "You need to login to do that."}
+ pi := Page{"Local Error", user, getDefaultHeaderVar(), tList, "You need to login to do that."}
if preRenderHooks["pre_render_error"] != nil {
if runPreRenderHook("pre_render_error", w, r, &user, &pi) {
return
}
}
- var b bytes.Buffer
- err := templates.ExecuteTemplate(&b, "error.html", pi)
+ err := templates.ExecuteTemplate(w, "error.html", pi)
if err != nil {
LogError(err)
}
- fmt.Fprintln(w, b.String())
} else {
_, _ = w.Write([]byte(`{"errmsg":"You need to login to do that."}`))
}
}
+// SecurityError is used whenever a session mismatch is found
+// ? - Should we add JS and JSQ versions of this?
func SecurityError(w http.ResponseWriter, r *http.Request, user User) {
w.WriteHeader(403)
- pi := Page{"Security Error", user, hvars, tList, "There was a security issue with your request."}
+ pi := Page{"Security Error", user, getDefaultHeaderVar(), tList, "There was a security issue with your request."}
if preRenderHooks["pre_render_security_error"] != nil {
if runPreRenderHook("pre_render_security_error", w, r, &user, &pi) {
return
}
}
- var b bytes.Buffer
- err := templates.ExecuteTemplate(&b, "error.html", pi)
+ err := templates.ExecuteTemplate(w, "error.html", pi)
if err != nil {
LogError(err)
}
- fmt.Fprintln(w, b.String())
}
+// ? - Add a JSQ and JS version of this?
+// ? - Add a user parameter?
func NotFound(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(404)
- _, _ = w.Write(errorNotfound)
+ // TODO: Centralise the user struct somewhere else
+ user := User{0, "guest", "Guest", "", 0, false, false, false, false, false, false, GuestPerms, nil, "", false, "", "", "", "", "", 0, 0, "0.0.0.0.0", 0}
+ pi := Page{"Not Found", user, getDefaultHeaderVar(), tList, "The requested page doesn't exist."}
+ err := templates.ExecuteTemplate(w, "error.html", pi)
+ if err != nil {
+ LogError(err)
+ }
}
// nolint
func CustomError(errmsg string, errcode int, errtitle string, w http.ResponseWriter, r *http.Request, user User) {
w.WriteHeader(errcode)
- pi := Page{errtitle, user, hvars, tList, errmsg}
+ pi := Page{errtitle, user, getDefaultHeaderVar(), tList, errmsg}
if preRenderHooks["pre_render_error"] != nil {
if runPreRenderHook("pre_render_error", w, r, &user, &pi) {
return
}
}
- var b bytes.Buffer
- err := templates.ExecuteTemplate(&b, "error.html", pi)
+ err := templates.ExecuteTemplate(w, "error.html", pi)
if err != nil {
LogError(err)
}
- fmt.Fprintln(w, b.String())
}
// nolint
func CustomErrorJSQ(errmsg string, errcode int, errtitle string, w http.ResponseWriter, r *http.Request, user User, isJs bool) {
- w.WriteHeader(errcode)
if !isJs {
- pi := Page{errtitle, user, hvars, tList, errmsg}
- if preRenderHooks["pre_render_error"] != nil {
- if runPreRenderHook("pre_render_error", w, r, &user, &pi) {
- return
- }
- }
- var b bytes.Buffer
- err := templates.ExecuteTemplate(&b, "error.html", pi)
- if err != nil {
- LogError(err)
- }
- fmt.Fprintln(w, b.String())
+ CustomError(errmsg, errcode, errtitle, w, r, user)
} else {
- _, _ = w.Write([]byte(`{"errmsg":"` + errmsg + `"}`))
+ CustomErrorJS(errmsg, errcode, errtitle, w, r, user)
}
}
+
+// nolint
+func CustomErrorJS(errmsg string, errcode int, errtitle string, w http.ResponseWriter, r *http.Request, user User) {
+ w.WriteHeader(errcode)
+ _, _ = w.Write([]byte(`{"errmsg":"` + errmsg + `"}`))
+}
diff --git a/extend.go b/extend.go
index a015b2da..eaf66f80 100644
--- a/extend.go
+++ b/extend.go
@@ -156,6 +156,7 @@ func NewPlugin(uname string, name string, author string, url string, settings st
}
}
+// ? - Is this racey?
func (plugin *Plugin) AddHook(name string, handler interface{}) {
switch h := handler.(type) {
case func(interface{}) interface{}:
@@ -193,6 +194,7 @@ func (plugin *Plugin) AddHook(name string, handler interface{}) {
}
}
+// ? - Is this racey?
func (plugin *Plugin) RemoveHook(name string, handler interface{}) {
switch handler.(type) {
case func(interface{}) interface{}:
@@ -250,6 +252,7 @@ func initPlugins() {
pluginsInited = true
}
+// ? - Are the following functions racey?
func runHook(name string, data interface{}) interface{} {
for _, hook := range hooks[name] {
data = hook(data)
diff --git a/gen_mysql.go b/gen_mysql.go
index a78691a6..7dfd8c2c 100644
--- a/gen_mysql.go
+++ b/gen_mysql.go
@@ -35,7 +35,6 @@ var getUserReplyUIDStmt *sql.Stmt
var hasLikedTopicStmt *sql.Stmt
var hasLikedReplyStmt *sql.Stmt
var getUserNameStmt *sql.Stmt
-var getUserActiveStmt *sql.Stmt
var getEmailsByUserStmt *sql.Stmt
var getTopicBasicStmt *sql.Stmt
var getActivityEntryStmt *sql.Stmt
@@ -83,6 +82,8 @@ var editTopicStmt *sql.Stmt
var editReplyStmt *sql.Stmt
var stickTopicStmt *sql.Stmt
var unstickTopicStmt *sql.Stmt
+var lockTopicStmt *sql.Stmt
+var unlockTopicStmt *sql.Stmt
var updateLastIPStmt *sql.Stmt
var updateSessionStmt *sql.Stmt
var setPasswordStmt *sql.Stmt
@@ -292,12 +293,6 @@ func _gen_mysql() (err error) {
return err
}
- log.Print("Preparing getUserActive statement.")
- getUserActiveStmt, err = db.Prepare("SELECT `active` FROM `users` WHERE `uid` = ?")
- if err != nil {
- return err
- }
-
log.Print("Preparing getEmailsByUser statement.")
getEmailsByUserStmt, err = db.Prepare("SELECT `email`,`validated`,`token` FROM `emails` WHERE `uid` = ?")
if err != nil {
@@ -557,7 +552,7 @@ func _gen_mysql() (err error) {
}
log.Print("Preparing editTopic statement.")
- editTopicStmt, err = db.Prepare("UPDATE `topics` SET `title` = ?,`content` = ?,`parsed_content` = ?,`is_closed` = ? WHERE `tid` = ?")
+ editTopicStmt, err = db.Prepare("UPDATE `topics` SET `title` = ?,`content` = ?,`parsed_content` = ? WHERE `tid` = ?")
if err != nil {
return err
}
@@ -580,6 +575,18 @@ func _gen_mysql() (err error) {
return err
}
+ log.Print("Preparing lockTopic statement.")
+ lockTopicStmt, err = db.Prepare("UPDATE `topics` SET `is_closed` = 1 WHERE `tid` = ?")
+ if err != nil {
+ return err
+ }
+
+ log.Print("Preparing unlockTopic statement.")
+ unlockTopicStmt, err = db.Prepare("UPDATE `topics` SET `is_closed` = 0 WHERE `tid` = ?")
+ if err != nil {
+ return err
+ }
+
log.Print("Preparing updateLastIP statement.")
updateLastIPStmt, err = db.Prepare("UPDATE `users` SET `last_ip` = ? WHERE `uid` = ?")
if err != nil {
diff --git a/gen_pgsql.go b/gen_pgsql.go
index bae46de9..cfa68b2d 100644
--- a/gen_pgsql.go
+++ b/gen_pgsql.go
@@ -18,6 +18,8 @@ var editTopicStmt *sql.Stmt
var editReplyStmt *sql.Stmt
var stickTopicStmt *sql.Stmt
var unstickTopicStmt *sql.Stmt
+var lockTopicStmt *sql.Stmt
+var unlockTopicStmt *sql.Stmt
var updateLastIPStmt *sql.Stmt
var updateSessionStmt *sql.Stmt
var setPasswordStmt *sql.Stmt
@@ -96,7 +98,7 @@ func _gen_pgsql() (err error) {
}
log.Print("Preparing editTopic statement.")
- editTopicStmt, err = db.Prepare("UPDATE `topics` SET `title` = ?,`content` = ?,`parsed_content` = ?,`is_closed` = ? WHERE `tid` = ?")
+ editTopicStmt, err = db.Prepare("UPDATE `topics` SET `title` = ?,`content` = ?,`parsed_content` = ? WHERE `tid` = ?")
if err != nil {
return err
}
@@ -119,6 +121,18 @@ func _gen_pgsql() (err error) {
return err
}
+ log.Print("Preparing lockTopic statement.")
+ lockTopicStmt, err = db.Prepare("UPDATE `topics` SET `is_closed` = 1 WHERE `tid` = ?")
+ if err != nil {
+ return err
+ }
+
+ log.Print("Preparing unlockTopic statement.")
+ unlockTopicStmt, err = db.Prepare("UPDATE `topics` SET `is_closed` = 0 WHERE `tid` = ?")
+ if err != nil {
+ return err
+ }
+
log.Print("Preparing updateLastIP statement.")
updateLastIPStmt, err = db.Prepare("UPDATE `users` SET `last_ip` = ? WHERE `uid` = ?")
if err != nil {
diff --git a/general_test.go b/general_test.go
index 382b1995..b34053c6 100644
--- a/general_test.go
+++ b/general_test.go
@@ -47,10 +47,6 @@ func gloinit() error {
initTemplates()
dbProd.SetMaxOpenConns(64)
- err = initErrors()
- if err != nil {
- return err
- }
err = initPhrases()
if err != nil {
diff --git a/main.go b/main.go
index 5f49027d..44f9e11c 100644
--- a/main.go
+++ b/main.go
@@ -72,7 +72,6 @@ func main() {
log.Print("Running Gosora v" + version.String())
fmt.Println("")
startTime = time.Now()
- //timeLocation = startTime.Location()
log.Print("Processing configuration data")
processConfig()
@@ -88,10 +87,6 @@ func main() {
}
initTemplates()
- err = initErrors()
- if err != nil {
- log.Fatal(err)
- }
err = initPhrases()
if err != nil {
@@ -135,6 +130,8 @@ func main() {
select {
case <-secondTicker.C:
//log.Print("Running the second ticker")
+ // TODO: Add a plugin hook here
+
err := handleExpiredScheduledGroups()
if err != nil {
LogError(err)
@@ -152,10 +149,16 @@ func main() {
// TODO: Manage the TopicStore, UserStore, and ForumStore
// TODO: Alert the admin, if CPU usage, RAM usage, or the number of posts in the past second are too high
// TODO: Clean-up alerts with no unread matches which are over two weeks old. Move this to a 24 hour task?
+
+ // TODO: Add a plugin hook here
case <-fifteenMinuteTicker.C:
+ // TODO: Add a plugin hook here
+
// TODO: Automatically lock topics, if they're really old, and the associated setting is enabled.
// TODO: Publish scheduled posts.
// TODO: Delete the empty users_groups_scheduler entries
+
+ // TODO: Add a plugin hook here
}
}
}()
@@ -181,6 +184,8 @@ func main() {
router.HandleFunc("/topic/delete/submit/", routeDeleteTopic)
router.HandleFunc("/topic/stick/submit/", routeStickTopic)
router.HandleFunc("/topic/unstick/submit/", routeUnstickTopic)
+ router.HandleFunc("/topic/lock/submit/", routeLockTopic)
+ router.HandleFunc("/topic/unlock/submit/", routeUnlockTopic)
router.HandleFunc("/topic/like/submit/", routeLikeTopic)
// Custom Pages
@@ -217,17 +222,17 @@ func main() {
// The Control Panel
// TODO: Rename the commented route handlers to the new camelCase format :'(
- ///router.HandleFunc("/panel/", route_panel)
- ///router.HandleFunc("/panel/forums/", route_panel_forums)
- ///router.HandleFunc("/panel/forums/create/", route_panel_forums_create_submit)
- ///router.HandleFunc("/panel/forums/delete/", route_panel_forums_delete)
- ///router.HandleFunc("/panel/forums/delete/submit/", route_panel_forums_delete_submit)
- ///router.HandleFunc("/panel/forums/edit/", route_panel_forums_edit)
- ///router.HandleFunc("/panel/forums/edit/submit/", route_panel_forums_edit_submit)
- ///router.HandleFunc("/panel/forums/edit/perms/submit/", route_panel_forums_edit_perms_submit)
- ///router.HandleFunc("/panel/settings/", route_panel_settings)
- ///router.HandleFunc("/panel/settings/edit/", route_panel_setting)
- ///router.HandleFunc("/panel/settings/edit/submit/", route_panel_setting_edit)
+ ////router.HandleFunc("/panel/", routePanel)
+ ////router.HandleFunc("/panel/forums/", routePanelForums)
+ ////router.HandleFunc("/panel/forums/create/", routePanelForumsCreateSubmit)
+ ////router.HandleFunc("/panel/forums/delete/", routePanelForumsDelete)
+ ////router.HandleFunc("/panel/forums/delete/submit/", routePanelForumsDeleteSubmit)
+ ////router.HandleFunc("/panel/forums/edit/", routePanelForumsEdit)
+ ////router.HandleFunc("/panel/forums/edit/submit/", routePanelForumsEditSubmit)
+ ////router.HandleFunc("/panel/forums/edit/perms/submit/", routePanelForumsEditPermsSubmit)
+ ////router.HandleFunc("/panel/settings/", routePanelSettings)
+ ////router.HandleFunc("/panel/settings/edit/", routePanelSetting)
+ ////router.HandleFunc("/panel/settings/edit/submit/", routePanelSettingEdit)
///router.HandleFunc("/panel/themes/", route_panel_themes)
///router.HandleFunc("/panel/themes/default/", route_panel_themes_default)
///router.HandleFunc("/panel/plugins/", route_panel_plugins)
@@ -245,9 +250,9 @@ func main() {
///router.HandleFunc("/panel/logs/mod/", route_panel_logs_mod)
///router.HandleFunc("/panel/debug/", route_panel_debug)
- ///router.HandleFunc("/api/", route_api)
- //router.HandleFunc("/exit/", route_exit)
- ///router.HandleFunc("/", default_route)
+ ////router.HandleFunc("/api/", routeAPI)
+ //router.HandleFunc("/exit/", routeExit)
+ ////router.HandleFunc("/", config.DefaultRoute)
router.HandleFunc("/ws/", routeWebsockets)
log.Print("Initialising the plugins")
diff --git a/member_routes.go b/member_routes.go
index 29f4c769..8d3b40f1 100644
--- a/member_routes.go
+++ b/member_routes.go
@@ -242,14 +242,10 @@ func routeCreateReply(w http.ResponseWriter, r *http.Request, user User) {
go notifyWatchers(lastID)
}
- // Reload the topic...
- err = topics.Reload(tid)
- if err != nil && err == ErrNoRows {
- LocalError("The destination no longer exists", w, r, user)
- return
- } else if err != nil {
- InternalError(err, w)
- return
+ // Flush the topic out of the cache
+ tcache, ok := topics.(TopicCache)
+ if ok {
+ tcache.CacheRemove(tid)
}
http.Redirect(w, r, "/topic/"+strconv.Itoa(tid), http.StatusSeeOther)
@@ -348,16 +344,11 @@ func routeLikeTopic(w http.ResponseWriter, r *http.Request, user User) {
// Live alerts, if the poster is online and WebSockets is enabled
_ = wsHub.pushAlert(topic.CreatedBy, int(lastID), "like", "topic", user.ID, topic.CreatedBy, tid)
- // Reload the topic...
- err = topics.Reload(tid)
- if err != nil && err == ErrNoRows {
- LocalError("The liked topic no longer exists", w, r, user)
- return
- } else if err != nil {
- InternalError(err, w)
- return
+ // Flush the topic out of the cache
+ tcache, ok := topics.(TopicCache)
+ if ok {
+ tcache.CacheRemove(tid)
}
-
http.Redirect(w, r, "/topic/"+strconv.Itoa(tid), http.StatusSeeOther)
}
@@ -818,10 +809,9 @@ func routeAccountOwnEditAvatarSubmit(w http.ResponseWriter, r *http.Request, use
return
}
user.Avatar = "/uploads/avatar_" + strconv.Itoa(user.ID) + "." + ext
- err = users.Reload(user.ID)
- if err != nil {
- LocalError("This user no longer exists!", w, r, user)
- return
+ ucache, ok := users.(UserCache)
+ if ok {
+ ucache.CacheRemove(user.ID)
}
headerVars.NoticeList = append(headerVars.NoticeList, "Your avatar was successfully updated")
@@ -876,10 +866,9 @@ func routeAccountOwnEditUsernameSubmit(w http.ResponseWriter, r *http.Request, u
// TODO: Use the reloaded data instead for the name?
user.Name = newUsername
- err = users.Reload(user.ID)
- if err != nil {
- LocalError("Your account doesn't exist!", w, r, user)
- return
+ ucache, ok := users.(UserCache)
+ if ok {
+ ucache.CacheRemove(user.ID)
}
headerVars.NoticeList = append(headerVars.NoticeList, "Your username was successfully updated")
diff --git a/mod_routes.go b/mod_routes.go
index 57aa9807..f916af3c 100644
--- a/mod_routes.go
+++ b/mod_routes.go
@@ -13,7 +13,6 @@ import (
// TODO: Update the stats after edits so that we don't under or over decrement stats during deletes
// TODO: Disable stat updates in posts handled by plugin_socialgroups
func routeEditTopic(w http.ResponseWriter, r *http.Request, user User) {
- //log.Print("in routeEditTopic")
err := r.ParseForm()
if err != nil {
PreError("Bad Form", w, r)
@@ -21,8 +20,7 @@ func routeEditTopic(w http.ResponseWriter, r *http.Request, user User) {
}
isJs := (r.PostFormValue("js") == "1")
- var tid int
- tid, err = strconv.Atoi(r.URL.Path[len("/topic/edit/submit/"):])
+ tid, err := strconv.Atoi(r.URL.Path[len("/topic/edit/submit/"):])
if err != nil {
PreErrorJSQ("The provided TopicID is not a valid number.", w, r, isJs)
return
@@ -48,60 +46,24 @@ func routeEditTopic(w http.ResponseWriter, r *http.Request, user User) {
}
topicName := r.PostFormValue("topic_name")
- topicStatus := r.PostFormValue("topic_status")
- isClosed := (topicStatus == "closed")
topicContent := html.EscapeString(r.PostFormValue("topic_content"))
// TODO: Move this bit to the TopicStore
- _, err = editTopicStmt.Exec(topicName, preparseMessage(topicContent), parseMessage(html.EscapeString(preparseMessage(topicContent))), isClosed, tid)
+ _, err = editTopicStmt.Exec(topicName, preparseMessage(topicContent), parseMessage(html.EscapeString(preparseMessage(topicContent))), tid)
if err != nil {
InternalErrorJSQ(err, w, r, isJs)
return
}
- ipaddress, _, err := net.SplitHostPort(r.RemoteAddr)
- if err != nil {
- LocalError("Bad IP", w, r, user)
+ err = fstore.UpdateLastTopic(topicName, tid, user.Name, user.ID, time.Now().Format("2006-01-02 15:04:05"), oldTopic.ParentID)
+ if err != nil && err != ErrNoRows {
+ InternalError(err, w)
return
}
- if oldTopic.IsClosed != isClosed {
- var action string
- if isClosed {
- action = "lock"
- } else {
- action = "unlock"
- }
-
- err = addModLog(action, tid, "topic", ipaddress, user.ID)
- if err != nil {
- InternalError(err, w)
- return
- }
- _, err = createActionReplyStmt.Exec(tid, action, ipaddress, user.ID)
- if err != nil {
- InternalError(err, w)
- return
- }
- _, err = addRepliesToTopicStmt.Exec(1, user.ID, tid)
- if err != nil {
- InternalError(err, w)
- return
- }
- err = fstore.UpdateLastTopic(topicName, tid, user.Name, user.ID, time.Now().Format("2006-01-02 15:04:05"), oldTopic.ParentID)
- if err != nil && err != ErrNoRows {
- InternalError(err, w)
- return
- }
- }
-
- err = topics.Reload(tid)
- if err == ErrNoRows {
- LocalErrorJSQ("This topic no longer exists!", w, r, user, isJs)
- return
- } else if err != nil {
- InternalErrorJSQ(err, w, r, isJs)
- return
+ tcache, ok := topics.(TopicCache)
+ if ok {
+ tcache.CacheRemove(oldTopic.ID)
}
if !isJs {
@@ -194,13 +156,13 @@ func routeStickTopic(w http.ResponseWriter, r *http.Request, user User) {
return
}
- // TODO: Move this into the TopicStore?
- _, err = stickTopicStmt.Exec(tid)
+ err = topic.Stick()
if err != nil {
InternalError(err, w)
return
}
+ // ! - Can we use user.LastIP here? It might be racey, if another thread mutates it... We need to fix this.
ipaddress, _, err := net.SplitHostPort(r.RemoteAddr)
if err != nil {
LocalError("Bad IP", w, r, user)
@@ -211,17 +173,11 @@ func routeStickTopic(w http.ResponseWriter, r *http.Request, user User) {
InternalError(err, w)
return
}
- _, err = createActionReplyStmt.Exec(tid, "stick", ipaddress, user.ID)
+ err = topic.CreateActionReply("stick", ipaddress, user)
if err != nil {
InternalError(err, w)
return
}
-
- err = topics.Reload(tid)
- if err != nil {
- LocalError("This topic doesn't exist!", w, r, user)
- return
- }
http.Redirect(w, r, "/topic/"+strconv.Itoa(tid), http.StatusSeeOther)
}
@@ -251,7 +207,7 @@ func routeUnstickTopic(w http.ResponseWriter, r *http.Request, user User) {
return
}
- _, err = unstickTopicStmt.Exec(tid)
+ err = topic.Unstick()
if err != nil {
InternalError(err, w)
return
@@ -267,15 +223,111 @@ func routeUnstickTopic(w http.ResponseWriter, r *http.Request, user User) {
InternalError(err, w)
return
}
- _, err = createActionReplyStmt.Exec(tid, "unstick", ipaddress, user.ID)
+ err = topic.CreateActionReply("unstick", ipaddress, user)
+ if err != nil {
+ InternalError(err, w)
+ return
+ }
+ http.Redirect(w, r, "/topic/"+strconv.Itoa(tid), http.StatusSeeOther)
+}
+
+func routeLockTopic(w http.ResponseWriter, r *http.Request, user User) {
+ tid, err := strconv.Atoi(r.URL.Path[len("/topic/lock/submit/"):])
+ if err != nil {
+ PreError("The provided TopicID is not a valid number.", w, r)
+ return
+ }
+
+ topic, err := topics.Get(tid)
+ if err == ErrNoRows {
+ PreError("The topic you tried to pin doesn't exist.", w, r)
+ return
+ } else if err != nil {
+ InternalError(err, w)
+ return
+ }
+
+ // TODO: Add hooks to make use of headerLite
+ _, ok := SimpleForumUserCheck(w, r, &user, topic.ParentID)
+ if !ok {
+ return
+ }
+ if !user.Perms.ViewTopic || !user.Perms.CloseTopic {
+ NoPermissions(w, r, user)
+ return
+ }
+
+ err = topic.Lock()
if err != nil {
InternalError(err, w)
return
}
- err = topics.Reload(tid)
+ // ! - Can we use user.LastIP here? It might be racey, if another thread mutates it... We need to fix this.
+ ipaddress, _, err := net.SplitHostPort(r.RemoteAddr)
if err != nil {
- LocalError("This topic doesn't exist!", w, r, user)
+ LocalError("Bad IP", w, r, user)
+ return
+ }
+ err = addModLog("lock", tid, "topic", ipaddress, user.ID)
+ if err != nil {
+ InternalError(err, w)
+ return
+ }
+ err = topic.CreateActionReply("lock", ipaddress, user)
+ if err != nil {
+ InternalError(err, w)
+ return
+ }
+ http.Redirect(w, r, "/topic/"+strconv.Itoa(tid), http.StatusSeeOther)
+}
+
+func routeUnlockTopic(w http.ResponseWriter, r *http.Request, user User) {
+ tid, err := strconv.Atoi(r.URL.Path[len("/topic/unlock/submit/"):])
+ if err != nil {
+ PreError("The provided TopicID is not a valid number.", w, r)
+ return
+ }
+
+ topic, err := topics.Get(tid)
+ if err == ErrNoRows {
+ PreError("The topic you tried to pin doesn't exist.", w, r)
+ return
+ } else if err != nil {
+ InternalError(err, w)
+ return
+ }
+
+ // TODO: Add hooks to make use of headerLite
+ _, ok := SimpleForumUserCheck(w, r, &user, topic.ParentID)
+ if !ok {
+ return
+ }
+ if !user.Perms.ViewTopic || !user.Perms.CloseTopic {
+ NoPermissions(w, r, user)
+ return
+ }
+
+ err = topic.Unlock()
+ if err != nil {
+ InternalError(err, w)
+ return
+ }
+
+ // ! - Can we use user.LastIP here? It might be racey, if another thread mutates it... We need to fix this.
+ ipaddress, _, err := net.SplitHostPort(r.RemoteAddr)
+ if err != nil {
+ LocalError("Bad IP", w, r, user)
+ return
+ }
+ err = addModLog("unlock", tid, "topic", ipaddress, user.ID)
+ if err != nil {
+ InternalError(err, w)
+ return
+ }
+ err = topic.CreateActionReply("unlock", ipaddress, user)
+ if err != nil {
+ InternalError(err, w)
return
}
http.Redirect(w, r, "/topic/"+strconv.Itoa(tid), http.StatusSeeOther)
@@ -422,11 +474,9 @@ func routeReplyDeleteSubmit(w http.ResponseWriter, r *http.Request, user User) {
InternalError(err, w)
return
}
-
- err = topics.Reload(reply.ParentID)
- if err != nil {
- LocalError("This topic no longer exists!", w, r, user)
- return
+ tcache, ok := topics.(TopicCache)
+ if ok {
+ tcache.CacheRemove(reply.ParentID)
}
}
@@ -612,7 +662,7 @@ func routeIps(w http.ResponseWriter, r *http.Request, user User) {
return
}
}
- err = templates.ExecuteTemplate(w, "ip-search.html", pi)
+ err = templates.ExecuteTemplate(w, "ip-search-results.html", pi)
if err != nil {
InternalError(err, w)
}
@@ -821,8 +871,7 @@ func routeActivate(w http.ResponseWriter, r *http.Request, user User) {
return
}
- var active bool
- err = getUserActiveStmt.QueryRow(uid).Scan(&active)
+ targetUser, err := users.Get(uid)
if err == ErrNoRows {
LocalError("The account you're trying to activate no longer exists.", w, r, user)
return
@@ -831,17 +880,11 @@ func routeActivate(w http.ResponseWriter, r *http.Request, user User) {
return
}
- if active {
+ if targetUser.Active {
LocalError("The account you're trying to activate has already been activated.", w, r, user)
return
}
- _, err = activateUserStmt.Exec(uid)
- if err != nil {
- InternalError(err, w)
- return
- }
-
- _, err = changeGroupStmt.Exec(config.DefaultGroup, uid)
+ err = targetUser.Activate()
if err != nil {
InternalError(err, w)
return
@@ -852,16 +895,10 @@ func routeActivate(w http.ResponseWriter, r *http.Request, user User) {
LocalError("Bad IP", w, r, user)
return
}
- err = addModLog("activate", uid, "user", ipaddress, user.ID)
+ err = addModLog("activate", targetUser.ID, "user", ipaddress, user.ID)
if err != nil {
InternalError(err, w)
return
}
-
- err = users.Reload(uid)
- if err != nil {
- LocalError("This user no longer exists!", w, r, user)
- return
- }
- http.Redirect(w, r, "/user/"+strconv.Itoa(uid), http.StatusSeeOther)
+ http.Redirect(w, r, "/user/"+strconv.Itoa(targetUser.ID), http.StatusSeeOther)
}
diff --git a/images/cosmo-320px.PNG b/old-images/cosmo-320px.PNG
similarity index 100%
rename from images/cosmo-320px.PNG
rename to old-images/cosmo-320px.PNG
diff --git a/images/cosmo-alerts.png b/old-images/cosmo-alerts.png
similarity index 100%
rename from images/cosmo-alerts.png
rename to old-images/cosmo-alerts.png
diff --git a/images/cosmo-conflux.png b/old-images/cosmo-conflux.png
similarity index 100%
rename from images/cosmo-conflux.png
rename to old-images/cosmo-conflux.png
diff --git a/images/cosmo-profiles.PNG b/old-images/cosmo-profiles.PNG
similarity index 100%
rename from images/cosmo-profiles.PNG
rename to old-images/cosmo-profiles.PNG
diff --git a/images/cosmo.png b/old-images/cosmo.png
similarity index 100%
rename from images/cosmo.png
rename to old-images/cosmo.png
diff --git a/pages.go b/pages.go
index cd57ba56..723da854 100644
--- a/pages.go
+++ b/pages.go
@@ -1,12 +1,14 @@
package main
-//import "fmt"
-import "sync"
-import "bytes"
-import "strings"
-import "strconv"
-import "regexp"
-import "html/template"
+import (
+ //"fmt"
+ "bytes"
+ "html/template"
+ "regexp"
+ "strconv"
+ "strings"
+ "sync"
+)
type HeaderVars struct {
NoticeList []string
@@ -32,12 +34,8 @@ type PageWidgets struct {
RightSidebar template.HTML
}
-/*type UnsafeExtData struct
-{
- items map[string]interface{} // Key: pluginname
-}*/
-
// TODO: Add a ExtDataHolder interface with methods for manipulating the contents?
+// ? - Could we use a sync.Map instead?
type ExtData struct {
items map[string]interface{} // Key: pluginname
sync.RWMutex
diff --git a/panel_routes.go b/panel_routes.go
index aa3fbe10..14f10a53 100644
--- a/panel_routes.go
+++ b/panel_routes.go
@@ -1154,14 +1154,14 @@ func routePanelUsers(w http.ResponseWriter, r *http.Request, user User) {
// TODO: Add a UserStore method for iterating over global users and global user offsets
for rows.Next() {
- puser := User{ID: 0}
+ puser := &User{ID: 0}
err := rows.Scan(&puser.ID, &puser.Name, &puser.Group, &puser.Active, &puser.IsSuperAdmin, &puser.Avatar)
if err != nil {
InternalError(err, w)
return
}
- initUserPerms(&puser)
+ puser.initPerms()
if puser.Avatar != "" {
if puser.Avatar[0] == '.' {
puser.Avatar = "/uploads/avatar_" + strconv.Itoa(puser.ID) + puser.Avatar
@@ -1175,7 +1175,7 @@ func routePanelUsers(w http.ResponseWriter, r *http.Request, user User) {
} else {
puser.Tag = ""
}
- userList = append(userList, puser)
+ userList = append(userList, *puser)
}
err = rows.Err()
if err != nil {
@@ -1346,12 +1346,10 @@ func routePanelUsersEditSubmit(w http.ResponseWriter, r *http.Request, user User
SetPassword(targetUser.ID, newpassword)
}
- err = users.Reload(targetUser.ID)
- if err != nil {
- LocalError("This user no longer exists!", w, r, user)
- return
+ ucache, ok := users.(UserCache)
+ if ok {
+ ucache.CacheRemove(targetUser.ID)
}
-
http.Redirect(w, r, "/panel/users/edit/"+strconv.Itoa(targetUser.ID), http.StatusSeeOther)
}
diff --git a/public/font-awesome-4.7.0/css/font-awesome.css b/public/font-awesome-4.7.0/css/font-awesome.css
new file mode 100644
index 00000000..ee906a81
--- /dev/null
+++ b/public/font-awesome-4.7.0/css/font-awesome.css
@@ -0,0 +1,2337 @@
+/*!
+ * Font Awesome 4.7.0 by @davegandy - http://fontawesome.io - @fontawesome
+ * License - http://fontawesome.io/license (Font: SIL OFL 1.1, CSS: MIT License)
+ */
+/* FONT PATH
+ * -------------------------- */
+@font-face {
+ font-family: 'FontAwesome';
+ src: url('../fonts/fontawesome-webfont.eot?v=4.7.0');
+ src: url('../fonts/fontawesome-webfont.eot?#iefix&v=4.7.0') format('embedded-opentype'), url('../fonts/fontawesome-webfont.woff2?v=4.7.0') format('woff2'), url('../fonts/fontawesome-webfont.woff?v=4.7.0') format('woff'), url('../fonts/fontawesome-webfont.ttf?v=4.7.0') format('truetype'), url('../fonts/fontawesome-webfont.svg?v=4.7.0#fontawesomeregular') format('svg');
+ font-weight: normal;
+ font-style: normal;
+}
+.fa {
+ display: inline-block;
+ font: normal normal normal 14px/1 FontAwesome;
+ font-size: inherit;
+ text-rendering: auto;
+ -webkit-font-smoothing: antialiased;
+ -moz-osx-font-smoothing: grayscale;
+}
+/* makes the font 33% larger relative to the icon container */
+.fa-lg {
+ font-size: 1.33333333em;
+ line-height: 0.75em;
+ vertical-align: -15%;
+}
+.fa-2x {
+ font-size: 2em;
+}
+.fa-3x {
+ font-size: 3em;
+}
+.fa-4x {
+ font-size: 4em;
+}
+.fa-5x {
+ font-size: 5em;
+}
+.fa-fw {
+ width: 1.28571429em;
+ text-align: center;
+}
+.fa-ul {
+ padding-left: 0;
+ margin-left: 2.14285714em;
+ list-style-type: none;
+}
+.fa-ul > li {
+ position: relative;
+}
+.fa-li {
+ position: absolute;
+ left: -2.14285714em;
+ width: 2.14285714em;
+ top: 0.14285714em;
+ text-align: center;
+}
+.fa-li.fa-lg {
+ left: -1.85714286em;
+}
+.fa-border {
+ padding: .2em .25em .15em;
+ border: solid 0.08em #eeeeee;
+ border-radius: .1em;
+}
+.fa-pull-left {
+ float: left;
+}
+.fa-pull-right {
+ float: right;
+}
+.fa.fa-pull-left {
+ margin-right: .3em;
+}
+.fa.fa-pull-right {
+ margin-left: .3em;
+}
+/* Deprecated as of 4.4.0 */
+.pull-right {
+ float: right;
+}
+.pull-left {
+ float: left;
+}
+.fa.pull-left {
+ margin-right: .3em;
+}
+.fa.pull-right {
+ margin-left: .3em;
+}
+.fa-spin {
+ -webkit-animation: fa-spin 2s infinite linear;
+ animation: fa-spin 2s infinite linear;
+}
+.fa-pulse {
+ -webkit-animation: fa-spin 1s infinite steps(8);
+ animation: fa-spin 1s infinite steps(8);
+}
+@-webkit-keyframes fa-spin {
+ 0% {
+ -webkit-transform: rotate(0deg);
+ transform: rotate(0deg);
+ }
+ 100% {
+ -webkit-transform: rotate(359deg);
+ transform: rotate(359deg);
+ }
+}
+@keyframes fa-spin {
+ 0% {
+ -webkit-transform: rotate(0deg);
+ transform: rotate(0deg);
+ }
+ 100% {
+ -webkit-transform: rotate(359deg);
+ transform: rotate(359deg);
+ }
+}
+.fa-rotate-90 {
+ -ms-filter: "progid:DXImageTransform.Microsoft.BasicImage(rotation=1)";
+ -webkit-transform: rotate(90deg);
+ -ms-transform: rotate(90deg);
+ transform: rotate(90deg);
+}
+.fa-rotate-180 {
+ -ms-filter: "progid:DXImageTransform.Microsoft.BasicImage(rotation=2)";
+ -webkit-transform: rotate(180deg);
+ -ms-transform: rotate(180deg);
+ transform: rotate(180deg);
+}
+.fa-rotate-270 {
+ -ms-filter: "progid:DXImageTransform.Microsoft.BasicImage(rotation=3)";
+ -webkit-transform: rotate(270deg);
+ -ms-transform: rotate(270deg);
+ transform: rotate(270deg);
+}
+.fa-flip-horizontal {
+ -ms-filter: "progid:DXImageTransform.Microsoft.BasicImage(rotation=0, mirror=1)";
+ -webkit-transform: scale(-1, 1);
+ -ms-transform: scale(-1, 1);
+ transform: scale(-1, 1);
+}
+.fa-flip-vertical {
+ -ms-filter: "progid:DXImageTransform.Microsoft.BasicImage(rotation=2, mirror=1)";
+ -webkit-transform: scale(1, -1);
+ -ms-transform: scale(1, -1);
+ transform: scale(1, -1);
+}
+:root .fa-rotate-90,
+:root .fa-rotate-180,
+:root .fa-rotate-270,
+:root .fa-flip-horizontal,
+:root .fa-flip-vertical {
+ filter: none;
+}
+.fa-stack {
+ position: relative;
+ display: inline-block;
+ width: 2em;
+ height: 2em;
+ line-height: 2em;
+ vertical-align: middle;
+}
+.fa-stack-1x,
+.fa-stack-2x {
+ position: absolute;
+ left: 0;
+ width: 100%;
+ text-align: center;
+}
+.fa-stack-1x {
+ line-height: inherit;
+}
+.fa-stack-2x {
+ font-size: 2em;
+}
+.fa-inverse {
+ color: #ffffff;
+}
+/* Font Awesome uses the Unicode Private Use Area (PUA) to ensure screen
+ readers do not read off random characters that represent icons */
+.fa-glass:before {
+ content: "\f000";
+}
+.fa-music:before {
+ content: "\f001";
+}
+.fa-search:before {
+ content: "\f002";
+}
+.fa-envelope-o:before {
+ content: "\f003";
+}
+.fa-heart:before {
+ content: "\f004";
+}
+.fa-star:before {
+ content: "\f005";
+}
+.fa-star-o:before {
+ content: "\f006";
+}
+.fa-user:before {
+ content: "\f007";
+}
+.fa-film:before {
+ content: "\f008";
+}
+.fa-th-large:before {
+ content: "\f009";
+}
+.fa-th:before {
+ content: "\f00a";
+}
+.fa-th-list:before {
+ content: "\f00b";
+}
+.fa-check:before {
+ content: "\f00c";
+}
+.fa-remove:before,
+.fa-close:before,
+.fa-times:before {
+ content: "\f00d";
+}
+.fa-search-plus:before {
+ content: "\f00e";
+}
+.fa-search-minus:before {
+ content: "\f010";
+}
+.fa-power-off:before {
+ content: "\f011";
+}
+.fa-signal:before {
+ content: "\f012";
+}
+.fa-gear:before,
+.fa-cog:before {
+ content: "\f013";
+}
+.fa-trash-o:before {
+ content: "\f014";
+}
+.fa-home:before {
+ content: "\f015";
+}
+.fa-file-o:before {
+ content: "\f016";
+}
+.fa-clock-o:before {
+ content: "\f017";
+}
+.fa-road:before {
+ content: "\f018";
+}
+.fa-download:before {
+ content: "\f019";
+}
+.fa-arrow-circle-o-down:before {
+ content: "\f01a";
+}
+.fa-arrow-circle-o-up:before {
+ content: "\f01b";
+}
+.fa-inbox:before {
+ content: "\f01c";
+}
+.fa-play-circle-o:before {
+ content: "\f01d";
+}
+.fa-rotate-right:before,
+.fa-repeat:before {
+ content: "\f01e";
+}
+.fa-refresh:before {
+ content: "\f021";
+}
+.fa-list-alt:before {
+ content: "\f022";
+}
+.fa-lock:before {
+ content: "\f023";
+}
+.fa-flag:before {
+ content: "\f024";
+}
+.fa-headphones:before {
+ content: "\f025";
+}
+.fa-volume-off:before {
+ content: "\f026";
+}
+.fa-volume-down:before {
+ content: "\f027";
+}
+.fa-volume-up:before {
+ content: "\f028";
+}
+.fa-qrcode:before {
+ content: "\f029";
+}
+.fa-barcode:before {
+ content: "\f02a";
+}
+.fa-tag:before {
+ content: "\f02b";
+}
+.fa-tags:before {
+ content: "\f02c";
+}
+.fa-book:before {
+ content: "\f02d";
+}
+.fa-bookmark:before {
+ content: "\f02e";
+}
+.fa-print:before {
+ content: "\f02f";
+}
+.fa-camera:before {
+ content: "\f030";
+}
+.fa-font:before {
+ content: "\f031";
+}
+.fa-bold:before {
+ content: "\f032";
+}
+.fa-italic:before {
+ content: "\f033";
+}
+.fa-text-height:before {
+ content: "\f034";
+}
+.fa-text-width:before {
+ content: "\f035";
+}
+.fa-align-left:before {
+ content: "\f036";
+}
+.fa-align-center:before {
+ content: "\f037";
+}
+.fa-align-right:before {
+ content: "\f038";
+}
+.fa-align-justify:before {
+ content: "\f039";
+}
+.fa-list:before {
+ content: "\f03a";
+}
+.fa-dedent:before,
+.fa-outdent:before {
+ content: "\f03b";
+}
+.fa-indent:before {
+ content: "\f03c";
+}
+.fa-video-camera:before {
+ content: "\f03d";
+}
+.fa-photo:before,
+.fa-image:before,
+.fa-picture-o:before {
+ content: "\f03e";
+}
+.fa-pencil:before {
+ content: "\f040";
+}
+.fa-map-marker:before {
+ content: "\f041";
+}
+.fa-adjust:before {
+ content: "\f042";
+}
+.fa-tint:before {
+ content: "\f043";
+}
+.fa-edit:before,
+.fa-pencil-square-o:before {
+ content: "\f044";
+}
+.fa-share-square-o:before {
+ content: "\f045";
+}
+.fa-check-square-o:before {
+ content: "\f046";
+}
+.fa-arrows:before {
+ content: "\f047";
+}
+.fa-step-backward:before {
+ content: "\f048";
+}
+.fa-fast-backward:before {
+ content: "\f049";
+}
+.fa-backward:before {
+ content: "\f04a";
+}
+.fa-play:before {
+ content: "\f04b";
+}
+.fa-pause:before {
+ content: "\f04c";
+}
+.fa-stop:before {
+ content: "\f04d";
+}
+.fa-forward:before {
+ content: "\f04e";
+}
+.fa-fast-forward:before {
+ content: "\f050";
+}
+.fa-step-forward:before {
+ content: "\f051";
+}
+.fa-eject:before {
+ content: "\f052";
+}
+.fa-chevron-left:before {
+ content: "\f053";
+}
+.fa-chevron-right:before {
+ content: "\f054";
+}
+.fa-plus-circle:before {
+ content: "\f055";
+}
+.fa-minus-circle:before {
+ content: "\f056";
+}
+.fa-times-circle:before {
+ content: "\f057";
+}
+.fa-check-circle:before {
+ content: "\f058";
+}
+.fa-question-circle:before {
+ content: "\f059";
+}
+.fa-info-circle:before {
+ content: "\f05a";
+}
+.fa-crosshairs:before {
+ content: "\f05b";
+}
+.fa-times-circle-o:before {
+ content: "\f05c";
+}
+.fa-check-circle-o:before {
+ content: "\f05d";
+}
+.fa-ban:before {
+ content: "\f05e";
+}
+.fa-arrow-left:before {
+ content: "\f060";
+}
+.fa-arrow-right:before {
+ content: "\f061";
+}
+.fa-arrow-up:before {
+ content: "\f062";
+}
+.fa-arrow-down:before {
+ content: "\f063";
+}
+.fa-mail-forward:before,
+.fa-share:before {
+ content: "\f064";
+}
+.fa-expand:before {
+ content: "\f065";
+}
+.fa-compress:before {
+ content: "\f066";
+}
+.fa-plus:before {
+ content: "\f067";
+}
+.fa-minus:before {
+ content: "\f068";
+}
+.fa-asterisk:before {
+ content: "\f069";
+}
+.fa-exclamation-circle:before {
+ content: "\f06a";
+}
+.fa-gift:before {
+ content: "\f06b";
+}
+.fa-leaf:before {
+ content: "\f06c";
+}
+.fa-fire:before {
+ content: "\f06d";
+}
+.fa-eye:before {
+ content: "\f06e";
+}
+.fa-eye-slash:before {
+ content: "\f070";
+}
+.fa-warning:before,
+.fa-exclamation-triangle:before {
+ content: "\f071";
+}
+.fa-plane:before {
+ content: "\f072";
+}
+.fa-calendar:before {
+ content: "\f073";
+}
+.fa-random:before {
+ content: "\f074";
+}
+.fa-comment:before {
+ content: "\f075";
+}
+.fa-magnet:before {
+ content: "\f076";
+}
+.fa-chevron-up:before {
+ content: "\f077";
+}
+.fa-chevron-down:before {
+ content: "\f078";
+}
+.fa-retweet:before {
+ content: "\f079";
+}
+.fa-shopping-cart:before {
+ content: "\f07a";
+}
+.fa-folder:before {
+ content: "\f07b";
+}
+.fa-folder-open:before {
+ content: "\f07c";
+}
+.fa-arrows-v:before {
+ content: "\f07d";
+}
+.fa-arrows-h:before {
+ content: "\f07e";
+}
+.fa-bar-chart-o:before,
+.fa-bar-chart:before {
+ content: "\f080";
+}
+.fa-twitter-square:before {
+ content: "\f081";
+}
+.fa-facebook-square:before {
+ content: "\f082";
+}
+.fa-camera-retro:before {
+ content: "\f083";
+}
+.fa-key:before {
+ content: "\f084";
+}
+.fa-gears:before,
+.fa-cogs:before {
+ content: "\f085";
+}
+.fa-comments:before {
+ content: "\f086";
+}
+.fa-thumbs-o-up:before {
+ content: "\f087";
+}
+.fa-thumbs-o-down:before {
+ content: "\f088";
+}
+.fa-star-half:before {
+ content: "\f089";
+}
+.fa-heart-o:before {
+ content: "\f08a";
+}
+.fa-sign-out:before {
+ content: "\f08b";
+}
+.fa-linkedin-square:before {
+ content: "\f08c";
+}
+.fa-thumb-tack:before {
+ content: "\f08d";
+}
+.fa-external-link:before {
+ content: "\f08e";
+}
+.fa-sign-in:before {
+ content: "\f090";
+}
+.fa-trophy:before {
+ content: "\f091";
+}
+.fa-github-square:before {
+ content: "\f092";
+}
+.fa-upload:before {
+ content: "\f093";
+}
+.fa-lemon-o:before {
+ content: "\f094";
+}
+.fa-phone:before {
+ content: "\f095";
+}
+.fa-square-o:before {
+ content: "\f096";
+}
+.fa-bookmark-o:before {
+ content: "\f097";
+}
+.fa-phone-square:before {
+ content: "\f098";
+}
+.fa-twitter:before {
+ content: "\f099";
+}
+.fa-facebook-f:before,
+.fa-facebook:before {
+ content: "\f09a";
+}
+.fa-github:before {
+ content: "\f09b";
+}
+.fa-unlock:before {
+ content: "\f09c";
+}
+.fa-credit-card:before {
+ content: "\f09d";
+}
+.fa-feed:before,
+.fa-rss:before {
+ content: "\f09e";
+}
+.fa-hdd-o:before {
+ content: "\f0a0";
+}
+.fa-bullhorn:before {
+ content: "\f0a1";
+}
+.fa-bell:before {
+ content: "\f0f3";
+}
+.fa-certificate:before {
+ content: "\f0a3";
+}
+.fa-hand-o-right:before {
+ content: "\f0a4";
+}
+.fa-hand-o-left:before {
+ content: "\f0a5";
+}
+.fa-hand-o-up:before {
+ content: "\f0a6";
+}
+.fa-hand-o-down:before {
+ content: "\f0a7";
+}
+.fa-arrow-circle-left:before {
+ content: "\f0a8";
+}
+.fa-arrow-circle-right:before {
+ content: "\f0a9";
+}
+.fa-arrow-circle-up:before {
+ content: "\f0aa";
+}
+.fa-arrow-circle-down:before {
+ content: "\f0ab";
+}
+.fa-globe:before {
+ content: "\f0ac";
+}
+.fa-wrench:before {
+ content: "\f0ad";
+}
+.fa-tasks:before {
+ content: "\f0ae";
+}
+.fa-filter:before {
+ content: "\f0b0";
+}
+.fa-briefcase:before {
+ content: "\f0b1";
+}
+.fa-arrows-alt:before {
+ content: "\f0b2";
+}
+.fa-group:before,
+.fa-users:before {
+ content: "\f0c0";
+}
+.fa-chain:before,
+.fa-link:before {
+ content: "\f0c1";
+}
+.fa-cloud:before {
+ content: "\f0c2";
+}
+.fa-flask:before {
+ content: "\f0c3";
+}
+.fa-cut:before,
+.fa-scissors:before {
+ content: "\f0c4";
+}
+.fa-copy:before,
+.fa-files-o:before {
+ content: "\f0c5";
+}
+.fa-paperclip:before {
+ content: "\f0c6";
+}
+.fa-save:before,
+.fa-floppy-o:before {
+ content: "\f0c7";
+}
+.fa-square:before {
+ content: "\f0c8";
+}
+.fa-navicon:before,
+.fa-reorder:before,
+.fa-bars:before {
+ content: "\f0c9";
+}
+.fa-list-ul:before {
+ content: "\f0ca";
+}
+.fa-list-ol:before {
+ content: "\f0cb";
+}
+.fa-strikethrough:before {
+ content: "\f0cc";
+}
+.fa-underline:before {
+ content: "\f0cd";
+}
+.fa-table:before {
+ content: "\f0ce";
+}
+.fa-magic:before {
+ content: "\f0d0";
+}
+.fa-truck:before {
+ content: "\f0d1";
+}
+.fa-pinterest:before {
+ content: "\f0d2";
+}
+.fa-pinterest-square:before {
+ content: "\f0d3";
+}
+.fa-google-plus-square:before {
+ content: "\f0d4";
+}
+.fa-google-plus:before {
+ content: "\f0d5";
+}
+.fa-money:before {
+ content: "\f0d6";
+}
+.fa-caret-down:before {
+ content: "\f0d7";
+}
+.fa-caret-up:before {
+ content: "\f0d8";
+}
+.fa-caret-left:before {
+ content: "\f0d9";
+}
+.fa-caret-right:before {
+ content: "\f0da";
+}
+.fa-columns:before {
+ content: "\f0db";
+}
+.fa-unsorted:before,
+.fa-sort:before {
+ content: "\f0dc";
+}
+.fa-sort-down:before,
+.fa-sort-desc:before {
+ content: "\f0dd";
+}
+.fa-sort-up:before,
+.fa-sort-asc:before {
+ content: "\f0de";
+}
+.fa-envelope:before {
+ content: "\f0e0";
+}
+.fa-linkedin:before {
+ content: "\f0e1";
+}
+.fa-rotate-left:before,
+.fa-undo:before {
+ content: "\f0e2";
+}
+.fa-legal:before,
+.fa-gavel:before {
+ content: "\f0e3";
+}
+.fa-dashboard:before,
+.fa-tachometer:before {
+ content: "\f0e4";
+}
+.fa-comment-o:before {
+ content: "\f0e5";
+}
+.fa-comments-o:before {
+ content: "\f0e6";
+}
+.fa-flash:before,
+.fa-bolt:before {
+ content: "\f0e7";
+}
+.fa-sitemap:before {
+ content: "\f0e8";
+}
+.fa-umbrella:before {
+ content: "\f0e9";
+}
+.fa-paste:before,
+.fa-clipboard:before {
+ content: "\f0ea";
+}
+.fa-lightbulb-o:before {
+ content: "\f0eb";
+}
+.fa-exchange:before {
+ content: "\f0ec";
+}
+.fa-cloud-download:before {
+ content: "\f0ed";
+}
+.fa-cloud-upload:before {
+ content: "\f0ee";
+}
+.fa-user-md:before {
+ content: "\f0f0";
+}
+.fa-stethoscope:before {
+ content: "\f0f1";
+}
+.fa-suitcase:before {
+ content: "\f0f2";
+}
+.fa-bell-o:before {
+ content: "\f0a2";
+}
+.fa-coffee:before {
+ content: "\f0f4";
+}
+.fa-cutlery:before {
+ content: "\f0f5";
+}
+.fa-file-text-o:before {
+ content: "\f0f6";
+}
+.fa-building-o:before {
+ content: "\f0f7";
+}
+.fa-hospital-o:before {
+ content: "\f0f8";
+}
+.fa-ambulance:before {
+ content: "\f0f9";
+}
+.fa-medkit:before {
+ content: "\f0fa";
+}
+.fa-fighter-jet:before {
+ content: "\f0fb";
+}
+.fa-beer:before {
+ content: "\f0fc";
+}
+.fa-h-square:before {
+ content: "\f0fd";
+}
+.fa-plus-square:before {
+ content: "\f0fe";
+}
+.fa-angle-double-left:before {
+ content: "\f100";
+}
+.fa-angle-double-right:before {
+ content: "\f101";
+}
+.fa-angle-double-up:before {
+ content: "\f102";
+}
+.fa-angle-double-down:before {
+ content: "\f103";
+}
+.fa-angle-left:before {
+ content: "\f104";
+}
+.fa-angle-right:before {
+ content: "\f105";
+}
+.fa-angle-up:before {
+ content: "\f106";
+}
+.fa-angle-down:before {
+ content: "\f107";
+}
+.fa-desktop:before {
+ content: "\f108";
+}
+.fa-laptop:before {
+ content: "\f109";
+}
+.fa-tablet:before {
+ content: "\f10a";
+}
+.fa-mobile-phone:before,
+.fa-mobile:before {
+ content: "\f10b";
+}
+.fa-circle-o:before {
+ content: "\f10c";
+}
+.fa-quote-left:before {
+ content: "\f10d";
+}
+.fa-quote-right:before {
+ content: "\f10e";
+}
+.fa-spinner:before {
+ content: "\f110";
+}
+.fa-circle:before {
+ content: "\f111";
+}
+.fa-mail-reply:before,
+.fa-reply:before {
+ content: "\f112";
+}
+.fa-github-alt:before {
+ content: "\f113";
+}
+.fa-folder-o:before {
+ content: "\f114";
+}
+.fa-folder-open-o:before {
+ content: "\f115";
+}
+.fa-smile-o:before {
+ content: "\f118";
+}
+.fa-frown-o:before {
+ content: "\f119";
+}
+.fa-meh-o:before {
+ content: "\f11a";
+}
+.fa-gamepad:before {
+ content: "\f11b";
+}
+.fa-keyboard-o:before {
+ content: "\f11c";
+}
+.fa-flag-o:before {
+ content: "\f11d";
+}
+.fa-flag-checkered:before {
+ content: "\f11e";
+}
+.fa-terminal:before {
+ content: "\f120";
+}
+.fa-code:before {
+ content: "\f121";
+}
+.fa-mail-reply-all:before,
+.fa-reply-all:before {
+ content: "\f122";
+}
+.fa-star-half-empty:before,
+.fa-star-half-full:before,
+.fa-star-half-o:before {
+ content: "\f123";
+}
+.fa-location-arrow:before {
+ content: "\f124";
+}
+.fa-crop:before {
+ content: "\f125";
+}
+.fa-code-fork:before {
+ content: "\f126";
+}
+.fa-unlink:before,
+.fa-chain-broken:before {
+ content: "\f127";
+}
+.fa-question:before {
+ content: "\f128";
+}
+.fa-info:before {
+ content: "\f129";
+}
+.fa-exclamation:before {
+ content: "\f12a";
+}
+.fa-superscript:before {
+ content: "\f12b";
+}
+.fa-subscript:before {
+ content: "\f12c";
+}
+.fa-eraser:before {
+ content: "\f12d";
+}
+.fa-puzzle-piece:before {
+ content: "\f12e";
+}
+.fa-microphone:before {
+ content: "\f130";
+}
+.fa-microphone-slash:before {
+ content: "\f131";
+}
+.fa-shield:before {
+ content: "\f132";
+}
+.fa-calendar-o:before {
+ content: "\f133";
+}
+.fa-fire-extinguisher:before {
+ content: "\f134";
+}
+.fa-rocket:before {
+ content: "\f135";
+}
+.fa-maxcdn:before {
+ content: "\f136";
+}
+.fa-chevron-circle-left:before {
+ content: "\f137";
+}
+.fa-chevron-circle-right:before {
+ content: "\f138";
+}
+.fa-chevron-circle-up:before {
+ content: "\f139";
+}
+.fa-chevron-circle-down:before {
+ content: "\f13a";
+}
+.fa-html5:before {
+ content: "\f13b";
+}
+.fa-css3:before {
+ content: "\f13c";
+}
+.fa-anchor:before {
+ content: "\f13d";
+}
+.fa-unlock-alt:before {
+ content: "\f13e";
+}
+.fa-bullseye:before {
+ content: "\f140";
+}
+.fa-ellipsis-h:before {
+ content: "\f141";
+}
+.fa-ellipsis-v:before {
+ content: "\f142";
+}
+.fa-rss-square:before {
+ content: "\f143";
+}
+.fa-play-circle:before {
+ content: "\f144";
+}
+.fa-ticket:before {
+ content: "\f145";
+}
+.fa-minus-square:before {
+ content: "\f146";
+}
+.fa-minus-square-o:before {
+ content: "\f147";
+}
+.fa-level-up:before {
+ content: "\f148";
+}
+.fa-level-down:before {
+ content: "\f149";
+}
+.fa-check-square:before {
+ content: "\f14a";
+}
+.fa-pencil-square:before {
+ content: "\f14b";
+}
+.fa-external-link-square:before {
+ content: "\f14c";
+}
+.fa-share-square:before {
+ content: "\f14d";
+}
+.fa-compass:before {
+ content: "\f14e";
+}
+.fa-toggle-down:before,
+.fa-caret-square-o-down:before {
+ content: "\f150";
+}
+.fa-toggle-up:before,
+.fa-caret-square-o-up:before {
+ content: "\f151";
+}
+.fa-toggle-right:before,
+.fa-caret-square-o-right:before {
+ content: "\f152";
+}
+.fa-euro:before,
+.fa-eur:before {
+ content: "\f153";
+}
+.fa-gbp:before {
+ content: "\f154";
+}
+.fa-dollar:before,
+.fa-usd:before {
+ content: "\f155";
+}
+.fa-rupee:before,
+.fa-inr:before {
+ content: "\f156";
+}
+.fa-cny:before,
+.fa-rmb:before,
+.fa-yen:before,
+.fa-jpy:before {
+ content: "\f157";
+}
+.fa-ruble:before,
+.fa-rouble:before,
+.fa-rub:before {
+ content: "\f158";
+}
+.fa-won:before,
+.fa-krw:before {
+ content: "\f159";
+}
+.fa-bitcoin:before,
+.fa-btc:before {
+ content: "\f15a";
+}
+.fa-file:before {
+ content: "\f15b";
+}
+.fa-file-text:before {
+ content: "\f15c";
+}
+.fa-sort-alpha-asc:before {
+ content: "\f15d";
+}
+.fa-sort-alpha-desc:before {
+ content: "\f15e";
+}
+.fa-sort-amount-asc:before {
+ content: "\f160";
+}
+.fa-sort-amount-desc:before {
+ content: "\f161";
+}
+.fa-sort-numeric-asc:before {
+ content: "\f162";
+}
+.fa-sort-numeric-desc:before {
+ content: "\f163";
+}
+.fa-thumbs-up:before {
+ content: "\f164";
+}
+.fa-thumbs-down:before {
+ content: "\f165";
+}
+.fa-youtube-square:before {
+ content: "\f166";
+}
+.fa-youtube:before {
+ content: "\f167";
+}
+.fa-xing:before {
+ content: "\f168";
+}
+.fa-xing-square:before {
+ content: "\f169";
+}
+.fa-youtube-play:before {
+ content: "\f16a";
+}
+.fa-dropbox:before {
+ content: "\f16b";
+}
+.fa-stack-overflow:before {
+ content: "\f16c";
+}
+.fa-instagram:before {
+ content: "\f16d";
+}
+.fa-flickr:before {
+ content: "\f16e";
+}
+.fa-adn:before {
+ content: "\f170";
+}
+.fa-bitbucket:before {
+ content: "\f171";
+}
+.fa-bitbucket-square:before {
+ content: "\f172";
+}
+.fa-tumblr:before {
+ content: "\f173";
+}
+.fa-tumblr-square:before {
+ content: "\f174";
+}
+.fa-long-arrow-down:before {
+ content: "\f175";
+}
+.fa-long-arrow-up:before {
+ content: "\f176";
+}
+.fa-long-arrow-left:before {
+ content: "\f177";
+}
+.fa-long-arrow-right:before {
+ content: "\f178";
+}
+.fa-apple:before {
+ content: "\f179";
+}
+.fa-windows:before {
+ content: "\f17a";
+}
+.fa-android:before {
+ content: "\f17b";
+}
+.fa-linux:before {
+ content: "\f17c";
+}
+.fa-dribbble:before {
+ content: "\f17d";
+}
+.fa-skype:before {
+ content: "\f17e";
+}
+.fa-foursquare:before {
+ content: "\f180";
+}
+.fa-trello:before {
+ content: "\f181";
+}
+.fa-female:before {
+ content: "\f182";
+}
+.fa-male:before {
+ content: "\f183";
+}
+.fa-gittip:before,
+.fa-gratipay:before {
+ content: "\f184";
+}
+.fa-sun-o:before {
+ content: "\f185";
+}
+.fa-moon-o:before {
+ content: "\f186";
+}
+.fa-archive:before {
+ content: "\f187";
+}
+.fa-bug:before {
+ content: "\f188";
+}
+.fa-vk:before {
+ content: "\f189";
+}
+.fa-weibo:before {
+ content: "\f18a";
+}
+.fa-renren:before {
+ content: "\f18b";
+}
+.fa-pagelines:before {
+ content: "\f18c";
+}
+.fa-stack-exchange:before {
+ content: "\f18d";
+}
+.fa-arrow-circle-o-right:before {
+ content: "\f18e";
+}
+.fa-arrow-circle-o-left:before {
+ content: "\f190";
+}
+.fa-toggle-left:before,
+.fa-caret-square-o-left:before {
+ content: "\f191";
+}
+.fa-dot-circle-o:before {
+ content: "\f192";
+}
+.fa-wheelchair:before {
+ content: "\f193";
+}
+.fa-vimeo-square:before {
+ content: "\f194";
+}
+.fa-turkish-lira:before,
+.fa-try:before {
+ content: "\f195";
+}
+.fa-plus-square-o:before {
+ content: "\f196";
+}
+.fa-space-shuttle:before {
+ content: "\f197";
+}
+.fa-slack:before {
+ content: "\f198";
+}
+.fa-envelope-square:before {
+ content: "\f199";
+}
+.fa-wordpress:before {
+ content: "\f19a";
+}
+.fa-openid:before {
+ content: "\f19b";
+}
+.fa-institution:before,
+.fa-bank:before,
+.fa-university:before {
+ content: "\f19c";
+}
+.fa-mortar-board:before,
+.fa-graduation-cap:before {
+ content: "\f19d";
+}
+.fa-yahoo:before {
+ content: "\f19e";
+}
+.fa-google:before {
+ content: "\f1a0";
+}
+.fa-reddit:before {
+ content: "\f1a1";
+}
+.fa-reddit-square:before {
+ content: "\f1a2";
+}
+.fa-stumbleupon-circle:before {
+ content: "\f1a3";
+}
+.fa-stumbleupon:before {
+ content: "\f1a4";
+}
+.fa-delicious:before {
+ content: "\f1a5";
+}
+.fa-digg:before {
+ content: "\f1a6";
+}
+.fa-pied-piper-pp:before {
+ content: "\f1a7";
+}
+.fa-pied-piper-alt:before {
+ content: "\f1a8";
+}
+.fa-drupal:before {
+ content: "\f1a9";
+}
+.fa-joomla:before {
+ content: "\f1aa";
+}
+.fa-language:before {
+ content: "\f1ab";
+}
+.fa-fax:before {
+ content: "\f1ac";
+}
+.fa-building:before {
+ content: "\f1ad";
+}
+.fa-child:before {
+ content: "\f1ae";
+}
+.fa-paw:before {
+ content: "\f1b0";
+}
+.fa-spoon:before {
+ content: "\f1b1";
+}
+.fa-cube:before {
+ content: "\f1b2";
+}
+.fa-cubes:before {
+ content: "\f1b3";
+}
+.fa-behance:before {
+ content: "\f1b4";
+}
+.fa-behance-square:before {
+ content: "\f1b5";
+}
+.fa-steam:before {
+ content: "\f1b6";
+}
+.fa-steam-square:before {
+ content: "\f1b7";
+}
+.fa-recycle:before {
+ content: "\f1b8";
+}
+.fa-automobile:before,
+.fa-car:before {
+ content: "\f1b9";
+}
+.fa-cab:before,
+.fa-taxi:before {
+ content: "\f1ba";
+}
+.fa-tree:before {
+ content: "\f1bb";
+}
+.fa-spotify:before {
+ content: "\f1bc";
+}
+.fa-deviantart:before {
+ content: "\f1bd";
+}
+.fa-soundcloud:before {
+ content: "\f1be";
+}
+.fa-database:before {
+ content: "\f1c0";
+}
+.fa-file-pdf-o:before {
+ content: "\f1c1";
+}
+.fa-file-word-o:before {
+ content: "\f1c2";
+}
+.fa-file-excel-o:before {
+ content: "\f1c3";
+}
+.fa-file-powerpoint-o:before {
+ content: "\f1c4";
+}
+.fa-file-photo-o:before,
+.fa-file-picture-o:before,
+.fa-file-image-o:before {
+ content: "\f1c5";
+}
+.fa-file-zip-o:before,
+.fa-file-archive-o:before {
+ content: "\f1c6";
+}
+.fa-file-sound-o:before,
+.fa-file-audio-o:before {
+ content: "\f1c7";
+}
+.fa-file-movie-o:before,
+.fa-file-video-o:before {
+ content: "\f1c8";
+}
+.fa-file-code-o:before {
+ content: "\f1c9";
+}
+.fa-vine:before {
+ content: "\f1ca";
+}
+.fa-codepen:before {
+ content: "\f1cb";
+}
+.fa-jsfiddle:before {
+ content: "\f1cc";
+}
+.fa-life-bouy:before,
+.fa-life-buoy:before,
+.fa-life-saver:before,
+.fa-support:before,
+.fa-life-ring:before {
+ content: "\f1cd";
+}
+.fa-circle-o-notch:before {
+ content: "\f1ce";
+}
+.fa-ra:before,
+.fa-resistance:before,
+.fa-rebel:before {
+ content: "\f1d0";
+}
+.fa-ge:before,
+.fa-empire:before {
+ content: "\f1d1";
+}
+.fa-git-square:before {
+ content: "\f1d2";
+}
+.fa-git:before {
+ content: "\f1d3";
+}
+.fa-y-combinator-square:before,
+.fa-yc-square:before,
+.fa-hacker-news:before {
+ content: "\f1d4";
+}
+.fa-tencent-weibo:before {
+ content: "\f1d5";
+}
+.fa-qq:before {
+ content: "\f1d6";
+}
+.fa-wechat:before,
+.fa-weixin:before {
+ content: "\f1d7";
+}
+.fa-send:before,
+.fa-paper-plane:before {
+ content: "\f1d8";
+}
+.fa-send-o:before,
+.fa-paper-plane-o:before {
+ content: "\f1d9";
+}
+.fa-history:before {
+ content: "\f1da";
+}
+.fa-circle-thin:before {
+ content: "\f1db";
+}
+.fa-header:before {
+ content: "\f1dc";
+}
+.fa-paragraph:before {
+ content: "\f1dd";
+}
+.fa-sliders:before {
+ content: "\f1de";
+}
+.fa-share-alt:before {
+ content: "\f1e0";
+}
+.fa-share-alt-square:before {
+ content: "\f1e1";
+}
+.fa-bomb:before {
+ content: "\f1e2";
+}
+.fa-soccer-ball-o:before,
+.fa-futbol-o:before {
+ content: "\f1e3";
+}
+.fa-tty:before {
+ content: "\f1e4";
+}
+.fa-binoculars:before {
+ content: "\f1e5";
+}
+.fa-plug:before {
+ content: "\f1e6";
+}
+.fa-slideshare:before {
+ content: "\f1e7";
+}
+.fa-twitch:before {
+ content: "\f1e8";
+}
+.fa-yelp:before {
+ content: "\f1e9";
+}
+.fa-newspaper-o:before {
+ content: "\f1ea";
+}
+.fa-wifi:before {
+ content: "\f1eb";
+}
+.fa-calculator:before {
+ content: "\f1ec";
+}
+.fa-paypal:before {
+ content: "\f1ed";
+}
+.fa-google-wallet:before {
+ content: "\f1ee";
+}
+.fa-cc-visa:before {
+ content: "\f1f0";
+}
+.fa-cc-mastercard:before {
+ content: "\f1f1";
+}
+.fa-cc-discover:before {
+ content: "\f1f2";
+}
+.fa-cc-amex:before {
+ content: "\f1f3";
+}
+.fa-cc-paypal:before {
+ content: "\f1f4";
+}
+.fa-cc-stripe:before {
+ content: "\f1f5";
+}
+.fa-bell-slash:before {
+ content: "\f1f6";
+}
+.fa-bell-slash-o:before {
+ content: "\f1f7";
+}
+.fa-trash:before {
+ content: "\f1f8";
+}
+.fa-copyright:before {
+ content: "\f1f9";
+}
+.fa-at:before {
+ content: "\f1fa";
+}
+.fa-eyedropper:before {
+ content: "\f1fb";
+}
+.fa-paint-brush:before {
+ content: "\f1fc";
+}
+.fa-birthday-cake:before {
+ content: "\f1fd";
+}
+.fa-area-chart:before {
+ content: "\f1fe";
+}
+.fa-pie-chart:before {
+ content: "\f200";
+}
+.fa-line-chart:before {
+ content: "\f201";
+}
+.fa-lastfm:before {
+ content: "\f202";
+}
+.fa-lastfm-square:before {
+ content: "\f203";
+}
+.fa-toggle-off:before {
+ content: "\f204";
+}
+.fa-toggle-on:before {
+ content: "\f205";
+}
+.fa-bicycle:before {
+ content: "\f206";
+}
+.fa-bus:before {
+ content: "\f207";
+}
+.fa-ioxhost:before {
+ content: "\f208";
+}
+.fa-angellist:before {
+ content: "\f209";
+}
+.fa-cc:before {
+ content: "\f20a";
+}
+.fa-shekel:before,
+.fa-sheqel:before,
+.fa-ils:before {
+ content: "\f20b";
+}
+.fa-meanpath:before {
+ content: "\f20c";
+}
+.fa-buysellads:before {
+ content: "\f20d";
+}
+.fa-connectdevelop:before {
+ content: "\f20e";
+}
+.fa-dashcube:before {
+ content: "\f210";
+}
+.fa-forumbee:before {
+ content: "\f211";
+}
+.fa-leanpub:before {
+ content: "\f212";
+}
+.fa-sellsy:before {
+ content: "\f213";
+}
+.fa-shirtsinbulk:before {
+ content: "\f214";
+}
+.fa-simplybuilt:before {
+ content: "\f215";
+}
+.fa-skyatlas:before {
+ content: "\f216";
+}
+.fa-cart-plus:before {
+ content: "\f217";
+}
+.fa-cart-arrow-down:before {
+ content: "\f218";
+}
+.fa-diamond:before {
+ content: "\f219";
+}
+.fa-ship:before {
+ content: "\f21a";
+}
+.fa-user-secret:before {
+ content: "\f21b";
+}
+.fa-motorcycle:before {
+ content: "\f21c";
+}
+.fa-street-view:before {
+ content: "\f21d";
+}
+.fa-heartbeat:before {
+ content: "\f21e";
+}
+.fa-venus:before {
+ content: "\f221";
+}
+.fa-mars:before {
+ content: "\f222";
+}
+.fa-mercury:before {
+ content: "\f223";
+}
+.fa-intersex:before,
+.fa-transgender:before {
+ content: "\f224";
+}
+.fa-transgender-alt:before {
+ content: "\f225";
+}
+.fa-venus-double:before {
+ content: "\f226";
+}
+.fa-mars-double:before {
+ content: "\f227";
+}
+.fa-venus-mars:before {
+ content: "\f228";
+}
+.fa-mars-stroke:before {
+ content: "\f229";
+}
+.fa-mars-stroke-v:before {
+ content: "\f22a";
+}
+.fa-mars-stroke-h:before {
+ content: "\f22b";
+}
+.fa-neuter:before {
+ content: "\f22c";
+}
+.fa-genderless:before {
+ content: "\f22d";
+}
+.fa-facebook-official:before {
+ content: "\f230";
+}
+.fa-pinterest-p:before {
+ content: "\f231";
+}
+.fa-whatsapp:before {
+ content: "\f232";
+}
+.fa-server:before {
+ content: "\f233";
+}
+.fa-user-plus:before {
+ content: "\f234";
+}
+.fa-user-times:before {
+ content: "\f235";
+}
+.fa-hotel:before,
+.fa-bed:before {
+ content: "\f236";
+}
+.fa-viacoin:before {
+ content: "\f237";
+}
+.fa-train:before {
+ content: "\f238";
+}
+.fa-subway:before {
+ content: "\f239";
+}
+.fa-medium:before {
+ content: "\f23a";
+}
+.fa-yc:before,
+.fa-y-combinator:before {
+ content: "\f23b";
+}
+.fa-optin-monster:before {
+ content: "\f23c";
+}
+.fa-opencart:before {
+ content: "\f23d";
+}
+.fa-expeditedssl:before {
+ content: "\f23e";
+}
+.fa-battery-4:before,
+.fa-battery:before,
+.fa-battery-full:before {
+ content: "\f240";
+}
+.fa-battery-3:before,
+.fa-battery-three-quarters:before {
+ content: "\f241";
+}
+.fa-battery-2:before,
+.fa-battery-half:before {
+ content: "\f242";
+}
+.fa-battery-1:before,
+.fa-battery-quarter:before {
+ content: "\f243";
+}
+.fa-battery-0:before,
+.fa-battery-empty:before {
+ content: "\f244";
+}
+.fa-mouse-pointer:before {
+ content: "\f245";
+}
+.fa-i-cursor:before {
+ content: "\f246";
+}
+.fa-object-group:before {
+ content: "\f247";
+}
+.fa-object-ungroup:before {
+ content: "\f248";
+}
+.fa-sticky-note:before {
+ content: "\f249";
+}
+.fa-sticky-note-o:before {
+ content: "\f24a";
+}
+.fa-cc-jcb:before {
+ content: "\f24b";
+}
+.fa-cc-diners-club:before {
+ content: "\f24c";
+}
+.fa-clone:before {
+ content: "\f24d";
+}
+.fa-balance-scale:before {
+ content: "\f24e";
+}
+.fa-hourglass-o:before {
+ content: "\f250";
+}
+.fa-hourglass-1:before,
+.fa-hourglass-start:before {
+ content: "\f251";
+}
+.fa-hourglass-2:before,
+.fa-hourglass-half:before {
+ content: "\f252";
+}
+.fa-hourglass-3:before,
+.fa-hourglass-end:before {
+ content: "\f253";
+}
+.fa-hourglass:before {
+ content: "\f254";
+}
+.fa-hand-grab-o:before,
+.fa-hand-rock-o:before {
+ content: "\f255";
+}
+.fa-hand-stop-o:before,
+.fa-hand-paper-o:before {
+ content: "\f256";
+}
+.fa-hand-scissors-o:before {
+ content: "\f257";
+}
+.fa-hand-lizard-o:before {
+ content: "\f258";
+}
+.fa-hand-spock-o:before {
+ content: "\f259";
+}
+.fa-hand-pointer-o:before {
+ content: "\f25a";
+}
+.fa-hand-peace-o:before {
+ content: "\f25b";
+}
+.fa-trademark:before {
+ content: "\f25c";
+}
+.fa-registered:before {
+ content: "\f25d";
+}
+.fa-creative-commons:before {
+ content: "\f25e";
+}
+.fa-gg:before {
+ content: "\f260";
+}
+.fa-gg-circle:before {
+ content: "\f261";
+}
+.fa-tripadvisor:before {
+ content: "\f262";
+}
+.fa-odnoklassniki:before {
+ content: "\f263";
+}
+.fa-odnoklassniki-square:before {
+ content: "\f264";
+}
+.fa-get-pocket:before {
+ content: "\f265";
+}
+.fa-wikipedia-w:before {
+ content: "\f266";
+}
+.fa-safari:before {
+ content: "\f267";
+}
+.fa-chrome:before {
+ content: "\f268";
+}
+.fa-firefox:before {
+ content: "\f269";
+}
+.fa-opera:before {
+ content: "\f26a";
+}
+.fa-internet-explorer:before {
+ content: "\f26b";
+}
+.fa-tv:before,
+.fa-television:before {
+ content: "\f26c";
+}
+.fa-contao:before {
+ content: "\f26d";
+}
+.fa-500px:before {
+ content: "\f26e";
+}
+.fa-amazon:before {
+ content: "\f270";
+}
+.fa-calendar-plus-o:before {
+ content: "\f271";
+}
+.fa-calendar-minus-o:before {
+ content: "\f272";
+}
+.fa-calendar-times-o:before {
+ content: "\f273";
+}
+.fa-calendar-check-o:before {
+ content: "\f274";
+}
+.fa-industry:before {
+ content: "\f275";
+}
+.fa-map-pin:before {
+ content: "\f276";
+}
+.fa-map-signs:before {
+ content: "\f277";
+}
+.fa-map-o:before {
+ content: "\f278";
+}
+.fa-map:before {
+ content: "\f279";
+}
+.fa-commenting:before {
+ content: "\f27a";
+}
+.fa-commenting-o:before {
+ content: "\f27b";
+}
+.fa-houzz:before {
+ content: "\f27c";
+}
+.fa-vimeo:before {
+ content: "\f27d";
+}
+.fa-black-tie:before {
+ content: "\f27e";
+}
+.fa-fonticons:before {
+ content: "\f280";
+}
+.fa-reddit-alien:before {
+ content: "\f281";
+}
+.fa-edge:before {
+ content: "\f282";
+}
+.fa-credit-card-alt:before {
+ content: "\f283";
+}
+.fa-codiepie:before {
+ content: "\f284";
+}
+.fa-modx:before {
+ content: "\f285";
+}
+.fa-fort-awesome:before {
+ content: "\f286";
+}
+.fa-usb:before {
+ content: "\f287";
+}
+.fa-product-hunt:before {
+ content: "\f288";
+}
+.fa-mixcloud:before {
+ content: "\f289";
+}
+.fa-scribd:before {
+ content: "\f28a";
+}
+.fa-pause-circle:before {
+ content: "\f28b";
+}
+.fa-pause-circle-o:before {
+ content: "\f28c";
+}
+.fa-stop-circle:before {
+ content: "\f28d";
+}
+.fa-stop-circle-o:before {
+ content: "\f28e";
+}
+.fa-shopping-bag:before {
+ content: "\f290";
+}
+.fa-shopping-basket:before {
+ content: "\f291";
+}
+.fa-hashtag:before {
+ content: "\f292";
+}
+.fa-bluetooth:before {
+ content: "\f293";
+}
+.fa-bluetooth-b:before {
+ content: "\f294";
+}
+.fa-percent:before {
+ content: "\f295";
+}
+.fa-gitlab:before {
+ content: "\f296";
+}
+.fa-wpbeginner:before {
+ content: "\f297";
+}
+.fa-wpforms:before {
+ content: "\f298";
+}
+.fa-envira:before {
+ content: "\f299";
+}
+.fa-universal-access:before {
+ content: "\f29a";
+}
+.fa-wheelchair-alt:before {
+ content: "\f29b";
+}
+.fa-question-circle-o:before {
+ content: "\f29c";
+}
+.fa-blind:before {
+ content: "\f29d";
+}
+.fa-audio-description:before {
+ content: "\f29e";
+}
+.fa-volume-control-phone:before {
+ content: "\f2a0";
+}
+.fa-braille:before {
+ content: "\f2a1";
+}
+.fa-assistive-listening-systems:before {
+ content: "\f2a2";
+}
+.fa-asl-interpreting:before,
+.fa-american-sign-language-interpreting:before {
+ content: "\f2a3";
+}
+.fa-deafness:before,
+.fa-hard-of-hearing:before,
+.fa-deaf:before {
+ content: "\f2a4";
+}
+.fa-glide:before {
+ content: "\f2a5";
+}
+.fa-glide-g:before {
+ content: "\f2a6";
+}
+.fa-signing:before,
+.fa-sign-language:before {
+ content: "\f2a7";
+}
+.fa-low-vision:before {
+ content: "\f2a8";
+}
+.fa-viadeo:before {
+ content: "\f2a9";
+}
+.fa-viadeo-square:before {
+ content: "\f2aa";
+}
+.fa-snapchat:before {
+ content: "\f2ab";
+}
+.fa-snapchat-ghost:before {
+ content: "\f2ac";
+}
+.fa-snapchat-square:before {
+ content: "\f2ad";
+}
+.fa-pied-piper:before {
+ content: "\f2ae";
+}
+.fa-first-order:before {
+ content: "\f2b0";
+}
+.fa-yoast:before {
+ content: "\f2b1";
+}
+.fa-themeisle:before {
+ content: "\f2b2";
+}
+.fa-google-plus-circle:before,
+.fa-google-plus-official:before {
+ content: "\f2b3";
+}
+.fa-fa:before,
+.fa-font-awesome:before {
+ content: "\f2b4";
+}
+.fa-handshake-o:before {
+ content: "\f2b5";
+}
+.fa-envelope-open:before {
+ content: "\f2b6";
+}
+.fa-envelope-open-o:before {
+ content: "\f2b7";
+}
+.fa-linode:before {
+ content: "\f2b8";
+}
+.fa-address-book:before {
+ content: "\f2b9";
+}
+.fa-address-book-o:before {
+ content: "\f2ba";
+}
+.fa-vcard:before,
+.fa-address-card:before {
+ content: "\f2bb";
+}
+.fa-vcard-o:before,
+.fa-address-card-o:before {
+ content: "\f2bc";
+}
+.fa-user-circle:before {
+ content: "\f2bd";
+}
+.fa-user-circle-o:before {
+ content: "\f2be";
+}
+.fa-user-o:before {
+ content: "\f2c0";
+}
+.fa-id-badge:before {
+ content: "\f2c1";
+}
+.fa-drivers-license:before,
+.fa-id-card:before {
+ content: "\f2c2";
+}
+.fa-drivers-license-o:before,
+.fa-id-card-o:before {
+ content: "\f2c3";
+}
+.fa-quora:before {
+ content: "\f2c4";
+}
+.fa-free-code-camp:before {
+ content: "\f2c5";
+}
+.fa-telegram:before {
+ content: "\f2c6";
+}
+.fa-thermometer-4:before,
+.fa-thermometer:before,
+.fa-thermometer-full:before {
+ content: "\f2c7";
+}
+.fa-thermometer-3:before,
+.fa-thermometer-three-quarters:before {
+ content: "\f2c8";
+}
+.fa-thermometer-2:before,
+.fa-thermometer-half:before {
+ content: "\f2c9";
+}
+.fa-thermometer-1:before,
+.fa-thermometer-quarter:before {
+ content: "\f2ca";
+}
+.fa-thermometer-0:before,
+.fa-thermometer-empty:before {
+ content: "\f2cb";
+}
+.fa-shower:before {
+ content: "\f2cc";
+}
+.fa-bathtub:before,
+.fa-s15:before,
+.fa-bath:before {
+ content: "\f2cd";
+}
+.fa-podcast:before {
+ content: "\f2ce";
+}
+.fa-window-maximize:before {
+ content: "\f2d0";
+}
+.fa-window-minimize:before {
+ content: "\f2d1";
+}
+.fa-window-restore:before {
+ content: "\f2d2";
+}
+.fa-times-rectangle:before,
+.fa-window-close:before {
+ content: "\f2d3";
+}
+.fa-times-rectangle-o:before,
+.fa-window-close-o:before {
+ content: "\f2d4";
+}
+.fa-bandcamp:before {
+ content: "\f2d5";
+}
+.fa-grav:before {
+ content: "\f2d6";
+}
+.fa-etsy:before {
+ content: "\f2d7";
+}
+.fa-imdb:before {
+ content: "\f2d8";
+}
+.fa-ravelry:before {
+ content: "\f2d9";
+}
+.fa-eercast:before {
+ content: "\f2da";
+}
+.fa-microchip:before {
+ content: "\f2db";
+}
+.fa-snowflake-o:before {
+ content: "\f2dc";
+}
+.fa-superpowers:before {
+ content: "\f2dd";
+}
+.fa-wpexplorer:before {
+ content: "\f2de";
+}
+.fa-meetup:before {
+ content: "\f2e0";
+}
+.sr-only {
+ position: absolute;
+ width: 1px;
+ height: 1px;
+ padding: 0;
+ margin: -1px;
+ overflow: hidden;
+ clip: rect(0, 0, 0, 0);
+ border: 0;
+}
+.sr-only-focusable:active,
+.sr-only-focusable:focus {
+ position: static;
+ width: auto;
+ height: auto;
+ margin: 0;
+ overflow: visible;
+ clip: auto;
+}
diff --git a/public/font-awesome-4.7.0/css/font-awesome.min.css b/public/font-awesome-4.7.0/css/font-awesome.min.css
new file mode 100644
index 00000000..540440ce
--- /dev/null
+++ b/public/font-awesome-4.7.0/css/font-awesome.min.css
@@ -0,0 +1,4 @@
+/*!
+ * Font Awesome 4.7.0 by @davegandy - http://fontawesome.io - @fontawesome
+ * License - http://fontawesome.io/license (Font: SIL OFL 1.1, CSS: MIT License)
+ */@font-face{font-family:'FontAwesome';src:url('../fonts/fontawesome-webfont.eot?v=4.7.0');src:url('../fonts/fontawesome-webfont.eot?#iefix&v=4.7.0') format('embedded-opentype'),url('../fonts/fontawesome-webfont.woff2?v=4.7.0') format('woff2'),url('../fonts/fontawesome-webfont.woff?v=4.7.0') format('woff'),url('../fonts/fontawesome-webfont.ttf?v=4.7.0') format('truetype'),url('../fonts/fontawesome-webfont.svg?v=4.7.0#fontawesomeregular') format('svg');font-weight:normal;font-style:normal}.fa{display:inline-block;font:normal normal normal 14px/1 FontAwesome;font-size:inherit;text-rendering:auto;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.fa-lg{font-size:1.33333333em;line-height:.75em;vertical-align:-15%}.fa-2x{font-size:2em}.fa-3x{font-size:3em}.fa-4x{font-size:4em}.fa-5x{font-size:5em}.fa-fw{width:1.28571429em;text-align:center}.fa-ul{padding-left:0;margin-left:2.14285714em;list-style-type:none}.fa-ul>li{position:relative}.fa-li{position:absolute;left:-2.14285714em;width:2.14285714em;top:.14285714em;text-align:center}.fa-li.fa-lg{left:-1.85714286em}.fa-border{padding:.2em .25em .15em;border:solid .08em #eee;border-radius:.1em}.fa-pull-left{float:left}.fa-pull-right{float:right}.fa.fa-pull-left{margin-right:.3em}.fa.fa-pull-right{margin-left:.3em}.pull-right{float:right}.pull-left{float:left}.fa.pull-left{margin-right:.3em}.fa.pull-right{margin-left:.3em}.fa-spin{-webkit-animation:fa-spin 2s infinite linear;animation:fa-spin 2s infinite linear}.fa-pulse{-webkit-animation:fa-spin 1s infinite steps(8);animation:fa-spin 1s infinite steps(8)}@-webkit-keyframes fa-spin{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}100%{-webkit-transform:rotate(359deg);transform:rotate(359deg)}}@keyframes fa-spin{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}100%{-webkit-transform:rotate(359deg);transform:rotate(359deg)}}.fa-rotate-90{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=1)";-webkit-transform:rotate(90deg);-ms-transform:rotate(90deg);transform:rotate(90deg)}.fa-rotate-180{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=2)";-webkit-transform:rotate(180deg);-ms-transform:rotate(180deg);transform:rotate(180deg)}.fa-rotate-270{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=3)";-webkit-transform:rotate(270deg);-ms-transform:rotate(270deg);transform:rotate(270deg)}.fa-flip-horizontal{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=0, mirror=1)";-webkit-transform:scale(-1, 1);-ms-transform:scale(-1, 1);transform:scale(-1, 1)}.fa-flip-vertical{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=2, mirror=1)";-webkit-transform:scale(1, -1);-ms-transform:scale(1, -1);transform:scale(1, -1)}:root .fa-rotate-90,:root .fa-rotate-180,:root .fa-rotate-270,:root .fa-flip-horizontal,:root .fa-flip-vertical{filter:none}.fa-stack{position:relative;display:inline-block;width:2em;height:2em;line-height:2em;vertical-align:middle}.fa-stack-1x,.fa-stack-2x{position:absolute;left:0;width:100%;text-align:center}.fa-stack-1x{line-height:inherit}.fa-stack-2x{font-size:2em}.fa-inverse{color:#fff}.fa-glass:before{content:"\f000"}.fa-music:before{content:"\f001"}.fa-search:before{content:"\f002"}.fa-envelope-o:before{content:"\f003"}.fa-heart:before{content:"\f004"}.fa-star:before{content:"\f005"}.fa-star-o:before{content:"\f006"}.fa-user:before{content:"\f007"}.fa-film:before{content:"\f008"}.fa-th-large:before{content:"\f009"}.fa-th:before{content:"\f00a"}.fa-th-list:before{content:"\f00b"}.fa-check:before{content:"\f00c"}.fa-remove:before,.fa-close:before,.fa-times:before{content:"\f00d"}.fa-search-plus:before{content:"\f00e"}.fa-search-minus:before{content:"\f010"}.fa-power-off:before{content:"\f011"}.fa-signal:before{content:"\f012"}.fa-gear:before,.fa-cog:before{content:"\f013"}.fa-trash-o:before{content:"\f014"}.fa-home:before{content:"\f015"}.fa-file-o:before{content:"\f016"}.fa-clock-o:before{content:"\f017"}.fa-road:before{content:"\f018"}.fa-download:before{content:"\f019"}.fa-arrow-circle-o-down:before{content:"\f01a"}.fa-arrow-circle-o-up:before{content:"\f01b"}.fa-inbox:before{content:"\f01c"}.fa-play-circle-o:before{content:"\f01d"}.fa-rotate-right:before,.fa-repeat:before{content:"\f01e"}.fa-refresh:before{content:"\f021"}.fa-list-alt:before{content:"\f022"}.fa-lock:before{content:"\f023"}.fa-flag:before{content:"\f024"}.fa-headphones:before{content:"\f025"}.fa-volume-off:before{content:"\f026"}.fa-volume-down:before{content:"\f027"}.fa-volume-up:before{content:"\f028"}.fa-qrcode:before{content:"\f029"}.fa-barcode:before{content:"\f02a"}.fa-tag:before{content:"\f02b"}.fa-tags:before{content:"\f02c"}.fa-book:before{content:"\f02d"}.fa-bookmark:before{content:"\f02e"}.fa-print:before{content:"\f02f"}.fa-camera:before{content:"\f030"}.fa-font:before{content:"\f031"}.fa-bold:before{content:"\f032"}.fa-italic:before{content:"\f033"}.fa-text-height:before{content:"\f034"}.fa-text-width:before{content:"\f035"}.fa-align-left:before{content:"\f036"}.fa-align-center:before{content:"\f037"}.fa-align-right:before{content:"\f038"}.fa-align-justify:before{content:"\f039"}.fa-list:before{content:"\f03a"}.fa-dedent:before,.fa-outdent:before{content:"\f03b"}.fa-indent:before{content:"\f03c"}.fa-video-camera:before{content:"\f03d"}.fa-photo:before,.fa-image:before,.fa-picture-o:before{content:"\f03e"}.fa-pencil:before{content:"\f040"}.fa-map-marker:before{content:"\f041"}.fa-adjust:before{content:"\f042"}.fa-tint:before{content:"\f043"}.fa-edit:before,.fa-pencil-square-o:before{content:"\f044"}.fa-share-square-o:before{content:"\f045"}.fa-check-square-o:before{content:"\f046"}.fa-arrows:before{content:"\f047"}.fa-step-backward:before{content:"\f048"}.fa-fast-backward:before{content:"\f049"}.fa-backward:before{content:"\f04a"}.fa-play:before{content:"\f04b"}.fa-pause:before{content:"\f04c"}.fa-stop:before{content:"\f04d"}.fa-forward:before{content:"\f04e"}.fa-fast-forward:before{content:"\f050"}.fa-step-forward:before{content:"\f051"}.fa-eject:before{content:"\f052"}.fa-chevron-left:before{content:"\f053"}.fa-chevron-right:before{content:"\f054"}.fa-plus-circle:before{content:"\f055"}.fa-minus-circle:before{content:"\f056"}.fa-times-circle:before{content:"\f057"}.fa-check-circle:before{content:"\f058"}.fa-question-circle:before{content:"\f059"}.fa-info-circle:before{content:"\f05a"}.fa-crosshairs:before{content:"\f05b"}.fa-times-circle-o:before{content:"\f05c"}.fa-check-circle-o:before{content:"\f05d"}.fa-ban:before{content:"\f05e"}.fa-arrow-left:before{content:"\f060"}.fa-arrow-right:before{content:"\f061"}.fa-arrow-up:before{content:"\f062"}.fa-arrow-down:before{content:"\f063"}.fa-mail-forward:before,.fa-share:before{content:"\f064"}.fa-expand:before{content:"\f065"}.fa-compress:before{content:"\f066"}.fa-plus:before{content:"\f067"}.fa-minus:before{content:"\f068"}.fa-asterisk:before{content:"\f069"}.fa-exclamation-circle:before{content:"\f06a"}.fa-gift:before{content:"\f06b"}.fa-leaf:before{content:"\f06c"}.fa-fire:before{content:"\f06d"}.fa-eye:before{content:"\f06e"}.fa-eye-slash:before{content:"\f070"}.fa-warning:before,.fa-exclamation-triangle:before{content:"\f071"}.fa-plane:before{content:"\f072"}.fa-calendar:before{content:"\f073"}.fa-random:before{content:"\f074"}.fa-comment:before{content:"\f075"}.fa-magnet:before{content:"\f076"}.fa-chevron-up:before{content:"\f077"}.fa-chevron-down:before{content:"\f078"}.fa-retweet:before{content:"\f079"}.fa-shopping-cart:before{content:"\f07a"}.fa-folder:before{content:"\f07b"}.fa-folder-open:before{content:"\f07c"}.fa-arrows-v:before{content:"\f07d"}.fa-arrows-h:before{content:"\f07e"}.fa-bar-chart-o:before,.fa-bar-chart:before{content:"\f080"}.fa-twitter-square:before{content:"\f081"}.fa-facebook-square:before{content:"\f082"}.fa-camera-retro:before{content:"\f083"}.fa-key:before{content:"\f084"}.fa-gears:before,.fa-cogs:before{content:"\f085"}.fa-comments:before{content:"\f086"}.fa-thumbs-o-up:before{content:"\f087"}.fa-thumbs-o-down:before{content:"\f088"}.fa-star-half:before{content:"\f089"}.fa-heart-o:before{content:"\f08a"}.fa-sign-out:before{content:"\f08b"}.fa-linkedin-square:before{content:"\f08c"}.fa-thumb-tack:before{content:"\f08d"}.fa-external-link:before{content:"\f08e"}.fa-sign-in:before{content:"\f090"}.fa-trophy:before{content:"\f091"}.fa-github-square:before{content:"\f092"}.fa-upload:before{content:"\f093"}.fa-lemon-o:before{content:"\f094"}.fa-phone:before{content:"\f095"}.fa-square-o:before{content:"\f096"}.fa-bookmark-o:before{content:"\f097"}.fa-phone-square:before{content:"\f098"}.fa-twitter:before{content:"\f099"}.fa-facebook-f:before,.fa-facebook:before{content:"\f09a"}.fa-github:before{content:"\f09b"}.fa-unlock:before{content:"\f09c"}.fa-credit-card:before{content:"\f09d"}.fa-feed:before,.fa-rss:before{content:"\f09e"}.fa-hdd-o:before{content:"\f0a0"}.fa-bullhorn:before{content:"\f0a1"}.fa-bell:before{content:"\f0f3"}.fa-certificate:before{content:"\f0a3"}.fa-hand-o-right:before{content:"\f0a4"}.fa-hand-o-left:before{content:"\f0a5"}.fa-hand-o-up:before{content:"\f0a6"}.fa-hand-o-down:before{content:"\f0a7"}.fa-arrow-circle-left:before{content:"\f0a8"}.fa-arrow-circle-right:before{content:"\f0a9"}.fa-arrow-circle-up:before{content:"\f0aa"}.fa-arrow-circle-down:before{content:"\f0ab"}.fa-globe:before{content:"\f0ac"}.fa-wrench:before{content:"\f0ad"}.fa-tasks:before{content:"\f0ae"}.fa-filter:before{content:"\f0b0"}.fa-briefcase:before{content:"\f0b1"}.fa-arrows-alt:before{content:"\f0b2"}.fa-group:before,.fa-users:before{content:"\f0c0"}.fa-chain:before,.fa-link:before{content:"\f0c1"}.fa-cloud:before{content:"\f0c2"}.fa-flask:before{content:"\f0c3"}.fa-cut:before,.fa-scissors:before{content:"\f0c4"}.fa-copy:before,.fa-files-o:before{content:"\f0c5"}.fa-paperclip:before{content:"\f0c6"}.fa-save:before,.fa-floppy-o:before{content:"\f0c7"}.fa-square:before{content:"\f0c8"}.fa-navicon:before,.fa-reorder:before,.fa-bars:before{content:"\f0c9"}.fa-list-ul:before{content:"\f0ca"}.fa-list-ol:before{content:"\f0cb"}.fa-strikethrough:before{content:"\f0cc"}.fa-underline:before{content:"\f0cd"}.fa-table:before{content:"\f0ce"}.fa-magic:before{content:"\f0d0"}.fa-truck:before{content:"\f0d1"}.fa-pinterest:before{content:"\f0d2"}.fa-pinterest-square:before{content:"\f0d3"}.fa-google-plus-square:before{content:"\f0d4"}.fa-google-plus:before{content:"\f0d5"}.fa-money:before{content:"\f0d6"}.fa-caret-down:before{content:"\f0d7"}.fa-caret-up:before{content:"\f0d8"}.fa-caret-left:before{content:"\f0d9"}.fa-caret-right:before{content:"\f0da"}.fa-columns:before{content:"\f0db"}.fa-unsorted:before,.fa-sort:before{content:"\f0dc"}.fa-sort-down:before,.fa-sort-desc:before{content:"\f0dd"}.fa-sort-up:before,.fa-sort-asc:before{content:"\f0de"}.fa-envelope:before{content:"\f0e0"}.fa-linkedin:before{content:"\f0e1"}.fa-rotate-left:before,.fa-undo:before{content:"\f0e2"}.fa-legal:before,.fa-gavel:before{content:"\f0e3"}.fa-dashboard:before,.fa-tachometer:before{content:"\f0e4"}.fa-comment-o:before{content:"\f0e5"}.fa-comments-o:before{content:"\f0e6"}.fa-flash:before,.fa-bolt:before{content:"\f0e7"}.fa-sitemap:before{content:"\f0e8"}.fa-umbrella:before{content:"\f0e9"}.fa-paste:before,.fa-clipboard:before{content:"\f0ea"}.fa-lightbulb-o:before{content:"\f0eb"}.fa-exchange:before{content:"\f0ec"}.fa-cloud-download:before{content:"\f0ed"}.fa-cloud-upload:before{content:"\f0ee"}.fa-user-md:before{content:"\f0f0"}.fa-stethoscope:before{content:"\f0f1"}.fa-suitcase:before{content:"\f0f2"}.fa-bell-o:before{content:"\f0a2"}.fa-coffee:before{content:"\f0f4"}.fa-cutlery:before{content:"\f0f5"}.fa-file-text-o:before{content:"\f0f6"}.fa-building-o:before{content:"\f0f7"}.fa-hospital-o:before{content:"\f0f8"}.fa-ambulance:before{content:"\f0f9"}.fa-medkit:before{content:"\f0fa"}.fa-fighter-jet:before{content:"\f0fb"}.fa-beer:before{content:"\f0fc"}.fa-h-square:before{content:"\f0fd"}.fa-plus-square:before{content:"\f0fe"}.fa-angle-double-left:before{content:"\f100"}.fa-angle-double-right:before{content:"\f101"}.fa-angle-double-up:before{content:"\f102"}.fa-angle-double-down:before{content:"\f103"}.fa-angle-left:before{content:"\f104"}.fa-angle-right:before{content:"\f105"}.fa-angle-up:before{content:"\f106"}.fa-angle-down:before{content:"\f107"}.fa-desktop:before{content:"\f108"}.fa-laptop:before{content:"\f109"}.fa-tablet:before{content:"\f10a"}.fa-mobile-phone:before,.fa-mobile:before{content:"\f10b"}.fa-circle-o:before{content:"\f10c"}.fa-quote-left:before{content:"\f10d"}.fa-quote-right:before{content:"\f10e"}.fa-spinner:before{content:"\f110"}.fa-circle:before{content:"\f111"}.fa-mail-reply:before,.fa-reply:before{content:"\f112"}.fa-github-alt:before{content:"\f113"}.fa-folder-o:before{content:"\f114"}.fa-folder-open-o:before{content:"\f115"}.fa-smile-o:before{content:"\f118"}.fa-frown-o:before{content:"\f119"}.fa-meh-o:before{content:"\f11a"}.fa-gamepad:before{content:"\f11b"}.fa-keyboard-o:before{content:"\f11c"}.fa-flag-o:before{content:"\f11d"}.fa-flag-checkered:before{content:"\f11e"}.fa-terminal:before{content:"\f120"}.fa-code:before{content:"\f121"}.fa-mail-reply-all:before,.fa-reply-all:before{content:"\f122"}.fa-star-half-empty:before,.fa-star-half-full:before,.fa-star-half-o:before{content:"\f123"}.fa-location-arrow:before{content:"\f124"}.fa-crop:before{content:"\f125"}.fa-code-fork:before{content:"\f126"}.fa-unlink:before,.fa-chain-broken:before{content:"\f127"}.fa-question:before{content:"\f128"}.fa-info:before{content:"\f129"}.fa-exclamation:before{content:"\f12a"}.fa-superscript:before{content:"\f12b"}.fa-subscript:before{content:"\f12c"}.fa-eraser:before{content:"\f12d"}.fa-puzzle-piece:before{content:"\f12e"}.fa-microphone:before{content:"\f130"}.fa-microphone-slash:before{content:"\f131"}.fa-shield:before{content:"\f132"}.fa-calendar-o:before{content:"\f133"}.fa-fire-extinguisher:before{content:"\f134"}.fa-rocket:before{content:"\f135"}.fa-maxcdn:before{content:"\f136"}.fa-chevron-circle-left:before{content:"\f137"}.fa-chevron-circle-right:before{content:"\f138"}.fa-chevron-circle-up:before{content:"\f139"}.fa-chevron-circle-down:before{content:"\f13a"}.fa-html5:before{content:"\f13b"}.fa-css3:before{content:"\f13c"}.fa-anchor:before{content:"\f13d"}.fa-unlock-alt:before{content:"\f13e"}.fa-bullseye:before{content:"\f140"}.fa-ellipsis-h:before{content:"\f141"}.fa-ellipsis-v:before{content:"\f142"}.fa-rss-square:before{content:"\f143"}.fa-play-circle:before{content:"\f144"}.fa-ticket:before{content:"\f145"}.fa-minus-square:before{content:"\f146"}.fa-minus-square-o:before{content:"\f147"}.fa-level-up:before{content:"\f148"}.fa-level-down:before{content:"\f149"}.fa-check-square:before{content:"\f14a"}.fa-pencil-square:before{content:"\f14b"}.fa-external-link-square:before{content:"\f14c"}.fa-share-square:before{content:"\f14d"}.fa-compass:before{content:"\f14e"}.fa-toggle-down:before,.fa-caret-square-o-down:before{content:"\f150"}.fa-toggle-up:before,.fa-caret-square-o-up:before{content:"\f151"}.fa-toggle-right:before,.fa-caret-square-o-right:before{content:"\f152"}.fa-euro:before,.fa-eur:before{content:"\f153"}.fa-gbp:before{content:"\f154"}.fa-dollar:before,.fa-usd:before{content:"\f155"}.fa-rupee:before,.fa-inr:before{content:"\f156"}.fa-cny:before,.fa-rmb:before,.fa-yen:before,.fa-jpy:before{content:"\f157"}.fa-ruble:before,.fa-rouble:before,.fa-rub:before{content:"\f158"}.fa-won:before,.fa-krw:before{content:"\f159"}.fa-bitcoin:before,.fa-btc:before{content:"\f15a"}.fa-file:before{content:"\f15b"}.fa-file-text:before{content:"\f15c"}.fa-sort-alpha-asc:before{content:"\f15d"}.fa-sort-alpha-desc:before{content:"\f15e"}.fa-sort-amount-asc:before{content:"\f160"}.fa-sort-amount-desc:before{content:"\f161"}.fa-sort-numeric-asc:before{content:"\f162"}.fa-sort-numeric-desc:before{content:"\f163"}.fa-thumbs-up:before{content:"\f164"}.fa-thumbs-down:before{content:"\f165"}.fa-youtube-square:before{content:"\f166"}.fa-youtube:before{content:"\f167"}.fa-xing:before{content:"\f168"}.fa-xing-square:before{content:"\f169"}.fa-youtube-play:before{content:"\f16a"}.fa-dropbox:before{content:"\f16b"}.fa-stack-overflow:before{content:"\f16c"}.fa-instagram:before{content:"\f16d"}.fa-flickr:before{content:"\f16e"}.fa-adn:before{content:"\f170"}.fa-bitbucket:before{content:"\f171"}.fa-bitbucket-square:before{content:"\f172"}.fa-tumblr:before{content:"\f173"}.fa-tumblr-square:before{content:"\f174"}.fa-long-arrow-down:before{content:"\f175"}.fa-long-arrow-up:before{content:"\f176"}.fa-long-arrow-left:before{content:"\f177"}.fa-long-arrow-right:before{content:"\f178"}.fa-apple:before{content:"\f179"}.fa-windows:before{content:"\f17a"}.fa-android:before{content:"\f17b"}.fa-linux:before{content:"\f17c"}.fa-dribbble:before{content:"\f17d"}.fa-skype:before{content:"\f17e"}.fa-foursquare:before{content:"\f180"}.fa-trello:before{content:"\f181"}.fa-female:before{content:"\f182"}.fa-male:before{content:"\f183"}.fa-gittip:before,.fa-gratipay:before{content:"\f184"}.fa-sun-o:before{content:"\f185"}.fa-moon-o:before{content:"\f186"}.fa-archive:before{content:"\f187"}.fa-bug:before{content:"\f188"}.fa-vk:before{content:"\f189"}.fa-weibo:before{content:"\f18a"}.fa-renren:before{content:"\f18b"}.fa-pagelines:before{content:"\f18c"}.fa-stack-exchange:before{content:"\f18d"}.fa-arrow-circle-o-right:before{content:"\f18e"}.fa-arrow-circle-o-left:before{content:"\f190"}.fa-toggle-left:before,.fa-caret-square-o-left:before{content:"\f191"}.fa-dot-circle-o:before{content:"\f192"}.fa-wheelchair:before{content:"\f193"}.fa-vimeo-square:before{content:"\f194"}.fa-turkish-lira:before,.fa-try:before{content:"\f195"}.fa-plus-square-o:before{content:"\f196"}.fa-space-shuttle:before{content:"\f197"}.fa-slack:before{content:"\f198"}.fa-envelope-square:before{content:"\f199"}.fa-wordpress:before{content:"\f19a"}.fa-openid:before{content:"\f19b"}.fa-institution:before,.fa-bank:before,.fa-university:before{content:"\f19c"}.fa-mortar-board:before,.fa-graduation-cap:before{content:"\f19d"}.fa-yahoo:before{content:"\f19e"}.fa-google:before{content:"\f1a0"}.fa-reddit:before{content:"\f1a1"}.fa-reddit-square:before{content:"\f1a2"}.fa-stumbleupon-circle:before{content:"\f1a3"}.fa-stumbleupon:before{content:"\f1a4"}.fa-delicious:before{content:"\f1a5"}.fa-digg:before{content:"\f1a6"}.fa-pied-piper-pp:before{content:"\f1a7"}.fa-pied-piper-alt:before{content:"\f1a8"}.fa-drupal:before{content:"\f1a9"}.fa-joomla:before{content:"\f1aa"}.fa-language:before{content:"\f1ab"}.fa-fax:before{content:"\f1ac"}.fa-building:before{content:"\f1ad"}.fa-child:before{content:"\f1ae"}.fa-paw:before{content:"\f1b0"}.fa-spoon:before{content:"\f1b1"}.fa-cube:before{content:"\f1b2"}.fa-cubes:before{content:"\f1b3"}.fa-behance:before{content:"\f1b4"}.fa-behance-square:before{content:"\f1b5"}.fa-steam:before{content:"\f1b6"}.fa-steam-square:before{content:"\f1b7"}.fa-recycle:before{content:"\f1b8"}.fa-automobile:before,.fa-car:before{content:"\f1b9"}.fa-cab:before,.fa-taxi:before{content:"\f1ba"}.fa-tree:before{content:"\f1bb"}.fa-spotify:before{content:"\f1bc"}.fa-deviantart:before{content:"\f1bd"}.fa-soundcloud:before{content:"\f1be"}.fa-database:before{content:"\f1c0"}.fa-file-pdf-o:before{content:"\f1c1"}.fa-file-word-o:before{content:"\f1c2"}.fa-file-excel-o:before{content:"\f1c3"}.fa-file-powerpoint-o:before{content:"\f1c4"}.fa-file-photo-o:before,.fa-file-picture-o:before,.fa-file-image-o:before{content:"\f1c5"}.fa-file-zip-o:before,.fa-file-archive-o:before{content:"\f1c6"}.fa-file-sound-o:before,.fa-file-audio-o:before{content:"\f1c7"}.fa-file-movie-o:before,.fa-file-video-o:before{content:"\f1c8"}.fa-file-code-o:before{content:"\f1c9"}.fa-vine:before{content:"\f1ca"}.fa-codepen:before{content:"\f1cb"}.fa-jsfiddle:before{content:"\f1cc"}.fa-life-bouy:before,.fa-life-buoy:before,.fa-life-saver:before,.fa-support:before,.fa-life-ring:before{content:"\f1cd"}.fa-circle-o-notch:before{content:"\f1ce"}.fa-ra:before,.fa-resistance:before,.fa-rebel:before{content:"\f1d0"}.fa-ge:before,.fa-empire:before{content:"\f1d1"}.fa-git-square:before{content:"\f1d2"}.fa-git:before{content:"\f1d3"}.fa-y-combinator-square:before,.fa-yc-square:before,.fa-hacker-news:before{content:"\f1d4"}.fa-tencent-weibo:before{content:"\f1d5"}.fa-qq:before{content:"\f1d6"}.fa-wechat:before,.fa-weixin:before{content:"\f1d7"}.fa-send:before,.fa-paper-plane:before{content:"\f1d8"}.fa-send-o:before,.fa-paper-plane-o:before{content:"\f1d9"}.fa-history:before{content:"\f1da"}.fa-circle-thin:before{content:"\f1db"}.fa-header:before{content:"\f1dc"}.fa-paragraph:before{content:"\f1dd"}.fa-sliders:before{content:"\f1de"}.fa-share-alt:before{content:"\f1e0"}.fa-share-alt-square:before{content:"\f1e1"}.fa-bomb:before{content:"\f1e2"}.fa-soccer-ball-o:before,.fa-futbol-o:before{content:"\f1e3"}.fa-tty:before{content:"\f1e4"}.fa-binoculars:before{content:"\f1e5"}.fa-plug:before{content:"\f1e6"}.fa-slideshare:before{content:"\f1e7"}.fa-twitch:before{content:"\f1e8"}.fa-yelp:before{content:"\f1e9"}.fa-newspaper-o:before{content:"\f1ea"}.fa-wifi:before{content:"\f1eb"}.fa-calculator:before{content:"\f1ec"}.fa-paypal:before{content:"\f1ed"}.fa-google-wallet:before{content:"\f1ee"}.fa-cc-visa:before{content:"\f1f0"}.fa-cc-mastercard:before{content:"\f1f1"}.fa-cc-discover:before{content:"\f1f2"}.fa-cc-amex:before{content:"\f1f3"}.fa-cc-paypal:before{content:"\f1f4"}.fa-cc-stripe:before{content:"\f1f5"}.fa-bell-slash:before{content:"\f1f6"}.fa-bell-slash-o:before{content:"\f1f7"}.fa-trash:before{content:"\f1f8"}.fa-copyright:before{content:"\f1f9"}.fa-at:before{content:"\f1fa"}.fa-eyedropper:before{content:"\f1fb"}.fa-paint-brush:before{content:"\f1fc"}.fa-birthday-cake:before{content:"\f1fd"}.fa-area-chart:before{content:"\f1fe"}.fa-pie-chart:before{content:"\f200"}.fa-line-chart:before{content:"\f201"}.fa-lastfm:before{content:"\f202"}.fa-lastfm-square:before{content:"\f203"}.fa-toggle-off:before{content:"\f204"}.fa-toggle-on:before{content:"\f205"}.fa-bicycle:before{content:"\f206"}.fa-bus:before{content:"\f207"}.fa-ioxhost:before{content:"\f208"}.fa-angellist:before{content:"\f209"}.fa-cc:before{content:"\f20a"}.fa-shekel:before,.fa-sheqel:before,.fa-ils:before{content:"\f20b"}.fa-meanpath:before{content:"\f20c"}.fa-buysellads:before{content:"\f20d"}.fa-connectdevelop:before{content:"\f20e"}.fa-dashcube:before{content:"\f210"}.fa-forumbee:before{content:"\f211"}.fa-leanpub:before{content:"\f212"}.fa-sellsy:before{content:"\f213"}.fa-shirtsinbulk:before{content:"\f214"}.fa-simplybuilt:before{content:"\f215"}.fa-skyatlas:before{content:"\f216"}.fa-cart-plus:before{content:"\f217"}.fa-cart-arrow-down:before{content:"\f218"}.fa-diamond:before{content:"\f219"}.fa-ship:before{content:"\f21a"}.fa-user-secret:before{content:"\f21b"}.fa-motorcycle:before{content:"\f21c"}.fa-street-view:before{content:"\f21d"}.fa-heartbeat:before{content:"\f21e"}.fa-venus:before{content:"\f221"}.fa-mars:before{content:"\f222"}.fa-mercury:before{content:"\f223"}.fa-intersex:before,.fa-transgender:before{content:"\f224"}.fa-transgender-alt:before{content:"\f225"}.fa-venus-double:before{content:"\f226"}.fa-mars-double:before{content:"\f227"}.fa-venus-mars:before{content:"\f228"}.fa-mars-stroke:before{content:"\f229"}.fa-mars-stroke-v:before{content:"\f22a"}.fa-mars-stroke-h:before{content:"\f22b"}.fa-neuter:before{content:"\f22c"}.fa-genderless:before{content:"\f22d"}.fa-facebook-official:before{content:"\f230"}.fa-pinterest-p:before{content:"\f231"}.fa-whatsapp:before{content:"\f232"}.fa-server:before{content:"\f233"}.fa-user-plus:before{content:"\f234"}.fa-user-times:before{content:"\f235"}.fa-hotel:before,.fa-bed:before{content:"\f236"}.fa-viacoin:before{content:"\f237"}.fa-train:before{content:"\f238"}.fa-subway:before{content:"\f239"}.fa-medium:before{content:"\f23a"}.fa-yc:before,.fa-y-combinator:before{content:"\f23b"}.fa-optin-monster:before{content:"\f23c"}.fa-opencart:before{content:"\f23d"}.fa-expeditedssl:before{content:"\f23e"}.fa-battery-4:before,.fa-battery:before,.fa-battery-full:before{content:"\f240"}.fa-battery-3:before,.fa-battery-three-quarters:before{content:"\f241"}.fa-battery-2:before,.fa-battery-half:before{content:"\f242"}.fa-battery-1:before,.fa-battery-quarter:before{content:"\f243"}.fa-battery-0:before,.fa-battery-empty:before{content:"\f244"}.fa-mouse-pointer:before{content:"\f245"}.fa-i-cursor:before{content:"\f246"}.fa-object-group:before{content:"\f247"}.fa-object-ungroup:before{content:"\f248"}.fa-sticky-note:before{content:"\f249"}.fa-sticky-note-o:before{content:"\f24a"}.fa-cc-jcb:before{content:"\f24b"}.fa-cc-diners-club:before{content:"\f24c"}.fa-clone:before{content:"\f24d"}.fa-balance-scale:before{content:"\f24e"}.fa-hourglass-o:before{content:"\f250"}.fa-hourglass-1:before,.fa-hourglass-start:before{content:"\f251"}.fa-hourglass-2:before,.fa-hourglass-half:before{content:"\f252"}.fa-hourglass-3:before,.fa-hourglass-end:before{content:"\f253"}.fa-hourglass:before{content:"\f254"}.fa-hand-grab-o:before,.fa-hand-rock-o:before{content:"\f255"}.fa-hand-stop-o:before,.fa-hand-paper-o:before{content:"\f256"}.fa-hand-scissors-o:before{content:"\f257"}.fa-hand-lizard-o:before{content:"\f258"}.fa-hand-spock-o:before{content:"\f259"}.fa-hand-pointer-o:before{content:"\f25a"}.fa-hand-peace-o:before{content:"\f25b"}.fa-trademark:before{content:"\f25c"}.fa-registered:before{content:"\f25d"}.fa-creative-commons:before{content:"\f25e"}.fa-gg:before{content:"\f260"}.fa-gg-circle:before{content:"\f261"}.fa-tripadvisor:before{content:"\f262"}.fa-odnoklassniki:before{content:"\f263"}.fa-odnoklassniki-square:before{content:"\f264"}.fa-get-pocket:before{content:"\f265"}.fa-wikipedia-w:before{content:"\f266"}.fa-safari:before{content:"\f267"}.fa-chrome:before{content:"\f268"}.fa-firefox:before{content:"\f269"}.fa-opera:before{content:"\f26a"}.fa-internet-explorer:before{content:"\f26b"}.fa-tv:before,.fa-television:before{content:"\f26c"}.fa-contao:before{content:"\f26d"}.fa-500px:before{content:"\f26e"}.fa-amazon:before{content:"\f270"}.fa-calendar-plus-o:before{content:"\f271"}.fa-calendar-minus-o:before{content:"\f272"}.fa-calendar-times-o:before{content:"\f273"}.fa-calendar-check-o:before{content:"\f274"}.fa-industry:before{content:"\f275"}.fa-map-pin:before{content:"\f276"}.fa-map-signs:before{content:"\f277"}.fa-map-o:before{content:"\f278"}.fa-map:before{content:"\f279"}.fa-commenting:before{content:"\f27a"}.fa-commenting-o:before{content:"\f27b"}.fa-houzz:before{content:"\f27c"}.fa-vimeo:before{content:"\f27d"}.fa-black-tie:before{content:"\f27e"}.fa-fonticons:before{content:"\f280"}.fa-reddit-alien:before{content:"\f281"}.fa-edge:before{content:"\f282"}.fa-credit-card-alt:before{content:"\f283"}.fa-codiepie:before{content:"\f284"}.fa-modx:before{content:"\f285"}.fa-fort-awesome:before{content:"\f286"}.fa-usb:before{content:"\f287"}.fa-product-hunt:before{content:"\f288"}.fa-mixcloud:before{content:"\f289"}.fa-scribd:before{content:"\f28a"}.fa-pause-circle:before{content:"\f28b"}.fa-pause-circle-o:before{content:"\f28c"}.fa-stop-circle:before{content:"\f28d"}.fa-stop-circle-o:before{content:"\f28e"}.fa-shopping-bag:before{content:"\f290"}.fa-shopping-basket:before{content:"\f291"}.fa-hashtag:before{content:"\f292"}.fa-bluetooth:before{content:"\f293"}.fa-bluetooth-b:before{content:"\f294"}.fa-percent:before{content:"\f295"}.fa-gitlab:before{content:"\f296"}.fa-wpbeginner:before{content:"\f297"}.fa-wpforms:before{content:"\f298"}.fa-envira:before{content:"\f299"}.fa-universal-access:before{content:"\f29a"}.fa-wheelchair-alt:before{content:"\f29b"}.fa-question-circle-o:before{content:"\f29c"}.fa-blind:before{content:"\f29d"}.fa-audio-description:before{content:"\f29e"}.fa-volume-control-phone:before{content:"\f2a0"}.fa-braille:before{content:"\f2a1"}.fa-assistive-listening-systems:before{content:"\f2a2"}.fa-asl-interpreting:before,.fa-american-sign-language-interpreting:before{content:"\f2a3"}.fa-deafness:before,.fa-hard-of-hearing:before,.fa-deaf:before{content:"\f2a4"}.fa-glide:before{content:"\f2a5"}.fa-glide-g:before{content:"\f2a6"}.fa-signing:before,.fa-sign-language:before{content:"\f2a7"}.fa-low-vision:before{content:"\f2a8"}.fa-viadeo:before{content:"\f2a9"}.fa-viadeo-square:before{content:"\f2aa"}.fa-snapchat:before{content:"\f2ab"}.fa-snapchat-ghost:before{content:"\f2ac"}.fa-snapchat-square:before{content:"\f2ad"}.fa-pied-piper:before{content:"\f2ae"}.fa-first-order:before{content:"\f2b0"}.fa-yoast:before{content:"\f2b1"}.fa-themeisle:before{content:"\f2b2"}.fa-google-plus-circle:before,.fa-google-plus-official:before{content:"\f2b3"}.fa-fa:before,.fa-font-awesome:before{content:"\f2b4"}.fa-handshake-o:before{content:"\f2b5"}.fa-envelope-open:before{content:"\f2b6"}.fa-envelope-open-o:before{content:"\f2b7"}.fa-linode:before{content:"\f2b8"}.fa-address-book:before{content:"\f2b9"}.fa-address-book-o:before{content:"\f2ba"}.fa-vcard:before,.fa-address-card:before{content:"\f2bb"}.fa-vcard-o:before,.fa-address-card-o:before{content:"\f2bc"}.fa-user-circle:before{content:"\f2bd"}.fa-user-circle-o:before{content:"\f2be"}.fa-user-o:before{content:"\f2c0"}.fa-id-badge:before{content:"\f2c1"}.fa-drivers-license:before,.fa-id-card:before{content:"\f2c2"}.fa-drivers-license-o:before,.fa-id-card-o:before{content:"\f2c3"}.fa-quora:before{content:"\f2c4"}.fa-free-code-camp:before{content:"\f2c5"}.fa-telegram:before{content:"\f2c6"}.fa-thermometer-4:before,.fa-thermometer:before,.fa-thermometer-full:before{content:"\f2c7"}.fa-thermometer-3:before,.fa-thermometer-three-quarters:before{content:"\f2c8"}.fa-thermometer-2:before,.fa-thermometer-half:before{content:"\f2c9"}.fa-thermometer-1:before,.fa-thermometer-quarter:before{content:"\f2ca"}.fa-thermometer-0:before,.fa-thermometer-empty:before{content:"\f2cb"}.fa-shower:before{content:"\f2cc"}.fa-bathtub:before,.fa-s15:before,.fa-bath:before{content:"\f2cd"}.fa-podcast:before{content:"\f2ce"}.fa-window-maximize:before{content:"\f2d0"}.fa-window-minimize:before{content:"\f2d1"}.fa-window-restore:before{content:"\f2d2"}.fa-times-rectangle:before,.fa-window-close:before{content:"\f2d3"}.fa-times-rectangle-o:before,.fa-window-close-o:before{content:"\f2d4"}.fa-bandcamp:before{content:"\f2d5"}.fa-grav:before{content:"\f2d6"}.fa-etsy:before{content:"\f2d7"}.fa-imdb:before{content:"\f2d8"}.fa-ravelry:before{content:"\f2d9"}.fa-eercast:before{content:"\f2da"}.fa-microchip:before{content:"\f2db"}.fa-snowflake-o:before{content:"\f2dc"}.fa-superpowers:before{content:"\f2dd"}.fa-wpexplorer:before{content:"\f2de"}.fa-meetup:before{content:"\f2e0"}.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0, 0, 0, 0);border:0}.sr-only-focusable:active,.sr-only-focusable:focus{position:static;width:auto;height:auto;margin:0;overflow:visible;clip:auto}
diff --git a/public/font-awesome-4.7.0/fonts/FontAwesome.otf b/public/font-awesome-4.7.0/fonts/FontAwesome.otf
new file mode 100644
index 00000000..401ec0f3
Binary files /dev/null and b/public/font-awesome-4.7.0/fonts/FontAwesome.otf differ
diff --git a/public/font-awesome-4.7.0/fonts/fontawesome-webfont.eot b/public/font-awesome-4.7.0/fonts/fontawesome-webfont.eot
new file mode 100644
index 00000000..e9f60ca9
Binary files /dev/null and b/public/font-awesome-4.7.0/fonts/fontawesome-webfont.eot differ
diff --git a/public/font-awesome-4.7.0/fonts/fontawesome-webfont.svg b/public/font-awesome-4.7.0/fonts/fontawesome-webfont.svg
new file mode 100644
index 00000000..855c845e
--- /dev/null
+++ b/public/font-awesome-4.7.0/fonts/fontawesome-webfont.svg
@@ -0,0 +1,2671 @@
+
+
+
diff --git a/public/font-awesome-4.7.0/fonts/fontawesome-webfont.ttf b/public/font-awesome-4.7.0/fonts/fontawesome-webfont.ttf
new file mode 100644
index 00000000..35acda2f
Binary files /dev/null and b/public/font-awesome-4.7.0/fonts/fontawesome-webfont.ttf differ
diff --git a/public/font-awesome-4.7.0/fonts/fontawesome-webfont.woff b/public/font-awesome-4.7.0/fonts/fontawesome-webfont.woff
new file mode 100644
index 00000000..400014a4
Binary files /dev/null and b/public/font-awesome-4.7.0/fonts/fontawesome-webfont.woff differ
diff --git a/public/font-awesome-4.7.0/fonts/fontawesome-webfont.woff2 b/public/font-awesome-4.7.0/fonts/fontawesome-webfont.woff2
new file mode 100644
index 00000000..4d13fc60
Binary files /dev/null and b/public/font-awesome-4.7.0/fonts/fontawesome-webfont.woff2 differ
diff --git a/query_gen/main.go b/query_gen/main.go
index 7ba38aac..08db342c 100644
--- a/query_gen/main.go
+++ b/query_gen/main.go
@@ -253,8 +253,6 @@ func write_selects(adapter qgen.DB_Adapter) error {
adapter.SimpleSelect("getUserName", "users", "name", "uid = ?", "", "")
- adapter.SimpleSelect("getUserActive", "users", "active", "uid = ?", "", "")
-
adapter.SimpleSelect("getEmailsByUser", "emails", "email, validated, token", "uid = ?", "", "")
adapter.SimpleSelect("getTopicBasic", "topics", "title, content", "tid = ?", "", "")
@@ -366,7 +364,7 @@ func write_updates(adapter qgen.DB_Adapter) error {
adapter.SimpleUpdate("addLikesToReply", "replies", "likeCount = likeCount + ?", "rid = ?")
- adapter.SimpleUpdate("editTopic", "topics", "title = ?, content = ?, parsed_content = ?, is_closed = ?", "tid = ?")
+ adapter.SimpleUpdate("editTopic", "topics", "title = ?, content = ?, parsed_content = ?", "tid = ?")
adapter.SimpleUpdate("editReply", "replies", "content = ?, parsed_content = ?", "rid = ?")
@@ -374,6 +372,10 @@ func write_updates(adapter qgen.DB_Adapter) error {
adapter.SimpleUpdate("unstickTopic", "topics", "sticky = 0", "tid = ?")
+ adapter.SimpleUpdate("lockTopic", "topics", "is_closed = 1", "tid = ?")
+
+ adapter.SimpleUpdate("unlockTopic", "topics", "is_closed = 0", "tid = ?")
+
adapter.SimpleUpdate("updateLastIP", "users", "last_ip = ?", "uid = ?")
adapter.SimpleUpdate("updateSession", "users", "session = ?", "uid = ?")
diff --git a/routes.go b/routes.go
index ae3dc8e8..e97721d4 100644
--- a/routes.go
+++ b/routes.go
@@ -25,14 +25,9 @@ import (
var tList []interface{}
//var nList []string
-var hvars *HeaderVars // We might need to rethink this now that it's a pointer
var successJSONBytes = []byte(`{"success":"1"}`)
var cacheControlMaxAge = "max-age=" + strconv.Itoa(day)
-func init() {
- hvars = &HeaderVars{Site: site}
-}
-
// HTTPSRedirect is a connection handler which redirects all HTTP requests to HTTPS
type HTTPSRedirect struct {
}
diff --git a/routes_common.go b/routes_common.go
index 8b097052..2891c24b 100644
--- a/routes_common.go
+++ b/routes_common.go
@@ -23,6 +23,12 @@ var MemberCheck func(w http.ResponseWriter, r *http.Request, user *User) (header
var SimpleUserCheck func(w http.ResponseWriter, r *http.Request, user *User) (headerLite *HeaderLite, success bool) = simpleUserCheck
var UserCheck func(w http.ResponseWriter, r *http.Request, user *User) (headerVars *HeaderVars, success bool) = userCheck
+// This is mostly for errors.go, please create *HeaderVars on the spot instead of relying on this or the atomic store underlying it, if possible
+// TODO: Write a test for this
+func getDefaultHeaderVar() *HeaderVars {
+ return &HeaderVars{Site: site, ThemeName: fallbackTheme}
+}
+
// TODO: Support for left sidebars and sidebars on both sides
// http.Request is for context.Context middleware. Mostly for plugin_socialgroups right now
func BuildWidgets(zone string, data interface{}, headerVars *HeaderVars, r *http.Request) {
@@ -312,7 +318,7 @@ func preRoute(w http.ResponseWriter, r *http.Request) (User, bool) {
InternalError(err, w)
return *user, false
}
- user.LastIP = host
+ user.LastIP = host // ! - Is this racey?
}
h := w.Header()
diff --git a/tasks.go b/tasks.go
index 72ab4b23..5cd449f9 100644
--- a/tasks.go
+++ b/tasks.go
@@ -22,6 +22,7 @@ func handleExpiredScheduledGroups() error {
defer rows.Close()
var uid int
+ ucache, ok := users.(UserCache)
for rows.Next() {
err := rows.Scan(&uid)
if err != nil {
@@ -35,7 +36,9 @@ func handleExpiredScheduledGroups() error {
if err != nil {
return err
}
- _ = users.Reload(uid)
+ if ok {
+ ucache.CacheRemove(uid)
+ }
}
return rows.Err()
}
diff --git a/template_list.go b/template_list.go
index d48d5527..80f3309b 100644
--- a/template_list.go
+++ b/template_list.go
@@ -103,134 +103,132 @@ var topic_13 = []byte(`">
`)
var topic_14 = []byte(`
`)
-var topic_15 = []byte(`🔒︎`)
+var topic_15 = []byte(`🔒︎`)
var topic_16 = []byte(`
- `)
-var topic_18 = []byte(``)
-var topic_19 = []byte(`
`)
-var topic_20 = []byte(`
+var topic_18 = []byte(`
+var topic_19 = []byte(`" style="`)
+var topic_20 = []byte(`background-image:url(`)
+var topic_21 = []byte(`), url(/static/post-avatar-bg.jpg);background-position: 0px `)
+var topic_22 = []byte(`-1`)
+var topic_23 = []byte(`0px;background-repeat:no-repeat, repeat-y;`)
+var topic_24 = []byte(`">
`)
-var topic_27 = []byte(`
+var topic_25 = []byte(`
+var topic_26 = []byte(`
`)
-var topic_30 = []byte(`
+var topic_27 = []byte(`" class="username real_username">`)
+var topic_28 = []byte(`
`)
-var topic_31 = []byte(`
+var topic_29 = []byte(`
`)
-var topic_35 = []byte(``)
-var topic_37 = []byte(``)
-var topic_39 = []byte(``)
-var topic_41 = []byte(``)
-var topic_43 = []byte(``)
-var topic_45 = []byte(`
+var topic_31 = []byte(`background-color:/*#eaffea*/#D6FFD6;`)
+var topic_32 = []byte(`">`)
+var topic_33 = []byte(``)
+var topic_35 = []byte(``)
+var topic_37 = []byte(``)
+var topic_39 = []byte(``)
+var topic_41 = []byte(``)
+var topic_43 = []byte(``)
+var topic_45 = []byte(``)
+var topic_47 = []byte(`
+var topic_48 = []byte(`?session=`)
+var topic_49 = []byte(`&type=topic" class="mod_button report_item" style="font-weight:normal;" title="Flag Topic">
`)
-var topic_48 = []byte(``)
-var topic_49 = []byte(``)
-var topic_50 = []byte(``)
-var topic_51 = []byte(``)
-var topic_52 = []byte(``)
-var topic_53 = []byte(``)
-var topic_54 = []byte(`
+var topic_50 = []byte(``)
+var topic_51 = []byte(``)
+var topic_52 = []byte(``)
+var topic_53 = []byte(``)
+var topic_54 = []byte(``)
+var topic_55 = []byte(``)
+var topic_56 = []byte(`
`)
-var topic_55 = []byte(`
+var topic_57 = []byte(`
`)
-var topic_56 = []byte(`
+var topic_58 = []byte(`
`)
-var topic_57 = []byte(`
+var topic_59 = []byte(`
`)
-var topic_58 = []byte(`
+var topic_60 = []byte(`
+var topic_61 = []byte(`" style="`)
+var topic_62 = []byte(`background-image:url(`)
+var topic_63 = []byte(`), url(/static/post-avatar-bg.jpg);background-position: 0px `)
+var topic_64 = []byte(`-1`)
+var topic_65 = []byte(`0px;background-repeat:no-repeat, repeat-y;`)
+var topic_66 = []byte(`">
`)
-var topic_65 = []byte(`
+var topic_67 = []byte(`
`)
-var topic_67 = []byte(`
+var topic_68 = []byte(`" class="username real_username">`)
+var topic_69 = []byte(`
`)
-var topic_68 = []byte(``)
-var topic_72 = []byte(``)
-var topic_74 = []byte(``)
-var topic_76 = []byte(``)
-var topic_78 = []byte(`
+var topic_70 = []byte(``)
+var topic_74 = []byte(``)
+var topic_76 = []byte(``)
+var topic_78 = []byte(``)
+var topic_80 = []byte(`
+var topic_81 = []byte(`?session=`)
+var topic_82 = []byte(`&type=reply" class="mod_button report_item" title="Flag Reply">
`)
-var topic_81 = []byte(``)
-var topic_82 = []byte(``)
-var topic_83 = []byte(``)
-var topic_84 = []byte(``)
-var topic_85 = []byte(``)
-var topic_86 = []byte(``)
-var topic_87 = []byte(`
+var topic_83 = []byte(``)
+var topic_84 = []byte(``)
+var topic_85 = []byte(``)
+var topic_86 = []byte(``)
+var topic_87 = []byte(``)
+var topic_88 = []byte(``)
+var topic_89 = []byte(`
`)
-var topic_88 = []byte(`
+var topic_90 = []byte(`
`)
-var topic_89 = []byte(`
+var topic_91 = []byte(`
`)
-var topic_91 = []byte(`
+var topic_93 = []byte(`
@@ -299,15 +297,9 @@ var topic_alt_14 = []byte(`
- `)
-var topic_alt_17 = []byte(``)
-var topic_alt_18 = []byte(`
`)
-var topic_alt_19 = []byte(`
+var topic_alt_17 = []byte(`
@@ -316,118 +308,122 @@ var topic_alt_19 = []byte(`
+var topic_alt_18 = []byte(`), url(/static/white-dot.jpg);background-position: 0px -10px;">
`)
-var topic_alt_22 = []byte(`
+var topic_alt_19 = []byte(`" class="the_name">`)
+var topic_alt_20 = []byte(`
`)
-var topic_alt_23 = []byte(``)
+var topic_alt_21 = []byte(`
`)
+var topic_alt_22 = []byte(`
`)
+var topic_alt_23 = []byte(`
Level `)
var topic_alt_24 = []byte(`
`)
-var topic_alt_25 = []byte(`
Level `)
-var topic_alt_26 = []byte(`
`)
-var topic_alt_27 = []byte(`
+var topic_alt_25 = []byte(`
`)
-var topic_alt_28 = []byte(`
+var topic_alt_26 = []byte(`
+var topic_alt_27 = []byte(`
`)
-var topic_alt_50 = []byte(`
+var topic_alt_52 = []byte(`
+var topic_alt_53 = []byte(`action_item`)
+var topic_alt_54 = []byte(`">
+var topic_alt_55 = []byte(`), url(/static/white-dot.jpg);background-position: 0px -10px;">
`)
-var topic_alt_55 = []byte(`
+var topic_alt_56 = []byte(`" class="the_name">`)
+var topic_alt_57 = []byte(`
`)
-var topic_alt_56 = []byte(``)
-var topic_alt_57 = []byte(`
`)
-var topic_alt_58 = []byte(`Level `)
+var topic_alt_58 = []byte(`
`)
var topic_alt_59 = []byte(`
`)
-var topic_alt_60 = []byte(`
+var topic_alt_60 = []byte(`
Level `)
+var topic_alt_61 = []byte(`
`)
+var topic_alt_62 = []byte(`
+var topic_alt_63 = []byte(`style="margin-left: 0px;"`)
+var topic_alt_64 = []byte(`>
`)
-var topic_alt_63 = []byte(`
+var topic_alt_65 = []byte(`
`)
-var topic_alt_64 = []byte(`
+var topic_alt_66 = []byte(`
`)
-var topic_alt_65 = []byte(`
+var topic_alt_67 = []byte(`
`)
-var topic_alt_66 = []byte(`
+var topic_alt_68 = []byte(`
`)
-var topic_alt_67 = []byte(`
+var topic_alt_69 = []byte(`
`)
-var topic_alt_84 = []byte(`
+var topic_alt_86 = []byte(`
`)
-var topic_alt_85 = []byte(`
+var topic_alt_87 = []byte(`
`)
-var topic_alt_86 = []byte(`
+var topic_alt_88 = []byte(`
`)
-var topic_alt_88 = []byte(`
+var topic_alt_90 = []byte(`
diff --git a/template_topic.go b/template_topic.go
index 53458c8c..9cb30820 100644
--- a/template_topic.go
+++ b/template_topic.go
@@ -109,169 +109,176 @@ if tmpl_topic_vars.CurrentUser.Perms.EditTopic {
w.Write(topic_16)
w.Write([]byte(tmpl_topic_vars.Topic.Title))
w.Write(topic_17)
-if tmpl_topic_vars.CurrentUser.Perms.CloseTopic {
+}
w.Write(topic_18)
-}
-w.Write(topic_19)
-}
-w.Write(topic_20)
w.Write([]byte(tmpl_topic_vars.Topic.ClassName))
-w.Write(topic_21)
+w.Write(topic_19)
if tmpl_topic_vars.Topic.Avatar != "" {
-w.Write(topic_22)
+w.Write(topic_20)
w.Write([]byte(tmpl_topic_vars.Topic.Avatar))
-w.Write(topic_23)
+w.Write(topic_21)
if tmpl_topic_vars.Topic.ContentLines <= 5 {
+w.Write(topic_22)
+}
+w.Write(topic_23)
+}
w.Write(topic_24)
-}
+w.Write([]byte(tmpl_topic_vars.Topic.Content))
w.Write(topic_25)
-}
+w.Write([]byte(tmpl_topic_vars.Topic.Content))
w.Write(topic_26)
-w.Write([]byte(tmpl_topic_vars.Topic.Content))
-w.Write(topic_27)
-w.Write([]byte(tmpl_topic_vars.Topic.Content))
-w.Write(topic_28)
w.Write([]byte(tmpl_topic_vars.Topic.UserLink))
-w.Write(topic_29)
+w.Write(topic_27)
w.Write([]byte(tmpl_topic_vars.Topic.CreatedByName))
-w.Write(topic_30)
+w.Write(topic_28)
if tmpl_topic_vars.CurrentUser.Perms.LikeItem {
-w.Write(topic_31)
+w.Write(topic_29)
w.Write([]byte(strconv.Itoa(tmpl_topic_vars.Topic.ID)))
-w.Write(topic_32)
+w.Write(topic_30)
if tmpl_topic_vars.Topic.Liked {
-w.Write(topic_33)
+w.Write(topic_31)
}
-w.Write(topic_34)
+w.Write(topic_32)
}
if tmpl_topic_vars.CurrentUser.Perms.EditTopic {
+w.Write(topic_33)
+w.Write([]byte(strconv.Itoa(tmpl_topic_vars.Topic.ID)))
+w.Write(topic_34)
+}
+if tmpl_topic_vars.CurrentUser.Perms.DeleteTopic {
w.Write(topic_35)
w.Write([]byte(strconv.Itoa(tmpl_topic_vars.Topic.ID)))
w.Write(topic_36)
}
-if tmpl_topic_vars.CurrentUser.Perms.DeleteTopic {
+if tmpl_topic_vars.CurrentUser.Perms.CloseTopic {
+if tmpl_topic_vars.Topic.IsClosed {
w.Write(topic_37)
w.Write([]byte(strconv.Itoa(tmpl_topic_vars.Topic.ID)))
w.Write(topic_38)
-}
-if tmpl_topic_vars.CurrentUser.Perms.PinTopic {
-if tmpl_topic_vars.Topic.Sticky {
+} else {
w.Write(topic_39)
w.Write([]byte(strconv.Itoa(tmpl_topic_vars.Topic.ID)))
w.Write(topic_40)
-} else {
+}
+}
+if tmpl_topic_vars.CurrentUser.Perms.PinTopic {
+if tmpl_topic_vars.Topic.Sticky {
w.Write(topic_41)
w.Write([]byte(strconv.Itoa(tmpl_topic_vars.Topic.ID)))
w.Write(topic_42)
+} else {
+w.Write(topic_43)
+w.Write([]byte(strconv.Itoa(tmpl_topic_vars.Topic.ID)))
+w.Write(topic_44)
}
}
if tmpl_topic_vars.CurrentUser.Perms.ViewIPs {
-w.Write(topic_43)
-w.Write([]byte(tmpl_topic_vars.Topic.IPAddress))
-w.Write(topic_44)
-}
w.Write(topic_45)
-w.Write([]byte(strconv.Itoa(tmpl_topic_vars.Topic.ID)))
+w.Write([]byte(tmpl_topic_vars.Topic.IPAddress))
w.Write(topic_46)
-w.Write([]byte(tmpl_topic_vars.CurrentUser.Session))
+}
w.Write(topic_47)
-if tmpl_topic_vars.Topic.LikeCount > 0 {
+w.Write([]byte(strconv.Itoa(tmpl_topic_vars.Topic.ID)))
w.Write(topic_48)
-w.Write([]byte(strconv.Itoa(tmpl_topic_vars.Topic.LikeCount)))
+w.Write([]byte(tmpl_topic_vars.CurrentUser.Session))
w.Write(topic_49)
+if tmpl_topic_vars.Topic.LikeCount > 0 {
+w.Write(topic_50)
+w.Write([]byte(strconv.Itoa(tmpl_topic_vars.Topic.LikeCount)))
+w.Write(topic_51)
}
if tmpl_topic_vars.Topic.Tag != "" {
-w.Write(topic_50)
-w.Write([]byte(tmpl_topic_vars.Topic.Tag))
-w.Write(topic_51)
-} else {
w.Write(topic_52)
-w.Write([]byte(strconv.Itoa(tmpl_topic_vars.Topic.Level)))
+w.Write([]byte(tmpl_topic_vars.Topic.Tag))
w.Write(topic_53)
-}
+} else {
w.Write(topic_54)
+w.Write([]byte(strconv.Itoa(tmpl_topic_vars.Topic.Level)))
+w.Write(topic_55)
+}
+w.Write(topic_56)
if len(tmpl_topic_vars.ItemList) != 0 {
for _, item := range tmpl_topic_vars.ItemList {
if item.ActionType != "" {
-w.Write(topic_55)
-w.Write([]byte(item.ActionIcon))
-w.Write(topic_56)
-w.Write([]byte(item.ActionType))
w.Write(topic_57)
-} else {
+w.Write([]byte(item.ActionIcon))
w.Write(topic_58)
-w.Write([]byte(item.ClassName))
+w.Write([]byte(item.ActionType))
w.Write(topic_59)
-if item.Avatar != "" {
+} else {
w.Write(topic_60)
-w.Write([]byte(item.Avatar))
+w.Write([]byte(item.ClassName))
w.Write(topic_61)
-if item.ContentLines <= 5 {
+if item.Avatar != "" {
w.Write(topic_62)
-}
+w.Write([]byte(item.Avatar))
w.Write(topic_63)
-}
+if item.ContentLines <= 5 {
w.Write(topic_64)
-w.Write([]byte(item.ContentHtml))
+}
w.Write(topic_65)
-w.Write([]byte(item.UserLink))
+}
w.Write(topic_66)
-w.Write([]byte(item.CreatedByName))
+w.Write([]byte(item.ContentHtml))
w.Write(topic_67)
-if tmpl_topic_vars.CurrentUser.Perms.LikeItem {
+w.Write([]byte(item.UserLink))
w.Write(topic_68)
-w.Write([]byte(strconv.Itoa(item.ID)))
+w.Write([]byte(item.CreatedByName))
w.Write(topic_69)
-if item.Liked {
+if tmpl_topic_vars.CurrentUser.Perms.LikeItem {
w.Write(topic_70)
-}
-w.Write(topic_71)
-}
-if tmpl_topic_vars.CurrentUser.Perms.EditReply {
-w.Write(topic_72)
w.Write([]byte(strconv.Itoa(item.ID)))
+w.Write(topic_71)
+if item.Liked {
+w.Write(topic_72)
+}
w.Write(topic_73)
}
-if tmpl_topic_vars.CurrentUser.Perms.DeleteReply {
+if tmpl_topic_vars.CurrentUser.Perms.EditReply {
w.Write(topic_74)
w.Write([]byte(strconv.Itoa(item.ID)))
w.Write(topic_75)
}
-if tmpl_topic_vars.CurrentUser.Perms.ViewIPs {
+if tmpl_topic_vars.CurrentUser.Perms.DeleteReply {
w.Write(topic_76)
-w.Write([]byte(item.IPAddress))
+w.Write([]byte(strconv.Itoa(item.ID)))
w.Write(topic_77)
}
+if tmpl_topic_vars.CurrentUser.Perms.ViewIPs {
w.Write(topic_78)
-w.Write([]byte(strconv.Itoa(item.ID)))
+w.Write([]byte(item.IPAddress))
w.Write(topic_79)
-w.Write([]byte(tmpl_topic_vars.CurrentUser.Session))
+}
w.Write(topic_80)
-if item.LikeCount > 0 {
+w.Write([]byte(strconv.Itoa(item.ID)))
w.Write(topic_81)
-w.Write([]byte(strconv.Itoa(item.LikeCount)))
+w.Write([]byte(tmpl_topic_vars.CurrentUser.Session))
w.Write(topic_82)
+if item.LikeCount > 0 {
+w.Write(topic_83)
+w.Write([]byte(strconv.Itoa(item.LikeCount)))
+w.Write(topic_84)
}
if item.Tag != "" {
-w.Write(topic_83)
-w.Write([]byte(item.Tag))
-w.Write(topic_84)
-} else {
w.Write(topic_85)
-w.Write([]byte(strconv.Itoa(item.Level)))
+w.Write([]byte(item.Tag))
w.Write(topic_86)
-}
+} else {
w.Write(topic_87)
-}
-}
-}
+w.Write([]byte(strconv.Itoa(item.Level)))
w.Write(topic_88)
-if tmpl_topic_vars.CurrentUser.Perms.CreateReply {
-w.Write(topic_89)
-w.Write([]byte(strconv.Itoa(tmpl_topic_vars.Topic.ID)))
-w.Write(topic_90)
}
+w.Write(topic_89)
+}
+}
+}
+w.Write(topic_90)
+if tmpl_topic_vars.CurrentUser.Perms.CreateReply {
w.Write(topic_91)
+w.Write([]byte(strconv.Itoa(tmpl_topic_vars.Topic.ID)))
+w.Write(topic_92)
+}
+w.Write(topic_93)
w.Write(footer_0)
if len(tmpl_topic_vars.Header.Themes) != 0 {
for _, item := range tmpl_topic_vars.Header.Themes {
diff --git a/template_topic_alt.go b/template_topic_alt.go
index ed48f6da..ec2748e7 100644
--- a/template_topic_alt.go
+++ b/template_topic_alt.go
@@ -108,163 +108,170 @@ if tmpl_topic_alt_vars.CurrentUser.Perms.EditTopic {
w.Write(topic_alt_15)
w.Write([]byte(tmpl_topic_alt_vars.Topic.Title))
w.Write(topic_alt_16)
-if tmpl_topic_alt_vars.CurrentUser.Perms.CloseTopic {
+}
w.Write(topic_alt_17)
-}
-w.Write(topic_alt_18)
-}
-w.Write(topic_alt_19)
w.Write([]byte(tmpl_topic_alt_vars.Topic.Avatar))
-w.Write(topic_alt_20)
+w.Write(topic_alt_18)
w.Write([]byte(tmpl_topic_alt_vars.Topic.UserLink))
-w.Write(topic_alt_21)
+w.Write(topic_alt_19)
w.Write([]byte(tmpl_topic_alt_vars.Topic.CreatedByName))
-w.Write(topic_alt_22)
+w.Write(topic_alt_20)
if tmpl_topic_alt_vars.Topic.Tag != "" {
-w.Write(topic_alt_23)
+w.Write(topic_alt_21)
w.Write([]byte(tmpl_topic_alt_vars.Topic.Tag))
-w.Write(topic_alt_24)
+w.Write(topic_alt_22)
} else {
-w.Write(topic_alt_25)
+w.Write(topic_alt_23)
w.Write([]byte(strconv.Itoa(tmpl_topic_alt_vars.Topic.Level)))
-w.Write(topic_alt_26)
+w.Write(topic_alt_24)
}
+w.Write(topic_alt_25)
+w.Write([]byte(tmpl_topic_alt_vars.Topic.Content))
+w.Write(topic_alt_26)
+w.Write([]byte(tmpl_topic_alt_vars.Topic.Content))
w.Write(topic_alt_27)
-w.Write([]byte(tmpl_topic_alt_vars.Topic.Content))
-w.Write(topic_alt_28)
-w.Write([]byte(tmpl_topic_alt_vars.Topic.Content))
-w.Write(topic_alt_29)
if tmpl_topic_alt_vars.CurrentUser.Loggedin {
if tmpl_topic_alt_vars.CurrentUser.Perms.LikeItem {
+w.Write(topic_alt_28)
+w.Write([]byte(strconv.Itoa(tmpl_topic_alt_vars.Topic.ID)))
+w.Write(topic_alt_29)
+}
+if tmpl_topic_alt_vars.CurrentUser.Perms.EditTopic {
w.Write(topic_alt_30)
w.Write([]byte(strconv.Itoa(tmpl_topic_alt_vars.Topic.ID)))
w.Write(topic_alt_31)
}
-if tmpl_topic_alt_vars.CurrentUser.Perms.EditTopic {
+if tmpl_topic_alt_vars.CurrentUser.Perms.DeleteTopic {
w.Write(topic_alt_32)
w.Write([]byte(strconv.Itoa(tmpl_topic_alt_vars.Topic.ID)))
w.Write(topic_alt_33)
}
-if tmpl_topic_alt_vars.CurrentUser.Perms.DeleteTopic {
+if tmpl_topic_alt_vars.CurrentUser.Perms.CloseTopic {
+if tmpl_topic_alt_vars.Topic.IsClosed {
w.Write(topic_alt_34)
w.Write([]byte(strconv.Itoa(tmpl_topic_alt_vars.Topic.ID)))
w.Write(topic_alt_35)
-}
-if tmpl_topic_alt_vars.CurrentUser.Perms.PinTopic {
-if tmpl_topic_alt_vars.Topic.Sticky {
+} else {
w.Write(topic_alt_36)
w.Write([]byte(strconv.Itoa(tmpl_topic_alt_vars.Topic.ID)))
w.Write(topic_alt_37)
-} else {
+}
+}
+if tmpl_topic_alt_vars.CurrentUser.Perms.PinTopic {
+if tmpl_topic_alt_vars.Topic.Sticky {
w.Write(topic_alt_38)
w.Write([]byte(strconv.Itoa(tmpl_topic_alt_vars.Topic.ID)))
w.Write(topic_alt_39)
-}
-}
+} else {
w.Write(topic_alt_40)
w.Write([]byte(strconv.Itoa(tmpl_topic_alt_vars.Topic.ID)))
w.Write(topic_alt_41)
-w.Write([]byte(tmpl_topic_alt_vars.CurrentUser.Session))
-w.Write(topic_alt_42)
}
-if tmpl_topic_alt_vars.Topic.LikeCount > 0 {
+}
+w.Write(topic_alt_42)
+w.Write([]byte(strconv.Itoa(tmpl_topic_alt_vars.Topic.ID)))
w.Write(topic_alt_43)
-w.Write([]byte(strconv.Itoa(tmpl_topic_alt_vars.Topic.LikeCount)))
+w.Write([]byte(tmpl_topic_alt_vars.CurrentUser.Session))
w.Write(topic_alt_44)
}
+if tmpl_topic_alt_vars.Topic.LikeCount > 0 {
w.Write(topic_alt_45)
-w.Write([]byte(tmpl_topic_alt_vars.Topic.CreatedAt))
+w.Write([]byte(strconv.Itoa(tmpl_topic_alt_vars.Topic.LikeCount)))
w.Write(topic_alt_46)
-if tmpl_topic_alt_vars.CurrentUser.Perms.ViewIPs {
-w.Write(topic_alt_47)
-w.Write([]byte(tmpl_topic_alt_vars.Topic.IPAddress))
-w.Write(topic_alt_48)
}
+w.Write(topic_alt_47)
+w.Write([]byte(tmpl_topic_alt_vars.Topic.CreatedAt))
+w.Write(topic_alt_48)
+if tmpl_topic_alt_vars.CurrentUser.Perms.ViewIPs {
w.Write(topic_alt_49)
+w.Write([]byte(tmpl_topic_alt_vars.Topic.IPAddress))
+w.Write(topic_alt_50)
+}
+w.Write(topic_alt_51)
if len(tmpl_topic_alt_vars.ItemList) != 0 {
for _, item := range tmpl_topic_alt_vars.ItemList {
-w.Write(topic_alt_50)
-if item.ActionType != "" {
-w.Write(topic_alt_51)
-}
w.Write(topic_alt_52)
-w.Write([]byte(item.Avatar))
-w.Write(topic_alt_53)
-w.Write([]byte(item.UserLink))
-w.Write(topic_alt_54)
-w.Write([]byte(item.CreatedByName))
-w.Write(topic_alt_55)
-if item.Tag != "" {
-w.Write(topic_alt_56)
-w.Write([]byte(item.Tag))
-w.Write(topic_alt_57)
-} else {
-w.Write(topic_alt_58)
-w.Write([]byte(strconv.Itoa(item.Level)))
-w.Write(topic_alt_59)
-}
-w.Write(topic_alt_60)
if item.ActionType != "" {
+w.Write(topic_alt_53)
+}
+w.Write(topic_alt_54)
+w.Write([]byte(item.Avatar))
+w.Write(topic_alt_55)
+w.Write([]byte(item.UserLink))
+w.Write(topic_alt_56)
+w.Write([]byte(item.CreatedByName))
+w.Write(topic_alt_57)
+if item.Tag != "" {
+w.Write(topic_alt_58)
+w.Write([]byte(item.Tag))
+w.Write(topic_alt_59)
+} else {
+w.Write(topic_alt_60)
+w.Write([]byte(strconv.Itoa(item.Level)))
w.Write(topic_alt_61)
}
w.Write(topic_alt_62)
if item.ActionType != "" {
w.Write(topic_alt_63)
-w.Write([]byte(item.ActionIcon))
+}
w.Write(topic_alt_64)
-w.Write([]byte(item.ActionType))
+if item.ActionType != "" {
w.Write(topic_alt_65)
-} else {
+w.Write([]byte(item.ActionIcon))
w.Write(topic_alt_66)
-w.Write([]byte(item.ContentHtml))
+w.Write([]byte(item.ActionType))
w.Write(topic_alt_67)
+} else {
+w.Write(topic_alt_68)
+w.Write([]byte(item.ContentHtml))
+w.Write(topic_alt_69)
if tmpl_topic_alt_vars.CurrentUser.Loggedin {
if tmpl_topic_alt_vars.CurrentUser.Perms.LikeItem {
-w.Write(topic_alt_68)
-w.Write([]byte(strconv.Itoa(item.ID)))
-w.Write(topic_alt_69)
-}
-if tmpl_topic_alt_vars.CurrentUser.Perms.EditReply {
w.Write(topic_alt_70)
w.Write([]byte(strconv.Itoa(item.ID)))
w.Write(topic_alt_71)
}
-if tmpl_topic_alt_vars.CurrentUser.Perms.DeleteReply {
+if tmpl_topic_alt_vars.CurrentUser.Perms.EditReply {
w.Write(topic_alt_72)
w.Write([]byte(strconv.Itoa(item.ID)))
w.Write(topic_alt_73)
}
+if tmpl_topic_alt_vars.CurrentUser.Perms.DeleteReply {
w.Write(topic_alt_74)
w.Write([]byte(strconv.Itoa(item.ID)))
w.Write(topic_alt_75)
-w.Write([]byte(tmpl_topic_alt_vars.CurrentUser.Session))
-w.Write(topic_alt_76)
}
-if item.LikeCount > 0 {
+w.Write(topic_alt_76)
+w.Write([]byte(strconv.Itoa(item.ID)))
w.Write(topic_alt_77)
-w.Write([]byte(strconv.Itoa(item.LikeCount)))
+w.Write([]byte(tmpl_topic_alt_vars.CurrentUser.Session))
w.Write(topic_alt_78)
}
+if item.LikeCount > 0 {
w.Write(topic_alt_79)
-w.Write([]byte(item.CreatedAt))
+w.Write([]byte(strconv.Itoa(item.LikeCount)))
w.Write(topic_alt_80)
-if tmpl_topic_alt_vars.CurrentUser.Perms.ViewIPs {
+}
w.Write(topic_alt_81)
-w.Write([]byte(item.IPAddress))
+w.Write([]byte(item.CreatedAt))
w.Write(topic_alt_82)
-}
+if tmpl_topic_alt_vars.CurrentUser.Perms.ViewIPs {
w.Write(topic_alt_83)
-}
+w.Write([]byte(item.IPAddress))
w.Write(topic_alt_84)
}
-}
w.Write(topic_alt_85)
-if tmpl_topic_alt_vars.CurrentUser.Perms.CreateReply {
-w.Write(topic_alt_86)
-w.Write([]byte(strconv.Itoa(tmpl_topic_alt_vars.Topic.ID)))
-w.Write(topic_alt_87)
}
+w.Write(topic_alt_86)
+}
+}
+w.Write(topic_alt_87)
+if tmpl_topic_alt_vars.CurrentUser.Perms.CreateReply {
w.Write(topic_alt_88)
+w.Write([]byte(strconv.Itoa(tmpl_topic_alt_vars.Topic.ID)))
+w.Write(topic_alt_89)
+}
+w.Write(topic_alt_90)
w.Write(footer_0)
if len(tmpl_topic_alt_vars.Header.Themes) != 0 {
for _, item := range tmpl_topic_alt_vars.Header.Themes {
diff --git a/templates/ip-search.html b/templates/ip-search-results.html
similarity index 100%
rename from templates/ip-search.html
rename to templates/ip-search-results.html
diff --git a/templates/topic.html b/templates/topic.html
index 88c35464..e49c04db 100644
--- a/templates/topic.html
+++ b/templates/topic.html
@@ -13,13 +13,9 @@
{{.Topic.Title}}
- {{if .Topic.IsClosed}}🔒︎{{end}}
+ {{if .Topic.IsClosed}}🔒︎{{end}}
{{if .CurrentUser.Perms.EditTopic}}
- {{if .CurrentUser.Perms.CloseTopic}}{{end}}
{{end}}
@@ -40,6 +36,8 @@
{{if .CurrentUser.Perms.DeleteTopic}}
{{end}}
+ {{if .CurrentUser.Perms.CloseTopic}}{{if .Topic.IsClosed}}
{{else}}
{{end}}{{end}}
+
{{if .CurrentUser.Perms.PinTopic}}{{if .Topic.Sticky}}
{{else}}
{{end}}{{end}}
{{if .CurrentUser.Perms.ViewIPs}}
{{end}}
diff --git a/templates/topic_alt.html b/templates/topic_alt.html
index 1eedccba..3a88c875 100644
--- a/templates/topic_alt.html
+++ b/templates/topic_alt.html
@@ -9,13 +9,10 @@