################################################################################
# Topic: Forums
# Description: Forum capabilities for DGS
# Author of this document: juga
## /*
## Dragon Go Server
## Copyright (C) 2001-2014 Erik Ouchterlony, Rod Ival, Jens-Uwe Gaspar
##
## This program is free software: you can redistribute it and/or modify
## it under the terms of the GNU Affero General Public License as
## published by the Free Software Foundation, either version 3 of the
## License, or (at your option) any later version.
##
## This program is distributed in the hope that it will be useful,
## but WITHOUT ANY WARRANTY; without even the implied warranty of
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
## GNU Affero General Public License for more details.
##
## You should have received a copy of the GNU Affero General Public License
## along with this program. If not, see .
## */
Topics:
1. Introduction and GUI
2. Classes and Files (comprising and supporting forum)
3. Development Guide (data model)
3.1 Database Schema
3.2 NEW-flag management
3.3 Update-"Trigger" for use-cases
4. FAQ
5. Future Enhancements
6. Known Bugs
#-------- (1) Introduction and GUI --------------------------------------------
# Legend:
# forum = forum identified by Forums.ID
# forum-list = list of all forums
# thread = list of posts of one thread identified by
# Posts.ID[Parent_ID=0 or ID=Thread_ID]
# thread-list = list of threads of a specific forum
# initial-post = thread starter post = initial post of a thread
# thread-posts = post-list = list of all posts of a specific thread
# shown post = post with Posts[Approved=Y]
# hidden post = moderated post with Posts[Approved<>Y]
# { fields } = database fields, for which new data can be entered
#
# PK = primary key
# user = normal user (non-admin, non-moderator)
# shown posts = non-hidden post, that has been approved and is not hidden
# new posts = unread posts by certain user
#
# calculated field = value can be calculated from other fields in SAME table
# triggered field = value can be calculated from data from OTHER tables,
# or is triggered on certain use case,
# field needs to be updated if source tables changes
Navigation
- navigation between views:
forum list - thread list - thread-post-list - search
- paging:
- forum list: show all
- thread list: show max-rows (configurable)
- thread-post list: show all
- revision history: show all
- different views for thread-post-list: tree, flat-old-first, flat-new-first
Forum view, shown by "forum/index.php":
- show list of all forums ordered by Forums.SortOrder
- show only forums, which user is allowed to see (Forums.Options)
- each forum is described by forum-name and forum-description
- name is linked to corresponding thread-list
- "new" is appended to name if there are new posts in forum
- each forum shows a moderated-state
- each forum can show a read-only-state
- for each hidden forum a "Hidden" is shown if user is allowed to view it
- each forum shows the number of (non-hidden) threads and posts
- each forum shows the date and author of the last added post in that forum
- the date is linked to the according last-post
- the author is linked to the according user-info
- for moderators at the bottom a link "Show forumlog" is shown and linked
to view the Forumlog
Thread list view (for single forum-id), shown by "forum/list.php":
- show list of threads ordered by last-changed threads
- for moderators also all hidden threads are shown (PostCount=0)
- each thread shows the thread-subject, the author of the initial-thread-post,
the number of hits and shown posts in the thread
- subject is linked to corresponding thread-post-list
- "new" is appended to subject if there are new posts in thread
- each thread shows the date and author of the last added post in that thread
- the date is linked to the according last-post
- the author is linked to the according user-info
- at the bottom a selectbox to control the max-rows is shown
Thread post view (for single thread-id), shown by "forum/read.php":
- viewing thread marks new posts as read
- view-modes (can be changed): tree-view, flat-old-first, flat-new-first
- thread-tree overview shows subject, author and last-change of each thread-post
- subject is linked to full post-data on same page
- subject is level-indented up to 40 levels
- subject is emphasized for own posts
- "new" is appended to subject and linked to corresponding new post
- tree-overview can be hidden
- post-number (order) is shown
- thread-post-list shows all non-hidden posts with subject, author
creation-date, last-edit-date if edited and the post-text, post-number (order)
- subject is linked to post itself to have the URL to be used as reference
- "new" is appended to subject if it's a new post and linked to next new post
- edited is linked to show post revision history
- author is linked to according user-info
- footer contains allowed actions:
- [reply] is linked to add a reply to post
- [quote] is linked to add a reply to post with quoting reply-text (if allowed in config)
- [edit] is linked for own post to edit own post
- arrows for navigation within thread are linked:
- to top (=initial thread-post), to bottom
- to previous/next parent
- to previous/next post (except for flat-views)
- to first answer
- for moderators additional actions:
- [approve] to approve a post that needs moderating -> non-hidden post
- [reject] to reject a post that needs moderating -> hidden post
- [show] to show a hidden post
- [hide] to hide a shown post
- show input-boxes to reply post if it's only one post
- show linked action [Add reply to inital thread post] at bottom
to add a reply to initial thread post
- for moderators also all hidden posts are shown
Post Revision History, shown by "forum/read.php":
- show all previous versions of a post
Forum search, shown by "forum/search.php":
- search in forum, subject, text-body, for author,
message-scope (all / first), creation-date
- matching entries sortable by search terms (subject/text),
creation or modification date
- show matching post-entries in list with link to post-thread
- textual search terms are highlighted in subject and text-body
- term relevance shown as float value
- moderators can also include all hidden posts in the search
New Posts characteristics:
- "new" only shown for other users posts, not for own posts
- no markup for posts older than 12 weeks (configured with FORUM_WEEKS_NEW_END)
- markup in red (CSS NewFlag) for new posts not older than 12 weeks
#-------- Use-Cases
Lists use-cases, that need to be taken into account to uphold the forums
structural integrity; especially when introducing new features.
# User tasks
(U01) Read list of forums
(U02) Read list of threads of forum $fid
(U03) Read thread-posts of thread $tid
(U04) Read revision history of a single post $pid
(U05) Forum Search
(U06) Add new thread in forum $fid: (a) by myself, (b) by other -> new post $ntid
(U07) Edit (own) post $pid of thread $tid with saving history -> new post $npid { Subject, Text }
(U08) Reply (or Quote-Reply) to post $pid -> new post $npid
(U09) Mark all posts in thread $tid as read
# Admin tasks
(A01) Admin-Add new forum -> new forum $nfid
(A02) Admin-Edit forum $fid { Name, Description, Moderated, SortOrder }
(A03) ( NOT-IMPLEMENTED ): Admin-Move thread $tid from forum $sfid to forum $dfid
(A04) Moderate-Approve (pending-approval) post $pid
(A05) Moderate-Reject (pending-approval) post $pid
(A06) Moderate-Hide post $pid
(A07) Moderate-Show (=unhide) post $pid
(A08) Cleanup old forum-reads
(A09) Recalc forum-fields
(A10) Recalc thread-fields
#-------- (2) Classes and Files -----------------------------------------------
This section shortly describes the classes and files for the forum.
For more details you may also check the source-code documentation
of the classes, their variables and functions.
- forum/forum_functions.php:
This file contains most of the classes and helper methods to
support the forum loading and rendering.
- forum/index.php:
Show forums list.
- forum/list.php:
Show thread list.
- forum/old_links_redirect.php:
Redirect old (before 2007) external links to the converted DGS-forum-post.
Example of old URL:
http://www.dragongoserver.net/phorum/read.php?f=4&i=4354&t=4354&v=t
f = old forum-id
t = old thread-id
i = old post-id
IMPORTANT NOTE: The i-arg is NOT unique, only UNIQUE(f,i).
This needs to be taken into account when implementing new forum-features.
- forum/post.php:
Contains only supporting functions to handle the creation/replying of posts.
- forum/read.php:
Show/edit post, show revision history, Moderate post.
- forum/search.php:
Forum search.
- forum/admin.php:
Administrative functions and page to add/edit forums.
- forum/admin_show_forumlog.php:
Show forumlogs.
There was a big refactoring in Aug/Sep-2008, which was an attempt to separate
application logic and the presentational layer (GUI & layout) from database
operations. This was accomplished by the introduction of classes serving
as containers and objects with behaviour supporting the application logic
and/or GUI. Another advantage was to be able to get rid of passing data
into functions using global vars and extract($row) statements.
Refactoring needs to be done in small baby steps to make it easier only
to change the implementation without changing the functionality. Normally,
refactorings are done writing unit-tests to secure the functionality
before making changes. But with the current code (mid 2008) it's difficult
to write unit-tests, because there are often not well defined, separated
interfaces, but the database and business logic is mixed. That makes it very
difficult to write unit-tests in an acceptable amount of time.
The refactoring to the following classes is far from perfect, but was a first
step from less OO-oriented code (mostly function-call-sequences passing
global vars) to concentrate object-behaviour in classes with the goals
explained above.
class DisplayForum
Serving as general container to help in displaying forum content
class Forum
Representing a forum-object
class ForumThread
Helping in loading list of posts of a thread
class ForumPost
Representing a Post with all its fields
class ForumUser
Temporary representation of a user for creating user-references
class ForumRead
Help with handling forum-reads and cope with 'new'-flag
#-------- (3) Development Guide (data model) ----------------------------------
This section contains the database scheme with a description of all fields
and all the uses-cases, that can happen in the forums. A further sub-section
contains the mapping between data model and database schema.
It's also described what "triggers" (not db-triggers) are needed to keep the
structural integrity of the forum data.
There are container-classes, that could be used to map to the according
entitities (=database tables), but at the moment (Sep-2008) those are not
used as pure entities, but more like enhanced DAOs (data access objects)
within the code (could be a future refactoring step).
#-------- (3.1) Database Schema
# Lists forum-related database tables with important field to keep structural
# integrity of forum data.
#
# NOTE: fields are functionally grouped (not like in DDL)
# NOTE: DDL available in specs/db/dragon-ddl.sql
#----- TABLE Forums
# Representation of a forum with name, description and moderating-status
TABLE Forums
ID - auto-inserted PK forum-id referred as $fid
LastPost - Posts.ID of last changed post for this forum
Updated - last-updated-date for forum (indicator to recalculate
NEW-state for threads and forums):
PostsInForum - number of shown posts for this forum
ThreadsInForum - number of shown threads for this forum
Name - forum-name
Description - forum-description
SortOrder - sort-order for forums
# forum-options
# - Bit-value 0x1 : if set, posts in forums need approval by moderator
# before being shown to users
# - Bit-value 0x2 : forum is within admin group (hidden normally)
# - Bit-value 0x4 : forum is within developer group (hidden normally)
# - Bit-value 0x8 : forum is read-only for non-moderators and non-developers
Options
# entered data
Name, Description, SortOrder, Options
# triggered fields
ID; LastPost, Updated, PostsInForum, ThreadsInForum
#----- TABLE Posts
# Representation of a thread and post
TABLE Posts
ID - auto-inserted PK post-id referred as $pid, or
thread-id[ID=Thread_ID or Parent_ID=0] referred as $tid
Forum_ID - post of forum Forums.ID
Thread_ID - thread-id, Posts.ID $tid of a post, see { ID }
# Fields only set for Thread (on initial post) with value=0 for normal post:
LastPost - Posts.ID of last changed post for this thread
Lastchanged - last-changed-date for thread (post-edit is NO change to thread)
PostsInThread - number of shown posts for this thread { Thread_ID }
Hits - number of hits (reads/action on thread-posts)
Flags - thread-flags, though could later be used for Posts-flags as well
User_ID - Players.ID, author of post
Parent_ID - Posts.ID of answered/replied post (to build thread-tree)
AnswerNr - reply-number for parent-post starting with 1,
consecutive number (sort-order) within post-list for
replied post with post-id Posts.Parent_ID,
< 64*64 (=4096) posts maximum in thread
Depth - depth of this post within thread-tree, starting with 1,
>1 for normal post, < FORUM_MAX_DEPTH (=40)
PosIndex(80) - sort-string used to correctly display thread-tree
Time - creation-date of post
Lastedited - last-edited-date of post
Approved - hidden-state of post:
- 'Y'=shown post,
- 'N'=hidden post
- 'P'=post needs moderation decision (approve/reject)
Subject(80) - subject of post
Text - text-body of post
crc32 - checksum of text of this post, crc32 := crc32(Text)
old_ID - referring to old database-scheme of DGS, used to redirect
some OLD external links to DGS-forums to converted posts,
see forum/old_links_redirect.php
# entered data
Forum_ID, Thread_ID, User_ID, Parent_ID, Flags, AnswerNr, Depth, PosIndex,
Time, Lastedited, Approved, Subject, Text, crc32, (old_ID)
# calculated fields
PosIndex (from Parent_ID, Depth, AnswerNr), crc32 (from Text)
# triggered fields
ID; LastPost, Lastchanged, PostsInThread, Hits;
PosIndex, Lastedited, Approved
#----- TABLE Forumreads
# Representation of forum-read information
# - last read-date of single thread, single forum, all forums
# for posts not older than FORUM_WEEKS_NEW_END
TABLE Forumreads
User_ID - Players.ID
Forum_ID - Forums.ID
Thread_ID - Posts.ID of thread
Time - last read-date,
used as date-trigger in combination with Forums.Updated
HasNew - 0|1, 1=there are new posts in forum(s), 0=no new posts,
used for forum-list and global-forum only,
0 for threads forum-read
These fields are updated to current timestamp and current
new-flag-state when something has been changed for
forum[Forums.ID], being an indicator when forum could have
something new and so forum-read for user must be checked.
# (a) Last read-date for all shown posts in thread $tid:
# post is read when older than FORUM_WEEKS_NEW_END weeks or
# there is a Forumreads-entry with FR.Time >= Posts.Lastchanged
User_ID := $uid, Forum_ID := $fid, Thread_ID := $tid
Time := MAX(Posts[$tid].Lastchanged) = date of last-read posts,
this ensures that posts added during own replying are not going to be "missed"
HasNew := 0
# (b) Last updated-date for NEW-flags for all threads in forum $fid:
User_ID := $uid, Forum_ID := $fid, Thread_ID := 0
Time := current timestamp of NEW-flag detection,
recalc of NEW-flag is needed when Forum[$fid].Updated > Time
HasNew := 0 (if there are no NEW posts in forum),
1 (if there are at least one NEW post in forum)
# (c) Last udpated-date for global NEW-flag for all forums:
User_ID := $uid, Forum_ID := 0, Thread_ID := 0
Time := current timestamp of NEW-flag detection,
recalc of NEW-flag is needed when global_forum_updated-date > Time,
global forum-update-date is stored in cached-file (faster than db-access)
but needs working CACHE_FOLDER (otherwise global-update always assumed)
HasNew := 0 (if there are no NEW posts in all forums),
1 (if there are at least one NEW post in all forums)
# NOTES:
- Forum_ID and Thread_ID for (a)-(c) are used for easier/faster calculation
of NEW-flag on forum-list and global forum-NEW-flag in main-menu
- PRIMARY-KEY for table is (Thread_ID,User_ID,Forum_ID):
- order for fields in primary-key is optimized for various use-cases
for NEW-handling using this table taking best cardinality into account
for BTREE-index-structure
- cleanup for use-case A08 could need other index or index-order
to avoid table-scan, but would need a force index on added Time-index
to be useful ... which isn't should a big deal as it is only executed
once a day.
- NEW-flag management uses lazy-loading and lazy-writing
- recalculating can be enforced by deleting all Forumreads-table entries
for all users with Thread_ID>0
# triggered fields
Time, HasNew
#----- TABLE Forumlog
# Representation of actions in forums
TABLE Forumlog
ID - auto-inserted PK
User_ID - FK to Players.ID for user, that performed forum-action
Thread_ID - thread-id, Posts.ID of thread the action was performed on
Post_ID - post-id, Posts.ID of post action was performed on
Time - action-date
Action - actions of users:
- 'new_post' : user added new post
- 'new_pend_post' : user added new post needing moderation
- 'edit_post' : user edited post
- 'edit_pend_post' : user edited post needing moderation
- actions of moderators:
- 'approve_post' : moderator approved pending post
- 'reject_post' : moderator rejected pending post
- 'show_post' : moderator showed hidden post
- 'hide_post' : moderator hide shown post
IP - IP of user, that performed forum-action
# entered data
User_ID, Thread_ID, Post_ID, Time, Action, IP
#-------- Calculated database fields
#----- Forum (stored in Forums-TABLE)
# label: recalc_forum
# Calculate fields for single forum $fid
Forums($fid):
# calculate
LastPost := X_LastPost or else 0 if nothing found:
SELECT ID AS X_LastPost FROM Posts
WHERE Forum_ID=$fid AND Thread_ID>0 AND Approved='Y' AND PosIndex>''
ORDER BY Time DESC LIMIT 1
# calculate
PostsInForum := X_Count :
SELECT COUNT(*) AS X_Count FROM Posts
WHERE Forum_ID=$fid AND Thread_ID>0 AND Approved='Y' AND PosIndex>''
# calculate, if threads are up-to-date
PostsInForum := X_Count :
SELECT SUM(PostsInThread) AS X_Count FROM Posts
WHERE Forum_ID=$fid AND Parent_ID=0
# calculate, if threads are up-to-date
ThreadsInForum := X_Count
SELECT COUNT(*) AS X_Count FROM Posts
WHERE Forum_ID=$fid AND Parent_ID=0 AND PostsInThread>0
#----- Thread (stored in Posts-TABLE)
# label: recalc_thread
# Calculate fields for all threads
Posts():
# calculate
PostsInThread := as X_CountPosts :
SELECT Thread_ID, COUNT(*) AS X_CountPosts FROM Posts
WHERE Thread_ID>0 AND Approved='Y' AND PosIndex>'' GROUP BY Thread_ID
# Calculate fields for single thread $tid
Posts($fid,$tid):
# combined calculate
LastPost := X_LastPost,
# set Lastchanged only if there is one; there could be none if all posts are hidden especially
# on single-post-threads (e.g. edit on shown post for moderated forum puts post back in hidden-state)
Lastchanged := X_Lastchanged :
SELECT ID AS X_LastPost, Time AS X_Lastchanged FROM Posts
WHERE Thread_ID=$tid AND Approved='Y' AND PosIndex>''
ORDER BY Time DESC LIMIT 1
# calculate
PostsInThread := as X_CountPosts :
SELECT COUNT(*) AS X_CountPosts FROM Posts
WHERE Thread_ID=$tid AND Approved='Y' AND PosIndex>''
#-------- (3.2) NEW-flag management ---------
* General comments
- Posts.Lastedited is NOT influencing NEW-flags on posts/threads/forums
- 'new'-flags for global-forum, forum-list and thread-list are independent
of each other, i.e. for example, that 'new'-flag for thread-list is NOT needed
to calculate 'new'-flag for forum-list or global-forum, nor is 'new'-flag
calculation for forum-list needed for global-forum 'new'-flag.
- There can be tricky cases that must not update Posts.Lastchanged for thread-entry
if the thread contains only hidden posts (esp. for single-post-threads).
There was a nasty bug originating from the fact, that a shown post in a moderated
forum was edited, which recalculated the threads Posts.Lastchanged resetting it
to $NOW, which was wrong as it make the NEW re-appear repeatedly.
* NEW-flags in thread-post view
- post is "read" when Posts[$pid].Time is older than FORUM_WEEKS_NEW_END weeks
- post is "read" when Forumreads[$uid,$fid,$tid].Time >= Posts[$pid].Time
- otherwise post is "unread" (='new')
- stored for FR:=Forumreads[User_ID=$uid,Forum_ID=$fid,Thread_ID=$tid]:
FR.HasNew := 0
FR.Time := MAX(Posts[Thread_ID=$tid].Time) [A]
instead of setting FR.Time to $NOW, [A] ensures,
that new posts from other users while replying are not missed
* NEW-flags in forum thread-list
- thread is "read" when Thread[$tid].Lastchanged is older than FORUM_WEEKS_NEW_END weeks
- thread is "read" when Forumreads[$uid,$fid,$tid=0].Time >= Thread[$tid].Lastchanged
- otherwise thread contains "unread" posts, therefore has a "new"-flag
- using stored Forumreads[User_ID=$uid,Forum_ID=$fid,Thread_ID=$tid]
to calculate 'new'-flag
* NEW-flags in forum-list
- forum is "new" when there is at least one unread thread in forum
- otherwise all posts for forum are "read"
- using stored Forumreads[User_ID=$uid,Forum_ID=$fid,Thread_ID=*]
to calculate 'new'-flag
- result of this "new"-check is stored for
FR:=Forumreads[User_ID=$uid,Forum_ID=$fid,Thread_ID=0]:
FR.HasNew := state of "new"-flag (1=set, 0=unset)
FR.Time := $NOW (=date of calculating 'new'-flag-state)
when according Forums[ID=$fid].Updated > FR.Time, then "recalculate" of 'new'-flag
is required.
* NEW-flags in main-menu (global-forum)
- global-forum is "new" when there is at least one unread thread in one of all forums
- otherwise all posts in all forums are "read"
- using stored Forumreads[User_ID=$uid,Forum_ID=*,Thread_ID=*]
to calculate 'new'-flag
- result of this "new"-check is stored for
FR:=Forumreads[User_ID=$uid,Forum_ID=0,Thread_ID=0]:
FR.HasNew := state of "new"-flag (1=set, 0=unset)
FR.Time := $NOW (=date of calculating 'new'-flag-state)
NOTE: since 21-Mar-2010 this has been replaced by storing it into:
Players.ForumReadTime == FR.Time
Players.ForumReadNew == FR.HasNew
# NOTE: if Players.ForumReadNew is <0 forum-has-new will be recalculated
when global-forum-updated-date (stored by modification-time of cache-file)
CACHEFILE_FORUM_GLOBAL.ModifiedTime > FR.Time,
then "recalculate" of 'new'-flag is required.
- NOTE: storing global 'new'-flag-state in Forumreads has a disadvantage:
to show it in the main-menu Forumreads-table-entry need to be loaded
for EVERY page-call showing the main-menu.
=> Therefore (later) it would be good to refactor storing the data
from FR above in the Players-table.
NOTE: implemented since 21-Mar-2010
#-------- (3.3) Update-"Trigger" for use-cases
# Trigger-fields:
# - Forums: ID; LastPost, Updated, PostsInForum, ThreadsInForum
# - Thread/Post: ID; LastPost, Lastchanged, PostsInThread, Hits;
# PosIndex, Lastedited, Approved
# - Forumreads: Time, HasNew
# - global-Forumreads: touch CACHEFILE_FORUM_GLOBAL-file to update
# modification-time representing Updated-date
#----- U01: read forum-list
* lazy-init of NEW-flags for non-hidden forums for user
- check if there is a NEW post for non-hidden forums of user
- write NEW-flags for each showed forum into Forumreads-table,
see (b)-description above
- see 'forum/forum_functions.php', Forum::load_forum_list(ForumOptions)
#----- U02: read thread-list
* lazy-init of NEW-flags for threads of forum for user
- check if there is a NEW post within current forum-threads
- read NEW-flags for each showed thread from Forumreads-table,
see (a)-description above
- see 'forum/forum_functions.php', Forum::load_threads($uid,$is_moderator,$maxrows,$offset)
#----- U03: read thread-post-list
* if not replying/previewing and at least one post from another user:
Thread($tid).Hits++
* if not replying/previewing/moderate-switching
- mark thread as read with MAX(viewed Posts.Lastchanged):
Forumreads($uid,$fid,$tid).Time=MAX(viewed Posts.Lastchanged), HasNew=0
* Forumreads($uid,$fid,tid=0).Time= .Time - 1 day (for forum-read)
* Forumreads($uid,fid=0,tid=0).Time= .Time - 1 day (for global-forum read)
#----- U04: read post revision history
* if not author of post:
Thread($tid).Hits++
#----- U05: search forum
* no change
#----- U06: add new thread
* insert Posts:
- Forum_ID=$fid, Parent_ID=0, Thread_ID=$ntid (update after insert),
Hits=0, Time=$NOW, Lastchanged=Time, Depth=1, AnswerNr=1,
crc32=crc32(Text)
- moderated (if forum moderated or user is moderated (ADMOPT_FORUM_MOD_POST)):
LastPost=0, PostsInThread=0, Approved='P'
- not moderated:
LastPost=$ntid, PostsInThread=1, Approved='Y'
* if not moderated:
- Forums($fid).ThreadsInForum++
- Forums($fid).PostsInForum++
- Forums($fid).LastPost=GREATEST(LastPost,$ntid)
- Forums($fid).Updated=GREATEST(Updated,$NOW)
- Global.Updated=GREATEST(Updated,$NOW)
* write Forumlog-entry
#----- U07: edit own post
# ID of post must stay the same (for LastPost-fields in other tables)
* update Posts($pid).Lastedited
* insert Posts (with old subject/text):
Time=$NOW, Parent_ID=$pid, Forum_ID=$fid, User_ID=$uid, PosIndex=''
- moderated (if forum moderated or user is moderated (ADMOPT_FORUM_MOD_POST)):
Approved='P'
* if moderated but non-moderated before edit:
-> similar to A06 (hide-post)
* Thread($tid).Hits++
* write Forumlog-entry
#----- U08: reply post + quote-reply post
# reply to post $spid
* insert Posts:
- Forum_ID=$fid, Thread_ID=$tid, Parent_ID=$spid, Time=$NOW,
Depth=Posts[ID=$spid].Depth+1, AnswerNr=max[Parent_ID=$spid](Posts.AnswerNr) + 1,
PosIndex=CALC, crc32=crc32(Text)
- moderated (if forum moderated or user is moderated (ADMOPT_FORUM_MOD_POST)):
Approved='P'
- not moderated:
Approved='Y'
* if not moderated:
- Thread($tid).PostsInThread++
- Thread($tid).LastPost=GREATEST(LastPost,$npid)
- Thread($tid).Lastchanged=IF(LastPost>$npid,Lastchanged,$npid.Time)
- Forums($fid).PostsInForum++
- Forums($fid).LastPost=GREATEST(LastPost,$npid)
- Forums($fid).Updated=GREATEST(Updated,$NOW)
- Global.Updated=GREATEST(Updated,$NOW)
* Thread($tid).Hits++
* write Forumlog-entry
#----- U09: mark all threads as read
* foreach thread $ctid with unread posts:
- insert Forumreads($uid,$fid,$ctid).Time=$NOW, HasNew=0
- see 'forum/forum_functions.php', ForumRead::mark_forum_read($markread,$is_moderator)
* Forumreads($uid,$fid,tid=0).Time= .Time - 1 day (for forum-read)
* Forumreads($uid,fid=0,tid=0).Time= .Time - 1 day (for global-forum read)
#----- A01: add new forum
* insert Forums with name, description, order, options
(insert with defaults)
#----- A02: edit forum
* update Forums($fid) on name, description, order, options
#----- A03: move thread
# move thread $tid from forum $sfid to forum $nfid
#TODO new feature, not implemented yet;
# but doubtful to come (probably brings more confusion than bringing order).
# problem is to cope with race-conditions when user at this moment replies in thread to be moved.
#
# also needed for implementation:
#TODO need URLs (also old), updated independently from forum_id (only on thread_id) to preserve old links !!
#TODO need old_ID be independent from forum_id -> recalc old_ID, see 'forum/old_links_redirect.php'
#TODO (optional) need READ-ONCE message feature (=NOTIFY-message deleting itself if marked as read)
* move thread-posts:
- Posts[Thread_ID=$tid].Forum_ID=$nfid
* move forum-reads:
- Forumreads[Thread_ID=$tid].Forum_ID=$nfid
* update source-forum (Forum_ID=$sfid):
- Forums.ThreadsInForum--
- Forums.PostsInForum -= Thread($tid).PostsInThread
- Forums.Updated=GREATEST(Updated,$NOW)
- recalc Forums.LastPost
* update target-forum (Forum_ID=$nfid):
- Forums.LastPost=GREATEST(LastPost,Threads($tid).LastPost)
- Forums.ThreadsInForum++
- Forums.PostsInForum += Thread($tid).PostsInThread
- Forums.Updated=GREATEST(Updated,$NOW)
* if admin chooses: send READ-ONCE message to all Thread($tid).User_ID
informing about thread-move
#FIXME * trigger on thread/forum/gobal
* write Forumlog-entry
#----- A04: approve post
* update Posts($pid):
- $dpid := Posts($pid).Time
- Approved='Y'
* Thread($tid).PostsInThread++
- $ptid := Thread($tid).PostsInThread
* if $ptid == 1: Forums($fid).ThreadsInForum++
* Forums($fid).PostsInForum++
* Forums($fid).LastPost=GREATEST(LastPost,$pid)
* if $dpid > Thread($pid).Lastchanged:
- Thread($pid).LastPost = $pid
- Thread($pid).Lastchanged = $dpid
* Forums($fid).Updated=GREATEST(Updated,$NOW)
* Global.Updated=GREATEST(Updated,$NOW)
#----- A05: reject post
* update Posts($pid):
- $dpid := Posts($pid).Time
- Approved='N'
#----- A06: hide post
* update Posts($pid).Approved='N'
* Thread($tid).PostsInThread--
$ptid := Thread($tid).PostsInThread
* if $pid == Thread($tid).LastPost:
if Thread($tid).PostsInThread>0: recalc Thread($tid).LastPost, Lastchanged
else: Thread($tid).LastPost=0
* Forums($fid).PostsInForum--
* Forums($fid).Updated=GREATEST(Updated,$NOW) (post could be a NEW one)
* if $ptid == 0: Forums($fid).ThreadsInForum--
* if $pid == Forums($fid).LastPost:
if Forums($fid).ThreadsInForum>0: recalc Forums($fid).LastPost
* Global.Updated=GREATEST(Updated,$NOW) (post could be a NEW one)
#----- A07: show post
* update Posts($pid).Approved='Y'
- $dpid := Posts($pid).Time
* Thread($tid).PostsInThread++
- $ptid := Thread($tid).PostsInThread
- $ltid := Thread($tid).LastPost
* if $dpid > Thread($pid).Lastchanged OR $ltid == 0:
- Thread($tid).LastPost=$pid
- Thread($tid).Lastchanged=$dpid
- if $ltid == Forums($fid).LastPost: Forums($fid).LastPost=$pid
* Forums($fid).Updated=GREATEST(Updated,$NOW)
* Forums($fid).PostsInForum++
* if $ptid==1: Forums($fid).ThreadsInForum++
* Global.Updated=GREATEST(Updated,$NOW)
#----- A08: cleanup
* delete old Forumreads-entries, to be placed in daily or weekly cron:
DELETE FROM Forumreads
WHERE Time < $GLOBAL_MIN_READDATE AND Thread_ID>0 AND User_ID>0 AND Forum_ID>0
#----- A09: recalc forum
* see label recalc_forum, see 'scripts/forum_consistency.php'
#----- A10: recalc thread
* see label recalc_thread, see 'scripts/forum_consistency.php'
#-------- (4) FAQ -------------------------------------------------------------
This section contains some questions regarding the usage of forums for
developers of DGS. More documentation you will also find in the sources.
* Q ?
A.
#-------- (5) Possible Future Enhancements ------------------------------------
# Priority (1=high, 5=low, ?=unknown) is added, e.g. Prio(1)
This section outlines some ideas regarding the forums, but are not necessarily
going to be implemented. Just wanted to write them down somewhere.
* Prio(1): "Forum tags" to add tags to posts (public or private customizable)
- allows to "agree"/"disagree" even realize 'voting'
- can mark post as "ad", with certain ""
- tags should be searchable
* Prio(?): use-case A03 (moving threads): could be irritating for users when their
posts have been moved. Also there are seldom threads solely assignable to ONE
forum.
#-------- (6) Known Bugs ------------------------------------------------------
# Priority (1=high, 3=low, E=enhancement/feature) is added, e.g. Prio(1)
The listed problems in this section doesn't disturb the basic functioning
of the forum, but are identified as bugs, though some could also
be considered a feature ;)
* Prio(3): approved and shown (once hidden) posts could be left unseen to the
readers when in the meantime the post-thread has a younger read of a user.
The post then "appears" in the shadows and remain undetected.