Localization is FINISHED, DAMN IT HELLAH YEAH! OVER WITH! Man, it feels to get that off my chest. Release is in under 48 hours, folks. And we're ready for it.
authorDan
Tue, 29 Jan 2008 23:15:44 -0500
changeset 391 85f91037cd4f
parent 390 9bcc185dc151
child 392 3395ecddd831
Localization is FINISHED, DAMN IT HELLAH YEAH! OVER WITH! Man, it feels to get that off my chest. Release is in under 48 hours, folks. And we're ready for it.
includes/clientside/static/l10n.js
includes/functions.php
includes/lang.php
includes/pageprocess.php
includes/pageutils.php
includes/paths.php
includes/payload.php
includes/render.php
includes/search.php
includes/sessions.php
includes/tagcloud.php
includes/template.php
index.php
install/includes/stages/confirm.php
install/includes/stages/database_post.php
install/includes/stages/finish.php
install/includes/stages/install.php
install/includes/stages/login.php
install/includes/stages/website.php
install/install.php
language/english/admin.json
language/english/core.json
language/english/install.json
language/english/tools.json
language/english/user.json
plugins/PrivateMessages.php
plugins/SpecialAdmin.php
themes/oxygen/footer.tpl
--- a/includes/clientside/static/l10n.js	Tue Jan 29 17:29:08 2008 -0500
+++ b/includes/clientside/static/l10n.js	Tue Jan 29 23:15:44 2008 -0500
@@ -9,9 +9,18 @@
   if ( typeof(enano_lang[lang_id]) != 'object' )
     return false;
   this.strings = enano_lang[lang_id];
+  this.lang_id = lang_id;
   
   this.get = function(string_id, subst)
   {
+    if ( window.console )
+    {
+      try {
+        window.console.log('$lang(' + this.lang_id + '): requested string: ' + string_id);
+      }
+      catch(e)
+      {}
+    }
     var catname = string_id.substr(0, string_id.indexOf('_'));
     var string_name = string_id.substr(string_id.indexOf('_') + 1);
     if ( typeof(this.strings[catname]) != 'object' )
--- a/includes/functions.php	Tue Jan 29 17:29:08 2008 -0500
+++ b/includes/functions.php	Tue Jan 29 23:15:44 2008 -0500
@@ -321,7 +321,11 @@
   if ( $timeout == 0 && empty($_POST) )
   {
     header('Location: ' . $url);
+    header('Content-length: 0');
     header('HTTP/1.1 307 Temporary Redirect');
+    
+    // with 3xx codes HTTP clients expect a response of 0 bytes, so just die here
+    exit();
   }
   
   if ( !is_object($template) )
@@ -330,7 +334,7 @@
     $template->load_theme('oxygen', 'bleu', false);
     $template->tpl_strings['SITE_NAME'] = 'Enano';
     $template->tpl_strings['SITE_DESC'] = 'This site is experiencing a critical error and cannot load.';
-    $template->tpl_strings['COPYRIGHT'] = 'Powered by Enano CMS - &copy; 2007 Dan Fuhry. This program is Free Software; see the <a href="' . scriptPath . '/install.php?mode=license">GPL file</a> included with this package for details.';
+    $template->tpl_strings['COPYRIGHT'] = 'Powered by Enano CMS - &copy; 2006-2008 Dan Fuhry. This program is Free Software; see the <a href="' . scriptPath . '/install.php?mode=license">GPL file</a> included with this package for details.';
     $template->tpl_strings['PAGE_NAME'] = htmlspecialchars($title);
   }
 
@@ -435,10 +439,6 @@
     // echo 'Keyname: '.$keylist[$idx] . '<br />'; flush(); ob_flush(); // Debugger
     if($idx < 0) return $arr;
     if($keylist[$idx] == '' || $keylist[$idx] < 0 || !$keylist[$idx]) {
-      /* echo 'Infinite loop caught in arrayItemTop(<br /><pre>';
-      print_r($arr);
-      echo '</pre><br />, '.$keyname.');<br /><br />EnanoCMS: Critical error during function call, exiting to prevent excessive server load.';
-      exit; */
       return $arr;
     }
     $arr = arrayItemUp($arr, $keylist[$idx]);
@@ -613,7 +613,7 @@
     {
       $db->_die();
     }
-    echo '<h3>Subcategories</h3>';
+    echo '<h3>' . $lang->get('onpage_cat_heading_subcategories') . '</h3>';
     echo '<div class="tblholder">';
     echo '<table border="0" cellspacing="1" cellpadding="4">';
     echo '<tr>';
@@ -636,10 +636,10 @@
         }
         else
         {
-          echo '<td class="' . $class . '">No subcategories.</td>';
+          echo '<td class="' . $class . '">' . $lang->get('onpage_cat_msg_no_subcategories') . '</td>';
         }
         echo '</tr></table></div>' . "\n\n";
-        echo '<h3>Pages</h3>';
+        echo '<h3>' . $lang->get('onpage_cat_heading_pages') . '</h3>';
         echo '<div class="tblholder">';
         echo '<table border="0" cellspacing="1" cellpadding="4">';
         echo '<tr>';
@@ -684,10 +684,10 @@
       }
       else
       {
-        echo '<td class="' . $class . '">No subcategories.</td>';
+        echo '<td class="' . $class . '">' . $lang->get('onpage_cat_msg_no_subcategories') . '</td>';
       }
       echo '</tr></table></div>' . "\n\n";
-      echo '<h3>Pages</h3>';
+      echo '<h3>' . $lang->get('onpage_cat_heading_pages') . '</h3>';
       echo '<div class="tblholder">';
       echo '<table border="0" cellspacing="1" cellpadding="4">';
       echo '<tr>';
@@ -706,7 +706,7 @@
     }
     else
     {
-      echo '<td class="' . $class . '">No pages in this category.</td>';
+      echo '<td class="' . $class . '">' . $lang->get('onpage_cat_msg_no_pages') . '</td>';
     }
     echo '</tr></table></div>' . "\n\n";
   }
@@ -770,65 +770,111 @@
 function show_file_info()
 {
   global $db, $session, $paths, $template, $plugins; // Common objects
-  if($paths->namespace != 'File') return null; // Prevent unnecessary work
-  $selfn = $paths->page_id; // substr($paths->page, strlen($paths->nslist['File']), strlen($paths->cpage));
-  if(substr($paths->cpage['name'], 0, strlen($paths->nslist['File']))==$paths->nslist['File']) $selfn = substr($paths->page_id, strlen($paths->nslist['File']), strlen($paths->page_id));
+  global $lang;
+  
+  // Prevent unnecessary work
+  if ( $paths->namespace != 'File' )
+    return null;
+  
+  $selfn = $paths->page_id;
+  if ( substr($paths->cpage['name'], 0, strlen($paths->nslist['File'])) == $paths->nslist['File'])
+  {
+    $selfn = substr($paths->page_id, strlen($paths->nslist['File']), strlen($paths->page_id));
+  }
   $q = $db->sql_query('SELECT mimetype,time_id,size FROM '.table_prefix.'files WHERE page_id=\''.$selfn.'\' ORDER BY time_id DESC;');
-  if(!$q) $db->_die('The file type could not be fetched.');
-  if($db->numrows() < 1) { echo '<div class="mdg-comment" style="margin-left: 0;"><h3>Uploaded file</h3><p>There are no files uploaded with this name yet. <a href="'.makeUrlNS('Special', 'UploadFile/'.$paths->page_id).'">Upload a file...</a></p></div><br />'; return; }
+  if ( !$q )
+  {
+    $db->_die('The file type could not be fetched.');
+  }
+  
+  if ( $db->numrows() < 1 )
+  {
+    echo '<div class="mdg-comment" style="margin-left: 0;">
+            <h3>' . $lang->get('onpage_filebox_heading') . '</h3>
+            <p>' . $lang->get('onpage_filebox_msg_not_found', array('upload_link' => makeUrlNS('Special', 'UploadFile/'.$paths->page_id))) . '</p>
+          </div>
+          <br />';
+    return;
+  }
   $r = $db->fetchrow();
   $mimetype = $r['mimetype'];
   $datestring = enano_date('F d, Y h:i a', (int)$r['time_id']);
-  echo '<div class="mdg-comment" style="margin-left: 0;"><p><h3>Uploaded file</h3></p><p>Type: '.$r['mimetype'].'<br />Size: ';
-  $fs = $r['size'];
-  echo $fs.' bytes';
-  $fs = (int)$fs;
-  if($fs >= 1048576)
+  echo '<div class="mdg-comment" style="margin-left: 0;">
+          <h3>' . $lang->get('onpage_filebox_heading') . '</h3>
+          <p>' . $lang->get('onpage_filebox_lbl_type') . ' '.$r['mimetype'].'<br />';
+  
+  $size = $r['size'] . ' ' . $lang->get('etc_unit_bytes');
+  if ( $r['size'] >= 1048576 )
+  {
+    $size .= ' (' . ( round($r['size'] / 1048576, 1) ) . ' ' . $lang->get('etc_unit_megabytes_short') . ')';
+  }
+  else if ( $r['size'] >= 1024 )
   {
-    $fs = round($fs / 1048576, 1);
-    echo ' ('.$fs.' MB)';
-  } elseif($fs >= 1024) {
-    $fs = round($fs / 1024, 1);
-    echo ' ('.$fs.' KB)';
+    $size .= ' (' . ( round($r['size'] / 1024, 1) ) . ' ' . $lang->get('etc_unit_kilobytes_short') . ')';
+  }
+          
+  echo $lang->get('onpage_filebox_lbl_size', array('size' => $size));
+  
+  echo '<br />' . $lang->get('onpage_filebox_lbl_uploaded') . ' ' . $datestring . '</p>';
+  if ( substr($mimetype, 0, 6) != 'image/' && ( substr($mimetype, 0, 5) != 'text/' || $mimetype == 'text/html' || $mimetype == 'text/javascript' ) )
+  {
+    echo '<div class="warning-box">
+            ' . $lang->get('onpage_filebox_msg_virus_warning') . '
+          </div>';
   }
-  echo '<br />Uploaded: '.$datestring.'</p>';
-  if(substr($mimetype, 0, 6)!='image/' && ( substr($mimetype, 0, 5) != 'text/' || $mimetype == 'text/html' || $mimetype == 'text/javascript' ))
+  if ( substr($mimetype, 0, 6) == 'image/' )
   {
-    echo '<div class="warning-box">This file type may contain viruses or other code that could harm your computer. You should exercise caution if you download it.</div>';
+    echo '<p>
+            <a href="'.makeUrlNS('Special', 'DownloadFile'.'/'.$selfn).'">
+              <img style="border: 0;" alt="'.$paths->page.'" src="'.makeUrlNS('Special', 'DownloadFile'.'/'.$selfn.htmlspecialchars(urlSeparator).'preview').'" />
+            </a>
+          </p>';
   }
-  if(substr($mimetype, 0, 6)=='image/')
-  {
-    echo '<p><a href="'.makeUrlNS('Special', 'DownloadFile'.'/'.$selfn).'"><img style="border: 0;" alt="'.$paths->page.'" src="'.makeUrlNS('Special', 'DownloadFile'.'/'.$selfn.htmlspecialchars(urlSeparator).'preview').'" /></a></p>';
-  }
-  echo '<p><a href="'.makeUrlNS('Special', 'DownloadFile'.'/'.$selfn.'/'.$r['time_id'].htmlspecialchars(urlSeparator).'download').'">Download this file</a>';
+  echo '<p>
+          <a href="'.makeUrlNS('Special', 'DownloadFile'.'/'.$selfn.'/'.$r['time_id'].htmlspecialchars(urlSeparator).'download').'">
+            ' . $lang->get('onpage_filebox_btn_download') . '
+          </a>';
   if(!$paths->page_protected && ( $paths->wiki_mode || $session->get_permissions('upload_new_version') ))
   {
-    echo '  |  <a href="'.makeUrlNS('Special', 'UploadFile'.'/'.$selfn).'">Upload new version</a>';
+    echo '  |  <a href="'.makeUrlNS('Special', 'UploadFile'.'/'.$selfn).'">
+            ' . $lang->get('onpage_filebox_btn_upload_new') . '
+          </a>';
   }
   echo '</p>';
-  if($db->numrows() > 1)
+  if ( $db->numrows() > 1 )
   {
-    echo '<h3>File history</h3><p>';
-    while($r = $db->fetchrow())
+    echo '<h3>' . $lang->get('onpage_filebox_heading_history') . '</h3><p>';
+    while ( $r = $db->fetchrow() )
     {
-      echo '(<a href="'.makeUrlNS('Special', 'DownloadFile'.'/'.$selfn.'/'.$r['time_id'].htmlspecialchars(urlSeparator).'download').'">this ver</a>) ';
-      if($session->get_permissions('history_rollback'))
-        echo ' (<a href="#" onclick="ajaxRollback(\''.$r['time_id'].'\'); return false;">revert</a>) ';
+      echo '(<a href="'.makeUrlNS('Special', 'DownloadFile'.'/'.$selfn.'/'.$r['time_id'].htmlspecialchars(urlSeparator).'download').'">' . $lang->get('onpage_filebox_btn_this_version') . '</a>) ';
+      if ( $session->get_permissions('history_rollback') )
+        echo ' (<a href="#" onclick="ajaxRollback(\''.$r['time_id'].'\'); return false;">' . $lang->get('onpage_filebox_btn_revert') . '</a>) ';
       $mimetype = $r['mimetype'];
       $datestring = enano_date('F d, Y h:i a', (int)$r['time_id']);
+      
       echo $datestring.': '.$r['mimetype'].', ';
+      
       $fs = $r['size'];
       $fs = (int)$fs;
+      
       if($fs >= 1048576)
       {
         $fs = round($fs / 1048576, 1);
-        echo ' '.$fs.' MB';
-      } elseif($fs >= 1024) {
+        $size = $fs . ' ' . $lang->get('etc_unit_megabytes_short');
+      }
+      else
+      if ( $fs >= 1024 )
+      {
         $fs = round($fs / 1024, 1);
-        echo ' '.$fs.' KB';
-      } else {
-        echo ' '.$fs.' bytes';
+        $size = $fs . ' ' . $lang->get('etc_unit_kilobytes_short');
       }
+      else
+      {
+        $size = $fs . ' ' . $lang->get('etc_unit_bytes');
+      }
+      
+      echo $size;
+      
       echo '<br />';
     }
     echo '</p>';
@@ -879,37 +925,6 @@
 }
 
 /**
- * Deprecated, do not use.
- */
-
-function password_prompt($id = false)
-{
-  global $db, $session, $paths, $template, $plugins; // Common objects
-  if(!$id) $id = $paths->page;
-  if(isset($paths->pages[$id]['password']) && strlen($paths->pages[$id]['password']) == 40 && !isset($_REQUEST['pagepass']))
-  {
-    die_friendly('Password required', '<p>You must supply a password to access this page.</p><form action="'.makeUrl($paths->pages[$id]['urlname']).'" method="post"><p>Password: <input name="pagepass" type="password" /></p><p><input type="submit" value="Submit" /></p>');
-  } elseif(isset($_REQUEST['pagepass'])) {
-    $p = (preg_match('#^([a-f0-9]*){40}$#', $_REQUEST['pagepass'])) ? $_REQUEST['pagepass'] : sha1($_REQUEST['pagepass']);
-    if($p != $paths->pages[$id]['password']) die_friendly('Password required', '<p style="color: red;">The password you entered is incorrect.</p><form action="'.makeUrl($paths->page).'" method="post"><p>Password: <input name="pagepass" type="password" /></p><p><input type="submit" value="Submit" /></p>');
-  }
-}
-
-/**
- * Some sort of primitive hex converter from back in the day. Deprecated, do not use.
- * @param string Text to encode
- * @return string
- */
-
-function str_hex($string){
-    $hex='';
-    for ($i=0; $i < strlen($string); $i++){
-        $hex .= ' '.dechex(ord($string[$i]));
-    }
-    return substr($hex, 1, strlen($hex));
-}
-
-/**
  * Essentially an return code reader for a socket. Don't use this unless you're writing mail code and smtp_send_email doesn't cut it. Ported from phpBB's smtp.php.
  * @param socket A socket resource
  * @param string The expected response from the server, this needs to be exactly three characters.
@@ -1187,14 +1202,6 @@
 }
 
 /**
- * What kinda sh** was I thinking when I wrote this. Deprecated.
- */
-
-function _dualurlenc($t) {
-  return rawurlencode(rawurlencode($t));
-}
-
-/**
  * Badly named function to send back eval'able Javascript code with an error message. Deprecated, use JSON instead.
  * @param string Message to send
  */
--- a/includes/lang.php	Tue Jan 29 17:29:08 2008 -0500
+++ b/includes/lang.php	Tue Jan 29 23:15:44 2008 -0500
@@ -554,7 +554,7 @@
       $subs[$key] = strval($value);
       $string = str_replace("%{$key}%", "{$subs[$key]}", $string);
     }
-    return "$string*";
+    return $string;
   }
   
 } // class Language
--- a/includes/pageprocess.php	Tue Jan 29 17:29:08 2008 -0500
+++ b/includes/pageprocess.php	Tue Jan 29 23:15:44 2008 -0500
@@ -213,7 +213,8 @@
     {
       if ( !$this->page_exists )
       {
-        die_semicritical('Exception in PageProcessor', '<p>Special page not existent but exception not previously caught by path manager.</p>');
+        $func_name = "page_{$this->namespace}_{$this->page_id}";
+        die_semicritical($lang->get('page_msg_admin_404_title'), $lang->get('page_msg_admin_404_body', array('func_name' => $func_name)));
       }
       $func_name = "page_{$this->namespace}_{$this->page_id}";
       if ( function_exists($func_name) )
--- a/includes/pageutils.php	Tue Jan 29 17:29:08 2008 -0500
+++ b/includes/pageutils.php	Tue Jan 29 23:15:44 2008 -0500
@@ -89,7 +89,7 @@
   }
   
   /**
-   * Basically a frontend to RenderMan::getPage(), with the ability to send valid data for nonexistent pages
+   * DEPRECATED. Previously returned the full rendered contents of a page.
    * @param $page the full page id (Namespace:Pagename)
    * @param $send_headers true if the theme headers should be sent (still dependent on current page settings), false otherwise
    * @return string
@@ -98,226 +98,6 @@
   public static function getpage($page, $send_headers = false, $hist_id = false)
   {
     die('PageUtils->getpage is deprecated.');
-    global $db, $session, $paths, $template, $plugins; // Common objects
-    ob_start();
-    $pid = RenderMan::strToPageID($page);
-    //die('<pre>'.print_r($pid, true).'</pre>');
-    if(isset($paths->pages[$page]['password']) && strlen($paths->pages[$page]['password']) == 40)
-    {
-      password_prompt($page);
-    }
-    if(isset($paths->pages[$page]))
-    {
-      doStats($pid[0], $pid[1]);
-    }
-    if($paths->custom_page || $pid[1] == 'Special')
-    {
-      // If we don't have access to the page, get out and quick!
-      if(!$session->get_permissions('read') && $pid[0] != 'Login' && $pid[0] != 'Register')
-      {
-        $template->tpl_strings['PAGE_NAME'] = 'Access denied';
-        
-        if ( $send_headers )
-        {
-          $template->header();
-        }
-        
-        echo '<div class="error-box"><b>Access to this page is denied.</b><br />This may be because you are not logged in or you have not met certain criteria for viewing this page.</div>';
-        
-        if ( $send_headers )
-        {
-          $template->footer();
-        }
-        
-        $r = ob_get_contents();
-        ob_end_clean();
-        return $r;
-      }
-      
-      $fname = 'page_' . $pid[1] . '_' . $paths->pages[$page]['urlname_nons'];
-      @call_user_func($fname);
-      
-    }
-    else if ( $pid[1] == 'Admin' )
-    {
-      // If we don't have access to the page, get out and quick!
-      if(!$session->get_permissions('read'))
-      {
-        $template->tpl_strings['PAGE_NAME'] = 'Access denied';
-        if ( $send_headers )
-        {
-          $template->header();
-        }
-        echo '<div class="error-box"><b>Access to this page is denied.</b><br />This may be because you are not logged in or you have not met certain criteria for viewing this page.</div>';
-        if ( $send_headers )
-        {
-          $template->footer();
-        }
-        $r = ob_get_contents();
-        ob_end_clean();
-        return $r;
-      }
-      
-      $fname = 'page_' . $pid[1] . '_' . $pid[0];
-      if ( !function_exists($fname) )
-      {
-        $title = 'Page backend not found';
-        $message = "The administration page you are looking for was properly registered using the page API, but the backend function
-                    (<tt>$fname</tt>) was not found. If this is a plugin page, then this is almost certainly a bug with the plugin.";
-        if ( $send_headers )
-        {
-          die_friendly($title, "<p>$message</p>");
-        }
-        else
-        {
-          echo "<h2>$title</h2>\n<p>$message</p>";
-        }
-      }
-      @call_user_func($fname);
-    }
-    else if ( !isset( $paths->pages[$page] ) )
-    {
-      ob_start();
-      $code = $plugins->setHook('page_not_found');
-      foreach ( $code as $cmd )
-      {
-        eval($cmd);
-      }
-      $text = ob_get_contents();
-      if ( $text != '' )
-      {
-        ob_end_clean();
-        return $text;
-      }
-      $template->header();
-      if($m = $paths->sysmsg('Page_not_found'))
-      {
-        eval('?>'.RenderMan::render($m));
-      }
-      else
-      {
-        header('HTTP/1.1 404 Not Found');
-        echo '<h3>There is no page with this title yet.</h3>
-               <p>You have requested a page that doesn\'t exist yet.';
-        if($session->get_permissions('create_page')) echo ' You can <a href="'.makeUrl($paths->page, 'do=edit', true).'" onclick="ajaxEditor(); return false;">create this page</a>, or return to the <a href="'.makeUrl(getConfig('main_page')).'">homepage</a>.';
-        else echo ' Return to the <a href="'.makeUrl(getConfig('main_page')).'">homepage</a>.</p>';
-        if ( $session->get_permissions('history_rollback') )
-        {
-          $e = $db->sql_query('SELECT * FROM ' . table_prefix.'logs WHERE action=\'delete\' AND page_id=\'' . $paths->page_id . '\' AND namespace=\'' . $pid[1] . '\' ORDER BY time_id DESC;');
-          if ( !$e )
-          {
-            $db->_die('The deletion log could not be selected.');
-          }
-          if ($db->numrows() > 0 )
-          {
-            $r = $db->fetchrow();
-            echo '<p>This page also appears to have some log entries in the database - it seems that it was deleted on ' . enano_date('d M Y h:i a', intval($r['time_id'])) . '. You can probably <a href="'.makeUrl($paths->page, 'do=rollback&amp;id=' . $r['time_id']) . '" onclick="ajaxRollback(\'' . $r['time_id'] . '\'); return false;">roll back</a> the deletion.</p>';
-          }
-          $db->free_result();
-        }
-        echo '<p>
-                HTTP Error: 404 Not Found
-              </p>';
-      }
-      $template->footer();
-    }
-    else
-    {
-      
-      // If we don't have access to the page, get out and quick!
-      if(!$session->get_permissions('read'))
-      {
-        $template->tpl_strings['PAGE_NAME'] = 'Access denied';
-        if($send_headers) $template->header();
-        echo '<div class="error-box"><b>Access to this page is denied.</b><br />This may be because you are not logged in or you have not met certain criteria for viewing this page.</div>';
-        if($send_headers) $template->footer();
-        $r = ob_get_contents();
-        ob_end_clean();
-        return $r;
-      }
-      
-      ob_start();
-      $code = $plugins->setHook('page_custom_handler');
-      foreach ( $code as $cmd )
-      {
-        eval($cmd);
-      }
-      $text = ob_get_contents();
-      if ( $text != '' )
-      {
-        ob_end_clean();
-        return $text;
-      }
-      
-      if ( $hist_id )
-      {
-        $e = $db->sql_query('SELECT page_text,date_string,char_tag FROM ' . table_prefix.'logs WHERE page_id=\'' . $paths->pages[$page]['urlname_nons'] . '\' AND namespace=\'' . $pid[1] . '\' AND log_type=\'page\' AND action=\'edit\' AND time_id=' . $db->escape($hist_id) . '');
-        if($db->numrows() < 1)
-        {
-          $db->_die('There were no rows in the text table that matched the page text query.');
-        }
-        $r = $db->fetchrow();
-        $db->free_result();
-        $message = '<div class="info-box" style="margin-left: 0; margin-top: 5px;"><b>Notice:</b><br />The page you are viewing was archived on ' . enano_date('d M Y h:i a', intval($r['time_id'])) . '.<br /><a href="'.makeUrl($page).'" onclick="ajaxReset(); return false;">View current version</a>  |  <a href="'.makeUrl($page, 'do=rollback&amp;id=' . $hist_id) . '" onclick="ajaxRollback(\'' . $hist_id . '\')">Restore this version</a></div><br />'.RenderMan::render($r['page_text']);
-        
-        if( !$paths->pages[$page]['special'] )
-        {
-          if($send_headers)
-          {
-            $template->header(); 
-          }
-          display_page_headers();
-        }
-        
-        eval('?>' . $message);
-        
-        if( !$paths->pages[$page]['special'] )
-        {
-          display_page_footers();
-          if($send_headers)
-          {
-            $template->footer();
-          }
-        }
-        
-      } else {
-        if(!$paths->pages[$page]['special'])
-        {
-          $message = RenderMan::getPage($paths->pages[$page]['urlname_nons'], $pid[1]);
-        }
-        else
-        {
-          $message = RenderMan::getPage($paths->pages[$page]['urlname_nons'], $pid[1], 0, false, false, false, false);
-        }
-        // This line is used to debug wikiformatted code
-        // die('<pre>'.htmlspecialchars($message).'</pre>');
-        
-        if( !$paths->pages[$page]['special'] )
-        {
-          if($send_headers)
-          {
-            $template->header(); 
-          }
-          display_page_headers();
-        }
-
-        // This is it, this is what all of Enano has been working up to...
-        
-        eval('?>' . $message);
-        
-        if( !$paths->pages[$page]['special'] )
-        {
-          display_page_footers();
-          if($send_headers)
-          {
-            $template->footer();
-          }
-        }
-      }
-    }
-    $ret = ob_get_contents();
-    ob_end_clean();
-    return $ret;
   }
   
   /**
--- a/includes/paths.php	Tue Jan 29 17:29:08 2008 -0500
+++ b/includes/paths.php	Tue Jan 29 23:15:44 2008 -0500
@@ -133,6 +133,7 @@
   function init()
   {
     global $db, $session, $paths, $template, $plugins; // Common objects
+    global $lang;
     
     $code = $plugins->setHook('paths_init_before');
     foreach ( $code as $cmd )
@@ -359,8 +360,7 @@
         {
           $main_page = makeUrl($this->pages[0]['urlname']);
         }
-        $sp_link = '<a href="' . makeUrlNS('Special', 'SpecialPages') . '">here</a>';
-        redirect($main_page, 'Can\'t load special page', 'The special page you requested could not be found. This may be due to a plugin failing to load. A list of all special pages on this website can be viewed '.$sp_link.'. You will be redirected to the main page in 15 seconds.', 14);
+        redirect($main_page, $lang->get('page_msg_special_404_title'), $lang->get('page_msg_special_404_body', array('sp_link' => makeUrlNS('Special', 'SpecialPages'))), 15);
         exit;
       }
       // Allow the user to create/modify his user page uncondtionally (admins can still protect the page)
--- a/includes/payload.php	Tue Jan 29 17:29:08 2008 -0500
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,312 +0,0 @@
-<?php
-
-/*
- * Enano - an open-source CMS capable of wiki functions, Drupal-like sidebar blocks, and everything in between
- * Version 1.1.1
- * Copyright (C) 2006-2007 Dan Fuhry
- * Installation package
- * payload.php - Installer payload (the installation logic)
- *
- * This program is Free Software; you can redistribute and/or modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied
- * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for details.
- */
-
-if ( !defined('IN_ENANO_INSTALL') )
-  die();
-
-return true;
-
-function stg_sim_good()
-{
-  return true;
-}
-
-function stg_sim_bad()
-{
-  return true;
-}
-
-function stg_password_decode()
-{
-  global $db;
-  static $pass = false;
-  
-  if ( $pass )
-    return $pass;
-  
-  if ( !isset($_POST['crypt_data']) && !empty($_POST['password']) && $_POST['password'] === $_POST['password_confirm'] )
-    $pass = $_POST['password'];
-  
-  $aes = AESCrypt::singleton(AES_BITS, AES_BLOCKSIZE);
-  // retrieve encryption key
-  $q = $db->sql_query('SELECT config_value FROM ' . table_prefix . 'config WHERE config_name=\'install_aes_key\';');
-  if ( !$q )
-    $db->_die();
-  if ( $db->numrows() < 1 )
-    return false;
-  list($aes_key) = $db->fetchrow_num();
-  $aes_key = $aes->hextostring($aes_key);
-  
-  $pass = $aes->decrypt($_POST['crypt_data'], $aes_key, ENC_HEX);
-  if ( !$pass )
-    return false;
-  
-  return $pass; // Will be true if the password isn't crapped
-}
-
-function stg_make_private_key()
-{
-  global $db;
-  static $site_key = false;
-  
-  if ( $site_key )
-    return $site_key;
-  
-  // Is there already a key cached in the database?
-  $q = $db->sql_query('SELECT config_value FROM ' . table_prefix . 'config WHERE config_name=\'site_aes_key\';');
-  if ( !$q )
-    $db->_die();
-  
-  if ( $db->numrows() > 0 )
-  {
-    list($site_key) = $db->fetchrow_num();
-    $db->free_result();
-    return $site_key;
-  }
-  
-  $aes = AESCrypt::singleton(AES_BITS, AES_BLOCKSIZE);
-  // This will use /dev/urandom if possible
-  $site_key = $aes->gen_readymade_key();
-  
-  // Stash it in the database, don't check for errors though because we can always regenerate it
-  $db->sql_query('INSERT INTO ' . table_prefix . 'config ( config_name, config_value ) VALUES ( \'site_aes_key\', \'' . $site_key . '\' );');
-  
-  return $site_key;
-}
-
-function stg_load_schema()
-{
-  global $db, $dbdriver, $installer_version;
-  static $sql_parser = false;
-  
-  if ( is_object($sql_parser) )
-    return $sql_parser->parse();
-  
-  $aes = AESCrypt::singleton(AES_BITS, AES_BLOCKSIZE);
-  
-  $site_key = stg_make_private_key();
-  $site_key = $aes->hextostring($site_key);
-  $admin_pass_clean = stg_password_decode();
-  $admin_pass = $aes->encrypt($admin_pass_clean, $site_key, ENC_HEX);
-  
-  unset($admin_pass_clean); // Security
-  
-  try
-  {
-    $sql_parser = new SQL_Parser( ENANO_ROOT . "/install/schemas/{$dbdriver}_stage2.sql" );
-  }
-  catch ( Exception $e )
-  {
-    echo "<pre>$e</pre>";
-    return false;
-  }
-  
-  $vars = array(
-      'TABLE_PREFIX'         => $_POST['table_prefix'],
-      'SITE_NAME'            => $db->escape($_POST['site_name']),
-      'SITE_DESC'            => $db->escape($_POST['site_desc']),
-      'COPYRIGHT'            => $db->escape($_POST['copyright']),
-      // FIXME: update form
-      'WIKI_MODE'            => ( isset($_POST['wiki_mode']) ? '1' : '0' ),
-      'ENABLE_CACHE'         => ( is_writable( ENANO_ROOT . '/cache/' ) ? '1' : '0' ),
-      'VERSION'              => $installer_version['version'],
-      'ADMIN_USER'           => $db->escape($_POST['username']),
-      'ADMIN_PASS'           => $admin_pass,
-      'ADMIN_EMAIL'          => $db->escape($_POST['email']),
-      'REAL_NAME'            => '', // This has always been stubbed.
-      'ADMIN_EMBED_PHP'      => strval(AUTH_DISALLOW),
-      'UNIX_TIME'            => strval(time())
-    );
-  
-  $sql_parser->assign_vars($vars);
-  return $sql_parser->parse();
-}
-
-function stg_deliver_payload()
-{
-  global $db;
-  $schema = stg_load_schema();
-  foreach ( $schema as $sql )
-  {
-    if ( !$db->sql_query($sql) )
-    {
-      echo $db->get_error();
-      return false;
-    }
-  }
-  return true;
-}
-
-function stg_write_config()
-{
-  global $dbhost, $dbuser, $dbpasswd, $dbname, $dbdriver;
-  $db_data = array(
-      'host' => str_replace("'", "\\'", $dbhost),
-      'user' => str_replace("'", "\\'", $dbuser),
-      'pass' => str_replace("'", "\\'", $dbpasswd),
-      'name' => str_replace("'", "\\'", $dbname),
-      'tp' => table_prefix,
-      'drv' => $dbdriver
-    );
-  
-  // Retrieves the existing key
-  $site_key = stg_make_private_key();
-  
-  // Determine contentPath
-  switch ( @$_POST['url_scheme'] )
-  {
-    case 'standard':
-    default:
-      $sp_append = 'index.php?title=';
-      break;
-    case 'shortened':
-      $sp_append = 'index.php/';
-      break;
-    case 'rewrite':
-      $sp_append = '/';
-      break;
-  }
-  
-  $scriptpath = scriptPath;
-  $contentpath = $scriptpath . $sp_append;
-  
-  $config_file = <<<EOF
-<?php
-
-/**
- * Enano site configuration
- * NOTE ON EDITING: You should almost never need to change anything in this
- * file. The only exceptions are when your DB password/other info is changed
- * or if you are moving your Enano installation to another directory.
- */
-
-//
-// DATABASE INFO
-//
-
-// Database type to use, currently mysql and postgresql are supported
-\$dbdriver = '{$db_data['drv']}';
-
-// Hostname of your database server, probably localhost
-\$dbhost = '{$db_data['host']}';
-
-// Username used to connect to the database
-\$dbuser = '{$db_data['user']}';
-// Database password
-\$dbpasswd = '{$db_data['pass']}';
-
-// Name of the database
-\$dbname = '{$db_data['name']}';
-
-//
-// CONSTANTS
-//
-
-// if they're already defined, no use re-defining them
-if ( !defined('ENANO_CONSTANTS') )
-{
-  // The prefix for the tables in the database. Useful for holding more than
-  // one Enano installation in the same database.
-  define('table_prefix', '{$db_data['tp']}');
-  
-  // The path to Enano's files on your server, from the document root. If
-  // Enano is installed in your document root this will be blank; installing
-  // Enano in /enano/ will result in "/enano" here, etc.
-  define('scriptPath', '$scriptpath');
-  
-  // The authoritative prefix for pages. This should be very literal: to
-  // generate a URL on the site, the format is basically
-  // contentPath . \$page_name. This is based off of scriptPath and the URL
-  // scheme selected during installation. Pattern:
-  //
-  //    * Standard URLs:  scriptPath . '/index.php?title='
-  //    * Shortened URLs: scriptPath . '/index.php/'
-  //    * mod_rewrite:    scriptPath . '/'
-  
-  define('contentPath', '$contentpath');
-  
-  // Tell the Enano API that we're installed and that this file is complete
-  define('ENANO_INSTALLED', 'You bet!');
-  
-  define('ENANO_CONSTANTS', '');
-}
-
-// The AES encryption key used to store passwords. We have a very specific
-// reason for doing this; see the rationale at:
-//   http://docs.enanocms.org/Help:Appendix_B
-\$crypto_key = '$site_key';
-
-EOF;
-  
-  // Write config file
-  
-  $ch = @fopen ( ENANO_ROOT . '/config.new.php', 'w' );
-  if ( !$ch )
-    return false;
-  
-  fwrite($ch, $config_file);
-  fclose($ch);
-  
-  // If we are using mod_rewrite, also append any existing .htaccess
-  if ( @$_POST['url_scheme'] === 'rewrite' )
-  {
-    $hh = @fopen ( ENANO_ROOT . '/.htaccess.new', 'w' );
-    if ( !$hh )
-      return false;
-    $hhc = <<<EOF
-#
-# START ENANO RULES
-#
-
-# Enable mod_rewrite
-RewriteEngine on
-
-# Don't rewrite if the user requested a real directory or file
-RewriteCond %{REQUEST_FILENAME} !-f
-RewriteCond %{REQUEST_FILENAME} !-d
-
-# Main rule - short and sweet
-RewriteRule (.*) index.php?title=\$1 [L,QSA]
-
-EOF;
-    fwrite($hh, $hhc);
-    fclose($hh);
-  }
-  
-  return true;
-}
-
-function stg_language_setup()
-{
-  global $languages, $db;
-  global $lang_id;
-  $lang_info =& $languages[$lang_id];
-  if ( !is_array($lang_info) )
-    return false;
-  
-  // Install the language
-  // ($lang_code, $lang_name_neutral, $lang_name_local, $lang_file = false)
-  $result = install_language($lang_id, $lang_info['name_eng'], $lang_info['name'], ENANO_ROOT . "/language/{$lang_info['dir']}/core.json");
-  if ( !$result )
-    return false;
-  
-  $lang_local = new Language($lang_id);
-  $lang_local->import( ENANO_ROOT . "/language/{$lang_info['dir']}/user.json" );
-  $lang_local->import( ENANO_ROOT . "/language/{$lang_info['dir']}/tools.json" );
-  $lang_local->import( ENANO_ROOT . "/language/{$lang_info['dir']}/admin.json" );
-  
-  return true;
-}
--- a/includes/render.php	Tue Jan 29 17:29:08 2008 -0500
+++ b/includes/render.php	Tue Jan 29 23:15:44 2008 -0500
@@ -763,7 +763,8 @@
     $keys = array_keys($smileys);
     foreach($keys as $k)
     {
-      $t = str_hex($k);
+      $t = hexencode($k, ' ', '');
+      $t = trim($t);
       $t = explode(' ', $t);
       $s = '';
       foreach($t as $b)
--- a/includes/search.php	Tue Jan 29 17:29:08 2008 -0500
+++ b/includes/search.php	Tue Jan 29 23:15:44 2008 -0500
@@ -168,7 +168,7 @@
   if ( count($query['any']) < 1 && count($query['req']) < 1 && count($query_phrase['any']) < 1 && count($query_phrase['req']) < 1 )
   {
     // This is both because of technical restrictions and devastation that would occur on shared servers/large sites.
-    $warnings[] = 'You need to have at least one keyword in your search query. Searching only for pages not containing a term is not allowed.';
+    $warnings[] = $lang->get('search_err_query_no_positive');
     return array();
   }
 
--- a/includes/sessions.php	Tue Jan 29 17:29:08 2008 -0500
+++ b/includes/sessions.php	Tue Jan 29 23:15:44 2008 -0500
@@ -398,13 +398,13 @@
           {
             case 'none':
             default:
-              $solution = 'Your account was most likely deactivated by an administrator. Please contact the site administration for further assistance.';
+              $solution = $lang->get('user_login_noact_solution_none');
               break;
             case 'user':
-              $solution = 'Please check your e-mail; you should have been sent a message with instructions on how to activate your account. If you do not receive an e-mail from this site within 24 hours, please contact the site administration for further assistance.';
+              $solution = $lang->get('user_login_noact_solution_user');
               break;
             case 'admin':
-              $solution = 'This website has been configured so that all user accounts must be activated by the administrator before they can be used, so your account will most likely be activated the next time an administrator visits the site.';
+              $solution = $lang->get('user_login_noact_solution_admin');
               break;
           }
           
@@ -425,14 +425,14 @@
             $this->auth_level =    USER_LEVEL_MEMBER;
             $this->user_level =    USER_LEVEL_MEMBER;
             $this->logout();
-            redirect(scriptPath . '/', 'Logged out', 'You have successfully been logged out. All cookies cleared.', 4);
+            redirect(scriptPath . '/', $lang->get('user_login_noact_msg_logout_success_title'), $lang->get('user_login_noact_msg_logout_success_body'), 5);
           }
           
           if ( $can_request && !isset($_POST['activation_request']) )
           {
-            $form = '<p>If you are having trouble or did not receive the e-mail, you can request account activation from the administrators of this site.</p>
+            $form = '<p>' . $lang->get('user_login_noact_msg_ask_admins') . '</p>
                      <form action="' . makeUrlNS('System', 'ActivateStub') . '" method="post">
-                       <p><input type="submit" name="activation_request" value="Request account activation" /> <input type="submit" name="logout" value="Log out" /></p>
+                       <p><input type="submit" name="activation_request" value="' . $lang->get('user_login_noact_btn_request_activation') . '" /> <input type="submit" name="logout" value="' . $lang->get('user_login_noact_btn_log_out') . '" /></p>
                      </form>';
           }
           else
@@ -440,21 +440,21 @@
             if ( $can_request && isset($_POST['activation_request']) )
             {
               $this->admin_activation_request($userdata['username']);
-              $form = '<p>A request has just been sent to the administrators of this site. They will be able to activate your account or send you another activation e-mail if needed.</p>
+              $form = '<p>' . $lang->get('user_login_noact_msg_admins_just_asked') . '</p>
                        <form action="' . makeUrlNS('System', 'ActivateStub') . '" method="post">
-                         <p><input type="submit" name="logout" value="Log out" /></p>
+                         <p><input type="submit" name="logout" value="' . $lang->get('user_login_noact_btn_log_out') . '" /></p>
                        </form>';
             }
             else
             {
-              $form = '<p>There is an active request in the administrators\' control panel for your account to be activated.</p>
+              $form = '<p>' . $lang->get('user_login_noact_msg_admins_asked') . '</p>
                        <form action="' . makeUrlNS('System', 'ActivateStub') . '" method="post">
-                         <p><input type="submit" name="logout" value="Log out" /></p>
+                         <p><input type="submit" name="logout" value="' . $lang->get('user_login_noact_btn_log_out') . '" /></p>
                        </form>';
             }
           }
           
-          die_semicritical('Account error', '<p>It appears that your user account has not yet been activated. '.$solution.'</p>' . $form);
+          die_semicritical($lang->get('user_login_noact_title'), '<p>' . $lang->get('user_login_noact_msg_intro') . ' '.$solution.'</p>' . $form);
         }
         
         $this->sid = $_COOKIE['sid'];
@@ -712,7 +712,7 @@
           eval($cmd);
         }
         
-        redirect($url, 'Login sucessful', 'Please wait while you are transferred to the Password Reset form.');
+        redirect($url, '', '', 0);
         exit;
       }
     }
@@ -1582,6 +1582,8 @@
   function check_banlist()
   {
     global $db, $session, $paths, $template, $plugins; // Common objects
+    global $lang;
+    
     $col_reason = ( $this->compat ) ? '"No reason entered (session manager is in compatibility mode)" AS reason' : 'reason';
     $banned = false;
     if ( $this->user_logged_in )
@@ -1612,7 +1614,7 @@
       $q = $this->sql($sql);
       if ( $db->numrows() > 0 )
       {
-        while ( list($reason, $ban_value, $ban_type, $is_regex) = $db->fetchrow_num() )
+        while ( list($reason_temp, $ban_value, $ban_type, $is_regex) = $db->fetchrow_num() )
         {
           if ( $ban_type == BAN_IP && $row['is_regex'] != 1 )
           {
@@ -1624,6 +1626,7 @@
             }
             if ( preg_match("/$regexp/", $_SERVER['REMOTE_ADDR']) )
             {
+              $reason = $reason_temp;
               $banned = true;
             }
           }
@@ -1631,6 +1634,7 @@
           {
             // User is banned
             $banned = true;
+            $reason = $reason_temp;
           }
         }
       }
@@ -1656,7 +1660,7 @@
       $q = $this->sql($sql);
       if ( $db->numrows() > 0 )
       {
-        while ( list($reason, $ban_value, $ban_type, $is_regex) = $db->fetchrow_num() )
+        while ( list($reason_temp, $ban_value, $ban_type, $is_regex) = $db->fetchrow_num() )
         {
           if ( $ban_type == BAN_IP && $row['is_regex'] != 1 )
           {
@@ -1666,12 +1670,14 @@
               continue;
             if ( preg_match("/$regexp/", $_SERVER['REMOTE_ADDR']) )
             {
+              $reason = $reason_temp;
               $banned = true;
             }
           }
           else
           {
             // User is banned
+            $reason = $reason_temp;
             $banned = true;
           }
         }
@@ -1681,7 +1687,7 @@
     if ( $banned && $paths->get_pageid_from_url() != $paths->nslist['Special'].'CSS' )
     {
       // This guy is banned - kill the session, kill the database connection, bail out, and be pretty about it
-      die_semicritical('Ban notice', '<div class="error-box">You have been banned from this website. Please contact the site administrator for more information.<br /><br />Reason:<br />'.$reason.'</div>');
+      die_semicritical($lang->get('user_ban_msg_title'), '<p>' . $lang->get('user_ban_msg_body') . '</p><div class="error-box"><b>' . $lang->get('user_ban_lbl_reason') . '</b><br />' . $reason . '</div>');
       exit;
     }
   }
@@ -2066,46 +2072,71 @@
   
   function userlevel_to_string($user_level, $short = false)
   {
-    if ( $short )
+    global $lang;
+    
+    static $levels = array(
+        'short' => array(
+            USER_LEVEL_GUEST => 'Guest',
+            USER_LEVEL_MEMBER => 'Member',
+            USER_LEVEL_CHPREF => 'Sensitive preferences changeable',
+            USER_LEVEL_MOD => 'Moderator',
+            USER_LEVEL_ADMIN => 'Administrative'
+          ),
+        'long' => array(
+            USER_LEVEL_GUEST => 'Low - guest privileges',
+            USER_LEVEL_MEMBER => 'Standard - normal member level',
+            USER_LEVEL_CHPREF => 'Medium - user can change his/her own e-mail address and password',
+            USER_LEVEL_MOD => 'High - moderator privileges',
+            USER_LEVEL_ADMIN => 'Highest - administrative privileges'
+          ),
+        'l10n' => false
+      );
+    
+    if ( is_object($lang) && !$levels['l10n'] )
     {
-      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";
-      }
+      $levels = array(
+          'short' => array(
+              USER_LEVEL_GUEST => $lang->get('user_level_short_guest'),
+              USER_LEVEL_MEMBER => $lang->get('user_level_short_member'),
+              USER_LEVEL_CHPREF => $lang->get('user_level_short_chpref'),
+              USER_LEVEL_MOD => $lang->get('user_level_short_mod'),
+              USER_LEVEL_ADMIN => $lang->get('user_level_short_admin')
+            ),
+          'long' => array(
+              USER_LEVEL_GUEST => $lang->get('user_level_long_guest'),
+              USER_LEVEL_MEMBER => $lang->get('user_level_long_member'),
+              USER_LEVEL_CHPREF => $lang->get('user_level_long_chpref'),
+              USER_LEVEL_MOD => $lang->get('user_level_long_mod'),
+              USER_LEVEL_ADMIN => $lang->get('user_level_long_admin')
+            ),
+          'l10n' => true
+        );
+    }
+    
+    $key = ( $short ) ? 'short' : 'long';
+    if ( isset($levels[$key][$user_level]) )
+    {
+      return $levels[$key][$user_level];
     }
     else
     {
-      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)";
+        return ( is_object($lang) ) ? $lang->get('user_level_short_unknown', array('user_level' => $user_level)) : "Unknown - $user_level";
+      }
+      else
+      {
+        return ( is_object($lang) ) ? $lang->get('user_level_long_unknown', array('user_level' => $user_level)) : "Unknown level ($user_level)";
       }
     }
+    
+    return 'Linux rocks!';
+    
   }
   
   /**
    * Updates a user's information in the database. Note that any of the values except $user_id can be false if you want to preserve the old values.
+   * Not localized because this really isn't used a whole lot anymore.
    * @param int $user_id The user ID of the user to update - this cannot be changed
    * @param string $username The new username
    * @param string $old_pass The current password - only required if sessionManager::$user_level < USER_LEVEL_ADMIN. This should usually be an UNENCRYPTED string. This can also be an array - if it is, key 0 is treated as data AES-encrypted with key 1
--- a/includes/tagcloud.php	Tue Jan 29 17:29:08 2008 -0500
+++ b/includes/tagcloud.php	Tue Jan 29 23:15:44 2008 -0500
@@ -132,6 +132,7 @@
    
   function make_html($span_class = 'normal', $div_align = 'center')
   {
+    global $lang;
     $html = array();
     $max  = max($this->words);
     $size = $this->get_cloud_size();
@@ -147,8 +148,8 @@
         $newline = ( $inc == 5 ) ? "<br />" : '';
         ( $inc == 5 ) ? $inc = 0 : null;
         $url = makeUrlNS('Special', 'TagCloud/' . htmlspecialchars($word));
-        $s = ( $popularity != 1 ) ? 's' : '';
-        $html[] = "<span class='tc_word_{$span_class} tc_{$span_class}_index_{$index}'><a href='$url' title='$popularity page$s'>$word</a></span>"; // $newline";
+        $popstring = ( $popularity == 1 ) ? $lang->get('pagetools_tagcloug_tip_popularity_one') : $lang->get('pagetools_tagcloug_tip_popularity_plural', array('popularity' => $popularity));
+        $html[] = "<span class='tc_word_{$span_class} tc_{$span_class}_index_{$index}'><a href='$url' title='$popstring'>$word</a></span>"; // $newline";
       }
     }
     $html = '<div style="text-align: ' . $div_align . '; margin: 0 auto; max-width: 400px;">' . implode("\n", $html) . '</div>';
--- a/includes/template.php	Tue Jan 29 17:29:08 2008 -0500
+++ b/includes/template.php	Tue Jan 29 23:15:44 2008 -0500
@@ -833,49 +833,16 @@
     if ( $this->site_disabled && $session->user_level >= USER_LEVEL_ADMIN && ( $paths->page != $paths->nslist['Special'] . 'Administration' ) )
     {
       $admin_link = makeUrlNS('Special', 'Administration', 'module=' . $paths->nslist['Admin'] . 'GeneralConfig', true);
-      echo '<div class="usermessage"><b>The site is currently disabled and thus is only accessible to administrators.</b><br />
-            You can re-enable the site through the <a href="' . $admin_link . '">administration panel</a>.
+      echo '<div class="usermessage"><b>' . $lang->get('page_sitedisabled_admin_msg_title') . '</b><br />
+            ' . $lang->get('page_sitedisabled_admin_msg_body', array('admin_link' => $admin_link)) . '
             </div>';
     }
   }
   
   function footer($simple = false)
   {
-    global $db, $session, $paths, $template, $plugins; // Common objects
-    if ( !$this->no_headers )
-    {
-      
-      if(!defined('ENANO_HEADERS_SENT'))
-        $this->header();
-      
-      global $_starttime;
-      if(isset($_GET['sqldbg']) && $session->get_permissions('mod_misc'))
-      {
-        echo '<h3>Query list as requested on URI</h3><pre style="margin-left: 1em">';
-        echo htmlspecialchars($db->sql_backtrace());
-        echo '</pre>';
-      }
-      
-      $f = microtime_float();
-      $f = $f - $_starttime;
-      $f = round($f, 4);
-      $dbg = 'Time: '.$f.'s  |  Queries: '.$db->num_queries;
-      $t = ( $simple ) ? $this->process_template('simple-footer.tpl') : $this->process_template('footer.tpl');
-      $t = str_replace('[[Stats]]', $dbg, $t);
-      $t = str_replace('[[NumQueries]]', (string)$db->num_queries, $t);
-      $t = str_replace('[[GenTime]]', (string)$f, $t);
-      
-      if ( defined('ENANO_DEBUG') )
-        $t = str_replace('</body>', '<div id="profile" style="margin: 10px;">' . profiler_make_html() . '</div></body>', $t);
-      
-      echo $t;
-      
-      ob_end_flush();
-    }
-    else
-    {
-      return '';
-    }
+    echo $this->getFooter();
+    ob_end_flush();
   }
   
   function getHeader()
@@ -888,28 +855,56 @@
   function getFooter()
   {
     global $db, $session, $paths, $template, $plugins; // Common objects
-    if(!$this->no_headers) {
+    global $lang;
+    if ( !$this->no_headers )
+    {
+      
+      if(!defined('ENANO_HEADERS_SENT'))
+        $this->header();
+      
       global $_starttime;
-      $t = '';
-      
       if(isset($_GET['sqldbg']) && $session->get_permissions('mod_misc'))
       {
-        $t .= '<h3>Query list as requested on URI</h3><pre style="margin-left: 1em">';
-        $t .= $db->sql_backtrace();
-        $t .= '</pre>';
+        echo '<h3>' . $lang->get('page_heading_sql_list') . '</h3><pre style="margin-left: 1em">';
+        echo htmlspecialchars($db->sql_backtrace());
+        echo '</pre>';
       }
       
+      $t = ( $simple ) ? $this->process_template('simple-footer.tpl') : $this->process_template('footer.tpl');
+      
       $f = microtime_float();
       $f = $f - $_starttime;
       $f = round($f, 4);
-      $dbg = 'Time: '.$f.'s  |  Queries: '.$db->num_queries;
-      $t.= $this->process_template('footer.tpl');
+      
+      $t_loc = $lang->get('page_msg_stats_gentime_short', array('time' => $f));
+      $t_loc_long = $lang->get('page_msg_stats_gentime_long', array('time' => $f));
+      $q_loc = '<a href="' . $this->tpl_strings['REPORT_URI'] . '">' . $lang->get('page_msg_stats_sql', array('nq' => $db->num_queries)) . '</a>';
+      $dbg = $t_loc;
+      $dbg_long = $t_loc_long;
+      if ( $session->user_level >= USER_LEVEL_ADMIN )
+      {
+        $dbg .= "&nbsp;&nbsp;|&nbsp;&nbsp;$q_loc";
+        $dbg_long .= "&nbsp;&nbsp;|&nbsp;&nbsp;$q_loc";
+      }
+      
       $t = str_replace('[[Stats]]', $dbg, $t);
+      $t = str_replace('[[StatsLong]]', $dbg_long, $t);
       $t = str_replace('[[NumQueries]]', (string)$db->num_queries, $t);
       $t = str_replace('[[GenTime]]', (string)$f, $t);
+      $t = str_replace('[[NumQueriesLoc]]', $q_loc, $t);
+      $t = str_replace('[[GenTimeLoc]]', $t_loc, $t);
+      $t = str_replace('[[EnanoPoweredLink]]', $lang->get('page_enano_powered', array('about_uri' => $this->tpl_strings['URL_ABOUT_ENANO'])), $t);
+      $t = str_replace('[[EnanoPoweredLinkLong]]', $lang->get('page_enano_powered_long', array('about_uri' => $this->tpl_strings['URL_ABOUT_ENANO'])), $t);
+      
+      if ( defined('ENANO_DEBUG') )
+        $t = str_replace('</body>', '<div id="profile" style="margin: 10px;">' . profiler_make_html() . '</div></body>', $t);
+      
       return $t;
     }
-    else return '';
+    else
+    {
+      return '';
+    }
   }
   
   /**
@@ -1033,7 +1028,7 @@
     $keywords = implode('|', $keywords);
     
     // Matches
-    //          1     2                               3                 4   56                       7     8
+    //          1     2                 3                 4   56                       7     8
     $regexp = '/(<!-- ('. $keywords .') ([A-z0-9_-]+) -->)(.*)((<!-- BEGINELSE \\3 -->)(.*))?(<!-- END \\3 -->)/isU';
     
     /*
@@ -2029,28 +2024,55 @@
   function footer($simple = false)
   {
     global $db, $session, $paths, $template, $plugins; // Common objects
+    global $lang;
+    
     if(!$this->no_headers) {
       global $_starttime;
       
-      $f = microtime(true);
+      $filename = ( $simple ) ? 'simple-footer.tpl' : 'footer.tpl';
+      $t = $this->process_template($filename);
+      
+      $f = microtime_float();
       $f = $f - $_starttime;
       $f = round($f, 4);
-      if(defined('IN_ENANO_INSTALL')) $nq = 'N/A';
-      else $nq = $db->num_queries;
-      if($nq == 0) $nq = 'N/A';
-      $dbg = 'Time: '.$f.'s  |  Queries: '.$nq;
-      $filename = ( $simple ) ? 'simple-footer.tpl' : 'footer.tpl';
-      $t = $this->process_template($filename);
-      $t = str_replace('[[Stats]]', $dbg, $t);
-      if ( is_object($db) )
+      
+      if ( is_object($lang) )
       {
-        $t = str_replace('[[NumQueries]]', (string)$db->num_queries, $t);
+        $t_loc = $lang->get('page_msg_stats_gentime_short', array('time' => $f));
+        $t_loc_long = $lang->get('page_msg_stats_gentime_long', array('time' => $f));
+        $q_loc = '<a href="' . $this->tpl_strings['REPORT_URI'] . '">' . $lang->get('page_msg_stats_sql', array('nq' => ( is_object($db) ? $db->num_queries : 'N/A' ))) . '</a>';
+        $dbg = $t_loc;
+        $dbg_long = $t_loc_long;
+        if ( $session->user_level >= USER_LEVEL_ADMIN )
+        {
+          $dbg .= "&nbsp;&nbsp;|&nbsp;&nbsp;$q_loc";
+          $dbg_long .= "&nbsp;&nbsp;|&nbsp;&nbsp;$q_loc";
+        }
+        $t = str_replace('[[EnanoPoweredLink]]', $lang->get('page_enano_powered', array('about_uri' => $this->tpl_strings['URL_ABOUT_ENANO'])), $t);
+        $t = str_replace('[[EnanoPoweredLinkLong]]', $lang->get('page_enano_powered_long', array('about_uri' => $this->tpl_strings['URL_ABOUT_ENANO'])), $t);
       }
       else
       {
-        $t = str_replace('[[NumQueries]]', '0', $t);
+        $t_loc = "Time: {$f}s";
+        $t_loc_long = "Generated in {$f}sec";
+        $q_loc = '<a href="' . $this->tpl_strings['REPORT_URI'] . '">' . ( is_object($db) ? "{$db->num_queries} SQL" : 'Queries: N/A' ) . '</a>';
+        $dbg = $t_loc;
+        $dbg_long = $t_loc_long;
+        if ( $session->user_level >= USER_LEVEL_ADMIN )
+        {
+          $dbg .= "&nbsp;&nbsp;|&nbsp;&nbsp;$q_loc";
+          $dbg_long .= "&nbsp;&nbsp;|&nbsp;&nbsp;$q_loc";
+        }
+        $t = str_replace('[[EnanoPoweredLink]]', 'Powered by <a href="http://enanocms.org/" onclick="window.open(this.href); return false;">Enano</a>', $t);
+        $t = str_replace('[[EnanoPoweredLinkLong]]', 'Website engine powered by <a href="http://enanocms.org/" onclick="window.open(this.href); return false;">Enano</a>', $t);
       }
+      
+      $t = str_replace('[[Stats]]', $dbg, $t);
+      $t = str_replace('[[StatsLong]]', $dbg_long, $t);
+      $t = str_replace('[[NumQueries]]', ( is_object($db) ? (string)$db->num_queries : '0' ), $t);
       $t = str_replace('[[GenTime]]', (string)$f, $t);
+      $t = str_replace('[[NumQueriesLoc]]', $q_loc, $t);
+      $t = str_replace('[[GenTimeLoc]]', $t_loc, $t);
       
       echo $t;
     }
--- a/index.php	Tue Jan 29 17:29:08 2008 -0500
+++ b/index.php	Tue Jan 29 23:15:44 2008 -0500
@@ -198,6 +198,7 @@
     case 'viewsource':
       $template->header();
       $text = RenderMan::getPage($paths->page_id, $paths->namespace, 0, false, false, false, false);
+      $text = htmlspecialchars($text);
       echo '
         <form action="'.makeUrl($paths->page, 'do=edit').'" method="post">
         <br />
--- a/install/includes/stages/confirm.php	Tue Jan 29 17:29:08 2008 -0500
+++ b/install/includes/stages/confirm.php	Tue Jan 29 23:15:44 2008 -0500
@@ -21,10 +21,10 @@
 
 $ui->show_header();
 ?>
-<h3>Enano is ready to install.</h3>
-            <p>Almost there! You've entered all the information we need for now. Click Continue to install the Enano database.</p>
-            <p style="font-size: smaller;"><b>A note on AES encryption:</b>
-               Enano is currently configured to use <?php echo AES_BITS; ?>-bit AES encryption. While the default value of 192 bits is perfectly acceptable for most sites, those in need of extreme security will want to change this value to 256 bits (the maximum available strength). If you need to change the cipher strength, please edit the file includes/constants.php and then <u>restart</u> this installation. Do not click Continue below until you redo the installation process up until this point, or you will experience severe problems with logging into your site.
+           <h3><?php echo $lang->get('confirm_title'); ?></h3>
+            <p><?php echo $lang->get('confirm_body'); ?></p>
+            <p style="font-size: smaller;"><b><?php echo $lang->get('confirm_info_aes_title'); ?></b>
+               <?php echo $lang->get('confirm_info_aes_body', array('aes_bits' => AES_BITS)); ?>
                </p>
             <form action="install.php?stage=install" method="post" name="install_login" onsubmit="return ( verify() && submit_encrypt() );"><?php
   foreach ( $_POST as $key => &$value )
@@ -39,6 +39,6 @@
 ?>
 
               <div style="text-align: center;">
-                <input type="submit" name="_cont" value="<?= $lang->get('meta_btn_continue'); ?>" />
+                <input type="submit" name="_cont" value="<?= $lang->get('confirm_btn_install_enano'); ?>" />
               </div>
             </form>
--- a/install/includes/stages/database_post.php	Tue Jan 29 17:29:08 2008 -0500
+++ b/install/includes/stages/database_post.php	Tue Jan 29 23:15:44 2008 -0500
@@ -96,7 +96,7 @@
       <h3>Configuration file generation failed.</h3>
       <p>Couldn't open the configuration file to write out database settings. Check your file permissions.</p>
       <p>
-        <input type="submit" name="_cont" value="Go back" />
+        <input type="submit" name="_cont" value="<?php echo $lang->get('database_btn_go_back'); ?>" />
       </p>
     </form>
     <?php
@@ -155,15 +155,15 @@
         <form action="install.php?stage=database" method="post" name="database_info">
           <input type="hidden" name="language" value="<?php echo $lang_id; ?>" />
           <input type="hidden" name="driver" value="<?php echo $driver; ?>" />
-          <h3>Database operation failed</h3>
-          <p>The installer couldn't create one of the tables used for installation.</p>
-          <p>Error description:
+          <h3><?php echo $lang->get('database_msg_sql_fail_title'); ?></h3>
+          <p><?php echo $lang->get('database_msg_sql_fail_body'); ?></p>
+          <p><?php echo $lang->get('database_msg_post_fail_desc'); ?>
             <?php
             echo $dbal->sql_error();
             ?>
           </p>
           <p>
-            <input type="submit" name="_cont" value="Go back" />
+            <input type="submit" name="_cont" value="<?php echo $lang->get('database_btn_go_back'); ?>" />
           </p>
         </form>
         <?php
@@ -186,9 +186,9 @@
   <?php
   // FIXME: l10n
   ?>
-  <h3>Connection successful</h3>
-  <p>The database has been contacted and initial tables created successfully. Redirecting...</p>
-  <p><input type="submit" name="_cont" value="<?php echo $lang->get('meta_btn_continue'); ?>" />  Click if you're not redirected within 2 seconds</p>
+  <h3><?php echo $lang->get('database_msg_success_title'); ?></h3>
+  <p><?php echo $lang->get('database_msg_success_body'); ?></p>
+  <p><input type="submit" name="_cont" value="<?php echo $lang->get('meta_btn_continue'); ?>" />  <?php echo $lang->get('database_msg_success_redirect'); ?></p>
   </form>
   <script type="text/javascript">
     setTimeout(function()
@@ -206,15 +206,15 @@
   <form action="install.php?stage=database" method="post" name="database_info">
     <input type="hidden" name="language" value="<?php echo $lang_id; ?>" />
     <input type="hidden" name="driver" value="<?php echo $driver; ?>" />
-    <h3>Database connection failed</h3>
-    <p>The installer couldn't connect to the database because something went wrong while the connection attempt was being made. Please press your browser's back button and correct your database information.</p>
-    <p>Error description:
+    <h3><?php echo $lang->get('database_msg_post_fail_title'); ?></h3>
+    <p><?php echo $lang->get('database_msg_post_fail_body'); ?></p>
+    <p><?php echo $lang->get('database_msg_post_fail_desc'); ?>
       <?php
       echo $dbal->sql_error();
       ?>
     </p>
     <p>
-      <input type="submit" name="_cont" value="Go back" />
+      <input type="submit" name="_cont" value="<?php echo $lang->get('database_btn_go_back'); ?>" />
     </p>
   </form>
   <?php
--- a/install/includes/stages/finish.php	Tue Jan 29 17:29:08 2008 -0500
+++ b/install/includes/stages/finish.php	Tue Jan 29 23:15:44 2008 -0500
@@ -33,8 +33,8 @@
 flush();
 
 ?>
-<h3>Performing final installation steps</h3>
-<p>Enano is cleaning up and performing some final installation tasks. Please wait...</p>
+<h3><?php echo $lang->get('finish_heading_progress'); ?></h3>
+<p><?php echo $lang->get('finish_msg_progress'); ?></p>
 
 <?php
 
@@ -52,17 +52,16 @@
 // FIXME: l10n
 start_install_table();
 
-run_installer_stage('load', 'Load installer files', 'stg_load_files', 'One of the files needed for installation couldn\'t be loaded. Please check your Enano directory.', false);
-run_installer_stage('cleanup', 'Clean up encryption keys', 'stg_aes_cleanup', 'There was a database error while removing the temporary encryption keys from the database. For maximum site security you should delete the config entries install_aes_key and site_aes_key manually.', false);
+run_installer_stage('load', $lang->get('install_stg_load_title'), 'stg_load_files', $lang->get('install_stg_load_body'), false);
+run_installer_stage('cleanup', $lang->get('install_stg_cleanup_title'), 'stg_aes_cleanup', $lang->get('install_stg_cleanup_body'), false);
 run_installer_stage('buildindex', $lang->get('install_stg_buildindex_title'), 'stg_build_index', $lang->get('install_stg_buildindex_body'));
 run_installer_stage('renameconfig', $lang->get('install_stg_rename_title'), 'stg_rename_config', $lang->get('install_stg_rename_body', array('mainpage_link' => scriptPath . '/index.php')));
 
 close_install_table();
 
 ?>
-<h3>Congratulations! You've finished installing Enano.</h3>
-<p>Enano has finished setting up on your server. Now you can go to your <a href="<?php echo makeUrlNS('Article', 'Main_Page');?>">new
-   website</a> and start creating content!</p>
+<h3><?php echo $lang->get('finish_msg_success_title'); ?></h3>
+<p><?php echo $lang->get('finish_msg_success_body', array('mainpage_link' => makeUrlNS('Article', 'Main_Page'))); ?></p>
 <?php 
   echo $lang->get('finish_body');
   echo '<p>' . $lang->get('finish_link_mainpage', array('mainpage_link' => scriptPath . '/index.php')) . '</p>';
--- a/install/includes/stages/install.php	Tue Jan 29 17:29:08 2008 -0500
+++ b/install/includes/stages/install.php	Tue Jan 29 23:15:44 2008 -0500
@@ -40,9 +40,9 @@
   <form action="install.php?stage=database" method="post" name="database_info">
     <input type="hidden" name="language" value="<?php echo $lang_id; ?>" />
     <input type="hidden" name="driver" value="<?php echo $dbdriver; ?>" />
-    <h3>Database connection failed</h3>
-    <p>The installer couldn't connect to the database because something went wrong while the connection attempt was being made. Please press your browser's back button and correct your database information.</p>
-    <p>Error description:
+    <h3><?php echo $lang->get('database_msg_post_fail_title'); ?></h3>
+    <p><?php echo $lang->get('database_msg_post_fail_body'); ?></p>
+    <p><?php echo $lang->get('database_msg_post_fail_desc'); ?>
       <?php
       echo $db->sql_error();
       ?>
@@ -61,10 +61,10 @@
 flush();
 
 ?>
-<h3>Installing Enano</h3>
-<p>Please wait while Enano creates its database and initial content on your server.</p>
+<h3><?php echo $lang->get('install_title'); ?></h3>
+<p><?php echo $lang->get('install_body'); ?></p>
 
-<h3>Installation progress</h3>
+<h3><?php echo $lang->get('install_heading_progress'); ?></h3>
 
 <?php
 
@@ -82,11 +82,11 @@
 // FIXME: l10n
 start_install_table();
 
-run_installer_stage('load', 'Load installer files', 'stg_load_files', 'One of the files needed for installation couldn\'t be loaded. Please check your Enano directory.', false);
-run_installer_stage('setpass', 'Retrieve administrator password', 'stg_password_decode', 'The administrator password couldn\'t be decrypted. This really shouldn\'t happen.');
-run_installer_stage('genaes', 'Generate private key', 'stg_make_private_key', 'Couldn\'t generate a private key for the site. This really shouldn\'t happen.');
-run_installer_stage('sqlparse', 'Prepare database schema', 'stg_load_schema', 'Couldn\'t load or parse the schema file. This really shouldn\'t happen.');
-run_installer_stage('payload', 'Install database', 'stg_deliver_payload', 'There was a problem with an SQL query.');
+run_installer_stage('load', $lang->get('install_stg_load_title'), 'stg_load_files', $lang->get('install_stg_load_body'), false);
+run_installer_stage('setpass', $lang->get('install_stg_setpass_title'), 'stg_password_decode', $lang->get('install_stg_setpass_body'));
+run_installer_stage('genaes', $lang->get('install_stg_genaes_title'), 'stg_make_private_key', $lang->get('install_stg_genaes_body'));
+run_installer_stage('sqlparse', $lang->get('install_stg_sqlparse_title'), 'stg_load_schema', $lang->get('install_stg_sqlparse_body'));
+run_installer_stage('payload', $lang->get('install_stg_payload_title'), 'stg_deliver_payload', $lang->get('install_stg_payload_body'));
 run_installer_stage('writeconfig', $lang->get('install_stg_writeconfig_title'), 'stg_write_config', $lang->get('install_stg_writeconfig_body'));
 
 // Now that the config is written, shutdown our primitive API and startup the full Enano API
--- a/install/includes/stages/login.php	Tue Jan 29 17:29:08 2008 -0500
+++ b/install/includes/stages/login.php	Tue Jan 29 23:15:44 2008 -0500
@@ -56,9 +56,8 @@
 
 // FIXME: l10n
 ?>
-<h3>Administration account</h3>
-<p>Now it's time to create the account you'll use to administer your site. The e-mail address you enter here will also be used for the global contact address; you can change this after installation is finished if need be.</p>
-<p>Do not forget the information you enter here. Otherwise you will be unable to administer your site.</p>
+<h3><?php echo $lang->get('login_welcome_title'); ?></h3>
+<?php echo $lang->get('login_welcome_body'); ?>
 
 <script type="text/javascript">
 
@@ -111,7 +110,8 @@
   {
     if ( verify() )
       return true;
-    alert("One or more of the form fields contains an incorrect value. Please correct any fields that have an X next to them.");
+    alert($lang.get('login_err_verify_failure'));
+    return false;
   }
   
   function submit_encrypt()
@@ -141,7 +141,7 @@
     
     if ( !crypt_data )
     {
-      alert('Received a bad response from rijndaelEncrypt(). Shift-click "reload" or "refresh" (depending on your browser) and try again.');
+      alert($lang.get('login_err_rijndael_failed'));
       return false;
     }
   
@@ -181,7 +181,7 @@
   
     <tr>
       <td style="width: 50%;">
-        <b>Username</b>
+        <b><?php echo $lang->get('login_field_username'); ?></b>
       </td>
       <td style="width: 50%;">
         <input type="text" tabindex="1" name="username" size="15" onkeyup="verify(this);" />
@@ -193,15 +193,15 @@
     
     <tr>
       <td>
-        <b>Password</b><br />
-        This will be encrypted with AES before it's sent to the server.
+        <b><?php echo $lang->get('login_field_password'); ?></b><br />
+        <?php echo $lang->get('login_aes_blurb'); ?>
       </td>
       <td>
         <input type="password" tabindex="2" name="password" size="15" onkeyup="password_score_field(this); verify(this);" /><br />
         <br />
         <div id="pwmeter"></div>
         <br />
-        <input type="password" tabindex="3" name="password_confirm" size="15" onkeyup="verify(this);" /> <small>(confirm)</small>
+        <input type="password" tabindex="3" name="password_confirm" size="15" onkeyup="verify(this);" /> <small><?php echo $lang->get('login_field_password_confirm'); ?></small>
       </td>
       <td>
         <img id="s_password" alt="Good/bad icon" src="../images/bad.gif" />
@@ -210,7 +210,7 @@
     
     <tr>
       <td style="width: 50%;">
-        <b>E-mail</b>
+        <b><?php echo $lang->get('login_field_email'); ?></b>
       </td>
       <td style="width: 50%;">
         <input type="text" tabindex="4" name="email" size="30" onkeyup="verify(this);" />
--- a/install/includes/stages/website.php	Tue Jan 29 17:29:08 2008 -0500
+++ b/install/includes/stages/website.php	Tue Jan 29 23:15:44 2008 -0500
@@ -70,9 +70,8 @@
       }
       else
       {
-        // FIXME: l10n
         install_unset_ajax_loading();
-        new messagebox(MB_OK | MB_ICONSTOP, 'All tests failed', 'None of the URL handling tests worked; you may have problems using Enano on your server.');
+        new messagebox(MB_OK | MB_ICONSTOP, $lang.get('website_msg_ajax_test_fail_title'), $lang.get('website_msg_ajax_test_fail_body'));
       }
     }
   }
@@ -88,17 +87,16 @@
     document.getElementById('url_radio_' + level).checked = true;
     document.getElementById('url_radio_' + level).focus();
     
-    // FIXME: l10n
     switch ( level )
     {
       case 'rewrite':
-        var str = 'The installer has detected that using rewritten URLs is the best level that will work.';
+        var str = $lang.get('website_msg_bestmethod_rewrite');
         break;
       case 'shortened':
-        var str = 'The installer has detected that using shortened URLs is the best level that will work.';
+        var str = $lang.get('website_msg_bestmethod_shortened');
         break;
       case 'standard':
-        var str = 'The installer has detected that using standard URLs is the only level that will work.';
+        var str = $lang.get('website_msg_bestmethod_standard');
         break;
     }
     document.getElementById('mrw_report').className = 'info-box-mini';
@@ -153,8 +151,8 @@
   
     <tr>
       <td>
-        <b>Pick a name</b><br />
-        <span id="hint_site_name" class="fieldtip">Now for the fun part - it's time to name your website. Try to pick something that doesn't include any special characters, since this can make project-page URLs look botched.</span>
+        <b><?php echo $lang->get('website_field_name'); ?></b><br />
+        <span id="hint_site_name" class="fieldtip"><?php echo $lang->get('website_field_name_hint'); ?></span>
       </td>
       <td style="width: 50%;">
         <input type="text" name="site_name" size="50" tabindex="1" />
@@ -163,8 +161,8 @@
     
     <tr>
       <td>
-        <b>Enter a short description</b><br />
-        <span id="hint_site_desc" class="fieldtip">Here you should enter a very short description of your site. Sometimes this is a slogan or, depending on the theme you've chosen, a set of keywords that can go into a META description tag.</span>
+        <b><?php echo $lang->get('website_field_desc'); ?></b><br />
+        <span id="hint_site_desc" class="fieldtip"><?php echo $lang->get('website_field_desc_hint'); ?></span>
       </td>
       <td>
         <input type="text" name="site_desc" size="50" tabindex="2" />
@@ -173,8 +171,8 @@
     
     <tr>
       <td>
-        <b>Copyright info</b><br />
-        <span id="hint_copyright" class="fieldtip">The text you enter here will be shown at the bottom of most pages. Typically this is where a copyright notice would go. Keep it short and sweet; you can use <a href="http://docs.enanocms.org/Help:3.1">internal links</a> to link to project pages you'll create later.</span>
+        <b><?php echo $lang->get('website_field_copyright'); ?></b><br />
+        <span id="hint_copyright" class="fieldtip"><?php echo $lang->get('website_field_copyright_hint'); ?></span>
       </td>
       <td>
         <input type="text" name="copyright" size="50" tabindex="3" />
@@ -183,8 +181,8 @@
     
     <tr>
       <td valign="top">
-        <b>URL formatting</b><br />
-        This lets you choose how URLs within your site will be formatted. If the setting you pick doesn't work, you can change it by editing config.php after installation.
+        <b><?php echo $lang->get('website_field_urlscheme'); ?></b><br />
+        <?php echo $lang->get('website_field_urlscheme_hint'); ?>
       </td>
       <td>
       
@@ -195,11 +193,11 @@
             </td>
             <td>
               <label for="url_radio_standard">
-                <b>Standard URLs</b>
+                <b><?php echo $lang->get('website_field_urlscheme_opt_standard'); ?></b>
               </label>
               <span class="fieldtip" id="hint_url_scheme_standard">
-                <p>Compatible with all servers. This is the default option and should be used unless you're sure that one of the other options below.</p>
-                <p><small><b>Example:</b> <tt><?php echo $scriptpath_full . 'index.php?title=Page'; ?></tt></small></p>
+                <p><?php echo $lang->get('website_field_urlscheme_opt_standard_hint'); ?></p>
+                <p><small><b><?php echo $lang->get('website_field_urlscheme_lbl_example'); ?></b> <tt><?php echo $scriptpath_full . 'index.php?title=Page'; ?></tt></small></p>
               </span>
             </td>
           </tr>
@@ -212,11 +210,11 @@
             </td>
             <td>
               <label for="url_radio_shortened">
-                <b>Shortened URLs</b>
+                <b><?php echo $lang->get('website_field_urlscheme_opt_shortened'); ?></b>
               </label>
               <span class="fieldtip" id="hint_url_scheme_shortened">
-                <p>This eliminates the "?title=" portion of your URL, and instead uses a slash. This is occasionally more friendly to search engines.</p>
-                <p><small><b>Example:</b> <tt><?php echo $scriptpath_full . 'index.php/Page'; ?></tt></small></p>
+                <p><?php echo $lang->get('website_field_urlscheme_opt_shortened_hint'); ?></p>
+                <p><small><b><?php echo $lang->get('website_field_urlscheme_lbl_example'); ?></b> <tt><?php echo $scriptpath_full . 'index.php/Page'; ?></tt></small></p>
               </span>
             </td>
           </tr>
@@ -229,18 +227,18 @@
             </td>
             <td>
               <label for="url_radio_rewrite">
-                <b>Rewritten URLs</b>
+                <b><?php echo $lang->get('website_field_urlscheme_opt_rewrite'); ?></b>
               </label>
               <span id="hint_url_scheme_rewrite" class="fieldtip">
-                <p>Using this option, you can completely eliminate the "index.php" from URLs. This is the most friendly option to search engines and looks very professional, but requires support for URL rewriting on your server. If you're running Apache and have the right permissions, Enano can configure this automatically. Otherwise, you'll need to configure your server manually and have a knowledge of regular expressions for this option to work.</p>
-                <p><small><b>Example:</b> <tt><?php echo $scriptpath_full . 'Page'; ?></tt></small></p>
+                <p><?php echo $lang->get('website_field_urlscheme_opt_rewrite_hint'); ?></p>
+                <p><small><b><?php echo $lang->get('website_field_urlscheme_lbl_example'); ?></b> <tt><?php echo $scriptpath_full . 'Page'; ?></tt></small></p>
               </span>
             </td>
           </tr>
         </table>
         
         <p>
-          <a href="#mrw_scan" onclick="ajaxMrwTest(); return false;" tabindex="4">Auto-detect the best formatting scheme</a>
+          <a href="#mrw_scan" onclick="ajaxMrwTest(); return false;" tabindex="4"><?php echo $lang->get('website_btn_urlscheme_detect'); ?></a>
         </p>
         
         <div id="mrw_report"></div>
--- a/install/install.php	Tue Jan 29 17:29:08 2008 -0500
+++ b/install/install.php	Tue Jan 29 23:15:44 2008 -0500
@@ -164,6 +164,7 @@
       $lang = new Language($lang_id);
       $lang->load_file(ENANO_ROOT . '/language/' . $language_dir . '/install.json');
       $lang->load_file(ENANO_ROOT . '/language/' . $language_dir . '/core.json');
+      $lang->load_file(ENANO_ROOT . '/language/' . $language_dir . '/user.json');
       
       $time_now = microtime_float();
       $test = "if ( typeof(enano_lang) != 'object' )
--- a/language/english/admin.json	Tue Jan 29 17:29:08 2008 -0500
+++ b/language/english/admin.json	Tue Jan 29 23:15:44 2008 -0500
@@ -330,6 +330,7 @@
       info_magick: 'You can allow Enano to generate thumbnails of images automatically. This feature requires ImageMagick to work properly. If your server does not have ImageMagick on it, Enano will try to use the GD library (if available) to scale images. This can be slower, but it works on a wider range of servers. If even that does not work, Enano will simply make your users\' browsers scale the images. In most cases this is fine, but if you are uploading large (>100KB) images and embedding them inside of pages, you should try to enable ImageMagick or configure GD because transferring these large images many times can cost you quite a lot of bandwidth.',
       field_magick_enable: 'Use ImageMagick to scale images',
       field_magick_path: 'Path to ImageMagick:',
+      err_magick_not_found: '<b>Warning:</b> the file "%magick_path%" was not found, and the ImageMagick file path was not updated.',
       // Translators: for the path here, please be sure to use a double-backslash in the Windows path. Avoid translating the file paths
       // anyway since they're generally the same even on non-English Windows systems.
       field_magick_path_hint: 'On Linux and Unix servers, the most likely options here are /usr/bin/convert and /usr/local/bin/convert. If you server runs Windows, then ImageMagick is most likely to be C:\\Windows\\Convert.exe or C:\\Windows\\System32\\Convert.exe.',
--- a/language/english/core.json	Tue Jan 29 17:29:08 2008 -0500
+++ b/language/english/core.json	Tue Jan 29 23:15:44 2008 -0500
@@ -49,6 +49,25 @@
       enano_about_lbl_pgsqlversion: '<a href="http://www.postgresql.org/">PostgreSQL</a> version:',
     },
     page: {
+      sitedisabled_admin_msg_title: 'The site is currently disabled and thus is only accessible to administrators.',
+      sitedisabled_admin_msg_body: 'You can re-enable the site through the <a href="%admin_link%">administration panel</a>.',
+      
+      heading_sql_list: 'Query list as requested on URI',
+      
+      msg_stats_gentime_long: 'Generated in %time%sec',
+      msg_stats_gentime_short: 'Time: %time%s',
+      msg_stats_sql: '%nq% SQL',
+      
+      w3c_valid_html32: 'Valid HTML 3.2',
+      w3c_valid_html40: 'Valid HTML 4.0',
+      w3c_valid_html401: 'Valid HTML 4.01',
+      w3c_valid_html50: 'Valid HTML 5.0',
+      w3c_valid_xhtml10: 'Valid XHTML 1.0',
+      w3c_valid_xhtml11: 'Valid XHTML 1.1',
+      w3c_valid_css: 'Valid CSS',
+      enano_powered: 'Powered by <a href="%about_uri%">Enano</a>',
+      enano_powered_long: 'Website engine powered by <a href="%about_uri%">Enano</a>',
+      
       protect_lbl_success_title: 'Page protected',
       protect_lbl_success_body: 'The protection setting has been applied. <a href="%page_link%">Return to the page</a>.',
       protect_err_need_reason: 'Error: you must enter a reason for protecting this page.',
@@ -132,6 +151,15 @@
       msg_archived_title: 'Notice:',
       msg_archived_body: 'The page you are viewing was archived on %archive_date% at %archive_time%.<br /><a href="%current_link%" onclick="ajaxReset(); return false;">View current version</a>  |  <a href="%restore_link%" onclick="%restore_onclick%">Restore this version</a>',
       
+      msg_special_404_title: 'Can\'t load special page',
+      msg_special_404_body: 'The special page you requested could not be found. This may be due to a plugin failing to load. A list of all special pages on this website can be viewed <a href="%sp_link%">here</a>. You will be redirected to the main page in 15 seconds.',
+      msg_admin_404_title: 'Administration page not found',
+      msg_admin_404_body: '<p>You\'ve requested an administration page, but the function <tt>%func_name%</tt> doesn\'t exist, so the page can\'t be loaded.</p>
+                             <h3>Plugin developer?</h3>
+                              <p>Create a function called <tt>%func_name%</tt> - it should be loaded when you refresh this page.</p>
+                             <h3>Otherwise...</h3>
+                              <p>If you\'re trying to use a plugin\'s administration page, contact the developer of the plugin. If you\'re trying to use a function that is built into Enano (not added by a plugin), then please contact the Enano development team.</p>',
+      
       msg_general_error: 'General error in page fetcher',
                     
       autosuggest_heading: 'Page name matches',
@@ -246,6 +274,24 @@
       tip_moreoptions: 'Additional options for working with this page',
       tip_password: 'Require a password in order for this page to be viewed',
       tip_aclmanager: 'Manage who can do what with this page (alt-m)',
+      
+      cat_heading_subcategories: 'Subcategories',
+      cat_msg_no_subcategories: 'No subcategories.',
+      cat_heading_pages: 'Pages',
+      cat_msg_no_pages: 'No pages in this category.',
+      
+      filebox_heading: 'Uploaded file',
+      filebox_msg_not_found: 'There are no files uploaded with this name yet. <a href="%upload_link%">Upload a file...</a>',
+      filebox_lbl_type: 'Type:',
+      filebox_lbl_size: 'Size: %size%',
+      filebox_lbl_uploaded: 'Uploaded:',
+      filebox_msg_virus_warning: 'This file type may contain viruses or other code that could harm your computer. You should exercise caution if you download it.',
+      filebox_btn_download: 'Download this file',
+      filebox_btn_upload_new: 'Upload new version',
+      filebox_heading_history: 'File history',
+      filebox_btn_this_version: 'this ver',
+      filebox_btn_revert: 'revert',
+      
     },
     editor: {
       err_server: 'There was a problem starting the editor',
--- a/language/english/install.json	Tue Jan 29 17:29:08 2008 -0500
+++ b/language/english/install.json	Tue Jan 29 23:15:44 2008 -0500
@@ -116,6 +116,19 @@
       
       objective_test: 'Check your MySQL connection using the "Test Connection" button.',
       objective_uncrypt: 'Be aware that your database information will be transmitted unencrypted several times.',
+      
+      // database_post module
+      btn_go_back: 'Go back',
+      msg_success_title: 'Connection successful',
+      msg_success_body: 'The database has been contacted and initial tables created successfully. Redirecting...',
+      msg_success_redirect: 'Click if you\'re not redirected within 2 seconds',
+      
+      msg_post_fail_title: 'Database connection failed',
+      msg_post_fail_body: 'The installer couldn\'t connect to the database because something went wrong while the connection attempt was being made. Please press your browser\'s back button and correct your database information.',
+      msg_post_fail_desc: 'Error description:',
+      
+      msg_sql_fail_title: 'Database operation failed',
+      msg_sql_fail_body: 'The installer couldn\'t create one of the tables used for installation.',
     },
     dbmysql: {
       msg_err_mysql_connect: '<b>Error:</b> The database server "%db_host%" couldn\'t be contacted.<br />%mysql_error%',
@@ -196,7 +209,28 @@
       modetitle_long: 'Website information',
       header_blurb: 'The next step is to enter some information about your website. You can always change this information later, using the administration panel.',
       
-      // Need l10n 
+      msg_ajax_test_fail_title: 'All tests failed',
+      msg_ajax_test_fail_body: 'None of the URL handling tests worked; you may have problems using Enano on your server.',
+      msg_bestmethod_rewrite: 'The installer has detected that using rewritten URLs is the best level that will work.',
+      msg_bestmethod_shortened: 'The installer has detected that using shortened URLs is the best level that will work.',
+      msg_bestmethod_standard: 'The installer has detected that using standard URLs is the only level that will work.',
+      
+      field_name: 'Pick a name',
+      field_name_hint: 'Now for the fun part - it\'s time to name your website. Try to pick something that doesn\'t include any special characters, since this can make project-page URLs look botched.',
+      field_desc: 'Enter a short description',
+      field_desc_hint: 'Here you should enter a very short description of your site. Sometimes this is a slogan or, depending on the theme you\'ve chosen, a set of keywords that can go into a META description tag.',
+      field_copyright: 'Copyright info',
+      field_copyright_hint: 'The text you enter here will be shown at the bottom of most pages. Typically this is where a copyright notice would go. Keep it short and sweet; you can use <a href="http://docs.enanocms.org/Help:3.1">internal links</a> to link to project pages you\'ll create later.',
+      field_urlscheme: 'URL formatting',
+      field_urlscheme_hint: 'This lets you choose how URLs within your site will be formatted. If the setting you pick doesn\'t work, you can change it by editing config.php after installation.',
+      field_urlscheme_lbl_example: 'Example:',
+      field_urlscheme_opt_standard: 'Standard URLs',
+      field_urlscheme_opt_standard_hint: 'Compatible with all servers. This is the default option and should be used unless you\'re sure that one of the other options below will work.',
+      field_urlscheme_opt_shortened: 'Shortened URLs',
+      field_urlscheme_opt_shortened_hint: 'This eliminates the "?title=" portion of your URL, and instead uses a slash. This is occasionally more friendly to search engines.',
+      field_urlscheme_opt_rewrite: 'Rewritten URLs',
+      field_urlscheme_opt_rewrite_hint: 'Using this option, you can completely eliminate the "index.php" from URLs. This is the most friendly option to search engines and looks very professional, but requires support for URL rewriting on your server. If you\'re running Apache and have the right permissions, Enano can configure this automatically. Otherwise, you\'ll need to configure your server manually and have a knowledge of regular expressions for this option to work.',
+      btn_urlscheme_detect: 'Auto-detect the best formatting scheme',
       
       objective_verify: 'Verify that your site information is correct. Again, all of the above settings can be changed from the administration panel.',
     },
@@ -205,9 +239,16 @@
       header_blurb: 'Next, enter your desired username and password. The account you create here will be used to administer your site.',
       modetitle_long: 'Administration login',
       
-      // Need l10n
-      
-      aes_blurb: 'If your browser supports Javascript, the password you enter here will be encrypted with AES before it is sent to the server.',
+      welcome_title: 'Administration account',
+      welcome_body: '<p>Now it\'s time to create the account you\'ll use to administer your site. The e-mail address you enter here will also be used for the global contact address; you can change this after installation is finished if need be.</p>
+                     <p>Do not forget the information you enter here. Otherwise you will be unable to administer your site.</p>',
+      err_verify_failure: 'One or more of the form fields contains an incorrect value. Please correct any fields that have an X next to them.',
+      err_rijndael_failed: 'Received a bad response from rijndaelEncrypt(). Shift-click "reload" or "refresh" (depending on your browser) and try again.',
+      field_username: 'Username',
+      field_password: 'Password',
+      aes_blurb: 'This will be encrypted with AES before it\'s sent to the server.',
+      field_password_confirm: '(confirm)',
+      field_email: 'E-mail',
       
       objective_remember: 'Remember the username and password you enter here! You will not be able to administer your site without the information you enter on this page.',
     },
@@ -215,40 +256,46 @@
       modetitle: 'Review',
       modetitle_long: 'Confirm installation',
       
-      header_blurb_title: 'Enano is ready to install.',
-      header_blurb_body: 'The wizard has finished collecting information and is ready to install the database schema. Please review the information below, and then click the button below to install the database.',
+      title: 'Enano is ready to install.',
+      body: 'Almost there! You\'ve entered all the information we need for now. Click the button below to install the Enano database.',
+      info_aes_title: 'A note on AES encryption:',
+      info_aes_body: 'Enano is currently configured to use %aes_bits%-bit AES encryption. While the default value of 192 bits is perfectly acceptable for most sites, those in need of extreme security will want to change this value to 256 bits (the maximum available strength). If you need to change the cipher strength, please edit the file includes/constants.php and then <u>restart</u> this installation. Do not click Continue below until you redo the installation process up until this point, or you will experience severe problems with logging into your site.',
       
-      lbl_db_host: 'Database hostname:',
-      lbl_db_name: 'Database name:',
-      lbl_db_user: 'Database user:',
-      lbl_db_pass: 'Database password: &lt;hidden&gt;',
-      lbl_sitename: 'Site name:',
-      lbl_sitedesc: 'Site description:',
-      lbl_adminuser: 'Administration username:',
-      lbl_aesbits: 'Cipher strength:',
-      lbl_aes_strength: '%aes_bits%-bit AES',
-      lbl_aes_change: 'Cipher strength is defined in the file constants.php; if you desire to change the cipher strength, you may do so and then restart installation. Unless your site is mission-critical, changing the cipher strength is not necessary.',
-      
-      objective_pray: 'Pray.',
       btn_install_enano: 'Install Enano!',
     },
     install: {
       modetitle: 'Install',
       modetitle_long: 'Database installation',
       
-      stg_connect_title: 'Connect to MySQL',
-      stg_connect_body: 'MySQL denied our attempt to connect to the database. This is most likely because your login information was incorrect. You will most likely need to <a href="install.php?mode=license">restart the installation</a>.',
-      stg_drop_title: 'Drop existing Enano tables',
-      stg_decrypt_title: 'Decrypt administration password',
-      stg_decrypt_body: 'The administration password you entered couldn\'t be decrypted. It is possible that your server did not properly store the encryption key in the configuration file. Please check the file permissions on config.new.php. You may have to return to the login stage of the installation, clear your browser cache, and then rerun this installation.',
-      stg_genkey_title: 'Generate %aes_bits%-bit AES private key',
-      stg_genkey_body: 'Enano encountered an internal error while generating the site encryption key. Please contact the Enano team for support.',
-      stg_parse_title: 'Prepare to execute schema file',
-      stg_parse_body: 'Enano encountered an internal error while parsing the SQL file that contains the database structure and initial data. Please contact the Enano team for support.',
-      stg_sql_title: 'Execute installer schema',
-      stg_sql_body: 'The installation failed because an SQL query wasn\'t quite correct. It is possible that you entered malformed data into a form field, or there may be a bug in Enano with your version of MySQL. Please contact the Enano team for support.',
+      title: 'Installing Enano',
+      body: 'Please wait while Enano creates its database and initial content on your server.',
+      heading_progress: 'Installation progress',
+      
+      stg_load_title: 'Load installer files',
+      stg_load_body: 'One of the files needed for installation couldn\'t be loaded. Please check your Enano directory.',
+      stg_setpass_title: 'Retrieve administrator password',
+      stg_setpass_body: 'The administrator password couldn\'t be decrypted. This really shouldn\'t happen.',
+      stg_genaes_title: 'Generate private key',
+      stg_genaes_body: 'Couldn\'t generate a private key for the site. This really shouldn\'t happen.',
+      stg_sqlparse_title: 'Prepare database schema',
+      stg_sqlparse_body: 'Couldn\'t load or parse the schema file. This really shouldn\'t happen.',
+      stg_payload_title: 'Install database',
+      stg_payload_body: 'There was a problem with an SQL query. Details are above.',
       stg_writeconfig_title: 'Write configuration files',
       stg_writeconfig_body: 'Enano was unable to write the configuration file with your site\'s database credentials. This is almost always because your configuration file does not have the correct permissions. On Windows servers, you may see this message even if the check on the System Requirements page passed. Temporarily running IIS as the Administrator user may help.',
+      
+      stg_startapi_title: 'Start the Enano API',
+      stg_startapi_body: 'The Enano API could not be started. This is an error that should never occur; please contact the Enano team for support.',
+      stg_importlang_title: 'Import default language',
+      stg_importlang_body: 'Enano couldn\'t import the English language file.',
+      stg_initlogs_title: 'Initialize logs',
+      stg_initlogs_body: '<b>The session manager denied the request to flush logs for the main page.</b><br />
+                           While under most circumstances you can still <a href="install.php?mode=finish">finish the installation</a>, you should be aware that some servers cannot
+                           properly set cookies due to limitations with PHP. These limitations are exposed primarily when this issue is encountered during installation. If you choose
+                           to finish the installation, please be aware that you may be unable to log into your site.',
+      
+      stg_cleanup_title: 'Clean up encryption keys',
+      stg_cleanup_body: 'There was a database error while removing the temporary encryption keys from the database. For maximum site security you should delete the config entries install_aes_key and site_aes_key manually.',
       stg_rename_title: 'Rename configuration files',
       stg_rename_body: 'Enano couldn\'t rename the configuration files to their correct production names. <span style="font-weight: bold; color: red;">Please perform the following rename operations and then follow the instructions to finish the installation below.</span>
                           <ul>
@@ -257,24 +304,18 @@
                           </ul>
                         %this.finish_body%
                         %this.finish_link_mainpage%',
-      stg_startapi_title: 'Start the Enano API',
-      stg_startapi_body: 'The Enano API could not be started. This is an error that should never occur; please contact the Enano team for support.',
-      stg_importlang_title: 'Import default language',
-      stg_importlang_body: 'Enano couldn\'t import the English language file.',
       stg_buildindex_title: 'Initialize search index',
       stg_buildindex_body: 'Something went wrong while the page manager was attempting to build a search index.',
-      stg_initlogs_title: 'Initialize logs',
-      stg_initlogs_body: '<b>The session manager denied the request to flush logs for the main page.</b><br />
-                           While under most circumstances you can still <a href="install.php?mode=finish">finish the installation</a>, you should be aware that some servers cannot
-                           properly set cookies due to limitations with PHP. These limitations are exposed primarily when this issue is encountered during installation. If you choose
-                           to finish the installation, please be aware that you may be unable to log into your site.',
-      msg_complete_title: 'Installation of Enano is complete.',
-      msg_complete_body: 'Review any warnings above, and then <a href="%finish_link%">click here to finish the installation</a>.',
     },
     finish: {
       modetitle: 'Finish',
       modetitle_long: 'Complete installation',
-      msg_congratulations: 'Congratulations!',
+      
+      heading_progress: 'Performing final installation steps',
+      msg_progress: 'Enano is cleaning up and performing some final installation tasks. Please wait...',
+      msg_success_title: 'Congratulations! You\'ve finished installing Enano.',
+      msg_success_body: 'Enano has finished setting up on your server. Now you can go to your <a href="%mainpage_link%">new website</a> and start creating content!',
+      
       body: '<h3>Wait... Now what?</h3>
              <p>Click the link below to see the main page for your website. Where to go from here:</p>
              <ul>
--- a/language/english/tools.json	Tue Jan 29 17:29:08 2008 -0500
+++ b/language/english/tools.json	Tue Jan 29 23:15:44 2008 -0500
@@ -57,6 +57,7 @@
       err_query_has_stopwords: 'One or more of your search terms was excluded because either it was less than 2 characters in length or is a common word (a stopword) that is typically found on a large number of pages. Examples of stopwords include "the", "this", "which", "with", etc.',
       err_query_dup_terms: 'One or more of your search terms was excluded because duplicate terms were encountered.',
       err_query_term_too_short: 'One or more of your search terms was excluded because terms must be at least 4 characters in length.',
+      err_query_no_positive: 'You need to have at least one keyword in your search query. Searching only for pages not containing a term is not allowed.',
       
       btn_search: 'Search',
       // note the case difference with th_advanced_search
@@ -122,6 +123,8 @@
       tagcloud_instructions: 'Hover your mouse over a tag to see how many pages have the tag. Click on a tag to see a list of the pages that have it.',
       tagcloud_sidebar_title: 'Tag cloud',
       tagcloud_sidebar_btn_larger: 'Larger version',
+      tagcloug_tip_popularity_one: '1 page',
+      tagcloug_tip_popularity_plural: '%popularity% pages',
     },
   }
 };
--- a/language/english/user.json	Tue Jan 29 17:29:08 2008 -0500
+++ b/language/english/user.json	Tue Jan 29 23:15:44 2008 -0500
@@ -44,6 +44,19 @@
       login_success_body_mainpage: 'the main page',
       login_success_short: 'Success.',
       
+      login_noact_title: 'Account error',
+      login_noact_msg_intro: 'It appears that your user account has not yet been activated.',
+      login_noact_solution_none: 'Your account was most likely deactivated by an administrator. Please contact the site administration for further assistance.',
+      login_noact_solution_user: 'Please check your e-mail; you should have been sent a message with instructions on how to activate your account. If you do not receive an e-mail from this site within 24 hours, please contact the site administration for further assistance.',
+      login_noact_solution_admin: 'This website has been configured so that all user accounts must be activated by the administrator before they can be used, so your account will most likely be activated the next time an administrator visits the site.',
+      login_noact_msg_logout_success_title: 'Logged out',
+      login_noact_msg_logout_success_body: 'You have successfully been logged out. All cookies cleared.',
+      login_noact_msg_ask_admins: 'If you are having trouble or did not receive the e-mail, you can request account activation from the administrators of this site.',
+      login_noact_msg_admins_just_asked: 'A request has just been sent to the administrators of this site. They will be able to activate your account or send you another activation e-mail if needed.',
+      login_noact_msg_admins_asked: 'There is an active request in the administrators\' control panel for your account to be activated.',
+      login_noact_btn_request_activation: 'Request account activation',
+      login_noact_btn_log_out: 'Log out',
+      
       login_ajax_fetching_key: 'Fetching an encryption key...',
       login_ajax_prompt_title: 'Please enter your username and password to continue.',
       login_ajax_prompt_title_elev: 'You are requesting a sensitive operation.',
@@ -74,6 +87,22 @@
       // Unused at this point
       logout_err_not_loggedin: 'You don\'t seem to be logged in.',
       
+      // User levels
+      level_short_guest: 'Guest',
+      level_short_member: 'Member',
+      level_short_chpref: 'Sensitive preferences changeable',
+      level_short_mod: 'Moderator',
+      level_short_admin: 'Administrative',
+      level_long_guest: 'Low - guest privileges',
+      level_long_member: 'Standard - normal member level',
+      level_long_chpref: 'Medium - user can change his/her own e-mail address and password',
+      level_long_mod: 'High - moderator privileges',
+      level_long_admin: 'Highest - administrative privileges',
+      
+      ban_msg_title: 'Ban notice',
+      ban_msg_body: 'You have been banned from this website. Please contact the site administrator for more information.',
+      ban_lbl_reason: 'Reason:',
+      
       keepalive_info_title: 'About the keep-alive feature',
       keepalive_info_body: 'Keep-alive is a new Enano feature that keeps your administrative session from timing out while you are using the administration panel. This feature can be useful if you are editing a large page or doing something in the administration interface that will take longer than 15 minutes.<br /><br />For security reasons, Enano mandates that high-privilege logins last only 15 minutes, with the time being reset each time a page is loaded (or, more specifically, each time the session API is started). The consequence of this is that if you are performing an action in the administration panel that takes more than 15 minutes, your session may be terminated. The keep-alive feature attempts to relieve this by sending a "ping" to the server every 10 minutes.<br /><br />Please note that keep-alive state is determined by a cookie. Thus, if you log out and then back in as a different administrator, keep-alive will use the same setting that was used when you were logged in as the first administrative user. In the same way, if you log into the administration panel under your account from another computer, keep-alive will be set to "off".<br /><br /><b>For more information:</b><br /><a href="http://docs.enanocms.org/Help:Appendix_B" onclick="window.open(this.href); return false;">Overview of Enano\'s security model</a>',
       
@@ -410,7 +439,21 @@
       folder_th_date: 'Date',
       folder_th_mark: 'Mark',
       
-      // AJAX interface
+      // Buddy / foe lists
+      th_buddy_list: 'Buddy list for %username%',
+      msg_no_buddies: 'No buddies in your list.',
+      btn_pm_all_buddies: 'Send a PM to all buddies',
+      heading_add_buddy: 'Add a new friend',
+      btn_add: 'Add',
+      lbl_username: 'Username:',
+      btn_buddy_remove: 'Remove',
+      btn_buddy_send_pm: 'Send private message',
+      
+      th_foe_list: 'Foe list for %username%',
+      msg_no_foes: 'No foes in your list.',
+      heading_add_foe: 'Add a new foe',
+      
+      // AJAX interface (up and coming)
       ajax_err_need_js: 'It looks like your browser doesn\'t have support for Javascript. You\'ll need to have Javascript support in order to use the new Private Message interface. You can also switch to the <a href="%basic_link%">old interface</a>, which doesn\'t require Javascript support.',
       ajax_err_json: 'The server had a problem processing your request.',
       
--- a/plugins/PrivateMessages.php	Tue Jan 29 17:29:08 2008 -0500
+++ b/plugins/PrivateMessages.php	Tue Jan 29 23:15:44 2008 -0500
@@ -719,14 +719,14 @@
           $r = $db->fetchrow();
           $db->free_result();
           $q = $db->sql_query('INSERT INTO '.table_prefix.'buddies(user_id,buddy_user_id,is_friend) VALUES('.$session->user_id.', '.$r['user_id'].', 1);');
-          if(!$q) echo '<h3>Warning:</h3><p>Buddy could not be added: '.mysql_error().'</p>';
+          if(!$q) echo '<h3>Warning:</h3><p>Buddy could not be added: '.$db->get_error().'</p>';
           $db->free_result();
         }
       } elseif($argv[1] == 'Remove' && preg_match('#^([0-9]+)$#', $argv[2])) {
         // Using WHERE user_id prevents users from deleting others' buddies
         $q = $db->sql_query('DELETE FROM '.table_prefix.'buddies WHERE user_id='.$session->user_id.' AND buddy_id='.$argv[2].';');
         $db->free_result();
-        if(!$q) echo '<h3>Warning:</h3><p>Buddy could not be deleted: '.mysql_error().'</p>';
+        if(!$q) echo '<h3>Warning:</h3><p>Buddy could not be deleted: '.$db->get_error().'</p>';
         if(mysql_affected_rows() < 1) echo '<h3>Warning:</h3><p>No rows were affected. Either the selected buddy ID does not exist or you tried to delete someone else\'s buddy.</p>';
       }
       $template->header();
@@ -736,15 +736,15 @@
           <tr>
           <td style="padding: 0px; width: 120px;" valign="top"  >
           <div class="tblholder" style="width: 120px;"><table border="0" width="120" cellspacing="1" cellpadding="4">
-          <tr><th><small>Private messages</small></th></tr>
-          <tr><td class="row1"><small><a href="<?php echo $session->append_sid('Inbox'); ?>">Inbox</a>    </small></td></tr>
-          <tr><td class="row2"><small><a href="<?php echo $session->append_sid('Outbox'); ?>">Outbox</a>  </small></td></tr>
-          <tr><td class="row1"><small><a href="<?php echo $session->append_sid('Sent'); ?>">Sent Items</a></small></td></tr>
-          <tr><td class="row2"><small><a href="<?php echo $session->append_sid('Drafts'); ?>">Drafts</a>  </small></td></tr>
-          <tr><td class="row1"><small><a href="<?php echo $session->append_sid('Archive'); ?>">Archive</a></small></td></tr>
-          <tr><th><small>Buddies</small></th></tr>
-          <tr><td class="row2"><small><a href="<?php echo makeUrlNS('Special', 'PrivateMessages/FriendList'); ?>">Friend list</a></small></td></tr>
-          <tr><td class="row1"><small><a href="<?php echo makeUrlNS('Special', 'PrivateMessages/FoeList'); ?>">Foe list</a></small></td></tr>
+          <tr><th><small><?php echo $lang->get('privmsgs_sidebar_th_privmsgs'); ?></small></th></tr>
+          <tr><td class="row1"><small><a href="<?php echo makeUrlNS('Special', 'PrivateMessages/Folder/Inbox'); ?>"><?php echo $lang->get('privmsgs_folder_inbox'); ?></a></small></td></tr>
+          <tr><td class="row2"><small><a href="<?php echo makeUrlNS('Special', 'PrivateMessages/Folder/Outbox'); ?>"><?php echo $lang->get('privmsgs_folder_outbox'); ?></a></small></td></tr>
+          <tr><td class="row1"><small><a href="<?php echo makeUrlNS('Special', 'PrivateMessages/Folder/Sent'); ?>"><?php echo $lang->get('privmsgs_folder_sent'); ?></a></small></td></tr>
+          <tr><td class="row2"><small><a href="<?php echo makeUrlNS('Special', 'PrivateMessages/Folder/Drafts'); ?>"><?php echo $lang->get('privmsgs_folder_drafts'); ?></a></small></td></tr>
+          <tr><td class="row1"><small><a href="<?php echo makeUrlNS('Special', 'PrivateMessages/Folder/Archive'); ?>"><?php echo $lang->get('privmsgs_folder_archive'); ?></a></small></td></tr>
+          <tr><th><small><?php echo $lang->get('privmsgs_sidebar_th_buddies'); ?></small></th></tr>
+          <tr><td class="row2"><small><a href="<?php echo makeUrlNS('Special', 'PrivateMessages/FriendList'); ?>"><?php echo $lang->get('privmsgs_sidebar_friend_list'); ?></a></small></td></tr>
+          <tr><td class="row1"><small><a href="<?php echo makeUrlNS('Special', 'PrivateMessages/FoeList'); ?>"><?php echo $lang->get('privmsgs_sidebar_foe_list'); ?></a></small></td></tr>
           </table></div>
           </td>
           <td valign="top">
@@ -754,26 +754,26 @@
         else 
         {
           $allbuds = '';
-          echo '<br /><div class="tblholder"><table border="0" width="100%" cellspacing="1" cellpadding="4"><tr><th colspan="3">Buddy list for '.$session->username.'</th></tr>';
-          if($db->numrows() < 1) echo '<tr><td class="row3">No buddies in your list.</td></tr>';
+          echo '<br /><div class="tblholder"><table border="0" width="100%" cellspacing="1" cellpadding="4"><tr><th colspan="3">' . $lang->get('privmsgs_th_buddy_list', array('username' => htmlspecialchars($session->username))) . '</th></tr>';
+          if($db->numrows() < 1) echo '<tr><td class="row3">' . $lang->get('privmsgs_msg_no_buddies') . '</td></tr>';
           $cls = 'row2';
           while ( $row = $db->fetchrow() )
           {
             if($cls=='row2') $cls = 'row1';
             else $cls = 'row2';
-            echo '<tr><td class="'.$cls.'"><a href="'.makeUrlNS('User', str_replace(' ', '_', $row['username'])).'" '. ( isPage($paths->nslist['User'].str_replace(' ', '_', $row['username'])) ? '' : 'class="wikilink-nonexistent" ' ) .'>'.$row['username'].'</a></td><td class="'.$cls.'"><a href="'.makeUrlNS('Special', 'PrivateMessages/Compose/to/'.str_replace(' ', '_', $row['username'])).'">Send private message</a></td><td class="'.$cls.'"><a onclick="return confirm(\'Are you sure you want to delete this user from your buddy list?\')" href="'.makeUrlNS('Special', 'PrivateMessages/FriendList/Remove/'.$row['buddy_id']).'">Remove</a></td></tr>';
+            echo '<tr><td class="'.$cls.'"><a href="'.makeUrlNS('User', str_replace(' ', '_', $row['username'])).'" '. ( isPage($paths->nslist['User'].str_replace(' ', '_', $row['username'])) ? '' : 'class="wikilink-nonexistent" ' ) .'>'.$row['username'].'</a></td><td class="'.$cls.'"><a href="'.makeUrlNS('Special', 'PrivateMessages/Compose/to/'.str_replace(' ', '_', $row['username'])).'">' . $lang->get('privmsgs_btn_buddy_send_pm') . '</a></td><td class="'.$cls.'"><a href="'.makeUrlNS('Special', 'PrivateMessages/FriendList/Remove/'.$row['buddy_id']).'">' . $lang->get('privmsgs_btn_buddy_remove') . '</a></td></tr>';
             $allbuds .= str_replace(' ', '_', $row['username']).',';
           }
           $db->free_result();
           $allbuds = substr($allbuds, 0, strlen($allbuds)-1);
           if($cls=='row2') $cls = 'row1';
           else $cls = 'row2';
-          echo '<tr><td colspan="3" class="'.$cls.'" style="text-align: center;"><a href="'.makeUrlNS('Special', 'PrivateMessages/Compose/to/'.$allbuds).'">Send a PM to all buddies</a></td></tr>';
+          echo '<tr><td colspan="3" class="'.$cls.'" style="text-align: center;"><a href="'.makeUrlNS('Special', 'PrivateMessages/Compose/to/'.$allbuds).'">' . $lang->get('privmsgs_btn_pm_all_buddies') . '</a></td></tr>';
           echo '</table></div>';
         }
         echo '<form action="'.makeUrlNS('Special', 'PrivateMessages/FriendList/Add').'" method="post" onsubmit="if(!submitAuthorized) return false;">
-              <h3>Add a new friend</h3>';
-        echo '<p>Username: '.$template->username_field('buddyname').'  <input type="submit" name="_go" value="Add" /></p>';
+              <h3>' . $lang->get('privmsgs_heading_add_buddy') . '</h3>';
+        echo '<p>' . $lang->get('privmsgs_lbl_username') . ' '.$template->username_field('buddyname').'  <input type="submit" name="_go" value="' . $lang->get('privmsgs_btn_add') . '" /></p>';
         echo '</form>';
         ?>
         </td>
@@ -791,61 +791,60 @@
         {
           $r = $db->fetchrow();
           $q = $db->sql_query('INSERT INTO '.table_prefix.'buddies(user_id,buddy_user_id,is_friend) VALUES('.$session->user_id.', '.$r['user_id'].', 0);');
-          if(!$q) echo '<h3>Warning:</h3><p>Buddy could not be added: '.mysql_error().'</p>';
+          if(!$q) echo '<h3>Warning:</h3><p>Buddy could not be added: '.$db->get_error().'</p>';
         }
         $db->free_result();
       } elseif($argv[1] == 'Remove' && preg_match('#^([0-9]+)$#', $argv[2])) {
         // Using WHERE user_id prevents users from deleting others' buddies
         $q = $db->sql_query('DELETE FROM '.table_prefix.'buddies WHERE user_id='.$session->user_id.' AND buddy_id='.$argv[2].';');
         $db->free_result();
-        if(!$q) echo '<h3>Warning:</h3><p>Buddy could not be deleted: '.mysql_error().'</p>';
+        if(!$q) echo '<h3>Warning:</h3><p>Buddy could not be deleted: '.$db->get_error().'</p>';
         if(mysql_affected_rows() < 1) echo '<h3>Warning:</h3><p>No rows were affected. Either the selected buddy ID does not exist or you tried to delete someone else\'s buddy.</p>';
       }
       $template->header();
       userprefs_show_menu();
       ?>
-      <table border="0" width="100%" cellspacing="10" cellpadding="0">
-          <tr>
-          <td style="padding: 0px; width: 120px;" valign="top"  >
-          <div class="tblholder" style="width: 120px;"><table border="0" width="120" cellspacing="1" cellpadding="4">
-          <tr><th><small>Private messages</small></th></tr>
-          <tr><td class="row1"><small><a href="<?php echo $session->append_sid('Inbox'); ?>">Inbox</a>    </small></td></tr>
-          <tr><td class="row2"><small><a href="<?php echo $session->append_sid('Outbox'); ?>">Outbox</a>  </small></td></tr>
-          <tr><td class="row1"><small><a href="<?php echo $session->append_sid('Sent'); ?>">Sent Items</a></small></td></tr>
-          <tr><td class="row2"><small><a href="<?php echo $session->append_sid('Drafts'); ?>">Drafts</a>  </small></td></tr>
-          <tr><td class="row1"><small><a href="<?php echo $session->append_sid('Archive'); ?>">Archive</a></small></td></tr>
-          <tr><th><small>Buddies</small></th></tr>
-          <tr><td class="row2"><small><a href="<?php echo makeUrlNS('Special', 'PrivateMessages/FriendList'); ?>">Friend list</a></small></td></tr>
-          <tr><td class="row1"><small><a href="<?php echo makeUrlNS('Special', 'PrivateMessages/FoeList'); ?>">Foe list</a></small></td></tr>
-          </table></div>
-          </td>
-          <td valign="top">
+        <table border="0" width="100%" cellspacing="10" cellpadding="0">
+        <tr>
+        <td style="padding: 0px; width: 120px;" valign="top"  >
+        <div class="tblholder" style="width: 120px;"><table border="0" width="120" cellspacing="1" cellpadding="4">
+        <tr><th><small><?php echo $lang->get('privmsgs_sidebar_th_privmsgs'); ?></small></th></tr>
+        <tr><td class="row1"><small><a href="<?php echo makeUrlNS('Special', 'PrivateMessages/Folder/Inbox'); ?>"><?php echo $lang->get('privmsgs_folder_inbox'); ?></a></small></td></tr>
+        <tr><td class="row2"><small><a href="<?php echo makeUrlNS('Special', 'PrivateMessages/Folder/Outbox'); ?>"><?php echo $lang->get('privmsgs_folder_outbox'); ?></a></small></td></tr>
+        <tr><td class="row1"><small><a href="<?php echo makeUrlNS('Special', 'PrivateMessages/Folder/Sent'); ?>"><?php echo $lang->get('privmsgs_folder_sent'); ?></a></small></td></tr>
+        <tr><td class="row2"><small><a href="<?php echo makeUrlNS('Special', 'PrivateMessages/Folder/Drafts'); ?>"><?php echo $lang->get('privmsgs_folder_drafts'); ?></a></small></td></tr>
+        <tr><td class="row1"><small><a href="<?php echo makeUrlNS('Special', 'PrivateMessages/Folder/Archive'); ?>"><?php echo $lang->get('privmsgs_folder_archive'); ?></a></small></td></tr>
+        <tr><th><small><?php echo $lang->get('privmsgs_sidebar_th_buddies'); ?></small></th></tr>
+        <tr><td class="row2"><small><a href="<?php echo makeUrlNS('Special', 'PrivateMessages/FriendList'); ?>"><?php echo $lang->get('privmsgs_sidebar_friend_list'); ?></a></small></td></tr>
+        <tr><td class="row1"><small><a href="<?php echo makeUrlNS('Special', 'PrivateMessages/FoeList'); ?>"><?php echo $lang->get('privmsgs_sidebar_foe_list'); ?></a></small></td></tr>
+        </table></div>
+        </td>
+        <td valign="top">
         <?php
         $q = $db->sql_query('SELECT u.username,b.buddy_id FROM '.table_prefix.'buddies AS b LEFT JOIN '.table_prefix.'users AS u ON ( u.user_id=b.buddy_user_id ) WHERE b.user_id='.$session->user_id.' AND is_friend=0;');
         if(!$q) $db->_die('The buddy list could not be selected.');
         else 
         {
           $allbuds = '';
-          echo '<br /><div class="tblholder"><table border="0" width="100%" cellspacing="1" cellpadding="4"><tr><th colspan="3">Foe list for '.$session->username.'</th></tr>';
-          if($db->numrows() < 1) echo '<tr><td class="row2">No foes in your list.</td></tr>';
+          echo '<br /><div class="tblholder"><table border="0" width="100%" cellspacing="1" cellpadding="4"><tr><th colspan="3">' . $lang->get('privmsgs_th_foe_list', array('username' => htmlspecialchars($session->username))) . '</th></tr>';
+          if($db->numrows() < 1) echo '<tr><td class="row3">' . $lang->get('privmsgs_msg_no_foes') . '</td></tr>';
           $cls = 'row2';
           while ( $row = $db->fetchrow() )
           {
             if($cls=='row2') $cls = 'row1';
             else $cls = 'row2';
-            echo '<tr><td class="'.$cls.'"><a href="'.makeUrlNS('User', str_replace(' ', '_', $row['username'])).'" '. ( isPage($paths->nslist['User'].str_replace(' ', '_', $row['username'])) ? '' : 'class="wikilink-nonexistent" ' ) .'>'.$row['username'].'</a></td><td class="'.$cls.'"><a href="'.makeUrlNS('Special', 'PrivateMessages/Compose/to/'.str_replace(' ', '_', $row['username'])).'">Send private message</a></td><td class="'.$cls.'"><a onclick="return confirm(\'Are you sure you want to delete this user from your buddy list?\')" href="'.makeUrlNS('Special', 'PrivateMessages/FriendList/Remove/'.$row['buddy_id']).'">Remove</a></td></tr>';
+            echo '<tr><td class="'.$cls.'"><a href="'.makeUrlNS('User', str_replace(' ', '_', $row['username'])).'" '. ( isPage($paths->nslist['User'].str_replace(' ', '_', $row['username'])) ? '' : 'class="wikilink-nonexistent" ' ) .'>'.$row['username'].'</a></td><td class="'.$cls.'"><a href="'.makeUrlNS('Special', 'PrivateMessages/Compose/to/'.str_replace(' ', '_', $row['username'])).'">' . $lang->get('privmsgs_btn_buddy_send_pm') . '</a></td><td class="'.$cls.'"><a href="'.makeUrlNS('Special', 'PrivateMessages/FoeList/Remove/'.$row['buddy_id']).'">' . $lang->get('privmsgs_btn_buddy_remove') . '</a></td></tr>';
             $allbuds .= str_replace(' ', '_', $row['username']).',';
           }
+          $db->free_result();
           $allbuds = substr($allbuds, 0, strlen($allbuds)-1);
           if($cls=='row2') $cls = 'row1';
           else $cls = 'row2';
-          //echo '<tr><td colspan="3" class="'.$cls.'" style="text-align: center;"><a href="'.makeUrlNS('Special', 'PrivateMessages/Compose/to/'.$allbuds).'">Send a PM to all buddies</a></td></tr>';
           echo '</table></div>';
         }
-        $db->free_result();
         echo '<form action="'.makeUrlNS('Special', 'PrivateMessages/FoeList/Add').'" method="post" onsubmit="if(!submitAuthorized) return false;">
-              <h3>Add a new foe</h3>';
-        echo '<p>Username: '.$template->username_field('buddyname').'  <input type="submit" name="_go" value="Add" /></p>';
+              <h3>' . $lang->get('privmsgs_heading_add_foe') . '</h3>';
+        echo '<p>' . $lang->get('privmsgs_lbl_username') . ' '.$template->username_field('buddyname').'  <input type="submit" name="_go" value="' . $lang->get('privmsgs_btn_add') . '" /></p>';
         echo '</form>';
         ?>
         </td>
--- a/plugins/SpecialAdmin.php	Tue Jan 29 17:29:08 2008 -0500
+++ b/plugins/SpecialAdmin.php	Tue Jan 29 23:15:44 2008 -0500
@@ -946,7 +946,7 @@
     }
     else if ( $_POST['imagemagick_path'] != getConfig('imagemagick_path') )
     {
-      echo '<span style="color: red"><b>Warning:</b> the file "'.htmlspecialchars($_POST['imagemagick_path']).'" was not found, and the ImageMagick file path was not updated.</span>';
+      echo '<span style="color: red">' . $lang->get('acpup_err_magick_not_found', array('magick_path' => htmlspecialchars($_POST['imagemagick_path']))) . '</span>';
     }
     $max_upload = floor((float)$_POST['max_file_size'] * (int)$_POST['fs_units']);
     if ( $max_upload > 1048576 && defined('ENANO_DEMO_MODE') )
--- a/themes/oxygen/footer.tpl	Tue Jan 29 17:29:08 2008 -0500
+++ b/themes/oxygen/footer.tpl	Tue Jan 29 23:15:44 2008 -0500
@@ -12,7 +12,7 @@
                  -->
             <div id="credits">
               <b>{COPYRIGHT}</b><br />
-              Website engine powered by <a href="{URL_ABOUT_ENANO}">Enano</a>&nbsp;&nbsp;|&nbsp;&nbsp;<a href="http://validator.w3.org/check?uri=referer">Valid XHTML 1.1</a>&nbsp;&nbsp;|&nbsp;&nbsp;<a href="http://jigsaw.w3.org/css-validator/validator?uri=referer">Valid CSS</a>&nbsp;&nbsp;|&nbsp;&nbsp;Generated in [[GenTime]]sec<!-- BEGIN auth_admin -->&nbsp;&nbsp;|&nbsp;&nbsp;<a href="{REPORT_URI}">[[NumQueries]] SQL</a><!-- END auth_admin -->
+              [[EnanoPoweredLinkLong]]&nbsp;&nbsp;|&nbsp;&nbsp;<!-- BEGINNOT stupid_mode --><a href="http://validator.w3.org/check?uri=referer">{lang:page_w3c_valid_xhtml11}</a>&nbsp;&nbsp;|&nbsp;&nbsp;<a href="http://jigsaw.w3.org/css-validator/validator?uri=referer">{lang:page_w3c_valid_css}</a>&nbsp;&nbsp;|&nbsp;&nbsp;<!-- END stupid_mode -->[[StatsLong]]
               <!-- Do not remove this line or scheduled tasks will not run. -->
               <img alt=" " src="{SCRIPTPATH}/cron.php" width="1" height="1" />
             </div>