Added proper pagination to the topic list.

Fixed two existence checks.
Tweaked the profile CSS for Cosora.
Added the TopicByReplyID function.
Split off the profile logic from Reply into ProfileReply.
Moved various hard-coded bits in the profile reply routes into ProfileReply.
Moved four reply routes into /routes/reply.go
Moved six topic routes into /routes/topic.go
We should now capture more suspicious activity.

Changed the definition of the revisions table.
This commit is contained in:
Azareal 2018-01-20 06:50:29 +00:00
parent ef839a601c
commit 2997135e80
30 changed files with 851 additions and 747 deletions

View File

@ -96,9 +96,7 @@ func buildAlert(asid int, event string, elementType string, actorID int, targetU
endFrag = "'s profile"
url = targetUser.Link
case "post":
reply := common.BlankReply()
reply.ID = elementID
topic, err := reply.Topic()
topic, err := common.TopicByReplyID(elementID)
if err != nil {
return "", errors.New("Unable to find the linked reply or parent topic")
}

View File

@ -67,6 +67,9 @@ type TopicsPage struct {
TopicList []*TopicsRow
ForumList []Forum
DefaultForum int
PageList []int
Page int
LastPage int
}
type ForumPage struct {

View File

@ -353,9 +353,7 @@ func ParseMessage(msg string, sectionID int, sectionType string /*, user User*/)
rid, intLen := CoerceIntBytes(msgbytes[start:])
i += intLen
reply := BlankReply()
reply.ID = rid
topic, err := reply.Topic()
topic, err := TopicByReplyID(rid)
if err != nil || !Forums.Exists(topic.ParentID) {
outbytes = append(outbytes, InvalidTopic...)
lastItem = i

58
common/profile_reply.go Normal file
View File

@ -0,0 +1,58 @@
package common
import (
"database/sql"
"html"
"time"
"../query_gen/lib"
)
var profileReplyStmts ProfileReplyStmts
type ProfileReply struct {
ID int
ParentID int
Content string
CreatedBy int
Group int
CreatedAt time.Time
RelativeCreatedAt string
LastEdit int
LastEditBy int
ContentLines int
IPAddress string
}
type ProfileReplyStmts struct {
edit *sql.Stmt
delete *sql.Stmt
}
func init() {
DbInits.Add(func(acc *qgen.Accumulator) error {
profileReplyStmts = ProfileReplyStmts{
edit: acc.Update("users_replies").Set("content = ?, parsed_content = ?").Where("rid = ?").Prepare(),
delete: acc.Delete("users_replies").Where("rid = ?").Prepare(),
}
return acc.FirstError()
})
}
// TODO: Write tests for this
func (reply *ProfileReply) Delete() error {
_, err := profileReplyStmts.delete.Exec(reply.ID)
return err
}
func (reply *ProfileReply) SetBody(content string) error {
content = PreparseMessage(html.UnescapeString(content))
parsedContent := ParseMessage(content, 0, "")
_, err := profileReplyStmts.edit.Exec(content, parsedContent, reply.ID)
return err
}
// TODO: We can get this from the topic store instead of a query which will always miss the cache...
func (reply *ProfileReply) Creator() (*User, error) {
return Users.Get(reply.CreatedBy)
}

View File

@ -9,7 +9,7 @@ import (
var Prstore ProfileReplyStore
type ProfileReplyStore interface {
Get(id int) (*Reply, error)
Get(id int) (*ProfileReply, error)
Create(profileID int, content string, createdBy int, ipaddress string) (id int, err error)
}
@ -28,8 +28,8 @@ func NewSQLProfileReplyStore() (*SQLProfileReplyStore, error) {
}, acc.FirstError()
}
func (store *SQLProfileReplyStore) Get(id int) (*Reply, error) {
reply := Reply{ID: id}
func (store *SQLProfileReplyStore) Get(id int) (*ProfileReply, error) {
reply := ProfileReply{ID: id}
err := store.get.QueryRow(id).Scan(&reply.ParentID, &reply.Content, &reply.CreatedBy, &reply.CreatedAt, &reply.LastEdit, &reply.LastEditBy, &reply.IPAddress)
return &reply, err
}

View File

@ -9,6 +9,7 @@ package common
import (
"database/sql"
"errors"
"html"
"time"
"../query_gen/lib"
@ -64,10 +65,10 @@ var replyStmts ReplyStmts
type ReplyStmts struct {
isLiked *sql.Stmt
createLike *sql.Stmt
edit *sql.Stmt
delete *sql.Stmt
addLikesToReply *sql.Stmt
removeRepliesFromTopic *sql.Stmt
getParent *sql.Stmt
}
func init() {
@ -75,10 +76,10 @@ func init() {
replyStmts = ReplyStmts{
isLiked: acc.Select("likes").Columns("targetItem").Where("sentBy = ? and targetItem = ? and targetType = 'replies'").Prepare(),
createLike: acc.Insert("likes").Columns("weight, targetItem, targetType, sentBy").Fields("?,?,?,?").Prepare(),
edit: acc.Update("replies").Set("content = ?, parsed_content = ?").Where("rid = ?").Prepare(),
delete: acc.Delete("replies").Where("rid = ?").Prepare(),
addLikesToReply: acc.Update("replies").Set("likeCount = likeCount + ?").Where("rid = ?").Prepare(),
removeRepliesFromTopic: acc.Update("topics").Set("postCount = postCount - ?").Where("tid = ?").Prepare(),
getParent: acc.SimpleLeftJoin("replies", "topics", "topics.tid, topics.title, topics.content, topics.createdBy, topics.createdAt, topics.is_closed, topics.sticky, topics.parentID, topics.ipaddress, topics.postCount, topics.likeCount, topics.data", "replies.tid = topics.tid", "rid = ?", "", ""),
}
return acc.FirstError()
})
@ -119,22 +120,22 @@ func (reply *Reply) Delete() error {
return err
}
func (reply *Reply) SetBody(content string) error {
topic, err := reply.Topic()
if err != nil {
return err
}
content = PreparseMessage(html.UnescapeString(content))
parsedContent := ParseMessage(content, topic.ParentID, "forums")
_, err = replyStmts.edit.Exec(content, parsedContent, reply.ID)
return err
}
func (reply *Reply) Topic() (*Topic, error) {
topic := Topic{ID: 0}
err := replyStmts.getParent.QueryRow(reply.ID).Scan(&topic.ID, &topic.Title, &topic.Content, &topic.CreatedBy, &topic.CreatedAt, &topic.IsClosed, &topic.Sticky, &topic.ParentID, &topic.IPAddress, &topic.PostCount, &topic.LikeCount, &topic.Data)
topic.Link = BuildTopicURL(NameToSlug(topic.Title), topic.ID)
return &topic, err
return Topics.Get(reply.ParentID)
}
// Copy gives you a non-pointer concurrency safe copy of the reply
func (reply *Reply) Copy() Reply {
return *reply
}
func BlankReply(ids ...int) *Reply {
var id int
if len(ids) != 0 {
id = ids[0]
}
return &Reply{ID: id}
}

View File

@ -149,7 +149,7 @@ func compileTemplates() error {
var topicsList []*TopicsRow
topicsList = append(topicsList, &TopicsRow{1, "topic-title", "Topic Title", "The topic content.", 1, false, false, "Date", time.Now(), "Date", user3.ID, 1, "", "127.0.0.1", 0, 1, "classname", "", &user2, "", 0, &user3, "General", "/forum/general.2"})
topicsPage := TopicsPage{"Topic List", user, headerVars, topicsList, forumList, Config.DefaultForum}
topicsPage := TopicsPage{"Topic List", user, headerVars, topicsList, forumList, Config.DefaultForum, []int{1}, 1, 1}
topicsTmpl, err := c.Compile("topics.html", "templates/", "common.TopicsPage", topicsPage, varList)
if err != nil {
return err

View File

@ -123,6 +123,7 @@ type TopicStmts struct {
createActionReply *sql.Stmt
getTopicUser *sql.Stmt // TODO: Can we get rid of this?
getByReplyID *sql.Stmt
}
var topicStmts TopicStmts
@ -144,6 +145,7 @@ func init() {
createActionReply: acc.Insert("replies").Columns("tid, actionType, ipaddress, createdBy, createdAt, lastUpdated, content, parsed_content").Fields("?,?,?,?,UTC_TIMESTAMP(),UTC_TIMESTAMP(),'',''").Prepare(),
getTopicUser: acc.SimpleLeftJoin("topics", "users", "topics.title, topics.content, topics.createdBy, topics.createdAt, topics.is_closed, topics.sticky, topics.parentID, topics.ipaddress, topics.postCount, topics.likeCount, users.name, users.avatar, users.group, users.url_prefix, users.url_name, users.level", "topics.createdBy = users.uid", "tid = ?", "", ""),
getByReplyID: acc.SimpleLeftJoin("replies", "topics", "topics.tid, topics.title, topics.content, topics.createdBy, topics.createdAt, topics.is_closed, topics.sticky, topics.parentID, topics.ipaddress, topics.postCount, topics.likeCount, topics.data", "replies.tid = topics.tid", "rid = ?", "", ""),
}
return acc.FirstError()
})
@ -272,6 +274,13 @@ func (topic *Topic) Copy() Topic {
return *topic
}
func TopicByReplyID(rid int) (*Topic, error) {
topic := Topic{ID: 0}
err := topicStmts.getByReplyID.QueryRow(rid).Scan(&topic.ID, &topic.Title, &topic.Content, &topic.CreatedBy, &topic.CreatedAt, &topic.IsClosed, &topic.Sticky, &topic.ParentID, &topic.IPAddress, &topic.PostCount, &topic.LikeCount, &topic.Data)
topic.Link = BuildTopicURL(NameToSlug(topic.Title), topic.ID)
return &topic, err
}
// TODO: Refactor the caller to take a Topic and a User rather than a combined TopicUser
func GetTopicUser(tid int) (TopicUser, error) {
tcache := Topics.GetCache()

View File

@ -16,9 +16,7 @@ type Stmts struct {
getModlogs *sql.Stmt
getModlogsOffset *sql.Stmt
getAdminlogsOffset *sql.Stmt
getReplyTID *sql.Stmt
getTopicFID *sql.Stmt
getUserReplyUID *sql.Stmt
getUserName *sql.Stmt
getEmailsByUser *sql.Stmt
getTopicBasic *sql.Stmt
@ -44,7 +42,6 @@ type Stmts struct {
addAttachment *sql.Stmt
createWordFilter *sql.Stmt
editReply *sql.Stmt
editProfileReply *sql.Stmt
updatePlugin *sql.Stmt
updatePluginInstall *sql.Stmt
updateTheme *sql.Stmt
@ -56,7 +53,6 @@ type Stmts struct {
setTempGroup *sql.Stmt
updateWordFilter *sql.Stmt
bumpSync *sql.Stmt
deleteProfileReply *sql.Stmt
deleteActivityStreamMatch *sql.Stmt
deleteWordFilter *sql.Stmt
reportExists *sql.Stmt
@ -130,13 +126,6 @@ func _gen_mssql() (err error) {
return err
}
log.Print("Preparing getReplyTID statement.")
stmts.getReplyTID, err = db.Prepare("SELECT [tid] FROM [replies] WHERE [rid] = ?1")
if err != nil {
log.Print("Bad Query: ","SELECT [tid] FROM [replies] WHERE [rid] = ?1")
return err
}
log.Print("Preparing getTopicFID statement.")
stmts.getTopicFID, err = db.Prepare("SELECT [parentID] FROM [topics] WHERE [tid] = ?1")
if err != nil {
@ -144,13 +133,6 @@ func _gen_mssql() (err error) {
return err
}
log.Print("Preparing getUserReplyUID statement.")
stmts.getUserReplyUID, err = db.Prepare("SELECT [uid] FROM [users_replies] WHERE [rid] = ?1")
if err != nil {
log.Print("Bad Query: ","SELECT [uid] FROM [users_replies] WHERE [rid] = ?1")
return err
}
log.Print("Preparing getUserName statement.")
stmts.getUserName, err = db.Prepare("SELECT [name] FROM [users] WHERE [uid] = ?1")
if err != nil {
@ -326,13 +308,6 @@ func _gen_mssql() (err error) {
return err
}
log.Print("Preparing editProfileReply statement.")
stmts.editProfileReply, err = db.Prepare("UPDATE [users_replies] SET [content] = ?,[parsed_content] = ? WHERE [rid] = ?")
if err != nil {
log.Print("Bad Query: ","UPDATE [users_replies] SET [content] = ?,[parsed_content] = ? WHERE [rid] = ?")
return err
}
log.Print("Preparing updatePlugin statement.")
stmts.updatePlugin, err = db.Prepare("UPDATE [plugins] SET [active] = ? WHERE [uname] = ?")
if err != nil {
@ -410,13 +385,6 @@ func _gen_mssql() (err error) {
return err
}
log.Print("Preparing deleteProfileReply statement.")
stmts.deleteProfileReply, err = db.Prepare("DELETE FROM [users_replies] WHERE [rid] = ?")
if err != nil {
log.Print("Bad Query: ","DELETE FROM [users_replies] WHERE [rid] = ?")
return err
}
log.Print("Preparing deleteActivityStreamMatch statement.")
stmts.deleteActivityStreamMatch, err = db.Prepare("DELETE FROM [activity_stream_matches] WHERE [watcher] = ? AND [asid] = ?")
if err != nil {

View File

@ -18,9 +18,7 @@ type Stmts struct {
getModlogs *sql.Stmt
getModlogsOffset *sql.Stmt
getAdminlogsOffset *sql.Stmt
getReplyTID *sql.Stmt
getTopicFID *sql.Stmt
getUserReplyUID *sql.Stmt
getUserName *sql.Stmt
getEmailsByUser *sql.Stmt
getTopicBasic *sql.Stmt
@ -46,7 +44,6 @@ type Stmts struct {
addAttachment *sql.Stmt
createWordFilter *sql.Stmt
editReply *sql.Stmt
editProfileReply *sql.Stmt
updatePlugin *sql.Stmt
updatePluginInstall *sql.Stmt
updateTheme *sql.Stmt
@ -58,7 +55,6 @@ type Stmts struct {
setTempGroup *sql.Stmt
updateWordFilter *sql.Stmt
bumpSync *sql.Stmt
deleteProfileReply *sql.Stmt
deleteActivityStreamMatch *sql.Stmt
deleteWordFilter *sql.Stmt
reportExists *sql.Stmt
@ -125,24 +121,12 @@ func _gen_mysql() (err error) {
return err
}
log.Print("Preparing getReplyTID statement.")
stmts.getReplyTID, err = db.Prepare("SELECT `tid` FROM `replies` WHERE `rid` = ?")
if err != nil {
return err
}
log.Print("Preparing getTopicFID statement.")
stmts.getTopicFID, err = db.Prepare("SELECT `parentID` FROM `topics` WHERE `tid` = ?")
if err != nil {
return err
}
log.Print("Preparing getUserReplyUID statement.")
stmts.getUserReplyUID, err = db.Prepare("SELECT `uid` FROM `users_replies` WHERE `rid` = ?")
if err != nil {
return err
}
log.Print("Preparing getUserName statement.")
stmts.getUserName, err = db.Prepare("SELECT `name` FROM `users` WHERE `uid` = ?")
if err != nil {
@ -293,12 +277,6 @@ func _gen_mysql() (err error) {
return err
}
log.Print("Preparing editProfileReply statement.")
stmts.editProfileReply, err = db.Prepare("UPDATE `users_replies` SET `content` = ?,`parsed_content` = ? WHERE `rid` = ?")
if err != nil {
return err
}
log.Print("Preparing updatePlugin statement.")
stmts.updatePlugin, err = db.Prepare("UPDATE `plugins` SET `active` = ? WHERE `uname` = ?")
if err != nil {
@ -365,12 +343,6 @@ func _gen_mysql() (err error) {
return err
}
log.Print("Preparing deleteProfileReply statement.")
stmts.deleteProfileReply, err = db.Prepare("DELETE FROM `users_replies` WHERE `rid` = ?")
if err != nil {
return err
}
log.Print("Preparing deleteActivityStreamMatch statement.")
stmts.deleteActivityStreamMatch, err = db.Prepare("DELETE FROM `activity_stream_matches` WHERE `watcher` = ? AND `asid` = ?")
if err != nil {

View File

@ -10,7 +10,6 @@ import "./common"
// nolint
type Stmts struct {
editReply *sql.Stmt
editProfileReply *sql.Stmt
updatePlugin *sql.Stmt
updatePluginInstall *sql.Stmt
updateTheme *sql.Stmt
@ -48,12 +47,6 @@ func _gen_pgsql() (err error) {
return err
}
log.Print("Preparing editProfileReply statement.")
stmts.editProfileReply, err = db.Prepare("UPDATE `users_replies` SET `content` = ?,`parsed_content` = ? WHERE `rid` = ?")
if err != nil {
return err
}
log.Print("Preparing updatePlugin statement.")
stmts.updatePlugin, err = db.Prepare("UPDATE `plugins` SET `active` = ? WHERE `uname` = ?")
if err != nil {

View File

@ -85,21 +85,21 @@ var RouteMap = map[string]interface{}{
"routeIps": routeIps,
"routeTopicCreateSubmit": routeTopicCreateSubmit,
"routes.EditTopicSubmit": routes.EditTopicSubmit,
"routeDeleteTopicSubmit": routeDeleteTopicSubmit,
"routeStickTopicSubmit": routeStickTopicSubmit,
"routeUnstickTopicSubmit": routeUnstickTopicSubmit,
"routeLockTopicSubmit": routeLockTopicSubmit,
"routeUnlockTopicSubmit": routeUnlockTopicSubmit,
"routeMoveTopicSubmit": routeMoveTopicSubmit,
"routes.DeleteTopicSubmit": routes.DeleteTopicSubmit,
"routes.StickTopicSubmit": routes.StickTopicSubmit,
"routes.UnstickTopicSubmit": routes.UnstickTopicSubmit,
"routes.LockTopicSubmit": routes.LockTopicSubmit,
"routes.UnlockTopicSubmit": routes.UnlockTopicSubmit,
"routes.MoveTopicSubmit": routes.MoveTopicSubmit,
"routeLikeTopicSubmit": routeLikeTopicSubmit,
"routeTopicID": routeTopicID,
"routeCreateReplySubmit": routeCreateReplySubmit,
"routeReplyEditSubmit": routeReplyEditSubmit,
"routeReplyDeleteSubmit": routeReplyDeleteSubmit,
"routes.ReplyEditSubmit": routes.ReplyEditSubmit,
"routes.ReplyDeleteSubmit": routes.ReplyDeleteSubmit,
"routeReplyLikeSubmit": routeReplyLikeSubmit,
"routeProfileReplyCreateSubmit": routeProfileReplyCreateSubmit,
"routeProfileReplyEditSubmit": routeProfileReplyEditSubmit,
"routeProfileReplyDeleteSubmit": routeProfileReplyDeleteSubmit,
"routes.ProfileReplyEditSubmit": routes.ProfileReplyEditSubmit,
"routes.ProfileReplyDeleteSubmit": routes.ProfileReplyDeleteSubmit,
"routeLogin": routeLogin,
"routeRegister": routeRegister,
"routeLogout": routeLogout,
@ -180,21 +180,21 @@ var routeMapEnum = map[string]int{
"routeIps": 66,
"routeTopicCreateSubmit": 67,
"routes.EditTopicSubmit": 68,
"routeDeleteTopicSubmit": 69,
"routeStickTopicSubmit": 70,
"routeUnstickTopicSubmit": 71,
"routeLockTopicSubmit": 72,
"routeUnlockTopicSubmit": 73,
"routeMoveTopicSubmit": 74,
"routes.DeleteTopicSubmit": 69,
"routes.StickTopicSubmit": 70,
"routes.UnstickTopicSubmit": 71,
"routes.LockTopicSubmit": 72,
"routes.UnlockTopicSubmit": 73,
"routes.MoveTopicSubmit": 74,
"routeLikeTopicSubmit": 75,
"routeTopicID": 76,
"routeCreateReplySubmit": 77,
"routeReplyEditSubmit": 78,
"routeReplyDeleteSubmit": 79,
"routes.ReplyEditSubmit": 78,
"routes.ReplyDeleteSubmit": 79,
"routeReplyLikeSubmit": 80,
"routeProfileReplyCreateSubmit": 81,
"routeProfileReplyEditSubmit": 82,
"routeProfileReplyDeleteSubmit": 83,
"routes.ProfileReplyEditSubmit": 82,
"routes.ProfileReplyDeleteSubmit": 83,
"routeLogin": 84,
"routeRegister": 85,
"routeLogout": 86,
@ -273,21 +273,21 @@ var reverseRouteMapEnum = map[int]string{
66: "routeIps",
67: "routeTopicCreateSubmit",
68: "routes.EditTopicSubmit",
69: "routeDeleteTopicSubmit",
70: "routeStickTopicSubmit",
71: "routeUnstickTopicSubmit",
72: "routeLockTopicSubmit",
73: "routeUnlockTopicSubmit",
74: "routeMoveTopicSubmit",
69: "routes.DeleteTopicSubmit",
70: "routes.StickTopicSubmit",
71: "routes.UnstickTopicSubmit",
72: "routes.LockTopicSubmit",
73: "routes.UnlockTopicSubmit",
74: "routes.MoveTopicSubmit",
75: "routeLikeTopicSubmit",
76: "routeTopicID",
77: "routeCreateReplySubmit",
78: "routeReplyEditSubmit",
79: "routeReplyDeleteSubmit",
78: "routes.ReplyEditSubmit",
79: "routes.ReplyDeleteSubmit",
80: "routeReplyLikeSubmit",
81: "routeProfileReplyCreateSubmit",
82: "routeProfileReplyEditSubmit",
83: "routeProfileReplyDeleteSubmit",
82: "routes.ProfileReplyEditSubmit",
83: "routes.ProfileReplyDeleteSubmit",
84: "routeLogin",
85: "routeRegister",
86: "routeLogout",
@ -395,9 +395,21 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
if len(req.URL.Path) == 0 || req.URL.Path[0] != '/' {
w.WriteHeader(405)
w.Write([]byte(""))
log.Print("Suspicious UA: ", req.UserAgent())
log.Print("Method: ", req.Method)
for key, value := range req.Header {
for _, vvalue := range value {
log.Print("Header '" + key + "': " + vvalue + "!!")
}
}
log.Print("req.URL.Path: ", req.URL.Path)
log.Print("req.URL.RawQuery: ", req.URL.RawQuery)
log.Print("req.Referer(): ", req.Referer())
log.Print("req.RemoteAddr: ", req.RemoteAddr)
return
}
if common.Dev.DebugMode {
// TODO: Cover more suspicious strings and at a lower layer than this
for _, char := range req.URL.Path {
if char != '&' && !(char > 44 && char < 58) && char != '=' && char != '?' && !(char > 64 && char < 91) && char != '\\' && char != '_' && !(char > 96 && char < 123) {
@ -409,8 +421,10 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
}
}
log.Print("req.URL.Path: ", req.URL.Path)
log.Print("req.URL.RawQuery: ", req.URL.RawQuery)
log.Print("req.Referer(): ", req.Referer())
log.Print("req.RemoteAddr: ", req.RemoteAddr)
break
}
}
if strings.Contains(req.URL.Path,"..") || strings.Contains(req.URL.Path,"--") {
@ -422,9 +436,11 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
}
}
log.Print("req.URL.Path: ", req.URL.Path)
log.Print("req.URL.RawQuery: ", req.URL.RawQuery)
log.Print("req.Referer(): ", req.Referer())
log.Print("req.RemoteAddr: ", req.RemoteAddr)
}
}
var prefix, extraData string
prefix = req.URL.Path[0:strings.IndexByte(req.URL.Path[1:],'/') + 1]
@ -443,6 +459,7 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
}
log.Print("prefix: ", prefix)
log.Print("req.URL.Path: ", req.URL.Path)
log.Print("req.URL.RawQuery: ", req.URL.RawQuery)
log.Print("extraData: ", extraData)
log.Print("req.Referer(): ", req.Referer())
log.Print("req.RemoteAddr: ", req.RemoteAddr)
@ -506,6 +523,7 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
}
log.Print("prefix: ", prefix)
log.Print("req.URL.Path: ", req.URL.Path)
log.Print("req.URL.RawQuery: ", req.URL.RawQuery)
log.Print("extraData: ", extraData)
log.Print("req.Referer(): ", req.Referer())
log.Print("req.RemoteAddr: ", req.RemoteAddr)
@ -522,6 +540,7 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
}
log.Print("prefix: ", prefix)
log.Print("req.URL.Path: ", req.URL.Path)
log.Print("req.URL.RawQuery: ", req.URL.RawQuery)
log.Print("extraData: ", extraData)
log.Print("req.Referer(): ", req.Referer())
log.Print("req.RemoteAddr: ", req.RemoteAddr)
@ -1155,7 +1174,7 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
req.URL.Path += extraData
common.RouteViewCounter.Bump(69)
err = routeDeleteTopicSubmit(w,req,user)
err = routes.DeleteTopicSubmit(w,req,user)
case "/topic/stick/submit/":
err = common.NoSessionMismatch(w,req,user)
if err != nil {
@ -1170,7 +1189,7 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
}
common.RouteViewCounter.Bump(70)
err = routeStickTopicSubmit(w,req,user,extraData)
err = routes.StickTopicSubmit(w,req,user,extraData)
case "/topic/unstick/submit/":
err = common.NoSessionMismatch(w,req,user)
if err != nil {
@ -1185,7 +1204,7 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
}
common.RouteViewCounter.Bump(71)
err = routeUnstickTopicSubmit(w,req,user,extraData)
err = routes.UnstickTopicSubmit(w,req,user,extraData)
case "/topic/lock/submit/":
err = common.NoSessionMismatch(w,req,user)
if err != nil {
@ -1201,7 +1220,7 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
req.URL.Path += extraData
common.RouteViewCounter.Bump(72)
err = routeLockTopicSubmit(w,req,user)
err = routes.LockTopicSubmit(w,req,user)
case "/topic/unlock/submit/":
err = common.NoSessionMismatch(w,req,user)
if err != nil {
@ -1216,7 +1235,7 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
}
common.RouteViewCounter.Bump(73)
err = routeUnlockTopicSubmit(w,req,user,extraData)
err = routes.UnlockTopicSubmit(w,req,user,extraData)
case "/topic/move/submit/":
err = common.NoSessionMismatch(w,req,user)
if err != nil {
@ -1231,7 +1250,7 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
}
common.RouteViewCounter.Bump(74)
err = routeMoveTopicSubmit(w,req,user,extraData)
err = routes.MoveTopicSubmit(w,req,user,extraData)
case "/topic/like/submit/":
err = common.NoSessionMismatch(w,req,user)
if err != nil {
@ -1290,7 +1309,7 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
}
common.RouteViewCounter.Bump(78)
err = routeReplyEditSubmit(w,req,user,extraData)
err = routes.ReplyEditSubmit(w,req,user,extraData)
case "/reply/delete/submit/":
err = common.NoSessionMismatch(w,req,user)
if err != nil {
@ -1305,7 +1324,7 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
}
common.RouteViewCounter.Bump(79)
err = routeReplyDeleteSubmit(w,req,user,extraData)
err = routes.ReplyDeleteSubmit(w,req,user,extraData)
case "/reply/like/submit/":
err = common.NoSessionMismatch(w,req,user)
if err != nil {
@ -1356,7 +1375,7 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
}
common.RouteViewCounter.Bump(82)
err = routeProfileReplyEditSubmit(w,req,user,extraData)
err = routes.ProfileReplyEditSubmit(w,req,user,extraData)
case "/profile/reply/delete/submit/":
err = common.NoSessionMismatch(w,req,user)
if err != nil {
@ -1371,7 +1390,7 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
}
common.RouteViewCounter.Bump(83)
err = routeProfileReplyDeleteSubmit(w,req,user,extraData)
err = routes.ProfileReplyDeleteSubmit(w,req,user,extraData)
}
if err != nil {
router.handleError(err,w,req,user)

View File

@ -1,9 +1,6 @@
package main
import (
"encoding/json"
"html"
"log"
"net/http"
"strconv"
"time"
@ -11,501 +8,6 @@ import (
"./common"
)
// TODO: Add support for soft-deletion and add a permission for hard delete in addition to the usual
// TODO: Disable stat updates in posts handled by plugin_guilds
func routeDeleteTopicSubmit(w http.ResponseWriter, r *http.Request, user common.User) common.RouteError {
// TODO: Move this to some sort of middleware
var tids []int
var isJs = false
if common.ReqIsJson(r) {
if r.Body == nil {
return common.PreErrorJS("No request body", w, r)
}
//log.Print("r.Body: ", r.Body)
err := json.NewDecoder(r.Body).Decode(&tids)
if err != nil {
//log.Print("parse err: ", err)
return common.PreErrorJS("We weren't able to parse your data", w, r)
}
isJs = true
} else {
tid, err := strconv.Atoi(r.URL.Path[len("/topic/delete/submit/"):])
if err != nil {
return common.PreError("The provided TopicID is not a valid number.", w, r)
}
tids = append(tids, tid)
}
if len(tids) == 0 {
return common.LocalErrorJSQ("You haven't provided any IDs", w, r, user, isJs)
}
for _, tid := range tids {
topic, err := common.Topics.Get(tid)
if err == ErrNoRows {
return common.PreErrorJSQ("The topic you tried to delete 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.DeleteTopic {
return common.NoPermissionsJSQ(w, r, user, isJs)
}
// We might be able to handle this err better
err = topic.Delete()
if err != nil {
return common.InternalErrorJSQ(err, w, r, isJs)
}
err = common.ModLogs.Create("delete", tid, "topic", user.LastIP, user.ID)
if err != nil {
return common.InternalErrorJSQ(err, w, r, isJs)
}
// ? - We might need to add soft-delete before we can do an action reply for this
/*_, err = stmts.createActionReply.Exec(tid,"delete",ipaddress,user.ID)
if err != nil {
return common.InternalErrorJSQ(err,w,r,isJs)
}*/
log.Printf("Topic #%d was deleted by common.User #%d", tid, user.ID)
}
http.Redirect(w, r, "/", http.StatusSeeOther)
return nil
}
func routeStickTopicSubmit(w http.ResponseWriter, r *http.Request, user common.User, stid string) common.RouteError {
tid, err := strconv.Atoi(stid)
if err != nil {
return common.PreError("The provided TopicID is not a valid number.", w, r)
}
topic, err := common.Topics.Get(tid)
if err == ErrNoRows {
return common.PreError("The topic you tried to pin doesn't exist.", w, r)
} else if err != nil {
return common.InternalError(err, w, r)
}
// 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.PinTopic {
return common.NoPermissions(w, r, user)
}
err = topic.Stick()
if err != nil {
return common.InternalError(err, w, r)
}
err = common.ModLogs.Create("stick", tid, "topic", user.LastIP, user.ID)
if err != nil {
return common.InternalError(err, w, r)
}
err = topic.CreateActionReply("stick", user.LastIP, user)
if err != nil {
return common.InternalError(err, w, r)
}
http.Redirect(w, r, "/topic/"+strconv.Itoa(tid), http.StatusSeeOther)
return nil
}
func routeUnstickTopicSubmit(w http.ResponseWriter, r *http.Request, user common.User, stid string) common.RouteError {
tid, err := strconv.Atoi(stid)
if err != nil {
return common.PreError("The provided TopicID is not a valid number.", w, r)
}
topic, err := common.Topics.Get(tid)
if err == ErrNoRows {
return common.PreError("The topic you tried to unpin doesn't exist.", w, r)
} else if err != nil {
return common.InternalError(err, w, r)
}
// 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.PinTopic {
return common.NoPermissions(w, r, user)
}
err = topic.Unstick()
if err != nil {
return common.InternalError(err, w, r)
}
err = common.ModLogs.Create("unstick", tid, "topic", user.LastIP, user.ID)
if err != nil {
return common.InternalError(err, w, r)
}
err = topic.CreateActionReply("unstick", user.LastIP, user)
if err != nil {
return common.InternalError(err, w, r)
}
http.Redirect(w, r, "/topic/"+strconv.Itoa(tid), http.StatusSeeOther)
return nil
}
func routeLockTopicSubmit(w http.ResponseWriter, r *http.Request, user common.User) common.RouteError {
// TODO: Move this to some sort of middleware
var tids []int
var isJs = false
if common.ReqIsJson(r) {
if r.Body == nil {
return common.PreErrorJS("No request body", w, r)
}
err := json.NewDecoder(r.Body).Decode(&tids)
if err != nil {
return common.PreErrorJS("We weren't able to parse your data", w, r)
}
isJs = true
} else {
tid, err := strconv.Atoi(r.URL.Path[len("/topic/lock/submit/"):])
if err != nil {
return common.PreError("The provided TopicID is not a valid number.", w, r)
}
tids = append(tids, tid)
}
if len(tids) == 0 {
return common.LocalErrorJSQ("You haven't provided any IDs", w, r, user, isJs)
}
for _, tid := range tids {
topic, err := common.Topics.Get(tid)
if err == ErrNoRows {
return common.PreErrorJSQ("The topic you tried to lock 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.CloseTopic {
return common.NoPermissionsJSQ(w, r, user, isJs)
}
err = topic.Lock()
if err != nil {
return common.InternalErrorJSQ(err, w, r, isJs)
}
err = common.ModLogs.Create("lock", tid, "topic", user.LastIP, user.ID)
if err != nil {
return common.InternalErrorJSQ(err, w, r, isJs)
}
err = topic.CreateActionReply("lock", user.LastIP, user)
if err != nil {
return common.InternalErrorJSQ(err, w, r, isJs)
}
}
if len(tids) == 1 {
http.Redirect(w, r, "/topic/"+strconv.Itoa(tids[0]), http.StatusSeeOther)
}
return nil
}
func routeUnlockTopicSubmit(w http.ResponseWriter, r *http.Request, user common.User, stid string) common.RouteError {
tid, err := strconv.Atoi(stid)
if err != nil {
return common.PreError("The provided TopicID is not a valid number.", w, r)
}
topic, err := common.Topics.Get(tid)
if err == ErrNoRows {
return common.PreError("The topic you tried to unlock doesn't exist.", w, r)
} else if err != nil {
return common.InternalError(err, w, r)
}
// 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.CloseTopic {
return common.NoPermissions(w, r, user)
}
err = topic.Unlock()
if err != nil {
return common.InternalError(err, w, r)
}
err = common.ModLogs.Create("unlock", tid, "topic", user.LastIP, user.ID)
if err != nil {
return common.InternalError(err, w, r)
}
err = topic.CreateActionReply("unlock", user.LastIP, user)
if err != nil {
return common.InternalError(err, w, r)
}
http.Redirect(w, r, "/topic/"+strconv.Itoa(tid), http.StatusSeeOther)
return nil
}
// ! JS only route
// TODO: Figure a way to get this route to work without JS
func routeMoveTopicSubmit(w http.ResponseWriter, r *http.Request, user common.User, sfid string) common.RouteError {
fid, err := strconv.Atoi(sfid)
if err != nil {
return common.PreErrorJS("The provided Forum ID is not a valid number.", w, r)
}
// TODO: Move this to some sort of middleware
var tids []int
if r.Body == nil {
return common.PreErrorJS("No request body", w, r)
}
err = json.NewDecoder(r.Body).Decode(&tids)
if err != nil {
return common.PreErrorJS("We weren't able to parse your data", w, r)
}
if len(tids) == 0 {
return common.LocalErrorJS("You haven't provided any IDs", w, r)
}
for _, tid := range tids {
topic, err := common.Topics.Get(tid)
if err == ErrNoRows {
return common.PreErrorJS("The topic you tried to move doesn't exist.", w, r)
} else if err != nil {
return common.InternalErrorJS(err, w, r)
}
// 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.MoveTopic {
return common.NoPermissionsJS(w, r, user)
}
_, ferr = common.SimpleForumUserCheck(w, r, &user, fid)
if ferr != nil {
return ferr
}
if !user.Perms.ViewTopic || !user.Perms.MoveTopic {
return common.NoPermissionsJS(w, r, user)
}
err = topic.MoveTo(fid)
if err != nil {
return common.InternalErrorJS(err, w, r)
}
// TODO: Log more data so we can list the destination forum in the action post?
err = common.ModLogs.Create("move", tid, "topic", user.LastIP, user.ID)
if err != nil {
return common.InternalErrorJS(err, w, r)
}
err = topic.CreateActionReply("move", user.LastIP, user)
if err != nil {
return common.InternalErrorJS(err, w, r)
}
}
if len(tids) == 1 {
http.Redirect(w, r, "/topic/"+strconv.Itoa(tids[0]), http.StatusSeeOther)
}
return nil
}
// TODO: Disable stat updates in posts handled by plugin_guilds
// TODO: Update the stats after edits so that we don't under or over decrement stats during deletes
func routeReplyEditSubmit(w http.ResponseWriter, r *http.Request, user common.User, srid string) common.RouteError {
isJs := (r.PostFormValue("js") == "1")
rid, err := strconv.Atoi(srid)
if err != nil {
return common.PreErrorJSQ("The provided Reply ID is not a valid number.", w, r, isJs)
}
// Get the Reply ID..
var tid int
err = stmts.getReplyTID.QueryRow(rid).Scan(&tid)
if err != nil {
return common.InternalErrorJSQ(err, w, r, isJs)
}
var fid int
err = stmts.getTopicFID.QueryRow(tid).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.EditReply {
return common.NoPermissionsJSQ(w, r, user, isJs)
}
content := common.PreparseMessage(html.UnescapeString(r.PostFormValue("edit_item")))
_, err = stmts.editReply.Exec(content, common.ParseMessage(content, fid, "forums"), rid)
if err != nil {
return common.InternalErrorJSQ(err, w, r, isJs)
}
if !isJs {
http.Redirect(w, r, "/topic/"+strconv.Itoa(tid)+"#reply-"+strconv.Itoa(rid), http.StatusSeeOther)
} else {
w.Write(successJSONBytes)
}
return nil
}
// TODO: Refactor this
// TODO: Disable stat updates in posts handled by plugin_guilds
func routeReplyDeleteSubmit(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("The reply you tried to delete 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.DeleteReply {
return common.NoPermissionsJSQ(w, r, user, isJs)
}
err = reply.Delete()
if err != nil {
return common.InternalErrorJSQ(err, w, r, isJs)
}
//log.Printf("Reply #%d was deleted by common.User #%d", rid, user.ID)
if !isJs {
http.Redirect(w, r, "/topic/"+strconv.Itoa(reply.ParentID), http.StatusSeeOther)
} else {
w.Write(successJSONBytes)
}
replyCreator, err := common.Users.Get(reply.CreatedBy)
if err == nil {
wcount := common.WordCount(reply.Content)
err = replyCreator.DecreasePostStats(wcount, false)
if err != nil {
return common.InternalErrorJSQ(err, w, r, isJs)
}
} else if err != ErrNoRows {
return common.InternalErrorJSQ(err, w, r, isJs)
}
err = common.ModLogs.Create("delete", reply.ParentID, "reply", user.LastIP, user.ID)
if err != nil {
return common.InternalErrorJSQ(err, w, r, isJs)
}
return nil
}
func routeProfileReplyEditSubmit(w http.ResponseWriter, r *http.Request, user common.User, srid string) common.RouteError {
isJs := (r.PostFormValue("js") == "1")
rid, err := strconv.Atoi(srid)
if err != nil {
return common.LocalErrorJSQ("The provided Reply ID is not a valid number.", w, r, user, isJs)
}
// Get the Reply ID..
var uid int
err = stmts.getUserReplyUID.QueryRow(rid).Scan(&uid)
if err != nil {
return common.InternalErrorJSQ(err, w, r, isJs)
}
if user.ID != uid && !user.Perms.EditReply {
return common.NoPermissionsJSQ(w, r, user, isJs)
}
content := common.PreparseMessage(html.UnescapeString(r.PostFormValue("edit_item")))
_, err = stmts.editProfileReply.Exec(content, common.ParseMessage(content, 0, ""), rid)
if err != nil {
return common.InternalErrorJSQ(err, w, r, isJs)
}
if !isJs {
http.Redirect(w, r, "/user/"+strconv.Itoa(uid)+"#reply-"+strconv.Itoa(rid), http.StatusSeeOther)
} else {
w.Write(successJSONBytes)
}
return nil
}
func routeProfileReplyDeleteSubmit(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.LocalErrorJSQ("The provided Reply ID is not a valid number.", w, r, user, isJs)
}
var uid int
err = stmts.getUserReplyUID.QueryRow(rid).Scan(&uid)
if err == ErrNoRows {
return common.LocalErrorJSQ("The reply you tried to delete doesn't exist.", w, r, user, isJs)
} else if err != nil {
return common.InternalErrorJSQ(err, w, r, isJs)
}
if user.ID != uid && !user.Perms.DeleteReply {
return common.NoPermissionsJSQ(w, r, user, isJs)
}
_, err = stmts.deleteProfileReply.Exec(rid)
if err != nil {
return common.InternalErrorJSQ(err, w, r, isJs)
}
//log.Printf("The profile post '%d' was deleted by common.User #%d", rid, user.ID)
if !isJs {
//http.Redirect(w,r, "/user/" + strconv.Itoa(uid), http.StatusSeeOther)
} else {
w.Write(successJSONBytes)
}
return nil
}
func routeIps(w http.ResponseWriter, r *http.Request, user common.User) common.RouteError {
headerVars, ferr := common.UserCheck(w, r, &user)
if ferr != nil {

View File

@ -2236,7 +2236,7 @@ func modlogsElementType(action string, elementType string, elementID int, actor
out = fmt.Sprintf(out, targetUser.Link, targetUser.Name, actor.Link, actor.Name)
case "reply":
if action == "delete" {
topic := handleUnknownTopic(common.BlankReply(elementID).Topic())
topic := handleUnknownTopic(common.TopicByReplyID(elementID))
out = fmt.Sprintf("A reply in <a href='%s'>%s</a> was deleted by <a href='%s'>%s</a>", topic.Link, topic.Title, actor.Link, actor.Name)
}
}

View File

@ -233,12 +233,8 @@ func writeSelects(adapter qgen.Adapter) error {
build.Select("getAdminlogsOffset").Table("administration_logs").Columns("action, elementID, elementType, ipaddress, actorID, doneAt").Orderby("doneAt DESC").Limit("?,?").Parse()
build.Select("getReplyTID").Table("replies").Columns("tid").Where("rid = ?").Parse()
build.Select("getTopicFID").Table("topics").Columns("parentID").Where("tid = ?").Parse()
build.Select("getUserReplyUID").Table("users_replies").Columns("uid").Where("rid = ?").Parse()
build.Select("getUserName").Table("users").Columns("name").Where("uid = ?").Parse()
build.Select("getEmailsByUser").Table("emails").Columns("email, validated, token").Where("uid = ?").Parse()
@ -312,8 +308,6 @@ func writeUpdates(adapter qgen.Adapter) error {
build.Update("editReply").Table("replies").Set("content = ?, parsed_content = ?").Where("rid = ?").Parse()
build.Update("editProfileReply").Table("users_replies").Set("content = ?, parsed_content = ?").Where("rid = ?").Parse()
build.Update("updatePlugin").Table("plugins").Set("active = ?").Where("uname = ?").Parse()
build.Update("updatePluginInstall").Table("plugins").Set("installed = ?").Where("uname = ?").Parse()
@ -342,8 +336,6 @@ func writeUpdates(adapter qgen.Adapter) error {
func writeDeletes(adapter qgen.Adapter) error {
build := adapter.Builder()
build.Delete("deleteProfileReply").Table("users_replies").Where("rid = ?").Parse()
//build.Delete("deleteForumPermsByForum").Table("forums_permissions").Where("fid = ?").Parse()
build.Delete("deleteActivityStreamMatch").Table("activity_stream_matches").Where("watcher = ? AND asid = ?").Parse()

View File

@ -207,10 +207,11 @@ func createTables(adapter qgen.Adapter) error {
qgen.Install.CreateTable("revisions", "utf8mb4", "utf8mb4_general_ci",
[]qgen.DBTableColumn{
qgen.DBTableColumn{"index", "int", 0, false, false, ""}, // TODO: Replace this with a proper revision ID x.x
qgen.DBTableColumn{"reviseID", "int", 0, false, true, ""},
qgen.DBTableColumn{"content", "text", 0, false, false, ""},
qgen.DBTableColumn{"contentID", "int", 0, false, false, ""},
qgen.DBTableColumn{"contentType", "varchar", 100, false, false, "replies"},
qgen.DBTableColumn{"createdAt", "createdAt", 0, false, false, ""},
},
[]qgen.DBTableKey{},
)

View File

@ -279,9 +279,21 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
if len(req.URL.Path) == 0 || req.URL.Path[0] != '/' {
w.WriteHeader(405)
w.Write([]byte(""))
log.Print("Suspicious UA: ", req.UserAgent())
log.Print("Method: ", req.Method)
for key, value := range req.Header {
for _, vvalue := range value {
log.Print("Header '" + key + "': " + vvalue + "!!")
}
}
log.Print("req.URL.Path: ", req.URL.Path)
log.Print("req.URL.RawQuery: ", req.URL.RawQuery)
log.Print("req.Referer(): ", req.Referer())
log.Print("req.RemoteAddr: ", req.RemoteAddr)
return
}
if common.Dev.DebugMode {
// TODO: Cover more suspicious strings and at a lower layer than this
for _, char := range req.URL.Path {
if char != '&' && !(char > 44 && char < 58) && char != '=' && char != '?' && !(char > 64 && char < 91) && char != '\\' && char != '_' && !(char > 96 && char < 123) {
@ -293,8 +305,10 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
}
}
log.Print("req.URL.Path: ", req.URL.Path)
log.Print("req.URL.RawQuery: ", req.URL.RawQuery)
log.Print("req.Referer(): ", req.Referer())
log.Print("req.RemoteAddr: ", req.RemoteAddr)
break
}
}
if strings.Contains(req.URL.Path,"..") || strings.Contains(req.URL.Path,"--") {
@ -306,9 +320,11 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
}
}
log.Print("req.URL.Path: ", req.URL.Path)
log.Print("req.URL.RawQuery: ", req.URL.RawQuery)
log.Print("req.Referer(): ", req.Referer())
log.Print("req.RemoteAddr: ", req.RemoteAddr)
}
}
var prefix, extraData string
prefix = req.URL.Path[0:strings.IndexByte(req.URL.Path[1:],'/') + 1]
@ -327,6 +343,7 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
}
log.Print("prefix: ", prefix)
log.Print("req.URL.Path: ", req.URL.Path)
log.Print("req.URL.RawQuery: ", req.URL.RawQuery)
log.Print("extraData: ", extraData)
log.Print("req.Referer(): ", req.Referer())
log.Print("req.RemoteAddr: ", req.RemoteAddr)
@ -390,6 +407,7 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
}
log.Print("prefix: ", prefix)
log.Print("req.URL.Path: ", req.URL.Path)
log.Print("req.URL.RawQuery: ", req.URL.RawQuery)
log.Print("extraData: ", extraData)
log.Print("req.Referer(): ", req.Referer())
log.Print("req.RemoteAddr: ", req.RemoteAddr)
@ -406,6 +424,7 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
}
log.Print("prefix: ", prefix)
log.Print("req.URL.Path: ", req.URL.Path)
log.Print("req.URL.RawQuery: ", req.URL.RawQuery)
log.Print("extraData: ", extraData)
log.Print("req.Referer(): ", req.Referer())
log.Print("req.RemoteAddr: ", req.RemoteAddr)

View File

@ -67,12 +67,12 @@ func buildTopicRoutes() {
View("routeTopicID", "/topic/", "extraData"),
Action("routeTopicCreateSubmit", "/topic/create/submit/"),
Action("routes.EditTopicSubmit", "/topic/edit/submit/", "extraData"),
Action("routeDeleteTopicSubmit", "/topic/delete/submit/").LitBefore("req.URL.Path += extraData"),
Action("routeStickTopicSubmit", "/topic/stick/submit/", "extraData"),
Action("routeUnstickTopicSubmit", "/topic/unstick/submit/", "extraData"),
Action("routeLockTopicSubmit", "/topic/lock/submit/").LitBefore("req.URL.Path += extraData"),
Action("routeUnlockTopicSubmit", "/topic/unlock/submit/", "extraData"),
Action("routeMoveTopicSubmit", "/topic/move/submit/", "extraData"),
Action("routes.DeleteTopicSubmit", "/topic/delete/submit/").LitBefore("req.URL.Path += extraData"),
Action("routes.StickTopicSubmit", "/topic/stick/submit/", "extraData"),
Action("routes.UnstickTopicSubmit", "/topic/unstick/submit/", "extraData"),
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"),
)
addRouteGroup(topicGroup)
@ -85,8 +85,8 @@ func buildReplyRoutes() {
replyGroup.Routes(
// TODO: Reduce this to 1MB for attachments for each file?
UploadAction("routeCreateReplySubmit", "/reply/create/").MaxSizeVar("common.Config.MaxRequestSize"), // TODO: Rename the route so it's /reply/create/submit/
Action("routeReplyEditSubmit", "/reply/edit/submit/", "extraData"),
Action("routeReplyDeleteSubmit", "/reply/delete/submit/", "extraData"),
Action("routes.ReplyEditSubmit", "/reply/edit/submit/", "extraData"),
Action("routes.ReplyDeleteSubmit", "/reply/delete/submit/", "extraData"),
Action("routeReplyLikeSubmit", "/reply/like/submit/", "extraData"),
)
addRouteGroup(replyGroup)
@ -98,8 +98,8 @@ func buildProfileReplyRoutes() {
pReplyGroup := newRouteGroup("/profile/")
pReplyGroup.Routes(
Action("routeProfileReplyCreateSubmit", "/profile/reply/create/"), // TODO: Add /submit/ to the end
Action("routeProfileReplyEditSubmit", "/profile/reply/edit/submit/", "extraData"),
Action("routeProfileReplyDeleteSubmit", "/profile/reply/delete/submit/", "extraData"),
Action("routes.ProfileReplyEditSubmit", "/profile/reply/edit/submit/", "extraData"),
Action("routes.ProfileReplyDeleteSubmit", "/profile/reply/delete/submit/", "extraData"),
)
addRouteGroup(pReplyGroup)
}

View File

@ -179,18 +179,7 @@ func routeTopics(w http.ResponseWriter, r *http.Request, user common.User) commo
// Get the current page
page, _ := strconv.Atoi(r.FormValue("page"))
// Calculate the offset
var offset int
lastPage := (topicCount / common.Config.ItemsPerPage) + 1
if page > 1 {
offset = (common.Config.ItemsPerPage * page) - common.Config.ItemsPerPage
} else if page == -1 {
page = lastPage
offset = (common.Config.ItemsPerPage * page) - common.Config.ItemsPerPage
} else {
page = 1
}
offset, page, lastPage := common.PageOffset(topicCount, page, common.Config.ItemsPerPage)
var topicList []*common.TopicsRow
stmt, err := qgen.Builder.SimpleSelect("topics", "tid, title, content, createdBy, is_closed, sticky, createdAt, lastReplyAt, lastReplyBy, parentID, postCount, likeCount", "parentID IN("+qlist+")", "sticky DESC, lastReplyAt DESC, createdBy DESC", "?,?")
@ -258,7 +247,8 @@ func routeTopics(w http.ResponseWriter, r *http.Request, user common.User) commo
topicItem.LastUser = userList[topicItem.LastReplyBy]
}
pi := common.TopicsPage{common.GetTitlePhrase("topics"), user, headerVars, topicList, forumList, common.Config.DefaultForum}
pageList := common.Paginate(topicCount, common.Config.ItemsPerPage, 5)
pi := common.TopicsPage{common.GetTitlePhrase("topics"), user, headerVars, topicList, forumList, common.Config.DefaultForum, pageList, page, lastPage}
if common.PreRenderHooks["pre_render_topic_list"] != nil {
if common.RunPreRenderHook("pre_render_topic_list", w, r, &user, &pi) {
return nil

196
routes/reply.go Normal file
View File

@ -0,0 +1,196 @@
package routes
import (
"database/sql"
"net/http"
"strconv"
"../common"
)
// TODO: Disable stat updates in posts handled by plugin_guilds
// TODO: Update the stats after edits so that we don't under or over decrement stats during deletes
func ReplyEditSubmit(w http.ResponseWriter, r *http.Request, user common.User, srid string) common.RouteError {
isJs := (r.PostFormValue("js") == "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("The target reply doesn't exist.", w, r, isJs)
} else if err != nil {
return common.InternalErrorJSQ(err, w, r, isJs)
}
topic, err := reply.Topic()
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.EditReply {
return common.NoPermissionsJSQ(w, r, user, isJs)
}
err = reply.SetBody(r.PostFormValue("edit_item"))
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)
}
if !isJs {
http.Redirect(w, r, "/topic/"+strconv.Itoa(topic.ID)+"#reply-"+strconv.Itoa(rid), http.StatusSeeOther)
} else {
w.Write(successJSONBytes)
}
return nil
}
// TODO: Refactor this
// TODO: Disable stat updates in posts handled by plugin_guilds
func ReplyDeleteSubmit(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("The reply you tried to delete 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.DeleteReply {
return common.NoPermissionsJSQ(w, r, user, isJs)
}
err = reply.Delete()
if err != nil {
return common.InternalErrorJSQ(err, w, r, isJs)
}
//log.Printf("Reply #%d was deleted by common.User #%d", rid, user.ID)
if !isJs {
http.Redirect(w, r, "/topic/"+strconv.Itoa(reply.ParentID), http.StatusSeeOther)
} else {
w.Write(successJSONBytes)
}
replyCreator, err := common.Users.Get(reply.CreatedBy)
if err == nil {
wcount := common.WordCount(reply.Content)
err = replyCreator.DecreasePostStats(wcount, false)
if err != nil {
return common.InternalErrorJSQ(err, w, r, isJs)
}
} else if err != sql.ErrNoRows {
return common.InternalErrorJSQ(err, w, r, isJs)
}
err = common.ModLogs.Create("delete", reply.ParentID, "reply", user.LastIP, user.ID)
if err != nil {
return common.InternalErrorJSQ(err, w, r, isJs)
}
return nil
}
func ProfileReplyEditSubmit(w http.ResponseWriter, r *http.Request, user common.User, srid string) common.RouteError {
isJs := (r.PostFormValue("js") == "1")
rid, err := strconv.Atoi(srid)
if err != nil {
return common.LocalErrorJSQ("The provided Reply ID is not a valid number.", w, r, user, isJs)
}
reply, err := common.Prstore.Get(rid)
if err == sql.ErrNoRows {
return common.PreErrorJSQ("The target reply doesn't exist.", w, r, isJs)
} else if err != nil {
return common.InternalErrorJSQ(err, w, r, isJs)
}
creator, err := common.Users.Get(reply.CreatedBy)
if err != nil {
return common.InternalErrorJSQ(err, w, r, isJs)
}
// ? Does the admin understand that this group perm affects this?
if user.ID != creator.ID && !user.Perms.EditReply {
return common.NoPermissionsJSQ(w, r, user, isJs)
}
err = reply.SetBody(r.PostFormValue("edit_item"))
if err != nil {
return common.InternalErrorJSQ(err, w, r, isJs)
}
if !isJs {
http.Redirect(w, r, "/user/"+strconv.Itoa(creator.ID)+"#reply-"+strconv.Itoa(rid), http.StatusSeeOther)
} else {
w.Write(successJSONBytes)
}
return nil
}
func ProfileReplyDeleteSubmit(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.LocalErrorJSQ("The provided Reply ID is not a valid number.", w, r, user, isJs)
}
reply, err := common.Prstore.Get(rid)
if err == sql.ErrNoRows {
return common.PreErrorJSQ("The target reply doesn't exist.", w, r, isJs)
} else if err != nil {
return common.InternalErrorJSQ(err, w, r, isJs)
}
creator, err := common.Users.Get(reply.CreatedBy)
if err != nil {
return common.InternalErrorJSQ(err, w, r, isJs)
}
if user.ID != creator.ID && !user.Perms.DeleteReply {
return common.NoPermissionsJSQ(w, r, user, isJs)
}
err = reply.Delete()
if err != nil {
return common.InternalErrorJSQ(err, w, r, isJs)
}
//log.Printf("The profile post '%d' was deleted by common.User #%d", reply.ID, user.ID)
if !isJs {
//http.Redirect(w,r, "/user/" + strconv.Itoa(creator.ID), http.StatusSeeOther)
} else {
w.Write(successJSONBytes)
}
return nil
}

View File

@ -2,6 +2,8 @@ package routes
import (
"database/sql"
"encoding/json"
"log"
"net/http"
"strconv"
@ -53,3 +55,317 @@ func EditTopicSubmit(w http.ResponseWriter, r *http.Request, user common.User, s
}
return nil
}
// TODO: Add support for soft-deletion and add a permission for hard delete in addition to the usual
// TODO: Disable stat updates in posts handled by plugin_guilds
func DeleteTopicSubmit(w http.ResponseWriter, r *http.Request, user common.User) common.RouteError {
// TODO: Move this to some sort of middleware
var tids []int
var isJs = false
if common.ReqIsJson(r) {
if r.Body == nil {
return common.PreErrorJS("No request body", w, r)
}
err := json.NewDecoder(r.Body).Decode(&tids)
if err != nil {
return common.PreErrorJS("We weren't able to parse your data", w, r)
}
isJs = true
} else {
tid, err := strconv.Atoi(r.URL.Path[len("/topic/delete/submit/"):])
if err != nil {
return common.PreError("The provided TopicID is not a valid number.", w, r)
}
tids = append(tids, tid)
}
if len(tids) == 0 {
return common.LocalErrorJSQ("You haven't provided any IDs", w, r, user, isJs)
}
for _, tid := range tids {
topic, err := common.Topics.Get(tid)
if err == sql.ErrNoRows {
return common.PreErrorJSQ("The topic you tried to delete 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.DeleteTopic {
return common.NoPermissionsJSQ(w, r, user, isJs)
}
// We might be able to handle this err better
err = topic.Delete()
if err != nil {
return common.InternalErrorJSQ(err, w, r, isJs)
}
err = common.ModLogs.Create("delete", tid, "topic", user.LastIP, user.ID)
if err != nil {
return common.InternalErrorJSQ(err, w, r, isJs)
}
// ? - We might need to add soft-delete before we can do an action reply for this
/*_, err = stmts.createActionReply.Exec(tid,"delete",ipaddress,user.ID)
if err != nil {
return common.InternalErrorJSQ(err,w,r,isJs)
}*/
log.Printf("Topic #%d was deleted by common.User #%d", tid, user.ID)
}
http.Redirect(w, r, "/", http.StatusSeeOther)
return nil
}
func StickTopicSubmit(w http.ResponseWriter, r *http.Request, user common.User, stid string) common.RouteError {
tid, err := strconv.Atoi(stid)
if err != nil {
return common.PreError("The provided TopicID is not a valid number.", w, r)
}
topic, err := common.Topics.Get(tid)
if err == sql.ErrNoRows {
return common.PreError("The topic you tried to pin doesn't exist.", w, r)
} else if err != nil {
return common.InternalError(err, w, r)
}
// 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.PinTopic {
return common.NoPermissions(w, r, user)
}
err = topic.Stick()
if err != nil {
return common.InternalError(err, w, r)
}
err = common.ModLogs.Create("stick", tid, "topic", user.LastIP, user.ID)
if err != nil {
return common.InternalError(err, w, r)
}
err = topic.CreateActionReply("stick", user.LastIP, user)
if err != nil {
return common.InternalError(err, w, r)
}
http.Redirect(w, r, "/topic/"+strconv.Itoa(tid), http.StatusSeeOther)
return nil
}
func UnstickTopicSubmit(w http.ResponseWriter, r *http.Request, user common.User, stid string) common.RouteError {
tid, err := strconv.Atoi(stid)
if err != nil {
return common.PreError("The provided TopicID is not a valid number.", w, r)
}
topic, err := common.Topics.Get(tid)
if err == sql.ErrNoRows {
return common.PreError("The topic you tried to unpin doesn't exist.", w, r)
} else if err != nil {
return common.InternalError(err, w, r)
}
// 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.PinTopic {
return common.NoPermissions(w, r, user)
}
err = topic.Unstick()
if err != nil {
return common.InternalError(err, w, r)
}
err = common.ModLogs.Create("unstick", tid, "topic", user.LastIP, user.ID)
if err != nil {
return common.InternalError(err, w, r)
}
err = topic.CreateActionReply("unstick", user.LastIP, user)
if err != nil {
return common.InternalError(err, w, r)
}
http.Redirect(w, r, "/topic/"+strconv.Itoa(tid), http.StatusSeeOther)
return nil
}
func LockTopicSubmit(w http.ResponseWriter, r *http.Request, user common.User) common.RouteError {
// TODO: Move this to some sort of middleware
var tids []int
var isJs = false
if common.ReqIsJson(r) {
if r.Body == nil {
return common.PreErrorJS("No request body", w, r)
}
err := json.NewDecoder(r.Body).Decode(&tids)
if err != nil {
return common.PreErrorJS("We weren't able to parse your data", w, r)
}
isJs = true
} else {
tid, err := strconv.Atoi(r.URL.Path[len("/topic/lock/submit/"):])
if err != nil {
return common.PreError("The provided TopicID is not a valid number.", w, r)
}
tids = append(tids, tid)
}
if len(tids) == 0 {
return common.LocalErrorJSQ("You haven't provided any IDs", w, r, user, isJs)
}
for _, tid := range tids {
topic, err := common.Topics.Get(tid)
if err == sql.ErrNoRows {
return common.PreErrorJSQ("The topic you tried to lock 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.CloseTopic {
return common.NoPermissionsJSQ(w, r, user, isJs)
}
err = topic.Lock()
if err != nil {
return common.InternalErrorJSQ(err, w, r, isJs)
}
err = common.ModLogs.Create("lock", tid, "topic", user.LastIP, user.ID)
if err != nil {
return common.InternalErrorJSQ(err, w, r, isJs)
}
err = topic.CreateActionReply("lock", user.LastIP, user)
if err != nil {
return common.InternalErrorJSQ(err, w, r, isJs)
}
}
if len(tids) == 1 {
http.Redirect(w, r, "/topic/"+strconv.Itoa(tids[0]), http.StatusSeeOther)
}
return nil
}
func UnlockTopicSubmit(w http.ResponseWriter, r *http.Request, user common.User, stid string) common.RouteError {
tid, err := strconv.Atoi(stid)
if err != nil {
return common.PreError("The provided TopicID is not a valid number.", w, r)
}
topic, err := common.Topics.Get(tid)
if err == sql.ErrNoRows {
return common.PreError("The topic you tried to unlock doesn't exist.", w, r)
} else if err != nil {
return common.InternalError(err, w, r)
}
// 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.CloseTopic {
return common.NoPermissions(w, r, user)
}
err = topic.Unlock()
if err != nil {
return common.InternalError(err, w, r)
}
err = common.ModLogs.Create("unlock", tid, "topic", user.LastIP, user.ID)
if err != nil {
return common.InternalError(err, w, r)
}
err = topic.CreateActionReply("unlock", user.LastIP, user)
if err != nil {
return common.InternalError(err, w, r)
}
http.Redirect(w, r, "/topic/"+strconv.Itoa(tid), http.StatusSeeOther)
return nil
}
// ! JS only route
// TODO: Figure a way to get this route to work without JS
func MoveTopicSubmit(w http.ResponseWriter, r *http.Request, user common.User, sfid string) common.RouteError {
fid, err := strconv.Atoi(sfid)
if err != nil {
return common.PreErrorJS("The provided Forum ID is not a valid number.", w, r)
}
// TODO: Move this to some sort of middleware
var tids []int
if r.Body == nil {
return common.PreErrorJS("No request body", w, r)
}
err = json.NewDecoder(r.Body).Decode(&tids)
if err != nil {
return common.PreErrorJS("We weren't able to parse your data", w, r)
}
if len(tids) == 0 {
return common.LocalErrorJS("You haven't provided any IDs", w, r)
}
for _, tid := range tids {
topic, err := common.Topics.Get(tid)
if err == sql.ErrNoRows {
return common.PreErrorJS("The topic you tried to move doesn't exist.", w, r)
} else if err != nil {
return common.InternalErrorJS(err, w, r)
}
// 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.MoveTopic {
return common.NoPermissionsJS(w, r, user)
}
_, ferr = common.SimpleForumUserCheck(w, r, &user, fid)
if ferr != nil {
return ferr
}
if !user.Perms.ViewTopic || !user.Perms.MoveTopic {
return common.NoPermissionsJS(w, r, user)
}
err = topic.MoveTo(fid)
if err != nil {
return common.InternalErrorJS(err, w, r)
}
// TODO: Log more data so we can list the destination forum in the action post?
err = common.ModLogs.Create("move", tid, "topic", user.LastIP, user.ID)
if err != nil {
return common.InternalErrorJS(err, w, r)
}
err = topic.CreateActionReply("move", user.LastIP, user)
if err != nil {
return common.InternalErrorJS(err, w, r)
}
}
if len(tids) == 1 {
http.Redirect(w, r, "/topic/"+strconv.Itoa(tids[0]), http.StatusSeeOther)
}
return nil
}

View File

@ -1,6 +1,7 @@
CREATE TABLE [revisions] (
[index] int not null,
[reviseID] int not null IDENTITY,
[content] nvarchar (MAX) not null,
[contentID] int not null,
[contentType] nvarchar (100) DEFAULT 'replies' not null
[contentType] nvarchar (100) DEFAULT 'replies' not null,
[createdAt] datetime not null
);

View File

@ -1,6 +1,7 @@
CREATE TABLE `revisions` (
`index` int not null,
`reviseID` int not null AUTO_INCREMENT,
`content` text not null,
`contentID` int not null,
`contentType` varchar(100) DEFAULT 'replies' not null
`contentType` varchar(100) DEFAULT 'replies' not null,
`createdAt` datetime not null
) CHARSET=utf8mb4 COLLATE utf8mb4_general_ci;

View File

@ -1,6 +1,7 @@
CREATE TABLE `revisions` (
`index` int not null,
`reviseID` serial not null,
`content` text not null,
`contentID` int not null,
`contentType` varchar (100) DEFAULT 'replies' not null
`contentType` varchar (100) DEFAULT 'replies' not null,
`createdAt` timestamp not null
);

View File

@ -669,6 +669,7 @@ var profile_comments_row_18 = []byte(`
var profile_comments_row_19 = []byte(`
<div class="rowitem passive deletable_block editable_parent comment `)
var profile_comments_row_20 = []byte(`">
<div class="topRow">
<div class="userbit">
<img src="`)
var profile_comments_row_21 = []byte(`" alt="`)
@ -684,25 +685,26 @@ var profile_comments_row_27 = []byte(`</a>`)
var profile_comments_row_28 = []byte(`
</span>
</div>
<div class="content_column">
<span class="editable_block user_content">`)
var profile_comments_row_29 = []byte(`</span>
<span class="controls">
`)
var profile_comments_row_30 = []byte(`
var profile_comments_row_29 = []byte(`
<a href="/profile/reply/edit/submit/`)
var profile_comments_row_31 = []byte(`?session=`)
var profile_comments_row_32 = []byte(`" class="mod_button" title="Edit Item"><button class="username edit_item edit_label"></button></a>
var profile_comments_row_30 = []byte(`?session=`)
var profile_comments_row_31 = []byte(`" class="mod_button" title="Edit Item"><button class="username edit_item edit_label"></button></a>
<a href="/profile/reply/delete/submit/`)
var profile_comments_row_33 = []byte(`?session=`)
var profile_comments_row_34 = []byte(`" class="mod_button" title="Delete Item"><button class="username delete_item trash_label"></button></a>
var profile_comments_row_32 = []byte(`?session=`)
var profile_comments_row_33 = []byte(`" class="mod_button" title="Delete Item"><button class="username delete_item trash_label"></button></a>
`)
var profile_comments_row_35 = []byte(`
var profile_comments_row_34 = []byte(`
<a class="mod_button" href="/report/submit/`)
var profile_comments_row_36 = []byte(`?session=`)
var profile_comments_row_37 = []byte(`&type=user-reply"><button class="username report_item flag_label"></button></a>
var profile_comments_row_35 = []byte(`?session=`)
var profile_comments_row_36 = []byte(`&type=user-reply"><button class="username report_item flag_label"></button></a>
</span>
</div>
<div class="content_column">
<span class="editable_block user_content">`)
var profile_comments_row_37 = []byte(`</span>
</div>
</div>
`)
var profile_22 = []byte(`</div>
@ -975,6 +977,24 @@ var topics_61 = []byte(`</div>`)
var topics_62 = []byte(`
</div>
`)
var topics_63 = []byte(`
<div class="pageset">
`)
var topics_64 = []byte(`<div class="pageitem"><a href="?page=`)
var topics_65 = []byte(`">Prev</a></div>`)
var topics_66 = []byte(`
<div class="pageitem"><a href="?page=`)
var topics_67 = []byte(`">`)
var topics_68 = []byte(`</a></div>
`)
var topics_69 = []byte(`<div class="pageitem"><a href="?page=`)
var topics_70 = []byte(`">Next</a></div>`)
var topics_71 = []byte(`
</div>
`)
var topics_72 = []byte(`
</main>
`)
var forum_0 = []byte(`<div id="prevFloat" class="prev_button"><a class="prev_link" aria-label="Go to the previous page" rel="prev" href="/forum/`)

View File

@ -3,9 +3,9 @@
// Code generated by Gosora. More below:
/* This file was automatically generated by the software. Please don't edit it as your changes may be overwritten at any moment. */
package main
import "net/http"
import "./common"
import "strconv"
import "net/http"
// nolint
func init() {
@ -186,23 +186,23 @@ w.Write([]byte(item.Tag))
w.Write(profile_comments_row_27)
}
w.Write(profile_comments_row_28)
w.Write([]byte(item.ContentHtml))
w.Write(profile_comments_row_29)
if tmpl_profile_vars.CurrentUser.IsMod {
w.Write(profile_comments_row_29)
w.Write([]byte(strconv.Itoa(item.ID)))
w.Write(profile_comments_row_30)
w.Write([]byte(strconv.Itoa(item.ID)))
w.Write([]byte(tmpl_profile_vars.CurrentUser.Session))
w.Write(profile_comments_row_31)
w.Write([]byte(tmpl_profile_vars.CurrentUser.Session))
w.Write([]byte(strconv.Itoa(item.ID)))
w.Write(profile_comments_row_32)
w.Write([]byte(strconv.Itoa(item.ID)))
w.Write([]byte(tmpl_profile_vars.CurrentUser.Session))
w.Write(profile_comments_row_33)
w.Write([]byte(tmpl_profile_vars.CurrentUser.Session))
w.Write(profile_comments_row_34)
}
w.Write(profile_comments_row_35)
w.Write(profile_comments_row_34)
w.Write([]byte(strconv.Itoa(item.ID)))
w.Write(profile_comments_row_36)
w.Write(profile_comments_row_35)
w.Write([]byte(tmpl_profile_vars.CurrentUser.Session))
w.Write(profile_comments_row_36)
w.Write([]byte(item.ContentHtml))
w.Write(profile_comments_row_37)
}
}

View File

@ -217,6 +217,30 @@ w.Write(topics_60)
w.Write(topics_61)
}
w.Write(topics_62)
if tmpl_topics_vars.LastPage > 1 {
w.Write(topics_63)
if tmpl_topics_vars.Page > 1 {
w.Write(topics_64)
w.Write([]byte(strconv.Itoa(tmpl_topics_vars.Page - 1)))
w.Write(topics_65)
}
if len(tmpl_topics_vars.PageList) != 0 {
for _, item := range tmpl_topics_vars.PageList {
w.Write(topics_66)
w.Write([]byte(strconv.Itoa(item)))
w.Write(topics_67)
w.Write([]byte(strconv.Itoa(item)))
w.Write(topics_68)
}
}
if tmpl_topics_vars.LastPage != tmpl_topics_vars.Page {
w.Write(topics_69)
w.Write([]byte(strconv.Itoa(tmpl_topics_vars.Page + 1)))
w.Write(topics_70)
}
w.Write(topics_71)
}
w.Write(topics_72)
w.Write(footer_0)
w.Write([]byte(common.BuildWidget("footer",tmpl_topics_vars.Header)))
w.Write(footer_1)

View File

@ -19,6 +19,7 @@
{{else}}
{{range .ItemList}}
<div class="rowitem passive deletable_block editable_parent comment {{.ClassName}}">
<div class="topRow">
<div class="userbit">
<img src="{{.Avatar}}" alt="{{.CreatedByName}}'s Avatar" title="{{.CreatedByName}}'s Avatar" />
<span class="nameAndTitle">
@ -26,8 +27,6 @@
{{if .Tag}}<a class="username hide_on_mobile user_tag" style="float: right;">{{.Tag}}</a>{{end}}
</span>
</div>
<div class="content_column">
<span class="editable_block user_content">{{.ContentHtml}}</span>
<span class="controls">
{{if $.CurrentUser.IsMod}}
<a href="/profile/reply/edit/submit/{{.ID}}?session={{$.CurrentUser.Session}}" class="mod_button" title="Edit Item"><button class="username edit_item edit_label"></button></a>
@ -36,6 +35,9 @@
<a class="mod_button" href="/report/submit/{{.ID}}?session={{$.CurrentUser.Session}}&type=user-reply"><button class="username report_item flag_label"></button></a>
</span>
</div>
<div class="content_column">
<span class="editable_block user_content">{{.ContentHtml}}</span>
</div>
</div>
{{end}}
{{end}}

View File

@ -115,5 +115,15 @@
</div>{{else}}<div class="rowitem passive">There aren't any topics yet.{{if .CurrentUser.Perms.CreateTopic}} <a href="/topics/create/">Start one?</a>{{end}}</div>{{end}}
</div>
{{if gt .LastPage 1}}
<div class="pageset">
{{if gt .Page 1}}<div class="pageitem"><a href="?page={{subtract .Page 1}}">Prev</a></div>{{end}}
{{range .PageList}}
<div class="pageitem"><a href="?page={{.}}">{{.}}</a></div>
{{end}}
{{if ne .LastPage .Page}}<div class="pageitem"><a href="?page={{add .Page 1}}">Next</a></div>{{end}}
</div>
{{end}}
</main>
{{template "footer.html" . }}

View File

@ -1050,6 +1050,16 @@ textarea {
#profile_right_lane .topic_reply_form {
width: auto;
}
#profile_comments .topRow {
display: flex;
}
#profile_comments .topRow .controls {
padding-top: 16px;
padding-right: 16px;
}
#profile_comments .content_column {
margin-bottom: 16px;
}
#profile_comments_head {
margin-top: 6px;
}