/topics/ (the default index) is now over 25 times faster than before.
Change the length of the ticks for referrer tracking. Fixed a bug in Shadow and Tempra Simple where the polls would appear when they shouldn't. Added styling to Shadow for Control Panel Sub-menus. Fixed the ridiculously wide attachment images on Cosora. The analytics pages should no longer be treated as the dashboard style-wise. The installer now works again. Tests now run again, as do the benchmarks. Refactored the topic list logic, and moved the route portion into the routes package. Fixed an inverted comparison in the referrer logic. Added friendly text for Mac operating systems to the english language pack. Removed two obsolete prepared statements. Fixed three race conditions in the database adapters. Potentially sped up the topic page by 20% Added several new benchmarks and refactored the others. Removed a redundant prepared statement in user.go Added reviseID as a primary key to the revisions table. The user_count column was added to the users_groups table, but it's unknown if it will stay.
This commit is contained in:
parent
1ac47bfdfd
commit
fa065bc584
|
@ -24,8 +24,6 @@ Always use strict mode.
|
|||
|
||||
Don't worry about ES5, we're targetting modern browsers. If we decide to backport code to older browsers, then we'll transpile the files.
|
||||
|
||||
Don't use await until Edge gets around to implementing it.
|
||||
|
||||
To keep consistency with Go code, variables must be camelCase.
|
||||
|
||||
# JSON
|
||||
|
|
|
@ -1,9 +1,11 @@
|
|||
package common
|
||||
|
||||
import "log"
|
||||
import "sync"
|
||||
import "net/http"
|
||||
import "runtime/debug"
|
||||
import (
|
||||
"log"
|
||||
"net/http"
|
||||
"runtime/debug"
|
||||
"sync"
|
||||
)
|
||||
|
||||
// TODO: Use the error_buffer variable to construct the system log in the Control Panel. Should we log errors caused by users too? Or just collect statistics on those or do nothing? Intercept recover()? Could we intercept the logger instead here? We might get too much information, if we intercept the logger, maybe make it part of the Debug page?
|
||||
// ? - Should we pass HeaderVars / HeaderLite rather than forcing the errors to pull the global HeaderVars instance?
|
||||
|
@ -120,6 +122,7 @@ func SilentInternalErrorXML(err error, w http.ResponseWriter, r *http.Request) R
|
|||
}
|
||||
|
||||
func PreError(errmsg string, w http.ResponseWriter, r *http.Request) RouteError {
|
||||
//LogError(errors.New(errmsg))
|
||||
w.WriteHeader(500)
|
||||
pi := Page{"Error", GuestUser, DefaultHeaderVar(), tList, errmsg}
|
||||
handleErrorTemplate(w, r, pi)
|
||||
|
@ -141,6 +144,7 @@ func PreErrorJSQ(errmsg string, w http.ResponseWriter, r *http.Request, isJs boo
|
|||
|
||||
// LocalError is an error shown to the end-user when something goes wrong and it's not the software's fault
|
||||
func LocalError(errmsg string, w http.ResponseWriter, r *http.Request, user User) RouteError {
|
||||
//LogError(errors.New(errmsg))
|
||||
w.WriteHeader(500)
|
||||
pi := Page{"Local Error", user, DefaultHeaderVar(), tList, errmsg}
|
||||
handleErrorTemplate(w, r, pi)
|
||||
|
@ -277,6 +281,7 @@ func CustomErrorJS(errmsg string, errcode int, w http.ResponseWriter, r *http.Re
|
|||
}
|
||||
|
||||
func handleErrorTemplate(w http.ResponseWriter, r *http.Request, pi Page) {
|
||||
//LogError(errors.New("error happened"))
|
||||
// TODO: What to do about this hook?
|
||||
if PreRenderHooks["pre_render_error"] != nil {
|
||||
if RunPreRenderHook("pre_render_error", w, r, &pi.CurrentUser, &pi) {
|
||||
|
|
|
@ -28,6 +28,7 @@ type Group struct {
|
|||
PluginPermsText []byte
|
||||
Forums []*ForumPerms
|
||||
CanSee []int // The IDs of the forums this group can see
|
||||
UserCount int // ! Might be temporary as I might want to lean on the database instead for this
|
||||
}
|
||||
|
||||
type GroupStmts struct {
|
||||
|
|
|
@ -39,6 +39,7 @@ type MemoryGroupStore struct {
|
|||
getAll *sql.Stmt
|
||||
get *sql.Stmt
|
||||
count *sql.Stmt
|
||||
userCount *sql.Stmt
|
||||
|
||||
sync.RWMutex
|
||||
}
|
||||
|
@ -51,6 +52,7 @@ func NewMemoryGroupStore() (*MemoryGroupStore, error) {
|
|||
getAll: acc.Select("users_groups").Columns("gid, name, permissions, plugin_perms, is_mod, is_admin, is_banned, tag").Prepare(),
|
||||
get: acc.Select("users_groups").Columns("name, permissions, plugin_perms, is_mod, is_admin, is_banned, tag").Where("gid = ?").Prepare(),
|
||||
count: acc.Count("users_groups").Prepare(),
|
||||
userCount: acc.Count("users").Where("group = ?").Prepare(),
|
||||
}, acc.FirstError()
|
||||
}
|
||||
|
||||
|
@ -177,6 +179,11 @@ func (mgs *MemoryGroupStore) initGroup(group *Group) error {
|
|||
if group.IsAdmin || group.IsMod {
|
||||
group.IsBanned = false
|
||||
}
|
||||
|
||||
err = mgs.userCount.QueryRow(group.ID).Scan(&group.UserCount)
|
||||
if err != sql.ErrNoRows {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -274,7 +281,7 @@ func (mgs *MemoryGroupStore) Create(name string, tag string, isAdmin bool, isMod
|
|||
}
|
||||
|
||||
mgs.Lock()
|
||||
mgs.groups[gid] = &Group{gid, name, isMod, isAdmin, isBanned, tag, perms, []byte(permstr), pluginPerms, pluginPermsBytes, blankForums, blankIntList}
|
||||
mgs.groups[gid] = &Group{gid, name, isMod, isAdmin, isBanned, tag, perms, []byte(permstr), pluginPerms, pluginPermsBytes, blankForums, blankIntList, 0}
|
||||
mgs.groupCount++
|
||||
mgs.Unlock()
|
||||
|
||||
|
|
|
@ -35,8 +35,8 @@ func NewDefaultReferrerTracker() (*DefaultReferrerTracker, error) {
|
|||
even: make(map[string]*ReferrerItem),
|
||||
insert: acc.Insert("viewchunks_referrers").Columns("count, createdAt, domain").Fields("?,UTC_TIMESTAMP(),?").Prepare(), // TODO: Do something more efficient than doing a query for each referrer
|
||||
}
|
||||
//AddScheduledFifteenMinuteTask(refTracker.Tick)
|
||||
AddScheduledSecondTask(refTracker.Tick)
|
||||
AddScheduledFifteenMinuteTask(refTracker.Tick)
|
||||
//AddScheduledSecondTask(refTracker.Tick)
|
||||
AddShutdownTask(refTracker.Tick)
|
||||
return refTracker, acc.FirstError()
|
||||
}
|
||||
|
|
|
@ -1,7 +1,299 @@
|
|||
package common
|
||||
|
||||
import "strconv"
|
||||
import "../query_gen/lib"
|
||||
import (
|
||||
"strconv"
|
||||
"sync"
|
||||
|
||||
"../query_gen/lib"
|
||||
)
|
||||
|
||||
var TopicList TopicListInt
|
||||
|
||||
type TopicListHolder struct {
|
||||
List []*TopicsRow
|
||||
ForumList []Forum
|
||||
PageList []int
|
||||
Page int
|
||||
LastPage int
|
||||
}
|
||||
|
||||
type TopicListInt interface {
|
||||
GetListByGroup(group *Group, page int) (topicList []*TopicsRow, forumList []Forum, pageList []int, outPage int, lastPage int, err error)
|
||||
GetList(page int) (topicList []*TopicsRow, forumList []Forum, pageList []int, outPage int, lastPage int, err error)
|
||||
}
|
||||
|
||||
type DefaultTopicList struct {
|
||||
oddGroups map[int]*TopicListHolder
|
||||
evenGroups map[int]*TopicListHolder
|
||||
oddLock sync.RWMutex
|
||||
evenLock sync.RWMutex
|
||||
|
||||
groupList []int // TODO: Use an atomic.Value instead to allow this to be updated on long ticks
|
||||
}
|
||||
|
||||
func NewDefaultTopicList() (*DefaultTopicList, error) {
|
||||
tList := &DefaultTopicList{
|
||||
oddGroups: make(map[int]*TopicListHolder),
|
||||
evenGroups: make(map[int]*TopicListHolder),
|
||||
}
|
||||
|
||||
var slots = make([]int, 8) // Only cache the topic list for eight groups
|
||||
|
||||
// TODO: Do something more efficient than this
|
||||
allGroups, err := Groups.GetAll()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if len(allGroups) > 0 {
|
||||
var stopHere int
|
||||
if len(allGroups) <= 8 {
|
||||
stopHere = len(allGroups)
|
||||
} else {
|
||||
stopHere = 8
|
||||
}
|
||||
|
||||
var lowest = allGroups[0].UserCount
|
||||
for i := 0; i < stopHere; i++ {
|
||||
slots[i] = i
|
||||
if allGroups[i].UserCount < lowest {
|
||||
lowest = allGroups[i].UserCount
|
||||
}
|
||||
}
|
||||
|
||||
var findNewLowest = func() {
|
||||
for _, slot := range slots {
|
||||
if allGroups[slot].UserCount < lowest {
|
||||
lowest = allGroups[slot].UserCount
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for i := 8; i < len(allGroups); i++ {
|
||||
if allGroups[i].UserCount > lowest {
|
||||
for ii, slot := range slots {
|
||||
if allGroups[i].UserCount > slot {
|
||||
slots[ii] = i
|
||||
lowest = allGroups[i].UserCount
|
||||
findNewLowest()
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
tList.groupList = slots
|
||||
}
|
||||
|
||||
err = tList.Tick()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
AddScheduledHalfSecondTask(tList.Tick)
|
||||
//AddScheduledSecondTask(tList.GroupCountTick) // TODO: Dynamically change the groups in the short list to be optimised every second
|
||||
return tList, nil
|
||||
}
|
||||
|
||||
// TODO: Add support for groups other than the guest group
|
||||
func (tList *DefaultTopicList) Tick() error {
|
||||
var oddLists = make(map[int]*TopicListHolder)
|
||||
var evenLists = make(map[int]*TopicListHolder)
|
||||
|
||||
var addList = func(gid int, topicList []*TopicsRow, forumList []Forum, pageList []int, page int, lastPage int) {
|
||||
if gid%2 == 0 {
|
||||
evenLists[gid] = &TopicListHolder{topicList, forumList, pageList, page, lastPage}
|
||||
} else {
|
||||
oddLists[gid] = &TopicListHolder{topicList, forumList, pageList, page, lastPage}
|
||||
}
|
||||
}
|
||||
|
||||
guestGroup, err := Groups.Get(GuestUser.Group)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
topicList, forumList, pageList, page, lastPage, err := tList.getListByGroup(guestGroup, 1)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
addList(guestGroup.ID, topicList, forumList, pageList, page, lastPage)
|
||||
|
||||
for _, gid := range tList.groupList {
|
||||
group, err := Groups.Get(gid) // TODO: Bulk load the groups?
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
topicList, forumList, pageList, page, lastPage, err := tList.getListByGroup(group, 1)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
addList(group.ID, topicList, forumList, pageList, page, lastPage)
|
||||
}
|
||||
|
||||
tList.oddLock.Lock()
|
||||
tList.oddGroups = oddLists
|
||||
tList.oddLock.Unlock()
|
||||
|
||||
tList.evenLock.Lock()
|
||||
tList.evenGroups = evenLists
|
||||
tList.evenLock.Unlock()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (tList *DefaultTopicList) GetListByGroup(group *Group, page int) (topicList []*TopicsRow, forumList []Forum, pageList []int, outPage int, lastPage int, err error) {
|
||||
// TODO: Cache the first three pages not just the first along with all the topics on this beaten track
|
||||
if page == 1 {
|
||||
var holder *TopicListHolder
|
||||
var ok bool
|
||||
if group.ID%2 == 0 {
|
||||
tList.evenLock.RLock()
|
||||
holder, ok = tList.evenGroups[group.ID]
|
||||
tList.evenLock.RUnlock()
|
||||
} else {
|
||||
tList.oddLock.RLock()
|
||||
holder, ok = tList.oddGroups[group.ID]
|
||||
tList.oddLock.RUnlock()
|
||||
}
|
||||
if ok {
|
||||
return holder.List, holder.ForumList, holder.PageList, holder.Page, holder.LastPage, nil
|
||||
}
|
||||
}
|
||||
|
||||
return tList.getListByGroup(group, page)
|
||||
}
|
||||
|
||||
func (tList *DefaultTopicList) getListByGroup(group *Group, page int) (topicList []*TopicsRow, forumList []Forum, pageList []int, outPage int, lastPage int, err error) {
|
||||
// TODO: Make CanSee a method on *Group with a canSee field? Have a CanSee method on *User to cover the case of superadmins?
|
||||
canSee := group.CanSee
|
||||
|
||||
// We need a list of the visible forums for Quick Topic
|
||||
// ? - Would it be useful, if we could post in social groups from /topics/?
|
||||
for _, fid := range canSee {
|
||||
forum := Forums.DirtyGet(fid)
|
||||
if forum.Name != "" && forum.Active && (forum.ParentType == "" || forum.ParentType == "forum") {
|
||||
fcopy := forum.Copy()
|
||||
// TODO: Add a hook here for plugin_guilds
|
||||
forumList = append(forumList, fcopy)
|
||||
}
|
||||
}
|
||||
|
||||
// ? - Should we be showing plugin_guilds posts on /topics/?
|
||||
argList, qlist := ForumListToArgQ(forumList)
|
||||
if qlist == "" {
|
||||
// We don't want to kill the page, so pass an empty slice and nil error
|
||||
return topicList, forumList, pageList, 1, 1, nil
|
||||
}
|
||||
|
||||
topicList, pageList, page, lastPage, err = tList.getList(page, argList, qlist)
|
||||
return topicList, forumList, pageList, page, lastPage, err
|
||||
}
|
||||
|
||||
// TODO: Reduce the number of returns
|
||||
func (tList *DefaultTopicList) GetList(page int) (topicList []*TopicsRow, forumList []Forum, pageList []int, outPage int, lastPage int, err error) {
|
||||
// TODO: Make CanSee a method on *Group with a canSee field? Have a CanSee method on *User to cover the case of superadmins?
|
||||
canSee, err := Forums.GetAllVisibleIDs()
|
||||
if err != nil {
|
||||
return nil, nil, nil, 1, 1, err
|
||||
}
|
||||
|
||||
// We need a list of the visible forums for Quick Topic
|
||||
// ? - Would it be useful, if we could post in social groups from /topics/?
|
||||
for _, fid := range canSee {
|
||||
forum := Forums.DirtyGet(fid)
|
||||
if forum.Name != "" && forum.Active && (forum.ParentType == "" || forum.ParentType == "forum") {
|
||||
fcopy := forum.Copy()
|
||||
// TODO: Add a hook here for plugin_guilds
|
||||
forumList = append(forumList, fcopy)
|
||||
}
|
||||
}
|
||||
|
||||
// ? - Should we be showing plugin_guilds posts on /topics/?
|
||||
argList, qlist := ForumListToArgQ(forumList)
|
||||
if qlist == "" {
|
||||
// If the super admin can't see anything, then things have gone terribly wrong
|
||||
return topicList, forumList, pageList, 1, 1, err
|
||||
}
|
||||
|
||||
topicList, pageList, outPage, lastPage, err = tList.getList(page, argList, qlist)
|
||||
return topicList, forumList, pageList, outPage, lastPage, err
|
||||
}
|
||||
|
||||
// TODO: Rename this to TopicListStore and pass back a TopicList instance holding the pagination data and topic list rather than passing them back one argument at a time
|
||||
func (tList *DefaultTopicList) getList(page int, argList []interface{}, qlist string) (topicList []*TopicsRow, pageList []int, outPage int, lastPage int, err error) {
|
||||
topicCount, err := ArgQToTopicCount(argList, qlist)
|
||||
if err != nil {
|
||||
return nil, nil, 1, 1, err
|
||||
}
|
||||
offset, page, lastPage := PageOffset(topicCount, page, Config.ItemsPerPage)
|
||||
|
||||
stmt, err := qgen.Builder.SimpleSelect("topics", "tid, title, content, createdBy, is_closed, sticky, createdAt, lastReplyAt, lastReplyBy, parentID, postCount, likeCount", "parentID IN("+qlist+")", "sticky DESC, lastReplyAt DESC, createdBy DESC", "?,?")
|
||||
if err != nil {
|
||||
return nil, nil, 1, 1, err
|
||||
}
|
||||
defer stmt.Close()
|
||||
|
||||
argList = append(argList, offset)
|
||||
argList = append(argList, Config.ItemsPerPage)
|
||||
|
||||
rows, err := stmt.Query(argList...)
|
||||
if err != nil {
|
||||
return nil, nil, 1, 1, err
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
var reqUserList = make(map[int]bool)
|
||||
for rows.Next() {
|
||||
topicItem := TopicsRow{ID: 0}
|
||||
err := rows.Scan(&topicItem.ID, &topicItem.Title, &topicItem.Content, &topicItem.CreatedBy, &topicItem.IsClosed, &topicItem.Sticky, &topicItem.CreatedAt, &topicItem.LastReplyAt, &topicItem.LastReplyBy, &topicItem.ParentID, &topicItem.PostCount, &topicItem.LikeCount)
|
||||
if err != nil {
|
||||
return nil, nil, 1, 1, err
|
||||
}
|
||||
|
||||
topicItem.Link = BuildTopicURL(NameToSlug(topicItem.Title), topicItem.ID)
|
||||
forum := Forums.DirtyGet(topicItem.ParentID)
|
||||
topicItem.ForumName = forum.Name
|
||||
topicItem.ForumLink = forum.Link
|
||||
|
||||
//topicItem.CreatedAt = RelativeTime(topicItem.CreatedAt)
|
||||
topicItem.RelativeLastReplyAt = RelativeTime(topicItem.LastReplyAt)
|
||||
|
||||
// TODO: Rename this Vhook to better reflect moving the topic list from /routes/ to /common/
|
||||
if Vhooks["topics_topic_row_assign"] != nil {
|
||||
RunVhook("topics_topic_row_assign", &topicItem, &forum)
|
||||
}
|
||||
topicList = append(topicList, &topicItem)
|
||||
reqUserList[topicItem.CreatedBy] = true
|
||||
reqUserList[topicItem.LastReplyBy] = true
|
||||
}
|
||||
err = rows.Err()
|
||||
if err != nil {
|
||||
return nil, nil, 1, 1, err
|
||||
}
|
||||
|
||||
// Convert the user ID map to a slice, then bulk load the users
|
||||
var idSlice = make([]int, len(reqUserList))
|
||||
var i int
|
||||
for userID := range reqUserList {
|
||||
idSlice[i] = userID
|
||||
i++
|
||||
}
|
||||
|
||||
// TODO: What if a user is deleted via the Control Panel?
|
||||
userList, err := Users.BulkGetMap(idSlice)
|
||||
if err != nil {
|
||||
return nil, nil, 1, 1, err
|
||||
}
|
||||
|
||||
// Second pass to the add the user data
|
||||
// TODO: Use a pointer to TopicsRow instead of TopicsRow itself?
|
||||
for _, topicItem := range topicList {
|
||||
topicItem.Creator = userList[topicItem.CreatedBy]
|
||||
topicItem.LastUser = userList[topicItem.LastReplyBy]
|
||||
}
|
||||
|
||||
pageList = Paginate(topicCount, Config.ItemsPerPage, 5)
|
||||
return topicList, pageList, page, lastPage, nil
|
||||
}
|
||||
|
||||
// Internal. Don't rely on it.
|
||||
func ForumListToArgQ(forums []Forum) (argList []interface{}, qlist string) {
|
||||
|
|
|
@ -82,11 +82,10 @@ func init() {
|
|||
DbInits.Add(func(acc *qgen.Accumulator) error {
|
||||
userStmts = UserStmts{
|
||||
activate: acc.SimpleUpdate("users", "active = 1", "uid = ?"),
|
||||
changeGroup: acc.SimpleUpdate("users", "group = ?", "uid = ?"),
|
||||
changeGroup: acc.SimpleUpdate("users", "group = ?", "uid = ?"), // TODO: Implement user_count for users_groups here
|
||||
delete: acc.SimpleDelete("users", "uid = ?"),
|
||||
setAvatar: acc.SimpleUpdate("users", "avatar = ?", "uid = ?"),
|
||||
setUsername: acc.SimpleUpdate("users", "name = ?", "uid = ?"),
|
||||
updateGroup: acc.SimpleUpdate("users", "group = ?", "uid = ?"),
|
||||
incrementTopics: acc.SimpleUpdate("users", "topics = topics + ?", "uid = ?"),
|
||||
updateLevel: acc.SimpleUpdate("users", "level = ?", "uid = ?"),
|
||||
incrementScore: acc.SimpleUpdate("users", "score = score + ?", "uid = ?"),
|
||||
|
|
|
@ -51,7 +51,7 @@ func NewDefaultUserStore(cache UserCache) (*DefaultUserStore, error) {
|
|||
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 = ?", "", ""),
|
||||
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()"),
|
||||
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 = ?", "", ""),
|
||||
userCount: acc.SimpleCount("users", "", ""),
|
||||
}, acc.FirstError()
|
||||
|
|
|
@ -45,7 +45,7 @@ func init() {
|
|||
common.Config.SMTPPort = "25"
|
||||
|
||||
// Misc
|
||||
common.Config.DefaultRoute = "routeTopics"
|
||||
common.Config.DefaultRoute = "routes.TopicList"
|
||||
common.Config.DefaultGroup = 3 // Should be a setting in the database
|
||||
common.Config.ActivationGroup = 5 // Should be a setting in the database
|
||||
common.Config.StaffCSS = "staff_post"
|
||||
|
|
16
gen_mssql.go
16
gen_mssql.go
|
@ -25,8 +25,6 @@ type Stmts struct {
|
|||
groupEntryExists *sql.Stmt
|
||||
getForumTopicsOffset *sql.Stmt
|
||||
getAttachment *sql.Stmt
|
||||
getTopicList *sql.Stmt
|
||||
getTopicReplies *sql.Stmt
|
||||
getForumTopics *sql.Stmt
|
||||
getProfileReplies *sql.Stmt
|
||||
getWatchers *sql.Stmt
|
||||
|
@ -182,20 +180,6 @@ func _gen_mssql() (err error) {
|
|||
return err
|
||||
}
|
||||
|
||||
log.Print("Preparing getTopicList statement.")
|
||||
stmts.getTopicList, err = db.Prepare("SELECT [topics].[tid],[topics].[title],[topics].[content],[topics].[createdBy],[topics].[is_closed],[topics].[sticky],[topics].[createdAt],[topics].[parentID],[users].[name],[users].[avatar] FROM [topics] LEFT JOIN [users] ON [topics].[createdBy] = [users].[uid] ORDER BY topics.sticky DESC,topics.lastReplyAt DESC,topics.createdBy DESC")
|
||||
if err != nil {
|
||||
log.Print("Bad Query: ","SELECT [topics].[tid],[topics].[title],[topics].[content],[topics].[createdBy],[topics].[is_closed],[topics].[sticky],[topics].[createdAt],[topics].[parentID],[users].[name],[users].[avatar] FROM [topics] LEFT JOIN [users] ON [topics].[createdBy] = [users].[uid] ORDER BY topics.sticky DESC,topics.lastReplyAt DESC,topics.createdBy DESC")
|
||||
return err
|
||||
}
|
||||
|
||||
log.Print("Preparing getTopicReplies statement.")
|
||||
stmts.getTopicReplies, err = db.Prepare("SELECT [replies].[rid],[replies].[content],[replies].[createdBy],[replies].[createdAt],[replies].[lastEdit],[replies].[lastEditBy],[users].[avatar],[users].[name],[users].[group],[users].[url_prefix],[users].[url_name],[users].[level],[replies].[ipaddress] FROM [replies] LEFT JOIN [users] ON [replies].[createdBy] = [users].[uid] WHERE [tid] = ?1")
|
||||
if err != nil {
|
||||
log.Print("Bad Query: ","SELECT [replies].[rid],[replies].[content],[replies].[createdBy],[replies].[createdAt],[replies].[lastEdit],[replies].[lastEditBy],[users].[avatar],[users].[name],[users].[group],[users].[url_prefix],[users].[url_name],[users].[level],[replies].[ipaddress] FROM [replies] LEFT JOIN [users] ON [replies].[createdBy] = [users].[uid] WHERE [tid] = ?1")
|
||||
return err
|
||||
}
|
||||
|
||||
log.Print("Preparing getForumTopics statement.")
|
||||
stmts.getForumTopics, err = db.Prepare("SELECT [topics].[tid],[topics].[title],[topics].[content],[topics].[createdBy],[topics].[is_closed],[topics].[sticky],[topics].[createdAt],[topics].[lastReplyAt],[topics].[parentID],[users].[name],[users].[avatar] FROM [topics] LEFT JOIN [users] ON [topics].[createdBy] = [users].[uid] WHERE [topics].[parentID] = ?1 ORDER BY topics.sticky DESC,topics.lastReplyAt DESC,topics.createdBy DESC")
|
||||
if err != nil {
|
||||
|
|
14
gen_mysql.go
14
gen_mysql.go
|
@ -27,8 +27,6 @@ type Stmts struct {
|
|||
groupEntryExists *sql.Stmt
|
||||
getForumTopicsOffset *sql.Stmt
|
||||
getAttachment *sql.Stmt
|
||||
getTopicList *sql.Stmt
|
||||
getTopicReplies *sql.Stmt
|
||||
getForumTopics *sql.Stmt
|
||||
getProfileReplies *sql.Stmt
|
||||
getWatchers *sql.Stmt
|
||||
|
@ -168,18 +166,6 @@ func _gen_mysql() (err error) {
|
|||
return err
|
||||
}
|
||||
|
||||
log.Print("Preparing getTopicList statement.")
|
||||
stmts.getTopicList, err = db.Prepare("SELECT `topics`.`tid`, `topics`.`title`, `topics`.`content`, `topics`.`createdBy`, `topics`.`is_closed`, `topics`.`sticky`, `topics`.`createdAt`, `topics`.`parentID`, `users`.`name`, `users`.`avatar` FROM `topics` LEFT JOIN `users` ON `topics`.`createdBy` = `users`.`uid` ORDER BY topics.sticky DESC,topics.lastReplyAt DESC,topics.createdBy DESC")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
log.Print("Preparing getTopicReplies statement.")
|
||||
stmts.getTopicReplies, err = db.Prepare("SELECT `replies`.`rid`, `replies`.`content`, `replies`.`createdBy`, `replies`.`createdAt`, `replies`.`lastEdit`, `replies`.`lastEditBy`, `users`.`avatar`, `users`.`name`, `users`.`group`, `users`.`url_prefix`, `users`.`url_name`, `users`.`level`, `replies`.`ipaddress` FROM `replies` LEFT JOIN `users` ON `replies`.`createdBy` = `users`.`uid` WHERE `tid` = ?")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
log.Print("Preparing getForumTopics statement.")
|
||||
stmts.getForumTopics, err = db.Prepare("SELECT `topics`.`tid`, `topics`.`title`, `topics`.`content`, `topics`.`createdBy`, `topics`.`is_closed`, `topics`.`sticky`, `topics`.`createdAt`, `topics`.`lastReplyAt`, `topics`.`parentID`, `users`.`name`, `users`.`avatar` FROM `topics` LEFT JOIN `users` ON `topics`.`createdBy` = `users`.`uid` WHERE `topics`.`parentID` = ? ORDER BY topics.sticky DESC,topics.lastReplyAt DESC,topics.createdBy DESC")
|
||||
if err != nil {
|
||||
|
|
|
@ -26,7 +26,7 @@ var RouteMap = map[string]interface{}{
|
|||
"routeWebsockets": routeWebsockets,
|
||||
"routeReportSubmit": routeReportSubmit,
|
||||
"routes.CreateTopic": routes.CreateTopic,
|
||||
"routeTopics": routeTopics,
|
||||
"routes.TopicList": routes.TopicList,
|
||||
"routePanelForums": routePanelForums,
|
||||
"routePanelForumsCreateSubmit": routePanelForumsCreateSubmit,
|
||||
"routePanelForumsDelete": routePanelForumsDelete,
|
||||
|
@ -128,7 +128,7 @@ var routeMapEnum = map[string]int{
|
|||
"routeWebsockets": 7,
|
||||
"routeReportSubmit": 8,
|
||||
"routes.CreateTopic": 9,
|
||||
"routeTopics": 10,
|
||||
"routes.TopicList": 10,
|
||||
"routePanelForums": 11,
|
||||
"routePanelForumsCreateSubmit": 12,
|
||||
"routePanelForumsDelete": 13,
|
||||
|
@ -228,7 +228,7 @@ var reverseRouteMapEnum = map[int]string{
|
|||
7: "routeWebsockets",
|
||||
8: "routeReportSubmit",
|
||||
9: "routes.CreateTopic",
|
||||
10: "routeTopics",
|
||||
10: "routes.TopicList",
|
||||
11: "routePanelForums",
|
||||
12: "routePanelForumsCreateSubmit",
|
||||
13: "routePanelForumsDelete",
|
||||
|
@ -524,6 +524,11 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
|||
|
||||
// Deflect malformed requests
|
||||
if len(req.URL.Path) == 0 || req.URL.Path[0] != '/' || req.Host != common.Site.Host {
|
||||
//log.Print("len(req.URL.Path): ",len(req.URL.Path))
|
||||
//log.Print("req.URL.Path[0]: ",req.URL.Path[0])
|
||||
//log.Print("req.Host: ",req.Host)
|
||||
//log.Print("common.Site.Host: ",common.Site.Host)
|
||||
|
||||
w.WriteHeader(200) // 400
|
||||
w.Write([]byte(""))
|
||||
log.Print("Malformed Request")
|
||||
|
@ -687,13 +692,13 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
|||
common.OSViewCounter.Bump(osMapEnum[os])
|
||||
}
|
||||
|
||||
referrer := req.Header.Get("Referer") // Check the referrer header too? :P
|
||||
referrer := req.Header.Get("Referer") // Check the 'referrer' header too? :P
|
||||
if referrer != "" {
|
||||
// ? Optimise this a little?
|
||||
referrer = strings.TrimPrefix(strings.TrimPrefix(referrer,"http://"),"https://")
|
||||
referrer = strings.Split(referrer,"/")[0]
|
||||
portless := strings.Split(referrer,":")[0]
|
||||
if portless != "localhost" && portless != "127.0.0.1" && portless == common.Site.Host {
|
||||
if portless != "localhost" && portless != "127.0.0.1" && portless != common.Site.Host {
|
||||
common.ReferrerTracker.Bump(referrer)
|
||||
}
|
||||
}
|
||||
|
@ -811,7 +816,7 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
|||
err = routes.CreateTopic(w,req,user,extraData)
|
||||
default:
|
||||
common.RouteViewCounter.Bump(10)
|
||||
err = routeTopics(w,req,user)
|
||||
err = routes.TopicList(w,req,user)
|
||||
}
|
||||
if err != nil {
|
||||
router.handleError(err,w,req,user)
|
||||
|
|
|
@ -2,15 +2,16 @@
|
|||
package main
|
||||
|
||||
var dbTablePrimaryKeys = map[string]string{
|
||||
"users_groups":"gid",
|
||||
"users_groups_scheduler":"uid",
|
||||
"replies":"rid",
|
||||
"word_filters":"wfid",
|
||||
"revisions":"reviseID",
|
||||
"users_replies":"rid",
|
||||
"activity_stream":"asid",
|
||||
"users":"uid",
|
||||
"forums":"fid",
|
||||
"word_filters":"wfid",
|
||||
"users_groups":"gid",
|
||||
"topics":"tid",
|
||||
"forums":"fid",
|
||||
"attachments":"attachID",
|
||||
"polls":"pollID",
|
||||
"users":"uid",
|
||||
"users_groups_scheduler":"uid",
|
||||
}
|
||||
|
|
360
general_test.go
360
general_test.go
|
@ -15,6 +15,7 @@ import (
|
|||
"./common"
|
||||
"./install/install"
|
||||
"./query_gen/lib"
|
||||
"./routes"
|
||||
//"github.com/husobee/vestigo"
|
||||
)
|
||||
|
||||
|
@ -99,8 +100,11 @@ func BenchmarkTopicAdminRouteParallel(b *testing.B) {
|
|||
b.Fatal(err)
|
||||
}
|
||||
}
|
||||
prev := common.Dev.DebugMode
|
||||
prev2 := common.Dev.SuperDebug
|
||||
common.Dev.DebugMode = false
|
||||
common.Dev.SuperDebug = false
|
||||
|
||||
b.RunParallel(func(pb *testing.PB) {
|
||||
admin, err := common.Users.Get(1)
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
|
@ -111,9 +115,10 @@ func BenchmarkTopicAdminRouteParallel(b *testing.B) {
|
|||
adminUIDCookie := http.Cookie{Name: "uid", Value: "1", Path: "/", MaxAge: common.Year}
|
||||
adminSessionCookie := http.Cookie{Name: "session", Value: admin.Session, Path: "/", MaxAge: common.Year}
|
||||
|
||||
b.RunParallel(func(pb *testing.PB) {
|
||||
for pb.Next() {
|
||||
topicW := httptest.NewRecorder()
|
||||
topicReq := httptest.NewRequest("get", "/topic/1", bytes.NewReader(nil))
|
||||
topicReqAdmin := topicReq
|
||||
topicReqAdmin := httptest.NewRequest("get", "/topic/hm.1", bytes.NewReader(nil))
|
||||
topicReqAdmin.AddCookie(&adminUIDCookie)
|
||||
topicReqAdmin.AddCookie(&adminSessionCookie)
|
||||
|
||||
|
@ -122,12 +127,75 @@ func BenchmarkTopicAdminRouteParallel(b *testing.B) {
|
|||
if !ok {
|
||||
b.Fatal("Mysterious error!")
|
||||
}
|
||||
|
||||
for pb.Next() {
|
||||
topicW.Body.Reset()
|
||||
routeTopicID(topicW, topicReqAdmin, user)
|
||||
//topicW.Body.Reset()
|
||||
routes.ViewTopic(topicW, topicReqAdmin, user, "1")
|
||||
if topicW.Code != 200 {
|
||||
b.Log(topicW.Body)
|
||||
b.Fatal("HTTP Error!")
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
common.Dev.DebugMode = prev
|
||||
common.Dev.SuperDebug = prev2
|
||||
}
|
||||
|
||||
func BenchmarkTopicAdminRouteParallelWithRouter(b *testing.B) {
|
||||
b.ReportAllocs()
|
||||
if !gloinited {
|
||||
err := gloinit()
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
}
|
||||
router = NewGenRouter(http.FileServer(http.Dir("./uploads")))
|
||||
prev := common.Dev.DebugMode
|
||||
prev2 := common.Dev.SuperDebug
|
||||
common.Dev.DebugMode = false
|
||||
common.Dev.SuperDebug = false
|
||||
|
||||
admin, err := common.Users.Get(1)
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
if !admin.IsAdmin {
|
||||
b.Fatal("UID1 is not an admin")
|
||||
}
|
||||
adminUIDCookie := http.Cookie{Name: "uid", Value: "1", Path: "/", MaxAge: common.Year}
|
||||
adminSessionCookie := http.Cookie{Name: "session", Value: admin.Session, Path: "/", MaxAge: common.Year}
|
||||
|
||||
b.RunParallel(func(pb *testing.PB) {
|
||||
for pb.Next() {
|
||||
topicW := httptest.NewRecorder()
|
||||
topicReqAdmin := httptest.NewRequest("get", "/topic/hm.1", bytes.NewReader(nil))
|
||||
topicReqAdmin.AddCookie(&adminUIDCookie)
|
||||
topicReqAdmin.AddCookie(&adminSessionCookie)
|
||||
topicReqAdmin.Header.Set("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.132 Safari/537.36")
|
||||
topicReqAdmin.Header.Set("Host", "localhost")
|
||||
topicReqAdmin.Host = "localhost"
|
||||
//topicW.Body.Reset()
|
||||
router.ServeHTTP(topicW, topicReqAdmin)
|
||||
if topicW.Code != 200 {
|
||||
b.Log(topicW.Body)
|
||||
b.Fatal("HTTP Error!")
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
common.Dev.DebugMode = prev
|
||||
common.Dev.SuperDebug = prev2
|
||||
}
|
||||
|
||||
func BenchmarkTopicAdminRouteParallelAlt(b *testing.B) {
|
||||
BenchmarkTopicAdminRouteParallel(b)
|
||||
}
|
||||
|
||||
func BenchmarkTopicAdminRouteParallelWithRouterAlt(b *testing.B) {
|
||||
BenchmarkTopicAdminRouteParallelWithRouter(b)
|
||||
}
|
||||
|
||||
func BenchmarkTopicAdminRouteParallelAltAlt(b *testing.B) {
|
||||
BenchmarkTopicAdminRouteParallel(b)
|
||||
}
|
||||
|
||||
func BenchmarkTopicGuestRouteParallel(b *testing.B) {
|
||||
|
@ -138,15 +206,225 @@ func BenchmarkTopicGuestRouteParallel(b *testing.B) {
|
|||
b.Fatal(err)
|
||||
}
|
||||
}
|
||||
prev := common.Dev.DebugMode
|
||||
prev2 := common.Dev.SuperDebug
|
||||
common.Dev.DebugMode = false
|
||||
common.Dev.SuperDebug = false
|
||||
|
||||
b.RunParallel(func(pb *testing.PB) {
|
||||
topicW := httptest.NewRecorder()
|
||||
topicReq := httptest.NewRequest("get", "/topic/1", bytes.NewReader(nil))
|
||||
for pb.Next() {
|
||||
topicW.Body.Reset()
|
||||
routeTopicID(topicW, topicReq, common.GuestUser)
|
||||
topicW := httptest.NewRecorder()
|
||||
topicReq := httptest.NewRequest("get", "/topic/hm.1", bytes.NewReader(nil))
|
||||
//topicW.Body.Reset()
|
||||
routes.ViewTopic(topicW, topicReq, common.GuestUser, "1")
|
||||
if topicW.Code != 200 {
|
||||
b.Log(topicW.Body)
|
||||
b.Fatal("HTTP Error!")
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
common.Dev.DebugMode = prev
|
||||
common.Dev.SuperDebug = prev2
|
||||
}
|
||||
|
||||
func BenchmarkTopicGuestRouteParallelDebugMode(b *testing.B) {
|
||||
b.ReportAllocs()
|
||||
if !gloinited {
|
||||
err := gloinit()
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
}
|
||||
prev := common.Dev.DebugMode
|
||||
prev2 := common.Dev.SuperDebug
|
||||
common.Dev.DebugMode = true
|
||||
common.Dev.SuperDebug = false
|
||||
|
||||
b.RunParallel(func(pb *testing.PB) {
|
||||
for pb.Next() {
|
||||
topicW := httptest.NewRecorder()
|
||||
topicReq := httptest.NewRequest("get", "/topic/hm.1", bytes.NewReader(nil))
|
||||
//topicW.Body.Reset()
|
||||
routes.ViewTopic(topicW, topicReq, common.GuestUser, "1")
|
||||
if topicW.Code != 200 {
|
||||
b.Log(topicW.Body)
|
||||
b.Fatal("HTTP Error!")
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
common.Dev.DebugMode = prev
|
||||
common.Dev.SuperDebug = prev2
|
||||
}
|
||||
|
||||
func BenchmarkTopicGuestRouteParallelWithRouter(b *testing.B) {
|
||||
b.ReportAllocs()
|
||||
if !gloinited {
|
||||
err := gloinit()
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
}
|
||||
router = NewGenRouter(http.FileServer(http.Dir("./uploads")))
|
||||
prev := common.Dev.DebugMode
|
||||
prev2 := common.Dev.SuperDebug
|
||||
common.Dev.DebugMode = false
|
||||
common.Dev.SuperDebug = false
|
||||
|
||||
/*f, err := os.Create("BenchmarkTopicGuestRouteParallelWithRouter.prof")
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
pprof.StartCPUProfile(f)*/
|
||||
|
||||
b.RunParallel(func(pb *testing.PB) {
|
||||
for pb.Next() {
|
||||
topicW := httptest.NewRecorder()
|
||||
topicReq := httptest.NewRequest("GET", "/topic/hm.1", bytes.NewReader(nil))
|
||||
topicReq.Header.Set("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.132 Safari/537.36")
|
||||
topicReq.Header.Set("Host", "localhost")
|
||||
topicReq.Host = "localhost"
|
||||
//topicW.Body.Reset()
|
||||
router.ServeHTTP(topicW, topicReq)
|
||||
if topicW.Code != 200 {
|
||||
b.Log(topicW.Body)
|
||||
b.Fatal("HTTP Error!")
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
//defer pprof.StopCPUProfile()
|
||||
common.Dev.DebugMode = prev
|
||||
common.Dev.SuperDebug = prev2
|
||||
}
|
||||
|
||||
func BenchmarkBadRouteGuestRouteParallelWithRouter(b *testing.B) {
|
||||
b.ReportAllocs()
|
||||
if !gloinited {
|
||||
err := gloinit()
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
}
|
||||
router = NewGenRouter(http.FileServer(http.Dir("./uploads")))
|
||||
prev := common.Dev.DebugMode
|
||||
prev2 := common.Dev.SuperDebug
|
||||
common.Dev.DebugMode = false
|
||||
common.Dev.SuperDebug = false
|
||||
|
||||
b.RunParallel(func(pb *testing.PB) {
|
||||
for pb.Next() {
|
||||
badW := httptest.NewRecorder()
|
||||
badReq := httptest.NewRequest("GET", "/garble/haa", bytes.NewReader(nil))
|
||||
badReq.Header.Set("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.132 Safari/537.36")
|
||||
badReq.Header.Set("Host", "localhost")
|
||||
badReq.Host = "localhost"
|
||||
router.ServeHTTP(badW, badReq)
|
||||
}
|
||||
})
|
||||
|
||||
common.Dev.DebugMode = prev
|
||||
common.Dev.SuperDebug = prev2
|
||||
}
|
||||
|
||||
func BenchmarkTopicsGuestRouteParallelWithRouter(b *testing.B) {
|
||||
b.ReportAllocs()
|
||||
if !gloinited {
|
||||
err := gloinit()
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
}
|
||||
router = NewGenRouter(http.FileServer(http.Dir("./uploads")))
|
||||
prev := common.Dev.DebugMode
|
||||
prev2 := common.Dev.SuperDebug
|
||||
common.Dev.DebugMode = false
|
||||
common.Dev.SuperDebug = false
|
||||
|
||||
b.RunParallel(func(pb *testing.PB) {
|
||||
for pb.Next() {
|
||||
listW := httptest.NewRecorder()
|
||||
listReq := httptest.NewRequest("GET", "/topics/", bytes.NewReader(nil))
|
||||
listReq.Header.Set("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.132 Safari/537.36")
|
||||
listReq.Header.Set("Host", "localhost")
|
||||
listReq.Host = "localhost"
|
||||
router.ServeHTTP(listW, listReq)
|
||||
if listW.Code != 200 {
|
||||
b.Log(listW.Body)
|
||||
b.Fatal("HTTP Error!")
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
common.Dev.DebugMode = prev
|
||||
common.Dev.SuperDebug = prev2
|
||||
}
|
||||
|
||||
func BenchmarkForumsGuestRouteParallelWithRouter(b *testing.B) {
|
||||
b.ReportAllocs()
|
||||
if !gloinited {
|
||||
err := gloinit()
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
}
|
||||
router = NewGenRouter(http.FileServer(http.Dir("./uploads")))
|
||||
prev := common.Dev.DebugMode
|
||||
prev2 := common.Dev.SuperDebug
|
||||
common.Dev.DebugMode = false
|
||||
common.Dev.SuperDebug = false
|
||||
|
||||
b.RunParallel(func(pb *testing.PB) {
|
||||
for pb.Next() {
|
||||
listW := httptest.NewRecorder()
|
||||
listReq := httptest.NewRequest("GET", "/forums/", bytes.NewReader(nil))
|
||||
listReq.Header.Set("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.132 Safari/537.36")
|
||||
listReq.Header.Set("Host", "localhost")
|
||||
listReq.Host = "localhost"
|
||||
router.ServeHTTP(listW, listReq)
|
||||
if listW.Code != 200 {
|
||||
b.Log(listW.Body)
|
||||
b.Fatal("HTTP Error!")
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
common.Dev.DebugMode = prev
|
||||
common.Dev.SuperDebug = prev2
|
||||
}
|
||||
|
||||
func BenchmarkForumGuestRouteParallelWithRouter(b *testing.B) {
|
||||
b.ReportAllocs()
|
||||
if !gloinited {
|
||||
err := gloinit()
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
}
|
||||
router = NewGenRouter(http.FileServer(http.Dir("./uploads")))
|
||||
prev := common.Dev.DebugMode
|
||||
prev2 := common.Dev.SuperDebug
|
||||
common.Dev.DebugMode = false
|
||||
common.Dev.SuperDebug = false
|
||||
|
||||
b.RunParallel(func(pb *testing.PB) {
|
||||
for pb.Next() {
|
||||
listW := httptest.NewRecorder()
|
||||
listReq := httptest.NewRequest("GET", "/forum/general.2", bytes.NewReader(nil))
|
||||
listReq.Header.Set("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.132 Safari/537.36")
|
||||
listReq.Header.Set("Host", "localhost")
|
||||
listReq.Host = "localhost"
|
||||
router.ServeHTTP(listW, listReq)
|
||||
if listW.Code != 200 {
|
||||
b.Log(listW.Body)
|
||||
b.Fatal("HTTP Error!")
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
common.Dev.DebugMode = prev
|
||||
common.Dev.SuperDebug = prev2
|
||||
}
|
||||
|
||||
// TODO: Make these routes compatible with the changes to the router
|
||||
|
@ -165,19 +443,19 @@ func BenchmarkForumsAdminRouteParallel(b *testing.B) {
|
|||
if !admin.Is_Admin {
|
||||
panic("UID1 is not an admin")
|
||||
}
|
||||
admin_uid_cookie := http.Cookie{Name:"uid",Value:"1",Path:"/",MaxAge: year}
|
||||
admin_session_cookie := http.Cookie{Name:"session",Value: admin.Session,Path:"/",MaxAge: year}
|
||||
adminUidCookie := http.Cookie{Name:"uid",Value:"1",Path:"/",MaxAge: year}
|
||||
adminSessionCookie := http.Cookie{Name:"session",Value: admin.Session,Path:"/",MaxAge: year}
|
||||
|
||||
forums_w := httptest.NewRecorder()
|
||||
forums_req := httptest.NewRequest("get","/forums/",bytes.NewReader(nil))
|
||||
forums_req_admin := forums_req
|
||||
forums_req_admin.AddCookie(&admin_uid_cookie)
|
||||
forums_req_admin.AddCookie(&admin_session_cookie)
|
||||
forums_handler := http.HandlerFunc(route_forums)
|
||||
forumsW := httptest.NewRecorder()
|
||||
forumsReq := httptest.NewRequest("get","/forums/",bytes.NewReader(nil))
|
||||
forumsReq_admin := forums_req
|
||||
forumsReq_admin.AddCookie(&adminUidCookie)
|
||||
forumsReq_admin.AddCookie(&adminSessionCookie)
|
||||
forumsHandler := http.HandlerFunc(route_forums)
|
||||
|
||||
for pb.Next() {
|
||||
forums_w.Body.Reset()
|
||||
forums_handler.ServeHTTP(forums_w,forums_req_admin)
|
||||
forumsW.Body.Reset()
|
||||
forumsHandler.ServeHTTP(forumsW,forumsReqAdmin)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
@ -196,47 +474,29 @@ func BenchmarkForumsAdminRouteParallelProf(b *testing.B) {
|
|||
if !admin.Is_Admin {
|
||||
panic("UID1 is not an admin")
|
||||
}
|
||||
admin_uid_cookie := http.Cookie{Name:"uid",Value:"1",Path:"/",MaxAge: year}
|
||||
admin_session_cookie := http.Cookie{Name:"session",Value: admin.Session,Path: "/",MaxAge: year}
|
||||
adminUidCookie := http.Cookie{Name:"uid",Value:"1",Path:"/",MaxAge: year}
|
||||
adminSessionCookie := http.Cookie{Name:"session",Value: admin.Session,Path: "/",MaxAge: year}
|
||||
|
||||
forums_w := httptest.NewRecorder()
|
||||
forums_req := httptest.NewRequest("get","/forums/",bytes.NewReader(nil))
|
||||
forums_req_admin := forums_req
|
||||
forums_req_admin.AddCookie(&admin_uid_cookie)
|
||||
forums_req_admin.AddCookie(&admin_session_cookie)
|
||||
forums_handler := http.HandlerFunc(route_forums)
|
||||
forumsW := httptest.NewRecorder()
|
||||
forumsReq := httptest.NewRequest("get","/forums/",bytes.NewReader(nil))
|
||||
forumsReqAdmin := forumsReq
|
||||
forumsReqAdmin.AddCookie(&admin_uid_cookie)
|
||||
forumsReqAdmin.AddCookie(&admin_session_cookie)
|
||||
forumsHandler := http.HandlerFunc(route_forums)
|
||||
f, err := os.Create("cpu_forums_admin_parallel.prof")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
pprof.StartCPUProfile(f)
|
||||
for pb.Next() {
|
||||
forums_w.Body.Reset()
|
||||
forums_handler.ServeHTTP(forums_w,forums_req_admin)
|
||||
forumsW.Body.Reset()
|
||||
forumsHandler.ServeHTTP(forumsW,forumsReqAdmin)
|
||||
}
|
||||
pprof.StopCPUProfile()
|
||||
})
|
||||
}
|
||||
|
||||
func BenchmarkForumsGuestRouteParallel(b *testing.B) {
|
||||
b.ReportAllocs()
|
||||
if !gloinited {
|
||||
gloinit()
|
||||
}
|
||||
|
||||
b.RunParallel(func(pb *testing.PB) {
|
||||
forums_w := httptest.NewRecorder()
|
||||
forums_req := httptest.NewRequest("get","/forums/",bytes.NewReader(nil))
|
||||
forums_handler := http.HandlerFunc(route_forums)
|
||||
for pb.Next() {
|
||||
forums_w.Body.Reset()
|
||||
forums_handler.ServeHTTP(forums_w,forums_req)
|
||||
}
|
||||
})
|
||||
}
|
||||
*/
|
||||
|
||||
/*func BenchmarkRoutesSerial(b *testing.B) {
|
||||
func BenchmarkRoutesSerial(b *testing.B) {
|
||||
b.ReportAllocs()
|
||||
admin, err := users.Get(1)
|
||||
if err != nil {
|
||||
|
|
|
@ -142,7 +142,7 @@ func init() {
|
|||
common.Config.SMTPPort = "25"
|
||||
|
||||
// Misc
|
||||
common.Config.DefaultRoute = "routeTopics"
|
||||
common.Config.DefaultRoute = "routes.TopicList"
|
||||
common.Config.DefaultGroup = 3 // Should be a setting in the database
|
||||
common.Config.ActivationGroup = 5 // Should be a setting in the database
|
||||
common.Config.StaffCSS = "staff_post"
|
||||
|
|
|
@ -126,6 +126,7 @@
|
|||
"OperatingSystems": {
|
||||
"windows": "Microsoft Windows",
|
||||
"linux":"Linux",
|
||||
"mac":"Apple Mac",
|
||||
"android": "Android",
|
||||
"iphone":"iPhone",
|
||||
"unknown":"Unknown"
|
||||
|
|
4
main.go
4
main.go
|
@ -96,6 +96,10 @@ func afterDBInit() (err error) {
|
|||
if err != nil {
|
||||
return err
|
||||
}
|
||||
common.TopicList, err = common.NewDefaultTopicList()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
common.GlobalViewCounter, err = common.NewGlobalViewCounter()
|
||||
if err != nil {
|
||||
|
|
|
@ -1135,6 +1135,9 @@ func _gen_mssql() (err error) {
|
|||
|
||||
// Internal methods, not exposed in the interface
|
||||
func (adapter *MssqlAdapter) pushStatement(name string, stype string, querystr string) {
|
||||
if name[0] == '_' {
|
||||
return
|
||||
}
|
||||
adapter.Buffer[name] = DBStmt{querystr, stype}
|
||||
adapter.BufferOrder = append(adapter.BufferOrder, name)
|
||||
}
|
||||
|
|
|
@ -696,6 +696,9 @@ func _gen_mysql() (err error) {
|
|||
|
||||
// Internal methods, not exposed in the interface
|
||||
func (adapter *MysqlAdapter) pushStatement(name string, stype string, querystr string) {
|
||||
if name[0] == '_' {
|
||||
return
|
||||
}
|
||||
adapter.Buffer[name] = DBStmt{querystr, stype}
|
||||
adapter.BufferOrder = append(adapter.BufferOrder, name)
|
||||
}
|
||||
|
|
|
@ -393,6 +393,9 @@ func _gen_pgsql() (err error) {
|
|||
|
||||
// Internal methods, not exposed in the interface
|
||||
func (adapter *PgsqlAdapter) pushStatement(name string, stype string, querystr string) {
|
||||
if name[0] == '_' {
|
||||
return
|
||||
}
|
||||
adapter.Buffer[name] = DBStmt{querystr, stype}
|
||||
adapter.BufferOrder = append(adapter.BufferOrder, name)
|
||||
}
|
||||
|
|
|
@ -255,10 +255,6 @@ func writeSelects(adapter qgen.Adapter) error {
|
|||
}
|
||||
|
||||
func writeLeftJoins(adapter qgen.Adapter) error {
|
||||
adapter.SimpleLeftJoin("getTopicList", "topics", "users", "topics.tid, topics.title, topics.content, topics.createdBy, topics.is_closed, topics.sticky, topics.createdAt, topics.parentID, users.name, users.avatar", "topics.createdBy = users.uid", "", "topics.sticky DESC, topics.lastReplyAt DESC, topics.createdBy DESC", "")
|
||||
|
||||
adapter.SimpleLeftJoin("getTopicReplies", "replies", "users", "replies.rid, replies.content, replies.createdBy, replies.createdAt, replies.lastEdit, replies.lastEditBy, users.avatar, users.name, users.group, users.url_prefix, users.url_name, users.level, replies.ipaddress", "replies.createdBy = users.uid", "tid = ?", "", "")
|
||||
|
||||
adapter.SimpleLeftJoin("getForumTopics", "topics", "users", "topics.tid, topics.title, topics.content, topics.createdBy, topics.is_closed, topics.sticky, topics.createdAt, topics.lastReplyAt, topics.parentID, users.name, users.avatar", "topics.createdBy = users.uid", "topics.parentID = ?", "topics.sticky DESC, topics.lastReplyAt DESC, topics.createdBy desc", "")
|
||||
|
||||
adapter.SimpleLeftJoin("getProfileReplies", "users_replies", "users", "users_replies.rid, users_replies.content, users_replies.createdBy, users_replies.createdAt, users_replies.lastEdit, users_replies.lastEditBy, users.avatar, users.name, users.group", "users_replies.createdBy = users.uid", "users_replies.uid = ?", "", "")
|
||||
|
@ -306,7 +302,7 @@ func writeUpdates(adapter qgen.Adapter) error {
|
|||
|
||||
build.Update("updateTheme").Table("themes").Set("default = ?").Where("uname = ?").Parse()
|
||||
|
||||
build.Update("updateUser").Table("users").Set("name = ?, email = ?, group = ?").Where("uid = ?").Parse()
|
||||
build.Update("updateUser").Table("users").Set("name = ?, email = ?, group = ?").Where("uid = ?").Parse() // TODO: Implement user_count for users_groups on things which use this
|
||||
|
||||
build.Update("updateGroupPerms").Table("users_groups").Set("permissions = ?").Where("gid = ?").Parse()
|
||||
|
||||
|
|
|
@ -48,6 +48,7 @@ func createTables(adapter qgen.Adapter) error {
|
|||
qgen.DBTableColumn{"is_mod", "boolean", 0, false, false, "0"},
|
||||
qgen.DBTableColumn{"is_admin", "boolean", 0, false, false, "0"},
|
||||
qgen.DBTableColumn{"is_banned", "boolean", 0, false, false, "0"},
|
||||
qgen.DBTableColumn{"user_count", "int", 0, false, false, "0"}, // TODO: Implement this
|
||||
|
||||
qgen.DBTableColumn{"tag", "varchar", 50, false, false, "''"},
|
||||
},
|
||||
|
@ -215,7 +216,9 @@ func createTables(adapter qgen.Adapter) error {
|
|||
qgen.DBTableColumn{"contentType", "varchar", 100, false, false, "replies"},
|
||||
qgen.DBTableColumn{"createdAt", "createdAt", 0, false, false, ""},
|
||||
},
|
||||
[]qgen.DBTableKey{},
|
||||
[]qgen.DBTableKey{
|
||||
qgen.DBTableKey{"reviseID", "primary"},
|
||||
},
|
||||
)
|
||||
|
||||
qgen.Install.CreateTable("polls", "utf8mb4", "utf8mb4_general_ci",
|
||||
|
|
|
@ -383,6 +383,11 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
|||
|
||||
// Deflect malformed requests
|
||||
if len(req.URL.Path) == 0 || req.URL.Path[0] != '/' || req.Host != common.Site.Host {
|
||||
//log.Print("len(req.URL.Path): ",len(req.URL.Path))
|
||||
//log.Print("req.URL.Path[0]: ",req.URL.Path[0])
|
||||
//log.Print("req.Host: ",req.Host)
|
||||
//log.Print("common.Site.Host: ",common.Site.Host)
|
||||
|
||||
w.WriteHeader(200) // 400
|
||||
w.Write([]byte(""))
|
||||
log.Print("Malformed Request")
|
||||
|
|
|
@ -19,7 +19,7 @@ func routes() {
|
|||
addRouteGroup(reportGroup)
|
||||
|
||||
topicGroup := newRouteGroup("/topics/",
|
||||
View("routeTopics", "/topics/"),
|
||||
View("routes.TopicList", "/topics/"),
|
||||
MemberView("routes.CreateTopic", "/topics/create/", "extraData"),
|
||||
)
|
||||
addRouteGroup(topicGroup)
|
||||
|
|
135
routes.go
135
routes.go
|
@ -15,7 +15,6 @@ import (
|
|||
"time"
|
||||
|
||||
"./common"
|
||||
"./query_gen/lib"
|
||||
)
|
||||
|
||||
// A blank list to fill out that parameter in Page for routes which don't use it
|
||||
|
@ -45,140 +44,6 @@ func routeUploads() {
|
|||
func BadRoute() {
|
||||
}
|
||||
|
||||
func routeTopics(w http.ResponseWriter, r *http.Request, user common.User) common.RouteError {
|
||||
headerVars, ferr := common.UserCheck(w, r, &user)
|
||||
if ferr != nil {
|
||||
return ferr
|
||||
}
|
||||
headerVars.Zone = "topics"
|
||||
headerVars.MetaDesc = headerVars.Settings["meta_desc"].(string)
|
||||
|
||||
group, err := common.Groups.Get(user.Group)
|
||||
if err != nil {
|
||||
log.Printf("Group #%d doesn't exist despite being used by common.User #%d", user.Group, user.ID)
|
||||
return common.LocalError("Something weird happened", w, r, user)
|
||||
}
|
||||
|
||||
// TODO: Make CanSee a method on *Group with a canSee field? Have a CanSee method on *User to cover the case of superadmins?
|
||||
var canSee []int
|
||||
if user.IsSuperAdmin {
|
||||
canSee, err = common.Forums.GetAllVisibleIDs()
|
||||
if err != nil {
|
||||
return common.InternalError(err, w, r)
|
||||
}
|
||||
} else {
|
||||
canSee = group.CanSee
|
||||
}
|
||||
|
||||
// We need a list of the visible forums for Quick Topic
|
||||
// ? - Would it be useful, if we could post in social groups from /topics/?
|
||||
var forumList []common.Forum
|
||||
for _, fid := range canSee {
|
||||
forum := common.Forums.DirtyGet(fid)
|
||||
if forum.Name != "" && forum.Active && (forum.ParentType == "" || forum.ParentType == "forum") {
|
||||
fcopy := forum.Copy()
|
||||
// TODO: Add a hook here for plugin_guilds
|
||||
forumList = append(forumList, fcopy)
|
||||
}
|
||||
}
|
||||
|
||||
// ? - Should we be showing plugin_guilds posts on /topics/?
|
||||
argList, qlist := common.ForumListToArgQ(forumList)
|
||||
|
||||
// ! Need an inline error not a page level error
|
||||
if qlist == "" {
|
||||
return common.NotFound(w, r)
|
||||
}
|
||||
|
||||
topicCount, err := common.ArgQToTopicCount(argList, qlist)
|
||||
if err != nil {
|
||||
return common.InternalError(err, w, r)
|
||||
}
|
||||
|
||||
// Get the current page
|
||||
page, _ := strconv.Atoi(r.FormValue("page"))
|
||||
offset, page, lastPage := common.PageOffset(topicCount, page, common.Config.ItemsPerPage)
|
||||
|
||||
var topicList []*common.TopicsRow
|
||||
stmt, err := qgen.Builder.SimpleSelect("topics", "tid, title, content, createdBy, is_closed, sticky, createdAt, lastReplyAt, lastReplyBy, parentID, postCount, likeCount", "parentID IN("+qlist+")", "sticky DESC, lastReplyAt DESC, createdBy DESC", "?,?")
|
||||
if err != nil {
|
||||
return common.InternalError(err, w, r)
|
||||
}
|
||||
defer stmt.Close()
|
||||
|
||||
argList = append(argList, offset)
|
||||
argList = append(argList, common.Config.ItemsPerPage)
|
||||
|
||||
rows, err := stmt.Query(argList...)
|
||||
if err != nil {
|
||||
return common.InternalError(err, w, r)
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
var reqUserList = make(map[int]bool)
|
||||
for rows.Next() {
|
||||
topicItem := common.TopicsRow{ID: 0}
|
||||
err := rows.Scan(&topicItem.ID, &topicItem.Title, &topicItem.Content, &topicItem.CreatedBy, &topicItem.IsClosed, &topicItem.Sticky, &topicItem.CreatedAt, &topicItem.LastReplyAt, &topicItem.LastReplyBy, &topicItem.ParentID, &topicItem.PostCount, &topicItem.LikeCount)
|
||||
if err != nil {
|
||||
return common.InternalError(err, w, r)
|
||||
}
|
||||
|
||||
topicItem.Link = common.BuildTopicURL(common.NameToSlug(topicItem.Title), topicItem.ID)
|
||||
|
||||
forum := common.Forums.DirtyGet(topicItem.ParentID)
|
||||
topicItem.ForumName = forum.Name
|
||||
topicItem.ForumLink = forum.Link
|
||||
|
||||
//topicItem.CreatedAt = common.RelativeTime(topicItem.CreatedAt)
|
||||
topicItem.RelativeLastReplyAt = common.RelativeTime(topicItem.LastReplyAt)
|
||||
|
||||
if common.Vhooks["topics_topic_row_assign"] != nil {
|
||||
common.RunVhook("topics_topic_row_assign", &topicItem, &forum)
|
||||
}
|
||||
topicList = append(topicList, &topicItem)
|
||||
reqUserList[topicItem.CreatedBy] = true
|
||||
reqUserList[topicItem.LastReplyBy] = true
|
||||
}
|
||||
err = rows.Err()
|
||||
if err != nil {
|
||||
return common.InternalError(err, w, r)
|
||||
}
|
||||
|
||||
// Convert the user ID map to a slice, then bulk load the users
|
||||
var idSlice = make([]int, len(reqUserList))
|
||||
var i int
|
||||
for userID := range reqUserList {
|
||||
idSlice[i] = userID
|
||||
i++
|
||||
}
|
||||
|
||||
// TODO: What if a user is deleted via the Control Panel?
|
||||
userList, err := common.Users.BulkGetMap(idSlice)
|
||||
if err != nil {
|
||||
return common.InternalError(err, w, r)
|
||||
}
|
||||
|
||||
// Second pass to the add the user data
|
||||
// TODO: Use a pointer to TopicsRow instead of TopicsRow itself?
|
||||
for _, topicItem := range topicList {
|
||||
topicItem.Creator = userList[topicItem.CreatedBy]
|
||||
topicItem.LastUser = userList[topicItem.LastReplyBy]
|
||||
}
|
||||
|
||||
pageList := common.Paginate(topicCount, common.Config.ItemsPerPage, 5)
|
||||
pi := common.TopicsPage{common.GetTitlePhrase("topics"), user, headerVars, topicList, forumList, common.Config.DefaultForum, pageList, page, lastPage}
|
||||
if common.PreRenderHooks["pre_render_topic_list"] != nil {
|
||||
if common.RunPreRenderHook("pre_render_topic_list", w, r, &user, &pi) {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
err = common.RunThemeTemplate(headerVars.Theme.Name, "topics", pi, w)
|
||||
if err != nil {
|
||||
return common.InternalError(err, w, r)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func routeForum(w http.ResponseWriter, r *http.Request, user common.User, sfid string) common.RouteError {
|
||||
page, _ := strconv.Atoi(r.FormValue("page"))
|
||||
|
||||
|
|
|
@ -18,6 +18,22 @@ import (
|
|||
"../query_gen/lib"
|
||||
)
|
||||
|
||||
type TopicStmts struct {
|
||||
getReplies *sql.Stmt
|
||||
}
|
||||
|
||||
var topicStmts TopicStmts
|
||||
|
||||
// TODO: Move these DbInits into a TopicList abstraction
|
||||
func init() {
|
||||
common.DbInits.Add(func(acc *qgen.Accumulator) error {
|
||||
topicStmts = TopicStmts{
|
||||
getReplies: acc.SimpleLeftJoin("replies", "users", "replies.rid, replies.content, replies.createdBy, replies.createdAt, replies.lastEdit, replies.lastEditBy, users.avatar, users.name, users.group, users.url_prefix, users.url_name, users.level, replies.ipaddress, replies.likeCount, replies.actionType", "replies.createdBy = users.uid", "replies.tid = ?", "replies.rid ASC", "?,?"),
|
||||
}
|
||||
return acc.FirstError()
|
||||
})
|
||||
}
|
||||
|
||||
var successJSONBytes = []byte(`{"success":"1"}`)
|
||||
|
||||
func ViewTopic(w http.ResponseWriter, r *http.Request, user common.User, urlBit string) common.RouteError {
|
||||
|
@ -97,12 +113,7 @@ func ViewTopic(w http.ResponseWriter, r *http.Request, user common.User, urlBit
|
|||
tpage := common.TopicPage{topic.Title, user, headerVars, replyList, topic, poll, page, lastPage}
|
||||
|
||||
// Get the replies..
|
||||
// TODO: Reuse this statement rather than preparing it on the spot, maybe via a TopicList abstraction
|
||||
stmt, err := qgen.Builder.SimpleLeftJoin("replies", "users", "replies.rid, replies.content, replies.createdBy, replies.createdAt, replies.lastEdit, replies.lastEditBy, users.avatar, users.name, users.group, users.url_prefix, users.url_name, users.level, replies.ipaddress, replies.likeCount, replies.actionType", "replies.createdBy = users.uid", "replies.tid = ?", "replies.rid ASC", "?,?")
|
||||
if err != nil {
|
||||
return common.InternalError(err, w, r)
|
||||
}
|
||||
rows, err := stmt.Query(topic.ID, offset, common.Config.ItemsPerPage)
|
||||
rows, err := topicStmts.getReplies.Query(topic.ID, offset, common.Config.ItemsPerPage)
|
||||
if err == sql.ErrNoRows {
|
||||
return common.LocalError("Bad Page. Some of the posts may have been deleted or you got here by directly typing in the page number.", w, r, user)
|
||||
} else if err != nil {
|
||||
|
@ -165,14 +176,16 @@ func ViewTopic(w http.ResponseWriter, r *http.Request, user common.User, urlBit
|
|||
if common.Vhooks["topic_reply_row_assign"] != nil {
|
||||
common.RunVhook("topic_reply_row_assign", &tpage, &replyItem)
|
||||
}
|
||||
replyList = append(replyList, replyItem)
|
||||
//replyList = append(replyList, replyItem)
|
||||
// TODO: Use a pointer instead to make it easier to abstract this loop? What impact would this have on escape analysis?
|
||||
tpage.ItemList = append(tpage.ItemList, replyItem)
|
||||
}
|
||||
err = rows.Err()
|
||||
if err != nil {
|
||||
return common.InternalError(err, w, r)
|
||||
}
|
||||
|
||||
tpage.ItemList = replyList
|
||||
//tpage.ItemList = replyList
|
||||
if common.PreRenderHooks["pre_render_view_topic"] != nil {
|
||||
if common.RunPreRenderHook("pre_render_view_topic", w, r, &user, &tpage) {
|
||||
return nil
|
||||
|
|
|
@ -0,0 +1,60 @@
|
|||
package routes
|
||||
|
||||
import (
|
||||
"log"
|
||||
"net/http"
|
||||
"strconv"
|
||||
|
||||
"../common"
|
||||
)
|
||||
|
||||
func TopicList(w http.ResponseWriter, r *http.Request, user common.User) common.RouteError {
|
||||
headerVars, ferr := common.UserCheck(w, r, &user)
|
||||
if ferr != nil {
|
||||
return ferr
|
||||
}
|
||||
headerVars.Zone = "topics"
|
||||
headerVars.MetaDesc = headerVars.Settings["meta_desc"].(string)
|
||||
|
||||
group, err := common.Groups.Get(user.Group)
|
||||
if err != nil {
|
||||
log.Printf("Group #%d doesn't exist despite being used by common.User #%d", user.Group, user.ID)
|
||||
return common.LocalError("Something weird happened", w, r, user)
|
||||
}
|
||||
|
||||
// Get the current page
|
||||
page, _ := strconv.Atoi(r.FormValue("page"))
|
||||
|
||||
// TODO: Pass a struct back rather than passing back so many variables
|
||||
var topicList []*common.TopicsRow
|
||||
var forumList []common.Forum
|
||||
var pageList []int
|
||||
var lastPage int
|
||||
if user.IsSuperAdmin {
|
||||
topicList, forumList, pageList, page, lastPage, err = common.TopicList.GetList(page)
|
||||
} else {
|
||||
topicList, forumList, pageList, page, lastPage, err = common.TopicList.GetListByGroup(group, page)
|
||||
}
|
||||
if err != nil {
|
||||
return common.InternalError(err, w, r)
|
||||
}
|
||||
|
||||
// ! Need an inline error not a page level error
|
||||
//log.Printf("topicList: %+v\n", topicList)
|
||||
//log.Printf("forumList: %+v\n", forumList)
|
||||
if len(topicList) == 0 {
|
||||
return common.NotFound(w, r)
|
||||
}
|
||||
|
||||
pi := common.TopicsPage{common.GetTitlePhrase("topics"), user, headerVars, topicList, forumList, common.Config.DefaultForum, pageList, page, lastPage}
|
||||
if common.PreRenderHooks["pre_render_topic_list"] != nil {
|
||||
if common.RunPreRenderHook("pre_render_topic_list", w, r, &user, &pi) {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
err = common.RunThemeTemplate(headerVars.Theme.Name, "topics", pi, w)
|
||||
if err != nil {
|
||||
return common.InternalError(err, w, r)
|
||||
}
|
||||
return nil
|
||||
}
|
|
@ -3,5 +3,6 @@ CREATE TABLE [revisions] (
|
|||
[content] nvarchar (MAX) not null,
|
||||
[contentID] int not null,
|
||||
[contentType] nvarchar (100) DEFAULT 'replies' not null,
|
||||
[createdAt] datetime not null
|
||||
[createdAt] datetime not null,
|
||||
primary key([reviseID])
|
||||
);
|
|
@ -6,6 +6,7 @@ CREATE TABLE [users_groups] (
|
|||
[is_mod] bit DEFAULT 0 not null,
|
||||
[is_admin] bit DEFAULT 0 not null,
|
||||
[is_banned] bit DEFAULT 0 not null,
|
||||
[user_count] int DEFAULT 0 not null,
|
||||
[tag] nvarchar (50) DEFAULT '' not null,
|
||||
primary key([gid])
|
||||
);
|
|
@ -3,5 +3,6 @@ CREATE TABLE `revisions` (
|
|||
`content` text not null,
|
||||
`contentID` int not null,
|
||||
`contentType` varchar(100) DEFAULT 'replies' not null,
|
||||
`createdAt` datetime not null
|
||||
`createdAt` datetime not null,
|
||||
primary key(`reviseID`)
|
||||
) CHARSET=utf8mb4 COLLATE utf8mb4_general_ci;
|
|
@ -6,6 +6,7 @@ CREATE TABLE `users_groups` (
|
|||
`is_mod` boolean DEFAULT 0 not null,
|
||||
`is_admin` boolean DEFAULT 0 not null,
|
||||
`is_banned` boolean DEFAULT 0 not null,
|
||||
`user_count` int DEFAULT 0 not null,
|
||||
`tag` varchar(50) DEFAULT '' not null,
|
||||
primary key(`gid`)
|
||||
) CHARSET=utf8mb4 COLLATE utf8mb4_general_ci;
|
|
@ -3,5 +3,6 @@ CREATE TABLE `revisions` (
|
|||
`content` text not null,
|
||||
`contentID` int not null,
|
||||
`contentType` varchar (100) DEFAULT 'replies' not null,
|
||||
`createdAt` timestamp not null
|
||||
`createdAt` timestamp not null,
|
||||
primary key(`reviseID`)
|
||||
);
|
|
@ -6,6 +6,7 @@ CREATE TABLE `users_groups` (
|
|||
`is_mod` boolean DEFAULT 0 not null,
|
||||
`is_admin` boolean DEFAULT 0 not null,
|
||||
`is_banned` boolean DEFAULT 0 not null,
|
||||
`user_count` int DEFAULT 0 not null,
|
||||
`tag` varchar (50) DEFAULT '' not null,
|
||||
primary key(`gid`)
|
||||
);
|
216
template_list.go
216
template_list.go
|
@ -126,177 +126,181 @@ var topic_20 = []byte(`' type="text" />
|
|||
var topic_21 = []byte(`
|
||||
</div>
|
||||
</div>
|
||||
`)
|
||||
var topic_22 = []byte(`
|
||||
<article class="rowblock post_container poll">
|
||||
<div class="rowitem passive editable_parent post_item poll_item `)
|
||||
var topic_22 = []byte(`" style="background-image: url(`)
|
||||
var topic_23 = []byte(`), url(/static/`)
|
||||
var topic_24 = []byte(`/post-avatar-bg.jpg);background-position: 0px `)
|
||||
var topic_25 = []byte(`-1`)
|
||||
var topic_26 = []byte(`0px;background-repeat:no-repeat, repeat-y;">
|
||||
var topic_23 = []byte(`" style="background-image: url(`)
|
||||
var topic_24 = []byte(`), url(/static/`)
|
||||
var topic_25 = []byte(`/post-avatar-bg.jpg);background-position: 0px `)
|
||||
var topic_26 = []byte(`-1`)
|
||||
var topic_27 = []byte(`0px;background-repeat:no-repeat, repeat-y;">
|
||||
<div class="topic_content user_content" style="margin:0;padding:0;">
|
||||
`)
|
||||
var topic_27 = []byte(`
|
||||
var topic_28 = []byte(`
|
||||
<div class="poll_option">
|
||||
<input form="poll_`)
|
||||
var topic_28 = []byte(`_form" id="poll_option_`)
|
||||
var topic_29 = []byte(`" name="poll_option_input" type="checkbox" value="`)
|
||||
var topic_30 = []byte(`" />
|
||||
var topic_29 = []byte(`_form" id="poll_option_`)
|
||||
var topic_30 = []byte(`" name="poll_option_input" type="checkbox" value="`)
|
||||
var topic_31 = []byte(`" />
|
||||
<label class="poll_option_label" for="poll_option_`)
|
||||
var topic_31 = []byte(`">
|
||||
var topic_32 = []byte(`">
|
||||
<div class="sel"></div>
|
||||
</label>
|
||||
<span id="poll_option_text_`)
|
||||
var topic_32 = []byte(`" class="poll_option_text">`)
|
||||
var topic_33 = []byte(`</span>
|
||||
var topic_33 = []byte(`" class="poll_option_text">`)
|
||||
var topic_34 = []byte(`</span>
|
||||
</div>
|
||||
`)
|
||||
var topic_34 = []byte(`
|
||||
var topic_35 = []byte(`
|
||||
<div class="poll_buttons">
|
||||
<button form="poll_`)
|
||||
var topic_35 = []byte(`_form" class="poll_vote_button">Vote</button>
|
||||
var topic_36 = []byte(`_form" class="poll_vote_button">Vote</button>
|
||||
<button class="poll_results_button" data-poll-id="`)
|
||||
var topic_36 = []byte(`">Results</button>
|
||||
var topic_37 = []byte(`">Results</button>
|
||||
<a href="#"><button class="poll_cancel_button">Cancel</button></a>
|
||||
</div>
|
||||
</div>
|
||||
<div id="poll_results_`)
|
||||
var topic_37 = []byte(`" class="poll_results auto_hide">
|
||||
var topic_38 = []byte(`" class="poll_results auto_hide">
|
||||
<div class="topic_content user_content"></div>
|
||||
</div>
|
||||
</div>
|
||||
</article>
|
||||
`)
|
||||
var topic_39 = []byte(`
|
||||
|
||||
<article itemscope itemtype="http://schema.org/CreativeWork" class="rowblock post_container top_post" aria-label="The opening post for this topic">
|
||||
<div class="rowitem passive editable_parent post_item `)
|
||||
var topic_38 = []byte(`" style="background-image: url(`)
|
||||
var topic_39 = []byte(`), url(/static/`)
|
||||
var topic_40 = []byte(`/post-avatar-bg.jpg);background-position: 0px `)
|
||||
var topic_41 = []byte(`-1`)
|
||||
var topic_42 = []byte(`0px;background-repeat:no-repeat, repeat-y;">
|
||||
var topic_40 = []byte(`" style="background-image: url(`)
|
||||
var topic_41 = []byte(`), url(/static/`)
|
||||
var topic_42 = []byte(`/post-avatar-bg.jpg);background-position: 0px `)
|
||||
var topic_43 = []byte(`-1`)
|
||||
var topic_44 = []byte(`0px;background-repeat:no-repeat, repeat-y;">
|
||||
<p class="hide_on_edit topic_content user_content" itemprop="text" style="margin:0;padding:0;">`)
|
||||
var topic_43 = []byte(`</p>
|
||||
var topic_45 = []byte(`</p>
|
||||
<textarea name="topic_content" class="show_on_edit topic_content_input">`)
|
||||
var topic_44 = []byte(`</textarea>
|
||||
var topic_46 = []byte(`</textarea>
|
||||
|
||||
<span class="controls" aria-label="Controls and Author Information">
|
||||
|
||||
<a href="`)
|
||||
var topic_45 = []byte(`" class="username real_username" rel="author">`)
|
||||
var topic_46 = []byte(`</a>
|
||||
var topic_47 = []byte(`" class="username real_username" rel="author">`)
|
||||
var topic_48 = []byte(`</a>
|
||||
`)
|
||||
var topic_47 = []byte(`<a href="/topic/like/submit/`)
|
||||
var topic_48 = []byte(`?session=`)
|
||||
var topic_49 = []byte(`" class="mod_button" title="Love it" `)
|
||||
var topic_50 = []byte(`aria-label="Unlike this topic"`)
|
||||
var topic_51 = []byte(`aria-label="Like this topic"`)
|
||||
var topic_52 = []byte(` style="color:#202020;">
|
||||
var topic_49 = []byte(`<a href="/topic/like/submit/`)
|
||||
var topic_50 = []byte(`?session=`)
|
||||
var topic_51 = []byte(`" class="mod_button" title="Love it" `)
|
||||
var topic_52 = []byte(`aria-label="Unlike this topic"`)
|
||||
var topic_53 = []byte(`aria-label="Like this topic"`)
|
||||
var topic_54 = []byte(` style="color:#202020;">
|
||||
<button class="username like_label"`)
|
||||
var topic_53 = []byte(` style="background-color:#D6FFD6;"`)
|
||||
var topic_54 = []byte(`></button></a>`)
|
||||
var topic_55 = []byte(`<a href='/topic/edit/`)
|
||||
var topic_56 = []byte(`' class="mod_button open_edit" style="font-weight:normal;" title="Edit Topic" aria-label="Edit this topic"><button class="username edit_label"></button></a>`)
|
||||
var topic_57 = []byte(`<a href='/topic/delete/submit/`)
|
||||
var topic_58 = []byte(`?session=`)
|
||||
var topic_59 = []byte(`' class="mod_button" style="font-weight:normal;" title="Delete Topic" aria-label="Delete this topic"><button class="username trash_label"></button></a>`)
|
||||
var topic_60 = []byte(`<a class="mod_button" href='/topic/unlock/submit/`)
|
||||
var topic_61 = []byte(`?session=`)
|
||||
var topic_62 = []byte(`' style="font-weight:normal;" title="Unlock Topic" aria-label="Unlock this topic"><button class="username unlock_label"></button></a>`)
|
||||
var topic_63 = []byte(`<a href='/topic/lock/submit/`)
|
||||
var topic_64 = []byte(`?session=`)
|
||||
var topic_65 = []byte(`' class="mod_button" style="font-weight:normal;" title="Lock Topic" aria-label="Lock this topic"><button class="username lock_label"></button></a>`)
|
||||
var topic_66 = []byte(`<a class="mod_button" href='/topic/unstick/submit/`)
|
||||
var topic_67 = []byte(`?session=`)
|
||||
var topic_68 = []byte(`' style="font-weight:normal;" title="Unpin Topic" aria-label="Unpin this topic"><button class="username unpin_label"></button></a>`)
|
||||
var topic_69 = []byte(`<a href='/topic/stick/submit/`)
|
||||
var topic_70 = []byte(`?session=`)
|
||||
var topic_71 = []byte(`' class="mod_button" style="font-weight:normal;" title="Pin Topic" aria-label="Pin this topic"><button class="username pin_label"></button></a>`)
|
||||
var topic_72 = []byte(`<a class="mod_button" href='/users/ips/?ip=`)
|
||||
var topic_73 = []byte(`' style="font-weight:normal;" title="View IP" aria-label="The poster's IP is `)
|
||||
var topic_74 = []byte(`"><button class="username ip_label"></button></a>`)
|
||||
var topic_75 = []byte(`
|
||||
var topic_55 = []byte(` style="background-color:#D6FFD6;"`)
|
||||
var topic_56 = []byte(`></button></a>`)
|
||||
var topic_57 = []byte(`<a href='/topic/edit/`)
|
||||
var topic_58 = []byte(`' class="mod_button open_edit" style="font-weight:normal;" title="Edit Topic" aria-label="Edit this topic"><button class="username edit_label"></button></a>`)
|
||||
var topic_59 = []byte(`<a href='/topic/delete/submit/`)
|
||||
var topic_60 = []byte(`?session=`)
|
||||
var topic_61 = []byte(`' class="mod_button" style="font-weight:normal;" title="Delete Topic" aria-label="Delete this topic"><button class="username trash_label"></button></a>`)
|
||||
var topic_62 = []byte(`<a class="mod_button" href='/topic/unlock/submit/`)
|
||||
var topic_63 = []byte(`?session=`)
|
||||
var topic_64 = []byte(`' style="font-weight:normal;" title="Unlock Topic" aria-label="Unlock this topic"><button class="username unlock_label"></button></a>`)
|
||||
var topic_65 = []byte(`<a href='/topic/lock/submit/`)
|
||||
var topic_66 = []byte(`?session=`)
|
||||
var topic_67 = []byte(`' class="mod_button" style="font-weight:normal;" title="Lock Topic" aria-label="Lock this topic"><button class="username lock_label"></button></a>`)
|
||||
var topic_68 = []byte(`<a class="mod_button" href='/topic/unstick/submit/`)
|
||||
var topic_69 = []byte(`?session=`)
|
||||
var topic_70 = []byte(`' style="font-weight:normal;" title="Unpin Topic" aria-label="Unpin this topic"><button class="username unpin_label"></button></a>`)
|
||||
var topic_71 = []byte(`<a href='/topic/stick/submit/`)
|
||||
var topic_72 = []byte(`?session=`)
|
||||
var topic_73 = []byte(`' class="mod_button" style="font-weight:normal;" title="Pin Topic" aria-label="Pin this topic"><button class="username pin_label"></button></a>`)
|
||||
var topic_74 = []byte(`<a class="mod_button" href='/users/ips/?ip=`)
|
||||
var topic_75 = []byte(`' style="font-weight:normal;" title="View IP" aria-label="The poster's IP is `)
|
||||
var topic_76 = []byte(`"><button class="username ip_label"></button></a>`)
|
||||
var topic_77 = []byte(`
|
||||
<a href="/report/submit/`)
|
||||
var topic_76 = []byte(`?session=`)
|
||||
var topic_77 = []byte(`&type=topic" class="mod_button report_item" style="font-weight:normal;" title="Flag this topic" aria-label="Flag this topic" rel="nofollow"><button class="username flag_label"></button></a>
|
||||
var topic_78 = []byte(`?session=`)
|
||||
var topic_79 = []byte(`&type=topic" class="mod_button report_item" style="font-weight:normal;" title="Flag this topic" aria-label="Flag this topic" rel="nofollow"><button class="username flag_label"></button></a>
|
||||
|
||||
`)
|
||||
var topic_78 = []byte(`<a class="username hide_on_micro like_count" aria-label="The number of likes on this topic">`)
|
||||
var topic_79 = []byte(`</a><a class="username hide_on_micro like_count_label" title="Like Count"></a>`)
|
||||
var topic_80 = []byte(`<a class="username hide_on_micro user_tag">`)
|
||||
var topic_81 = []byte(`</a>`)
|
||||
var topic_82 = []byte(`<a class="username hide_on_micro level" aria-label="The poster's level">`)
|
||||
var topic_83 = []byte(`</a><a class="username hide_on_micro level_label" style="float:right;" title="Level"></a>`)
|
||||
var topic_84 = []byte(`
|
||||
var topic_80 = []byte(`<a class="username hide_on_micro like_count" aria-label="The number of likes on this topic">`)
|
||||
var topic_81 = []byte(`</a><a class="username hide_on_micro like_count_label" title="Like Count"></a>`)
|
||||
var topic_82 = []byte(`<a class="username hide_on_micro user_tag">`)
|
||||
var topic_83 = []byte(`</a>`)
|
||||
var topic_84 = []byte(`<a class="username hide_on_micro level" aria-label="The poster's level">`)
|
||||
var topic_85 = []byte(`</a><a class="username hide_on_micro level_label" style="float:right;" title="Level"></a>`)
|
||||
var topic_86 = []byte(`
|
||||
|
||||
</span>
|
||||
</div>
|
||||
</article>
|
||||
|
||||
<div class="rowblock post_container" aria-label="The current page for this topic" style="overflow: hidden;">`)
|
||||
var topic_85 = []byte(`
|
||||
var topic_87 = []byte(`
|
||||
<article itemscope itemtype="http://schema.org/CreativeWork" class="rowitem passive deletable_block editable_parent post_item action_item">
|
||||
<span class="action_icon" style="font-size: 18px;padding-right: 5px;">`)
|
||||
var topic_86 = []byte(`</span>
|
||||
var topic_88 = []byte(`</span>
|
||||
<span itemprop="text">`)
|
||||
var topic_87 = []byte(`</span>
|
||||
var topic_89 = []byte(`</span>
|
||||
</article>
|
||||
`)
|
||||
var topic_88 = []byte(`
|
||||
var topic_90 = []byte(`
|
||||
<article itemscope itemtype="http://schema.org/CreativeWork" class="rowitem passive deletable_block editable_parent post_item `)
|
||||
var topic_89 = []byte(`" style="background-image: url(`)
|
||||
var topic_90 = []byte(`), url(/static/`)
|
||||
var topic_91 = []byte(`/post-avatar-bg.jpg);background-position: 0px `)
|
||||
var topic_92 = []byte(`-1`)
|
||||
var topic_93 = []byte(`0px;background-repeat:no-repeat, repeat-y;">
|
||||
var topic_91 = []byte(`" style="background-image: url(`)
|
||||
var topic_92 = []byte(`), url(/static/`)
|
||||
var topic_93 = []byte(`/post-avatar-bg.jpg);background-position: 0px `)
|
||||
var topic_94 = []byte(`-1`)
|
||||
var topic_95 = []byte(`0px;background-repeat:no-repeat, repeat-y;">
|
||||
`)
|
||||
var topic_94 = []byte(`
|
||||
var topic_96 = []byte(`
|
||||
<p class="editable_block user_content" itemprop="text" style="margin:0;padding:0;">`)
|
||||
var topic_95 = []byte(`</p>
|
||||
var topic_97 = []byte(`</p>
|
||||
|
||||
<span class="controls">
|
||||
|
||||
<a href="`)
|
||||
var topic_96 = []byte(`" class="username real_username" rel="author">`)
|
||||
var topic_97 = []byte(`</a>
|
||||
var topic_98 = []byte(`" class="username real_username" rel="author">`)
|
||||
var topic_99 = []byte(`</a>
|
||||
`)
|
||||
var topic_98 = []byte(`<a href="/reply/like/submit/`)
|
||||
var topic_99 = []byte(`?session=`)
|
||||
var topic_100 = []byte(`" class="mod_button" title="Love it" style="color:#202020;"><button class="username like_label"`)
|
||||
var topic_101 = []byte(` style="background-color:#D6FFD6;"`)
|
||||
var topic_102 = []byte(`></button></a>`)
|
||||
var topic_103 = []byte(`<a href="/reply/edit/submit/`)
|
||||
var topic_104 = []byte(`?session=`)
|
||||
var topic_105 = []byte(`" class="mod_button" title="Edit Reply"><button class="username edit_item edit_label"></button></a>`)
|
||||
var topic_106 = []byte(`<a href="/reply/delete/submit/`)
|
||||
var topic_107 = []byte(`?session=`)
|
||||
var topic_108 = []byte(`" class="mod_button" title="Delete Reply"><button class="username delete_item trash_label"></button></a>`)
|
||||
var topic_109 = []byte(`<a class="mod_button" href='/users/ips/?ip=`)
|
||||
var topic_110 = []byte(`' style="font-weight:normal;" title="View IP"><button class="username ip_label"></button></a>`)
|
||||
var topic_111 = []byte(`
|
||||
var topic_100 = []byte(`<a href="/reply/like/submit/`)
|
||||
var topic_101 = []byte(`?session=`)
|
||||
var topic_102 = []byte(`" class="mod_button" title="Love it" style="color:#202020;"><button class="username like_label"`)
|
||||
var topic_103 = []byte(` style="background-color:#D6FFD6;"`)
|
||||
var topic_104 = []byte(`></button></a>`)
|
||||
var topic_105 = []byte(`<a href="/reply/edit/submit/`)
|
||||
var topic_106 = []byte(`?session=`)
|
||||
var topic_107 = []byte(`" class="mod_button" title="Edit Reply"><button class="username edit_item edit_label"></button></a>`)
|
||||
var topic_108 = []byte(`<a href="/reply/delete/submit/`)
|
||||
var topic_109 = []byte(`?session=`)
|
||||
var topic_110 = []byte(`" class="mod_button" title="Delete Reply"><button class="username delete_item trash_label"></button></a>`)
|
||||
var topic_111 = []byte(`<a class="mod_button" href='/users/ips/?ip=`)
|
||||
var topic_112 = []byte(`' style="font-weight:normal;" title="View IP"><button class="username ip_label"></button></a>`)
|
||||
var topic_113 = []byte(`
|
||||
<a href="/report/submit/`)
|
||||
var topic_112 = []byte(`?session=`)
|
||||
var topic_113 = []byte(`&type=reply" class="mod_button report_item" title="Flag this reply" aria-label="Flag this reply" rel="nofollow"><button class="username report_item flag_label"></button></a>
|
||||
var topic_114 = []byte(`?session=`)
|
||||
var topic_115 = []byte(`&type=reply" class="mod_button report_item" title="Flag this reply" aria-label="Flag this reply" rel="nofollow"><button class="username report_item flag_label"></button></a>
|
||||
|
||||
`)
|
||||
var topic_114 = []byte(`<a class="username hide_on_micro like_count">`)
|
||||
var topic_115 = []byte(`</a><a class="username hide_on_micro like_count_label" title="Like Count"></a>`)
|
||||
var topic_116 = []byte(`<a class="username hide_on_micro user_tag">`)
|
||||
var topic_117 = []byte(`</a>`)
|
||||
var topic_118 = []byte(`<a class="username hide_on_micro level">`)
|
||||
var topic_119 = []byte(`</a><a class="username hide_on_micro level_label" style="float:right;" title="Level"></a>`)
|
||||
var topic_120 = []byte(`
|
||||
var topic_116 = []byte(`<a class="username hide_on_micro like_count">`)
|
||||
var topic_117 = []byte(`</a><a class="username hide_on_micro like_count_label" title="Like Count"></a>`)
|
||||
var topic_118 = []byte(`<a class="username hide_on_micro user_tag">`)
|
||||
var topic_119 = []byte(`</a>`)
|
||||
var topic_120 = []byte(`<a class="username hide_on_micro level">`)
|
||||
var topic_121 = []byte(`</a><a class="username hide_on_micro level_label" style="float:right;" title="Level"></a>`)
|
||||
var topic_122 = []byte(`
|
||||
|
||||
</span>
|
||||
</article>
|
||||
`)
|
||||
var topic_121 = []byte(`</div>
|
||||
var topic_123 = []byte(`</div>
|
||||
|
||||
`)
|
||||
var topic_122 = []byte(`
|
||||
var topic_124 = []byte(`
|
||||
<div class="rowblock topic_reply_form quick_create_form">
|
||||
<form id="quick_post_form" enctype="multipart/form-data" action="/reply/create/?session=`)
|
||||
var topic_123 = []byte(`" method="post"></form>
|
||||
var topic_125 = []byte(`" method="post"></form>
|
||||
<input form="quick_post_form" name="tid" value='`)
|
||||
var topic_124 = []byte(`' type="hidden" />
|
||||
var topic_126 = []byte(`' type="hidden" />
|
||||
<input form="quick_post_form" id="has_poll_input" name="has_poll" value="0" type="hidden" />
|
||||
<div class="formrow real_first_child">
|
||||
<div class="formitem">
|
||||
|
@ -317,16 +321,16 @@ var topic_124 = []byte(`' type="hidden" />
|
|||
<button form="quick_post_form" name="reply-button" class="formbutton">Create Reply</button>
|
||||
<button form="quick_post_form" class="formbutton" id="add_poll_button">Add Poll</button>
|
||||
`)
|
||||
var topic_125 = []byte(`
|
||||
var topic_127 = []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 topic_126 = []byte(`
|
||||
var topic_128 = []byte(`
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`)
|
||||
var topic_127 = []byte(`
|
||||
var topic_129 = []byte(`
|
||||
|
||||
</main>
|
||||
|
||||
|
|
|
@ -127,22 +127,22 @@ w.Write([]byte(tmpl_topic_vars.Topic.Title))
|
|||
w.Write(topic_20)
|
||||
}
|
||||
w.Write(topic_21)
|
||||
w.Write([]byte(tmpl_topic_vars.Topic.ClassName))
|
||||
if tmpl_topic_vars.Poll.ID > 0 {
|
||||
w.Write(topic_22)
|
||||
w.Write([]byte(tmpl_topic_vars.Topic.Avatar))
|
||||
w.Write([]byte(tmpl_topic_vars.Topic.ClassName))
|
||||
w.Write(topic_23)
|
||||
w.Write([]byte(tmpl_topic_vars.Header.Theme.Name))
|
||||
w.Write([]byte(tmpl_topic_vars.Topic.Avatar))
|
||||
w.Write(topic_24)
|
||||
if tmpl_topic_vars.Topic.ContentLines <= 5 {
|
||||
w.Write([]byte(tmpl_topic_vars.Header.Theme.Name))
|
||||
w.Write(topic_25)
|
||||
}
|
||||
if tmpl_topic_vars.Topic.ContentLines <= 5 {
|
||||
w.Write(topic_26)
|
||||
}
|
||||
w.Write(topic_27)
|
||||
if len(tmpl_topic_vars.Poll.QuickOptions) != 0 {
|
||||
for _, item := range tmpl_topic_vars.Poll.QuickOptions {
|
||||
w.Write(topic_27)
|
||||
w.Write([]byte(strconv.Itoa(tmpl_topic_vars.Poll.ID)))
|
||||
w.Write(topic_28)
|
||||
w.Write([]byte(strconv.Itoa(item.ID)))
|
||||
w.Write([]byte(strconv.Itoa(tmpl_topic_vars.Poll.ID)))
|
||||
w.Write(topic_29)
|
||||
w.Write([]byte(strconv.Itoa(item.ID)))
|
||||
w.Write(topic_30)
|
||||
|
@ -150,214 +150,218 @@ w.Write([]byte(strconv.Itoa(item.ID)))
|
|||
w.Write(topic_31)
|
||||
w.Write([]byte(strconv.Itoa(item.ID)))
|
||||
w.Write(topic_32)
|
||||
w.Write([]byte(item.Value))
|
||||
w.Write([]byte(strconv.Itoa(item.ID)))
|
||||
w.Write(topic_33)
|
||||
}
|
||||
}
|
||||
w.Write([]byte(item.Value))
|
||||
w.Write(topic_34)
|
||||
w.Write([]byte(strconv.Itoa(tmpl_topic_vars.Poll.ID)))
|
||||
}
|
||||
}
|
||||
w.Write(topic_35)
|
||||
w.Write([]byte(strconv.Itoa(tmpl_topic_vars.Poll.ID)))
|
||||
w.Write(topic_36)
|
||||
w.Write([]byte(strconv.Itoa(tmpl_topic_vars.Poll.ID)))
|
||||
w.Write(topic_37)
|
||||
w.Write([]byte(tmpl_topic_vars.Topic.ClassName))
|
||||
w.Write([]byte(strconv.Itoa(tmpl_topic_vars.Poll.ID)))
|
||||
w.Write(topic_38)
|
||||
w.Write([]byte(tmpl_topic_vars.Topic.Avatar))
|
||||
}
|
||||
w.Write(topic_39)
|
||||
w.Write([]byte(tmpl_topic_vars.Header.Theme.Name))
|
||||
w.Write([]byte(tmpl_topic_vars.Topic.ClassName))
|
||||
w.Write(topic_40)
|
||||
if tmpl_topic_vars.Topic.ContentLines <= 5 {
|
||||
w.Write([]byte(tmpl_topic_vars.Topic.Avatar))
|
||||
w.Write(topic_41)
|
||||
}
|
||||
w.Write([]byte(tmpl_topic_vars.Header.Theme.Name))
|
||||
w.Write(topic_42)
|
||||
w.Write([]byte(tmpl_topic_vars.Topic.ContentHTML))
|
||||
if tmpl_topic_vars.Topic.ContentLines <= 5 {
|
||||
w.Write(topic_43)
|
||||
w.Write([]byte(tmpl_topic_vars.Topic.Content))
|
||||
w.Write(topic_44)
|
||||
w.Write([]byte(tmpl_topic_vars.Topic.UserLink))
|
||||
w.Write(topic_45)
|
||||
w.Write([]byte(tmpl_topic_vars.Topic.CreatedByName))
|
||||
w.Write(topic_46)
|
||||
if tmpl_topic_vars.CurrentUser.Perms.LikeItem {
|
||||
w.Write(topic_47)
|
||||
w.Write([]byte(strconv.Itoa(tmpl_topic_vars.Topic.ID)))
|
||||
w.Write(topic_48)
|
||||
w.Write([]byte(tmpl_topic_vars.CurrentUser.Session))
|
||||
w.Write(topic_49)
|
||||
if tmpl_topic_vars.Topic.Liked {
|
||||
w.Write(topic_50)
|
||||
} else {
|
||||
w.Write(topic_51)
|
||||
}
|
||||
w.Write(topic_52)
|
||||
w.Write(topic_44)
|
||||
w.Write([]byte(tmpl_topic_vars.Topic.ContentHTML))
|
||||
w.Write(topic_45)
|
||||
w.Write([]byte(tmpl_topic_vars.Topic.Content))
|
||||
w.Write(topic_46)
|
||||
w.Write([]byte(tmpl_topic_vars.Topic.UserLink))
|
||||
w.Write(topic_47)
|
||||
w.Write([]byte(tmpl_topic_vars.Topic.CreatedByName))
|
||||
w.Write(topic_48)
|
||||
if tmpl_topic_vars.CurrentUser.Perms.LikeItem {
|
||||
w.Write(topic_49)
|
||||
w.Write([]byte(strconv.Itoa(tmpl_topic_vars.Topic.ID)))
|
||||
w.Write(topic_50)
|
||||
w.Write([]byte(tmpl_topic_vars.CurrentUser.Session))
|
||||
w.Write(topic_51)
|
||||
if tmpl_topic_vars.Topic.Liked {
|
||||
w.Write(topic_52)
|
||||
} else {
|
||||
w.Write(topic_53)
|
||||
}
|
||||
w.Write(topic_54)
|
||||
}
|
||||
if tmpl_topic_vars.CurrentUser.Perms.EditTopic {
|
||||
if tmpl_topic_vars.Topic.Liked {
|
||||
w.Write(topic_55)
|
||||
w.Write([]byte(strconv.Itoa(tmpl_topic_vars.Topic.ID)))
|
||||
}
|
||||
w.Write(topic_56)
|
||||
}
|
||||
if tmpl_topic_vars.CurrentUser.Perms.DeleteTopic {
|
||||
if tmpl_topic_vars.CurrentUser.Perms.EditTopic {
|
||||
w.Write(topic_57)
|
||||
w.Write([]byte(strconv.Itoa(tmpl_topic_vars.Topic.ID)))
|
||||
w.Write(topic_58)
|
||||
w.Write([]byte(tmpl_topic_vars.CurrentUser.Session))
|
||||
}
|
||||
if tmpl_topic_vars.CurrentUser.Perms.DeleteTopic {
|
||||
w.Write(topic_59)
|
||||
w.Write([]byte(strconv.Itoa(tmpl_topic_vars.Topic.ID)))
|
||||
w.Write(topic_60)
|
||||
w.Write([]byte(tmpl_topic_vars.CurrentUser.Session))
|
||||
w.Write(topic_61)
|
||||
}
|
||||
if tmpl_topic_vars.CurrentUser.Perms.CloseTopic {
|
||||
if tmpl_topic_vars.Topic.IsClosed {
|
||||
w.Write(topic_60)
|
||||
w.Write([]byte(strconv.Itoa(tmpl_topic_vars.Topic.ID)))
|
||||
w.Write(topic_61)
|
||||
w.Write([]byte(tmpl_topic_vars.CurrentUser.Session))
|
||||
w.Write(topic_62)
|
||||
} else {
|
||||
w.Write(topic_63)
|
||||
w.Write([]byte(strconv.Itoa(tmpl_topic_vars.Topic.ID)))
|
||||
w.Write(topic_64)
|
||||
w.Write(topic_63)
|
||||
w.Write([]byte(tmpl_topic_vars.CurrentUser.Session))
|
||||
w.Write(topic_64)
|
||||
} else {
|
||||
w.Write(topic_65)
|
||||
w.Write([]byte(strconv.Itoa(tmpl_topic_vars.Topic.ID)))
|
||||
w.Write(topic_66)
|
||||
w.Write([]byte(tmpl_topic_vars.CurrentUser.Session))
|
||||
w.Write(topic_67)
|
||||
}
|
||||
}
|
||||
if tmpl_topic_vars.CurrentUser.Perms.PinTopic {
|
||||
if tmpl_topic_vars.Topic.Sticky {
|
||||
w.Write(topic_66)
|
||||
w.Write([]byte(strconv.Itoa(tmpl_topic_vars.Topic.ID)))
|
||||
w.Write(topic_67)
|
||||
w.Write([]byte(tmpl_topic_vars.CurrentUser.Session))
|
||||
w.Write(topic_68)
|
||||
} else {
|
||||
w.Write(topic_69)
|
||||
w.Write([]byte(strconv.Itoa(tmpl_topic_vars.Topic.ID)))
|
||||
w.Write(topic_70)
|
||||
w.Write(topic_69)
|
||||
w.Write([]byte(tmpl_topic_vars.CurrentUser.Session))
|
||||
w.Write(topic_70)
|
||||
} else {
|
||||
w.Write(topic_71)
|
||||
w.Write([]byte(strconv.Itoa(tmpl_topic_vars.Topic.ID)))
|
||||
w.Write(topic_72)
|
||||
w.Write([]byte(tmpl_topic_vars.CurrentUser.Session))
|
||||
w.Write(topic_73)
|
||||
}
|
||||
}
|
||||
if tmpl_topic_vars.CurrentUser.Perms.ViewIPs {
|
||||
w.Write(topic_72)
|
||||
w.Write([]byte(tmpl_topic_vars.Topic.IPAddress))
|
||||
w.Write(topic_73)
|
||||
w.Write([]byte(tmpl_topic_vars.Topic.IPAddress))
|
||||
w.Write(topic_74)
|
||||
}
|
||||
w.Write([]byte(tmpl_topic_vars.Topic.IPAddress))
|
||||
w.Write(topic_75)
|
||||
w.Write([]byte(strconv.Itoa(tmpl_topic_vars.Topic.ID)))
|
||||
w.Write([]byte(tmpl_topic_vars.Topic.IPAddress))
|
||||
w.Write(topic_76)
|
||||
w.Write([]byte(tmpl_topic_vars.CurrentUser.Session))
|
||||
}
|
||||
w.Write(topic_77)
|
||||
if tmpl_topic_vars.Topic.LikeCount > 0 {
|
||||
w.Write([]byte(strconv.Itoa(tmpl_topic_vars.Topic.ID)))
|
||||
w.Write(topic_78)
|
||||
w.Write([]byte(strconv.Itoa(tmpl_topic_vars.Topic.LikeCount)))
|
||||
w.Write([]byte(tmpl_topic_vars.CurrentUser.Session))
|
||||
w.Write(topic_79)
|
||||
if tmpl_topic_vars.Topic.LikeCount > 0 {
|
||||
w.Write(topic_80)
|
||||
w.Write([]byte(strconv.Itoa(tmpl_topic_vars.Topic.LikeCount)))
|
||||
w.Write(topic_81)
|
||||
}
|
||||
if tmpl_topic_vars.Topic.Tag != "" {
|
||||
w.Write(topic_80)
|
||||
w.Write([]byte(tmpl_topic_vars.Topic.Tag))
|
||||
w.Write(topic_81)
|
||||
} else {
|
||||
w.Write(topic_82)
|
||||
w.Write([]byte(strconv.Itoa(tmpl_topic_vars.Topic.Level)))
|
||||
w.Write([]byte(tmpl_topic_vars.Topic.Tag))
|
||||
w.Write(topic_83)
|
||||
}
|
||||
} else {
|
||||
w.Write(topic_84)
|
||||
w.Write([]byte(strconv.Itoa(tmpl_topic_vars.Topic.Level)))
|
||||
w.Write(topic_85)
|
||||
}
|
||||
w.Write(topic_86)
|
||||
if len(tmpl_topic_vars.ItemList) != 0 {
|
||||
for _, item := range tmpl_topic_vars.ItemList {
|
||||
if item.ActionType != "" {
|
||||
w.Write(topic_85)
|
||||
w.Write([]byte(item.ActionIcon))
|
||||
w.Write(topic_86)
|
||||
w.Write([]byte(item.ActionType))
|
||||
w.Write(topic_87)
|
||||
} else {
|
||||
w.Write([]byte(item.ActionIcon))
|
||||
w.Write(topic_88)
|
||||
w.Write([]byte(item.ClassName))
|
||||
w.Write([]byte(item.ActionType))
|
||||
w.Write(topic_89)
|
||||
w.Write([]byte(item.Avatar))
|
||||
} else {
|
||||
w.Write(topic_90)
|
||||
w.Write([]byte(tmpl_topic_vars.Header.Theme.Name))
|
||||
w.Write([]byte(item.ClassName))
|
||||
w.Write(topic_91)
|
||||
if item.ContentLines <= 5 {
|
||||
w.Write([]byte(item.Avatar))
|
||||
w.Write(topic_92)
|
||||
}
|
||||
w.Write([]byte(tmpl_topic_vars.Header.Theme.Name))
|
||||
w.Write(topic_93)
|
||||
if item.ContentLines <= 5 {
|
||||
w.Write(topic_94)
|
||||
w.Write([]byte(item.ContentHtml))
|
||||
w.Write(topic_95)
|
||||
w.Write([]byte(item.UserLink))
|
||||
w.Write(topic_96)
|
||||
w.Write([]byte(item.CreatedByName))
|
||||
w.Write(topic_97)
|
||||
if tmpl_topic_vars.CurrentUser.Perms.LikeItem {
|
||||
w.Write(topic_98)
|
||||
w.Write([]byte(strconv.Itoa(item.ID)))
|
||||
w.Write(topic_99)
|
||||
w.Write([]byte(tmpl_topic_vars.CurrentUser.Session))
|
||||
w.Write(topic_100)
|
||||
if item.Liked {
|
||||
w.Write(topic_101)
|
||||
}
|
||||
w.Write(topic_95)
|
||||
w.Write(topic_96)
|
||||
w.Write([]byte(item.ContentHtml))
|
||||
w.Write(topic_97)
|
||||
w.Write([]byte(item.UserLink))
|
||||
w.Write(topic_98)
|
||||
w.Write([]byte(item.CreatedByName))
|
||||
w.Write(topic_99)
|
||||
if tmpl_topic_vars.CurrentUser.Perms.LikeItem {
|
||||
w.Write(topic_100)
|
||||
w.Write([]byte(strconv.Itoa(item.ID)))
|
||||
w.Write(topic_101)
|
||||
w.Write([]byte(tmpl_topic_vars.CurrentUser.Session))
|
||||
w.Write(topic_102)
|
||||
if item.Liked {
|
||||
w.Write(topic_103)
|
||||
}
|
||||
w.Write(topic_104)
|
||||
}
|
||||
if tmpl_topic_vars.CurrentUser.Perms.EditReply {
|
||||
w.Write(topic_103)
|
||||
w.Write([]byte(strconv.Itoa(item.ID)))
|
||||
w.Write(topic_104)
|
||||
w.Write([]byte(tmpl_topic_vars.CurrentUser.Session))
|
||||
w.Write(topic_105)
|
||||
w.Write([]byte(strconv.Itoa(item.ID)))
|
||||
w.Write(topic_106)
|
||||
w.Write([]byte(tmpl_topic_vars.CurrentUser.Session))
|
||||
w.Write(topic_107)
|
||||
}
|
||||
if tmpl_topic_vars.CurrentUser.Perms.DeleteReply {
|
||||
w.Write(topic_106)
|
||||
w.Write([]byte(strconv.Itoa(item.ID)))
|
||||
w.Write(topic_107)
|
||||
w.Write([]byte(tmpl_topic_vars.CurrentUser.Session))
|
||||
w.Write(topic_108)
|
||||
}
|
||||
if tmpl_topic_vars.CurrentUser.Perms.ViewIPs {
|
||||
w.Write([]byte(strconv.Itoa(item.ID)))
|
||||
w.Write(topic_109)
|
||||
w.Write([]byte(item.IPAddress))
|
||||
w.Write([]byte(tmpl_topic_vars.CurrentUser.Session))
|
||||
w.Write(topic_110)
|
||||
}
|
||||
if tmpl_topic_vars.CurrentUser.Perms.ViewIPs {
|
||||
w.Write(topic_111)
|
||||
w.Write([]byte(strconv.Itoa(item.ID)))
|
||||
w.Write([]byte(item.IPAddress))
|
||||
w.Write(topic_112)
|
||||
w.Write([]byte(tmpl_topic_vars.CurrentUser.Session))
|
||||
}
|
||||
w.Write(topic_113)
|
||||
if item.LikeCount > 0 {
|
||||
w.Write([]byte(strconv.Itoa(item.ID)))
|
||||
w.Write(topic_114)
|
||||
w.Write([]byte(strconv.Itoa(item.LikeCount)))
|
||||
w.Write([]byte(tmpl_topic_vars.CurrentUser.Session))
|
||||
w.Write(topic_115)
|
||||
if item.LikeCount > 0 {
|
||||
w.Write(topic_116)
|
||||
w.Write([]byte(strconv.Itoa(item.LikeCount)))
|
||||
w.Write(topic_117)
|
||||
}
|
||||
if item.Tag != "" {
|
||||
w.Write(topic_116)
|
||||
w.Write([]byte(item.Tag))
|
||||
w.Write(topic_117)
|
||||
} else {
|
||||
w.Write(topic_118)
|
||||
w.Write([]byte(strconv.Itoa(item.Level)))
|
||||
w.Write([]byte(item.Tag))
|
||||
w.Write(topic_119)
|
||||
}
|
||||
} else {
|
||||
w.Write(topic_120)
|
||||
}
|
||||
}
|
||||
}
|
||||
w.Write([]byte(strconv.Itoa(item.Level)))
|
||||
w.Write(topic_121)
|
||||
if tmpl_topic_vars.CurrentUser.Perms.CreateReply {
|
||||
}
|
||||
w.Write(topic_122)
|
||||
w.Write([]byte(tmpl_topic_vars.CurrentUser.Session))
|
||||
}
|
||||
}
|
||||
}
|
||||
w.Write(topic_123)
|
||||
w.Write([]byte(strconv.Itoa(tmpl_topic_vars.Topic.ID)))
|
||||
if tmpl_topic_vars.CurrentUser.Perms.CreateReply {
|
||||
w.Write(topic_124)
|
||||
if tmpl_topic_vars.CurrentUser.Perms.UploadFiles {
|
||||
w.Write([]byte(tmpl_topic_vars.CurrentUser.Session))
|
||||
w.Write(topic_125)
|
||||
}
|
||||
w.Write([]byte(strconv.Itoa(tmpl_topic_vars.Topic.ID)))
|
||||
w.Write(topic_126)
|
||||
}
|
||||
if tmpl_topic_vars.CurrentUser.Perms.UploadFiles {
|
||||
w.Write(topic_127)
|
||||
}
|
||||
w.Write(topic_128)
|
||||
}
|
||||
w.Write(topic_129)
|
||||
w.Write(footer_0)
|
||||
w.Write([]byte(common.BuildWidget("footer",tmpl_topic_vars.Header)))
|
||||
w.Write(footer_1)
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
{{template "header.html" . }}
|
||||
<div class="colstack panel_stack">
|
||||
{{template "panel-menu.html" . }}
|
||||
<main id="panel_dashboard_right" class="colstack_right">
|
||||
<main id="panel_analytics_right" class="colstack_right">
|
||||
<form id="timeRangeForm" name="timeRangeForm" action="/panel/analytics/agent/{{.Agent}}" method="get">
|
||||
<div class="colstack_item colstack_head">
|
||||
<div class="rowitem">
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
{{template "header.html" . }}
|
||||
<div class="colstack panel_stack">
|
||||
{{template "panel-menu.html" . }}
|
||||
<main id="panel_dashboard_right" class="colstack_right">
|
||||
<main id="panel_analytics_right" class="colstack_right">
|
||||
<form id="timeRangeForm" name="timeRangeForm" action="/panel/analytics/agents/" method="get">
|
||||
<div class="colstack_item colstack_head">
|
||||
<div class="rowitem">
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
{{template "header.html" . }}
|
||||
<div class="colstack panel_stack">
|
||||
{{template "panel-menu.html" . }}
|
||||
<main id="panel_dashboard_right" class="colstack_right">
|
||||
<main id="panel_analytics_right" class="colstack_right">
|
||||
<form id="timeRangeForm" name="timeRangeForm" action="/panel/analytics/posts/" method="get">
|
||||
<div class="colstack_item colstack_head">
|
||||
<div class="rowitem">
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
{{template "header.html" . }}
|
||||
<div class="colstack panel_stack">
|
||||
{{template "panel-menu.html" . }}
|
||||
<main id="panel_dashboard_right" class="colstack_right">
|
||||
<main id="panel_analytics_right" class="colstack_right">
|
||||
<form id="timeRangeForm" name="timeRangeForm" action="/panel/analytics/referrer/{{.Agent}}" method="get">
|
||||
<div class="colstack_item colstack_head">
|
||||
<div class="rowitem">
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
{{template "header.html" . }}
|
||||
<div class="colstack panel_stack">
|
||||
{{template "panel-menu.html" . }}
|
||||
<main id="panel_dashboard_right" class="colstack_right">
|
||||
<main id="panel_analytics_right" class="colstack_right">
|
||||
<form id="timeRangeForm" name="timeRangeForm" action="/panel/analytics/referrers/" method="get">
|
||||
<div class="colstack_item colstack_head">
|
||||
<div class="rowitem">
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
{{template "header.html" . }}
|
||||
<div class="colstack panel_stack">
|
||||
{{template "panel-menu.html" . }}
|
||||
<main id="panel_dashboard_right" class="colstack_right">
|
||||
<main id="panel_analytics_right" class="colstack_right">
|
||||
<form id="timeRangeForm" name="timeRangeForm" action="/panel/analytics/route/{{.Route}}" method="get">
|
||||
<div class="colstack_item colstack_head">
|
||||
<div class="rowitem">
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
{{template "header.html" . }}
|
||||
<div class="colstack panel_stack">
|
||||
{{template "panel-menu.html" . }}
|
||||
<main id="panel_dashboard_right" class="colstack_right">
|
||||
<main id="panel_analytics_right" class="colstack_right">
|
||||
<form id="timeRangeForm" name="timeRangeForm" action="/panel/analytics/routes/" method="get">
|
||||
<div class="colstack_item colstack_head">
|
||||
<div class="rowitem">
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
{{template "header.html" . }}
|
||||
<div class="colstack panel_stack">
|
||||
{{template "panel-menu.html" . }}
|
||||
<main id="panel_dashboard_right" class="colstack_right">
|
||||
<main id="panel_analytics_right" class="colstack_right">
|
||||
<form id="timeRangeForm" name="timeRangeForm" action="/panel/analytics/system/{{.Agent}}" method="get">
|
||||
<div class="colstack_item colstack_head">
|
||||
<div class="rowitem">
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
{{template "header.html" . }}
|
||||
<div class="colstack panel_stack">
|
||||
{{template "panel-menu.html" . }}
|
||||
<main id="panel_dashboard_right" class="colstack_right">
|
||||
<main id="panel_analytics_right" class="colstack_right">
|
||||
<form id="timeRangeForm" name="timeRangeForm" action="/panel/analytics/systems/" method="get">
|
||||
<div class="colstack_item colstack_head">
|
||||
<div class="rowitem">
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
{{template "header.html" . }}
|
||||
<div class="colstack panel_stack">
|
||||
{{template "panel-menu.html" . }}
|
||||
<main id="panel_dashboard_right" class="colstack_right">
|
||||
<main id="panel_analytics_right" class="colstack_right">
|
||||
<form id="timeRangeForm" name="timeRangeForm" action="/panel/analytics/topics/" method="get">
|
||||
<div class="colstack_item colstack_head">
|
||||
<div class="rowitem">
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
{{template "header.html" . }}
|
||||
<div class="colstack panel_stack">
|
||||
{{template "panel-menu.html" . }}
|
||||
<main id="panel_dashboard_right" class="colstack_right">
|
||||
<main id="panel_analytics_right" class="colstack_right">
|
||||
<form id="timeRangeForm" name="timeRangeForm" action="/panel/analytics/views/" method="get">
|
||||
<div class="colstack_item colstack_head">
|
||||
<div class="rowitem">
|
||||
|
|
|
@ -21,6 +21,7 @@
|
|||
{{end}}
|
||||
</div>
|
||||
</div>
|
||||
{{if .Poll.ID}}
|
||||
<article class="rowblock post_container poll">
|
||||
<div class="rowitem passive editable_parent post_item poll_item {{.Topic.ClassName}}" style="background-image: url({{.Topic.Avatar}}), url(/static/{{.Header.Theme.Name}}/post-avatar-bg.jpg);background-position: 0px {{if le .Topic.ContentLines 5}}-1{{end}}0px;background-repeat:no-repeat, repeat-y;">
|
||||
<div class="topic_content user_content" style="margin:0;padding:0;">
|
||||
|
@ -44,6 +45,7 @@
|
|||
</div>
|
||||
</div>
|
||||
</article>
|
||||
{{end}}
|
||||
|
||||
<article itemscope itemtype="http://schema.org/CreativeWork" class="rowblock post_container top_post" aria-label="The opening post for this topic">
|
||||
<div class="rowitem passive editable_parent post_item {{.Topic.ClassName}}" style="background-image: url({{.Topic.Avatar}}), url(/static/{{.Header.Theme.Name}}/post-avatar-bg.jpg);background-position: 0px {{if le .Topic.ContentLines 5}}-1{{end}}0px;background-repeat:no-repeat, repeat-y;">
|
||||
|
|
|
@ -917,6 +917,9 @@ textarea {
|
|||
margin-top: 0px;
|
||||
}
|
||||
|
||||
.postImage {
|
||||
width: 100%;
|
||||
}
|
||||
.post_item {
|
||||
display: flex;
|
||||
margin-bottom: 16px;
|
||||
|
|
|
@ -1,3 +1,16 @@
|
|||
.submenu a {
|
||||
margin-left: 4px;
|
||||
}
|
||||
.colstack_head .rowitem {
|
||||
display: flex;
|
||||
}
|
||||
.colstack_head .rowitem h1, .colstack_head .rowitem a {
|
||||
margin-right: auto;
|
||||
}
|
||||
.colstack_head .rowitem a h1 {
|
||||
margin-right: 0px;
|
||||
}
|
||||
|
||||
.rowlist .tag-mini {
|
||||
font-size: 10px;
|
||||
margin-left: 2px;
|
||||
|
|
Loading…
Reference in New Issue