2023-09-02 00:12:27 +00:00
|
|
|
package models
|
|
|
|
|
2024-06-29 23:44:18 +00:00
|
|
|
// Supplementary functions to do with Shy Accounts and PublicAvatar consent.
|
2023-09-02 00:12:27 +00:00
|
|
|
|
|
|
|
import "code.nonshy.com/nonshy/website/pkg/log"
|
|
|
|
|
|
|
|
// IsShy returns whether the user might have an "empty" profile from the perspective of anybody.
|
|
|
|
//
|
|
|
|
// An empty profile means their profile is Private or else ALL of their photos are non-public; so that
|
|
|
|
// somebody viewing their page might see nothing at all from them and consider them a "blank" profile.
|
|
|
|
func (u *User) IsShy() bool {
|
|
|
|
// NOTE: if you change the logic for Shy Accounts, also align your changes
|
|
|
|
// in the functions: WhereClauseShyAccounts.
|
|
|
|
|
|
|
|
// Non-certified users are considered empty.
|
|
|
|
if !u.Certified {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
|
|
|
|
// Private profile automatically applies.
|
|
|
|
if u.Visibility == UserVisibilityPrivate {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
|
|
|
|
// If ALL of our photos are non-public, that counts too.
|
|
|
|
var photoTypes = u.DistinctPhotoTypes()
|
|
|
|
if _, ok := photoTypes[PhotoPublic]; !ok {
|
|
|
|
log.Info("IsEmptyProfile: true because visibilities %+v did not include public", photoTypes)
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
|
|
|
// WhereClauseShyAccounts returns SQL query fragments when running User table queries
|
|
|
|
// that will filter for shy accounts.
|
|
|
|
//
|
|
|
|
// This is used by SearchUsers and MapShyAccounts.
|
|
|
|
func WhereClauseShyAccounts() (where string, placeholders []interface{}) {
|
|
|
|
where = `(
|
|
|
|
certified IS NOT true
|
|
|
|
OR visibility = ?
|
|
|
|
OR NOT EXISTS (
|
|
|
|
SELECT 1 FROM photos
|
|
|
|
WHERE user_id = users.id
|
|
|
|
AND visibility = ?
|
|
|
|
)
|
|
|
|
)`
|
|
|
|
placeholders = []interface{}{
|
|
|
|
UserVisibilityPrivate,
|
|
|
|
PhotoPublic,
|
|
|
|
}
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
// ShyMap maps user IDs to Shy Account status in bulk queries.
|
|
|
|
type ShyMap map[uint64]bool
|
|
|
|
|
|
|
|
// MapShyAccounts looks up a set of user IDs in bulk and returns a ShyMap suitable for templates.
|
|
|
|
func MapShyAccounts(users []*User) ShyMap {
|
|
|
|
var (
|
|
|
|
usermap = ShyMap{}
|
|
|
|
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 = []*User{}
|
|
|
|
where, placeholders = WhereClauseShyAccounts()
|
|
|
|
result = (&User{}).
|
|
|
|
Preload().
|
|
|
|
Where("id IN ?", distinct).
|
|
|
|
Where(where, placeholders...).
|
|
|
|
Find(&matched)
|
|
|
|
)
|
|
|
|
|
|
|
|
if result.Error == nil {
|
|
|
|
for _, row := range matched {
|
|
|
|
usermap[row.ID] = true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return usermap
|
|
|
|
}
|
|
|
|
|
|
|
|
// Get a user from the ShyMap.
|
|
|
|
func (um ShyMap) Get(id uint64) bool {
|
|
|
|
return um[id]
|
|
|
|
}
|
2024-06-29 23:44:18 +00:00
|
|
|
|
2024-06-30 02:28:51 +00:00
|
|
|
// NeedsPublicAvatarConsent returns true if the user will be impacted by the Public Avatar policy.
|
|
|
|
//
|
|
|
|
// That is, their default profile picture is non-public and they have not answered the consent form.
|
|
|
|
func (u *User) NeedsPublicAvatarConsent() bool {
|
|
|
|
return u.ProfilePhoto.ID > 0 && u.ProfilePhoto.Visibility != PhotoPublic && u.GetProfileField("public_avatar_consent") != "true"
|
|
|
|
}
|
|
|
|
|
2024-06-29 23:44:18 +00:00
|
|
|
// MapPublicAvatarConsent checks a set of users if they have answered the public avatar consent form.
|
|
|
|
func MapPublicAvatarConsent(users []*User) (map[uint64]bool, error) {
|
|
|
|
var (
|
|
|
|
userIDs = []uint64{}
|
|
|
|
result = map[uint64]bool{}
|
|
|
|
)
|
|
|
|
for _, user := range users {
|
|
|
|
userIDs = append(userIDs, user.ID)
|
|
|
|
result[user.ID] = false
|
|
|
|
}
|
|
|
|
|
|
|
|
type record struct {
|
|
|
|
UserID uint64
|
|
|
|
Value string
|
|
|
|
}
|
|
|
|
var rows = []record{}
|
|
|
|
|
|
|
|
if res := DB.Table(
|
|
|
|
"profile_fields",
|
|
|
|
).Select(
|
|
|
|
"user_id, value",
|
|
|
|
).Where(
|
|
|
|
"user_id IN ? AND name='public_avatar_consent'",
|
|
|
|
userIDs,
|
|
|
|
).Scan(&rows); res.Error != nil {
|
|
|
|
return nil, res.Error
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, row := range rows {
|
|
|
|
result[row.UserID] = row.Value == "true"
|
|
|
|
}
|
|
|
|
|
|
|
|
return result, nil
|
|
|
|
}
|