################################################################################ # Topic: Forums # Description: Forum capabilities for DGS # Author of this document: juga ## /* ## Dragon Go Server ## Copyright (C) 2001- 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 (U10) Set/remove tag on forum post (U11) Change Likes counter on forum post # 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/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) CountLikes - counter for likes on this post # entered data Forum_ID, Thread_ID, User_ID, Parent_ID, Flags, AnswerNr, Depth, PosIndex, Time, Lastedited, Approved, Subject, Text, crc32 # calculated fields PosIndex (from Parent_ID, Depth, AnswerNr), crc32 (from Text) # triggered fields ID; LastPost, Lastchanged, PostsInThread, Hits; PosIndex, Lastedited, Approved, CountLikes #----- 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>'' #----- Posts # Calculate likes counter for all posts Posts($pid): # calculate CountLikes := X_CountLikes SELECT COUNT(*) AS X_CountLikes FROM Tagged WHERE Post_ID=$pid AND Tag_ID=-1 #-------- (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) #----- U10: set/remove tag on forum post - set: insert Tagged ($uid,$pid,$tag_id) - remove: delete Tagged ($uid,$pid,$tag_id) #----- U11: change likes counter on forum post - increase counter: - update Posts set CountLikes=CountLikes+1 where ID=$pid - insert Tagged ($uid,$pid,$tag_id) with $tag_id == -1 - decrease counter: - update Posts set CountLikes=CountLikes-1 where ID=$pid - delete Tagged ($uid,$pid,$tag_id) with $tag_id == -1 #----- 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 (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(?): 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.