General:
Added better pagination to /forum/{id} Fixed the ordering of the elements in the pagesets. Added hooks to the task system. Reduced the amount of boilerplate for RunVhook and RunVhookNoReturn. Added RunVhookNeedHook to allow plugin developers to add custom content types to the report system. Added BulkGetMap to the DefaultPollStore. Renamed the MultiServer config setting to ServerCount and made it an integer. Don't cache topic lists for empty groups. Don't load replies for topics without any. Tempra Simple: Added chart and submenu CSS. Improved the input and select CSS. Fixed the pageset CSS. Fixed the poll input CSS. Tempra Conflux: Fixed the quick reply button CSS. Hid the avatar in the quick reply area. Added the poll CSS. Fixed the pageset CSS. Shadow: Fixed the time range selector CSS. Fixed the poll input CSS.
This commit is contained in:
parent
fa065bc584
commit
be47066770
|
@ -42,6 +42,15 @@ var VhookSkippable = map[string]func(...interface{}) (bool, RouteError){
|
|||
|
||||
//var vhookErrorable = map[string]func(...interface{}) (interface{}, RouteError){}
|
||||
|
||||
var taskHooks = map[string][]func() error{
|
||||
"before_half_second_tick": nil,
|
||||
"after_half_second_tick": nil,
|
||||
"before_second_tick": nil,
|
||||
"after_second_tick": nil,
|
||||
"before_fifteen_minute_tick": nil,
|
||||
"after_fifteen_minute_tick": nil,
|
||||
}
|
||||
|
||||
// Coming Soon:
|
||||
type Message interface {
|
||||
ID() int
|
||||
|
@ -225,6 +234,7 @@ func NewPlugin(uname string, name string, author string, url string, settings st
|
|||
}
|
||||
|
||||
// ? - Is this racey?
|
||||
// TODO: Generate the cases in this switch
|
||||
func (plugin *Plugin) AddHook(name string, handler interface{}) {
|
||||
switch h := handler.(type) {
|
||||
case func(interface{}) interface{}:
|
||||
|
@ -254,6 +264,15 @@ func (plugin *Plugin) AddHook(name string, handler interface{}) {
|
|||
PreRenderHooks[name] = append(PreRenderHooks[name], h)
|
||||
}
|
||||
plugin.Hooks[name] = len(PreRenderHooks[name])
|
||||
case func() error: // ! We might want a more generic name, as we might use this signature for things other than tasks hooks
|
||||
if len(taskHooks[name]) == 0 {
|
||||
var hookSlice []func() error
|
||||
hookSlice = append(hookSlice, h)
|
||||
taskHooks[name] = hookSlice
|
||||
} else {
|
||||
taskHooks[name] = append(taskHooks[name], h)
|
||||
}
|
||||
plugin.Hooks[name] = len(taskHooks[name])
|
||||
case func(...interface{}) interface{}:
|
||||
Vhooks[name] = h
|
||||
plugin.Hooks[name] = 0
|
||||
|
@ -266,6 +285,7 @@ func (plugin *Plugin) AddHook(name string, handler interface{}) {
|
|||
}
|
||||
|
||||
// ? - Is this racey?
|
||||
// TODO: Generate the cases in this switch
|
||||
func (plugin *Plugin) RemoveHook(name string, handler interface{}) {
|
||||
switch handler.(type) {
|
||||
case func(interface{}) interface{}:
|
||||
|
@ -295,6 +315,15 @@ func (plugin *Plugin) RemoveHook(name string, handler interface{}) {
|
|||
hook = append(hook[:key], hook[key+1:]...)
|
||||
}
|
||||
PreRenderHooks[name] = hook
|
||||
case func() error:
|
||||
key := plugin.Hooks[name]
|
||||
hook := taskHooks[name]
|
||||
if len(hook) == 1 {
|
||||
hook = []func() error{}
|
||||
} else {
|
||||
hook = append(hook[:key], hook[key+1:]...)
|
||||
}
|
||||
taskHooks[name] = hook
|
||||
case func(...interface{}) interface{}:
|
||||
delete(Vhooks, name)
|
||||
case func(...interface{}) (bool, RouteError):
|
||||
|
@ -340,7 +369,11 @@ func RunHookNoreturn(name string, data interface{}) {
|
|||
}
|
||||
|
||||
func RunVhook(name string, data ...interface{}) interface{} {
|
||||
return Vhooks[name](data...)
|
||||
hook := Vhooks[name]
|
||||
if hook != nil {
|
||||
return hook(data...)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func RunVhookSkippable(name string, data ...interface{}) (bool, RouteError) {
|
||||
|
@ -348,7 +381,29 @@ func RunVhookSkippable(name string, data ...interface{}) (bool, RouteError) {
|
|||
}
|
||||
|
||||
func RunVhookNoreturn(name string, data ...interface{}) {
|
||||
_ = Vhooks[name](data...)
|
||||
hook := Vhooks[name]
|
||||
if hook != nil {
|
||||
_ = hook(data...)
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Find a better way of doing this
|
||||
func RunVhookNeedHook(name string, data ...interface{}) (ret interface{}, hasHook bool) {
|
||||
hook := Vhooks[name]
|
||||
if hook != nil {
|
||||
return hook(data...), true
|
||||
}
|
||||
return nil, false
|
||||
}
|
||||
|
||||
func RunTaskHook(name string) error {
|
||||
for _, hook := range taskHooks[name] {
|
||||
err := hook()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Trying to get a teeny bit of type-safety where-ever possible, especially for such a critical set of hooks
|
||||
|
@ -360,14 +415,14 @@ func RunSshook(name string, data string) string {
|
|||
}
|
||||
|
||||
func RunPreRenderHook(name string, w http.ResponseWriter, r *http.Request, user *User, data interface{}) (halt bool) {
|
||||
// This hook runs on ALL pre_render hooks
|
||||
// This hook runs on ALL PreRender hooks
|
||||
for _, hook := range PreRenderHooks["pre_render"] {
|
||||
if hook(w, r, user, data) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
// The actual pre_render hook
|
||||
// The actual PreRender hook
|
||||
for _, hook := range PreRenderHooks[name] {
|
||||
if hook(w, r, user, data) {
|
||||
return true
|
||||
|
|
|
@ -232,9 +232,7 @@ func (mgs *MemoryGroupStore) Create(name string, tag string, isAdmin bool, isMod
|
|||
var blankIntList []int
|
||||
var pluginPerms = make(map[string]bool)
|
||||
var pluginPermsBytes = []byte("{}")
|
||||
if Vhooks["create_group_preappend"] != nil {
|
||||
RunVhook("create_group_preappend", &pluginPerms, &pluginPermsBytes)
|
||||
}
|
||||
|
||||
// Generate the forum permissions based on the presets...
|
||||
fdata, err := Forums.GetAll()
|
||||
|
|
|
@ -79,6 +79,7 @@ type ForumPage struct {
|
|||
Header *HeaderVars
|
||||
ItemList []*TopicsRow
|
||||
Forum *Forum
|
||||
PageList []int
|
||||
Page int
|
||||
LastPage int
|
||||
}
|
||||
|
|
|
@ -3,6 +3,9 @@ package common
|
|||
import (
|
||||
"database/sql"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"log"
|
||||
"strconv"
|
||||
|
||||
"../query_gen/lib"
|
||||
)
|
||||
|
@ -116,6 +119,92 @@ func (store *DefaultPollStore) Get(id int) (*Poll, error) {
|
|||
return poll, err
|
||||
}
|
||||
|
||||
// TODO: Optimise the query to avoid preparing it on the spot? Maybe, use knowledge of the most common IN() parameter counts?
|
||||
// TODO: ID of 0 should always error?
|
||||
func (store *DefaultPollStore) BulkGetMap(ids []int) (list map[int]*Poll, err error) {
|
||||
var idCount = len(ids)
|
||||
list = make(map[int]*Poll)
|
||||
if idCount == 0 {
|
||||
return list, nil
|
||||
}
|
||||
|
||||
var stillHere []int
|
||||
sliceList := store.cache.BulkGet(ids)
|
||||
for i, sliceItem := range sliceList {
|
||||
if sliceItem != nil {
|
||||
list[sliceItem.ID] = sliceItem
|
||||
} else {
|
||||
stillHere = append(stillHere, ids[i])
|
||||
}
|
||||
}
|
||||
ids = stillHere
|
||||
|
||||
// If every user is in the cache, then return immediately
|
||||
if len(ids) == 0 {
|
||||
return list, nil
|
||||
}
|
||||
|
||||
// TODO: Add a function for the qlist stuff
|
||||
var qlist string
|
||||
var pollIDList []interface{}
|
||||
for _, id := range ids {
|
||||
pollIDList = append(pollIDList, strconv.Itoa(id))
|
||||
qlist += "?,"
|
||||
}
|
||||
qlist = qlist[0 : len(qlist)-1]
|
||||
|
||||
acc := qgen.Builder.Accumulator()
|
||||
rows, err := acc.Select("polls").Columns("pollID, parentID, parentTable, type, options, votes").Where("pollID IN(" + qlist + ")").Query(pollIDList...)
|
||||
if err != nil {
|
||||
return list, err
|
||||
}
|
||||
|
||||
for rows.Next() {
|
||||
poll := &Poll{ID: 0}
|
||||
var optionTxt []byte
|
||||
err := rows.Scan(&poll.ID, &poll.ParentID, &poll.ParentTable, &poll.Type, &optionTxt, &poll.VoteCount)
|
||||
if err != nil {
|
||||
return list, err
|
||||
}
|
||||
|
||||
err = json.Unmarshal(optionTxt, &poll.Options)
|
||||
if err != nil {
|
||||
return list, err
|
||||
}
|
||||
poll.QuickOptions = store.unpackOptionsMap(poll.Options)
|
||||
store.cache.Set(poll)
|
||||
|
||||
list[poll.ID] = poll
|
||||
}
|
||||
|
||||
// Did we miss any polls?
|
||||
if idCount > len(list) {
|
||||
var sidList string
|
||||
for _, id := range ids {
|
||||
_, ok := list[id]
|
||||
if !ok {
|
||||
sidList += strconv.Itoa(id) + ","
|
||||
}
|
||||
}
|
||||
|
||||
// We probably don't need this, but it might be useful in case of bugs in BulkCascadeGetMap
|
||||
if sidList == "" {
|
||||
if Dev.DebugMode {
|
||||
log.Print("This data is sampled later in the BulkCascadeGetMap function, so it might miss the cached IDs")
|
||||
log.Print("idCount", idCount)
|
||||
log.Print("ids", ids)
|
||||
log.Print("list", list)
|
||||
}
|
||||
return list, errors.New("We weren't able to find a poll, but we don't know which one")
|
||||
}
|
||||
sidList = sidList[0 : len(sidList)-1]
|
||||
|
||||
err = errors.New("Unable to find the polls with the following IDs: " + sidList)
|
||||
}
|
||||
|
||||
return list, err
|
||||
}
|
||||
|
||||
func (store *DefaultPollStore) Reload(id int) error {
|
||||
poll := &Poll{ID: id}
|
||||
var optionTxt []byte
|
||||
|
|
|
@ -67,7 +67,7 @@ type config struct {
|
|||
StaffCSS string // ? - Move this into the settings table? Might be better to implement this as Group CSS
|
||||
DefaultForum int // The forum posts go in by default, this used to be covered by the Uncategorised Forum, but we want to replace it with a more robust solution. Make this a setting?
|
||||
MinifyTemplates bool
|
||||
MultiServer bool
|
||||
ServerCount int
|
||||
|
||||
Noavatar string // ? - Move this into the settings table?
|
||||
ItemsPerPage int // ? - Move this into the settings table?
|
||||
|
@ -104,6 +104,9 @@ func VerifyConfig() error {
|
|||
if !Forums.Exists(Config.DefaultForum) {
|
||||
return errors.New("Invalid default forum")
|
||||
}
|
||||
if Config.ServerCount < 1 {
|
||||
return errors.New("You can't have less than one server")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
|
@ -85,7 +85,13 @@ func HandleExpiredScheduledGroups() error {
|
|||
}
|
||||
|
||||
// TODO: Use AddScheduledSecondTask
|
||||
// TODO: Be a little more granular with the synchronisation
|
||||
func HandleServerSync() error {
|
||||
// We don't want to run any unnecessary queries when there is nothing to synchronise
|
||||
/*if Config.ServerCount > 1 {
|
||||
return nil
|
||||
}*/
|
||||
|
||||
var lastUpdate time.Time
|
||||
err := taskStmts.getSync.QueryRow().Scan(&lastUpdate)
|
||||
if err != nil {
|
||||
|
@ -93,7 +99,6 @@ func HandleServerSync() error {
|
|||
}
|
||||
|
||||
if lastUpdate.After(lastSync) {
|
||||
// TODO: A more granular sync
|
||||
err = Forums.LoadForums()
|
||||
if err != nil {
|
||||
log.Print("Unable to reload the forums")
|
||||
|
|
|
@ -162,7 +162,7 @@ func compileTemplates() error {
|
|||
//var topicList []TopicUser
|
||||
//topicList = append(topicList,TopicUser{1,"topic-title","Topic Title","The topic content.",1,false,false,"Date","Date",1,"","127.0.0.1",0,1,"classname","","admin-fred","Admin Fred",config.DefaultGroup,"",0,"","","","",58,false})
|
||||
forumItem := BlankForum(1, "general-forum.1", "General Forum", "Where the general stuff happens", true, "all", 0, "", 0)
|
||||
forumPage := ForumPage{"General Forum", user, headerVars, topicsList, forumItem, 1, 1}
|
||||
forumPage := ForumPage{"General Forum", user, headerVars, topicsList, forumItem, []int{1}, 1, 1}
|
||||
forumTmpl, err := c.Compile("forum.html", "templates/", "common.ForumPage", forumPage, varList)
|
||||
if err != nil {
|
||||
return err
|
||||
|
|
|
@ -122,6 +122,10 @@ func (tList *DefaultTopicList) Tick() error {
|
|||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if group.UserCount == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
topicList, forumList, pageList, page, lastPage, err := tList.getListByGroup(group, 1)
|
||||
if err != nil {
|
||||
return err
|
||||
|
@ -258,9 +262,7 @@ func (tList *DefaultTopicList) getList(page int, argList []interface{}, qlist st
|
|||
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
|
||||
|
|
|
@ -128,14 +128,14 @@ func (mus *DefaultUserStore) BulkGetMap(ids []int) (list map[int]*User, err erro
|
|||
acc := qgen.Builder.Accumulator()
|
||||
rows, err := acc.Select("users").Columns("uid, name, group, is_super_admin, session, email, avatar, message, url_prefix, url_name, level, score, last_ip, temp_group").Where("uid IN(" + qlist + ")").Query(uidList...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return list, err
|
||||
}
|
||||
|
||||
for rows.Next() {
|
||||
user := &User{Loggedin: true}
|
||||
err := rows.Scan(&user.ID, &user.Name, &user.Group, &user.IsSuperAdmin, &user.Session, &user.Email, &user.Avatar, &user.Message, &user.URLPrefix, &user.URLName, &user.Level, &user.Score, &user.LastIP, &user.TempGroup)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return list, err
|
||||
}
|
||||
|
||||
user.Init()
|
||||
|
|
|
@ -51,7 +51,7 @@ func init() {
|
|||
common.Config.StaffCSS = "staff_post"
|
||||
common.Config.DefaultForum = 2
|
||||
common.Config.MinifyTemplates = true
|
||||
common.Config.MultiServer = false // Experimental: Enable Cross-Server Synchronisation and several other features
|
||||
common.Config.ServerCount = 1 // Experimental: Enable Cross-Server Synchronisation and several other features
|
||||
|
||||
//common.Config.Noavatar = "https://api.adorable.io/avatars/{width}/{id}@{site_url}.png"
|
||||
common.Config.Noavatar = "https://api.adorable.io/avatars/285/{id}@{site_url}.png"
|
||||
|
|
|
@ -148,7 +148,7 @@ func init() {
|
|||
common.Config.StaffCSS = "staff_post"
|
||||
common.Config.DefaultForum = 2
|
||||
common.Config.MinifyTemplates = true
|
||||
common.Config.MultiServer = false // Experimental: Enable Cross-Server Synchronisation and several other features
|
||||
common.Config.ServerCount = 1 // Experimental: Enable Cross-Server Synchronisation and several other features
|
||||
|
||||
//common.Config.Noavatar = "https://api.adorable.io/avatars/{width}/{id}@{site_url}.png"
|
||||
common.Config.Noavatar = "https://api.adorable.io/avatars/285/{id}@{site_url}.png"
|
||||
|
|
20
main.go
20
main.go
|
@ -289,14 +289,20 @@ func main() {
|
|||
fifteenMinuteTicker := time.NewTicker(15 * time.Minute)
|
||||
//hourTicker := time.NewTicker(1 * time.Hour)
|
||||
go func() {
|
||||
var runHook = func(name string) {
|
||||
err := common.RunTaskHook(name)
|
||||
if err != nil {
|
||||
common.LogError(err)
|
||||
}
|
||||
}
|
||||
for {
|
||||
select {
|
||||
case <-halfSecondTicker.C:
|
||||
// TODO: Add a plugin hook here
|
||||
runHook("before_half_second_tick")
|
||||
runTasks(common.ScheduledHalfSecondTasks)
|
||||
// TODO: Add a plugin hook here
|
||||
runHook("after_half_second_tick")
|
||||
case <-secondTicker.C:
|
||||
// TODO: Add a plugin hook here
|
||||
runHook("before_second_tick")
|
||||
runTasks(common.ScheduledSecondTasks)
|
||||
|
||||
// TODO: Stop hard-coding this
|
||||
|
@ -317,16 +323,14 @@ func main() {
|
|||
// TODO: Alert the admin, if CPU usage, RAM usage, or the number of posts in the past second are too high
|
||||
// TODO: Clean-up alerts with no unread matches which are over two weeks old. Move this to a 24 hour task?
|
||||
// TODO: Rescan the static files for changes
|
||||
|
||||
// TODO: Add a plugin hook here
|
||||
runHook("after_second_tick")
|
||||
case <-fifteenMinuteTicker.C:
|
||||
// TODO: Add a plugin hook here
|
||||
runHook("before_fifteen_minute_tick")
|
||||
runTasks(common.ScheduledFifteenMinuteTasks)
|
||||
|
||||
// TODO: Automatically lock topics, if they're really old, and the associated setting is enabled.
|
||||
// TODO: Publish scheduled posts.
|
||||
|
||||
// TODO: Add a plugin hook here
|
||||
runHook("after_fifteen_minute_tick")
|
||||
}
|
||||
|
||||
// TODO: Handle the daily clean-up.
|
||||
|
|
|
@ -405,10 +405,11 @@ func routeReportSubmit(w http.ResponseWriter, r *http.Request, user common.User,
|
|||
title = "Topic: " + title
|
||||
content = content + "\n\nOriginal Post: #tid-" + strconv.Itoa(itemID)
|
||||
} else {
|
||||
if common.Vhooks["report_preassign"] != nil {
|
||||
common.RunVhookNoreturn("report_preassign", &itemID, &itemType)
|
||||
_, hasHook := common.RunVhookNeedHook("report_preassign", &itemID, &itemType)
|
||||
if hasHook {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Don't try to guess the type
|
||||
return common.LocalError("Unknown type", w, r, user)
|
||||
}
|
||||
|
|
|
@ -97,9 +97,7 @@ func routeForum(w http.ResponseWriter, r *http.Request, user common.User, sfid s
|
|||
topicItem.Link = common.BuildTopicURL(common.NameToSlug(topicItem.Title), topicItem.ID)
|
||||
topicItem.RelativeLastReplyAt = common.RelativeTime(topicItem.LastReplyAt)
|
||||
|
||||
if common.Vhooks["forum_trow_assign"] != nil {
|
||||
common.RunVhook("forum_trow_assign", &topicItem, &forum)
|
||||
}
|
||||
topicList = append(topicList, &topicItem)
|
||||
reqUserList[topicItem.CreatedBy] = true
|
||||
reqUserList[topicItem.LastReplyBy] = true
|
||||
|
@ -130,7 +128,8 @@ func routeForum(w http.ResponseWriter, r *http.Request, user common.User, sfid s
|
|||
topicItem.LastUser = userList[topicItem.LastReplyBy]
|
||||
}
|
||||
|
||||
pi := common.ForumPage{forum.Name, user, headerVars, topicList, forum, page, lastPage}
|
||||
pageList := common.Paginate(forum.TopicCount, common.Config.ItemsPerPage, 5)
|
||||
pi := common.ForumPage{forum.Name, user, headerVars, topicList, forum, pageList, page, lastPage}
|
||||
if common.PreRenderHooks["pre_render_forum"] != nil {
|
||||
if common.RunPreRenderHook("pre_render_forum", w, r, &user, &pi) {
|
||||
return nil
|
||||
|
|
|
@ -37,8 +37,6 @@ func init() {
|
|||
var successJSONBytes = []byte(`{"success":"1"}`)
|
||||
|
||||
func ViewTopic(w http.ResponseWriter, r *http.Request, user common.User, urlBit string) common.RouteError {
|
||||
var err error
|
||||
var replyList []common.ReplyUser
|
||||
page, _ := strconv.Atoi(r.FormValue("page"))
|
||||
|
||||
// SEO URLs...
|
||||
|
@ -110,9 +108,10 @@ func ViewTopic(w http.ResponseWriter, r *http.Request, user common.User, urlBit
|
|||
|
||||
// Calculate the offset
|
||||
offset, page, lastPage := common.PageOffset(topic.PostCount, page, common.Config.ItemsPerPage)
|
||||
tpage := common.TopicPage{topic.Title, user, headerVars, replyList, topic, poll, page, lastPage}
|
||||
tpage := common.TopicPage{topic.Title, user, headerVars, []common.ReplyUser{}, topic, poll, page, lastPage}
|
||||
|
||||
// Get the replies..
|
||||
// Get the replies if we have any...
|
||||
if topic.PostCount > 0 {
|
||||
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)
|
||||
|
@ -173,10 +172,7 @@ func ViewTopic(w http.ResponseWriter, r *http.Request, user common.User, urlBit
|
|||
}
|
||||
replyItem.Liked = false
|
||||
|
||||
if common.Vhooks["topic_reply_row_assign"] != nil {
|
||||
common.RunVhook("topic_reply_row_assign", &tpage, &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)
|
||||
}
|
||||
|
@ -184,8 +180,8 @@ func ViewTopic(w http.ResponseWriter, r *http.Request, user common.User, urlBit
|
|||
if err != nil {
|
||||
return common.InternalError(err, w, r)
|
||||
}
|
||||
}
|
||||
|
||||
//tpage.ItemList = replyList
|
||||
if common.PreRenderHooks["pre_render_view_topic"] != nil {
|
||||
if common.RunPreRenderHook("pre_render_view_topic", w, r, &user, &tpage) {
|
||||
return nil
|
||||
|
@ -195,7 +191,7 @@ func ViewTopic(w http.ResponseWriter, r *http.Request, user common.User, urlBit
|
|||
if err != nil {
|
||||
return common.InternalError(err, w, r)
|
||||
}
|
||||
common.TopicViewCounter.Bump(topic.ID) // TODO Move this into the router?
|
||||
common.TopicViewCounter.Bump(topic.ID) // TODO: Move this into the router?
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -228,9 +224,7 @@ func CreateTopic(w http.ResponseWriter, r *http.Request, user common.User, sfid
|
|||
// Lock this to the forum being linked?
|
||||
// Should we always put it in strictmode when it's linked from another forum? Well, the user might end up changing their mind on what forum they want to post in and it would be a hassle, if they had to switch pages, even if it is a single click for many (exc. mobile)
|
||||
var strictmode bool
|
||||
if common.Vhooks["topic_create_pre_loop"] != nil {
|
||||
common.RunVhook("topic_create_pre_loop", w, r, fid, &headerVars, &user, &strictmode)
|
||||
}
|
||||
|
||||
// TODO: Re-add support for plugin_guilds
|
||||
var forumList []common.Forum
|
||||
|
|
|
@ -3,9 +3,9 @@
|
|||
// Code generated by Gosora. More below:
|
||||
/* This file was automatically generated by the software. Please don't edit it as your changes may be overwritten at any moment. */
|
||||
package main
|
||||
import "strconv"
|
||||
import "net/http"
|
||||
import "./common"
|
||||
import "strconv"
|
||||
|
||||
// nolint
|
||||
func init() {
|
||||
|
@ -93,118 +93,142 @@ w.Write([]byte(strconv.Itoa(tmpl_forum_vars.Forum.ID)))
|
|||
w.Write(forum_4)
|
||||
w.Write([]byte(strconv.Itoa(tmpl_forum_vars.Page + 1)))
|
||||
w.Write(forum_5)
|
||||
w.Write([]byte(strconv.Itoa(tmpl_forum_vars.Forum.ID)))
|
||||
}
|
||||
w.Write(forum_6)
|
||||
w.Write([]byte(strconv.Itoa(tmpl_forum_vars.Page + 1)))
|
||||
if tmpl_forum_vars.CurrentUser.ID != 0 {
|
||||
w.Write(forum_7)
|
||||
}
|
||||
w.Write(forum_8)
|
||||
if tmpl_forum_vars.CurrentUser.ID != 0 {
|
||||
w.Write(forum_9)
|
||||
}
|
||||
w.Write(forum_10)
|
||||
w.Write([]byte(tmpl_forum_vars.Title))
|
||||
w.Write(forum_9)
|
||||
if tmpl_forum_vars.CurrentUser.ID != 0 {
|
||||
if tmpl_forum_vars.CurrentUser.Perms.CreateTopic {
|
||||
w.Write(forum_10)
|
||||
w.Write([]byte(strconv.Itoa(tmpl_forum_vars.Forum.ID)))
|
||||
w.Write(forum_11)
|
||||
if tmpl_forum_vars.CurrentUser.ID != 0 {
|
||||
if tmpl_forum_vars.CurrentUser.Perms.CreateTopic {
|
||||
w.Write(forum_12)
|
||||
w.Write([]byte(strconv.Itoa(tmpl_forum_vars.Forum.ID)))
|
||||
w.Write(forum_13)
|
||||
w.Write(forum_14)
|
||||
} else {
|
||||
w.Write(forum_13)
|
||||
}
|
||||
w.Write(forum_14)
|
||||
}
|
||||
w.Write(forum_15)
|
||||
}
|
||||
w.Write(forum_16)
|
||||
}
|
||||
w.Write(forum_17)
|
||||
if tmpl_forum_vars.CurrentUser.ID != 0 {
|
||||
w.Write(forum_18)
|
||||
w.Write(forum_16)
|
||||
if tmpl_forum_vars.CurrentUser.Perms.CreateTopic {
|
||||
w.Write(forum_19)
|
||||
w.Write(forum_17)
|
||||
w.Write([]byte(tmpl_forum_vars.CurrentUser.Avatar))
|
||||
w.Write(forum_20)
|
||||
w.Write(forum_18)
|
||||
w.Write([]byte(strconv.Itoa(tmpl_forum_vars.Forum.ID)))
|
||||
w.Write(forum_21)
|
||||
w.Write(forum_19)
|
||||
if tmpl_forum_vars.CurrentUser.Perms.UploadFiles {
|
||||
w.Write(forum_20)
|
||||
}
|
||||
w.Write(forum_21)
|
||||
}
|
||||
}
|
||||
w.Write(forum_22)
|
||||
}
|
||||
w.Write(forum_23)
|
||||
}
|
||||
}
|
||||
w.Write(forum_24)
|
||||
if len(tmpl_forum_vars.ItemList) != 0 {
|
||||
for _, item := range tmpl_forum_vars.ItemList {
|
||||
w.Write(forum_25)
|
||||
w.Write(forum_23)
|
||||
w.Write([]byte(strconv.Itoa(item.ID)))
|
||||
w.Write(forum_24)
|
||||
if item.Sticky {
|
||||
w.Write(forum_25)
|
||||
} else {
|
||||
if item.IsClosed {
|
||||
w.Write(forum_26)
|
||||
if item.Sticky {
|
||||
}
|
||||
}
|
||||
w.Write(forum_27)
|
||||
} else {
|
||||
if item.IsClosed {
|
||||
w.Write([]byte(item.Creator.Link))
|
||||
w.Write(forum_28)
|
||||
}
|
||||
}
|
||||
w.Write(forum_29)
|
||||
w.Write([]byte(item.Creator.Link))
|
||||
w.Write(forum_30)
|
||||
w.Write([]byte(item.Creator.Avatar))
|
||||
w.Write(forum_29)
|
||||
w.Write([]byte(item.Creator.Name))
|
||||
w.Write(forum_30)
|
||||
w.Write([]byte(item.Creator.Name))
|
||||
w.Write(forum_31)
|
||||
w.Write([]byte(item.Creator.Name))
|
||||
w.Write(forum_32)
|
||||
w.Write([]byte(item.Creator.Name))
|
||||
w.Write(forum_33)
|
||||
w.Write([]byte(item.Link))
|
||||
w.Write(forum_34)
|
||||
w.Write(forum_32)
|
||||
w.Write([]byte(item.Title))
|
||||
w.Write(forum_35)
|
||||
w.Write(forum_33)
|
||||
w.Write([]byte(item.Creator.Link))
|
||||
w.Write(forum_36)
|
||||
w.Write(forum_34)
|
||||
w.Write([]byte(item.Creator.Name))
|
||||
w.Write(forum_37)
|
||||
w.Write(forum_35)
|
||||
if item.IsClosed {
|
||||
w.Write(forum_36)
|
||||
}
|
||||
if item.Sticky {
|
||||
w.Write(forum_37)
|
||||
}
|
||||
w.Write(forum_38)
|
||||
}
|
||||
if item.Sticky {
|
||||
w.Write(forum_39)
|
||||
}
|
||||
w.Write(forum_40)
|
||||
w.Write([]byte(strconv.Itoa(item.PostCount)))
|
||||
w.Write(forum_41)
|
||||
w.Write(forum_39)
|
||||
w.Write([]byte(strconv.Itoa(item.LikeCount)))
|
||||
w.Write(forum_42)
|
||||
w.Write(forum_40)
|
||||
if item.Sticky {
|
||||
w.Write(forum_43)
|
||||
w.Write(forum_41)
|
||||
} else {
|
||||
if item.IsClosed {
|
||||
w.Write(forum_44)
|
||||
w.Write(forum_42)
|
||||
}
|
||||
}
|
||||
w.Write(forum_45)
|
||||
w.Write(forum_43)
|
||||
w.Write([]byte(item.LastUser.Link))
|
||||
w.Write(forum_46)
|
||||
w.Write(forum_44)
|
||||
w.Write([]byte(item.LastUser.Avatar))
|
||||
w.Write(forum_47)
|
||||
w.Write(forum_45)
|
||||
w.Write([]byte(item.LastUser.Name))
|
||||
w.Write(forum_46)
|
||||
w.Write([]byte(item.LastUser.Name))
|
||||
w.Write(forum_47)
|
||||
w.Write([]byte(item.LastUser.Link))
|
||||
w.Write(forum_48)
|
||||
w.Write([]byte(item.LastUser.Name))
|
||||
w.Write(forum_49)
|
||||
w.Write([]byte(item.LastUser.Link))
|
||||
w.Write(forum_50)
|
||||
w.Write([]byte(item.LastUser.Name))
|
||||
w.Write(forum_51)
|
||||
w.Write([]byte(item.RelativeLastReplyAt))
|
||||
w.Write(forum_52)
|
||||
w.Write(forum_50)
|
||||
}
|
||||
} else {
|
||||
w.Write(forum_53)
|
||||
w.Write(forum_51)
|
||||
if tmpl_forum_vars.CurrentUser.Perms.CreateTopic {
|
||||
w.Write(forum_54)
|
||||
w.Write(forum_52)
|
||||
w.Write([]byte(strconv.Itoa(tmpl_forum_vars.Forum.ID)))
|
||||
w.Write(forum_53)
|
||||
}
|
||||
w.Write(forum_54)
|
||||
}
|
||||
w.Write(forum_55)
|
||||
}
|
||||
if tmpl_forum_vars.LastPage > 1 {
|
||||
w.Write(forum_56)
|
||||
}
|
||||
if tmpl_forum_vars.Page > 1 {
|
||||
w.Write(forum_57)
|
||||
w.Write([]byte(strconv.Itoa(tmpl_forum_vars.Page - 1)))
|
||||
w.Write(forum_58)
|
||||
w.Write([]byte(strconv.Itoa(tmpl_forum_vars.Page - 1)))
|
||||
w.Write(forum_59)
|
||||
}
|
||||
if len(tmpl_forum_vars.PageList) != 0 {
|
||||
for _, item := range tmpl_forum_vars.PageList {
|
||||
w.Write(forum_60)
|
||||
w.Write([]byte(strconv.Itoa(item)))
|
||||
w.Write(forum_61)
|
||||
w.Write([]byte(strconv.Itoa(item)))
|
||||
w.Write(forum_62)
|
||||
}
|
||||
}
|
||||
if tmpl_forum_vars.LastPage != tmpl_forum_vars.Page {
|
||||
w.Write(forum_63)
|
||||
w.Write([]byte(strconv.Itoa(tmpl_forum_vars.Page + 1)))
|
||||
w.Write(forum_64)
|
||||
w.Write([]byte(strconv.Itoa(tmpl_forum_vars.Page + 1)))
|
||||
w.Write(forum_65)
|
||||
}
|
||||
w.Write(forum_66)
|
||||
}
|
||||
w.Write(forum_67)
|
||||
w.Write(footer_0)
|
||||
w.Write([]byte(common.BuildWidget("footer",tmpl_forum_vars.Header)))
|
||||
w.Write(footer_1)
|
||||
|
|
138
template_list.go
138
template_list.go
|
@ -1114,11 +1114,10 @@ var topics_62 = []byte(`
|
|||
var topics_63 = []byte(`
|
||||
<div class="pageset">
|
||||
`)
|
||||
var topics_64 = []byte(`
|
||||
var topics_64 = []byte(`<div class="pageitem"><a href="?page=`)
|
||||
var topics_65 = []byte(`" rel="prev" aria-label="Go to the previous page">Prev</a></div>
|
||||
<link rel="prev" href="?page=`)
|
||||
var topics_65 = []byte(`" />
|
||||
<div class="pageitem"><a href="?page=`)
|
||||
var topics_66 = []byte(`" rel="prev" aria-label="Go to the previous page">Prev</a></div>`)
|
||||
var topics_66 = []byte(`" />`)
|
||||
var topics_67 = []byte(`
|
||||
<div class="pageitem"><a href="?page=`)
|
||||
var topics_68 = []byte(`">`)
|
||||
|
@ -1139,41 +1138,38 @@ var topics_74 = []byte(`
|
|||
var forum_0 = []byte(`<div id="prevFloat" class="prev_button"><a class="prev_link" aria-label="Go to the previous page" rel="prev" href="/forum/`)
|
||||
var forum_1 = []byte(`?page=`)
|
||||
var forum_2 = []byte(`"><</a></div>`)
|
||||
var forum_3 = []byte(`<link rel="prerender" href="/forum/`)
|
||||
var forum_3 = []byte(`<div id="nextFloat" class="next_button"><a class="next_link" aria-label="Go to the next page" rel="next" href="/forum/`)
|
||||
var forum_4 = []byte(`?page=`)
|
||||
var forum_5 = []byte(`" />
|
||||
<div id="nextFloat" class="next_button"><a class="next_link" aria-label="Go to the next page" rel="next" href="/forum/`)
|
||||
var forum_6 = []byte(`?page=`)
|
||||
var forum_7 = []byte(`">></a></div>`)
|
||||
var forum_8 = []byte(`
|
||||
var forum_5 = []byte(`">></a></div>`)
|
||||
var forum_6 = []byte(`
|
||||
|
||||
<main itemscope itemtype="http://schema.org/ItemList">
|
||||
<div id="forum_head_block" class="rowblock rowhead topic_list_title_block">
|
||||
<div class="rowitem forum_title`)
|
||||
var forum_9 = []byte(` has_opt`)
|
||||
var forum_10 = []byte(`">
|
||||
var forum_7 = []byte(` has_opt`)
|
||||
var forum_8 = []byte(`">
|
||||
<h1 itemprop="name">`)
|
||||
var forum_11 = []byte(`</h1>
|
||||
var forum_9 = []byte(`</h1>
|
||||
</div>
|
||||
`)
|
||||
var forum_12 = []byte(`
|
||||
var forum_10 = []byte(`
|
||||
<div class="pre_opt auto_hide"></div>
|
||||
<div class="opt create_topic_opt" title="Create Topic" aria-label="Create a topic"><a class="create_topic_link" href="/topics/create/`)
|
||||
var forum_13 = []byte(`"></a></div>
|
||||
var forum_11 = []byte(`"></a></div>
|
||||
`)
|
||||
var forum_14 = []byte(`
|
||||
var forum_12 = []byte(`
|
||||
<div class="opt mod_opt" title="Moderate">
|
||||
<a class="moderate_link" href="#" aria-label="Moderate Posts"></a>
|
||||
</div>
|
||||
`)
|
||||
var forum_15 = []byte(`<div class="opt locked_opt" title="You don't have the permissions needed to create a topic" aria-label="You don't have the permissions needed to make a topic in this forum"><a></a></div>`)
|
||||
var forum_16 = []byte(`
|
||||
var forum_13 = []byte(`<div class="opt locked_opt" title="You don't have the permissions needed to create a topic" aria-label="You don't have the permissions needed to make a topic in this forum"><a></a></div>`)
|
||||
var forum_14 = []byte(`
|
||||
<div style="clear: both;"></div>
|
||||
`)
|
||||
var forum_17 = []byte(`
|
||||
var forum_15 = []byte(`
|
||||
</div>
|
||||
`)
|
||||
var forum_18 = []byte(`
|
||||
var forum_16 = []byte(`
|
||||
<div class="mod_floater auto_hide">
|
||||
<form method="post">
|
||||
<div class="mod_floater_head">
|
||||
|
@ -1190,13 +1186,13 @@ var forum_18 = []byte(`
|
|||
</form>
|
||||
</div>
|
||||
`)
|
||||
var forum_19 = []byte(`
|
||||
var forum_17 = []byte(`
|
||||
<div id="forum_topic_create_form" class="rowblock topic_create_form quick_create_form" style="display: none;" aria-label="Quick Topic Form">
|
||||
<form id="quick_post_form" enctype="multipart/form-data" action="/topic/create/submit/" method="post"></form>
|
||||
<img class="little_row_avatar" src="`)
|
||||
var forum_20 = []byte(`" height="64" alt="Your Avatar" title="Your Avatar" />
|
||||
var forum_18 = []byte(`" height="64" alt="Your Avatar" title="Your Avatar" />
|
||||
<input form="quick_post_form" id="topic_board_input" name="topic-board" value="`)
|
||||
var forum_21 = []byte(`" type="hidden">
|
||||
var forum_19 = []byte(`" type="hidden">
|
||||
<div class="main_form">
|
||||
<div class="topic_meta">
|
||||
<div class="formrow topic_name_row real_first_child">
|
||||
|
@ -1220,75 +1216,99 @@ var forum_21 = []byte(`" type="hidden">
|
|||
<button form="quick_post_form" name="topic-button" class="formbutton">Create Topic</button>
|
||||
<button form="quick_post_form" class="formbutton" id="add_poll_button">Add Poll</button>
|
||||
`)
|
||||
var forum_22 = []byte(`
|
||||
var forum_20 = []byte(`
|
||||
<input name="upload_files" form="quick_post_form" id="upload_files" multiple type="file" style="display: none;" />
|
||||
<label for="upload_files" class="formbutton add_file_button">Add File</label>
|
||||
<div id="upload_file_dock"></div>`)
|
||||
var forum_23 = []byte(`
|
||||
var forum_21 = []byte(`
|
||||
<button class="formbutton close_form">Cancel</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`)
|
||||
var forum_24 = []byte(`
|
||||
var forum_22 = []byte(`
|
||||
<div id="forum_topic_list" class="rowblock topic_list">
|
||||
`)
|
||||
var forum_25 = []byte(`<div class="topic_row" data-tid="`)
|
||||
var forum_26 = []byte(`">
|
||||
var forum_23 = []byte(`<div class="topic_row" data-tid="`)
|
||||
var forum_24 = []byte(`">
|
||||
<div class="rowitem topic_left passive datarow `)
|
||||
var forum_27 = []byte(`topic_sticky`)
|
||||
var forum_28 = []byte(`topic_closed`)
|
||||
var forum_29 = []byte(`">
|
||||
var forum_25 = []byte(`topic_sticky`)
|
||||
var forum_26 = []byte(`topic_closed`)
|
||||
var forum_27 = []byte(`">
|
||||
<span class="selector"></span>
|
||||
<a href="`)
|
||||
var forum_30 = []byte(`"><img src="`)
|
||||
var forum_31 = []byte(`" height="64" alt="`)
|
||||
var forum_32 = []byte(`'s Avatar" title="`)
|
||||
var forum_33 = []byte(`'s Avatar" /></a>
|
||||
var forum_28 = []byte(`"><img src="`)
|
||||
var forum_29 = []byte(`" height="64" alt="`)
|
||||
var forum_30 = []byte(`'s Avatar" title="`)
|
||||
var forum_31 = []byte(`'s Avatar" /></a>
|
||||
<span class="topic_inner_left">
|
||||
<a class="rowtopic" href="`)
|
||||
var forum_34 = []byte(`" itemprop="itemListElement"><span>`)
|
||||
var forum_35 = []byte(`</span></a>
|
||||
var forum_32 = []byte(`" itemprop="itemListElement"><span>`)
|
||||
var forum_33 = []byte(`</span></a>
|
||||
<br /><a class="rowsmall starter" href="`)
|
||||
var forum_36 = []byte(`">`)
|
||||
var forum_37 = []byte(`</a>
|
||||
var forum_34 = []byte(`">`)
|
||||
var forum_35 = []byte(`</a>
|
||||
`)
|
||||
var forum_38 = []byte(`<span class="rowsmall topic_status_e topic_status_closed" title="Status: Closed"> | 🔒︎</span>`)
|
||||
var forum_39 = []byte(`<span class="rowsmall topic_status_e topic_status_sticky" title="Status: Pinned"> | 📍︎</span>`)
|
||||
var forum_40 = []byte(`
|
||||
var forum_36 = []byte(`<span class="rowsmall topic_status_e topic_status_closed" title="Status: Closed"> | 🔒︎</span>`)
|
||||
var forum_37 = []byte(`<span class="rowsmall topic_status_e topic_status_sticky" title="Status: Pinned"> | 📍︎</span>`)
|
||||
var forum_38 = []byte(`
|
||||
</span>
|
||||
<span class="topic_inner_right rowsmall" style="float: right;">
|
||||
<span class="replyCount">`)
|
||||
var forum_41 = []byte(`</span><br />
|
||||
var forum_39 = []byte(`</span><br />
|
||||
<span class="likeCount">`)
|
||||
var forum_42 = []byte(`</span>
|
||||
var forum_40 = []byte(`</span>
|
||||
</span>
|
||||
</div>
|
||||
<div class="rowitem topic_right passive datarow `)
|
||||
var forum_43 = []byte(`topic_sticky`)
|
||||
var forum_44 = []byte(`topic_closed`)
|
||||
var forum_45 = []byte(`">
|
||||
var forum_41 = []byte(`topic_sticky`)
|
||||
var forum_42 = []byte(`topic_closed`)
|
||||
var forum_43 = []byte(`">
|
||||
<a href="`)
|
||||
var forum_46 = []byte(`"><img src="`)
|
||||
var forum_47 = []byte(`" height="64" alt="`)
|
||||
var forum_48 = []byte(`'s Avatar" title="`)
|
||||
var forum_49 = []byte(`'s Avatar" /></a>
|
||||
var forum_44 = []byte(`"><img src="`)
|
||||
var forum_45 = []byte(`" height="64" alt="`)
|
||||
var forum_46 = []byte(`'s Avatar" title="`)
|
||||
var forum_47 = []byte(`'s Avatar" /></a>
|
||||
<span>
|
||||
<a href="`)
|
||||
var forum_50 = []byte(`" class="lastName" style="font-size: 14px;">`)
|
||||
var forum_51 = []byte(`</a><br>
|
||||
var forum_48 = []byte(`" class="lastName" style="font-size: 14px;">`)
|
||||
var forum_49 = []byte(`</a><br>
|
||||
<span class="rowsmall lastReplyAt">`)
|
||||
var forum_52 = []byte(`</span>
|
||||
var forum_50 = []byte(`</span>
|
||||
</span>
|
||||
</div>
|
||||
</div>`)
|
||||
var forum_53 = []byte(`<div class="rowitem passive">There aren't any topics in this forum yet.`)
|
||||
var forum_54 = []byte(` <a href="/topics/create/`)
|
||||
var forum_55 = []byte(`">Start one?</a>`)
|
||||
var forum_56 = []byte(`</div>`)
|
||||
var forum_57 = []byte(`
|
||||
var forum_51 = []byte(`<div class="rowitem passive">There aren't any topics in this forum yet.`)
|
||||
var forum_52 = []byte(` <a href="/topics/create/`)
|
||||
var forum_53 = []byte(`">Start one?</a>`)
|
||||
var forum_54 = []byte(`</div>`)
|
||||
var forum_55 = []byte(`
|
||||
</div>
|
||||
|
||||
`)
|
||||
var forum_56 = []byte(`
|
||||
<div class="pageset">
|
||||
`)
|
||||
var forum_57 = []byte(`<div class="pageitem"><a href="?page=`)
|
||||
var forum_58 = []byte(`" rel="prev" aria-label="Go to the previous page">Prev</a></div>
|
||||
<link rel="prev" href="?page=`)
|
||||
var forum_59 = []byte(`" />`)
|
||||
var forum_60 = []byte(`
|
||||
<div class="pageitem"><a href="?page=`)
|
||||
var forum_61 = []byte(`">`)
|
||||
var forum_62 = []byte(`</a></div>
|
||||
`)
|
||||
var forum_63 = []byte(`
|
||||
<link rel="next" href="?page=`)
|
||||
var forum_64 = []byte(`" />
|
||||
<div class="pageitem"><a href="?page=`)
|
||||
var forum_65 = []byte(`" rel="next" aria-label="Go to the next page">Next</a></div>`)
|
||||
var forum_66 = []byte(`
|
||||
</div>
|
||||
`)
|
||||
var forum_67 = []byte(`
|
||||
|
||||
</main>
|
||||
`)
|
||||
var guilds_guild_list_0 = []byte(`
|
||||
|
|
|
@ -1,8 +1,7 @@
|
|||
{{template "header.html" . }}
|
||||
|
||||
{{if gt .Page 1}}<div id="prevFloat" class="prev_button"><a class="prev_link" aria-label="Go to the previous page" rel="prev" href="/forum/{{.Forum.ID}}?page={{subtract .Page 1}}"><</a></div>{{end}}
|
||||
{{if ne .LastPage .Page}}<link rel="prerender" href="/forum/{{.Forum.ID}}?page={{add .Page 1}}" />
|
||||
<div id="nextFloat" class="next_button"><a class="next_link" aria-label="Go to the next page" rel="next" href="/forum/{{.Forum.ID}}?page={{add .Page 1}}">></a></div>{{end}}
|
||||
{{if ne .LastPage .Page}}<div id="nextFloat" class="next_button"><a class="next_link" aria-label="Go to the next page" rel="next" href="/forum/{{.Forum.ID}}?page={{add .Page 1}}">></a></div>{{end}}
|
||||
|
||||
<main itemscope itemtype="http://schema.org/ItemList">
|
||||
<div id="forum_head_block" class="rowblock rowhead topic_list_title_block">
|
||||
|
@ -101,5 +100,19 @@
|
|||
</div>
|
||||
</div>{{else}}<div class="rowitem passive">There aren't any topics in this forum yet.{{if .CurrentUser.Perms.CreateTopic}} <a href="/topics/create/{{.Forum.ID}}">Start one?</a>{{end}}</div>{{end}}
|
||||
</div>
|
||||
|
||||
{{if gt .LastPage 1}}
|
||||
<div class="pageset">
|
||||
{{if gt .Page 1}}<div class="pageitem"><a href="?page={{subtract .Page 1}}" rel="prev" aria-label="Go to the previous page">Prev</a></div>
|
||||
<link rel="prev" href="?page={{subtract .Page 1}}" />{{end}}
|
||||
{{range .PageList}}
|
||||
<div class="pageitem"><a href="?page={{.}}">{{.}}</a></div>
|
||||
{{end}}
|
||||
{{if ne .LastPage .Page}}
|
||||
<link rel="next" href="?page={{add .Page 1}}" />
|
||||
<div class="pageitem"><a href="?page={{add .Page 1}}" rel="next" aria-label="Go to the next page">Next</a></div>{{end}}
|
||||
</div>
|
||||
{{end}}
|
||||
|
||||
</main>
|
||||
{{template "footer.html" . }}
|
||||
|
|
|
@ -128,9 +128,8 @@
|
|||
|
||||
{{if gt .LastPage 1}}
|
||||
<div class="pageset">
|
||||
{{if gt .Page 1}}
|
||||
<link rel="prev" href="?page={{subtract .Page 1}}" />
|
||||
<div class="pageitem"><a href="?page={{subtract .Page 1}}" rel="prev" aria-label="Go to the previous page">Prev</a></div>{{end}}
|
||||
{{if gt .Page 1}}<div class="pageitem"><a href="?page={{subtract .Page 1}}" rel="prev" aria-label="Go to the previous page">Prev</a></div>
|
||||
<link rel="prev" href="?page={{subtract .Page 1}}" />{{end}}
|
||||
{{range .PageList}}
|
||||
<div class="pageitem"><a href="?page={{.}}">{{.}}</a></div>
|
||||
{{end}}
|
||||
|
|
|
@ -484,7 +484,7 @@ textarea.large {
|
|||
padding: 5px;
|
||||
width: calc(100% - 16px);
|
||||
}
|
||||
.formitem select {
|
||||
select {
|
||||
background-color: var(--input-background-color);
|
||||
border: 1px solid var(--input-border-color);
|
||||
color: var(--input-text-color);
|
||||
|
@ -613,6 +613,13 @@ input[type=checkbox]:checked + label.poll_option_label .sel {
|
|||
margin-left: 3px;
|
||||
background: var(--bright-input-border-color);
|
||||
}
|
||||
.pollinput {
|
||||
display: flex;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
.quick_create_form .pollinputlabel {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/*#poll_option_text_0 {
|
||||
color: hsl(359,98%,43%);
|
||||
|
|
|
@ -16,6 +16,11 @@
|
|||
margin-left: 2px;
|
||||
}
|
||||
|
||||
.timeRangeSelector {
|
||||
padding: 2px;
|
||||
margin-top: -3px;
|
||||
margin-bottom: -3px;
|
||||
}
|
||||
.panel_floater {
|
||||
margin-left: auto;
|
||||
}
|
||||
|
|
|
@ -488,24 +488,24 @@ li a {
|
|||
.little_row_avatar {
|
||||
display: none;
|
||||
}
|
||||
.topic_create_form .topic_button_row .formitem {
|
||||
.quick_create_form .quick_button_row .formitem {
|
||||
display: flex;
|
||||
}
|
||||
.topic_create_form .formbutton:first-child {
|
||||
.quick_create_form .formbutton:first-child {
|
||||
margin-left: 0px;
|
||||
margin-right: 5px;
|
||||
}
|
||||
.topic_create_form .formbutton:not(:first-child) {
|
||||
.quick_create_form .formbutton:not(:first-child) {
|
||||
margin-left: 0px;
|
||||
margin-right: 5px;
|
||||
}
|
||||
.topic_create_form .formbutton:last-child {
|
||||
.quick_create_form .formbutton:last-child {
|
||||
margin-left: auto;
|
||||
}
|
||||
.topic_create_form .upload_file_dock {
|
||||
.quick_create_form .upload_file_dock {
|
||||
display: flex;
|
||||
}
|
||||
.topic_create_form .uploadItem {
|
||||
.quick_create_form .uploadItem {
|
||||
display: inline-block;
|
||||
margin-left: 8px;
|
||||
margin-right: 8px;
|
||||
|
@ -765,6 +765,51 @@ button.username {
|
|||
border-bottom: 1.5px inset var(--main-border-color);
|
||||
}
|
||||
|
||||
/* TODO: Show the avatar next to the reply form */
|
||||
.topic_reply_container .userinfo {
|
||||
display: none;
|
||||
}
|
||||
|
||||
input[type=checkbox] {
|
||||
display: none;
|
||||
}
|
||||
input[type=checkbox] + label {
|
||||
display: inline-block;
|
||||
width: 12px;
|
||||
height: 12px;
|
||||
margin-bottom: -2px;
|
||||
border: 1px solid hsl(0, 0%, 80%);
|
||||
background-color: white;
|
||||
}
|
||||
input[type=checkbox]:checked + label .sel {
|
||||
display: inline-block;
|
||||
width: 5px;
|
||||
height: 5px;
|
||||
background-color: white;
|
||||
}
|
||||
input[type=checkbox] + label.poll_option_label {
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
margin-right: 2px;
|
||||
background-color: white;
|
||||
border: 1px solid hsl(0, 0%, 70%);
|
||||
color: #505050;
|
||||
}
|
||||
input[type=checkbox]:checked + label.poll_option_label .sel {
|
||||
display: inline-block;
|
||||
width: 10px;
|
||||
height: 10px;
|
||||
margin-left: 3px;
|
||||
background: hsl(0,0%,70%);
|
||||
}
|
||||
.poll_option {
|
||||
margin-bottom: 1px;
|
||||
}
|
||||
|
||||
.quick_create_form .pollinputlabel {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.simple {
|
||||
background-color: white;
|
||||
}
|
||||
|
@ -945,7 +990,7 @@ button.username {
|
|||
.pageset {
|
||||
display: flex;
|
||||
margin-bottom: 10px;
|
||||
margin-top: -5px;
|
||||
margin-top: 8px;
|
||||
}
|
||||
.pageitem {
|
||||
border: 1px solid var(--main-border-color);
|
||||
|
@ -958,6 +1003,9 @@ button.username {
|
|||
color: black;
|
||||
text-decoration: none;
|
||||
}
|
||||
.colstack_right .pageset {
|
||||
margin-top: -5px;
|
||||
}
|
||||
|
||||
/* Firefox specific CSS */
|
||||
@supports (-moz-appearance: none) {
|
||||
|
|
|
@ -372,8 +372,18 @@ li a {
|
|||
border: none;
|
||||
}
|
||||
|
||||
input, select {
|
||||
padding: 3px;
|
||||
}
|
||||
/* Mostly for textareas */
|
||||
.formitem:only-child { width: 100%; }
|
||||
.formitem:only-child {
|
||||
width: 100%;
|
||||
}
|
||||
.formitem:only-child select {
|
||||
padding: 1px;
|
||||
margin-top: -1px;
|
||||
margin-bottom: -1px;
|
||||
}
|
||||
.formitem textarea {
|
||||
width: 100%;
|
||||
height: 100px;
|
||||
|
@ -383,9 +393,6 @@ li a {
|
|||
margin: 0 auto;
|
||||
float: none;
|
||||
}
|
||||
.formitem:not(:only-child) input, .formitem:not(:only-child) select {
|
||||
padding: 3px;
|
||||
}
|
||||
.formitem:not(:only-child).formlabel {
|
||||
padding-top: 15px;
|
||||
padding-bottom: 12px;
|
||||
|
@ -417,6 +424,9 @@ li a {
|
|||
|
||||
/* Topics */
|
||||
|
||||
.topic_list {
|
||||
border-bottom: none;
|
||||
}
|
||||
.topic_list .topic_row {
|
||||
display: grid;
|
||||
grid-template-columns: calc(100% - 204px) 204px;
|
||||
|
@ -737,6 +747,10 @@ input[type=checkbox]:checked + label.poll_option_label .sel {
|
|||
margin-left: auto;
|
||||
}
|
||||
|
||||
.quick_create_form .pollinputlabel {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.alert {
|
||||
display: block;
|
||||
padding: 5px;
|
||||
|
@ -869,7 +883,7 @@ input[type=checkbox]:checked + label.poll_option_label .sel {
|
|||
.pageset {
|
||||
display: flex;
|
||||
margin-bottom: 10px;
|
||||
margin-top: -5px;
|
||||
margin-top: 8px;
|
||||
}
|
||||
.pageitem {
|
||||
background-color: white;
|
||||
|
@ -882,5 +896,8 @@ input[type=checkbox]:checked + label.poll_option_label .sel {
|
|||
color: black;
|
||||
text-decoration: none;
|
||||
}
|
||||
.colstack_right .pageset {
|
||||
margin-top: -5px;
|
||||
}
|
||||
|
||||
{{template "media.partial.css" }}
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
/* Control Panel */
|
||||
.submenu a {
|
||||
margin-left: 8px;
|
||||
}
|
||||
|
||||
.edit_button:before {
|
||||
content: "Edit";
|
||||
|
@ -126,3 +128,13 @@
|
|||
padding-left: 2px;
|
||||
padding-right: 2px;
|
||||
}
|
||||
.ct_chart {
|
||||
padding-left: 10px;
|
||||
padding-top: 14px;
|
||||
padding-bottom: 4px;
|
||||
padding-right: 10px;
|
||||
margin-bottom: 12px;
|
||||
|
||||
background-color: white;
|
||||
border: 1px solid hsl(0,0%,85%);
|
||||
}
|
Loading…
Reference in New Issue