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)
|
||||
|
||||
comments, err := models.PaginateComments(currentUser, "threads", thread.ID, pager)
|
||||
comments, err := models.PaginateComments(currentUser, "threads", thread.ID, canModerate, pager)
|
||||
if err != nil {
|
||||
session.FlashError(w, r, "Couldn't paginate comments: %s", err)
|
||||
templates.Redirect(w, "/")
|
||||
|
|
|
@ -2,7 +2,6 @@ package models
|
|||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"gorm.io/gorm"
|
||||
|
@ -66,7 +65,9 @@ func (u *User) CertifiedSince() (time.Time, error) {
|
|||
}
|
||||
|
||||
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
|
||||
|
|
|
@ -104,21 +104,27 @@ func CountCommentsReceived(user *User) int64 {
|
|||
}
|
||||
|
||||
// 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 (
|
||||
cs = []*Comment{}
|
||||
query = (&Comment{}).Preload()
|
||||
blockedUserIDs = BlockedUserIDs(user)
|
||||
wheres = []string{}
|
||||
placeholders = []interface{}{}
|
||||
cs = []*Comment{}
|
||||
query = (&Comment{}).Preload()
|
||||
wheres = []string{}
|
||||
placeholders = []interface{}{}
|
||||
)
|
||||
|
||||
wheres = append(wheres, "table_name = ? AND table_id = ?")
|
||||
placeholders = append(placeholders, tableName, tableID)
|
||||
|
||||
if len(blockedUserIDs) > 0 {
|
||||
wheres = append(wheres, "user_id NOT IN ?")
|
||||
placeholders = append(placeholders, blockedUserIDs)
|
||||
if !noBlockLists {
|
||||
blockedUserIDs := BlockedUserIDs(user)
|
||||
if len(blockedUserIDs) > 0 {
|
||||
wheres = append(wheres, "user_id NOT IN ?")
|
||||
placeholders = append(placeholders, blockedUserIDs)
|
||||
}
|
||||
}
|
||||
|
||||
// Don't show comments from banned or disabled accounts.
|
||||
|
|
|
@ -140,8 +140,9 @@ func PaginateForums(user *User, categories []string, search *Search, subscribed
|
|||
WHERE user_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.
|
||||
|
|
|
@ -226,6 +226,10 @@ func PaginatePrivatePhotoList(user *User, grantee bool, pager *Pagination) ([]*U
|
|||
query *gorm.DB
|
||||
wheres = []string{}
|
||||
placeholders = []interface{}{}
|
||||
blocklist = BlockedUserIDs(user)
|
||||
|
||||
// Column name of "other user" depending on direction
|
||||
otherUserColumn string
|
||||
)
|
||||
|
||||
// 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.
|
||||
wheres = append(wheres, "target_user_id = ?")
|
||||
placeholders = append(placeholders, user.ID)
|
||||
otherUserColumn = "source_user_id"
|
||||
} else {
|
||||
// Return the users that YOU have granted access to YOUR private pictures.
|
||||
wheres = append(wheres, "source_user_id = ?")
|
||||
placeholders = append(placeholders, user.ID)
|
||||
otherUserColumn = "target_user_id"
|
||||
}
|
||||
|
||||
// Users that actually exist.
|
||||
wheres = append(wheres, `
|
||||
EXISTS (
|
||||
SELECT 1
|
||||
FROM users
|
||||
WHERE users.id = private_photos.target_user_id
|
||||
OR users.id = private_photos.source_user_id
|
||||
)`,
|
||||
// Filter out users who are banned/disabled.
|
||||
wheres = append(wheres,
|
||||
fmt.Sprintf(`
|
||||
EXISTS (
|
||||
SELECT 1
|
||||
FROM users
|
||||
WHERE private_photos.%s = users.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(
|
||||
strings.Join(wheres, " AND "),
|
||||
placeholders...,
|
||||
|
|
|
@ -8,6 +8,7 @@ type UserRelationship struct {
|
|||
Computed bool // check whether the SetUserRelationships function has been run
|
||||
IsFriend bool // if true, a friends-only 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
|
||||
|
@ -17,12 +18,14 @@ func SetUserRelationships(currentUser *User, users []*User) error {
|
|||
var (
|
||||
friendIDs = FriendIDs(currentUser.ID)
|
||||
privateGrants = PrivateGrantedUserIDs(currentUser.ID)
|
||||
blockedIDs = BlockedUserIDs(currentUser)
|
||||
)
|
||||
|
||||
// Map them for easier lookup.
|
||||
var (
|
||||
friendMap = map[uint64]interface{}{}
|
||||
privateMap = map[uint64]interface{}{}
|
||||
blockedMap = map[uint64]interface{}{}
|
||||
)
|
||||
|
||||
for _, id := range friendIDs {
|
||||
|
@ -33,6 +36,10 @@ func SetUserRelationships(currentUser *User, users []*User) error {
|
|||
privateMap[id] = nil
|
||||
}
|
||||
|
||||
for _, id := range blockedIDs {
|
||||
blockedMap[id] = nil
|
||||
}
|
||||
|
||||
// Inject the UserRelationships.
|
||||
for _, u := range users {
|
||||
if u.ID == currentUser.ID {
|
||||
|
@ -49,6 +56,10 @@ func SetUserRelationships(currentUser *User, users []*User) error {
|
|||
if _, ok := privateMap[u.ID]; ok {
|
||||
u.UserRelationship.IsPrivateGranted = true
|
||||
}
|
||||
|
||||
if _, ok := blockedMap[u.ID]; ok {
|
||||
u.UserRelationship.IsBlocked = true
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -111,12 +111,20 @@
|
|||
<div class="box has-background-success-light has-text-dark">
|
||||
<div class="columns">
|
||||
<div class="column is-2 has-text-centered pt-0 pb-1">
|
||||
<div>
|
||||
<a href="/u/{{.Comment.User.Username}}">
|
||||
{{template "avatar-64x64" .Comment.User}}
|
||||
</a>
|
||||
</div>
|
||||
<a href="/u/{{.Comment.User.Username}}">{{.Comment.User.NameOrUsername}}</a>
|
||||
<!-- Thread starter is blocked? -->
|
||||
{{if .Comment.User.UserRelationship.IsBlocked}}
|
||||
<div>
|
||||
{{template "avatar-64x64"}}
|
||||
</div>
|
||||
[unavailable]
|
||||
{{else}}
|
||||
<div>
|
||||
<a href="/u/{{.Comment.User.Username}}">
|
||||
{{template "avatar-64x64" .Comment.User}}
|
||||
</a>
|
||||
</div>
|
||||
<a href="/u/{{.Comment.User.Username}}">{{.Comment.User.NameOrUsername}}</a>
|
||||
{{end}}
|
||||
</div>
|
||||
<div class="column content pt-1 pb-0">
|
||||
<a href="/forum/thread/{{.ID}}">
|
||||
|
|
|
@ -133,12 +133,21 @@
|
|||
<div class="box has-background-link-light has-text-dark" id="p{{.ID}}">
|
||||
<div class="columns">
|
||||
<div class="column is-2 has-text-centered">
|
||||
<div>
|
||||
<a href="/u/{{$c.User.Username}}">
|
||||
{{template "avatar-96x96" $c.User}}
|
||||
</a>
|
||||
</div>
|
||||
<a href="/u/{{$c.User.Username}}">{{$c.User.NameOrUsername}}</a>
|
||||
<!-- Thread starter is blocked? -->
|
||||
{{if .User.UserRelationship.IsBlocked}}
|
||||
<div>
|
||||
{{template "avatar-64x64"}}
|
||||
</div>
|
||||
[unavailable]
|
||||
{{else}}
|
||||
<div>
|
||||
<a href="/u/{{$c.User.Username}}">
|
||||
{{template "avatar-96x96" $c.User}}
|
||||
</a>
|
||||
</div>
|
||||
<a href="/u/{{$c.User.Username}}">{{$c.User.NameOrUsername}}</a>
|
||||
{{end}}
|
||||
|
||||
{{if $c.User.IsAdmin}}
|
||||
<div class="is-size-7 mt-1">
|
||||
<span class="tag is-danger is-light">
|
||||
|
|
Loading…
Reference in New Issue
Block a user