User Forums Blocking Behavior + Misc Fixes
Make some adjustments to blocking behavior regarding the forums: * Pre-existing bug: on a forum's home page (threads list), if a thread was created by a blocked user, the thread still appeared with the user's name and picture visible. Now: their picture and name will be "[unavailable]" but the thread title/message and link to the thread will remain. Note: in the thread view itself, posts by the blocked user will be missing as normal. * Make some tweaks to allow forum moderators (and owners of user-owned forums) able to see messages from blocked users on their forum: * In threads: a blocked user's picture and name are "[unavailable]" but the content of their message is still shown, and can be deleted by moderators. Misc fixes: * Private photos: when viewing your granted/grantee lists, hide users whose accounts are inactive or who are blocked. * CertifiedSince: in case a user was manually certified but their cert photo status is not correct, return their user CreatedAt time instead.
This commit is contained in:
parent
617cd48308
commit
c37d0298b0
|
@ -74,7 +74,7 @@ func Thread() http.HandlerFunc {
|
||||||
}
|
}
|
||||||
pager.ParsePage(r)
|
pager.ParsePage(r)
|
||||||
|
|
||||||
comments, err := models.PaginateComments(currentUser, "threads", thread.ID, pager)
|
comments, err := models.PaginateComments(currentUser, "threads", thread.ID, canModerate, pager)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
session.FlashError(w, r, "Couldn't paginate comments: %s", err)
|
session.FlashError(w, r, "Couldn't paginate comments: %s", err)
|
||||||
templates.Redirect(w, "/")
|
templates.Redirect(w, "/")
|
||||||
|
|
|
@ -2,7 +2,6 @@ package models
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"gorm.io/gorm"
|
"gorm.io/gorm"
|
||||||
|
@ -66,7 +65,9 @@ func (u *User) CertifiedSince() (time.Time, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if cert.Status != CertificationPhotoApproved {
|
if cert.Status != CertificationPhotoApproved {
|
||||||
return time.Time{}, fmt.Errorf("cert photo status is: %s (expected 'approved')", cert.Status)
|
// The edge case can come up if a user was manually certified but didn't have an approved picture.
|
||||||
|
// Return their CreatedAt instead.
|
||||||
|
return u.CreatedAt, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
return cert.UpdatedAt, nil
|
return cert.UpdatedAt, nil
|
||||||
|
|
|
@ -104,11 +104,14 @@ func CountCommentsReceived(user *User) int64 {
|
||||||
}
|
}
|
||||||
|
|
||||||
// PaginateComments provides a page of comments on something.
|
// PaginateComments provides a page of comments on something.
|
||||||
func PaginateComments(user *User, tableName string, tableID uint64, pager *Pagination) ([]*Comment, error) {
|
//
|
||||||
|
// Note: noBlockLists is to facilitate user-owned forums, where forum owners/moderators should override the block lists
|
||||||
|
// and retain full visibility into all user comments on their forum. Default/recommended is to leave it false, where
|
||||||
|
// the user's block list filters the view.
|
||||||
|
func PaginateComments(user *User, tableName string, tableID uint64, noBlockLists bool, pager *Pagination) ([]*Comment, error) {
|
||||||
var (
|
var (
|
||||||
cs = []*Comment{}
|
cs = []*Comment{}
|
||||||
query = (&Comment{}).Preload()
|
query = (&Comment{}).Preload()
|
||||||
blockedUserIDs = BlockedUserIDs(user)
|
|
||||||
wheres = []string{}
|
wheres = []string{}
|
||||||
placeholders = []interface{}{}
|
placeholders = []interface{}{}
|
||||||
)
|
)
|
||||||
|
@ -116,10 +119,13 @@ func PaginateComments(user *User, tableName string, tableID uint64, pager *Pagin
|
||||||
wheres = append(wheres, "table_name = ? AND table_id = ?")
|
wheres = append(wheres, "table_name = ? AND table_id = ?")
|
||||||
placeholders = append(placeholders, tableName, tableID)
|
placeholders = append(placeholders, tableName, tableID)
|
||||||
|
|
||||||
|
if !noBlockLists {
|
||||||
|
blockedUserIDs := BlockedUserIDs(user)
|
||||||
if len(blockedUserIDs) > 0 {
|
if len(blockedUserIDs) > 0 {
|
||||||
wheres = append(wheres, "user_id NOT IN ?")
|
wheres = append(wheres, "user_id NOT IN ?")
|
||||||
placeholders = append(placeholders, blockedUserIDs)
|
placeholders = append(placeholders, blockedUserIDs)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Don't show comments from banned or disabled accounts.
|
// Don't show comments from banned or disabled accounts.
|
||||||
wheres = append(wheres, `
|
wheres = append(wheres, `
|
||||||
|
|
|
@ -140,8 +140,9 @@ func PaginateForums(user *User, categories []string, search *Search, subscribed
|
||||||
WHERE user_id = ?
|
WHERE user_id = ?
|
||||||
AND forum_id = forums.id
|
AND forum_id = forums.id
|
||||||
)
|
)
|
||||||
|
OR forums.owner_id = ?
|
||||||
`)
|
`)
|
||||||
placeholders = append(placeholders, user.ID)
|
placeholders = append(placeholders, user.ID, user.ID)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Apply their search terms.
|
// Apply their search terms.
|
||||||
|
|
|
@ -226,6 +226,10 @@ func PaginatePrivatePhotoList(user *User, grantee bool, pager *Pagination) ([]*U
|
||||||
query *gorm.DB
|
query *gorm.DB
|
||||||
wheres = []string{}
|
wheres = []string{}
|
||||||
placeholders = []interface{}{}
|
placeholders = []interface{}{}
|
||||||
|
blocklist = BlockedUserIDs(user)
|
||||||
|
|
||||||
|
// Column name of "other user" depending on direction
|
||||||
|
otherUserColumn string
|
||||||
)
|
)
|
||||||
|
|
||||||
// Which direction are we going?
|
// Which direction are we going?
|
||||||
|
@ -233,22 +237,33 @@ func PaginatePrivatePhotoList(user *User, grantee bool, pager *Pagination) ([]*U
|
||||||
// Return the private photo grants for whom YOU are the recipient.
|
// Return the private photo grants for whom YOU are the recipient.
|
||||||
wheres = append(wheres, "target_user_id = ?")
|
wheres = append(wheres, "target_user_id = ?")
|
||||||
placeholders = append(placeholders, user.ID)
|
placeholders = append(placeholders, user.ID)
|
||||||
|
otherUserColumn = "source_user_id"
|
||||||
} else {
|
} else {
|
||||||
// Return the users that YOU have granted access to YOUR private pictures.
|
// Return the users that YOU have granted access to YOUR private pictures.
|
||||||
wheres = append(wheres, "source_user_id = ?")
|
wheres = append(wheres, "source_user_id = ?")
|
||||||
placeholders = append(placeholders, user.ID)
|
placeholders = append(placeholders, user.ID)
|
||||||
|
otherUserColumn = "target_user_id"
|
||||||
}
|
}
|
||||||
|
|
||||||
// Users that actually exist.
|
// Filter out users who are banned/disabled.
|
||||||
wheres = append(wheres, `
|
wheres = append(wheres,
|
||||||
|
fmt.Sprintf(`
|
||||||
EXISTS (
|
EXISTS (
|
||||||
SELECT 1
|
SELECT 1
|
||||||
FROM users
|
FROM users
|
||||||
WHERE users.id = private_photos.target_user_id
|
WHERE private_photos.%s = users.id
|
||||||
OR users.id = private_photos.source_user_id
|
AND users.status = 'active'
|
||||||
)`,
|
)`,
|
||||||
|
otherUserColumn,
|
||||||
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Filter blocked users.
|
||||||
|
if len(blocklist) > 0 {
|
||||||
|
wheres = append(wheres, fmt.Sprintf("%s NOT IN ?", otherUserColumn))
|
||||||
|
placeholders = append(placeholders, blocklist)
|
||||||
|
}
|
||||||
|
|
||||||
query = DB.Where(
|
query = DB.Where(
|
||||||
strings.Join(wheres, " AND "),
|
strings.Join(wheres, " AND "),
|
||||||
placeholders...,
|
placeholders...,
|
||||||
|
|
|
@ -8,6 +8,7 @@ type UserRelationship struct {
|
||||||
Computed bool // check whether the SetUserRelationships function has been run
|
Computed bool // check whether the SetUserRelationships function has been run
|
||||||
IsFriend bool // if true, a friends-only profile pic can show
|
IsFriend bool // if true, a friends-only profile pic can show
|
||||||
IsPrivateGranted bool // if true, a private profile pic can show
|
IsPrivateGranted bool // if true, a private profile pic can show
|
||||||
|
IsBlocked bool // if true, the users are blocking each other
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetUserRelationships updates a set of User objects to populate their UserRelationships in
|
// SetUserRelationships updates a set of User objects to populate their UserRelationships in
|
||||||
|
@ -17,12 +18,14 @@ func SetUserRelationships(currentUser *User, users []*User) error {
|
||||||
var (
|
var (
|
||||||
friendIDs = FriendIDs(currentUser.ID)
|
friendIDs = FriendIDs(currentUser.ID)
|
||||||
privateGrants = PrivateGrantedUserIDs(currentUser.ID)
|
privateGrants = PrivateGrantedUserIDs(currentUser.ID)
|
||||||
|
blockedIDs = BlockedUserIDs(currentUser)
|
||||||
)
|
)
|
||||||
|
|
||||||
// Map them for easier lookup.
|
// Map them for easier lookup.
|
||||||
var (
|
var (
|
||||||
friendMap = map[uint64]interface{}{}
|
friendMap = map[uint64]interface{}{}
|
||||||
privateMap = map[uint64]interface{}{}
|
privateMap = map[uint64]interface{}{}
|
||||||
|
blockedMap = map[uint64]interface{}{}
|
||||||
)
|
)
|
||||||
|
|
||||||
for _, id := range friendIDs {
|
for _, id := range friendIDs {
|
||||||
|
@ -33,6 +36,10 @@ func SetUserRelationships(currentUser *User, users []*User) error {
|
||||||
privateMap[id] = nil
|
privateMap[id] = nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for _, id := range blockedIDs {
|
||||||
|
blockedMap[id] = nil
|
||||||
|
}
|
||||||
|
|
||||||
// Inject the UserRelationships.
|
// Inject the UserRelationships.
|
||||||
for _, u := range users {
|
for _, u := range users {
|
||||||
if u.ID == currentUser.ID {
|
if u.ID == currentUser.ID {
|
||||||
|
@ -49,6 +56,10 @@ func SetUserRelationships(currentUser *User, users []*User) error {
|
||||||
if _, ok := privateMap[u.ID]; ok {
|
if _, ok := privateMap[u.ID]; ok {
|
||||||
u.UserRelationship.IsPrivateGranted = true
|
u.UserRelationship.IsPrivateGranted = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if _, ok := blockedMap[u.ID]; ok {
|
||||||
|
u.UserRelationship.IsBlocked = true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -111,12 +111,20 @@
|
||||||
<div class="box has-background-success-light has-text-dark">
|
<div class="box has-background-success-light has-text-dark">
|
||||||
<div class="columns">
|
<div class="columns">
|
||||||
<div class="column is-2 has-text-centered pt-0 pb-1">
|
<div class="column is-2 has-text-centered pt-0 pb-1">
|
||||||
|
<!-- Thread starter is blocked? -->
|
||||||
|
{{if .Comment.User.UserRelationship.IsBlocked}}
|
||||||
|
<div>
|
||||||
|
{{template "avatar-64x64"}}
|
||||||
|
</div>
|
||||||
|
[unavailable]
|
||||||
|
{{else}}
|
||||||
<div>
|
<div>
|
||||||
<a href="/u/{{.Comment.User.Username}}">
|
<a href="/u/{{.Comment.User.Username}}">
|
||||||
{{template "avatar-64x64" .Comment.User}}
|
{{template "avatar-64x64" .Comment.User}}
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
<a href="/u/{{.Comment.User.Username}}">{{.Comment.User.NameOrUsername}}</a>
|
<a href="/u/{{.Comment.User.Username}}">{{.Comment.User.NameOrUsername}}</a>
|
||||||
|
{{end}}
|
||||||
</div>
|
</div>
|
||||||
<div class="column content pt-1 pb-0">
|
<div class="column content pt-1 pb-0">
|
||||||
<a href="/forum/thread/{{.ID}}">
|
<a href="/forum/thread/{{.ID}}">
|
||||||
|
|
|
@ -133,12 +133,21 @@
|
||||||
<div class="box has-background-link-light has-text-dark" id="p{{.ID}}">
|
<div class="box has-background-link-light has-text-dark" id="p{{.ID}}">
|
||||||
<div class="columns">
|
<div class="columns">
|
||||||
<div class="column is-2 has-text-centered">
|
<div class="column is-2 has-text-centered">
|
||||||
|
<!-- Thread starter is blocked? -->
|
||||||
|
{{if .User.UserRelationship.IsBlocked}}
|
||||||
|
<div>
|
||||||
|
{{template "avatar-64x64"}}
|
||||||
|
</div>
|
||||||
|
[unavailable]
|
||||||
|
{{else}}
|
||||||
<div>
|
<div>
|
||||||
<a href="/u/{{$c.User.Username}}">
|
<a href="/u/{{$c.User.Username}}">
|
||||||
{{template "avatar-96x96" $c.User}}
|
{{template "avatar-96x96" $c.User}}
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
<a href="/u/{{$c.User.Username}}">{{$c.User.NameOrUsername}}</a>
|
<a href="/u/{{$c.User.Username}}">{{$c.User.NameOrUsername}}</a>
|
||||||
|
{{end}}
|
||||||
|
|
||||||
{{if $c.User.IsAdmin}}
|
{{if $c.User.IsAdmin}}
|
||||||
<div class="is-size-7 mt-1">
|
<div class="is-size-7 mt-1">
|
||||||
<span class="tag is-danger is-light">
|
<span class="tag is-danger is-light">
|
||||||
|
|
Loading…
Reference in New Issue
Block a user