diff --git a/common/attachments.go b/common/attachments.go new file mode 100644 index 00000000..5d419022 --- /dev/null +++ b/common/attachments.go @@ -0,0 +1,29 @@ +package common + +import ( + "database/sql" + + "../query_gen/lib" +) + +var Attachments AttachmentStore + +type AttachmentStore interface { + Add(sectionID int, sectionTable string, originID int, originTable string, uploadedBy int, path string) error +} + +type DefaultAttachmentStore struct { + add *sql.Stmt +} + +func NewDefaultAttachmentStore() (*DefaultAttachmentStore, error) { + acc := qgen.Builder.Accumulator() + return &DefaultAttachmentStore{ + add: acc.Insert("attachments").Columns("sectionID, sectionTable, originID, originTable, uploadedBy, path").Fields("?,?,?,?,?,?").Prepare(), + }, acc.FirstError() +} + +func (store *DefaultAttachmentStore) Add(sectionID int, sectionTable string, originID int, originTable string, uploadedBy int, path string) error { + _, err := store.add.Exec(sectionID, sectionTable, originID, originTable, uploadedBy, path) + return err +} diff --git a/common/ip_search.go b/common/ip_search.go index c81dfec5..4407db84 100644 --- a/common/ip_search.go +++ b/common/ip_search.go @@ -13,18 +13,20 @@ type IPSearcher interface { } type DefaultIPSearcher struct { - searchUsers *sql.Stmt - searchReplies *sql.Stmt - searchTopics *sql.Stmt + searchUsers *sql.Stmt + searchTopics *sql.Stmt + searchReplies *sql.Stmt + searchUsersReplies *sql.Stmt } // NewDefaultIPSearcher gives you a new instance of DefaultIPSearcher func NewDefaultIPSearcher() (*DefaultIPSearcher, error) { acc := qgen.Builder.Accumulator() return &DefaultIPSearcher{ - searchUsers: acc.Select("users").Columns("uid").Where("last_ip = ?").Prepare(), - searchTopics: acc.Select("users").Columns("uid").InQ("uid", acc.Select("topics").Columns("createdBy").Where("ipaddress = ?")).Prepare(), - searchReplies: acc.Select("users").Columns("uid").InQ("uid", acc.Select("replies").Columns("createdBy").Where("ipaddress = ?")).Prepare(), + searchUsers: acc.Select("users").Columns("uid").Where("last_ip = ?").Prepare(), + searchTopics: acc.Select("users").Columns("uid").InQ("uid", acc.Select("topics").Columns("createdBy").Where("ipaddress = ?")).Prepare(), + searchReplies: acc.Select("users").Columns("uid").InQ("uid", acc.Select("replies").Columns("createdBy").Where("ipaddress = ?")).Prepare(), + searchUsersReplies: acc.Select("users").Columns("uid").InQ("uid", acc.Select("users_replies").Columns("createdBy").Where("ipaddress = ?")).Prepare(), }, acc.FirstError() } @@ -53,16 +55,18 @@ func (searcher *DefaultIPSearcher) Lookup(ip string) (uids []int, err error) { if err != nil { return uids, err } - err = runQuery(searcher.searchTopics) if err != nil { return uids, err } - err = runQuery(searcher.searchReplies) if err != nil { return uids, err } + err = runQuery(searcher.searchUsersReplies) + if err != nil { + return uids, err + } // Convert the user ID map to a slice, then bulk load the users uids = make([]int, len(reqUserList)) diff --git a/common/subscription.go b/common/subscription.go new file mode 100644 index 00000000..8ad83715 --- /dev/null +++ b/common/subscription.go @@ -0,0 +1,27 @@ +package common + +import "database/sql" +import "../query_gen/lib" + +var Subscriptions SubscriptionStore + +// ? Should we have a subscription store for each zone? topic, forum, etc? +type SubscriptionStore interface { + Add(uid int, elementID int, elementType string) error +} + +type DefaultSubscriptionStore struct { + add *sql.Stmt +} + +func NewDefaultSubscriptionStore() (*DefaultSubscriptionStore, error) { + acc := qgen.Builder.Accumulator() + return &DefaultSubscriptionStore{ + add: acc.Insert("activity_subscriptions").Columns("user, targetID, targetType, level").Fields("?,?,?,2").Prepare(), + }, acc.FirstError() +} + +func (store *DefaultSubscriptionStore) Add(uid int, elementID int, elementType string) error { + _, err := store.add.Exec(uid, elementID, elementType) + return err +} diff --git a/gen_mssql.go b/gen_mssql.go index a3a9ec1e..76bfac36 100644 --- a/gen_mssql.go +++ b/gen_mssql.go @@ -35,11 +35,9 @@ type Stmts struct { addActivity *sql.Stmt notifyOne *sql.Stmt addEmail *sql.Stmt - addSubscription *sql.Stmt addForumPermsToForum *sql.Stmt addPlugin *sql.Stmt addTheme *sql.Stmt - addAttachment *sql.Stmt createWordFilter *sql.Stmt editReply *sql.Stmt updatePlugin *sql.Stmt @@ -256,13 +254,6 @@ func _gen_mssql() (err error) { return err } - log.Print("Preparing addSubscription statement.") - stmts.addSubscription, err = db.Prepare("INSERT INTO [activity_subscriptions] ([user],[targetID],[targetType],[level]) VALUES (?,?,?,2)") - if err != nil { - log.Print("Bad Query: ","INSERT INTO [activity_subscriptions] ([user],[targetID],[targetType],[level]) VALUES (?,?,?,2)") - return err - } - log.Print("Preparing addForumPermsToForum statement.") stmts.addForumPermsToForum, err = db.Prepare("INSERT INTO [forums_permissions] ([gid],[fid],[preset],[permissions]) VALUES (?,?,?,?)") if err != nil { @@ -284,13 +275,6 @@ func _gen_mssql() (err error) { return err } - log.Print("Preparing addAttachment statement.") - stmts.addAttachment, err = db.Prepare("INSERT INTO [attachments] ([sectionID],[sectionTable],[originID],[originTable],[uploadedBy],[path]) VALUES (?,?,?,?,?,?)") - if err != nil { - log.Print("Bad Query: ","INSERT INTO [attachments] ([sectionID],[sectionTable],[originID],[originTable],[uploadedBy],[path]) VALUES (?,?,?,?,?,?)") - return err - } - log.Print("Preparing createWordFilter statement.") stmts.createWordFilter, err = db.Prepare("INSERT INTO [word_filters] ([find],[replacement]) VALUES (?,?)") if err != nil { diff --git a/gen_mysql.go b/gen_mysql.go index 55e07e0d..9f3fb318 100644 --- a/gen_mysql.go +++ b/gen_mysql.go @@ -37,11 +37,9 @@ type Stmts struct { addActivity *sql.Stmt notifyOne *sql.Stmt addEmail *sql.Stmt - addSubscription *sql.Stmt addForumPermsToForum *sql.Stmt addPlugin *sql.Stmt addTheme *sql.Stmt - addAttachment *sql.Stmt createWordFilter *sql.Stmt editReply *sql.Stmt updatePlugin *sql.Stmt @@ -232,12 +230,6 @@ func _gen_mysql() (err error) { return err } - log.Print("Preparing addSubscription statement.") - stmts.addSubscription, err = db.Prepare("INSERT INTO `activity_subscriptions`(`user`,`targetID`,`targetType`,`level`) VALUES (?,?,?,2)") - if err != nil { - return err - } - log.Print("Preparing addForumPermsToForum statement.") stmts.addForumPermsToForum, err = db.Prepare("INSERT INTO `forums_permissions`(`gid`,`fid`,`preset`,`permissions`) VALUES (?,?,?,?)") if err != nil { @@ -256,12 +248,6 @@ func _gen_mysql() (err error) { return err } - log.Print("Preparing addAttachment statement.") - stmts.addAttachment, err = db.Prepare("INSERT INTO `attachments`(`sectionID`,`sectionTable`,`originID`,`originTable`,`uploadedBy`,`path`) VALUES (?,?,?,?,?,?)") - if err != nil { - return err - } - log.Print("Preparing createWordFilter statement.") stmts.createWordFilter, err = db.Prepare("INSERT INTO `word_filters`(`find`,`replacement`) VALUES (?,?)") if err != nil { diff --git a/gen_router.go b/gen_router.go index 536e9e2e..f889e4f9 100644 --- a/gen_router.go +++ b/gen_router.go @@ -69,7 +69,7 @@ var RouteMap = map[string]interface{}{ "routePanelBackups": routePanelBackups, "routePanelLogsMod": routePanelLogsMod, "routePanelDebug": routePanelDebug, - "routePanel": routePanel, + "routePanelDashboard": routePanelDashboard, "routeAccountEditCritical": routeAccountEditCritical, "routeAccountEditCriticalSubmit": routeAccountEditCriticalSubmit, "routeAccountEditAvatar": routeAccountEditAvatar, @@ -83,7 +83,7 @@ var RouteMap = map[string]interface{}{ "routes.UnbanUser": routes.UnbanUser, "routes.ActivateUser": routes.ActivateUser, "routes.IPSearch": routes.IPSearch, - "routeCreateTopicSubmit": routeCreateTopicSubmit, + "routes.CreateTopicSubmit": routes.CreateTopicSubmit, "routes.EditTopicSubmit": routes.EditTopicSubmit, "routes.DeleteTopicSubmit": routes.DeleteTopicSubmit, "routes.StickTopicSubmit": routes.StickTopicSubmit, @@ -164,7 +164,7 @@ var routeMapEnum = map[string]int{ "routePanelBackups": 50, "routePanelLogsMod": 51, "routePanelDebug": 52, - "routePanel": 53, + "routePanelDashboard": 53, "routeAccountEditCritical": 54, "routeAccountEditCriticalSubmit": 55, "routeAccountEditAvatar": 56, @@ -178,7 +178,7 @@ var routeMapEnum = map[string]int{ "routes.UnbanUser": 64, "routes.ActivateUser": 65, "routes.IPSearch": 66, - "routeCreateTopicSubmit": 67, + "routes.CreateTopicSubmit": 67, "routes.EditTopicSubmit": 68, "routes.DeleteTopicSubmit": 69, "routes.StickTopicSubmit": 70, @@ -257,7 +257,7 @@ var reverseRouteMapEnum = map[int]string{ 50: "routePanelBackups", 51: "routePanelLogsMod", 52: "routePanelDebug", - 53: "routePanel", + 53: "routePanelDashboard", 54: "routeAccountEditCritical", 55: "routeAccountEditCriticalSubmit", 56: "routeAccountEditAvatar", @@ -271,7 +271,7 @@ var reverseRouteMapEnum = map[int]string{ 64: "routes.UnbanUser", 65: "routes.ActivateUser", 66: "routes.IPSearch", - 67: "routeCreateTopicSubmit", + 67: "routes.CreateTopicSubmit", 68: "routes.EditTopicSubmit", 69: "routes.DeleteTopicSubmit", 70: "routes.StickTopicSubmit", @@ -951,7 +951,7 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) { err = routePanelDebug(w,req,user) default: common.RouteViewCounter.Bump(53) - err = routePanel(w,req,user) + err = routePanelDashboard(w,req,user) } if err != nil { router.handleError(err,w,req,user) @@ -1130,20 +1130,25 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) { case "/topic": switch(req.URL.Path) { case "/topic/create/submit/": - err = common.NoSessionMismatch(w,req,user) - if err != nil { - router.handleError(err,w,req,user) - return - } - err = common.MemberOnly(w,req,user) if err != nil { router.handleError(err,w,req,user) return } + err = common.HandleUploadRoute(w,req,user,common.Config.MaxRequestSize) + if err != nil { + router.handleError(err,w,req,user) + return + } + err = common.NoUploadSessionMismatch(w,req,user) + if err != nil { + router.handleError(err,w,req,user) + return + } + common.RouteViewCounter.Bump(67) - err = routeCreateTopicSubmit(w,req,user) + err = routes.CreateTopicSubmit(w,req,user) case "/topic/edit/submit/": err = common.NoSessionMismatch(w,req,user) if err != nil { diff --git a/main.go b/main.go index e2a5c135..830d425b 100644 --- a/main.go +++ b/main.go @@ -84,6 +84,14 @@ func afterDBInit() (err error) { if err != nil { return err } + common.Subscriptions, err = common.NewDefaultSubscriptionStore() + if err != nil { + return err + } + common.Attachments, err = common.NewDefaultAttachmentStore() + if err != nil { + return err + } common.GlobalViewCounter, err = common.NewGlobalViewCounter() if err != nil { diff --git a/member_routes.go b/member_routes.go index 30ab8243..285c1b9e 100644 --- a/member_routes.go +++ b/member_routes.go @@ -16,135 +16,6 @@ import ( "./common" ) -func routeCreateTopicSubmit(w http.ResponseWriter, r *http.Request, user common.User) common.RouteError { - // TODO: Reduce this to 1MB for attachments for each file? - if r.ContentLength > int64(common.Config.MaxRequestSize) { - size, unit := common.ConvertByteUnit(float64(common.Config.MaxRequestSize)) - return common.CustomError("Your attachments are too big. Your files need to be smaller than "+strconv.Itoa(int(size))+unit+".", http.StatusExpectationFailed, "Error", w, r, user) - } - r.Body = http.MaxBytesReader(w, r.Body, int64(common.Config.MaxRequestSize)) - - err := r.ParseMultipartForm(int64(common.Megabyte)) - if err != nil { - return common.LocalError("Unable to parse the form", w, r, user) - } - - fid, err := strconv.Atoi(r.PostFormValue("topic-board")) - if err != nil { - return common.LocalError("The provided ForumID is not a valid number.", w, r, user) - } - - // 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.CreateTopic { - return common.NoPermissions(w, r, user) - } - - topicName := html.EscapeString(strings.Replace(r.PostFormValue("topic-name"), "\n", "", -1)) - content := common.PreparseMessage(r.PostFormValue("topic-content")) - // TODO: Fully parse the post and store it in the parsed column - tid, err := common.Topics.Create(fid, topicName, content, user.ID, user.LastIP) - if err != nil { - switch err { - case common.ErrNoRows: - return common.LocalError("Something went wrong, perhaps the forum got deleted?", w, r, user) - case common.ErrNoTitle: - return common.LocalError("This topic doesn't have a title", w, r, user) - case common.ErrNoBody: - return common.LocalError("This topic doesn't have a body", w, r, user) - default: - return common.InternalError(err, w, r) - } - } - - _, err = stmts.addSubscription.Exec(user.ID, tid, "topic") - if err != nil { - return common.InternalError(err, w, r) - } - - err = user.IncreasePostStats(common.WordCount(content), true) - if err != nil { - return common.InternalError(err, w, r) - } - - // Handle the file attachments - // TODO: Stop duplicating this code - if user.Perms.UploadFiles { - files, ok := r.MultipartForm.File["upload_files"] - if ok { - if len(files) > 5 { - return common.LocalError("You can't attach more than five files", w, r, user) - } - - for _, file := range files { - if common.Dev.DebugMode { - log.Print("file.Filename ", file.Filename) - } - extarr := strings.Split(file.Filename, ".") - if len(extarr) < 2 { - return common.LocalError("Bad file", w, r, user) - } - ext := extarr[len(extarr)-1] - - // TODO: Can we do this without a regex? - reg, err := regexp.Compile("[^A-Za-z0-9]+") - if err != nil { - return common.LocalError("Bad file extension", w, r, user) - } - ext = strings.ToLower(reg.ReplaceAllString(ext, "")) - if !common.AllowedFileExts.Contains(ext) { - return common.LocalError("You're not allowed to upload files with this extension", w, r, user) - } - - infile, err := file.Open() - if err != nil { - return common.LocalError("Upload failed", w, r, user) - } - defer infile.Close() - - hasher := sha256.New() - _, err = io.Copy(hasher, infile) - if err != nil { - return common.LocalError("Upload failed [Hashing Failed]", w, r, user) - } - infile.Close() - - checksum := hex.EncodeToString(hasher.Sum(nil)) - filename := checksum + "." + ext - outfile, err := os.Create("." + "/attachs/" + filename) - if err != nil { - return common.LocalError("Upload failed [File Creation Failed]", w, r, user) - } - defer outfile.Close() - - infile, err = file.Open() - if err != nil { - return common.LocalError("Upload failed", w, r, user) - } - defer infile.Close() - - _, err = io.Copy(outfile, infile) - if err != nil { - return common.LocalError("Upload failed [Copy Failed]", w, r, user) - } - - _, err = stmts.addAttachment.Exec(fid, "forums", tid, "topics", user.ID, filename) - if err != nil { - return common.InternalError(err, w, r) - } - } - } - } - - common.PostCounter.Bump() - common.TopicCounter.Bump() - http.Redirect(w, r, "/topic/"+strconv.Itoa(tid), http.StatusSeeOther) - return nil -} - func routeCreateReplySubmit(w http.ResponseWriter, r *http.Request, user common.User) common.RouteError { tid, err := strconv.Atoi(r.PostFormValue("tid")) if err != nil { @@ -226,7 +97,7 @@ func routeCreateReplySubmit(w http.ResponseWriter, r *http.Request, user common. return common.LocalError("Upload failed [Copy Failed]", w, r, user) } - _, err = stmts.addAttachment.Exec(topic.ParentID, "forums", tid, "replies", user.ID, filename) + err = common.Attachments.Add(topic.ParentID, "forums", tid, "replies", user.ID, filename) if err != nil { return common.InternalError(err, w, r) } diff --git a/panel_routes.go b/panel_routes.go index 1b61829b..1bc94edd 100644 --- a/panel_routes.go +++ b/panel_routes.go @@ -43,7 +43,7 @@ func panelRenderTemplate(tmplName string, w http.ResponseWriter, r *http.Request return nil } -func routePanel(w http.ResponseWriter, r *http.Request, user common.User) common.RouteError { +func routePanelDashboard(w http.ResponseWriter, r *http.Request, user common.User) common.RouteError { headerVars, stats, ferr := common.PanelUserCheck(w, r, &user) if ferr != nil { return ferr diff --git a/query_gen/main.go b/query_gen/main.go index 3e73dcda..72be2475 100644 --- a/query_gen/main.go +++ b/query_gen/main.go @@ -288,16 +288,12 @@ func writeInserts(adapter qgen.Adapter) error { build.Insert("addEmail").Table("emails").Columns("email, uid, validated, token").Fields("?,?,?,?").Parse() - build.Insert("addSubscription").Table("activity_subscriptions").Columns("user, targetID, targetType, level").Fields("?,?,?,2").Parse() - build.Insert("addForumPermsToForum").Table("forums_permissions").Columns("gid,fid,preset,permissions").Fields("?,?,?,?").Parse() build.Insert("addPlugin").Table("plugins").Columns("uname, active, installed").Fields("?,?,?").Parse() build.Insert("addTheme").Table("themes").Columns("uname, default").Fields("?,?").Parse() - build.Insert("addAttachment").Table("attachments").Columns("sectionID, sectionTable, originID, originTable, uploadedBy, path").Fields("?,?,?,?,?,?").Parse() - build.Insert("createWordFilter").Table("word_filters").Columns("find, replacement").Fields("?,?").Parse() return nil diff --git a/router_gen/routes.go b/router_gen/routes.go index 49149be2..4dbde9da 100644 --- a/router_gen/routes.go +++ b/router_gen/routes.go @@ -65,7 +65,7 @@ func buildTopicRoutes() { topicGroup := newRouteGroup("/topic/") topicGroup.Routes( View("routeTopicID", "/topic/", "extraData"), - Action("routeCreateTopicSubmit", "/topic/create/submit/"), + UploadAction("routes.CreateTopicSubmit", "/topic/create/submit/").MaxSizeVar("common.Config.MaxRequestSize"), Action("routes.EditTopicSubmit", "/topic/edit/submit/", "extraData"), Action("routes.DeleteTopicSubmit", "/topic/delete/submit/").LitBefore("req.URL.Path += extraData"), Action("routes.StickTopicSubmit", "/topic/stick/submit/", "extraData"), @@ -120,7 +120,7 @@ func buildAccountRoutes() { func buildPanelRoutes() { panelGroup := newRouteGroup("/panel/").Before("SuperModOnly") panelGroup.Routes( - View("routePanel", "/panel/"), + View("routePanelDashboard", "/panel/"), View("routePanelForums", "/panel/forums/"), Action("routePanelForumsCreateSubmit", "/panel/forums/create/"), Action("routePanelForumsDelete", "/panel/forums/delete/", "extraData"), diff --git a/routes/topic.go b/routes/topic.go index 8de77a0b..4a4fe46d 100644 --- a/routes/topic.go +++ b/routes/topic.go @@ -1,11 +1,18 @@ package routes import ( + "crypto/sha256" "database/sql" + "encoding/hex" "encoding/json" + "html" + "io" "log" "net/http" + "os" + "regexp" "strconv" + "strings" "../common" ) @@ -99,6 +106,123 @@ func CreateTopic(w http.ResponseWriter, r *http.Request, user common.User, sfid return nil } +func CreateTopicSubmit(w http.ResponseWriter, r *http.Request, user common.User) common.RouteError { + fid, err := strconv.Atoi(r.PostFormValue("topic-board")) + if err != nil { + return common.LocalError("The provided ForumID is not a valid number.", w, r, user) + } + + // 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.CreateTopic { + return common.NoPermissions(w, r, user) + } + + topicName := html.EscapeString(strings.Replace(r.PostFormValue("topic-name"), "\n", "", -1)) + content := common.PreparseMessage(r.PostFormValue("topic-content")) + // TODO: Fully parse the post and store it in the parsed column + tid, err := common.Topics.Create(fid, topicName, content, user.ID, user.LastIP) + if err != nil { + switch err { + case common.ErrNoRows: + return common.LocalError("Something went wrong, perhaps the forum got deleted?", w, r, user) + case common.ErrNoTitle: + return common.LocalError("This topic doesn't have a title", w, r, user) + case common.ErrNoBody: + return common.LocalError("This topic doesn't have a body", w, r, user) + default: + return common.InternalError(err, w, r) + } + } + + err = common.Subscriptions.Add(user.ID, tid, "topic") + if err != nil { + return common.InternalError(err, w, r) + } + + err = user.IncreasePostStats(common.WordCount(content), true) + if err != nil { + return common.InternalError(err, w, r) + } + + // Handle the file attachments + // TODO: Stop duplicating this code + if user.Perms.UploadFiles { + files, ok := r.MultipartForm.File["upload_files"] + if ok { + if len(files) > 5 { + return common.LocalError("You can't attach more than five files", w, r, user) + } + + for _, file := range files { + if common.Dev.DebugMode { + log.Print("file.Filename ", file.Filename) + } + extarr := strings.Split(file.Filename, ".") + if len(extarr) < 2 { + return common.LocalError("Bad file", w, r, user) + } + ext := extarr[len(extarr)-1] + + // TODO: Can we do this without a regex? + reg, err := regexp.Compile("[^A-Za-z0-9]+") + if err != nil { + return common.LocalError("Bad file extension", w, r, user) + } + ext = strings.ToLower(reg.ReplaceAllString(ext, "")) + if !common.AllowedFileExts.Contains(ext) { + return common.LocalError("You're not allowed to upload files with this extension", w, r, user) + } + + infile, err := file.Open() + if err != nil { + return common.LocalError("Upload failed", w, r, user) + } + defer infile.Close() + + hasher := sha256.New() + _, err = io.Copy(hasher, infile) + if err != nil { + return common.LocalError("Upload failed [Hashing Failed]", w, r, user) + } + infile.Close() + + checksum := hex.EncodeToString(hasher.Sum(nil)) + filename := checksum + "." + ext + outfile, err := os.Create("." + "/attachs/" + filename) + if err != nil { + return common.LocalError("Upload failed [File Creation Failed]", w, r, user) + } + defer outfile.Close() + + infile, err = file.Open() + if err != nil { + return common.LocalError("Upload failed", w, r, user) + } + defer infile.Close() + + _, err = io.Copy(outfile, infile) + if err != nil { + return common.LocalError("Upload failed [Copy Failed]", w, r, user) + } + + err = common.Attachments.Add(fid, "forums", tid, "topics", user.ID, filename) + if err != nil { + return common.InternalError(err, w, r) + } + } + } + } + + common.PostCounter.Bump() + common.TopicCounter.Bump() + http.Redirect(w, r, "/topic/"+strconv.Itoa(tid), http.StatusSeeOther) + return nil +} + // TODO: Update the stats after edits so that we don't under or over decrement stats during deletes // TODO: Disable stat updates in posts handled by plugin_guilds func EditTopicSubmit(w http.ResponseWriter, r *http.Request, user common.User, stid string) common.RouteError { diff --git a/templates/panel_users.html b/templates/panel_users.html index f88adb13..d050fabc 100644 --- a/templates/panel_users.html +++ b/templates/panel_users.html @@ -10,8 +10,8 @@ {{range .ItemList}}
{{.Name}}'s Avatar - {{.Name}} - Profile + {{.Name}} + Profile {{if (.Tag) and (.IsSuperMod)}}{{.Tag}}{{end}} diff --git a/themes/cosora/public/main.css b/themes/cosora/public/main.css index 298939fb..06f507f1 100644 --- a/themes/cosora/public/main.css +++ b/themes/cosora/public/main.css @@ -642,6 +642,10 @@ textarea { .rowlist.bgavatars .rowitem { flex-direction: column; } +.rowlist.bgavatars .rowitem { + padding-top: 16px; + padding-bottom: 10px; +} .bgavatars .bgsub { border-radius: 30px; height: 48px; @@ -1060,6 +1064,28 @@ textarea { #profile_comments .content_column { margin-bottom: 16px; } +#profile_comments button { + background: inherit; + color: var(--lighter-text-color); + padding-left: 8px; + padding-right: 8px; + cursor: pointer; +} +#profile_comments button:hover { + color: var(--light-text-color); +} +#profile_comments button.edit_item:after { + font: normal normal normal 14px/1 FontAwesome; + content: "\f040"; +} +#profile_comments button.delete_item:after { + font: normal normal normal 14px/1 FontAwesome; + content: "\f1f8"; +} +#profile_comments button.report_item:after { + font: normal normal normal 14px/1 FontAwesome; + content: "\f024"; +} #profile_comments_head { margin-top: 6px; } @@ -1167,7 +1193,7 @@ textarea { /* TODO: Highlight the one we're currently on? */ .pageset { display: flex; - margin-left: 16px; + margin-left: 14px; } .pageitem { padding: 8px; diff --git a/themes/cosora/public/panel.css b/themes/cosora/public/panel.css index 1d6ea155..da44b372 100644 --- a/themes/cosora/public/panel.css +++ b/themes/cosora/public/panel.css @@ -91,6 +91,9 @@ #panel_users .panel_tag:not(.panel_right_button) { display: none; } +.panel_right_button + .panel_right_button { + margin-left: 3px; +} .panel_group_perms .formitem a { margin-top: 5px; @@ -128,6 +131,12 @@ display: block; } +#panel_users .rowitem .rowTitle { + border-bottom: 1px solid var(--lighter-text-color); + padding-bottom: 4px; + margin-bottom: 4px; +} + #forum_quick_perms .formitem { display: flex; } @@ -189,4 +198,8 @@ padding: 14px; font-size: 16px; } -*/ \ No newline at end of file +*/ + +.pageset { + margin-left: 16px; +} \ No newline at end of file