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 Private bool `gorm:"index"` 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 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 private forums except for admins. if !user.IsAdmin { wheres = append(wheres, "private 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, search *Search, pager *Pagination) ([]*Forum, error) { var ( fs = []*Forum{} query = (&Forum{}).Preload() wheres = []string{} placeholders = []interface{}{} ) // Users see only their owned forums. if !isAdmin { wheres = append(wheres, "owner_id = ?") placeholders = append(placeholders, userID) } // Apply their search terms. if search != nil { for _, term := range search.Includes { var ilike = "%" + strings.ToLower(term) + "%" wheres = append(wheres, "(fragment ILIKE ? OR title ILIKE ? OR description ILIKE ?)") placeholders = append(placeholders, ilike, ilike, ilike) } for _, term := range search.Excludes { var ilike = "%" + strings.ToLower(term) + "%" wheres = append(wheres, "(fragment NOT ILIKE ? AND title NOT ILIKE ? AND description NOT ILIKE ?)") placeholders = append(placeholders, ilike, ilike, ilike) } } query = query.Where( strings.Join(wheres, " AND "), placeholders..., ).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 }