2019-07-26 23:29:42 +00:00
package routes
import (
2022-02-21 03:32:53 +00:00
"database/sql"
"errors"
"html"
"math/rand"
"net/http"
"strconv"
"strings"
//"log"
c "github.com/Azareal/Gosora/common"
p "github.com/Azareal/Gosora/common/phrases"
2019-07-26 23:29:42 +00:00
)
2021-01-24 23:16:54 +00:00
func convoNotice ( h * c . Header ) {
2022-02-21 03:32:53 +00:00
//h.AddNotice("convo_dev")
c := rand . Intn ( 3 )
h . AddNotice ( "convo_rand_" + strconv . Itoa ( c ) )
2021-01-24 23:16:54 +00:00
}
2020-03-18 09:21:34 +00:00
func Convos ( w http . ResponseWriter , r * http . Request , user * c . User , h * c . Header ) c . RouteError {
2022-02-21 03:32:53 +00:00
accountEditHead ( "convos" , w , r , user , h )
h . AddScript ( "convo.js" )
h . AddSheet ( h . Theme . Name + "/convo.css" )
convoNotice ( h )
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 )
2019-07-26 23:29:42 +00:00
}
2021-01-24 23:16:54 +00:00
func Convo ( w http . ResponseWriter , r * http . Request , user * c . User , h * c . Header , scid string ) c . RouteError {
2022-02-21 03:32:53 +00:00
accountEditHead ( "convo" , w , r , user , h )
h . AddSheet ( h . Theme . Name + "/convo.css" )
convoNotice ( h )
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 , h )
} else if err != nil {
return c . InternalError ( err , w , r )
}
pcount := convo . PostsCount ( )
if pcount == 0 {
return c . NotFound ( w , r , h )
}
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 , h )
} else if err != nil {
return c . InternalError ( err , w , r )
}
uids , err := convo . Uids ( )
if err == sql . ErrNoRows {
return c . NotFound ( w , r , h )
} 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 , h )
} 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 { h , "dashboard" , "convo" , c . ConvoViewPage { h , convo , pitems , users , canReply , c . Paginator { pageList , page , lastPage } } }
return renderTemplate ( "account" , w , r , h , pi )
2019-07-26 23:29:42 +00:00
}
2020-03-18 09:21:34 +00:00
func ConvosCreate ( w http . ResponseWriter , r * http . Request , user * c . User , h * c . Header ) c . RouteError {
2022-02-21 03:32:53 +00:00
accountEditHead ( "create_convo" , w , r , user , h )
if ! user . Perms . UseConvos && ! user . Perms . UseConvosOnlyWithMod {
return c . NoPermissions ( w , r , user )
}
convoNotice ( h )
uid , err := strconv . Atoi ( r . FormValue ( "with" ) )
if err != nil {
return c . LocalError ( "invalid integer in parameter with" , w , r , user )
}
recp , 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 ( recp . Name ) } }
return renderTemplate ( "account" , w , r , h , pi )
2019-07-26 23:29:42 +00:00
}
2020-03-18 09:21:34 +00:00
func ConvosCreateSubmit ( w http . ResponseWriter , r * http . Request , user * c . User ) c . RouteError {
2022-02-21 03:32:53 +00:00
_ , 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
2019-07-26 23:29:42 +00:00
}
2020-03-18 09:21:34 +00:00
/ * func ConvosDeleteSubmit ( w http . ResponseWriter , r * http . Request , user * c . User , scid string ) c . RouteError {
2022-02-21 03:32:53 +00:00
_ , 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
2019-08-20 08:44:37 +00:00
} * /
2019-07-26 23:29:42 +00:00
2020-03-18 09:21:34 +00:00
func ConvosCreateReplySubmit ( w http . ResponseWriter , r * http . Request , user * c . User , scid string ) c . RouteError {
2022-02-21 03:32:53 +00:00
_ , 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
2019-07-26 23:29:42 +00:00
}
2021-01-24 23:16:54 +00:00
func ConvosDeleteReplySubmit ( w http . ResponseWriter , r * http . Request , u * c . User , scpid string ) c . RouteError {
2022-02-21 03:32:53 +00:00
_ , ferr := c . SimpleUserCheck ( w , r , u )
if ferr != nil {
return ferr
}
cpid , err := strconv . Atoi ( scpid )
if err != nil {
return c . LocalError ( p . GetErrorPhrase ( "id_must_be_integer" ) , w , r , u )
}
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 u . ID != post . CreatedBy && ! u . IsSuperMod {
return c . NoPermissions ( w , r , u )
}
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
2019-08-14 10:39:04 +00:00
}
2020-03-18 09:21:34 +00:00
func ConvosEditReplySubmit ( w http . ResponseWriter , r * http . Request , user * c . User , scpid string ) c . RouteError {
2022-02-21 03:32:53 +00:00
_ , 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 )
2019-08-14 10:39:04 +00:00
}
2019-10-13 08:50:55 +00:00
2020-07-14 21:50:29 +00:00
func RelationsBlockCreate ( w http . ResponseWriter , r * http . Request , u * c . User , h * c . Header , spid string ) c . RouteError {
2022-02-21 03:32:53 +00:00
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 )
2019-10-18 00:35:13 +00:00
}
2020-07-14 21:50:29 +00:00
func RelationsBlockCreateSubmit ( w http . ResponseWriter , r * http . Request , u * c . User , spid string ) c . RouteError {
2022-02-21 03:32:53 +00:00
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
2019-10-18 00:35:13 +00:00
}
2020-07-14 21:50:29 +00:00
func RelationsBlockRemove ( w http . ResponseWriter , r * http . Request , u * c . User , h * c . Header , spid string ) c . RouteError {
2022-02-21 03:32:53 +00:00
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 )
2019-10-13 08:50:55 +00:00
}
2020-07-14 21:50:29 +00:00
func RelationsBlockRemoveSubmit ( w http . ResponseWriter , r * http . Request , u * c . User , spid string ) c . RouteError {
2022-02-21 03:32:53 +00:00
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
2019-10-18 00:35:13 +00:00
}