IP Search now searches profile replies.
Added the subscription and attachment stores. Refactored the routes to use the new subscription and attachment stores. Fixed up the buttons on the profile comments for Cosora. Made various enhancements to Cosora. Renamed routePanel to routePanelDashboard. Changed routeCreateTopicSubmit into an upload action and moved it into /routes/topic.go and refactored some of the redundant bits away.
This commit is contained in:
parent
8252c481df
commit
a7fec11170
|
@ -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
|
||||
}
|
|
@ -14,8 +14,9 @@ type IPSearcher interface {
|
|||
|
||||
type DefaultIPSearcher struct {
|
||||
searchUsers *sql.Stmt
|
||||
searchReplies *sql.Stmt
|
||||
searchTopics *sql.Stmt
|
||||
searchReplies *sql.Stmt
|
||||
searchUsersReplies *sql.Stmt
|
||||
}
|
||||
|
||||
// NewDefaultIPSearcher gives you a new instance of DefaultIPSearcher
|
||||
|
@ -25,6 +26,7 @@ func NewDefaultIPSearcher() (*DefaultIPSearcher, error) {
|
|||
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))
|
||||
|
|
|
@ -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
|
||||
}
|
16
gen_mssql.go
16
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 {
|
||||
|
|
14
gen_mysql.go
14
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 {
|
||||
|
|
|
@ -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 {
|
||||
|
|
8
main.go
8
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 {
|
||||
|
|
131
member_routes.go
131
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)
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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"),
|
||||
|
|
124
routes/topic.go
124
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 {
|
||||
|
|
|
@ -10,8 +10,8 @@
|
|||
{{range .ItemList}}
|
||||
<div class="rowitem editable_parent" style="background-image: url('{{.Avatar}}');">
|
||||
<img class="bgsub" src="{{.Avatar}}" alt="{{.Name}}'s Avatar" />
|
||||
<a class="rowTitle" {{if $.CurrentUser.Perms.EditUser}}href="/panel/users/edit/{{.ID}}?session={{$.CurrentUser.Session}}" {{end}}class="editable_block">{{.Name}}</a>
|
||||
<a href="/user/{{.ID}}" class="tag-mini">Profile</a>
|
||||
<a class="rowTitle editable_block"{{if $.CurrentUser.Perms.EditUser}} href="/panel/users/edit/{{.ID}}?session={{$.CurrentUser.Session}}"{{end}}>{{.Name}}</a>
|
||||
<a href="/user/{{.ID}}" class="tag-mini profile_url">Profile</a>
|
||||
{{if (.Tag) and (.IsSuperMod)}}<span style="float: right;"><span class="panel_tag" style="margin-left 4px;">{{.Tag}}</span></span>{{end}}
|
||||
|
||||
<span class="panel_floater">
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
@ -190,3 +199,7 @@
|
|||
font-size: 16px;
|
||||
}
|
||||
*/
|
||||
|
||||
.pageset {
|
||||
margin-left: 16px;
|
||||
}
|
Loading…
Reference in New Issue