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.
162 lines
3.4 KiB
Go
162 lines
3.4 KiB
Go
package models
|
|
|
|
import (
|
|
"errors"
|
|
"strings"
|
|
"time"
|
|
|
|
"gorm.io/gorm"
|
|
)
|
|
|
|
// Forum table.
|
|
type Forum struct {
|
|
ID uint64 `gorm:"primaryKey"`
|
|
OwnerID uint64 `gorm:"index"`
|
|
Owner User `gorm:"foreignKey:owner_id"`
|
|
Category string `gorm:"index"`
|
|
Fragment string `gorm:"uniqueIndex"`
|
|
Title string
|
|
Description string
|
|
Explicit bool `gorm:"index"`
|
|
Privileged bool
|
|
PermitPhotos bool
|
|
CreatedAt time.Time
|
|
UpdatedAt time.Time
|
|
}
|
|
|
|
// Preload related tables for the forum (classmethod).
|
|
func (f *Forum) Preload() *gorm.DB {
|
|
return DB.Preload("Owner")
|
|
}
|
|
|
|
// GetForum by ID.
|
|
func GetForum(id uint64) (*Forum, error) {
|
|
forum := &Forum{}
|
|
result := forum.Preload().First(&forum, id)
|
|
return forum, result.Error
|
|
}
|
|
|
|
// ForumByFragment looks up a forum by its URL fragment.
|
|
func ForumByFragment(fragment string) (*Forum, error) {
|
|
if fragment == "" {
|
|
return nil, errors.New("the URL fragment is required")
|
|
}
|
|
|
|
var (
|
|
f = &Forum{}
|
|
result = f.Preload().Where(
|
|
"fragment = ?",
|
|
fragment,
|
|
).First(&f)
|
|
)
|
|
|
|
return f, result.Error
|
|
}
|
|
|
|
/*
|
|
PaginateForums scans over the available forums for a user.
|
|
|
|
Parameters:
|
|
|
|
- userID: of who is looking
|
|
- categories: optional, filter within categories
|
|
- pager
|
|
*/
|
|
func PaginateForums(user *User, categories []string, pager *Pagination) ([]*Forum, error) {
|
|
var (
|
|
fs = []*Forum{}
|
|
query = (&Forum{}).Preload()
|
|
wheres = []string{}
|
|
placeholders = []interface{}{}
|
|
)
|
|
|
|
if categories != nil && len(categories) > 0 {
|
|
wheres = append(wheres, "category IN ?")
|
|
placeholders = append(placeholders, categories)
|
|
}
|
|
|
|
// Hide explicit forum if user hasn't opted into it.
|
|
if !user.Explicit && !user.IsAdmin {
|
|
wheres = append(wheres, "explicit = false")
|
|
}
|
|
|
|
// Filters?
|
|
if len(wheres) > 0 {
|
|
query = query.Where(
|
|
strings.Join(wheres, " AND "),
|
|
placeholders...,
|
|
)
|
|
}
|
|
|
|
query = query.Order(pager.Sort)
|
|
query.Model(&Forum{}).Count(&pager.Total)
|
|
result := query.Offset(pager.GetOffset()).Limit(pager.PerPage).Find(&fs)
|
|
return fs, result.Error
|
|
}
|
|
|
|
// PaginateOwnedForums returns forums the user owns (or all forums to admins).
|
|
func PaginateOwnedForums(userID uint64, isAdmin bool, pager *Pagination) ([]*Forum, error) {
|
|
var (
|
|
fs = []*Forum{}
|
|
query = (&Forum{}).Preload()
|
|
)
|
|
|
|
if !isAdmin {
|
|
query = query.Where(
|
|
"owner_id = ?",
|
|
userID,
|
|
)
|
|
}
|
|
|
|
query = query.Order(pager.Sort)
|
|
query.Model(&Forum{}).Count(&pager.Total)
|
|
result := query.Offset(pager.GetOffset()).Limit(pager.PerPage).Find(&fs)
|
|
return fs, result.Error
|
|
}
|
|
|
|
// CreateForum.
|
|
func CreateForum(f *Forum) error {
|
|
result := DB.Create(f)
|
|
return result.Error
|
|
}
|
|
|
|
// Save a forum.
|
|
func (f *Forum) Save() error {
|
|
return DB.Save(f).Error
|
|
}
|
|
|
|
// CategorizedForum supports the main index page with custom categories.
|
|
type CategorizedForum struct {
|
|
Category string
|
|
Forums []*Forum
|
|
}
|
|
|
|
// CategorizeForums buckets forums into categories for front-end.
|
|
func CategorizeForums(fs []*Forum, categories []string) []*CategorizedForum {
|
|
var (
|
|
result = []*CategorizedForum{}
|
|
idxMap = map[string]int{}
|
|
)
|
|
|
|
// Initialize the result set.
|
|
for i, category := range categories {
|
|
result = append(result, &CategorizedForum{
|
|
Category: category,
|
|
Forums: []*Forum{},
|
|
})
|
|
idxMap[category] = i
|
|
}
|
|
|
|
// Bucket the forums into their categories.
|
|
for _, forum := range fs {
|
|
category := forum.Category
|
|
if category == "" {
|
|
continue
|
|
}
|
|
idx := idxMap[category]
|
|
result[idx].Forums = append(result[idx].Forums, forum)
|
|
}
|
|
|
|
return result
|
|
}
|