Added ability to detag deleted pages
authorDan
Tue, 14 Aug 2007 15:13:40 -0400
changeset 91 8079b0288e8e
parent 90 9d29f7e101d6
child 92 aa8ffe41d1c4
Added ability to detag deleted pages
ajax.php
includes/clientside/css/enano-shared.css
includes/dbal.php
includes/functions.php
includes/pageprocess.php
includes/render.php
includes/template.php
index.php
install.php
plugins/SpecialPageFuncs.php
schema.sql
themes/oxygen/css/bleu.css
themes/oxygen/css/mint.css
themes/oxygen/footer.tpl
upgrade.php
upgrade.sql
--- a/includes/clientside/css/enano-shared.css	Sun Aug 12 14:56:52 2007 -0400
+++ b/includes/clientside/css/enano-shared.css	Tue Aug 14 15:13:40 2007 -0400
@@ -150,6 +150,8 @@
   padding-right: 5px;
 }
 
+div.breadcrumbs                   { margin: 10px 0; padding: 5px; border: 1px solid #AAAAAA; background-color: #E8E8E8; font-size: smaller; font-weight: bold; }
+
 /* Tables */
 .tblholder                        { margin: 10px 0 0 0; padding: 0; border: 1px solid #AAAAAA; background-color: #E8E8E8; }
 
--- a/includes/dbal.php	Sun Aug 12 14:56:52 2007 -0400
+++ b/includes/dbal.php	Tue Aug 14 15:13:40 2007 -0400
@@ -83,8 +83,17 @@
     $bt = $this->latest_query; // $this->sql_backtrace();
     $e = htmlspecialchars(mysql_error());
     if($e=='') $e='<none>';
-    if(defined('ENANO_CONFIG_FETCHED')) die_semicritical('Database error', '<h3>An error occurred during a database query.</h3><p>'.$t.'<br />Error returned by MySQL: '.$e.'<br />SQL Backtrace:</p><pre>'.$bt.'</pre>');
-    else                                   grinding_halt('Database error', '<h3>An error occurred during a database query.</h3><p>'.$t.'<br />Error returned by MySQL: '.$e.'<br />SQL Backtrace:</p><pre>'.$bt.'</pre>');
+    $t = ( !empty($t) ) ? $t : '&lt;No error description provided&gt;';
+    global $email;
+    $email_info = ( defined('ENANO_CONFIG_FETCHED') && is_object($email) ) ? ', at &lt;' . $email->jscode() . $email->encryptEmail(getConfig('contact_email')) . '&gt;' : '';
+    $internal_text = '<h3>The site was unable to finish serving your request.</h3>
+                      <p>We apologize for the inconveience, but an error occurred in the Enano database layer. Please report the full text of this page to the administrator of this site' . $email_info . '.</p>
+                      <p>Description or location of error: '.$t.'<br />
+                      Error returned by MySQL extension: ' . $e . '<br />
+                      Most recent SQL query:</p>
+                      <pre>'.$bt.'</pre>';
+    if(defined('ENANO_CONFIG_FETCHED')) die_semicritical('Database error', $internal_text);
+    else                                   grinding_halt('Database error', $internal_text);
     exit;
   }
   
@@ -101,8 +110,15 @@
     $bt = $this->sql_backtrace();
     $e = htmlspecialchars(mysql_error());
     if($e=='') $e='&lt;none&gt;';
-    $text = '<h3>An error occurred during a database query.</h3><p>'.$t.'<br />Error returned by MySQL: '.$e.'<br />SQL Backtrace:</p><pre>'.$bt.'</pre>';
-    return $text;
+    global $email;
+    $email_info = ( defined('ENANO_CONFIG_FETCHED') && is_object($email) ) ? ', at &lt;' . $email->jscode() . $email->encryptEmail(getConfig('contact_email')) . '&gt;' : '';
+    $internal_text = '<h3>The site was unable to finish serving your request.</h3>
+                      <p>We apologize for the inconveience, but an error occurred in the Enano database layer. Please report the full text of this page to the administrator of this site' . $email_info . '.</p>
+                      <p>Description or location of error: '.$t.'<br />
+                      Error returned by MySQL extension: ' . $e . '<br />
+                      Most recent SQL query:</p>
+                      <pre>'.$bt.'</pre>';
+    return $internal_text;
   }
   
   function connect() {
--- a/includes/functions.php	Sun Aug 12 14:56:52 2007 -0400
+++ b/includes/functions.php	Tue Aug 14 15:13:40 2007 -0400
@@ -71,6 +71,7 @@
   global $db, $session, $paths, $template, $plugins; // Common objects
   $flags = '';
   $sep = urlSeparator;
+  $t = sanitize_page_id($t);
   if ( isset($_GET['printable'] ) )
   {
     $flags .= $sep . 'printable=yes';
@@ -226,17 +227,24 @@
 /**
  * Tells you the title for the given page ID string
  * @param string Page ID string (ex: Special:Administration)
+ * @param bool Optional. If true, and if the namespace turns out to be something other than Article, the namespace prefix will be prepended to the return value.
  * @return string
  */
 
-function get_page_title($page_id)
+function get_page_title($page_id, $show_ns = true)
 {
   global $db, $session, $paths, $template, $plugins; // Common objects
 
   $idata = RenderMan::strToPageID($page_id);
   $page_id_key = $paths->nslist[ $idata[1] ] . $idata[0];
+  $page_id_key = sanitize_page_id($page_id_key);
   $page_data = $paths->pages[$page_id_key];
-  $title = ( isset($page_data['name']) ) ? ( $page_data['namespace'] == 'Article' ? '' : $paths->nslist[ $idata[1] ] ) . $page_data['name'] : $paths->nslist[$idata[1]] . str_replace('_', ' ', dirtify_page_id( $idata[0] ) );
+  $title = ( isset($page_data['name']) ) ?
+    ( ( $page_data['namespace'] == 'Article' || !$show_ns ) ?
+      '' :
+      $paths->nslist[ $idata[1] ] )
+    . $page_data['name'] :
+    ( $show_ns ? $paths->nslist[$idata[1]] : '' ) . str_replace('_', ' ', dirtify_page_id( $idata[0] ) );
   return $title;
 }
 
--- a/includes/pageprocess.php	Sun Aug 12 14:56:52 2007 -0400
+++ b/includes/pageprocess.php	Tue Aug 14 15:13:40 2007 -0400
@@ -212,7 +212,7 @@
         return false;
       }
     }
-    else if ( $this->namespace == 'User' )
+    else if ( $this->namespace == 'User' && strpos($this->page_id, '/') === false )
     {
       $this->_handle_userpage();
     }
@@ -371,6 +371,8 @@
     global $db, $session, $paths, $template, $plugins; // Common objects
     
     $text = $this->fetch_text();
+    $text = preg_replace('/([\s]*)__NOBREADCRUMBS__([\s]*)/', '', $text);
+    $text = preg_replace('/([\s]*)__NOTOC__([\s]*)/', '', $text);
     
     $redir_enabled = false;
     if ( preg_match('/^#redirect \[\[([^\]]+?)\]\]/i', $text, $match ) )
@@ -418,6 +420,7 @@
     $template->tpl_strings['PAGE_NAME'] = htmlspecialchars( $this->title );
     
     $this->header();
+    $this->do_breadcrumbs();
     
     if ( $_errormsg )
     {
@@ -631,6 +634,8 @@
         ));
     
     $target_username = preg_replace('/^' . preg_quote($paths->nslist['User']) . '/', '', $target_username);
+    $target_username = explode('/', $target_username);
+    $target_username = $target_username[0];
     
     if ( ( $page_name == str_replace('_', ' ', $this->page_id) || $page_name == $paths->nslist['User'] . str_replace('_', ' ', $this->page_id) ) || !$this->page_exists )
     {
@@ -1044,8 +1049,11 @@
   {
     global $db, $session, $paths, $template, $plugins; // Common objects
     
+    header('HTTP/1.1 404 Not Found');
+    
     $this->header();
-    header('HTTP/1.1 404 Not Found');
+    $this->do_breadcrumbs();
+    
     if ( $userpage )
     {
       echo '<h3>There is no page with this title yet.</h3>
@@ -1078,6 +1086,10 @@
       }
       $db->free_result();
     }
+    if ( $session->user_level >= USER_LEVEL_ADMIN )
+    {
+      echo '<p>Additional admin options: <a href="' . makeUrl($paths->page, 'do=detag', true) . '" title="Remove any tags on this page">detag page</a></p>';
+    }
     echo '<p>
             HTTP Error: 404 Not Found
           </p>';
@@ -1085,6 +1097,58 @@
   }
   
   /**
+   * Echoes out breadcrumb data, if appropriate.
+   * @access private
+   */
+  
+  function do_breadcrumbs()
+  {
+    global $db, $session, $paths, $template, $plugins; // Common objects
+    if ( strpos($this->text_cache, '__NOBREADCRUMBS__') !== false )
+      return false;
+    $breadcrumb_data = explode('/', $this->page_id);
+    if ( count($breadcrumb_data) > 1 )
+    {
+      echo '<!-- Start breadcrumbs -->
+            <div class="breadcrumbs">
+              ';
+      foreach ( $breadcrumb_data as $i => $higherpage )
+      {
+        $higherpage = $paths->nslist[$this->namespace] . sanitize_page_id(implode('/', array_slice($breadcrumb_data, 0, ($i+1))));
+        if ( ($i + 1) == count($breadcrumb_data) )
+        {
+          $title = get_page_title($higherpage, false);
+          if ( !$this->page_exists )
+          {
+            $title = explode('/', $title);
+            $title = array_reverse($title);
+            $title = $title[0];
+          }
+          echo htmlspecialchars($title);
+          break;
+        }
+        else if ( isPage($higherpage) )
+        {
+          $title = get_page_title($higherpage, false);
+          echo '<a href="' . makeUrl($higherpage, false, true) . '">' . htmlspecialchars($title) . '</a>';
+        }
+        else
+        {
+          $title = get_page_title($higherpage, false);
+          $title = explode('/', $title);
+          $title = array_reverse($title);
+          $title = $title[0];
+          echo '<a href="' . makeUrl($higherpage, false, true) . '" class="wikilink-nonexistent">' . htmlspecialchars($title) . '</a>';
+        }
+        echo ' &raquo; ';
+      }
+      echo '</div>
+            <!-- End breadcrumbs -->
+            ';
+    }
+  }
+  
+  /**
    * PHP 4 constructor.
    * @see PageProcessor::__construct()
    */
--- a/includes/render.php	Sun Aug 12 14:56:52 2007 -0400
+++ b/includes/render.php	Tue Aug 14 15:13:40 2007 -0400
@@ -408,7 +408,7 @@
   function sanitize_html($text)
   {
     $text = htmlspecialchars($text);
-    $allowed_tags = Array('b', 'i', 'u', 'pre', 'code', 'tt', 'br', 'p', 'nowiki', '!--([^.]+)--');
+    $allowed_tags = Array('b', 'i', 'u', 'pre', 'code', 'tt', 'br', 'p', 'nowiki', '!--([\w\W]+)--');
     foreach($allowed_tags as $t)
     {
       $text = preg_replace('#&lt;'.$t.'&gt;(.*?)&lt;/'.$t.'&gt;#is', '<'.$t.'>\\1</'.$t.'>', $text);
@@ -418,6 +418,52 @@
     return $text;
   }
   
+  /**
+   * Parses internal links (wikilinks) in a block of text.
+   * @param string Text to process
+   * @return string
+   */
+  
+  function parse_internal_links($text)
+  {
+    
+    // stage 1 - links with alternate text
+    preg_match_all('/\[\[([^\[\]<>\{\}\|]+)\|(.+?)\]\]/', $text, $matches);
+    foreach ( $matches[0] as $i => $match )
+    {
+      list($page_id, $namespace) = RenderMan::strToPageID($matches[1][$i]);
+      $pid_clean = $paths->nslist[$namespace] . sanitize_page_id($page_id);
+      
+      $url = makeUrl($pid_clean, false, true);
+      $inner_text = $matches[2][$i];
+      $quot = '"';
+      $exists = ( isPage($pid_clean) ) ? '' : ' class="wikilink-nonexistent"';
+      
+      $link = "<a href={$quot}{$url}{$quot}{$exists}>{$inner_text}</a>";
+      
+      $text = str_replace($match, $link, $text);
+    }
+    
+    // stage 2 - links with no alternate text
+    preg_match_all('/\[\[([^\[\]<>\{\}\|]+)\]\]/', $text, $matches);
+    foreach ( $matches[0] as $i => $match )
+    {
+      list($page_id, $namespace) = RenderMan::strToPageID($matches[1][$i]);
+      $pid_clean = $paths->nslist[$namespace] . sanitize_page_id($page_id);
+      
+      $url = makeUrl($matches[1][$i], false, true);
+      $inner_text = htmlspecialchars(get_page_title($pid_clean));
+      $quot = '"';
+      $exists = ( isPage($pid_clean) ) ? '' : ' class="wikilink-nonexistent"';
+      
+      $link = "<a href={$quot}{$url}{$quot}{$exists}>{$inner_text}</a>";
+      
+      $text = str_replace($match, $link, $text);
+    }
+    
+    return $text;
+  }
+  
   /* *
    * Replaces template inclusions with the templates
    * @param string $message The text to format
--- a/includes/template.php	Sun Aug 12 14:56:52 2007 -0400
+++ b/includes/template.php	Tue Aug 14 15:13:40 2007 -0400
@@ -732,7 +732,7 @@
       'ADMIN_SID_AMP_HTML'=>$ash,
       'ADMIN_SID_AUTO'=>$as2,
       'ADDITIONAL_HEADERS'=>$this->additional_headers,
-      'COPYRIGHT'=>getConfig('copyright_notice'),
+      'COPYRIGHT'=>RenderMan::parse_internal_links(getConfig('copyright_notice')),
       'TOOLBAR_EXTRAS'=>$this->toolbar_menu,
       'REQUEST_URI'=>$_SERVER['HTTP_HOST'].$_SERVER['REQUEST_URI'],
       'STYLE_LINK'=>makeUrlNS('Special', 'CSS'.$p, null, true), //contentPath.$paths->nslist['Special'].'CSS' . $p,
@@ -1672,6 +1672,7 @@
     global $db, $session, $paths, $template, $plugins; // Common objects
     if(!$this->no_headers) {
       global $_starttime;
+      
       $f = microtime(true);
       $f = $f - $_starttime;
       $f = round($f, 4);
@@ -1681,6 +1682,9 @@
       $dbg = 'Time: '.$f.'s  |  Queries: '.$nq;
       $t = $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);
+      
       echo $t;
     }
     else return '';
--- a/index.php	Sun Aug 12 14:56:52 2007 -0400
+++ b/index.php	Tue Aug 14 15:13:40 2007 -0400
@@ -359,6 +359,20 @@
       echo PageUtils::pagediff($paths->cpage['urlname_nons'], $paths->namespace, $id1, $id2);
       $template->footer();
       break;
+    case 'detag':
+      if ( $session->user_level < USER_LEVEL_ADMIN )
+      {
+        die_friendly('Access denied', '<p>You need to be an administrator to detag pages.</p>');
+      }
+      if ( $paths->page_exists )
+      {
+        die_friendly('Invalid request', '<p>The detag action is only valid for pages that have been deleted in the past.</p>');
+      }
+      $q = $db->sql_query('DELETE FROM '.table_prefix.'tags WHERE page_id=\'' . $db->escape($paths->cpage['urlname_nons']) . '\' AND namespace=\'' . $paths->namespace . '\';');
+      if ( !$q )
+        $db->_die('Detag query, index.php:'.__LINE__);
+      die_friendly('Page detagged', '<p>All stale tags have been removed from this page.</p>');
+      break;
     case 'aclmanager':
       $data = ( isset($_POST['data']) ) ? $_POST['data'] : Array('mode' => 'listgroups');
       PageUtils::aclmanager($data);
--- a/themes/oxygen/css/bleu.css	Sun Aug 12 14:56:52 2007 -0400
+++ b/themes/oxygen/css/bleu.css	Tue Aug 14 15:13:40 2007 -0400
@@ -28,7 +28,7 @@
 .dbx-handle                        { cursor: move !important; }
 
 /* The credits thingy at the bottom */
-div#credits                        { margin: 0; padding: 10px; padding-bottom: 0px; padding-top: 12px; background-color: #E8E8E8; color: #AAA; font-size: 7pt; }
+div#credits                        { margin: 0; padding: 10px; padding-bottom: 0px; padding-top: 12px; background-color: #E8E8E8; color: #AAA; font-size: 7pt; font-family: lucida grande, verdana, arial, sans-serif; }
 div#credits a                      { color: #90B0D0; text-decoration: none; }
 div#credits a:hover                { color: #80A0C0; text-decoration: underline; }
 
--- a/themes/oxygen/css/mint.css	Sun Aug 12 14:56:52 2007 -0400
+++ b/themes/oxygen/css/mint.css	Tue Aug 14 15:13:40 2007 -0400
@@ -28,7 +28,7 @@
 .dbx-handle                        { cursor: move !important; }
 
 /* The credits thingy at the bottom */
-div#credits                        { margin: 0; padding: 10px; padding-bottom: 0px; padding-top: 12px; background-color: #E8E8E8; color: #AAA; font-size: 7pt; }
+div#credits                        { margin: 0; padding: 10px; padding-bottom: 0px; padding-top: 12px; background-color: #E8E8E8; color: #AAA; font-size: 7pt; font-family: lucida grande, verdana, arial, sans-serif; }
 div#credits a                      { color: #90D0B0; text-decoration: none; }
 div#credits a:hover                { color: #80C0A0; text-decoration: underline; }
 
--- a/themes/oxygen/footer.tpl	Sun Aug 12 14:56:52 2007 -0400
+++ b/themes/oxygen/footer.tpl	Tue Aug 14 15:13:40 2007 -0400
@@ -11,8 +11,8 @@
                  -Dan
                  -->
             <div id="credits">
-              {COPYRIGHT}<br />
-              Powered by <a href="<!-- BEGIN stupid_mode -->http://enanocms.org/<!-- BEGINELSE stupid_mode -->{URL_ABOUT_ENANO}<!-- END stupid_mode -->">Enano</a>  |  <a href="http://validator.w3.org/check?uri=referer">Valid XHTML 1.1</a>  |  <a href="http://jigsaw.w3.org/css-validator/validator?uri=referer">Valid CSS</a>  |  [[Stats]]
+              <b>{COPYRIGHT}</b><br />
+              Website engine powered by <a href="<!-- BEGIN stupid_mode -->http://enanocms.org/<!-- BEGINELSE stupid_mode -->{URL_ABOUT_ENANO}<!-- END stupid_mode -->">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
             </div>
           
           </td><td id="mdg-btr"></td></tr>
--- a/upgrade.php	Sun Aug 12 14:56:52 2007 -0400
+++ b/upgrade.php	Tue Aug 14 15:13:40 2007 -0400
@@ -493,6 +493,7 @@
         <li>You have completely backed up your database (<b><?php echo "$dbhost:$dbname"; ?></b>)</li>
         <li>You have backed up the entire Enano directory (<b><?php echo ENANO_ROOT; ?></b>)</li>
         <li>You have reviewed the release notes for this version, and you<br />are comfortable with any known bugs or issues</li>
+        <li>If you've configured Enano to work using a MySQL user with restricted<br />privileges, you need to enable ALTER, CREATE TABLE, and CREATE INDEX privileges<br />for this upgrade to work.</li>
       </ul>
     </div>
     <div style="text-align: center; margin-top: 10px;">