Replaced the menu in the admin theme with something much more visually pleasureable; minor fix in Special:UploadFile; finished patching a couple of XSS problems from Banshee; finished Admin:PageGroups; removed unneeded code in flyin.js; finished tag system (except tag cloud); 1.0.1 release candidate
authorDan
Sun, 05 Aug 2007 15:58:50 -0400
changeset 81 d7fc25acd3f3
parent 80 cb7dde69c301
child 82 03c7f5ec1e4c
Replaced the menu in the admin theme with something much more visually pleasureable; minor fix in Special:UploadFile; finished patching a couple of XSS problems from Banshee; finished Admin:PageGroups; removed unneeded code in flyin.js; finished tag system (except tag cloud); 1.0.1 release candidate
ajax.php
includes/clientside/static/flyin.js
includes/functions.php
includes/pageutils.php
index.php
plugins/SpecialUpdownload.php
plugins/SpecialUserFuncs.php
plugins/admin/PageGroups.php
themes/admin/header.tpl
themes/admin/js/menu.js
themes/admin/simple-header.tpl
themes/oxygen/header.tpl
themes/printable/header.tpl
themes/stpatty/header.tpl
--- a/ajax.php	Wed Aug 01 13:39:27 2007 -0400
+++ b/ajax.php	Sun Aug 05 15:58:50 2007 -0400
@@ -164,7 +164,7 @@
         for($i=0;$i<sizeof($u);$i++) // Can't use foreach because we need the value of $i and we need to use both $u and $n
         {
           echo "userlist[$i] = '".addslashes($n[$i])."';\n";
-          echo "namelist[$i] = '".addslashes($u[$i])."';\n";
+          echo "namelist[$i] = '".addslashes(htmlspecialchars($u[$i]))."';\n";
         }
       } else {
         die('userlist = new Array(); namelist = new Array(); errorstring=\'No page matches found.\'');
--- a/includes/clientside/static/flyin.js	Wed Aug 01 13:39:27 2007 -0400
+++ b/includes/clientside/static/flyin.js	Sun Aug 05 15:58:50 2007 -0400
@@ -157,94 +157,8 @@
     
   }
   
-  /*
-   * Framestepper parameters
-   * /
-  
-  // starting value for inertia
-  var inertiabase = 1;
-  // increment for inertia, or 0 to disable inertia effects
-  var inertiainc  = 1;
-  // when the progress reaches this %, deceleration is activated
-  var divider = 0.666667;
-  // multiplier for deceleration, setting this above 2 can cause some weird slowdown effects
-  var decelerate  = 2; // 1 / divider; // reciprocal of the divider
-  
-  /*
-   * Timer parameters
-   * /
-  
-  // how long animation start is delayed, you want this at 0
-  var timer = 0;
-  // frame ttl
-  var timestep = 12;
-  // sanity check
-  var frames = 0;
-  
-  // cache element so it can be changed from within setTimeout()
-  var rand_seed = Math.floor(Math.random() * 1000000);
-  fly_in_cache[rand_seed] = element;
-  
-  // set element left pos, you can comment this out to preserve left position
-  element.style.left = left + 'px';
-  
-  // total distance to be traveled
-  dist = abs(top - topi);
-  
-  // animation loop
+  // old framestepper code removed from here in Loch Ness
   
-  while ( true )
-  {
-    // used for a sanity check
-    frames++;
-    
-    // time until this frame should be executed
-    timer += timestep;
-    
-    // math stuff
-    // how far we are along in animation...
-    diff = abs(top - topi);
-    // ...in %
-    ratio = abs( 1 - ( diff / dist ) );
-    // decelerate if we're more than 2/3 of the way there
-    if ( ratio < divider )
-      inertiabase += inertiainc;
-    else
-      inertiabase -= ( inertiainc * decelerate );
-    
-    // if the deceleration factor is anywhere above 1 then technically that can cause an infinite loop
-    // so leave this in there unless decelerate is set to 1
-    if ( inertiabase < 1 )
-      inertiabase = 1;
-    
-    // uncomment to disable inertia
-    // inertiabase = 3;
-    
-    // figure out frame Y position
-    topi = ( abs_dir == FI_UP ) ? topi - inertiabase : topi + inertiabase;
-    if ( ( abs_dir == FI_DOWN && topi > top ) || ( abs_dir == FI_UP && top > topi ) )
-      topi = top;
-    
-    // tell the browser to do it
-    setTimeout('var o = fly_in_cache['+rand_seed+']; o.style.top=\''+topi+'px\';', timer);
-    if ( !nofade )
-    {
-      // handle fade
-      opac_factor = ratio * 100;
-      if ( direction == FI_OUT )
-        opac_factor = 100 - opac_factor;
-      setTimeout('var o = fly_in_cache['+rand_seed+']; domObjChangeOpac('+opac_factor+', o);', timer);
-    }
-    
-    // if we're done or if our sanity check failed then break out of the loop
-    if ( ( abs_dir == FI_DOWN && topi >= top ) || ( abs_dir == FI_UP && top >= topi ) || frames > 1000 )
-      break;
-  }
-  
-  timer += timestep;
-  setTimeout('delete(fly_in_cache['+rand_seed+']);', timer);
-  return timer;
-  */
   timeout += timerstep;
   return timeout;
 }
--- a/includes/functions.php	Wed Aug 01 13:39:27 2007 -0400
+++ b/includes/functions.php	Sun Aug 05 15:58:50 2007 -0400
@@ -2680,15 +2680,19 @@
   //
   if ( $do_gzip && function_exists('ob_gzhandler') )
   {
-    //
-    // Copied from phpBB, which was in turn borrowed from php.net
-    //
     $gzip_contents = ob_get_contents();
     ob_end_clean();
     
-    header('Content-encoding: gzip');
-    $gzip_contents = ob_gzhandler($gzip_contents);
-    echo $gzip_contents;
+    $return = ob_gzhandler($gzip_contents);
+    if ( $return )
+    {
+      header('Content-encoding: gzip');
+      echo $gzip_contents;
+    }
+    else
+    {
+      echo $gzip_contents;
+    }
   }
 }
 
@@ -2763,8 +2767,9 @@
     // fix for firefox issue
     $js = preg_replace('/\};([\s]*)(else|\))/i', '}\\2', $js);
     
+    $replacement = "<script{$jscript[1][$i]}>/* <![CDATA[ */ $js /* ]]> */</script>";
     // apply changes
-    $html = str_replace($jscript[0][$i], "<script{$jscript[1][$i]}>$js</script>", $html);
+    $html = str_replace($jscript[0][$i], $replacement, $html);
   }
   
   // Which tags to strip - you can change this if needed
@@ -2797,11 +2802,11 @@
   $size_after = strlen($html);
   
   // Tell snoopish users what's going on
-  $html = str_replace('<html>', "\n".'<!-- NOTE: Enano has performed an HTML optimization routine on the HTML you see here. This is to enhance page loading speeds.
+  $html = str_replace('<html', "\n".'<!-- NOTE: Enano has performed an HTML optimization routine on the HTML you see here. This is to enhance page loading speeds.
      To view the uncompressed source of this page, add the "nocompress" parameter to the URI of this page: index.php?title=Main_Page&nocompress or Main_Page?nocompress'."
      Size before compression: $size_before bytes
      Size after compression:  $size_after bytes
-     -->\n<html>", $html);
+     -->\n<html", $html);
   return $html;
 }
 
--- a/includes/pageutils.php	Wed Aug 01 13:39:27 2007 -0400
+++ b/includes/pageutils.php	Sun Aug 05 15:58:50 2007 -0400
@@ -608,13 +608,14 @@
         
         // 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 'Protected page</td><td class="'.$cls.'">Reason: '.$r['edit_summary'];
         elseif($r['action']=='unprot')   echo 'Unprotected page</td><td class="'.$cls.'">Reason: '.$r['edit_summary'];
         elseif($r['action']=='semiprot') echo 'Semi-protected page</td><td class="'.$cls.'">Reason: '.$r['edit_summary'];
-        elseif($r['action']=='rename')   echo 'Renamed page</td><td class="'.$cls.'">Old title: '.$r['edit_summary'];
+        elseif($r['action']=='rename')   echo 'Renamed page</td><td class="'.$cls.'">Old title: '.htmlspecialchars($r['edit_summary']);
         elseif($r['action']=='create')   echo 'Created page</td><td class="'.$cls.'">';
         elseif($r['action']=='delete')   echo 'Deleted page</td><td class="'.$cls.'">Reason: '.$r['edit_summary'];
-        elseif($r['action']=='reupload') echo 'Uploaded new file version</td><td class="'.$cls.'">Reason: '.$r['edit_summary'];
+        elseif($r['action']=='reupload') echo 'Uploaded new file version</td><td class="'.$cls.'">Reason: '.htmlspecialchars($r['edit_summary']);
         echo '</td>';
         
         // Actions!
--- a/index.php	Wed Aug 01 13:39:27 2007 -0400
+++ b/index.php	Sun Aug 05 15:58:50 2007 -0400
@@ -13,7 +13,7 @@
  *
  */
 
-  // Se t up gzip encoding before any output is sent
+  // Set up gzip encoding before any output is sent
   
   $aggressive_optimize_html = true;
   
--- a/plugins/SpecialUpdownload.php	Wed Aug 01 13:39:27 2007 -0400
+++ b/plugins/SpecialUpdownload.php	Sun Aug 05 15:58:50 2007 -0400
@@ -129,7 +129,7 @@
     $ext = substr($filename, strrpos($filename, '.'), strlen($filename));
     $flen = filesize($file['tmp_name']);
     
-    $comments = $db->escape(RenderMan::strip_php($_POST['comments']));
+    $comments = ( isset($_POST['update']) ) ? $db->escape($_POST['comments']) : $db->escape(RenderMan::preprocess_text($_POST['comments'], false, false));
     $chartag = sha1(microtime());
     $urln = str_replace(' ', '_', $filename);
     
--- a/plugins/SpecialUserFuncs.php	Wed Aug 01 13:39:27 2007 -0400
+++ b/plugins/SpecialUserFuncs.php	Sun Aug 05 15:58:50 2007 -0400
@@ -577,8 +577,7 @@
   {
     echo 'No user selected!';
     $template->footer();
-    $db->close();
-    exit;
+    return;
   }
   
   $user = $db->escape($user);
@@ -587,8 +586,10 @@
   if(!$db->sql_query($q)) $db->_die('The history data for the page "'.$paths->cpage['name'].'" could not be selected.');
   echo 'History of edits and actions<h3>Edits:</h3>';
   if($db->numrows() < 1) echo 'No history entries in this category.';
-  while($r = $db->fetchrow()) {    
-    echo '<a href="#" onclick="ajaxHistView(\''.$r['time_id'].'\', \''.$paths->nslist[$r['namespace']].$r['page_id'].'\'); return false;"><i>'.$r['date_string'].'</i></a> (<a href="#" onclick="ajaxRollback(\''.$r['time_id'].'\'); return false;">revert</a>) <a href="'.makeUrl($paths->nslist[$r['namespace']].$r['page_id']).'">'.$paths->nslist[$r['namespace']].$r['page_id'].'</a>: '.$r['edit_summary'];
+  while($r = $db->fetchrow())
+  {
+    $title = get_page_title($r['page_id'], $r['namespace']);    
+    echo '<a href="' . makeUrlNS($r['namespace'], $r['page_id'], "oldid={$r['time_id']}", true) . '" onclick="ajaxHistView(\''.$r['time_id'].'\', \''.$paths->nslist[$r['namespace']].$r['page_id'].'\'); return false;"><i>'.$r['date_string'].'</i></a> (<a href="#" onclick="ajaxRollback(\''.$r['time_id'].'\'); return false;">revert to</a>) <a href="'.makeUrl($paths->nslist[$r['namespace']].$r['page_id']).'">'.htmlspecialchars($title).'</a>: '.$r['edit_summary'];
     if($r['minor_edit']) echo '<b> - minor edit</b>';
     echo '<br />';
   }
@@ -597,17 +598,22 @@
   $q = 'SELECT log_type,time_id,action,date_string,page_id,namespace,author,edit_summary,minor_edit,page_id,namespace FROM '.table_prefix.'logs WHERE author=\''.$user.'\' AND action!=\'edit\' 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.');
   if($db->numrows() < 1) echo 'No history entries in this category.';
-  while($r = $db->fetchrow()) {
-    if($r['log_type']=='page') {
-    echo '(<a href="#" onclick="ajaxRollback(\''.$r['time_id'].'\'); return false;">rollback</a>) <i>'.$r['date_string'].'</i> <a href="'.makeUrl($paths->nslist[$r['namespace']].$r['page_id']).'">'.$paths->nslist[$r['namespace']].$r['page_id'].'</a>: ';
-    if($r['action']=='prot') echo 'Protected page; reason: '.$r['edit_summary'];
-    elseif($r['action']=='unprot') echo 'Unprotected page; reason: '.$r['edit_summary'];
-    elseif($r['action']=='rename') echo 'Renamed page; old title was: '.$r['edit_summary'];
-    elseif($r['action']=='create') echo 'Created page';
-    elseif($r['action']=='delete') echo 'Deleted page';
-    if($r['minor_edit']) echo '<b> - minor edit</b>';
-    echo '<br />';
-    } elseif($r['log_type']=='security') {
+  while($r = $db->fetchrow()) 
+  {
+    if ( $r['log_type'] == 'page' )
+    {
+      $title = get_page_title($r['page_id'], $r['namespace']);
+      echo '(<a href="#" onclick="ajaxRollback(\''.$r['time_id'].'\'); return false;">rollback</a>) <i>'.$r['date_string'].'</i> <a href="'.makeUrl($paths->nslist[$r['namespace']].$r['page_id']).'">'.htmlspecialchars($title).'</a>: ';
+      if      ( $r['action'] == 'prot'   ) echo 'Protected page; reason: '.$r['edit_summary'];
+      else if ( $r['action'] == 'unprot' ) echo 'Unprotected page; reason: '.$r['edit_summary'];
+      else if ( $r['action'] == 'rename' ) echo 'Renamed page; old title was: '.htmlspecialchars($r['edit_summary']);
+      else if ( $r['action'] == 'create' ) echo 'Created page';
+      else if ( $r['action'] == 'delete' ) echo 'Deleted page';
+      if ( $r['minor_edit'] ) echo '<b> - minor edit</b>';
+      echo '<br />';
+    }
+    else if($r['log_type']=='security') 
+    {
       // Not implemented, and when it is, it won't be public
     }
   }
@@ -621,6 +627,10 @@
   if(!$session->user_logged_in) die_friendly('Access denied', '<p>You must be logged in to change your style. Spoofer.</p>');
   if(isset($_POST['theme']) && isset($_POST['style']) && isset($_POST['return_to']))
   {
+    if ( !preg_match('/^([a-z0-9_-]+)$/i', $_POST['theme']) )
+      die('Hacking attempt');
+    if ( !preg_match('/^([a-z0-9_-]+)$/i', $_POST['style']) )
+      die('Hacking attempt');
     $d = ENANO_ROOT . '/themes/' . $_POST['theme'];
     $f = ENANO_ROOT . '/themes/' . $_POST['theme'] . '/css/' . $_POST['style'] . '.css';
     if(!file_exists($d) || !is_dir($d)) die('The directory "'.$d.'" does not exist.');
--- a/plugins/admin/PageGroups.php	Wed Aug 01 13:39:27 2007 -0400
+++ b/plugins/admin/PageGroups.php	Sun Aug 05 15:58:50 2007 -0400
@@ -404,7 +404,7 @@
       $q = $db->sql_query('DELETE FROM '.table_prefix.'page_group_members WHERE pg_id=' . $delete_id . ';');
       if ( !$q )
         $db->_die();
-      echo "<div class='info-box'>The group ".'"'."$pg_name".'"'." has been deleted.</div>";
+      echo "<div class='info-box'>The group ".'"'.htmlspecialchars("$pg_name").'"'." has been deleted.</div>";
     }
     else if ( isset($_POST['action']['edit']) && !isset($_POST['action']['noop']) )
     {
@@ -439,16 +439,26 @@
           return;
         }
         
+        /*
+        // We're gonna allow adding nonexistent pages for now
         if ( !isPage($page) )
         {
           $return = array('mode' => 'error', 'text' => 'The page you are trying to add (' . htmlspecialchars($page) . ') does not exist.');
           echo $json->encode($return);
           return;
         }
+        */
         
         list($page_id, $namespace) = RenderMan::strToPageID($page);
         $page_id = sanitize_page_id($page_id);
         
+        if ( !isset($paths->namespace[$namespace]) )
+        {
+          $return = array('mode' => 'error', 'text' => 'Invalid namespace return from RenderMan::strToPageID()');
+          echo $json->encode($return);
+          return;
+        }
+        
         $q = $db->sql_query('SELECT "x" FROM '.table_prefix.'page_group_members WHERE pg_id=' . $edit_id . ' AND page_id=\'' . $db->escape($page_id) . '\' AND namespace=\'' . $namespace . '\';');
         if ( !$q )
         {
@@ -479,9 +489,76 @@
         return;
       }
       
-      if ( isset($_POST['action']['edit_save']) )
+      if ( isset($_POST['action']['edit_save']) && isset($_POST['pg_name']) )
       {
         $edit_id = $_POST['action']['edit'];
+        $edit_id = intval($edit_id);
+        if ( !empty($edit_id) )
+        {
+          // Update group name
+          $new_name = $_POST['pg_name'];
+          if ( empty($new_name) )
+          {
+            echo '<div class="error-box">Please enter a valid name for this group.</div>';
+          }
+          else
+          {
+            $q = $db->sql_query('SELECT pg_name FROM '.table_prefix.'page_groups WHERE pg_id=' . $edit_id . ';');
+            if ( !$q )
+              $db->_die();
+            $row = $db->fetchrow();
+            $db->free_result();
+            if ( $new_name != $row['pg_name'] )
+            {
+              $new_name = $db->escape(trim($new_name));
+              $q = $db->sql_query('UPDATE '.table_prefix.'page_groups SET pg_name=\'' . $new_name . '\' WHERE pg_id=' . $edit_id . ';');
+              if ( !$q )
+                $db->_die();
+              else
+                echo '<div class="info-box">The group name was updated successfully.</div>';
+            }
+            if ( $_POST['pg_type'] == PAGE_GRP_TAGGED )
+            {
+              $target = $_POST['pg_target'];
+              $target = sanitize_tag($target);
+              if ( empty($target) )
+              {
+                echo '<div class="error-box">Please enter a valid tag.</div>';
+              }
+              else
+              {
+                $target = $db->escape($target);
+                $q = $db->sql_query('UPDATE '.table_prefix.'page_groups SET pg_target=\'' . $target . '\' WHERE pg_id=' . $edit_id . ';');
+                if ( !$q )
+                  $db->_die();
+                else
+                  echo '<div class="info-box">The affecting tag was updated.</div>';
+              }
+            }
+            else if ( $_POST['pg_type'] == PAGE_GRP_CATLINK )
+            {
+              $target = $_POST['pg_target'];
+              if ( empty($target) )
+              {
+                echo '<div class="error-box">No category ID specified on POST URI.</div>';
+              }
+              else
+              {
+                $target = $db->escape($target);
+                $q = $db->sql_query('UPDATE '.table_prefix.'page_groups SET pg_target=\'' . $target . '\' WHERE pg_id=' . $edit_id . ';');
+                if ( !$q )
+                  $db->_die();
+                else
+                  echo '<div class="info-box">The affecting category was updated.</div>';
+              }
+            }
+          }
+        }
+      }
+      else if ( isset($_POST['action']['edit_save']) )
+      {
+        $edit_id = $_POST['action']['edit'];
+        $edit_id = intval($edit_id);
       }
       else
       {
@@ -495,7 +572,7 @@
         return;
       }
       
-      if ( isset($_POST['action']['edit_save']['do_rm']) )
+      if ( isset($_POST['action']['edit_save']['do_rm']) && !isset($_POST['pg_name']) )
       {
         $vals = array_keys($_POST['action']['edit_save']['rm']);
         $good = array();
@@ -504,13 +581,20 @@
           if ( strval(intval($id)) == $id )
             $good[] = $id;
         }
-        $subquery = ( count($good) > 0 ) ? 'pg_member_id=' . implode(' OR pg_member_id=', $good) : "'foo'='foo'";
-        $sql = 'DELETE FROM '.table_prefix."page_group_members WHERE ( $subquery ) AND pg_id=$edit_id;";
-        if ( !$db->sql_query($sql) )
+        $subquery = ( count($good) > 0 ) ? 'pg_member_id=' . implode(' OR pg_member_id=', $good) : "'foo'='bar'";
+        if ( $subquery == "'foo'='bar'" )
+        {
+          echo '<div class="warning-box">No pages were selected for deletion, and thus none were deleted.</div>';
+        }
+        else
         {
-          $db->_die();
+          $sql = 'DELETE FROM '.table_prefix."page_group_members WHERE ( $subquery ) AND pg_id=$edit_id;";
+          if ( !$db->sql_query($sql) )
+          {
+            $db->_die();
+          }
+          echo '<div class="info-box">The requested page group members have been deleted.</div>';
         }
-        echo '<div class="info-box">The requested page group members have been deleted.</div>';
       }
       
       // Fetch information about page group
@@ -529,6 +613,7 @@
       
       echo '<form name="pg_edit_frm" action="'.makeUrl($paths->nslist['Special'].'Administration', 'module='.$paths->cpage['module']).'" method="post" onsubmit="if(!submitAuthorized) return false;" enctype="multipart/form-data">';
       echo '<input type="hidden" name="action[edit]" value="' . $edit_id . '" />';
+      echo '<input type="hidden" name="pg_type" value="' . $row['pg_type'] . '" />';
       echo '<div class="tblholder">
               <table border="0" cellspacing="1" cellpadding="4">
                 <tr>
@@ -553,10 +638,20 @@
       switch ( $row['pg_type'] )
       {
         case PAGE_GRP_NORMAL:
+          
           // You have guessed correct.
           // *Sits in chair for 10 minutes listening to the radio in an effort to put off writing the code you see below*
           
           echo '<tr><th colspan="3" class="subhead"><input type="submit" name="action[edit_save]" value="Save group name" /></th></tr>';
+          echo '</table></div>';
+          echo '</form>';
+          echo '<form name="pg_static_rm_frm" action="'.makeUrl($paths->nslist['Special'].'Administration', 'module='.$paths->cpage['module']).'" method="post" enctype="multipart/form-data">';
+          echo '<input type="hidden" name="action[edit]" value="' . $edit_id . '" />';
+          echo '<div class="tblholder">
+                  <table border="0" cellspacing="1" cellpadding="4">
+                    <tr>
+                      <th colspan="3">Remove pages from this group</th>
+                    </tr>';
           
           $q = $db->sql_query('SELECT m.pg_member_id,m.page_id,m.namespace FROM '.table_prefix.'page_group_members AS m
                                  LEFT JOIN '.table_prefix.'pages AS p
@@ -689,6 +784,50 @@
           
           break;
         case PAGE_GRP_TAGGED:
+          echo '<tr>
+                  <td class="row2">
+                    Include pages with this tag:
+                  </td>
+                  <td class="row1">
+                    <input type="text" name="pg_target" value="' . htmlspecialchars($row['pg_target']) . '" size="30" />
+                  </td>
+                </tr>';
+          break;
+        case PAGE_GRP_CATLINK:
+          
+          // Build category list
+          $q = $db->sql_query('SELECT name,urlname FROM '.table_prefix.'pages WHERE namespace=\'Category\';');
+          if ( !$q )
+            $db->_die();
+          
+          if ( $db->numrows() < 1 )
+          {
+            $catlist = 'There aren\'t any categories on this site.';
+          }
+          else
+          {
+            $catlist = '<select name="pg_target">';
+            while ( $catrow = $db->fetchrow() )
+            {
+              $selected = ( $catrow['urlname'] == $row['pg_target'] ) ? ' selected="selected"' : '';
+              $catlist .= '<option value="' . htmlspecialchars($catrow['urlname']) . '"' . $selected . '>' . htmlspecialchars($catrow['name']) . '</option>';
+            }
+            $catlist .= '</select>';
+          }
+          
+          echo '<tr>
+                  <td class="row2">
+                    Include pages that are in this category:<br />
+                    <small><b>Reminder:</b> Enano does not automatically place any access controls on the category. If you
+                           don\'t want users to be able to freely add and remove pages from the category (assuming Wiki Mode is enabled
+                           for the category) then you need to enable protection on the category using the button on the more options menu.
+                           </small>
+                  </td>
+                  <td class="row1">
+                    ' . $catlist . '
+                  </td>
+                </tr>';
+          
           break;
       }
       
--- a/themes/admin/header.tpl	Wed Aug 01 13:39:27 2007 -0400
+++ b/themes/admin/header.tpl	Sun Aug 05 15:58:50 2007 -0400
@@ -1,5 +1,5 @@
 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
-<html>
+<html xmlns="http://www.w3.org/1999/xhtml">
   <head>
     <title>{PAGE_NAME} &bull; {SITE_NAME}</title>
     <meta http-equiv="Content-type" content="text/html; charset=utf-8" />
@@ -36,7 +36,7 @@
         <td class="left"></td>
         <td class="main">
           <div style="float: right;">
-            <image alt=" " src="{SCRIPTPATH}/images/spacer.gif" id="ajaxloadicon" />
+            <img alt=" " src="{SCRIPTPATH}/images/spacer.gif" id="ajaxloadicon" />
           </div>
           <h2 class="pagename">{PAGE_NAME}</h2>
           <div id="ajaxEditContainer">
--- a/themes/admin/js/menu.js	Wed Aug 01 13:39:27 2007 -0400
+++ b/themes/admin/js/menu.js	Sun Aug 05 15:58:50 2007 -0400
@@ -42,6 +42,7 @@
   var exheight = height - magic;
   expander.style.height = exheight + 'px';
   expander.style.top = magic + 'px';
+  expander_set_pos();
 }
 
 function expander_onload()
@@ -59,7 +60,26 @@
   }
 }
 
+function expander_set_pos()
+{
+  var winheight = getHeight();
+  var magic = $('header').Height() + $('pagebar_main').Height();
+  var top = getScrollOffset();
+  if ( typeof(top) != 'number' )
+  {
+    return null;
+  }
+  magic = magic - top;
+  if ( magic < 0 )
+    magic = 0;
+  var bartop = magic + top;
+  var barheight = winheight - magic;
+  var expander = document.getElementById('sidebar-hide');
+  expander.style.top = bartop + 'px';
+  expander.style.height = barheight + 'px';
+}
+
 addOnloadHook(expander_set_height);
 addOnloadHook(expander_onload);
 window.onresize = expander_set_height;
-
+window.onscroll = expander_set_pos;
--- a/themes/admin/simple-header.tpl	Wed Aug 01 13:39:27 2007 -0400
+++ b/themes/admin/simple-header.tpl	Sun Aug 05 15:58:50 2007 -0400
@@ -36,7 +36,7 @@
         <td class="left"></td>
         <td class="main">
           <div style="float: right;">
-            <image alt=" " src="{SCRIPTPATH}/images/spacer.gif" id="ajaxloadicon" />
+            <img alt=" " src="{SCRIPTPATH}/images/spacer.gif" id="ajaxloadicon" />
           </div>
           <h2 class="pagename">{PAGE_NAME}</h2>
           <div id="ajaxEditContainer">
--- a/themes/oxygen/header.tpl	Wed Aug 01 13:39:27 2007 -0400
+++ b/themes/oxygen/header.tpl	Sun Aug 05 15:58:50 2007 -0400
@@ -1,5 +1,5 @@
 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
-<html>
+<html xmlns="http://www.w3.org/1999/xhtml">
   <head>
     <title>{PAGE_NAME} &bull; {SITE_NAME}</title>
     <meta http-equiv="Content-type" content="text/html; charset=utf-8" />
@@ -73,7 +73,7 @@
         elem = document.getElementById('h2PageName');
         if(!elem) return;
         elem.style.display = 'none';
-        name = elem.innerHTML;
+        name = elem.firstChild.nodeValue;
         textbox = document.createElement('input');
         textbox.type = 'text';
         textbox.value = name;
@@ -90,7 +90,8 @@
         if(!elem1 || !elem2) return;
         value = elem2.value;
         elem2.parentNode.removeChild(elem2); // just destroy the thing
-        elem1.innerHTML = value;
+        elem1.removeChild(elem1.firstChild);
+        elem1.appendChild(document.createTextNode(value));
         elem1.style.display = 'block';
         if(!value || value=='') return;
         ajaxPost(stdAjaxPrefix+'&_mode=rename', 'newtitle='+escape(value), function() {
--- a/themes/printable/header.tpl	Wed Aug 01 13:39:27 2007 -0400
+++ b/themes/printable/header.tpl	Sun Aug 05 15:58:50 2007 -0400
@@ -1,5 +1,5 @@
 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
-<html>
+<html xmlns="http://www.w3.org/1999/xhtml">
   <head>
     <title>{PAGE_NAME} &bull; {SITE_NAME}</title>
     <meta http-equiv="Content-type" content="text/html; charset=utf-8" />
--- a/themes/stpatty/header.tpl	Wed Aug 01 13:39:27 2007 -0400
+++ b/themes/stpatty/header.tpl	Sun Aug 05 15:58:50 2007 -0400
@@ -1,5 +1,5 @@
 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
-<html>
+<html xmlns="http://www.w3.org/1999/xhtml">
   <head>
     <title>{PAGE_NAME} &bull; {SITE_NAME}</title>
     <meta http-equiv="Content-type" content="text/html; charset=utf-8" />
@@ -19,7 +19,7 @@
         elem = document.getElementById('pagetitle');
         if(!elem) return;
         elem.style.display = 'none';
-        name = elem.innerHTML;
+        name = elem.firstChild.nodeValue;
         textbox = document.createElement('input');
         textbox.type = 'text';
         textbox.value = name;
@@ -36,7 +36,8 @@
         if(!elem1 || !elem2) return;
         value = elem2.value;
         elem2.parentNode.removeChild(elem2); // just destroy the thing
-        elem1.innerHTML = value;
+        elem1.removeChild(elem1.firstChild);
+        elem1.appendChild(document.createTextNode(value));
         elem1.style.display = 'block';
         if(!value || value=='') return;
         ajaxPost(stdAjaxPrefix+'&_mode=rename', 'newtitle='+escape(value), function() {