DIR : /home/kozerus/public_html/go/dgs0/specs/forums.txt

/home/kozerus/public_html/go/dgs0/specs

################################################################################
# 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 <http://www.gnu.org/licenses/>.
## */


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 "<topics>"
  - 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.

//


koh5_pano



Your browser does not support the HTML5 canvas element.


Drag mouse to navigate.

Navigation





17.Aug.2010, Martin Wengenmayer