Added support for phrases in templates.
The language of the end-user is now tracked and presented in the Analytics Manager. Profile owners now get alerts when someone posts on their profiles. The login page is now transpiled, estimated to be sixty times faster. The registration page is now transpiled, estimated to be sixty times faster. The IP Search page is now transpiled, estimated to be sixty times faster. The error pages are now transpiled, estimated to be sixty times faster. The login page now uses phrases. The registration page now uses phrases. IP Search now uses phrases. Renamed the ip-search template to ip_search. Alerts are now held in an alertbox container div. Added ids for the main container divs for the account manager sections. Added an id to the main container for the topic list template. Added an id to the main container for the forum list template. Added an id to the main container for the forum template. Added an avatar box CSS class for the avatar box in the account manager's avatar page. Did a bit of renaming for a future refactor in the routes counter. Did a bit of renaming for a future refactor in the operating system counter. A notice is shown to the user now when their account is inactive. The account activation status is now fetched by the user store. We now track Slackbot. You can now prepend strings to the start of router.DumpRequest request dumps to avoid tearing these bits of contextual data away from the bodies. .action file extensions are now seen as suspicious by the router. Moved routeWebsockets to common.RouteWebsockets for now. Moved routeCreateReplySubmit to routes.CreateReplySubmit. Moved alert.go into common. Moved the WebSockets logic into common. Escape strings a little earlier in the analytics routes and use integers instead of strings where possible. We now show a success notification when you update a user via the User Manager. Split the configuration properties off from CTemplateSet into CTemplateConfig. Renamed some of the properties of CTemplateSet to make them easier to understand. Removed some obsolete properties from CTemplateSet. Did a bit of spring cleaning in the template transpiler to cut down on unneccessary lines and to reduce duplication. Fixed a double else bug in ranges over maps in the template transpiler. Split the minifiers off the main template transpilation file into their own file. Refactored some of the routes which rely on alerts to use shared functions rather than having unique implementations in the routes themselves. All Themes Except Cosora: Refactored the opt nodes to make it easier to roll out bulk moderation. Shadow: Improved the notice CSS. Tweaked the sticky border colour. Cosora: The theme JS file now uses strict mode. Notices are shunted under rowhead with JS now, although this change might be reverted soon. Added CSS for notices. Fixed the padding under the avatar box in the account manager avatar page. Schema: Added the viewchunks_langs table.
This commit is contained in:
parent
8ecc637ab9
commit
5a8b994877
164
alerts.go
164
alerts.go
@ -1,164 +0,0 @@
|
||||
/*
|
||||
*
|
||||
* Gosora Alerts System
|
||||
* Copyright Azareal 2017 - 2018
|
||||
*
|
||||
*/
|
||||
package main
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"log"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"./common"
|
||||
)
|
||||
|
||||
// These notes are for me, don't worry about it too much ^_^
|
||||
/*
|
||||
"You received a friend invite from {user}"
|
||||
"{x}{mentioned you on}{user}{'s profile}"
|
||||
"{x}{mentioned you in}{topic}"
|
||||
"{x}{likes}{you}"
|
||||
"{x}{liked}{your topic}{topic}"
|
||||
"{x}{liked}{your post on}{user}{'s profile}" todo
|
||||
"{x}{liked}{your post in}{topic}"
|
||||
"{x}{replied to}{your post in}{topic}" todo
|
||||
"{x}{replied to}{topic}"
|
||||
"{x}{replied to}{your topic}{topic}"
|
||||
"{x}{created a new topic}{topic}"
|
||||
*/
|
||||
|
||||
func buildAlert(asid int, event string, elementType string, actorID int, targetUserID int, elementID int, user common.User /* The current user */) (string, error) {
|
||||
var targetUser *common.User
|
||||
|
||||
actor, err := common.Users.Get(actorID)
|
||||
if err != nil {
|
||||
return "", errors.New("Unable to find the actor")
|
||||
}
|
||||
|
||||
/*if elementType != "forum" {
|
||||
targetUser, err = users.Get(targetUser_id)
|
||||
if err != nil {
|
||||
LocalErrorJS("Unable to find the target user",w,r)
|
||||
return
|
||||
}
|
||||
}*/
|
||||
|
||||
if event == "friend_invite" {
|
||||
return `{"msg":"You received a friend invite from {0}","sub":["` + actor.Name + `"],"path":"` + actor.Link + `","avatar":"` + strings.Replace(actor.Avatar, "/", "\\/", -1) + `","asid":"` + strconv.Itoa(asid) + `"}`, nil
|
||||
}
|
||||
|
||||
var act, postAct, url, area string
|
||||
var startFrag, endFrag string
|
||||
switch elementType {
|
||||
case "forum":
|
||||
if event == "reply" {
|
||||
act = "created a new topic"
|
||||
topic, err := common.Topics.Get(elementID)
|
||||
if err != nil {
|
||||
common.DebugLogf("Unable to find linked topic %d", elementID)
|
||||
return "", errors.New("Unable to find the linked topic")
|
||||
}
|
||||
url = topic.Link
|
||||
area = topic.Title
|
||||
// Store the forum ID in the targetUser column instead of making a new one? o.O
|
||||
// Add an additional column for extra information later on when we add the ability to link directly to posts. We don't need the forum data for now...
|
||||
} else {
|
||||
act = "did something in a forum"
|
||||
}
|
||||
case "topic":
|
||||
topic, err := common.Topics.Get(elementID)
|
||||
if err != nil {
|
||||
common.DebugLogf("Unable to find linked topic %d", elementID)
|
||||
return "", errors.New("Unable to find the linked topic")
|
||||
}
|
||||
url = topic.Link
|
||||
area = topic.Title
|
||||
|
||||
if targetUserID == user.ID {
|
||||
postAct = " your topic"
|
||||
}
|
||||
case "user":
|
||||
targetUser, err = common.Users.Get(elementID)
|
||||
if err != nil {
|
||||
common.DebugLogf("Unable to find target user %d", elementID)
|
||||
return "", errors.New("Unable to find the target user")
|
||||
}
|
||||
area = targetUser.Name
|
||||
endFrag = "'s profile"
|
||||
url = targetUser.Link
|
||||
case "post":
|
||||
topic, err := common.TopicByReplyID(elementID)
|
||||
if err != nil {
|
||||
return "", errors.New("Unable to find the linked reply or parent topic")
|
||||
}
|
||||
url = topic.Link
|
||||
area = topic.Title
|
||||
if targetUserID == user.ID {
|
||||
postAct = " your post in"
|
||||
}
|
||||
default:
|
||||
return "", errors.New("Invalid elementType")
|
||||
}
|
||||
|
||||
switch event {
|
||||
case "like":
|
||||
if elementType == "user" {
|
||||
act = "likes"
|
||||
endFrag = ""
|
||||
if targetUser.ID == user.ID {
|
||||
area = "you"
|
||||
}
|
||||
} else {
|
||||
act = "liked"
|
||||
}
|
||||
case "mention":
|
||||
if elementType == "user" {
|
||||
act = "mentioned you on"
|
||||
} else {
|
||||
act = "mentioned you in"
|
||||
postAct = ""
|
||||
}
|
||||
case "reply":
|
||||
act = "replied to"
|
||||
}
|
||||
|
||||
return `{"msg":"{0} ` + startFrag + act + postAct + ` {1}` + endFrag + `","sub":["` + actor.Name + `","` + area + `"],"path":"` + url + `","avatar":"` + actor.Avatar + `","asid":"` + strconv.Itoa(asid) + `"}`, nil
|
||||
}
|
||||
|
||||
func notifyWatchers(asid int64) {
|
||||
rows, err := stmts.getWatchers.Query(asid)
|
||||
if err != nil && err != ErrNoRows {
|
||||
log.Fatal(err.Error())
|
||||
return
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
var uid int
|
||||
var uids []int
|
||||
for rows.Next() {
|
||||
err := rows.Scan(&uid)
|
||||
if err != nil {
|
||||
log.Fatal(err.Error())
|
||||
return
|
||||
}
|
||||
uids = append(uids, uid)
|
||||
}
|
||||
err = rows.Err()
|
||||
if err != nil {
|
||||
log.Fatal(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
var actorID, targetUserID, elementID int
|
||||
var event, elementType string
|
||||
err = stmts.getActivityEntry.QueryRow(asid).Scan(&actorID, &targetUserID, &event, &elementType, &elementID)
|
||||
if err != nil && err != ErrNoRows {
|
||||
log.Fatal(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
_ = wsHub.pushAlerts(uids, int(asid), event, elementType, actorID, targetUserID, elementID)
|
||||
}
|
243
common/alerts.go
Normal file
243
common/alerts.go
Normal file
@ -0,0 +1,243 @@
|
||||
/*
|
||||
*
|
||||
* Gosora Alerts System
|
||||
* Copyright Azareal 2017 - 2018
|
||||
*
|
||||
*/
|
||||
package common
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"errors"
|
||||
"log"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"../query_gen/lib"
|
||||
)
|
||||
|
||||
type AlertStmts struct {
|
||||
addActivity *sql.Stmt
|
||||
notifyWatchers *sql.Stmt
|
||||
notifyOne *sql.Stmt
|
||||
getWatchers *sql.Stmt
|
||||
getActivityEntry *sql.Stmt
|
||||
}
|
||||
|
||||
var alertStmts AlertStmts
|
||||
|
||||
// TODO: Move these statements into some sort of activity abstraction
|
||||
// TODO: Rewrite the alerts logic
|
||||
func init() {
|
||||
DbInits.Add(func(acc *qgen.Accumulator) error {
|
||||
alertStmts = AlertStmts{
|
||||
addActivity: acc.Insert("activity_stream").Columns("actor, targetUser, event, elementType, elementID").Fields("?,?,?,?,?").Prepare(),
|
||||
notifyWatchers: acc.SimpleInsertInnerJoin(
|
||||
qgen.DBInsert{"activity_stream_matches", "watcher, asid", ""},
|
||||
qgen.DBJoin{"activity_stream", "activity_subscriptions", "activity_subscriptions.user, activity_stream.asid", "activity_subscriptions.targetType = activity_stream.elementType AND activity_subscriptions.targetID = activity_stream.elementID AND activity_subscriptions.user != activity_stream.actor", "asid = ?", "", ""},
|
||||
),
|
||||
notifyOne: acc.Insert("activity_stream_matches").Columns("watcher, asid").Fields("?,?").Prepare(),
|
||||
getWatchers: acc.SimpleInnerJoin("activity_stream", "activity_subscriptions", "activity_subscriptions.user", "activity_subscriptions.targetType = activity_stream.elementType AND activity_subscriptions.targetID = activity_stream.elementID AND activity_subscriptions.user != activity_stream.actor", "asid = ?", "", ""),
|
||||
getActivityEntry: acc.Select("activity_stream").Columns("actor, targetUser, event, elementType, elementID").Where("asid = ?").Prepare(),
|
||||
}
|
||||
return acc.FirstError()
|
||||
})
|
||||
}
|
||||
|
||||
// These notes are for me, don't worry about it too much ^_^
|
||||
/*
|
||||
"You received a friend invite from {user}"
|
||||
"{x}{mentioned you on}{user}{'s profile}"
|
||||
"{x}{mentioned you in}{topic}"
|
||||
"{x}{likes}{you}"
|
||||
"{x}{liked}{your topic}{topic}"
|
||||
"{x}{liked}{your post on}{user}{'s profile}" todo
|
||||
"{x}{liked}{your post in}{topic}"
|
||||
"{x}{replied to}{your post in}{topic}" todo
|
||||
"{x}{replied to}{topic}"
|
||||
"{x}{replied to}{your topic}{topic}"
|
||||
"{x}{created a new topic}{topic}"
|
||||
*/
|
||||
|
||||
func BuildAlert(asid int, event string, elementType string, actorID int, targetUserID int, elementID int, user User /* The current user */) (string, error) {
|
||||
var targetUser *User
|
||||
|
||||
actor, err := Users.Get(actorID)
|
||||
if err != nil {
|
||||
return "", errors.New("Unable to find the actor")
|
||||
}
|
||||
|
||||
/*if elementType != "forum" {
|
||||
targetUser, err = users.Get(targetUser_id)
|
||||
if err != nil {
|
||||
LocalErrorJS("Unable to find the target user",w,r)
|
||||
return
|
||||
}
|
||||
}*/
|
||||
|
||||
if event == "friend_invite" {
|
||||
return `{"msg":"You received a friend invite from {0}","sub":["` + actor.Name + `"],"path":"` + actor.Link + `","avatar":"` + strings.Replace(actor.Avatar, "/", "\\/", -1) + `","asid":"` + strconv.Itoa(asid) + `"}`, nil
|
||||
}
|
||||
|
||||
var act, postAct, url, area string
|
||||
var startFrag, endFrag string
|
||||
switch elementType {
|
||||
case "forum":
|
||||
if event == "reply" {
|
||||
act = "created a new topic"
|
||||
topic, err := Topics.Get(elementID)
|
||||
if err != nil {
|
||||
DebugLogf("Unable to find linked topic %d", elementID)
|
||||
return "", errors.New("Unable to find the linked topic")
|
||||
}
|
||||
url = topic.Link
|
||||
area = topic.Title
|
||||
// Store the forum ID in the targetUser column instead of making a new one? o.O
|
||||
// Add an additional column for extra information later on when we add the ability to link directly to posts. We don't need the forum data for now...
|
||||
} else {
|
||||
act = "did something in a forum"
|
||||
}
|
||||
case "topic":
|
||||
topic, err := Topics.Get(elementID)
|
||||
if err != nil {
|
||||
DebugLogf("Unable to find linked topic %d", elementID)
|
||||
return "", errors.New("Unable to find the linked topic")
|
||||
}
|
||||
url = topic.Link
|
||||
area = topic.Title
|
||||
|
||||
if targetUserID == user.ID {
|
||||
postAct = " your topic"
|
||||
}
|
||||
case "user":
|
||||
targetUser, err = Users.Get(elementID)
|
||||
if err != nil {
|
||||
DebugLogf("Unable to find target user %d", elementID)
|
||||
return "", errors.New("Unable to find the target user")
|
||||
}
|
||||
area = targetUser.Name
|
||||
endFrag = "'s profile"
|
||||
url = targetUser.Link
|
||||
case "post":
|
||||
topic, err := TopicByReplyID(elementID)
|
||||
if err != nil {
|
||||
return "", errors.New("Unable to find the linked reply or parent topic")
|
||||
}
|
||||
url = topic.Link
|
||||
area = topic.Title
|
||||
if targetUserID == user.ID {
|
||||
postAct = " your post in"
|
||||
}
|
||||
default:
|
||||
return "", errors.New("Invalid elementType")
|
||||
}
|
||||
|
||||
switch event {
|
||||
case "like":
|
||||
if elementType == "user" {
|
||||
act = "likes"
|
||||
endFrag = ""
|
||||
if targetUser.ID == user.ID {
|
||||
area = "you"
|
||||
}
|
||||
} else {
|
||||
act = "liked"
|
||||
}
|
||||
case "mention":
|
||||
if elementType == "user" {
|
||||
act = "mentioned you on"
|
||||
} else {
|
||||
act = "mentioned you in"
|
||||
postAct = ""
|
||||
}
|
||||
case "reply":
|
||||
act = "replied to"
|
||||
}
|
||||
|
||||
return `{"msg":"{0} ` + startFrag + act + postAct + ` {1}` + endFrag + `","sub":["` + actor.Name + `","` + area + `"],"path":"` + url + `","avatar":"` + actor.Avatar + `","asid":"` + strconv.Itoa(asid) + `"}`, nil
|
||||
}
|
||||
|
||||
func AddActivityAndNotifyAll(actor int, targetUser int, event string, elementType string, elementID int) error {
|
||||
res, err := alertStmts.addActivity.Exec(actor, targetUser, event, elementType, elementID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
lastID, err := res.LastInsertId()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return NotifyWatchers(lastID)
|
||||
}
|
||||
|
||||
func AddActivityAndNotifyTarget(actor int, targetUser int, event string, elementType string, elementID int) error {
|
||||
res, err := alertStmts.addActivity.Exec(actor, targetUser, event, elementType, elementID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
lastID, err := res.LastInsertId()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = NotifyOne(targetUser, lastID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Live alerts, if the target is online and WebSockets is enabled
|
||||
_ = WsHub.pushAlert(targetUser, int(lastID), event, elementType, actor, targetUser, elementID)
|
||||
return nil
|
||||
}
|
||||
|
||||
func NotifyOne(watcher int, asid int64) error {
|
||||
_, err := alertStmts.notifyOne.Exec(watcher, asid)
|
||||
return err
|
||||
}
|
||||
|
||||
func NotifyWatchers(asid int64) error {
|
||||
_, err := alertStmts.notifyWatchers.Exec(asid)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Alert the subscribers about this without blocking us from doing something else
|
||||
if EnableWebsockets {
|
||||
go notifyWatchers(asid)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func notifyWatchers(asid int64) {
|
||||
rows, err := alertStmts.getWatchers.Query(asid)
|
||||
if err != nil && err != ErrNoRows {
|
||||
log.Fatal(err.Error())
|
||||
return
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
var uid int
|
||||
var uids []int
|
||||
for rows.Next() {
|
||||
err := rows.Scan(&uid)
|
||||
if err != nil {
|
||||
log.Fatal(err.Error())
|
||||
return
|
||||
}
|
||||
uids = append(uids, uid)
|
||||
}
|
||||
err = rows.Err()
|
||||
if err != nil {
|
||||
log.Fatal(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
var actorID, targetUserID, elementID int
|
||||
var event, elementType string
|
||||
err = alertStmts.getActivityEntry.QueryRow(asid).Scan(&actorID, &targetUserID, &event, &elementType, &elementID)
|
||||
if err != nil && err != ErrNoRows {
|
||||
log.Fatal(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
_ = WsHub.pushAlerts(uids, int(asid), event, elementType, actorID, targetUserID, elementID)
|
||||
}
|
166
common/counters/langs.go
Normal file
166
common/counters/langs.go
Normal file
@ -0,0 +1,166 @@
|
||||
package counters
|
||||
|
||||
import "database/sql"
|
||||
import ".."
|
||||
import "../../query_gen/lib"
|
||||
|
||||
var LangViewCounter *DefaultLangViewCounter
|
||||
|
||||
var langCodes = []string{
|
||||
"unknown",
|
||||
"af",
|
||||
"ar",
|
||||
"az",
|
||||
"be",
|
||||
"bg",
|
||||
"bs",
|
||||
"ca",
|
||||
"cs",
|
||||
"cy",
|
||||
"da",
|
||||
"de",
|
||||
"dv",
|
||||
"el",
|
||||
"en",
|
||||
"eo",
|
||||
"es",
|
||||
"et",
|
||||
"eu",
|
||||
"fa",
|
||||
"fi",
|
||||
"fo",
|
||||
"fr",
|
||||
"gl",
|
||||
"gu",
|
||||
"he",
|
||||
"hi",
|
||||
"hr",
|
||||
"hu",
|
||||
"hy",
|
||||
"id",
|
||||
"is",
|
||||
"it",
|
||||
"ja",
|
||||
"ka",
|
||||
"kk",
|
||||
"kn",
|
||||
"ko",
|
||||
"kok",
|
||||
"ky",
|
||||
"lt",
|
||||
"lv",
|
||||
"mi",
|
||||
"mk",
|
||||
"mn",
|
||||
"mr",
|
||||
"ms",
|
||||
"mt",
|
||||
"nb",
|
||||
"nl",
|
||||
"nn",
|
||||
"ns",
|
||||
"pa",
|
||||
"pl",
|
||||
"ps",
|
||||
"pt",
|
||||
"qu",
|
||||
"ro",
|
||||
"ru",
|
||||
"sa",
|
||||
"se",
|
||||
"sk",
|
||||
"sl",
|
||||
"sq",
|
||||
"sr",
|
||||
"sv",
|
||||
"sw",
|
||||
"syr",
|
||||
"ta",
|
||||
"te",
|
||||
"th",
|
||||
"tl",
|
||||
"tn",
|
||||
"tr",
|
||||
"tt",
|
||||
"ts",
|
||||
"uk",
|
||||
"ur",
|
||||
"uz",
|
||||
"vi",
|
||||
"xh",
|
||||
"zh",
|
||||
"zu",
|
||||
}
|
||||
|
||||
type DefaultLangViewCounter struct {
|
||||
buckets []*RWMutexCounterBucket //[OSID]count
|
||||
codesToIndices map[string]int
|
||||
insert *sql.Stmt
|
||||
}
|
||||
|
||||
func NewDefaultLangViewCounter() (*DefaultLangViewCounter, error) {
|
||||
acc := qgen.Builder.Accumulator()
|
||||
|
||||
var langBuckets = make([]*RWMutexCounterBucket, len(langCodes))
|
||||
for bucketID, _ := range langBuckets {
|
||||
langBuckets[bucketID] = &RWMutexCounterBucket{counter: 0}
|
||||
}
|
||||
var codesToIndices = make(map[string]int)
|
||||
for index, code := range langCodes {
|
||||
codesToIndices[code] = index
|
||||
}
|
||||
|
||||
counter := &DefaultLangViewCounter{
|
||||
buckets: langBuckets,
|
||||
codesToIndices: codesToIndices,
|
||||
insert: acc.Insert("viewchunks_langs").Columns("count, createdAt, lang").Fields("?,UTC_TIMESTAMP(),?").Prepare(),
|
||||
}
|
||||
|
||||
common.AddScheduledFifteenMinuteTask(counter.Tick)
|
||||
//common.AddScheduledSecondTask(counter.Tick)
|
||||
common.AddShutdownTask(counter.Tick)
|
||||
return counter, acc.FirstError()
|
||||
}
|
||||
|
||||
func (counter *DefaultLangViewCounter) Tick() error {
|
||||
for id, bucket := range counter.buckets {
|
||||
var count int
|
||||
bucket.RLock()
|
||||
count = bucket.counter
|
||||
bucket.counter = 0 // TODO: Add a SetZero method to reduce the amount of duplicate code between the OS and agent counters?
|
||||
bucket.RUnlock()
|
||||
|
||||
err := counter.insertChunk(count, id) // TODO: Bulk insert for speed?
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (counter *DefaultLangViewCounter) insertChunk(count int, id int) error {
|
||||
if count == 0 {
|
||||
return nil
|
||||
}
|
||||
var langCode = langCodes[id]
|
||||
common.DebugLogf("Inserting a viewchunk with a count of %d for lang %s (%d)", count, langCode, id)
|
||||
_, err := counter.insert.Exec(count, langCode)
|
||||
return err
|
||||
}
|
||||
|
||||
func (counter *DefaultLangViewCounter) Bump(langCode string) {
|
||||
id, ok := counter.codesToIndices[langCode]
|
||||
if !ok {
|
||||
// TODO: Tell the caller that the code's invalid
|
||||
id = 0 // Unknown
|
||||
}
|
||||
|
||||
// TODO: Test this check
|
||||
common.DebugDetail("counter.buckets[", id, "]: ", counter.buckets[id])
|
||||
if len(counter.buckets) <= id || id < 0 {
|
||||
return
|
||||
}
|
||||
counter.buckets[id].Lock()
|
||||
counter.buckets[id].counter++
|
||||
counter.buckets[id].Unlock()
|
||||
}
|
@ -8,8 +8,8 @@ var RouteViewCounter *DefaultRouteViewCounter
|
||||
|
||||
// TODO: Make this lockless?
|
||||
type DefaultRouteViewCounter struct {
|
||||
routeBuckets []*RWMutexCounterBucket //[RouteID]count
|
||||
insert *sql.Stmt
|
||||
buckets []*RWMutexCounterBucket //[RouteID]count
|
||||
insert *sql.Stmt
|
||||
}
|
||||
|
||||
func NewDefaultRouteViewCounter() (*DefaultRouteViewCounter, error) {
|
||||
@ -19,8 +19,8 @@ func NewDefaultRouteViewCounter() (*DefaultRouteViewCounter, error) {
|
||||
routeBuckets[bucketID] = &RWMutexCounterBucket{counter: 0}
|
||||
}
|
||||
counter := &DefaultRouteViewCounter{
|
||||
routeBuckets: routeBuckets,
|
||||
insert: acc.Insert("viewchunks").Columns("count, createdAt, route").Fields("?,UTC_TIMESTAMP(),?").Prepare(),
|
||||
buckets: routeBuckets,
|
||||
insert: acc.Insert("viewchunks").Columns("count, createdAt, route").Fields("?,UTC_TIMESTAMP(),?").Prepare(),
|
||||
}
|
||||
common.AddScheduledFifteenMinuteTask(counter.Tick) // There could be a lot of routes, so we don't want to be running this every second
|
||||
//common.AddScheduledSecondTask(counter.Tick)
|
||||
@ -29,7 +29,7 @@ func NewDefaultRouteViewCounter() (*DefaultRouteViewCounter, error) {
|
||||
}
|
||||
|
||||
func (counter *DefaultRouteViewCounter) Tick() error {
|
||||
for routeID, routeBucket := range counter.routeBuckets {
|
||||
for routeID, routeBucket := range counter.buckets {
|
||||
var count int
|
||||
routeBucket.RLock()
|
||||
count = routeBucket.counter
|
||||
@ -56,11 +56,11 @@ func (counter *DefaultRouteViewCounter) insertChunk(count int, route int) error
|
||||
|
||||
func (counter *DefaultRouteViewCounter) Bump(route int) {
|
||||
// TODO: Test this check
|
||||
common.DebugDetail("counter.routeBuckets[", route, "]: ", counter.routeBuckets[route])
|
||||
if len(counter.routeBuckets) <= route || route < 0 {
|
||||
common.DebugDetail("counter.buckets[", route, "]: ", counter.buckets[route])
|
||||
if len(counter.buckets) <= route || route < 0 {
|
||||
return
|
||||
}
|
||||
counter.routeBuckets[route].Lock()
|
||||
counter.routeBuckets[route].counter++
|
||||
counter.routeBuckets[route].Unlock()
|
||||
counter.buckets[route].Lock()
|
||||
counter.buckets[route].counter++
|
||||
counter.buckets[route].Unlock()
|
||||
}
|
||||
|
@ -7,8 +7,8 @@ import "../../query_gen/lib"
|
||||
var OSViewCounter *DefaultOSViewCounter
|
||||
|
||||
type DefaultOSViewCounter struct {
|
||||
osBuckets []*RWMutexCounterBucket //[OSID]count
|
||||
insert *sql.Stmt
|
||||
buckets []*RWMutexCounterBucket //[OSID]count
|
||||
insert *sql.Stmt
|
||||
}
|
||||
|
||||
func NewDefaultOSViewCounter() (*DefaultOSViewCounter, error) {
|
||||
@ -18,8 +18,8 @@ func NewDefaultOSViewCounter() (*DefaultOSViewCounter, error) {
|
||||
osBuckets[bucketID] = &RWMutexCounterBucket{counter: 0}
|
||||
}
|
||||
counter := &DefaultOSViewCounter{
|
||||
osBuckets: osBuckets,
|
||||
insert: acc.Insert("viewchunks_systems").Columns("count, createdAt, system").Fields("?,UTC_TIMESTAMP(),?").Prepare(),
|
||||
buckets: osBuckets,
|
||||
insert: acc.Insert("viewchunks_systems").Columns("count, createdAt, system").Fields("?,UTC_TIMESTAMP(),?").Prepare(),
|
||||
}
|
||||
common.AddScheduledFifteenMinuteTask(counter.Tick)
|
||||
//common.AddScheduledSecondTask(counter.Tick)
|
||||
@ -28,14 +28,14 @@ func NewDefaultOSViewCounter() (*DefaultOSViewCounter, error) {
|
||||
}
|
||||
|
||||
func (counter *DefaultOSViewCounter) Tick() error {
|
||||
for osID, osBucket := range counter.osBuckets {
|
||||
for id, bucket := range counter.buckets {
|
||||
var count int
|
||||
osBucket.RLock()
|
||||
count = osBucket.counter
|
||||
osBucket.counter = 0 // TODO: Add a SetZero method to reduce the amount of duplicate code between the OS and agent counters?
|
||||
osBucket.RUnlock()
|
||||
bucket.RLock()
|
||||
count = bucket.counter
|
||||
bucket.counter = 0 // TODO: Add a SetZero method to reduce the amount of duplicate code between the OS and agent counters?
|
||||
bucket.RUnlock()
|
||||
|
||||
err := counter.insertChunk(count, osID) // TODO: Bulk insert for speed?
|
||||
err := counter.insertChunk(count, id) // TODO: Bulk insert for speed?
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -53,13 +53,13 @@ func (counter *DefaultOSViewCounter) insertChunk(count int, os int) error {
|
||||
return err
|
||||
}
|
||||
|
||||
func (counter *DefaultOSViewCounter) Bump(os int) {
|
||||
func (counter *DefaultOSViewCounter) Bump(id int) {
|
||||
// TODO: Test this check
|
||||
common.DebugDetail("counter.osBuckets[", os, "]: ", counter.osBuckets[os])
|
||||
if len(counter.osBuckets) <= os || os < 0 {
|
||||
common.DebugDetail("counter.buckets[", id, "]: ", counter.buckets[id])
|
||||
if len(counter.buckets) <= id || id < 0 {
|
||||
return
|
||||
}
|
||||
counter.osBuckets[os].Lock()
|
||||
counter.osBuckets[os].counter++
|
||||
counter.osBuckets[os].Unlock()
|
||||
counter.buckets[id].Lock()
|
||||
counter.buckets[id].counter++
|
||||
counter.buckets[id].Unlock()
|
||||
}
|
||||
|
@ -290,7 +290,7 @@ func handleErrorTemplate(w http.ResponseWriter, r *http.Request, pi Page) {
|
||||
if RunPreRenderHook("pre_render_error", w, r, &pi.CurrentUser, &pi) {
|
||||
return
|
||||
}
|
||||
err := Templates.ExecuteTemplate(w, "error.html", pi)
|
||||
err := RunThemeTemplate(pi.Header.Theme.Name, "error", pi, w)
|
||||
if err != nil {
|
||||
LogError(err)
|
||||
}
|
||||
|
@ -1,12 +1,12 @@
|
||||
// +build no_ws
|
||||
|
||||
package main
|
||||
package common
|
||||
|
||||
import "errors"
|
||||
import "net/http"
|
||||
|
||||
// TODO: Disable WebSockets on high load? Add a Control Panel interface for disabling it?
|
||||
var enableWebsockets = false // Put this in caps for consistency with the other constants?
|
||||
var EnableWebsockets = false // Put this in caps for consistency with the other constants?
|
||||
|
||||
var wsHub WSHub
|
||||
var errWsNouser = errors.New("This user isn't connected via WebSockets")
|
||||
@ -38,5 +38,5 @@ func (hub *WSHub) pushAlerts(_ []int, _ int, _ string, _ string, _ int, _ int, _
|
||||
return errWsNouser
|
||||
}
|
||||
|
||||
func routeWebsockets(_ http.ResponseWriter, _ *http.Request, _ User) {
|
||||
func RouteWebsockets(_ http.ResponseWriter, _ *http.Request, _ User) {
|
||||
}
|
@ -44,12 +44,17 @@ type LanguagePack struct {
|
||||
Accounts map[string]string // TODO: Apply these phrases in the software proper
|
||||
UserAgents map[string]string
|
||||
OperatingSystems map[string]string
|
||||
HumanLanguages map[string]string
|
||||
Errors map[string]map[string]string // map[category]map[name]value
|
||||
PageTitles map[string]string
|
||||
TmplPhrases map[string]string
|
||||
|
||||
TmplIndicesToPhrases [][][]byte // [tmplID][index]phrase
|
||||
}
|
||||
|
||||
// TODO: Add the ability to edit language JSON files from the Control Panel and automatically scan the files for changes
|
||||
var langPacks sync.Map // nolint it is used
|
||||
var langPacks sync.Map // nolint it is used
|
||||
var langTmplIndicesToNames [][]string // [tmplID][index]phraseName
|
||||
|
||||
func InitPhrases() error {
|
||||
log.Print("Loading the language packs")
|
||||
@ -75,6 +80,15 @@ func InitPhrases() error {
|
||||
return err
|
||||
}
|
||||
|
||||
langPack.TmplIndicesToPhrases = make([][][]byte, len(langTmplIndicesToNames))
|
||||
for tmplID, phraseNames := range langTmplIndicesToNames {
|
||||
var phraseSet = make([][]byte, len(phraseNames))
|
||||
for index, phraseName := range phraseNames {
|
||||
phraseSet[index] = []byte(langPack.TmplPhrases[phraseName])
|
||||
}
|
||||
langPack.TmplIndicesToPhrases[tmplID] = phraseSet
|
||||
}
|
||||
|
||||
log.Print("Adding the '" + langPack.Name + "' language pack")
|
||||
langPacks.Store(langPack.Name, &langPack)
|
||||
langPackCount++
|
||||
@ -170,6 +184,14 @@ func GetOSPhrase(name string) (string, bool) {
|
||||
return res, true
|
||||
}
|
||||
|
||||
func GetHumanLangPhrase(name string) (string, bool) {
|
||||
res, ok := currentLangPack.Load().(*LanguagePack).HumanLanguages[name]
|
||||
if !ok {
|
||||
return "", false
|
||||
}
|
||||
return res, true
|
||||
}
|
||||
|
||||
// TODO: Does comma ok work with multi-dimensional maps?
|
||||
func GetErrorPhrase(category string, name string) string {
|
||||
res, ok := currentLangPack.Load().(*LanguagePack).Errors[category][name]
|
||||
@ -187,6 +209,14 @@ func GetTitlePhrase(name string) string {
|
||||
return res
|
||||
}
|
||||
|
||||
func GetTmplPhrase(name string) string {
|
||||
res, ok := currentLangPack.Load().(*LanguagePack).TmplPhrases[name]
|
||||
if !ok {
|
||||
return getPhrasePlaceholder()
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
func getPhrasePlaceholder() string {
|
||||
return "{name}"
|
||||
}
|
||||
@ -213,3 +243,14 @@ func ChangeLanguagePack(name string) (exists bool) {
|
||||
currentLangPack.Store(pack)
|
||||
return true
|
||||
}
|
||||
|
||||
// Template Transpiler Stuff
|
||||
|
||||
func RegisterTmplPhraseNames(phraseNames []string) (tmplID int) {
|
||||
langTmplIndicesToNames = append(langTmplIndicesToNames, phraseNames)
|
||||
return len(langTmplIndicesToNames) - 1
|
||||
}
|
||||
|
||||
func GetTmplPhrasesBytes(tmplID int) [][]byte {
|
||||
return currentLangPack.Load().(*LanguagePack).TmplIndicesToPhrases[tmplID]
|
||||
}
|
||||
|
@ -217,6 +217,9 @@ func userCheck(w http.ResponseWriter, r *http.Request, user *User) (headerVars *
|
||||
if user.IsBanned {
|
||||
headerVars.NoticeList = append(headerVars.NoticeList, "Your account has been suspended. Some of your permissions may have been revoked.")
|
||||
}
|
||||
if user.Loggedin && !user.Active {
|
||||
headerVars.NoticeList = append(headerVars.NoticeList, "Your account hasn't been activated yet. Some features may remain unavailable until it is.")
|
||||
}
|
||||
|
||||
if len(theme.Resources) > 0 {
|
||||
rlist := theme.Resources
|
||||
|
@ -22,6 +22,7 @@ type CTmpl struct {
|
||||
Imports []string
|
||||
}
|
||||
|
||||
// TODO: Stop duplicating these bits of code
|
||||
// nolint
|
||||
func interpreted_topic_template(pi TopicPage, w http.ResponseWriter) error {
|
||||
mapping, ok := Themes[DefaultThemeBox.Load().(string)].TemplatesMap["topic"]
|
||||
@ -80,18 +81,57 @@ var Template_create_topic_handle func(CreateTopicPage, http.ResponseWriter) erro
|
||||
return Templates.ExecuteTemplate(w, mapping+".html", pi)
|
||||
}
|
||||
|
||||
// nolint
|
||||
var Template_login_handle func(Page, http.ResponseWriter) error = func(pi Page, w http.ResponseWriter) error {
|
||||
mapping, ok := Themes[DefaultThemeBox.Load().(string)].TemplatesMap["login"]
|
||||
if !ok {
|
||||
mapping = "login"
|
||||
}
|
||||
return Templates.ExecuteTemplate(w, mapping+".html", pi)
|
||||
}
|
||||
|
||||
// nolint
|
||||
var Template_register_handle func(Page, http.ResponseWriter) error = func(pi Page, w http.ResponseWriter) error {
|
||||
mapping, ok := Themes[DefaultThemeBox.Load().(string)].TemplatesMap["register"]
|
||||
if !ok {
|
||||
mapping = "register"
|
||||
}
|
||||
return Templates.ExecuteTemplate(w, mapping+".html", pi)
|
||||
}
|
||||
|
||||
// nolint
|
||||
var Template_error_handle func(Page, http.ResponseWriter) error = func(pi Page, w http.ResponseWriter) error {
|
||||
mapping, ok := Themes[DefaultThemeBox.Load().(string)].TemplatesMap["error"]
|
||||
if !ok {
|
||||
mapping = "error"
|
||||
}
|
||||
return Templates.ExecuteTemplate(w, mapping+".html", pi)
|
||||
}
|
||||
|
||||
// nolint
|
||||
var Template_ip_search_handle func(IPSearchPage, http.ResponseWriter) error = func(pi IPSearchPage, w http.ResponseWriter) error {
|
||||
mapping, ok := Themes[DefaultThemeBox.Load().(string)].TemplatesMap["ip_search"]
|
||||
if !ok {
|
||||
mapping = "ip_search"
|
||||
}
|
||||
return Templates.ExecuteTemplate(w, mapping+".html", pi)
|
||||
}
|
||||
|
||||
// ? - Add template hooks?
|
||||
func compileTemplates() error {
|
||||
var config tmpl.CTemplateConfig
|
||||
config.Minify = Config.MinifyTemplates
|
||||
config.SuperDebug = Dev.TemplateDebug
|
||||
|
||||
var c tmpl.CTemplateSet
|
||||
c.Minify(Config.MinifyTemplates)
|
||||
c.SuperDebug(Dev.TemplateDebug)
|
||||
c.SetConfig(config)
|
||||
|
||||
// Schemas to train the template compiler on what to expect
|
||||
// TODO: Add support for interface{}s
|
||||
user := User{62, BuildProfileURL("fake-user", 62), "Fake User", "compiler@localhost", 0, false, false, false, false, false, false, GuestPerms, make(map[string]bool), "", false, "", "", "", "", "", 0, 0, "0.0.0.0.0", 0}
|
||||
user := User{62, BuildProfileURL("fake-user", 62), "Fake User", "compiler@localhost", 0, false, false, false, false, false, false, GuestPerms, make(map[string]bool), "", false, BuildAvatar(62, ""), "", "", "", "", 0, 0, "0.0.0.0.0", 0}
|
||||
// TODO: Do a more accurate level calculation for this?
|
||||
user2 := User{1, BuildProfileURL("admin-alice", 1), "Admin Alice", "alice@localhost", 1, true, true, true, true, false, false, AllPerms, make(map[string]bool), "", true, "", "", "", "", "", 58, 1000, "127.0.0.1", 0}
|
||||
user3 := User{2, BuildProfileURL("admin-fred", 62), "Admin Fred", "fred@localhost", 1, true, true, true, true, false, false, AllPerms, make(map[string]bool), "", true, "", "", "", "", "", 42, 900, "::1", 0}
|
||||
user2 := User{1, BuildProfileURL("admin-alice", 1), "Admin Alice", "alice@localhost", 1, true, true, true, true, false, false, AllPerms, make(map[string]bool), "", true, BuildAvatar(1, ""), "", "", "", "", 58, 1000, "127.0.0.1", 0}
|
||||
user3 := User{2, BuildProfileURL("admin-fred", 62), "Admin Fred", "fred@localhost", 1, true, true, true, true, false, false, AllPerms, make(map[string]bool), "", true, BuildAvatar(2, ""), "", "", "", "", 42, 900, "::1", 0}
|
||||
headerVars := &HeaderVars{
|
||||
Site: Site,
|
||||
Settings: SettingBox.Load().(SettingMap),
|
||||
@ -168,12 +208,37 @@ func compileTemplates() error {
|
||||
return err
|
||||
}
|
||||
|
||||
// Let plugins register their own templates
|
||||
if Dev.DebugMode {
|
||||
log.Print("Registering the templates for the plugins")
|
||||
loginPage := Page{"Login Page", user, headerVars, tList, nil}
|
||||
loginTmpl, err := c.Compile("login.html", "templates/", "common.Page", loginPage, varList)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
c.SkipHandles(true)
|
||||
|
||||
registerPage := Page{"Registration Page", user, headerVars, tList, nil}
|
||||
registerTmpl, err := c.Compile("register.html", "templates/", "common.Page", registerPage, varList)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
errorPage := Page{"Error", user, headerVars, tList, "A problem has occurred in the system."}
|
||||
errorTmpl, err := c.Compile("error.html", "templates/", "common.Page", errorPage, varList)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var ipUserList = make(map[int]*User)
|
||||
ipUserList[1] = &user2
|
||||
ipSearchPage := IPSearchPage{"IP Search", user2, headerVars, ipUserList, "::1"}
|
||||
ipSearchTmpl, err := c.Compile("ip_search.html", "templates/", "common.IPSearchPage", ipSearchPage, varList)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Let plugins register their own templates
|
||||
DebugLog("Registering the templates for the plugins")
|
||||
config = c.GetConfig()
|
||||
config.SkipHandles = true
|
||||
c.SetConfig(config)
|
||||
for _, tmplfunc := range PrebuildTmplList {
|
||||
tmplItem := tmplfunc(user, headerVars)
|
||||
varList = make(map[string]tmpl.VarItem)
|
||||
@ -191,6 +256,10 @@ func compileTemplates() error {
|
||||
go writeTemplate("forums", forumsTmpl)
|
||||
go writeTemplate("topics", topicsTmpl)
|
||||
go writeTemplate("forum", forumTmpl)
|
||||
go writeTemplate("login", loginTmpl)
|
||||
go writeTemplate("register", registerTmpl)
|
||||
go writeTemplate("ip_search", ipSearchTmpl)
|
||||
go writeTemplate("error", errorTmpl)
|
||||
go func() {
|
||||
err := writeFile("./template_list.go", "package main\n\n// nolint\n"+c.FragOut)
|
||||
if err != nil {
|
||||
@ -279,10 +348,16 @@ func InitTemplates() error {
|
||||
return template.HTML(BuildWidget(dock.(string), headerVarInt.(*HeaderVars)))
|
||||
}
|
||||
|
||||
// The interpreted templates...
|
||||
if Dev.DebugMode {
|
||||
log.Print("Loading the template files...")
|
||||
fmap["lang"] = func(phraseNameInt interface{}) interface{} {
|
||||
phraseName, ok := phraseNameInt.(string)
|
||||
if !ok {
|
||||
panic("phraseNameInt is not a string")
|
||||
}
|
||||
return GetTmplPhrase(phraseName)
|
||||
}
|
||||
|
||||
// The interpreted templates...
|
||||
DebugLog("Loading the template files...")
|
||||
Templates.Funcs(fmap)
|
||||
template.Must(Templates.ParseGlob("templates/*"))
|
||||
template.Must(Templates.ParseGlob("pages/*"))
|
||||
|
54
common/templates/minifiers.go
Normal file
54
common/templates/minifiers.go
Normal file
@ -0,0 +1,54 @@
|
||||
package tmpl
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// TODO: Write unit tests for this
|
||||
func minify(data string) string {
|
||||
data = strings.Replace(data, "\t", "", -1)
|
||||
data = strings.Replace(data, "\v", "", -1)
|
||||
data = strings.Replace(data, "\n", "", -1)
|
||||
data = strings.Replace(data, "\r", "", -1)
|
||||
data = strings.Replace(data, " ", " ", -1)
|
||||
return data
|
||||
}
|
||||
|
||||
// TODO: Strip comments
|
||||
// TODO: Handle CSS nested in <style> tags?
|
||||
// TODO: Write unit tests for this
|
||||
func minifyHTML(data string) string {
|
||||
return minify(data)
|
||||
}
|
||||
|
||||
// TODO: Have static files use this
|
||||
// TODO: Strip comments
|
||||
// TODO: Convert the rgb()s to hex codes?
|
||||
// TODO: Write unit tests for this
|
||||
func minifyCSS(data string) string {
|
||||
return minify(data)
|
||||
}
|
||||
|
||||
// TODO: Convert this to three character hex strings whenever possible?
|
||||
// TODO: Write unit tests for this
|
||||
// nolint
|
||||
func rgbToHexstr(red int, green int, blue int) string {
|
||||
return strconv.FormatInt(int64(red), 16) + strconv.FormatInt(int64(green), 16) + strconv.FormatInt(int64(blue), 16)
|
||||
}
|
||||
|
||||
/*
|
||||
// TODO: Write unit tests for this
|
||||
func hexstrToRgb(hexstr string) (red int, blue int, green int, err error) {
|
||||
// Strip the # at the start
|
||||
if hexstr[0] == '#' {
|
||||
hexstr = strings.TrimPrefix(hexstr,"#")
|
||||
}
|
||||
if len(hexstr) != 3 && len(hexstr) != 6 {
|
||||
return 0, 0, 0, errors.New("Hex colour codes may only be three or six characters long")
|
||||
}
|
||||
|
||||
if len(hexstr) == 3 {
|
||||
hexstr = hexstr[0] + hexstr[0] + hexstr[1] + hexstr[1] + hexstr[2] + hexstr[2]
|
||||
}
|
||||
}*/
|
@ -3,33 +3,41 @@ package tmpl
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"log"
|
||||
"strconv"
|
||||
"strings"
|
||||
//"regexp"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"path/filepath"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"strings"
|
||||
"text/template/parse"
|
||||
)
|
||||
|
||||
// TODO: Turn this file into a library
|
||||
var textOverlapList = make(map[string]int)
|
||||
|
||||
// nolint
|
||||
type VarItem struct {
|
||||
Name string
|
||||
Destination string
|
||||
Type string
|
||||
}
|
||||
|
||||
type VarItemReflect struct {
|
||||
Name string
|
||||
Destination string
|
||||
Value reflect.Value
|
||||
}
|
||||
|
||||
type CTemplateConfig struct {
|
||||
Minify bool
|
||||
Debug bool
|
||||
SuperDebug bool
|
||||
SkipHandles bool
|
||||
}
|
||||
|
||||
// nolint
|
||||
type CTemplateSet struct {
|
||||
tlist map[string]*parse.Tree
|
||||
dir string
|
||||
templateList map[string]*parse.Tree
|
||||
fileDir string
|
||||
funcMap map[string]interface{}
|
||||
importMap map[string]string
|
||||
Fragments map[string]int
|
||||
@ -39,43 +47,31 @@ type CTemplateSet struct {
|
||||
localVars map[string]map[string]VarItemReflect
|
||||
hasDispInt bool
|
||||
localDispStructIndex int
|
||||
langIndexToName []string
|
||||
stats map[string]int
|
||||
pVarList string
|
||||
pVarPosition int
|
||||
previousNode parse.NodeType
|
||||
currentNode parse.NodeType
|
||||
nextNode parse.NodeType
|
||||
//tempVars map[string]string
|
||||
doImports bool
|
||||
minify bool
|
||||
debug bool
|
||||
superDebug bool
|
||||
skipHandles bool
|
||||
expectsInt interface{}
|
||||
config CTemplateConfig
|
||||
doImports bool
|
||||
expectsInt interface{}
|
||||
}
|
||||
|
||||
func (c *CTemplateSet) Minify(on bool) {
|
||||
c.minify = on
|
||||
func (c *CTemplateSet) SetConfig(config CTemplateConfig) {
|
||||
c.config = config
|
||||
}
|
||||
|
||||
func (c *CTemplateSet) Debug(on bool) {
|
||||
c.debug = on
|
||||
func (c *CTemplateSet) GetConfig() CTemplateConfig {
|
||||
return c.config
|
||||
}
|
||||
|
||||
func (c *CTemplateSet) SuperDebug(on bool) {
|
||||
c.superDebug = on
|
||||
}
|
||||
|
||||
func (c *CTemplateSet) SkipHandles(on bool) {
|
||||
c.skipHandles = on
|
||||
}
|
||||
|
||||
func (c *CTemplateSet) Compile(name string, dir string, expects string, expectsInt interface{}, varList map[string]VarItem, imports ...string) (out string, err error) {
|
||||
if c.debug {
|
||||
func (c *CTemplateSet) Compile(name string, fileDir string, expects string, expectsInt interface{}, varList map[string]VarItem, imports ...string) (out string, err error) {
|
||||
if c.config.Debug {
|
||||
fmt.Println("Compiling template '" + name + "'")
|
||||
}
|
||||
|
||||
c.dir = dir
|
||||
c.fileDir = fileDir
|
||||
c.doImports = true
|
||||
c.funcMap = map[string]interface{}{
|
||||
"and": "&&",
|
||||
@ -92,6 +88,7 @@ func (c *CTemplateSet) Compile(name string, dir string, expects string, expectsI
|
||||
"multiply": true,
|
||||
"divide": true,
|
||||
"dock": true,
|
||||
"lang": true,
|
||||
}
|
||||
|
||||
c.importMap = map[string]string{
|
||||
@ -107,19 +104,17 @@ func (c *CTemplateSet) Compile(name string, dir string, expects string, expectsI
|
||||
c.varList = varList
|
||||
c.hasDispInt = false
|
||||
c.localDispStructIndex = 0
|
||||
//c.pVarList = ""
|
||||
//c.pVarPosition = 0
|
||||
c.stats = make(map[string]int)
|
||||
c.expectsInt = expectsInt
|
||||
holdreflect := reflect.ValueOf(expectsInt)
|
||||
|
||||
res, err := ioutil.ReadFile(dir + name)
|
||||
res, err := ioutil.ReadFile(fileDir + name)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
content := string(res)
|
||||
if c.minify {
|
||||
if c.config.Minify {
|
||||
content = minify(content)
|
||||
}
|
||||
|
||||
@ -133,21 +128,20 @@ func (c *CTemplateSet) Compile(name string, dir string, expects string, expectsI
|
||||
|
||||
out = ""
|
||||
fname := strings.TrimSuffix(name, filepath.Ext(name))
|
||||
c.tlist = make(map[string]*parse.Tree)
|
||||
c.tlist[fname] = tree
|
||||
c.templateList = map[string]*parse.Tree{fname: tree}
|
||||
varholder := "tmpl_" + fname + "_vars"
|
||||
|
||||
c.log(c.tlist)
|
||||
c.log(c.templateList)
|
||||
c.localVars = make(map[string]map[string]VarItemReflect)
|
||||
c.localVars[fname] = make(map[string]VarItemReflect)
|
||||
c.localVars[fname]["."] = VarItemReflect{".", varholder, holdreflect}
|
||||
if c.Fragments == nil {
|
||||
c.Fragments = make(map[string]int)
|
||||
}
|
||||
c.FragmentCursor = make(map[string]int)
|
||||
c.FragmentCursor[fname] = 0
|
||||
c.FragmentCursor = map[string]int{fname: 0}
|
||||
c.langIndexToName = nil
|
||||
|
||||
out += c.rootIterate(c.tlist[fname], varholder, holdreflect, fname)
|
||||
out += c.rootIterate(c.templateList[fname], varholder, holdreflect, fname)
|
||||
|
||||
var importList string
|
||||
if c.doImports {
|
||||
@ -163,18 +157,31 @@ func (c *CTemplateSet) Compile(name string, dir string, expects string, expectsI
|
||||
|
||||
fout := "// +build !no_templategen\n\n// Code generated by Gosora. More below:\n/* This file was automatically generated by the software. Please don't edit it as your changes may be overwritten at any moment. */\n"
|
||||
|
||||
fout += "package main\n" + importList + c.pVarList + "\n"
|
||||
fout += "package main\n" + importList + "\n"
|
||||
fout += "var " + fname + "_Tmpl_Phrase_ID int\n\n"
|
||||
fout += "// nolint\nfunc init() {\n"
|
||||
|
||||
if !c.skipHandles {
|
||||
if !c.config.SkipHandles {
|
||||
fout += "\tcommon.Template_" + fname + "_handle = Template_" + fname + "\n"
|
||||
|
||||
fout += "\tcommon.Ctemplates = append(common.Ctemplates,\"" + fname + "\")\n\tcommon.TmplPtrMap[\"" + fname + "\"] = &common.Template_" + fname + "_handle\n"
|
||||
}
|
||||
|
||||
fout += "\tcommon.TmplPtrMap[\"o_" + fname + "\"] = Template_" + fname + "\n}\n\n"
|
||||
fout += "\tcommon.TmplPtrMap[\"o_" + fname + "\"] = Template_" + fname + "\n"
|
||||
if len(c.langIndexToName) > 0 {
|
||||
fout += "\t" + fname + "_Tmpl_Phrase_ID = common.RegisterTmplPhraseNames([]string{\n"
|
||||
for _, name := range c.langIndexToName {
|
||||
fout += "\t\t" + `"` + name + `"` + ",\n"
|
||||
}
|
||||
fout += "\t})\n"
|
||||
}
|
||||
fout += "}\n\n"
|
||||
|
||||
fout += "// nolint\nfunc Template_" + fname + "(tmpl_" + fname + "_vars " + expects + ", w http.ResponseWriter) error {\n" + varString + out + "\treturn nil\n}\n"
|
||||
fout += "// nolint\nfunc Template_" + fname + "(tmpl_" + fname + "_vars " + expects + ", w http.ResponseWriter) error {\n"
|
||||
if len(c.langIndexToName) > 0 {
|
||||
fout += "\tvar phrases = common.GetTmplPhrasesBytes(" + fname + "_Tmpl_Phrase_ID)\n"
|
||||
}
|
||||
fout += varString + out + "\treturn nil\n}\n"
|
||||
|
||||
fout = strings.Replace(fout, `))
|
||||
w.Write([]byte(`, " + ", -1)
|
||||
@ -183,13 +190,12 @@ w.Write([]byte(`, " + ", -1)
|
||||
//whitespaceWrites := regexp.MustCompile(`(?s)w.Write\(\[\]byte\(`+spstr+`\)\)`)
|
||||
//fout = whitespaceWrites.ReplaceAllString(fout,"")
|
||||
|
||||
if c.debug {
|
||||
if c.config.Debug {
|
||||
for index, count := range c.stats {
|
||||
fmt.Println(index+": ", strconv.Itoa(count))
|
||||
}
|
||||
fmt.Println(" ")
|
||||
}
|
||||
|
||||
c.log("Output!")
|
||||
c.log(fout)
|
||||
return fout, nil
|
||||
@ -224,26 +230,26 @@ func (c *CTemplateSet) compileSwitch(varholder string, holdreflect reflect.Value
|
||||
case *parse.IfNode:
|
||||
c.log("If Node:")
|
||||
c.log("node.Pipe", node.Pipe)
|
||||
|
||||
var expr string
|
||||
for _, cmd := range node.Pipe.Cmds {
|
||||
c.log("If Node Bit:", cmd)
|
||||
c.log("If Node Bit Type:", reflect.ValueOf(cmd).Type().Name())
|
||||
c.log("Bit Type:", reflect.ValueOf(cmd).Type().Name())
|
||||
expr += c.compileVarswitch(varholder, holdreflect, templateName, cmd)
|
||||
c.log("If Node Expression Step:", c.compileVarswitch(varholder, holdreflect, templateName, cmd))
|
||||
c.log("Expression Step:", c.compileVarswitch(varholder, holdreflect, templateName, cmd))
|
||||
}
|
||||
|
||||
c.log("If Node Expression:", expr)
|
||||
c.log("Expression:", expr)
|
||||
c.previousNode = c.currentNode
|
||||
c.currentNode = parse.NodeList
|
||||
c.nextNode = -1
|
||||
out = "if " + expr + " {\n" + c.compileSwitch(varholder, holdreflect, templateName, node.List) + "}"
|
||||
if node.ElseList == nil {
|
||||
c.log("Selected Branch 1")
|
||||
return "if " + expr + " {\n" + c.compileSwitch(varholder, holdreflect, templateName, node.List) + "}\n"
|
||||
return out + "\n"
|
||||
}
|
||||
|
||||
c.log("Selected Branch 2")
|
||||
return "if " + expr + " {\n" + c.compileSwitch(varholder, holdreflect, templateName, node.List) + "} else {\n" + c.compileSwitch(varholder, holdreflect, templateName, node.ElseList) + "}\n"
|
||||
return out + " else {\n" + c.compileSwitch(varholder, holdreflect, templateName, node.ElseList) + "}\n"
|
||||
case *parse.ListNode:
|
||||
c.log("List Node")
|
||||
for _, subnode := range node.Nodes {
|
||||
@ -279,7 +285,6 @@ func (c *CTemplateSet) compileSwitch(varholder string, holdreflect reflect.Value
|
||||
func (c *CTemplateSet) compileRangeNode(varholder string, holdreflect reflect.Value, templateName string, node *parse.RangeNode) (out string) {
|
||||
c.log("Range Node!")
|
||||
c.log(node.Pipe)
|
||||
|
||||
var outVal reflect.Value
|
||||
for _, cmd := range node.Pipe.Cmds {
|
||||
c.log("Range Bit:", cmd)
|
||||
@ -294,17 +299,15 @@ func (c *CTemplateSet) compileRangeNode(varholder string, holdreflect reflect.Va
|
||||
for _, key := range outVal.MapKeys() {
|
||||
item = outVal.MapIndex(key)
|
||||
}
|
||||
if c.debug {
|
||||
fmt.Println("Range item:", item)
|
||||
}
|
||||
|
||||
c.log("Range item:", item)
|
||||
if !item.IsValid() {
|
||||
panic("item" + "^\n" + "Invalid map. Maybe, it doesn't have any entries for the template engine to analyse?")
|
||||
}
|
||||
|
||||
out = "if len(" + out + ") != 0 {\nfor _, item := range " + out + " {\n" + c.compileSwitch("item", item, templateName, node.List) + "}\n}"
|
||||
if node.ElseList != nil {
|
||||
out = "if len(" + out + ") != 0 {\nfor _, item := range " + out + " {\n" + c.compileSwitch("item", item, templateName, node.List) + "}\n} else {\n" + c.compileSwitch("item", item, templateName, node.ElseList) + "}\n"
|
||||
} else {
|
||||
out = "if len(" + out + ") != 0 {\nfor _, item := range " + out + " {\n" + c.compileSwitch("item", item, templateName, node.List) + "}\n}"
|
||||
out += " else {\n" + c.compileSwitch("item", item, templateName, node.ElseList) + "}\n"
|
||||
}
|
||||
case reflect.Slice:
|
||||
if outVal.Len() == 0 {
|
||||
@ -312,13 +315,13 @@ func (c *CTemplateSet) compileRangeNode(varholder string, holdreflect reflect.Va
|
||||
}
|
||||
item := outVal.Index(0)
|
||||
out = "if len(" + out + ") != 0 {\nfor _, item := range " + out + " {\n" + c.compileSwitch("item", item, templateName, node.List) + "}\n}"
|
||||
if node.ElseList != nil {
|
||||
out += " else {\n" + c.compileSwitch(varholder, holdreflect, templateName, node.ElseList) + "}"
|
||||
}
|
||||
case reflect.Invalid:
|
||||
return ""
|
||||
}
|
||||
|
||||
if node.ElseList != nil {
|
||||
out += " else {\n" + c.compileSwitch(varholder, holdreflect, templateName, node.ElseList) + "}"
|
||||
}
|
||||
return out + "\n"
|
||||
}
|
||||
|
||||
@ -328,7 +331,6 @@ func (c *CTemplateSet) compileSubswitch(varholder string, holdreflect reflect.Va
|
||||
switch n := firstWord.(type) {
|
||||
case *parse.FieldNode:
|
||||
c.log("Field Node:", n.Ident)
|
||||
|
||||
/* Use reflect to determine if the field is for a method, otherwise assume it's a variable. Variable declarations are coming soon! */
|
||||
cur := holdreflect
|
||||
|
||||
@ -362,15 +364,14 @@ func (c *CTemplateSet) compileSubswitch(varholder string, holdreflect reflect.Va
|
||||
c.error("Debug Data:")
|
||||
c.error("Holdreflect:", holdreflect)
|
||||
c.error("Holdreflect.Kind():", holdreflect.Kind())
|
||||
if !c.superDebug {
|
||||
if !c.config.SuperDebug {
|
||||
c.error("cur.Kind():", cur.Kind().String())
|
||||
}
|
||||
c.error("")
|
||||
if !multiline {
|
||||
panic(varholder + varbit + "^\n" + "Invalid value. Maybe, it doesn't exist?")
|
||||
} else {
|
||||
panic(varbit + "^\n" + "Invalid value. Maybe, it doesn't exist?")
|
||||
}
|
||||
panic(varbit + "^\n" + "Invalid value. Maybe, it doesn't exist?")
|
||||
}
|
||||
|
||||
c.log("in-loop varbit: " + varbit)
|
||||
@ -395,8 +396,8 @@ func (c *CTemplateSet) compileSubswitch(varholder string, holdreflect reflect.Va
|
||||
newVarByte = ":"
|
||||
c.localDispStructIndex++
|
||||
}
|
||||
varbit = "disp" + dispStr + " " + newVarByte + "= " + varholder + varbit + "\n"
|
||||
varholder = "disp" + dispStr
|
||||
varbit = varholder + " " + newVarByte + "= " + varholder + varbit + "\n"
|
||||
multiline = true
|
||||
} else {
|
||||
continue
|
||||
@ -466,7 +467,7 @@ func (c *CTemplateSet) compileVarswitch(varholder string, holdreflect reflect.Va
|
||||
firstWord := node.Args[0]
|
||||
switch n := firstWord.(type) {
|
||||
case *parse.FieldNode:
|
||||
if c.superDebug {
|
||||
if c.config.SuperDebug {
|
||||
fmt.Println("Field Node:", n.Ident)
|
||||
for _, id := range n.Ident {
|
||||
fmt.Println("Field Bit:", id)
|
||||
@ -477,25 +478,24 @@ func (c *CTemplateSet) compileVarswitch(varholder string, holdreflect reflect.Va
|
||||
return c.compileBoolsub(n.String(), varholder, templateName, holdreflect)
|
||||
case *parse.ChainNode:
|
||||
c.log("Chain Node:", n.Node)
|
||||
c.log("Chain Node Args:", node.Args)
|
||||
c.log("Node Args:", node.Args)
|
||||
case *parse.IdentifierNode:
|
||||
c.log("Identifier Node:", node)
|
||||
c.log("Identifier Node Args:", node.Args)
|
||||
c.log("Node Args:", node.Args)
|
||||
return c.compileIdentSwitchN(varholder, holdreflect, templateName, node)
|
||||
case *parse.DotNode:
|
||||
return varholder
|
||||
case *parse.VariableNode:
|
||||
c.log("Variable Node:", n.String())
|
||||
c.log("Variable Node Identifier:", n.Ident)
|
||||
c.log("Node Identifier:", n.Ident)
|
||||
out, _ = c.compileIfVarsub(n.String(), varholder, templateName, holdreflect)
|
||||
case *parse.NilNode:
|
||||
panic("Nil is not a command x.x")
|
||||
case *parse.PipeNode:
|
||||
c.log("Pipe Node!")
|
||||
c.log(n)
|
||||
c.log("Args:", node.Args)
|
||||
c.log("Node Args:", node.Args)
|
||||
out += c.compileIdentSwitchN(varholder, holdreflect, templateName, node)
|
||||
|
||||
c.log("Out:", out)
|
||||
default:
|
||||
return c.unknownNode(firstWord)
|
||||
@ -539,7 +539,6 @@ func (c *CTemplateSet) simpleMath(varholder string, holdreflect reflect.Value, t
|
||||
numSample := 1
|
||||
val = reflect.ValueOf(numSample)
|
||||
}
|
||||
|
||||
c.dumpSymbol(pos, node, symbol)
|
||||
return leftParam + " " + symbol + " " + rightParam, val
|
||||
}
|
||||
@ -647,16 +646,34 @@ ArgLoop:
|
||||
}
|
||||
|
||||
rightParam, val3 := c.compileIfVarsub(rightOperand, varholder, templateName, holdreflect)
|
||||
if val3.IsValid() {
|
||||
val = val3
|
||||
} else {
|
||||
if !val3.IsValid() {
|
||||
panic("val3 is invalid")
|
||||
}
|
||||
val = val3
|
||||
|
||||
// TODO: Refactor this
|
||||
out = "w.Write([]byte(common.BuildWidget(" + leftParam + "," + rightParam + ")))\n"
|
||||
literal = true
|
||||
break ArgLoop
|
||||
case "lang":
|
||||
var leftParam string
|
||||
// TODO: Implement string literals properly
|
||||
leftOperand := node.Args[pos+1].String()
|
||||
if len(leftOperand) == 0 {
|
||||
panic("The left operand for the language string cannot be left blank")
|
||||
}
|
||||
|
||||
if leftOperand[0] == '"' {
|
||||
// ! Slightly crude but it does the job
|
||||
leftParam = strings.Replace(leftOperand, "\"", "", -1)
|
||||
} else {
|
||||
panic("Phrase names cannot be dynamic")
|
||||
}
|
||||
|
||||
c.langIndexToName = append(c.langIndexToName, leftParam)
|
||||
out = "w.Write(phrases[" + strconv.Itoa(len(c.langIndexToName)-1) + "])\n"
|
||||
literal = true
|
||||
break ArgLoop
|
||||
default:
|
||||
c.log("Variable!")
|
||||
if len(node.Args) > (pos + 1) {
|
||||
@ -676,7 +693,7 @@ func (c *CTemplateSet) compileReflectSwitch(varholder string, holdreflect reflec
|
||||
firstWord := node.Args[0]
|
||||
switch n := firstWord.(type) {
|
||||
case *parse.FieldNode:
|
||||
if c.superDebug {
|
||||
if c.config.SuperDebug {
|
||||
fmt.Println("Field Node:", n.Ident)
|
||||
for _, id := range n.Ident {
|
||||
fmt.Println("Field Bit:", id)
|
||||
@ -759,7 +776,6 @@ func (c *CTemplateSet) compileIfVarsub(varname string, varholder string, templat
|
||||
if !cur.IsValid() {
|
||||
panic(out + "^\n" + "Invalid value. Maybe, it doesn't exist?")
|
||||
}
|
||||
|
||||
c.log("Data Kind:", cur.Kind())
|
||||
c.log("Data Type:", cur.Type().Name())
|
||||
}
|
||||
@ -887,13 +903,13 @@ func (c *CTemplateSet) compileSubtemplate(pvarholder string, pholdreflect reflec
|
||||
}
|
||||
|
||||
// TODO: Cascade errors back up the tree to the caller?
|
||||
res, err := ioutil.ReadFile(c.dir + node.Name)
|
||||
res, err := ioutil.ReadFile(c.fileDir + node.Name)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
content := string(res)
|
||||
if c.minify {
|
||||
if c.config.Minify {
|
||||
content = minify(content)
|
||||
}
|
||||
|
||||
@ -904,8 +920,8 @@ func (c *CTemplateSet) compileSubtemplate(pvarholder string, pholdreflect reflec
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
c.tlist[fname] = tree
|
||||
subtree := c.tlist[fname]
|
||||
c.templateList[fname] = tree
|
||||
subtree := c.templateList[fname]
|
||||
c.log("subtree.Root", subtree.Root)
|
||||
|
||||
c.localVars[fname] = make(map[string]VarItemReflect)
|
||||
@ -916,68 +932,22 @@ func (c *CTemplateSet) compileSubtemplate(pvarholder string, pholdreflect reflec
|
||||
return out
|
||||
}
|
||||
|
||||
// TODO: Should we rethink the way the log methods work or their names?
|
||||
|
||||
func (c *CTemplateSet) log(args ...interface{}) {
|
||||
if c.superDebug {
|
||||
if c.config.SuperDebug {
|
||||
fmt.Println(args...)
|
||||
}
|
||||
}
|
||||
|
||||
func (c *CTemplateSet) logf(left string, args ...interface{}) {
|
||||
if c.superDebug {
|
||||
if c.config.SuperDebug {
|
||||
fmt.Printf(left, args...)
|
||||
}
|
||||
}
|
||||
|
||||
func (c *CTemplateSet) error(args ...interface{}) {
|
||||
if c.debug {
|
||||
if c.config.Debug {
|
||||
fmt.Println(args...)
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Write unit tests for this
|
||||
func minify(data string) string {
|
||||
data = strings.Replace(data, "\t", "", -1)
|
||||
data = strings.Replace(data, "\v", "", -1)
|
||||
data = strings.Replace(data, "\n", "", -1)
|
||||
data = strings.Replace(data, "\r", "", -1)
|
||||
data = strings.Replace(data, " ", " ", -1)
|
||||
return data
|
||||
}
|
||||
|
||||
// TODO: Strip comments
|
||||
// TODO: Handle CSS nested in <style> tags?
|
||||
// TODO: Write unit tests for this
|
||||
func minifyHTML(data string) string {
|
||||
return minify(data)
|
||||
}
|
||||
|
||||
// TODO: Have static files use this
|
||||
// TODO: Strip comments
|
||||
// TODO: Convert the rgb()s to hex codes?
|
||||
// TODO: Write unit tests for this
|
||||
func minifyCSS(data string) string {
|
||||
return minify(data)
|
||||
}
|
||||
|
||||
// TODO: Convert this to three character hex strings whenever possible?
|
||||
// TODO: Write unit tests for this
|
||||
// nolint
|
||||
func rgbToHexstr(red int, green int, blue int) string {
|
||||
return strconv.FormatInt(int64(red), 16) + strconv.FormatInt(int64(green), 16) + strconv.FormatInt(int64(blue), 16)
|
||||
}
|
||||
|
||||
/*
|
||||
// TODO: Write unit tests for this
|
||||
func hexstrToRgb(hexstr string) (red int, blue int, green int, err error) {
|
||||
// Strip the # at the start
|
||||
if hexstr[0] == '#' {
|
||||
hexstr = strings.TrimPrefix(hexstr,"#")
|
||||
}
|
||||
if len(hexstr) != 3 && len(hexstr) != 6 {
|
||||
return 0, 0, 0, errors.New("Hex colour codes may only be three or six characters long")
|
||||
}
|
||||
|
||||
if len(hexstr) == 3 {
|
||||
hexstr = hexstr[0] + hexstr[0] + hexstr[1] + hexstr[1] + hexstr[2] + hexstr[2]
|
||||
}
|
||||
}*/
|
||||
|
@ -317,6 +317,15 @@ func (theme *Theme) MapTemplates() {
|
||||
default:
|
||||
log.Fatal("The source and destination templates are incompatible")
|
||||
}
|
||||
case *func(IPSearchPage, http.ResponseWriter):
|
||||
switch sTmplPtr := sourceTmplPtr.(type) {
|
||||
case *func(IPSearchPage, http.ResponseWriter):
|
||||
//overridenTemplates[themeTmpl.Name] = d_tmpl_ptr
|
||||
overridenTemplates[themeTmpl.Name] = true
|
||||
*dTmplPtr = *sTmplPtr
|
||||
default:
|
||||
log.Fatal("The source and destination templates are incompatible")
|
||||
}
|
||||
case *func(Page, http.ResponseWriter):
|
||||
switch sTmplPtr := sourceTmplPtr.(type) {
|
||||
case *func(Page, http.ResponseWriter):
|
||||
@ -395,6 +404,13 @@ func ResetTemplateOverrides() {
|
||||
default:
|
||||
log.Fatal("The origin and destination templates are incompatible")
|
||||
}
|
||||
case func(IPSearchPage, http.ResponseWriter):
|
||||
switch dPtr := destTmplPtr.(type) {
|
||||
case *func(IPSearchPage, http.ResponseWriter):
|
||||
*dPtr = oPtr
|
||||
default:
|
||||
log.Fatal("The origin and destination templates are incompatible")
|
||||
}
|
||||
case func(Page, http.ResponseWriter):
|
||||
switch dPtr := destTmplPtr.(type) {
|
||||
case *func(Page, http.ResponseWriter):
|
||||
@ -435,6 +451,9 @@ func RunThemeTemplate(theme string, template string, pi interface{}, w http.Resp
|
||||
case *func(CreateTopicPage, http.ResponseWriter) error:
|
||||
var tmpl = *tmplO
|
||||
return tmpl(pi.(CreateTopicPage), w)
|
||||
case *func(IPSearchPage, http.ResponseWriter) error:
|
||||
var tmpl = *tmplO
|
||||
return tmpl(pi.(IPSearchPage), w)
|
||||
case *func(Page, http.ResponseWriter) error:
|
||||
var tmpl = *tmplO
|
||||
return tmpl(pi.(Page), w)
|
||||
@ -450,6 +469,8 @@ func RunThemeTemplate(theme string, template string, pi interface{}, w http.Resp
|
||||
return tmplO(pi.(ProfilePage), w)
|
||||
case func(CreateTopicPage, http.ResponseWriter) error:
|
||||
return tmplO(pi.(CreateTopicPage), w)
|
||||
case func(IPSearchPage, http.ResponseWriter) error:
|
||||
return tmplO(pi.(IPSearchPage), w)
|
||||
case func(Page, http.ResponseWriter) error:
|
||||
return tmplO(pi.(Page), w)
|
||||
case string:
|
||||
|
@ -49,7 +49,7 @@ func NewDefaultUserStore(cache UserCache) (*DefaultUserStore, error) {
|
||||
// TODO: Add an admin version of registerStmt with more flexibility?
|
||||
return &DefaultUserStore{
|
||||
cache: cache,
|
||||
get: acc.SimpleSelect("users", "name, group, is_super_admin, session, email, avatar, message, url_prefix, url_name, level, score, last_ip, temp_group", "uid = ?", "", ""),
|
||||
get: acc.SimpleSelect("users", "name, group, active, is_super_admin, session, email, avatar, message, url_prefix, url_name, level, score, last_ip, temp_group", "uid = ?", "", ""),
|
||||
exists: acc.SimpleSelect("users", "uid", "uid = ?", "", ""),
|
||||
register: acc.SimpleInsert("users", "name, email, password, salt, group, is_super_admin, session, active, message, createdAt, lastActiveAt", "?,?,?,?,?,0,'',?,'',UTC_TIMESTAMP(),UTC_TIMESTAMP()"), // TODO: Implement user_count on users_groups here
|
||||
usernameExists: acc.SimpleSelect("users", "name", "name = ?", "", ""),
|
||||
@ -64,7 +64,7 @@ func (mus *DefaultUserStore) DirtyGet(id int) *User {
|
||||
}
|
||||
|
||||
user = &User{ID: id, Loggedin: true}
|
||||
err = mus.get.QueryRow(id).Scan(&user.Name, &user.Group, &user.IsSuperAdmin, &user.Session, &user.Email, &user.Avatar, &user.Message, &user.URLPrefix, &user.URLName, &user.Level, &user.Score, &user.LastIP, &user.TempGroup)
|
||||
err = mus.get.QueryRow(id).Scan(&user.Name, &user.Group, &user.Active, &user.IsSuperAdmin, &user.Session, &user.Email, &user.Avatar, &user.Message, &user.URLPrefix, &user.URLName, &user.Level, &user.Score, &user.LastIP, &user.TempGroup)
|
||||
|
||||
user.Init()
|
||||
if err == nil {
|
||||
@ -82,7 +82,7 @@ func (mus *DefaultUserStore) Get(id int) (*User, error) {
|
||||
}
|
||||
|
||||
user = &User{ID: id, Loggedin: true}
|
||||
err = mus.get.QueryRow(id).Scan(&user.Name, &user.Group, &user.IsSuperAdmin, &user.Session, &user.Email, &user.Avatar, &user.Message, &user.URLPrefix, &user.URLName, &user.Level, &user.Score, &user.LastIP, &user.TempGroup)
|
||||
err = mus.get.QueryRow(id).Scan(&user.Name, &user.Group, &user.Active, &user.IsSuperAdmin, &user.Session, &user.Email, &user.Avatar, &user.Message, &user.URLPrefix, &user.URLName, &user.Level, &user.Score, &user.LastIP, &user.TempGroup)
|
||||
|
||||
user.Init()
|
||||
if err == nil {
|
||||
@ -126,14 +126,14 @@ func (mus *DefaultUserStore) BulkGetMap(ids []int) (list map[int]*User, err erro
|
||||
qlist = qlist[0 : len(qlist)-1]
|
||||
|
||||
acc := qgen.Builder.Accumulator()
|
||||
rows, err := acc.Select("users").Columns("uid, name, group, is_super_admin, session, email, avatar, message, url_prefix, url_name, level, score, last_ip, temp_group").Where("uid IN(" + qlist + ")").Query(uidList...)
|
||||
rows, err := acc.Select("users").Columns("uid, name, group, active, is_super_admin, session, email, avatar, message, url_prefix, url_name, level, score, last_ip, temp_group").Where("uid IN(" + qlist + ")").Query(uidList...)
|
||||
if err != nil {
|
||||
return list, err
|
||||
}
|
||||
|
||||
for rows.Next() {
|
||||
user := &User{Loggedin: true}
|
||||
err := rows.Scan(&user.ID, &user.Name, &user.Group, &user.IsSuperAdmin, &user.Session, &user.Email, &user.Avatar, &user.Message, &user.URLPrefix, &user.URLName, &user.Level, &user.Score, &user.LastIP, &user.TempGroup)
|
||||
err := rows.Scan(&user.ID, &user.Name, &user.Group, &user.Active, &user.IsSuperAdmin, &user.Session, &user.Email, &user.Avatar, &user.Message, &user.URLPrefix, &user.URLName, &user.Level, &user.Score, &user.LastIP, &user.TempGroup)
|
||||
if err != nil {
|
||||
return list, err
|
||||
}
|
||||
@ -174,7 +174,7 @@ func (mus *DefaultUserStore) BulkGetMap(ids []int) (list map[int]*User, err erro
|
||||
|
||||
func (mus *DefaultUserStore) BypassGet(id int) (*User, error) {
|
||||
user := &User{ID: id, Loggedin: true}
|
||||
err := mus.get.QueryRow(id).Scan(&user.Name, &user.Group, &user.IsSuperAdmin, &user.Session, &user.Email, &user.Avatar, &user.Message, &user.URLPrefix, &user.URLName, &user.Level, &user.Score, &user.LastIP, &user.TempGroup)
|
||||
err := mus.get.QueryRow(id).Scan(&user.Name, &user.Group, &user.Active, &user.IsSuperAdmin, &user.Session, &user.Email, &user.Avatar, &user.Message, &user.URLPrefix, &user.URLName, &user.Level, &user.Score, &user.LastIP, &user.TempGroup)
|
||||
|
||||
user.Init()
|
||||
return user, err
|
||||
@ -182,7 +182,7 @@ func (mus *DefaultUserStore) BypassGet(id int) (*User, error) {
|
||||
|
||||
func (mus *DefaultUserStore) Reload(id int) error {
|
||||
user := &User{ID: id, Loggedin: true}
|
||||
err := mus.get.QueryRow(id).Scan(&user.Name, &user.Group, &user.IsSuperAdmin, &user.Session, &user.Email, &user.Avatar, &user.Message, &user.URLPrefix, &user.URLName, &user.Level, &user.Score, &user.LastIP, &user.TempGroup)
|
||||
err := mus.get.QueryRow(id).Scan(&user.Name, &user.Group, &user.Active, &user.IsSuperAdmin, &user.Session, &user.Email, &user.Avatar, &user.Message, &user.URLPrefix, &user.URLName, &user.Level, &user.Score, &user.LastIP, &user.TempGroup)
|
||||
if err != nil {
|
||||
mus.cache.Remove(id)
|
||||
return err
|
||||
|
@ -6,7 +6,7 @@
|
||||
* Copyright Azareal 2017 - 2018
|
||||
*
|
||||
*/
|
||||
package main
|
||||
package common
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
@ -18,7 +18,6 @@ import (
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"./common"
|
||||
"github.com/Azareal/gopsutil/cpu"
|
||||
"github.com/Azareal/gopsutil/mem"
|
||||
"github.com/gorilla/websocket"
|
||||
@ -26,60 +25,60 @@ import (
|
||||
|
||||
type WSUser struct {
|
||||
conn *websocket.Conn
|
||||
User *common.User
|
||||
User *User
|
||||
}
|
||||
|
||||
type WSHub struct {
|
||||
onlineUsers map[int]*WSUser
|
||||
onlineGuests map[*WSUser]bool
|
||||
guests sync.RWMutex
|
||||
users sync.RWMutex
|
||||
OnlineUsers map[int]*WSUser
|
||||
OnlineGuests map[*WSUser]bool
|
||||
GuestLock sync.RWMutex
|
||||
UserLock sync.RWMutex
|
||||
}
|
||||
|
||||
// TODO: Disable WebSockets on high load? Add a Control Panel interface for disabling it?
|
||||
var enableWebsockets = true // Put this in caps for consistency with the other constants?
|
||||
var EnableWebsockets = true // Put this in caps for consistency with the other constants?
|
||||
|
||||
var wsHub WSHub
|
||||
var WsHub WSHub
|
||||
var wsUpgrader = websocket.Upgrader{ReadBufferSize: 1024, WriteBufferSize: 1024}
|
||||
var errWsNouser = errors.New("This user isn't connected via WebSockets")
|
||||
|
||||
func init() {
|
||||
adminStatsWatchers = make(map[*WSUser]bool)
|
||||
wsHub = WSHub{
|
||||
onlineUsers: make(map[int]*WSUser),
|
||||
onlineGuests: make(map[*WSUser]bool),
|
||||
WsHub = WSHub{
|
||||
OnlineUsers: make(map[int]*WSUser),
|
||||
OnlineGuests: make(map[*WSUser]bool),
|
||||
}
|
||||
}
|
||||
|
||||
func (hub *WSHub) guestCount() int {
|
||||
defer hub.guests.RUnlock()
|
||||
hub.guests.RLock()
|
||||
return len(hub.onlineGuests)
|
||||
func (hub *WSHub) GuestCount() int {
|
||||
defer hub.GuestLock.RUnlock()
|
||||
hub.GuestLock.RLock()
|
||||
return len(hub.OnlineGuests)
|
||||
}
|
||||
|
||||
func (hub *WSHub) userCount() int {
|
||||
defer hub.users.RUnlock()
|
||||
hub.users.RLock()
|
||||
return len(hub.onlineUsers)
|
||||
func (hub *WSHub) UserCount() int {
|
||||
defer hub.UserLock.RUnlock()
|
||||
hub.UserLock.RLock()
|
||||
return len(hub.OnlineUsers)
|
||||
}
|
||||
|
||||
func (hub *WSHub) broadcastMessage(msg string) error {
|
||||
hub.users.RLock()
|
||||
for _, wsUser := range hub.onlineUsers {
|
||||
hub.UserLock.RLock()
|
||||
for _, wsUser := range hub.OnlineUsers {
|
||||
w, err := wsUser.conn.NextWriter(websocket.TextMessage)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, _ = w.Write([]byte(msg))
|
||||
}
|
||||
hub.users.RUnlock()
|
||||
hub.UserLock.RUnlock()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (hub *WSHub) pushMessage(targetUser int, msg string) error {
|
||||
hub.users.RLock()
|
||||
wsUser, ok := hub.onlineUsers[targetUser]
|
||||
hub.users.RUnlock()
|
||||
hub.UserLock.RLock()
|
||||
wsUser, ok := hub.OnlineUsers[targetUser]
|
||||
hub.UserLock.RUnlock()
|
||||
if !ok {
|
||||
return errWsNouser
|
||||
}
|
||||
@ -96,15 +95,15 @@ func (hub *WSHub) pushMessage(targetUser int, msg string) error {
|
||||
|
||||
func (hub *WSHub) pushAlert(targetUser int, asid int, event string, elementType string, actorID int, targetUserID int, elementID int) error {
|
||||
//log.Print("In pushAlert")
|
||||
hub.users.RLock()
|
||||
wsUser, ok := hub.onlineUsers[targetUser]
|
||||
hub.users.RUnlock()
|
||||
hub.UserLock.RLock()
|
||||
wsUser, ok := hub.OnlineUsers[targetUser]
|
||||
hub.UserLock.RUnlock()
|
||||
if !ok {
|
||||
return errWsNouser
|
||||
}
|
||||
|
||||
//log.Print("Building alert")
|
||||
alert, err := buildAlert(asid, event, elementType, actorID, targetUserID, elementID, *wsUser.User)
|
||||
alert, err := BuildAlert(asid, event, elementType, actorID, targetUserID, elementID, *wsUser.User)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -122,12 +121,12 @@ func (hub *WSHub) pushAlert(targetUser int, asid int, event string, elementType
|
||||
|
||||
func (hub *WSHub) pushAlerts(users []int, asid int, event string, elementType string, actorID int, targetUserID int, elementID int) error {
|
||||
var wsUsers []*WSUser
|
||||
hub.users.RLock()
|
||||
hub.UserLock.RLock()
|
||||
// We don't want to keep a lock on this for too long, so we'll accept some nil pointers
|
||||
for _, uid := range users {
|
||||
wsUsers = append(wsUsers, hub.onlineUsers[uid])
|
||||
wsUsers = append(wsUsers, hub.OnlineUsers[uid])
|
||||
}
|
||||
hub.users.RUnlock()
|
||||
hub.UserLock.RUnlock()
|
||||
if len(wsUsers) == 0 {
|
||||
return errWsNouser
|
||||
}
|
||||
@ -138,7 +137,7 @@ func (hub *WSHub) pushAlerts(users []int, asid int, event string, elementType st
|
||||
continue
|
||||
}
|
||||
|
||||
alert, err := buildAlert(asid, event, elementType, actorID, targetUserID, elementID, *wsUser.User)
|
||||
alert, err := BuildAlert(asid, event, elementType, actorID, targetUserID, elementID, *wsUser.User)
|
||||
if err != nil {
|
||||
errs = append(errs, err)
|
||||
}
|
||||
@ -161,25 +160,26 @@ func (hub *WSHub) pushAlerts(users []int, asid int, event string, elementType st
|
||||
}
|
||||
|
||||
// TODO: How should we handle errors for this?
|
||||
func routeWebsockets(w http.ResponseWriter, r *http.Request, user common.User) common.RouteError {
|
||||
// TODO: Move this out of common?
|
||||
func RouteWebsockets(w http.ResponseWriter, r *http.Request, user User) RouteError {
|
||||
conn, err := wsUpgrader.Upgrade(w, r, nil)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
userptr, err := common.Users.Get(user.ID)
|
||||
if err != nil && err != common.ErrStoreCapacityOverflow {
|
||||
userptr, err := Users.Get(user.ID)
|
||||
if err != nil && err != ErrStoreCapacityOverflow {
|
||||
return nil
|
||||
}
|
||||
|
||||
wsUser := &WSUser{conn, userptr}
|
||||
if user.ID == 0 {
|
||||
wsHub.guests.Lock()
|
||||
wsHub.onlineGuests[wsUser] = true
|
||||
wsHub.guests.Unlock()
|
||||
WsHub.GuestLock.Lock()
|
||||
WsHub.OnlineGuests[wsUser] = true
|
||||
WsHub.GuestLock.Unlock()
|
||||
} else {
|
||||
wsHub.users.Lock()
|
||||
wsHub.onlineUsers[user.ID] = wsUser
|
||||
wsHub.users.Unlock()
|
||||
WsHub.UserLock.Lock()
|
||||
WsHub.OnlineUsers[user.ID] = wsUser
|
||||
WsHub.UserLock.Unlock()
|
||||
}
|
||||
|
||||
//conn.SetReadLimit(/* put the max request size from earlier here? */)
|
||||
@ -189,13 +189,13 @@ func routeWebsockets(w http.ResponseWriter, r *http.Request, user common.User) c
|
||||
_, message, err := conn.ReadMessage()
|
||||
if err != nil {
|
||||
if user.ID == 0 {
|
||||
wsHub.guests.Lock()
|
||||
delete(wsHub.onlineGuests, wsUser)
|
||||
wsHub.guests.Unlock()
|
||||
WsHub.GuestLock.Lock()
|
||||
delete(WsHub.OnlineGuests, wsUser)
|
||||
WsHub.GuestLock.Unlock()
|
||||
} else {
|
||||
wsHub.users.Lock()
|
||||
delete(wsHub.onlineUsers, user.ID)
|
||||
wsHub.users.Unlock()
|
||||
WsHub.UserLock.Lock()
|
||||
delete(WsHub.OnlineUsers, user.ID)
|
||||
WsHub.UserLock.Unlock()
|
||||
}
|
||||
break
|
||||
}
|
||||
@ -239,9 +239,9 @@ func wsPageResponses(wsUser *WSUser, page []byte) {
|
||||
return
|
||||
}
|
||||
|
||||
log.Print(wsHub.online_users)
|
||||
uonline := wsHub.userCount()
|
||||
gonline := wsHub.guestCount()
|
||||
log.Print(WsHub.online_users)
|
||||
uonline := WsHub.UserCount()
|
||||
gonline := WsHub.GuestCount()
|
||||
totonline := uonline + gonline
|
||||
|
||||
w.Write([]byte("set #dash-totonline " + strconv.Itoa(totonline) + " online\r"))
|
||||
@ -321,8 +321,8 @@ AdminStatLoop:
|
||||
|
||||
cpuPerc, cpuerr = cpu.Percent(time.Second, true)
|
||||
memres, ramerr = mem.VirtualMemory()
|
||||
uonline := wsHub.userCount()
|
||||
gonline := wsHub.guestCount()
|
||||
uonline := WsHub.UserCount()
|
||||
gonline := WsHub.GuestCount()
|
||||
totonline := uonline + gonline
|
||||
reqCount := 0
|
||||
|
||||
@ -339,9 +339,9 @@ AdminStatLoop:
|
||||
onlineGuestsColour = greaterThanSwitch(gonline, 1, 10)
|
||||
onlineUsersColour = greaterThanSwitch(uonline, 1, 5)
|
||||
|
||||
totonline, totunit = common.ConvertFriendlyUnit(totonline)
|
||||
uonline, uunit = common.ConvertFriendlyUnit(uonline)
|
||||
gonline, gunit = common.ConvertFriendlyUnit(gonline)
|
||||
totonline, totunit = ConvertFriendlyUnit(totonline)
|
||||
uonline, uunit = ConvertFriendlyUnit(uonline)
|
||||
gonline, gunit = ConvertFriendlyUnit(gonline)
|
||||
}
|
||||
|
||||
if cpuerr != nil {
|
||||
@ -363,8 +363,8 @@ AdminStatLoop:
|
||||
if ramerr != nil {
|
||||
ramstr = "Unknown"
|
||||
} else {
|
||||
totalCount, totalUnit := common.ConvertByteUnit(float64(memres.Total))
|
||||
usedCount := common.ConvertByteInUnit(float64(memres.Total-memres.Available), totalUnit)
|
||||
totalCount, totalUnit := ConvertByteUnit(float64(memres.Total))
|
||||
usedCount := ConvertByteInUnit(float64(memres.Total-memres.Available), totalUnit)
|
||||
|
||||
// Round totals with .9s up, it's how most people see it anyway. Floats are notoriously imprecise, so do it off 0.85
|
||||
var totstr string
|
45
gen_mssql.go
45
gen_mssql.go
@ -20,15 +20,11 @@ type Stmts struct {
|
||||
getUserName *sql.Stmt
|
||||
getEmailsByUser *sql.Stmt
|
||||
getTopicBasic *sql.Stmt
|
||||
getActivityEntry *sql.Stmt
|
||||
forumEntryExists *sql.Stmt
|
||||
groupEntryExists *sql.Stmt
|
||||
getAttachment *sql.Stmt
|
||||
getForumTopics *sql.Stmt
|
||||
getWatchers *sql.Stmt
|
||||
createReport *sql.Stmt
|
||||
addActivity *sql.Stmt
|
||||
notifyOne *sql.Stmt
|
||||
addForumPermsToForum *sql.Stmt
|
||||
addPlugin *sql.Stmt
|
||||
addTheme *sql.Stmt
|
||||
@ -48,7 +44,6 @@ type Stmts struct {
|
||||
deleteActivityStreamMatch *sql.Stmt
|
||||
deleteWordFilter *sql.Stmt
|
||||
reportExists *sql.Stmt
|
||||
notifyWatchers *sql.Stmt
|
||||
|
||||
getActivityFeedByWatcher *sql.Stmt
|
||||
getActivityCountByWatcher *sql.Stmt
|
||||
@ -152,14 +147,6 @@ func _gen_mssql() (err error) {
|
||||
return err
|
||||
}
|
||||
|
||||
common.DebugLog("Preparing getActivityEntry statement.")
|
||||
stmts.getActivityEntry, err = db.Prepare("SELECT [actor],[targetUser],[event],[elementType],[elementID] FROM [activity_stream] WHERE [asid] = ?1")
|
||||
if err != nil {
|
||||
log.Print("Error in getActivityEntry statement.")
|
||||
log.Print("Bad Query: ","SELECT [actor],[targetUser],[event],[elementType],[elementID] FROM [activity_stream] WHERE [asid] = ?1")
|
||||
return err
|
||||
}
|
||||
|
||||
common.DebugLog("Preparing forumEntryExists statement.")
|
||||
stmts.forumEntryExists, err = db.Prepare("SELECT [fid] FROM [forums] WHERE [name] = '' ORDER BY fid ASC OFFSET 0 ROWS FETCH NEXT 1 ROWS ONLY")
|
||||
if err != nil {
|
||||
@ -192,14 +179,6 @@ func _gen_mssql() (err error) {
|
||||
return err
|
||||
}
|
||||
|
||||
common.DebugLog("Preparing getWatchers statement.")
|
||||
stmts.getWatchers, err = db.Prepare("SELECT [activity_subscriptions].[user] FROM [activity_stream] INNER JOIN [activity_subscriptions] ON [activity_subscriptions].[targetType] = [activity_stream].[elementType] AND [activity_subscriptions].[targetID] = [activity_stream].[elementID] AND [activity_subscriptions].[user] != [activity_stream].[actor] WHERE [asid] = ?1")
|
||||
if err != nil {
|
||||
log.Print("Error in getWatchers statement.")
|
||||
log.Print("Bad Query: ","SELECT [activity_subscriptions].[user] FROM [activity_stream] INNER JOIN [activity_subscriptions] ON [activity_subscriptions].[targetType] = [activity_stream].[elementType] AND [activity_subscriptions].[targetID] = [activity_stream].[elementID] AND [activity_subscriptions].[user] != [activity_stream].[actor] WHERE [asid] = ?1")
|
||||
return err
|
||||
}
|
||||
|
||||
common.DebugLog("Preparing createReport statement.")
|
||||
stmts.createReport, err = db.Prepare("INSERT INTO [topics] ([title],[content],[parsed_content],[createdAt],[lastReplyAt],[createdBy],[lastReplyBy],[data],[parentID],[css_class]) VALUES (?,?,?,GETUTCDATE(),GETUTCDATE(),?,?,?,1,'report')")
|
||||
if err != nil {
|
||||
@ -208,22 +187,6 @@ func _gen_mssql() (err error) {
|
||||
return err
|
||||
}
|
||||
|
||||
common.DebugLog("Preparing addActivity statement.")
|
||||
stmts.addActivity, err = db.Prepare("INSERT INTO [activity_stream] ([actor],[targetUser],[event],[elementType],[elementID]) VALUES (?,?,?,?,?)")
|
||||
if err != nil {
|
||||
log.Print("Error in addActivity statement.")
|
||||
log.Print("Bad Query: ","INSERT INTO [activity_stream] ([actor],[targetUser],[event],[elementType],[elementID]) VALUES (?,?,?,?,?)")
|
||||
return err
|
||||
}
|
||||
|
||||
common.DebugLog("Preparing notifyOne statement.")
|
||||
stmts.notifyOne, err = db.Prepare("INSERT INTO [activity_stream_matches] ([watcher],[asid]) VALUES (?,?)")
|
||||
if err != nil {
|
||||
log.Print("Error in notifyOne statement.")
|
||||
log.Print("Bad Query: ","INSERT INTO [activity_stream_matches] ([watcher],[asid]) VALUES (?,?)")
|
||||
return err
|
||||
}
|
||||
|
||||
common.DebugLog("Preparing addForumPermsToForum statement.")
|
||||
stmts.addForumPermsToForum, err = db.Prepare("INSERT INTO [forums_permissions] ([gid],[fid],[preset],[permissions]) VALUES (?,?,?,?)")
|
||||
if err != nil {
|
||||
@ -375,14 +338,6 @@ func _gen_mssql() (err error) {
|
||||
log.Print("Bad Query: ","SELECT COUNT(*) AS [count] FROM [topics] WHERE [data] = ? AND [data] != '' AND [parentID] = 1")
|
||||
return err
|
||||
}
|
||||
|
||||
common.DebugLog("Preparing notifyWatchers statement.")
|
||||
stmts.notifyWatchers, err = db.Prepare("INSERT INTO [activity_stream_matches] ([watcher],[asid]) SELECT [activity_subscriptions].[user],[activity_stream].[asid] FROM [activity_stream] INNER JOIN [activity_subscriptions] ON [activity_subscriptions].[targetType] = [activity_stream].[elementType] AND [activity_subscriptions].[targetID] = [activity_stream].[elementID] AND [activity_subscriptions].[user] != [activity_stream].[actor] WHERE [asid] = ?1")
|
||||
if err != nil {
|
||||
log.Print("Error in notifyWatchers statement.")
|
||||
log.Print("Bad Query: ","INSERT INTO [activity_stream_matches] ([watcher],[asid]) SELECT [activity_subscriptions].[user],[activity_stream].[asid] FROM [activity_stream] INNER JOIN [activity_subscriptions] ON [activity_subscriptions].[targetType] = [activity_stream].[elementType] AND [activity_subscriptions].[targetID] = [activity_stream].[elementID] AND [activity_subscriptions].[user] != [activity_stream].[actor] WHERE [asid] = ?1")
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
40
gen_mysql.go
40
gen_mysql.go
@ -22,15 +22,11 @@ type Stmts struct {
|
||||
getUserName *sql.Stmt
|
||||
getEmailsByUser *sql.Stmt
|
||||
getTopicBasic *sql.Stmt
|
||||
getActivityEntry *sql.Stmt
|
||||
forumEntryExists *sql.Stmt
|
||||
groupEntryExists *sql.Stmt
|
||||
getAttachment *sql.Stmt
|
||||
getForumTopics *sql.Stmt
|
||||
getWatchers *sql.Stmt
|
||||
createReport *sql.Stmt
|
||||
addActivity *sql.Stmt
|
||||
notifyOne *sql.Stmt
|
||||
addForumPermsToForum *sql.Stmt
|
||||
addPlugin *sql.Stmt
|
||||
addTheme *sql.Stmt
|
||||
@ -50,7 +46,6 @@ type Stmts struct {
|
||||
deleteActivityStreamMatch *sql.Stmt
|
||||
deleteWordFilter *sql.Stmt
|
||||
reportExists *sql.Stmt
|
||||
notifyWatchers *sql.Stmt
|
||||
|
||||
getActivityFeedByWatcher *sql.Stmt
|
||||
getActivityCountByWatcher *sql.Stmt
|
||||
@ -143,13 +138,6 @@ func _gen_mysql() (err error) {
|
||||
return err
|
||||
}
|
||||
|
||||
common.DebugLog("Preparing getActivityEntry statement.")
|
||||
stmts.getActivityEntry, err = db.Prepare("SELECT `actor`,`targetUser`,`event`,`elementType`,`elementID` FROM `activity_stream` WHERE `asid` = ?")
|
||||
if err != nil {
|
||||
log.Print("Error in getActivityEntry statement.")
|
||||
return err
|
||||
}
|
||||
|
||||
common.DebugLog("Preparing forumEntryExists statement.")
|
||||
stmts.forumEntryExists, err = db.Prepare("SELECT `fid` FROM `forums` WHERE `name` = '' ORDER BY fid ASC LIMIT 0,1")
|
||||
if err != nil {
|
||||
@ -178,13 +166,6 @@ func _gen_mysql() (err error) {
|
||||
return err
|
||||
}
|
||||
|
||||
common.DebugLog("Preparing getWatchers statement.")
|
||||
stmts.getWatchers, err = db.Prepare("SELECT `activity_subscriptions`.`user` FROM `activity_stream` INNER JOIN `activity_subscriptions` ON `activity_subscriptions`.`targetType` = `activity_stream`.`elementType` AND `activity_subscriptions`.`targetID` = `activity_stream`.`elementID` AND `activity_subscriptions`.`user` != `activity_stream`.`actor` WHERE `asid` = ?")
|
||||
if err != nil {
|
||||
log.Print("Error in getWatchers statement.")
|
||||
return err
|
||||
}
|
||||
|
||||
common.DebugLog("Preparing createReport statement.")
|
||||
stmts.createReport, err = db.Prepare("INSERT INTO `topics`(`title`,`content`,`parsed_content`,`createdAt`,`lastReplyAt`,`createdBy`,`lastReplyBy`,`data`,`parentID`,`css_class`) VALUES (?,?,?,UTC_TIMESTAMP(),UTC_TIMESTAMP(),?,?,?,1,'report')")
|
||||
if err != nil {
|
||||
@ -192,20 +173,6 @@ func _gen_mysql() (err error) {
|
||||
return err
|
||||
}
|
||||
|
||||
common.DebugLog("Preparing addActivity statement.")
|
||||
stmts.addActivity, err = db.Prepare("INSERT INTO `activity_stream`(`actor`,`targetUser`,`event`,`elementType`,`elementID`) VALUES (?,?,?,?,?)")
|
||||
if err != nil {
|
||||
log.Print("Error in addActivity statement.")
|
||||
return err
|
||||
}
|
||||
|
||||
common.DebugLog("Preparing notifyOne statement.")
|
||||
stmts.notifyOne, err = db.Prepare("INSERT INTO `activity_stream_matches`(`watcher`,`asid`) VALUES (?,?)")
|
||||
if err != nil {
|
||||
log.Print("Error in notifyOne statement.")
|
||||
return err
|
||||
}
|
||||
|
||||
common.DebugLog("Preparing addForumPermsToForum statement.")
|
||||
stmts.addForumPermsToForum, err = db.Prepare("INSERT INTO `forums_permissions`(`gid`,`fid`,`preset`,`permissions`) VALUES (?,?,?,?)")
|
||||
if err != nil {
|
||||
@ -338,13 +305,6 @@ func _gen_mysql() (err error) {
|
||||
log.Print("Error in reportExists statement.")
|
||||
return err
|
||||
}
|
||||
|
||||
common.DebugLog("Preparing notifyWatchers statement.")
|
||||
stmts.notifyWatchers, err = db.Prepare("INSERT INTO `activity_stream_matches`(`watcher`,`asid`) SELECT `activity_subscriptions`.`user`, `activity_stream`.`asid` FROM `activity_stream` INNER JOIN `activity_subscriptions` ON `activity_subscriptions`.`targetType` = `activity_stream`.`elementType` AND `activity_subscriptions`.`targetID` = `activity_stream`.`elementID` AND `activity_subscriptions`.`user` != `activity_stream`.`actor` WHERE `asid` = ?")
|
||||
if err != nil {
|
||||
log.Print("Error in notifyWatchers statement.")
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
481
gen_router.go
481
gen_router.go
@ -5,6 +5,7 @@ package main
|
||||
import (
|
||||
"log"
|
||||
"strings"
|
||||
"strconv"
|
||||
"sync"
|
||||
"errors"
|
||||
"net/http"
|
||||
@ -24,7 +25,7 @@ var RouteMap = map[string]interface{}{
|
||||
"routes.ViewForum": routes.ViewForum,
|
||||
"routeChangeTheme": routeChangeTheme,
|
||||
"routeShowAttachment": routeShowAttachment,
|
||||
"routeWebsockets": routeWebsockets,
|
||||
"common.RouteWebsockets": common.RouteWebsockets,
|
||||
"routeReportSubmit": routeReportSubmit,
|
||||
"routes.CreateTopic": routes.CreateTopic,
|
||||
"routes.TopicList": routes.TopicList,
|
||||
@ -58,11 +59,13 @@ var RouteMap = map[string]interface{}{
|
||||
"routePanelAnalyticsRoutes": routePanelAnalyticsRoutes,
|
||||
"routePanelAnalyticsAgents": routePanelAnalyticsAgents,
|
||||
"routePanelAnalyticsSystems": routePanelAnalyticsSystems,
|
||||
"routePanelAnalyticsLanguages": routePanelAnalyticsLanguages,
|
||||
"routePanelAnalyticsReferrers": routePanelAnalyticsReferrers,
|
||||
"routePanelAnalyticsRouteViews": routePanelAnalyticsRouteViews,
|
||||
"routePanelAnalyticsAgentViews": routePanelAnalyticsAgentViews,
|
||||
"routePanelAnalyticsForumViews": routePanelAnalyticsForumViews,
|
||||
"routePanelAnalyticsSystemViews": routePanelAnalyticsSystemViews,
|
||||
"routePanelAnalyticsLanguageViews": routePanelAnalyticsLanguageViews,
|
||||
"routePanelAnalyticsReferrerViews": routePanelAnalyticsReferrerViews,
|
||||
"routePanelAnalyticsPosts": routePanelAnalyticsPosts,
|
||||
"routePanelAnalyticsTopics": routePanelAnalyticsTopics,
|
||||
@ -100,7 +103,7 @@ var RouteMap = map[string]interface{}{
|
||||
"routes.MoveTopicSubmit": routes.MoveTopicSubmit,
|
||||
"routeLikeTopicSubmit": routeLikeTopicSubmit,
|
||||
"routes.ViewTopic": routes.ViewTopic,
|
||||
"routeCreateReplySubmit": routeCreateReplySubmit,
|
||||
"routes.CreateReplySubmit": routes.CreateReplySubmit,
|
||||
"routes.ReplyEditSubmit": routes.ReplyEditSubmit,
|
||||
"routes.ReplyDeleteSubmit": routes.ReplyDeleteSubmit,
|
||||
"routeReplyLikeSubmit": routeReplyLikeSubmit,
|
||||
@ -129,7 +132,7 @@ var routeMapEnum = map[string]int{
|
||||
"routes.ViewForum": 4,
|
||||
"routeChangeTheme": 5,
|
||||
"routeShowAttachment": 6,
|
||||
"routeWebsockets": 7,
|
||||
"common.RouteWebsockets": 7,
|
||||
"routeReportSubmit": 8,
|
||||
"routes.CreateTopic": 9,
|
||||
"routes.TopicList": 10,
|
||||
@ -163,66 +166,68 @@ var routeMapEnum = map[string]int{
|
||||
"routePanelAnalyticsRoutes": 38,
|
||||
"routePanelAnalyticsAgents": 39,
|
||||
"routePanelAnalyticsSystems": 40,
|
||||
"routePanelAnalyticsReferrers": 41,
|
||||
"routePanelAnalyticsRouteViews": 42,
|
||||
"routePanelAnalyticsAgentViews": 43,
|
||||
"routePanelAnalyticsForumViews": 44,
|
||||
"routePanelAnalyticsSystemViews": 45,
|
||||
"routePanelAnalyticsReferrerViews": 46,
|
||||
"routePanelAnalyticsPosts": 47,
|
||||
"routePanelAnalyticsTopics": 48,
|
||||
"routePanelAnalyticsForums": 49,
|
||||
"routePanelGroups": 50,
|
||||
"routePanelGroupsEdit": 51,
|
||||
"routePanelGroupsEditPerms": 52,
|
||||
"routePanelGroupsEditSubmit": 53,
|
||||
"routePanelGroupsEditPermsSubmit": 54,
|
||||
"routePanelGroupsCreateSubmit": 55,
|
||||
"routePanelBackups": 56,
|
||||
"routePanelLogsMod": 57,
|
||||
"routePanelDebug": 58,
|
||||
"routePanelDashboard": 59,
|
||||
"routes.AccountEditCritical": 60,
|
||||
"routeAccountEditCriticalSubmit": 61,
|
||||
"routeAccountEditAvatar": 62,
|
||||
"routeAccountEditAvatarSubmit": 63,
|
||||
"routeAccountEditUsername": 64,
|
||||
"routeAccountEditUsernameSubmit": 65,
|
||||
"routeAccountEditEmail": 66,
|
||||
"routeAccountEditEmailTokenSubmit": 67,
|
||||
"routes.ViewProfile": 68,
|
||||
"routes.BanUserSubmit": 69,
|
||||
"routes.UnbanUser": 70,
|
||||
"routes.ActivateUser": 71,
|
||||
"routes.IPSearch": 72,
|
||||
"routes.CreateTopicSubmit": 73,
|
||||
"routes.EditTopicSubmit": 74,
|
||||
"routes.DeleteTopicSubmit": 75,
|
||||
"routes.StickTopicSubmit": 76,
|
||||
"routes.UnstickTopicSubmit": 77,
|
||||
"routes.LockTopicSubmit": 78,
|
||||
"routes.UnlockTopicSubmit": 79,
|
||||
"routes.MoveTopicSubmit": 80,
|
||||
"routeLikeTopicSubmit": 81,
|
||||
"routes.ViewTopic": 82,
|
||||
"routeCreateReplySubmit": 83,
|
||||
"routes.ReplyEditSubmit": 84,
|
||||
"routes.ReplyDeleteSubmit": 85,
|
||||
"routeReplyLikeSubmit": 86,
|
||||
"routeProfileReplyCreateSubmit": 87,
|
||||
"routes.ProfileReplyEditSubmit": 88,
|
||||
"routes.ProfileReplyDeleteSubmit": 89,
|
||||
"routes.PollVote": 90,
|
||||
"routes.PollResults": 91,
|
||||
"routes.AccountLogin": 92,
|
||||
"routes.AccountRegister": 93,
|
||||
"routeLogout": 94,
|
||||
"routes.AccountLoginSubmit": 95,
|
||||
"routes.AccountRegisterSubmit": 96,
|
||||
"routeDynamic": 97,
|
||||
"routeUploads": 98,
|
||||
"routes.StaticFile": 99,
|
||||
"BadRoute": 100,
|
||||
"routePanelAnalyticsLanguages": 41,
|
||||
"routePanelAnalyticsReferrers": 42,
|
||||
"routePanelAnalyticsRouteViews": 43,
|
||||
"routePanelAnalyticsAgentViews": 44,
|
||||
"routePanelAnalyticsForumViews": 45,
|
||||
"routePanelAnalyticsSystemViews": 46,
|
||||
"routePanelAnalyticsLanguageViews": 47,
|
||||
"routePanelAnalyticsReferrerViews": 48,
|
||||
"routePanelAnalyticsPosts": 49,
|
||||
"routePanelAnalyticsTopics": 50,
|
||||
"routePanelAnalyticsForums": 51,
|
||||
"routePanelGroups": 52,
|
||||
"routePanelGroupsEdit": 53,
|
||||
"routePanelGroupsEditPerms": 54,
|
||||
"routePanelGroupsEditSubmit": 55,
|
||||
"routePanelGroupsEditPermsSubmit": 56,
|
||||
"routePanelGroupsCreateSubmit": 57,
|
||||
"routePanelBackups": 58,
|
||||
"routePanelLogsMod": 59,
|
||||
"routePanelDebug": 60,
|
||||
"routePanelDashboard": 61,
|
||||
"routes.AccountEditCritical": 62,
|
||||
"routeAccountEditCriticalSubmit": 63,
|
||||
"routeAccountEditAvatar": 64,
|
||||
"routeAccountEditAvatarSubmit": 65,
|
||||
"routeAccountEditUsername": 66,
|
||||
"routeAccountEditUsernameSubmit": 67,
|
||||
"routeAccountEditEmail": 68,
|
||||
"routeAccountEditEmailTokenSubmit": 69,
|
||||
"routes.ViewProfile": 70,
|
||||
"routes.BanUserSubmit": 71,
|
||||
"routes.UnbanUser": 72,
|
||||
"routes.ActivateUser": 73,
|
||||
"routes.IPSearch": 74,
|
||||
"routes.CreateTopicSubmit": 75,
|
||||
"routes.EditTopicSubmit": 76,
|
||||
"routes.DeleteTopicSubmit": 77,
|
||||
"routes.StickTopicSubmit": 78,
|
||||
"routes.UnstickTopicSubmit": 79,
|
||||
"routes.LockTopicSubmit": 80,
|
||||
"routes.UnlockTopicSubmit": 81,
|
||||
"routes.MoveTopicSubmit": 82,
|
||||
"routeLikeTopicSubmit": 83,
|
||||
"routes.ViewTopic": 84,
|
||||
"routes.CreateReplySubmit": 85,
|
||||
"routes.ReplyEditSubmit": 86,
|
||||
"routes.ReplyDeleteSubmit": 87,
|
||||
"routeReplyLikeSubmit": 88,
|
||||
"routeProfileReplyCreateSubmit": 89,
|
||||
"routes.ProfileReplyEditSubmit": 90,
|
||||
"routes.ProfileReplyDeleteSubmit": 91,
|
||||
"routes.PollVote": 92,
|
||||
"routes.PollResults": 93,
|
||||
"routes.AccountLogin": 94,
|
||||
"routes.AccountRegister": 95,
|
||||
"routeLogout": 96,
|
||||
"routes.AccountLoginSubmit": 97,
|
||||
"routes.AccountRegisterSubmit": 98,
|
||||
"routeDynamic": 99,
|
||||
"routeUploads": 100,
|
||||
"routes.StaticFile": 101,
|
||||
"BadRoute": 102,
|
||||
}
|
||||
var reverseRouteMapEnum = map[int]string{
|
||||
0: "routeAPI",
|
||||
@ -232,7 +237,7 @@ var reverseRouteMapEnum = map[int]string{
|
||||
4: "routes.ViewForum",
|
||||
5: "routeChangeTheme",
|
||||
6: "routeShowAttachment",
|
||||
7: "routeWebsockets",
|
||||
7: "common.RouteWebsockets",
|
||||
8: "routeReportSubmit",
|
||||
9: "routes.CreateTopic",
|
||||
10: "routes.TopicList",
|
||||
@ -266,66 +271,68 @@ var reverseRouteMapEnum = map[int]string{
|
||||
38: "routePanelAnalyticsRoutes",
|
||||
39: "routePanelAnalyticsAgents",
|
||||
40: "routePanelAnalyticsSystems",
|
||||
41: "routePanelAnalyticsReferrers",
|
||||
42: "routePanelAnalyticsRouteViews",
|
||||
43: "routePanelAnalyticsAgentViews",
|
||||
44: "routePanelAnalyticsForumViews",
|
||||
45: "routePanelAnalyticsSystemViews",
|
||||
46: "routePanelAnalyticsReferrerViews",
|
||||
47: "routePanelAnalyticsPosts",
|
||||
48: "routePanelAnalyticsTopics",
|
||||
49: "routePanelAnalyticsForums",
|
||||
50: "routePanelGroups",
|
||||
51: "routePanelGroupsEdit",
|
||||
52: "routePanelGroupsEditPerms",
|
||||
53: "routePanelGroupsEditSubmit",
|
||||
54: "routePanelGroupsEditPermsSubmit",
|
||||
55: "routePanelGroupsCreateSubmit",
|
||||
56: "routePanelBackups",
|
||||
57: "routePanelLogsMod",
|
||||
58: "routePanelDebug",
|
||||
59: "routePanelDashboard",
|
||||
60: "routes.AccountEditCritical",
|
||||
61: "routeAccountEditCriticalSubmit",
|
||||
62: "routeAccountEditAvatar",
|
||||
63: "routeAccountEditAvatarSubmit",
|
||||
64: "routeAccountEditUsername",
|
||||
65: "routeAccountEditUsernameSubmit",
|
||||
66: "routeAccountEditEmail",
|
||||
67: "routeAccountEditEmailTokenSubmit",
|
||||
68: "routes.ViewProfile",
|
||||
69: "routes.BanUserSubmit",
|
||||
70: "routes.UnbanUser",
|
||||
71: "routes.ActivateUser",
|
||||
72: "routes.IPSearch",
|
||||
73: "routes.CreateTopicSubmit",
|
||||
74: "routes.EditTopicSubmit",
|
||||
75: "routes.DeleteTopicSubmit",
|
||||
76: "routes.StickTopicSubmit",
|
||||
77: "routes.UnstickTopicSubmit",
|
||||
78: "routes.LockTopicSubmit",
|
||||
79: "routes.UnlockTopicSubmit",
|
||||
80: "routes.MoveTopicSubmit",
|
||||
81: "routeLikeTopicSubmit",
|
||||
82: "routes.ViewTopic",
|
||||
83: "routeCreateReplySubmit",
|
||||
84: "routes.ReplyEditSubmit",
|
||||
85: "routes.ReplyDeleteSubmit",
|
||||
86: "routeReplyLikeSubmit",
|
||||
87: "routeProfileReplyCreateSubmit",
|
||||
88: "routes.ProfileReplyEditSubmit",
|
||||
89: "routes.ProfileReplyDeleteSubmit",
|
||||
90: "routes.PollVote",
|
||||
91: "routes.PollResults",
|
||||
92: "routes.AccountLogin",
|
||||
93: "routes.AccountRegister",
|
||||
94: "routeLogout",
|
||||
95: "routes.AccountLoginSubmit",
|
||||
96: "routes.AccountRegisterSubmit",
|
||||
97: "routeDynamic",
|
||||
98: "routeUploads",
|
||||
99: "routes.StaticFile",
|
||||
100: "BadRoute",
|
||||
41: "routePanelAnalyticsLanguages",
|
||||
42: "routePanelAnalyticsReferrers",
|
||||
43: "routePanelAnalyticsRouteViews",
|
||||
44: "routePanelAnalyticsAgentViews",
|
||||
45: "routePanelAnalyticsForumViews",
|
||||
46: "routePanelAnalyticsSystemViews",
|
||||
47: "routePanelAnalyticsLanguageViews",
|
||||
48: "routePanelAnalyticsReferrerViews",
|
||||
49: "routePanelAnalyticsPosts",
|
||||
50: "routePanelAnalyticsTopics",
|
||||
51: "routePanelAnalyticsForums",
|
||||
52: "routePanelGroups",
|
||||
53: "routePanelGroupsEdit",
|
||||
54: "routePanelGroupsEditPerms",
|
||||
55: "routePanelGroupsEditSubmit",
|
||||
56: "routePanelGroupsEditPermsSubmit",
|
||||
57: "routePanelGroupsCreateSubmit",
|
||||
58: "routePanelBackups",
|
||||
59: "routePanelLogsMod",
|
||||
60: "routePanelDebug",
|
||||
61: "routePanelDashboard",
|
||||
62: "routes.AccountEditCritical",
|
||||
63: "routeAccountEditCriticalSubmit",
|
||||
64: "routeAccountEditAvatar",
|
||||
65: "routeAccountEditAvatarSubmit",
|
||||
66: "routeAccountEditUsername",
|
||||
67: "routeAccountEditUsernameSubmit",
|
||||
68: "routeAccountEditEmail",
|
||||
69: "routeAccountEditEmailTokenSubmit",
|
||||
70: "routes.ViewProfile",
|
||||
71: "routes.BanUserSubmit",
|
||||
72: "routes.UnbanUser",
|
||||
73: "routes.ActivateUser",
|
||||
74: "routes.IPSearch",
|
||||
75: "routes.CreateTopicSubmit",
|
||||
76: "routes.EditTopicSubmit",
|
||||
77: "routes.DeleteTopicSubmit",
|
||||
78: "routes.StickTopicSubmit",
|
||||
79: "routes.UnstickTopicSubmit",
|
||||
80: "routes.LockTopicSubmit",
|
||||
81: "routes.UnlockTopicSubmit",
|
||||
82: "routes.MoveTopicSubmit",
|
||||
83: "routeLikeTopicSubmit",
|
||||
84: "routes.ViewTopic",
|
||||
85: "routes.CreateReplySubmit",
|
||||
86: "routes.ReplyEditSubmit",
|
||||
87: "routes.ReplyDeleteSubmit",
|
||||
88: "routeReplyLikeSubmit",
|
||||
89: "routeProfileReplyCreateSubmit",
|
||||
90: "routes.ProfileReplyEditSubmit",
|
||||
91: "routes.ProfileReplyDeleteSubmit",
|
||||
92: "routes.PollVote",
|
||||
93: "routes.PollResults",
|
||||
94: "routes.AccountLogin",
|
||||
95: "routes.AccountRegister",
|
||||
96: "routeLogout",
|
||||
97: "routes.AccountLoginSubmit",
|
||||
98: "routes.AccountRegisterSubmit",
|
||||
99: "routeDynamic",
|
||||
100: "routeUploads",
|
||||
101: "routes.StaticFile",
|
||||
102: "BadRoute",
|
||||
}
|
||||
var osMapEnum = map[string]int{
|
||||
"unknown": 0,
|
||||
@ -366,12 +373,13 @@ var agentMapEnum = map[string]int{
|
||||
"twitter": 19,
|
||||
"cloudflare": 20,
|
||||
"uptimebot": 21,
|
||||
"discourse": 22,
|
||||
"lynx": 23,
|
||||
"blank": 24,
|
||||
"malformed": 25,
|
||||
"suspicious": 26,
|
||||
"zgrab": 27,
|
||||
"slackbot": 22,
|
||||
"discourse": 23,
|
||||
"lynx": 24,
|
||||
"blank": 25,
|
||||
"malformed": 26,
|
||||
"suspicious": 27,
|
||||
"zgrab": 28,
|
||||
}
|
||||
var reverseAgentMapEnum = map[int]string{
|
||||
0: "unknown",
|
||||
@ -396,12 +404,13 @@ var reverseAgentMapEnum = map[int]string{
|
||||
19: "twitter",
|
||||
20: "cloudflare",
|
||||
21: "uptimebot",
|
||||
22: "discourse",
|
||||
23: "lynx",
|
||||
24: "blank",
|
||||
25: "malformed",
|
||||
26: "suspicious",
|
||||
27: "zgrab",
|
||||
22: "slackbot",
|
||||
23: "discourse",
|
||||
24: "lynx",
|
||||
25: "blank",
|
||||
26: "malformed",
|
||||
27: "suspicious",
|
||||
28: "zgrab",
|
||||
}
|
||||
var markToAgent = map[string]string{
|
||||
"OPR":"opera",
|
||||
@ -424,6 +433,7 @@ var markToAgent = map[string]string{
|
||||
"SeznamBot":"seznambot",
|
||||
"CloudFlare":"cloudflare", // Track alwayson specifically in case there are other bots?
|
||||
"Uptimebot":"uptimebot",
|
||||
"Slackbot":"slackbot",
|
||||
"Discordbot":"discord",
|
||||
"Twitterbot":"twitter",
|
||||
"Discourse":"discourse",
|
||||
@ -497,7 +507,7 @@ func (router *GenRouter) StripNewlines(data string) string {
|
||||
return strings.Replace(strings.Replace(data,"\n","",-1),"\r","",-1)
|
||||
}
|
||||
|
||||
func (router *GenRouter) DumpRequest(req *http.Request) {
|
||||
func (router *GenRouter) DumpRequest(req *http.Request, prepend string) {
|
||||
var heads string
|
||||
for key, value := range req.Header {
|
||||
for _, vvalue := range value {
|
||||
@ -505,7 +515,8 @@ func (router *GenRouter) DumpRequest(req *http.Request) {
|
||||
}
|
||||
}
|
||||
|
||||
log.Print("\nUA: " + router.StripNewlines(req.UserAgent()) + "\n" +
|
||||
log.Print(prepend +
|
||||
"\nUA: " + router.StripNewlines(req.UserAgent()) + "\n" +
|
||||
"Method: " + router.StripNewlines(req.Method) + "\n" + heads +
|
||||
"req.Host: " + router.StripNewlines(req.Host) + "\n" +
|
||||
"req.URL.Path: " + router.StripNewlines(req.URL.Path) + "\n" +
|
||||
@ -515,9 +526,8 @@ func (router *GenRouter) DumpRequest(req *http.Request) {
|
||||
}
|
||||
|
||||
func (router *GenRouter) SuspiciousRequest(req *http.Request) {
|
||||
log.Print("Suspicious Request")
|
||||
router.DumpRequest(req)
|
||||
counters.AgentViewCounter.Bump(26)
|
||||
router.DumpRequest(req,"Suspicious Request")
|
||||
counters.AgentViewCounter.Bump(27)
|
||||
}
|
||||
|
||||
// TODO: Pass the default route or config struct to the router rather than accessing it via a package global
|
||||
@ -543,24 +553,23 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
||||
if len(req.URL.Path) == 0 || req.URL.Path[0] != '/' || req.Host != common.Site.Host {
|
||||
w.WriteHeader(200) // 400
|
||||
w.Write([]byte(""))
|
||||
log.Print("Malformed Request")
|
||||
router.DumpRequest(req)
|
||||
counters.AgentViewCounter.Bump(25)
|
||||
router.DumpRequest(req,"Malformed Request")
|
||||
counters.AgentViewCounter.Bump(26)
|
||||
return
|
||||
}
|
||||
|
||||
// TODO: Cover more suspicious strings and at a lower layer than this
|
||||
for _, char := range req.URL.Path {
|
||||
if char != '&' && !(char > 44 && char < 58) && char != '=' && char != '?' && !(char > 64 && char < 91) && char != '\\' && char != '_' && !(char > 96 && char < 123) {
|
||||
router.SuspiciousRequest(req)
|
||||
break
|
||||
}
|
||||
}
|
||||
lowerPath := strings.ToLower(req.URL.Path)
|
||||
// TODO: Flag any requests which has a dot with anything but a number after that
|
||||
if strings.Contains(req.URL.Path,"..") || strings.Contains(req.URL.Path,"--") || strings.Contains(lowerPath,".php") || strings.Contains(lowerPath,".asp") || strings.Contains(lowerPath,".cgi") || strings.Contains(lowerPath,".py") || strings.Contains(lowerPath,".sql") {
|
||||
for _, char := range req.URL.Path {
|
||||
if char != '&' && !(char > 44 && char < 58) && char != '=' && char != '?' && !(char > 64 && char < 91) && char != '\\' && char != '_' && !(char > 96 && char < 123) {
|
||||
router.SuspiciousRequest(req)
|
||||
break
|
||||
}
|
||||
}
|
||||
lowerPath := strings.ToLower(req.URL.Path)
|
||||
// TODO: Flag any requests which has a dot with anything but a number after that
|
||||
if strings.Contains(req.URL.Path,"..") || strings.Contains(req.URL.Path,"--") || strings.Contains(lowerPath,".php") || strings.Contains(lowerPath,".asp") || strings.Contains(lowerPath,".cgi") || strings.Contains(lowerPath,".py") || strings.Contains(lowerPath,".sql") || strings.Contains(lowerPath,".action") {
|
||||
router.SuspiciousRequest(req)
|
||||
}
|
||||
|
||||
var prefix, extraData string
|
||||
prefix = req.URL.Path[0:strings.IndexByte(req.URL.Path[1:],'/') + 1]
|
||||
@ -570,14 +579,13 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
||||
}
|
||||
|
||||
if common.Dev.SuperDebug {
|
||||
log.Print("before routes.StaticFile")
|
||||
router.DumpRequest(req)
|
||||
router.DumpRequest(req,"before routes.StaticFile")
|
||||
}
|
||||
// Increment the request counter
|
||||
counters.GlobalViewCounter.Bump()
|
||||
|
||||
if prefix == "/static" {
|
||||
counters.RouteViewCounter.Bump(99)
|
||||
counters.RouteViewCounter.Bump(101)
|
||||
req.URL.Path += extraData
|
||||
routes.StaticFile(w, req)
|
||||
return
|
||||
@ -591,10 +599,13 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
||||
// TODO: Use a more efficient detector instead of smashing every possible combination in
|
||||
ua := strings.TrimSpace(strings.Replace(strings.TrimPrefix(req.UserAgent(),"Mozilla/5.0 ")," Safari/537.36","",-1)) // Noise, no one's going to be running this and it would require some sort of agent ranking system to determine which identifier should be prioritised over another
|
||||
if ua == "" {
|
||||
counters.AgentViewCounter.Bump(24)
|
||||
counters.AgentViewCounter.Bump(25)
|
||||
if common.Dev.DebugMode {
|
||||
log.Print("Blank UA: ", req.UserAgent())
|
||||
router.DumpRequest(req)
|
||||
var prepend string
|
||||
for _, char := range req.UserAgent() {
|
||||
prepend += strconv.Itoa(int(char)) + " "
|
||||
}
|
||||
router.DumpRequest(req,"Blank UA: " + prepend)
|
||||
}
|
||||
} else {
|
||||
var runeEquals = func(a []rune, b []rune) bool {
|
||||
@ -693,8 +704,11 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
||||
if agent == "" {
|
||||
counters.AgentViewCounter.Bump(0)
|
||||
if common.Dev.DebugMode {
|
||||
log.Print("Unknown UA: ", req.UserAgent())
|
||||
router.DumpRequest(req)
|
||||
var prepend string
|
||||
for _, char := range req.UserAgent() {
|
||||
prepend += strconv.Itoa(int(char)) + " "
|
||||
}
|
||||
router.DumpRequest(req,"Blank UA: " + prepend)
|
||||
}
|
||||
} else {
|
||||
counters.AgentViewCounter.Bump(agentMapEnum[agent])
|
||||
@ -702,6 +716,15 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
||||
counters.OSViewCounter.Bump(osMapEnum[os])
|
||||
}
|
||||
|
||||
// TODO: Do we want to track missing language headers too? Maybe as it's own type, e.g. "noheader"?
|
||||
lang := req.Header.Get("Accept-Language")
|
||||
if lang != "" {
|
||||
lang = strings.TrimSpace(lang)
|
||||
lLang := strings.Split(lang,"-")
|
||||
common.DebugDetail("lLang:", lLang)
|
||||
counters.LangViewCounter.Bump(lLang[0])
|
||||
}
|
||||
|
||||
referrer := req.Header.Get("Referer") // Check the 'referrer' header too? :P
|
||||
if referrer != "" {
|
||||
// ? Optimise this a little?
|
||||
@ -782,7 +805,7 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
||||
case "/ws":
|
||||
req.URL.Path += extraData
|
||||
counters.RouteViewCounter.Bump(7)
|
||||
err = routeWebsockets(w,req,user)
|
||||
err = common.RouteWebsockets(w,req,user)
|
||||
if err != nil {
|
||||
router.handleError(err,w,req,user)
|
||||
}
|
||||
@ -1043,7 +1066,7 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
||||
|
||||
counters.RouteViewCounter.Bump(40)
|
||||
err = routePanelAnalyticsSystems(w,req,user)
|
||||
case "/panel/analytics/referrers/":
|
||||
case "/panel/analytics/langs/":
|
||||
err = common.ParseForm(w,req,user)
|
||||
if err != nil {
|
||||
router.handleError(err,w,req,user)
|
||||
@ -1051,21 +1074,33 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
||||
}
|
||||
|
||||
counters.RouteViewCounter.Bump(41)
|
||||
err = routePanelAnalyticsLanguages(w,req,user)
|
||||
case "/panel/analytics/referrers/":
|
||||
err = common.ParseForm(w,req,user)
|
||||
if err != nil {
|
||||
router.handleError(err,w,req,user)
|
||||
return
|
||||
}
|
||||
|
||||
counters.RouteViewCounter.Bump(42)
|
||||
err = routePanelAnalyticsReferrers(w,req,user)
|
||||
case "/panel/analytics/route/":
|
||||
counters.RouteViewCounter.Bump(42)
|
||||
counters.RouteViewCounter.Bump(43)
|
||||
err = routePanelAnalyticsRouteViews(w,req,user,extraData)
|
||||
case "/panel/analytics/agent/":
|
||||
counters.RouteViewCounter.Bump(43)
|
||||
counters.RouteViewCounter.Bump(44)
|
||||
err = routePanelAnalyticsAgentViews(w,req,user,extraData)
|
||||
case "/panel/analytics/forum/":
|
||||
counters.RouteViewCounter.Bump(44)
|
||||
counters.RouteViewCounter.Bump(45)
|
||||
err = routePanelAnalyticsForumViews(w,req,user,extraData)
|
||||
case "/panel/analytics/system/":
|
||||
counters.RouteViewCounter.Bump(45)
|
||||
err = routePanelAnalyticsSystemViews(w,req,user,extraData)
|
||||
case "/panel/analytics/referrer/":
|
||||
counters.RouteViewCounter.Bump(46)
|
||||
err = routePanelAnalyticsSystemViews(w,req,user,extraData)
|
||||
case "/panel/analytics/lang/":
|
||||
counters.RouteViewCounter.Bump(47)
|
||||
err = routePanelAnalyticsLanguageViews(w,req,user,extraData)
|
||||
case "/panel/analytics/referrer/":
|
||||
counters.RouteViewCounter.Bump(48)
|
||||
err = routePanelAnalyticsReferrerViews(w,req,user,extraData)
|
||||
case "/panel/analytics/posts/":
|
||||
err = common.ParseForm(w,req,user)
|
||||
@ -1074,7 +1109,7 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
counters.RouteViewCounter.Bump(47)
|
||||
counters.RouteViewCounter.Bump(49)
|
||||
err = routePanelAnalyticsPosts(w,req,user)
|
||||
case "/panel/analytics/topics/":
|
||||
err = common.ParseForm(w,req,user)
|
||||
@ -1083,7 +1118,7 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
counters.RouteViewCounter.Bump(48)
|
||||
counters.RouteViewCounter.Bump(50)
|
||||
err = routePanelAnalyticsTopics(w,req,user)
|
||||
case "/panel/analytics/forums/":
|
||||
err = common.ParseForm(w,req,user)
|
||||
@ -1092,16 +1127,16 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
counters.RouteViewCounter.Bump(49)
|
||||
counters.RouteViewCounter.Bump(51)
|
||||
err = routePanelAnalyticsForums(w,req,user)
|
||||
case "/panel/groups/":
|
||||
counters.RouteViewCounter.Bump(50)
|
||||
counters.RouteViewCounter.Bump(52)
|
||||
err = routePanelGroups(w,req,user)
|
||||
case "/panel/groups/edit/":
|
||||
counters.RouteViewCounter.Bump(51)
|
||||
counters.RouteViewCounter.Bump(53)
|
||||
err = routePanelGroupsEdit(w,req,user,extraData)
|
||||
case "/panel/groups/edit/perms/":
|
||||
counters.RouteViewCounter.Bump(52)
|
||||
counters.RouteViewCounter.Bump(54)
|
||||
err = routePanelGroupsEditPerms(w,req,user,extraData)
|
||||
case "/panel/groups/edit/submit/":
|
||||
err = common.NoSessionMismatch(w,req,user)
|
||||
@ -1110,7 +1145,7 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
counters.RouteViewCounter.Bump(53)
|
||||
counters.RouteViewCounter.Bump(55)
|
||||
err = routePanelGroupsEditSubmit(w,req,user,extraData)
|
||||
case "/panel/groups/edit/perms/submit/":
|
||||
err = common.NoSessionMismatch(w,req,user)
|
||||
@ -1119,7 +1154,7 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
counters.RouteViewCounter.Bump(54)
|
||||
counters.RouteViewCounter.Bump(56)
|
||||
err = routePanelGroupsEditPermsSubmit(w,req,user,extraData)
|
||||
case "/panel/groups/create/":
|
||||
err = common.NoSessionMismatch(w,req,user)
|
||||
@ -1128,7 +1163,7 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
counters.RouteViewCounter.Bump(55)
|
||||
counters.RouteViewCounter.Bump(57)
|
||||
err = routePanelGroupsCreateSubmit(w,req,user)
|
||||
case "/panel/backups/":
|
||||
err = common.SuperAdminOnly(w,req,user)
|
||||
@ -1137,10 +1172,10 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
counters.RouteViewCounter.Bump(56)
|
||||
counters.RouteViewCounter.Bump(58)
|
||||
err = routePanelBackups(w,req,user,extraData)
|
||||
case "/panel/logs/mod/":
|
||||
counters.RouteViewCounter.Bump(57)
|
||||
counters.RouteViewCounter.Bump(59)
|
||||
err = routePanelLogsMod(w,req,user)
|
||||
case "/panel/debug/":
|
||||
err = common.AdminOnly(w,req,user)
|
||||
@ -1149,10 +1184,10 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
counters.RouteViewCounter.Bump(58)
|
||||
counters.RouteViewCounter.Bump(60)
|
||||
err = routePanelDebug(w,req,user)
|
||||
default:
|
||||
counters.RouteViewCounter.Bump(59)
|
||||
counters.RouteViewCounter.Bump(61)
|
||||
err = routePanelDashboard(w,req,user)
|
||||
}
|
||||
if err != nil {
|
||||
@ -1167,7 +1202,7 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
counters.RouteViewCounter.Bump(60)
|
||||
counters.RouteViewCounter.Bump(62)
|
||||
err = routes.AccountEditCritical(w,req,user)
|
||||
case "/user/edit/critical/submit/":
|
||||
err = common.NoSessionMismatch(w,req,user)
|
||||
@ -1182,7 +1217,7 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
counters.RouteViewCounter.Bump(61)
|
||||
counters.RouteViewCounter.Bump(63)
|
||||
err = routeAccountEditCriticalSubmit(w,req,user)
|
||||
case "/user/edit/avatar/":
|
||||
err = common.MemberOnly(w,req,user)
|
||||
@ -1191,7 +1226,7 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
counters.RouteViewCounter.Bump(62)
|
||||
counters.RouteViewCounter.Bump(64)
|
||||
err = routeAccountEditAvatar(w,req,user)
|
||||
case "/user/edit/avatar/submit/":
|
||||
err = common.MemberOnly(w,req,user)
|
||||
@ -1211,7 +1246,7 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
counters.RouteViewCounter.Bump(63)
|
||||
counters.RouteViewCounter.Bump(65)
|
||||
err = routeAccountEditAvatarSubmit(w,req,user)
|
||||
case "/user/edit/username/":
|
||||
err = common.MemberOnly(w,req,user)
|
||||
@ -1220,7 +1255,7 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
counters.RouteViewCounter.Bump(64)
|
||||
counters.RouteViewCounter.Bump(66)
|
||||
err = routeAccountEditUsername(w,req,user)
|
||||
case "/user/edit/username/submit/":
|
||||
err = common.NoSessionMismatch(w,req,user)
|
||||
@ -1235,7 +1270,7 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
counters.RouteViewCounter.Bump(65)
|
||||
counters.RouteViewCounter.Bump(67)
|
||||
err = routeAccountEditUsernameSubmit(w,req,user)
|
||||
case "/user/edit/email/":
|
||||
err = common.MemberOnly(w,req,user)
|
||||
@ -1244,7 +1279,7 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
counters.RouteViewCounter.Bump(66)
|
||||
counters.RouteViewCounter.Bump(68)
|
||||
err = routeAccountEditEmail(w,req,user)
|
||||
case "/user/edit/token/":
|
||||
err = common.NoSessionMismatch(w,req,user)
|
||||
@ -1259,11 +1294,11 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
counters.RouteViewCounter.Bump(67)
|
||||
counters.RouteViewCounter.Bump(69)
|
||||
err = routeAccountEditEmailTokenSubmit(w,req,user,extraData)
|
||||
default:
|
||||
req.URL.Path += extraData
|
||||
counters.RouteViewCounter.Bump(68)
|
||||
counters.RouteViewCounter.Bump(70)
|
||||
err = routes.ViewProfile(w,req,user)
|
||||
}
|
||||
if err != nil {
|
||||
@ -1284,7 +1319,7 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
counters.RouteViewCounter.Bump(69)
|
||||
counters.RouteViewCounter.Bump(71)
|
||||
err = routes.BanUserSubmit(w,req,user,extraData)
|
||||
case "/users/unban/":
|
||||
err = common.NoSessionMismatch(w,req,user)
|
||||
@ -1299,7 +1334,7 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
counters.RouteViewCounter.Bump(70)
|
||||
counters.RouteViewCounter.Bump(72)
|
||||
err = routes.UnbanUser(w,req,user,extraData)
|
||||
case "/users/activate/":
|
||||
err = common.NoSessionMismatch(w,req,user)
|
||||
@ -1314,7 +1349,7 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
counters.RouteViewCounter.Bump(71)
|
||||
counters.RouteViewCounter.Bump(73)
|
||||
err = routes.ActivateUser(w,req,user,extraData)
|
||||
case "/users/ips/":
|
||||
err = common.MemberOnly(w,req,user)
|
||||
@ -1323,7 +1358,7 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
counters.RouteViewCounter.Bump(72)
|
||||
counters.RouteViewCounter.Bump(74)
|
||||
err = routes.IPSearch(w,req,user)
|
||||
}
|
||||
if err != nil {
|
||||
@ -1349,7 +1384,7 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
counters.RouteViewCounter.Bump(73)
|
||||
counters.RouteViewCounter.Bump(75)
|
||||
err = routes.CreateTopicSubmit(w,req,user)
|
||||
case "/topic/edit/submit/":
|
||||
err = common.NoSessionMismatch(w,req,user)
|
||||
@ -1364,7 +1399,7 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
counters.RouteViewCounter.Bump(74)
|
||||
counters.RouteViewCounter.Bump(76)
|
||||
err = routes.EditTopicSubmit(w,req,user,extraData)
|
||||
case "/topic/delete/submit/":
|
||||
err = common.NoSessionMismatch(w,req,user)
|
||||
@ -1380,7 +1415,7 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
||||
}
|
||||
|
||||
req.URL.Path += extraData
|
||||
counters.RouteViewCounter.Bump(75)
|
||||
counters.RouteViewCounter.Bump(77)
|
||||
err = routes.DeleteTopicSubmit(w,req,user)
|
||||
case "/topic/stick/submit/":
|
||||
err = common.NoSessionMismatch(w,req,user)
|
||||
@ -1395,7 +1430,7 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
counters.RouteViewCounter.Bump(76)
|
||||
counters.RouteViewCounter.Bump(78)
|
||||
err = routes.StickTopicSubmit(w,req,user,extraData)
|
||||
case "/topic/unstick/submit/":
|
||||
err = common.NoSessionMismatch(w,req,user)
|
||||
@ -1410,7 +1445,7 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
counters.RouteViewCounter.Bump(77)
|
||||
counters.RouteViewCounter.Bump(79)
|
||||
err = routes.UnstickTopicSubmit(w,req,user,extraData)
|
||||
case "/topic/lock/submit/":
|
||||
err = common.NoSessionMismatch(w,req,user)
|
||||
@ -1426,7 +1461,7 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
||||
}
|
||||
|
||||
req.URL.Path += extraData
|
||||
counters.RouteViewCounter.Bump(78)
|
||||
counters.RouteViewCounter.Bump(80)
|
||||
err = routes.LockTopicSubmit(w,req,user)
|
||||
case "/topic/unlock/submit/":
|
||||
err = common.NoSessionMismatch(w,req,user)
|
||||
@ -1441,7 +1476,7 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
counters.RouteViewCounter.Bump(79)
|
||||
counters.RouteViewCounter.Bump(81)
|
||||
err = routes.UnlockTopicSubmit(w,req,user,extraData)
|
||||
case "/topic/move/submit/":
|
||||
err = common.NoSessionMismatch(w,req,user)
|
||||
@ -1456,7 +1491,7 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
counters.RouteViewCounter.Bump(80)
|
||||
counters.RouteViewCounter.Bump(82)
|
||||
err = routes.MoveTopicSubmit(w,req,user,extraData)
|
||||
case "/topic/like/submit/":
|
||||
err = common.NoSessionMismatch(w,req,user)
|
||||
@ -1471,10 +1506,10 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
counters.RouteViewCounter.Bump(81)
|
||||
counters.RouteViewCounter.Bump(83)
|
||||
err = routeLikeTopicSubmit(w,req,user,extraData)
|
||||
default:
|
||||
counters.RouteViewCounter.Bump(82)
|
||||
counters.RouteViewCounter.Bump(84)
|
||||
err = routes.ViewTopic(w,req,user, extraData)
|
||||
}
|
||||
if err != nil {
|
||||
@ -1500,8 +1535,8 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
counters.RouteViewCounter.Bump(83)
|
||||
err = routeCreateReplySubmit(w,req,user)
|
||||
counters.RouteViewCounter.Bump(85)
|
||||
err = routes.CreateReplySubmit(w,req,user)
|
||||
case "/reply/edit/submit/":
|
||||
err = common.NoSessionMismatch(w,req,user)
|
||||
if err != nil {
|
||||
@ -1515,7 +1550,7 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
counters.RouteViewCounter.Bump(84)
|
||||
counters.RouteViewCounter.Bump(86)
|
||||
err = routes.ReplyEditSubmit(w,req,user,extraData)
|
||||
case "/reply/delete/submit/":
|
||||
err = common.NoSessionMismatch(w,req,user)
|
||||
@ -1530,7 +1565,7 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
counters.RouteViewCounter.Bump(85)
|
||||
counters.RouteViewCounter.Bump(87)
|
||||
err = routes.ReplyDeleteSubmit(w,req,user,extraData)
|
||||
case "/reply/like/submit/":
|
||||
err = common.NoSessionMismatch(w,req,user)
|
||||
@ -1545,7 +1580,7 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
counters.RouteViewCounter.Bump(86)
|
||||
counters.RouteViewCounter.Bump(88)
|
||||
err = routeReplyLikeSubmit(w,req,user,extraData)
|
||||
}
|
||||
if err != nil {
|
||||
@ -1566,7 +1601,7 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
counters.RouteViewCounter.Bump(87)
|
||||
counters.RouteViewCounter.Bump(89)
|
||||
err = routeProfileReplyCreateSubmit(w,req,user)
|
||||
case "/profile/reply/edit/submit/":
|
||||
err = common.NoSessionMismatch(w,req,user)
|
||||
@ -1581,7 +1616,7 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
counters.RouteViewCounter.Bump(88)
|
||||
counters.RouteViewCounter.Bump(90)
|
||||
err = routes.ProfileReplyEditSubmit(w,req,user,extraData)
|
||||
case "/profile/reply/delete/submit/":
|
||||
err = common.NoSessionMismatch(w,req,user)
|
||||
@ -1596,7 +1631,7 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
counters.RouteViewCounter.Bump(89)
|
||||
counters.RouteViewCounter.Bump(91)
|
||||
err = routes.ProfileReplyDeleteSubmit(w,req,user,extraData)
|
||||
}
|
||||
if err != nil {
|
||||
@ -1617,10 +1652,10 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
counters.RouteViewCounter.Bump(90)
|
||||
counters.RouteViewCounter.Bump(92)
|
||||
err = routes.PollVote(w,req,user,extraData)
|
||||
case "/poll/results/":
|
||||
counters.RouteViewCounter.Bump(91)
|
||||
counters.RouteViewCounter.Bump(93)
|
||||
err = routes.PollResults(w,req,user,extraData)
|
||||
}
|
||||
if err != nil {
|
||||
@ -1629,10 +1664,10 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
||||
case "/accounts":
|
||||
switch(req.URL.Path) {
|
||||
case "/accounts/login/":
|
||||
counters.RouteViewCounter.Bump(92)
|
||||
counters.RouteViewCounter.Bump(94)
|
||||
err = routes.AccountLogin(w,req,user)
|
||||
case "/accounts/create/":
|
||||
counters.RouteViewCounter.Bump(93)
|
||||
counters.RouteViewCounter.Bump(95)
|
||||
err = routes.AccountRegister(w,req,user)
|
||||
case "/accounts/logout/":
|
||||
err = common.NoSessionMismatch(w,req,user)
|
||||
@ -1647,7 +1682,7 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
counters.RouteViewCounter.Bump(94)
|
||||
counters.RouteViewCounter.Bump(96)
|
||||
err = routeLogout(w,req,user)
|
||||
case "/accounts/login/submit/":
|
||||
err = common.ParseForm(w,req,user)
|
||||
@ -1656,7 +1691,7 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
counters.RouteViewCounter.Bump(95)
|
||||
counters.RouteViewCounter.Bump(97)
|
||||
err = routes.AccountLoginSubmit(w,req,user)
|
||||
case "/accounts/create/submit/":
|
||||
err = common.ParseForm(w,req,user)
|
||||
@ -1665,7 +1700,7 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
counters.RouteViewCounter.Bump(96)
|
||||
counters.RouteViewCounter.Bump(98)
|
||||
err = routes.AccountRegisterSubmit(w,req,user)
|
||||
}
|
||||
if err != nil {
|
||||
@ -1682,7 +1717,7 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
||||
common.NotFound(w,req,nil)
|
||||
return
|
||||
}
|
||||
counters.RouteViewCounter.Bump(98)
|
||||
counters.RouteViewCounter.Bump(100)
|
||||
req.URL.Path += extraData
|
||||
// TODO: Find a way to propagate errors up from this?
|
||||
router.UploadHandler(w,req) // TODO: Count these views
|
||||
@ -1725,7 +1760,7 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
||||
router.RUnlock()
|
||||
|
||||
if ok {
|
||||
counters.RouteViewCounter.Bump(97) // TODO: Be more specific about *which* dynamic route it is
|
||||
counters.RouteViewCounter.Bump(99) // TODO: Be more specific about *which* dynamic route it is
|
||||
req.URL.Path += extraData
|
||||
err = handle(w,req,user)
|
||||
if err != nil {
|
||||
@ -1739,7 +1774,7 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
||||
if strings.Contains(lowerPath,"admin") || strings.Contains(lowerPath,"sql") || strings.Contains(lowerPath,"manage") || strings.Contains(lowerPath,"//") || strings.Contains(lowerPath,"\\\\") || strings.Contains(lowerPath,"wp") || strings.Contains(lowerPath,"wordpress") || strings.Contains(lowerPath,"config") || strings.Contains(lowerPath,"setup") || strings.Contains(lowerPath,"install") || strings.Contains(lowerPath,"update") || strings.Contains(lowerPath,"php") {
|
||||
router.SuspiciousRequest(req)
|
||||
}
|
||||
counters.RouteViewCounter.Bump(100)
|
||||
counters.RouteViewCounter.Bump(102)
|
||||
common.NotFound(w,req,nil)
|
||||
}
|
||||
}
|
||||
|
@ -1,9 +1,11 @@
|
||||
{
|
||||
"Name": "english",
|
||||
|
||||
"Levels": {
|
||||
"Level": "Level {0}",
|
||||
"LevelMax": ""
|
||||
},
|
||||
|
||||
"GlobalPerms": {
|
||||
"BanUsers": "Can ban users",
|
||||
"ActivateUsers": "Can activate users",
|
||||
@ -27,6 +29,7 @@
|
||||
|
||||
"UploadFiles": "Can upload files"
|
||||
},
|
||||
|
||||
"LocalPerms": {
|
||||
"ViewTopic": "Can view topics",
|
||||
"LikeItem": "Can like items",
|
||||
@ -40,9 +43,11 @@
|
||||
"CloseTopic": "Can lock topics",
|
||||
"MoveTopic": "Can move topics in or out"
|
||||
},
|
||||
|
||||
"SettingLabels": {
|
||||
"activation_type": "Activate All,Email Activation,Admin Approval"
|
||||
},
|
||||
|
||||
"PermPresets": {
|
||||
"all":"Public",
|
||||
"announce":"Announcements",
|
||||
@ -53,14 +58,17 @@
|
||||
"custom":"Custom",
|
||||
"unknown":"Unknown"
|
||||
},
|
||||
|
||||
"Accounts": {
|
||||
"VerifyEmailSubject": "Validate Your Email @ {{name}}",
|
||||
"VerifyEmailBody": "Dear {{username}}, following your registration on our forums, we ask you to validate your email, so that we can confirm that this email actually belongs to you.\n\nClick on the following link to do so. {{schema}}://{{url}}/user/edit/token/{{token}}\n\nIf you haven't created an account here, then please feel free to ignore this email.\nWe're sorry for the inconvenience this may have caused."
|
||||
},
|
||||
|
||||
"Errors": {
|
||||
"NoPerms": {
|
||||
}
|
||||
},
|
||||
|
||||
"PageTitles": {
|
||||
"overview":"Overview",
|
||||
"page":"Page",
|
||||
@ -68,7 +76,7 @@
|
||||
"forums":"Forum List",
|
||||
"login":"Login",
|
||||
"register":"Registration",
|
||||
"ip-search":"IP Search",
|
||||
"ip_search":"IP Search",
|
||||
|
||||
"panel_dashboard":"Control Panel Dashboard",
|
||||
"panel_forums":"Forum Manager",
|
||||
@ -114,6 +122,7 @@
|
||||
"twitter":"Twitterbot",
|
||||
"cloudflare":"Cloudflare Alwayson",
|
||||
"uptimebot":"Uptimebot",
|
||||
"slackbot":"Slackbot",
|
||||
"discourse":"Discourse Forum Onebox",
|
||||
"lynx":"Lynx",
|
||||
|
||||
@ -123,6 +132,7 @@
|
||||
"blank":"Blank",
|
||||
"malformed":"Malformed"
|
||||
},
|
||||
|
||||
"OperatingSystems": {
|
||||
"windows": "Microsoft Windows",
|
||||
"linux":"Linux",
|
||||
@ -130,5 +140,110 @@
|
||||
"android": "Android",
|
||||
"iphone":"iPhone",
|
||||
"unknown":"Unknown"
|
||||
},
|
||||
|
||||
"HumanLanguages": {
|
||||
"af":"Afrikaans",
|
||||
"ar":"Arabic",
|
||||
"az":"Azeri (Latin)",
|
||||
"be":"Belarusian",
|
||||
"bg":"Bulgarian",
|
||||
"bs":"Bosnian (Bosnia and Herzegovina)",
|
||||
"ca":"Catalan",
|
||||
"cs":"Czech",
|
||||
"cy":"Welsh",
|
||||
"da":"Danish",
|
||||
"de":"German",
|
||||
"dv":"Divehi",
|
||||
"el":"Greek",
|
||||
"en":"English",
|
||||
"eo":"Esperanto",
|
||||
"es":"Spanish",
|
||||
"et":"Estonian",
|
||||
"eu":"Basque",
|
||||
"fa":"Farsi",
|
||||
"fi":"Finnish",
|
||||
"fo":"Faroese",
|
||||
"fr":"French",
|
||||
"gl":"Galician",
|
||||
"gu":"Gujarati",
|
||||
"he":"Hebrew",
|
||||
"hi":"Hindi",
|
||||
"hr":"Croatian",
|
||||
"hu":"Hungarian",
|
||||
"hy":"Armenian",
|
||||
"id":"Indonesian",
|
||||
"is":"Icelandic",
|
||||
"it":"Italian",
|
||||
"ja":"Japanese",
|
||||
"ka":"Georgian",
|
||||
"kk":"Kazakh",
|
||||
"kn":"Kannada",
|
||||
"ko":"Korean",
|
||||
"kok":"Konkani",
|
||||
"ky":"Kyrgyz",
|
||||
"lt":"Lithuanian",
|
||||
"lv":"Latvian",
|
||||
"mi":"Maori",
|
||||
"mk":"FYRO Macedonian",
|
||||
"mn":"Mongolian",
|
||||
"mr":"Marathi",
|
||||
"ms":"Malay",
|
||||
"mt":"Maltese",
|
||||
"nb":"Norwegian (Bokm?l)",
|
||||
"nl":"Dutch",
|
||||
"nn":"Norwegian (Nynorsk) (Norway)",
|
||||
"ns":"Northern Sotho",
|
||||
"pa":"Punjabi",
|
||||
"pl":"Polish",
|
||||
"ps":"Pashto",
|
||||
"pt":"Portuguese",
|
||||
"qu":"Quechua",
|
||||
"ro":"Romanian",
|
||||
"ru":"Russian",
|
||||
"sa":"Sanskrit",
|
||||
"se":"Sami (Northern)",
|
||||
"sk":"Slovak",
|
||||
"sl":"Slovenian",
|
||||
"sq":"Albanian",
|
||||
"sr":"Serbian (Latin)",
|
||||
"sv":"Swedish",
|
||||
"sw":"Swahili",
|
||||
"syr":"Syriac",
|
||||
"ta":"Tamil",
|
||||
"te":"Telugu",
|
||||
"th":"Thai",
|
||||
"tl":"Tagalog",
|
||||
"tn":"Tswana",
|
||||
"tr":"Turkish",
|
||||
"tt":"Tatar",
|
||||
"ts":"Tsonga",
|
||||
"uk":"Ukrainian",
|
||||
"ur":"Urdu",
|
||||
"uz":"Uzbek (Latin)",
|
||||
"vi":"Vietnamese",
|
||||
"xh":"Xhosa",
|
||||
"zh":"Chinese",
|
||||
"zu":"Zulu"
|
||||
},
|
||||
|
||||
"TmplPhrases": {
|
||||
"login_head":"Login",
|
||||
"login_account_name":"Account Name",
|
||||
"login_account_password":"Password",
|
||||
"login_submit_button":"Login",
|
||||
"login_no_account":"Don't have an account?",
|
||||
|
||||
"register_head":"Create Account",
|
||||
"register_account_name":"Account Name",
|
||||
"register_account_email":"Email",
|
||||
"register_account_password":"Password",
|
||||
"register_account_confirm_password":"Confirm Password",
|
||||
"register_submit_button":"Create Account",
|
||||
|
||||
"ip_search_head":"IP Search",
|
||||
"ip_search_no_users":"No users found.",
|
||||
|
||||
"error_head":"An error has occured"
|
||||
}
|
||||
}
|
8
main.go
8
main.go
@ -114,6 +114,10 @@ func afterDBInit() (err error) {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
counters.LangViewCounter, err = counters.NewDefaultLangViewCounter()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
counters.RouteViewCounter, err = counters.NewDefaultRouteViewCounter()
|
||||
if err != nil {
|
||||
return err
|
||||
@ -290,9 +294,9 @@ func main() {
|
||||
|
||||
// Run this goroutine once every half second
|
||||
halfSecondTicker := time.NewTicker(time.Second / 2)
|
||||
secondTicker := time.NewTicker(1 * time.Second)
|
||||
secondTicker := time.NewTicker(time.Second)
|
||||
fifteenMinuteTicker := time.NewTicker(15 * time.Minute)
|
||||
//hourTicker := time.NewTicker(1 * time.Hour)
|
||||
//hourTicker := time.NewTicker(time.Hour)
|
||||
go func() {
|
||||
var runHook = func(name string) {
|
||||
err := common.RunTaskHook(name)
|
||||
|
226
member_routes.go
226
member_routes.go
@ -1,11 +1,8 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"crypto/sha256"
|
||||
"encoding/hex"
|
||||
"html"
|
||||
"io"
|
||||
"log"
|
||||
"net/http"
|
||||
"os"
|
||||
"path/filepath"
|
||||
@ -17,188 +14,6 @@ import (
|
||||
"./common/counters"
|
||||
)
|
||||
|
||||
func routeCreateReplySubmit(w http.ResponseWriter, r *http.Request, user common.User) common.RouteError {
|
||||
tid, err := strconv.Atoi(r.PostFormValue("tid"))
|
||||
if err != nil {
|
||||
return common.PreError("Failed to convert the Topic ID", w, r)
|
||||
}
|
||||
|
||||
topic, err := common.Topics.Get(tid)
|
||||
if err == ErrNoRows {
|
||||
return common.PreError("Couldn't find the parent topic", w, r)
|
||||
} else if err != nil {
|
||||
return common.InternalError(err, w, r)
|
||||
}
|
||||
|
||||
// TODO: Add hooks to make use of headerLite
|
||||
_, ferr := common.SimpleForumUserCheck(w, r, &user, topic.ParentID)
|
||||
if ferr != nil {
|
||||
return ferr
|
||||
}
|
||||
if !user.Perms.ViewTopic || !user.Perms.CreateReply {
|
||||
return common.NoPermissions(w, r, user)
|
||||
}
|
||||
|
||||
// Handle the file attachments
|
||||
// TODO: Stop duplicating this code
|
||||
if user.Perms.UploadFiles {
|
||||
files, ok := r.MultipartForm.File["upload_files"]
|
||||
if ok {
|
||||
if len(files) > 5 {
|
||||
return common.LocalError("You can't attach more than five files", w, r, user)
|
||||
}
|
||||
|
||||
for _, file := range files {
|
||||
log.Print("file.Filename ", file.Filename)
|
||||
extarr := strings.Split(file.Filename, ".")
|
||||
if len(extarr) < 2 {
|
||||
return common.LocalError("Bad file", w, r, user)
|
||||
}
|
||||
ext := extarr[len(extarr)-1]
|
||||
|
||||
// TODO: Can we do this without a regex?
|
||||
reg, err := regexp.Compile("[^A-Za-z0-9]+")
|
||||
if err != nil {
|
||||
return common.LocalError("Bad file extension", w, r, user)
|
||||
}
|
||||
ext = strings.ToLower(reg.ReplaceAllString(ext, ""))
|
||||
if !common.AllowedFileExts.Contains(ext) {
|
||||
return common.LocalError("You're not allowed to upload files with this extension", w, r, user)
|
||||
}
|
||||
|
||||
infile, err := file.Open()
|
||||
if err != nil {
|
||||
return common.LocalError("Upload failed", w, r, user)
|
||||
}
|
||||
defer infile.Close()
|
||||
|
||||
hasher := sha256.New()
|
||||
_, err = io.Copy(hasher, infile)
|
||||
if err != nil {
|
||||
return common.LocalError("Upload failed [Hashing Failed]", w, r, user)
|
||||
}
|
||||
infile.Close()
|
||||
|
||||
checksum := hex.EncodeToString(hasher.Sum(nil))
|
||||
filename := checksum + "." + ext
|
||||
outfile, err := os.Create("." + "/attachs/" + filename)
|
||||
if err != nil {
|
||||
return common.LocalError("Upload failed [File Creation Failed]", w, r, user)
|
||||
}
|
||||
defer outfile.Close()
|
||||
|
||||
infile, err = file.Open()
|
||||
if err != nil {
|
||||
return common.LocalError("Upload failed", w, r, user)
|
||||
}
|
||||
defer infile.Close()
|
||||
|
||||
_, err = io.Copy(outfile, infile)
|
||||
if err != nil {
|
||||
return common.LocalError("Upload failed [Copy Failed]", w, r, user)
|
||||
}
|
||||
|
||||
err = common.Attachments.Add(topic.ParentID, "forums", tid, "replies", user.ID, filename)
|
||||
if err != nil {
|
||||
return common.InternalError(err, w, r)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
content := common.PreparseMessage(r.PostFormValue("reply-content"))
|
||||
// TODO: Fully parse the post and put that in the parsed column
|
||||
rid, err := common.Rstore.Create(topic, content, user.LastIP, user.ID)
|
||||
if err != nil {
|
||||
return common.InternalError(err, w, r)
|
||||
}
|
||||
|
||||
reply, err := common.Rstore.Get(rid)
|
||||
if err != nil {
|
||||
return common.LocalError("Unable to load the reply", w, r, user)
|
||||
}
|
||||
if r.PostFormValue("has_poll") == "1" {
|
||||
var maxPollOptions = 10
|
||||
var pollInputItems = make(map[int]string)
|
||||
for key, values := range r.Form {
|
||||
//if common.Dev.SuperDebug {
|
||||
log.Print("key: ", key)
|
||||
log.Printf("values: %+v\n", values)
|
||||
//}
|
||||
for _, value := range values {
|
||||
if strings.HasPrefix(key, "pollinputitem[") {
|
||||
halves := strings.Split(key, "[")
|
||||
if len(halves) != 2 {
|
||||
return common.LocalError("Malformed pollinputitem", w, r, user)
|
||||
}
|
||||
halves[1] = strings.TrimSuffix(halves[1], "]")
|
||||
|
||||
index, err := strconv.Atoi(halves[1])
|
||||
if err != nil {
|
||||
return common.LocalError("Malformed pollinputitem", w, r, user)
|
||||
}
|
||||
|
||||
// If there are duplicates, then something has gone horribly wrong, so let's ignore them, this'll likely happen during an attack
|
||||
_, exists := pollInputItems[index]
|
||||
if !exists && len(html.EscapeString(value)) != 0 {
|
||||
pollInputItems[index] = html.EscapeString(value)
|
||||
if len(pollInputItems) >= maxPollOptions {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Make sure the indices are sequential to avoid out of bounds issues
|
||||
var seqPollInputItems = make(map[int]string)
|
||||
for i := 0; i < len(pollInputItems); i++ {
|
||||
seqPollInputItems[i] = pollInputItems[i]
|
||||
}
|
||||
|
||||
pollType := 0 // Basic single choice
|
||||
_, err := common.Polls.Create(reply, pollType, seqPollInputItems)
|
||||
if err != nil {
|
||||
return common.LocalError("Failed to add poll to reply", w, r, user) // TODO: Might need to be an internal error as it could leave phantom polls?
|
||||
}
|
||||
}
|
||||
|
||||
err = common.Forums.UpdateLastTopic(tid, user.ID, topic.ParentID)
|
||||
if err != nil && err != ErrNoRows {
|
||||
return common.InternalError(err, w, r)
|
||||
}
|
||||
|
||||
res, err := stmts.addActivity.Exec(user.ID, topic.CreatedBy, "reply", "topic", tid)
|
||||
if err != nil {
|
||||
return common.InternalError(err, w, r)
|
||||
}
|
||||
lastID, err := res.LastInsertId()
|
||||
if err != nil {
|
||||
return common.InternalError(err, w, r)
|
||||
}
|
||||
|
||||
_, err = stmts.notifyWatchers.Exec(lastID)
|
||||
if err != nil {
|
||||
return common.InternalError(err, w, r)
|
||||
}
|
||||
|
||||
// Alert the subscribers about this post without blocking this post from being posted
|
||||
if enableWebsockets {
|
||||
go notifyWatchers(lastID)
|
||||
}
|
||||
|
||||
http.Redirect(w, r, "/topic/"+strconv.Itoa(tid), http.StatusSeeOther)
|
||||
|
||||
wcount := common.WordCount(content)
|
||||
err = user.IncreasePostStats(wcount, false)
|
||||
if err != nil {
|
||||
return common.InternalError(err, w, r)
|
||||
}
|
||||
|
||||
counters.PostCounter.Bump()
|
||||
return nil
|
||||
}
|
||||
|
||||
// TODO: Refactor this
|
||||
func routeLikeTopicSubmit(w http.ResponseWriter, r *http.Request, user common.User, stid string) common.RouteError {
|
||||
tid, err := strconv.Atoi(stid)
|
||||
@ -240,22 +55,10 @@ func routeLikeTopicSubmit(w http.ResponseWriter, r *http.Request, user common.Us
|
||||
return common.InternalError(err, w, r)
|
||||
}
|
||||
|
||||
res, err := stmts.addActivity.Exec(user.ID, topic.CreatedBy, "like", "topic", tid)
|
||||
err = common.AddActivityAndNotifyTarget(user.ID, topic.CreatedBy, "like", "topic", tid)
|
||||
if err != nil {
|
||||
return common.InternalError(err, w, r)
|
||||
}
|
||||
lastID, err := res.LastInsertId()
|
||||
if err != nil {
|
||||
return common.InternalError(err, w, r)
|
||||
}
|
||||
|
||||
_, err = stmts.notifyOne.Exec(topic.CreatedBy, lastID)
|
||||
if err != nil {
|
||||
return common.InternalError(err, w, r)
|
||||
}
|
||||
|
||||
// Live alerts, if the poster is online and WebSockets is enabled
|
||||
_ = wsHub.pushAlert(topic.CreatedBy, int(lastID), "like", "topic", user.ID, topic.CreatedBy, tid)
|
||||
|
||||
http.Redirect(w, r, "/topic/"+strconv.Itoa(tid), http.StatusSeeOther)
|
||||
return nil
|
||||
@ -308,22 +111,10 @@ func routeReplyLikeSubmit(w http.ResponseWriter, r *http.Request, user common.Us
|
||||
return common.InternalError(err, w, r)
|
||||
}
|
||||
|
||||
res, err := stmts.addActivity.Exec(user.ID, reply.CreatedBy, "like", "post", rid)
|
||||
err = common.AddActivityAndNotifyTarget(user.ID, reply.CreatedBy, "like", "post", rid)
|
||||
if err != nil {
|
||||
return common.InternalError(err, w, r)
|
||||
}
|
||||
lastID, err := res.LastInsertId()
|
||||
if err != nil {
|
||||
return common.InternalError(err, w, r)
|
||||
}
|
||||
|
||||
_, err = stmts.notifyOne.Exec(reply.CreatedBy, lastID)
|
||||
if err != nil {
|
||||
return common.InternalError(err, w, r)
|
||||
}
|
||||
|
||||
// Live alerts, if the poster is online and WebSockets is enabled
|
||||
_ = wsHub.pushAlert(reply.CreatedBy, int(lastID), "like", "post", user.ID, reply.CreatedBy, rid)
|
||||
|
||||
http.Redirect(w, r, "/topic/"+strconv.Itoa(reply.ParentID), http.StatusSeeOther)
|
||||
return nil
|
||||
@ -338,13 +129,22 @@ func routeProfileReplyCreateSubmit(w http.ResponseWriter, r *http.Request, user
|
||||
if err != nil {
|
||||
return common.LocalError("Invalid UID", w, r, user)
|
||||
}
|
||||
if !common.Users.Exists(uid) {
|
||||
|
||||
profileOwner, err := common.Users.Get(uid)
|
||||
if err == ErrNoRows {
|
||||
return common.LocalError("The profile you're trying to post on doesn't exist.", w, r, user)
|
||||
} else if err != nil {
|
||||
return common.InternalError(err,w,r)
|
||||
}
|
||||
|
||||
content := common.PreparseMessage(r.PostFormValue("reply-content"))
|
||||
// TODO: Fully parse the post and store it in the parsed column
|
||||
_, err = common.Prstore.Create(uid, content, user.ID, user.LastIP)
|
||||
_, err = common.Prstore.Create(profileOwner.ID, content, user.ID, user.LastIP)
|
||||
if err != nil {
|
||||
return common.InternalError(err, w, r)
|
||||
}
|
||||
|
||||
err = common.AddActivityAndNotifyTarget(user.ID, profileOwner.ID, "reply", "user", profileOwner.ID)
|
||||
if err != nil {
|
||||
return common.InternalError(err, w, r)
|
||||
}
|
||||
|
117
panel_routes.go
117
panel_routes.go
@ -132,9 +132,9 @@ func routePanelDashboard(w http.ResponseWriter, r *http.Request, user common.Use
|
||||
common.GridElement{"dash-ram", "RAM: " + ramstr, 2, "grid_istat " + ramColour, "", "", "The global RAM usage of this server"},
|
||||
}
|
||||
|
||||
if enableWebsockets {
|
||||
uonline := wsHub.userCount()
|
||||
gonline := wsHub.guestCount()
|
||||
if common.EnableWebsockets {
|
||||
uonline := common.WsHub.UserCount()
|
||||
gonline := common.WsHub.GuestCount()
|
||||
totonline := uonline + gonline
|
||||
reqCount := 0
|
||||
|
||||
@ -743,6 +743,9 @@ func routePanelAnalyticsAgentViews(w http.ResponseWriter, r *http.Request, user
|
||||
}
|
||||
revLabelList, labelList, viewMap := panelAnalyticsTimeRangeToLabelList(timeRange)
|
||||
|
||||
// ? Only allow valid agents? The problem with this is that agents wind up getting renamed and it would take a migration to get them all up to snuff
|
||||
agent = html.EscapeString(agent)
|
||||
|
||||
common.DebugLog("in routePanelAnalyticsAgentViews")
|
||||
acc := qgen.Builder.Accumulator()
|
||||
// TODO: Verify the agent is valid
|
||||
@ -763,8 +766,6 @@ func routePanelAnalyticsAgentViews(w http.ResponseWriter, r *http.Request, user
|
||||
graph := common.PanelTimeGraph{Series: viewList, Labels: labelList}
|
||||
common.DebugLogf("graph: %+v\n", graph)
|
||||
|
||||
// ? Only allow valid agents? The problem with this is that agents wind up getting renamed and it would take a migration to get them all up to snuff
|
||||
agent = html.EscapeString(agent)
|
||||
friendlyAgent, ok := common.GetUserAgentPhrase(agent)
|
||||
if !ok {
|
||||
friendlyAgent = agent
|
||||
@ -789,10 +790,15 @@ func routePanelAnalyticsForumViews(w http.ResponseWriter, r *http.Request, user
|
||||
}
|
||||
revLabelList, labelList, viewMap := panelAnalyticsTimeRangeToLabelList(timeRange)
|
||||
|
||||
fid, err := strconv.Atoi(sfid)
|
||||
if err != nil {
|
||||
return common.LocalError("Invalid integer", w, r, user)
|
||||
}
|
||||
|
||||
common.DebugLog("in routePanelAnalyticsForumViews")
|
||||
acc := qgen.Builder.Accumulator()
|
||||
// TODO: Verify the agent is valid
|
||||
rows, err := acc.Select("viewchunks_forums").Columns("count, createdAt").Where("forum = ?").DateCutoff("createdAt", timeRange.Quantity, timeRange.Unit).Query(sfid)
|
||||
rows, err := acc.Select("viewchunks_forums").Columns("count, createdAt").Where("forum = ?").DateCutoff("createdAt", timeRange.Quantity, timeRange.Unit).Query(fid)
|
||||
if err != nil && err != ErrNoRows {
|
||||
return common.InternalError(err, w, r)
|
||||
}
|
||||
@ -809,10 +815,6 @@ func routePanelAnalyticsForumViews(w http.ResponseWriter, r *http.Request, user
|
||||
graph := common.PanelTimeGraph{Series: viewList, Labels: labelList}
|
||||
common.DebugLogf("graph: %+v\n", graph)
|
||||
|
||||
fid, err := strconv.Atoi(sfid)
|
||||
if err != nil {
|
||||
return common.InternalError(err, w, r)
|
||||
}
|
||||
forum, err := common.Forums.Get(fid)
|
||||
if err != nil {
|
||||
return common.InternalError(err, w, r)
|
||||
@ -836,10 +838,11 @@ func routePanelAnalyticsSystemViews(w http.ResponseWriter, r *http.Request, user
|
||||
return common.LocalError(err.Error(), w, r, user)
|
||||
}
|
||||
revLabelList, labelList, viewMap := panelAnalyticsTimeRangeToLabelList(timeRange)
|
||||
system = html.EscapeString(system)
|
||||
|
||||
common.DebugLog("in routePanelAnalyticsSystemViews")
|
||||
acc := qgen.Builder.Accumulator()
|
||||
// TODO: Verify the agent is valid
|
||||
// TODO: Verify the OS name is valid
|
||||
rows, err := acc.Select("viewchunks_systems").Columns("count, createdAt").Where("system = ?").DateCutoff("createdAt", timeRange.Quantity, timeRange.Unit).Query(system)
|
||||
if err != nil && err != ErrNoRows {
|
||||
return common.InternalError(err, w, r)
|
||||
@ -857,7 +860,6 @@ func routePanelAnalyticsSystemViews(w http.ResponseWriter, r *http.Request, user
|
||||
graph := common.PanelTimeGraph{Series: viewList, Labels: labelList}
|
||||
common.DebugLogf("graph: %+v\n", graph)
|
||||
|
||||
system = html.EscapeString(system)
|
||||
friendlySystem, ok := common.GetOSPhrase(system)
|
||||
if !ok {
|
||||
friendlySystem = system
|
||||
@ -867,6 +869,51 @@ func routePanelAnalyticsSystemViews(w http.ResponseWriter, r *http.Request, user
|
||||
return panelRenderTemplate("panel_analytics_system_views", w, r, user, &pi)
|
||||
}
|
||||
|
||||
func routePanelAnalyticsLanguageViews(w http.ResponseWriter, r *http.Request, user common.User, lang string) common.RouteError {
|
||||
headerVars, stats, ferr := common.PanelUserCheck(w, r, &user)
|
||||
if ferr != nil {
|
||||
return ferr
|
||||
}
|
||||
headerVars.Stylesheets = append(headerVars.Stylesheets, "chartist/chartist.min.css")
|
||||
headerVars.Scripts = append(headerVars.Scripts, "chartist/chartist.min.js")
|
||||
headerVars.Scripts = append(headerVars.Scripts, "analytics.js")
|
||||
|
||||
timeRange, err := panelAnalyticsTimeRange(r.FormValue("timeRange"))
|
||||
if err != nil {
|
||||
return common.LocalError(err.Error(), w, r, user)
|
||||
}
|
||||
revLabelList, labelList, viewMap := panelAnalyticsTimeRangeToLabelList(timeRange)
|
||||
lang = html.EscapeString(lang)
|
||||
|
||||
common.DebugLog("in routePanelAnalyticsLanguageViews")
|
||||
acc := qgen.Builder.Accumulator()
|
||||
// TODO: Verify the language code is valid
|
||||
rows, err := acc.Select("viewchunks_langs").Columns("count, createdAt").Where("lang = ?").DateCutoff("createdAt", timeRange.Quantity, timeRange.Unit).Query(lang)
|
||||
if err != nil && err != ErrNoRows {
|
||||
return common.InternalError(err, w, r)
|
||||
}
|
||||
|
||||
viewMap, err = panelAnalyticsRowsToViewMap(rows, labelList, viewMap)
|
||||
if err != nil {
|
||||
return common.InternalError(err, w, r)
|
||||
}
|
||||
|
||||
var viewList []int64
|
||||
for _, value := range revLabelList {
|
||||
viewList = append(viewList, viewMap[value])
|
||||
}
|
||||
graph := common.PanelTimeGraph{Series: viewList, Labels: labelList}
|
||||
common.DebugLogf("graph: %+v\n", graph)
|
||||
|
||||
friendlyLang, ok := common.GetHumanLangPhrase(lang)
|
||||
if !ok {
|
||||
friendlyLang = lang
|
||||
}
|
||||
|
||||
pi := common.PanelAnalyticsAgentPage{common.GetTitlePhrase("panel_analytics"), user, headerVars, stats, "analytics", lang, friendlyLang, graph, timeRange.Range}
|
||||
return panelRenderTemplate("panel_analytics_lang_views", w, r, user, &pi)
|
||||
}
|
||||
|
||||
func routePanelAnalyticsReferrerViews(w http.ResponseWriter, r *http.Request, user common.User, domain string) common.RouteError {
|
||||
headerVars, stats, ferr := common.PanelUserCheck(w, r, &user)
|
||||
if ferr != nil {
|
||||
@ -1163,6 +1210,46 @@ func routePanelAnalyticsSystems(w http.ResponseWriter, r *http.Request, user com
|
||||
return panelRenderTemplate("panel_analytics_systems", w, r, user, &pi)
|
||||
}
|
||||
|
||||
func routePanelAnalyticsLanguages(w http.ResponseWriter, r *http.Request, user common.User) common.RouteError {
|
||||
headerVars, stats, ferr := common.PanelUserCheck(w, r, &user)
|
||||
if ferr != nil {
|
||||
return ferr
|
||||
}
|
||||
timeRange, err := panelAnalyticsTimeRange(r.FormValue("timeRange"))
|
||||
if err != nil {
|
||||
return common.LocalError(err.Error(), w, r, user)
|
||||
}
|
||||
|
||||
acc := qgen.Builder.Accumulator()
|
||||
rows, err := acc.Select("viewchunks_langs").Columns("count, lang").DateCutoff("createdAt", timeRange.Quantity, timeRange.Unit).Query()
|
||||
if err != nil && err != ErrNoRows {
|
||||
return common.InternalError(err, w, r)
|
||||
}
|
||||
|
||||
langMap, err := panelAnalyticsRowsToNameMap(rows)
|
||||
if err != nil {
|
||||
return common.InternalError(err, w, r)
|
||||
}
|
||||
|
||||
// TODO: Can we de-duplicate these analytics functions further?
|
||||
// TODO: Sort this slice
|
||||
var langItems []common.PanelAnalyticsAgentsItem
|
||||
for lang, count := range langMap {
|
||||
lLang, ok := common.GetHumanLangPhrase(lang)
|
||||
if !ok {
|
||||
lLang = lang
|
||||
}
|
||||
langItems = append(langItems, common.PanelAnalyticsAgentsItem{
|
||||
Agent: lang,
|
||||
FriendlyAgent: lLang,
|
||||
Count: count,
|
||||
})
|
||||
}
|
||||
|
||||
pi := common.PanelAnalyticsAgentsPage{common.GetTitlePhrase("panel_analytics"), user, headerVars, stats, "analytics", langItems, timeRange.Range}
|
||||
return panelRenderTemplate("panel_analytics_langs", w, r, user, &pi)
|
||||
}
|
||||
|
||||
func routePanelAnalyticsReferrers(w http.ResponseWriter, r *http.Request, user common.User) common.RouteError {
|
||||
headerVars, stats, ferr := common.PanelUserCheck(w, r, &user)
|
||||
if ferr != nil {
|
||||
@ -1688,6 +1775,10 @@ func routePanelUsersEdit(w http.ResponseWriter, r *http.Request, user common.Use
|
||||
groupList = append(groupList, group)
|
||||
}
|
||||
|
||||
if r.FormValue("updated") == "1" {
|
||||
headerVars.NoticeList = append(headerVars.NoticeList, "The user was successfully updated")
|
||||
}
|
||||
|
||||
pi := common.PanelPage{common.GetTitlePhrase("panel_edit_user"), user, headerVars, stats, "users", groupList, targetUser}
|
||||
if common.RunPreRenderHook("pre_render_panel_edit_user", w, r, &user, &pi) {
|
||||
return nil
|
||||
@ -1776,7 +1867,7 @@ func routePanelUsersEditSubmit(w http.ResponseWriter, r *http.Request, user comm
|
||||
}
|
||||
|
||||
targetUser.CacheRemove()
|
||||
http.Redirect(w, r, "/panel/users/edit/"+strconv.Itoa(targetUser.ID), http.StatusSeeOther)
|
||||
http.Redirect(w, r, "/panel/users/edit/"+strconv.Itoa(targetUser.ID)+"?updated=1", http.StatusSeeOther)
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -241,8 +241,6 @@ func writeSelects(adapter qgen.Adapter) error {
|
||||
|
||||
build.Select("getTopicBasic").Table("topics").Columns("title, content").Where("tid = ?").Parse()
|
||||
|
||||
build.Select("getActivityEntry").Table("activity_stream").Columns("actor, targetUser, event, elementType, elementID").Where("asid = ?").Parse()
|
||||
|
||||
build.Select("forumEntryExists").Table("forums").Columns("fid").Where("name = ''").Orderby("fid ASC").Limit("0,1").Parse()
|
||||
|
||||
build.Select("groupEntryExists").Table("users_groups").Columns("gid").Where("name = ''").Orderby("gid ASC").Limit("0,1").Parse()
|
||||
@ -259,11 +257,6 @@ func writeLeftJoins(adapter qgen.Adapter) error {
|
||||
}
|
||||
|
||||
func writeInnerJoins(adapter qgen.Adapter) (err error) {
|
||||
_, err = adapter.SimpleInnerJoin("getWatchers", "activity_stream", "activity_subscriptions", "activity_subscriptions.user", "activity_subscriptions.targetType = activity_stream.elementType AND activity_subscriptions.targetID = activity_stream.elementID AND activity_subscriptions.user != activity_stream.actor", "asid = ?", "", "")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -272,10 +265,6 @@ func writeInserts(adapter qgen.Adapter) error {
|
||||
|
||||
build.Insert("createReport").Table("topics").Columns("title, content, parsed_content, createdAt, lastReplyAt, createdBy, lastReplyBy, data, parentID, css_class").Fields("?,?,?,UTC_TIMESTAMP(),UTC_TIMESTAMP(),?,?,?,1,'report'").Parse()
|
||||
|
||||
build.Insert("addActivity").Table("activity_stream").Columns("actor, targetUser, event, elementType, elementID").Fields("?,?,?,?,?").Parse()
|
||||
|
||||
build.Insert("notifyOne").Table("activity_stream_matches").Columns("watcher, asid").Fields("?,?").Parse()
|
||||
|
||||
build.Insert("addForumPermsToForum").Table("forums_permissions").Columns("gid,fid,preset,permissions").Fields("?,?,?,?").Parse()
|
||||
|
||||
build.Insert("addPlugin").Table("plugins").Columns("uname, active, installed").Fields("?,?,?").Parse()
|
||||
@ -361,11 +350,6 @@ func writeInsertLeftJoins(adapter qgen.Adapter) error {
|
||||
}
|
||||
|
||||
func writeInsertInnerJoins(adapter qgen.Adapter) error {
|
||||
adapter.SimpleInsertInnerJoin("notifyWatchers",
|
||||
qgen.DBInsert{"activity_stream_matches", "watcher, asid", ""},
|
||||
qgen.DBJoin{"activity_stream", "activity_subscriptions", "activity_subscriptions.user, activity_stream.asid", "activity_subscriptions.targetType = activity_stream.elementType AND activity_subscriptions.targetID = activity_stream.elementID AND activity_subscriptions.user != activity_stream.actor", "asid = ?", "", ""},
|
||||
)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -424,6 +424,15 @@ func createTables(adapter qgen.Adapter) error {
|
||||
[]qgen.DBTableKey{},
|
||||
)
|
||||
|
||||
qgen.Install.CreateTable("viewchunks_langs", "", "",
|
||||
[]qgen.DBTableColumn{
|
||||
qgen.DBTableColumn{"count", "int", 0, false, false, "0"},
|
||||
qgen.DBTableColumn{"createdAt", "datetime", 0, false, false, ""},
|
||||
qgen.DBTableColumn{"lang", "varchar", 200, false, false, ""}, // en, ru, etc.
|
||||
},
|
||||
[]qgen.DBTableKey{},
|
||||
)
|
||||
|
||||
qgen.Install.CreateTable("viewchunks_referrers", "", "",
|
||||
[]qgen.DBTableColumn{
|
||||
qgen.DBTableColumn{"count", "int", 0, false, false, "0"},
|
||||
|
@ -200,6 +200,7 @@ func main() {
|
||||
"twitter",
|
||||
"cloudflare",
|
||||
"uptimebot",
|
||||
"slackbot",
|
||||
"discourse",
|
||||
"lynx",
|
||||
"blank",
|
||||
@ -220,6 +221,7 @@ package main
|
||||
import (
|
||||
"log"
|
||||
"strings"
|
||||
"strconv"
|
||||
"sync"
|
||||
"errors"
|
||||
"net/http"
|
||||
@ -275,6 +277,7 @@ var markToAgent = map[string]string{
|
||||
"SeznamBot":"seznambot",
|
||||
"CloudFlare":"cloudflare", // Track alwayson specifically in case there are other bots?
|
||||
"Uptimebot":"uptimebot",
|
||||
"Slackbot":"slackbot",
|
||||
"Discordbot":"discord",
|
||||
"Twitterbot":"twitter",
|
||||
"Discourse":"discourse",
|
||||
@ -348,7 +351,7 @@ func (router *GenRouter) StripNewlines(data string) string {
|
||||
return strings.Replace(strings.Replace(data,"\n","",-1),"\r","",-1)
|
||||
}
|
||||
|
||||
func (router *GenRouter) DumpRequest(req *http.Request) {
|
||||
func (router *GenRouter) DumpRequest(req *http.Request, prepend string) {
|
||||
var heads string
|
||||
for key, value := range req.Header {
|
||||
for _, vvalue := range value {
|
||||
@ -356,7 +359,8 @@ func (router *GenRouter) DumpRequest(req *http.Request) {
|
||||
}
|
||||
}
|
||||
|
||||
log.Print("\nUA: " + router.StripNewlines(req.UserAgent()) + "\n" +
|
||||
log.Print(prepend +
|
||||
"\nUA: " + router.StripNewlines(req.UserAgent()) + "\n" +
|
||||
"Method: " + router.StripNewlines(req.Method) + "\n" + heads +
|
||||
"req.Host: " + router.StripNewlines(req.Host) + "\n" +
|
||||
"req.URL.Path: " + router.StripNewlines(req.URL.Path) + "\n" +
|
||||
@ -366,8 +370,7 @@ func (router *GenRouter) DumpRequest(req *http.Request) {
|
||||
}
|
||||
|
||||
func (router *GenRouter) SuspiciousRequest(req *http.Request) {
|
||||
log.Print("Suspicious Request")
|
||||
router.DumpRequest(req)
|
||||
router.DumpRequest(req,"Suspicious Request")
|
||||
counters.AgentViewCounter.Bump({{.AllAgentMap.suspicious}})
|
||||
}
|
||||
|
||||
@ -394,24 +397,23 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
||||
if len(req.URL.Path) == 0 || req.URL.Path[0] != '/' || req.Host != common.Site.Host {
|
||||
w.WriteHeader(200) // 400
|
||||
w.Write([]byte(""))
|
||||
log.Print("Malformed Request")
|
||||
router.DumpRequest(req)
|
||||
router.DumpRequest(req,"Malformed Request")
|
||||
counters.AgentViewCounter.Bump({{.AllAgentMap.malformed}})
|
||||
return
|
||||
}
|
||||
|
||||
// TODO: Cover more suspicious strings and at a lower layer than this
|
||||
for _, char := range req.URL.Path {
|
||||
if char != '&' && !(char > 44 && char < 58) && char != '=' && char != '?' && !(char > 64 && char < 91) && char != '\\' && char != '_' && !(char > 96 && char < 123) {
|
||||
router.SuspiciousRequest(req)
|
||||
break
|
||||
}
|
||||
}
|
||||
lowerPath := strings.ToLower(req.URL.Path)
|
||||
// TODO: Flag any requests which has a dot with anything but a number after that
|
||||
if strings.Contains(req.URL.Path,"..") || strings.Contains(req.URL.Path,"--") || strings.Contains(lowerPath,".php") || strings.Contains(lowerPath,".asp") || strings.Contains(lowerPath,".cgi") || strings.Contains(lowerPath,".py") || strings.Contains(lowerPath,".sql") {
|
||||
for _, char := range req.URL.Path {
|
||||
if char != '&' && !(char > 44 && char < 58) && char != '=' && char != '?' && !(char > 64 && char < 91) && char != '\\' && char != '_' && !(char > 96 && char < 123) {
|
||||
router.SuspiciousRequest(req)
|
||||
break
|
||||
}
|
||||
}
|
||||
lowerPath := strings.ToLower(req.URL.Path)
|
||||
// TODO: Flag any requests which has a dot with anything but a number after that
|
||||
if strings.Contains(req.URL.Path,"..") || strings.Contains(req.URL.Path,"--") || strings.Contains(lowerPath,".php") || strings.Contains(lowerPath,".asp") || strings.Contains(lowerPath,".cgi") || strings.Contains(lowerPath,".py") || strings.Contains(lowerPath,".sql") || strings.Contains(lowerPath,".action") {
|
||||
router.SuspiciousRequest(req)
|
||||
}
|
||||
|
||||
var prefix, extraData string
|
||||
prefix = req.URL.Path[0:strings.IndexByte(req.URL.Path[1:],'/') + 1]
|
||||
@ -421,8 +423,7 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
||||
}
|
||||
|
||||
if common.Dev.SuperDebug {
|
||||
log.Print("before routes.StaticFile")
|
||||
router.DumpRequest(req)
|
||||
router.DumpRequest(req,"before routes.StaticFile")
|
||||
}
|
||||
// Increment the request counter
|
||||
counters.GlobalViewCounter.Bump()
|
||||
@ -444,8 +445,11 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
||||
if ua == "" {
|
||||
counters.AgentViewCounter.Bump({{.AllAgentMap.blank}})
|
||||
if common.Dev.DebugMode {
|
||||
log.Print("Blank UA: ", req.UserAgent())
|
||||
router.DumpRequest(req)
|
||||
var prepend string
|
||||
for _, char := range req.UserAgent() {
|
||||
prepend += strconv.Itoa(int(char)) + " "
|
||||
}
|
||||
router.DumpRequest(req,"Blank UA: " + prepend)
|
||||
}
|
||||
} else {
|
||||
var runeEquals = func(a []rune, b []rune) bool {
|
||||
@ -544,8 +548,11 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
||||
if agent == "" {
|
||||
counters.AgentViewCounter.Bump({{.AllAgentMap.unknown}})
|
||||
if common.Dev.DebugMode {
|
||||
log.Print("Unknown UA: ", req.UserAgent())
|
||||
router.DumpRequest(req)
|
||||
var prepend string
|
||||
for _, char := range req.UserAgent() {
|
||||
prepend += strconv.Itoa(int(char)) + " "
|
||||
}
|
||||
router.DumpRequest(req,"Blank UA: " + prepend)
|
||||
}
|
||||
} else {
|
||||
counters.AgentViewCounter.Bump(agentMapEnum[agent])
|
||||
@ -553,6 +560,15 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
||||
counters.OSViewCounter.Bump(osMapEnum[os])
|
||||
}
|
||||
|
||||
// TODO: Do we want to track missing language headers too? Maybe as it's own type, e.g. "noheader"?
|
||||
lang := req.Header.Get("Accept-Language")
|
||||
if lang != "" {
|
||||
lang = strings.TrimSpace(lang)
|
||||
lLang := strings.Split(lang,"-")
|
||||
common.DebugDetail("lLang:", lLang)
|
||||
counters.LangViewCounter.Bump(lLang[0])
|
||||
}
|
||||
|
||||
referrer := req.Header.Get("Referer") // Check the 'referrer' header too? :P
|
||||
if referrer != "" {
|
||||
// ? Optimise this a little?
|
||||
|
@ -32,7 +32,7 @@ func routes() {
|
||||
buildPollRoutes()
|
||||
buildAccountRoutes()
|
||||
|
||||
addRoute(Special("routeWebsockets", "/ws/"))
|
||||
addRoute(Special("common.RouteWebsockets", "/ws/"))
|
||||
}
|
||||
|
||||
// TODO: Test the email token route
|
||||
@ -85,7 +85,7 @@ func buildReplyRoutes() {
|
||||
replyGroup := newRouteGroup("/reply/")
|
||||
replyGroup.Routes(
|
||||
// TODO: Reduce this to 1MB for attachments for each file?
|
||||
UploadAction("routeCreateReplySubmit", "/reply/create/").MaxSizeVar("common.Config.MaxRequestSize"), // TODO: Rename the route so it's /reply/create/submit/
|
||||
UploadAction("routes.CreateReplySubmit", "/reply/create/").MaxSizeVar("common.Config.MaxRequestSize"), // TODO: Rename the route so it's /reply/create/submit/
|
||||
Action("routes.ReplyEditSubmit", "/reply/edit/submit/", "extraData"),
|
||||
Action("routes.ReplyDeleteSubmit", "/reply/delete/submit/", "extraData"),
|
||||
Action("routeReplyLikeSubmit", "/reply/like/submit/", "extraData"),
|
||||
@ -167,11 +167,13 @@ func buildPanelRoutes() {
|
||||
View("routePanelAnalyticsRoutes", "/panel/analytics/routes/").Before("ParseForm"),
|
||||
View("routePanelAnalyticsAgents", "/panel/analytics/agents/").Before("ParseForm"),
|
||||
View("routePanelAnalyticsSystems", "/panel/analytics/systems/").Before("ParseForm"),
|
||||
View("routePanelAnalyticsLanguages", "/panel/analytics/langs/").Before("ParseForm"),
|
||||
View("routePanelAnalyticsReferrers", "/panel/analytics/referrers/").Before("ParseForm"),
|
||||
View("routePanelAnalyticsRouteViews", "/panel/analytics/route/", "extraData"),
|
||||
View("routePanelAnalyticsAgentViews", "/panel/analytics/agent/", "extraData"),
|
||||
View("routePanelAnalyticsForumViews", "/panel/analytics/forum/", "extraData"),
|
||||
View("routePanelAnalyticsSystemViews", "/panel/analytics/system/", "extraData"),
|
||||
View("routePanelAnalyticsLanguageViews", "/panel/analytics/lang/", "extraData"),
|
||||
View("routePanelAnalyticsReferrerViews", "/panel/analytics/referrer/", "extraData"),
|
||||
View("routePanelAnalyticsPosts", "/panel/analytics/posts/").Before("ParseForm"),
|
||||
View("routePanelAnalyticsTopics", "/panel/analytics/topics/").Before("ParseForm"),
|
||||
|
@ -175,7 +175,7 @@ func routeAPI(w http.ResponseWriter, r *http.Request, user common.User) common.R
|
||||
if err != nil {
|
||||
return common.InternalErrorJS(err, w, r)
|
||||
}
|
||||
res, err := buildAlert(asid, event, elementType, actorID, targetUserID, elementID, user)
|
||||
res, err := common.BuildAlert(asid, event, elementType, actorID, targetUserID, elementID, user)
|
||||
if err != nil {
|
||||
return common.LocalErrorJS(err.Error(), w, r)
|
||||
}
|
||||
|
@ -25,7 +25,7 @@ func AccountLogin(w http.ResponseWriter, r *http.Request, user common.User) comm
|
||||
if common.RunPreRenderHook("pre_render_login", w, r, &user, &pi) {
|
||||
return nil
|
||||
}
|
||||
err := common.Templates.ExecuteTemplate(w, "login.html", pi)
|
||||
err := common.RunThemeTemplate(headerVars.Theme.Name, "login", pi, w)
|
||||
if err != nil {
|
||||
return common.InternalError(err, w, r)
|
||||
}
|
||||
@ -84,7 +84,7 @@ func AccountRegister(w http.ResponseWriter, r *http.Request, user common.User) c
|
||||
if common.RunPreRenderHook("pre_render_register", w, r, &user, &pi) {
|
||||
return nil
|
||||
}
|
||||
err := common.Templates.ExecuteTemplate(w, "register.html", pi)
|
||||
err := common.RunThemeTemplate(headerVars.Theme.Name, "register", pi, w)
|
||||
if err != nil {
|
||||
return common.InternalError(err, w, r)
|
||||
}
|
||||
|
@ -30,11 +30,11 @@ func IPSearch(w http.ResponseWriter, r *http.Request, user common.User) common.R
|
||||
return common.InternalError(err, w, r)
|
||||
}
|
||||
|
||||
pi := common.IPSearchPage{common.GetTitlePhrase("ip-search"), user, headerVars, userList, ip}
|
||||
pi := common.IPSearchPage{common.GetTitlePhrase("ip_search"), user, headerVars, userList, ip}
|
||||
if common.RunPreRenderHook("pre_render_ip_search", w, r, &user, &pi) {
|
||||
return nil
|
||||
}
|
||||
err = common.Templates.ExecuteTemplate(w, "ip-search.html", pi)
|
||||
return nil
|
||||
}
|
||||
err = common.RunThemeTemplate(headerVars.Theme.Name, "ip_search", pi, w)
|
||||
if err != nil {
|
||||
return common.InternalError(err, w, r)
|
||||
}
|
||||
|
175
routes/reply.go
175
routes/reply.go
@ -1,13 +1,188 @@
|
||||
package routes
|
||||
|
||||
import (
|
||||
"crypto/sha256"
|
||||
"database/sql"
|
||||
"encoding/hex"
|
||||
"html"
|
||||
"io"
|
||||
"log"
|
||||
"net/http"
|
||||
"os"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"../common"
|
||||
"../common/counters"
|
||||
)
|
||||
|
||||
// TODO: De-duplicate the upload logic
|
||||
func CreateReplySubmit(w http.ResponseWriter, r *http.Request, user common.User) common.RouteError {
|
||||
tid, err := strconv.Atoi(r.PostFormValue("tid"))
|
||||
if err != nil {
|
||||
return common.PreError("Failed to convert the Topic ID", w, r)
|
||||
}
|
||||
|
||||
topic, err := common.Topics.Get(tid)
|
||||
if err == sql.ErrNoRows {
|
||||
return common.PreError("Couldn't find the parent topic", w, r)
|
||||
} else if err != nil {
|
||||
return common.InternalError(err, w, r)
|
||||
}
|
||||
|
||||
// TODO: Add hooks to make use of headerLite
|
||||
_, ferr := common.SimpleForumUserCheck(w, r, &user, topic.ParentID)
|
||||
if ferr != nil {
|
||||
return ferr
|
||||
}
|
||||
if !user.Perms.ViewTopic || !user.Perms.CreateReply {
|
||||
return common.NoPermissions(w, r, user)
|
||||
}
|
||||
|
||||
// Handle the file attachments
|
||||
// TODO: Stop duplicating this code
|
||||
if user.Perms.UploadFiles {
|
||||
files, ok := r.MultipartForm.File["upload_files"]
|
||||
if ok {
|
||||
if len(files) > 5 {
|
||||
return common.LocalError("You can't attach more than five files", w, r, user)
|
||||
}
|
||||
|
||||
for _, file := range files {
|
||||
log.Print("file.Filename ", file.Filename)
|
||||
extarr := strings.Split(file.Filename, ".")
|
||||
if len(extarr) < 2 {
|
||||
return common.LocalError("Bad file", w, r, user)
|
||||
}
|
||||
ext := extarr[len(extarr)-1]
|
||||
|
||||
// TODO: Can we do this without a regex?
|
||||
reg, err := regexp.Compile("[^A-Za-z0-9]+")
|
||||
if err != nil {
|
||||
return common.LocalError("Bad file extension", w, r, user)
|
||||
}
|
||||
ext = strings.ToLower(reg.ReplaceAllString(ext, ""))
|
||||
if !common.AllowedFileExts.Contains(ext) {
|
||||
return common.LocalError("You're not allowed to upload files with this extension", w, r, user)
|
||||
}
|
||||
|
||||
infile, err := file.Open()
|
||||
if err != nil {
|
||||
return common.LocalError("Upload failed", w, r, user)
|
||||
}
|
||||
defer infile.Close()
|
||||
|
||||
hasher := sha256.New()
|
||||
_, err = io.Copy(hasher, infile)
|
||||
if err != nil {
|
||||
return common.LocalError("Upload failed [Hashing Failed]", w, r, user)
|
||||
}
|
||||
infile.Close()
|
||||
|
||||
checksum := hex.EncodeToString(hasher.Sum(nil))
|
||||
filename := checksum + "." + ext
|
||||
outfile, err := os.Create("." + "/attachs/" + filename)
|
||||
if err != nil {
|
||||
return common.LocalError("Upload failed [File Creation Failed]", w, r, user)
|
||||
}
|
||||
defer outfile.Close()
|
||||
|
||||
infile, err = file.Open()
|
||||
if err != nil {
|
||||
return common.LocalError("Upload failed", w, r, user)
|
||||
}
|
||||
defer infile.Close()
|
||||
|
||||
_, err = io.Copy(outfile, infile)
|
||||
if err != nil {
|
||||
return common.LocalError("Upload failed [Copy Failed]", w, r, user)
|
||||
}
|
||||
|
||||
err = common.Attachments.Add(topic.ParentID, "forums", tid, "replies", user.ID, filename)
|
||||
if err != nil {
|
||||
return common.InternalError(err, w, r)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
content := common.PreparseMessage(r.PostFormValue("reply-content"))
|
||||
// TODO: Fully parse the post and put that in the parsed column
|
||||
rid, err := common.Rstore.Create(topic, content, user.LastIP, user.ID)
|
||||
if err != nil {
|
||||
return common.InternalError(err, w, r)
|
||||
}
|
||||
|
||||
reply, err := common.Rstore.Get(rid)
|
||||
if err != nil {
|
||||
return common.LocalError("Unable to load the reply", w, r, user)
|
||||
}
|
||||
if r.PostFormValue("has_poll") == "1" {
|
||||
var maxPollOptions = 10
|
||||
var pollInputItems = make(map[int]string)
|
||||
for key, values := range r.Form {
|
||||
common.DebugDetail("key: ", key)
|
||||
common.DebugDetailf("values: %+v\n", values)
|
||||
for _, value := range values {
|
||||
if strings.HasPrefix(key, "pollinputitem[") {
|
||||
halves := strings.Split(key, "[")
|
||||
if len(halves) != 2 {
|
||||
return common.LocalError("Malformed pollinputitem", w, r, user)
|
||||
}
|
||||
halves[1] = strings.TrimSuffix(halves[1], "]")
|
||||
|
||||
index, err := strconv.Atoi(halves[1])
|
||||
if err != nil {
|
||||
return common.LocalError("Malformed pollinputitem", w, r, user)
|
||||
}
|
||||
|
||||
// If there are duplicates, then something has gone horribly wrong, so let's ignore them, this'll likely happen during an attack
|
||||
_, exists := pollInputItems[index]
|
||||
if !exists && len(html.EscapeString(value)) != 0 {
|
||||
pollInputItems[index] = html.EscapeString(value)
|
||||
if len(pollInputItems) >= maxPollOptions {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Make sure the indices are sequential to avoid out of bounds issues
|
||||
var seqPollInputItems = make(map[int]string)
|
||||
for i := 0; i < len(pollInputItems); i++ {
|
||||
seqPollInputItems[i] = pollInputItems[i]
|
||||
}
|
||||
|
||||
pollType := 0 // Basic single choice
|
||||
_, err := common.Polls.Create(reply, pollType, seqPollInputItems)
|
||||
if err != nil {
|
||||
return common.LocalError("Failed to add poll to reply", w, r, user) // TODO: Might need to be an internal error as it could leave phantom polls?
|
||||
}
|
||||
}
|
||||
|
||||
err = common.Forums.UpdateLastTopic(tid, user.ID, topic.ParentID)
|
||||
if err != nil && err != sql.ErrNoRows {
|
||||
return common.InternalError(err, w, r)
|
||||
}
|
||||
|
||||
common.AddActivityAndNotifyAll(user.ID, topic.CreatedBy, "reply", "topic", tid)
|
||||
if err != nil {
|
||||
return common.InternalError(err, w, r)
|
||||
}
|
||||
http.Redirect(w, r, "/topic/"+strconv.Itoa(tid), http.StatusSeeOther)
|
||||
|
||||
wcount := common.WordCount(content)
|
||||
err = user.IncreasePostStats(wcount, false)
|
||||
if err != nil {
|
||||
return common.InternalError(err, w, r)
|
||||
}
|
||||
|
||||
counters.PostCounter.Bump()
|
||||
return nil
|
||||
}
|
||||
|
||||
// TODO: Disable stat updates in posts handled by plugin_guilds
|
||||
// TODO: Update the stats after edits so that we don't under or over decrement stats during deletes
|
||||
func ReplyEditSubmit(w http.ResponseWriter, r *http.Request, user common.User, srid string) common.RouteError {
|
||||
|
5
schema/mssql/query_viewchunks_langs.sql
Normal file
5
schema/mssql/query_viewchunks_langs.sql
Normal file
@ -0,0 +1,5 @@
|
||||
CREATE TABLE [viewchunks_langs] (
|
||||
[count] int DEFAULT 0 not null,
|
||||
[createdAt] datetime not null,
|
||||
[lang] nvarchar (200) not null
|
||||
);
|
5
schema/mysql/query_viewchunks_langs.sql
Normal file
5
schema/mysql/query_viewchunks_langs.sql
Normal file
@ -0,0 +1,5 @@
|
||||
CREATE TABLE `viewchunks_langs` (
|
||||
`count` int DEFAULT 0 not null,
|
||||
`createdAt` datetime not null,
|
||||
`lang` varchar(200) not null
|
||||
);
|
5
schema/pgsql/query_viewchunks_langs.sql
Normal file
5
schema/pgsql/query_viewchunks_langs.sql
Normal file
@ -0,0 +1,5 @@
|
||||
CREATE TABLE `viewchunks_langs` (
|
||||
`count` int DEFAULT 0 not null,
|
||||
`createdAt` timestamp not null,
|
||||
`lang` varchar (200) not null
|
||||
);
|
115
template_error.go
Normal file
115
template_error.go
Normal file
@ -0,0 +1,115 @@
|
||||
// +build !no_templategen
|
||||
|
||||
// Code generated by Gosora. More below:
|
||||
/* This file was automatically generated by the software. Please don't edit it as your changes may be overwritten at any moment. */
|
||||
package main
|
||||
import "net/http"
|
||||
import "./common"
|
||||
|
||||
var error_Tmpl_Phrase_ID int
|
||||
|
||||
// nolint
|
||||
func init() {
|
||||
common.Template_error_handle = Template_error
|
||||
common.Ctemplates = append(common.Ctemplates,"error")
|
||||
common.TmplPtrMap["error"] = &common.Template_error_handle
|
||||
common.TmplPtrMap["o_error"] = Template_error
|
||||
error_Tmpl_Phrase_ID = common.RegisterTmplPhraseNames([]string{
|
||||
"error_head",
|
||||
})
|
||||
}
|
||||
|
||||
// nolint
|
||||
func Template_error(tmpl_error_vars common.Page, w http.ResponseWriter) error {
|
||||
var phrases = common.GetTmplPhrasesBytes(error_Tmpl_Phrase_ID)
|
||||
w.Write(header_0)
|
||||
w.Write([]byte(tmpl_error_vars.Title))
|
||||
w.Write(header_1)
|
||||
w.Write([]byte(tmpl_error_vars.Header.Site.Name))
|
||||
w.Write(header_2)
|
||||
w.Write([]byte(tmpl_error_vars.Header.Theme.Name))
|
||||
w.Write(header_3)
|
||||
if len(tmpl_error_vars.Header.Stylesheets) != 0 {
|
||||
for _, item := range tmpl_error_vars.Header.Stylesheets {
|
||||
w.Write(header_4)
|
||||
w.Write([]byte(item))
|
||||
w.Write(header_5)
|
||||
}
|
||||
}
|
||||
w.Write(header_6)
|
||||
if len(tmpl_error_vars.Header.Scripts) != 0 {
|
||||
for _, item := range tmpl_error_vars.Header.Scripts {
|
||||
w.Write(header_7)
|
||||
w.Write([]byte(item))
|
||||
w.Write(header_8)
|
||||
}
|
||||
}
|
||||
w.Write(header_9)
|
||||
w.Write([]byte(tmpl_error_vars.CurrentUser.Session))
|
||||
w.Write(header_10)
|
||||
w.Write([]byte(tmpl_error_vars.Header.Site.URL))
|
||||
w.Write(header_11)
|
||||
if tmpl_error_vars.Header.MetaDesc != "" {
|
||||
w.Write(header_12)
|
||||
w.Write([]byte(tmpl_error_vars.Header.MetaDesc))
|
||||
w.Write(header_13)
|
||||
}
|
||||
w.Write(header_14)
|
||||
if !tmpl_error_vars.CurrentUser.IsSuperMod {
|
||||
w.Write(header_15)
|
||||
}
|
||||
w.Write(header_16)
|
||||
w.Write(menu_0)
|
||||
w.Write(menu_1)
|
||||
w.Write([]byte(tmpl_error_vars.Header.Site.ShortName))
|
||||
w.Write(menu_2)
|
||||
if tmpl_error_vars.CurrentUser.Loggedin {
|
||||
w.Write(menu_3)
|
||||
w.Write([]byte(tmpl_error_vars.CurrentUser.Link))
|
||||
w.Write(menu_4)
|
||||
w.Write([]byte(tmpl_error_vars.CurrentUser.Session))
|
||||
w.Write(menu_5)
|
||||
} else {
|
||||
w.Write(menu_6)
|
||||
}
|
||||
w.Write(menu_7)
|
||||
w.Write(header_17)
|
||||
if tmpl_error_vars.Header.Widgets.RightSidebar != "" {
|
||||
w.Write(header_18)
|
||||
}
|
||||
w.Write(header_19)
|
||||
if len(tmpl_error_vars.Header.NoticeList) != 0 {
|
||||
for _, item := range tmpl_error_vars.Header.NoticeList {
|
||||
w.Write(header_20)
|
||||
w.Write([]byte(item))
|
||||
w.Write(header_21)
|
||||
}
|
||||
}
|
||||
w.Write(error_0)
|
||||
w.Write(phrases[0])
|
||||
w.Write(error_1)
|
||||
w.Write([]byte(tmpl_error_vars.Something.(string)))
|
||||
w.Write(error_2)
|
||||
w.Write(footer_0)
|
||||
w.Write([]byte(common.BuildWidget("footer",tmpl_error_vars.Header)))
|
||||
w.Write(footer_1)
|
||||
if len(tmpl_error_vars.Header.Themes) != 0 {
|
||||
for _, item := range tmpl_error_vars.Header.Themes {
|
||||
if !item.HideFromThemes {
|
||||
w.Write(footer_2)
|
||||
w.Write([]byte(item.Name))
|
||||
w.Write(footer_3)
|
||||
if tmpl_error_vars.Header.Theme.Name == item.Name {
|
||||
w.Write(footer_4)
|
||||
}
|
||||
w.Write(footer_5)
|
||||
w.Write([]byte(item.FriendlyName))
|
||||
w.Write(footer_6)
|
||||
}
|
||||
}
|
||||
}
|
||||
w.Write(footer_7)
|
||||
w.Write([]byte(common.BuildWidget("rightSidebar",tmpl_error_vars.Header)))
|
||||
w.Write(footer_8)
|
||||
return nil
|
||||
}
|
@ -3,9 +3,11 @@
|
||||
// Code generated by Gosora. More below:
|
||||
/* This file was automatically generated by the software. Please don't edit it as your changes may be overwritten at any moment. */
|
||||
package main
|
||||
import "strconv"
|
||||
import "net/http"
|
||||
import "./common"
|
||||
import "strconv"
|
||||
|
||||
var forum_Tmpl_Phrase_ID int
|
||||
|
||||
// nolint
|
||||
func init() {
|
||||
@ -102,133 +104,134 @@ w.Write(forum_8)
|
||||
w.Write([]byte(tmpl_forum_vars.Title))
|
||||
w.Write(forum_9)
|
||||
if tmpl_forum_vars.CurrentUser.ID != 0 {
|
||||
if tmpl_forum_vars.CurrentUser.Perms.CreateTopic {
|
||||
w.Write(forum_10)
|
||||
w.Write([]byte(strconv.Itoa(tmpl_forum_vars.Forum.ID)))
|
||||
if tmpl_forum_vars.CurrentUser.Perms.CreateTopic {
|
||||
w.Write(forum_11)
|
||||
w.Write([]byte(strconv.Itoa(tmpl_forum_vars.Forum.ID)))
|
||||
w.Write(forum_12)
|
||||
} else {
|
||||
w.Write(forum_13)
|
||||
}
|
||||
} else {
|
||||
w.Write(forum_14)
|
||||
}
|
||||
w.Write(forum_15)
|
||||
if tmpl_forum_vars.CurrentUser.ID != 0 {
|
||||
w.Write(forum_16)
|
||||
if tmpl_forum_vars.CurrentUser.Perms.CreateTopic {
|
||||
w.Write(forum_17)
|
||||
w.Write([]byte(tmpl_forum_vars.CurrentUser.Avatar))
|
||||
w.Write(forum_18)
|
||||
w.Write([]byte(strconv.Itoa(tmpl_forum_vars.Forum.ID)))
|
||||
w.Write(forum_19)
|
||||
if tmpl_forum_vars.CurrentUser.Perms.UploadFiles {
|
||||
w.Write(forum_20)
|
||||
}
|
||||
w.Write(forum_16)
|
||||
if tmpl_forum_vars.CurrentUser.ID != 0 {
|
||||
w.Write(forum_17)
|
||||
if tmpl_forum_vars.CurrentUser.Perms.CreateTopic {
|
||||
w.Write(forum_18)
|
||||
w.Write([]byte(tmpl_forum_vars.CurrentUser.Avatar))
|
||||
w.Write(forum_19)
|
||||
w.Write([]byte(strconv.Itoa(tmpl_forum_vars.Forum.ID)))
|
||||
w.Write(forum_20)
|
||||
if tmpl_forum_vars.CurrentUser.Perms.UploadFiles {
|
||||
w.Write(forum_21)
|
||||
}
|
||||
}
|
||||
w.Write(forum_22)
|
||||
}
|
||||
}
|
||||
w.Write(forum_23)
|
||||
if len(tmpl_forum_vars.ItemList) != 0 {
|
||||
for _, item := range tmpl_forum_vars.ItemList {
|
||||
w.Write(forum_23)
|
||||
w.Write([]byte(strconv.Itoa(item.ID)))
|
||||
w.Write(forum_24)
|
||||
if item.Sticky {
|
||||
w.Write([]byte(strconv.Itoa(item.ID)))
|
||||
w.Write(forum_25)
|
||||
if item.Sticky {
|
||||
w.Write(forum_26)
|
||||
} else {
|
||||
if item.IsClosed {
|
||||
w.Write(forum_26)
|
||||
}
|
||||
}
|
||||
w.Write(forum_27)
|
||||
w.Write([]byte(item.Creator.Link))
|
||||
}
|
||||
}
|
||||
w.Write(forum_28)
|
||||
w.Write([]byte(item.Creator.Avatar))
|
||||
w.Write([]byte(item.Creator.Link))
|
||||
w.Write(forum_29)
|
||||
w.Write([]byte(item.Creator.Name))
|
||||
w.Write([]byte(item.Creator.Avatar))
|
||||
w.Write(forum_30)
|
||||
w.Write([]byte(item.Creator.Name))
|
||||
w.Write(forum_31)
|
||||
w.Write([]byte(item.Link))
|
||||
w.Write(forum_32)
|
||||
w.Write([]byte(item.Title))
|
||||
w.Write(forum_33)
|
||||
w.Write([]byte(item.Creator.Link))
|
||||
w.Write(forum_34)
|
||||
w.Write([]byte(item.Creator.Name))
|
||||
w.Write(forum_32)
|
||||
w.Write([]byte(item.Link))
|
||||
w.Write(forum_33)
|
||||
w.Write([]byte(item.Title))
|
||||
w.Write(forum_34)
|
||||
w.Write([]byte(item.Creator.Link))
|
||||
w.Write(forum_35)
|
||||
if item.IsClosed {
|
||||
w.Write([]byte(item.Creator.Name))
|
||||
w.Write(forum_36)
|
||||
}
|
||||
if item.Sticky {
|
||||
if item.IsClosed {
|
||||
w.Write(forum_37)
|
||||
}
|
||||
w.Write(forum_38)
|
||||
w.Write([]byte(strconv.Itoa(item.PostCount)))
|
||||
w.Write(forum_39)
|
||||
w.Write([]byte(strconv.Itoa(item.LikeCount)))
|
||||
w.Write(forum_40)
|
||||
if item.Sticky {
|
||||
w.Write(forum_38)
|
||||
}
|
||||
w.Write(forum_39)
|
||||
w.Write([]byte(strconv.Itoa(item.PostCount)))
|
||||
w.Write(forum_40)
|
||||
w.Write([]byte(strconv.Itoa(item.LikeCount)))
|
||||
w.Write(forum_41)
|
||||
if item.Sticky {
|
||||
w.Write(forum_42)
|
||||
} else {
|
||||
if item.IsClosed {
|
||||
w.Write(forum_42)
|
||||
}
|
||||
}
|
||||
w.Write(forum_43)
|
||||
w.Write([]byte(item.LastUser.Link))
|
||||
}
|
||||
}
|
||||
w.Write(forum_44)
|
||||
w.Write([]byte(item.LastUser.Avatar))
|
||||
w.Write([]byte(item.LastUser.Link))
|
||||
w.Write(forum_45)
|
||||
w.Write([]byte(item.LastUser.Name))
|
||||
w.Write([]byte(item.LastUser.Avatar))
|
||||
w.Write(forum_46)
|
||||
w.Write([]byte(item.LastUser.Name))
|
||||
w.Write(forum_47)
|
||||
w.Write([]byte(item.LastUser.Link))
|
||||
w.Write(forum_48)
|
||||
w.Write([]byte(item.LastUser.Name))
|
||||
w.Write(forum_48)
|
||||
w.Write([]byte(item.LastUser.Link))
|
||||
w.Write(forum_49)
|
||||
w.Write([]byte(item.RelativeLastReplyAt))
|
||||
w.Write([]byte(item.LastUser.Name))
|
||||
w.Write(forum_50)
|
||||
w.Write([]byte(item.RelativeLastReplyAt))
|
||||
w.Write(forum_51)
|
||||
}
|
||||
} else {
|
||||
w.Write(forum_51)
|
||||
if tmpl_forum_vars.CurrentUser.Perms.CreateTopic {
|
||||
w.Write(forum_52)
|
||||
w.Write([]byte(strconv.Itoa(tmpl_forum_vars.Forum.ID)))
|
||||
if tmpl_forum_vars.CurrentUser.Perms.CreateTopic {
|
||||
w.Write(forum_53)
|
||||
}
|
||||
w.Write([]byte(strconv.Itoa(tmpl_forum_vars.Forum.ID)))
|
||||
w.Write(forum_54)
|
||||
}
|
||||
w.Write(forum_55)
|
||||
if tmpl_forum_vars.LastPage > 1 {
|
||||
}
|
||||
w.Write(forum_56)
|
||||
if tmpl_forum_vars.Page > 1 {
|
||||
if tmpl_forum_vars.LastPage > 1 {
|
||||
w.Write(forum_57)
|
||||
w.Write([]byte(strconv.Itoa(tmpl_forum_vars.Page - 1)))
|
||||
if tmpl_forum_vars.Page > 1 {
|
||||
w.Write(forum_58)
|
||||
w.Write([]byte(strconv.Itoa(tmpl_forum_vars.Page - 1)))
|
||||
w.Write(forum_59)
|
||||
w.Write([]byte(strconv.Itoa(tmpl_forum_vars.Page - 1)))
|
||||
w.Write(forum_60)
|
||||
}
|
||||
if len(tmpl_forum_vars.PageList) != 0 {
|
||||
for _, item := range tmpl_forum_vars.PageList {
|
||||
w.Write(forum_60)
|
||||
w.Write([]byte(strconv.Itoa(item)))
|
||||
w.Write(forum_61)
|
||||
w.Write([]byte(strconv.Itoa(item)))
|
||||
w.Write(forum_62)
|
||||
w.Write([]byte(strconv.Itoa(item)))
|
||||
w.Write(forum_63)
|
||||
}
|
||||
}
|
||||
if tmpl_forum_vars.LastPage != tmpl_forum_vars.Page {
|
||||
w.Write(forum_63)
|
||||
w.Write([]byte(strconv.Itoa(tmpl_forum_vars.Page + 1)))
|
||||
w.Write(forum_64)
|
||||
w.Write([]byte(strconv.Itoa(tmpl_forum_vars.Page + 1)))
|
||||
w.Write(forum_65)
|
||||
}
|
||||
w.Write([]byte(strconv.Itoa(tmpl_forum_vars.Page + 1)))
|
||||
w.Write(forum_66)
|
||||
}
|
||||
w.Write(forum_67)
|
||||
}
|
||||
w.Write(forum_68)
|
||||
w.Write(footer_0)
|
||||
w.Write([]byte(common.BuildWidget("footer",tmpl_forum_vars.Header)))
|
||||
w.Write(footer_1)
|
||||
|
@ -3,8 +3,10 @@
|
||||
// Code generated by Gosora. More below:
|
||||
/* This file was automatically generated by the software. Please don't edit it as your changes may be overwritten at any moment. */
|
||||
package main
|
||||
import "net/http"
|
||||
import "./common"
|
||||
import "net/http"
|
||||
|
||||
var forums_Tmpl_Phrase_ID int
|
||||
|
||||
// nolint
|
||||
func init() {
|
||||
|
@ -3,10 +3,12 @@
|
||||
// Code generated by Gosora. More below:
|
||||
/* This file was automatically generated by the software. Please don't edit it as your changes may be overwritten at any moment. */
|
||||
package main
|
||||
import "./common"
|
||||
import "./extend/guilds/lib"
|
||||
import "strconv"
|
||||
import "net/http"
|
||||
import "./common"
|
||||
|
||||
var guilds_guild_list_Tmpl_Phrase_ID int
|
||||
|
||||
// nolint
|
||||
func init() {
|
||||
|
145
template_ip_search.go
Normal file
145
template_ip_search.go
Normal file
@ -0,0 +1,145 @@
|
||||
// +build !no_templategen
|
||||
|
||||
// Code generated by Gosora. More below:
|
||||
/* This file was automatically generated by the software. Please don't edit it as your changes may be overwritten at any moment. */
|
||||
package main
|
||||
import "net/http"
|
||||
import "./common"
|
||||
|
||||
var ip_search_Tmpl_Phrase_ID int
|
||||
|
||||
// nolint
|
||||
func init() {
|
||||
common.Template_ip_search_handle = Template_ip_search
|
||||
common.Ctemplates = append(common.Ctemplates,"ip_search")
|
||||
common.TmplPtrMap["ip_search"] = &common.Template_ip_search_handle
|
||||
common.TmplPtrMap["o_ip_search"] = Template_ip_search
|
||||
ip_search_Tmpl_Phrase_ID = common.RegisterTmplPhraseNames([]string{
|
||||
"ip_search_head",
|
||||
"ip_search_no_users",
|
||||
})
|
||||
}
|
||||
|
||||
// nolint
|
||||
func Template_ip_search(tmpl_ip_search_vars common.IPSearchPage, w http.ResponseWriter) error {
|
||||
var phrases = common.GetTmplPhrasesBytes(ip_search_Tmpl_Phrase_ID)
|
||||
w.Write(header_0)
|
||||
w.Write([]byte(tmpl_ip_search_vars.Title))
|
||||
w.Write(header_1)
|
||||
w.Write([]byte(tmpl_ip_search_vars.Header.Site.Name))
|
||||
w.Write(header_2)
|
||||
w.Write([]byte(tmpl_ip_search_vars.Header.Theme.Name))
|
||||
w.Write(header_3)
|
||||
if len(tmpl_ip_search_vars.Header.Stylesheets) != 0 {
|
||||
for _, item := range tmpl_ip_search_vars.Header.Stylesheets {
|
||||
w.Write(header_4)
|
||||
w.Write([]byte(item))
|
||||
w.Write(header_5)
|
||||
}
|
||||
}
|
||||
w.Write(header_6)
|
||||
if len(tmpl_ip_search_vars.Header.Scripts) != 0 {
|
||||
for _, item := range tmpl_ip_search_vars.Header.Scripts {
|
||||
w.Write(header_7)
|
||||
w.Write([]byte(item))
|
||||
w.Write(header_8)
|
||||
}
|
||||
}
|
||||
w.Write(header_9)
|
||||
w.Write([]byte(tmpl_ip_search_vars.CurrentUser.Session))
|
||||
w.Write(header_10)
|
||||
w.Write([]byte(tmpl_ip_search_vars.Header.Site.URL))
|
||||
w.Write(header_11)
|
||||
if tmpl_ip_search_vars.Header.MetaDesc != "" {
|
||||
w.Write(header_12)
|
||||
w.Write([]byte(tmpl_ip_search_vars.Header.MetaDesc))
|
||||
w.Write(header_13)
|
||||
}
|
||||
w.Write(header_14)
|
||||
if !tmpl_ip_search_vars.CurrentUser.IsSuperMod {
|
||||
w.Write(header_15)
|
||||
}
|
||||
w.Write(header_16)
|
||||
w.Write(menu_0)
|
||||
w.Write(menu_1)
|
||||
w.Write([]byte(tmpl_ip_search_vars.Header.Site.ShortName))
|
||||
w.Write(menu_2)
|
||||
if tmpl_ip_search_vars.CurrentUser.Loggedin {
|
||||
w.Write(menu_3)
|
||||
w.Write([]byte(tmpl_ip_search_vars.CurrentUser.Link))
|
||||
w.Write(menu_4)
|
||||
w.Write([]byte(tmpl_ip_search_vars.CurrentUser.Session))
|
||||
w.Write(menu_5)
|
||||
} else {
|
||||
w.Write(menu_6)
|
||||
}
|
||||
w.Write(menu_7)
|
||||
w.Write(header_17)
|
||||
if tmpl_ip_search_vars.Header.Widgets.RightSidebar != "" {
|
||||
w.Write(header_18)
|
||||
}
|
||||
w.Write(header_19)
|
||||
if len(tmpl_ip_search_vars.Header.NoticeList) != 0 {
|
||||
for _, item := range tmpl_ip_search_vars.Header.NoticeList {
|
||||
w.Write(header_20)
|
||||
w.Write([]byte(item))
|
||||
w.Write(header_21)
|
||||
}
|
||||
}
|
||||
w.Write(ip_search_0)
|
||||
w.Write(phrases[0])
|
||||
w.Write(ip_search_1)
|
||||
if tmpl_ip_search_vars.IP != "" {
|
||||
w.Write(ip_search_2)
|
||||
w.Write([]byte(tmpl_ip_search_vars.IP))
|
||||
w.Write(ip_search_3)
|
||||
}
|
||||
w.Write(ip_search_4)
|
||||
if tmpl_ip_search_vars.IP != "" {
|
||||
w.Write(ip_search_5)
|
||||
if len(tmpl_ip_search_vars.ItemList) != 0 {
|
||||
for _, item := range tmpl_ip_search_vars.ItemList {
|
||||
w.Write(ip_search_6)
|
||||
w.Write([]byte(item.Avatar))
|
||||
w.Write(ip_search_7)
|
||||
w.Write([]byte(item.Avatar))
|
||||
w.Write(ip_search_8)
|
||||
w.Write([]byte(item.Name))
|
||||
w.Write(ip_search_9)
|
||||
w.Write([]byte(item.Link))
|
||||
w.Write(ip_search_10)
|
||||
w.Write([]byte(item.Name))
|
||||
w.Write(ip_search_11)
|
||||
}
|
||||
} else {
|
||||
w.Write(ip_search_12)
|
||||
w.Write(phrases[1])
|
||||
w.Write(ip_search_13)
|
||||
}
|
||||
|
||||
w.Write(ip_search_14)
|
||||
}
|
||||
w.Write(ip_search_15)
|
||||
w.Write(footer_0)
|
||||
w.Write([]byte(common.BuildWidget("footer",tmpl_ip_search_vars.Header)))
|
||||
w.Write(footer_1)
|
||||
if len(tmpl_ip_search_vars.Header.Themes) != 0 {
|
||||
for _, item := range tmpl_ip_search_vars.Header.Themes {
|
||||
if !item.HideFromThemes {
|
||||
w.Write(footer_2)
|
||||
w.Write([]byte(item.Name))
|
||||
w.Write(footer_3)
|
||||
if tmpl_ip_search_vars.Header.Theme.Name == item.Name {
|
||||
w.Write(footer_4)
|
||||
}
|
||||
w.Write(footer_5)
|
||||
w.Write([]byte(item.FriendlyName))
|
||||
w.Write(footer_6)
|
||||
}
|
||||
}
|
||||
}
|
||||
w.Write(footer_7)
|
||||
w.Write([]byte(common.BuildWidget("rightSidebar",tmpl_ip_search_vars.Header)))
|
||||
w.Write(footer_8)
|
||||
return nil
|
||||
}
|
424
template_list.go
424
template_list.go
@ -83,8 +83,10 @@ var header_17 = []byte(`
|
||||
var header_18 = []byte(`class="shrink_main"`)
|
||||
var header_19 = []byte(`>
|
||||
`)
|
||||
var header_20 = []byte(`<div class="alert">`)
|
||||
var header_21 = []byte(`</div>`)
|
||||
var header_20 = []byte(`<div class="alertbox">
|
||||
<div class="alert">`)
|
||||
var header_21 = []byte(`</div>
|
||||
</div>`)
|
||||
var topic_0 = []byte(`
|
||||
|
||||
<form id="edit_topic_form" action='/topic/edit/submit/`)
|
||||
@ -882,7 +884,7 @@ window.addEventListener("hashchange", handle_profile_hashbit, false)
|
||||
|
||||
`)
|
||||
var forums_0 = []byte(`
|
||||
<main itemscope itemtype="http://schema.org/ItemList">
|
||||
<main id="forumsItemList" itemscope itemtype="http://schema.org/ItemList">
|
||||
|
||||
<div class="rowblock opthead">
|
||||
<div class="rowitem"><h1 itemprop="name">Forums</h1></div>
|
||||
@ -936,31 +938,35 @@ var forums_22 = []byte(`
|
||||
</main>
|
||||
`)
|
||||
var topics_0 = []byte(`
|
||||
<main itemscope itemtype="http://schema.org/ItemList">
|
||||
<main id="topicsItemList" itemscope itemtype="http://schema.org/ItemList">
|
||||
|
||||
<div class="rowblock rowhead topic_list_title_block">
|
||||
<div class="rowitem topic_list_title`)
|
||||
<div class="rowblock rowhead topic_list_title_block`)
|
||||
var topics_1 = []byte(` has_opt`)
|
||||
var topics_2 = []byte(`"><h1 itemprop="name">All Topics</h1></div>
|
||||
var topics_2 = []byte(`">
|
||||
<div class="rowitem topic_list_title"><h1 itemprop="name">All Topics</h1></div>
|
||||
`)
|
||||
var topics_3 = []byte(`
|
||||
<div class="pre_opt auto_hide"></div>
|
||||
<div class="opt create_topic_opt" title="Create Topic" aria-label="Create a topic"><a class="create_topic_link" href="/topics/create/"></a></div>
|
||||
<div class="optbox">
|
||||
`)
|
||||
var topics_4 = []byte(`
|
||||
<div class="opt mod_opt" title="Moderate">
|
||||
<a class="moderate_link" href="#" aria-label="Moderate Posts"></a>
|
||||
<div class="pre_opt auto_hide"></div>
|
||||
<div class="opt create_topic_opt" title="Create Topic" aria-label="Create a topic"><a class="create_topic_link" href="/topics/create/"></a></div>
|
||||
`)
|
||||
var topics_5 = []byte(`
|
||||
<div class="opt mod_opt" title="Moderate">
|
||||
<a class="moderate_link" href="#" aria-label="Moderate Posts"></a>
|
||||
</div>
|
||||
`)
|
||||
var topics_6 = []byte(`<div class="opt locked_opt" title="You don't have the permissions needed to create a topic" aria-label="You don't have the permissions needed to make a topic anywhere"><a></a></div>`)
|
||||
var topics_7 = []byte(`
|
||||
</div>
|
||||
`)
|
||||
var topics_5 = []byte(`<div class="opt locked_opt" title="You don't have the permissions needed to create a topic" aria-label="You don't have the permissions needed to make a topic anywhere"><a></a></div>`)
|
||||
var topics_6 = []byte(`
|
||||
<div style="clear: both;"></div>
|
||||
`)
|
||||
var topics_7 = []byte(`
|
||||
var topics_8 = []byte(`
|
||||
</div>
|
||||
|
||||
`)
|
||||
var topics_8 = []byte(`
|
||||
var topics_9 = []byte(`
|
||||
<div class="mod_floater auto_hide">
|
||||
<form method="post">
|
||||
<div class="mod_floater_head">
|
||||
@ -978,10 +984,10 @@ var topics_8 = []byte(`
|
||||
</div>
|
||||
|
||||
`)
|
||||
var topics_9 = []byte(`
|
||||
var topics_10 = []byte(`
|
||||
<div id="mod_topic_mover" class="modal_pane auto_hide">
|
||||
<form action="/topic/move/submit/?session=`)
|
||||
var topics_10 = []byte(`" method="post">
|
||||
var topics_11 = []byte(`" method="post">
|
||||
<input id="mover_fid" name="fid" value="0" type="hidden" />
|
||||
<div class="pane_header">
|
||||
<h3>Move these topics to?</h3>
|
||||
@ -989,11 +995,11 @@ var topics_10 = []byte(`" method="post">
|
||||
<div class="pane_body">
|
||||
<div class="pane_table">
|
||||
`)
|
||||
var topics_11 = []byte(`<div id="mover_fid_`)
|
||||
var topics_12 = []byte(`" data-fid="`)
|
||||
var topics_13 = []byte(`" class="pane_row">`)
|
||||
var topics_14 = []byte(`</div>`)
|
||||
var topics_15 = []byte(`
|
||||
var topics_12 = []byte(`<div id="mover_fid_`)
|
||||
var topics_13 = []byte(`" data-fid="`)
|
||||
var topics_14 = []byte(`" class="pane_row">`)
|
||||
var topics_15 = []byte(`</div>`)
|
||||
var topics_16 = []byte(`
|
||||
</div>
|
||||
</div>
|
||||
<div class="pane_buttons">
|
||||
@ -1003,21 +1009,21 @@ var topics_15 = []byte(`
|
||||
</div>
|
||||
<div class="rowblock topic_create_form quick_create_form" style="display: none;" aria-label="Quick Topic Form">
|
||||
<form name="topic_create_form_form" id="quick_post_form" enctype="multipart/form-data" action="/topic/create/submit/?session=`)
|
||||
var topics_16 = []byte(`" method="post"></form>
|
||||
var topics_17 = []byte(`" method="post"></form>
|
||||
<input form="quick_post_form" id="has_poll_input" name="has_poll" value="0" type="hidden" />
|
||||
<img class="little_row_avatar" src="`)
|
||||
var topics_17 = []byte(`" height="64" alt="Your Avatar" title="Your Avatar" />
|
||||
var topics_18 = []byte(`" height="64" alt="Your Avatar" title="Your Avatar" />
|
||||
<div class="main_form">
|
||||
<div class="topic_meta">
|
||||
<div class="formrow topic_board_row real_first_child">
|
||||
<div class="formitem"><select form="quick_post_form" id="topic_board_input" name="topic-board">
|
||||
`)
|
||||
var topics_18 = []byte(`<option `)
|
||||
var topics_19 = []byte(`selected`)
|
||||
var topics_20 = []byte(` value="`)
|
||||
var topics_21 = []byte(`">`)
|
||||
var topics_22 = []byte(`</option>`)
|
||||
var topics_23 = []byte(`
|
||||
var topics_19 = []byte(`<option `)
|
||||
var topics_20 = []byte(`selected`)
|
||||
var topics_21 = []byte(` value="`)
|
||||
var topics_22 = []byte(`">`)
|
||||
var topics_23 = []byte(`</option>`)
|
||||
var topics_24 = []byte(`
|
||||
</select></div>
|
||||
</div>
|
||||
<div class="formrow topic_name_row">
|
||||
@ -1045,101 +1051,101 @@ var topics_23 = []byte(`
|
||||
<button form="quick_post_form" class="formbutton">Create Topic</button>
|
||||
<button form="quick_post_form" class="formbutton" id="add_poll_button">Add Poll</button>
|
||||
`)
|
||||
var topics_24 = []byte(`
|
||||
var topics_25 = []byte(`
|
||||
<input name="upload_files" form="quick_post_form" id="upload_files" multiple type="file" style="display: none;" />
|
||||
<label for="upload_files" class="formbutton add_file_button">Add File</label>
|
||||
<div id="upload_file_dock"></div>`)
|
||||
var topics_25 = []byte(`
|
||||
var topics_26 = []byte(`
|
||||
<button class="formbutton close_form">Cancel</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`)
|
||||
var topics_26 = []byte(`
|
||||
var topics_27 = []byte(`
|
||||
<div id="topic_list" class="rowblock topic_list" aria-label="A list containing topics from every forum">
|
||||
`)
|
||||
var topics_27 = []byte(`<div class="topic_row" data-tid="`)
|
||||
var topics_28 = []byte(`">
|
||||
var topics_28 = []byte(`<div class="topic_row" data-tid="`)
|
||||
var topics_29 = []byte(`">
|
||||
<div class="rowitem topic_left passive datarow `)
|
||||
var topics_29 = []byte(`topic_sticky`)
|
||||
var topics_30 = []byte(`topic_closed`)
|
||||
var topics_31 = []byte(`">
|
||||
var topics_30 = []byte(`topic_sticky`)
|
||||
var topics_31 = []byte(`topic_closed`)
|
||||
var topics_32 = []byte(`">
|
||||
<span class="selector"></span>
|
||||
<a href="`)
|
||||
var topics_32 = []byte(`"><img src="`)
|
||||
var topics_33 = []byte(`" height="64" alt="`)
|
||||
var topics_34 = []byte(`'s Avatar" title="`)
|
||||
var topics_35 = []byte(`'s Avatar" /></a>
|
||||
var topics_33 = []byte(`"><img src="`)
|
||||
var topics_34 = []byte(`" height="64" alt="`)
|
||||
var topics_35 = []byte(`'s Avatar" title="`)
|
||||
var topics_36 = []byte(`'s Avatar" /></a>
|
||||
<span class="topic_inner_left">
|
||||
<a class="rowtopic" href="`)
|
||||
var topics_36 = []byte(`" itemprop="itemListElement"><span>`)
|
||||
var topics_37 = []byte(`</span></a> `)
|
||||
var topics_38 = []byte(`<a class="rowsmall parent_forum" href="`)
|
||||
var topics_39 = []byte(`">`)
|
||||
var topics_40 = []byte(`</a>`)
|
||||
var topics_41 = []byte(`
|
||||
var topics_37 = []byte(`" itemprop="itemListElement"><span>`)
|
||||
var topics_38 = []byte(`</span></a> `)
|
||||
var topics_39 = []byte(`<a class="rowsmall parent_forum" href="`)
|
||||
var topics_40 = []byte(`">`)
|
||||
var topics_41 = []byte(`</a>`)
|
||||
var topics_42 = []byte(`
|
||||
<br /><a class="rowsmall starter" href="`)
|
||||
var topics_42 = []byte(`">`)
|
||||
var topics_43 = []byte(`</a>
|
||||
var topics_43 = []byte(`">`)
|
||||
var topics_44 = []byte(`</a>
|
||||
`)
|
||||
var topics_44 = []byte(`<span class="rowsmall topic_status_e topic_status_closed" title="Status: Closed"> | 🔒︎</span>`)
|
||||
var topics_45 = []byte(`<span class="rowsmall topic_status_e topic_status_sticky" title="Status: Pinned"> | 📍︎</span>`)
|
||||
var topics_46 = []byte(`
|
||||
var topics_45 = []byte(`<span class="rowsmall topic_status_e topic_status_closed" title="Status: Closed"> | 🔒︎</span>`)
|
||||
var topics_46 = []byte(`<span class="rowsmall topic_status_e topic_status_sticky" title="Status: Pinned"> | 📍︎</span>`)
|
||||
var topics_47 = []byte(`
|
||||
</span>
|
||||
<span class="topic_inner_right rowsmall" style="float: right;">
|
||||
<span class="replyCount">`)
|
||||
var topics_47 = []byte(`</span><br />
|
||||
var topics_48 = []byte(`</span><br />
|
||||
<span class="likeCount">`)
|
||||
var topics_48 = []byte(`</span>
|
||||
var topics_49 = []byte(`</span>
|
||||
</span>
|
||||
</div>
|
||||
<div class="rowitem topic_right passive datarow `)
|
||||
var topics_49 = []byte(`topic_sticky`)
|
||||
var topics_50 = []byte(`topic_closed`)
|
||||
var topics_51 = []byte(`">
|
||||
var topics_50 = []byte(`topic_sticky`)
|
||||
var topics_51 = []byte(`topic_closed`)
|
||||
var topics_52 = []byte(`">
|
||||
<a href="`)
|
||||
var topics_52 = []byte(`"><img src="`)
|
||||
var topics_53 = []byte(`" height="64" alt="`)
|
||||
var topics_54 = []byte(`'s Avatar" title="`)
|
||||
var topics_55 = []byte(`'s Avatar" /></a>
|
||||
var topics_53 = []byte(`"><img src="`)
|
||||
var topics_54 = []byte(`" height="64" alt="`)
|
||||
var topics_55 = []byte(`'s Avatar" title="`)
|
||||
var topics_56 = []byte(`'s Avatar" /></a>
|
||||
<span>
|
||||
<a href="`)
|
||||
var topics_56 = []byte(`" class="lastName" style="font-size: 14px;">`)
|
||||
var topics_57 = []byte(`</a><br>
|
||||
var topics_57 = []byte(`" class="lastName" style="font-size: 14px;">`)
|
||||
var topics_58 = []byte(`</a><br>
|
||||
<span class="rowsmall lastReplyAt">`)
|
||||
var topics_58 = []byte(`</span>
|
||||
var topics_59 = []byte(`</span>
|
||||
</span>
|
||||
</div>
|
||||
</div>`)
|
||||
var topics_59 = []byte(`<div class="rowitem passive rowmsg">There aren't any topics yet.`)
|
||||
var topics_60 = []byte(` <a href="/topics/create/">Start one?</a>`)
|
||||
var topics_61 = []byte(`</div>`)
|
||||
var topics_62 = []byte(`
|
||||
var topics_60 = []byte(`<div class="rowitem passive rowmsg">There aren't any topics yet.`)
|
||||
var topics_61 = []byte(` <a href="/topics/create/">Start one?</a>`)
|
||||
var topics_62 = []byte(`</div>`)
|
||||
var topics_63 = []byte(`
|
||||
</div>
|
||||
|
||||
`)
|
||||
var topics_63 = []byte(`
|
||||
var topics_64 = []byte(`
|
||||
<div class="pageset">
|
||||
`)
|
||||
var topics_64 = []byte(`<div class="pageitem"><a href="?page=`)
|
||||
var topics_65 = []byte(`" rel="prev" aria-label="Go to the previous page">Prev</a></div>
|
||||
var topics_65 = []byte(`<div class="pageitem"><a href="?page=`)
|
||||
var topics_66 = []byte(`" rel="prev" aria-label="Go to the previous page">Prev</a></div>
|
||||
<link rel="prev" href="?page=`)
|
||||
var topics_66 = []byte(`" />`)
|
||||
var topics_67 = []byte(`
|
||||
var topics_67 = []byte(`" />`)
|
||||
var topics_68 = []byte(`
|
||||
<div class="pageitem"><a href="?page=`)
|
||||
var topics_68 = []byte(`">`)
|
||||
var topics_69 = []byte(`</a></div>
|
||||
var topics_69 = []byte(`">`)
|
||||
var topics_70 = []byte(`</a></div>
|
||||
`)
|
||||
var topics_70 = []byte(`
|
||||
var topics_71 = []byte(`
|
||||
<link rel="next" href="?page=`)
|
||||
var topics_71 = []byte(`" />
|
||||
var topics_72 = []byte(`" />
|
||||
<div class="pageitem"><a href="?page=`)
|
||||
var topics_72 = []byte(`" rel="next" aria-label="Go to the next page">Next</a></div>`)
|
||||
var topics_73 = []byte(`
|
||||
var topics_73 = []byte(`" rel="next" aria-label="Go to the next page">Next</a></div>`)
|
||||
var topics_74 = []byte(`
|
||||
</div>
|
||||
`)
|
||||
var topics_74 = []byte(`
|
||||
var topics_75 = []byte(`
|
||||
|
||||
</main>
|
||||
`)
|
||||
@ -1151,33 +1157,37 @@ var forum_4 = []byte(`?page=`)
|
||||
var forum_5 = []byte(`">></a></div>`)
|
||||
var forum_6 = []byte(`
|
||||
|
||||
<main itemscope itemtype="http://schema.org/ItemList">
|
||||
<div id="forum_head_block" class="rowblock rowhead topic_list_title_block">
|
||||
<div class="rowitem forum_title`)
|
||||
<main id="forumItemList" itemscope itemtype="http://schema.org/ItemList">
|
||||
<div id="forum_head_block" class="rowblock rowhead topic_list_title_block`)
|
||||
var forum_7 = []byte(` has_opt`)
|
||||
var forum_8 = []byte(`">
|
||||
<div class="rowitem forum_title">
|
||||
<h1 itemprop="name">`)
|
||||
var forum_9 = []byte(`</h1>
|
||||
</div>
|
||||
`)
|
||||
var forum_10 = []byte(`
|
||||
<div class="pre_opt auto_hide"></div>
|
||||
<div class="opt create_topic_opt" title="Create Topic" aria-label="Create a topic"><a class="create_topic_link" href="/topics/create/`)
|
||||
var forum_11 = []byte(`"></a></div>
|
||||
`)
|
||||
var forum_12 = []byte(`
|
||||
<div class="opt mod_opt" title="Moderate">
|
||||
<a class="moderate_link" href="#" aria-label="Moderate Posts"></a>
|
||||
<div class="optbox">
|
||||
`)
|
||||
var forum_11 = []byte(`
|
||||
<div class="pre_opt auto_hide"></div>
|
||||
<div class="opt create_topic_opt" title="Create Topic" aria-label="Create a topic"><a class="create_topic_link" href="/topics/create/`)
|
||||
var forum_12 = []byte(`"></a></div>
|
||||
`)
|
||||
var forum_13 = []byte(`
|
||||
<div class="opt mod_opt" title="Moderate">
|
||||
<a class="moderate_link" href="#" aria-label="Moderate Posts"></a>
|
||||
</div>
|
||||
`)
|
||||
var forum_14 = []byte(`<div class="opt locked_opt" title="You don't have the permissions needed to create a topic" aria-label="You don't have the permissions needed to make a topic in this forum"><a></a></div>`)
|
||||
var forum_15 = []byte(`
|
||||
</div>
|
||||
`)
|
||||
var forum_13 = []byte(`<div class="opt locked_opt" title="You don't have the permissions needed to create a topic" aria-label="You don't have the permissions needed to make a topic in this forum"><a></a></div>`)
|
||||
var forum_14 = []byte(`
|
||||
<div style="clear: both;"></div>
|
||||
`)
|
||||
var forum_15 = []byte(`
|
||||
var forum_16 = []byte(`
|
||||
</div>
|
||||
`)
|
||||
var forum_16 = []byte(`
|
||||
var forum_17 = []byte(`
|
||||
<div class="mod_floater auto_hide">
|
||||
<form method="post">
|
||||
<div class="mod_floater_head">
|
||||
@ -1194,13 +1204,13 @@ var forum_16 = []byte(`
|
||||
</form>
|
||||
</div>
|
||||
`)
|
||||
var forum_17 = []byte(`
|
||||
var forum_18 = []byte(`
|
||||
<div id="forum_topic_create_form" class="rowblock topic_create_form quick_create_form" style="display: none;" aria-label="Quick Topic Form">
|
||||
<form id="quick_post_form" enctype="multipart/form-data" action="/topic/create/submit/" method="post"></form>
|
||||
<img class="little_row_avatar" src="`)
|
||||
var forum_18 = []byte(`" height="64" alt="Your Avatar" title="Your Avatar" />
|
||||
var forum_19 = []byte(`" height="64" alt="Your Avatar" title="Your Avatar" />
|
||||
<input form="quick_post_form" id="topic_board_input" name="topic-board" value="`)
|
||||
var forum_19 = []byte(`" type="hidden">
|
||||
var forum_20 = []byte(`" type="hidden">
|
||||
<div class="main_form">
|
||||
<div class="topic_meta">
|
||||
<div class="formrow topic_name_row real_first_child">
|
||||
@ -1224,99 +1234,217 @@ var forum_19 = []byte(`" type="hidden">
|
||||
<button form="quick_post_form" name="topic-button" class="formbutton">Create Topic</button>
|
||||
<button form="quick_post_form" class="formbutton" id="add_poll_button">Add Poll</button>
|
||||
`)
|
||||
var forum_20 = []byte(`
|
||||
var forum_21 = []byte(`
|
||||
<input name="upload_files" form="quick_post_form" id="upload_files" multiple type="file" style="display: none;" />
|
||||
<label for="upload_files" class="formbutton add_file_button">Add File</label>
|
||||
<div id="upload_file_dock"></div>`)
|
||||
var forum_21 = []byte(`
|
||||
var forum_22 = []byte(`
|
||||
<button class="formbutton close_form">Cancel</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`)
|
||||
var forum_22 = []byte(`
|
||||
var forum_23 = []byte(`
|
||||
<div id="forum_topic_list" class="rowblock topic_list">
|
||||
`)
|
||||
var forum_23 = []byte(`<div class="topic_row" data-tid="`)
|
||||
var forum_24 = []byte(`">
|
||||
var forum_24 = []byte(`<div class="topic_row" data-tid="`)
|
||||
var forum_25 = []byte(`">
|
||||
<div class="rowitem topic_left passive datarow `)
|
||||
var forum_25 = []byte(`topic_sticky`)
|
||||
var forum_26 = []byte(`topic_closed`)
|
||||
var forum_27 = []byte(`">
|
||||
var forum_26 = []byte(`topic_sticky`)
|
||||
var forum_27 = []byte(`topic_closed`)
|
||||
var forum_28 = []byte(`">
|
||||
<span class="selector"></span>
|
||||
<a href="`)
|
||||
var forum_28 = []byte(`"><img src="`)
|
||||
var forum_29 = []byte(`" height="64" alt="`)
|
||||
var forum_30 = []byte(`'s Avatar" title="`)
|
||||
var forum_31 = []byte(`'s Avatar" /></a>
|
||||
var forum_29 = []byte(`"><img src="`)
|
||||
var forum_30 = []byte(`" height="64" alt="`)
|
||||
var forum_31 = []byte(`'s Avatar" title="`)
|
||||
var forum_32 = []byte(`'s Avatar" /></a>
|
||||
<span class="topic_inner_left">
|
||||
<a class="rowtopic" href="`)
|
||||
var forum_32 = []byte(`" itemprop="itemListElement"><span>`)
|
||||
var forum_33 = []byte(`</span></a>
|
||||
var forum_33 = []byte(`" itemprop="itemListElement"><span>`)
|
||||
var forum_34 = []byte(`</span></a>
|
||||
<br /><a class="rowsmall starter" href="`)
|
||||
var forum_34 = []byte(`">`)
|
||||
var forum_35 = []byte(`</a>
|
||||
var forum_35 = []byte(`">`)
|
||||
var forum_36 = []byte(`</a>
|
||||
`)
|
||||
var forum_36 = []byte(`<span class="rowsmall topic_status_e topic_status_closed" title="Status: Closed"> | 🔒︎</span>`)
|
||||
var forum_37 = []byte(`<span class="rowsmall topic_status_e topic_status_sticky" title="Status: Pinned"> | 📍︎</span>`)
|
||||
var forum_38 = []byte(`
|
||||
var forum_37 = []byte(`<span class="rowsmall topic_status_e topic_status_closed" title="Status: Closed"> | 🔒︎</span>`)
|
||||
var forum_38 = []byte(`<span class="rowsmall topic_status_e topic_status_sticky" title="Status: Pinned"> | 📍︎</span>`)
|
||||
var forum_39 = []byte(`
|
||||
</span>
|
||||
<span class="topic_inner_right rowsmall" style="float: right;">
|
||||
<span class="replyCount">`)
|
||||
var forum_39 = []byte(`</span><br />
|
||||
var forum_40 = []byte(`</span><br />
|
||||
<span class="likeCount">`)
|
||||
var forum_40 = []byte(`</span>
|
||||
var forum_41 = []byte(`</span>
|
||||
</span>
|
||||
</div>
|
||||
<div class="rowitem topic_right passive datarow `)
|
||||
var forum_41 = []byte(`topic_sticky`)
|
||||
var forum_42 = []byte(`topic_closed`)
|
||||
var forum_43 = []byte(`">
|
||||
var forum_42 = []byte(`topic_sticky`)
|
||||
var forum_43 = []byte(`topic_closed`)
|
||||
var forum_44 = []byte(`">
|
||||
<a href="`)
|
||||
var forum_44 = []byte(`"><img src="`)
|
||||
var forum_45 = []byte(`" height="64" alt="`)
|
||||
var forum_46 = []byte(`'s Avatar" title="`)
|
||||
var forum_47 = []byte(`'s Avatar" /></a>
|
||||
var forum_45 = []byte(`"><img src="`)
|
||||
var forum_46 = []byte(`" height="64" alt="`)
|
||||
var forum_47 = []byte(`'s Avatar" title="`)
|
||||
var forum_48 = []byte(`'s Avatar" /></a>
|
||||
<span>
|
||||
<a href="`)
|
||||
var forum_48 = []byte(`" class="lastName" style="font-size: 14px;">`)
|
||||
var forum_49 = []byte(`</a><br>
|
||||
var forum_49 = []byte(`" class="lastName" style="font-size: 14px;">`)
|
||||
var forum_50 = []byte(`</a><br>
|
||||
<span class="rowsmall lastReplyAt">`)
|
||||
var forum_50 = []byte(`</span>
|
||||
var forum_51 = []byte(`</span>
|
||||
</span>
|
||||
</div>
|
||||
</div>`)
|
||||
var forum_51 = []byte(`<div class="rowitem passive rowmsg">There aren't any topics in this forum yet.`)
|
||||
var forum_52 = []byte(` <a href="/topics/create/`)
|
||||
var forum_53 = []byte(`">Start one?</a>`)
|
||||
var forum_54 = []byte(`</div>`)
|
||||
var forum_55 = []byte(`
|
||||
var forum_52 = []byte(`<div class="rowitem passive rowmsg">There aren't any topics in this forum yet.`)
|
||||
var forum_53 = []byte(` <a href="/topics/create/`)
|
||||
var forum_54 = []byte(`">Start one?</a>`)
|
||||
var forum_55 = []byte(`</div>`)
|
||||
var forum_56 = []byte(`
|
||||
</div>
|
||||
|
||||
`)
|
||||
var forum_56 = []byte(`
|
||||
var forum_57 = []byte(`
|
||||
<div class="pageset">
|
||||
`)
|
||||
var forum_57 = []byte(`<div class="pageitem"><a href="?page=`)
|
||||
var forum_58 = []byte(`" rel="prev" aria-label="Go to the previous page">Prev</a></div>
|
||||
var forum_58 = []byte(`<div class="pageitem"><a href="?page=`)
|
||||
var forum_59 = []byte(`" rel="prev" aria-label="Go to the previous page">Prev</a></div>
|
||||
<link rel="prev" href="?page=`)
|
||||
var forum_59 = []byte(`" />`)
|
||||
var forum_60 = []byte(`
|
||||
var forum_60 = []byte(`" />`)
|
||||
var forum_61 = []byte(`
|
||||
<div class="pageitem"><a href="?page=`)
|
||||
var forum_61 = []byte(`">`)
|
||||
var forum_62 = []byte(`</a></div>
|
||||
var forum_62 = []byte(`">`)
|
||||
var forum_63 = []byte(`</a></div>
|
||||
`)
|
||||
var forum_63 = []byte(`
|
||||
var forum_64 = []byte(`
|
||||
<link rel="next" href="?page=`)
|
||||
var forum_64 = []byte(`" />
|
||||
var forum_65 = []byte(`" />
|
||||
<div class="pageitem"><a href="?page=`)
|
||||
var forum_65 = []byte(`" rel="next" aria-label="Go to the next page">Next</a></div>`)
|
||||
var forum_66 = []byte(`
|
||||
var forum_66 = []byte(`" rel="next" aria-label="Go to the next page">Next</a></div>`)
|
||||
var forum_67 = []byte(`
|
||||
</div>
|
||||
`)
|
||||
var forum_67 = []byte(`
|
||||
var forum_68 = []byte(`
|
||||
|
||||
</main>
|
||||
`)
|
||||
var login_0 = []byte(`
|
||||
<main id="login_page">
|
||||
<div class="rowblock rowhead">
|
||||
<div class="rowitem"><h1>`)
|
||||
var login_1 = []byte(`</h1></div>
|
||||
</div>
|
||||
<div class="rowblock">
|
||||
<form action="/accounts/login/submit/" method="post">
|
||||
<div class="formrow login_name_row">
|
||||
<div class="formitem formlabel"><a>`)
|
||||
var login_2 = []byte(`</a></div>
|
||||
<div class="formitem"><input name="username" type="text" placeholder="`)
|
||||
var login_3 = []byte(`" required /></div>
|
||||
</div>
|
||||
<div class="formrow login_password_row">
|
||||
<div class="formitem formlabel"><a>`)
|
||||
var login_4 = []byte(`</a></div>
|
||||
<div class="formitem"><input name="password" type="password" autocomplete="current-password" placeholder="*****" required /></div>
|
||||
</div>
|
||||
<div class="formrow login_button_row">
|
||||
<div class="formitem"><button name="login-button" class="formbutton">`)
|
||||
var login_5 = []byte(`</button></div>
|
||||
<div class="formitem dont_have_account">`)
|
||||
var login_6 = []byte(`</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</main>
|
||||
`)
|
||||
var register_0 = []byte(`
|
||||
<main id="register_page">
|
||||
<div class="rowblock rowhead">
|
||||
<div class="rowitem"><h1>`)
|
||||
var register_1 = []byte(`</h1></div>
|
||||
</div>
|
||||
<div class="rowblock">
|
||||
<form action="/accounts/create/submit/" method="post">
|
||||
<div class="formrow">
|
||||
<div class="formitem formlabel"><a id="username_label">`)
|
||||
var register_2 = []byte(`</a></div>
|
||||
<div class="formitem"><input name="username" type="text" placeholder="`)
|
||||
var register_3 = []byte(`" aria-labelledby="username_label" required /></div>
|
||||
</div>
|
||||
<div class="formrow">
|
||||
<div class="formitem formlabel"><a id="email_label">`)
|
||||
var register_4 = []byte(`</a></div>
|
||||
<div class="formitem"><input name="email" type="email" placeholder="joe.doe@example.com" aria-labelledby="email_label" required /></div>
|
||||
</div>
|
||||
<div class="formrow">
|
||||
<div class="formitem formlabel"><a id="password_label">`)
|
||||
var register_5 = []byte(`</a></div>
|
||||
<div class="formitem"><input name="password" type="password" autocomplete="new-password" placeholder="*****" aria-labelledby="password_label" required /></div>
|
||||
</div>
|
||||
<div class="formrow">
|
||||
<div class="formitem formlabel"><a id="confirm_password_label">`)
|
||||
var register_6 = []byte(`</a></div>
|
||||
<div class="formitem"><input name="confirm_password" type="password" placeholder="*****" aria-labelledby="confirm_password_label" required /></div>
|
||||
</div>
|
||||
<div class="formrow">
|
||||
<div class="formitem"><button name="register-button" class="formbutton">`)
|
||||
var register_7 = []byte(`</button></div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</main>
|
||||
`)
|
||||
var error_0 = []byte(`
|
||||
<main>
|
||||
<div class="rowblock rowhead">
|
||||
<div class="rowitem"><h1>`)
|
||||
var error_1 = []byte(`</h1></div>
|
||||
</div>
|
||||
<div class="rowblock">
|
||||
<div class="rowitem passive rowmsg">`)
|
||||
var error_2 = []byte(`</div>
|
||||
</div>
|
||||
</main>
|
||||
`)
|
||||
var ip_search_0 = []byte(`
|
||||
<main id="ip_search_container">
|
||||
<div class="rowblock rowhead">
|
||||
<div class="rowitem">
|
||||
<h1>`)
|
||||
var ip_search_1 = []byte(`</h1>
|
||||
</div>
|
||||
</div>
|
||||
<form action="/users/ips/" method="get" id="ip-search-form"></form>
|
||||
<div class="rowblock ip_search_block">
|
||||
<div class="rowitem passive">
|
||||
<input form="ip-search-form" name="ip" class="ip_search_input" type="search" placeholder="🔍︎"`)
|
||||
var ip_search_2 = []byte(` value="`)
|
||||
var ip_search_3 = []byte(`"`)
|
||||
var ip_search_4 = []byte(` />
|
||||
<input form="ip-search-form" class="ip_search_search" type="submit" value="Search" />
|
||||
</div>
|
||||
</div>
|
||||
`)
|
||||
var ip_search_5 = []byte(`
|
||||
<div class="rowblock rowlist bgavatars">
|
||||
`)
|
||||
var ip_search_6 = []byte(`<div class="rowitem" style="background-image: url('`)
|
||||
var ip_search_7 = []byte(`');">
|
||||
<img src="`)
|
||||
var ip_search_8 = []byte(`" class="bgsub" alt="`)
|
||||
var ip_search_9 = []byte(`'s Avatar" />
|
||||
<a class="rowTitle" href="`)
|
||||
var ip_search_10 = []byte(`">`)
|
||||
var ip_search_11 = []byte(`</a>
|
||||
</div>
|
||||
`)
|
||||
var ip_search_12 = []byte(`<div class="rowitem rowmsg">`)
|
||||
var ip_search_13 = []byte(`</div>`)
|
||||
var ip_search_14 = []byte(`
|
||||
</div>
|
||||
`)
|
||||
var ip_search_15 = []byte(`
|
||||
</main>
|
||||
`)
|
||||
var guilds_guild_list_0 = []byte(`
|
||||
|
128
template_login.go
Normal file
128
template_login.go
Normal file
@ -0,0 +1,128 @@
|
||||
// +build !no_templategen
|
||||
|
||||
// Code generated by Gosora. More below:
|
||||
/* This file was automatically generated by the software. Please don't edit it as your changes may be overwritten at any moment. */
|
||||
package main
|
||||
import "net/http"
|
||||
import "./common"
|
||||
|
||||
var login_Tmpl_Phrase_ID int
|
||||
|
||||
// nolint
|
||||
func init() {
|
||||
common.Template_login_handle = Template_login
|
||||
common.Ctemplates = append(common.Ctemplates,"login")
|
||||
common.TmplPtrMap["login"] = &common.Template_login_handle
|
||||
common.TmplPtrMap["o_login"] = Template_login
|
||||
login_Tmpl_Phrase_ID = common.RegisterTmplPhraseNames([]string{
|
||||
"login_head",
|
||||
"login_account_name",
|
||||
"login_account_name",
|
||||
"login_account_password",
|
||||
"login_submit_button",
|
||||
"login_no_account",
|
||||
})
|
||||
}
|
||||
|
||||
// nolint
|
||||
func Template_login(tmpl_login_vars common.Page, w http.ResponseWriter) error {
|
||||
var phrases = common.GetTmplPhrasesBytes(login_Tmpl_Phrase_ID)
|
||||
w.Write(header_0)
|
||||
w.Write([]byte(tmpl_login_vars.Title))
|
||||
w.Write(header_1)
|
||||
w.Write([]byte(tmpl_login_vars.Header.Site.Name))
|
||||
w.Write(header_2)
|
||||
w.Write([]byte(tmpl_login_vars.Header.Theme.Name))
|
||||
w.Write(header_3)
|
||||
if len(tmpl_login_vars.Header.Stylesheets) != 0 {
|
||||
for _, item := range tmpl_login_vars.Header.Stylesheets {
|
||||
w.Write(header_4)
|
||||
w.Write([]byte(item))
|
||||
w.Write(header_5)
|
||||
}
|
||||
}
|
||||
w.Write(header_6)
|
||||
if len(tmpl_login_vars.Header.Scripts) != 0 {
|
||||
for _, item := range tmpl_login_vars.Header.Scripts {
|
||||
w.Write(header_7)
|
||||
w.Write([]byte(item))
|
||||
w.Write(header_8)
|
||||
}
|
||||
}
|
||||
w.Write(header_9)
|
||||
w.Write([]byte(tmpl_login_vars.CurrentUser.Session))
|
||||
w.Write(header_10)
|
||||
w.Write([]byte(tmpl_login_vars.Header.Site.URL))
|
||||
w.Write(header_11)
|
||||
if tmpl_login_vars.Header.MetaDesc != "" {
|
||||
w.Write(header_12)
|
||||
w.Write([]byte(tmpl_login_vars.Header.MetaDesc))
|
||||
w.Write(header_13)
|
||||
}
|
||||
w.Write(header_14)
|
||||
if !tmpl_login_vars.CurrentUser.IsSuperMod {
|
||||
w.Write(header_15)
|
||||
}
|
||||
w.Write(header_16)
|
||||
w.Write(menu_0)
|
||||
w.Write(menu_1)
|
||||
w.Write([]byte(tmpl_login_vars.Header.Site.ShortName))
|
||||
w.Write(menu_2)
|
||||
if tmpl_login_vars.CurrentUser.Loggedin {
|
||||
w.Write(menu_3)
|
||||
w.Write([]byte(tmpl_login_vars.CurrentUser.Link))
|
||||
w.Write(menu_4)
|
||||
w.Write([]byte(tmpl_login_vars.CurrentUser.Session))
|
||||
w.Write(menu_5)
|
||||
} else {
|
||||
w.Write(menu_6)
|
||||
}
|
||||
w.Write(menu_7)
|
||||
w.Write(header_17)
|
||||
if tmpl_login_vars.Header.Widgets.RightSidebar != "" {
|
||||
w.Write(header_18)
|
||||
}
|
||||
w.Write(header_19)
|
||||
if len(tmpl_login_vars.Header.NoticeList) != 0 {
|
||||
for _, item := range tmpl_login_vars.Header.NoticeList {
|
||||
w.Write(header_20)
|
||||
w.Write([]byte(item))
|
||||
w.Write(header_21)
|
||||
}
|
||||
}
|
||||
w.Write(login_0)
|
||||
w.Write(phrases[0])
|
||||
w.Write(login_1)
|
||||
w.Write(phrases[1])
|
||||
w.Write(login_2)
|
||||
w.Write(phrases[2])
|
||||
w.Write(login_3)
|
||||
w.Write(phrases[3])
|
||||
w.Write(login_4)
|
||||
w.Write(phrases[4])
|
||||
w.Write(login_5)
|
||||
w.Write(phrases[5])
|
||||
w.Write(login_6)
|
||||
w.Write(footer_0)
|
||||
w.Write([]byte(common.BuildWidget("footer",tmpl_login_vars.Header)))
|
||||
w.Write(footer_1)
|
||||
if len(tmpl_login_vars.Header.Themes) != 0 {
|
||||
for _, item := range tmpl_login_vars.Header.Themes {
|
||||
if !item.HideFromThemes {
|
||||
w.Write(footer_2)
|
||||
w.Write([]byte(item.Name))
|
||||
w.Write(footer_3)
|
||||
if tmpl_login_vars.Header.Theme.Name == item.Name {
|
||||
w.Write(footer_4)
|
||||
}
|
||||
w.Write(footer_5)
|
||||
w.Write([]byte(item.FriendlyName))
|
||||
w.Write(footer_6)
|
||||
}
|
||||
}
|
||||
}
|
||||
w.Write(footer_7)
|
||||
w.Write([]byte(common.BuildWidget("rightSidebar",tmpl_login_vars.Header)))
|
||||
w.Write(footer_8)
|
||||
return nil
|
||||
}
|
@ -3,9 +3,11 @@
|
||||
// Code generated by Gosora. More below:
|
||||
/* This file was automatically generated by the software. Please don't edit it as your changes may be overwritten at any moment. */
|
||||
package main
|
||||
import "strconv"
|
||||
import "net/http"
|
||||
import "./common"
|
||||
import "strconv"
|
||||
|
||||
var profile_Tmpl_Phrase_ID int
|
||||
|
||||
// nolint
|
||||
func init() {
|
||||
|
131
template_register.go
Normal file
131
template_register.go
Normal file
@ -0,0 +1,131 @@
|
||||
// +build !no_templategen
|
||||
|
||||
// Code generated by Gosora. More below:
|
||||
/* This file was automatically generated by the software. Please don't edit it as your changes may be overwritten at any moment. */
|
||||
package main
|
||||
import "net/http"
|
||||
import "./common"
|
||||
|
||||
var register_Tmpl_Phrase_ID int
|
||||
|
||||
// nolint
|
||||
func init() {
|
||||
common.Template_register_handle = Template_register
|
||||
common.Ctemplates = append(common.Ctemplates,"register")
|
||||
common.TmplPtrMap["register"] = &common.Template_register_handle
|
||||
common.TmplPtrMap["o_register"] = Template_register
|
||||
register_Tmpl_Phrase_ID = common.RegisterTmplPhraseNames([]string{
|
||||
"register_head",
|
||||
"register_account_name",
|
||||
"register_account_name",
|
||||
"register_account_email",
|
||||
"register_account_password",
|
||||
"register_account_confirm_password",
|
||||
"register_submit_button",
|
||||
})
|
||||
}
|
||||
|
||||
// nolint
|
||||
func Template_register(tmpl_register_vars common.Page, w http.ResponseWriter) error {
|
||||
var phrases = common.GetTmplPhrasesBytes(register_Tmpl_Phrase_ID)
|
||||
w.Write(header_0)
|
||||
w.Write([]byte(tmpl_register_vars.Title))
|
||||
w.Write(header_1)
|
||||
w.Write([]byte(tmpl_register_vars.Header.Site.Name))
|
||||
w.Write(header_2)
|
||||
w.Write([]byte(tmpl_register_vars.Header.Theme.Name))
|
||||
w.Write(header_3)
|
||||
if len(tmpl_register_vars.Header.Stylesheets) != 0 {
|
||||
for _, item := range tmpl_register_vars.Header.Stylesheets {
|
||||
w.Write(header_4)
|
||||
w.Write([]byte(item))
|
||||
w.Write(header_5)
|
||||
}
|
||||
}
|
||||
w.Write(header_6)
|
||||
if len(tmpl_register_vars.Header.Scripts) != 0 {
|
||||
for _, item := range tmpl_register_vars.Header.Scripts {
|
||||
w.Write(header_7)
|
||||
w.Write([]byte(item))
|
||||
w.Write(header_8)
|
||||
}
|
||||
}
|
||||
w.Write(header_9)
|
||||
w.Write([]byte(tmpl_register_vars.CurrentUser.Session))
|
||||
w.Write(header_10)
|
||||
w.Write([]byte(tmpl_register_vars.Header.Site.URL))
|
||||
w.Write(header_11)
|
||||
if tmpl_register_vars.Header.MetaDesc != "" {
|
||||
w.Write(header_12)
|
||||
w.Write([]byte(tmpl_register_vars.Header.MetaDesc))
|
||||
w.Write(header_13)
|
||||
}
|
||||
w.Write(header_14)
|
||||
if !tmpl_register_vars.CurrentUser.IsSuperMod {
|
||||
w.Write(header_15)
|
||||
}
|
||||
w.Write(header_16)
|
||||
w.Write(menu_0)
|
||||
w.Write(menu_1)
|
||||
w.Write([]byte(tmpl_register_vars.Header.Site.ShortName))
|
||||
w.Write(menu_2)
|
||||
if tmpl_register_vars.CurrentUser.Loggedin {
|
||||
w.Write(menu_3)
|
||||
w.Write([]byte(tmpl_register_vars.CurrentUser.Link))
|
||||
w.Write(menu_4)
|
||||
w.Write([]byte(tmpl_register_vars.CurrentUser.Session))
|
||||
w.Write(menu_5)
|
||||
} else {
|
||||
w.Write(menu_6)
|
||||
}
|
||||
w.Write(menu_7)
|
||||
w.Write(header_17)
|
||||
if tmpl_register_vars.Header.Widgets.RightSidebar != "" {
|
||||
w.Write(header_18)
|
||||
}
|
||||
w.Write(header_19)
|
||||
if len(tmpl_register_vars.Header.NoticeList) != 0 {
|
||||
for _, item := range tmpl_register_vars.Header.NoticeList {
|
||||
w.Write(header_20)
|
||||
w.Write([]byte(item))
|
||||
w.Write(header_21)
|
||||
}
|
||||
}
|
||||
w.Write(register_0)
|
||||
w.Write(phrases[0])
|
||||
w.Write(register_1)
|
||||
w.Write(phrases[1])
|
||||
w.Write(register_2)
|
||||
w.Write(phrases[2])
|
||||
w.Write(register_3)
|
||||
w.Write(phrases[3])
|
||||
w.Write(register_4)
|
||||
w.Write(phrases[4])
|
||||
w.Write(register_5)
|
||||
w.Write(phrases[5])
|
||||
w.Write(register_6)
|
||||
w.Write(phrases[6])
|
||||
w.Write(register_7)
|
||||
w.Write(footer_0)
|
||||
w.Write([]byte(common.BuildWidget("footer",tmpl_register_vars.Header)))
|
||||
w.Write(footer_1)
|
||||
if len(tmpl_register_vars.Header.Themes) != 0 {
|
||||
for _, item := range tmpl_register_vars.Header.Themes {
|
||||
if !item.HideFromThemes {
|
||||
w.Write(footer_2)
|
||||
w.Write([]byte(item.Name))
|
||||
w.Write(footer_3)
|
||||
if tmpl_register_vars.Header.Theme.Name == item.Name {
|
||||
w.Write(footer_4)
|
||||
}
|
||||
w.Write(footer_5)
|
||||
w.Write([]byte(item.FriendlyName))
|
||||
w.Write(footer_6)
|
||||
}
|
||||
}
|
||||
}
|
||||
w.Write(footer_7)
|
||||
w.Write([]byte(common.BuildWidget("rightSidebar",tmpl_register_vars.Header)))
|
||||
w.Write(footer_8)
|
||||
return nil
|
||||
}
|
@ -7,6 +7,8 @@ import "net/http"
|
||||
import "./common"
|
||||
import "strconv"
|
||||
|
||||
var topic_Tmpl_Phrase_ID int
|
||||
|
||||
// nolint
|
||||
func init() {
|
||||
common.Template_topic_handle = Template_topic
|
||||
|
@ -7,6 +7,8 @@ import "net/http"
|
||||
import "./common"
|
||||
import "strconv"
|
||||
|
||||
var topic_alt_Tmpl_Phrase_ID int
|
||||
|
||||
// nolint
|
||||
func init() {
|
||||
common.Template_topic_alt_handle = Template_topic_alt
|
||||
|
@ -7,6 +7,8 @@ import "net/http"
|
||||
import "./common"
|
||||
import "strconv"
|
||||
|
||||
var topics_Tmpl_Phrase_ID int
|
||||
|
||||
// nolint
|
||||
func init() {
|
||||
common.Template_topics_handle = Template_topics
|
||||
@ -86,165 +88,166 @@ w.Write(topics_1)
|
||||
}
|
||||
w.Write(topics_2)
|
||||
if tmpl_topics_vars.CurrentUser.ID != 0 {
|
||||
if len(tmpl_topics_vars.ForumList) != 0 {
|
||||
w.Write(topics_3)
|
||||
if len(tmpl_topics_vars.ForumList) != 0 {
|
||||
w.Write(topics_4)
|
||||
} else {
|
||||
w.Write(topics_5)
|
||||
}
|
||||
} else {
|
||||
w.Write(topics_6)
|
||||
}
|
||||
w.Write(topics_7)
|
||||
if tmpl_topics_vars.CurrentUser.ID != 0 {
|
||||
}
|
||||
w.Write(topics_8)
|
||||
if len(tmpl_topics_vars.ForumList) != 0 {
|
||||
if tmpl_topics_vars.CurrentUser.ID != 0 {
|
||||
w.Write(topics_9)
|
||||
w.Write([]byte(tmpl_topics_vars.CurrentUser.Session))
|
||||
if len(tmpl_topics_vars.ForumList) != 0 {
|
||||
w.Write(topics_10)
|
||||
w.Write([]byte(tmpl_topics_vars.CurrentUser.Session))
|
||||
w.Write(topics_11)
|
||||
if len(tmpl_topics_vars.ForumList) != 0 {
|
||||
for _, item := range tmpl_topics_vars.ForumList {
|
||||
w.Write(topics_11)
|
||||
w.Write([]byte(strconv.Itoa(item.ID)))
|
||||
w.Write(topics_12)
|
||||
w.Write([]byte(strconv.Itoa(item.ID)))
|
||||
w.Write(topics_13)
|
||||
w.Write([]byte(item.Name))
|
||||
w.Write([]byte(strconv.Itoa(item.ID)))
|
||||
w.Write(topics_14)
|
||||
}
|
||||
}
|
||||
w.Write([]byte(item.Name))
|
||||
w.Write(topics_15)
|
||||
w.Write([]byte(tmpl_topics_vars.CurrentUser.Session))
|
||||
}
|
||||
}
|
||||
w.Write(topics_16)
|
||||
w.Write([]byte(tmpl_topics_vars.CurrentUser.Avatar))
|
||||
w.Write([]byte(tmpl_topics_vars.CurrentUser.Session))
|
||||
w.Write(topics_17)
|
||||
w.Write([]byte(tmpl_topics_vars.CurrentUser.Avatar))
|
||||
w.Write(topics_18)
|
||||
if len(tmpl_topics_vars.ForumList) != 0 {
|
||||
for _, item := range tmpl_topics_vars.ForumList {
|
||||
w.Write(topics_18)
|
||||
if item.ID == tmpl_topics_vars.DefaultForum {
|
||||
w.Write(topics_19)
|
||||
}
|
||||
if item.ID == tmpl_topics_vars.DefaultForum {
|
||||
w.Write(topics_20)
|
||||
w.Write([]byte(strconv.Itoa(item.ID)))
|
||||
}
|
||||
w.Write(topics_21)
|
||||
w.Write([]byte(item.Name))
|
||||
w.Write([]byte(strconv.Itoa(item.ID)))
|
||||
w.Write(topics_22)
|
||||
}
|
||||
}
|
||||
w.Write([]byte(item.Name))
|
||||
w.Write(topics_23)
|
||||
if tmpl_topics_vars.CurrentUser.Perms.UploadFiles {
|
||||
w.Write(topics_24)
|
||||
}
|
||||
}
|
||||
w.Write(topics_24)
|
||||
if tmpl_topics_vars.CurrentUser.Perms.UploadFiles {
|
||||
w.Write(topics_25)
|
||||
}
|
||||
}
|
||||
w.Write(topics_26)
|
||||
}
|
||||
}
|
||||
w.Write(topics_27)
|
||||
if len(tmpl_topics_vars.TopicList) != 0 {
|
||||
for _, item := range tmpl_topics_vars.TopicList {
|
||||
w.Write(topics_27)
|
||||
w.Write([]byte(strconv.Itoa(item.ID)))
|
||||
w.Write(topics_28)
|
||||
if item.Sticky {
|
||||
w.Write([]byte(strconv.Itoa(item.ID)))
|
||||
w.Write(topics_29)
|
||||
if item.Sticky {
|
||||
w.Write(topics_30)
|
||||
} else {
|
||||
if item.IsClosed {
|
||||
w.Write(topics_30)
|
||||
}
|
||||
}
|
||||
w.Write(topics_31)
|
||||
w.Write([]byte(item.Creator.Link))
|
||||
}
|
||||
}
|
||||
w.Write(topics_32)
|
||||
w.Write([]byte(item.Creator.Avatar))
|
||||
w.Write([]byte(item.Creator.Link))
|
||||
w.Write(topics_33)
|
||||
w.Write([]byte(item.Creator.Name))
|
||||
w.Write([]byte(item.Creator.Avatar))
|
||||
w.Write(topics_34)
|
||||
w.Write([]byte(item.Creator.Name))
|
||||
w.Write(topics_35)
|
||||
w.Write([]byte(item.Link))
|
||||
w.Write(topics_36)
|
||||
w.Write([]byte(item.Title))
|
||||
w.Write(topics_37)
|
||||
if item.ForumName != "" {
|
||||
w.Write(topics_38)
|
||||
w.Write([]byte(item.ForumLink))
|
||||
w.Write(topics_39)
|
||||
w.Write([]byte(item.ForumName))
|
||||
w.Write(topics_40)
|
||||
}
|
||||
w.Write(topics_41)
|
||||
w.Write([]byte(item.Creator.Link))
|
||||
w.Write(topics_42)
|
||||
w.Write([]byte(item.Creator.Name))
|
||||
w.Write(topics_43)
|
||||
if item.IsClosed {
|
||||
w.Write(topics_44)
|
||||
w.Write(topics_36)
|
||||
w.Write([]byte(item.Link))
|
||||
w.Write(topics_37)
|
||||
w.Write([]byte(item.Title))
|
||||
w.Write(topics_38)
|
||||
if item.ForumName != "" {
|
||||
w.Write(topics_39)
|
||||
w.Write([]byte(item.ForumLink))
|
||||
w.Write(topics_40)
|
||||
w.Write([]byte(item.ForumName))
|
||||
w.Write(topics_41)
|
||||
}
|
||||
if item.Sticky {
|
||||
w.Write(topics_42)
|
||||
w.Write([]byte(item.Creator.Link))
|
||||
w.Write(topics_43)
|
||||
w.Write([]byte(item.Creator.Name))
|
||||
w.Write(topics_44)
|
||||
if item.IsClosed {
|
||||
w.Write(topics_45)
|
||||
}
|
||||
w.Write(topics_46)
|
||||
w.Write([]byte(strconv.Itoa(item.PostCount)))
|
||||
w.Write(topics_47)
|
||||
w.Write([]byte(strconv.Itoa(item.LikeCount)))
|
||||
w.Write(topics_48)
|
||||
if item.Sticky {
|
||||
w.Write(topics_46)
|
||||
}
|
||||
w.Write(topics_47)
|
||||
w.Write([]byte(strconv.Itoa(item.PostCount)))
|
||||
w.Write(topics_48)
|
||||
w.Write([]byte(strconv.Itoa(item.LikeCount)))
|
||||
w.Write(topics_49)
|
||||
if item.Sticky {
|
||||
w.Write(topics_50)
|
||||
} else {
|
||||
if item.IsClosed {
|
||||
w.Write(topics_50)
|
||||
}
|
||||
}
|
||||
w.Write(topics_51)
|
||||
w.Write([]byte(item.LastUser.Link))
|
||||
}
|
||||
}
|
||||
w.Write(topics_52)
|
||||
w.Write([]byte(item.LastUser.Avatar))
|
||||
w.Write([]byte(item.LastUser.Link))
|
||||
w.Write(topics_53)
|
||||
w.Write([]byte(item.LastUser.Name))
|
||||
w.Write([]byte(item.LastUser.Avatar))
|
||||
w.Write(topics_54)
|
||||
w.Write([]byte(item.LastUser.Name))
|
||||
w.Write(topics_55)
|
||||
w.Write([]byte(item.LastUser.Link))
|
||||
w.Write(topics_56)
|
||||
w.Write([]byte(item.LastUser.Name))
|
||||
w.Write(topics_56)
|
||||
w.Write([]byte(item.LastUser.Link))
|
||||
w.Write(topics_57)
|
||||
w.Write([]byte(item.RelativeLastReplyAt))
|
||||
w.Write([]byte(item.LastUser.Name))
|
||||
w.Write(topics_58)
|
||||
w.Write([]byte(item.RelativeLastReplyAt))
|
||||
w.Write(topics_59)
|
||||
}
|
||||
} else {
|
||||
w.Write(topics_59)
|
||||
if tmpl_topics_vars.CurrentUser.Perms.CreateTopic {
|
||||
w.Write(topics_60)
|
||||
}
|
||||
if tmpl_topics_vars.CurrentUser.Perms.CreateTopic {
|
||||
w.Write(topics_61)
|
||||
}
|
||||
w.Write(topics_62)
|
||||
if tmpl_topics_vars.LastPage > 1 {
|
||||
}
|
||||
w.Write(topics_63)
|
||||
if tmpl_topics_vars.Page > 1 {
|
||||
if tmpl_topics_vars.LastPage > 1 {
|
||||
w.Write(topics_64)
|
||||
w.Write([]byte(strconv.Itoa(tmpl_topics_vars.Page - 1)))
|
||||
if tmpl_topics_vars.Page > 1 {
|
||||
w.Write(topics_65)
|
||||
w.Write([]byte(strconv.Itoa(tmpl_topics_vars.Page - 1)))
|
||||
w.Write(topics_66)
|
||||
w.Write([]byte(strconv.Itoa(tmpl_topics_vars.Page - 1)))
|
||||
w.Write(topics_67)
|
||||
}
|
||||
if len(tmpl_topics_vars.PageList) != 0 {
|
||||
for _, item := range tmpl_topics_vars.PageList {
|
||||
w.Write(topics_67)
|
||||
w.Write([]byte(strconv.Itoa(item)))
|
||||
w.Write(topics_68)
|
||||
w.Write([]byte(strconv.Itoa(item)))
|
||||
w.Write(topics_69)
|
||||
w.Write([]byte(strconv.Itoa(item)))
|
||||
w.Write(topics_70)
|
||||
}
|
||||
}
|
||||
if tmpl_topics_vars.LastPage != tmpl_topics_vars.Page {
|
||||
w.Write(topics_70)
|
||||
w.Write([]byte(strconv.Itoa(tmpl_topics_vars.Page + 1)))
|
||||
w.Write(topics_71)
|
||||
w.Write([]byte(strconv.Itoa(tmpl_topics_vars.Page + 1)))
|
||||
w.Write(topics_72)
|
||||
}
|
||||
w.Write([]byte(strconv.Itoa(tmpl_topics_vars.Page + 1)))
|
||||
w.Write(topics_73)
|
||||
}
|
||||
w.Write(topics_74)
|
||||
}
|
||||
w.Write(topics_75)
|
||||
w.Write(footer_0)
|
||||
w.Write([]byte(common.BuildWidget("footer",tmpl_topics_vars.Header)))
|
||||
w.Write(footer_1)
|
||||
|
@ -1,5 +1,5 @@
|
||||
{{template "header.html" . }}
|
||||
<div class="colstack account">
|
||||
<div id="account_edit_password" class="colstack account">
|
||||
{{template "account_menu.html" . }}
|
||||
<main class="colstack_right">
|
||||
<div class="colstack_item colstack_head rowhead">
|
||||
|
@ -1,11 +1,11 @@
|
||||
{{template "header.html" . }}
|
||||
<div class="colstack account">
|
||||
<div id="account_edit_avatar" class="colstack account">
|
||||
{{template "account_menu.html" . }}
|
||||
<main class="colstack_right">
|
||||
<div class="colstack_item colstack_head rowhead">
|
||||
<div class="rowitem"><h1>Edit Avatar</h1></div>
|
||||
</div>
|
||||
<div class="colstack_item">
|
||||
<div class="colstack_item avatar_box">
|
||||
<div class="rowitem"><img src="{{.CurrentUser.Avatar}}" height="128px" max-width="128px" /></div>
|
||||
</div>
|
||||
<div class="colstack_item the_form">
|
||||
|
@ -1,5 +1,5 @@
|
||||
{{template "header.html" . }}
|
||||
<div class="colstack account account_emails">
|
||||
<div id="account_edit_emails" class="colstack account account_emails">
|
||||
{{template "account_menu.html" . }}
|
||||
<main class="colstack_right">
|
||||
<div class="colstack_item colstack_head rowhead">
|
||||
|
@ -1,5 +1,5 @@
|
||||
{{template "header.html" . }}
|
||||
<div class="colstack account">
|
||||
<div id="account_edit_username" class="colstack account">
|
||||
{{template "account_menu.html" . }}
|
||||
<main class="colstack_right">
|
||||
<div class="colstack_item colstack_head rowhead">
|
||||
|
@ -1,7 +1,7 @@
|
||||
{{template "header.html" . }}
|
||||
<main>
|
||||
<div class="rowblock rowhead">
|
||||
<div class="rowitem"><h1>An error has occured</h1></div>
|
||||
<div class="rowitem"><h1>{{lang "error_head"}}</h1></div>
|
||||
</div>
|
||||
<div class="rowblock">
|
||||
<div class="rowitem passive rowmsg">{{.Something}}</div>
|
||||
|
@ -3,20 +3,22 @@
|
||||
{{if gt .Page 1}}<div id="prevFloat" class="prev_button"><a class="prev_link" aria-label="Go to the previous page" rel="prev" href="/forum/{{.Forum.ID}}?page={{subtract .Page 1}}"><</a></div>{{end}}
|
||||
{{if ne .LastPage .Page}}<div id="nextFloat" class="next_button"><a class="next_link" aria-label="Go to the next page" rel="next" href="/forum/{{.Forum.ID}}?page={{add .Page 1}}">></a></div>{{end}}
|
||||
|
||||
<main itemscope itemtype="http://schema.org/ItemList">
|
||||
<div id="forum_head_block" class="rowblock rowhead topic_list_title_block">
|
||||
<div class="rowitem forum_title{{if ne .CurrentUser.ID 0}} has_opt{{end}}">
|
||||
<main id="forumItemList" itemscope itemtype="http://schema.org/ItemList">
|
||||
<div id="forum_head_block" class="rowblock rowhead topic_list_title_block{{if ne .CurrentUser.ID 0}} has_opt{{end}}">
|
||||
<div class="rowitem forum_title">
|
||||
<h1 itemprop="name">{{.Title}}</h1>
|
||||
</div>
|
||||
{{if ne .CurrentUser.ID 0}}
|
||||
{{if .CurrentUser.Perms.CreateTopic}}
|
||||
<div class="pre_opt auto_hide"></div>
|
||||
<div class="opt create_topic_opt" title="Create Topic" aria-label="Create a topic"><a class="create_topic_link" href="/topics/create/{{.Forum.ID}}"></a></div>
|
||||
{{/** TODO: Add a permissions check for this **/}}
|
||||
<div class="opt mod_opt" title="Moderate">
|
||||
<a class="moderate_link" href="#" aria-label="Moderate Posts"></a>
|
||||
<div class="optbox">
|
||||
{{if .CurrentUser.Perms.CreateTopic}}
|
||||
<div class="pre_opt auto_hide"></div>
|
||||
<div class="opt create_topic_opt" title="Create Topic" aria-label="Create a topic"><a class="create_topic_link" href="/topics/create/{{.Forum.ID}}"></a></div>
|
||||
{{/** TODO: Add a permissions check for this **/}}
|
||||
<div class="opt mod_opt" title="Moderate">
|
||||
<a class="moderate_link" href="#" aria-label="Moderate Posts"></a>
|
||||
</div>
|
||||
{{else}}<div class="opt locked_opt" title="You don't have the permissions needed to create a topic" aria-label="You don't have the permissions needed to make a topic in this forum"><a></a></div>{{end}}
|
||||
</div>
|
||||
{{else}}<div class="opt locked_opt" title="You don't have the permissions needed to create a topic" aria-label="You don't have the permissions needed to make a topic in this forum"><a></a></div>{{end}}
|
||||
<div style="clear: both;"></div>
|
||||
{{end}}
|
||||
</div>
|
||||
|
@ -1,5 +1,5 @@
|
||||
{{template "header.html" . }}
|
||||
<main itemscope itemtype="http://schema.org/ItemList">
|
||||
<main id="forumsItemList" itemscope itemtype="http://schema.org/ItemList">
|
||||
|
||||
<div class="rowblock opthead">
|
||||
<div class="rowitem"><h1 itemprop="name">Forums</h1></div>
|
||||
|
@ -24,4 +24,6 @@
|
||||
<div class="container">
|
||||
{{template "menu.html" .}}
|
||||
<div id="back"><div id="main" {{if .Header.Widgets.RightSidebar}}class="shrink_main"{{end}}>
|
||||
{{range .Header.NoticeList}}<div class="alert">{{.}}</div>{{end}}
|
||||
{{range .Header.NoticeList}}<div class="alertbox">
|
||||
<div class="alert">{{.}}</div>
|
||||
</div>{{end}}
|
||||
|
@ -2,7 +2,7 @@
|
||||
<main id="ip_search_container">
|
||||
<div class="rowblock rowhead">
|
||||
<div class="rowitem">
|
||||
<h1>IP Search</h1>
|
||||
<h1>{{lang "ip_search_head"}}</h1>
|
||||
</div>
|
||||
</div>
|
||||
<form action="/users/ips/" method="get" id="ip-search-form"></form>
|
||||
@ -18,7 +18,7 @@
|
||||
<img src="{{.Avatar}}" class="bgsub" alt="{{.Name}}'s Avatar" />
|
||||
<a class="rowTitle" href="{{.Link}}">{{.Name}}</a>
|
||||
</div>
|
||||
{{else}}<div class="rowitem">No users found.</div>{{end}}
|
||||
{{else}}<div class="rowitem rowmsg">{{lang "ip_search_no_users"}}</div>{{end}}
|
||||
</div>
|
||||
{{end}}
|
||||
</main>
|
@ -1,21 +1,21 @@
|
||||
{{template "header.html" . }}
|
||||
<main id="login_page">
|
||||
<div class="rowblock rowhead">
|
||||
<div class="rowitem"><h1>Login</h1></div>
|
||||
<div class="rowitem"><h1>{{lang "login_head"}}</h1></div>
|
||||
</div>
|
||||
<div class="rowblock">
|
||||
<form action="/accounts/login/submit/" method="post">
|
||||
<div class="formrow login_name_row">
|
||||
<div class="formitem formlabel"><a>Account Name</a></div>
|
||||
<div class="formitem"><input name="username" type="text" placeholder="Account Name" required /></div>
|
||||
<div class="formitem formlabel"><a>{{lang "login_account_name"}}</a></div>
|
||||
<div class="formitem"><input name="username" type="text" placeholder="{{lang "login_account_name"}}" required /></div>
|
||||
</div>
|
||||
<div class="formrow login_password_row">
|
||||
<div class="formitem formlabel"><a>Password</a></div>
|
||||
<div class="formitem formlabel"><a>{{lang "login_account_password"}}</a></div>
|
||||
<div class="formitem"><input name="password" type="password" autocomplete="current-password" placeholder="*****" required /></div>
|
||||
</div>
|
||||
<div class="formrow login_button_row">
|
||||
<div class="formitem"><button name="login-button" class="formbutton">Login</button></div>
|
||||
<div class="formitem dont_have_account">Don't have an account?</div>
|
||||
<div class="formitem"><button name="login-button" class="formbutton">{{lang "login_submit_button"}}</button></div>
|
||||
<div class="formitem dont_have_account">{{lang "login_no_account"}}</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
@ -50,6 +50,9 @@
|
||||
<div class="rowitem passive submenu">
|
||||
<a href="/panel/analytics/systems/">Systems</a>
|
||||
</div>
|
||||
<div class="rowitem passive submenu">
|
||||
<a href="/panel/analytics/langs/">Languages</a>
|
||||
</div>
|
||||
<div class="rowitem passive submenu">
|
||||
<a href="/panel/analytics/referrers/">Referrers</a>
|
||||
</div>
|
||||
|
34
templates/panel_analytics_lang_views.html
Normal file
34
templates/panel_analytics_lang_views.html
Normal file
@ -0,0 +1,34 @@
|
||||
{{template "header.html" . }}
|
||||
<div class="colstack panel_stack">
|
||||
{{template "panel-menu.html" . }}
|
||||
<main id="panel_analytics_right" class="colstack_right">
|
||||
<form id="timeRangeForm" name="timeRangeForm" action="/panel/analytics/lang/{{.Agent}}" method="get">
|
||||
<div class="colstack_item colstack_head">
|
||||
<div class="rowitem">
|
||||
<a>{{.FriendlyAgent}} Views</a>
|
||||
<select class="timeRangeSelector to_right" name="timeRange">
|
||||
<option val="one-month"{{if eq .TimeRange "one-month"}} selected{{end}}>1 month</option>
|
||||
<option val="one-week"{{if eq .TimeRange "one-week"}} selected{{end}}>1 week</option>
|
||||
<option val="two-days"{{if eq .TimeRange "two-days"}} selected{{end}}>2 days</option>
|
||||
<option val="one-day"{{if eq .TimeRange "one-day"}} selected{{end}}>1 day</option>
|
||||
<option val="twelve-hours"{{if eq .TimeRange "twelve-hours"}} selected{{end}}>12 hours</option>
|
||||
<option val="six-hours"{{if eq .TimeRange "six-hours"}} selected{{end}}>6 hours</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
<div id="panel_analytics_langs" class="colstack_graph_holder">
|
||||
<div class="ct_chart"></div>
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
<script>
|
||||
let rawLabels = [{{range .PrimaryGraph.Labels}}
|
||||
{{.}},{{end}}
|
||||
];
|
||||
let seriesData = [{{range .PrimaryGraph.Series}}
|
||||
{{.}},{{end}}
|
||||
];
|
||||
buildStatsChart(rawLabels, seriesData, "{{.TimeRange}}");
|
||||
</script>
|
||||
{{template "footer.html" . }}
|
30
templates/panel_analytics_langs.html
Normal file
30
templates/panel_analytics_langs.html
Normal file
@ -0,0 +1,30 @@
|
||||
{{template "header.html" . }}
|
||||
<div class="colstack panel_stack">
|
||||
{{template "panel-menu.html" . }}
|
||||
<main id="panel_analytics_right" class="colstack_right">
|
||||
<form id="timeRangeForm" name="timeRangeForm" action="/panel/analytics/langs/" method="get">
|
||||
<div class="colstack_item colstack_head">
|
||||
<div class="rowitem">
|
||||
<a>Languages</a>
|
||||
<select class="timeRangeSelector to_right" name="timeRange">
|
||||
<option val="one-month"{{if eq .TimeRange "one-month"}} selected{{end}}>1 month</option>
|
||||
<option val="one-week"{{if eq .TimeRange "one-week"}} selected{{end}}>1 week</option>
|
||||
<option val="two-days"{{if eq .TimeRange "two-days"}} selected{{end}}>2 days</option>
|
||||
<option val="one-day"{{if eq .TimeRange "one-day"}} selected{{end}}>1 day</option>
|
||||
<option val="twelve-hours"{{if eq .TimeRange "twelve-hours"}} selected{{end}}>12 hours</option>
|
||||
<option val="six-hours"{{if eq .TimeRange "six-hours"}} selected{{end}}>6 hours</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
<div id="panel_analytics_langs" class="colstack_item rowlist">
|
||||
{{range .ItemList}}
|
||||
<div class="rowitem panel_compactrow editable_parent">
|
||||
<a href="/panel/analytics/lang/{{.Agent}}" class="panel_upshift">{{.FriendlyAgent}}</a>
|
||||
<span class="panel_compacttext to_right">{{.Count}} views</span>
|
||||
</div>
|
||||
{{else}}<div class="rowitem passive rowmsg">No language could be found in the selected time range</div>{{end}}
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
{{template "footer.html" . }}
|
@ -1,28 +1,28 @@
|
||||
{{template "header.html" . }}
|
||||
<main id="register_page">
|
||||
<div class="rowblock rowhead">
|
||||
<div class="rowitem"><h1>Create Account</h1></div>
|
||||
<div class="rowitem"><h1>{{lang "register_head"}}</h1></div>
|
||||
</div>
|
||||
<div class="rowblock">
|
||||
<form action="/accounts/create/submit/" method="post">
|
||||
<div class="formrow">
|
||||
<div class="formitem formlabel"><a id="username_label">Account Name</a></div>
|
||||
<div class="formitem"><input name="username" type="text" placeholder="Account Name" aria-labelledby="username_label" required /></div>
|
||||
<div class="formitem formlabel"><a id="username_label">{{lang "register_account_name"}}</a></div>
|
||||
<div class="formitem"><input name="username" type="text" placeholder="{{lang "register_account_name"}}" aria-labelledby="username_label" required /></div>
|
||||
</div>
|
||||
<div class="formrow">
|
||||
<div class="formitem formlabel"><a id="email_label">Email</a></div>
|
||||
<div class="formitem formlabel"><a id="email_label">{{lang "register_account_email"}}</a></div>
|
||||
<div class="formitem"><input name="email" type="email" placeholder="joe.doe@example.com" aria-labelledby="email_label" required /></div>
|
||||
</div>
|
||||
<div class="formrow">
|
||||
<div class="formitem formlabel"><a id="password_label">Password</a></div>
|
||||
<div class="formitem formlabel"><a id="password_label">{{lang "register_account_password"}}</a></div>
|
||||
<div class="formitem"><input name="password" type="password" autocomplete="new-password" placeholder="*****" aria-labelledby="password_label" required /></div>
|
||||
</div>
|
||||
<div class="formrow">
|
||||
<div class="formitem formlabel"><a id="confirm_password_label">Confirm Password</a></div>
|
||||
<div class="formitem formlabel"><a id="confirm_password_label">{{lang "register_account_confirm_password"}}</a></div>
|
||||
<div class="formitem"><input name="confirm_password" type="password" placeholder="*****" aria-labelledby="confirm_password_label" required /></div>
|
||||
</div>
|
||||
<div class="formrow">
|
||||
<div class="formitem"><button name="register-button" class="formbutton">Create Account</button></div>
|
||||
<div class="formitem"><button name="register-button" class="formbutton">{{lang "register_submit_button"}}</button></div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
@ -1,17 +1,19 @@
|
||||
{{template "header.html" . }}
|
||||
<main itemscope itemtype="http://schema.org/ItemList">
|
||||
<main id="topicsItemList" itemscope itemtype="http://schema.org/ItemList">
|
||||
|
||||
<div class="rowblock rowhead topic_list_title_block">
|
||||
<div class="rowitem topic_list_title{{if ne .CurrentUser.ID 0}} has_opt{{end}}"><h1 itemprop="name">All Topics</h1></div>
|
||||
<div class="rowblock rowhead topic_list_title_block{{if ne .CurrentUser.ID 0}} has_opt{{end}}">
|
||||
<div class="rowitem topic_list_title"><h1 itemprop="name">All Topics</h1></div>
|
||||
{{if ne .CurrentUser.ID 0}}
|
||||
<div class="optbox">
|
||||
{{if .ForumList}}
|
||||
<div class="pre_opt auto_hide"></div>
|
||||
<div class="opt create_topic_opt" title="Create Topic" aria-label="Create a topic"><a class="create_topic_link" href="/topics/create/"></a></div>
|
||||
{{/** TODO: Add a permissions check for this **/}}
|
||||
<div class="opt mod_opt" title="Moderate">
|
||||
<a class="moderate_link" href="#" aria-label="Moderate Posts"></a>
|
||||
<div class="pre_opt auto_hide"></div>
|
||||
<div class="opt create_topic_opt" title="Create Topic" aria-label="Create a topic"><a class="create_topic_link" href="/topics/create/"></a></div>
|
||||
{{/** TODO: Add a permissions check for this **/}}
|
||||
<div class="opt mod_opt" title="Moderate">
|
||||
<a class="moderate_link" href="#" aria-label="Moderate Posts"></a>
|
||||
</div>
|
||||
{{else}}<div class="opt locked_opt" title="You don't have the permissions needed to create a topic" aria-label="You don't have the permissions needed to make a topic anywhere"><a></a></div>{{end}}
|
||||
</div>
|
||||
{{else}}<div class="opt locked_opt" title="You don't have the permissions needed to create a topic" aria-label="You don't have the permissions needed to make a topic anywhere"><a></a></div>{{end}}
|
||||
<div style="clear: both;"></div>
|
||||
{{end}}
|
||||
</div>
|
||||
|
@ -1132,11 +1132,22 @@ textarea {
|
||||
margin-right: 12px;
|
||||
}
|
||||
.colstack_right .colstack_item:not(.rowlist):not(#profile_comments),
|
||||
#profile_comments .comment {
|
||||
#profile_comments .comment, .alert {
|
||||
border: 1px solid var(--element-border-color);
|
||||
border-bottom: 2px solid var(--element-border-color);
|
||||
background-color: var(--element-background-color);
|
||||
}
|
||||
.alert {
|
||||
padding: 12px;
|
||||
margin-top: -3px;
|
||||
margin-bottom: 8px;
|
||||
margin-left: 12px;
|
||||
margin-right: 12px;
|
||||
}
|
||||
.colstack_right .alert {
|
||||
margin-left: 16px;
|
||||
margin-right: 0px;
|
||||
}
|
||||
.colstack_right .colstack_item, .colstack_right .colstack_grid {
|
||||
margin-left: 16px;
|
||||
}
|
||||
@ -1268,6 +1279,10 @@ textarea {
|
||||
margin-left: auto;
|
||||
}
|
||||
|
||||
#account_edit_avatar .avatar_box {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
#create_topic_page .close_form, #create_topic_page .formlabel, #login_page .formlabel, #register_page .formlabel {
|
||||
display: none;
|
||||
}
|
||||
|
@ -1,3 +1,5 @@
|
||||
"use strict"
|
||||
|
||||
$(document).ready(function(){
|
||||
// Is there we way we can append instead? Maybe, an editor plugin?
|
||||
attachItemCallback = function(attachItem) {
|
||||
@ -33,7 +35,6 @@ $(document).ready(function(){
|
||||
// TODO: Refactor this to use `each` less
|
||||
$('.button_menu').click(function(){
|
||||
console.log(".button_menu");
|
||||
|
||||
// The outer container
|
||||
let buttonPane = newElement("div","button_menu_pane");
|
||||
let postItem = $(this).parents('.post_item');
|
||||
@ -86,6 +87,14 @@ $(document).ready(function(){
|
||||
|
||||
document.getElementById("back").appendChild(buttonPane);
|
||||
});
|
||||
|
||||
// Move the alerts under the first header
|
||||
let colSel = $(".colstack_right .colstack_head:first");
|
||||
if(colSel.length > 0) {
|
||||
$('.alert').insertAfter(colSel);
|
||||
} else {
|
||||
$('.alert').insertAfter(".rowhead:first");
|
||||
}
|
||||
});
|
||||
|
||||
function newElement(etype, eclass) {
|
||||
|
@ -160,11 +160,20 @@ a {
|
||||
color: var(--main-text-color);
|
||||
}
|
||||
|
||||
.alertbox {
|
||||
display: flex;
|
||||
}
|
||||
.alert {
|
||||
padding-bottom: 12px;
|
||||
background-color: var(--main-block-color);
|
||||
border-left: 4px solid hsl(21, 100%, 50%);
|
||||
padding: 12px;
|
||||
display: block;
|
||||
margin-top: 8px;
|
||||
margin-bottom: -3px;
|
||||
margin-left: 8px;
|
||||
margin-right: 8px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.rowblock {
|
||||
@ -652,25 +661,25 @@ input[type=checkbox]:checked + label.poll_option_label .sel {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
}
|
||||
|
||||
.has_opt {
|
||||
margin-right: 0px;
|
||||
display: inline-block;
|
||||
float: left;
|
||||
padding-right: 0px;
|
||||
}
|
||||
.rowhead .rowitem:not(.has_opt), .opthead .rowitem, .colstack_head .rowitem {
|
||||
.rowhead:not(.has_opt) .rowitem, .opthead .rowitem, .colstack_head .rowitem {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.opt {
|
||||
float: left;
|
||||
margin-top: 8px;
|
||||
height: 30.4px;
|
||||
.optbox {
|
||||
display: flex;
|
||||
padding-left: 5px;
|
||||
padding-top: 10.5px;
|
||||
margin-top: 7px;
|
||||
width: 100%;
|
||||
background-color: var(--main-block-color);
|
||||
padding-top: 11px;
|
||||
}
|
||||
.has_opt .rowitem {
|
||||
margin-right: 0px;
|
||||
display: inline-block;
|
||||
padding-right: 0px;
|
||||
margin-top: 7px;
|
||||
padding-left: 12px;
|
||||
padding-top: 12px;
|
||||
}
|
||||
.opt a {
|
||||
font-size: 11px;
|
||||
@ -702,7 +711,7 @@ input[type=checkbox]:checked + label.poll_option_label .sel {
|
||||
overflow: hidden;
|
||||
}
|
||||
.topic_left.topic_sticky .topic_inner_left {
|
||||
border-top: 4px solid hsl(51, 100%, 50%);
|
||||
border-top: 4px solid hsl(41, 100%, 50%);
|
||||
padding-left: 8px;
|
||||
padding-top: 8px;
|
||||
margin-top: 0px;
|
||||
|
@ -319,17 +319,23 @@ li a {
|
||||
color: hsl(0,0%,40%);
|
||||
}
|
||||
|
||||
.opthead { display: none; }
|
||||
.rowitem.has_opt {
|
||||
float: left;
|
||||
.opthead {
|
||||
display: none;
|
||||
}
|
||||
.has_opt {
|
||||
display: flex;
|
||||
}
|
||||
.has_opt .rowitem {
|
||||
display: flex;
|
||||
width: calc(100% - 50px);
|
||||
border-right: 1px solid #ccc;
|
||||
border-bottom: none;
|
||||
}
|
||||
.optbox {
|
||||
margin-left: auto;
|
||||
}
|
||||
.opt {
|
||||
float: left;
|
||||
font-size: 32px;
|
||||
height: 100%;
|
||||
background-color: hsl(0, 0%, 99%);
|
||||
width: 50px;
|
||||
text-align: center;
|
||||
|
@ -296,17 +296,21 @@ li a {
|
||||
.opthead {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.rowitem.has_opt {
|
||||
float: left;
|
||||
width: calc(100% - 50px);
|
||||
.topic_list_title_block {
|
||||
display: flex;
|
||||
}
|
||||
.has_opt {
|
||||
border-bottom: 1px solid hsl(0, 0%, 80%);
|
||||
}
|
||||
.has_opt .rowitem {
|
||||
border-right: 1px solid hsl(0, 0%, 80%);
|
||||
border-bottom: none;
|
||||
}
|
||||
.optbox {
|
||||
margin-left: auto;
|
||||
}
|
||||
.opt {
|
||||
float: left;
|
||||
font-size: 32px;
|
||||
height: 100%;
|
||||
background-color: white;
|
||||
width: 50px;
|
||||
text-align: center;
|
||||
@ -324,6 +328,13 @@ li a {
|
||||
.locked_opt:before {
|
||||
content: '🔒︎';
|
||||
}
|
||||
/*.mod_opt a.moderate_link:before {
|
||||
content: '🔨︎';
|
||||
}
|
||||
.mod_opt, .mod_opt a {
|
||||
color: rgb(120,120,120);
|
||||
text-decoration: none;
|
||||
}*/
|
||||
|
||||
.to_right {
|
||||
margin-left: auto;
|
||||
|
Loading…
Reference in New Issue
Block a user