Commented out more debug code.
Main Menu is now shown on the main menu in the menu list for extra clarity. Travis should now be able to run it's tests. Moved routeChangeTheme to the routes package. Moved routeShowAttachment to the routes package and partially refactored it. Moved routeLikeTopicSubmit to the routes package. Moved routeReplyLikeSubmit to the routes package and partially refactored it. Moved routeProfileReplyCreateSubmit to the routes package. Moved routeLogout to the routes package, now known as routes.AccountLogout. Moved the routeDynamic stub to the routes package, now known as routes.DynamicRoute. Moved the routeUploads stub to the routes package, now known as routes.UploadedFile. Moved the BadRoute stub to the routes package, now known as routes.BadRoute. All routes moved to the routes package have had the route prefix dropped from their name. Simplified the email token route to redirect back to the main email route instead of rendering the same template. Refactored the panel menus to use the new submenu system instead of the old one which had a lot of menu duplication. Added a stub directory for Nox, the next major theme after Cosora. Fixed a bug where the alerts wouldn't load outside of the index. Tweaked the CSS in the topic creation and reply forms on Shadow. Tweaked the padding on the stickies on Shadow. Improved the submenu CSS on every theme. Fixed the submitrow CSS on Shadow, Tempra Conflux. Fixed some double borders on Tempra Conflux. The frontend sidebar should no longer show up in the Control Panel in Tempra Conflux and Tempra Simple. Tweaked the title CSS on Cosora. Tweaked the user manager CSS on Cosora. Changed the primary text colour on Cosora. Fixed attachment images taking up too much space on Cosora. Run the patcher or update script for this commit.
This commit is contained in:
parent
3976c49054
commit
2be884edc1
@ -3,7 +3,6 @@ package common
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"mime"
|
||||
"strings"
|
||||
"sync"
|
||||
@ -38,9 +37,10 @@ type CSSData struct {
|
||||
}
|
||||
|
||||
func (list SFileList) JSTmplInit() error {
|
||||
DebugLog("Initialising the client side templates")
|
||||
var fragMap = make(map[string][][]byte)
|
||||
fragMap["alert"] = tmpl.GetFrag("alert")
|
||||
fmt.Println("fragMap: ", fragMap)
|
||||
DebugLog("fragMap: ", fragMap)
|
||||
return filepath.Walk("./tmpl_client", func(path string, f os.FileInfo, err error) error {
|
||||
if f.IsDir() {
|
||||
return nil
|
||||
@ -75,15 +75,15 @@ func (list SFileList) JSTmplInit() error {
|
||||
if !hasBrace {
|
||||
return errors.New("no right brace found after the template function name")
|
||||
}
|
||||
fmt.Println("spaceIndex: ", spaceIndex)
|
||||
fmt.Println("endBrace: ", endBrace)
|
||||
fmt.Println("string(data[spaceIndex:endBrace]): ", string(data[spaceIndex:endBrace]))
|
||||
//fmt.Println("spaceIndex: ", spaceIndex)
|
||||
//fmt.Println("endBrace: ", endBrace)
|
||||
//fmt.Println("string(data[spaceIndex:endBrace]): ", string(data[spaceIndex:endBrace]))
|
||||
preLen := len(data)
|
||||
data = replace(data, string(data[spaceIndex:endBrace]), "")
|
||||
data = replace(data, "))\n", "\n")
|
||||
endBrace -= preLen - len(data) // Offset it as we've deleted portions
|
||||
|
||||
var showPos = func(data []byte, index int) (out string) {
|
||||
/*var showPos = func(data []byte, index int) (out string) {
|
||||
out = "["
|
||||
for j, char := range data {
|
||||
if index == j {
|
||||
@ -93,16 +93,16 @@ func (list SFileList) JSTmplInit() error {
|
||||
}
|
||||
}
|
||||
return out + "]"
|
||||
}
|
||||
}*/
|
||||
|
||||
// ? Can we just use a regex? I'm thinking of going more efficient, or just outright rolling wasm, this is a temp hack in a place where performance doesn't particularly matter
|
||||
var each = func(phrase string, handle func(index int)) {
|
||||
fmt.Println("find each '" + phrase + "'")
|
||||
//fmt.Println("find each '" + phrase + "'")
|
||||
var index = endBrace
|
||||
var foundIt bool
|
||||
for {
|
||||
fmt.Println("in index: ", index)
|
||||
fmt.Println("pos: ", showPos(data, index))
|
||||
//fmt.Println("in index: ", index)
|
||||
//fmt.Println("pos: ", showPos(data, index))
|
||||
index, foundIt = skipAllUntilCharsExist(data, index, []byte(phrase))
|
||||
if !foundIt {
|
||||
break
|
||||
@ -136,7 +136,7 @@ func (list SFileList) JSTmplInit() error {
|
||||
}
|
||||
})
|
||||
each("if ", func(index int) {
|
||||
fmt.Println("if index: ", index)
|
||||
//fmt.Println("if index: ", index)
|
||||
braceAt, hasBrace := skipUntilIfExists(data, index, '{')
|
||||
if hasBrace {
|
||||
if data[braceAt-1] != ' ' {
|
||||
@ -161,7 +161,7 @@ func (list SFileList) JSTmplInit() error {
|
||||
tmplName := strings.TrimSuffix(path, ".go")
|
||||
fragset, ok := fragMap[strings.TrimPrefix(tmplName, "template_")]
|
||||
if !ok {
|
||||
fmt.Println("tmplName: ", tmplName)
|
||||
DebugLog("tmplName: ", tmplName)
|
||||
return errors.New("couldn't find template in fragmap")
|
||||
}
|
||||
|
||||
|
@ -254,6 +254,7 @@ type PanelThemesPage struct {
|
||||
}
|
||||
|
||||
type PanelMenuListItem struct {
|
||||
Name string
|
||||
ID int
|
||||
ItemCount int
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
package main
|
||||
package config
|
||||
|
||||
import "../common"
|
||||
|
||||
|
@ -21,7 +21,6 @@ type Stmts struct {
|
||||
getTopicBasic *sql.Stmt
|
||||
forumEntryExists *sql.Stmt
|
||||
groupEntryExists *sql.Stmt
|
||||
getAttachment *sql.Stmt
|
||||
getForumTopics *sql.Stmt
|
||||
createReport *sql.Stmt
|
||||
addForumPermsToForum *sql.Stmt
|
||||
@ -154,14 +153,6 @@ func _gen_mssql() (err error) {
|
||||
return err
|
||||
}
|
||||
|
||||
common.DebugLog("Preparing getAttachment statement.")
|
||||
stmts.getAttachment, err = db.Prepare("SELECT [sectionID],[sectionTable],[originID],[originTable],[uploadedBy],[path] FROM [attachments] WHERE [path] = ?1 AND [sectionID] = ?2 AND [sectionTable] = ?3")
|
||||
if err != nil {
|
||||
log.Print("Error in getAttachment statement.")
|
||||
log.Print("Bad Query: ","SELECT [sectionID],[sectionTable],[originID],[originTable],[uploadedBy],[path] FROM [attachments] WHERE [path] = ?1 AND [sectionID] = ?2 AND [sectionTable] = ?3")
|
||||
return err
|
||||
}
|
||||
|
||||
common.DebugLog("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 {
|
||||
|
@ -23,7 +23,6 @@ type Stmts struct {
|
||||
getTopicBasic *sql.Stmt
|
||||
forumEntryExists *sql.Stmt
|
||||
groupEntryExists *sql.Stmt
|
||||
getAttachment *sql.Stmt
|
||||
getForumTopics *sql.Stmt
|
||||
createReport *sql.Stmt
|
||||
addForumPermsToForum *sql.Stmt
|
||||
@ -144,13 +143,6 @@ func _gen_mysql() (err error) {
|
||||
return err
|
||||
}
|
||||
|
||||
common.DebugLog("Preparing getAttachment statement.")
|
||||
stmts.getAttachment, err = db.Prepare("SELECT `sectionID`,`sectionTable`,`originID`,`originTable`,`uploadedBy`,`path` FROM `attachments` WHERE `path` = ? AND `sectionID` = ? AND `sectionTable` = ?")
|
||||
if err != nil {
|
||||
log.Print("Error in getAttachment statement.")
|
||||
return err
|
||||
}
|
||||
|
||||
common.DebugLog("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 {
|
||||
|
@ -24,8 +24,8 @@ var RouteMap = map[string]interface{}{
|
||||
"routes.CustomPage": routes.CustomPage,
|
||||
"routes.ForumList": routes.ForumList,
|
||||
"routes.ViewForum": routes.ViewForum,
|
||||
"routeChangeTheme": routeChangeTheme,
|
||||
"routeShowAttachment": routeShowAttachment,
|
||||
"routes.ChangeTheme": routes.ChangeTheme,
|
||||
"routes.ShowAttachment": routes.ShowAttachment,
|
||||
"common.RouteWebsockets": common.RouteWebsockets,
|
||||
"routeReportSubmit": routeReportSubmit,
|
||||
"routes.CreateTopic": routes.CreateTopic,
|
||||
@ -109,28 +109,28 @@ var RouteMap = map[string]interface{}{
|
||||
"routes.LockTopicSubmit": routes.LockTopicSubmit,
|
||||
"routes.UnlockTopicSubmit": routes.UnlockTopicSubmit,
|
||||
"routes.MoveTopicSubmit": routes.MoveTopicSubmit,
|
||||
"routeLikeTopicSubmit": routeLikeTopicSubmit,
|
||||
"routes.LikeTopicSubmit": routes.LikeTopicSubmit,
|
||||
"routes.ViewTopic": routes.ViewTopic,
|
||||
"routes.CreateReplySubmit": routes.CreateReplySubmit,
|
||||
"routes.ReplyEditSubmit": routes.ReplyEditSubmit,
|
||||
"routes.ReplyDeleteSubmit": routes.ReplyDeleteSubmit,
|
||||
"routeReplyLikeSubmit": routeReplyLikeSubmit,
|
||||
"routeProfileReplyCreateSubmit": routeProfileReplyCreateSubmit,
|
||||
"routes.ReplyLikeSubmit": routes.ReplyLikeSubmit,
|
||||
"routes.ProfileReplyCreateSubmit": routes.ProfileReplyCreateSubmit,
|
||||
"routes.ProfileReplyEditSubmit": routes.ProfileReplyEditSubmit,
|
||||
"routes.ProfileReplyDeleteSubmit": routes.ProfileReplyDeleteSubmit,
|
||||
"routes.PollVote": routes.PollVote,
|
||||
"routes.PollResults": routes.PollResults,
|
||||
"routes.AccountLogin": routes.AccountLogin,
|
||||
"routes.AccountRegister": routes.AccountRegister,
|
||||
"routeLogout": routeLogout,
|
||||
"routes.AccountLogout": routes.AccountLogout,
|
||||
"routes.AccountLoginSubmit": routes.AccountLoginSubmit,
|
||||
"routes.AccountRegisterSubmit": routes.AccountRegisterSubmit,
|
||||
"routeDynamic": routeDynamic,
|
||||
"routeUploads": routeUploads,
|
||||
"routes.DynamicRoute": routes.DynamicRoute,
|
||||
"routes.UploadedFile": routes.UploadedFile,
|
||||
"routes.StaticFile": routes.StaticFile,
|
||||
"routes.RobotsTxt": routes.RobotsTxt,
|
||||
"routes.SitemapXml": routes.SitemapXml,
|
||||
"BadRoute": BadRoute,
|
||||
"routes.BadRoute": routes.BadRoute,
|
||||
}
|
||||
|
||||
// ! NEVER RELY ON THESE REMAINING THE SAME BETWEEN COMMITS
|
||||
@ -140,8 +140,8 @@ var routeMapEnum = map[string]int{
|
||||
"routes.CustomPage": 2,
|
||||
"routes.ForumList": 3,
|
||||
"routes.ViewForum": 4,
|
||||
"routeChangeTheme": 5,
|
||||
"routeShowAttachment": 6,
|
||||
"routes.ChangeTheme": 5,
|
||||
"routes.ShowAttachment": 6,
|
||||
"common.RouteWebsockets": 7,
|
||||
"routeReportSubmit": 8,
|
||||
"routes.CreateTopic": 9,
|
||||
@ -225,28 +225,28 @@ var routeMapEnum = map[string]int{
|
||||
"routes.LockTopicSubmit": 87,
|
||||
"routes.UnlockTopicSubmit": 88,
|
||||
"routes.MoveTopicSubmit": 89,
|
||||
"routeLikeTopicSubmit": 90,
|
||||
"routes.LikeTopicSubmit": 90,
|
||||
"routes.ViewTopic": 91,
|
||||
"routes.CreateReplySubmit": 92,
|
||||
"routes.ReplyEditSubmit": 93,
|
||||
"routes.ReplyDeleteSubmit": 94,
|
||||
"routeReplyLikeSubmit": 95,
|
||||
"routeProfileReplyCreateSubmit": 96,
|
||||
"routes.ReplyLikeSubmit": 95,
|
||||
"routes.ProfileReplyCreateSubmit": 96,
|
||||
"routes.ProfileReplyEditSubmit": 97,
|
||||
"routes.ProfileReplyDeleteSubmit": 98,
|
||||
"routes.PollVote": 99,
|
||||
"routes.PollResults": 100,
|
||||
"routes.AccountLogin": 101,
|
||||
"routes.AccountRegister": 102,
|
||||
"routeLogout": 103,
|
||||
"routes.AccountLogout": 103,
|
||||
"routes.AccountLoginSubmit": 104,
|
||||
"routes.AccountRegisterSubmit": 105,
|
||||
"routeDynamic": 106,
|
||||
"routeUploads": 107,
|
||||
"routes.DynamicRoute": 106,
|
||||
"routes.UploadedFile": 107,
|
||||
"routes.StaticFile": 108,
|
||||
"routes.RobotsTxt": 109,
|
||||
"routes.SitemapXml": 110,
|
||||
"BadRoute": 111,
|
||||
"routes.BadRoute": 111,
|
||||
}
|
||||
var reverseRouteMapEnum = map[int]string{
|
||||
0: "routeAPI",
|
||||
@ -254,8 +254,8 @@ var reverseRouteMapEnum = map[int]string{
|
||||
2: "routes.CustomPage",
|
||||
3: "routes.ForumList",
|
||||
4: "routes.ViewForum",
|
||||
5: "routeChangeTheme",
|
||||
6: "routeShowAttachment",
|
||||
5: "routes.ChangeTheme",
|
||||
6: "routes.ShowAttachment",
|
||||
7: "common.RouteWebsockets",
|
||||
8: "routeReportSubmit",
|
||||
9: "routes.CreateTopic",
|
||||
@ -339,28 +339,28 @@ var reverseRouteMapEnum = map[int]string{
|
||||
87: "routes.LockTopicSubmit",
|
||||
88: "routes.UnlockTopicSubmit",
|
||||
89: "routes.MoveTopicSubmit",
|
||||
90: "routeLikeTopicSubmit",
|
||||
90: "routes.LikeTopicSubmit",
|
||||
91: "routes.ViewTopic",
|
||||
92: "routes.CreateReplySubmit",
|
||||
93: "routes.ReplyEditSubmit",
|
||||
94: "routes.ReplyDeleteSubmit",
|
||||
95: "routeReplyLikeSubmit",
|
||||
96: "routeProfileReplyCreateSubmit",
|
||||
95: "routes.ReplyLikeSubmit",
|
||||
96: "routes.ProfileReplyCreateSubmit",
|
||||
97: "routes.ProfileReplyEditSubmit",
|
||||
98: "routes.ProfileReplyDeleteSubmit",
|
||||
99: "routes.PollVote",
|
||||
100: "routes.PollResults",
|
||||
101: "routes.AccountLogin",
|
||||
102: "routes.AccountRegister",
|
||||
103: "routeLogout",
|
||||
103: "routes.AccountLogout",
|
||||
104: "routes.AccountLoginSubmit",
|
||||
105: "routes.AccountRegisterSubmit",
|
||||
106: "routeDynamic",
|
||||
107: "routeUploads",
|
||||
106: "routes.DynamicRoute",
|
||||
107: "routes.UploadedFile",
|
||||
108: "routes.StaticFile",
|
||||
109: "routes.RobotsTxt",
|
||||
110: "routes.SitemapXml",
|
||||
111: "BadRoute",
|
||||
111: "routes.BadRoute",
|
||||
}
|
||||
var osMapEnum = map[string]int{
|
||||
"unknown": 0,
|
||||
@ -860,7 +860,7 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
||||
}
|
||||
|
||||
counters.RouteViewCounter.Bump(5)
|
||||
err = routeChangeTheme(w,req,user)
|
||||
err = routes.ChangeTheme(w,req,user)
|
||||
if err != nil {
|
||||
router.handleError(err,w,req,user)
|
||||
}
|
||||
@ -872,7 +872,7 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
||||
}
|
||||
|
||||
counters.RouteViewCounter.Bump(6)
|
||||
err = routeShowAttachment(w,req,user,extraData)
|
||||
err = routes.ShowAttachment(w,req,user,extraData)
|
||||
if err != nil {
|
||||
router.handleError(err,w,req,user)
|
||||
}
|
||||
@ -1632,7 +1632,7 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
||||
}
|
||||
|
||||
counters.RouteViewCounter.Bump(90)
|
||||
err = routeLikeTopicSubmit(w,req,user,extraData)
|
||||
err = routes.LikeTopicSubmit(w,req,user,extraData)
|
||||
default:
|
||||
counters.RouteViewCounter.Bump(91)
|
||||
err = routes.ViewTopic(w,req,user, extraData)
|
||||
@ -1712,7 +1712,7 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
||||
}
|
||||
|
||||
counters.RouteViewCounter.Bump(95)
|
||||
err = routeReplyLikeSubmit(w,req,user,extraData)
|
||||
err = routes.ReplyLikeSubmit(w,req,user,extraData)
|
||||
}
|
||||
if err != nil {
|
||||
router.handleError(err,w,req,user)
|
||||
@ -1733,7 +1733,7 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
||||
}
|
||||
|
||||
counters.RouteViewCounter.Bump(96)
|
||||
err = routeProfileReplyCreateSubmit(w,req,user)
|
||||
err = routes.ProfileReplyCreateSubmit(w,req,user)
|
||||
case "/profile/reply/edit/submit/":
|
||||
err = common.NoSessionMismatch(w,req,user)
|
||||
if err != nil {
|
||||
@ -1814,7 +1814,7 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
||||
}
|
||||
|
||||
counters.RouteViewCounter.Bump(103)
|
||||
err = routeLogout(w,req,user)
|
||||
err = routes.AccountLogout(w,req,user)
|
||||
case "/accounts/login/submit/":
|
||||
err = common.ParseForm(w,req,user)
|
||||
if err != nil {
|
||||
|
@ -701,6 +701,7 @@
|
||||
"panel_themes_make_default":"Make Default",
|
||||
|
||||
"panel_themes_menus_head":"Menus",
|
||||
"panel_themes_menus_main":"Main Menu",
|
||||
"panel_themes_menus_item_edit_button_aria":"Edit menu item",
|
||||
"panel_themes_menus_item_delete_button_aria":"Delete menu item",
|
||||
|
||||
|
3
main.go
3
main.go
@ -23,6 +23,7 @@ import (
|
||||
"./common/counters"
|
||||
"./config"
|
||||
"./query_gen/lib"
|
||||
"./routes"
|
||||
"github.com/fsnotify/fsnotify"
|
||||
)
|
||||
|
||||
@ -445,7 +446,7 @@ func main() {
|
||||
// TODO: Redirect to port 443
|
||||
go func() {
|
||||
log.Print("Listening on port 80")
|
||||
err = newServer(":80", &HTTPSRedirect{}).ListenAndServe()
|
||||
err = newServer(":80", &routes.HTTPSRedirect{}).ListenAndServe()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
225
member_routes.go
225
member_routes.go
@ -2,166 +2,12 @@ package main
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"./common"
|
||||
"./common/counters"
|
||||
)
|
||||
|
||||
// TODO: Refactor this
|
||||
func routeLikeTopicSubmit(w http.ResponseWriter, r *http.Request, user common.User, stid string) common.RouteError {
|
||||
isJs := (r.PostFormValue("isJs") == "1")
|
||||
tid, err := strconv.Atoi(stid)
|
||||
if err != nil {
|
||||
return common.PreErrorJSQ("Topic IDs can only ever be numbers.", w, r, isJs)
|
||||
}
|
||||
|
||||
topic, err := common.Topics.Get(tid)
|
||||
if err == ErrNoRows {
|
||||
return common.PreErrorJSQ("The requested topic doesn't exist.", w, r, isJs)
|
||||
} else if err != nil {
|
||||
return common.InternalErrorJSQ(err, w, r, isJs)
|
||||
}
|
||||
|
||||
// TODO: Add hooks to make use of headerLite
|
||||
_, ferr := common.SimpleForumUserCheck(w, r, &user, topic.ParentID)
|
||||
if ferr != nil {
|
||||
return ferr
|
||||
}
|
||||
if !user.Perms.ViewTopic || !user.Perms.LikeItem {
|
||||
return common.NoPermissionsJSQ(w, r, user, isJs)
|
||||
}
|
||||
if topic.CreatedBy == user.ID {
|
||||
return common.LocalErrorJSQ("You can't like your own topics", w, r, user, isJs)
|
||||
}
|
||||
|
||||
_, err = common.Users.Get(topic.CreatedBy)
|
||||
if err != nil && err == ErrNoRows {
|
||||
return common.LocalErrorJSQ("The target user doesn't exist", w, r, user, isJs)
|
||||
} else if err != nil {
|
||||
return common.InternalErrorJSQ(err, w, r, isJs)
|
||||
}
|
||||
|
||||
score := 1
|
||||
err = topic.Like(score, user.ID)
|
||||
//log.Print("likeErr: ", err)
|
||||
if err == common.ErrAlreadyLiked {
|
||||
return common.LocalErrorJSQ("You already liked this", w, r, user, isJs)
|
||||
} else if err != nil {
|
||||
return common.InternalErrorJSQ(err, w, r, isJs)
|
||||
}
|
||||
|
||||
err = common.AddActivityAndNotifyTarget(user.ID, topic.CreatedBy, "like", "topic", tid)
|
||||
if err != nil {
|
||||
return common.InternalErrorJSQ(err, w, r, isJs)
|
||||
}
|
||||
|
||||
if !isJs {
|
||||
http.Redirect(w, r, "/topic/"+strconv.Itoa(tid), http.StatusSeeOther)
|
||||
} else {
|
||||
_, _ = w.Write(successJSONBytes)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func routeReplyLikeSubmit(w http.ResponseWriter, r *http.Request, user common.User, srid string) common.RouteError {
|
||||
isJs := (r.PostFormValue("isJs") == "1")
|
||||
|
||||
rid, err := strconv.Atoi(srid)
|
||||
if err != nil {
|
||||
return common.PreErrorJSQ("The provided Reply ID is not a valid number.", w, r, isJs)
|
||||
}
|
||||
|
||||
reply, err := common.Rstore.Get(rid)
|
||||
if err == ErrNoRows {
|
||||
return common.PreErrorJSQ("You can't like something which doesn't exist!", w, r, isJs)
|
||||
} else if err != nil {
|
||||
return common.InternalErrorJSQ(err, w, r, isJs)
|
||||
}
|
||||
|
||||
var fid int
|
||||
err = stmts.getTopicFID.QueryRow(reply.ParentID).Scan(&fid)
|
||||
if err == ErrNoRows {
|
||||
return common.PreErrorJSQ("The parent topic doesn't exist.", w, r, isJs)
|
||||
} else if err != nil {
|
||||
return common.InternalErrorJSQ(err, w, r, isJs)
|
||||
}
|
||||
|
||||
// TODO: Add hooks to make use of headerLite
|
||||
_, ferr := common.SimpleForumUserCheck(w, r, &user, fid)
|
||||
if ferr != nil {
|
||||
return ferr
|
||||
}
|
||||
if !user.Perms.ViewTopic || !user.Perms.LikeItem {
|
||||
return common.NoPermissionsJSQ(w, r, user, isJs)
|
||||
}
|
||||
if reply.CreatedBy == user.ID {
|
||||
return common.LocalErrorJSQ("You can't like your own replies", w, r, user, isJs)
|
||||
}
|
||||
|
||||
_, err = common.Users.Get(reply.CreatedBy)
|
||||
if err != nil && err != ErrNoRows {
|
||||
return common.LocalErrorJSQ("The target user doesn't exist", w, r, user, isJs)
|
||||
} else if err != nil {
|
||||
return common.InternalErrorJSQ(err, w, r, isJs)
|
||||
}
|
||||
|
||||
err = reply.Like(user.ID)
|
||||
if err == common.ErrAlreadyLiked {
|
||||
return common.LocalErrorJSQ("You've already liked this!", w, r, user, isJs)
|
||||
} else if err != nil {
|
||||
return common.InternalErrorJSQ(err, w, r, isJs)
|
||||
}
|
||||
|
||||
err = common.AddActivityAndNotifyTarget(user.ID, reply.CreatedBy, "like", "post", rid)
|
||||
if err != nil {
|
||||
return common.InternalErrorJSQ(err, w, r, isJs)
|
||||
}
|
||||
|
||||
if !isJs {
|
||||
http.Redirect(w, r, "/topic/"+strconv.Itoa(reply.ParentID), http.StatusSeeOther)
|
||||
} else {
|
||||
_, _ = w.Write(successJSONBytes)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func routeProfileReplyCreateSubmit(w http.ResponseWriter, r *http.Request, user common.User) common.RouteError {
|
||||
if !user.Perms.ViewTopic || !user.Perms.CreateReply {
|
||||
return common.NoPermissions(w, r, user)
|
||||
}
|
||||
|
||||
uid, err := strconv.Atoi(r.PostFormValue("uid"))
|
||||
if err != nil {
|
||||
return common.LocalError("Invalid UID", w, r, user)
|
||||
}
|
||||
|
||||
profileOwner, err := common.Users.Get(uid)
|
||||
if err == ErrNoRows {
|
||||
return common.LocalError("The profile you're trying to post on doesn't exist.", w, r, user)
|
||||
} else if err != nil {
|
||||
return common.InternalError(err, w, r)
|
||||
}
|
||||
|
||||
content := common.PreparseMessage(r.PostFormValue("reply-content"))
|
||||
// TODO: Fully parse the post and store it in the parsed column
|
||||
_, err = common.Prstore.Create(profileOwner.ID, content, user.ID, user.LastIP)
|
||||
if err != nil {
|
||||
return common.InternalError(err, w, r)
|
||||
}
|
||||
|
||||
err = common.AddActivityAndNotifyTarget(user.ID, profileOwner.ID, "reply", "user", profileOwner.ID)
|
||||
if err != nil {
|
||||
return common.InternalError(err, w, r)
|
||||
}
|
||||
|
||||
counters.PostCounter.Bump()
|
||||
http.Redirect(w, r, "/user/"+strconv.Itoa(uid), http.StatusSeeOther)
|
||||
return nil
|
||||
}
|
||||
|
||||
func routeReportSubmit(w http.ResponseWriter, r *http.Request, user common.User, sitemID string) common.RouteError {
|
||||
itemID, err := strconv.Atoi(sitemID)
|
||||
if err != nil {
|
||||
@ -292,9 +138,13 @@ func routeAccountEditEmail(w http.ResponseWriter, r *http.Request, user common.U
|
||||
email.Primary = true
|
||||
emailList = append(emailList, email)
|
||||
}
|
||||
|
||||
if !common.Site.EnableEmails {
|
||||
headerVars.NoticeList = append(headerVars.NoticeList, common.GetNoticePhrase("account_mail_disabled"))
|
||||
}
|
||||
if r.FormValue("verified") == "1" {
|
||||
headerVars.NoticeList = append(headerVars.NoticeList, common.GetNoticePhrase("account_mail_verify_success"))
|
||||
}
|
||||
|
||||
pi := common.Page{"Email Manager", user, headerVars, emailList, nil}
|
||||
if common.RunPreRenderHook("pre_render_account_own_edit_email", w, r, &user, &pi) {
|
||||
@ -361,72 +211,7 @@ func routeAccountEditEmailTokenSubmit(w http.ResponseWriter, r *http.Request, us
|
||||
return common.InternalError(err, w, r)
|
||||
}
|
||||
}
|
||||
http.Redirect(w, r, "/user/edit/email/?verified=1", http.StatusSeeOther)
|
||||
|
||||
if !common.Site.EnableEmails {
|
||||
headerVars.NoticeList = append(headerVars.NoticeList, common.GetNoticePhrase("account_mail_disabled"))
|
||||
}
|
||||
headerVars.NoticeList = append(headerVars.NoticeList, common.GetNoticePhrase("account_mail_verify_success"))
|
||||
pi := common.Page{"Email Manager", user, headerVars, emailList, nil}
|
||||
if common.RunPreRenderHook("pre_render_account_own_edit_email", w, r, &user, &pi) {
|
||||
return nil
|
||||
}
|
||||
err = common.Templates.ExecuteTemplate(w, "account_own_edit_email.html", pi)
|
||||
if err != nil {
|
||||
return common.InternalError(err, w, r)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func routeLogout(w http.ResponseWriter, r *http.Request, user common.User) common.RouteError {
|
||||
if !user.Loggedin {
|
||||
return common.LocalError("You can't logout without logging in first.", w, r, user)
|
||||
}
|
||||
common.Auth.Logout(w, user.ID)
|
||||
http.Redirect(w, r, "/", http.StatusSeeOther)
|
||||
return nil
|
||||
}
|
||||
|
||||
func routeShowAttachment(w http.ResponseWriter, r *http.Request, user common.User, filename string) common.RouteError {
|
||||
filename = common.Stripslashes(filename)
|
||||
var ext = filepath.Ext("./attachs/" + filename)
|
||||
//log.Print("ext ", ext)
|
||||
//log.Print("filename ", filename)
|
||||
if !common.AllowedFileExts.Contains(strings.TrimPrefix(ext, ".")) {
|
||||
return common.LocalError("Bad extension", w, r, user)
|
||||
}
|
||||
|
||||
sectionID, err := strconv.Atoi(r.FormValue("sectionID"))
|
||||
if err != nil {
|
||||
return common.LocalError("The sectionID is not an integer", w, r, user)
|
||||
}
|
||||
var sectionTable = r.FormValue("sectionType")
|
||||
|
||||
var originTable string
|
||||
var originID, uploadedBy int
|
||||
err = stmts.getAttachment.QueryRow(filename, sectionID, sectionTable).Scan(§ionID, §ionTable, &originID, &originTable, &uploadedBy, &filename)
|
||||
if err == ErrNoRows {
|
||||
return common.NotFound(w, r, nil)
|
||||
} else if err != nil {
|
||||
return common.InternalError(err, w, r)
|
||||
}
|
||||
|
||||
if sectionTable == "forums" {
|
||||
_, ferr := common.SimpleForumUserCheck(w, r, &user, sectionID)
|
||||
if ferr != nil {
|
||||
return ferr
|
||||
}
|
||||
if !user.Perms.ViewTopic {
|
||||
return common.NoPermissions(w, r, user)
|
||||
}
|
||||
} else {
|
||||
return common.LocalError("Unknown section", w, r, user)
|
||||
}
|
||||
|
||||
if originTable != "topics" && originTable != "replies" {
|
||||
return common.LocalError("Unknown origin", w, r, user)
|
||||
}
|
||||
|
||||
// TODO: Fix the problem where non-existent files aren't greeted with custom 404s on ServeFile()'s side
|
||||
http.ServeFile(w, r, "./attachs/"+filename)
|
||||
return nil
|
||||
}
|
||||
|
@ -2397,7 +2397,12 @@ func routePanelThemesMenus(w http.ResponseWriter, r *http.Request, user common.U
|
||||
|
||||
var menuList []common.PanelMenuListItem
|
||||
for mid, list := range common.Menus.GetAllMap() {
|
||||
var name = ""
|
||||
if mid == 1 {
|
||||
name = common.GetTmplPhrase("panel_themes_menus_main")
|
||||
}
|
||||
menuList = append(menuList, common.PanelMenuListItem{
|
||||
Name: name,
|
||||
ID: mid,
|
||||
ItemCount: len(list.List),
|
||||
})
|
||||
|
@ -92,13 +92,16 @@ func patcher(scanner *bufio.Scanner) error {
|
||||
}
|
||||
|
||||
fmt.Println("Applying the patches")
|
||||
if dbVersion < 1 {
|
||||
err := patch0(scanner)
|
||||
if err != nil {
|
||||
return err
|
||||
if dbVersion < 2 {
|
||||
if dbVersion < 1 {
|
||||
err := patch0(scanner)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return patch1(scanner)
|
||||
}
|
||||
return patch1(scanner)
|
||||
return patch2(scanner)
|
||||
}
|
||||
|
||||
func execStmt(stmt *sql.Stmt, err error) error {
|
||||
|
@ -152,3 +152,57 @@ func patch1(scanner *bufio.Scanner) error {
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func patch2(scanner *bufio.Scanner) error {
|
||||
// ! Don't reuse this function blindly, it doesn't escape apostrophes
|
||||
var replaceTextWhere = func(replaceThis string, withThis string) error {
|
||||
return execStmt(qgen.Builder.SimpleUpdate("viewchunks", "route = '"+withThis+"'", "route = '"+replaceThis+"'"))
|
||||
}
|
||||
|
||||
err := replaceTextWhere("routeLogout", "routes.AccountLogout")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = replaceTextWhere("routeShowAttachment", "routes.ShowAttachment")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = replaceTextWhere("routeChangeTheme", "routes.ChangeTheme")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = replaceTextWhere("routeProfileReplyCreateSubmit", "routes.ProfileReplyCreateSubmit")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = replaceTextWhere("routeLikeTopicSubmit", "routes.LikeTopicSubmit")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = replaceTextWhere("routeReplyLikeSubmit", "routes.ReplyLikeSubmit")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = replaceTextWhere("routeDynamic", "routes.DynamicRoute")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = replaceTextWhere("routeUploads", "routes.UploadedFile")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = replaceTextWhere("BadRoute", "routes.BadRoute")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
@ -217,16 +217,30 @@ function runWebSockets() {
|
||||
}
|
||||
}
|
||||
|
||||
function loadScript(name, callback) {
|
||||
let url = "//" +siteURL+"/static/"+name
|
||||
$.getScript(url)
|
||||
.done(callback)
|
||||
.fail((e,xhr,settings,ex) => {
|
||||
console.log("Unable to get script '"+url+"'");
|
||||
console.log("e: ", e);
|
||||
console.log("xhr: ", xhr);
|
||||
console.log("settings: ", settings);
|
||||
console.log("ex: ",ex);
|
||||
console.trace();
|
||||
});
|
||||
}
|
||||
|
||||
$(document).ready(function(){
|
||||
runHook("start_init");
|
||||
$.getScript( "./static/template_alert.js", () => {
|
||||
loadScript("template_alert.js",() => {
|
||||
console.log("Loaded template_alert.js");
|
||||
alertsInitted = true;
|
||||
var alertMenuList = document.getElementsByClassName("menu_alerts");
|
||||
for(var i = 0; i < alertMenuList.length; i++) {
|
||||
loadAlerts(alertMenuList[i]);
|
||||
}
|
||||
});
|
||||
})
|
||||
if(window["WebSocket"]) runWebSockets();
|
||||
else conn = false;
|
||||
|
||||
|
@ -283,8 +283,6 @@ func writeSelects(adapter qgen.Adapter) error {
|
||||
|
||||
build.Select("groupEntryExists").Table("users_groups").Columns("gid").Where("name = ''").Orderby("gid ASC").Limit("0,1").Parse()
|
||||
|
||||
build.Select("getAttachment").Table("attachments").Columns("sectionID, sectionTable, originID, originTable, uploadedBy, path").Where("path = ? AND sectionID = ? AND sectionTable = ?").Parse()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -155,12 +155,12 @@ func main() {
|
||||
}
|
||||
|
||||
// Stubs for us to refer to these routes through
|
||||
mapIt("routeDynamic")
|
||||
mapIt("routeUploads")
|
||||
mapIt("routes.DynamicRoute")
|
||||
mapIt("routes.UploadedFile")
|
||||
mapIt("routes.StaticFile")
|
||||
mapIt("routes.RobotsTxt")
|
||||
mapIt("routes.SitemapXml")
|
||||
mapIt("BadRoute")
|
||||
mapIt("routes.BadRoute")
|
||||
tmplVars.AllRouteNames = allRouteNames
|
||||
tmplVars.AllRouteMap = allRouteMap
|
||||
|
||||
@ -652,7 +652,7 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
||||
common.NotFound(w,req,nil)
|
||||
return
|
||||
}
|
||||
counters.RouteViewCounter.Bump({{.AllRouteMap.routeUploads}})
|
||||
counters.RouteViewCounter.Bump({{index .AllRouteMap "routes.UploadedFile" }})
|
||||
req.URL.Path += extraData
|
||||
// TODO: Find a way to propagate errors up from this?
|
||||
router.UploadHandler(w,req) // TODO: Count these views
|
||||
@ -697,7 +697,7 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
||||
router.RUnlock()
|
||||
|
||||
if ok {
|
||||
counters.RouteViewCounter.Bump({{.AllRouteMap.routeDynamic}}) // TODO: Be more specific about *which* dynamic route it is
|
||||
counters.RouteViewCounter.Bump({{index .AllRouteMap "routes.DynamicRoute" }}) // TODO: Be more specific about *which* dynamic route it is
|
||||
req.URL.Path += extraData
|
||||
err = handle(w,req,user)
|
||||
if err != nil {
|
||||
@ -712,7 +712,7 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
||||
} else {
|
||||
router.DumpRequest(req,"Bad Route")
|
||||
}
|
||||
counters.RouteViewCounter.Bump({{.AllRouteMap.BadRoute}})
|
||||
counters.RouteViewCounter.Bump({{index .AllRouteMap "routes.BadRoute" }})
|
||||
common.NotFound(w,req,nil)
|
||||
}
|
||||
}
|
||||
|
@ -7,9 +7,9 @@ func routes() {
|
||||
addRoute(View("routes.CustomPage", "/pages/", "extraData"))
|
||||
addRoute(View("routes.ForumList", "/forums/" /*,"&forums"*/))
|
||||
addRoute(View("routes.ViewForum", "/forum/", "extraData"))
|
||||
addRoute(AnonAction("routeChangeTheme", "/theme/"))
|
||||
addRoute(AnonAction("routes.ChangeTheme", "/theme/"))
|
||||
addRoute(
|
||||
View("routeShowAttachment", "/attachs/", "extraData").Before("ParseForm"),
|
||||
View("routes.ShowAttachment", "/attachs/", "extraData").Before("ParseForm"),
|
||||
)
|
||||
|
||||
// TODO: Reduce the number of Befores. With a new method, perhaps?
|
||||
@ -74,7 +74,7 @@ func buildTopicRoutes() {
|
||||
Action("routes.LockTopicSubmit", "/topic/lock/submit/").LitBefore("req.URL.Path += extraData"),
|
||||
Action("routes.UnlockTopicSubmit", "/topic/unlock/submit/", "extraData"),
|
||||
Action("routes.MoveTopicSubmit", "/topic/move/submit/", "extraData"),
|
||||
Action("routeLikeTopicSubmit", "/topic/like/submit/", "extraData").Before("ParseForm"),
|
||||
Action("routes.LikeTopicSubmit", "/topic/like/submit/", "extraData").Before("ParseForm"),
|
||||
)
|
||||
addRouteGroup(topicGroup)
|
||||
}
|
||||
@ -88,7 +88,7 @@ func buildReplyRoutes() {
|
||||
UploadAction("routes.CreateReplySubmit", "/reply/create/").MaxSizeVar("int(common.Config.MaxRequestSize)"), // TODO: Rename the route so it's /reply/create/submit/
|
||||
Action("routes.ReplyEditSubmit", "/reply/edit/submit/", "extraData"),
|
||||
Action("routes.ReplyDeleteSubmit", "/reply/delete/submit/", "extraData"),
|
||||
Action("routeReplyLikeSubmit", "/reply/like/submit/", "extraData").Before("ParseForm"),
|
||||
Action("routes.ReplyLikeSubmit", "/reply/like/submit/", "extraData").Before("ParseForm"),
|
||||
)
|
||||
addRouteGroup(replyGroup)
|
||||
}
|
||||
@ -98,7 +98,7 @@ func buildProfileReplyRoutes() {
|
||||
//router.HandleFunc("/user/edit/submit/", routeLogout) // routeLogout? what on earth? o.o
|
||||
pReplyGroup := newRouteGroup("/profile/")
|
||||
pReplyGroup.Routes(
|
||||
Action("routeProfileReplyCreateSubmit", "/profile/reply/create/"), // TODO: Add /submit/ to the end
|
||||
Action("routes.ProfileReplyCreateSubmit", "/profile/reply/create/"), // TODO: Add /submit/ to the end
|
||||
Action("routes.ProfileReplyEditSubmit", "/profile/reply/edit/submit/", "extraData"),
|
||||
Action("routes.ProfileReplyDeleteSubmit", "/profile/reply/delete/submit/", "extraData"),
|
||||
)
|
||||
@ -120,7 +120,7 @@ func buildAccountRoutes() {
|
||||
accReplyGroup.Routes(
|
||||
View("routes.AccountLogin", "/accounts/login/"),
|
||||
View("routes.AccountRegister", "/accounts/create/"),
|
||||
Action("routeLogout", "/accounts/logout/"),
|
||||
Action("routes.AccountLogout", "/accounts/logout/"),
|
||||
AnonAction("routes.AccountLoginSubmit", "/accounts/login/submit/"), // TODO: Guard this with a token, maybe the IP hashed with a rotated key?
|
||||
AnonAction("routes.AccountRegisterSubmit", "/accounts/create/submit/"),
|
||||
)
|
||||
|
49
routes.go
49
routes.go
@ -7,7 +7,6 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"html"
|
||||
"net/http"
|
||||
"strconv"
|
||||
|
||||
@ -20,51 +19,8 @@ var tList []interface{}
|
||||
//var nList []string
|
||||
var successJSONBytes = []byte(`{"success":"1"}`)
|
||||
|
||||
// HTTPSRedirect is a connection handler which redirects all HTTP requests to HTTPS
|
||||
type HTTPSRedirect struct {
|
||||
}
|
||||
|
||||
func (red *HTTPSRedirect) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
||||
w.Header().Set("Connection", "close")
|
||||
dest := "https://" + req.Host + req.URL.Path
|
||||
if len(req.URL.RawQuery) > 0 {
|
||||
dest += "?" + req.URL.RawQuery
|
||||
}
|
||||
http.Redirect(w, req, dest, http.StatusTemporaryRedirect)
|
||||
}
|
||||
|
||||
// Temporary stubs for view tracking
|
||||
func routeDynamic() {
|
||||
}
|
||||
func routeUploads() {
|
||||
}
|
||||
func BadRoute() {
|
||||
}
|
||||
|
||||
// TODO: Set the cookie domain
|
||||
func routeChangeTheme(w http.ResponseWriter, r *http.Request, user common.User) common.RouteError {
|
||||
//headerLite, _ := SimpleUserCheck(w, r, &user)
|
||||
// TODO: Rename isJs to something else, just in case we rewrite the JS side in WebAssembly?
|
||||
isJs := (r.PostFormValue("isJs") == "1")
|
||||
newTheme := html.EscapeString(r.PostFormValue("newTheme"))
|
||||
|
||||
theme, ok := common.Themes[newTheme]
|
||||
if !ok || theme.HideFromThemes {
|
||||
return common.LocalErrorJSQ("That theme doesn't exist", w, r, user, isJs)
|
||||
}
|
||||
|
||||
cookie := http.Cookie{Name: "current_theme", Value: newTheme, Path: "/", MaxAge: int(common.Year)}
|
||||
http.SetCookie(w, &cookie)
|
||||
|
||||
if !isJs {
|
||||
http.Redirect(w, r, "/", http.StatusSeeOther)
|
||||
} else {
|
||||
_, _ = w.Write(successJSONBytes)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// TODO: Refactor this
|
||||
// TODO: Use the phrase system
|
||||
var phraseLoginAlerts = []byte(`{"msgs":[{"msg":"Login to see your alerts","path":"/accounts/login"}]}`)
|
||||
|
||||
// TODO: Refactor this endpoint
|
||||
@ -81,8 +37,7 @@ func routeAPI(w http.ResponseWriter, r *http.Request, user common.User) common.R
|
||||
return common.PreErrorJS("Invalid Action", w, r)
|
||||
}
|
||||
|
||||
module := r.FormValue("module")
|
||||
switch module {
|
||||
switch r.FormValue("module") {
|
||||
case "dismiss-alert":
|
||||
asid, err := strconv.Atoi(r.FormValue("asid"))
|
||||
if err != nil {
|
||||
|
@ -77,6 +77,15 @@ func AccountLoginSubmit(w http.ResponseWriter, r *http.Request, user common.User
|
||||
return nil
|
||||
}
|
||||
|
||||
func AccountLogout(w http.ResponseWriter, r *http.Request, user common.User) common.RouteError {
|
||||
if !user.Loggedin {
|
||||
return common.LocalError("You can't logout without logging in first.", w, r, user)
|
||||
}
|
||||
common.Auth.Logout(w, user.ID)
|
||||
http.Redirect(w, r, "/", http.StatusSeeOther)
|
||||
return nil
|
||||
}
|
||||
|
||||
func AccountRegister(w http.ResponseWriter, r *http.Request, user common.User) common.RouteError {
|
||||
header, ferr := common.UserCheck(w, r, &user)
|
||||
if ferr != nil {
|
||||
|
@ -2,13 +2,17 @@ package routes
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"database/sql"
|
||||
"html"
|
||||
"io"
|
||||
"net/http"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"../common"
|
||||
"../query_gen/lib"
|
||||
)
|
||||
|
||||
var cacheControlMaxAge = "max-age=" + strconv.Itoa(int(common.Day)) // TODO: Make this a common.Config value
|
||||
@ -86,3 +90,87 @@ func CustomPage(w http.ResponseWriter, r *http.Request, user common.User, name s
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type AttachmentStmts struct {
|
||||
get *sql.Stmt
|
||||
}
|
||||
|
||||
var attachmentStmts AttachmentStmts
|
||||
|
||||
// TODO: Move these DbInits into a TopicList abstraction
|
||||
func init() {
|
||||
common.DbInits.Add(func(acc *qgen.Accumulator) error {
|
||||
attachmentStmts = AttachmentStmts{
|
||||
get: acc.Select("attachments").Columns("sectionID, sectionTable, originID, originTable, uploadedBy, path").Where("path = ? AND sectionID = ? AND sectionTable = ?").Prepare(),
|
||||
}
|
||||
return acc.FirstError()
|
||||
})
|
||||
}
|
||||
|
||||
func ShowAttachment(w http.ResponseWriter, r *http.Request, user common.User, filename string) common.RouteError {
|
||||
filename = common.Stripslashes(filename)
|
||||
var ext = filepath.Ext("./attachs/" + filename)
|
||||
//log.Print("ext ", ext)
|
||||
//log.Print("filename ", filename)
|
||||
if !common.AllowedFileExts.Contains(strings.TrimPrefix(ext, ".")) {
|
||||
return common.LocalError("Bad extension", w, r, user)
|
||||
}
|
||||
|
||||
sectionID, err := strconv.Atoi(r.FormValue("sectionID"))
|
||||
if err != nil {
|
||||
return common.LocalError("The sectionID is not an integer", w, r, user)
|
||||
}
|
||||
var sectionTable = r.FormValue("sectionType")
|
||||
|
||||
var originTable string
|
||||
var originID, uploadedBy int
|
||||
err = attachmentStmts.get.QueryRow(filename, sectionID, sectionTable).Scan(§ionID, §ionTable, &originID, &originTable, &uploadedBy, &filename)
|
||||
if err == sql.ErrNoRows {
|
||||
return common.NotFound(w, r, nil)
|
||||
} else if err != nil {
|
||||
return common.InternalError(err, w, r)
|
||||
}
|
||||
|
||||
if sectionTable == "forums" {
|
||||
_, ferr := common.SimpleForumUserCheck(w, r, &user, sectionID)
|
||||
if ferr != nil {
|
||||
return ferr
|
||||
}
|
||||
if !user.Perms.ViewTopic {
|
||||
return common.NoPermissions(w, r, user)
|
||||
}
|
||||
} else {
|
||||
return common.LocalError("Unknown section", w, r, user)
|
||||
}
|
||||
|
||||
if originTable != "topics" && originTable != "replies" {
|
||||
return common.LocalError("Unknown origin", w, r, user)
|
||||
}
|
||||
|
||||
// TODO: Fix the problem where non-existent files aren't greeted with custom 404s on ServeFile()'s side
|
||||
http.ServeFile(w, r, "./attachs/"+filename)
|
||||
return nil
|
||||
}
|
||||
|
||||
// TODO: Set the cookie domain
|
||||
func ChangeTheme(w http.ResponseWriter, r *http.Request, user common.User) common.RouteError {
|
||||
//headerLite, _ := SimpleUserCheck(w, r, &user)
|
||||
// TODO: Rename isJs to something else, just in case we rewrite the JS side in WebAssembly?
|
||||
isJs := (r.PostFormValue("isJs") == "1")
|
||||
newTheme := html.EscapeString(r.PostFormValue("newTheme"))
|
||||
|
||||
theme, ok := common.Themes[newTheme]
|
||||
if !ok || theme.HideFromThemes {
|
||||
return common.LocalErrorJSQ("That theme doesn't exist", w, r, user, isJs)
|
||||
}
|
||||
|
||||
cookie := http.Cookie{Name: "current_theme", Value: newTheme, Path: "/", MaxAge: int(common.Year)}
|
||||
http.SetCookie(w, &cookie)
|
||||
|
||||
if !isJs {
|
||||
http.Redirect(w, r, "/", http.StatusSeeOther)
|
||||
} else {
|
||||
_, _ = w.Write(successJSONBytes)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
@ -297,6 +297,41 @@ func ReplyDeleteSubmit(w http.ResponseWriter, r *http.Request, user common.User,
|
||||
return nil
|
||||
}
|
||||
|
||||
// TODO: Move the profile reply routes to their own file?
|
||||
func ProfileReplyCreateSubmit(w http.ResponseWriter, r *http.Request, user common.User) common.RouteError {
|
||||
if !user.Perms.ViewTopic || !user.Perms.CreateReply {
|
||||
return common.NoPermissions(w, r, user)
|
||||
}
|
||||
|
||||
uid, err := strconv.Atoi(r.PostFormValue("uid"))
|
||||
if err != nil {
|
||||
return common.LocalError("Invalid UID", w, r, user)
|
||||
}
|
||||
|
||||
profileOwner, err := common.Users.Get(uid)
|
||||
if err == sql.ErrNoRows {
|
||||
return common.LocalError("The profile you're trying to post on doesn't exist.", w, r, user)
|
||||
} else if err != nil {
|
||||
return common.InternalError(err, w, r)
|
||||
}
|
||||
|
||||
content := common.PreparseMessage(r.PostFormValue("reply-content"))
|
||||
// TODO: Fully parse the post and store it in the parsed column
|
||||
_, err = common.Prstore.Create(profileOwner.ID, content, user.ID, user.LastIP)
|
||||
if err != nil {
|
||||
return common.InternalError(err, w, r)
|
||||
}
|
||||
|
||||
err = common.AddActivityAndNotifyTarget(user.ID, profileOwner.ID, "reply", "user", profileOwner.ID)
|
||||
if err != nil {
|
||||
return common.InternalError(err, w, r)
|
||||
}
|
||||
|
||||
counters.PostCounter.Bump()
|
||||
http.Redirect(w, r, "/user/"+strconv.Itoa(uid), http.StatusSeeOther)
|
||||
return nil
|
||||
}
|
||||
|
||||
func ProfileReplyEditSubmit(w http.ResponseWriter, r *http.Request, user common.User, srid string) common.RouteError {
|
||||
isJs := (r.PostFormValue("js") == "1")
|
||||
|
||||
@ -372,3 +407,64 @@ func ProfileReplyDeleteSubmit(w http.ResponseWriter, r *http.Request, user commo
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func ReplyLikeSubmit(w http.ResponseWriter, r *http.Request, user common.User, srid string) common.RouteError {
|
||||
isJs := (r.PostFormValue("isJs") == "1")
|
||||
|
||||
rid, err := strconv.Atoi(srid)
|
||||
if err != nil {
|
||||
return common.PreErrorJSQ("The provided Reply ID is not a valid number.", w, r, isJs)
|
||||
}
|
||||
|
||||
reply, err := common.Rstore.Get(rid)
|
||||
if err == sql.ErrNoRows {
|
||||
return common.PreErrorJSQ("You can't like something which doesn't exist!", w, r, isJs)
|
||||
} else if err != nil {
|
||||
return common.InternalErrorJSQ(err, w, r, isJs)
|
||||
}
|
||||
|
||||
topic, err := common.Topics.Get(reply.ParentID)
|
||||
if err == sql.ErrNoRows {
|
||||
return common.PreErrorJSQ("The parent topic doesn't exist.", w, r, isJs)
|
||||
} else if err != nil {
|
||||
return common.InternalErrorJSQ(err, w, r, isJs)
|
||||
}
|
||||
|
||||
// TODO: Add hooks to make use of headerLite
|
||||
_, ferr := common.SimpleForumUserCheck(w, r, &user, topic.ParentID)
|
||||
if ferr != nil {
|
||||
return ferr
|
||||
}
|
||||
if !user.Perms.ViewTopic || !user.Perms.LikeItem {
|
||||
return common.NoPermissionsJSQ(w, r, user, isJs)
|
||||
}
|
||||
if reply.CreatedBy == user.ID {
|
||||
return common.LocalErrorJSQ("You can't like your own replies", w, r, user, isJs)
|
||||
}
|
||||
|
||||
_, err = common.Users.Get(reply.CreatedBy)
|
||||
if err != nil && err != sql.ErrNoRows {
|
||||
return common.LocalErrorJSQ("The target user doesn't exist", w, r, user, isJs)
|
||||
} else if err != nil {
|
||||
return common.InternalErrorJSQ(err, w, r, isJs)
|
||||
}
|
||||
|
||||
err = reply.Like(user.ID)
|
||||
if err == common.ErrAlreadyLiked {
|
||||
return common.LocalErrorJSQ("You've already liked this!", w, r, user, isJs)
|
||||
} else if err != nil {
|
||||
return common.InternalErrorJSQ(err, w, r, isJs)
|
||||
}
|
||||
|
||||
err = common.AddActivityAndNotifyTarget(user.ID, reply.CreatedBy, "like", "post", rid)
|
||||
if err != nil {
|
||||
return common.InternalErrorJSQ(err, w, r, isJs)
|
||||
}
|
||||
|
||||
if !isJs {
|
||||
http.Redirect(w, r, "/topic/"+strconv.Itoa(reply.ParentID), http.StatusSeeOther)
|
||||
} else {
|
||||
_, _ = w.Write(successJSONBytes)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
24
routes/stubs.go
Normal file
24
routes/stubs.go
Normal file
@ -0,0 +1,24 @@
|
||||
package routes
|
||||
|
||||
import "net/http"
|
||||
|
||||
// HTTPSRedirect is a connection handler which redirects all HTTP requests to HTTPS
|
||||
type HTTPSRedirect struct {
|
||||
}
|
||||
|
||||
func (red *HTTPSRedirect) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
||||
w.Header().Set("Connection", "close")
|
||||
dest := "https://" + req.Host + req.URL.Path
|
||||
if len(req.URL.RawQuery) > 0 {
|
||||
dest += "?" + req.URL.RawQuery
|
||||
}
|
||||
http.Redirect(w, req, dest, http.StatusTemporaryRedirect)
|
||||
}
|
||||
|
||||
// Temporary stubs for view tracking
|
||||
func DynamicRoute() {
|
||||
}
|
||||
func UploadedFile() {
|
||||
}
|
||||
func BadRoute() {
|
||||
}
|
@ -842,3 +842,59 @@ func addTopicAction(action string, topic *common.Topic, user common.User) error
|
||||
}
|
||||
return topic.CreateActionReply(action, user.LastIP, user)
|
||||
}
|
||||
|
||||
// TODO: Refactor this
|
||||
func LikeTopicSubmit(w http.ResponseWriter, r *http.Request, user common.User, stid string) common.RouteError {
|
||||
isJs := (r.PostFormValue("isJs") == "1")
|
||||
tid, err := strconv.Atoi(stid)
|
||||
if err != nil {
|
||||
return common.PreErrorJSQ("Topic IDs can only ever be numbers.", w, r, isJs)
|
||||
}
|
||||
|
||||
topic, err := common.Topics.Get(tid)
|
||||
if err == sql.ErrNoRows {
|
||||
return common.PreErrorJSQ("The requested topic doesn't exist.", w, r, isJs)
|
||||
} else if err != nil {
|
||||
return common.InternalErrorJSQ(err, w, r, isJs)
|
||||
}
|
||||
|
||||
// TODO: Add hooks to make use of headerLite
|
||||
_, ferr := common.SimpleForumUserCheck(w, r, &user, topic.ParentID)
|
||||
if ferr != nil {
|
||||
return ferr
|
||||
}
|
||||
if !user.Perms.ViewTopic || !user.Perms.LikeItem {
|
||||
return common.NoPermissionsJSQ(w, r, user, isJs)
|
||||
}
|
||||
if topic.CreatedBy == user.ID {
|
||||
return common.LocalErrorJSQ("You can't like your own topics", w, r, user, isJs)
|
||||
}
|
||||
|
||||
_, err = common.Users.Get(topic.CreatedBy)
|
||||
if err != nil && err == sql.ErrNoRows {
|
||||
return common.LocalErrorJSQ("The target user doesn't exist", w, r, user, isJs)
|
||||
} else if err != nil {
|
||||
return common.InternalErrorJSQ(err, w, r, isJs)
|
||||
}
|
||||
|
||||
score := 1
|
||||
err = topic.Like(score, user.ID)
|
||||
//log.Print("likeErr: ", err)
|
||||
if err == common.ErrAlreadyLiked {
|
||||
return common.LocalErrorJSQ("You already liked this", w, r, user, isJs)
|
||||
} else if err != nil {
|
||||
return common.InternalErrorJSQ(err, w, r, isJs)
|
||||
}
|
||||
|
||||
err = common.AddActivityAndNotifyTarget(user.ID, topic.CreatedBy, "like", "topic", tid)
|
||||
if err != nil {
|
||||
return common.InternalErrorJSQ(err, w, r, isJs)
|
||||
}
|
||||
|
||||
if !isJs {
|
||||
http.Redirect(w, r, "/topic/"+strconv.Itoa(tid), http.StatusSeeOther)
|
||||
} else {
|
||||
_, _ = w.Write(successJSONBytes)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
{
|
||||
"DBVersion":"2",
|
||||
"DBVersion":"3",
|
||||
"DynamicFileVersion":"0",
|
||||
"MinGoVersion":"1.10",
|
||||
"MinVersion":""
|
||||
|
@ -17,9 +17,15 @@
|
||||
<div class="rowitem passive">
|
||||
<a href="/panel/settings/word-filters/">{{lang "panel_menu_word_filters"}}</a> <a class="menu_stats" href="#">({{.Stats.WordFilters}})</a>
|
||||
</div>{{end}}
|
||||
{{if .CurrentUser.Perms.ManageThemes}}<div class="rowitem passive">
|
||||
{{if .CurrentUser.Perms.ManageThemes}}
|
||||
<div class="rowitem passive">
|
||||
<a href="/panel/themes/">{{lang "panel_menu_themes"}}</a> <a class="menu_stats" href="#">({{.Stats.Themes}})</a>
|
||||
</div>{{end}}
|
||||
</div>
|
||||
{{if eq .Zone "themes"}}
|
||||
<div class="rowitem passive submenu"><a href="/panel/themes/menus/">Menus</a></div>
|
||||
<div class="rowitem passive submenu"><a href="#">Widgets</a></div>
|
||||
{{end}}
|
||||
{{end}}
|
||||
</div>
|
||||
<div class="colstack_item colstack_head">
|
||||
<div class="rowitem"><a href="#">{{lang "panel_menu_events"}}</a></div>
|
||||
|
@ -1,15 +1,6 @@
|
||||
{{template "header.html" . }}
|
||||
<div class="colstack panel_stack">
|
||||
<nav class="colstack_left" aria-label="The control panel menu">
|
||||
<div class="colstack_item colstack_head submenu_fallback">
|
||||
<div class="rowitem"><a href="/panel/logs/mod/">{{lang "panel_logs_menu_head"}}</a></div>
|
||||
</div>
|
||||
<div class="colstack_item rowmenu submenu_fallback">
|
||||
<div class="rowitem passive"><a href="/panel/logs/mod/">{{lang "panel_logs_menu_moderation"}}</a></div>
|
||||
{{if .CurrentUser.Perms.ViewAdminLogs}}<div class="rowitem passive"><a>{{lang "panel_logs_menu_administration"}}</a></div>{{end}}
|
||||
</div>
|
||||
{{template "panel-inner-menu.html" . }}
|
||||
</nav>
|
||||
{{template "panel-menu.html" . }}
|
||||
<main class="colstack_right">
|
||||
<div class="colstack_item colstack_head">
|
||||
<div class="rowitem"><h1>{{lang "panel_logs_administration_head"}}</h1></div>
|
||||
|
@ -1,15 +1,6 @@
|
||||
{{template "header.html" . }}
|
||||
<div class="colstack panel_stack">
|
||||
<nav class="colstack_left" aria-label="The control panel menu">
|
||||
<div class="colstack_item colstack_head submenu_fallback">
|
||||
<div class="rowitem"><a href="/panel/logs/mod/">{{lang "panel_logs_menu_head"}}</a></div>
|
||||
</div>
|
||||
<div class="colstack_item rowmenu submenu_fallback">
|
||||
<div class="rowitem passive"><a href="/panel/logs/mod/">{{lang "panel_logs_menu_moderation"}}</a></div>
|
||||
{{if .CurrentUser.Perms.ViewAdminLogs}}<div class="rowitem passive"><a>{{lang "panel_logs_menu_administration"}}</a></div>{{end}}
|
||||
</div>
|
||||
{{template "panel-inner-menu.html" . }}
|
||||
</nav>
|
||||
{{template "panel-menu.html" . }}
|
||||
<main class="colstack_right">
|
||||
<div class="colstack_item colstack_head">
|
||||
<div class="rowitem"><h1>{{lang "panel_logs_moderation_head"}}</h1></div>
|
||||
|
@ -1,19 +1,6 @@
|
||||
{{template "header.html" . }}
|
||||
<div class="colstack panel_stack">
|
||||
|
||||
<nav class="colstack_left" aria-label="Theme manager menu">
|
||||
<div class="colstack_item colstack_head">
|
||||
<div class="rowitem"><a href="/panel/themes/">Theme Manager</a></div>
|
||||
</div>
|
||||
<div class="colstack_item rowmenu">
|
||||
<div class="rowitem passive"><a href="/panel/themes/menus/">Menus</a></div>
|
||||
</div>
|
||||
<div class="colstack_item rowmenu">
|
||||
<div class="rowitem passive"><a href="#">Widgets</a></div>
|
||||
</div>
|
||||
{{template "panel-inner-menu.html" . }}
|
||||
</nav>
|
||||
|
||||
{{template "panel-menu.html" . }}
|
||||
{{/** Stop inlining this x.x **/}}
|
||||
<style type="text/css">
|
||||
.rowitem::after {
|
||||
|
@ -1,19 +1,6 @@
|
||||
{{template "header.html" . }}
|
||||
<div class="colstack panel_stack">
|
||||
|
||||
<nav class="colstack_left" aria-label="Theme manager menu">
|
||||
<div class="colstack_item colstack_head">
|
||||
<div class="rowitem"><a href="/panel/themes/">Theme Manager</a></div>
|
||||
</div>
|
||||
<div class="colstack_item rowmenu">
|
||||
<div class="rowitem passive"><a href="/panel/themes/menus/">Menus</a></div>
|
||||
</div>
|
||||
<div class="colstack_item rowmenu">
|
||||
<div class="rowitem passive"><a href="#">Widgets</a></div>
|
||||
</div>
|
||||
{{template "panel-inner-menu.html" . }}
|
||||
</nav>
|
||||
|
||||
{{template "panel-menu.html" . }}
|
||||
<main class="colstack_right">
|
||||
<div class="colstack_item colstack_head">
|
||||
<div class="rowitem"><h1>{{lang "panel_themes_menus_head"}}</h1></div>
|
||||
@ -21,7 +8,7 @@
|
||||
<div id="panel_settings" class="colstack_item rowlist">
|
||||
{{range .ItemList}}
|
||||
<div class="rowitem panel_compactrow editable_parent">
|
||||
<a href="/panel/themes/menus/edit/{{.ID}}" class="editable_block panel_upshift">#{{.ID}}</a>
|
||||
<a href="/panel/themes/menus/edit/{{.ID}}" class="editable_block panel_upshift">{{if .Name}}{{.Name}} - {{end}}#{{.ID}}</a>
|
||||
<a class="panel_compacttext to_right">{{.ItemCount}} items</a>
|
||||
</div>
|
||||
{{end}}
|
||||
|
@ -1,20 +1,8 @@
|
||||
{{template "header.html" . }}
|
||||
<div class="colstack panel_stack">
|
||||
|
||||
{{/** TODO: Set the order based on the order here **/}}
|
||||
{{/** TODO: Write the backend code and JS code for saving this menu **/}}
|
||||
<nav class="colstack_left" aria-label="Theme manager menu">
|
||||
<div class="colstack_item colstack_head">
|
||||
<div class="rowitem"><a href="/panel/themes/">Theme Manager</a></div>
|
||||
</div>
|
||||
<div class="colstack_item rowmenu">
|
||||
<div class="rowitem passive"><a href="/panel/themes/menus/">Menus</a></div>
|
||||
</div>
|
||||
<div class="colstack_item rowmenu">
|
||||
<div class="rowitem passive"><a href="#">Widgets</a></div>
|
||||
</div>
|
||||
{{template "panel-inner-menu.html" . }}
|
||||
</nav>
|
||||
{{template "panel-menu.html" . }}
|
||||
<main class="colstack_right">
|
||||
<div class="colstack_item colstack_head">
|
||||
<div class="rowitem"><h1>{{lang "panel_themes_menus_edit_head"}}</h1></div>
|
||||
|
@ -1,17 +1,6 @@
|
||||
{{template "header.html" . }}
|
||||
<div class="colstack panel_stack">
|
||||
<nav class="colstack_left" aria-label="Control panel menu">
|
||||
<div class="colstack_item colstack_head">
|
||||
<div class="rowitem"><a href="/panel/themes/">Theme Manager</a></div>
|
||||
</div>
|
||||
<div class="colstack_item rowmenu">
|
||||
<div class="rowitem passive"><a href="/panel/themes/menus/">Menus</a></div>
|
||||
</div>
|
||||
<div class="colstack_item rowmenu">
|
||||
<div class="rowitem passive"><a href="#">Widgets</a></div>
|
||||
</div>
|
||||
{{template "panel-inner-menu.html" . }}
|
||||
</nav>
|
||||
{{template "panel-menu.html" . }}
|
||||
<main class="colstack_right">
|
||||
<div class="colstack_item colstack_head">
|
||||
<div class="rowitem"><h1>{{lang "panel_themes_menus_head"}}</h1></div>
|
||||
|
@ -35,7 +35,7 @@ body {
|
||||
font-size: 16px;
|
||||
font-family: arial;
|
||||
margin: 0px;
|
||||
color: var(--primary-text-color);
|
||||
color: var(--lightened-primary-text-color);
|
||||
}
|
||||
a {
|
||||
text-decoration: none;
|
||||
@ -228,9 +228,9 @@ ul {
|
||||
margin-top: 8px;
|
||||
}
|
||||
.rowhead h1, .opthead h1, .colstack_head h1 {
|
||||
font-size: 20px;
|
||||
font-size: 19px;
|
||||
font-weight: normal;
|
||||
color: var(--primary-text-color);
|
||||
color: var(--lightened-primary-text-color);
|
||||
display: inline-block;
|
||||
}
|
||||
.colstack_head a h1 {
|
||||
@ -905,6 +905,7 @@ textarea {
|
||||
|
||||
.postImage {
|
||||
width: 100%;
|
||||
max-width: 150px;
|
||||
}
|
||||
.post_item {
|
||||
display: flex;
|
||||
|
@ -145,7 +145,7 @@
|
||||
#panel_users .rowitem .rowTitle {
|
||||
border-bottom: 1px solid var(--lighter-text-color);
|
||||
padding-bottom: 4px;
|
||||
margin-bottom: 4px;
|
||||
margin-bottom: 6px;
|
||||
}
|
||||
|
||||
#forum_quick_perms .formitem {
|
||||
|
0
themes/nox/public/main.css
Normal file
0
themes/nox/public/main.css
Normal file
32
themes/nox/theme.json
Normal file
32
themes/nox/theme.json
Normal file
@ -0,0 +1,32 @@
|
||||
{
|
||||
"Name": "nox",
|
||||
"FriendlyName": "Nox",
|
||||
"Version": "0.0.1",
|
||||
"Creator": "Azareal",
|
||||
"URL": "github.com/Azareal/Gosora",
|
||||
"Tag": "WIP",
|
||||
"HideFromThemes":true,
|
||||
"Docks":["topMenu","rightSidebar","footer"],
|
||||
"Templates": [
|
||||
{
|
||||
"Name": "topic",
|
||||
"Source": "topic_alt"
|
||||
}
|
||||
],
|
||||
"Resources": [
|
||||
{
|
||||
"Name":"EQCSS.js",
|
||||
"Location":"global"
|
||||
},
|
||||
{
|
||||
"Name":"trumbowyg/trumbowyg.min.js",
|
||||
"Location":"global",
|
||||
"Loggedin": true
|
||||
},
|
||||
{
|
||||
"Name":"trumbowyg/ui/trumbowyg.custom.css",
|
||||
"Location":"global",
|
||||
"Loggedin":true
|
||||
}
|
||||
]
|
||||
}
|
@ -510,7 +510,7 @@ input, select, textarea {
|
||||
}
|
||||
.topic_create_form input, .topic_create_form select {
|
||||
padding: 7px;
|
||||
font-family: monospace;
|
||||
font-size: 13px;
|
||||
}
|
||||
.topic_create_form select {
|
||||
padding: 6px;
|
||||
@ -519,8 +519,11 @@ input, select, textarea {
|
||||
width: calc(100% - 14px);
|
||||
}
|
||||
.topic_create_form textarea, .topic_reply_form textarea {
|
||||
width: calc(100% - 5px);
|
||||
width: calc(100% - 26px);
|
||||
min-height: 80px;
|
||||
font-family: arial;
|
||||
font-size: 14px;
|
||||
padding: 12px;
|
||||
}
|
||||
.topic_create_form textarea {
|
||||
padding: 7px;
|
||||
@ -690,8 +693,8 @@ input[type=checkbox]:checked + label.poll_option_label .sel {
|
||||
}
|
||||
.topic_left.topic_sticky .topic_inner_left {
|
||||
border-top: 4px solid hsl(41, 100%, 50%);
|
||||
padding-left: 8px;
|
||||
padding-top: 8px;
|
||||
padding-left: 10px;
|
||||
padding-top: 10px;
|
||||
margin-top: 0px;
|
||||
margin-left: 0px;
|
||||
width: 100%;
|
||||
|
@ -1,5 +1,6 @@
|
||||
.submenu a {
|
||||
margin-left: 4px;
|
||||
.submenu:before {
|
||||
content: "-";
|
||||
margin-right: 6px;
|
||||
}
|
||||
.colstack_head .rowitem {
|
||||
display: flex;
|
||||
@ -68,16 +69,16 @@
|
||||
padding-right: 2px;
|
||||
}
|
||||
|
||||
.panel_submitrow {
|
||||
.panel_submitrow .rowitem {
|
||||
display: flex;
|
||||
}
|
||||
.panel_submitrow *:first-child {
|
||||
.panel_submitrow .rowitem *:first-child {
|
||||
margin-left: auto;
|
||||
}
|
||||
.panel_submitrow *:last-child {
|
||||
.panel_submitrow .rowitem *:last-child {
|
||||
margin-right: auto;
|
||||
}
|
||||
.panel_submitrow button {
|
||||
.panel_submitrow .rowitem button {
|
||||
padding-top: 5px;
|
||||
padding-bottom: 5px;
|
||||
}
|
||||
|
@ -769,7 +769,9 @@ button.username {
|
||||
margin-top: 20px;
|
||||
}
|
||||
#profile_right_lane .topic_reply_form {
|
||||
border-bottom: 1.5px inset var(--main-border-color);
|
||||
border-bottom: 2px inset var(--main-border-color);
|
||||
border-left: 1px solid var(--main-border-color);
|
||||
border-right: 1px solid var(--main-border-color);
|
||||
}
|
||||
|
||||
/* TODO: Show the avatar next to the reply form */
|
||||
@ -924,7 +926,7 @@ input[type=checkbox]:checked + label.poll_option_label .sel {
|
||||
border-bottom: none;
|
||||
}
|
||||
.topic_reply_form {
|
||||
border-top: 1px solid var(--main-border-color);
|
||||
border: none;
|
||||
}
|
||||
.post_container .post_item {
|
||||
background-color: #eaeaea;
|
||||
|
@ -1,3 +1,6 @@
|
||||
.sidebar {
|
||||
display: none;
|
||||
}
|
||||
/*.submenu {
|
||||
padding-left: 18px;
|
||||
}*/
|
||||
@ -102,8 +105,13 @@
|
||||
padding-left: 2px;
|
||||
padding-right: 2px;
|
||||
}
|
||||
.colstack_item + .panel_submitrow {
|
||||
margin-bottom: 0px;
|
||||
.panel_submitrow {
|
||||
margin-top: -12px;
|
||||
border-top: none;
|
||||
}
|
||||
.panel_submitrow button {
|
||||
padding-top: 3px;
|
||||
padding-bottom: 3px;
|
||||
}
|
||||
|
||||
.ct_chart {
|
||||
|
@ -1,3 +1,9 @@
|
||||
.sidebar {
|
||||
display: none;
|
||||
}
|
||||
.submenu:before {
|
||||
content: "-";
|
||||
}
|
||||
.submenu a {
|
||||
margin-left: 8px;
|
||||
}
|
||||
@ -118,8 +124,13 @@
|
||||
display: none;
|
||||
}
|
||||
|
||||
.colstack_item + .panel_submitrow {
|
||||
margin-bottom: 0px;
|
||||
.panel_submitrow {
|
||||
margin-top: -12px;
|
||||
border-top: none;
|
||||
}
|
||||
.panel_submitrow button {
|
||||
padding-top: 3px;
|
||||
padding-bottom: 3px;
|
||||
}
|
||||
|
||||
#panel_word_filters .itemSeparator:before {
|
||||
|
Loading…
Reference in New Issue
Block a user