website/pkg/models/forum.go

192 lines
4.1 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
InnerCircle 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
}
// GetForums queries a set of thread IDs and returns them mapped.
func GetForums(IDs []uint64) (map[uint64]*Forum, error) {
var (
mt = map[uint64]*Forum{}
fs = []*Forum{}
)
result := (&Forum{}).Preload().Where("id IN ?", IDs).Find(&fs)
for _, row := range fs {
mt[row.ID] = row
}
return mt, 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")
}
// Hide circle forums if the user isn't in the circle.
if !user.IsInnerCircle() {
wheres = append(wheres, "inner_circle is not true")
}
// 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)
}
// Remove any blank categories with no boards.
var filtered = []*CategorizedForum{}
for _, forum := range result {
if len(forum.Forums) == 0 {
continue
}
filtered = append(filtered, forum)
}
return filtered
}