Finished everything on the TODO list (yay!); several CSS cleanups; tons more changes in this commit - see the patch for details
authorDan
Sun, 01 Jul 2007 14:08:39 -0400
changeset 32 4d87aad3c4c0
parent 31 dc8741857bde
child 33 143d3ee32f3c
Finished everything on the TODO list (yay!); several CSS cleanups; tons more changes in this commit - see the patch for details
TODO
ajax.php
images/error.png
images/info.png
images/question.png
images/wait.png
images/warning.png
includes/clientside/css/enano-shared.css
includes/clientside/static/ajax.js
includes/clientside/static/faders.js
includes/common.php
includes/functions.php
includes/pageprocess.php
includes/pageutils.php
includes/sessions.php
includes/wikiengine/Parse/Mediawiki/Newline.php
includes/wikiengine/Parse/Mediawiki/Paragraph.php
includes/wikiformat.php
index.php
install.php
plugins/SpecialUserFuncs.php
plugins/SpecialUserPrefs.php
schema.sql
themes/admin/css/default.css
themes/boxart/css/blueberry.css
themes/oxygen/css/bleu.css
themes/oxygen/header.tpl
themes/stpatty/css/shamrock.css
--- a/TODO	Thu Jun 28 15:26:40 2007 -0400
+++ b/TODO	Sun Jul 01 14:08:39 2007 -0400
@@ -5,13 +5,18 @@
 [x] Add in Moderators group
     [x] Create default ACL rule for mods
 [x] Fix invalid HTML in SF.net logo
-[ ] Clean up the wikitext parser - a lot. It needs some serious work.
+[x] Clean up the wikitext parser - a lot. It needs some serious work.
     We need a way to detect whether the text is mostly HTML, and if
     so, then leave stuff like automatic adding of <p> and <br /> out
     of the picture. Continue to parse wikilinks.
+    [x] Probably the way to do this is to have a wrapper for (or modified version of) sanitize_html that uses the existing
+        XML pseudo-parser. Have it look for certain key block-level elements (div, p, table, blockquote, pre), and wrap <litewiki>
+        tags around those areas to have the wiki parser only parse internal links, image includes, external links, and basic
+        formatting items. Hopefully use Text_Wiki to handle the lite formatting. Need better TinyMCE compatibility, especially
+        in IE. (thanks to Christine Emmanuel for partly bringing this to my attention)
 [x] Add a system_group column and if it's set to 1, give (at least) a
     stern warning before deleting the group. Maybe disable the delete
-    button altogether?
+    button altogether? (delete button disabled)
 [x] SQL exporter: fix structure exporting when an auto column is defined
     and it's a named key (see pun_search_words)
 [x] Possibly add these fields: AIM, Yahoo, MSN, XMPP messenger icons, then homepage, location, occupation, hobbies, allow public e-mail display
@@ -31,6 +36,7 @@
     [] Come up with some alternative to the "formatting help" link (**IN 1.1**)
        [] By 1.1, all JWS code should be phased out and removed!
 [ ] Get the new page ID cleaner code working 100% perfectly - this is the core of Enano and should be completely bug free
+    Update 6/28: Is this done? Probably is, we need some QA done on it though
 
 
 Enano Clurichaun - TODO
--- a/ajax.php	Thu Jun 28 15:26:40 2007 -0400
+++ b/ajax.php	Sun Jul 01 14:08:39 2007 -0400
@@ -30,6 +30,10 @@
       // echo PageUtils::getpage($paths->page, false, ( (isset($_GET['oldid'])) ? $_GET['oldid'] : false ));
       $revision_id = ( (isset($_GET['oldid'])) ? intval($_GET['oldid']) : 0 );
       $page = new PageProcessor( $paths->cpage['urlname_nons'], $paths->namespace, $revision_id );
+      
+      $pagepass = ( isset($_REQUEST['pagepass']) ) ? $_REQUEST['pagepass'] : '';
+      $page->password = $pagepass;
+            
       $page->send();
       break;
     case "savepage":
@@ -106,12 +110,21 @@
       break;
     case "fillusername":
       $name = (isset($_GET['name'])) ? $db->escape($_GET['name']) : false;
-      if(!$name) die('userlist = new Array(); errorstring=\'Invalid URI\'');
-      $q = $db->sql_query('SELECT username,user_id FROM '.table_prefix.'users WHERE username LIKE \'%'.$name.'%\';');
-      if(!$q) die('userlist = new Array(); errorstring=\'MySQL error selecting username data: '.addslashes(mysql_error()).'\'');
-      if($db->numrows() < 1) die('userlist = new Array(); errorstring=\'No usernames found.\'');
+      if ( !$name ) 
+      {
+        die('userlist = new Array(); errorstring=\'Invalid URI\'');
+      }
+      $q = $db->sql_query('SELECT username,user_id FROM '.table_prefix.'users WHERE lcase(username) LIKE lcase(\'%'.$name.'%\');');
+      if ( !$q )
+      {
+        die('userlist = new Array(); errorstring=\'MySQL error selecting username data: '.addslashes(mysql_error()).'\'');
+      }
+      if($db->numrows() < 1)
+      {
+        die('userlist = new Array(); errorstring=\'No usernames found\';');
+      }
       echo 'var errorstring = false; userlist = new Array();';
-      $i=0;
+      $i = 0;
       while($r = $db->fetchrow())
       {
         echo "userlist[$i] = '".addslashes($r['username'])."'; ";
Binary file images/error.png has changed
Binary file images/info.png has changed
Binary file images/question.png has changed
Binary file images/wait.png has changed
Binary file images/warning.png has changed
--- a/includes/clientside/css/enano-shared.css	Thu Jun 28 15:26:40 2007 -0400
+++ b/includes/clientside/css/enano-shared.css	Sun Jul 01 14:08:39 2007 -0400
@@ -3,11 +3,28 @@
  */
 
 /* Information, warning, question, error, and wait boxes */
-div.error-box                     { background-image: url(../../../images/error.png);    background-repeat: no-repeat; background-color: #FFF4F4; border: 1px dashed #406080; padding: 10px 10px 10px 50px; margin: 1em 0 0 0; min-height: 25px; }
-div.info-box                      { background-image: url(../../../images/info.png);     background-repeat: no-repeat; background-color: #F4F4FF; border: 1px dashed #406080; padding: 10px 10px 10px 50px; margin: 1em 0 0 0; min-height: 25px; }
-div.warning-box                   { background-image: url(../../../images/warning.png);  background-repeat: no-repeat; background-color: #FFFFF4; border: 1px dashed #406080; padding: 10px 10px 10px 50px; margin: 1em 0 0 0; min-height: 25px; }
-div.question-box                  { background-image: url(../../../images/question.png); background-repeat: no-repeat; background-color: #F4FFF4; border: 1px dashed #406080; padding: 10px 10px 10px 50px; margin: 1em 0 0 0; min-height: 25px; }
-div.wait-box                      { background-image: url(../../../images/wait.png);     background-repeat: no-repeat; background-color: #FFF4FF; border: 1px dashed #406080; padding: 10px 10px 10px 50px; margin: 1em 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: 1em 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: 1em 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: 1em 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: 1em 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: 1em 0 0 0; min-height: 25px; }
+
+div.error-box-mini                { background-image: url(../../../images/mini-error.png);    background-position: 5px 5px; background-repeat: no-repeat; background-color: #FFF4F4; border: 1px solid #406080; padding: 4px 4px 4px 26px; margin: 1em 0; min-height: 17px; }
+div.info-box-mini                 { background-image: url(../../../images/mini-info.png);     background-position: 5px 5px; background-repeat: no-repeat; background-color: #F4F4FF; border: 1px solid #406080; padding: 4px 4px 4px 26px; margin: 1em 0; min-height: 17px; }
+div.warning-box-mini              { background-image: url(../../../images/mini-warning.png);  background-position: 5px 5px; background-repeat: no-repeat; background-color: #FFFFF4; border: 1px solid #406080; padding: 4px 4px 4px 26px; margin: 1em 0; min-height: 17px; }
+div.question-box-mini             { background-image: url(../../../images/mini-question.png); background-position: 5px 5px; background-repeat: no-repeat; background-color: #F4FFF4; border: 1px solid #406080; padding: 4px 4px 4px 26px; margin: 1em 0; min-height: 17px; }
+div.wait-box-mini                 { background-image: url(../../../images/mini-wait.png);     background-position: 5px 5px; background-repeat: no-repeat; background-color: #FFF4FF; border: 1px solid #406080; padding: 4px 4px 4px 26px; margin: 1em 0; min-height: 17px; }
+
+/* Similar to the Mediawiki-ish alert box (usermessage) */
+
+div.alert {
+  background-color: #F09090;
+  border: 1px solid #D03030;
+  color: #300000;
+  padding: 3px;
+  position: relative;
+  top: -3px;
+}
 
 /* toolbar */
 div.toolbar {
--- a/includes/clientside/static/ajax.js	Thu Jun 28 15:26:40 2007 -0400
+++ b/includes/clientside/static/ajax.js	Sun Jul 01 14:08:39 2007 -0400
@@ -226,12 +226,12 @@
 }
 
 function ajaxDeletePage() {
-  var reason = prompt('Please enter you reason for deleting this page.');
+  var reason = prompt('Please enter your reason for deleting this page.');
   if ( !reason || reason == '' )
   {
     return false;
   }
-  c = confirm('You are about to DESTROY this page. Do you REALLY want to do this?');
+  c = confirm('You are about to REVERSIBLY delete this page. Do you REALLY want to do this?\n\n(Comments and categorization data, as well as any attached files, will be permanently lost)');
   if(!c)
   {
     return;
--- a/includes/clientside/static/faders.js	Thu Jun 28 15:26:40 2007 -0400
+++ b/includes/clientside/static/faders.js	Sun Jul 01 14:08:39 2007 -0400
@@ -133,6 +133,7 @@
     mydiv.style.paddingLeft = '50px';
     mydiv.style.width = '360px';
     mydiv.style.backgroundRepeat = 'no-repeat';
+    mydiv.style.backgroundPosition = '8px 8px';
   }
   
   if(type & MB_ICONINFORMATION)
--- a/includes/common.php	Thu Jun 28 15:26:40 2007 -0400
+++ b/includes/common.php	Sun Jul 01 14:08:39 2007 -0400
@@ -191,56 +191,59 @@
 
 define('ENANO_BASE_CLASSES_INITIALIZED', '');
 
-$code = $plugins->setHook('base_classes_initted');
-foreach ( $code as $cmd )
+if ( !defined('IN_ENANO_INSTALL') )
 {
-  eval($cmd);
-}
+  $code = $plugins->setHook('base_classes_initted');
+  foreach ( $code as $cmd )
+  {
+    eval($cmd);
+  }
+    
+  $p = RenderMan::strToPageId($paths->get_pageid_from_url());
+  if( ( $p[1] == 'Admin' || $p[1] == 'Special' ) && function_exists('page_'.$p[1].'_'.$p[0].'_preloader'))
+  {
+    @call_user_func('page_'.$p[1].'_'.$p[0].'_preloader');
+  }
+  
+  $session->start();
+  $paths->init();
+  
+  define('ENANO_MAINSTREAM', '');
   
-$p = RenderMan::strToPageId($paths->get_pageid_from_url());
-if( ( $p[1] == 'Admin' || $p[1] == 'Special' ) && function_exists('page_'.$p[1].'_'.$p[0].'_preloader'))
-{
-  @call_user_func('page_'.$p[1].'_'.$p[0].'_preloader');
+  // If the site is disabled, bail out, unless we're trying to log in or administer the site
+  if(getConfig('site_disabled') == '1' && $session->user_level < USER_LEVEL_ADMIN)
+  {
+    if ( $paths->namespace == 'Admin' || ( $paths->namespace == 'Special' && ( $paths->cpage['urlname_nons'] == 'CSS' || $paths->cpage['urlname_nons'] == 'Administration' || $paths->cpage['urlname_nons'] == 'Login' ) ) )
+    {
+      // do nothing; allow execution to continue
+    }
+    else
+    {
+      if(!$n = getConfig('site_disabled_notice')) 
+      {
+        $n = 'The administrator has disabled the site. Please check back later.';
+      }
+      
+      $text = RenderMan::render($n) . '
+      <div class="info-box">
+        If you have an administrative account, you may <a href="'.makeUrlNS('Special', 'Login').'">log in</a> to the site or <a href="'.makeUrlNS('Special', 'Administration').'">use the administration panel</a>.
+      </div>';
+      $paths->wiki_mode = 0;
+      die_semicritical('Site disabled', $text);
+    }
+  }
+  else if(getConfig('site_disabled') == '1' && $session->user_level >= USER_LEVEL_ADMIN)
+  {
+    $template->site_disabled = true;
+  }
+  
+  $code = $plugins->setHook('session_started');
+  foreach ( $code as $cmd )
+  {
+    eval($cmd);
+  }
+  
+  if(isset($_GET['noheaders'])) $template->no_headers = true;
 }
 
-$session->start();
-$paths->init();
-
-define('ENANO_MAINSTREAM', '');
-
-// If the site is disabled, bail out, unless we're trying to log in or administer the site
-if(getConfig('site_disabled') == '1' && $session->user_level < USER_LEVEL_ADMIN)
-{
-  if ( $paths->namespace == 'Admin' || ( $paths->namespace == 'Special' && ( $paths->cpage['urlname_nons'] == 'CSS' || $paths->cpage['urlname_nons'] == 'Administration' || $paths->cpage['urlname_nons'] == 'Login' ) ) )
-  {
-    // do nothing; allow execution to continue
-  }
-  else
-  {
-    if(!$n = getConfig('site_disabled_notice')) 
-    {
-      $n = 'The administrator has disabled the site. Please check back later.';
-    }
-    
-    $text = RenderMan::render($n) . '
-    <div class="info-box">
-      If you have an administrative account, you may <a href="'.makeUrlNS('Special', 'Login').'">log in</a> to the site or <a href="'.makeUrlNS('Special', 'Administration').'">use the administration panel</a>.
-    </div>';
-    $paths->wiki_mode = 0;
-    die_semicritical('Site disabled', $text);
-  }
-}
-else if(getConfig('site_disabled') == '1' && $session->user_level >= USER_LEVEL_ADMIN)
-{
-  $template->site_disabled = true;
-}
-
-$code = $plugins->setHook('session_started');
-foreach ( $code as $cmd )
-{
-  eval($cmd);
-}
-
-if(isset($_GET['noheaders'])) $template->no_headers = true;
-
 ?>
--- a/includes/functions.php	Thu Jun 28 15:26:40 2007 -0400
+++ b/includes/functions.php	Sun Jul 01 14:08:39 2007 -0400
@@ -1609,6 +1609,158 @@
   
 }
 
+/**
+ * Using the same parsing code as sanitize_html(), this function adds <litewiki> tags around certain block-level elements
+ * @param string $html The input HTML
+ * @return string formatted HTML
+ */
+
+function wikiformat_process_block($html)
+{
+  
+  $tok1 = "<litewiki>";
+  $tok2 = "</litewiki>";
+  
+  $block_tags = array('div', 'p', 'table', 'blockquote', 'pre');
+  
+  $len = strlen($html);
+  $in_quote = false;
+  $quote_char = '';
+  $tag_start = 0;
+  $tag_name = '';
+  $in_tag = false;
+  $trk_name = false;
+  
+  $diag = 0;
+  
+  $block_tagname = '';
+  $in_blocksec = 0;
+  $block_start = 0;
+  
+  for ( $i = 0; $i < $len; $i++ )
+  {
+    $chr = $html{$i};
+    $prev = ( $i == 0 ) ? '' : $html{ $i - 1 };
+    $next = ( ( $i + 1 ) == $len ) ? '' : $html { $i + 1 };
+    
+    // Are we inside of a quoted section?
+    if ( $in_quote && $in_tag )
+    {
+      if ( $quote_char == $chr && $prev != '\\' )
+        $in_quote = false;
+    }
+    elseif ( ( $chr == '"' || $chr == "'" ) && $prev != '\\' && $in_tag )
+    {
+      $in_quote = true;
+      $quote_char = $chr;
+    }
+    
+    if ( $chr == '<' && !$in_tag && $next == '/' )
+    {
+      // Iterate through until we've got a tag name
+      $tag_name = '';
+      $i++;
+      while(true)
+      {
+        $i++;
+        // echo $i . ' ';
+        $chr = $html{$i};
+        $prev = ( $i == 0 ) ? '' : $html{ $i - 1 };
+        $next = ( ( $i + 1 ) == $len ) ? '' : $html { $i + 1 };
+        $tag_name .= $chr;
+        if ( $next == '>' )
+          break;
+      }
+      // echo '<br />';
+      if ( in_array($tag_name, $block_tags) )
+      {
+        if ( $block_tagname == $tag_name )
+        {
+          $in_blocksec -= 1;
+          if ( $in_blocksec == 0 )
+          {
+            $block_tagname = '';
+            $i += 2;
+            // echo 'Finished wiki litewiki wraparound calc at pos: ' . $i;
+            $full_litewiki = substr($html, $block_start, ( $i - $block_start ));
+            $new_text = "{$tok1}{$full_litewiki}{$tok2}";
+            $html = substr($html, 0, $block_start) . $new_text . substr($html, $i);
+            
+            $i += ( strlen($tok1) + strlen($tok2) ) - 1;
+            $len = strlen($html);
+            
+            //die('<pre>' . htmlspecialchars($html) . '</pre>');
+          }
+        }
+      }
+      
+      $in_tag = false;
+      $in_quote = false;
+      $tag_name = '';
+      
+      continue;
+    }
+    else if ( $chr == '<' && !$in_tag && $next != '/' )
+    {
+      // start of a tag
+      $tag_start = $i;
+      $in_tag = true;
+      $trk_name = true;
+    }
+    else if ( !$in_quote && $in_tag && $chr == '>' )
+    {
+      if ( !in_array($tag_name, $block_tags) )
+      {
+        // Inline tag - reset and go to the next one
+        // echo '&lt;inline ' . $tag_name . '&gt; ';
+        
+        $in_tag = false;
+        $tag_name = '';
+        continue;
+      }
+      else
+      {
+        // echo '&lt;block: ' . $tag_name . ' @ ' . $i . '&gt;<br/>';
+        if ( $in_blocksec == 0 )
+        {
+          //die('Found a starting tag for a block element: ' . $tag_name . ' at pos ' . $tag_start);
+          $block_tagname = $tag_name;
+          $block_start = $tag_start;
+          $in_blocksec++;
+        }
+        else if ( $block_tagname == $tag_name )
+        {
+          $in_blocksec++;
+        }
+        
+        $in_tag = false;
+        $tag_name = '';
+        continue;
+      }
+    }
+    elseif ( $in_tag && $trk_name )
+    {
+      $is_alphabetical = ( strtolower($chr) != strtoupper($chr) || in_array($chr, array('0', '1', '2', '3', '4', '5', '6', '7', '8', '9')) || $chr == '?' || $chr == '!' || $chr == '-' );
+      if ( $is_alphabetical )
+        $tag_name .= $chr;
+      else
+      {
+        $trk_name = false;
+      }
+    }
+    
+    // Tokenization complete
+    
+  }
+  
+  $regex = '/' . str_replace('/', '\\/', preg_quote($tok2)) . '([\s]*)' . preg_quote($tok1) . '/is';
+  // die(htmlspecialchars($regex));
+  $html = preg_replace($regex, '\\1', $html);
+  
+  return $html;
+  
+}
+
 function htmlalternatives($string)
 {
   $ret = '';
@@ -2102,6 +2254,21 @@
   }
 }
 
+/**
+ * Injects a string into another string at the specified position.
+ * @param string The haystack
+ * @param string The needle
+ * @param int    Position at which to insert the needle
+ */
+
+function inject_substr($haystack, $needle, $pos)
+{
+  $str1 = substr($haystack, 0, $pos);
+  $pos++;
+  $str2 = substr($haystack, $pos);
+  return "{$str1}{$needle}{$str2}";
+}
+
 //die('<pre>Original:  01010101010100101010100101010101011010'."\nProcessed: ".uncompress_bitfield(compress_bitfield('01010101010100101010100101010101011010')).'</pre>');
 
 ?>
--- a/includes/pageprocess.php	Thu Jun 28 15:26:40 2007 -0400
+++ b/includes/pageprocess.php	Sun Jul 01 14:08:39 2007 -0400
@@ -60,6 +60,13 @@
   var $perms = null;
   
   /**
+   * The SHA1 hash of the user-inputted password for the page
+   * @var string
+   */
+   
+  var $password = '';
+  
+  /**
    * Switch to track if redirects are allowed. Defaults to true.
    * @var bool
    */
@@ -143,6 +150,15 @@
         $this->send_headers = false;
         $strict_no_headers = true;
       }
+      if ( $paths->pages[$pathskey]['password'] != '' && $paths->pages[$pathskey]['password'] != sha1('') )
+      {
+        $password =& $paths->pages[$pathskey]['password'];
+        if ( $this->password != $password )
+        {
+          $this->err_wrong_password();
+          return false;
+        }
+      }
     }
     if ( $this->namespace == 'Special' || $this->namespace == 'Admin' )
     {
@@ -472,6 +488,7 @@
   function _handle_userpage()
   {
     global $db, $session, $paths, $template, $plugins; // Common objects
+    global $email;
     
     if ( $this->page_id == $paths->cpage['urlname_nons'] && $this->namespace == $paths->namespace )
     {
@@ -689,7 +706,6 @@
     if ( $userdata['email_public'] == 1 )
     {
       $class = ( $class == 'row1' ) ? 'row3' : 'row1';
-      global $email;
       $email_link = $email->encryptEmail($userdata['email']);
       echo '<tr><td class="'.$class.'">E-mail address: ' . $email_link . '</td></tr>';
     }
@@ -808,6 +824,36 @@
   }
   
   /**
+   * Inform the user of an incorrect or absent password
+   * @access private
+   */
+   
+  function err_wrong_password()
+  {
+    global $db, $session, $paths, $template, $plugins; // Common objects
+    
+    $title = 'Password required';
+    $message = ( empty($this->password) ) ? '<p>Access to this page requires a password. Please enter the password for this page below:</p>' : '<p>The password you entered for this page was incorrect. Please enter the password for this page below:</p>';
+    $message .= '<form action="' . makeUrlNS($this->namespace, $this->page_id) . '" method="post">
+                   <p>
+                     <label>Password: <input name="pagepass" type="password" /></label>&nbsp;&nbsp;<input type="submit" value="Submit" />
+                   </p>
+                 </form>';
+    if ( $this->send_headers )
+    {
+      $template->tpl_strings['PAGE_NAME'] = $title;
+      $template->header();
+      echo "$message";
+      $template->footer();
+    }
+    else
+    {
+      echo "<h2>$title</h2>
+            $message";
+    }
+  }
+  
+  /**
    * Send the error message to the user complaining that there weren't any rows.
    * @access private
    */
--- a/includes/pageutils.php	Thu Jun 28 15:26:40 2007 -0400
+++ b/includes/pageutils.php	Sun Jul 01 14:08:39 2007 -0400
@@ -1240,14 +1240,20 @@
   {
     global $db, $session, $paths, $template, $plugins; // Common objects
     if(!$session->get_permissions('clear_logs')) die('Administrative privileges are required to flush logs, you loser.');
-    $e = $db->sql_query('DELETE FROM '.table_prefix.'logs WHERE page_id=\''.$page_id.'\' AND namespace=\''.$namespace.'\';');
+    $e = $db->sql_query('DELETE FROM '.table_prefix.'logs WHERE page_id=\''.$db->escape($page_id).'\' AND namespace=\''.$db->escape($namespace).'\';');
     if(!$e) $db->_die('The log entries could not be deleted.');
-    $e = $db->sql_query('SELECT page_text,char_tag FROM '.table_prefix.'page_text WHERE page_id=\''.$page_id.'\' AND namespace=\''.$namespace.'\';');
-    if(!$e) $db->_die('The current page text could not be selected; as a result, creating the backup of the page failed. Please make a backup copy of the page by clicking Edit this page and then clicking Save Changes.');
-    $row = $db->fetchrow();
-    $db->free_result();
-    $q='INSERT INTO '.table_prefix.'logs(log_type,action,time_id,date_string,page_id,namespace,page_text,char_tag,author,edit_summary,minor_edit) VALUES(\'page\', \'edit\', '.time().', \''.date('d M Y h:i a').'\', \''.$page_id.'\', \''.$namespace.'\', \''.$db->escape($row['page_text']).'\', \''.$row['char_tag'].'\', \''.$session->username.'\', \''."Automatic backup created when logs were purged".'\', '.'false'.');';
-    if(!$db->sql_query($q)) $db->_die('The history (log) entry could not be inserted into the logs table.');
+    
+    // If the page exists, make a backup of it in case it gets spammed/vandalized
+    // If not, the admin's probably deleting a trash page
+    if ( isset($paths->pages[ $paths->nslist[$namespace] . $page_id ]) )
+    {
+      $e = $db->sql_query('SELECT page_text,char_tag FROM '.table_prefix.'page_text WHERE page_id=\''.$page_id.'\' AND namespace=\''.$namespace.'\';');
+      if(!$e) $db->_die('The current page text could not be selected; as a result, creating the backup of the page failed. Please make a backup copy of the page by clicking Edit this page and then clicking Save Changes.');
+      $row = $db->fetchrow();
+      $db->free_result();
+      $q='INSERT INTO '.table_prefix.'logs(log_type,action,time_id,date_string,page_id,namespace,page_text,char_tag,author,edit_summary,minor_edit) VALUES(\'page\', \'edit\', '.time().', \''.date('d M Y h:i a').'\', \''.$page_id.'\', \''.$namespace.'\', \''.$db->escape($row['page_text']).'\', \''.$row['char_tag'].'\', \''.$session->username.'\', \''."Automatic backup created when logs were purged".'\', '.'false'.');';
+      if(!$db->sql_query($q)) $db->_die('The history (log) entry could not be inserted into the logs table.');
+    }
     return('The logs for this page have been cleared. A backup of this page has been added to the logs table so that this page can be restored in case of vandalism or spam later.');
   }
   
--- a/includes/sessions.php	Thu Jun 28 15:26:40 2007 -0400
+++ b/includes/sessions.php	Sun Jul 01 14:08:39 2007 -0400
@@ -712,6 +712,7 @@
         {
           eval($cmd);
         }
+        
         return 'success';
       }
       else
@@ -771,32 +772,50 @@
    
   function register_session($user_id, $username, $password, $level = USER_LEVEL_MEMBER)
   {
+    // Random key identifier
     $salt = md5(microtime() . mt_rand());
+    
+    // SHA1 hash of password, stored in the key
     $passha1 = sha1($password);
+    
+    // Unencrypted session key
     $session_key = "u=$username;p=$passha1;s=$salt";
+    
+    // Encrypt the key
     $aes = new AESCrypt(AES_BITS, AES_BLOCKSIZE);
     $session_key = $aes->encrypt($session_key, $this->private_key, ENC_HEX);
+    
+    // If we're registering an elevated-privilege key, it needs to be on GET
     if($level > USER_LEVEL_MEMBER)
     {
+      // Reverse it - cosmetic only ;-)
       $hexkey = strrev($session_key);
       $this->sid_super = $hexkey;
       $_GET['auth'] = $hexkey;
     }
     else
     {
+      // Stash it in a cookie
+      // For now, make the cookie last forever, we can change this in 1.1.x
       setcookie( 'sid', $session_key, time()+315360000, scriptPath.'/' );
       $_COOKIE['sid'] = $session_key;
     }
+    // $keyhash is stored in the database, this is for compatibility with the older DB structure
     $keyhash = md5($session_key);
+    // Record the user's IP
     $ip = ip2hex($_SERVER['REMOTE_ADDR']);
     if(!$ip)
       die('$session->register_session: Remote-Addr was spoofed');
+    // The time needs to be stashed to enforce the 15-minute limit on elevated session keys
     $time = time();
+    
+    // Sanity check
     if(!is_int($user_id))
       die('Somehow an SQL injection attempt crawled into our session registrar! (1)');
     if(!is_int($level))
       die('Somehow an SQL injection attempt crawled into our session registrar! (2)');
     
+    // All done!
     $query = $this->sql('INSERT INTO '.table_prefix.'session_keys(session_key, salt, user_id, auth_level, source_ip, time) VALUES(\''.$keyhash.'\', \''.$salt.'\', '.$user_id.', '.$level.', \''.$ip.'\', '.$time.');');
     return true;
   }
@@ -1564,7 +1583,7 @@
     }
     elseif(is_string($user))
     {
-      $q = $this->sql('SELECT user_id,username,email FROM '.table_prefix.'users WHERE username=\''.$db->escape($user).'\';');
+      $q = $this->sql('SELECT user_id,username,email FROM '.table_prefix.'users WHERE lcase(username)=lcase(\''.$db->escape($user).'\');');
     }
     else
     {
@@ -1664,25 +1683,47 @@
   /**
    * For a given user level identifier (USER_LEVEL_*), returns a string describing that user level.
    * @param int User level
+   * @param bool If true, returns a shorter string. Optional.
    * @return string
    */
   
-  function userlevel_to_string($user_level)
+  function userlevel_to_string($user_level, $short = false)
   {
-    switch ( $user_level )
+    if ( $short )
     {
-      case USER_LEVEL_GUEST:
-        return 'Low - guest privileges';
-      case USER_LEVEL_MEMBER:
-        return 'Standard - normal member level';
-      case USER_LEVEL_CHPREF:
-        return 'Medium - user can change his/her own e-mail address and password';
-      case USER_LEVEL_MOD:
-        return 'High - moderator privileges';
-      case USER_LEVEL_ADMIN:
-        return 'Highest - administrative privileges';
-      default:
-        return "Unknown ($user_level)";
+      switch ( $user_level )
+      {
+        case USER_LEVEL_GUEST:
+          return 'Guest';
+        case USER_LEVEL_MEMBER:
+          return 'Member';
+        case USER_LEVEL_CHPREF:
+          return 'Sensitive preferences changeable';
+        case USER_LEVEL_MOD:
+          return 'Moderator';
+        case USER_LEVEL_ADMIN:
+          return 'Administrative';
+        default:
+          return "Level $user_level";
+      }
+    }
+    else
+    {
+      switch ( $user_level )
+      {
+        case USER_LEVEL_GUEST:
+          return 'Low - guest privileges';
+        case USER_LEVEL_MEMBER:
+          return 'Standard - normal member level';
+        case USER_LEVEL_CHPREF:
+          return 'Medium - user can change his/her own e-mail address and password';
+        case USER_LEVEL_MOD:
+          return 'High - moderator privileges';
+        case USER_LEVEL_ADMIN:
+          return 'Highest - administrative privileges';
+        default:
+          return "Unknown ($user_level)";
+      }
     }
   }
   
@@ -1784,7 +1825,7 @@
       if(is_string($email))
       {
         // I didn't write this regex.
-        if(!preg_match('/^(?:[\w\d]+\.?)+@(?:(?:[\w\d]\-?)+\.)+\w{2,4}$/', $email))
+        if(!preg_match('/^(?:[\w\d]+\.?)+@((?:(?:[\w\d]\-?)+\.)+\w{2,4}|localhost)$/', $email))
           $errors[] = 'The e-mail address you entered is invalid.';
         $strs[] = 'email=\''.$db->escape($email).'\'';
       }
@@ -2340,7 +2381,6 @@
               alert(\'The key is messed up\\nType: \'+typeof(cryptkey)+len);
             }
           }
-          if(frm.username) frm.username.focus();
           function runEncryption()
           {
             if(testpassed)
--- a/includes/wikiengine/Parse/Mediawiki/Newline.php	Thu Jun 28 15:26:40 2007 -0400
+++ b/includes/wikiengine/Parse/Mediawiki/Newline.php	Sun Jul 01 14:08:39 2007 -0400
@@ -70,6 +70,55 @@
             $this->wiki->addToken($this->rule) .
             $matches[2];
     }
+    
+    /**
+    *
+    * Abstrct method to parse source text for matches.
+    *
+    * Applies the rule's regular expression to the source text, passes
+    * every match to the process() method, and replaces the matched text
+    * with the results of the processing.
+    *
+    * @access public
+    *
+    * @see Text_Wiki_Parse::process()
+    *
+    */
+
+    function parse()
+    {
+        $source =& $this->wiki->source;
+        
+        // This regex attempts to find HTML tags that can be safely compacted together without formatting loss
+        // The idea is to make it easier for the HTML parser to find litewiki elements
+        //$source = preg_replace('/<\/([a-z0-9:-]+?)>([\s]*[\n]+[\s]+|[\s]+[\n]+[\s]*|[\n]+)<([a-z0-9:-]+)(.*?)>/i', '</\\1><\\3\\4>', $source);
+        $source = wikiformat_process_block($source);
+        
+        $rand_key = md5( str_rot13(strval(dechex(time()))) . microtime() . strval(mt_rand()) );
+        preg_match_all('/<(litewiki|pre)([^>]*?)>(.*?)<\/\\1>/is', $this->wiki->source, $matches);
+        
+        $poslist = array();
+        
+        foreach ( $matches[0] as $i => $match )
+        {
+            $source = str_replace($match, "{LITEWIKI:$i:$rand_key}", $source);
+        }
+        
+        $this->wiki->source = preg_replace_callback(
+            $this->regex,
+            array(&$this, 'process'),
+            $this->wiki->source
+        );
+        
+        foreach ( $matches[3] as $i => $match )
+        {
+            $source = str_replace("{LITEWIKI:$i:$rand_key}", $match, $source);
+        }
+        
+        // die('<pre>'.htmlspecialchars($source).'</pre>');
+        
+        unset($matches, $source, $rand_key);
+    }
 }
 
 ?>
\ No newline at end of file
--- a/includes/wikiengine/Parse/Mediawiki/Paragraph.php	Thu Jun 28 15:26:40 2007 -0400
+++ b/includes/wikiengine/Parse/Mediawiki/Paragraph.php	Sun Jul 01 14:08:39 2007 -0400
@@ -56,7 +56,8 @@
             'deflist',
             'table',
             'list',
-            'toc'
+            'toc',
+            'pre'
         )
     );
     
@@ -142,5 +143,37 @@
             return $start . trim($matches[0]) . $end;
         }
     }
+    
+    /**
+    *
+    * Abstrct method to parse source text for matches.
+    *
+    * Applies the rule's regular expression to the source text, passes
+    * every match to the process() method, and replaces the matched text
+    * with the results of the processing.
+    *
+    * @access public
+    *
+    * @see Text_Wiki_Parse::process()
+    *
+    */
+
+    function parse()
+    {
+        $source =& $this->wiki->source;
+        $source = wikiformat_process_block($source);
+        
+        $source = preg_replace('/<litewiki>(.*?)<\/litewiki>([\s]+?|$)/is', '<litewiki>\\1\\2</litewiki>', $source);
+        
+        // die('<pre>' . htmlspecialchars($source) . '</pre>');
+        
+        $this->wiki->source = preg_replace_callback(
+            $this->regex,
+            array(&$this, 'process'),
+            $this->wiki->source
+        );
+        
+        $source = preg_replace('/<litewiki>(.*?)<\/litewiki>/is', '\\1', $source);
+    }
 }
 ?>
\ No newline at end of file
--- a/includes/wikiformat.php	Thu Jun 28 15:26:40 2007 -0400
+++ b/includes/wikiformat.php	Sun Jul 01 14:08:39 2007 -0400
@@ -65,7 +65,8 @@
     var $disable = array(
         'Html',
         'Include',
-        'Embed'
+        'Embed',
+        'Tighten'
     );
 
     var $parseConf = array();
@@ -357,6 +358,8 @@
           if (is_object($this->parseObj[$name])) {
             $this->parseObj[$name]->parse();
           }
+          // For debugging
+          // echo('<p>' . $name . ':</p><pre>'.htmlspecialchars($this->source).'</pre>');
         }
       }
     }
--- a/index.php	Thu Jun 28 15:26:40 2007 -0400
+++ b/index.php	Sun Jul 01 14:08:39 2007 -0400
@@ -54,6 +54,8 @@
       $rev_id = ( (isset($_GET['oldid'])) ? intval($_GET['oldid']) : 0 );
       $page = new PageProcessor( $paths->cpage['urlname_nons'], $paths->namespace, $rev_id );
       $page->send_headers = true;
+      $pagepass = ( isset($_REQUEST['pagepass']) ) ? sha1($_REQUEST['pagepass']) : '';
+      $page->password = $pagepass;
       $page->send();
       break;
     case 'comments':
@@ -187,7 +189,7 @@
       break;
     case 'moreoptions':
       $template->header();
-      echo '<div class="pagebar" id="pagebarpopup2" style="width: 150px; padding: 0;">'.$template->tpl_strings['TOOLBAR_EXTRAS'].'</div>';
+      echo '<div class="menu_nojs" style="width: 150px; padding: 0;"><ul style="display: block;"><li><div class="label">More options for this page</div><div style="clear: both;"></div></li>'.$template->tpl_strings['TOOLBAR_EXTRAS'].'</ul></div>';
       $template->footer();
       break;
     case 'protect':
--- a/install.php	Thu Jun 28 15:26:40 2007 -0400
+++ b/install.php	Sun Jul 01 14:08:39 2007 -0400
@@ -1128,13 +1128,43 @@
       fwrite($cf_handle, $config_file);
       fclose($cf_handle);
       
+      echo 'done!<br />Starting the Enano API...';
+      
+      $template_bak = $template;
+      
+      // Get Enano loaded
+      $_GET['title'] = 'Main_Page';
+      require('includes/common.php');
+      
+      // We need to be logged in (with admin rights) before logs can be flushed
+      $session->login_without_crypto($_POST['admin_user'], $dec, false);
+      
+      // Now that login cookies are set, initialize the session manager and ACLs
+      $session->start();
+      $paths->init();
+      
+      unset($template);
+      $template =& $template_bak;
+      
       echo 'done!<br />Initializing logs...';
       
-      $q = mysql_query('INSERT INTO ' . $_POST['table_prefix'] . 'logs(log_type,action,time_id,date_string,author,page_text,edit_summary) VALUES(\'security\', \'install_enano\', ' . time() . ', \'' . date('d M Y h:i a') . '\', \'' . mysql_real_escape_string($_POST['admin_user']) . '\', \'' . mysql_real_escape_string(ENANO_VERSION) . '\', \'' . mysql_real_escape_string($_SERVER['REMOTE_ADDR']) . '\');', $conn);
+      $q = $db->sql_query('INSERT INTO ' . $_POST['table_prefix'] . 'logs(log_type,action,time_id,date_string,author,page_text,edit_summary) VALUES(\'security\', \'install_enano\', ' . time() . ', \'' . date('d M Y h:i a') . '\', \'' . mysql_real_escape_string($_POST['admin_user']) . '\', \'' . mysql_real_escape_string(ENANO_VERSION) . '\', \'' . mysql_real_escape_string($_SERVER['REMOTE_ADDR']) . '\');', $conn);
       if ( !$q )
-        err('Error setting up logs: '.mysql_error());
+        err('Error setting up logs: '.$db->get_error());
+      
+      if ( !$session->get_permissions('clear_logs') )
+      {
+        echo '<br />Error: session manager won\'t permit flushing logs, these is a bug.';
+        break;
+      }
       
-      echo 'done!<h3>Installation of Enano is complete.</h3><p>Review any warnings above, and then <a href="install.php?mode=finish">click here to  finish the installation</a>.';
+      // unset($session);
+      // $session = new sessionManager();
+      // $session->start();
+      
+      PageUtils::flushlogs('Main_Page', 'Article');
+      
+      echo 'done!<h3>Installation of Enano is complete.</h3><p>Review any warnings above, and then <a href="install.php?mode=finish">click here to finish the installation</a>.';
       
       // echo '<script type="text/javascript">window.location="'.scriptPath.'/install.php?mode=finish";</script>';
       
--- a/plugins/SpecialUserFuncs.php	Thu Jun 28 15:26:40 2007 -0400
+++ b/plugins/SpecialUserFuncs.php	Sun Jul 01 14:08:39 2007 -0400
@@ -167,6 +167,10 @@
               {
                 echo 'tabindex="1" ';
               }
+              else
+              {
+                echo 'tabindex="3" ';
+              }
               if ( $session->user_logged_in )
               {
                 echo 'value="' . $session->username . '"';
@@ -192,7 +196,7 @@
          </tr>
          <?php } ?>
          <tr>
-           <th colspan="3" style="text-align: center" class="subhead"><input type="submit" name="login" value="Log in" tabindex="3" /></th>
+           <th colspan="3" style="text-align: center" class="subhead"><input type="submit" name="login" value="Log in" tabindex="<?php echo ( $level <= USER_LEVEL_MEMBER ) ? '3' : '2'; ?>" /></th>
          </tr>
       </table>
     </div>
@@ -201,6 +205,15 @@
       <input type="hidden" name="crypt_key" value="<?php echo $pubkey; ?>" />
       <input type="hidden" name="crypt_data" value="" />
       <input type="hidden" name="auth_level" value="<?php echo (string)$level; ?>" />
+      <?php if ( $level <= USER_LEVEL_MEMBER ): ?>
+      <script type="text/javascript">
+        document.forms.loginform.username.focus();
+      </script>
+      <?php else: ?>
+      <script type="text/javascript">
+        document.forms.loginform.pass.focus();
+      </script>
+      <?php endif; ?>
     </form>
     <?php
       echo $session->aes_javascript('loginform', 'pass', 'use_crypt', 'crypt_key', 'crypt_data', 'challenge_data');
--- a/plugins/SpecialUserPrefs.php	Thu Jun 28 15:26:40 2007 -0400
+++ b/plugins/SpecialUserPrefs.php	Sun Jul 01 14:08:39 2007 -0400
@@ -165,7 +165,9 @@
           $result = $session->update_user($session->user_id, false, $old_pass, false, $new_email);
           if ( $result != 'success' )
           {
-            die_friendly('Error updating e-mail address', '<p>Session API returned error: ' . $result . '</p>');
+            $message = '<p>The following errors were encountered while saving your e-mail address:</p>';
+            $message .= '<ul><li>' . implode("</li>\n<li>", $result) . '</li></ul>';
+            die_friendly('Error updating e-mail address', $message);
           }
           $email_changed = true;
         }
@@ -192,27 +194,30 @@
             if ( strlen($newpass) < 6 )
               $errors .= '<div class="error-box">Password must be at least 6 characters. You hacked my script, darn you!</div>';
             // Encrypt new password
-            $newpass_enc = $aes->encrypt($newpass, $session->private_key, ENC_HEX);
-            // Perform the swap
-            $q = $db->sql_query('UPDATE '.table_prefix.'users SET password=\'' . $newpass_enc . '\' WHERE user_id=' . $session->user_id . ';');
-            if ( !$q )
-              $db->_die();
-            // Log out and back in
-            $username = $session->username;
-            $session->logout();
-            if ( $email_changed )
+            if ( empty($errors) )
             {
-              if ( getConfig('account_activation') == 'user' )
-              {
-                redirect(makeUrl(getConfig('main_page')), 'Profile changed', 'Your password and e-mail address have been changed. Since e-mail activation is required on this site, you will need to re-activate your account to continue. An e-mail has been sent to the new e-mail address with an activation link. You must click that link in order to log in again.', 19);
-              }
-              else if ( getConfig('account_activation') == 'admin' )
+              $newpass_enc = $aes->encrypt($newpass, $session->private_key, ENC_HEX);
+              // Perform the swap
+              $q = $db->sql_query('UPDATE '.table_prefix.'users SET password=\'' . $newpass_enc . '\' WHERE user_id=' . $session->user_id . ';');
+              if ( !$q )
+                $db->_die();
+              // Log out and back in
+              $username = $session->username;
+              $session->logout();
+              if ( $email_changed )
               {
-                redirect(makeUrl(getConfig('main_page')), 'Profile changed', 'Your password and e-mail address have been changed. Since administrative activation is requires on this site, a request has been sent to the administrators to activate your account for you. You will not be able to use your account until it is activated by an administrator.', 19);
+                if ( getConfig('account_activation') == 'user' )
+                {
+                  redirect(makeUrl(getConfig('main_page')), 'Profile changed', 'Your password and e-mail address have been changed. Since e-mail activation is required on this site, you will need to re-activate your account to continue. An e-mail has been sent to the new e-mail address with an activation link. You must click that link in order to log in again.', 19);
+                }
+                else if ( getConfig('account_activation') == 'admin' )
+                {
+                  redirect(makeUrl(getConfig('main_page')), 'Profile changed', 'Your password and e-mail address have been changed. Since administrative activation is requires on this site, a request has been sent to the administrators to activate your account for you. You will not be able to use your account until it is activated by an administrator.', 19);
+                }
               }
+              $session->login_without_crypto($session->username, $newpass);
+              redirect(makeUrlNS('Special', 'Preferences'), 'Password changed', 'Your password has been changed, and you will now be redirected back to the user control panel.', 4);
             }
-            $session->login_without_crypto($session->username, $newpass);
-            redirect(makeUrlNS('Special', 'Preferences'), 'Password changed', 'Your password has been changed, and you will now be redirected back to the user control panel.', 4);
           }
         }
         else
@@ -432,7 +437,7 @@
         
         $session->real_name = $real_name;
         
-        if ( !preg_match('/@([a-z0-9-]+)(\.([a-z0-9-\.]+))?/', $imaddr_msn) )
+        if ( !preg_match('/@([a-z0-9-]+)(\.([a-z0-9-\.]+))?/', $imaddr_msn) && !empty($imaddr_msn) )
         {
           $imaddr_msn = "$imaddr_msn@hotmail.com";
         }
--- a/schema.sql	Thu Jun 28 15:26:40 2007 -0400
+++ b/schema.sql	Sun Jul 01 14:08:39 2007 -0400
@@ -261,10 +261,12 @@
   ('oxygen', 'Oxygen', 1, 'bleu.css', 1),
   ('stpatty', 'St. Patty', 2, 'shamrock.css', 1);
 
-INSERT INTO {{TABLE_PREFIX}}users(user_id, username, password, email, real_name, user_level, theme, style, signature, reg_time) VALUES(1, 'Anonymous', 'invalid-pass-hash', 'anonspam@enanocms.org', 'None', 1, 'oxygen', 'bleu', '', 0);
+INSERT INTO {{TABLE_PREFIX}}users(user_id, username, password, email, real_name, user_level, theme, style, signature, reg_time, account_active) VALUES
+  (1, 'Anonymous', 'invalid-pass-hash', 'anonspam@enanocms.org', 'None', 1, 'oxygen', 'bleu', '', 0, 0),
+  (2, '{{ADMIN_USER}}', '{{ADMIN_PASS}}', '{{ADMIN_EMAIL}}', '{{REAL_NAME}}', 9, 'oxygen', 'bleu', 1, UNIX_TIMESTAMP(), 1);
 
-INSERT INTO {{TABLE_PREFIX}}users(user_id, username, password, email, real_name, user_level, theme, style, account_active, reg_time) VALUES
-  (2, '{{ADMIN_USER}}', '{{ADMIN_PASS}}', '{{ADMIN_EMAIL}}', '{{REAL_NAME}}', 9, 'oxygen', 'bleu', 1, UNIX_TIMESTAMP());
+INSERT INTO {{TABLE_PREFIX}}users_extra(user_id) VALUES
+  (2);
 
 INSERT INTO {{TABLE_PREFIX}}groups(group_id,group_name,group_type,system_group) VALUES(1, 'Everyone', 3, 1),
   (2,'Administrators',3,1),
--- a/themes/admin/css/default.css	Thu Jun 28 15:26:40 2007 -0400
+++ b/themes/admin/css/default.css	Sun Jul 01 14:08:39 2007 -0400
@@ -48,12 +48,12 @@
  * jBox menu system
  */
 
-div.menu {
+div.menu, div.menu_nojs {
   background-color: #B0D0F0;
   font-size: 7pt;
   border-width: 0;
 }
-div.menu a, div.menu div.label {
+div.menu a, div.menu_nojs a, div.menu div.label, div.menu_nojs div.label {
   padding: 2.5pt 5px;
   margin-right: 3px;
   text-decoration: none;
@@ -61,50 +61,50 @@
   float: left;
   color: #406080;
 }
-div.menu div.label {
+div.menu div.label, div.menu_nojs div.label {
   color: #001020;
   cursor: default;
 }
-div.menu span.sep {
+div.menu span.sep, div.menu_nojs span.sep {
   display: block;
   float: left;
   width: 5px;
 }
-div.menu div.multopts {
+div.menu div.multopts, div.menu_nojs div.multopts {
   line-height: 17pt;
 }
-div.menu div.multopts a, div.menu div.multopts div.label {
+div.menu div.multopts a, div.menu_nojs div.multopts a, div.menu div.multopts div.label, div.menu_nojs div.multopts div.label {
   float: none;
   display: inline;
 }
-div.menu a.liteselected, div.menu a.liteselected:hover, div.menu a:hover {
+div.menu a.liteselected, div.menu_nojs a.liteselected, div.menu a.liteselected:hover, div.menu_nojs a.liteselected:hover, div.menu a:hover, div.menu_nojs a:hover {
   color: #406080;
   background-color: #D0F0FF;
 }
-div.menu input[type ^="text"], div.menu input[type ^="password"] {
+div.menu input[type ^="text"], div.menu_nojs input[type ^="text"], div.menu input[type ^="password"], div.menu_nojs input[type ^="password"] {
   border-width: 0;
   font-size: 9pt;
   padding: 4px 5px;
   max-width: 70px;
   background-color: #D0F0FF;
 }
-div.menu input[type ^="text"]:hover, div.menu input[type ^="password"]:hover {
+div.menu input[type ^="text"]:hover, div.menu_nojs input[type ^="text"]:hover, div.menu input[type ^="password"]:hover, div.menu_nojs input[type ^="password"]:hover {
   background-color: #E0F0FF;
 }
-div.menu input[type ^="text"]:focus, div.menu input[type ^="password"]:focus {
+div.menu input[type ^="text"]:focus, div.menu_nojs input[type ^="text"]:focus, div.menu input[type ^="password"]:focus, div.menu_nojs input[type ^="password"]:focus {
   background-color: #F0F0FF;
 }
-div.menu input[type ^="button"], div.menu input[type ^="submit"] {
+div.menu input[type ^="button"], div.menu_nojs input[type ^="button"], div.menu input[type ^="submit"], div.menu_nojs input[type ^="submit"] {
   border-width: 0;
   font-size: 9pt;
   padding: 3px 5px;
   max-width: 70px;
 }
-div.menu a.current, div.menu a.current:hover, div.menu a.selected, div.menu a.selected:hover {
+div.menu a.current, div.menu_nojs a.current, div.menu a.current:hover, div.menu_nojs a.current:hover, div.menu a.selected, div.menu_nojs a.selected, div.menu a.selected:hover, div.menu_nojs a.selected:hover {
   color: #000040;
   background-color: #FFFFFF;
 }
-div.menu ul {
+div.menu ul, div.menu_nojs ul {
   display: none;
   position: absolute;
   padding: 0;
@@ -113,10 +113,10 @@
   border-width: 0;
   min-width: 120px;
 }
-div.menu ul li {
+div.menu ul li, div.menu_nojs ul li {
   list-style: none;
 }
-div.menu ul a {
+div.menu ul a, div.menu_nojs ul a {
   float: none;
   margin: 0;
 }
--- a/themes/boxart/css/blueberry.css	Thu Jun 28 15:26:40 2007 -0400
+++ b/themes/boxart/css/blueberry.css	Sun Jul 01 14:08:39 2007 -0400
@@ -89,12 +89,12 @@
  * jBox menu system
  */
 
-div.menu {
+div.menu, div.menu_nojs {
   background-color: #85A7D8;
   font-size: 7pt;
   border-width: 0;
 }
-div.menu a, div.menu div.label {
+div.menu a, div.menu_nojs a, div.menu div.label, div.menu_nojs div.label {
   padding: 2.5pt 5px;
   margin-right: 3px;
   text-decoration: none;
@@ -102,50 +102,50 @@
   float: left;
   color: #254778;
 }
-div.menu div.label {
+div.menu div.label, div.menu_nojs div.label {
   color: #001020;
   cursor: default;
 }
-div.menu span.sep {
+div.menu span.sep, div.menu_nojs span.sep {
   display: block;
   float: left;
   width: 5px;
 }
-div.menu div.multopts {
+div.menu div.multopts, div.menu_nojs div.multopts {
   line-height: 17pt;
 }
-div.menu div.multopts a, div.menu div.multopts div.label {
+div.menu div.multopts a, div.menu_nojs div.multopts a, div.menu div.multopts div.label, div.menu_nojs div.multopts div.label {
   float: none;
   display: inline;
 }
-div.menu a.liteselected, div.menu a.liteselected:hover, div.menu a:hover {
+div.menu a.liteselected, div.menu_nojs a.liteselected, div.menu a.liteselected:hover, div.menu_nojs a.liteselected:hover, div.menu a:hover, div.menu_nojs a:hover {
   color: #001748;
   background-color: #A5C7F8;
 }
-div.menu input[type ^="text"], div.menu input[type ^="password"] {
+div.menu input[type ^="text"], div.menu_nojs input[type ^="text"], div.menu input[type ^="password"], div.menu_nojs input[type ^="password"] {
   border-width: 0;
   font-size: 9pt;
   padding: 4px 5px;
   max-width: 70px;
   background-color: #D0F0FF;
 }
-div.menu input[type ^="text"]:hover, div.menu input[type ^="password"]:hover {
+div.menu input[type ^="text"]:hover, div.menu_nojs input[type ^="text"]:hover, div.menu input[type ^="password"]:hover, div.menu_nojs input[type ^="password"]:hover {
   background-color: #E0F0FF;
 }
-div.menu input[type ^="text"]:focus, div.menu input[type ^="password"]:focus {
+div.menu input[type ^="text"]:focus, div.menu_nojs input[type ^="text"]:focus, div.menu input[type ^="password"]:focus, div.menu_nojs input[type ^="password"]:focus {
   background-color: #F0F0FF;
 }
-div.menu input[type ^="button"], div.menu input[type ^="submit"] {
+div.menu input[type ^="button"], div.menu_nojs input[type ^="button"], div.menu input[type ^="submit"], div.menu_nojs input[type ^="submit"] {
   border-width: 0;
   font-size: 9pt;
   padding: 3px 5px;
   max-width: 70px;
 }
-div.menu a.current, div.menu a.current:hover, div.menu a.selected, div.menu a.selected:hover {
+div.menu a.current, div.menu_nojs a.current, div.menu a.current:hover, div.menu_nojs a.current:hover, div.menu a.selected, div.menu_nojs a.selected, div.menu a.selected:hover, div.menu_nojs a.selected:hover {
   color: #000040;
   background-color: #FFFFFF;
 }
-div.menu ul {
+div.menu ul, div.menu_nojs ul {
   display: none;
   position: absolute;
   padding: 0;
@@ -154,10 +154,10 @@
   border-width: 0;
   min-width: 120px;
 }
-div.menu ul li {
+div.menu ul li, div.menu_nojs ul li {
   list-style: none;
 }
-div.menu ul a {
+div.menu ul a, div.menu_nojs ul a {
   float: none;
   margin: 0;
 }
--- a/themes/oxygen/css/bleu.css	Thu Jun 28 15:26:40 2007 -0400
+++ b/themes/oxygen/css/bleu.css	Sun Jul 01 14:08:39 2007 -0400
@@ -90,6 +90,9 @@
   font-size: 7pt;
   border-width: 0;
 }
+.menu_bg {
+  background-color: #B0D0F0;
+}
 div.menu a, div.menu div.label {
   padding: 2.5pt 5px;
   margin-right: 3px;
@@ -103,6 +106,7 @@
   margin-right: 3px;
   text-decoration: none;
   display: block;
+  float: left;
   color: #406080;
 }
 div.menu div.label, div.menu_nojs div.label {
@@ -148,7 +152,7 @@
   color: #000040;
   background-color: #FFFFFF;
 }
-div.menu ul {
+div.menu ul, div.menu_nojs ul {
   display: none;
   position: absolute;
   padding: 0;
@@ -157,6 +161,7 @@
   border-width: 0;
   min-width: 120px;
 }
+/*
 div.menu_nojs ul {
   display: block;
   padding: 0;
@@ -165,6 +170,7 @@
   border-width: 0;
   min-width: 120px;
 }
+*/
 div.menu ul li, div.menu_nojs ul li {
   list-style: none;
 }
@@ -231,11 +237,11 @@
 .catCheck:hover                   { padding: 3px; background-color: #F0F0F0; }
 
 /* Information, warning, question, error, and wait boxes */
-div.error-box                     { background-image: url(../../../images/error.png);    background-repeat: no-repeat; background-color: #FFF4F4; border: 1px dashed #406080; padding: 10px 10px 10px 50px; margin: 1em 0 0 1em; min-height: 25px; }
-div.info-box                      { background-image: url(../../../images/info.png);     background-repeat: no-repeat; background-color: #F4F4FF; border: 1px dashed #406080; padding: 10px 10px 10px 50px; margin: 1em 0 0 1em; min-height: 25px; }
-div.warning-box                   { background-image: url(../../../images/warning.png);  background-repeat: no-repeat; background-color: #FFFFF4; border: 1px dashed #406080; padding: 10px 10px 10px 50px; margin: 1em 0 0 1em; min-height: 25px; }
-div.question-box                  { background-image: url(../../../images/question.png); background-repeat: no-repeat; background-color: #F4FFF4; border: 1px dashed #406080; padding: 10px 10px 10px 50px; margin: 1em 0 0 1em; min-height: 25px; }
-div.wait-box                      { background-image: url(../../../images/wait.png);     background-repeat: no-repeat; background-color: #FFF4FF; border: 1px dashed #406080; padding: 10px 10px 10px 50px; margin: 1em 0 0 1em; 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: 1em 0 0 1em; 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: 1em 0 0 1em; 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: 1em 0 0 1em; 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: 1em 0 0 1em; 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: 1em 0 0 1em; min-height: 25px; }
 
 /* This stuff is mostly unused, left in for compatibility */
 div#ajaxEditContainer table       { border: 0px solid #FFFFFF; }
--- a/themes/oxygen/header.tpl	Thu Jun 28 15:26:40 2007 -0400
+++ b/themes/oxygen/header.tpl	Sun Jul 01 14:08:39 2007 -0400
@@ -130,14 +130,16 @@
         
         <tr><td id="mdg-brl"></td><td style="background-color: #FFFFFF;"></td><td id="mdg-brr"></td></tr>
         
-        <tr><td id="mdg-bl"></td><td>
+        <tr>
+          <td id="mdg-bl"></td>
+          <td class="menu_bg">
           <div class="menu_nojs" id="pagebar_main">
             <div class="label">Page tools</div>
             {TOOLBAR}
             <ul>
               {TOOLBAR_EXTRAS}
             </ul>
-            <span class="menuclear">&nbsp;</span>
+            <span class="menuclear"></span>
           </div>
         </td><td id="mdg-br"></td></tr>
         <tr><td id="mdg-ml"></td><td style="background-color: #FFFFFF;">
--- a/themes/stpatty/css/shamrock.css	Thu Jun 28 15:26:40 2007 -0400
+++ b/themes/stpatty/css/shamrock.css	Sun Jul 01 14:08:39 2007 -0400
@@ -223,12 +223,12 @@
  * jBox menu system
  */
 
-div.menu {
+div.menu, div.menu_nojs {
   background-color: #93e058;
   font-size: 7pt;
   border-width: 0;
 }
-div.menu a, div.menu div.label {
+div.menu a, div.menu_nojs a, div.menu div.label, div.menu_nojs div.label {
   padding: 2.5pt 5px;
   margin-right: 3px;
   text-decoration: none;
@@ -237,51 +237,51 @@
   color: #235000;
   border-bottom-width: 0 !important;
 }
-div.menu div.label {
+div.menu div.label, div.menu_nojs div.label {
   color: #002010;
   cursor: default;
 }
-div.menu span.sep {
+div.menu span.sep, div.menu_nojs span.sep {
   display: block;
   float: left;
   width: 5px;
 }
-div.menu div.multopts {
+div.menu div.multopts, div.menu_nojs div.multopts {
   line-height: 17pt;
 }
-div.menu div.multopts a, div.menu div.multopts div.label {
+div.menu div.multopts a, div.menu_nojs div.multopts a, div.menu div.multopts div.label, div.menu_nojs div.multopts div.label {
   float: none;
   display: inline;
 }
-div.menu a.liteselected, div.menu a.liteselected:hover, div.menu a:hover {
+div.menu a.liteselected, div.menu_nojs a.liteselected, div.menu a.liteselected:hover, div.menu_nojs a.liteselected:hover, div.menu a:hover, div.menu_nojs a:hover {
   color: #235000;
   background-color: #A3F068;
 }
-div.menu input[type ^="text"], div.menu input[type ^="password"] {
+div.menu input[type ^="text"], div.menu_nojs input[type ^="text"], div.menu input[type ^="password"], div.menu_nojs input[type ^="password"] {
   border-width: 0;
   font-size: 9pt;
   padding: 4px 5px;
   max-width: 70px;
   background-color: #A3F068;
 }
-div.menu input[type ^="text"]:hover, div.menu input[type ^="password"]:hover {
+div.menu input[type ^="text"]:hover, div.menu_nojs input[type ^="text"]:hover, div.menu input[type ^="password"]:hover, div.menu_nojs input[type ^="password"]:hover {
   background-color: #AAF870;
 }
-div.menu input[type ^="text"]:focus, div.menu input[type ^="password"]:focus {
+div.menu input[type ^="text"]:focus, div.menu_nojs input[type ^="text"]:focus, div.menu input[type ^="password"]:focus, div.menu_nojs input[type ^="password"]:focus {
   background-color: #B3FF78;
 }
-div.menu input[type ^="button"], div.menu input[type ^="submit"] {
+div.menu input[type ^="button"], div.menu_nojs input[type ^="button"], div.menu input[type ^="submit"], div.menu_nojs input[type ^="submit"] {
   border-width: 0;
   font-size: 9pt;
   padding: 3px 5px;
   max-width: 70px;
 }
-div.menu a.current, div.menu a.current:hover, div.menu a.selected, div.menu a.selected:hover {
+div.menu a.current, div.menu_nojs a.current, div.menu a.current:hover, div.menu_nojs a.current:hover, div.menu a.selected, div.menu_nojs a.selected, div.menu a.selected:hover, div.menu_nojs a.selected:hover {
   color: #000040;
   background-color: #f4fff7;
   font-weight: bold;
 }
-div.menu ul {
+div.menu ul, div.menu_nojs ul {
   display: none;
   position: absolute;
   padding: 0;
@@ -290,10 +290,15 @@
   border-width: 0;
   min-width: 120px;
 }
-div.menu ul li {
+/*
+div.menu_nojs ul {
+  display: block !important;
+}
+*/
+div.menu ul li, div.menu_nojs ul li {
   list-style: none;
 }
-div.menu ul a {
+div.menu ul a, div.menu_nojs ul a {
   float: none;
   margin: 0;
 }