- Add edit tracking (24h limit), soft delete, and JSONB reactions to ForumTopic/ForumReply - Create ForumTopicSubscription, ForumReport, ForumEditHistory models - Add 15 new API endpoints for user actions and admin moderation - Implement reactions (👍❤️🎉), topic subscriptions, content reporting - Add solution marking, restore deleted content, edit history for admins - Create forum_reports.html and forum_deleted.html admin templates - Integrate notifications for replies, reactions, solutions, and reports - Add SQL migration 024_forum_modernization.sql Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
169 lines
7.6 KiB
SQL
169 lines
7.6 KiB
SQL
-- Forum Modernization Migration
|
|
-- Author: Claude Code
|
|
-- Date: 2026-01-31
|
|
-- Description: Adds edit tracking, soft delete, reactions, subscriptions, reports, and edit history
|
|
|
|
-- ============================================================
|
|
-- PHASE 1: Add new columns to forum_topics
|
|
-- ============================================================
|
|
|
|
-- Edit tracking
|
|
ALTER TABLE forum_topics ADD COLUMN IF NOT EXISTS edited_at TIMESTAMP;
|
|
ALTER TABLE forum_topics ADD COLUMN IF NOT EXISTS edited_by INTEGER REFERENCES users(id);
|
|
ALTER TABLE forum_topics ADD COLUMN IF NOT EXISTS edit_count INTEGER DEFAULT 0;
|
|
|
|
-- Soft delete
|
|
ALTER TABLE forum_topics ADD COLUMN IF NOT EXISTS is_deleted BOOLEAN DEFAULT FALSE;
|
|
ALTER TABLE forum_topics ADD COLUMN IF NOT EXISTS deleted_at TIMESTAMP;
|
|
ALTER TABLE forum_topics ADD COLUMN IF NOT EXISTS deleted_by INTEGER REFERENCES users(id);
|
|
|
|
-- Reactions (JSONB)
|
|
ALTER TABLE forum_topics ADD COLUMN IF NOT EXISTS reactions JSONB DEFAULT '{}';
|
|
|
|
-- ============================================================
|
|
-- PHASE 2: Add new columns to forum_replies
|
|
-- ============================================================
|
|
|
|
-- Edit tracking
|
|
ALTER TABLE forum_replies ADD COLUMN IF NOT EXISTS edited_at TIMESTAMP;
|
|
ALTER TABLE forum_replies ADD COLUMN IF NOT EXISTS edited_by INTEGER REFERENCES users(id);
|
|
ALTER TABLE forum_replies ADD COLUMN IF NOT EXISTS edit_count INTEGER DEFAULT 0;
|
|
|
|
-- Soft delete
|
|
ALTER TABLE forum_replies ADD COLUMN IF NOT EXISTS is_deleted BOOLEAN DEFAULT FALSE;
|
|
ALTER TABLE forum_replies ADD COLUMN IF NOT EXISTS deleted_at TIMESTAMP;
|
|
ALTER TABLE forum_replies ADD COLUMN IF NOT EXISTS deleted_by INTEGER REFERENCES users(id);
|
|
|
|
-- Reactions (JSONB)
|
|
ALTER TABLE forum_replies ADD COLUMN IF NOT EXISTS reactions JSONB DEFAULT '{}';
|
|
|
|
-- Solution marking
|
|
ALTER TABLE forum_replies ADD COLUMN IF NOT EXISTS is_solution BOOLEAN DEFAULT FALSE;
|
|
ALTER TABLE forum_replies ADD COLUMN IF NOT EXISTS marked_as_solution_by INTEGER REFERENCES users(id);
|
|
ALTER TABLE forum_replies ADD COLUMN IF NOT EXISTS marked_as_solution_at TIMESTAMP;
|
|
|
|
-- ============================================================
|
|
-- PHASE 3: Create forum_topic_subscriptions table
|
|
-- ============================================================
|
|
|
|
CREATE TABLE IF NOT EXISTS forum_topic_subscriptions (
|
|
id SERIAL PRIMARY KEY,
|
|
user_id INTEGER NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
|
topic_id INTEGER NOT NULL REFERENCES forum_topics(id) ON DELETE CASCADE,
|
|
notify_email BOOLEAN DEFAULT TRUE,
|
|
notify_app BOOLEAN DEFAULT TRUE,
|
|
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
|
CONSTRAINT uq_forum_subscription_user_topic UNIQUE (user_id, topic_id)
|
|
);
|
|
|
|
-- Indexes for subscriptions
|
|
CREATE INDEX IF NOT EXISTS idx_forum_subscriptions_user ON forum_topic_subscriptions(user_id);
|
|
CREATE INDEX IF NOT EXISTS idx_forum_subscriptions_topic ON forum_topic_subscriptions(topic_id);
|
|
|
|
-- ============================================================
|
|
-- PHASE 4: Create forum_reports table
|
|
-- ============================================================
|
|
|
|
CREATE TABLE IF NOT EXISTS forum_reports (
|
|
id SERIAL PRIMARY KEY,
|
|
reporter_id INTEGER NOT NULL REFERENCES users(id),
|
|
|
|
-- Polymorphic relationship
|
|
content_type VARCHAR(20) NOT NULL CHECK (content_type IN ('topic', 'reply')),
|
|
topic_id INTEGER REFERENCES forum_topics(id) ON DELETE CASCADE,
|
|
reply_id INTEGER REFERENCES forum_replies(id) ON DELETE CASCADE,
|
|
|
|
reason VARCHAR(50) NOT NULL CHECK (reason IN ('spam', 'offensive', 'off-topic', 'other')),
|
|
description TEXT,
|
|
|
|
status VARCHAR(20) DEFAULT 'pending' CHECK (status IN ('pending', 'reviewed', 'dismissed')),
|
|
reviewed_by INTEGER REFERENCES users(id),
|
|
reviewed_at TIMESTAMP,
|
|
review_note TEXT,
|
|
|
|
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
|
|
|
-- Ensure at least one of topic_id or reply_id is set
|
|
CONSTRAINT chk_forum_report_content CHECK (
|
|
(content_type = 'topic' AND topic_id IS NOT NULL AND reply_id IS NULL) OR
|
|
(content_type = 'reply' AND reply_id IS NOT NULL)
|
|
)
|
|
);
|
|
|
|
-- Indexes for reports
|
|
CREATE INDEX IF NOT EXISTS idx_forum_reports_status ON forum_reports(status);
|
|
CREATE INDEX IF NOT EXISTS idx_forum_reports_reporter ON forum_reports(reporter_id);
|
|
CREATE INDEX IF NOT EXISTS idx_forum_reports_topic ON forum_reports(topic_id);
|
|
CREATE INDEX IF NOT EXISTS idx_forum_reports_reply ON forum_reports(reply_id);
|
|
|
|
-- ============================================================
|
|
-- PHASE 5: Create forum_edit_history table
|
|
-- ============================================================
|
|
|
|
CREATE TABLE IF NOT EXISTS forum_edit_history (
|
|
id SERIAL PRIMARY KEY,
|
|
|
|
-- Polymorphic relationship
|
|
content_type VARCHAR(20) NOT NULL CHECK (content_type IN ('topic', 'reply')),
|
|
topic_id INTEGER REFERENCES forum_topics(id) ON DELETE CASCADE,
|
|
reply_id INTEGER REFERENCES forum_replies(id) ON DELETE CASCADE,
|
|
|
|
editor_id INTEGER NOT NULL REFERENCES users(id),
|
|
old_content TEXT NOT NULL,
|
|
new_content TEXT NOT NULL,
|
|
edit_reason VARCHAR(255),
|
|
|
|
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
|
|
|
-- Ensure at least one of topic_id or reply_id is set
|
|
CONSTRAINT chk_forum_edit_history_content CHECK (
|
|
(content_type = 'topic' AND topic_id IS NOT NULL AND reply_id IS NULL) OR
|
|
(content_type = 'reply' AND reply_id IS NOT NULL)
|
|
)
|
|
);
|
|
|
|
-- Indexes for edit history
|
|
CREATE INDEX IF NOT EXISTS idx_forum_edit_history_topic ON forum_edit_history(topic_id);
|
|
CREATE INDEX IF NOT EXISTS idx_forum_edit_history_reply ON forum_edit_history(reply_id);
|
|
CREATE INDEX IF NOT EXISTS idx_forum_edit_history_editor ON forum_edit_history(editor_id);
|
|
|
|
-- ============================================================
|
|
-- PHASE 6: Additional indexes for performance
|
|
-- ============================================================
|
|
|
|
-- Index for soft-deleted topics (admin queries)
|
|
CREATE INDEX IF NOT EXISTS idx_forum_topics_is_deleted ON forum_topics(is_deleted) WHERE is_deleted = TRUE;
|
|
|
|
-- Index for soft-deleted replies (admin queries)
|
|
CREATE INDEX IF NOT EXISTS idx_forum_replies_is_deleted ON forum_replies(is_deleted) WHERE is_deleted = TRUE;
|
|
|
|
-- Index for solution replies
|
|
CREATE INDEX IF NOT EXISTS idx_forum_replies_is_solution ON forum_replies(is_solution) WHERE is_solution = TRUE;
|
|
|
|
-- ============================================================
|
|
-- PHASE 7: Grant permissions
|
|
-- ============================================================
|
|
|
|
GRANT ALL ON TABLE forum_topic_subscriptions TO nordabiz_app;
|
|
GRANT ALL ON TABLE forum_reports TO nordabiz_app;
|
|
GRANT ALL ON TABLE forum_edit_history TO nordabiz_app;
|
|
|
|
GRANT USAGE, SELECT ON SEQUENCE forum_topic_subscriptions_id_seq TO nordabiz_app;
|
|
GRANT USAGE, SELECT ON SEQUENCE forum_reports_id_seq TO nordabiz_app;
|
|
GRANT USAGE, SELECT ON SEQUENCE forum_edit_history_id_seq TO nordabiz_app;
|
|
|
|
-- ============================================================
|
|
-- VERIFICATION QUERIES
|
|
-- ============================================================
|
|
|
|
-- Verify new columns in forum_topics
|
|
-- SELECT column_name, data_type FROM information_schema.columns
|
|
-- WHERE table_name = 'forum_topics' AND column_name IN ('edited_at', 'edited_by', 'edit_count', 'is_deleted', 'deleted_at', 'deleted_by', 'reactions');
|
|
|
|
-- Verify new columns in forum_replies
|
|
-- SELECT column_name, data_type FROM information_schema.columns
|
|
-- WHERE table_name = 'forum_replies' AND column_name IN ('edited_at', 'edited_by', 'edit_count', 'is_deleted', 'deleted_at', 'deleted_by', 'reactions', 'is_solution', 'marked_as_solution_by', 'marked_as_solution_at');
|
|
|
|
-- Verify new tables
|
|
-- SELECT table_name FROM information_schema.tables WHERE table_name IN ('forum_topic_subscriptions', 'forum_reports', 'forum_edit_history');
|