Add edit functionality to forum management and implemented a sick drag-and-drop reordering system for forums
authorDan
Tue, 13 Nov 2007 22:28:30 -0500
changeset 7 37387f84fe25
parent 6 3f66ec435f08
child 8 850e1b1209a9
Add edit functionality to forum management and implemented a sick drag-and-drop reordering system for forums
decir/admincp/admin_base.php
decir/admincp/admin_forums.php
decir/admincp/admin_index.php
decir/bbcode.php
decir/functions.php
decir/js/admin/reorder.js
plugins/Decir.php
--- a/decir/admincp/admin_base.php	Tue Nov 13 19:39:50 2007 -0500
+++ b/decir/admincp/admin_base.php	Tue Nov 13 22:28:30 2007 -0500
@@ -52,6 +52,8 @@
   $GLOBALS['template'] = new template();
   $template =& $GLOBALS['template'];
   
+  $template->add_header('<script type="text/javascript" src="' . scriptPath . '/decir/js/admin/reorder.js"></script>');
+  
   $template->header();
   ?>
   Add or remove forums, control user permissions, and check forum statistics.
--- a/decir/admincp/admin_forums.php	Tue Nov 13 19:39:50 2007 -0500
+++ b/decir/admincp/admin_forums.php	Tue Nov 13 22:28:30 2007 -0500
@@ -28,7 +28,20 @@
   
   if ( isset($_POST['act']) )
   {
-    switch ( $_POST['act'] )
+    $act = ( strpos($_POST['act'], ';') ) ? substr($_POST['act'], 0, strpos($_POST['act'], ';')) : $_POST['act'];
+    if ( strpos($_POST['act'], ';') )
+    {
+      $parms = substr($_POST['act'], strpos($_POST['act'], ';') + 1);
+      preg_match_all('/([a-z0-9_]+)=([^;]*)/', $parms, $matches);
+      $parms = array();
+      foreach ( $matches[2] as $id => $parmdata )
+      {
+        if ( preg_match('/^[0-9]+$/', $parmdata) )
+          $parmdata = intval($parmdata);
+        $parms[ $matches[1][$id] ] = $parmdata;
+      }
+    }
+    switch ( $act )
     {
       case "create":
       case "create_finish":
@@ -49,7 +62,7 @@
         
         $db->free_result();
         
-        if ( $_POST['act'] == 'create_finish' )
+        if ( $act == 'create_finish' )
         {
           $errors = array();
           $forum_type = intval($_POST['forum_type']);
@@ -73,6 +86,7 @@
           {
             // Errors encountered - bounce form back to the user
             $show_main_menu = false;
+            echo '<div class="error-box">The forum could not be created.<ul><li>' . implode("</li>\n      <li>", $errors) . '</li></ul></div>';
             $form = new Decir_Admin_SmartForm_Forum(DECIR_ADMIN_MODE_CREATE);
             $form->forum_name = $forum_name;
             $form->forum_desc = $forum_desc;
@@ -102,6 +116,128 @@
         $form->category_list = $cats;
         echo $form->html();
         break;
+      case 'edit':
+      case 'edit_finish':
+        
+        if ( !isset($parms['fid']) || ( isset($parms['fid']) && !is_int($parms['fid']) ) )
+        {
+          echo '<div class="error-box">Invalid forum ID passed to editor.</div>';
+          break;
+        }
+        
+        // Fetch category list
+        $q = $db->sql_query('SELECT forum_id, forum_name FROM ' . table_prefix . 'decir_forums WHERE forum_type = ' . FORUM_CATEGORY . ';');
+        if ( !$q )
+          $db->_die('Decir admin_forums.php retrieving category count');
+        $need_category = ( $db->numrows() < 1 );
+        $cats = array();
+        if ( !$need_category )
+        {
+          while ( list($cat_id, $cat_name) = $db->fetchrow_num() )
+          {
+            $cats[ $cat_id ] = $cat_name;
+          }
+        }
+        
+        $db->free_result();
+        
+        // $fid is safe (validated as an integer).
+        $fid =& $parms['fid'];
+        $q = $db->sql_query('SELECT forum_id, forum_name, forum_desc, parent, forum_type FROM ' . table_prefix . 'decir_forums WHERE forum_id = ' . $fid . ';');
+        if ( !$q )
+          $db->_die('Decir admin_forums.php selecting forum data for edit');
+        
+        $row = $db->fetchrow();
+        $db->free_result();
+        
+        if ( $act == 'edit_finish' )
+        {
+          $errors = array();
+          // Validate and update
+          if ( $row['forum_type'] == FORUM_FORUM )
+          {
+            $forum_name = trim($_POST['forum_name']);
+            if ( empty($forum_name) )
+              $errors[] = 'Please enter a name for this forum.';
+            
+            $forum_desc = trim($_POST['forum_desc']);
+            if ( empty($forum_desc) )
+              $errors[] = 'Please enter a description for this forum.';
+            
+            $forum_parent = intval($_POST['forum_parent']);
+            if ( !isset($cats[$forum_parent]) )
+              $errors[] = 'Invalid parent category';
+            
+            $forum_name_db = $db->escape($forum_name);
+            $forum_desc_db = $db->escape($forum_desc);
+            
+            $sql = 'UPDATE ' . table_prefix . "decir_forums SET forum_name='$forum_name_db',forum_desc='$forum_desc_db',parent=$forum_parent WHERE forum_id = $fid;";
+          }
+          else if ( $row['forum_type'] == FORUM_CATEGORY )
+          {
+            $forum_name = trim($_POST['forum_name']);
+            if ( empty($forum_name) )
+              $errors[] = 'Please enter a name for this forum.';
+            $forum_name_db = $db->escape($forum_name);
+            
+            $sql = 'UPDATE ' . table_prefix . "decir_forums SET forum_name='$forum_name_db' WHERE forum_id = $fid;";
+          }
+          else
+          {
+            $db->_die('Mom, I feel sick. Can I lay down for a while? ' . __FILE__ . ':' . __LINE__);
+          }
+          if ( count($errors) < 1 )
+          {
+            if ( $db->sql_query($sql) )
+            {
+              $show_main_menu = true;
+              echo '<div class="info-box">The forum or category was updated.</div>';
+              break;
+            }
+            else
+            {
+              $db->_die('Decir admin_forums.php update forum main SQL query');
+            }
+          }
+          else
+          {
+            echo '<div class="error-box">The forum was not updated because you entered something invalid.<ul><li>' . implode("</li>\n      <li>", $errors) . '</li></ul></div>';
+          }
+        }
+        
+        // This is the amazing part. We'll let the smart form do the work for us.
+        $form = new Decir_Admin_SmartForm_Forum(DECIR_ADMIN_MODE_EDIT);
+        $form->forum_name = $row['forum_name'];
+        $form->forum_desc = $row['forum_desc'];
+        $form->forum_type = $row['forum_type'];
+        $form->forum_parent = $row['parent'];
+        $form->forum_id = $row['forum_id'];
+        $form->category_list = $cats;
+        echo $form->html();
+        
+        $show_main_menu = false;
+        break;
+      case 'save_order':
+        $order = explode(',', $_POST['forum_order']);
+        $i = 0;
+        $sql = array();
+        foreach ( $order as $forum_id )
+        {
+          $i++;
+          if ( strval(intval($forum_id)) != $forum_id )
+          {
+            echo '<p>Hacking attempt</p>';
+            break;
+          }
+          $sql[] = 'UPDATE ' . table_prefix . "decir_forums SET forum_order = $i WHERE forum_id = $forum_id;";
+        }
+        foreach ( $sql as $s )
+        {
+          if ( !$db->sql_query($s) )
+            $db->_die('Decir admin_forums.php updating forum order');
+        }
+        echo '<div class="info-box">The forum order was updated.</div>';
+        break;
     }
   }
   
@@ -116,17 +252,65 @@
                 <th colspan="4">Forum administration</th>
               </tr>';
     // Select and display all forums
-    $q = $db->sql_unbuffered_query('SELECT forum_id, forum_name, forum_type FROM ' . table_prefix . 'decir_forums ORDER BY ( forum_type = ' . FORUM_CATEGORY . ' ) DESC, forum_order;');
+    $q = $db->sql_unbuffered_query('SELECT forum_id, forum_name, forum_desc, forum_type, num_topics, num_posts FROM ' . table_prefix . 'decir_forums GROUP BY parent, forum_id ORDER BY forum_order;');
     
     if ( !$q )
       $db->_die('Decir admin_forums.php selecting main forum datum');
     
+    $order_forums = array();
+    $order_cats = array();
     if ( $row = $db->fetchrow() )
     {
+      $cat_open = false;
+      echo '<tr>
+              <th class="subhead">Forum</th>
+              <th class="subhead" style="max-width: 50px;">Topics</th>
+              <th class="subhead" style="max-width: 50px;">Posts</th>
+              <th class="subhead">Admin tasks</th>
+            </tr>';
       do
       {
+        switch ( $row['forum_type'] )
+        {
+          case FORUM_FORUM:
+            // Forum
+            echo '<tr>
+                    <td class="row2 decir_forum"><input type="hidden" value="' . $row['forum_id'] . '" />
+                      <b><a href="' . makeUrlNS('DecirForum', $row['forum_id']) . '">'
+                      . $row['forum_name'] . '</a></b><br />' . $row['forum_desc'].'
+                    </td>
+                   <td class="row3" style="text-align: center;">' . $row['num_topics'] . '</td>
+                   <td class="row3" style="text-align: center;">' . $row['num_posts'] . '</td>
+                   <td class="row1" style="text-align: center;">';
+            
+            echo '<button name="act" value="edit;fid=' . $row['forum_id'] . '">Edit</button>&nbsp;';
+            echo '<button name="act" value="delete;fid=' . $row['forum_id'] . '">Delete</button>';
+            
+            echo '</td>
+                 </tr>';
+            $order_forums[] = $row['forum_id'];
+            break;
+          case FORUM_CATEGORY:
+            // Category
+            if ( $cat_open )
+              echo '</tbody>';
+            echo '<tr>
+                    <td class="row1 decir_category" colspan="1"><input type="hidden" value="' . $row['forum_id'] . '" />
+                      <h3 style="margin: 0; padding: 0;">' . $row['forum_name'] . '</h3>
+                    </td>
+                    <td class="row2" colspan="2"></td>';
+            echo '<td class="row1" style="text-align: center;">';
+            echo '<button name="act" value="edit;fid=' . $row['forum_id'] . '">Edit</button>&nbsp;';
+            echo '<button name="act" value="delete;fid=' . $row['forum_id'] . '">Delete</button>';
+            echo '</td>';
+            echo '</tr>
+                  <tbody id="forum_cat_' . $row['forum_id'] . '">';
+            $cat_open = true;
+            $order_cats[] = $row['forum_id'];
+            break;
+        }
       }
-      while ( $row = $db->fetchrow() );
+      while ( $row = $db->fetchrow($q) );
     }
     else
     {
@@ -135,13 +319,16 @@
     
     // Create forum button
     echo '    <tr>
-                <th class="subhead">
+                <th class="subhead" colspan="4">
                   <button name="act" value="create">Create new forum</button>
+                  <button name="act" value="save_order">Save forum order</button>
                 </th>
               </tr>';
     
     echo '  </table>
           </div>';
+    $order = /* implode(',', $order_cats) . ';' . */ implode(',', $order_forums);
+    echo '<input type="text" name="forum_order" id="forum_order" value="' . $order . '" />';
     echo "</form>";
   }
 }
@@ -165,14 +352,21 @@
   var $form_mode;
   
   /**
-   * The name of the forum - only used in edit mode.
+   * The unique ID of the forum - only used in edit mode.
+   * @var int
+   */
+  
+  var $forum_id = 0;
+  
+  /**
+   * The name of the forum - only used in edit mode or if performing a bounceback from a failed form validation.
    * @var string
    */
   
   var $forum_name = '';
   
   /**
-   * The description of the forum - only used in edit mode.
+   * The description of the forum - only used in edit mode or if performing a bounceback from a failed form validation.
    * @var string
    */
   
@@ -200,6 +394,13 @@
   var $category_list = array();
   
   /**
+   * The parent category of the forum we're editing.
+   * @var int
+   */
+  
+  var $forum_parent = -1;
+  
+  /**
    * Instance ID for javascripting
    * @var string
    */
@@ -245,6 +446,7 @@
     $tpl_code = <<<EOF
         <!-- Start forum creation/edit smartform {INSTANCE_ID} -->
         
+        <!-- BEGIN mode_is_create -->
         <script type="text/javascript">
         
           function set_form_type_category_{INSTANCE_ID}()
@@ -271,8 +473,8 @@
           }
           
           addOnloadHook(set_form_type_auto_{INSTANCE_ID});
-        
         </script>
+        <!-- END mode_is_create -->
         
         <form action="{FORM_ACTION}" name="decir_forum_smartform_{INSTANCE_ID}" method="post" enctype="multipart/form-data">
         
@@ -283,7 +485,7 @@
                 <!-- BEGIN mode_is_create -->
                 Create new forum
                 <!-- BEGINELSE mode_is_create -->
-                Edit forum {FORUM_NAME}
+                Editing {FORUM_NAME}
                 <!-- END mode_is_create -->
               </th>
             </tr>
@@ -304,7 +506,15 @@
             <!-- END mode_is_create -->
             <tr>
               <td class="row2" style="width: 50%;">
-                Forum description:
+                <!-- BEGINNOT mode_is_create -->
+                  <!-- BEGINNOT type_is_forum -->
+                    Category name:
+                  <!-- BEGINELSE type_is_forum -->
+                    Forum name:
+                  <!-- END type_is_forum -->
+                <!-- BEGINELSE mode_is_create -->
+                  Forum name:
+                <!-- END mode_is_create -->                  
               </td>
               <td class="row1" style="width: 50%;">
               <input type="text" name="forum_name" size="40" value="{FORUM_NAME}" />
@@ -331,7 +541,11 @@
             </tr>
             <tr>
               <th class="subhead" colspan="2">
+                <!-- BEGIN mode_is_create -->
                 <button name="act" value="create_finish"><b>Create category</b></button>
+                <!-- BEGINELSE mode_is_create -->
+                <button name="act" value="edit_finish;fid={FORUM_ID}"><b>Save changes</b></button>
+                <!-- END mode_is_create -->
                 <button name="act" value="noop" style="font-weight: normal;">Cancel</button>
               </th>
             </tr>
@@ -367,7 +581,7 @@
               </tr>
               <tr>
                 <td class="row2">
-                  Create in category:
+                  <!-- BEGIN mode_is_create -->Create in category:<!-- BEGINELSE mode_is_create -->Parent category:<!-- END mode_is_create -->
                 </td>
                 <td class="row1">
                   <select name="forum_parent">
@@ -376,7 +590,11 @@
               </tr>
               <tr>
                 <th class="subhead" colspan="2">
+                  <!-- BEGIN mode_is_create -->
                   <button name="act" value="create_finish"><b>Create forum</b></button>
+                  <!-- BEGINELSE mode_is_create -->
+                  <button name="act" value="edit_finish;fid={FORUM_ID}"><b>Save changes</b></button>
+                  <!-- END mode_is_create -->
                   <button name="act" value="noop" style="font-weight: normal;">Cancel</button>
                 </th>
               </tr>
@@ -399,7 +617,8 @@
     {
       $cat_id = intval($cat_id);
       $cat_name = htmlspecialchars($cat_name);
-      $category_list .= "<option value=\"$cat_id\">$cat_name</option>\n                    ";
+      $sel = ( $cat_id == $this->forum_parent ) ? ' selected="selected"' : '';
+      $category_list .= "<option {$sel}value=\"$cat_id\">$cat_name</option>\n                    ";
     }
     
     // FIXME: these should really call addslashes and htmlspecialchars
@@ -408,6 +627,7 @@
         'INSTANCE_ID' => $this->instance_id,
         'FORUM_NAME' => htmlspecialchars($this->forum_name),
         'FORUM_DESC' => htmlspecialchars($this->forum_desc),
+        'FORUM_ID' => $this->forum_id,
         'FORM_ACTION' => makeUrlNS('Special', 'DecirAdmin', 'module=' . $paths->nslist['Admin'] . 'DecirForums', true),
         'TYPE_FORUM' => FORUM_FORUM,
         'TYPE_CATEGORY' => FORUM_CATEGORY,
--- a/decir/admincp/admin_index.php	Tue Nov 13 19:39:50 2007 -0500
+++ b/decir/admincp/admin_index.php	Tue Nov 13 22:28:30 2007 -0500
@@ -62,7 +62,7 @@
   }
   else
   {
-    $avg_posts = $num_posts / $board_age_days;
+    $avg_posts = round($num_posts / $board_age_days, 3);
   }
   
   echo '<h3>Administration home</h3>';
--- a/decir/bbcode.php	Tue Nov 13 19:39:50 2007 -0500
+++ b/decir/bbcode.php	Tue Nov 13 22:28:30 2007 -0500
@@ -49,6 +49,9 @@
   // Newlines
   $text = str_replace("\n", "<br />\n", $text);
   
+  // Wikilinks
+  $text = RenderMan::parse_internal_links($text);
+  
   // Restore [code] blocks
   $text = decir_bbcode_restore_code($text, $bbcode_uid, $_code);
   
--- a/decir/functions.php	Tue Nov 13 19:39:50 2007 -0500
+++ b/decir/functions.php	Tue Nov 13 22:28:30 2007 -0500
@@ -403,10 +403,15 @@
 function decir_show_footers()
 {
   global $db, $session, $paths, $template, $plugins; // Common objects
+  echo '<p style="text-align: center; margin: 20px 0 0 0; color: #808080;"><small>';
+  echo 'Powered by the <a href="http://decir.enanocms.org/">Decir Forum Engine</a> for <a href="' . makeUrlNS('Special', 'About_Enano') . '">Enano</a><br />
+        &copy; 2007 Dan Fuhry and the Enano CMS team';
   if ( $session->user_level >= USER_LEVEL_ADMIN )
   {
-    echo '<p style="text-align: center; margin: 20px 0 0 0;"><small><a href="' . makeUrlNS('Special', 'DecirAdmin') . '">Administration control panel</a></small></p>';
+    echo '<br />';
+    echo '<a href="' . makeUrlNS('Special', 'DecirAdmin') . '">Administration control panel</a>';
   }
+  echo '</small></p>';
 }
 
 ?>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/decir/js/admin/reorder.js	Tue Nov 13 22:28:30 2007 -0500
@@ -0,0 +1,175 @@
+var reorder_state = {
+  _: false,
+  type: false,
+  id: false,
+  obj: false,
+  over: false
+}
+
+function decir_admin_dragforum_start(e)
+{
+  document.onmouseup = decir_admin_dragforum_close;
+  
+  reorder_state.obj = this;
+  reorder_state.over = this;
+  
+  reorder_state._ = true;
+  if ( $dynano(this).hasClass('decir_forum') )
+  {
+    reorder_state.type = 'forum';
+  }
+  else if ( $dynano(this).hasClass('decir_category') )
+  {
+    alert(this.parentNode.DecirForumID);
+    document.getElementById('forum_cat_' + this.parentNode.DecirForumID).lastChild.lastChild.style.borderBottom = '5px solid #000000';
+    reorder_state.type = 'category';
+  }
+  
+  document.onmousemove = decir_admin_dragforum_onmouse;
+  
+  return false;
+}
+
+function decir_admin_dragforum_onmouse(e)
+{
+  setMousePos(e);
+  
+  // Determine threshold for moving the target
+  var nodes = reorder_state.obj.parentNode.parentNode.childNodes;
+  var threshold = new Object();
+  for ( var i = 0; i < nodes.length; i++ )
+  {
+    var node = nodes[i];
+    if ( node.tagName == 'TR' && node.DecirForumID )
+    {
+      // This is a row with a forum in it - add to the threshold list.
+      var t = $(node).Top();
+      var h = $(node).Height();
+      threshold[node.DecirForumID] = t + ( h / 2 );
+    }
+  }
+  // Move the bar if mouseY is lower than the threshold for the next cell up or higher (lower on screen) than the next cell down.
+  var moveme = false;
+  var current_threshold = threshold[ reorder_state.over.parentNode.DecirForumID ];
+  var next_threshold = threshold[ parseInt(reorder_state.over.parentNode.DecirForumID) + 1 ];
+  if ( !next_threshold )
+    next_threshold = 99999999;
+  window.console.debug('current ', current_threshold, ' next ', next_threshold);
+  if ( mouseY < current_threshold )
+  {
+    var prev_tr = reorder_state.over.parentNode.previousSibling;
+    if ( !prev_tr )
+      // already at the top
+      return false;
+    // find the prev_td
+    var i = 0;
+    var prev_td;
+    while ( true )
+    {
+      prev_td = prev_tr.childNodes[i];
+      if ( !prev_td )
+        continue;
+      if ( prev_td.tagName == 'TD' || i == 10000 )
+        break;
+      i++;
+    }
+    if ( prev_td.tagName != 'TD' )
+      return false;
+    reorder_state.over.style.borderBottom = null;
+    reorder_state.over = prev_td;
+    reorder_state.over.style.borderBottom = '5px solid #000000';
+  }
+  else if ( mouseY > next_threshold )
+  {
+    var next_tr = reorder_state.over.parentNode.nextSibling;
+    if ( !next_tr )
+      // already at the top
+      return false;
+    // find the next_td
+    var i = 0;
+    var next_td;
+    while ( true )
+    {
+      next_td = next_tr.childNodes[i];
+      if ( i >= 100 )
+        break;
+      if ( !next_td )
+      {
+        i++;
+        continue;
+      }
+      if ( next_td.tagName == 'TD' || i == 100 )
+        break;
+      i++;
+    }
+    if ( !next_td )
+      return false;
+    if ( next_td.tagName != 'TD' )
+      return false;
+    reorder_state.over.style.borderBottom = null;
+    reorder_state.over = next_td;
+    reorder_state.over.style.borderBottom = '5px solid #000000';
+  }
+}
+
+function decir_admin_dragforum_reset(e)
+{
+  var cls = ( reorder_state.type == 'forum' ? 'decir_forum' : 'decir_category' ); 
+  var forums = getElementsByClassName(document, '*', cls);
+  for ( var i = 0; i < forums.length; i++ )
+  {
+    forums[i].onmousedown = decir_admin_dragforum_start;
+    forums[i].style.cursor = 'move';
+    forums[i].style.borderBottom = null;
+  }
+}
+
+function decir_admin_dragforum_close(e)
+{
+  document.onmousemove = function(e) {
+    setMousePos(e);
+  }
+  document.onmouseup = function() {};
+  decir_admin_dragforum_reset(e);
+  
+  reorder_state._ = false;
+  
+  // Move the row (or rather copy and delete)
+  var newnode = reorder_state.obj.parentNode.cloneNode(true);
+  insertAfter(reorder_state.obj.parentNode.parentNode, newnode, reorder_state.over.parentNode);
+  reorder_state.obj.parentNode.parentNode.removeChild(reorder_state.obj.parentNode);
+  
+  // for some reason this needs to be called again in gecko (to reinit some values)
+  decir_admin_dragforum_onload();
+  
+  var idlist = [];
+  var forums = getElementsByClassName(document, '*', 'decir_forum');
+  for ( var i = 0; i < forums.length; i++ )
+  {
+    idlist.push(forums[i].parentNode.DecirForumID);
+  }
+  idlist = implode(',', idlist);
+  $('forum_order').object.value = idlist;
+}
+
+var decir_admin_dragforum_onload = function(e)
+{
+  var forums = getElementsByClassName(document, '*', 'decir_forum');
+  for ( var i = 0; i < forums.length; i++ )
+  {
+    forums[i].onmousedown = decir_admin_dragforum_start;
+    forums[i].style.cursor = 'move';
+    forums[i].parentNode.DecirForumID = forums[i].firstChild.value;
+    forums[i].title = 'Click and drag to re-order this forum';
+  }
+  var forums = getElementsByClassName(document, '*', 'decir_category');
+  for ( var i = 0; i < forums.length; i++ )
+  {
+    //forums[i].onmousedown = decir_admin_dragforum_start;
+    //forums[i].style.cursor = 'move';
+    //forums[i].parentNode.DecirForumID = forums[i].firstChild.value;
+  }
+}
+
+addOnloadHook(decir_admin_dragforum_onload);
+
--- a/plugins/Decir.php	Tue Nov 13 19:39:50 2007 -0500
+++ b/plugins/Decir.php	Tue Nov 13 22:28:30 2007 -0500
@@ -38,14 +38,28 @@
 function decir_early_init(&$paths, &$session)
 {
   $paths->addAdminNode('Decir forum configuration', 'General settings', 'DecirGeneral');
-  $paths->nslist['DecirForum']  = $paths->nslist['Special'] . 'Forum/ViewForum/';
-  $paths->nslist['DecirPost']   = $paths->nslist['Special'] . 'Forum/Post/';
-  $paths->nslist['DecirTopic']  = $paths->nslist['Special'] . 'Forum/Topic/';
+  $paths->create_namespace('DecirForum', $paths->nslist['Special'] . 'Forum/ViewForum/');
+  $paths->create_namespace('DecirPost',  $paths->nslist['Special'] . 'Forum/Post/');
+  $paths->create_namespace('DecirTopic', $paths->nslist['Special'] . 'Forum/Topic/');
   
   $session->register_acl_type('decir_see_forum',  AUTH_ALLOW, 'See forum in index', Array('read'),             'DecirForum');
   $session->register_acl_type('decir_view_forum', AUTH_ALLOW, 'View forum',         Array('decir_see_forum'),  'DecirForum');
   $session->register_acl_type('decir_post',       AUTH_ALLOW, 'Post new topics',    Array('decir_view_forum'), 'DecirForum');
   $session->register_acl_type('decir_reply',      AUTH_ALLOW, 'Reply to topics',    Array('decir_post'),       'DecirTopic');
+  $session->register_acl_type('decir_edit_own',   AUTH_ALLOW, 'Edit own posts',     Array('decir_post'),       'DecirPost');
+  $session->register_acl_type('decir_edit_other', AUTH_DISALLOW, 'Edit others\' posts', Array('decir_post'),   'DecirPost');
+  $session->register_acl_type('decir_delete_own_post_soft', AUTH_ALLOW, 'Delete own posts (soft)', Array('decir_edit_own'), 'DecirPost');
+  $session->register_acl_type('decir_delete_own_post_hard', AUTH_DISALLOW, 'Delete own posts (hard)', Array('decir_delete_own_post_soft'), 'DecirPost');
+  $session->register_acl_type('decir_delete_other_post_soft', AUTH_DISALLOW, 'Delete others\' posts (soft)', Array('decir_edit_other'), 'DecirPost');
+  $session->register_acl_type('decir_delete_other_post_hard', AUTH_DISALLOW, 'Delete others\' posts (hard)', Array('decir_delete_other_post_soft'), 'DecirPost');
+  $session->register_acl_type('decir_undelete_own_post', AUTH_DISALLOW, 'Undelete own posts', Array('decir_edit_own'), 'DecirPost');
+  $session->register_acl_type('decir_undelete_other_post', AUTH_DISALLOW, 'Undelete others\' posts', Array('decir_edit_other'), 'DecirPost');
+  $session->register_acl_type('decir_undelete_own_topic', AUTH_DISALLOW, 'Undelete own topics', Array('read'), 'DecirTopic');
+  $session->register_acl_type('decir_undelete_other_topic', AUTH_DISALLOW, 'Undelete others\' topics', Array('read'), 'DecirTopic');
+  $session->register_acl_type('decir_see_deleted_post', AUTH_ALLOW, 'See placeholders for deleted posts', Array('read'), 'Special|DecirPost|DecirTopic|DecirForum');
+  $session->register_acl_type('decir_see_deleted_post_full', AUTH_DISALLOW, 'Read the full contents of deleted posts', Array('decir_see_deleted_post'), 'Special|DecirPost|DecirTopic|DecirForum');
+  $session->register_acl_type('decir_see_deleted_topic', AUTH_ALLOW, 'See placeholders for deleted topics', Array('read'), 'DecirTopic|DecirForum');
+  $session->register_acl_type('decir_see_deleted_topic_full', AUTH_DISALLOW, 'Read the full contents of deleted topics', Array('decir_see_deleted_topic'), 'Special|DecirTopic|DecirForum');
 }
 
 function page_Special_Forum()
@@ -81,6 +95,15 @@
     case 'new':
       require('posting.php');
       break;
+    case 'edit':
+      require('edit.php');
+      break;
+    case 'delete':
+      require('delete.php');
+      break;
+    case 'restoretopic':
+      require('restoretopic.php');
+      break;
   }
   
   chdir($curdir);