Noah
93c13882aa
Finish implementing the basic forum features: * Pinned threads (admin or board owner only) * Edit Thread settings when you edit the top-most comment. * NoReply threads remove all the reply buttons. * Explicit forums and threads are filtered out unless opted-in (admins always see them). * Count the unique members who participated in each forum. * Get the most recently updated thread to show on forum list page. * Contact/Report page: handle receiving a comment ID to report on. Implement Likes & Notifications * Like buttons added to Photos and Profile Pages. Implemented via simple vanilla JS (likes.js) to make ajax requests to back-end to like/unlike. * Notifications: for your photo or profile being liked. If you unlike, the existing notifications about the like are revoked. * The notifications appear as an alert number in the nav bar and are read on the User Dashboard. Click to mark a notification as "read" or click the "mark all as read" button. Update DeleteUser to scrub likes, notifications, threads, and comments.
225 lines
5.4 KiB
Go
225 lines
5.4 KiB
Go
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
|
|
)
|
|
|
|
// PhotoVisibility preset settings.
|
|
var (
|
|
PhotoVisibilityAll = []PhotoVisibility{
|
|
PhotoPublic,
|
|
PhotoFriends,
|
|
PhotoPrivate,
|
|
}
|
|
|
|
// Site Gallery visibility for when your friends show up in the gallery.
|
|
// Or: "Friends + Gallery" photos can appear to your friends in the Site Gallery.
|
|
PhotoVisibilityFriends = []string{
|
|
string(PhotoPublic),
|
|
string(PhotoFriends),
|
|
}
|
|
)
|
|
|
|
// 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
|
|
}
|
|
|
|
// GetPhotos by an array of IDs, mapped to their IDs.
|
|
func GetPhotos(IDs []uint64) (map[uint64]*Photo, error) {
|
|
var (
|
|
mp = map[uint64]*Photo{}
|
|
ps = []*Photo{}
|
|
)
|
|
|
|
result := DB.Model(&Photo{}).Where("id IN ?", IDs).Find(&ps)
|
|
for _, row := range ps {
|
|
mp[row.ID] = row
|
|
}
|
|
|
|
return mp, 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
|
|
}
|
|
|
|
// CountPhotos returns the total number of photos on a user's account.
|
|
func CountPhotos(userID uint64) (int64, error) {
|
|
var count int64
|
|
result := DB.Where(
|
|
"user_id = ?",
|
|
userID,
|
|
).Model(&Photo{}).Count(&count)
|
|
return count, 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)
|
|
friendIDs = FriendIDs(userID)
|
|
wheres = []string{}
|
|
placeholders = []interface{}{}
|
|
)
|
|
|
|
// Include ourself in our friend IDs.
|
|
friendIDs = append(friendIDs, userID)
|
|
|
|
// You can see friends' Friend photos but only public for non-friends.
|
|
wheres = append(wheres,
|
|
"(user_id IN ? AND visibility IN ?) OR (user_id NOT IN ? AND visibility = ?)",
|
|
)
|
|
placeholders = append(placeholders,
|
|
friendIDs, PhotoVisibilityFriends, friendIDs, PhotoPublic,
|
|
)
|
|
|
|
// Gallery photos only.
|
|
wheres = append(wheres, "gallery = ?")
|
|
placeholders = append(placeholders, 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)",
|
|
)
|
|
|
|
// Exclude private users' photos.
|
|
wheres = append(wheres,
|
|
"NOT EXISTS (SELECT 1 FROM users WHERE id = photos.user_id AND visibility = 'private')",
|
|
)
|
|
|
|
// 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
|
|
}
|