website/pkg/models/certification.go
Noah Petherbridge b8146ae485 Forum Creation Quotas
Add minimum quotas for users to earn the ability to create custom forums.

The entry requirements that could earn the first forum include:
1. Having a Certified account status for at least 45 days.
2. Having written 10 posts or replies in the forums.

Additional quota is granted in increasing difficulty based on the count of
forum posts created.

Other changes:

* Admin view of Manage Forums can filter for official/community.
* "Certified Since" now shown on profile pages.
* Update FAQ page for Forums feature.
2024-08-22 21:57:14 -07:00

113 lines
3.1 KiB
Go

package models
import (
"errors"
"fmt"
"time"
"gorm.io/gorm"
)
// CertificationPhoto table.
type CertificationPhoto struct {
ID uint64 `gorm:"primaryKey"`
UserID uint64 `gorm:"uniqueIndex"`
Filename string
Filesize int64
Status CertificationPhotoStatus
AdminComment string
SecondaryNeeded bool // a secondary form of ID has been requested
SecondaryFilename string // photo ID upload
SecondaryVerified bool // mark true when ID checked so original can be deleted
IPAddress string // the IP they uploaded the photo from
CreatedAt time.Time
UpdatedAt time.Time
}
type CertificationPhotoStatus string
const (
CertificationPhotoNeeded CertificationPhotoStatus = "needed"
CertificationPhotoPending CertificationPhotoStatus = "pending"
CertificationPhotoApproved CertificationPhotoStatus = "approved"
CertificationPhotoRejected CertificationPhotoStatus = "rejected"
// If a photo is pending approval but the admin wants to engage the
// secondary check (prompt user for a photo ID upload)
CertificationPhotoSecondary CertificationPhotoStatus = "secondary"
)
// GetCertificationPhoto retrieves the user's record from the DB or upserts their initial record.
func GetCertificationPhoto(userID uint64) (*CertificationPhoto, error) {
p := &CertificationPhoto{}
result := DB.Where("user_id = ?", userID).First(&p)
if result.Error == gorm.ErrRecordNotFound {
p = &CertificationPhoto{
UserID: userID,
Status: CertificationPhotoNeeded,
}
result = DB.Create(p)
return p, result.Error
}
return p, result.Error
}
// CertifiedSince retrieve's the last updated date of the user's certification photo, if approved.
//
// This incurs a DB query for their cert photo.
func (u *User) CertifiedSince() (time.Time, error) {
if !u.Certified {
return time.Time{}, errors.New("user is not certified")
}
cert, err := GetCertificationPhoto(u.ID)
if err != nil {
return time.Time{}, err
}
if cert.Status != CertificationPhotoApproved {
return time.Time{}, fmt.Errorf("cert photo status is: %s (expected 'approved')", cert.Status)
}
return cert.UpdatedAt, nil
}
// CertificationPhotosNeedingApproval returns a pager of the pictures that require admin approval.
func CertificationPhotosNeedingApproval(status CertificationPhotoStatus, pager *Pagination) ([]*CertificationPhoto, error) {
var p = []*CertificationPhoto{}
query := DB.Where(
"status = ?",
status,
).Order(
pager.Sort,
)
// Get the total count.
query.Model(&CertificationPhoto{}).Count(&pager.Total)
result := query.Offset(
pager.GetOffset(),
).Limit(pager.PerPage).Find(&p)
return p, result.Error
}
// CountCertificationPhotosNeedingApproval gets the count of pending photos for admin alert.
func CountCertificationPhotosNeedingApproval() int64 {
var count int64
DB.Where("status = ?", CertificationPhotoPending).Model(&CertificationPhoto{}).Count(&count)
return count
}
// Save photo.
func (p *CertificationPhoto) Save() error {
result := DB.Save(p)
return result.Error
}
// Delete the DB entry.
func (p *CertificationPhoto) Delete() error {
return DB.Delete(p).Error
}