package models import ( "errors" "strings" "time" "gorm.io/gorm" ) // Photo table. type Photo struct { ID uint64 `gorm:"primaryKey"` UserID uint64 `gorm:"index"` Filename string CroppedFilename string // if cropped, e.g. for profile photo Filesize int64 Caption string Flagged bool // photo has been reported by the community Visibility PhotoVisibility Gallery bool // photo appears in the public gallery (if public) Explicit bool // is an explicit photo CreatedAt time.Time UpdatedAt time.Time } // PhotoVisibility settings. type PhotoVisibility string const ( PhotoPublic PhotoVisibility = "public" // on profile page and/or public gallery PhotoFriends = "friends" // only friends can see it PhotoPrivate = "private" // private ) var PhotoVisibilityAll = []PhotoVisibility{ PhotoPublic, PhotoFriends, PhotoPrivate, } // CreatePhoto with most of the settings you want (not ID or timestamps) in the database. func CreatePhoto(tmpl Photo) (*Photo, error) { if tmpl.UserID == 0 { return nil, errors.New("UserID required") } p := &Photo{ UserID: tmpl.UserID, Filename: tmpl.Filename, CroppedFilename: tmpl.CroppedFilename, Caption: tmpl.Caption, Visibility: tmpl.Visibility, Gallery: tmpl.Gallery, Explicit: tmpl.Explicit, } result := DB.Create(p) return p, result.Error } // GetPhoto by ID. func GetPhoto(id uint64) (*Photo, error) { p := &Photo{} result := DB.First(&p, id) return p, result.Error } /* PaginateUserPhotos gets a page of photos belonging to a user ID. */ func PaginateUserPhotos(userID uint64, visibility []PhotoVisibility, explicitOK bool, pager *Pagination) ([]*Photo, error) { var p = []*Photo{} var explicit = []bool{false} if explicitOK { explicit = []bool{true, false} } query := DB.Where( "user_id = ? AND visibility IN ? AND explicit IN ?", userID, visibility, explicit, ).Order( pager.Sort, ) // Get the total count. query.Model(&Photo{}).Count(&pager.Total) result := query.Offset( pager.GetOffset(), ).Limit(pager.PerPage).Find(&p) return p, result.Error } // CountExplicitPhotos returns the number of explicit photos a user has (so non-explicit viewers can see some do exist) func CountExplicitPhotos(userID uint64, visibility []PhotoVisibility) (int64, error) { query := DB.Where( "user_id = ? AND visibility IN ? AND explicit = ?", userID, visibility, true, ) var count int64 result := query.Model(&Photo{}).Count(&count) return count, result.Error } // PaginateGalleryPhotos gets a page of all public user photos for the site gallery. Admin view // returns ALL photos regardless of Gallery status. func PaginateGalleryPhotos(userID uint64, adminView bool, explicitOK bool, pager *Pagination) ([]*Photo, error) { var ( p = []*Photo{} query *gorm.DB blocklist = BlockedUserIDs(userID) wheres = []string{} placeholders = []interface{}{} ) // Universal filters: public + gallery photos only. wheres = append(wheres, "visibility = ?", "gallery = ?") placeholders = append(placeholders, PhotoPublic, true) // Filter blocked users. if len(blocklist) > 0 { wheres = append(wheres, "user_id NOT IN ?") placeholders = append(placeholders, blocklist) } // Non-explicit pics unless the user opted in. if !explicitOK { wheres = append(wheres, "explicit = ?") placeholders = append(placeholders, false) } // Only certified user photos. wheres = append(wheres, "EXISTS (SELECT 1 FROM users WHERE id = photos.user_id AND certified = true)", ) // Admin view: get ALL PHOTOS on the site, period. if adminView { query = DB } else { query = DB.Where( strings.Join(wheres, " AND "), placeholders..., ) } query = query.Order(pager.Sort) query.Model(&Photo{}).Count(&pager.Total) result := query.Offset(pager.GetOffset()).Limit(pager.PerPage).Find(&p) return p, result.Error } // Save photo. func (p *Photo) Save() error { result := DB.Save(p) return result.Error } // Delete photo. func (p *Photo) Delete() error { result := DB.Delete(p) return result.Error }