b1af963916
Honor user blocks in ProfileReplyEditSubmit. Reduce boilerplate. Update account_privacy_profile_comments phrase. Add account_privacy_profile_comments_public phrase. Add account_privacy_profile_comments_registered phrase. Add account_privacy_profile_comments_self phrase. Add account_privacy_enable_embeds phrase. Add profile_comments column to users table. Add who_can_convo column to users table. You will need to run the updater / patcher for this commit.
483 lines
14 KiB
Go
483 lines
14 KiB
Go
package routes
|
|
|
|
import (
|
|
"database/sql"
|
|
"errors"
|
|
"html"
|
|
"net/http"
|
|
"strconv"
|
|
"strings"
|
|
|
|
//"log"
|
|
|
|
c "github.com/Azareal/Gosora/common"
|
|
p "github.com/Azareal/Gosora/common/phrases"
|
|
)
|
|
|
|
func Convos(w http.ResponseWriter, r *http.Request, user *c.User, h *c.Header) c.RouteError {
|
|
accountEditHead("convos", w, r, user, h)
|
|
h.AddScript("convo.js")
|
|
h.AddSheet(h.Theme.Name + "/convo.css")
|
|
h.AddNotice("convo_dev")
|
|
ccount := c.Convos.GetUserCount(user.ID)
|
|
page, _ := strconv.Atoi(r.FormValue("page"))
|
|
offset, page, lastPage := c.PageOffset(ccount, page, c.Config.ItemsPerPage)
|
|
pageList := c.Paginate(page, lastPage, 5)
|
|
|
|
convos, err := c.Convos.GetUserExtra(user.ID, offset)
|
|
//log.Printf("convos: %+v\n", convos)
|
|
if err != sql.ErrNoRows && err != nil {
|
|
return c.InternalError(err, w, r)
|
|
}
|
|
|
|
var cRows []c.ConvoListRow
|
|
for _, convo := range convos {
|
|
var parti []*c.User
|
|
notMe := false
|
|
for _, u := range convo.Users {
|
|
if u.ID == user.ID {
|
|
continue
|
|
}
|
|
parti = append(parti, u)
|
|
notMe = true
|
|
}
|
|
if !notMe {
|
|
parti = convo.Users
|
|
}
|
|
cRows = append(cRows, c.ConvoListRow{convo, parti, len(parti) == 1})
|
|
}
|
|
|
|
pi := c.Account{h, "dashboard", "convos", c.ConvoListPage{h, cRows, c.Paginator{pageList, page, lastPage}}}
|
|
return renderTemplate("account", w, r, h, pi)
|
|
}
|
|
|
|
func Convo(w http.ResponseWriter, r *http.Request, user *c.User, header *c.Header, scid string) c.RouteError {
|
|
accountEditHead("convo", w, r, user, header)
|
|
header.AddSheet(header.Theme.Name + "/convo.css")
|
|
header.AddNotice("convo_dev")
|
|
cid, err := strconv.Atoi(scid)
|
|
if err != nil {
|
|
return c.LocalError(p.GetErrorPhrase("id_must_be_integer"), w, r, user)
|
|
}
|
|
|
|
convo, err := c.Convos.Get(cid)
|
|
if err == sql.ErrNoRows {
|
|
return c.NotFound(w, r, header)
|
|
} else if err != nil {
|
|
return c.InternalError(err, w, r)
|
|
}
|
|
pcount := convo.PostsCount()
|
|
if pcount == 0 {
|
|
return c.NotFound(w, r, header)
|
|
}
|
|
|
|
page, _ := strconv.Atoi(r.FormValue("page"))
|
|
offset, page, lastPage := c.PageOffset(pcount, page, c.Config.ItemsPerPage)
|
|
pageList := c.Paginate(page, lastPage, 5)
|
|
|
|
posts, err := convo.Posts(offset, c.Config.ItemsPerPage)
|
|
// TODO: Report a better error for no posts
|
|
if err == sql.ErrNoRows {
|
|
return c.NotFound(w, r, header)
|
|
} else if err != nil {
|
|
return c.InternalError(err, w, r)
|
|
}
|
|
|
|
uids, err := convo.Uids()
|
|
if err == sql.ErrNoRows {
|
|
return c.NotFound(w, r, header)
|
|
} else if err != nil {
|
|
return c.InternalError(err, w, r)
|
|
}
|
|
umap, err := c.Users.BulkGetMap(uids)
|
|
if err == sql.ErrNoRows {
|
|
return c.NotFound(w, r, header)
|
|
} else if err != nil {
|
|
return c.InternalError(err, w, r)
|
|
}
|
|
users := make([]*c.User, len(umap))
|
|
i := 0
|
|
for _, user := range umap {
|
|
users[i] = user
|
|
i++
|
|
}
|
|
|
|
pitems := make([]c.ConvoViewRow, len(posts))
|
|
for i, post := range posts {
|
|
uuser, ok := umap[post.CreatedBy]
|
|
if !ok {
|
|
return c.InternalError(errors.New("convo post creator not in umap"), w, r)
|
|
}
|
|
canModify := user.ID == post.CreatedBy || user.IsSuperMod
|
|
pitems[i] = c.ConvoViewRow{post, uuser, "", 4, canModify}
|
|
}
|
|
|
|
canReply := user.Perms.UseConvos || user.Perms.UseConvosOnlyWithMod
|
|
if !user.Perms.UseConvos && user.Perms.UseConvosOnlyWithMod {
|
|
u, err := c.Users.Get(convo.CreatedBy)
|
|
if err != nil {
|
|
return c.InternalError(err, w, r)
|
|
}
|
|
if !u.IsSuperMod {
|
|
canReply = false
|
|
}
|
|
}
|
|
|
|
pi := c.Account{header, "dashboard", "convo", c.ConvoViewPage{header, convo, pitems, users, canReply, c.Paginator{pageList, page, lastPage}}}
|
|
return renderTemplate("account", w, r, header, pi)
|
|
}
|
|
|
|
func ConvosCreate(w http.ResponseWriter, r *http.Request, user *c.User, h *c.Header) c.RouteError {
|
|
accountEditHead("create_convo", w, r, user, h)
|
|
if !user.Perms.UseConvos && !user.Perms.UseConvosOnlyWithMod {
|
|
return c.NoPermissions(w, r, user)
|
|
}
|
|
h.AddNotice("convo_dev")
|
|
uid, err := strconv.Atoi(r.FormValue("with"))
|
|
if err != nil {
|
|
return c.LocalError("invalid integer in parameter with", w, r, user)
|
|
}
|
|
u, err := c.Users.Get(uid)
|
|
if err != nil {
|
|
return c.LocalError("Unable to fetch user", w, r, user)
|
|
}
|
|
// TODO: Avoid potential double escape?
|
|
pi := c.Account{h, "dashboard", "create_convo", c.ConvoCreatePage{h, html.EscapeString(u.Name)}}
|
|
return renderTemplate("account", w, r, h, pi)
|
|
}
|
|
|
|
func ConvosCreateSubmit(w http.ResponseWriter, r *http.Request, user *c.User) c.RouteError {
|
|
_, ferr := c.SimpleUserCheck(w, r, user)
|
|
if ferr != nil {
|
|
return ferr
|
|
}
|
|
if !user.Perms.UseConvos && !user.Perms.UseConvosOnlyWithMod {
|
|
return c.NoPermissions(w, r, user)
|
|
}
|
|
|
|
sRecps := c.SanitiseSingleLine(r.PostFormValue("recp"))
|
|
body := c.PreparseMessage(r.PostFormValue("body"))
|
|
rlist := []int{}
|
|
|
|
// De-dupe recipients
|
|
var recps []string
|
|
unames := make(map[string]struct{})
|
|
for _, recp := range strings.Split(sRecps, ",") {
|
|
recp = strings.TrimSpace(recp)
|
|
_, exists := unames[recp]
|
|
if !exists {
|
|
recps = append(recps, recp)
|
|
unames[recp] = struct{}{}
|
|
}
|
|
}
|
|
|
|
max := 10 // max number of recipients that can be added at once
|
|
for i, recp := range recps {
|
|
if i >= max {
|
|
break
|
|
}
|
|
|
|
u, err := c.Users.GetByName(recp)
|
|
if err == sql.ErrNoRows {
|
|
return c.LocalError("One of the recipients doesn't exist", w, r, user)
|
|
} else if err != nil {
|
|
return c.InternalError(err, w, r)
|
|
}
|
|
// TODO: Should we kick them out of existing conversations if they're moved into a group without permission or the permission is revoked from their group? We might want to give them a chance to delete their messages though to avoid privacy headaches here and it may only be temporarily to tackle a specific incident.
|
|
if !u.Perms.UseConvos && !u.Perms.UseConvosOnlyWithMod {
|
|
return c.LocalError("One of the recipients doesn't have permission to use the conversations system", w, r, user)
|
|
}
|
|
if !user.Perms.UseConvos && !u.IsSuperMod && user.Perms.UseConvosOnlyWithMod {
|
|
return c.LocalError("You are only allowed to message global moderators.", w, r, user)
|
|
}
|
|
if !user.IsSuperMod && !u.Perms.UseConvos && u.Perms.UseConvosOnlyWithMod {
|
|
return c.LocalError("One of the recipients doesn't have permission to engage with conversation with you.", w, r, user)
|
|
}
|
|
blocked, err := c.UserBlocks.IsBlockedBy(u.ID, user.ID)
|
|
if err != nil {
|
|
return c.InternalError(err, w, r)
|
|
}
|
|
// Supermods can bypass blocks so they can tell people off when they do something stupid or have to convey important information
|
|
if blocked && !user.IsSuperMod {
|
|
return c.LocalError("You don't have permission to send messages to one of these users.", w, r, user)
|
|
}
|
|
|
|
rlist = append(rlist, u.ID)
|
|
}
|
|
|
|
cid, err := c.Convos.Create(body, user.ID, rlist)
|
|
if err != nil {
|
|
return c.InternalError(err, w, r)
|
|
}
|
|
|
|
// TODO: Don't bother making the subscription if the convo creator is the only recipient?
|
|
for _, uid := range rlist {
|
|
if uid == user.ID {
|
|
continue
|
|
}
|
|
err := c.Subscriptions.Add(uid, cid, "convo")
|
|
if err != nil {
|
|
return c.InternalError(err, w, r)
|
|
}
|
|
}
|
|
err = c.Subscriptions.Add(user.ID, cid, "convo")
|
|
if err != nil {
|
|
return c.InternalError(err, w, r)
|
|
}
|
|
|
|
err = c.AddActivityAndNotifyAll(c.Alert{ActorID: user.ID, Event: "create", ElementType: "convo", ElementID: cid, Actor: user})
|
|
if err != nil {
|
|
return c.InternalError(err, w, r)
|
|
}
|
|
|
|
http.Redirect(w, r, "/user/convo/"+strconv.Itoa(cid), http.StatusSeeOther)
|
|
return nil
|
|
}
|
|
|
|
/*func ConvosDeleteSubmit(w http.ResponseWriter, r *http.Request, user *c.User, scid string) c.RouteError {
|
|
_, ferr := c.SimpleUserCheck(w, r, user)
|
|
if ferr != nil {
|
|
return ferr
|
|
}
|
|
cid, err := strconv.Atoi(scid)
|
|
if err != nil {
|
|
return c.LocalError(p.GetErrorPhrase("id_must_be_integer"), w, r, user)
|
|
}
|
|
if err := c.Convos.Delete(cid); err != nil {
|
|
return c.InternalError(err, w, r)
|
|
}
|
|
http.Redirect(w, r, "/user/convos/", http.StatusSeeOther)
|
|
return nil
|
|
}*/
|
|
|
|
func ConvosCreateReplySubmit(w http.ResponseWriter, r *http.Request, user *c.User, scid string) c.RouteError {
|
|
_, ferr := c.SimpleUserCheck(w, r, user)
|
|
if ferr != nil {
|
|
return ferr
|
|
}
|
|
if !user.Perms.UseConvos && !user.Perms.UseConvosOnlyWithMod {
|
|
return c.NoPermissions(w, r, user)
|
|
}
|
|
cid, err := strconv.Atoi(scid)
|
|
if err != nil {
|
|
return c.LocalError(p.GetErrorPhrase("id_must_be_integer"), w, r, user)
|
|
}
|
|
|
|
convo, err := c.Convos.Get(cid)
|
|
if err == sql.ErrNoRows {
|
|
return c.NotFound(w, r, nil)
|
|
} else if err != nil {
|
|
return c.InternalError(err, w, r)
|
|
}
|
|
pcount := convo.PostsCount()
|
|
if pcount == 0 {
|
|
return c.NotFound(w, r, nil)
|
|
}
|
|
if !convo.Has(user.ID) {
|
|
return c.LocalError("You are not in this conversation.", w, r, user)
|
|
}
|
|
// TODO: Let the user reply if they're the convo creator in a convo with a mod
|
|
if !user.Perms.UseConvos && user.Perms.UseConvosOnlyWithMod {
|
|
u, err := c.Users.Get(convo.CreatedBy)
|
|
if err != nil {
|
|
return c.InternalError(err, w, r)
|
|
}
|
|
if !u.IsSuperMod {
|
|
return c.LocalError("You're only allowed to talk to global moderators.", w, r, user)
|
|
}
|
|
}
|
|
|
|
body := c.PreparseMessage(r.PostFormValue("content"))
|
|
post := &c.ConversationPost{CID: cid, Body: body, CreatedBy: user.ID}
|
|
pid, err := post.Create()
|
|
if err != nil {
|
|
return c.InternalError(err, w, r)
|
|
}
|
|
err = c.AddActivityAndNotifyAll(c.Alert{ActorID: user.ID, Event: "reply", ElementType: "convo", ElementID: cid, Actor: user, Extra: strconv.Itoa(pid)})
|
|
if err != nil {
|
|
return c.InternalError(err, w, r)
|
|
}
|
|
|
|
http.Redirect(w, r, "/user/convo/"+strconv.Itoa(convo.ID), http.StatusSeeOther)
|
|
return nil
|
|
}
|
|
|
|
func ConvosDeleteReplySubmit(w http.ResponseWriter, r *http.Request, user *c.User, scpid string) c.RouteError {
|
|
_, ferr := c.SimpleUserCheck(w, r, user)
|
|
if ferr != nil {
|
|
return ferr
|
|
}
|
|
cpid, err := strconv.Atoi(scpid)
|
|
if err != nil {
|
|
return c.LocalError(p.GetErrorPhrase("id_must_be_integer"), w, r, user)
|
|
}
|
|
|
|
post := &c.ConversationPost{ID: cpid}
|
|
err = post.Fetch()
|
|
if err == sql.ErrNoRows {
|
|
return c.NotFound(w, r, nil)
|
|
} else if err != nil {
|
|
return c.InternalError(err, w, r)
|
|
}
|
|
|
|
convo, err := c.Convos.Get(post.CID)
|
|
if err == sql.ErrNoRows {
|
|
return c.NotFound(w, r, nil)
|
|
} else if err != nil {
|
|
return c.InternalError(err, w, r)
|
|
}
|
|
pcount := convo.PostsCount()
|
|
if pcount == 0 {
|
|
return c.NotFound(w, r, nil)
|
|
}
|
|
if user.ID != post.CreatedBy && !user.IsSuperMod {
|
|
return c.NoPermissions(w, r, user)
|
|
}
|
|
|
|
posts, err := convo.Posts(0, c.Config.ItemsPerPage)
|
|
// TODO: Report a better error for no posts
|
|
if err == sql.ErrNoRows {
|
|
return c.NotFound(w, r, nil)
|
|
} else if err != nil {
|
|
return c.InternalError(err, w, r)
|
|
}
|
|
|
|
if post.ID == posts[0].ID {
|
|
err = c.Convos.Delete(convo.ID)
|
|
} else {
|
|
err = post.Delete()
|
|
}
|
|
if err != nil {
|
|
return c.InternalError(err, w, r)
|
|
}
|
|
|
|
http.Redirect(w, r, "/user/convo/"+strconv.Itoa(post.CID), http.StatusSeeOther)
|
|
return nil
|
|
}
|
|
|
|
func ConvosEditReplySubmit(w http.ResponseWriter, r *http.Request, user *c.User, scpid string) c.RouteError {
|
|
_, ferr := c.SimpleUserCheck(w, r, user)
|
|
if ferr != nil {
|
|
return ferr
|
|
}
|
|
cpid, err := strconv.Atoi(scpid)
|
|
if err != nil {
|
|
return c.LocalError(p.GetErrorPhrase("id_must_be_integer"), w, r, user)
|
|
}
|
|
if !user.Perms.UseConvos {
|
|
return c.NoPermissions(w, r, user)
|
|
}
|
|
js := r.PostFormValue("js") == "1"
|
|
|
|
post := &c.ConversationPost{ID: cpid}
|
|
err = post.Fetch()
|
|
if err == sql.ErrNoRows {
|
|
return c.NotFound(w, r, nil)
|
|
} else if err != nil {
|
|
return c.InternalError(err, w, r)
|
|
}
|
|
|
|
convo, err := c.Convos.Get(post.CID)
|
|
if err == sql.ErrNoRows {
|
|
return c.NotFound(w, r, nil)
|
|
} else if err != nil {
|
|
return c.InternalError(err, w, r)
|
|
}
|
|
pcount := convo.PostsCount()
|
|
if pcount == 0 {
|
|
return c.NotFound(w, r, nil)
|
|
}
|
|
if user.ID != post.CreatedBy && !user.IsSuperMod {
|
|
return c.NoPermissions(w, r, user)
|
|
}
|
|
if !convo.Has(user.ID) {
|
|
return c.LocalError("You are not in this conversation.", w, r, user)
|
|
}
|
|
|
|
post.Body = c.PreparseMessage(r.PostFormValue("edit_item"))
|
|
err = post.Update()
|
|
if err != nil {
|
|
return c.InternalError(err, w, r)
|
|
}
|
|
return actionSuccess(w, r, "/user/convo/"+strconv.Itoa(post.CID), js)
|
|
}
|
|
|
|
func RelationsBlockCreate(w http.ResponseWriter, r *http.Request, u *c.User, h *c.Header, spid string) c.RouteError {
|
|
h.Title = p.GetTitlePhrase("create_block")
|
|
pid, err := strconv.Atoi(spid)
|
|
if err != nil {
|
|
return c.LocalError(p.GetErrorPhrase("id_must_be_integer"), w, r, u)
|
|
}
|
|
puser, err := c.Users.Get(pid)
|
|
if err == sql.ErrNoRows {
|
|
return c.LocalError("The user you're trying to block doesn't exist.", w, r, u)
|
|
} else if err != nil {
|
|
return c.InternalError(err, w, r)
|
|
}
|
|
|
|
pi := c.Page{h, nil, c.AreYouSure{"/user/block/create/submit/" + strconv.Itoa(puser.ID), p.GetTmplPhrase("create_block_msg")}}
|
|
return renderTemplate("are_you_sure", w, r, h, pi)
|
|
}
|
|
|
|
func RelationsBlockCreateSubmit(w http.ResponseWriter, r *http.Request, u *c.User, spid string) c.RouteError {
|
|
pid, err := strconv.Atoi(spid)
|
|
if err != nil {
|
|
return c.LocalError(p.GetErrorPhrase("id_must_be_integer"), w, r, u)
|
|
}
|
|
puser, err := c.Users.Get(pid)
|
|
if err == sql.ErrNoRows {
|
|
return c.LocalError("The user you're trying to block doesn't exist.", w, r, u)
|
|
} else if err != nil {
|
|
return c.InternalError(err, w, r)
|
|
}
|
|
if u.ID == puser.ID {
|
|
return c.LocalError("You can't block yourself.", w, r, u)
|
|
}
|
|
|
|
err = c.UserBlocks.Add(u.ID, puser.ID)
|
|
if err != nil {
|
|
return c.InternalError(err, w, r)
|
|
}
|
|
|
|
http.Redirect(w, r, "/user/"+strconv.Itoa(puser.ID), http.StatusSeeOther)
|
|
return nil
|
|
}
|
|
|
|
func RelationsBlockRemove(w http.ResponseWriter, r *http.Request, u *c.User, h *c.Header, spid string) c.RouteError {
|
|
h.Title = p.GetTitlePhrase("remove_block")
|
|
pid, err := strconv.Atoi(spid)
|
|
if err != nil {
|
|
return c.LocalError(p.GetErrorPhrase("id_must_be_integer"), w, r, u)
|
|
}
|
|
puser, err := c.Users.Get(pid)
|
|
if err == sql.ErrNoRows {
|
|
return c.LocalError("The user you're trying to block doesn't exist.", w, r, u)
|
|
} else if err != nil {
|
|
return c.InternalError(err, w, r)
|
|
}
|
|
|
|
pi := c.Page{h, nil, c.AreYouSure{"/user/block/remove/submit/" + strconv.Itoa(puser.ID), p.GetTmplPhrase("remove_block_msg")}}
|
|
return renderTemplate("are_you_sure", w, r, h, pi)
|
|
}
|
|
|
|
func RelationsBlockRemoveSubmit(w http.ResponseWriter, r *http.Request, u *c.User, spid string) c.RouteError {
|
|
pid, err := strconv.Atoi(spid)
|
|
if err != nil {
|
|
return c.LocalError(p.GetErrorPhrase("id_must_be_integer"), w, r, u)
|
|
}
|
|
puser, err := c.Users.Get(pid)
|
|
if err == sql.ErrNoRows {
|
|
return c.LocalError("The user you're trying to unblock doesn't exist.", w, r, u)
|
|
} else if err != nil {
|
|
return c.InternalError(err, w, r)
|
|
}
|
|
|
|
err = c.UserBlocks.Remove(u.ID, puser.ID)
|
|
if err != nil {
|
|
return c.InternalError(err, w, r)
|
|
}
|
|
|
|
http.Redirect(w, r, "/user/"+strconv.Itoa(puser.ID), http.StatusSeeOther)
|
|
return nil
|
|
}
|