481bd0ae61
* Add a way for users to temporarily deactivate their accounts, in a recoverable way should they decide to return later. * A deactivated account may log in but have limited options: to reactivate their account, permanently delete it, or log out. * Fix several bugs around the display of comments, messages and forum threads for disabled, banned, or blocked users: * Messages (inbox and sentbox) will be hidden and the unread indicator will not count unread messages the user can't access. * Comments on photos and forum posts are hidden, and top-level threads on the "Newest" tab will show "[unavailable]" for their text and username. * Your historical notifications will hide users who are blocked, banned or disabled. * Add a "Friends" tab to user profile pages, to see other users' friends. * The page is Certification Required so non-cert users can't easily discover any members on the site.
206 lines
4.8 KiB
Go
206 lines
4.8 KiB
Go
package models
|
|
|
|
import (
|
|
"time"
|
|
|
|
"code.nonshy.com/nonshy/website/pkg/log"
|
|
"gorm.io/gorm"
|
|
)
|
|
|
|
// Block table.
|
|
type Block struct {
|
|
ID uint64 `gorm:"primaryKey"`
|
|
SourceUserID uint64 `gorm:"index"`
|
|
TargetUserID uint64 `gorm:"index"`
|
|
CreatedAt time.Time
|
|
UpdatedAt time.Time
|
|
}
|
|
|
|
// AddBlock is sourceUserId adding targetUserId to their block list.
|
|
func AddBlock(sourceUserID, targetUserID uint64) error {
|
|
// Unfriend in the process.
|
|
RemoveFriend(sourceUserID, targetUserID)
|
|
|
|
// Did we already block this user?
|
|
var b *Block
|
|
forward := DB.Where(
|
|
"source_user_id = ? AND target_user_id = ?",
|
|
sourceUserID, targetUserID,
|
|
).First(&b).Error
|
|
|
|
// Update existing.
|
|
if forward == nil {
|
|
return nil
|
|
}
|
|
|
|
// Create the block.
|
|
b = &Block{
|
|
SourceUserID: sourceUserID,
|
|
TargetUserID: targetUserID,
|
|
}
|
|
return DB.Create(b).Error
|
|
}
|
|
|
|
// IsBlocking quickly sees if either user blocks the other.
|
|
func IsBlocking(sourceUserID, targetUserID uint64) bool {
|
|
b := &Block{}
|
|
result := DB.Where(
|
|
"(source_user_id = ? AND target_user_id = ?) OR "+
|
|
"(target_user_id = ? AND source_user_id = ?)",
|
|
sourceUserID, targetUserID,
|
|
sourceUserID, targetUserID,
|
|
).First(&b)
|
|
return result.Error == nil
|
|
}
|
|
|
|
// IsBlocked quickly checks if sourceUserID currently blocks targetUserID.
|
|
func IsBlocked(sourceUserID, targetUserID uint64) bool {
|
|
b := &Block{}
|
|
result := DB.Where(
|
|
"source_user_id = ? AND target_user_id = ?",
|
|
sourceUserID, targetUserID,
|
|
).First(&b)
|
|
return result.Error == nil
|
|
}
|
|
|
|
// PaginateBlockList views a user's blocklist.
|
|
func PaginateBlockList(user *User, pager *Pagination) ([]*User, error) {
|
|
// We paginate over the Block table.
|
|
var (
|
|
bs = []*Block{}
|
|
userIDs = []uint64{}
|
|
query *gorm.DB
|
|
)
|
|
|
|
query = DB.Where(
|
|
"source_user_id = ?",
|
|
user.ID,
|
|
)
|
|
|
|
query = query.Order(pager.Sort)
|
|
query.Model(&Block{}).Count(&pager.Total)
|
|
result := query.Offset(pager.GetOffset()).Limit(pager.PerPage).Find(&bs)
|
|
if result.Error != nil {
|
|
return nil, result.Error
|
|
}
|
|
|
|
// Now of these friends get their User objects.
|
|
for _, b := range bs {
|
|
userIDs = append(userIDs, b.TargetUserID)
|
|
}
|
|
|
|
return GetUsers(user, userIDs)
|
|
}
|
|
|
|
// BlockedUserIDs returns all user IDs blocked by the user (bidirectional, source or target user).
|
|
func BlockedUserIDs(user *User) []uint64 {
|
|
// Have we looked this up already on this request?
|
|
if user.cacheBlockedUserIDs != nil {
|
|
return user.cacheBlockedUserIDs
|
|
}
|
|
|
|
var (
|
|
bs = []*Block{}
|
|
userIDs = []uint64{}
|
|
)
|
|
DB.Where("source_user_id = ? OR target_user_id = ?", user.ID, user.ID).Find(&bs)
|
|
for _, row := range bs {
|
|
for _, uid := range []uint64{row.TargetUserID, row.SourceUserID} {
|
|
if uid != user.ID {
|
|
userIDs = append(userIDs, uid)
|
|
}
|
|
}
|
|
}
|
|
|
|
// Cache the result in the User so we don't query it again.
|
|
user.cacheBlockedUserIDs = userIDs
|
|
|
|
return userIDs
|
|
}
|
|
|
|
// MapBlockedUserIDs returns BlockedUserIDs as a lookup hashmap (not for front-end templates currently).
|
|
func MapBlockedUserIDs(user *User) map[uint64]interface{} {
|
|
var (
|
|
result = map[uint64]interface{}{}
|
|
blockedIDs = BlockedUserIDs(user)
|
|
)
|
|
for _, uid := range blockedIDs {
|
|
result[uid] = nil
|
|
}
|
|
return result
|
|
}
|
|
|
|
// FilterBlockingUserIDs narrows down a set of User IDs to remove ones that block (or are blocked by) the current user.
|
|
func FilterBlockingUserIDs(currentUser *User, userIDs []uint64) []uint64 {
|
|
var (
|
|
// Get the IDs to exclude.
|
|
blockedIDs = MapBlockedUserIDs(currentUser)
|
|
|
|
// Filter the result.
|
|
result = []uint64{}
|
|
)
|
|
for _, uid := range userIDs {
|
|
if _, ok := blockedIDs[uid]; ok {
|
|
continue
|
|
}
|
|
result = append(result, uid)
|
|
}
|
|
return result
|
|
}
|
|
|
|
// BlockedUserIDsByUser returns all user IDs blocked by the user (one directional only)
|
|
func BlockedUserIDsByUser(userId uint64) []uint64 {
|
|
var (
|
|
bs = []*Block{}
|
|
userIDs = []uint64{}
|
|
)
|
|
DB.Where("source_user_id = ?", userId).Find(&bs)
|
|
for _, row := range bs {
|
|
for _, uid := range []uint64{row.TargetUserID, row.SourceUserID} {
|
|
if uid != userId {
|
|
userIDs = append(userIDs, uid)
|
|
}
|
|
}
|
|
}
|
|
return userIDs
|
|
}
|
|
|
|
// BlockedUsernames returns all usernames blocked by (or blocking) the user.
|
|
func BlockedUsernames(user *User) []string {
|
|
var (
|
|
userIDs = BlockedUserIDs(user)
|
|
usernames = []string{}
|
|
)
|
|
|
|
if len(userIDs) == 0 {
|
|
return usernames
|
|
}
|
|
|
|
if res := DB.Table(
|
|
"users",
|
|
).Select(
|
|
"username",
|
|
).Where(
|
|
"id IN ?", userIDs,
|
|
).Scan(&usernames); res.Error != nil {
|
|
log.Error("BlockedUsernames(%d): %s", user.Username, res.Error)
|
|
}
|
|
|
|
return usernames
|
|
}
|
|
|
|
// UnblockUser removes targetUserID from your blocklist.
|
|
func UnblockUser(sourceUserID, targetUserID uint64) error {
|
|
result := DB.Where(
|
|
"source_user_id = ? AND target_user_id = ?",
|
|
sourceUserID, targetUserID,
|
|
).Delete(&Block{})
|
|
return result.Error
|
|
}
|
|
|
|
// Save photo.
|
|
func (b *Block) Save() error {
|
|
result := DB.Save(b)
|
|
return result.Error
|
|
}
|