--- a/includes/namespaces/default.php Sun Mar 28 21:49:26 2010 -0400
+++ b/includes/namespaces/default.php Sun Mar 28 23:10:46 2010 -0400
@@ -21,1013 +21,1013 @@
class Namespace_Default
{
- /**
- * Page ID
- * @var string
- */
-
- public $page_id;
-
- /**
- * Namespace
- * @var string
- */
-
- public $namespace;
-
- /**
- * Local copy of the page text
- */
-
- public $text_cache;
-
- /**
- * Revision ID to send. If 0, the latest revision.
- * @var int
- */
-
- public $revision_id = 0;
-
- /**
- * Tracks whether the page exists
- * @var bool
- */
-
- public $exists = false;
-
- /**
- * Page title
- * @var string
- */
-
- public $title = '';
-
- /**
- * PathManager info array ("cdata") for this page. (The one with urlname, name, namespace, delvotes, delvote_ips, protected, visible, etc.)
- * @var array
- */
-
- public $cdata = array();
-
- /**
- * ACL calculation instance for this page.
- * @var object(Session_ACLPageInfo)
- */
-
- public $perms = false;
-
- /**
- * Protection calculation
- * @var bool
- */
-
- public $page_protected = false;
-
- /**
- * Wiki mode calculation
- * @var bool
- */
-
- public $wiki_mode = false;
-
- /**
- * Page conditions. These represent the final decision as to whether an action is allowed or not. They are set to true if ACLs permit AND if
- * the action "makes sense." (e.g., you can't vote to delete a non-wikimode page.)
- * @var array
- */
-
- public $conds = array();
-
- /**
- * Constructor.
- */
-
- public function __construct($page_id, $namespace, $revision_id = 0)
- {
- global $db, $session, $paths, $template, $plugins; // Common objects
-
- $this->page_id = sanitize_page_id($page_id);
- $this->namespace = $namespace;
- $this->revision_id = intval($revision_id);
-
- // grab the cdata
- $this->build_cdata();
-
- $this->page_protected = $this->cdata['really_protected'] ? true : false;
- switch($this->cdata['wiki_mode'])
- {
- case 0: $this->wiki_mode = false; break;
- case 1: $this->wiki_mode = true; break;
- default: case 2: $this->wiki_mode = getConfig('wiki_mode') == 1; break;
- }
- }
-
- /**
- * Build the page's cdata.
- */
-
- public function build_cdata()
- {
- global $db, $session, $paths, $template, $plugins; // Common objects
- static $cdata_cache = array();
- $pathskey = $paths->get_pathskey($this->page_id, $this->namespace);
- if ( isset($cdata_cache[$pathskey]) )
- {
- $this->cdata = $cdata_cache[$pathskey];
- $this->exists = $cdata_cache[$pathskey]['page_exists'];
- $this->title = $cdata_cache[$pathskey]['name'];
- return null;
- }
-
- $this->exists = false;
- $ns_char = substr($paths->nslist['Special'], -1);
- $page_name = $this->namespace == 'Article' ? dirtify_page_id($this->page_id) : "{$this->namespace}{$ns_char}" . dirtify_page_id($this->page_id);
- $page_name = str_replace('_', ' ', $page_name);
- $this->title = $page_name;
-
- $this->cdata = array(
- 'name' => $page_name,
- 'urlname' => $this->page_id,
- 'namespace' => $this->namespace,
- 'special' => 0,
- 'visible' => 0,
- 'comments_on' => 1,
- 'protected' => 0,
- 'delvotes' => 0,
- 'delvote_ips' => '',
- 'wiki_mode' => 2,
- 'page_exists' => false,
- 'page_format' => getConfig('default_page_format', 'wikitext')
- );
-
- if ( $data_from_db = Namespace_Default::get_cdata_from_db($this->page_id, $this->namespace) )
- {
- $this->exists = true;
- $this->cdata = $data_from_db;
- $this->cdata['page_exists'] = true;
- $this->title = $this->cdata['name'];
- }
-
- $this->cdata = Namespace_Default::bake_cdata($this->cdata);
-
- $cdata_cache[$pathskey] = $this->cdata;
- }
-
- /**
- * Pulls the page's actual text from the database.
- */
-
- function fetch_text()
- {
- global $db, $session, $paths, $template, $plugins; // Common objects
-
- if ( !empty($this->text_cache) )
- {
- return $this->text_cache;
- }
-
- if ( $this->revision_id > 0 && is_int($this->revision_id) )
- {
-
- $q = $db->sql_query('SELECT page_text, char_tag, time_id FROM '.table_prefix.'logs WHERE log_type=\'page\' AND action=\'edit\' AND page_id=\'' . $this->page_id . '\' AND namespace=\'' . $this->namespace . '\' AND log_id=' . $this->revision_id . ';');
- if ( !$q )
- {
- $this->send_error('Error during SQL query.', true);
- }
- if ( $db->numrows() < 1 )
- {
- // Compatibility fix for old pages with dots in the page ID
- if ( strstr($this->page_id, '.2e') )
- {
- $db->free_result();
- $page_id = str_replace('.2e', '.', $this->page_id);
- $q = $db->sql_query('SELECT page_text, char_tag, time_id FROM '.table_prefix.'logs WHERE log_type=\'page\' AND action=\'edit\' AND page_id=\'' . $page_id . '\' AND namespace=\'' . $this->namespace . '\' AND log_id=' . $this->revision_id . ';');
- if ( !$q )
- {
- $this->send_error('Error during SQL query.', true);
- }
- if ( $db->numrows() < 1 )
- {
- $this->page_exists = false;
- return 'err_no_text_rows';
- }
- }
- else
- {
- $this->page_exists = false;
- return 'err_no_text_rows';
- }
- }
- else
- {
- $row = $db->fetchrow();
- }
-
- $db->free_result();
-
- }
- else
- {
- $q = $db->sql_query('SELECT t.page_text, t.char_tag, l.time_id FROM '.table_prefix."page_text AS t\n"
- . " LEFT JOIN " . table_prefix . "logs AS l\n"
- . " ON ( l.page_id = t.page_id AND l.namespace = t.namespace )\n"
- . " WHERE t.page_id='$this->page_id' AND t.namespace='$this->namespace'\n"
- . " ORDER BY l.time_id DESC LIMIT 1;");
- if ( !$q )
- {
- $this->send_error('Error during SQL query.', true);
- }
- if ( $db->numrows() < 1 )
- {
- // Compatibility fix for old pages with dots in the page ID
- if ( strstr($this->page_id, '.2e') )
- {
- $db->free_result();
- $page_id = str_replace('.2e', '.', $this->page_id);
- $q = $db->sql_query('SELECT page_text, char_tag FROM '.table_prefix.'page_text WHERE page_id=\'' . $page_id . '\' AND namespace=\'' . $this->namespace . '\';');
- if ( !$q )
- {
- $this->send_error('Error during SQL query.', true);
- }
- if ( $db->numrows() < 1 )
- {
- $this->page_exists = false;
- return 'err_no_text_rows';
- }
- }
- else
- {
- $this->page_exists = false;
- return 'err_no_text_rows';
- }
- }
-
- $row = $db->fetchrow();
- $db->free_result();
-
- }
-
- if ( !empty($row['char_tag']) )
- {
- // This page text entry uses the old text-escaping format
- $from = array(
- "{APOS:{$row['char_tag']}}",
- "{QUOT:{$row['char_tag']}}",
- "{SLASH:{$row['char_tag']}}"
- );
- $to = array("'", '"', '\\');
- $row['page_text'] = str_replace($from, $to, $row['page_text']);
- }
-
- $this->text_cache = $row['page_text'];
-
- if ( isset($row['time_id']) )
- {
- $this->revision_time = intval($row['time_id']);
- }
-
- return $row['page_text'];
- }
-
- /**
- * Send the page.
- */
-
- public function send()
- {
- global $db, $session, $paths, $template, $plugins; // Common objects
- global $output;
-
- $output->add_before_footer($this->display_categories());
-
- if ( $this->exists )
- $this->send_from_db();
- else
- {
- // This is the DEPRECATED way to extend namespaces. It's left in only for compatibility with older plugins.
- ob_start();
- $code = $plugins->setHook('page_not_found');
- foreach ( $code as $cmd )
- {
- eval($cmd);
- }
- $c = ob_get_contents();
- if ( !empty($c) )
- {
- ob_end_clean();
- echo $c;
- }
- else
- {
- $output->header();
- $this->error_404();
- $output->footer();
- }
- }
- }
-
- /**
- * Get a redirect, if there is one.
- * @return mixed Array: Page ID and namespace, associative; bool: false (no redirect)
- */
-
- public function get_redirect()
- {
- $text = $this->fetch_text();
- if ( preg_match('/^#redirect \[\[([^\]]+?)\]\]/i', $text, $match ) )
- {
- list($page_id, $namespace) = RenderMan::strToPageID($match[1]);
- return array(
- 'page_id' => $page_id,
- 'namespace' => $namespace
- );
- }
- return false;
- }
-
- /**
- * The "real" send-the-page function. The reason for this is so other namespaces can re-use the code
- * to fetch the page from the DB while being able to install their own wrappers.
- */
-
- public function send_from_db($incl_inner_headers = true, $send_headers = true)
- {
- global $db, $session, $paths, $template, $plugins; // Common objects
- global $lang;
- global $output;
-
- $text = $this->fetch_text();
-
- profiler_log("Namespace [$this->namespace, $this->page_id]: pulled text from DB");
-
- $text = preg_replace('/([\s]*)__NOBREADCRUMBS__([\s]*)/', '', $text);
- $text = preg_replace('/([\s]*)__NOTOC__([\s]*)/', '', $text);
- $text = preg_replace('/^#redirect \[\[.+?\]\]\s*/i', '', $text);
-
- if ( $send_headers )
- {
- $output->set_title($this->title);
- $output->header();
- }
- $this->do_breadcrumbs();
-
- if ( $incl_inner_headers )
- {
- if ( !$this->perms )
- $this->perms = $session->fetch_page_acl($this->page_id, $this->namespace);
-
- if ( $this->perms->get_permissions('vote_reset') && $this->cdata['delvotes'] > 0)
- {
- $delvote_ips = unserialize($this->cdata['delvote_ips']);
- $hr = htmlspecialchars(implode(', ', $delvote_ips['u']));
-
- $string_id = ( $this->cdata['delvotes'] == 1 ) ? 'delvote_lbl_votes_one' : 'delvote_lbl_votes_plural';
- $string = $lang->get($string_id, array('num_users' => $this->cdata['delvotes']));
-
- echo '<div class="info-box" style="margin-left: 0; margin-top: 5px;" id="mdgDeleteVoteNoticeBox">
- <b>' . $lang->get('etc_lbl_notice') . '</b> ' . $string . '<br />
- <b>' . $lang->get('delvote_lbl_users_that_voted') . '</b> ' . $hr . '<br />
- <a href="'.makeUrl($paths->page, 'do=deletepage').'" onclick="ajaxDeletePage(); return false;">' . $lang->get('delvote_btn_deletepage') . '</a> | <a href="'.makeUrl($paths->page, 'do=resetvotes').'" onclick="ajaxResetDelVotes(); return false;">' . $lang->get('delvote_btn_resetvotes') . '</a>
- </div>';
- }
- }
-
- if ( $this->revision_id )
- {
- echo '<div class="info-box" style="margin-left: 0; margin-top: 5px;">
- <b>' . $lang->get('page_msg_archived_title') . '</b><br />
- ' . $lang->get('page_msg_archived_body', array(
- 'archive_date' => enano_date(ED_DATE, $this->revision_time),
- 'archive_time' => enano_date(ED_TIME, $this->revision_time),
- 'current_link' => makeUrlNS($this->namespace, $this->page_id),
- 'restore_link' => makeUrlNS($this->namespace, $this->page_id, 'do=edit&revid='.$this->revision_id),
- 'restore_onclick' => 'ajaxEditor(\''.$this->revision_id.'\'); return false;',
- )) . '
- </div>';
- $q = $db->sql_query('SELECT page_format FROM ' . table_prefix . "logs WHERE log_id = {$this->revision_id};");
- if ( !$q )
- $db->_die();
-
- list($page_format) = $db->fetchrow_num();
- $db->free_result();
- }
- else
- {
- $page_format = $this->cdata['page_format'];
- }
-
- $code = $plugins->setHook('pageprocess_render_head');
- foreach ( $code as $cmd )
- {
- eval($cmd);
- }
-
- $prof_contentevent = profiler_log("Namespace [$this->namespace, $this->page_id]: headers and preprocessing done - about to send content");
-
- if ( $incl_inner_headers )
- {
- if ( $page_format === 'wikitext' )
- {
- $text = '?>' . RenderMan::render($text);
- }
- else
- {
- // Page format is XHTML. This means we want to disable functionality that MCE takes care of, while still retaining
- // the ability to wikilink, the ability to use images, etc. Basically, RENDER_INLINEONLY disables all behavior in
- // the rendering engine/Text_Wiki that conflicts with MCE.
- $text = '?>' . RenderMan::render($text, RENDER_INLINE);
- }
- }
- else
- {
- $text = '?>' . $text;
- $text = preg_replace('/<nowiki>(.*?)<\/nowiki>/s', '\\1', $text);
- }
-
- eval ( $text );
-
- profiler_log("Namespace [$this->namespace, $this->page_id]: content sent", true, $prof_contentevent);
-
- $code = $plugins->setHook('pageprocess_render_tail');
- foreach ( $code as $cmd )
- {
- eval($cmd);
- }
-
- if ( $incl_inner_headers )
- {
- display_page_footers();
- }
-
- profiler_log("Namespace [$this->namespace, $this->page_id]: sent footers");
-
- if ( $send_headers )
- $output->footer();
- }
-
- /**
- * Echoes out breadcrumb data, if appropriate.
- * @access private
- */
-
- function do_breadcrumbs()
- {
- global $db, $session, $paths, $template, $plugins; // Common objects
- global $lang;
-
- if ( strpos($this->text_cache, '__NOBREADCRUMBS__') !== false )
- return false;
-
- $mode = getConfig('breadcrumb_mode');
-
- if ( $mode == 'never' )
- // Breadcrumbs are disabled
- return true;
-
- // Minimum depth for breadcrumb display
- $threshold = ( $mode == 'always' ) ? 0 : 1;
-
- $breadcrumb_data = explode('/', $this->page_id);
- if ( count($breadcrumb_data) > $threshold )
- {
- // If we're not on a subpage of the main page, add "Home" to the list
- $show_home = false;
- if ( $mode == 'always' )
- {
- $show_home = true;
- }
- echo '<!-- Start breadcrumbs -->
- <div class="breadcrumbs">
- ';
- if ( $show_home )
- {
- // Display the "home" link first.
- $pathskey = $paths->nslist[ $this->namespace ] . $this->page_id;
- if ( $pathskey !== get_main_page() )
- echo '<a href="' . makeUrl(get_main_page(), false, true) . '">';
- echo $lang->get('onpage_btn_breadcrumbs_home');
- if ( $pathskey !== get_main_page() )
- echo '</a>';
- }
- foreach ( $breadcrumb_data as $i => $crumb )
- {
- $cumulative = implode('/', array_slice($breadcrumb_data, 0, ( $i + 1 )));
- if ( $show_home && $cumulative === get_main_page() )
- continue;
- if ( $show_home || $i > 0 )
- echo ' » ';
- $title = ( isPage($cumulative) ) ? get_page_title($cumulative) : get_page_title($crumb);
- if ( $i + 1 == count($breadcrumb_data) )
- {
- echo htmlspecialchars($title);
- }
- else
- {
- $exists = ( isPage($cumulative) ) ? '' : ' class="wikilink-nonexistent"';
- echo '<a href="' . makeUrl($cumulative, false, true) . '"' . $exists . '>' . htmlspecialchars($title) . '</a>';
- }
- }
- echo '</div>
- <!-- End breadcrumbs -->
- ';
- }
- }
-
- public function error_404()
- {
- global $db, $session, $paths, $template, $plugins; // Common objects
- global $lang, $output;
-
- $userpage = $this->namespace == 'User';
-
- @header('HTTP/1.1 404 Not Found');
-
- $msg = ( $pp = $paths->sysmsg('Page_not_found') ) ? $pp : '{STANDARD404}';
-
- $standard_404 = '';
-
- if ( $userpage )
- {
- $standard_404 .= '<h3>' . $lang->get('page_msg_404_title_userpage') . '</h3>
- <p>' . $lang->get('page_msg_404_body_userpage');
- }
- else
- {
- $standard_404 .= '<h3>' . $lang->get('page_msg_404_title') . '</h3>
- <p>' . $lang->get('page_msg_404_body');
- }
- if ( $session->get_permissions('create_page') )
- {
- $standard_404 .= ' ' . $lang->get('page_msg_404_create', array(
- 'create_flags' => 'href="'.makeUrlNS($this->namespace, $this->page_id, 'do=edit', true).'" onclick="ajaxEditor(); return false;"',
- 'mainpage_link' => makeUrl(get_main_page(), false, true)
- ));
- }
- else
- {
- $standard_404 .= ' ' . $lang->get('page_msg_404_gohome', array(
- 'mainpage_link' => makeUrl(get_main_page(), false, true)
- ));
- }
- $standard_404 .= '</p>';
- if ( $session->get_permissions('history_rollback') )
- {
- $e = $db->sql_query('SELECT * FROM ' . table_prefix . 'logs WHERE action=\'delete\' AND page_id=\'' . $this->page_id . '\' AND namespace=\'' . $this->namespace . '\' ORDER BY time_id DESC;');
- if ( !$e )
- {
- $db->_die('The deletion log could not be selected.');
- }
- if ( $db->numrows() > 0 )
- {
- $r = $db->fetchrow();
- $standard_404 .= '<p>' . $lang->get('page_msg_404_was_deleted', array(
- 'delete_time' => enano_date(ED_DATE | ED_TIME, $r['time_id']),
- 'delete_reason' => htmlspecialchars($r['edit_summary']),
- 'rollback_flags' => 'href="'.makeUrl($paths->page, 'do=rollback&id='.$r['log_id']).'" onclick="ajaxRollback(\''.$r['log_id'].'\'); return false;"'
- ))
- . '</p>';
- if ( $session->user_level >= USER_LEVEL_ADMIN )
- {
- $standard_404 .= '<p>' . $lang->get('page_msg_404_admin_opts', array(
- 'detag_link' => makeUrl($paths->page, 'do=detag', true)
- ))
- . '</p>';
- }
- }
- $db->free_result();
- }
- $standard_404 .= '<p>
- ' . $lang->get('page_msg_404_http_response') . '
- </p>';
-
- $parser = $template->makeParserText($msg);
- $parser->assign_vars(array(
- 'STANDARD404' => $standard_404
- ));
-
- $msg = RenderMan::render($parser->run());
- eval( '?>' . $msg );
- }
-
- /**
- * Display the categories a page is in. If the current page is a category, its contents will also be printed.
- */
-
- function display_categories()
- {
- global $db, $session, $paths, $template, $plugins; // Common objects
- global $lang;
-
- $html = '';
-
- if ( $this->namespace == 'Category' )
- {
- // Show member pages and subcategories
- $q = $db->sql_query('SELECT p.urlname, p.namespace, p.name, p.namespace=\'Category\' AS is_category FROM '.table_prefix.'categories AS c
- LEFT JOIN '.table_prefix.'pages AS p
- ON ( p.urlname = c.page_id AND p.namespace = c.namespace )
- WHERE c.category_id=\'' . $db->escape($this->page_id) . '\'
- ORDER BY is_category DESC, p.name ASC;');
- if ( !$q )
- {
- $db->_die();
- }
- $html .= '<h3>' . $lang->get('onpage_cat_heading_subcategories') . '</h3>';
- $html .= '<div class="tblholder">';
- $html .= '<table border="0" cellspacing="1" cellpadding="4">';
- $html .= '<tr>';
- $ticker = 0;
- $counter = 0;
- $switched = false;
- $class = 'row1';
- while ( $row = $db->fetchrow($q) )
- {
- if ( $row['is_category'] == 0 && !$switched )
- {
- if ( $counter > 0 )
- {
- // Fill-in
- while ( $ticker < 3 )
- {
- $ticker++;
- $html .= '<td class="' . $class . '" style="width: 33.3%;"></td>';
- }
- }
- else
- {
- $html .= '<td class="' . $class . '">' . $lang->get('onpage_cat_msg_no_subcategories') . '</td>';
- }
- $html .= '</tr></table></div>' . "\n\n";
- $html .= '<h3>' . $lang->get('onpage_cat_heading_pages') . '</h3>';
- $html .= '<div class="tblholder">';
- $html .= '<table border="0" cellspacing="1" cellpadding="4">';
- $html .= '<tr>';
- $counter = 0;
- $ticker = -1;
- $switched = true;
- }
- $counter++;
- $ticker++;
- if ( $ticker == 3 )
- {
- $html .= '</tr><tr>';
- $ticker = 0;
- $class = ( $class == 'row3' ) ? 'row1' : 'row3';
- }
- $html .= "<td class=\"{$class}\" style=\"width: 33.3%;\">"; // " to workaround stupid jEdit bug
-
- $link = makeUrlNS($row['namespace'], sanitize_page_id($row['urlname']));
- $html .= '<a href="' . $link . '"';
- $key = $paths->nslist[$row['namespace']] . sanitize_page_id($row['urlname']);
- if ( !isPage( $key ) )
- {
- $html .= ' class="wikilink-nonexistent"';
- }
- $html .= '>';
- $title = get_page_title_ns($row['urlname'], $row['namespace']);
- $html .= htmlspecialchars($title);
- $html .= '</a>';
-
- $html .= "</td>";
- }
- if ( !$switched )
- {
- if ( $counter > 0 )
- {
- // Fill-in
- while ( $ticker < 2 )
- {
- $ticker++;
- $html .= '<td class="' . $class . '" style="width: 33.3%;"></td>';
- }
- }
- else
- {
- $html .= '<td class="' . $class . '">' . $lang->get('onpage_cat_msg_no_subcategories') . '</td>';
- }
- $html .= '</tr></table></div>' . "\n\n";
- $html .= '<h3>' . $lang->get('onpage_cat_heading_pages') . '</h3>';
- $html .= '<div class="tblholder">';
- $html .= '<table border="0" cellspacing="1" cellpadding="4">';
- $html .= '<tr>';
- $counter = 0;
- $ticker = 0;
- $switched = true;
- }
- if ( $counter > 0 )
- {
- // Fill-in
- while ( $ticker < 2 )
- {
- $ticker++;
- $html .= '<td class="' . $class . '" style="width: 33.3%;"></td>';
- }
- }
- else
- {
- $html .= '<td class="' . $class . '">' . $lang->get('onpage_cat_msg_no_pages') . '</td>';
- }
- $html .= '</tr></table></div>' . "\n\n";
- }
-
- if ( $this->namespace != 'Special' && $this->namespace != 'Admin' )
- {
- $html .= '<div class="mdg-comment" style="margin: 10px 0 0 0;" id="category_box_wrapper">';
- $html .= '<div style="float: right;">';
- $html .= '(<a href="#" onclick="ajaxCatToTag(); return false;">' . $lang->get('tags_catbox_link') . '</a>)';
- $html .= '</div>';
- $html .= '<div id="mdgCatBox">' . $lang->get('catedit_catbox_lbl_categories') . ' ';
-
- $q = $db->sql_query('SELECT category_id FROM ' . table_prefix . "categories WHERE page_id = '$this->page_id' AND namespace = '$this->namespace';");
- if ( !$q )
- $db->_die();
-
- if ( $row = $db->fetchrow() )
- {
- $list = array();
- do
- {
- $cid = sanitize_page_id($row['category_id']);
- $title = get_page_title_ns($cid, 'Category');
- $link = makeUrlNS('Category', $cid);
- $list[] = '<a href="' . $link . '">' . htmlspecialchars($title) . '</a>';
- }
- while ( $row = $db->fetchrow($q) );
- $html .= implode(', ', $list);
- }
- else
- {
- $html .= $lang->get('catedit_catbox_lbl_uncategorized');
- }
-
- $can_edit = ( $session->get_permissions('edit_cat') && ( !$paths->page_protected || $session->get_permissions('even_when_protected') ) );
- if ( $can_edit )
- {
- $edit_link = '<a href="' . makeUrl($paths->page, 'do=catedit', true) . '" onclick="ajaxCatEdit(); return false;">' . $lang->get('catedit_catbox_link_edit') . '</a>';
- $html .= ' [ ' . $edit_link . ' ]';
- }
-
- $html .= '</div></div>';
- }
- return $html;
- }
-
- /**
- * Pull in switches as to whether a specific toolbar button should be used or not. This sets things up according to the current page being displayed.
- * @return array Associative
- */
-
- function set_conds()
- {
- global $db, $session, $paths, $template, $plugins; // Common objects
-
- if ( !$this->perms )
- $this->perms = $session->fetch_page_acl($this->page_id, $this->namespace);
-
- if ( !$this->perms )
- {
- // We're trying to send a page WAY too early (session hasn't been started yet), such as for a redirect. Send a default set of conds because
- // there's NO way to get permissions to determine anything otherwise. Yes, starting $session here might be dangerous.
- $this->conds = array(
- 'article' => true,
- 'comments' => false,
- 'edit' => false,
- 'viewsource' => false,
- 'history' => false,
- 'rename' => false,
- 'delvote' => false,
- 'resetvotes' => false,
- 'delete' => false,
- 'printable' => false,
- 'protect' => false,
- 'setwikimode' => false,
- 'clearlogs' => false,
- 'password' => false,
- 'acledit' => false,
- 'adminpage' => false
- );
- return $this->conds;
- }
-
- // die('have perms: <pre>' . print_r($this->perms, true) . "\n---------------------------------\nBacktrace:\n" . enano_debug_print_backtrace(true));
-
- $enforce_protection = ( $this->page_protected && ( ( $session->check_acl_scope('even_when_protected', $this->namespace) && !$this->perms->get_permissions('even_when_protected') ) || !$session->check_acl_scope('even_when_protected', $this->namespace) ) );
-
- $conds = array();
-
- // Article: always show
- $conds['article'] = true;
-
- // Discussion: Show if comments are enabled on the site, and if comments are on for this page.
- $conds['comments'] = $this->perms->get_permissions('read') && getConfig('enable_comments', '1')=='1' && $this->cdata['comments_on'] == 1;
-
- // Edit: Show if we have permission to edit the page, and if we don't have protection in effect
- $conds['edit'] = $this->perms->get_permissions('read') && $session->check_acl_scope('edit_page', $this->namespace) && $this->perms->get_permissions('edit_page') && !$enforce_protection;
-
- // View source: Show if we have permission to view source and either ACLs prohibit editing or protection is in effect
- $conds['viewsource'] = $session->check_acl_scope('view_source', $this->namespace) && $this->perms->get_permissions('view_source') && ( !$this->perms->get_permissions('edit_page') || $enforce_protection ) && $this->namespace != 'API';
-
- // History: Show if we have permission to see history and if the page exists
- $conds['history'] = $session->check_acl_scope('history_view', $this->namespace) && $this->exists && $this->perms->get_permissions('history_view');
-
- // Rename: Show if the page exists, if we have permission to rename, and if protection isn't in effect
- $conds['rename'] = $session->check_acl_scope('rename', $this->namespace) && $this->exists && $this->perms->get_permissions('rename') && !$enforce_protection;
-
- // Vote-to-delete: Show if we have Wiki Mode on, if we have permission to vote for deletion, and if the page exists (can't vote to delete a nonexistent page)
- $conds['delvote'] = $this->wiki_mode && $session->check_acl_scope('vote_delete', $this->namespace) && $this->perms->get_permissions('vote_delete') && $this->exists;
-
- // Reset votes: Show if we have Wiki Mode on, if we have permission to reset votes, if the page exists, and if there's at least one vote
- $conds['resetvotes'] = $session->check_acl_scope('vote_reset', $this->namespace) && $this->wiki_mode && $this->exists && $this->perms->get_permissions('vote_reset') && $this->cdata['delvotes'] > 0;
-
- // Delete page: Show if the page exists and if we have permission to delete it
- $conds['delete'] = $session->check_acl_scope('delete_page', $this->namespace) && $this->exists && $this->perms->get_permissions('delete_page');
-
- // Printable view: Show if the page exists
- $conds['printable'] = $this->exists;
-
- // Protect: Show if we have Wiki Mode on, if the page exists, and if we have permission to protect the page.
- $conds['protect'] = $session->check_acl_scope('protect', $this->namespace) && $this->wiki_mode && $this->exists && $this->perms->get_permissions('protect');
-
- // Set Wiki Mode: Show if the page exists and if we have permission to set wiki mode
- $conds['setwikimode'] = $session->check_acl_scope('set_wiki_mode', $this->namespace) && $this->exists && $this->perms->get_permissions('set_wiki_mode');
-
- // Clear logs: Show if we have permission to clear logs
- $conds['clearlogs'] = $session->check_acl_scope('clear_logs', $this->namespace) && $this->perms->get_permissions('clear_logs');
-
- // Set password: a little bit complicated. If there's a password, check for password_reset; else, check for password_set.
- $conds['password'] = empty($this->cdata['password']) ?
- $session->check_acl_scope('password_set', $this->namespace) && $this->perms->get_permissions('password_set') :
- $session->check_acl_scope('password_reset', $this->namespace) && $this->perms->get_permissions('password_reset');
-
- // Edit ACLs: Show if this is a non-Enano page that's calling the Enano API and (a) if we have permissions to edit ACLs or (b) we're an admin AND ACL_ALWAYS_ALLOW_ADMIN_EDIT_ACL is on
- $conds['acledit'] = $this->namespace != 'API' && $session->check_acl_scope('edit_acl', $this->namespace) && ( $this->perms->get_permissions('edit_acl') || ( defined('ACL_ALWAYS_ALLOW_ADMIN_EDIT_ACL') && $session->user_level >= USER_LEVEL_ADMIN ) );
-
- // Admin page: Show if the page exists and if we're an admin
- $conds['adminpage'] = $session->user_level >= USER_LEVEL_ADMIN && $this->exists;
-
- // Allow plugins to change stuff
- $code = $plugins->setHook('page_conds_set');
- foreach ( $code as $cmd )
- {
- eval($cmd);
- }
-
- $this->conds = $conds;
- }
-
- /**
- * Return page conditions
- * @return array
- */
-
- public function get_conds()
- {
- if ( empty($this->conds) )
- $this->set_conds();
-
- return $this->conds;
- }
-
- /**
- * Just tell us if the current page exists or not.
- * @return bool
- */
-
- public function exists()
- {
- return $this->exists;
- }
-
- /**
- * Return cdata
- * @return array
- */
-
- public function get_cdata()
- {
- return $this->cdata;
- }
-
- /**
- * Bake, or finalize the processing of, a cdata array.
- * @static
- * @access public
- */
-
- public static function bake_cdata($cdata)
- {
- global $db, $session, $paths, $template, $plugins; // Common objects
-
- // urlname_nons is the actual page_id.
- $cdata['urlname_nons'] = $cdata['urlname'];
- if ( isset($paths->nslist[ $cdata['namespace'] ]) )
- {
- $cdata['urlname'] = $paths->nslist[ $cdata['namespace'] ] . $cdata['urlname'];
- }
- else
- {
- $ns_char = substr($paths->nslist['Special'], -1);
- $cdata['urlname'] = $cdata['namespace'] . $ns_char . $cdata['urlname'];
- }
-
- // add missing keys
- $defaults = array(
- 'special' => 0,
- 'visible' => 0,
- 'comments_on' => 1,
- 'protected' => 0,
- 'delvotes' => 0,
- 'delvote_ips' => serialize(array()),
- 'wiki_mode' => 2,
- 'page_format' => getConfig('default_page_format', 'wikitext')
- );
- foreach ( $defaults as $key => $value )
- {
- if ( !isset($cdata[$key]) )
- $cdata[$key] = $value;
- }
-
- // fix up deletion votes
- if ( empty($cdata['delvotes']) )
- $cdata['delvotes'] = 0;
-
- // fix up deletion vote IP list
- if ( empty($cdata['delvote_ips']) )
- $cdata['delvote_ips'] = serialize(array());
-
- // calculate wiki mode
- $cdata['really_wiki_mode'] = ( $cdata['wiki_mode'] == 1 || ( $cdata['wiki_mode'] == 2 && getConfig('wiki_mode', 0) == 1 ) );
-
- // calculate protection
- $cdata['really_protected'] = ( $cdata['protected'] > 0 );
- if ( $cdata['protected'] == 2 )
- {
- $cdata['really_protected'] = !$session->user_logged_in || ( $session->user_logged_in && $session->reg_time + 86400*4 > time() );
- }
-
- return $cdata;
- }
-
- /**
- * Grabs raw (unbaked) cdata from the database, caching if possible.
- * @param string Page ID
- * @param string Namespace.
- * @static
- */
-
- public static function get_cdata_from_db($page_id, $namespace)
- {
- global $db, $session, $paths, $template, $plugins; // Common objects
- static $cache = array();
-
- $pathskey = $paths->get_pathskey($page_id, $namespace);
- if ( isset($cache[$pathskey]) )
- return $cache[$pathskey];
-
- $page_id_db = $db->escape($page_id);
- $namespace_db = $db->escape($namespace);
-
- $q = $db->sql_query('SELECT p.*'
- . ' FROM ' . table_prefix . "pages AS p\n"
- . " WHERE p.urlname = '$page_id_db' AND p.namespace = '$namespace_db'\n"
- . " GROUP BY p.urlname, p.name, p.namespace, p.page_order, p.special, p.visible, p.protected, p.wiki_mode, p.comments_on, p.delvotes, p.delvote_ips, p.page_format, p.password;");
-
- if ( !$q )
- $db->_die();
-
- if ( $db->numrows() < 1 )
- {
- $db->free_result();
- $cache[$pathskey] = false;
- return false;
- }
-
- $row = $db->fetchrow();
-
- // Get comment counts
- // FIXME: Apparently there's a bit of recursion in here. Fetching permissions depends on this cdata function.
- // Perhaps we should eliminate session's dependency on cdata? (What is it used for?)
- $q = $db->sql_query('SELECT approved FROM ' . table_prefix . "comments WHERE page_id = '$page_id_db' AND namespace = '$namespace_db';");
- // yay parallel assignment
- $row['comments_approved'] = $row['comments_unapproved'] = $row['comments_spam'] = 0;
- while ( $commentrow = $db->fetchrow() )
- switch($commentrow['approved'])
- {
- case COMMENT_APPROVED:
- default:
- $row['comments_approved']++;
- break;
- case COMMENT_UNAPPROVED:
- $row['comments_unapproved']++;
- break;
- case COMMENT_SPAM:
- $row['comments_spam']++;
- break;
- }
-
- $cache[$pathskey] = $row;
- return $row;
- }
+ /**
+ * Page ID
+ * @var string
+ */
+
+ public $page_id;
+
+ /**
+ * Namespace
+ * @var string
+ */
+
+ public $namespace;
+
+ /**
+ * Local copy of the page text
+ */
+
+ public $text_cache;
+
+ /**
+ * Revision ID to send. If 0, the latest revision.
+ * @var int
+ */
+
+ public $revision_id = 0;
+
+ /**
+ * Tracks whether the page exists
+ * @var bool
+ */
+
+ public $exists = false;
+
+ /**
+ * Page title
+ * @var string
+ */
+
+ public $title = '';
+
+ /**
+ * PathManager info array ("cdata") for this page. (The one with urlname, name, namespace, delvotes, delvote_ips, protected, visible, etc.)
+ * @var array
+ */
+
+ public $cdata = array();
+
+ /**
+ * ACL calculation instance for this page.
+ * @var object(Session_ACLPageInfo)
+ */
+
+ public $perms = false;
+
+ /**
+ * Protection calculation
+ * @var bool
+ */
+
+ public $page_protected = false;
+
+ /**
+ * Wiki mode calculation
+ * @var bool
+ */
+
+ public $wiki_mode = false;
+
+ /**
+ * Page conditions. These represent the final decision as to whether an action is allowed or not. They are set to true if ACLs permit AND if
+ * the action "makes sense." (e.g., you can't vote to delete a non-wikimode page.)
+ * @var array
+ */
+
+ public $conds = array();
+
+ /**
+ * Constructor.
+ */
+
+ public function __construct($page_id, $namespace, $revision_id = 0)
+ {
+ global $db, $session, $paths, $template, $plugins; // Common objects
+
+ $this->page_id = sanitize_page_id($page_id);
+ $this->namespace = $namespace;
+ $this->revision_id = intval($revision_id);
+
+ // grab the cdata
+ $this->build_cdata();
+
+ $this->page_protected = $this->cdata['really_protected'] ? true : false;
+ switch($this->cdata['wiki_mode'])
+ {
+ case 0: $this->wiki_mode = false; break;
+ case 1: $this->wiki_mode = true; break;
+ default: case 2: $this->wiki_mode = getConfig('wiki_mode') == 1; break;
+ }
+ }
+
+ /**
+ * Build the page's cdata.
+ */
+
+ public function build_cdata()
+ {
+ global $db, $session, $paths, $template, $plugins; // Common objects
+ static $cdata_cache = array();
+ $pathskey = $paths->get_pathskey($this->page_id, $this->namespace);
+ if ( isset($cdata_cache[$pathskey]) )
+ {
+ $this->cdata = $cdata_cache[$pathskey];
+ $this->exists = $cdata_cache[$pathskey]['page_exists'];
+ $this->title = $cdata_cache[$pathskey]['name'];
+ return null;
+ }
+
+ $this->exists = false;
+ $ns_char = substr($paths->nslist['Special'], -1);
+ $page_name = $this->namespace == 'Article' ? dirtify_page_id($this->page_id) : "{$this->namespace}{$ns_char}" . dirtify_page_id($this->page_id);
+ $page_name = str_replace('_', ' ', $page_name);
+ $this->title = $page_name;
+
+ $this->cdata = array(
+ 'name' => $page_name,
+ 'urlname' => $this->page_id,
+ 'namespace' => $this->namespace,
+ 'special' => 0,
+ 'visible' => 0,
+ 'comments_on' => 1,
+ 'protected' => 0,
+ 'delvotes' => 0,
+ 'delvote_ips' => '',
+ 'wiki_mode' => 2,
+ 'page_exists' => false,
+ 'page_format' => getConfig('default_page_format', 'wikitext')
+ );
+
+ if ( $data_from_db = Namespace_Default::get_cdata_from_db($this->page_id, $this->namespace) )
+ {
+ $this->exists = true;
+ $this->cdata = $data_from_db;
+ $this->cdata['page_exists'] = true;
+ $this->title = $this->cdata['name'];
+ }
+
+ $this->cdata = Namespace_Default::bake_cdata($this->cdata);
+
+ $cdata_cache[$pathskey] = $this->cdata;
+ }
+
+ /**
+ * Pulls the page's actual text from the database.
+ */
+
+ function fetch_text()
+ {
+ global $db, $session, $paths, $template, $plugins; // Common objects
+
+ if ( !empty($this->text_cache) )
+ {
+ return $this->text_cache;
+ }
+
+ if ( $this->revision_id > 0 && is_int($this->revision_id) )
+ {
+
+ $q = $db->sql_query('SELECT page_text, char_tag, time_id FROM '.table_prefix.'logs WHERE log_type=\'page\' AND action=\'edit\' AND page_id=\'' . $this->page_id . '\' AND namespace=\'' . $this->namespace . '\' AND log_id=' . $this->revision_id . ';');
+ if ( !$q )
+ {
+ $this->send_error('Error during SQL query.', true);
+ }
+ if ( $db->numrows() < 1 )
+ {
+ // Compatibility fix for old pages with dots in the page ID
+ if ( strstr($this->page_id, '.2e') )
+ {
+ $db->free_result();
+ $page_id = str_replace('.2e', '.', $this->page_id);
+ $q = $db->sql_query('SELECT page_text, char_tag, time_id FROM '.table_prefix.'logs WHERE log_type=\'page\' AND action=\'edit\' AND page_id=\'' . $page_id . '\' AND namespace=\'' . $this->namespace . '\' AND log_id=' . $this->revision_id . ';');
+ if ( !$q )
+ {
+ $this->send_error('Error during SQL query.', true);
+ }
+ if ( $db->numrows() < 1 )
+ {
+ $this->page_exists = false;
+ return 'err_no_text_rows';
+ }
+ }
+ else
+ {
+ $this->page_exists = false;
+ return 'err_no_text_rows';
+ }
+ }
+ else
+ {
+ $row = $db->fetchrow();
+ }
+
+ $db->free_result();
+
+ }
+ else
+ {
+ $q = $db->sql_query('SELECT t.page_text, t.char_tag, l.time_id FROM '.table_prefix."page_text AS t\n"
+ . " LEFT JOIN " . table_prefix . "logs AS l\n"
+ . " ON ( l.page_id = t.page_id AND l.namespace = t.namespace )\n"
+ . " WHERE t.page_id='$this->page_id' AND t.namespace='$this->namespace'\n"
+ . " ORDER BY l.time_id DESC LIMIT 1;");
+ if ( !$q )
+ {
+ $this->send_error('Error during SQL query.', true);
+ }
+ if ( $db->numrows() < 1 )
+ {
+ // Compatibility fix for old pages with dots in the page ID
+ if ( strstr($this->page_id, '.2e') )
+ {
+ $db->free_result();
+ $page_id = str_replace('.2e', '.', $this->page_id);
+ $q = $db->sql_query('SELECT page_text, char_tag FROM '.table_prefix.'page_text WHERE page_id=\'' . $page_id . '\' AND namespace=\'' . $this->namespace . '\';');
+ if ( !$q )
+ {
+ $this->send_error('Error during SQL query.', true);
+ }
+ if ( $db->numrows() < 1 )
+ {
+ $this->page_exists = false;
+ return 'err_no_text_rows';
+ }
+ }
+ else
+ {
+ $this->page_exists = false;
+ return 'err_no_text_rows';
+ }
+ }
+
+ $row = $db->fetchrow();
+ $db->free_result();
+
+ }
+
+ if ( !empty($row['char_tag']) )
+ {
+ // This page text entry uses the old text-escaping format
+ $from = array(
+ "{APOS:{$row['char_tag']}}",
+ "{QUOT:{$row['char_tag']}}",
+ "{SLASH:{$row['char_tag']}}"
+ );
+ $to = array("'", '"', '\\');
+ $row['page_text'] = str_replace($from, $to, $row['page_text']);
+ }
+
+ $this->text_cache = $row['page_text'];
+
+ if ( isset($row['time_id']) )
+ {
+ $this->revision_time = intval($row['time_id']);
+ }
+
+ return $row['page_text'];
+ }
+
+ /**
+ * Send the page.
+ */
+
+ public function send()
+ {
+ global $db, $session, $paths, $template, $plugins; // Common objects
+ global $output;
+
+ $output->add_before_footer($this->display_categories());
+
+ if ( $this->exists )
+ $this->send_from_db();
+ else
+ {
+ // This is the DEPRECATED way to extend namespaces. It's left in only for compatibility with older plugins.
+ ob_start();
+ $code = $plugins->setHook('page_not_found');
+ foreach ( $code as $cmd )
+ {
+ eval($cmd);
+ }
+ $c = ob_get_contents();
+ if ( !empty($c) )
+ {
+ ob_end_clean();
+ echo $c;
+ }
+ else
+ {
+ $output->header();
+ $this->error_404();
+ $output->footer();
+ }
+ }
+ }
+
+ /**
+ * Get a redirect, if there is one.
+ * @return mixed Array: Page ID and namespace, associative; bool: false (no redirect)
+ */
+
+ public function get_redirect()
+ {
+ $text = $this->fetch_text();
+ if ( preg_match('/^#redirect \[\[([^\]]+?)\]\]/i', $text, $match ) )
+ {
+ list($page_id, $namespace) = RenderMan::strToPageID($match[1]);
+ return array(
+ 'page_id' => $page_id,
+ 'namespace' => $namespace
+ );
+ }
+ return false;
+ }
+
+ /**
+ * The "real" send-the-page function. The reason for this is so other namespaces can re-use the code
+ * to fetch the page from the DB while being able to install their own wrappers.
+ */
+
+ public function send_from_db($incl_inner_headers = true, $send_headers = true)
+ {
+ global $db, $session, $paths, $template, $plugins; // Common objects
+ global $lang;
+ global $output;
+
+ $text = $this->fetch_text();
+
+ profiler_log("Namespace [$this->namespace, $this->page_id]: pulled text from DB");
+
+ $text = preg_replace('/([\s]*)__NOBREADCRUMBS__([\s]*)/', '', $text);
+ $text = preg_replace('/([\s]*)__NOTOC__([\s]*)/', '', $text);
+ $text = preg_replace('/^#redirect \[\[.+?\]\]\s*/i', '', $text);
+
+ if ( $send_headers )
+ {
+ $output->set_title($this->title);
+ $output->header();
+ }
+ $this->do_breadcrumbs();
+
+ if ( $incl_inner_headers )
+ {
+ if ( !$this->perms )
+ $this->perms = $session->fetch_page_acl($this->page_id, $this->namespace);
+
+ if ( $this->perms->get_permissions('vote_reset') && $this->cdata['delvotes'] > 0)
+ {
+ $delvote_ips = unserialize($this->cdata['delvote_ips']);
+ $hr = htmlspecialchars(implode(', ', $delvote_ips['u']));
+
+ $string_id = ( $this->cdata['delvotes'] == 1 ) ? 'delvote_lbl_votes_one' : 'delvote_lbl_votes_plural';
+ $string = $lang->get($string_id, array('num_users' => $this->cdata['delvotes']));
+
+ echo '<div class="info-box" style="margin-left: 0; margin-top: 5px;" id="mdgDeleteVoteNoticeBox">
+ <b>' . $lang->get('etc_lbl_notice') . '</b> ' . $string . '<br />
+ <b>' . $lang->get('delvote_lbl_users_that_voted') . '</b> ' . $hr . '<br />
+ <a href="'.makeUrl($paths->page, 'do=deletepage').'" onclick="ajaxDeletePage(); return false;">' . $lang->get('delvote_btn_deletepage') . '</a> | <a href="'.makeUrl($paths->page, 'do=resetvotes').'" onclick="ajaxResetDelVotes(); return false;">' . $lang->get('delvote_btn_resetvotes') . '</a>
+ </div>';
+ }
+ }
+
+ if ( $this->revision_id )
+ {
+ echo '<div class="info-box" style="margin-left: 0; margin-top: 5px;">
+ <b>' . $lang->get('page_msg_archived_title') . '</b><br />
+ ' . $lang->get('page_msg_archived_body', array(
+ 'archive_date' => enano_date(ED_DATE, $this->revision_time),
+ 'archive_time' => enano_date(ED_TIME, $this->revision_time),
+ 'current_link' => makeUrlNS($this->namespace, $this->page_id),
+ 'restore_link' => makeUrlNS($this->namespace, $this->page_id, 'do=edit&revid='.$this->revision_id),
+ 'restore_onclick' => 'ajaxEditor(\''.$this->revision_id.'\'); return false;',
+ )) . '
+ </div>';
+ $q = $db->sql_query('SELECT page_format FROM ' . table_prefix . "logs WHERE log_id = {$this->revision_id};");
+ if ( !$q )
+ $db->_die();
+
+ list($page_format) = $db->fetchrow_num();
+ $db->free_result();
+ }
+ else
+ {
+ $page_format = $this->cdata['page_format'];
+ }
+
+ $code = $plugins->setHook('pageprocess_render_head');
+ foreach ( $code as $cmd )
+ {
+ eval($cmd);
+ }
+
+ $prof_contentevent = profiler_log("Namespace [$this->namespace, $this->page_id]: headers and preprocessing done - about to send content");
+
+ if ( $incl_inner_headers )
+ {
+ if ( $page_format === 'wikitext' )
+ {
+ $text = '?>' . RenderMan::render($text);
+ }
+ else
+ {
+ // Page format is XHTML. This means we want to disable functionality that MCE takes care of, while still retaining
+ // the ability to wikilink, the ability to use images, etc. Basically, RENDER_INLINEONLY disables all behavior in
+ // the rendering engine/Text_Wiki that conflicts with MCE.
+ $text = '?>' . RenderMan::render($text, RENDER_INLINE);
+ }
+ }
+ else
+ {
+ $text = '?>' . $text;
+ $text = preg_replace('/<nowiki>(.*?)<\/nowiki>/s', '\\1', $text);
+ }
+
+ eval ( $text );
+
+ profiler_log("Namespace [$this->namespace, $this->page_id]: content sent", true, $prof_contentevent);
+
+ $code = $plugins->setHook('pageprocess_render_tail');
+ foreach ( $code as $cmd )
+ {
+ eval($cmd);
+ }
+
+ if ( $incl_inner_headers )
+ {
+ display_page_footers();
+ }
+
+ profiler_log("Namespace [$this->namespace, $this->page_id]: sent footers");
+
+ if ( $send_headers )
+ $output->footer();
+ }
+
+ /**
+ * Echoes out breadcrumb data, if appropriate.
+ * @access private
+ */
+
+ function do_breadcrumbs()
+ {
+ global $db, $session, $paths, $template, $plugins; // Common objects
+ global $lang;
+
+ if ( strpos($this->text_cache, '__NOBREADCRUMBS__') !== false )
+ return false;
+
+ $mode = getConfig('breadcrumb_mode');
+
+ if ( $mode == 'never' )
+ // Breadcrumbs are disabled
+ return true;
+
+ // Minimum depth for breadcrumb display
+ $threshold = ( $mode == 'always' ) ? 0 : 1;
+
+ $breadcrumb_data = explode('/', $this->page_id);
+ if ( count($breadcrumb_data) > $threshold )
+ {
+ // If we're not on a subpage of the main page, add "Home" to the list
+ $show_home = false;
+ if ( $mode == 'always' )
+ {
+ $show_home = true;
+ }
+ echo '<!-- Start breadcrumbs -->
+ <div class="breadcrumbs">
+ ';
+ if ( $show_home )
+ {
+ // Display the "home" link first.
+ $pathskey = $paths->nslist[ $this->namespace ] . $this->page_id;
+ if ( $pathskey !== get_main_page() )
+ echo '<a href="' . makeUrl(get_main_page(), false, true) . '">';
+ echo $lang->get('onpage_btn_breadcrumbs_home');
+ if ( $pathskey !== get_main_page() )
+ echo '</a>';
+ }
+ foreach ( $breadcrumb_data as $i => $crumb )
+ {
+ $cumulative = implode('/', array_slice($breadcrumb_data, 0, ( $i + 1 )));
+ if ( $show_home && $cumulative === get_main_page() )
+ continue;
+ if ( $show_home || $i > 0 )
+ echo ' » ';
+ $title = ( isPage($cumulative) ) ? get_page_title($cumulative) : get_page_title($crumb);
+ if ( $i + 1 == count($breadcrumb_data) )
+ {
+ echo htmlspecialchars($title);
+ }
+ else
+ {
+ $exists = ( isPage($cumulative) ) ? '' : ' class="wikilink-nonexistent"';
+ echo '<a href="' . makeUrl($cumulative, false, true) . '"' . $exists . '>' . htmlspecialchars($title) . '</a>';
+ }
+ }
+ echo '</div>
+ <!-- End breadcrumbs -->
+ ';
+ }
+ }
+
+ public function error_404()
+ {
+ global $db, $session, $paths, $template, $plugins; // Common objects
+ global $lang, $output;
+
+ $userpage = $this->namespace == 'User';
+
+ @header('HTTP/1.1 404 Not Found');
+
+ $msg = ( $pp = $paths->sysmsg('Page_not_found') ) ? $pp : '{STANDARD404}';
+
+ $standard_404 = '';
+
+ if ( $userpage )
+ {
+ $standard_404 .= '<h3>' . $lang->get('page_msg_404_title_userpage') . '</h3>
+ <p>' . $lang->get('page_msg_404_body_userpage');
+ }
+ else
+ {
+ $standard_404 .= '<h3>' . $lang->get('page_msg_404_title') . '</h3>
+ <p>' . $lang->get('page_msg_404_body');
+ }
+ if ( $session->get_permissions('create_page') )
+ {
+ $standard_404 .= ' ' . $lang->get('page_msg_404_create', array(
+ 'create_flags' => 'href="'.makeUrlNS($this->namespace, $this->page_id, 'do=edit', true).'" onclick="ajaxEditor(); return false;"',
+ 'mainpage_link' => makeUrl(get_main_page(), false, true)
+ ));
+ }
+ else
+ {
+ $standard_404 .= ' ' . $lang->get('page_msg_404_gohome', array(
+ 'mainpage_link' => makeUrl(get_main_page(), false, true)
+ ));
+ }
+ $standard_404 .= '</p>';
+ if ( $session->get_permissions('history_rollback') )
+ {
+ $e = $db->sql_query('SELECT * FROM ' . table_prefix . 'logs WHERE action=\'delete\' AND page_id=\'' . $this->page_id . '\' AND namespace=\'' . $this->namespace . '\' ORDER BY time_id DESC;');
+ if ( !$e )
+ {
+ $db->_die('The deletion log could not be selected.');
+ }
+ if ( $db->numrows() > 0 )
+ {
+ $r = $db->fetchrow();
+ $standard_404 .= '<p>' . $lang->get('page_msg_404_was_deleted', array(
+ 'delete_time' => enano_date(ED_DATE | ED_TIME, $r['time_id']),
+ 'delete_reason' => htmlspecialchars($r['edit_summary']),
+ 'rollback_flags' => 'href="'.makeUrl($paths->page, 'do=rollback&id='.$r['log_id']).'" onclick="ajaxRollback(\''.$r['log_id'].'\'); return false;"'
+ ))
+ . '</p>';
+ if ( $session->user_level >= USER_LEVEL_ADMIN )
+ {
+ $standard_404 .= '<p>' . $lang->get('page_msg_404_admin_opts', array(
+ 'detag_link' => makeUrl($paths->page, 'do=detag', true)
+ ))
+ . '</p>';
+ }
+ }
+ $db->free_result();
+ }
+ $standard_404 .= '<p>
+ ' . $lang->get('page_msg_404_http_response') . '
+ </p>';
+
+ $parser = $template->makeParserText($msg);
+ $parser->assign_vars(array(
+ 'STANDARD404' => $standard_404
+ ));
+
+ $msg = RenderMan::render($parser->run());
+ eval( '?>' . $msg );
+ }
+
+ /**
+ * Display the categories a page is in. If the current page is a category, its contents will also be printed.
+ */
+
+ function display_categories()
+ {
+ global $db, $session, $paths, $template, $plugins; // Common objects
+ global $lang;
+
+ $html = '';
+
+ if ( $this->namespace == 'Category' )
+ {
+ // Show member pages and subcategories
+ $q = $db->sql_query('SELECT p.urlname, p.namespace, p.name, p.namespace=\'Category\' AS is_category FROM '.table_prefix.'categories AS c
+ LEFT JOIN '.table_prefix.'pages AS p
+ ON ( p.urlname = c.page_id AND p.namespace = c.namespace )
+ WHERE c.category_id=\'' . $db->escape($this->page_id) . '\'
+ ORDER BY is_category DESC, p.name ASC;');
+ if ( !$q )
+ {
+ $db->_die();
+ }
+ $html .= '<h3>' . $lang->get('onpage_cat_heading_subcategories') . '</h3>';
+ $html .= '<div class="tblholder">';
+ $html .= '<table border="0" cellspacing="1" cellpadding="4">';
+ $html .= '<tr>';
+ $ticker = 0;
+ $counter = 0;
+ $switched = false;
+ $class = 'row1';
+ while ( $row = $db->fetchrow($q) )
+ {
+ if ( $row['is_category'] == 0 && !$switched )
+ {
+ if ( $counter > 0 )
+ {
+ // Fill-in
+ while ( $ticker < 3 )
+ {
+ $ticker++;
+ $html .= '<td class="' . $class . '" style="width: 33.3%;"></td>';
+ }
+ }
+ else
+ {
+ $html .= '<td class="' . $class . '">' . $lang->get('onpage_cat_msg_no_subcategories') . '</td>';
+ }
+ $html .= '</tr></table></div>' . "\n\n";
+ $html .= '<h3>' . $lang->get('onpage_cat_heading_pages') . '</h3>';
+ $html .= '<div class="tblholder">';
+ $html .= '<table border="0" cellspacing="1" cellpadding="4">';
+ $html .= '<tr>';
+ $counter = 0;
+ $ticker = -1;
+ $switched = true;
+ }
+ $counter++;
+ $ticker++;
+ if ( $ticker == 3 )
+ {
+ $html .= '</tr><tr>';
+ $ticker = 0;
+ $class = ( $class == 'row3' ) ? 'row1' : 'row3';
+ }
+ $html .= "<td class=\"{$class}\" style=\"width: 33.3%;\">"; // " to workaround stupid jEdit bug
+
+ $link = makeUrlNS($row['namespace'], sanitize_page_id($row['urlname']));
+ $html .= '<a href="' . $link . '"';
+ $key = $paths->nslist[$row['namespace']] . sanitize_page_id($row['urlname']);
+ if ( !isPage( $key ) )
+ {
+ $html .= ' class="wikilink-nonexistent"';
+ }
+ $html .= '>';
+ $title = get_page_title_ns($row['urlname'], $row['namespace']);
+ $html .= htmlspecialchars($title);
+ $html .= '</a>';
+
+ $html .= "</td>";
+ }
+ if ( !$switched )
+ {
+ if ( $counter > 0 )
+ {
+ // Fill-in
+ while ( $ticker < 2 )
+ {
+ $ticker++;
+ $html .= '<td class="' . $class . '" style="width: 33.3%;"></td>';
+ }
+ }
+ else
+ {
+ $html .= '<td class="' . $class . '">' . $lang->get('onpage_cat_msg_no_subcategories') . '</td>';
+ }
+ $html .= '</tr></table></div>' . "\n\n";
+ $html .= '<h3>' . $lang->get('onpage_cat_heading_pages') . '</h3>';
+ $html .= '<div class="tblholder">';
+ $html .= '<table border="0" cellspacing="1" cellpadding="4">';
+ $html .= '<tr>';
+ $counter = 0;
+ $ticker = 0;
+ $switched = true;
+ }
+ if ( $counter > 0 )
+ {
+ // Fill-in
+ while ( $ticker < 2 )
+ {
+ $ticker++;
+ $html .= '<td class="' . $class . '" style="width: 33.3%;"></td>';
+ }
+ }
+ else
+ {
+ $html .= '<td class="' . $class . '">' . $lang->get('onpage_cat_msg_no_pages') . '</td>';
+ }
+ $html .= '</tr></table></div>' . "\n\n";
+ }
+
+ if ( $this->namespace != 'Special' && $this->namespace != 'Admin' )
+ {
+ $html .= '<div class="mdg-comment" style="margin: 10px 0 0 0;" id="category_box_wrapper">';
+ $html .= '<div style="float: right;">';
+ $html .= '(<a href="#" onclick="ajaxCatToTag(); return false;">' . $lang->get('tags_catbox_link') . '</a>)';
+ $html .= '</div>';
+ $html .= '<div id="mdgCatBox">' . $lang->get('catedit_catbox_lbl_categories') . ' ';
+
+ $q = $db->sql_query('SELECT category_id FROM ' . table_prefix . "categories WHERE page_id = '$this->page_id' AND namespace = '$this->namespace';");
+ if ( !$q )
+ $db->_die();
+
+ if ( $row = $db->fetchrow() )
+ {
+ $list = array();
+ do
+ {
+ $cid = sanitize_page_id($row['category_id']);
+ $title = get_page_title_ns($cid, 'Category');
+ $link = makeUrlNS('Category', $cid);
+ $list[] = '<a href="' . $link . '">' . htmlspecialchars($title) . '</a>';
+ }
+ while ( $row = $db->fetchrow($q) );
+ $html .= implode(', ', $list);
+ }
+ else
+ {
+ $html .= $lang->get('catedit_catbox_lbl_uncategorized');
+ }
+
+ $can_edit = ( $session->get_permissions('edit_cat') && ( !$paths->page_protected || $session->get_permissions('even_when_protected') ) );
+ if ( $can_edit )
+ {
+ $edit_link = '<a href="' . makeUrl($paths->page, 'do=catedit', true) . '" onclick="ajaxCatEdit(); return false;">' . $lang->get('catedit_catbox_link_edit') . '</a>';
+ $html .= ' [ ' . $edit_link . ' ]';
+ }
+
+ $html .= '</div></div>';
+ }
+ return $html;
+ }
+
+ /**
+ * Pull in switches as to whether a specific toolbar button should be used or not. This sets things up according to the current page being displayed.
+ * @return array Associative
+ */
+
+ function set_conds()
+ {
+ global $db, $session, $paths, $template, $plugins; // Common objects
+
+ if ( !$this->perms )
+ $this->perms = $session->fetch_page_acl($this->page_id, $this->namespace);
+
+ if ( !$this->perms )
+ {
+ // We're trying to send a page WAY too early (session hasn't been started yet), such as for a redirect. Send a default set of conds because
+ // there's NO way to get permissions to determine anything otherwise. Yes, starting $session here might be dangerous.
+ $this->conds = array(
+ 'article' => true,
+ 'comments' => false,
+ 'edit' => false,
+ 'viewsource' => false,
+ 'history' => false,
+ 'rename' => false,
+ 'delvote' => false,
+ 'resetvotes' => false,
+ 'delete' => false,
+ 'printable' => false,
+ 'protect' => false,
+ 'setwikimode' => false,
+ 'clearlogs' => false,
+ 'password' => false,
+ 'acledit' => false,
+ 'adminpage' => false
+ );
+ return $this->conds;
+ }
+
+ // die('have perms: <pre>' . print_r($this->perms, true) . "\n---------------------------------\nBacktrace:\n" . enano_debug_print_backtrace(true));
+
+ $enforce_protection = ( $this->page_protected && ( ( $session->check_acl_scope('even_when_protected', $this->namespace) && !$this->perms->get_permissions('even_when_protected') ) || !$session->check_acl_scope('even_when_protected', $this->namespace) ) );
+
+ $conds = array();
+
+ // Article: always show
+ $conds['article'] = true;
+
+ // Discussion: Show if comments are enabled on the site, and if comments are on for this page.
+ $conds['comments'] = $this->perms->get_permissions('read') && getConfig('enable_comments', '1')=='1' && $this->cdata['comments_on'] == 1;
+
+ // Edit: Show if we have permission to edit the page, and if we don't have protection in effect
+ $conds['edit'] = $this->perms->get_permissions('read') && $session->check_acl_scope('edit_page', $this->namespace) && $this->perms->get_permissions('edit_page') && !$enforce_protection;
+
+ // View source: Show if we have permission to view source and either ACLs prohibit editing or protection is in effect
+ $conds['viewsource'] = $session->check_acl_scope('view_source', $this->namespace) && $this->perms->get_permissions('view_source') && ( !$this->perms->get_permissions('edit_page') || $enforce_protection ) && $this->namespace != 'API';
+
+ // History: Show if we have permission to see history and if the page exists
+ $conds['history'] = $session->check_acl_scope('history_view', $this->namespace) && $this->exists && $this->perms->get_permissions('history_view');
+
+ // Rename: Show if the page exists, if we have permission to rename, and if protection isn't in effect
+ $conds['rename'] = $session->check_acl_scope('rename', $this->namespace) && $this->exists && $this->perms->get_permissions('rename') && !$enforce_protection;
+
+ // Vote-to-delete: Show if we have Wiki Mode on, if we have permission to vote for deletion, and if the page exists (can't vote to delete a nonexistent page)
+ $conds['delvote'] = $this->wiki_mode && $session->check_acl_scope('vote_delete', $this->namespace) && $this->perms->get_permissions('vote_delete') && $this->exists;
+
+ // Reset votes: Show if we have Wiki Mode on, if we have permission to reset votes, if the page exists, and if there's at least one vote
+ $conds['resetvotes'] = $session->check_acl_scope('vote_reset', $this->namespace) && $this->wiki_mode && $this->exists && $this->perms->get_permissions('vote_reset') && $this->cdata['delvotes'] > 0;
+
+ // Delete page: Show if the page exists and if we have permission to delete it
+ $conds['delete'] = $session->check_acl_scope('delete_page', $this->namespace) && $this->exists && $this->perms->get_permissions('delete_page');
+
+ // Printable view: Show if the page exists
+ $conds['printable'] = $this->exists;
+
+ // Protect: Show if we have Wiki Mode on, if the page exists, and if we have permission to protect the page.
+ $conds['protect'] = $session->check_acl_scope('protect', $this->namespace) && $this->wiki_mode && $this->exists && $this->perms->get_permissions('protect');
+
+ // Set Wiki Mode: Show if the page exists and if we have permission to set wiki mode
+ $conds['setwikimode'] = $session->check_acl_scope('set_wiki_mode', $this->namespace) && $this->exists && $this->perms->get_permissions('set_wiki_mode');
+
+ // Clear logs: Show if we have permission to clear logs
+ $conds['clearlogs'] = $session->check_acl_scope('clear_logs', $this->namespace) && $this->perms->get_permissions('clear_logs');
+
+ // Set password: a little bit complicated. If there's a password, check for password_reset; else, check for password_set.
+ $conds['password'] = empty($this->cdata['password']) ?
+ $session->check_acl_scope('password_set', $this->namespace) && $this->perms->get_permissions('password_set') :
+ $session->check_acl_scope('password_reset', $this->namespace) && $this->perms->get_permissions('password_reset');
+
+ // Edit ACLs: Show if this is a non-Enano page that's calling the Enano API and (a) if we have permissions to edit ACLs or (b) we're an admin AND ACL_ALWAYS_ALLOW_ADMIN_EDIT_ACL is on
+ $conds['acledit'] = $this->namespace != 'API' && $session->check_acl_scope('edit_acl', $this->namespace) && ( $this->perms->get_permissions('edit_acl') || ( defined('ACL_ALWAYS_ALLOW_ADMIN_EDIT_ACL') && $session->user_level >= USER_LEVEL_ADMIN ) );
+
+ // Admin page: Show if the page exists and if we're an admin
+ $conds['adminpage'] = $session->user_level >= USER_LEVEL_ADMIN && $this->exists;
+
+ // Allow plugins to change stuff
+ $code = $plugins->setHook('page_conds_set');
+ foreach ( $code as $cmd )
+ {
+ eval($cmd);
+ }
+
+ $this->conds = $conds;
+ }
+
+ /**
+ * Return page conditions
+ * @return array
+ */
+
+ public function get_conds()
+ {
+ if ( empty($this->conds) )
+ $this->set_conds();
+
+ return $this->conds;
+ }
+
+ /**
+ * Just tell us if the current page exists or not.
+ * @return bool
+ */
+
+ public function exists()
+ {
+ return $this->exists;
+ }
+
+ /**
+ * Return cdata
+ * @return array
+ */
+
+ public function get_cdata()
+ {
+ return $this->cdata;
+ }
+
+ /**
+ * Bake, or finalize the processing of, a cdata array.
+ * @static
+ * @access public
+ */
+
+ public static function bake_cdata($cdata)
+ {
+ global $db, $session, $paths, $template, $plugins; // Common objects
+
+ // urlname_nons is the actual page_id.
+ $cdata['urlname_nons'] = $cdata['urlname'];
+ if ( isset($paths->nslist[ $cdata['namespace'] ]) )
+ {
+ $cdata['urlname'] = $paths->nslist[ $cdata['namespace'] ] . $cdata['urlname'];
+ }
+ else
+ {
+ $ns_char = substr($paths->nslist['Special'], -1);
+ $cdata['urlname'] = $cdata['namespace'] . $ns_char . $cdata['urlname'];
+ }
+
+ // add missing keys
+ $defaults = array(
+ 'special' => 0,
+ 'visible' => 0,
+ 'comments_on' => 1,
+ 'protected' => 0,
+ 'delvotes' => 0,
+ 'delvote_ips' => serialize(array()),
+ 'wiki_mode' => 2,
+ 'page_format' => getConfig('default_page_format', 'wikitext')
+ );
+ foreach ( $defaults as $key => $value )
+ {
+ if ( !isset($cdata[$key]) )
+ $cdata[$key] = $value;
+ }
+
+ // fix up deletion votes
+ if ( empty($cdata['delvotes']) )
+ $cdata['delvotes'] = 0;
+
+ // fix up deletion vote IP list
+ if ( empty($cdata['delvote_ips']) )
+ $cdata['delvote_ips'] = serialize(array());
+
+ // calculate wiki mode
+ $cdata['really_wiki_mode'] = ( $cdata['wiki_mode'] == 1 || ( $cdata['wiki_mode'] == 2 && getConfig('wiki_mode', 0) == 1 ) );
+
+ // calculate protection
+ $cdata['really_protected'] = ( $cdata['protected'] > 0 );
+ if ( $cdata['protected'] == 2 )
+ {
+ $cdata['really_protected'] = !$session->user_logged_in || ( $session->user_logged_in && $session->reg_time + 86400*4 > time() );
+ }
+
+ return $cdata;
+ }
+
+ /**
+ * Grabs raw (unbaked) cdata from the database, caching if possible.
+ * @param string Page ID
+ * @param string Namespace.
+ * @static
+ */
+
+ public static function get_cdata_from_db($page_id, $namespace)
+ {
+ global $db, $session, $paths, $template, $plugins; // Common objects
+ static $cache = array();
+
+ $pathskey = $paths->get_pathskey($page_id, $namespace);
+ if ( isset($cache[$pathskey]) )
+ return $cache[$pathskey];
+
+ $page_id_db = $db->escape($page_id);
+ $namespace_db = $db->escape($namespace);
+
+ $q = $db->sql_query('SELECT p.*'
+ . ' FROM ' . table_prefix . "pages AS p\n"
+ . " WHERE p.urlname = '$page_id_db' AND p.namespace = '$namespace_db'\n"
+ . " GROUP BY p.urlname, p.name, p.namespace, p.page_order, p.special, p.visible, p.protected, p.wiki_mode, p.comments_on, p.delvotes, p.delvote_ips, p.page_format, p.password;");
+
+ if ( !$q )
+ $db->_die();
+
+ if ( $db->numrows() < 1 )
+ {
+ $db->free_result();
+ $cache[$pathskey] = false;
+ return false;
+ }
+
+ $row = $db->fetchrow();
+
+ // Get comment counts
+ // FIXME: Apparently there's a bit of recursion in here. Fetching permissions depends on this cdata function.
+ // Perhaps we should eliminate session's dependency on cdata? (What is it used for?)
+ $q = $db->sql_query('SELECT approved FROM ' . table_prefix . "comments WHERE page_id = '$page_id_db' AND namespace = '$namespace_db';");
+ // yay parallel assignment
+ $row['comments_approved'] = $row['comments_unapproved'] = $row['comments_spam'] = 0;
+ while ( $commentrow = $db->fetchrow() )
+ switch($commentrow['approved'])
+ {
+ case COMMENT_APPROVED:
+ default:
+ $row['comments_approved']++;
+ break;
+ case COMMENT_UNAPPROVED:
+ $row['comments_unapproved']++;
+ break;
+ case COMMENT_SPAM:
+ $row['comments_spam']++;
+ break;
+ }
+
+ $cache[$pathskey] = $row;
+ return $row;
+ }
}
/**