feat: add article comment system with moderation
- Comment model with nested replies (ParentID), dual authorship (logged-in UserID / anonymous GuestToken cookie), email hash for Gravatar, private flag, and moderation status - CommentConfig singleton (enabled / allow guest / guest-require-approval / use Gravatar) cached like the other platform config - Markdown comments with built-in emoji picker, preview, and markdown help; rendered client-side via marked + DOMPurify, with server-side HTML/dangerous-scheme stripping as a first XSS defense - Private comments visible only to admin and the author; pending comments visible only to admin and the author - Admin moderation list (pending/approved/rejected/all tabs) with approve/reject/delete and a pending-count badge - Comment settings page with the four toggles - One-time session flash notice (auto-dismissed after 4s) so the "comment posted" banner no longer persists across refreshes Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -14,6 +14,7 @@ var configCache = struct {
|
||||
mu sync.RWMutex
|
||||
site *SiteSetting
|
||||
upload *UploadConfig
|
||||
comment *CommentConfig
|
||||
types []UploadFileType
|
||||
baseURLs []DownloadBaseURL
|
||||
}{
|
||||
@@ -41,6 +42,13 @@ func LoadConfigCache(db *gorm.DB) {
|
||||
configCache.upload = &UploadConfig{ID: 1, Enabled: true, DefaultMaxSize: DefaultUploadMaxSize, StorageDir: "attachments"}
|
||||
}
|
||||
|
||||
var cc CommentConfig
|
||||
if err := db.First(&cc, 1).Error; err == nil {
|
||||
configCache.comment = &cc
|
||||
} else {
|
||||
configCache.comment = defaultCommentConfig()
|
||||
}
|
||||
|
||||
var t []UploadFileType
|
||||
if err := db.Order("sort asc, id asc").Find(&t).Error; err == nil {
|
||||
configCache.types = t
|
||||
@@ -73,6 +81,13 @@ func GetUploadConfig() *UploadConfig {
|
||||
return configCache.upload
|
||||
}
|
||||
|
||||
// GetCommentConfig returns the cached comment policy.
|
||||
func GetCommentConfig() *CommentConfig {
|
||||
configCache.mu.RLock()
|
||||
defer configCache.mu.RUnlock()
|
||||
return configCache.comment
|
||||
}
|
||||
|
||||
// GetUploadFileTypes returns the cached list of permitted file types.
|
||||
func GetUploadFileTypes() []UploadFileType {
|
||||
configCache.mu.RLock()
|
||||
|
||||
Reference in New Issue
Block a user