2022-09-08 04:18:54 +00:00
package models
import (
2024-10-19 19:44:47 +00:00
"errors"
2023-08-05 01:54:04 +00:00
"fmt"
2022-09-08 04:18:54 +00:00
"strings"
"time"
2023-08-05 01:54:04 +00:00
"code.nonshy.com/nonshy/website/pkg/log"
2022-09-08 04:18:54 +00:00
"gorm.io/gorm"
)
// PrivatePhoto table to track who you have unlocked your private photos for.
type PrivatePhoto struct {
ID uint64 ` gorm:"primaryKey" `
SourceUserID uint64 ` gorm:"index" ` // the owner of a photo
TargetUserID uint64 ` gorm:"index" ` // the receiver
CreatedAt time . Time
UpdatedAt time . Time
}
// UnlockPrivatePhotos is sourceUserId allowing targetUserId to see their private photos.
func UnlockPrivatePhotos ( sourceUserID , targetUserID uint64 ) error {
// Did we already allow this user?
var pb * PrivatePhoto
exist := DB . Where (
"source_user_id = ? AND target_user_id = ?" ,
sourceUserID , targetUserID ,
) . First ( & pb ) . Error
// Update existing.
if exist == nil {
return nil
}
// Create the PrivatePhoto.
pb = & PrivatePhoto {
SourceUserID : sourceUserID ,
TargetUserID : targetUserID ,
}
return DB . Create ( pb ) . Error
}
// RevokePrivatePhotos is sourceUserId revoking targetUserId to see their private photos.
func RevokePrivatePhotos ( sourceUserID , targetUserID uint64 ) error {
result := DB . Where (
"source_user_id = ? AND target_user_id = ?" ,
sourceUserID , targetUserID ,
) . Delete ( & PrivatePhoto { } )
return result . Error
}
// RevokePrivatePhotosAll is sourceUserId revoking ALL USERS from their private photos.
func RevokePrivatePhotosAll ( sourceUserID uint64 ) error {
result := DB . Where (
"source_user_id = ?" ,
sourceUserID ,
) . Delete ( & PrivatePhoto { } )
return result . Error
}
2023-08-05 01:54:04 +00:00
// RevokePrivatePhotoNotifications removes notifications about newly uploaded private photos
// that were sent to one (or multiple) members when the user revokes their access later. Pass
// a nil fromUserID to revoke the photo upload notifications from ALL users.
2024-01-11 06:25:50 +00:00
func RevokePrivatePhotoNotifications ( currentUser , fromUser * User ) error {
2023-08-05 01:54:04 +00:00
// Gather the IDs of all our private photos to nuke notifications for.
photoIDs , err := currentUser . AllPrivatePhotoIDs ( )
if err != nil {
return err
} else if len ( photoIDs ) == 0 {
// Nothing to do.
return nil
}
// Who to clear the notifications for?
2024-01-11 06:25:50 +00:00
if fromUser == nil {
2023-08-05 01:54:04 +00:00
log . Info ( "RevokePrivatePhotoNotifications(%s): forget about private photo uploads for EVERYBODY on photo IDs: %v" , currentUser . Username , photoIDs )
return RemoveNotificationBulk ( "photos" , photoIDs )
} else {
2024-01-11 06:25:50 +00:00
log . Info ( "RevokePrivatePhotoNotifications(%s): forget about private photo uploads for user %s on photo IDs: %v" , currentUser . Username , fromUser . Username , photoIDs )
return RemoveSpecificNotificationBulk ( [ ] * User { currentUser , fromUser } , NotificationNewPhoto , "photos" , photoIDs )
2023-08-05 01:54:04 +00:00
}
}
// AllPrivatePhotoIDs returns the listing of all IDs of the user's private photos.
func ( u * User ) AllPrivatePhotoIDs ( ) ( [ ] uint64 , error ) {
var photoIDs = [ ] uint64 { }
err := DB . Table (
"photos" ,
) . Select (
"photos.id AS id" ,
) . Where (
"user_id = ? AND visibility = ?" ,
u . ID , PhotoPrivate ,
) . Scan ( & photoIDs )
if err . Error != nil {
return photoIDs , fmt . Errorf ( "AllPrivatePhotoIDs(%s): %s" , u . Username , err . Error )
}
return photoIDs , nil
}
2023-09-16 20:46:26 +00:00
// AllPhotoIDs returns the listing of all IDs of the user's photos.
func ( u * User ) AllPhotoIDs ( ) ( [ ] uint64 , error ) {
2023-10-24 02:05:34 +00:00
if u . cachePhotoIDs != nil {
return u . cachePhotoIDs , nil
}
2023-09-16 20:46:26 +00:00
var photoIDs = [ ] uint64 { }
err := DB . Table (
"photos" ,
) . Select (
"photos.id AS id" ,
) . Where (
"user_id = ?" ,
u . ID ,
) . Scan ( & photoIDs )
if err . Error != nil {
return photoIDs , fmt . Errorf ( "AllPhotoIDs(%s): %s" , u . Username , err . Error )
}
2023-10-24 02:05:34 +00:00
u . cachePhotoIDs = photoIDs
2023-09-16 20:46:26 +00:00
return photoIDs , nil
}
2024-10-19 19:44:47 +00:00
/ *
ShouldShowPrivateUnlockPrompt determines whether the current user should be shown a prompt , when viewing
the other user ' s gallery , to unlock their private photos for that user .
This function verifies that the source user actually has a private photo to share , and that the target
user doesn ' t have a privacy setting enabled that should block the private photo unlock request .
* /
func ShouldShowPrivateUnlockPrompt ( sourceUser , targetUser * User ) ( bool , error ) {
// If the current user doesn't even have a private photo to share, no prompt.
if CountUserPhotosByVisibility ( sourceUser . ID , PhotoPrivate ) == 0 {
return false , errors . New ( "you do not currently have a private photo on your gallery to share" )
}
// Does the target user have a privacy setting enabled?
if pp := targetUser . GetProfileField ( "private_photo_gate" ) ; pp != "" {
areFriends := AreFriends ( sourceUser . ID , targetUser . ID )
switch pp {
case "nobody" :
return false , errors . New ( "they decline all private photo sharing" )
case "friends" :
if areFriends {
return true , nil
}
return false , errors . New ( "they are only accepting private photos from their friends" )
case "messaged" :
if areFriends || HasSentAMessage ( targetUser , sourceUser ) {
return true , nil
}
return false , errors . New ( "they are only accepting private photos from their friends or from people they have sent a DM to" )
}
}
return true , nil
}
2022-09-08 04:18:54 +00:00
// IsPrivateUnlocked quickly sees if sourceUserID has unlocked private photos for targetUserID to see.
func IsPrivateUnlocked ( sourceUserID , targetUserID uint64 ) bool {
pb := & PrivatePhoto { }
result := DB . Where (
"source_user_id = ? AND target_user_id = ?" ,
sourceUserID , targetUserID ,
) . First ( & pb )
return result . Error == nil
}
// CountPrivateGrantee returns how many users have granted you access to their private photos.
func CountPrivateGrantee ( userID uint64 ) int64 {
var count int64
DB . Model ( & PrivatePhoto { } ) . Where (
"target_user_id = ?" ,
userID ,
) . Count ( & count )
return count
}
// PrivateGrantedUserIDs returns all user IDs who have granted access for userId to see their private photos.
func PrivateGrantedUserIDs ( userId uint64 ) [ ] uint64 {
var (
ps = [ ] * PrivatePhoto { }
userIDs = [ ] uint64 { userId }
)
DB . Where ( "target_user_id = ?" , userId ) . Find ( & ps )
for _ , row := range ps {
userIDs = append ( userIDs , row . SourceUserID )
}
return userIDs
}
2023-10-26 03:07:28 +00:00
// PrivateGrantedUserIDsAreFriends returns user IDs who have granted us their private pictures, and are also our friends.
func PrivateGrantedUserIDsAreFriends ( currentUser * User ) [ ] uint64 {
var (
ps = [ ] * PrivatePhoto { }
userIDs = [ ] uint64 { }
)
DB . Model ( & PrivatePhoto { } ) . Joins (
"JOIN friends ON friends.source_user_id = private_photos.source_user_id AND friends.target_user_id = private_photos.target_user_id" ,
) . Where (
"private_photos.target_user_id = ? AND friends.approved IS true" , currentUser . ID ,
) . Find ( & ps )
for _ , row := range ps {
userIDs = append ( userIDs , row . SourceUserID )
}
return userIDs
}
2023-03-17 03:04:43 +00:00
// PrivateGranteeUserIDs are the users whom WE have granted access to our photos (userId is the photo owners).
func PrivateGranteeUserIDs ( userId uint64 ) [ ] uint64 {
var (
ps = [ ] * PrivatePhoto { }
userIDs = [ ] uint64 { }
)
DB . Where ( "source_user_id = ?" , userId ) . Find ( & ps )
for _ , row := range ps {
userIDs = append ( userIDs , row . TargetUserID )
}
return userIDs
}
2023-08-05 01:54:04 +00:00
// PrivateGranteeAreExplicitUserIDs gets your private photo grantees who have opted-in to see explicit content.
func PrivateGranteeAreExplicitUserIDs ( userId uint64 ) [ ] uint64 {
var (
userIDs = [ ] uint64 { }
)
err := DB . Table (
"private_photos" ,
) . Joins (
"JOIN users ON (users.id = private_photos.target_user_id)" ,
) . Select (
"private_photos.target_user_id AS user_id" ,
) . Where (
"source_user_id = ? AND users.explicit IS TRUE" ,
userId ,
) . Scan ( & userIDs )
if err . Error != nil {
log . Error ( "PrivateGranteeAreExplicitUserIDs: %s" , err . Error )
}
return userIDs
}
2022-09-08 04:18:54 +00:00
/ *
PaginatePrivatePhotoList views a user ' s list of private photo grants .
If grantee is true , it returns the list of users who have granted YOU access to see THEIR
private photos . If grantee is false , it returns the users that YOU have granted access to
see YOUR OWN private photos .
* /
2022-09-09 04:42:20 +00:00
func PaginatePrivatePhotoList ( user * User , grantee bool , pager * Pagination ) ( [ ] * User , error ) {
2022-09-08 04:18:54 +00:00
var (
pbs = [ ] * PrivatePhoto { }
userIDs = [ ] uint64 { }
query * gorm . DB
wheres = [ ] string { }
placeholders = [ ] interface { } { }
2024-08-29 01:42:49 +00:00
blocklist = BlockedUserIDs ( user )
// Column name of "other user" depending on direction
otherUserColumn string
2022-09-08 04:18:54 +00:00
)
// Which direction are we going?
if grantee {
// Return the private photo grants for whom YOU are the recipient.
wheres = append ( wheres , "target_user_id = ?" )
2022-09-09 04:42:20 +00:00
placeholders = append ( placeholders , user . ID )
2024-08-29 01:42:49 +00:00
otherUserColumn = "source_user_id"
2022-09-08 04:18:54 +00:00
} else {
// Return the users that YOU have granted access to YOUR private pictures.
wheres = append ( wheres , "source_user_id = ?" )
2022-09-09 04:42:20 +00:00
placeholders = append ( placeholders , user . ID )
2024-08-29 01:42:49 +00:00
otherUserColumn = "target_user_id"
2022-09-08 04:18:54 +00:00
}
2024-08-29 01:42:49 +00:00
// 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 ,
) ,
2023-09-02 05:41:33 +00:00
)
2024-08-29 01:42:49 +00:00
// Filter blocked users.
if len ( blocklist ) > 0 {
wheres = append ( wheres , fmt . Sprintf ( "%s NOT IN ?" , otherUserColumn ) )
placeholders = append ( placeholders , blocklist )
}
2022-09-08 04:18:54 +00:00
query = DB . Where (
strings . Join ( wheres , " AND " ) ,
placeholders ... ,
)
query = query . Order ( pager . Sort )
query . Model ( & PrivatePhoto { } ) . Count ( & pager . Total )
result := query . Offset ( pager . GetOffset ( ) ) . Limit ( pager . PerPage ) . Find ( & pbs )
if result . Error != nil {
return nil , result . Error
}
// Now of these user IDs get their User objects.
for _ , b := range pbs {
if grantee {
userIDs = append ( userIDs , b . SourceUserID )
} else {
userIDs = append ( userIDs , b . TargetUserID )
}
}
2022-09-09 04:42:20 +00:00
return GetUsers ( user , userIDs )
2022-09-08 04:18:54 +00:00
}
// Save photo.
func ( pb * PrivatePhoto ) Save ( ) error {
result := DB . Save ( pb )
return result . Error
}
2023-09-02 05:27:18 +00:00
// PrivateGranteeMap maps user IDs to whether they have granted you their private photos.
type PrivateGranteeMap map [ uint64 ] bool
2023-09-02 06:07:15 +00:00
// MapPrivatePhotoGrantee looks up a set of user IDs in bulk and returns a PrivateGranteeMap suitable for templates.
2023-09-02 05:27:18 +00:00
func MapPrivatePhotoGrantee ( currentUser * User , users [ ] * User ) PrivateGranteeMap {
var (
2023-09-02 05:41:33 +00:00
usermap = PrivateGranteeMap { }
set = map [ uint64 ] interface { } { }
distinct = [ ] uint64 { }
2023-09-02 05:27:18 +00:00
)
2023-09-02 05:41:33 +00:00
// Uniqueify users.
for _ , user := range users {
if _ , ok := set [ user . ID ] ; ok {
continue
}
set [ user . ID ] = nil
distinct = append ( distinct , user . ID )
}
2023-09-02 05:27:18 +00:00
var (
matched = [ ] * PrivatePhoto { }
result = DB . Model ( & PrivatePhoto { } ) .
2023-09-02 05:41:33 +00:00
Where ( "target_user_id = ? AND source_user_id IN ?" , currentUser . ID , distinct ) .
2023-09-02 05:27:18 +00:00
Find ( & matched )
)
if result . Error == nil {
for _ , row := range matched {
usermap [ row . SourceUserID ] = true
}
}
return usermap
}
// Get a user from the PrivateGranteeMap.
func ( um PrivateGranteeMap ) Get ( id uint64 ) bool {
return um [ id ]
}
2023-09-02 06:07:15 +00:00
// PrivateGrantedMap maps user IDs to whether we have granted our private pictures to them.
type PrivateGrantedMap map [ uint64 ] bool
// MapPrivatePhotoGranted looks up a set of user IDs in bulk and returns a PrivateGrantedMap suitable for templates.
func MapPrivatePhotoGranted ( currentUser * User , users [ ] * User ) PrivateGrantedMap {
var (
usermap = PrivateGrantedMap { }
set = map [ uint64 ] interface { } { }
distinct = [ ] uint64 { }
)
// Uniqueify users.
for _ , user := range users {
if _ , ok := set [ user . ID ] ; ok {
continue
}
set [ user . ID ] = nil
distinct = append ( distinct , user . ID )
}
var (
matched = [ ] * PrivatePhoto { }
result = DB . Model ( & PrivatePhoto { } ) .
Where ( "source_user_id = ? AND target_user_id IN ?" , currentUser . ID , distinct ) .
Find ( & matched )
)
if result . Error == nil {
for _ , row := range matched {
usermap [ row . TargetUserID ] = true
}
}
return usermap
}
// Get a user from the PrivateGrantedMap.
func ( um PrivateGrantedMap ) Get ( id uint64 ) bool {
return um [ id ]
}