Let users unlike posts.
Hide like buttons on own posts for Tempra Simple and Shadow themes too. fix like visual ui. fix topic.Unlike err return. Add topic.minus_one phrase.
This commit is contained in:
parent
6935637867
commit
b5fa9c69f7
|
@ -15,7 +15,7 @@ import (
|
|||
"sync"
|
||||
"sync/atomic"
|
||||
|
||||
"github.com/Azareal/Gosora/query_gen"
|
||||
qgen "github.com/Azareal/Gosora/query_gen"
|
||||
)
|
||||
|
||||
var ErrPluginNotInstallable = errors.New("This plugin is not installable")
|
||||
|
@ -98,6 +98,7 @@ var hookTable = &HookTable{
|
|||
"action_end_unstick_topic": nil,
|
||||
"action_end_move_topic": nil,
|
||||
"action_end_like_topic": nil,
|
||||
"action_end_unlike_topic": nil,
|
||||
|
||||
"action_end_create_reply": nil,
|
||||
"action_end_edit_reply": nil,
|
||||
|
@ -106,6 +107,7 @@ var hookTable = &HookTable{
|
|||
"action_end_remove_attach_from_reply": nil,
|
||||
|
||||
"action_end_like_reply": nil,
|
||||
"action_end_unlike_reply": nil,
|
||||
|
||||
"action_end_ban_user": nil,
|
||||
"action_end_unban_user": nil,
|
||||
|
@ -143,8 +145,8 @@ func GetHookTable() *HookTable {
|
|||
}
|
||||
|
||||
// Hooks with a single argument. Is this redundant? Might be useful for inlining, as variadics aren't inlined? Are closures even inlined to begin with?
|
||||
func (table *HookTable) Hook(name string, data interface{}) interface{} {
|
||||
hooks, ok := table.Hooks[name]
|
||||
func (t *HookTable) Hook(name string, data interface{}) interface{} {
|
||||
hooks, ok := t.Hooks[name]
|
||||
if ok {
|
||||
for _, hook := range hooks {
|
||||
data = hook(data)
|
||||
|
@ -154,8 +156,8 @@ func (table *HookTable) Hook(name string, data interface{}) interface{} {
|
|||
}
|
||||
|
||||
// To cover the case in routes/topic.go's CreateTopic route, we could probably obsolete this use and replace it
|
||||
func (table *HookTable) HookSkippable(name string, data interface{}) (skip bool) {
|
||||
hooks, ok := table.Hooks[name]
|
||||
func (t *HookTable) HookSkippable(name string, data interface{}) (skip bool) {
|
||||
hooks, ok := t.Hooks[name]
|
||||
if ok {
|
||||
for _, hook := range hooks {
|
||||
skip = hook(data).(bool)
|
||||
|
@ -169,24 +171,24 @@ func (table *HookTable) HookSkippable(name string, data interface{}) (skip bool)
|
|||
|
||||
// Hooks with a variable number of arguments
|
||||
// TODO: Use RunHook semantics to allow multiple lined up plugins / modules their turn?
|
||||
func (table *HookTable) Vhook(name string, data ...interface{}) interface{} {
|
||||
hook := table.Vhooks[name]
|
||||
func (t *HookTable) Vhook(name string, data ...interface{}) interface{} {
|
||||
hook := t.Vhooks[name]
|
||||
if hook != nil {
|
||||
return hook(data...)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (table *HookTable) VhookNoRet(name string, data ...interface{}) {
|
||||
hook := table.Vhooks[name]
|
||||
func (t *HookTable) VhookNoRet(name string, data ...interface{}) {
|
||||
hook := t.Vhooks[name]
|
||||
if hook != nil {
|
||||
_ = hook(data...)
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Find a better way of doing this
|
||||
func (table *HookTable) VhookNeedHook(name string, data ...interface{}) (ret interface{}, hasHook bool) {
|
||||
hook := table.Vhooks[name]
|
||||
func (t *HookTable) VhookNeedHook(name string, data ...interface{}) (ret interface{}, hasHook bool) {
|
||||
hook := t.Vhooks[name]
|
||||
if hook != nil {
|
||||
return hook(data...), true
|
||||
}
|
||||
|
@ -194,8 +196,8 @@ func (table *HookTable) VhookNeedHook(name string, data ...interface{}) (ret int
|
|||
}
|
||||
|
||||
// Hooks with a variable number of arguments and return values for skipping the parent function and propagating an error upwards
|
||||
func (table *HookTable) VhookSkippable(name string, data ...interface{}) (bool, RouteError) {
|
||||
hook := table.VhookSkippable_[name]
|
||||
func (t *HookTable) VhookSkippable(name string, data ...interface{}) (bool, RouteError) {
|
||||
hook := t.VhookSkippable_[name]
|
||||
if hook != nil {
|
||||
return hook(data...)
|
||||
}
|
||||
|
@ -204,8 +206,8 @@ func (table *HookTable) VhookSkippable(name string, data ...interface{}) (bool,
|
|||
|
||||
// Hooks which take in and spit out a string. This is usually used for parser components
|
||||
// Trying to get a teeny bit of type-safety where-ever possible, especially for such a critical set of hooks
|
||||
func (table *HookTable) Sshook(name string, data string) string {
|
||||
ssHooks, ok := table.Sshooks[name]
|
||||
func (t *HookTable) Sshook(name, data string) string {
|
||||
ssHooks, ok := t.Sshooks[name]
|
||||
if ok {
|
||||
for _, hook := range ssHooks {
|
||||
data = hook(data)
|
||||
|
@ -331,17 +333,17 @@ type Plugin struct {
|
|||
Data interface{} // Usually used for hosting the VMs / reusable elements of non-native plugins
|
||||
}
|
||||
|
||||
func (plugin *Plugin) BypassActive() (active bool, err error) {
|
||||
err = extendStmts.isActive.QueryRow(plugin.UName).Scan(&active)
|
||||
func (pl *Plugin) BypassActive() (active bool, err error) {
|
||||
err = extendStmts.isActive.QueryRow(pl.UName).Scan(&active)
|
||||
if err != nil && err != sql.ErrNoRows {
|
||||
return false, err
|
||||
}
|
||||
return active, nil
|
||||
}
|
||||
|
||||
func (plugin *Plugin) InDatabase() (exists bool, err error) {
|
||||
func (pl *Plugin) InDatabase() (exists bool, err error) {
|
||||
var sink bool
|
||||
err = extendStmts.isActive.QueryRow(plugin.UName).Scan(&sink)
|
||||
err = extendStmts.isActive.QueryRow(pl.UName).Scan(&sink)
|
||||
if err != nil && err != sql.ErrNoRows {
|
||||
return false, err
|
||||
}
|
||||
|
@ -349,31 +351,31 @@ func (plugin *Plugin) InDatabase() (exists bool, err error) {
|
|||
}
|
||||
|
||||
// TODO: Silently add to the database, if it doesn't exist there rather than forcing users to call AddToDatabase instead?
|
||||
func (plugin *Plugin) SetActive(active bool) (err error) {
|
||||
_, err = extendStmts.setActive.Exec(active, plugin.UName)
|
||||
func (pl *Plugin) SetActive(active bool) (err error) {
|
||||
_, err = extendStmts.setActive.Exec(active, pl.UName)
|
||||
if err == nil {
|
||||
plugin.Active = active
|
||||
pl.Active = active
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// TODO: Silently add to the database, if it doesn't exist there rather than forcing users to call AddToDatabase instead?
|
||||
func (plugin *Plugin) SetInstalled(installed bool) (err error) {
|
||||
if !plugin.Installable {
|
||||
func (pl *Plugin) SetInstalled(installed bool) (err error) {
|
||||
if !pl.Installable {
|
||||
return ErrPluginNotInstallable
|
||||
}
|
||||
_, err = extendStmts.setInstalled.Exec(installed, plugin.UName)
|
||||
_, err = extendStmts.setInstalled.Exec(installed, pl.UName)
|
||||
if err == nil {
|
||||
plugin.Installed = installed
|
||||
pl.Installed = installed
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func (plugin *Plugin) AddToDatabase(active bool, installed bool) (err error) {
|
||||
_, err = extendStmts.add.Exec(plugin.UName, active, installed)
|
||||
func (pl *Plugin) AddToDatabase(active, installed bool) (err error) {
|
||||
_, err = extendStmts.add.Exec(pl.UName, active, installed)
|
||||
if err == nil {
|
||||
plugin.Active = active
|
||||
plugin.Installed = installed
|
||||
pl.Active = active
|
||||
pl.Installed = installed
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
|
|
@ -4,7 +4,7 @@ import (
|
|||
"database/sql"
|
||||
"encoding/json"
|
||||
|
||||
"github.com/Azareal/Gosora/query_gen"
|
||||
qgen "github.com/Azareal/Gosora/query_gen"
|
||||
)
|
||||
|
||||
var blankGroup = Group{ID: 0, Name: ""}
|
||||
|
@ -54,7 +54,7 @@ func init() {
|
|||
})
|
||||
}
|
||||
|
||||
func (g *Group) ChangeRank(isAdmin bool, isMod bool, isBanned bool) (err error) {
|
||||
func (g *Group) ChangeRank(isAdmin, isMod, isBanned bool) (err error) {
|
||||
_, err = groupStmts.updateGroupRank.Exec(isAdmin, isMod, isBanned, g.ID)
|
||||
if err != nil {
|
||||
return err
|
||||
|
@ -63,7 +63,7 @@ func (g *Group) ChangeRank(isAdmin bool, isMod bool, isBanned bool) (err error)
|
|||
return nil
|
||||
}
|
||||
|
||||
func (g *Group) Update(name string, tag string) (err error) {
|
||||
func (g *Group) Update(name, tag string) (err error) {
|
||||
_, err = groupStmts.updateGroup.Exec(name, tag, g.ID)
|
||||
if err != nil {
|
||||
return err
|
||||
|
|
|
@ -10,7 +10,7 @@ import (
|
|||
"strings"
|
||||
|
||||
"github.com/Azareal/Gosora/common/phrases"
|
||||
"github.com/Azareal/Gosora/query_gen"
|
||||
qgen "github.com/Azareal/Gosora/query_gen"
|
||||
)
|
||||
|
||||
type MenuItemList []MenuItem
|
||||
|
|
|
@ -10,8 +10,8 @@ import (
|
|||
"database/sql"
|
||||
"errors"
|
||||
"html"
|
||||
"time"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
qgen "github.com/Azareal/Gosora/query_gen"
|
||||
)
|
||||
|
@ -124,6 +124,21 @@ func (r *Reply) Like(uid int) (err error) {
|
|||
return err
|
||||
}
|
||||
|
||||
// TODO: Use a transaction
|
||||
func (r *Reply) Unlike(uid int) error {
|
||||
err := Likes.Delete(r.ID, "replies")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = replyStmts.addLikesToReply.Exec(-1, r.ID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = userStmts.decLiked.Exec(1, uid)
|
||||
_ = Rstore.GetCache().Remove(r.ID)
|
||||
return err
|
||||
}
|
||||
|
||||
// TODO: Refresh topic list?
|
||||
func (r *Reply) Delete() error {
|
||||
creator, err := Users.Get(r.CreatedBy)
|
||||
|
|
|
@ -336,7 +336,7 @@ func (t *Topic) Unlike(uid int) error {
|
|||
}
|
||||
_, err = userStmts.decLiked.Exec(1, uid)
|
||||
t.cacheRemove()
|
||||
return nil
|
||||
return err
|
||||
}
|
||||
|
||||
func handleLikedTopicReplies(tid int) error {
|
||||
|
|
172
gen_router.go
172
gen_router.go
|
@ -166,6 +166,7 @@ var RouteMap = map[string]interface{}{
|
|||
"routes.ReplyEditSubmit": routes.ReplyEditSubmit,
|
||||
"routes.ReplyDeleteSubmit": routes.ReplyDeleteSubmit,
|
||||
"routes.ReplyLikeSubmit": routes.ReplyLikeSubmit,
|
||||
"routes.ReplyUnlikeSubmit": routes.ReplyUnlikeSubmit,
|
||||
"routes.AddAttachToReplySubmit": routes.AddAttachToReplySubmit,
|
||||
"routes.RemoveAttachFromReplySubmit": routes.RemoveAttachFromReplySubmit,
|
||||
"routes.ProfileReplyCreateSubmit": routes.ProfileReplyCreateSubmit,
|
||||
|
@ -339,32 +340,33 @@ var routeMapEnum = map[string]int{
|
|||
"routes.ReplyEditSubmit": 140,
|
||||
"routes.ReplyDeleteSubmit": 141,
|
||||
"routes.ReplyLikeSubmit": 142,
|
||||
"routes.AddAttachToReplySubmit": 143,
|
||||
"routes.RemoveAttachFromReplySubmit": 144,
|
||||
"routes.ProfileReplyCreateSubmit": 145,
|
||||
"routes.ProfileReplyEditSubmit": 146,
|
||||
"routes.ProfileReplyDeleteSubmit": 147,
|
||||
"routes.PollVote": 148,
|
||||
"routes.PollResults": 149,
|
||||
"routes.AccountLogin": 150,
|
||||
"routes.AccountRegister": 151,
|
||||
"routes.AccountLogout": 152,
|
||||
"routes.AccountLoginSubmit": 153,
|
||||
"routes.AccountLoginMFAVerify": 154,
|
||||
"routes.AccountLoginMFAVerifySubmit": 155,
|
||||
"routes.AccountRegisterSubmit": 156,
|
||||
"routes.AccountPasswordReset": 157,
|
||||
"routes.AccountPasswordResetSubmit": 158,
|
||||
"routes.AccountPasswordResetToken": 159,
|
||||
"routes.AccountPasswordResetTokenSubmit": 160,
|
||||
"routes.DynamicRoute": 161,
|
||||
"routes.UploadedFile": 162,
|
||||
"routes.StaticFile": 163,
|
||||
"routes.RobotsTxt": 164,
|
||||
"routes.SitemapXml": 165,
|
||||
"routes.OpenSearchXml": 166,
|
||||
"routes.BadRoute": 167,
|
||||
"routes.HTTPSRedirect": 168,
|
||||
"routes.ReplyUnlikeSubmit": 143,
|
||||
"routes.AddAttachToReplySubmit": 144,
|
||||
"routes.RemoveAttachFromReplySubmit": 145,
|
||||
"routes.ProfileReplyCreateSubmit": 146,
|
||||
"routes.ProfileReplyEditSubmit": 147,
|
||||
"routes.ProfileReplyDeleteSubmit": 148,
|
||||
"routes.PollVote": 149,
|
||||
"routes.PollResults": 150,
|
||||
"routes.AccountLogin": 151,
|
||||
"routes.AccountRegister": 152,
|
||||
"routes.AccountLogout": 153,
|
||||
"routes.AccountLoginSubmit": 154,
|
||||
"routes.AccountLoginMFAVerify": 155,
|
||||
"routes.AccountLoginMFAVerifySubmit": 156,
|
||||
"routes.AccountRegisterSubmit": 157,
|
||||
"routes.AccountPasswordReset": 158,
|
||||
"routes.AccountPasswordResetSubmit": 159,
|
||||
"routes.AccountPasswordResetToken": 160,
|
||||
"routes.AccountPasswordResetTokenSubmit": 161,
|
||||
"routes.DynamicRoute": 162,
|
||||
"routes.UploadedFile": 163,
|
||||
"routes.StaticFile": 164,
|
||||
"routes.RobotsTxt": 165,
|
||||
"routes.SitemapXml": 166,
|
||||
"routes.OpenSearchXml": 167,
|
||||
"routes.BadRoute": 168,
|
||||
"routes.HTTPSRedirect": 169,
|
||||
}
|
||||
var reverseRouteMapEnum = map[int]string{
|
||||
0: "routes.Overview",
|
||||
|
@ -510,32 +512,33 @@ var reverseRouteMapEnum = map[int]string{
|
|||
140: "routes.ReplyEditSubmit",
|
||||
141: "routes.ReplyDeleteSubmit",
|
||||
142: "routes.ReplyLikeSubmit",
|
||||
143: "routes.AddAttachToReplySubmit",
|
||||
144: "routes.RemoveAttachFromReplySubmit",
|
||||
145: "routes.ProfileReplyCreateSubmit",
|
||||
146: "routes.ProfileReplyEditSubmit",
|
||||
147: "routes.ProfileReplyDeleteSubmit",
|
||||
148: "routes.PollVote",
|
||||
149: "routes.PollResults",
|
||||
150: "routes.AccountLogin",
|
||||
151: "routes.AccountRegister",
|
||||
152: "routes.AccountLogout",
|
||||
153: "routes.AccountLoginSubmit",
|
||||
154: "routes.AccountLoginMFAVerify",
|
||||
155: "routes.AccountLoginMFAVerifySubmit",
|
||||
156: "routes.AccountRegisterSubmit",
|
||||
157: "routes.AccountPasswordReset",
|
||||
158: "routes.AccountPasswordResetSubmit",
|
||||
159: "routes.AccountPasswordResetToken",
|
||||
160: "routes.AccountPasswordResetTokenSubmit",
|
||||
161: "routes.DynamicRoute",
|
||||
162: "routes.UploadedFile",
|
||||
163: "routes.StaticFile",
|
||||
164: "routes.RobotsTxt",
|
||||
165: "routes.SitemapXml",
|
||||
166: "routes.OpenSearchXml",
|
||||
167: "routes.BadRoute",
|
||||
168: "routes.HTTPSRedirect",
|
||||
143: "routes.ReplyUnlikeSubmit",
|
||||
144: "routes.AddAttachToReplySubmit",
|
||||
145: "routes.RemoveAttachFromReplySubmit",
|
||||
146: "routes.ProfileReplyCreateSubmit",
|
||||
147: "routes.ProfileReplyEditSubmit",
|
||||
148: "routes.ProfileReplyDeleteSubmit",
|
||||
149: "routes.PollVote",
|
||||
150: "routes.PollResults",
|
||||
151: "routes.AccountLogin",
|
||||
152: "routes.AccountRegister",
|
||||
153: "routes.AccountLogout",
|
||||
154: "routes.AccountLoginSubmit",
|
||||
155: "routes.AccountLoginMFAVerify",
|
||||
156: "routes.AccountLoginMFAVerifySubmit",
|
||||
157: "routes.AccountRegisterSubmit",
|
||||
158: "routes.AccountPasswordReset",
|
||||
159: "routes.AccountPasswordResetSubmit",
|
||||
160: "routes.AccountPasswordResetToken",
|
||||
161: "routes.AccountPasswordResetTokenSubmit",
|
||||
162: "routes.DynamicRoute",
|
||||
163: "routes.UploadedFile",
|
||||
164: "routes.StaticFile",
|
||||
165: "routes.RobotsTxt",
|
||||
166: "routes.SitemapXml",
|
||||
167: "routes.OpenSearchXml",
|
||||
168: "routes.BadRoute",
|
||||
169: "routes.HTTPSRedirect",
|
||||
}
|
||||
var osMapEnum = map[string]int{
|
||||
"unknown": 0,
|
||||
|
@ -693,7 +696,7 @@ type HTTPSRedirect struct {}
|
|||
|
||||
func (red *HTTPSRedirect) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
||||
w.Header().Set("Connection", "close")
|
||||
co.RouteViewCounter.Bump(168)
|
||||
co.RouteViewCounter.Bump(169)
|
||||
dest := "https://" + req.Host + req.URL.String()
|
||||
http.Redirect(w, req, dest, http.StatusTemporaryRedirect)
|
||||
}
|
||||
|
@ -901,7 +904,7 @@ func (r *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
|||
co.GlobalViewCounter.Bump()
|
||||
|
||||
if prefix == "/s" { //old prefix: /static
|
||||
co.RouteViewCounter.Bump(163)
|
||||
co.RouteViewCounter.Bump(164)
|
||||
req.URL.Path += extraData
|
||||
routes.StaticFile(w, req)
|
||||
return
|
||||
|
@ -2386,6 +2389,19 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c
|
|||
|
||||
co.RouteViewCounter.Bump(142)
|
||||
err = routes.ReplyLikeSubmit(w,req,user,extraData)
|
||||
case "/reply/unlike/submit/":
|
||||
err = c.NoSessionMismatch(w,req,user)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = c.MemberOnly(w,req,user)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
co.RouteViewCounter.Bump(143)
|
||||
err = routes.ReplyUnlikeSubmit(w,req,user,extraData)
|
||||
case "/reply/attach/add/submit/":
|
||||
err = c.MemberOnly(w,req,user)
|
||||
if err != nil {
|
||||
|
@ -2401,7 +2417,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c
|
|||
return err
|
||||
}
|
||||
|
||||
co.RouteViewCounter.Bump(143)
|
||||
co.RouteViewCounter.Bump(144)
|
||||
err = routes.AddAttachToReplySubmit(w,req,user,extraData)
|
||||
case "/reply/attach/remove/submit/":
|
||||
err = c.NoSessionMismatch(w,req,user)
|
||||
|
@ -2414,7 +2430,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c
|
|||
return err
|
||||
}
|
||||
|
||||
co.RouteViewCounter.Bump(144)
|
||||
co.RouteViewCounter.Bump(145)
|
||||
err = routes.RemoveAttachFromReplySubmit(w,req,user,extraData)
|
||||
}
|
||||
case "/profile":
|
||||
|
@ -2430,7 +2446,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c
|
|||
return err
|
||||
}
|
||||
|
||||
co.RouteViewCounter.Bump(145)
|
||||
co.RouteViewCounter.Bump(146)
|
||||
err = routes.ProfileReplyCreateSubmit(w,req,user)
|
||||
case "/profile/reply/edit/submit/":
|
||||
err = c.NoSessionMismatch(w,req,user)
|
||||
|
@ -2443,7 +2459,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c
|
|||
return err
|
||||
}
|
||||
|
||||
co.RouteViewCounter.Bump(146)
|
||||
co.RouteViewCounter.Bump(147)
|
||||
err = routes.ProfileReplyEditSubmit(w,req,user,extraData)
|
||||
case "/profile/reply/delete/submit/":
|
||||
err = c.NoSessionMismatch(w,req,user)
|
||||
|
@ -2456,7 +2472,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c
|
|||
return err
|
||||
}
|
||||
|
||||
co.RouteViewCounter.Bump(147)
|
||||
co.RouteViewCounter.Bump(148)
|
||||
err = routes.ProfileReplyDeleteSubmit(w,req,user,extraData)
|
||||
}
|
||||
case "/poll":
|
||||
|
@ -2472,23 +2488,23 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c
|
|||
return err
|
||||
}
|
||||
|
||||
co.RouteViewCounter.Bump(148)
|
||||
co.RouteViewCounter.Bump(149)
|
||||
err = routes.PollVote(w,req,user,extraData)
|
||||
case "/poll/results/":
|
||||
co.RouteViewCounter.Bump(149)
|
||||
co.RouteViewCounter.Bump(150)
|
||||
err = routes.PollResults(w,req,user,extraData)
|
||||
}
|
||||
case "/accounts":
|
||||
switch(req.URL.Path) {
|
||||
case "/accounts/login/":
|
||||
co.RouteViewCounter.Bump(150)
|
||||
co.RouteViewCounter.Bump(151)
|
||||
head, err := c.UserCheck(w,req,&user)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = routes.AccountLogin(w,req,user,head)
|
||||
case "/accounts/create/":
|
||||
co.RouteViewCounter.Bump(151)
|
||||
co.RouteViewCounter.Bump(152)
|
||||
head, err := c.UserCheck(w,req,&user)
|
||||
if err != nil {
|
||||
return err
|
||||
|
@ -2505,7 +2521,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c
|
|||
return err
|
||||
}
|
||||
|
||||
co.RouteViewCounter.Bump(152)
|
||||
co.RouteViewCounter.Bump(153)
|
||||
err = routes.AccountLogout(w,req,user)
|
||||
case "/accounts/login/submit/":
|
||||
err = c.ParseForm(w,req,user)
|
||||
|
@ -2513,10 +2529,10 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c
|
|||
return err
|
||||
}
|
||||
|
||||
co.RouteViewCounter.Bump(153)
|
||||
co.RouteViewCounter.Bump(154)
|
||||
err = routes.AccountLoginSubmit(w,req,user)
|
||||
case "/accounts/mfa_verify/":
|
||||
co.RouteViewCounter.Bump(154)
|
||||
co.RouteViewCounter.Bump(155)
|
||||
head, err := c.UserCheck(w,req,&user)
|
||||
if err != nil {
|
||||
return err
|
||||
|
@ -2528,7 +2544,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c
|
|||
return err
|
||||
}
|
||||
|
||||
co.RouteViewCounter.Bump(155)
|
||||
co.RouteViewCounter.Bump(156)
|
||||
err = routes.AccountLoginMFAVerifySubmit(w,req,user)
|
||||
case "/accounts/create/submit/":
|
||||
err = c.ParseForm(w,req,user)
|
||||
|
@ -2536,10 +2552,10 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c
|
|||
return err
|
||||
}
|
||||
|
||||
co.RouteViewCounter.Bump(156)
|
||||
co.RouteViewCounter.Bump(157)
|
||||
err = routes.AccountRegisterSubmit(w,req,user)
|
||||
case "/accounts/password-reset/":
|
||||
co.RouteViewCounter.Bump(157)
|
||||
co.RouteViewCounter.Bump(158)
|
||||
head, err := c.UserCheck(w,req,&user)
|
||||
if err != nil {
|
||||
return err
|
||||
|
@ -2551,10 +2567,10 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c
|
|||
return err
|
||||
}
|
||||
|
||||
co.RouteViewCounter.Bump(158)
|
||||
co.RouteViewCounter.Bump(159)
|
||||
err = routes.AccountPasswordResetSubmit(w,req,user)
|
||||
case "/accounts/password-reset/token/":
|
||||
co.RouteViewCounter.Bump(159)
|
||||
co.RouteViewCounter.Bump(160)
|
||||
head, err := c.UserCheck(w,req,&user)
|
||||
if err != nil {
|
||||
return err
|
||||
|
@ -2566,7 +2582,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c
|
|||
return err
|
||||
}
|
||||
|
||||
co.RouteViewCounter.Bump(160)
|
||||
co.RouteViewCounter.Bump(161)
|
||||
err = routes.AccountPasswordResetTokenSubmit(w,req,user)
|
||||
}
|
||||
/*case "/sitemaps": // TODO: Count these views
|
||||
|
@ -2583,7 +2599,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c
|
|||
h.Del("Content-Type")
|
||||
h.Del("Content-Encoding")
|
||||
}
|
||||
co.RouteViewCounter.Bump(162)
|
||||
co.RouteViewCounter.Bump(163)
|
||||
req.URL.Path += extraData
|
||||
// TODO: Find a way to propagate errors up from this?
|
||||
r.UploadHandler(w,req) // TODO: Count these views
|
||||
|
@ -2593,7 +2609,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c
|
|||
// TODO: Add support for favicons and robots.txt files
|
||||
switch(extraData) {
|
||||
case "robots.txt":
|
||||
co.RouteViewCounter.Bump(164)
|
||||
co.RouteViewCounter.Bump(165)
|
||||
return routes.RobotsTxt(w,req)
|
||||
case "favicon.ico":
|
||||
gzw, ok := w.(c.GzipResponseWriter)
|
||||
|
@ -2607,10 +2623,10 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c
|
|||
routes.StaticFile(w,req)
|
||||
return nil
|
||||
case "opensearch.xml":
|
||||
co.RouteViewCounter.Bump(166)
|
||||
co.RouteViewCounter.Bump(167)
|
||||
return routes.OpenSearchXml(w,req)
|
||||
/*case "sitemap.xml":
|
||||
co.RouteViewCounter.Bump(165)
|
||||
co.RouteViewCounter.Bump(166)
|
||||
return routes.SitemapXml(w,req)*/
|
||||
}
|
||||
return c.NotFound(w,req,nil)
|
||||
|
@ -2621,7 +2637,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c
|
|||
r.RUnlock()
|
||||
|
||||
if ok {
|
||||
co.RouteViewCounter.Bump(161) // TODO: Be more specific about *which* dynamic route it is
|
||||
co.RouteViewCounter.Bump(162) // TODO: Be more specific about *which* dynamic route it is
|
||||
req.URL.Path += extraData
|
||||
return handle(w,req,user)
|
||||
}
|
||||
|
@ -2632,7 +2648,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c
|
|||
} else {
|
||||
r.DumpRequest(req,"Bad Route")
|
||||
}
|
||||
co.RouteViewCounter.Bump(167)
|
||||
co.RouteViewCounter.Bump(168)
|
||||
return c.NotFound(w,req,nil)
|
||||
}
|
||||
return err
|
||||
|
|
|
@ -394,6 +394,7 @@
|
|||
"topic.view_count_suffix":" views",
|
||||
"topic.plus":"+",
|
||||
"topic.plus_one":"+1",
|
||||
"topic.minus_one":"-1",
|
||||
"topic.gap_up":" up",
|
||||
"topic.quote_button_text":"Quote",
|
||||
"topic.edit_button_text":"Edit",
|
||||
|
|
|
@ -35,7 +35,7 @@ function ajaxError(xhr,status,errstr) {
|
|||
function postLink(event) {
|
||||
event.preventDefault();
|
||||
let formAction = $(event.target).closest('a').attr("href");
|
||||
$.ajax({ url: formAction, type: "POST", dataType: "json", error: ajaxError, data: {js: "1"} });
|
||||
$.ajax({ url: formAction, type: "POST", dataType: "json", error: ajaxError, data: {js: 1} });
|
||||
}
|
||||
|
||||
function bindToAlerts() {
|
||||
|
@ -98,12 +98,6 @@ function updateAlertList(menuAlerts) {
|
|||
|
||||
alertListNode.innerHTML = "";
|
||||
let any = false;
|
||||
/*let outList = "";
|
||||
let j = 0;
|
||||
for(var i = 0; i < alertList.length && j < 8; i++) {
|
||||
outList += alertMapping[alertList[i]];
|
||||
j++;
|
||||
}*/
|
||||
let j = 0;
|
||||
for(var i = 0; i < alertList.length && j < 8; i++) {
|
||||
any = true;
|
||||
|
@ -213,9 +207,9 @@ function wsAlertEvent(data) {
|
|||
}
|
||||
|
||||
function runWebSockets(resume = false) {
|
||||
if(window.location.protocol == "https:") {
|
||||
conn = new WebSocket("wss://" + document.location.host + "/ws/");
|
||||
} else conn = new WebSocket("ws://" + document.location.host + "/ws/");
|
||||
let s = "";
|
||||
if(window.location.protocol == "https:") s = "s";
|
||||
conn = new WebSocket("ws"+s+"://" + document.location.host + "/ws/");
|
||||
|
||||
conn.onerror = (err) => {
|
||||
console.log(err);
|
||||
|
@ -418,20 +412,32 @@ function mainInit(){
|
|||
moreTopicCount = 0;
|
||||
})
|
||||
|
||||
$(".add_like").click(function(event) {
|
||||
$(".add_like,.remove_like").click(function(event) {
|
||||
event.preventDefault();
|
||||
//$(this).unbind("click");
|
||||
let target = this.closest("a").getAttribute("href");
|
||||
console.log("target:", target);
|
||||
this.classList.remove("add_like");
|
||||
this.classList.add("remove_like");
|
||||
|
||||
let controls = this.closest(".controls");
|
||||
let hadLikes = controls.classList.contains("has_likes");
|
||||
if(!hadLikes) controls.classList.add("has_likes");
|
||||
let likeCountNode = controls.getElementsByClassName("like_count")[0];
|
||||
console.log("likeCountNode",likeCountNode);
|
||||
if(this.classList.contains("add_like")) {
|
||||
this.classList.remove("add_like");
|
||||
this.classList.add("remove_like");
|
||||
if(!hadLikes) controls.classList.add("has_likes");
|
||||
this.closest("a").setAttribute("href", target.replace("like","unlike"));
|
||||
likeCountNode.innerHTML = parseInt(likeCountNode.innerHTML) + 1;
|
||||
let likeButton = this;
|
||||
} else {
|
||||
this.classList.remove("remove_like");
|
||||
this.classList.add("add_like");
|
||||
let likeCount = parseInt(likeCountNode.innerHTML);
|
||||
if(likeCount==1) controls.classList.remove("has_likes");
|
||||
this.closest("a").setAttribute("href", target.replace("unlike","like"));
|
||||
likeCountNode.innerHTML = parseInt(likeCountNode.innerHTML) - 1;
|
||||
}
|
||||
|
||||
//let likeButton = this;
|
||||
$.ajax({
|
||||
url: target,
|
||||
type: "POST",
|
||||
|
@ -441,10 +447,7 @@ function mainInit(){
|
|||
success: function (data, status, xhr) {
|
||||
if("success" in data && data["success"] == "1") return;
|
||||
// addNotice("Failed to add a like: {err}")
|
||||
likeButton.classList.add("add_like");
|
||||
likeButton.classList.remove("remove_like");
|
||||
if(!hadLikes) controls.classList.remove("has_likes");
|
||||
likeCountNode.innerHTML = parseInt(likeCountNode.innerHTML) - 1;
|
||||
//likeCountNode.innerHTML = parseInt(likeCountNode.innerHTML) - 1;
|
||||
console.log("data", data);
|
||||
console.log("status", status);
|
||||
console.log("xhr", xhr);
|
||||
|
@ -691,9 +694,9 @@ function mainInit(){
|
|||
$(blockParent).find('.hide_on_block_edit').removeClass("edit_opened");
|
||||
$(blockParent).find('.show_on_block_edit').removeClass("edit_opened");
|
||||
block.classList.remove("in_edit");
|
||||
let newContent = block.querySelector('textarea').value;
|
||||
block.innerHTML = quickParse(newContent);
|
||||
if(srcNode!=null) srcNode.innerText = newContent;
|
||||
let content = block.querySelector('textarea').value;
|
||||
block.innerHTML = quickParse(content);
|
||||
if(srcNode!=null) srcNode.innerText = content;
|
||||
|
||||
let formAction = this.closest('a').getAttribute("href");
|
||||
// TODO: Bounce the parsed post back and set innerHTML to it?
|
||||
|
@ -701,7 +704,7 @@ function mainInit(){
|
|||
url: formAction,
|
||||
type: "POST",
|
||||
dataType: "json",
|
||||
data: { js: "1", edit_item: newContent },
|
||||
data: { js: 1, edit_item: content },
|
||||
error: ajaxError,
|
||||
success: (data,status,xhr) => {
|
||||
if("Content" in data) block.innerHTML = data["Content"];
|
||||
|
@ -720,8 +723,8 @@ function mainInit(){
|
|||
event.preventDefault();
|
||||
let blockParent = $(this).closest('.editable_parent');
|
||||
let block = blockParent.find('.editable_block').eq(0);
|
||||
let newContent = block.find('input').eq(0).val();
|
||||
block.html(newContent);
|
||||
let content = block.find('input').eq(0).val();
|
||||
block.html(content);
|
||||
|
||||
let formAction = $(this).closest('a').attr("href");
|
||||
$.ajax({
|
||||
|
@ -729,7 +732,7 @@ function mainInit(){
|
|||
type: "POST",
|
||||
dataType: "json",
|
||||
error: ajaxError,
|
||||
data: { js: "1", edit_item: newContent }
|
||||
data: { js: 1, edit_item: content }
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -771,7 +774,7 @@ function mainInit(){
|
|||
|
||||
$(".submit_edit").click(function(event) {
|
||||
event.preventDefault();
|
||||
var outData = {js: "1"}
|
||||
var outData = {js: 1}
|
||||
var blockParent = $(this).closest('.editable_parent');
|
||||
blockParent.find('.editable_block').each(function() {
|
||||
var fieldName = this.getAttribute("data-field");
|
||||
|
|
|
@ -140,6 +140,7 @@ func replyRoutes() *RouteGroup {
|
|||
Action("routes.ReplyEditSubmit", "/reply/edit/submit/", "extraData"),
|
||||
Action("routes.ReplyDeleteSubmit", "/reply/delete/submit/", "extraData"),
|
||||
Action("routes.ReplyLikeSubmit", "/reply/like/submit/", "extraData"),
|
||||
Action("routes.ReplyUnlikeSubmit", "/reply/unlike/submit/", "extraData"),
|
||||
//MemberView("routes.ReplyEdit","/reply/edit/","extraData"), // No js fallback
|
||||
//MemberView("routes.ReplyDelete","/reply/delete/","extraData"), // No js confirmation page? We could have a confirmation modal for the JS case
|
||||
UploadAction("routes.AddAttachToReplySubmit", "/reply/attach/add/submit/", "extraData").MaxSizeVar("int(c.Config.MaxRequestSize)"),
|
||||
|
|
|
@ -524,3 +524,63 @@ func ReplyLikeSubmit(w http.ResponseWriter, r *http.Request, user c.User, srid s
|
|||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func ReplyUnlikeSubmit(w http.ResponseWriter, r *http.Request, user c.User, srid string) c.RouteError {
|
||||
js := r.PostFormValue("js") == "1"
|
||||
rid, err := strconv.Atoi(srid)
|
||||
if err != nil {
|
||||
return c.PreErrorJSQ("The provided Reply ID is not a valid number.", w, r, js)
|
||||
}
|
||||
|
||||
reply, err := c.Rstore.Get(rid)
|
||||
if err == sql.ErrNoRows {
|
||||
return c.PreErrorJSQ("You can't unlike something which doesn't exist!", w, r, js)
|
||||
} else if err != nil {
|
||||
return c.InternalErrorJSQ(err, w, r, js)
|
||||
}
|
||||
|
||||
topic, err := c.Topics.Get(reply.ParentID)
|
||||
if err == sql.ErrNoRows {
|
||||
return c.PreErrorJSQ("The parent topic doesn't exist.", w, r, js)
|
||||
} else if err != nil {
|
||||
return c.InternalErrorJSQ(err, w, r, js)
|
||||
}
|
||||
|
||||
// TODO: Add hooks to make use of headerLite
|
||||
lite, ferr := c.SimpleForumUserCheck(w, r, &user, topic.ParentID)
|
||||
if ferr != nil {
|
||||
return ferr
|
||||
}
|
||||
if !user.Perms.ViewTopic || !user.Perms.LikeItem {
|
||||
return c.NoPermissionsJSQ(w, r, user, js)
|
||||
}
|
||||
|
||||
_, err = c.Users.Get(reply.CreatedBy)
|
||||
if err != nil && err != sql.ErrNoRows {
|
||||
return c.LocalErrorJSQ("The target user doesn't exist", w, r, user, js)
|
||||
} else if err != nil {
|
||||
return c.InternalErrorJSQ(err, w, r, js)
|
||||
}
|
||||
|
||||
err = reply.Unlike(user.ID)
|
||||
if err != nil {
|
||||
return c.InternalErrorJSQ(err, w, r, js)
|
||||
}
|
||||
// TODO: Push dismiss-event alerts to the users.
|
||||
err = c.Activity.DeleteByParams("like", topic.ID, "reply")
|
||||
if err != nil {
|
||||
return c.InternalErrorJSQ(err, w, r, js)
|
||||
}
|
||||
|
||||
skip, rerr := lite.Hooks.VhookSkippable("action_end_unlike_reply", reply.ID, &user)
|
||||
if skip || rerr != nil {
|
||||
return rerr
|
||||
}
|
||||
|
||||
if !js {
|
||||
http.Redirect(w, r, "/topic/"+strconv.Itoa(reply.ParentID), http.StatusSeeOther)
|
||||
} else {
|
||||
_, _ = w.Write(successJSONBytes)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -38,8 +38,15 @@
|
|||
<a href="{{.Topic.UserLink}}" class="username real_username" rel="author">{{.Topic.CreatedByName}}</a>
|
||||
|
||||
{{if .CurrentUser.Loggedin}}
|
||||
{{if .CurrentUser.Perms.LikeItem}}<a href="/topic/like/submit/{{.Topic.ID}}?s={{.CurrentUser.Session}}" class="mod_button" {{if .Topic.Liked}}title="{{lang "topic.unlike_tooltip"}}" aria-label="{{lang "topic.unlike_aria"}}"{{else}}title="{{lang "topic.like_tooltip"}}" aria-label="{{lang "topic.like_aria"}}"{{end}}>
|
||||
<button class="username like_label {{if .Topic.Liked}}remove{{else}}add{{end}}_like"></button></a>{{end}}
|
||||
{{if .CurrentUser.Perms.LikeItem}}{{if ne .CurrentUser.ID .Topic.CreatedBy}}
|
||||
|
||||
{{if .Topic.Liked}}
|
||||
<a href="/topic/unlike/submit/{{.Topic.ID}}?s={{.CurrentUser.Session}}" class="mod_button" title="{{lang "topic.unlike_tooltip"}}" aria-label="{{lang "topic.unlike_aria"}}">
|
||||
<button class="username like_label remove_like"></button></a>{{else}}
|
||||
<a href="/topic/like/submit/{{.Topic.ID}}?s={{.CurrentUser.Session}}" class="mod_button" title="{{lang "topic.like_tooltip"}}" aria-label="{{lang "topic.like_aria"}}">
|
||||
<button class="username like_label add_like"></button></a>{{end}}
|
||||
|
||||
{{end}}{{end}}
|
||||
|
||||
<a href="" class="mod_button quote_item" title="{{lang "topic.quote_tooltip"}}" aria-label="{{lang "topic.quote_aria"}}"><button class="username quote_label"></button></a>
|
||||
|
||||
|
|
|
@ -31,7 +31,10 @@
|
|||
<div class="controls button_container{{if .LikeCount}} has_likes{{end}}">
|
||||
<div class="action_button_left">
|
||||
{{if $.CurrentUser.Loggedin}}
|
||||
{{if $.CurrentUser.Perms.LikeItem}}{{if ne $.CurrentUser.ID .CreatedBy}}<a href="/reply/like/submit/{{.ID}}?s={{$.CurrentUser.Session}}" class="action_button like_item {{if .Liked}}remove{{else}}add{{end}}_like" aria-label="{{lang "topic.post_like_aria"}}" data-action="like"></a>{{end}}{{end}}
|
||||
{{if $.CurrentUser.Perms.LikeItem}}{{if ne $.CurrentUser.ID .CreatedBy}}
|
||||
{{if .Liked}}<a href="/reply/unlike/submit/{{.ID}}?s={{$.CurrentUser.Session}}" class="action_button like_item remove_like" aria-label="{{lang "topic.post_unlike_aria"}}" data-action="unlike"></a>{{else}}
|
||||
<a href="/reply/like/submit/{{.ID}}?s={{$.CurrentUser.Session}}" class="action_button like_item add_like" aria-label="{{lang "topic.post_like_aria"}}" data-action="like"></a>{{end}}
|
||||
{{end}}{{end}}
|
||||
<a href="" class="action_button quote_item" aria-label="{{lang "topic.quote_aria"}}" data-action="quote"></a>
|
||||
{{if not $.Topic.IsClosed or $.CurrentUser.Perms.CloseTopic}}
|
||||
{{if $.CurrentUser.Perms.EditReply}}<a href="/reply/edit/submit/{{.ID}}?s={{$.CurrentUser.Session}}" class="action_button edit_item" aria-label="{{lang "topic.post_edit_aria"}}" data-action="edit"></a>{{end}}
|
||||
|
|
|
@ -13,7 +13,7 @@
|
|||
<span class="controls{{if .LikeCount}} has_likes{{end}}">
|
||||
|
||||
<a href="{{.UserLink}}" class="username real_username" rel="author">{{.CreatedByName}}</a>
|
||||
{{if $.CurrentUser.Perms.LikeItem}}{{if .Liked}}<a href="/reply/like/submit/{{.ID}}?s={{$.CurrentUser.Session}}" class="mod_button" title="{{lang "topic.post_like_tooltip"}}" aria-label="{{lang "topic.post_like_aria"}}"><button class="username like_label remove_like"></button></a>{{else}}<a href="/reply/like/submit/{{.ID}}?s={{$.CurrentUser.Session}}" class="mod_button" title="{{lang "topic.post_unlike_tooltip"}}" aria-label="{{lang "topic.post_unlike_aria"}}"><button class="username like_label add_like"></button></a>{{end}}{{end}}
|
||||
{{if $.CurrentUser.Perms.LikeItem}}{{if ne $.CurrentUser.ID .CreatedBy}}{{if .Liked}}<a href="/reply/unlike/submit/{{.ID}}?s={{$.CurrentUser.Session}}" class="mod_button" title="{{lang "topic.post_unlike_tooltip"}}" aria-label="{{lang "topic.post_unlike_aria"}}"><button class="username like_label remove_like"></button></a>{{else}}<a href="/reply/like/submit/{{.ID}}?s={{$.CurrentUser.Session}}" class="mod_button" title="{{lang "topic.post_like_tooltip"}}" aria-label="{{lang "topic.post_like_aria"}}"><button class="username like_label add_like"></button></a>{{end}}{{end}}{{end}}
|
||||
|
||||
<a href="" class="mod_button quote_item" title="{{lang "topic.quote_tooltip"}}" aria-label="{{lang "topic.quote_aria"}}"><button class="username quote_label"></button></a>
|
||||
|
||||
|
|
|
@ -1018,6 +1018,9 @@ input[type=checkbox]:not(:checked):hover + label .sel {
|
|||
.add_like:before, .remove_like:before {
|
||||
content: "{{lang "topic.plus_one" . }}";
|
||||
}
|
||||
.remove_like:before {
|
||||
content: "{{lang "topic.minus_one" . }}";
|
||||
}
|
||||
.button_container .open_edit:after, .edit_item:after {
|
||||
content: "{{lang "topic.edit_button_text" . }}";
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue