Fixed the fact that cron just didn't work at all (brain fart that day or something)
authorDan
Sat, 01 Mar 2008 18:57:07 -0500
changeset 468 194a19711346
parent 467 e4bbd6fb8df3
child 469 3f11279bac50
Fixed the fact that cron just didn't work at all (brain fart that day or something)
ajax.php
cron.php
includes/clientside/css/enano-shared-ie.css
includes/clientside/static/l10n.js
includes/common.php
includes/constants.php
includes/dbal.php
includes/functions.php
includes/json.php
includes/pageprocess.php
includes/pageutils.php
includes/template.php
index.php
install/schemas/mysql_stage2.sql
install/schemas/postgresql_stage2.sql
install/schemas/upgrade/1.1.2-1.1.3-mysql.sql
install/schemas/upgrade/1.1.2-1.1.3-postgresql.sql
language/english/core.json
themes/oxygen/css/bleu.css
--- a/ajax.php	Sat Mar 01 18:56:37 2008 -0500
+++ b/ajax.php	Sat Mar 01 18:57:07 2008 -0500
@@ -157,42 +157,53 @@
         }
       }
       
+      $return['undo_info'] = array();
+      
       if ( $revid > 0 )
       {
         // Retrieve information about this revision and the current one
         $q = $db->sql_query('SELECT l1.author AS currentrev_author, l2.author AS oldrev_author FROM ' . table_prefix . 'logs AS l1
   LEFT JOIN ' . table_prefix . 'logs AS l2
-    ON ( l2.time_id = ' . $revid . '
+    ON ( l2.log_id = ' . $revid . '
          AND l2.log_type  = \'page\'
          AND l2.action    = \'edit\'
          AND l2.page_id   = \'' . $db->escape($paths->page_id)   . '\'
          AND l2.namespace = \'' . $db->escape($paths->namespace) . '\'
+         AND l2.is_draft != 1
         )
   WHERE l1.log_type  = \'page\'
     AND l1.action    = \'edit\'
     AND l1.page_id   = \'' . $db->escape($paths->page_id)   . '\'
     AND l1.namespace = \'' . $db->escape($paths->namespace) . '\'
-    AND l1.time_id >= ' . $revid . '
+    AND l1.time_id   > ' . $page->revision_time . '
+    AND l1.is_draft != 1
   ORDER BY l1.time_id DESC;');
         if ( !$q )
           $db->die_json();
         
-        $rev_count = $db->numrows() - 1;
-        if ( $rev_count == -1 )
+        if ( $db->numrows() > 0 )
         {
-          $return = array(
-              'mode' => 'error',
-              'error' => '[Internal] No rows returned by revision info query. SQL:<pre>' . $db->latest_query . '</pre>'
+          $rev_count = $db->numrows() - 1;
+          if ( $rev_count == -1 )
+          {
+            $return = array(
+                'mode' => 'error',
+                'error' => '[Internal] No rows returned by revision info query. SQL:<pre>' . $db->latest_query . '</pre>'
+              );
+          }
+          else
+          {
+            $row = $db->fetchrow();
+            $return['undo_info'] = array(
+              'old_author'     => $row['oldrev_author'],
+              'current_author' => $row['currentrev_author'],
+              'undo_count'     => $rev_count
             );
+          }
         }
         else
         {
-          $row = $db->fetchrow();
-          $return['undo_info'] = array(
-            'old_author'     => $row['oldrev_author'],
-            'current_author' => $row['currentrev_author'],
-            'undo_count'     => $rev_count
-          );
+          $return['revid'] = $revid = 0;
         }
       }
       
@@ -218,6 +229,7 @@
       $page->send();
       break;
     case "savepage":
+      /* **** OBSOLETE **** */
       $summ = ( isset($_POST['summary']) ) ? $_POST['summary'] : '';
       $minor = isset($_POST['minor']);
       $e = PageUtils::savepage($paths->page_id, $paths->namespace, $_POST['text'], $summ, $minor);
@@ -346,15 +358,15 @@
             $return['new_captcha'] = $session->make_captcha();
           }
         }
-        
-        // If this is based on a draft version, delete the draft - we no longer need it.
-        if ( @$request['used_draft'] )
-        {
-          $q = $db->sql_query('DELETE FROM ' . table_prefix . 'logs WHERE log_type = \'page\' AND action = \'edit\'
-                                 AND page_id = \'' . $db->escape($paths->page_id) . '\'
-                                 AND namespace = \'' . $db->escape($paths->namespace) . '\'
-                                 AND is_draft = 1;');
-        }
+      }
+      
+      // If this is based on a draft version, delete the draft - we no longer need it.
+      if ( @$request['used_draft'] )
+      {
+        $q = $db->sql_query('DELETE FROM ' . table_prefix . 'logs WHERE log_type = \'page\' AND action = \'edit\'
+                               AND page_id = \'' . $db->escape($paths->page_id) . '\'
+                               AND namespace = \'' . $db->escape($paths->namespace) . '\'
+                               AND is_draft = 1;');
       }
       
       echo enano_json_encode($return);
@@ -385,13 +397,23 @@
       
       break;
     case "protect":
-      echo PageUtils::protect($paths->page_id, $paths->namespace, (int)$_POST['level'], $_POST['reason']);
+      // echo PageUtils::protect($paths->page_id, $paths->namespace, (int)$_POST['level'], $_POST['reason']);
+      $page = new PageProcessor($paths->page_id, $paths->namespace);
+      header('Content-type: application/json');
+      
+      $result = $page->protect_page(intval($_POST['level']), $_POST['reason']);
+      echo enano_json_encode($result);
       break;
     case "histlist":
       echo PageUtils::histlist($paths->page_id, $paths->namespace);
       break;
     case "rollback":
-      echo PageUtils::rollback( (int)$_GET['id'] );
+      $id = intval(@$_GET['id']);
+      $page = new PageProcessor($paths->page_id, $paths->namespace);
+      header('Content-type: application/json');
+      
+      $result = $page->rollback_log_entry($id);
+      echo enano_json_encode($result);
       break;
     case "comments":
       $comments = new Comments($paths->page_id, $paths->namespace);
@@ -405,7 +427,11 @@
       }
       break;
     case "rename":
-      echo PageUtils::rename($paths->page_id, $paths->namespace, $_POST['newtitle']);
+      $page = new PageProcessor($paths->page_id, $paths->namespace);
+      header('Content-type: application/json');
+      
+      $result = $page->rename_page($_POST['newtitle']);
+      echo enano_json_encode($result);
       break;
     case "flushlogs":
       echo PageUtils::flushlogs($paths->page_id, $paths->namespace);
--- a/cron.php	Sat Mar 01 18:56:37 2008 -0500
+++ b/cron.php	Sat Mar 01 18:57:07 2008 -0500
@@ -20,27 +20,24 @@
 define('ENANO_GIF_SPACER', base64_decode('R0lGODlhAQABAIAAAP///////yH+FUNyZWF0ZWQgd2l0aCBUaGUgR0lNUAAh+QQBCgABACwAAAAAAQABAAACAkwBADs='));
 
 // Don't need a page to load, all we should need is the Enano API
-$_GET['title'] = 'Enano:Cron';
 require('includes/common.php');
 
 global $db, $session, $paths, $template, $plugins; // Common objects
 
 // Hope now that plugins are loaded :-)
-$last_run = ( $x = getConfig('cron_last_run') ) ? $x : 0;
-$time = strval(time());
-setConfig('cron_last_run', $time);
-
 global $cron_tasks;
 
 foreach ( $cron_tasks as $interval => $tasks )
 {
-  $last_run_threshold = time() - ( $interval * 3600 );
+  $last_run = intval(getConfig("cron_lastrun_ivl_$interval"));
+  $last_run_threshold = time() - ( 3600 * $interval );
   if ( $last_run_threshold >= $last_run )
   {
     foreach ( $tasks as $task )
     {
       @call_user_func($task);
     }
+    setConfig("cron_lastrun_ivl_$interval", strval(time()));
   }
 }
 
--- a/includes/clientside/css/enano-shared-ie.css	Sat Mar 01 18:56:37 2008 -0500
+++ b/includes/clientside/css/enano-shared-ie.css	Sat Mar 01 18:57:07 2008 -0500
@@ -27,7 +27,3 @@
   filter: alpha(opacity=30);
 }
 
-* html div#ajaxEditContainer {
-  display: none;
-}
-
--- a/includes/clientside/static/l10n.js	Sat Mar 01 18:56:37 2008 -0500
+++ b/includes/clientside/static/l10n.js	Sat Mar 01 18:57:07 2008 -0500
@@ -77,7 +77,6 @@
 // isn't ready yet
 function language_onload_resched()
 {
-  alert('delaying language init by 0.2s');
   setTimeout('language_onload();', 200);
 }
 
--- a/includes/common.php	Sat Mar 01 18:56:37 2008 -0500
+++ b/includes/common.php	Sat Mar 01 18:57:07 2008 -0500
@@ -125,6 +125,7 @@
 require_once(ENANO_ROOT.'/includes/search.php');
 require_once(ENANO_ROOT.'/includes/json.php');
 require_once(ENANO_ROOT.'/includes/json2.php');
+require_once(ENANO_ROOT.'/includes/math.php');
 require_once(ENANO_ROOT.'/includes/wikiengine/Tables.php');
 require_once(ENANO_ROOT.'/includes/pageprocess.php');
 require_once(ENANO_ROOT.'/includes/tagcloud.php');
--- a/includes/constants.php	Sat Mar 01 18:56:37 2008 -0500
+++ b/includes/constants.php	Sat Mar 01 18:57:07 2008 -0500
@@ -98,6 +98,12 @@
 define('GENERAL_NOTICE', 'Information');
 define('CRITICAL_ERROR', 'Critical error');
 
+// Protection levels
+// These are still hardcoded in some places, so don't change them
+define('PROTECT_NONE', 0);
+define('PROTECT_FULL', 1);
+define('PROTECT_SEMI', 2);
+
 //
 // Enano versions progress
 //
--- a/includes/dbal.php	Sat Mar 01 18:56:37 2008 -0500
+++ b/includes/dbal.php	Sat Mar 01 18:57:07 2008 -0500
@@ -103,8 +103,8 @@
   function die_json()
   {
     $e = addslashes(htmlspecialchars(mysql_error()));
-    $q = addslashes($this->latest_query);
-    $t = "{'mode':'error','error':'An error occurred during database query.\nQuery was:\n  $q\n\nError returned by MySQL: $e'}";
+    $q = str_replace("\n", "\\n", addslashes($this->latest_query));
+    $t = "{'mode':'error','error':'An error occurred during database query.\\nQuery was:\\n  $q\\n\\nError returned by MySQL: $e'}";
     die($t);
   }
   
--- a/includes/functions.php	Sat Mar 01 18:56:37 2008 -0500
+++ b/includes/functions.php	Sat Mar 01 18:57:07 2008 -0500
@@ -144,10 +144,13 @@
     $flags .= $sep . 'lang=' . urlencode($_GET['lang']);
     $sep = '&';
   }
+  
+  $ns_prefix = "$n:";
 
   if(defined('ENANO_BASE_CLASSES_INITIALIZED'))
   {
-    $url = contentPath . $paths->nslist[$n] . $t . $flags;
+    $ns_prefix = ( isset($paths->nslist[$n]) ) ? $paths->nslist[$n] : $n . substr($paths->nslist['Special'], -1);
+    $url = contentPath . $ns_prefix . $t . $flags;
   }
   else
   {
@@ -311,7 +314,8 @@
 {
   global $db, $session, $paths, $template, $plugins; // Common objects
 
-  $page_id_key = $paths->nslist[ $namespace ] . $page_id;
+  $ns_prefix = ( isset($paths->nslist[ $namespace ]) ) ? $paths->nslist[ $namespace ] : $namespace . substr($paths->nslist['Special'], -1);
+  $page_id_key = $ns_prefix . $page_id;
   if ( isset($paths->pages[$page_id_key]) )
   {
     $page_data = $paths->pages[$page_id_key];
@@ -320,7 +324,7 @@
   {
     $page_data = array();
   }
-  $title = ( isset($page_data['name']) ) ? $page_data['name'] : $paths->nslist[$namespace] . str_replace('_', ' ', dirtify_page_id( $page_id ) );
+  $title = ( isset($page_data['name']) ) ? $page_data['name'] : $ns_prefix . str_replace('_', ' ', dirtify_page_id( $page_id ) );
   return $title;
 }
 
--- a/includes/json.php	Sat Mar 01 18:56:37 2008 -0500
+++ b/includes/json.php	Sat Mar 01 18:57:07 2008 -0500
@@ -94,9 +94,7 @@
 /**
  * Converts to and from JSON format.
  *
- * Brief example of use:
- *
- * <code>
+ * @example <code>
    // create a new instance of Services_JSON
    $json = new Services_JSON();
   
@@ -111,6 +109,7 @@
    $input = file_get_contents('php://input', 1000000);
    $value = $json->decode($input);
    </code>
+ *
  */
 class Services_JSON
 {
--- a/includes/pageprocess.php	Sat Mar 01 18:56:37 2008 -0500
+++ b/includes/pageprocess.php	Sat Mar 01 18:57:07 2008 -0500
@@ -55,6 +55,13 @@
   var $revision_id = 0;
   
   /**
+   * The time this revision was saved, as a UNIX timestamp
+   * @var int
+   */
+  
+  var $revision_time = 0;
+  
+  /**
    * Unsanitized page ID.
    * @var string
    */
@@ -460,6 +467,14 @@
       return false;
     }
     
+    // If there's an identical draft copy, delete it
+    $sql = 'DELETE FROM ' . table_prefix . "logs WHERE is_draft = 1 AND page_id = '{$this->page_id}' AND namespace = '{$this->namespace}' AND page_text = '{$text}';";
+    if ( !$db->sql_query($sql) )
+    {
+      $this->raise_error($db->get_error());
+      return false;
+    }
+    
     // Rebuild the search index
     $paths->rebuild_page_index($this->page_id, $this->namespace);
     
@@ -560,6 +575,200 @@
   }
   
   /**
+   * Rolls back a non-edit action in the logs
+   * @param int Log entry (log_id) to roll back
+   * @return array Standard Enano error/success protocol
+   */
+  
+  function rollback_log_entry($log_id)
+  {
+    global $db, $session, $paths, $template, $plugins; // Common objects
+    
+    // Verify permissions
+    if ( !$this->perms->get_permissions('history_rollback') )
+    {
+      return array(
+        'success' => false,
+        'error' => 'access_denied'
+        );
+    }
+    
+    // Check input
+    $log_id = intval($log_id);
+    if ( empty($log_id) )
+    {
+      return array(
+        'success' => false,
+        'error' => 'invalid_parameter'
+        );
+    }
+    
+    // Fetch the log entry
+    $q = $db->sql_query('SELECT * FROM ' . table_prefix . "logs WHERE log_type = 'page' AND page_id='{$this->page_id}' AND namespace='{$this->namespace}' AND log_id = $log_id;");
+    if ( !$q )
+      $db->_die();
+    
+    // Is this even a valid log entry for this context?
+    if ( $db->numrows() < 1 )
+    {
+      return array(
+        'success' => false,
+        'error' => 'entry_not_found'
+        );
+    }
+    
+    // All good, fetch and free the result
+    $log_entry = $db->fetchrow();
+    $db->free_result();
+    
+    // Let's see, what do we have here...
+    switch ( $log_entry['action'] )
+    {
+      case 'rename':
+        // Page was renamed, let the rename method handle this
+        return $this->rename($log_entry['edit_summary']);
+        break;
+      case 'prot':
+      case 'unprot':
+      case 'semiprot':
+        return $this->protect_page(intval($log_entry['page_text']), '__REVERSION__');
+        break;
+      default:
+        break;
+    }
+  }
+  
+  /**
+   * Renames the page
+   * @param string New name
+   * @return array Standard Enano error/success protocol
+   */
+  
+  function rename_page($new_name)
+  {
+    global $db, $session, $paths, $template, $plugins; // Common objects
+    
+    // Check permissions
+    if ( !$this->perms->get_permissions('rename') )
+    {
+      return array(
+        'success' => false,
+        'error' => 'access_denied'
+        );
+    }
+    
+    // If this is the same as the current name, return success
+    $page_name = get_page_title_ns($this->page_id, $this->namespace);
+    if ( $page_name === $new_name )
+    {
+      return array(
+        'success' => true
+        );
+    }
+    
+    // Make sure the name is valid
+    $new_name = trim($new_name);
+    if ( empty($new_name) )
+    {
+      return array(
+        'success' => false,
+        'error' => 'invalid_parameter'
+        );
+    }
+    
+    // Log the action
+    $username = $db->escape($session->username);
+    $page_name = $db->escape($page_name);
+    $time = time();
+    
+    $q = $db->sql_query('INSERT INTO ' . table_prefix . "logs ( log_type, action, page_id, namespace, author, edit_summary, time_id, date_string ) VALUES\n"
+                      . "  ( 'page', 'rename', '{$this->page_id}', '{$this->namespace}', '$username', '$page_name', '$time', 'DATE_STRING COLUMN OBSOLETE, USE time_id' );");
+    if ( !$q )
+      $db->_die();
+    
+    // Not much to do but to rename it now
+    $new_name = $db->escape($new_name);
+    $q = $db->sql_query('UPDATE ' . table_prefix . "pages SET name = '$new_name' WHERE urlname = '{$this->page_id}' AND namespace = '{$this->namespace}';");
+    if ( !$q )
+      $db->_die();
+    
+    return array(
+      'success' => true
+      );
+  }
+  
+  /**
+   * Sets the protection level of the page
+   * @param int Protection level, one of PROTECT_{FULL,SEMI,NONE}
+   * @param string Reason for protection - required
+   */
+  
+  function protect_page($protection_level, $reason)
+  {
+    global $db, $session, $paths, $template, $plugins; // Common objects
+    
+    // Validate permissions
+    if ( !$this->perms->get_permissions('protect') )
+    {
+      return array(
+        'success' => false,
+        'error' => 'access_denied'
+        );
+    }
+    
+    // Validate input
+    $reason = trim($reason);
+    if ( !in_array($protection_level, array(PROTECT_NONE, PROTECT_FULL, PROTECT_SEMI)) || empty($reason) )
+    {
+      return array(
+        'success' => false,
+        'error' => 'invalid_parameter'
+        );
+    }
+    
+    // Retrieve page metadata
+    $pathskey = $paths->nslist[ $this->namespace ] . $this->page_id;
+    if ( !isset($paths->pages[$pathskey]) )
+    {
+      return array(
+        'success' => false,
+        'error' => 'page_metadata_not_found'
+        );
+    }
+    $metadata =& $paths->pages[$pathskey];
+    
+    // Log the action
+    $username = $db->escape($session->username);
+    $time = time();
+    $existing_protection = intval($metadata['protected']);
+    $reason = $db->escape($reason);
+    
+    $action = '[ insanity ]';
+    switch($protection_level)
+    {
+      case PROTECT_FULL: $action = 'prot'; break;
+      case PROTECT_NONE: $action = 'unprot'; break;
+      case PROTECT_SEMI: $action = 'semiprot'; break;
+    }
+    
+    $sql = 'INSERT INTO ' . table_prefix . "logs ( log_type, action, page_id, namespace, author, edit_summary, time_id, page_text, date_string ) VALUES\n"
+         . "  ( 'page', '$action', '{$this->page_id}', '{$this->namespace}', '$username', '$reason', '$time', '$existing_protection', 'DATE_STRING COLUMN OBSOLETE, USE time_id' );";
+    if ( !$db->sql_query($sql) )
+    {
+      $db->_die();
+    }
+    
+    // Perform the actual protection
+    $q = $db->sql_query('UPDATE ' . table_prefix . "pages SET protected = $protection_level WHERE urlname = '{$this->page_id}' AND namespace = '{$this->namespace}';");
+    if ( !$q )
+      $db->_die();
+    
+    return array(
+      'success' => true
+      );
+  }
+  
+  /**
    * Sets internal variables.
    * @access private
    */
@@ -710,14 +919,13 @@
       echo '<div class="info-box" style="margin-left: 0; margin-top: 5px;">
               <b>' . $lang->get('page_msg_archived_title') . '</b><br />
               ' . $lang->get('page_msg_archived_body', array(
-                  'archive_date' => enano_date('F d, Y', $this->revision_id),
-                  'archive_time' => enano_date('h:i a', $this->revision_id),
+                  'archive_date' => enano_date('F d, Y', $this->revision_time),
+                  'archive_time' => enano_date('h:i a', $this->revision_time),
                   'current_link' => makeUrlNS($this->namespace, $this->page_id),
-                  'restore_link' => makeUrlNS($this->namespace, $this->page_id, 'do=rollback&amp;id='.$this->revision_id),
-                  'restore_onclick' => 'ajaxRollback(\''.$this->revision_id.'\'); return false;',
+                  'restore_link' => makeUrlNS($this->namespace, $this->page_id, 'do=edit&amp;revid='.$this->revision_id),
+                  'restore_onclick' => 'ajaxEditor(\''.$this->revision_id.'\'); return false;',
                 )) . '
-            </div>
-            <br />';
+            </div>';
     }
     
     if ( $redir_enabled )
@@ -796,7 +1004,7 @@
     if ( $this->revision_id > 0 && is_int($this->revision_id) )
     {
     
-      $q = $db->sql_query('SELECT page_text, char_tag, date_string FROM '.table_prefix.'logs WHERE page_id=\'' . $this->page_id . '\' AND namespace=\'' . $this->namespace . '\' AND time_id=' . $this->revision_id . ';');
+      $q = $db->sql_query('SELECT page_text, char_tag, time_id FROM '.table_prefix.'logs WHERE log_type=\'page\' AND action=\'edit\' AND page_id=\'' . $this->page_id . '\' AND namespace=\'' . $this->namespace . '\' AND log_id=' . $this->revision_id . ';');
       if ( !$q )
       {
         $this->send_error('Error during SQL query.', true);
@@ -808,7 +1016,7 @@
         {
           $db->free_result();
           $page_id = str_replace('.2e', '.', $this->page_id);
-          $q = $db->sql_query('SELECT page_text, char_tag, date_string FROM '.table_prefix.'logs WHERE page_id=\'' . $page_id . '\' AND namespace=\'' . $this->namespace . '\' AND time_id=' . $this->revision_id . ';');
+          $q = $db->sql_query('SELECT page_text, char_tag, time_id FROM '.table_prefix.'logs WHERE log_type=\'page\' AND action=\'edit\' AND page_id=\'' . $page_id . '\' AND namespace=\'' . $this->namespace . '\' AND log_id=' . $this->revision_id . ';');
           if ( !$q )
           {
             $this->send_error('Error during SQL query.', true);
@@ -885,6 +1093,11 @@
     
     $this->text_cache = $row['page_text'];
     
+    if ( isset($row['time_id']) )
+    {
+      $this->revision_time = intval($row['time_id']);
+    }
+    
     return $row['page_text'];
     
   }
@@ -1527,8 +1740,8 @@
   
   /**
    * Send an error message and die. For debugging or critical technical errors only - nothing that would under normal circumstances be shown to the user.
-   * @var string Error message
-   * @var bool If true, send DBAL's debugging information as well
+   * @param string Error message
+   * @param bool If true, send DBAL's debugging information as well
    */
    
   function send_error($message, $sql = false)
--- a/includes/pageutils.php	Sat Mar 01 18:56:37 2008 -0500
+++ b/includes/pageutils.php	Sat Mar 01 18:57:07 2008 -0500
@@ -342,7 +342,7 @@
     $wiki = ( ( $paths->pages[$pname]['wiki_mode'] == 2 && getConfig('wiki_mode') == '1') || $paths->pages[$pname]['wiki_mode'] == 1) ? true : false;
     $prot = ( ( $paths->pages[$pname]['protected'] == 2 && $session->user_logged_in && $session->reg_time + 60*60*24*4 < time() ) || $paths->pages[$pname]['protected'] == 1) ? true : false;
     
-    $q = 'SELECT time_id,date_string,page_id,namespace,author,edit_summary,minor_edit FROM ' . table_prefix.'logs WHERE log_type=\'page\' AND action=\'edit\' AND page_id=\'' . $page_id . '\' AND namespace=\'' . $namespace . '\' AND is_draft != 1 ORDER BY time_id DESC;';
+    $q = 'SELECT log_id,time_id,date_string,page_id,namespace,author,edit_summary,minor_edit FROM ' . table_prefix.'logs WHERE log_type=\'page\' AND action=\'edit\' AND page_id=\'' . $page_id . '\' AND namespace=\'' . $namespace . '\' AND is_draft != 1 ORDER BY time_id DESC;';
     if(!$db->sql_query($q)) $db->_die('The history data for the page "' . $paths->cpage['name'] . '" could not be selected.');
     echo $lang->get('history_page_subtitle') . '
           <h3>' . $lang->get('history_heading_edits') . '</h3>';
@@ -431,9 +431,9 @@
         echo '<td class="' . $cls . '" style="text-align: center;">'. (( $r['minor_edit'] ) ? 'M' : '' ) .'</td>'."\n";
         
         // Actions!
-        echo '<td class="' . $cls . '" style="text-align: center;"><a rel="nofollow" href="'.makeUrlNS($namespace, $page_id, 'oldid=' . $r['time_id']) . '" onclick="ajaxHistView(\'' . $r['time_id'] . '\'); return false;">' . $lang->get('history_action_view') . '</a></td>'."\n";
+        echo '<td class="' . $cls . '" style="text-align: center;"><a rel="nofollow" href="'.makeUrlNS($namespace, $page_id, 'oldid=' . $r['log_id']) . '" onclick="ajaxHistView(\'' . $r['log_id'] . '\'); return false;">' . $lang->get('history_action_view') . '</a></td>'."\n";
         echo '<td class="' . $cls . '" style="text-align: center;"><a rel="nofollow" href="'.makeUrl($paths->nslist['Special'].'Contributions/' . $r['author']) . '">' . $lang->get('history_action_contrib') . '</a></td>'."\n";
-        echo '<td class="' . $cls . '" style="text-align: center;"><a rel="nofollow" href="'.makeUrlNS($namespace, $page_id, 'do=edit&amp;revid=' . $r['time_id']) . '" onclick="ajaxEditor(\'' . $r['time_id'] . '\'); return false;">' . $lang->get('history_action_restore') . '</a></td>'."\n";
+        echo '<td class="' . $cls . '" style="text-align: center;"><a rel="nofollow" href="'.makeUrlNS($namespace, $page_id, 'do=edit&amp;revid=' . $r['log_id']) . '" onclick="ajaxEditor(\'' . $r['log_id'] . '\'); return false;">' . $lang->get('history_action_restore') . '</a></td>'."\n";
         
         echo '</tr>'."\n"."\n";
         
@@ -448,7 +448,7 @@
     }
     $db->free_result();
     echo '<h3>' . $lang->get('history_heading_other') . '</h3>';
-    $q = 'SELECT time_id,action,date_string,page_id,namespace,author,edit_summary,minor_edit FROM ' . table_prefix.'logs WHERE log_type=\'page\' AND action!=\'edit\' AND page_id=\'' . $paths->page_id . '\' AND namespace=\'' . $paths->namespace . '\' ORDER BY time_id DESC;';
+    $q = 'SELECT log_id,time_id,action,date_string,page_id,namespace,author,edit_summary,minor_edit FROM ' . table_prefix.'logs WHERE log_type=\'page\' AND action!=\'edit\' AND page_id=\'' . $paths->page_id . '\' AND namespace=\'' . $paths->namespace . '\' ORDER BY time_id DESC;';
     if ( !$db->sql_query($q) )
     {
       $db->_die('The history data for the page "' . htmlspecialchars($paths->cpage['name']) . '" could not be selected.');
@@ -492,9 +492,9 @@
         // Action taken
         echo '<td class="' . $cls . '">';
         // Some of these are sanitized at insert-time. Others follow the newer Enano policy of stripping HTML at runtime.
-        if    ($r['action']=='prot')     echo $lang->get('history_log_protect') . '</td><td class="' . $cls . '">' . $lang->get('history_extra_reason') . ' ' . $r['edit_summary'];
-        elseif($r['action']=='unprot')   echo $lang->get('history_log_unprotect') . '</td><td class="' . $cls . '">' . $lang->get('history_extra_reason') . ' ' . $r['edit_summary'];
-        elseif($r['action']=='semiprot') echo $lang->get('history_log_semiprotect') . '</td><td class="' . $cls . '">' . $lang->get('history_extra_reason') . ' ' . $r['edit_summary'];
+        if    ($r['action']=='prot')     echo $lang->get('history_log_protect') . '</td><td class="' . $cls . '">'     . $lang->get('history_extra_reason') . ' ' . ( $r['edit_summary'] === '__REVERSION__' ? $lang->get('history_extra_protection_reversion') : htmlspecialchars($r['edit_summary']) );
+        elseif($r['action']=='unprot')   echo $lang->get('history_log_unprotect') . '</td><td class="' . $cls . '">'   . $lang->get('history_extra_reason') . ' ' . ( $r['edit_summary'] === '__REVERSION__' ? $lang->get('history_extra_protection_reversion') : htmlspecialchars($r['edit_summary']) );
+        elseif($r['action']=='semiprot') echo $lang->get('history_log_semiprotect') . '</td><td class="' . $cls . '">' . $lang->get('history_extra_reason') . ' ' . ( $r['edit_summary'] === '__REVERSION__' ? $lang->get('history_extra_protection_reversion') : htmlspecialchars($r['edit_summary']) );
         elseif($r['action']=='rename')   echo $lang->get('history_log_rename') . '</td><td class="' . $cls . '">' . $lang->get('history_extra_oldtitle') . ' '.htmlspecialchars($r['edit_summary']);
         elseif($r['action']=='create')   echo $lang->get('history_log_create') . '</td><td class="' . $cls . '">';
         elseif($r['action']=='delete')   echo $lang->get('history_log_delete') . '</td><td class="' . $cls . '">' . $lang->get('history_extra_reason') . ' ' . $r['edit_summary'];
@@ -503,7 +503,7 @@
         
         // Actions!
         echo '<td class="' . $cls . '" style="text-align: center;"><a rel="nofollow" href="'.makeUrl($paths->nslist['Special'].'Contributions/' . $r['author']) . '">' . $lang->get('history_action_contrib') . '</a></td>';
-        echo '<td class="' . $cls . '" style="text-align: center;"><a rel="nofollow" href="'.makeUrlNS($namespace, $page_id, 'do=rollback&amp;id=' . $r['time_id']) . '" onclick="ajaxRollback(\'' . $r['time_id'] . '\'); return false;">' . $lang->get('history_action_revert') . '</a></td>';
+        echo '<td class="' . $cls . '" style="text-align: center;"><a rel="nofollow" href="'.makeUrlNS($namespace, $page_id, 'do=rollback&amp;id=' . $r['log_id']) . '" onclick="ajaxRollback(\'' . $r['log_id'] . '\'); return false;">' . $lang->get('history_action_revert') . '</a></td>';
         
         echo '</tr>';
       }
@@ -536,7 +536,7 @@
     {
       return('The value "id" on the query string must be an integer.');
     }
-    $e = $db->sql_query('SELECT log_type,action,date_string,page_id,namespace,page_text,char_tag,author,edit_summary FROM ' . table_prefix.'logs WHERE time_id=' . $id . ';');
+    $e = $db->sql_query('SELECT time_id,log_type,action,date_string,page_id,namespace,page_text,char_tag,author,edit_summary FROM ' . table_prefix.'logs WHERE log_id=' . $id . ';');
     if ( !$e )
     {
       $db->_die('The rollback data could not be selected.');
@@ -610,7 +610,7 @@
             }
             else
             {
-              return 'The page "' . $paths->pages[$paths->nslist[$rb['namespace']].$rb['page_id']]['name'].'" has been rolled back to the name it had ("' . $rb['edit_summary'] . '") before ' . enano_date('d M Y h:i a', intval($rb['time_id'])) . '.';
+              return 'The page "' . htmlspecialchars($paths->pages[$paths->nslist[$rb['namespace']].$rb['page_id']]['name']) . '" has been rolled back to the name it had ("' . htmlspecialchars($rb['edit_summary']) . '") before ' . enano_date('d M Y h:i a', intval($rb['time_id'])) . '.';
             }
             break;
           case "prot":
--- a/includes/template.php	Sat Mar 01 18:56:37 2008 -0500
+++ b/includes/template.php	Sat Mar 01 18:57:07 2008 -0500
@@ -130,7 +130,7 @@
     if ( !$this->theme )
     {
       $this->theme = $this->theme_list[0]['theme_id'];
-      $this->style = substr($this->theme_list[0]['default_style'], 0, strlen($this->theme_list[0]['default_style'])-4);
+      $this->style = preg_replace('/\.css$/', '', $this->theme_list[0]['default_style']);
     }
     $this->theme_loaded = true;
   }
--- a/index.php	Sat Mar 01 18:56:37 2008 -0500
+++ b/index.php	Sat Mar 01 18:57:07 2008 -0500
@@ -168,33 +168,44 @@
       }
       if ( $revid > 0 )
       {
-        echo '<div class="usermessage">' . $lang->get('editor_msg_editing_old_revision') . '</div>';
+        $time = $page->revision_time;
         // Retrieve information about this revision and the current one
         $q = $db->sql_query('SELECT l1.author AS currentrev_author, l2.author AS oldrev_author FROM ' . table_prefix . 'logs AS l1
   LEFT JOIN ' . table_prefix . 'logs AS l2
-    ON ( l2.time_id = ' . $revid . '
+    ON ( l2.log_id = ' . $revid . '
          AND l2.log_type  = \'page\'
          AND l2.action    = \'edit\'
-         AND l2.page_id   = \'ACL_Tests\'
-         AND l2.namespace = \'Article\'
+         AND l2.page_id   = \'' . $db->escape($paths->page_id) . '\'
+         AND l2.namespace = \'' . $db->escape($paths->namespace) . '\'
+         AND l1.is_draft != 1
         )
   WHERE l1.log_type  = \'page\'
     AND l1.action    = \'edit\'
-    AND l1.page_id   = \'ACL_Tests\'
-    AND l1.namespace = \'Article\'
-    AND l1.time_id >= ' . $revid . '
+    AND l1.page_id   = \'' . $db->escape($paths->page_id) . '\'
+    AND l1.namespace = \'' . $db->escape($paths->namespace) . '\'
+    AND l1.time_id > ' . $time . '
+    AND l1.is_draft != 1
   ORDER BY l1.time_id DESC;');
         if ( !$q )
           $db->die_json();
         
-        $rev_count = $db->numrows() - 1;
-        $row = $db->fetchrow();
-        $undo_info = array(
-          'old_author'     => $row['oldrev_author'],
-          'current_author' => $row['currentrev_author'],
-          'undo_count'     => $rev_count,
-          'last_rev_id'    => $revid
-        );
+        if ( $db->numrows() > 0 )
+        {
+          echo '<div class="usermessage">' . $lang->get('editor_msg_editing_old_revision') . '</div>';
+          
+          $rev_count = $db->numrows() - 2;
+          $row = $db->fetchrow();
+          $undo_info = array(
+            'old_author'     => $row['oldrev_author'],
+            'current_author' => $row['currentrev_author'],
+            'undo_count'     => max($rev_count, 1),
+            'last_rev_id'    => $revid
+          );
+        }
+        else
+        {
+          $revid = 0;
+        }
         $db->free_result();
       }
       echo '
--- a/install/schemas/mysql_stage2.sql	Sat Mar 01 18:56:37 2008 -0500
+++ b/install/schemas/mysql_stage2.sql	Sat Mar 01 18:57:07 2008 -0500
@@ -75,7 +75,7 @@
   salt varchar(32),
   user_id mediumint(8),
   auth_level tinyint(1) NOT NULL DEFAULT '0',
-  source_ip varchar(10) default '0x7f000001',
+  source_ip varchar(39) NOT NULL DEFAULT '127.0.0.1',
   time bigint(15) default '0'
 ) CHARACTER SET `utf8` COLLATE `utf8_bin`;
 
@@ -86,7 +86,7 @@
   default_style varchar(63) NOT NULL DEFAULT '',
   enabled tinyint(1) NOT NULL DEFAULT '1',
   group_list text DEFAULT NULL,
-  group_policy ENUM('allow', 'deny') NOT NULL DEFAULT 'deny'
+  group_policy ENUM('allow_all', 'whitelist', 'blacklist') NOT NULL DEFAULT 'allow_all'
 ) CHARACTER SET `utf8` COLLATE `utf8_bin`;
 
 CREATE TABLE {{TABLE_PREFIX}}users(
--- a/install/schemas/postgresql_stage2.sql	Sat Mar 01 18:56:37 2008 -0500
+++ b/install/schemas/postgresql_stage2.sql	Sat Mar 01 18:57:07 2008 -0500
@@ -74,7 +74,7 @@
   salt varchar(32),
   user_id int,
   auth_level smallint NOT NULL DEFAULT '0',
-  source_ip varchar(10) DEFAULT '0x7f000001',
+  source_ip varchar(39) NOT NULL DEFAULT '127.0.0.1',
   time bigint DEFAULT '0'
 );
 
@@ -85,8 +85,8 @@
   default_style varchar(63) NOT NULL DEFAULT '',
   enabled smallint NOT NULL DEFAULT '1',
   group_list text DEFAULT NULL,
-  group_policy varchar(5) NOT NULL DEFAULT 'deny',
-  CHECK (group_policy IN ('allow', 'deny'))
+  group_policy varchar(5) NOT NULL DEFAULT 'allow_all',
+  CHECK (group_policy IN ('allow_all', 'whitelist', 'blacklist'))
 );
 
 CREATE TABLE {{TABLE_PREFIX}}users(
--- a/install/schemas/upgrade/1.1.2-1.1.3-mysql.sql	Sat Mar 01 18:56:37 2008 -0500
+++ b/install/schemas/upgrade/1.1.2-1.1.3-mysql.sql	Sat Mar 01 18:57:07 2008 -0500
@@ -9,3 +9,7 @@
   public_key text,
   PRIMARY KEY ( key_id )
 ) CHARACTER SET `utf8` COLLATE `utf8_bin`;
+
+ALTER TABLE {{TABLE_PREFIX}}session_keys MODIFY COLUMN source_ip varchar(39) NOT NULL DEFAULT '127.0.0.1';
+ALTER TABLE {{TABLE_PREFIX}}themes MODIFY COLUMN group_policy ENUM('allow_all', 'whitelist', 'blacklist') NOT NULL DEFAULT 'allow_all';
+UPDATE {{TABLE_PREFIX}}themes SET group_policy = 'allow_all';
--- a/install/schemas/upgrade/1.1.2-1.1.3-postgresql.sql	Sat Mar 01 18:56:37 2008 -0500
+++ b/install/schemas/upgrade/1.1.2-1.1.3-postgresql.sql	Sat Mar 01 18:57:07 2008 -0500
@@ -9,3 +9,7 @@
   public_key text,
   PRIMARY KEY ( key_id )
 );
+
+ALTER TABLE {{TABLE_PREFIX}}session_keys MODIFY COLUMN source_ip varchar(39) NOT NULL DEFAULT '127.0.0.1';
+ALTER TABLE {{TABLE_PREFIX}}themes DROP group_policy, ADD COLUMN group_policy varchar(9) NOT NULL DEFAULT 'allow_all', ADD CHECK ( group_policy IN ('allow_all', 'whitelist', 'blacklist') );
+
--- a/language/english/core.json	Sat Mar 01 18:56:37 2008 -0500
+++ b/language/english/core.json	Sat Mar 01 18:57:07 2008 -0500
@@ -371,6 +371,7 @@
       col_extra: 'Extra info',
       extra_reason: 'Reason:',
       extra_oldtitle: 'Old title:',
+      extra_protection_reversion: '(Reversion of previous protection)',
       tip_rdns: 'Click cell background for reverse DNS info',
       action_view: 'View',
       action_contrib: 'User contribs',
--- a/themes/oxygen/css/bleu.css	Sat Mar 01 18:56:37 2008 -0500
+++ b/themes/oxygen/css/bleu.css	Sat Mar 01 18:57:07 2008 -0500
@@ -3,85 +3,372 @@
  * Designed by Dan Fuhry, (C) 2006
  * This theme is Free Software; see the file "GPL" included with this package for details.
  */
- 
+
 /* The basics */
-html,body                          { height: 100%; }
-body                               { /* color added in 1.0.2 to fix light text in dark desktop themes */ color: #202020; margin: 0; padding: 0; background: url(../images/bleu/bg.png); font-family: trebuchet ms, verdana, arial, helvetica, sans-serif; font-size: 9pt; }
-body#tinymce                       { background-color: white; background-image: none; }
-.holder                            { border: 1px solid #CCCCCC; padding: 1px; background-color: #FFFFFF; color: #444444 }
-div.pad                            { padding: 10px; }                         
-table#title                        { margin: 0; padding: 0; height: 100px; background-color: #90B0D0; text-align: center; }
-table.simple-layout td#mainhead    { margin: 0; padding: 0; background-color: #90B0D0; text-align: center; }
-table.simple-layout td#mainhead h1 { margin: 15px 0; padding: 0; font-size: 14pt; }
+html,body {
+  height: 100%;
+}
+
+body {
+  /* color added in 1.0.2 to fix light text in dark desktop themes */ 
+  color: #202020;
+  margin: 0;
+  padding: 0;
+  background: url(../images/bleu/bg.png);
+  font-family: trebuchet ms, verdana, arial, helvetica, sans-serif;
+  font-size: 9pt;
+}
+
+body#tinymce {
+  background-color: white;
+  background-image: none;
+}
+
+.holder {
+  border: 1px solid #CCCCCC;
+  padding: 1px;
+  background-color: #FFFFFF;
+  color: #444444
+}
+
+div.pad {
+  padding: 10px;
+}
+
+table#title {
+  margin: 0;
+  padding: 0;
+  height: 100px;
+  background-color: #90B0D0;
+  text-align: center;
+}
+
+table.simple-layout td#mainhead {
+  margin: 0;
+  padding: 0;
+  background-color: #90B0D0;
+  text-align: center;
+}
+
+table.simple-layout td#mainhead h1 {
+  margin: 15px 0;
+  padding: 0;
+  font-size: 14pt;
+}
 
 /* Sidebar */
-td.mdgSidebarHolder                { width: 140px; }
-div.sidebar, .dbx-group            { width: 138px; background-color: #F8F8F8; border-left: 1px solid #CCC; border-right: 1px solid #CCC; padding: 1px 0px 0px 0px; }
-div.sidebar .head, .dbx-handle             { background-color: #F0F0F0;  display: block; margin: 0px 1px 1px 1px; font-family: Trebuchet MS, Arial, helvetica, sans-serif; font-size: 7pt; cursor: pointer; text-decoration: none; color: #111; padding: 5px; font-weight: bold; }
-div.sidebar .head:hover, .dbx-handle:hover { background-color: #F4F4F4;  display: block; margin: 0px 1px 1px 1px; font-family: Trebuchet MS, Arial, helvetica, sans-serif; font-size: 7pt; cursor: pointer; text-decoration: none; color: #111; padding: 5px; font-weight: bold; }
-div.sidebar div.slideblock a, .dbx-content li             { background-color: #DDD;     display: block; margin: 0px 1px; border-bottom: 1px solid #FFF; font-family: Trebuchet MS, Arial, helvetica, sans-serif; font-size: 7pt; cursor: pointer; text-decoration: none; color: #666; padding: 5px 5px 5px 9px; list-style-type: none; }
-div.sidebar div.slideblock a:hover, .dbx-content li:hover { background-color: #EEE;     display: block; margin: 0px 1px; border-bottom: 1px solid #FFF; font-family: Trebuchet MS, Arial, helvetica, sans-serif; font-size: 7pt; cursor: pointer; text-decoration: none; color: #666; padding: 5px 5px 5px 9px; }
-div.recttop                        { width: 140px; height: 12px; margin: 0; padding: 0; }
-td.recttoptop                      { width: 100%;  height: 12px; background-image: url(../images/bleu/border-menu-t.gif);   background-repeat: repeat-x;  margin: 0; padding: 0; }
-td.recttoptop:hover                { width: 100%;  height: 12px; background-image: url(../images/bleu/border-menu-t-h.gif); background-repeat: repeat-x;  margin: 0; padding: 0; cursor: pointer; }
-div.rectbot                        { width: 140px; height: 12px; margin: 0; padding: 0; }  
-td.rectbottop                      { width: 100%;  height: 12px; background-image: url(../images/bleu/border-btm.gif); background-repeat: repeat-x;  margin: 0; padding: 0; }
-div.slideblock, .dbx-content       { overflow: hidden; background-color: #DDD; }
-div.slideblock2                    { overflow: hidden; background-color: #DDD; margin: 0px 1px 0px 1px; border-bottom: 1px solid #FFF; }
-.dbx-handle                        { cursor: move !important; }
+td.mdgSidebarHolder {
+  width: 140px;
+}
+
+div.sidebar, .dbx-group {
+  width: 138px;
+  background-color: #F8F8F8; border-left: 1px solid #CCC; border-right: 1px solid #CCC; padding: 1px 0px 0px 0px;
+}
+
+div.sidebar .head, .dbx-handle {
+  background-color: #F0F0F0;
+  display: block;
+  margin: 0px 1px 1px 1px;
+  font-family: Trebuchet MS, Arial, helvetica, sans-serif;
+  font-size: 7pt;
+  cursor: pointer;
+  text-decoration: none;
+  color: #111;
+  padding: 5px;
+  font-weight: bold;
+}
+
+div.sidebar .head:hover, .dbx-handle:hover {
+  background-color: #F4F4F4;
+  display: block;
+  margin: 0px 1px 1px 1px;
+  font-family: Trebuchet MS, Arial, helvetica, sans-serif;
+  font-size: 7pt;
+  cursor: pointer;
+  text-decoration: none;
+  color: #111;
+  padding: 5px;
+  font-weight: bold;
+}
+
+div.sidebar div.slideblock a, .dbx-content li {
+  background-color: #DDD;
+  display: block;
+  margin: 0px 1px;
+  border-bottom: 1px solid #FFF;
+  font-family: Trebuchet MS, Arial, helvetica, sans-serif;
+  font-size: 7pt;
+  cursor: pointer;
+  text-decoration: none;
+  color: #666;
+  padding: 5px 5px 5px 9px;
+  list-style-type: none;
+}
+
+div.sidebar div.slideblock a:hover, .dbx-content li:hover {
+  background-color: #EEE;
+  display: block;
+  margin: 0px 1px;
+  border-bottom: 1px solid #FFF;
+  font-family: Trebuchet MS, Arial, helvetica, sans-serif;
+  font-size: 7pt;
+  cursor: pointer;
+  text-decoration: none;
+  color: #666;
+  padding: 5px 5px 5px 9px;
+}
+
+div.recttop {
+  width: 140px;
+  height: 12px;
+  margin: 0;
+  padding: 0;
+}
+
+td.recttoptop {
+  width: 100%;
+  height: 12px;
+  background-image: url(../images/bleu/border-menu-t.gif);
+  background-repeat: repeat-x;
+  margin: 0;
+  padding: 0;
+}
+
+td.recttoptop:hover {
+  width: 100%;
+  height: 12px;
+  background-image: url(../images/bleu/border-menu-t-h.gif);
+  background-repeat: repeat-x;
+  margin: 0;
+  padding: 0;
+  cursor: pointer;
+}
+
+div.rectbot {
+  width: 140px;
+  height: 12px;
+  margin: 0;
+  padding: 0;
+}
+
+td.rectbottop {
+  width: 100%;
+  height: 12px;
+  background-image: url(../images/bleu/border-btm.gif);
+  background-repeat: repeat-x;
+  margin: 0;
+  padding: 0;
+}
+
+div.slideblock, .dbx-content {
+  overflow: hidden;
+  background-color: #DDD;
+}
+
+div.slideblock2 {
+  overflow: hidden;
+  background-color: #DDD;
+  margin: 0px 1px 0px 1px;
+  border-bottom: 1px solid #FFF;
+}
+
+.dbx-handle {
+  cursor: move !important;
+}
 
 /* The credits thingy at the bottom */
-div#credits                        { margin: 0; padding: 10px; padding-bottom: 0px; padding-top: 12px; background-color: #E8E8E8; color: #AAA; font-size: 7pt; font-family: lucida grande, verdana, arial, sans-serif; }
-div#credits a                      { color: #90B0D0; text-decoration: none; }
-div#credits a:hover                { color: #80A0C0; text-decoration: underline; }
+div#credits { margin: 0; padding: 10px; padding-bottom: 0px; padding-top: 12px; background-color: #E8E8E8; color: #AAA; font-size: 7pt; font-family: lucida grande, verdana, arial, sans-serif; }
+div#credits a { color: #90B0D0; text-decoration: none; }
+div#credits a:hover { color: #80A0C0; text-decoration: underline; }
 
 /* The link hidden in plain "site" at the top of the page */
-td#mainhead a                      { text-decoration: none; color: #000000; }
-td#mainhead a:hover                { text-decoration: none; color: #000000; border-bottom: 1px dotted #406080; }
+td#mainhead a { text-decoration: none; color: #000000; }
+td#mainhead a:hover { text-decoration: none; color: #000000; border-bottom: 1px dotted #406080; }
 
 /* Text, headings, and links inside the main div (usually #ajaxEditContainer but used some other places as well) */
-div.contentDiv h2                                                                    { border-bottom: 1px solid #90B0D0; margin-bottom: 0; }
-div.contentDiv h3                                                                    { font-size: 11pt; font-weight: bold; }
-div.contentDiv ul li                     , div#messageBox ul li                      { list-style: url(../images/bleu/bullet.gif); }
-div.contentDiv p                         , div#messageBox p                          { margin-left: 1.0em; }
-table.simple-layout div.contentDiv p                                                 { margin-left: 0em; }
-div.contentDiv blockquote                , div#messageBox blockquote                 { background-color: #F4F4F4; border: 1px dotted #406080; margin: 1em; padding: 10px; max-height: 250px; overflow: auto; }
-div.contentDiv                           , div#messageBox                            { font-size: 9pt; }
-div.contentDiv a                         , div#messageBox a                          { color: #7090B0; }
-div.contentDiv a:hover                   , div#messageBox a:hover                    { color: #90B0D0; }
-div.contentDiv a[href ^="http://"]       , div#messageBox a[href ^="http://"]        { color: #80A0C0; background: url(../images/bleu/external.gif) center right no-repeat; padding-right: 16px; }
-div.contentDiv a[href ^="https://"]      , div#messageBox a[href ^="https://"]       { color: #80A0C0; background: url(../images/bleu/https.gif)    center right no-repeat; padding-right: 16px; }
-div.contentDiv a[href ^="mailto:"]       , div#messageBox a[href ^="mailto:"]        { color: #80A0C0; background: url(../images/bleu/email.gif)    center right no-repeat; padding-right: 16px; }
-div.contentDiv a[href ^="irc://"]        , div#messageBox a[href ^="irc://"]         { color: #80A0C0; background: url(../images/bleu/irc.gif)      center right no-repeat; padding-right: 16px; }
-div.contentDiv a[href ^="http://"]:hover , div#messageBox a[href ^="http://"]:hover  { color: #A0C0E0; background: url(../images/bleu/external.gif) center right no-repeat; padding-right: 16px; }
-div.contentDiv a[href ^="https://"]:hover, div#messageBox a[href ^="https://"]:hover { color: #A0C0E0; background: url(../images/bleu/https.gif)    center right no-repeat; padding-right: 16px; }
-div.contentDiv a[href ^="mailto:"]:hover , div#messageBox a[href ^="mailto:"]:hover  { color: #A0C0E0; background: url(../images/bleu/email.gif)    center right no-repeat; padding-right: 16px; }
-div.contentDiv a[href ^="irc://"]:hover  , div#messageBox a[href ^="irc://"]:hover   { color: #A0C0E0; background: url(../images/bleu/irc.gif)      center right no-repeat; padding-right: 16px; }
+div.contentDiv h2 {
+  border-bottom: 1px solid #90B0D0;
+  margin-bottom: 0;
+}
+
+div.contentDiv h3 {
+  font-size: 11pt;
+  font-weight: bold;
+}
+
+div.contentDiv ul li, div#messageBox ul li {
+  list-style: url(../images/bleu/bullet.gif);
+}
+
+div.contentDiv p, div#messageBox p {
+  margin-left: 1.0em;
+}
+
+table.simple-layout div.contentDiv p {
+  margin-left: 0em;
+}
+
+div.contentDiv blockquote, div#messageBox blockquote {
+  background-color: #F4F4F4;
+  border: 1px dotted #406080;
+  margin: 1em;
+  padding: 10px;
+  max-height: 250px;
+  overflow: auto;
+}
+
+div.contentDiv, div#messageBox {
+  font-size: 9pt;
+}
+
+div.contentDiv a, div#messageBox a {
+  color: #7090B0;
+}
+
+div.contentDiv a:hover, div#messageBox a:hover {
+  color: #90B0D0;
+}
+
+div.contentDiv a[href ^="http://"], div#messageBox a[href ^="http://"] {
+  color: #80A0C0;
+  background: url(../images/bleu/external.gif) center right no-repeat;
+  padding-right: 16px;
+}
+
+div.contentDiv a[href ^="https://"], div#messageBox a[href ^="https://"] {
+  color: #80A0C0;
+  background: url(../images/bleu/https.gif)    center right no-repeat;
+  padding-right: 16px;
+}
+
+div.contentDiv a[href ^="mailto:"], div#messageBox a[href ^="mailto:"] {
+  color: #80A0C0;
+  background: url(../images/bleu/email.gif)    center right no-repeat;
+  padding-right: 16px;
+}
+
+div.contentDiv a[href ^="irc://"], div#messageBox a[href ^="irc://"] {
+  color: #80A0C0;
+  background: url(../images/bleu/irc.gif)      center right no-repeat;
+  padding-right: 16px;
+}
+
+div.contentDiv a[href ^="http://"]:hover, div#messageBox a[href ^="http://"]:hover {
+  color: #A0C0E0;
+  background: url(../images/bleu/external.gif) center right no-repeat;
+  padding-right: 16px;
+}
+
+div.contentDiv a[href ^="https://"]:hover, div#messageBox a[href ^="https://"]:hover {
+  color: #A0C0E0;
+  background: url(../images/bleu/https.gif)    center right no-repeat;
+  padding-right: 16px;
+}
+
+div.contentDiv a[href ^="mailto:"]:hover, div#messageBox a[href ^="mailto:"]:hover {
+  color: #A0C0E0;
+  background: url(../images/bleu/email.gif)    center right no-repeat;
+  padding-right: 16px;
+}
+
+div.contentDiv a[href ^="irc://"]:hover, div#messageBox a[href ^="irc://"]:hover {
+  color: #A0C0E0;
+  background: url(../images/bleu/irc.gif)      center right no-repeat;
+  padding-right: 16px;
+}
 
 /* Wikilinks to pages that don't exist */
-div.contentDiv a.wikilink-nonexistent       { color: #B05020; }
-div.contentDiv a.wikilink-nonexistent:hover { color: #D06030; }
+div.contentDiv a.wikilink-nonexistent {
+  color: #B05020;
+}
+
+div.contentDiv a.wikilink-nonexistent:hover {
+  color: #D06030;
+}
 
 /* Well, not Midget and not comments (usually), but that's what the class is called ;-). Basically an informational window or used as a wrapper for tables. */
-.mdg-comment, .mdg-infobox        { margin-left: 1em; padding: 7px; border: 1px solid #AAAAAA; background-color: #E8E8E8; }
+.mdg-comment, .mdg-infobox {
+  margin-left: 1em;
+  padding: 7px;
+  border: 1px solid #AAAAAA;
+  background-color: #E8E8E8;
+}
 
-.tblholder                        { margin: 10px 0 0 0; padding: 0; border: 1px solid #AAAAAA; background-color: #E8E8E8; }
+.tblholder {
+  margin: 10px 0 0 0;
+  padding: 0;
+  border: 1px solid #AAAAAA;
+  background-color: #E8E8E8;
+}
 
 /* The beautiful tables inside what may not obviously be mdg-comment divs */
-div.tblholder td.row1             { padding: 4px; background-color: #E0E0E0; }
-div.tblholder td.row2             { padding: 4px; background-color: #F0F0F0; }
-div.tblholder td.row3             { padding: 4px; background-color: #E8E8E8; }
-div.tblholder th                  { padding: 4px; background-color: #7080A0; font-weight: bold; text-align: center; color: #FFFFFF; }
-div.tblholder th.subhead          { padding: 4px; background-color: #90A0B0; font-weight: bold; text-align: center; color: #FFFFFF; }
-div.tblholder table               { background-color: #FFFFFF; width: 100%; }
+div.tblholder td.row1 {
+  padding: 4px;
+  background-color: #E0E0E0;
+}
+
+div.tblholder td.row2 {
+  padding: 4px;
+  background-color: #F0F0F0;
+}
+
+div.tblholder td.row3 {
+  padding: 4px;
+  background-color: #E8E8E8;
+}
+
+div.tblholder th {
+  padding: 4px;
+  background-color: #7080A0;
+  font-weight: bold;
+  text-align: center;
+  color: #FFFFFF;
+}
+
+div.tblholder th.subhead {
+  padding: 4px;
+  background-color: #90A0B0;
+  font-weight: bold;
+  text-align: center;
+  color: #FFFFFF;
+}
+
+div.tblholder table {
+  background-color: #FFFFFF;
+  width: 100%;
+}
 
 /* Colored table cells */
-div.tblholder td.row1_red         { padding: 4px; background-color: #F8E0E0; }
-div.tblholder td.row2_red         { padding: 4px; background-color: #FFF0F0; }
-div.tblholder td.row3_red         { padding: 4px; background-color: #FFE8E8; }
-div.tblholder td.row1_green       { padding: 4px; background-color: #E0F8E0; }
-div.tblholder td.row2_green       { padding: 4px; background-color: #F0FFF0; }
-div.tblholder td.row3_green       { padding: 4px; background-color: #E8FFE8; }
+div.tblholder td.row1_red {
+  padding: 4px;
+  background-color: #F8E0E0;
+}
+
+div.tblholder td.row2_red {
+  padding: 4px;
+  background-color: #FFF0F0;
+}
+
+div.tblholder td.row3_red {
+  padding: 4px;
+  background-color: #FFE8E8;
+}
+
+div.tblholder td.row1_green {
+  padding: 4px;
+  background-color: #E0F8E0;
+}
+
+div.tblholder td.row2_green {
+  padding: 4px;
+  background-color: #F0FFF0;
+}
+
+div.tblholder td.row3_green {
+  padding: 4px;
+  background-color: #E8FFE8;
+}
 
 div.tblholder th a {
   color: #FFFFFF !important;
@@ -93,16 +380,6 @@
   text-decoration: underline !important;
 }
 
-/* The "page tools" bar below the site logo but above the page content 
-div.pagebar                       { background-color: #B0D0F0; margin-top: 0px; padding: 3px; font-size: 7pt; }
-div.pagebar a                     { cursor: pointer; padding: 3px; margin-left: 3px; margin-right: 3px; text-decoration: none; color: #406080; }
-div.pagebar a.selected            { background-color: #FFFFFF; color: #000040; font-weight: bold; }
-div.pagebar a:hover               { cursor: pointer; padding: 3px; margin-left: 3px; margin-right: 3px; text-decoration: none; color: #406080; background-color: #D0F0FF; }
-div.pagebar input                 { font-family: Bon Apetit, sans-serif; font-size: 7pt; border: 0; margin: 0px 0px 0px 0px; text-decoration: none; color: #406080; background-color: #E0F0FF; } 
-div.pagebar input:hover           { font-family: Bon Apetit, sans-serif; font-size: 7pt; border: 0; margin: 0px 0px 0px 0px; text-decoration: none; color: #406080; background-color: #D0F0FF; }
-div.pagebar input:focus           { font-family: Bon Apetit, sans-serif; font-size: 7pt; border: 0; margin: 0px 0px 0px 0px; text-decoration: none; color: #406080; background-color: #F0F0FF; }
-*/
-
 /*
  * jBox menu system
  */
@@ -173,10 +450,6 @@
 div.menu a.current, div.menu a.current:hover, div.menu a.selected, div.menu a.selected:hover, div.menu_nojs a.current, div.menu_nojs a.current:hover, div.menu_nojs a.selected, div.menu_nojs a.selected:hover {
   color: #000040;
   background-color: #FFFFFF;
-  /*
-  Some people like this. Most people don't.
-  -moz-border-radius: 5px 5px 0 0;
-  */
 }
 div.menu ul, div.menu_nojs ul {
   display: none;
@@ -187,16 +460,6 @@
   border-width: 0;
   min-width: 120px;
 }
-/*
-div.menu_nojs ul {
-  display: block;
-  padding: 0;
-  margin: 0 0 0 1em;
-  background-color: #B0D0F0;
-  border-width: 0;
-  min-width: 120px;
-}
-*/
 div.menu ul li, div.menu_nojs ul li {
   list-style: none;
 }
@@ -238,12 +501,44 @@
 td.mdg-menu-btm                   {              height: 12px;     background: url(../images/bleu/border-btm.gif);   }
 
 /* Buttons and textboxes - these settings are used almost everywhere */
-input, textarea, select, button           { border: 1px solid #406080; background-color: #F2F2F2; padding: 3px; font-family: arial, helvetica, sans-serif; font-size: 8pt; }
-input:hover, textarea:hover, select:hover { border: 1px solid #6080A0; background-color: #F8F8F8; padding: 3px; }
-input:focus, textarea:focus, select:focus { border: 1px solid #90B0D0; background-color: #FFFFFF; padding: 3px; }
-label                                     { padding: 3px; cursor: pointer; font-family: arial, helvetica, sans-serif; font-size: 8pt; }
-label:hover                               { padding: 3px; cursor: pointer; background-color: #F0F0F0; }
-input#pageheading                         { font-size: 14pt; border-bottom: 1px solid #90B0D0; margin-bottom: 0; }
+input, textarea, select, button {
+  border: 1px solid #406080;
+  background-color: #F2F2F2;
+  padding: 3px;
+  font-family: arial, helvetica, sans-serif;
+  font-size: 8pt;
+}
+
+input:hover, textarea:hover, select:hover {
+  border: 1px solid #6080A0;
+  background-color: #F8F8F8;
+  padding: 3px;
+}
+
+input:focus, textarea:focus, select:focus {
+  border: 1px solid #90B0D0;
+  background-color: #FFFFFF;
+  padding: 3px;
+}
+
+label {
+  padding: 3px;
+  cursor: pointer;
+  font-family: arial, helvetica, sans-serif;
+  font-size: 8pt;
+}
+
+label:hover {
+  padding: 3px;
+  cursor: pointer;
+  background-color: #F0F0F0;
+}
+
+input#pageheading {
+  font-size: 14pt;
+  border-bottom: 1px solid #90B0D0;
+  margin-bottom: 0;
+}
 
 input[type ^="button"], input[type ^="submit"], button {
   background-image: url(../images/buttonbg.gif);
@@ -262,30 +557,89 @@
   background-color: #e0e0e0 !important;
 }
 
-/* JWS window theming */
-div.jswindow                      { border: 2px solid #7090B0; border-top: 5px solid #7090B0; padding: 0px; font-family: Trebuchet MS, tahoma, verdana, arial, sans-serif; font-size: 9pt; display: none; position: absolute; background-color: #FFFFFF; }
-div.titlebar                      { background-color: #7090B0; color: #FFFFFF; font-family: Trebuchet MS, tahoma, verdana, arial, sans-serif; font-size: 9pt; padding-bottom: 4px; cursor: default; }
-div.titlebar div.closebtn         { width: 16px; height: 16px; border: 1px solid #B0D0F0; background-color: #90B0D0; display: block; }
-div.titlebar div.closebtn:hover   { width: 16px; height: 16px; border: 1px solid #FFFFFF; background-color: #B0D0F0; display: block; }
-div.titlebar table, div.titlebar td { margin: 0; padding: 0; }
-div.jswindow div.content          { padding: 10px; margin: 0; background-color: #FFFFFF; }
+/* The Wordpress-like fills behind checkboxes and their labels */
+.catCheck {
+  padding: 3px;
+}
 
-/* The Wordpress-like fills behind checkboxes and their labels */
-.catCheck                         { padding: 3px; }
-.catCheck:hover                   { padding: 3px; background-color: #F0F0F0; }
+.catCheck:hover {
+  padding: 3px;
+  background-color: #F0F0F0;
+}
 
 /* Information, warning, question, error, and wait boxes */
-div.error-box                     { background-image: url(../../../images/error.png);    background-position: 8px 8px; background-repeat: no-repeat; background-color: #FFF4F4; border: 1px dashed #406080; padding: 10px 10px 10px 50px; margin: 0.5em 0 0 0; min-height: 25px; }
-div.info-box                      { background-image: url(../../../images/info.png);     background-position: 8px 8px; background-repeat: no-repeat; background-color: #F4F4FF; border: 1px dashed #406080; padding: 10px 10px 10px 50px; margin: 0.5em 0 0 0; min-height: 25px; }
-div.warning-box                   { background-image: url(../../../images/warning.png);  background-position: 8px 8px; background-repeat: no-repeat; background-color: #FFFFF4; border: 1px dashed #406080; padding: 10px 10px 10px 50px; margin: 0.5em 0 0 0; min-height: 25px; }
-div.question-box                  { background-image: url(../../../images/question.png); background-position: 8px 8px; background-repeat: no-repeat; background-color: #F4FFF4; border: 1px dashed #406080; padding: 10px 10px 10px 50px; margin: 0.5em 0 0 0; min-height: 25px; }
-div.wait-box                      { background-image: url(../../../images/wait.png);     background-position: 8px 8px; background-repeat: no-repeat; background-color: #FFF4FF; border: 1px dashed #406080; padding: 10px 10px 10px 50px; margin: 0.5em 0 0 0; min-height: 25px; }
+div.error-box {
+  background-image: url(../../../images/error.png);
+  background-position: 8px 8px;
+  background-repeat: no-repeat;
+  background-color: #FFF4F4;
+  border: 1px dashed #406080;
+  padding: 10px 10px 10px 50px;
+  margin: 0.5em 0 0 0;
+  min-height: 25px;
+}
+
+div.info-box {
+  background-image: url(../../../images/info.png);
+  background-position: 8px 8px;
+  background-repeat: no-repeat;
+  background-color: #F4F4FF;
+  border: 1px dashed #406080;
+  padding: 10px 10px 10px 50px;
+  margin: 0.5em 0 0 0;
+  min-height: 25px;
+}
+
+div.warning-box {
+  background-image: url(../../../images/warning.png);
+  background-position: 8px 8px;
+  background-repeat: no-repeat;
+  background-color: #FFFFF4;
+  border: 1px dashed #406080;
+  padding: 10px 10px 10px 50px;
+  margin: 0.5em 0 0 0;
+  min-height: 25px;
+}
+
+div.question-box {
+  background-image: url(../../../images/question.png);
+  background-position: 8px 8px;
+  background-repeat: no-repeat;
+  background-color: #F4FFF4;
+  border: 1px dashed #406080;
+  padding: 10px 10px 10px 50px;
+  margin: 0.5em 0 0 0;
+  min-height: 25px;
+}
+
+div.wait-box {
+  background-image: url(../../../images/wait.png);
+  background-position: 8px 8px;
+  background-repeat: no-repeat;
+  background-color: #FFF4FF;
+  border: 1px dashed #406080;
+  padding: 10px 10px 10px 50px;
+  margin: 0.5em 0 0 0;
+  min-height: 25px;
+}
 
 /* This stuff is mostly unused, left in for compatibility */
-div#ajaxEditContainer table       { border: 0px solid #FFFFFF; }
-div#ajaxEditContainer td          { margin: 1px; }
-/* div#ajaxEditContainer             { overflow: auto; } /* Makes ajaxEditContainer scroll horizontally in firefox if the content is too wide - prevents that ugly clipping effect */
-div#ajaxEditContainer pre         { margin-left: 1em; background-color: #F8F8F8; border: 1px dashed #90B0D0; padding: 10px; overflow: auto; max-height: 150px; }
+div#ajaxEditContainer table {
+  border: 0px solid #FFFFFF;
+}
+
+div#ajaxEditContainer td {
+  margin: 1px;
+}
+
+div#ajaxEditContainer pre {
+  margin-left: 1em;
+  background-color: #F8F8F8;
+  border: 1px dashed #90B0D0;
+  padding: 10px;
+  overflow: auto;
+  max-height: 150px;
+}
 
 /* toolbar */
 div.toolbar {
@@ -308,11 +662,9 @@
 }
 div.toolbar a img {
   opacity: 0.6;
-  /*filter: alpha(opacity=60);*/
 }
 div.toolbar a:hover img, div.toolbar a:focus img {
   opacity: 1;
-  /*filter: alpha(opacity=100);*/
 }
 div.toolbar a {
   display: block;