New, beautiful, rethought Admin:Home. No, really, you'll like it.
authorDan
Wed, 15 Apr 2009 19:44:47 -0400
changeset 915 91f4da84966f
parent 914 f808e55fb92a
child 916 05c341ea7545
New, beautiful, rethought Admin:Home. No, really, you'll like it.
includes/functions.php
includes/paths.php
includes/sessions.php
includes/template.php
language/english/admin.json
plugins/SpecialAdmin.php
plugins/admin/Home.php
themes/admin/css/default.css
--- a/includes/functions.php	Wed Apr 15 19:37:10 2009 -0400
+++ b/includes/functions.php	Wed Apr 15 19:44:47 2009 -0400
@@ -2618,6 +2618,35 @@
 }
 
 /**
+ * Converts a number to a human file size.
+ * @param int File size
+ * @return string
+ */
+
+function humanize_filesize($size)
+{
+  global $lang;
+  
+  if ( $size > ( 1099511627776 * 0.9 ) )
+  {
+    return number_format($size / 1099511627776, 1) . $lang->get('etc_unit_terabytes_short');
+  }
+  if ( $size > ( 1073741824 * 0.9 ) )
+  {
+    return number_format($size / 1073741824, 1) . $lang->get('etc_unit_gigabytes_short');
+  }
+  if ( $size > ( 1048576 * 0.9 ) )
+  {
+    return number_format($size / 1048576, 1) . $lang->get('etc_unit_megabytes_short');
+  }
+  if ( $size > ( 1024 * 0.9 ) )
+  {
+    return number_format($size / 1024, 1) . $lang->get('etc_unit_kilobytes_short');
+  }
+  return "$size " . $lang->get('etc_unit_bytes');
+}
+
+/**
  * Injects a string into another string at the specified position.
  * @param string The haystack
  * @param string The needle
--- a/includes/paths.php	Wed Apr 15 19:37:10 2009 -0400
+++ b/includes/paths.php	Wed Apr 15 19:44:47 2009 -0400
@@ -427,6 +427,14 @@
   function sysmsg($n)
   {
     global $db, $session, $paths, $template, $plugins; // Common objects
+    
+    // sometimes this gets called during die_semicritical()...
+    if ( !is_object($db) )
+      return false;
+    
+    if ( !@$db->_conn )
+      return false;
+    
     $q = $db->sql_query('SELECT page_text, char_tag FROM '.table_prefix.'page_text WHERE page_id=\''.$db->escape(sanitize_page_id($n)).'\' AND namespace=\'System\'');
     if( !$q )
     {
--- a/includes/sessions.php	Wed Apr 15 19:37:10 2009 -0400
+++ b/includes/sessions.php	Wed Apr 15 19:44:47 2009 -0400
@@ -1424,7 +1424,6 @@
     $lang = new Language($language);
     @setlocale(LC_ALL, $lang->lang_code);
     
-    $this->logout();
     $a = getConfig('account_activation');
     switch($a)
     {
--- a/includes/template.php	Wed Apr 15 19:37:10 2009 -0400
+++ b/includes/template.php	Wed Apr 15 19:44:47 2009 -0400
@@ -3170,6 +3170,11 @@
     else
       $this->tpl_strings = $vars;
   }
+  
+  function get_theme_hook()
+  {
+    return '';
+  }
    
 } // class template_nodb
 
--- a/language/english/admin.json	Wed Apr 15 19:37:10 2009 -0400
+++ b/language/english/admin.json	Wed Apr 15 19:44:47 2009 -0400
@@ -214,17 +214,43 @@
       inherit_key_local_user: '%this.acl_inherit_local_user% (most specific)',
     },
     acphome: {
-      heading_main: 'Welcome to Runt, the Enano administration panel.',
-      welcome_line1: 'Thank you for choosing Enano as your CMS. This screen allows you to see some information about your website, plus some details about how your site is doing statistically.',
-      welcome_line2: 'Using the links on the left you can control every aspect of your website\'s look and feel, plus you can manage users, work with pages, and install plugins to make your Enano installation even better.',
+      heading_main: 'Welcome to the Administration Dashboard.',
+      welcome_line1: 'Thanks for choosing Enano as your site\'s CMS! The administration panel gives you control over nearly every aspect of your site. The links to the left allow you to navigate around the panel. Below you will find some statistics on your site and any alerts you might need to attend to.',
+      
+      // Stats!
+      stat_header: 'Site statistics',
+      stat_enano_version: 'Enano CMS %version% (%releasename%)<br /><a href="%aboutlink%">View server information and license</a>',
+      stat_numpages: 'Number of pages:',
+      stat_edits: 'Number of edits made:',
+      stat_edits_data: '%edit_count% (average of %per_day% per day)',
+      stat_comments: 'Number of comments:',
+      stat_comments_data: '%comment_count% (average of %per_day% per day)',
+      stat_users: 'Number of users:',
+      stat_filesize: 'Total size of uploaded files:',
+      stat_cachesize: 'Total size of cache:',
+      stat_avatarsize: 'Space occupied by avatars:',
+      stat_dbsize: 'Database size:',
+      stat_dbsize_unsupported: 'Unsupported',
+      stat_installdate: 'Site started:',
+      stat_lastupdate: 'Last Enano upgrade:',
+      stat_lastupdate_never: 'Never',
+      
+      heading_alerts: 'Active alerts',
       msg_demo_title: 'Enano is running in demo mode.',
       msg_demo_body: 'If you borked something up, or if you\'re done testing, you can <a href="%reset_url%">reset this site</a>. The site is reset automatically once every two hours. When a reset is performed, all custom modifications to the site are lost and replaced with default values.',
-      msg_install_files: '<b>NOTE:</b> It appears that the install/ directory still exists in your Enano installation root. Even though sensitive tools try to avoid hacking attempts, it is highly recommended that you remove the entire install directory from your server for security reasons.',
+      msg_install_files_title: 'Installer files found',
+      msg_install_files_body: 'Please delete the install/ directory from your Enano installation folder &ndash; it contains sensitive tools that might allow your site to be compromised.',
       heading_updates: 'Check for updates',
-      msg_updates_info: 'Periodically, new releases of Enano will be made available. Click the button below to check for updates to Enano. During this process, a request will be sent to the Enano CMS server (ktulu.enanocms.org) over HTTP for an <a href="%updates_url%">XML file</a> containing a list of the latest releases. No information about your Enano installation will be transmitted.',
+      msg_updates_info: 'The Enano team will on occasion release new versions of Enano. We always recommend that you run the latest available version because many releases contain security patches. Enano checks for updates by looking at an <a href="%updates_url%">XML file</a> and doesn\'t share any information about your site.',
       btn_check_updates: 'Check for updates',
-      msg_inactive_users_one: 'It appears that 1 user is awaiting account activation. You can activate the account by going to the <a %um_flags%>User Manager</a>.',
-      msg_inactive_users_plural: 'It appears that %num_users% users are awaiting account activation. You can activate those accounts by going to the <a %um_flags%>User Manager</a>.',
+      heading_inactive_users: 'Users are awaiting activation',
+      msg_inactive_users_one: '1 user has requested manual account activation. You can activate the account by going to the <a %um_flags%>User Manager</a>.',
+      msg_inactive_users_plural: '%num_users% users have requested manual account activation. You can activate those accounts by going to the <a %um_flags%>User Manager</a>.',
+      heading_docs: 'Enano documentation',
+      msg_docs_info: 'The <a href="http://docs.enanocms.org/" onclick="window.open(this.href); return false;">Enano administrator\'s handbook</a> is maintained as a wiki. It will help you get started with Enano and learn about how we do things differently.',
+      heading_support: 'Get support',
+      msg_support_info: 'Stuck? Think you found a bug? Tell us about it! Post a message in the <a href="http://forum.enanocms.org/" onclick="window.open(this.href); return false;">Enano support forums</a> to obtain community support for Enano. You may also be interested in our <a href="http://enanocms.org/Support" onclick="window.open(this.href); return false;">other support channels</a>.',
+      
       heading_top_pages: 'Most requested pages',
       th_toppages_page: 'Page',
       th_toppages_hits: 'Hits',
--- a/plugins/SpecialAdmin.php	Wed Apr 15 19:37:10 2009 -0400
+++ b/plugins/SpecialAdmin.php	Wed Apr 15 19:44:47 2009 -0400
@@ -73,6 +73,7 @@
   list($pid, $ns) = RenderMan::strToPageID($paths->get_pageid_from_url());
   if ( $ns == 'Admin' || ( $pid == 'Administration' && $ns == 'Special' ) )
   {
+    require(ENANO_ROOT . '/plugins/admin/Home.php');
     require(ENANO_ROOT . '/plugins/admin/PageManager.php');
     require(ENANO_ROOT . '/plugins/admin/PageEditor.php');
     require(ENANO_ROOT . '/plugins/admin/PageGroups.php');
@@ -96,139 +97,6 @@
 
 // function names are IMPORTANT!!! The name pattern is: page_<namespace ID>_<page URLname, without namespace>
 
-function page_Admin_Home() {
-  global $db, $session, $paths, $template, $plugins; // Common objects
-  global $lang;
-  if ( $session->auth_level < USER_LEVEL_ADMIN || $session->user_level < USER_LEVEL_ADMIN )
-  {
-    $login_link = makeUrlNS('Special', 'Login/' . $paths->nslist['Special'] . 'Administration', 'level=' . USER_LEVEL_ADMIN, true);
-    echo '<h3>' . $lang->get('adm_err_not_auth_title') . '</h3>';
-    echo '<p>' . $lang->get('adm_err_not_auth_body', array( 'login_link' => $login_link )) . '</p>';
-    return;
-  }
-  
-  if ( $paths->getParam(0) == 'updates.xml' )
-  {
-    require_once(ENANO_ROOT . '/includes/http.php');
-    $req = new Request_HTTP('ktulu.enanocms.org', '/meta/updates.xml');
-    $response = $req->get_response_body();
-    header('Content-type: application/xml');
-    if ( $req->response_code != HTTP_OK )
-    {
-      // Error in response
-      echo '<enano><latest><error><![CDATA[
-Did not properly receive response from server. Response code: ' . $req->response_code . ' ' . $req->response_string . '
-]]></error></latest></enano>';
-    }
-    else
-    {
-      // Retrieve first update
-      $first_update = preg_match('/<release tag="([^"]+)" version="([^"]+)" (codename="([^"]+)" )?relnotes="([^"]+)" ?\/>/', $response, $match);
-      if ( !$first_update )
-      {
-        echo '<enano><latest><error><![CDATA[
-Received invalid XML response.
-]]></error></latest></enano>';
-      }
-      else
-      {
-        if ( version_compare(enano_version(true), $match[2], '<') )
-        {
-          $response = str_replace_once('</latest>', "  <haveupdates />\n  </latest>", $response);
-        }
-        echo $response;
-      }
-    }
-    return;
-  }
-  
-  // Basic information
-  echo '<h2>' . $lang->get('acphome_heading_main') . '</h2>';
-  echo '<p>' . $lang->get('acphome_welcome_line1') . '</p>';
-  echo '<p>' . $lang->get('acphome_welcome_line2') . '</p>';
-  
-  // Demo mode
-  if ( defined('ENANO_DEMO_MODE') )
-  {
-    echo '<h3>' . $lang->get('acphome_msg_demo_title') . '</h3>
-          <p>' . $lang->get('acphome_msg_demo_body', array('reset_url' => makeUrlNS('Special', 'DemoReset', false, true))) . '</p>';
-  }
-  
-  // Check for the installer scripts
-  if( file_exists(ENANO_ROOT.'/install/install.php') && !defined('ENANO_DEMO_MODE') )
-  {
-    echo '<div class="error-box">
-            ' . $lang->get('acphome_msg_install_files') . '
-          </div>';
-  }
-  
-  echo '<h3>' . $lang->get('acphome_heading_updates') . '</h3>';
-  echo '<p>' . $lang->get('acphome_msg_updates_info', array('updates_url' => 'http://ktulu.enanocms.org/meta/updates.xml')) . '</p>';
-  echo '<div id="update_check_container"><input type="button" onclick="ajaxUpdateCheck(this.parentNode.id);" value="' . $lang->get('acphome_btn_check_updates') . '" /></div>';
-  
-  // Inactive users
-  $q = $db->sql_query('SELECT time_id FROM '.table_prefix.'logs WHERE log_type=\'admin\' AND action=\'activ_req\';');
-  if ( $q )
-  {
-    if ( $db->numrows() > 0 )
-    {
-      $n = $db->numrows();
-      $um_flags = 'href="#" onclick="ajaxPage(\''.$paths->nslist['Admin'].'UserManager\'); return false;"';
-      if ( $n == 1 )
-        $s = $lang->get('acphome_msg_inactive_users_one', array('um_flags' => $um_flags));
-      else
-        $s = $lang->get('acphome_msg_inactive_users_plural', array('um_flags' => $um_flags));
-      echo '<div class="warning-box">
-              ' . $s . '
-            </div>';
-    }
-  }
-  $db->free_result();
-  // Stats
-  if(getConfig('log_hits') == '1')
-  {
-    require_once(ENANO_ROOT . '/includes/stats.php');
-    $stats = stats_top_pages(10);
-    //die('<pre>'.print_r($stats,true).'</pre>');
-    $c = 0;
-    $cls = 'row2';
-    echo '<h3>' . $lang->get('acphome_heading_top_pages') . '</h3>
-          <div class="tblholder">
-            <table style="width: 100%;" border="0" cellspacing="1" cellpadding="4">
-              <tr>
-                <th>' . $lang->get('acphome_th_toppages_page') . '</th>
-                <th>' . $lang->get('acphome_th_toppages_hits') . '</th>
-              </tr>';
-    foreach($stats as $data)
-    {
-      echo   '<tr>';
-      $cls = ( $cls == 'row1' ) ? 'row2' : 'row1';
-      echo     '<td class="'.$cls.'">
-                  <a href="'.makeUrl($data['page_urlname']).'">'.$data['page_title'].'</a></td><td style="text-align: center;" class="'.$cls.'">'.$data['num_hits']
-             . '</td>';
-      echo   '</tr>';
-    }
-    echo '  </table>
-          </div>';
-  }
-  
-  // Any hooks?
-  $code = $plugins->setHook('acp_home');
-  foreach ( $code as $cmd )
-  {
-    eval($cmd);
-  }
-  
-  // Security log
-  echo '<h3>' . $lang->get('acphome_heading_seclog') . '</h3>';
-  echo '<p>' . $lang->get('acphome_msg_seclog_info') . '</p>';
-  $seclog = get_security_log(5);
-  echo $seclog;
-  
-  echo '<p><a href="#" onclick="ajaxPage(\''.$paths->nslist['Admin'].'SecurityLog\'); return false;">' . $lang->get('acphome_btn_seclog_full') . '</a></p>';
-  
-}
-
 function page_Admin_GeneralConfig() {
   global $db, $session, $paths, $template, $plugins; // Common objects
   global $lang;
@@ -390,21 +258,7 @@
       setConfig('gravatar_rating', $_POST['gravatar_rating']);
     }
     
-    if ( is_dir(ENANO_ROOT . '/' . $_POST['avatar_directory']) )
-    {
-      if ( preg_match('/^[A-z0-9_-]+(?:\/(?:[A-z0-9_-]+))*\/?$/', $_POST['avatar_directory']) )
-      {
-        setConfig('avatar_directory', $_POST['avatar_directory']);
-      }
-      else
-      {
-        echo '<div class="error-box">' . $lang->get('acpgc_err_avatar_dir_invalid') . '</div>';
-      }
-    }
-    else
-    {
-      echo '<div class="error-box">' . $lang->get('acpgc_err_avatar_dir_not_exist') . '</div>';
-    }
+    setConfig('avatar_directory', 'files/avatars');
     
     setConfig('userpage_grant_acl', ( isset($_POST['userpage_grant_acl']) ? '1' : '0' ));
     
@@ -1028,16 +882,6 @@
         </td>
       </tr>
       
-      <tr>
-        <td class="row2">
-          <?php echo $lang->get('acpgc_field_avatar_directory'); ?><br />
-          <small><?php echo $lang->get('acpgc_field_avatar_directory_hint'); ?></small>
-        </td>
-        <td class="row2">
-          <input type="text" name="avatar_directory" size="30" <?php if ( $x = getConfig('avatar_directory') ) echo "value=\"$x\" "; else echo "value=\"files/avatars\" "; ?>/>
-        </td>
-      </tr>
-      
     <!-- Misc. options -->
     
       <tr>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/plugins/admin/Home.php	Wed Apr 15 19:44:47 2009 -0400
@@ -0,0 +1,419 @@
+<?php
+
+/*
+ * Enano - an open-source CMS capable of wiki functions, Drupal-like sidebar blocks, and everything in between
+ * Version 1.1.6 (Caoineag beta 1)
+ * Copyright (C) 2006-2008 Dan Fuhry
+ *
+ * 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.
+ */
+
+function page_Admin_Home()
+{
+  global $db, $session, $paths, $template, $plugins; // Common objects
+  global $lang;
+  if ( $session->auth_level < USER_LEVEL_ADMIN || $session->user_level < USER_LEVEL_ADMIN )
+  {
+    $login_link = makeUrlNS('Special', 'Login/' . $paths->nslist['Special'] . 'Administration', 'level=' . USER_LEVEL_ADMIN, true);
+    echo '<h3>' . $lang->get('adm_err_not_auth_title') . '</h3>';
+    echo '<p>' . $lang->get('adm_err_not_auth_body', array( 'login_link' => $login_link )) . '</p>';
+    return;
+  }
+  
+  if ( $paths->getParam(0) == 'updates.xml' )
+  {
+    return acphome_process_updates();
+  }
+  
+  // Welcome
+  echo '<h2>' . $lang->get('acphome_heading_main') . '</h2>';
+  echo '<p>' . $lang->get('acphome_welcome_line1') . '</p>';
+  
+  // Stats
+  acphome_show_stats();
+  
+  //
+  // Alerts
+  //
+  
+  echo '<h3>' . $lang->get('acphome_heading_alerts') . '</h3>';
+  
+  // Demo mode
+  if ( defined('ENANO_DEMO_MODE') )
+  {
+    echo '<div class="acphome-box info">';
+      echo '<h3>' . $lang->get('acphome_msg_demo_title') . '</h3>
+            <p>' . $lang->get('acphome_msg_demo_body', array('reset_url' => makeUrlNS('Special', 'DemoReset', false, true))) . '</p>';
+    echo '</div>';
+  }
+  
+  // Check for the installer scripts
+  if( file_exists(ENANO_ROOT.'/install/install.php') && !defined('ENANO_DEMO_MODE') )
+  {
+    echo '<div class="acphome-box warning">
+            <h3>' . $lang->get('acphome_msg_install_files_title') . '</h3>
+            <p>' . $lang->get('acphome_msg_install_files_body') . '</p>
+          </div>';
+  }
+  
+  // Inactive users
+  $q = $db->sql_query('SELECT time_id FROM '.table_prefix.'logs WHERE log_type=\'admin\' AND action=\'activ_req\';');
+  if ( $q )
+  {
+    if ( $db->numrows() > 0 )
+    {
+      $n = $db->numrows();
+      $um_flags = 'href="#" onclick="ajaxPage(\''.$paths->nslist['Admin'].'UserManager\'); return false;"';
+      if ( $n == 1 )
+        $s = $lang->get('acphome_msg_inactive_users_one', array('um_flags' => $um_flags));
+      else
+        $s = $lang->get('acphome_msg_inactive_users_plural', array('um_flags' => $um_flags));
+      echo '<div class="acphome-box notice">
+              <h3>' . $lang->get('acphome_heading_inactive_users') . '</h3>
+              ' . $s . '
+            </div>';
+    }
+  }
+  $db->free_result();
+  
+  // Update checker
+  echo '<div class="acphome-box info">';
+    echo '<h3>' . $lang->get('acphome_heading_updates') . '</h3>';
+    echo '<p>' . $lang->get('acphome_msg_updates_info', array('updates_url' => 'http://ktulu.enanocms.org/meta/updates.xml')) . '</p>';
+    echo '<div id="update_check_container"><input type="button" onclick="ajaxUpdateCheck(this.parentNode.id);" value="' . $lang->get('acphome_btn_check_updates') . '" /></div>';
+  echo '</div>';
+  
+  // Docs
+  echo '<div class="acphome-box info halfwidth">';
+  echo '<h3>' . $lang->get('acphome_heading_docs') . '</h3>';
+  echo '<p>' . $lang->get('acphome_msg_docs_info') . '</p>';
+  echo '</div>';
+  
+  // Support
+  echo '<div class="acphome-box info halfwidth">';
+  echo '<h3>' . $lang->get('acphome_heading_support') . '</h3>';
+  echo '<p>' . $lang->get('acphome_msg_support_info') . '</p>';
+  echo '</div>';
+  
+  echo '<span class="menuclear"></span>';
+  
+  //
+  // Stats
+  //
+  
+  if(getConfig('log_hits') == '1')
+  {
+    require_once(ENANO_ROOT . '/includes/stats.php');
+    $stats = stats_top_pages(10);
+    //die('<pre>'.print_r($stats,true).'</pre>');
+    $c = 0;
+    $cls = 'row2';
+    echo '<h3>' . $lang->get('acphome_heading_top_pages') . '</h3>
+          <div class="tblholder">
+            <table style="width: 100%;" border="0" cellspacing="1" cellpadding="4">
+              <tr>
+                <th>' . $lang->get('acphome_th_toppages_page') . '</th>
+                <th>' . $lang->get('acphome_th_toppages_hits') . '</th>
+              </tr>';
+    foreach($stats as $data)
+    {
+      echo   '<tr>';
+      $cls = ( $cls == 'row1' ) ? 'row2' : 'row1';
+      echo     '<td class="'.$cls.'">
+                  <a href="'.makeUrl($data['page_urlname']).'">'.$data['page_title'].'</a></td><td style="text-align: center;" class="'.$cls.'">'.$data['num_hits']
+             . '</td>';
+      echo   '</tr>';
+    }
+    echo '  </table>
+          </div>';
+  }
+  
+  // Any hooks?
+  $code = $plugins->setHook('acp_home');
+  foreach ( $code as $cmd )
+  {
+    eval($cmd);
+  }
+  
+  //
+  // Security log
+  //
+  
+  echo '<h3>' . $lang->get('acphome_heading_seclog') . '</h3>';
+  echo '<p>' . $lang->get('acphome_msg_seclog_info') . '</p>';
+  $seclog = get_security_log(5);
+  echo $seclog;
+  
+  echo '<p><a href="#" onclick="ajaxPage(\''.$paths->nslist['Admin'].'SecurityLog\'); return false;">' . $lang->get('acphome_btn_seclog_full') . '</a></p>';
+  
+}
+
+function acphome_process_updates()
+{
+  require_once(ENANO_ROOT . '/includes/http.php');
+  
+  $req = new Request_HTTP('ktulu.enanocms.org', '/meta/updates.xml');
+  $response = $req->get_response_body();
+  header('Content-type: application/xml');
+  if ( $req->response_code != HTTP_OK )
+  {
+    // Error in response
+    echo '<enano><latest><error><![CDATA[
+Did not properly receive response from server. Response code: ' . $req->response_code . ' ' . $req->response_string . '
+]]></error></latest></enano>';
+  }
+  else
+  {
+    // Retrieve first update
+    $first_update = preg_match('/<release tag="([^"]+)" version="([^"]+)" (codename="([^"]+)" )?relnotes="([^"]+)" ?\/>/', $response, $match);
+    if ( !$first_update )
+    {
+      echo '<enano><latest><error><![CDATA[
+Received invalid XML response.
+]]></error></latest></enano>';
+    }
+    else
+    {
+      if ( version_compare(enano_version(true), $match[2], '<') )
+      {
+        $response = str_replace_once('</latest>', "  <haveupdates />\n  </latest>", $response);
+      }
+      echo $response;
+    }
+  }
+  return true;
+}
+
+function acphome_show_stats()
+{
+  global $db, $session, $paths, $template, $plugins; // Common objects
+  global $lang;
+  
+  // Page count
+  $q = $db->sql_query('SELECT COUNT(*) FROM ' . table_prefix . "pages");
+  if ( !$q )
+    $db->_die();
+  list($page_count) = $db->fetchrow_num();
+  $db->free_result();
+  
+  // Edits per day
+  $q = $db->sql_query('SELECT ( COUNT(*) - 1 ) AS edit_count, MIN(time_id) AS install_date FROM ' . table_prefix . 'logs WHERE ( log_type = \'page\' AND action = \'edit\' ) OR ( log_type = \'security\' AND action = \'install_enano\' );');
+  if ( !$q )
+    $db->_die();
+  $edit_info = $db->fetchrow();
+  $install_date =& $edit_info['install_date'];
+  $db->free_result();
+  
+  $days_installed = round( (time() / 86400) - ($install_date / 86400) );
+  
+  // Comments
+  $q = $db->sql_query('SELECT COUNT(*) FROM ' . table_prefix . "comments");
+  if ( !$q )
+    $db->_die();
+  list($comment_count) = $db->fetchrow_num();
+  $db->free_result();
+  
+  // Users
+  $q = $db->sql_query('SELECT ( COUNT(*) - 1 ) FROM ' . table_prefix . "users");
+  if ( !$q )
+    $db->_die();
+  list($user_count) = $db->fetchrow_num();
+  $db->free_result();
+  
+  // Cache size
+  $cache_size = 0;
+  if ( $dr = @opendir(ENANO_ROOT . '/cache/') )
+  {
+    while ( $dh = @readdir($dr) )
+    {
+      $file = ENANO_ROOT . "/cache/$dh";
+      if ( @is_file($file) )
+        $cache_size += filesize($file);
+    }
+    closedir($dr);
+  }
+  $cache_size = humanize_filesize($cache_size);
+  
+  // Files directory size
+  $files_size = 0;
+  if ( $dr = @opendir(ENANO_ROOT . '/files/') )
+  {
+    while ( $dh = @readdir($dr) )
+    {
+      $file = ENANO_ROOT . "/files/$dh";
+      if ( @is_file($file) )
+        $files_size += filesize($file);
+    }
+    closedir($dr);
+  }
+  $files_size = humanize_filesize($files_size);
+  
+  // Avatar directory size
+  $avatar_size = 0;
+  if ( $dr = @opendir(ENANO_ROOT . '/files/avatars/') )
+  {
+    while ( $dh = @readdir($dr) )
+    {
+      $file = ENANO_ROOT . "/files/avatars/$dh";
+      if ( @is_file($file) )
+        $avatar_size += filesize($file);
+    }
+    closedir($dr);
+  }
+  $avatar_size = humanize_filesize($avatar_size);
+  
+  // Database size
+  $db_size = $lang->get('acphome_stat_dbsize_unsupported');
+  if ( ENANO_DBLAYER == 'MYSQL' )
+  {
+    $q = $db->sql_query('SHOW TABLE STATUS;');
+    if ( $q )
+    {
+      $db_size = 0;
+      while ( $row = $db->fetchrow() )
+      {
+        if ( preg_match('/^' . table_prefix . '/', $row['Name']) )
+        {
+          $db_size += $row['Data_length'] + $row['Index_length'];
+        }
+      }
+      $db_size = humanize_filesize($db_size);
+    }
+  }
+  else if ( ENANO_DBLAYER == 'PGSQL' )
+  {
+    require(ENANO_ROOT . '/config.php');
+    global $dbname, $dbuser, $dbpasswd;
+    $dbuser = false;
+    $dbpasswd = false;
+    
+    $q = $db->sql_query('SELECT pg_database_size(\'' . $db->escape($dbname) . '\');');
+    if ( $q )
+    {
+      list($db_size) = $db->fetchrow_num();
+      $db_size = humanize_filesize($db_size);
+      $db->free_result();
+    }
+  }
+  
+  // Install date
+  $install_date_human = MemberlistFormatter::format_date($install_date);
+  
+  // Last upgrade
+  $q = $db->sql_query('SELECT time_id FROM ' . table_prefix . "logs WHERE log_type = 'security' AND action = 'upgrade_enano' ORDER BY time_id DESC LIMIT 1;");
+  if ( !$q )
+    $db->_die();
+  
+  if ( $db->numrows() < 1 )
+  {
+    $last_upgrade = $lang->get('acphome_stat_lastupdate_never');
+  }
+  else
+  {
+    list($last_upgrade) = $db->fetchrow_num();
+    $last_upgrade = MemberlistFormatter::format_date($last_upgrade);
+  }
+  $db->free_result();
+  
+  ?>
+  <div class="tblholder">
+  <table border="0" cellspacing="1" cellpadding="4">
+    <tr>
+      <th colspan="4">
+        <?php echo $lang->get('acphome_stat_header'); ?>
+      </th>
+    </tr>
+    
+    <tr>
+      <td class="row2" style="width: 25%;">
+        <?php echo $lang->get('acphome_stat_numpages'); ?>
+      </td>
+      <td class="row1" style="width: 25%;">
+        <?php echo strval($page_count); ?>
+      </td>
+      <td class="row2" style="width: 25%;">
+        <?php echo $lang->get('acphome_stat_edits'); ?>
+      </td>
+      <td class="row1" style="width: 25%;">
+        <?php echo $lang->get('acphome_stat_edits_data', array('edit_count' => $edit_info['edit_count'], 'per_day' => number_format($edit_info['edit_count'] / $days_installed, 2))); ?>
+      </td>
+    </tr>
+    
+    <tr>
+      <td class="row2" style="width: 25%;">
+        <?php echo $lang->get('acphome_stat_comments'); ?>
+      </td>
+      <td class="row1" style="width: 25%;">
+        <?php echo $lang->get('acphome_stat_comments_data', array('comment_count' => $comment_count, 'per_day' => number_format($comment_count / $days_installed, 2))); ?>
+      </td>
+      <td class="row2" style="width: 25%;">
+        <?php echo $lang->get('acphome_stat_users'); ?>
+      </td>
+      <td class="row1" style="width: 25%;">
+        <?php echo strval($user_count); ?>
+      </td>
+    </tr>
+    
+    <tr>
+      <td class="row2" style="width: 25%;">
+        <?php echo $lang->get('acphome_stat_filesize'); ?>
+      </td>
+      <td class="row1" style="width: 25%;">
+        <?php echo $files_size; ?>
+      </td>
+      <td class="row2" style="width: 25%;">
+        <?php echo $lang->get('acphome_stat_cachesize'); ?>
+      </td>
+      <td class="row1" style="width: 25%;">
+        <?php echo $cache_size; ?>
+      </td>
+    </tr>
+    
+    <tr>
+      <td class="row2" style="width: 25%;">
+        <?php echo $lang->get('acphome_stat_avatarsize'); ?>
+      </td>
+      <td class="row1" style="width: 25%;">
+        <?php echo $avatar_size; ?>
+      </td>
+      <td class="row2" style="width: 25%;">
+        <?php echo $lang->get('acphome_stat_dbsize'); ?>
+      </td>
+      <td class="row1" style="width: 25%;">
+        <?php echo $db_size; ?>
+      </td>
+    </tr>
+    
+    <tr>
+      <td class="row2" style="width: 25%;">
+        <?php echo $lang->get('acphome_stat_installdate'); ?>
+      </td>
+      <td class="row1" style="width: 25%;">
+        <?php echo $install_date_human; ?>
+      </td>
+      <td class="row2" style="width: 25%;">
+        <?php echo $lang->get('acphome_stat_lastupdate'); ?>
+      </td>
+      <td class="row1" style="width: 25%;">
+        <?php echo $last_upgrade; ?>
+      </td>
+    </tr>
+    
+    <tr>
+      <th colspan="4" class="subhead systemversion">
+        <?php echo $lang->get('acphome_stat_enano_version', array(
+            'version' => enano_version(true),
+            'releasename' => enano_codename(),
+            'aboutlink' => makeUrlNS('Special', 'About_Enano')
+          )); ?>
+      </th>
+    </tr>
+    
+  </table>
+  </div>
+  <?php
+}
--- a/themes/admin/css/default.css	Wed Apr 15 19:37:10 2009 -0400
+++ b/themes/admin/css/default.css	Wed Apr 15 19:44:47 2009 -0400
@@ -199,3 +199,62 @@
   color: #888;
 }
 
+/* ACP home elements */
+
+div.acphome-box {
+  border-radius: 4px;
+  -moz-border-radius: 4px;
+  padding: 14px 11px;
+  margin: 4px;
+}
+
+div.acphome-box.halfwidth {
+  padding: 1.5%;
+  margin: 4px 0.5%;
+  float: left;
+  width: 46%;
+}
+
+div.acphome-box h3 {
+  text-decoration: underline;
+  margin: 0 0 10px 0;
+}
+
+div.acphome-box p {
+  margin: 0 0 10px 0;
+}
+
+div.acphome-box.warning {
+  background-color: #900000;
+  color: #fff;
+  text-align: center;
+  font-weight: bold;
+}
+
+div.acphome-box.info {
+  background-color: #f2f4fe;
+  color: #202020;
+}
+
+div.acphome-box.notice {
+  background-color: #009000;
+  color: #fff;
+}
+
+div.acphome-box.warning a:link, div.acphome-box.notice a:link {
+  color: #eee;
+}
+
+div.acphome-box.warning a:hover, div.acphome-box.notice a:hover {
+  color: #fff !important;
+}
+
+th.systemversion a {
+  font-weight: normal;
+  color: #fff !important;
+}
+
+th.systemversion a:hover {
+  font-weight: normal;
+  color: #ff0 !important;
+}