package models // Supplementary functions to do with Shy Accounts and PublicAvatar consent. 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] } // 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 }