--- a/includes/paths.php Sun Mar 28 21:49:26 2010 -0400
+++ b/includes/paths.php Sun Mar 28 23:10:46 2010 -0400
@@ -18,1083 +18,1083 @@
class pathManager
{
- public $title, $pages, $custom_page, $cpage, $page, $fullpage, $page_exists, $page_id, $namespace, $nslist, $admin_tree, $wiki_mode, $page_protected, $template_cache, $external_api_page;
-
- /**
- * List of custom processing functions for namespaces. This is protected so trying to do anything with it will throw an error.
- * @access private
- * @var array
- */
-
- protected $namespace_processors;
-
- function __construct()
- {
- global $db, $session, $paths, $template, $plugins; // Common objects
-
- $GLOBALS['paths'] =& $this;
- $this->pages = Array();
-
- // DEFINE NAMESPACES HERE
- // The key names should NOT EVER be changed, or Enano will be very broken
- $namespace_delimiter = ( defined('WINDOWS_MOD_REWRITE_WORKAROUNDS') ) ? '.' : ':';
- $this->nslist = Array(
- 'Article' => '',
- 'User' => 'User' . $namespace_delimiter,
- 'File' => 'File' . $namespace_delimiter,
- 'Help' => 'Help' . $namespace_delimiter,
- 'Admin' => 'Admin' . $namespace_delimiter,
- 'Special' => 'Special' . $namespace_delimiter,
- 'System' => 'Enano' . $namespace_delimiter,
- 'Template' => 'Template' . $namespace_delimiter,
- 'Category' => 'Category' . $namespace_delimiter,
- 'API' => 'SystemAPI' . $namespace_delimiter,
- 'Project' => sanitize_page_id(getConfig('site_name')) . $namespace_delimiter,
- );
-
- // ACL types
- // These can also be added from within plugins
-
- $session->register_acl_type('read', AUTH_ALLOW, 'perm_read');
- $session->register_acl_type('post_comments', AUTH_ALLOW, 'perm_post_comments', Array('read'), 'Article|User|Project|Template|File|Help|System|Category');
- $session->register_acl_type('edit_comments', AUTH_ALLOW, 'perm_edit_comments', Array('post_comments'), 'Article|User|Project|Template|File|Help|System|Category');
- $session->register_acl_type('edit_page', AUTH_WIKIMODE, 'perm_edit_page', Array('view_source'), 'Article|User|Project|Template|File|Help|System|Category');
- $session->register_acl_type('edit_wysiwyg', AUTH_ALLOW, 'perm_edit_wysiwyg', Array(), 'Article|User|Project|Template|File|Help|System|Category');
- $session->register_acl_type('view_source', AUTH_WIKIMODE, 'perm_view_source', Array('read'), 'Article|User|Project|Template|File|Help|System|Category'); // Only used if the page is protected
- $session->register_acl_type('mod_comments', AUTH_DISALLOW, 'perm_mod_comments', Array('edit_comments'), 'Article|User|Project|Template|File|Help|System|Category');
- $session->register_acl_type('history_view', AUTH_WIKIMODE, 'perm_history_view', Array('read'), 'Article|User|Project|Template|File|Help|System|Category');
- $session->register_acl_type('history_rollback', AUTH_DISALLOW, 'perm_history_rollback', Array('history_view'), 'Article|User|Project|Template|File|Help|System|Category');
- $session->register_acl_type('history_rollback_extra', AUTH_DISALLOW, 'perm_history_rollback_extra', Array('history_rollback'), 'Article|User|Project|Template|File|Help|System|Category');
- $session->register_acl_type('protect', AUTH_DISALLOW, 'perm_protect', Array('read'), 'Article|User|Project|Template|File|Help|System|Category');
- $session->register_acl_type('rename', AUTH_WIKIMODE, 'perm_rename', Array('read'), 'Article|User|Project|Template|File|Help|System|Category');
- $session->register_acl_type('clear_logs', AUTH_DISALLOW, 'perm_clear_logs', Array('read', 'protect', 'even_when_protected'), 'Article|User|Project|Template|File|Help|System|Category');
- $session->register_acl_type('vote_delete', AUTH_ALLOW, 'perm_vote_delete', Array('read'), 'Article|User|Project|Template|File|Help|System|Category');
- $session->register_acl_type('vote_reset', AUTH_DISALLOW, 'perm_vote_reset', Array('read'), 'Article|User|Project|Template|File|Help|System|Category');
- $session->register_acl_type('delete_page', AUTH_DISALLOW, 'perm_delete_page', Array(), 'Article|User|Project|Template|File|Help|System|Category');
- $session->register_acl_type('tag_create', AUTH_ALLOW, 'perm_tag_create', Array('read'), 'Article|User|Project|Template|File|Help|System|Category');
- $session->register_acl_type('tag_delete_own', AUTH_ALLOW, 'perm_tag_delete_own', Array('read', 'tag_create'), 'Article|User|Project|Template|File|Help|System|Category');
- $session->register_acl_type('tag_delete_other', AUTH_DISALLOW, 'perm_tag_delete_other', Array('read'), 'Article|User|Project|Template|File|Help|System|Category');
- $session->register_acl_type('set_wiki_mode', AUTH_DISALLOW, 'perm_set_wiki_mode', Array('read'), 'Article|User|Project|Template|File|Help|System|Category');
- $session->register_acl_type('password_set', AUTH_DISALLOW, 'perm_password_set', Array('read'), 'Article|User|Project|Template|File|Help|System|Category');
- $session->register_acl_type('password_reset', AUTH_DISALLOW, 'perm_password_reset', Array('read'), 'Article|User|Project|Template|File|Help|System|Category');
- $session->register_acl_type('mod_misc', AUTH_DISALLOW, 'perm_mod_misc', Array(), 'All');
- $session->register_acl_type('edit_cat', AUTH_WIKIMODE, 'perm_edit_cat', Array('read'), 'Article|User|Project|Template|File|Help|System|Category');
- $session->register_acl_type('even_when_protected', AUTH_DISALLOW, 'perm_even_when_protected', Array('edit_page', 'rename', 'mod_comments', 'edit_cat'), 'Article|User|Project|Template|File|Help|System|Category');
- $session->register_acl_type('create_page', AUTH_WIKIMODE, 'perm_create_page', Array(), 'All');
- $session->register_acl_type('upload_files', AUTH_DISALLOW, 'perm_upload_files', Array('create_page'), 'All');
- $session->register_acl_type('upload_new_version', AUTH_WIKIMODE, 'perm_upload_new_version', Array('upload_files'), 'All');
- $session->register_acl_type('html_in_pages', AUTH_DISALLOW, 'perm_html_in_pages', Array('edit_page'), 'Article|User|Project|Template|File|Help|System|Category|Admin');
- $session->register_acl_type('php_in_pages', AUTH_DISALLOW, 'perm_php_in_pages', Array('edit_page', 'html_in_pages'), 'Article|User|Project|Template|File|Help|System|Category|Admin');
- $session->register_acl_type('custom_user_title', AUTH_DISALLOW, 'perm_custom_user_title', Array(), 'User|Special');
- $session->register_acl_type('edit_acl', AUTH_DISALLOW, 'perm_edit_acl', Array());
-
- // DO NOT add new admin pages here! Use a plugin to call $paths->addAdminNode();
- $this->addAdminNode('adm_cat_general', 'adm_page_general_config', 'GeneralConfig', array(2, 2));
- $this->addAdminNode('adm_cat_general', 'adm_page_file_uploads', 'UploadConfig', array(2, 5));
- $this->addAdminNode('adm_cat_general', 'adm_page_file_types', 'UploadAllowedMimeTypes', array(1, 5));
- $this->addAdminNode('adm_cat_content', 'adm_page_manager', 'PageManager', array(1, 4));
- $this->addAdminNode('adm_cat_content', 'adm_page_editor', 'PageEditor', array(3, 3));
- $this->addAdminNode('adm_cat_content', 'adm_page_pg_groups', 'PageGroups', array(4, 3));
- $this->addAdminNode('adm_cat_appearance', 'adm_page_themes', 'ThemeManager', array(4, 4));
- $this->addAdminNode('adm_cat_appearance', 'adm_page_plugins', 'PluginManager', array(2, 4));
- $this->addAdminNode('adm_cat_appearance', 'adm_page_db_backup', 'DBBackup', array(1, 2));
- $this->addAdminNode('adm_cat_appearance', 'adm_page_lang_manager', 'LangManager', array(1, 3));
- $this->addAdminNode('adm_cat_appearance', 'adm_page_cache_manager', 'CacheManager', array(3, 1));
- $this->addAdminNode('adm_cat_users', 'adm_page_users', 'UserManager', array(3, 5));
- $this->addAdminNode('adm_cat_users', 'adm_page_user_groups', 'GroupManager', array(3, 2));
- $this->addAdminNode('adm_cat_users', 'adm_page_coppa', 'COPPA', array(4, 1));
- $this->addAdminNode('adm_cat_users', 'adm_page_mass_email', 'MassEmail', array(2, 3));
- $this->addAdminNode('adm_cat_users', 'adm_page_user_ranks', 'UserRanks', array(4, 5));
- $this->addAdminNode('adm_cat_security', 'adm_page_security_log', 'SecurityLog', array(3, 4));
- $this->addAdminNode('adm_cat_security', 'adm_page_ban_control', 'BanControl', array(2, 1));
-
- $code = $plugins->setHook('acl_rule_init');
- foreach ( $code as $cmd )
- {
- eval($cmd);
- }
-
- $this->wiki_mode = ( getConfig('wiki_mode') == '1' ) ? 1 : 0;
- $this->template_cache = Array();
- }
-
- function init($title)
- {
- global $db, $session, $paths, $template, $plugins; // Common objects
- global $lang;
- global $cache;
-
- $code = $plugins->setHook('paths_init_before');
- foreach ( $code as $cmd )
- {
- eval($cmd);
- }
-
- if ( defined('ENANO_INTERFACE_INDEX') || defined('ENANO_INTERFACE_AJAX') || defined('IN_ENANO_UPGRADE') )
- {
- if ( empty($title) )
- $title = get_title();
-
- if ( empty($title) && !have_blank_urlname_page() )
- {
- $this->main_page();
- }
- if ( strstr($title, ' ') || strstr($title, '+') || strstr($title, '%20') )
- {
- $title = sanitize_page_id($title);
- redirect(makeUrl($title), '', '', 0);
- }
- $title = sanitize_page_id($title);
- // We've got the title, pull the namespace from it
- $namespace = 'Article';
- $page_id = $title;
- foreach ( $this->nslist as $ns => $prefix )
- {
- $prefix_len = strlen($prefix);
- if ( substr($title, 0, $prefix_len) == $prefix )
- {
- $page_id = substr($title, $prefix_len);
- $namespace = $ns;
- }
- }
- $this->namespace = $namespace;
- $this->fullpage = $title;
- if ( $namespace == 'Special' || $namespace == 'Admin' )
- {
- list($page_id) = explode('/', $page_id);
- }
- $this->page = $this->nslist[$namespace] . $page_id;
- $this->page_id = $page_id;
- // die("All done setting parameters. What we've got:<br/>namespace: $namespace<br/>fullpage: $this->fullpage<br/>page: $this->page<br/>page_id: $this->page_id");
- }
- else
- {
- // Starting up Enano with the API from a page that wants to do its own thing. Generate
- // metadata for an anonymous page and avoid redirection at all costs.
- if ( isset($GLOBALS['title']) )
- {
- $title =& $GLOBALS['title'];
- }
- else
- {
- $title = basename($_SERVER['SCRIPT_NAME']);
- }
- $base_uri = scriptPath == '' ? ltrim($_SERVER['SCRIPT_NAME'], '/') : str_replace( scriptPath . '/', '', $_SERVER['SCRIPT_NAME'] );
-
- $this->page = $this->nslist['API'] . sanitize_page_id($base_uri);
- $this->fullpage = $this->nslist['API'] . sanitize_page_id($base_uri);
- $this->namespace = 'API';
- $this->cpage = array(
- 'name' => $title,
- 'urlname' => sanitize_page_id($base_uri),
- 'namespace' => 'API',
- 'special' => 1,
- 'visible' => 1,
- 'comments_on' => 1,
- 'protected' => 1,
- 'delvotes' => 0,
- 'delvote_ips' => '',
- 'page_format' => getConfig('default_page_format', 'wikitext')
- );
- $this->external_api_page = true;
- $code = $plugins->setHook('paths_external_api_page');
- foreach ( $code as $cmd )
- {
- eval($cmd);
- }
- }
-
- $this->page = sanitize_page_id($this->page);
- $this->fullpage = sanitize_page_id($this->fullpage);
-
- if(isset($this->pages[$this->page]))
- {
- $this->page_exists = true;
- $this->cpage = $this->pages[$this->page];
- $this->page_id =& $this->cpage['urlname_nons'];
- $this->namespace = $this->cpage['namespace'];
- if(!isset($this->cpage['wiki_mode'])) $this->cpage['wiki_mode'] = 2;
-
- // Determine the wiki mode for this page, now that we have this->cpage established
- if($this->cpage['wiki_mode'] == 2)
- {
- $this->wiki_mode = (int)getConfig('wiki_mode');
- }
- else
- {
- $this->wiki_mode = $this->cpage['wiki_mode'];
- }
- // Allow the user to create/modify his user page uncondtionally (admins can still protect the page)
- if($this->page == $this->nslist['User'].str_replace(' ', '_', $session->username))
- {
- $this->wiki_mode = true;
- }
- // And above all, if the site requires wiki mode to be off for non-logged-in users, disable it now
- if(getConfig('wiki_mode_require_login')=='1' && !$session->user_logged_in)
- {
- $this->wiki_mode = false;
- }
- if($this->cpage['protected'] == 2)
- {
- // The page is semi-protected, determine permissions
- if($session->user_logged_in && $session->reg_time + 60*60*24*4 < time())
- {
- $this->page_protected = 0;
- }
- else
- {
- $this->page_protected = 1;
- }
- }
- else
- {
- $this->page_protected = $this->cpage['protected'];
- }
- }
- else
- {
- $this->page_exists = false;
- $page_name = dirtify_page_id($this->page);
- $page_name = str_replace('_', ' ', $page_name);
-
- $pid_cleaned = sanitize_page_id($this->page);
- if ( $pid_cleaned != $this->page )
- {
- redirect(makeUrl($pid_cleaned), 'Sanitizer message', 'page id sanitized', 0);
- }
-
- if ( !is_array($this->cpage) )
- {
- $this->cpage = Array(
- 'name' => $page_name,
- 'urlname' => $this->page,
- 'namespace' => 'Article',
- 'special' => 0,
- 'visible' => 0,
- 'comments_on' => 1,
- 'protected' => 0,
- 'delvotes' => 0,
- 'delvote_ips' => '',
- 'wiki_mode' => 2,
- 'page_format' => getConfig('default_page_format', 'wikitext')
- );
- }
- // Look for a namespace prefix in the urlname, and assign a different namespace, if necessary
- $k = array_keys($this->nslist);
- for($i=0;$i<sizeof($this->nslist);$i++)
- {
- $ln = strlen($this->nslist[$k[$i]]);
- if( substr($this->page, 0, $ln) == $this->nslist[$k[$i]] )
- {
- $this->cpage['namespace'] = $k[$i];
- $this->cpage['urlname_nons'] = substr($this->page, strlen($this->nslist[$this->cpage['namespace']]), strlen($this->page));
- if(!isset($this->cpage['wiki_mode']))
- {
- $this->cpage['wiki_mode'] = 2;
- }
- }
- }
- $this->namespace = $this->cpage['namespace'];
- $this->page_id =& $this->cpage['urlname_nons'];
-
- if($this->namespace=='System')
- {
- $this->cpage['protected'] = 1;
- }
- if($this->namespace == 'Special' && !$this->external_api_page)
- {
- // Can't load nonexistent pages
- if( is_string(get_main_page()) )
- {
- $main_page = makeUrl(get_main_page());
- }
- else
- {
- $main_page = makeUrl($this->pages[0]['urlname']);
- }
- redirect($main_page, $lang->get('page_msg_special_404_title'), $lang->get('page_msg_special_404_body', array('sp_link' => makeUrlNS('Special', 'SpecialPages'))), 15);
- exit;
- }
- // Allow the user to create/modify his user page uncondtionally (admins can still protect the page)
- if($this->page == $this->nslist['User'].str_replace(' ', '_', $session->username))
- {
- $this->wiki_mode = true;
- }
- }
- // This is used in the admin panel to keep track of form submission targets
- $this->cpage['module'] = $this->cpage['urlname'];
-
- $this->cpage['require_admin'] = ( $this->cpage['namespace'] === 'Admin' );
-
- // Page is set up, call any hooks
- $code = $plugins->setHook('page_set');
- foreach ( $code as $cmd )
- {
- eval($cmd);
- }
-
- profiler_log('Paths and CMS core initted');
- $session->init_permissions();
- }
-
- /**
- * Fetch cdata (metadata) for a page.
- * @param string Page ID
- * @param string Namespace
- * @return array
- */
-
- function get_cdata($page_id, $namespace)
- {
- global $db, $session, $paths, $template, $plugins; // Common objects
-
- $pathskey = $this->get_pathskey($page_id, $namespace);
- if ( isset($this->pages[$pathskey]) )
- return $this->pages[$pathskey];
-
- $page = namespace_factory($page_id, $namespace);
- $cdata = $page->get_cdata();
-
- $this->pages[$pathskey] = $cdata;
- return $cdata;
- }
-
- /**
- * For a given page ID and namespace, generate a flat string that can be used to access $paths->pages.
- * @param string Page ID
- * @param string Namespace
- */
-
- function get_pathskey($page_id, $namespace)
- {
- return ( isset($this->nslist[$namespace]) ) ? "{$this->nslist[$namespace]}{$page_id}" : "{$namespace}:{$page_id}";
- }
-
- function add_page($flags)
- {
- global $lang;
- $flags['urlname_nons'] = $flags['urlname'];
- $flags['urlname'] = $this->nslist[$flags['namespace']] . $flags['urlname']; // Applies the User:/File:/etc prefixes to the URL names
-
- if ( is_object($lang) )
- {
- if ( preg_match('/^[a-z0-9]+_[a-z0-9_]+$/', $flags['name']) )
- $flags['name'] = $lang->get($flags['name']);
- }
-
- $flags['require_admin'] = ( $flags['namespace'] === 'Admin' );
- $flags['page_exists'] = true;
-
- $this->pages[$flags['urlname']] = $flags;
- }
-
- function main_page()
- {
- if( is_string(get_main_page()) )
- {
- $main_page = makeUrl(get_main_page());
- }
- else
- {
- $main_page = makeUrl($this->pages[0]['urlname']);
- }
- redirect($main_page, 'Redirecting...', 'Invalid request, redirecting to main page', 0);
- exit;
- }
-
- function sysmsg($n)
- {
- global $db, $session, $paths, $template, $plugins; // Common objects
- static $sm_cache = array();
-
- if ( isset($sm_cache[$n]) )
- return $sm_cache[$n];
-
- // 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 )
- {
- $db->_die('Error during generic selection of system page data.');
- }
- if($db->numrows() < 1)
- {
- return false;
- //$db->_die('Error during generic selection of system page data: there were no rows in the text table that matched the page text query.');
- }
- $r = $db->fetchrow();
- $db->free_result();
- $message = $r['page_text'];
-
- $message = preg_replace('/<noinclude>(.*?)<\/noinclude>/is', '', $message);
- $message = preg_replace('/<nodisplay>(.*?)<\/nodisplay>/is', '\\1', $message);
-
- $sm_cache[$n] = $message;
-
- return $message;
- }
- function get_pageid_from_url()
- {
- return get_title(true, true);
- }
- // Parses a (very carefully formed) array into Javascript code compatible with the Tigra Tree Menu used in the admin menu
- function parseAdminTree()
- {
- global $lang;
-
- $k = array_keys($this->admin_tree);
- $i = 0;
- $ret = '';
- $icon = $this->make_sprite_icon(4, 2);
- $ret .= "var TREE_ITEMS = ";
- $tree = array(
- array($icon . $lang->get('adm_btn_home'), "javascript:ajaxPage('{$this->nslist['Admin']}Home');")
- );
-
- $root =& $tree[0];
-
- foreach($k as $key)
- {
- $i++;
- $name = $lang->get($key);
- $group = array($name, "javascript:trees[0].toggle($i)");
-
- foreach($this->admin_tree[$key] as $nodeid => $c)
- {
- $i++;
- $name = $lang->get($c['name']);
- if ( $c['icon'] && $c['icon'] != cdnPath . '/images/spacer.gif' )
- {
- if ( is_array($c['icon']) )
- {
- // this is a sprite reference
- list($ix, $iy) = $c['icon'];
- $icon = $this->make_sprite_icon($ix, $iy);
- }
- else
- {
- $icon = "<img alt=\"\" src=\"{$c['icon']}\" style=\"border-width: 0; margin-right: 3px;\" /> ";
- }
- }
- else
- {
- $icon = '';
- }
- $group[] = array("$icon$name", "javascript:ajaxPage('{$this->nslist['Admin']}{$c['pageid']}');");
- }
-
- $root[] = $group;
- }
- $icon = $this->make_sprite_icon(1, 1);
- $root[] = array(
- $icon . $lang->get('adm_btn_logout'),
- "javascript:ajaxPage('{$this->nslist['Admin']}AdminLogout');"
- );
- $root[] = array(
- "<span id=\"keepalivestat\">" . $lang->get('adm_btn_keepalive_loading') . "</span>",
- "javascript:ajaxToggleKeepalive();",
- array(
- $lang->get('adm_btn_keepalive_about'),
- "javascript:aboutKeepAlive();"
- )
- );
-
- $ret .= enano_json_encode($tree) . ';';
-
- // I used this while I painstakingly wrote the Runt code that auto-expands certain nodes based on the value of a bitfield stored in a cookie. *shudders*
- // $ret .= " ['(debug) Clear menu bitfield', 'javascript:createCookie(\\'admin_menu_state\\', \\'1\\', 365);'],\n";
- return $ret;
- }
-
- /**
- * Internal function to generate HTML code for an icon in the admin panel tree which is sprited.
- * @param int X index of icon
- * @param int Y index of icon
- * @return string
- */
-
- function make_sprite_icon($ix, $iy)
- {
- $xpos = 16 * ( $ix - 1 );
- $ypos = 16 * ( $iy - 1 );
- return "<img alt=\"\" src=\"" . cdnPath . "/images/spacer.gif\" class=\"adminiconsprite\" style=\"border-width: 0; margin-right: 3px; background-position: -{$xpos}px -{$ypos}px;\" /> ";
- }
-
- /**
- * Creates a new entry in the administration panel's navigation tree.
- * @param string Section name - if this is a language string identifier, it will be sent through $lang->get()
- * @param string The title of the page, also may be a language string identifier
- * @param string The page ID of the admin page, the namespace Admin is assumed
- * @param string Optional. The path to a 16x16 image that will be displayed as the icon for this admin page
- */
-
- function addAdminNode($section, $page_title, $url, $icon = false)
- {
- if ( !$icon )
- {
- $icon = cdnPath . '/images/spacer.gif';
- }
- if(!isset($this->admin_tree[$section]))
- {
- $this->admin_tree[$section] = Array();
- }
- $this->admin_tree[$section][] = Array(
- 'name' => $page_title,
- 'pageid' => $url,
- 'icon' => $icon
- );
- }
- function getParam($id = 0)
- {
- $title = $this->fullpage;
- list(, $ns) = RenderMan::strToPageID($title);
- $title = substr($title, strlen($this->nslist[$ns]));
- $regex = '/^' . str_replace('/', '\\/', preg_quote($this->nslist[$ns])) . '\\/?/';
- $title = preg_replace($regex, '', $title);
- $title = explode('/', $title);
- $id = $id + 1;
- return ( isset($title[$id]) ) ? $title[$id] : false;
- }
-
- function getAllParams()
- {
- $title = $this->fullpage;
- $regex = '/^' . str_replace('/', '\\/', preg_quote($this->nslist[$this->namespace])) . '\\/?/';
- $title = preg_replace($regex, '', $title);
- $title = explode('/', $title);
- unset($title[0]);
- return implode('/', $title);
- }
-
- /**
- * Creates a new namespace in memory
- * @param string $id the namespace ID
- * @param string $prefix the URL prefix, must not be blank or already used
- * @return bool true on success false on failure
- */
-
- function create_namespace($id, $prefix)
- {
- if(in_array($prefix, $this->nslist))
- {
- // echo '<b>Warning:</b> pathManager::create_namespace: Prefix "'.$prefix.'" is already taken<br />';
- return false;
- }
- if( isset($this->nslist[$id]) )
- {
- // echo '<b>Warning:</b> pathManager::create_namespace: Namespace ID "'.$prefix.'" is already taken<br />';
- return false;
- }
- $this->nslist[$id] = $prefix;
- }
-
- /**
- * Deprecated.
- */
-
- function update_metadata_cache()
- {
- global $db, $session, $paths, $template, $plugins; // Common objects
-
- return false;
- }
-
- /**
- * Takes a result row from the pages table and calculates correct values for it.
- * @param array
- * @return array
- */
-
- function calculate_metadata_from_row($r)
- {
- return Namespace_Default::bake_cdata($r);
- }
-
- /**
- * Registers a handler to manually process a namespace instead of the default PageProcessor behavior.
- * The first and only parameter passed to the processing function will be the PageProcessor instance.
- * @param string Namespace to process
- * @param mixed Function address. Either a function name or an array of the form array(0 => mixed (string:class name or object), 1 => string:method)
- */
-
- function register_namespace_processor($namespace, $function)
- {
- if ( isset($this->namespace_processors[$namespace]) )
- {
- $processorname = ( is_string($this->namespace_processors[$namespace]) ) ?
- $this->namespace_processors[$namespace] :
- ( is_object($this->namespace_processors[$namespace][0]) ? get_class($this->namespace_processors[$namespace][0]) : $this->namespace_processors[$namespace][0] ) . '::' .
- $this->namespace_processors[$namespace][1];
-
- trigger_error("Namespace \"$namespace\" is already being processed by $processorname - replacing caller", E_USER_WARNING);
- }
- if ( !is_string($function) )
- {
- if ( !is_array($function) )
- return false;
- if ( count($function) != 2 )
- return false;
- if ( !is_string($function[0]) && !is_object($function[0]) )
- return false;
- if ( !is_string($function[1]) )
- return false;
- }
-
- // security: don't allow Special or Admin namespaces to be overridden
- if ( $namespace == 'Special' || $namespace == 'Admin' )
- {
- trigger_error("Security manager denied attempt to override processor for $namespace", E_USER_ERROR);
- }
-
- $this->namespace_processors[$namespace] = $function;
- }
-
- /**
- * Returns a namespace processor if one exists, otherwise returns false.
- * @param string Namespace
- * @return mixed
- */
-
- function get_namespace_processor($namespace)
- {
- return ( isset($this->namespace_processors[$namespace]) ) ? $this->namespace_processors[$namespace] : false;
- }
-
- /**
- * Fetches the page texts for searching
- */
-
- function fetch_page_search_texts()
- {
- global $db, $session, $paths, $template, $plugins; // Common objects
- $texts = Array();
- $q = $db->sql_query('SELECT t.page_id,t.namespace,t.page_text,t.char_tag FROM '.table_prefix.'page_text AS t
- LEFT JOIN '.table_prefix.'pages AS p
- ON t.page_id=p.urlname
- WHERE p.namespace=t.namespace
- AND ( p.password=\'\' OR p.password=\'da39a3ee5e6b4b0d3255bfef95601890afd80709\' )
- AND p.visible=1;'); // Only indexes "visible" pages
-
- if( !$q )
- {
- return false;
- }
- while($row = $db->fetchrow())
- {
- $pid = $this->nslist[$row['namespace']] . $row['page_id'];
- $texts[$pid] = $row['page_text'];
- }
- $db->free_result();
-
- return $texts;
- }
-
- /**
- * Generates an SQL query to grab all of the text
- */
-
- function fetch_page_search_resource()
- {
- global $db, $session, $paths, $template, $plugins; // Common objects
- // sha1('') returns "da39a3ee5e6b4b0d3255bfef95601890afd80709"
-
- $concat_column = ( ENANO_DBLAYER == 'MYSQL' ) ?
- 'CONCAT(\'ns=\',t.namespace,\';pid=\',t.page_id)' :
- "'ns=' || t.namespace || ';pid=' || t.page_id";
-
- $texts = 'SELECT t.page_text, ' . $concat_column . ' AS page_idstring, t.page_id, t.namespace FROM '.table_prefix.'page_text AS t
- LEFT JOIN '.table_prefix.'pages AS p
- ON ( t.page_id=p.urlname AND t.namespace=p.namespace )
- WHERE p.namespace=t.namespace
- AND ( p.password=\'\' OR p.password=\'da39a3ee5e6b4b0d3255bfef95601890afd80709\' )
- AND p.visible=1;'; // Only indexes "visible" pages
- return $texts;
- }
-
- /**
- * Builds a word list for search indexing.
- * @param string Text to index
- * @param string Page ID of the page being indexed
- * @param string Title of the page being indexed
- * @return array List of words
- */
-
- function calculate_word_list($text, $page_id, $page_name)
- {
- $page_id = dirtify_page_id($page_id);
- $text = preg_replace('/[^a-z0-9\']/i', ' ', $text);
- $page_id = preg_replace('/[^a-z0-9\']/i', ' ', $page_id);
- $page_name = preg_replace('/[^a-z0-9\']/i', ' ', $page_name);
- $text .= " $page_id $page_name";
- $text = explode(' ', $text);
- foreach ( $text as $i => &$word )
- {
- if ( strstr($word, "''") )
- $word = preg_replace("/[']{2,}/", '', $word);
- if ( strlen($word) < 2 )
- unset($text[$i]);
- }
- $text = array_unique(array_values($text));
- // for debugging purposes (usually XSS safe because of character stripping)
- // echo ' ' . implode(' ', $text) . '<br />';
- return $text;
- }
-
- /**
- * Rebuilds the site's entire search index. Considerably more exciting if run from the command line.
- * @param bool If true, verbose output.
- * @param bool If true, verbose + debugging output.
- */
-
- function rebuild_search_index($verbose = false, $debug = false)
- {
- global $db, $session, $paths, $template, $plugins; // Common objects
- require_once(ENANO_ROOT . '/includes/search.php');
-
- $progress = false;
- if ( class_exists('ProgressBar') )
- {
- // CLI only.
- $progress = new ProgressBar('Rebuilding search index: [', ']', 'Initializing...', 'green', 'blue', 'white', 'yellow');
- $verbose = false;
- $debug = false;
- $progress->start();
- }
-
- @set_time_limit(0);
-
- $q = $db->sql_query('DELETE FROM ' . table_prefix . 'search_index;');
- if ( !$q )
- $db->_die();
-
- $sha1_blank = sha1('');
-
- //
- // Index $pages_in_batch pages at a time
- //
- $pages_in_batch = 15;
-
- // First find out how many pages there are
- $q = $db->sql_query('SELECT COUNT(p.urlname) AS num_pages FROM ' . table_prefix . "page_text AS t\n"
- . " LEFT JOIN " . table_prefix . "pages AS p\n"
- . " ON ( p.urlname = t.page_id AND p.namespace = t.namespace )\n"
- . " WHERE ( p.password = '' OR p.password = '$sha1_blank' )\n"
- . " AND ( p.visible = 1 );");
- if ( !$q )
- $db->_die();
-
- list($num_pages) = $db->fetchrow_num();
- $num_pages = intval($num_pages);
- $loops = ceil($num_pages / $pages_in_batch);
- $master_word_list = array();
- $stopwords = get_stopwords();
-
- for ( $j = 0; $j < $loops; )
- {
- $offset = $j * $pages_in_batch;
-
- $j++;
-
- if ( $verbose && $debug )
- {
- echo "Running indexing round $j of $loops (offset $offset)\n" . ( isset($_SERVER['REQUEST_URI']) ? '<br />' : '' );
- }
-
- // this is friendly to both MySQL and PostgreSQL.
- $texts = $db->sql_query('SELECT p.name, p.visible, t.page_id, t.namespace, t.page_text FROM ' . table_prefix . "page_text AS t\n"
- . " LEFT JOIN " . table_prefix . "pages AS p\n"
- . " ON ( p.urlname = t.page_id AND p.namespace = t.namespace )\n"
- . " WHERE ( p.password = '' OR p.password = '$sha1_blank' )\n"
- . " AND ( p.visible = 1 )\n"
- . " LIMIT $pages_in_batch OFFSET $offset;", false);
- if ( !$texts )
- $db->_die();
-
- $k = $offset;
-
- if ( $row = $db->fetchrow($texts) )
- {
- do
- {
- $k++;
- if ( $verbose )
- {
- $mu = memory_get_usage();
- echo " Indexing page $k of $num_pages: {$row['namespace']}:{$row['page_id']}";
- if ( $debug )
- echo ", mem = $mu...";
- flush();
- }
- else if ( is_object($progress) )
- {
- $progress->update_text_quiet("$k/$num_pages {$row['namespace']}:{$row['page_id']}");
- $progress->set($k, $num_pages);
- }
-
- // skip this page if it's not supposed to be indexed
- if ( $row['visible'] == 0 )
- {
- if ( $verbose )
- {
- echo "skipped";
- if ( isset($_SERVER['REQUEST_URI']) )
- echo '<br />';
- echo "\n";
- }
- continue;
- }
-
- // Indexing identifier for the page in the DB
- $page_uniqid = "ns={$row['namespace']};pid=" . sanitize_page_id($row['page_id']);
- $page_uniqid = $db->escape($page_uniqid);
-
- // List of words on the page
- $wordlist = $this->calculate_word_list($row['page_text'], $row['page_id'], $row['name']);
-
- // Index calculation complete -- run inserts
- $inserts = array();
- foreach ( $wordlist as $word )
- {
- if ( in_array($word, $stopwords) || strval(intval($word)) === $word || strlen($word) < 3 )
- continue;
- $word_db = $db->escape($word);
- $word_db_lc = $db->escape(strtolower($word));
- if ( !in_array($word, $master_word_list) )
- {
- $inserts[] = "( '$word_db', '$word_db_lc', '$page_uniqid' )";
- }
- else
- {
- if ( $verbose && $debug )
- echo '.';
- $pid_col = ( ENANO_DBLAYER == 'MYSQL' ) ?
- "CONCAT( page_names, ',$page_uniqid' )":
- "page_names || ',$page_uniqid'";
- $q = $db->sql_query('UPDATE ' . table_prefix . "search_index SET page_names = $pid_col WHERE word = '$word_db';", false);
- if ( !$q )
- $db->_die();
- }
- }
- if ( count($inserts) > 0 )
- {
- if ( $verbose && $debug )
- echo 'i';
- $inserts = implode(",\n ", $inserts);
- $q = $db->sql_query('INSERT INTO ' . table_prefix . "search_index(word, word_lcase, page_names) VALUES\n $inserts;", false);
- if ( !$q )
- $db->_die();
- }
-
- $master_word_list = array_unique(array_merge($master_word_list, $wordlist));
- if ( $verbose )
- {
- if ( isset($_SERVER['REQUEST_URI']) )
- echo '<br />';
- echo "\n";
- }
- unset($inserts, $wordlist, $page_uniqid, $word_db, $q, $word, $row);
- }
- while ( $row = $db->fetchrow($texts) );
- }
- $db->free_result($texts);
- }
- if ( $verbose )
- {
- echo "Indexing complete.";
- if ( isset($_SERVER['REQUEST_URI']) )
- echo '<br />';
- echo "\n";
- }
- else if ( is_object($progress) )
- {
- $progress->update_text('Complete.');
- $progress->end();
- }
- return true;
- }
-
- /**
- * Partially rebuilds the search index, removing/inserting entries only for the current page
- * @param string $page_id
- * @param string $namespace
- */
-
- function rebuild_page_index($page_id, $namespace)
- {
- global $db, $session, $paths, $template, $plugins; // Common objects
- require_once(ENANO_ROOT . '/includes/search.php');
-
- if(!$db->sql_query('SELECT page_text FROM '.table_prefix.'page_text
- WHERE page_id=\''.$db->escape($page_id).'\' AND namespace=\''.$db->escape($namespace).'\';'))
- {
- return $db->get_error();
- }
- if ( $db->numrows() < 1 )
- return 'E: No rows';
- $idstring = $this->nslist[$namespace] . sanitize_page_id($page_id);
- if ( !isset($this->pages[$idstring]) )
- {
- return 'E: Can\'t find page metadata';
- }
- $row = $db->fetchrow();
- $db->free_result();
- $search = new Searcher();
-
- // if the page shouldn't be indexed, send a blank set of strings to the indexing engine
- if ( $this->pages[$idstring]['visible'] == 0 )
- {
- $search->buildIndex(Array("ns={$namespace};pid={$page_id}"=>''));
- }
- else
- {
- $search->buildIndex(Array("ns={$namespace};pid={$page_id}"=>$row['page_text'] . ' ' . $this->pages[$idstring]['name']));
- }
-
- $new_index = $search->index;
-
- if ( count($search->index) == 0 )
- // o_O
- // nothing indexed.
- return true;
-
- if ( ENANO_DBLAYER == 'MYSQL' )
- {
- $keys = array_keys($search->index);
- foreach($keys as $i => $k)
- {
- $c =& $keys[$i];
- $c = hexencode($c, '', '');
- }
- $keys = "word=0x" . implode ( " OR word=0x", $keys ) . "";
- }
- else
- {
- $keys = array_keys($search->index);
- foreach($keys as $i => $k)
- {
- $c =& $keys[$i];
- $c = $db->escape($c);
- }
- $keys = "word='" . implode ( "' OR word='", $keys ) . "'";
- }
-
- $query = $db->sql_query('SELECT word,page_names FROM '.table_prefix.'search_index WHERE '.$keys.';');
-
- while($row = $db->fetchrow())
- {
- $row['word'] = rtrim($row['word'], "\0");
- $new_index[ $row['word'] ] = $row['page_names'] . ',' . $search->index[ $row['word'] ];
- }
- $db->free_result();
-
- $db->sql_query('DELETE FROM '.table_prefix.'search_index WHERE '.$keys.';');
-
- $secs = Array();
- $q = 'INSERT INTO '.table_prefix.'search_index(word,word_lcase,page_names) VALUES';
- foreach($new_index as $word => $pages)
- {
- $secs[] = '(\''.$db->escape($word).'\', \'' . $db->escape(strtolower($word)) . '\', \''.$db->escape($pages).'\')';
- }
- $q .= implode(',', $secs);
- unset($secs);
- $q .= ';';
- if(!$db->check_query($q))
- {
- die('BUG: PathManager::rebuild_page_index: Query rejected by SQL parser:<pre>'.$q.'</pre>');
- }
- $result = $db->sql_query($q);
- if($result)
- return true;
- else
- $db->_die('The search index was trying to rebuild itself when the error occured.');
-
- }
-
- /**
- * Returns a list of groups that a given page is a member of.
- * @param string Page ID
- * @param string Namespace
- * @return array
- */
-
- function get_page_groups($page_id, $namespace)
- {
- global $db, $session, $paths, $template, $plugins; // Common objects
-
- static $cache = array();
-
- if ( count($cache) == 0 )
- {
- foreach ( $this->nslist as $key => $_ )
- {
- $cache[$key] = array();
- }
- }
-
- if ( !isset($this->nslist[$namespace]) )
- die('$paths->get_page_groups(): HACKING ATTEMPT: namespace "'. htmlspecialchars($namespace) .'" doesn\'t exist');
-
- $page_id_unescaped = $paths->nslist[$namespace] .
- dirtify_page_id($page_id);
- $page_id_str = $paths->nslist[$namespace] .
- sanitize_page_id($page_id);
-
- $page_id = $db->escape(sanitize_page_id($page_id));
-
- if ( isset($cache[$namespace][$page_id]) )
- {
- return $cache[$namespace][$page_id];
- }
-
- $group_list = array();
-
- // What linked categories have this page?
- $q = $db->sql_unbuffered_query('SELECT g.pg_id, g.pg_type, g.pg_target FROM '.table_prefix.'page_groups AS g
- LEFT JOIN '.table_prefix.'categories AS c
- ON ( ( c.category_id = g.pg_target AND g.pg_type = ' . PAGE_GRP_CATLINK . ' ) OR c.category_id IS NULL )
- LEFT JOIN '.table_prefix.'page_group_members AS m
- ON ( ( g.pg_id = m.pg_id AND g.pg_type = ' . PAGE_GRP_NORMAL . ' ) OR ( m.pg_id IS NULL ) )
- LEFT JOIN '.table_prefix.'tags AS t
- ON ( ( t.tag_name = g.pg_target AND pg_type = ' . PAGE_GRP_TAGGED . ' ) OR t.tag_name IS NULL )
- WHERE
- ( c.page_id=\'' . $page_id . '\' AND c.namespace=\'' . $namespace . '\' ) OR
- ( t.page_id=\'' . $page_id . '\' AND t.namespace=\'' . $namespace . '\' ) OR
- ( m.page_id=\'' . $page_id . '\' AND m.namespace=\'' . $namespace . '\' ) OR
- ( g.pg_type = ' . PAGE_GRP_REGEX . ' );');
- if ( !$q )
- $db->_die();
-
- while ( $row = $db->fetchrow() )
- {
- if ( $row['pg_type'] == PAGE_GRP_REGEX )
- {
- //echo "<debug> matching page " . htmlspecialchars($page_id_unescaped) . " against regex <tt>" . htmlspecialchars($row['pg_target']) . "</tt>.";
- if ( @preg_match($row['pg_target'], $page_id_unescaped) || @preg_match($row['pg_target'], $page_id_str) )
- {
- //echo "..matched";
- $group_list[] = $row['pg_id'];
- }
- //echo "<br />";
- }
- else
- {
- $group_list[] = $row['pg_id'];
- }
- }
-
- $db->free_result();
-
- $cache[$namespace][$page_id] = $group_list;
-
- return $group_list;
-
- }
-
+ public $title, $pages, $custom_page, $cpage, $page, $fullpage, $page_exists, $page_id, $namespace, $nslist, $admin_tree, $wiki_mode, $page_protected, $template_cache, $external_api_page;
+
+ /**
+ * List of custom processing functions for namespaces. This is protected so trying to do anything with it will throw an error.
+ * @access private
+ * @var array
+ */
+
+ protected $namespace_processors;
+
+ function __construct()
+ {
+ global $db, $session, $paths, $template, $plugins; // Common objects
+
+ $GLOBALS['paths'] =& $this;
+ $this->pages = Array();
+
+ // DEFINE NAMESPACES HERE
+ // The key names should NOT EVER be changed, or Enano will be very broken
+ $namespace_delimiter = ( defined('WINDOWS_MOD_REWRITE_WORKAROUNDS') ) ? '.' : ':';
+ $this->nslist = Array(
+ 'Article' => '',
+ 'User' => 'User' . $namespace_delimiter,
+ 'File' => 'File' . $namespace_delimiter,
+ 'Help' => 'Help' . $namespace_delimiter,
+ 'Admin' => 'Admin' . $namespace_delimiter,
+ 'Special' => 'Special' . $namespace_delimiter,
+ 'System' => 'Enano' . $namespace_delimiter,
+ 'Template' => 'Template' . $namespace_delimiter,
+ 'Category' => 'Category' . $namespace_delimiter,
+ 'API' => 'SystemAPI' . $namespace_delimiter,
+ 'Project' => sanitize_page_id(getConfig('site_name')) . $namespace_delimiter,
+ );
+
+ // ACL types
+ // These can also be added from within plugins
+
+ $session->register_acl_type('read', AUTH_ALLOW, 'perm_read');
+ $session->register_acl_type('post_comments', AUTH_ALLOW, 'perm_post_comments', Array('read'), 'Article|User|Project|Template|File|Help|System|Category');
+ $session->register_acl_type('edit_comments', AUTH_ALLOW, 'perm_edit_comments', Array('post_comments'), 'Article|User|Project|Template|File|Help|System|Category');
+ $session->register_acl_type('edit_page', AUTH_WIKIMODE, 'perm_edit_page', Array('view_source'), 'Article|User|Project|Template|File|Help|System|Category');
+ $session->register_acl_type('edit_wysiwyg', AUTH_ALLOW, 'perm_edit_wysiwyg', Array(), 'Article|User|Project|Template|File|Help|System|Category');
+ $session->register_acl_type('view_source', AUTH_WIKIMODE, 'perm_view_source', Array('read'), 'Article|User|Project|Template|File|Help|System|Category'); // Only used if the page is protected
+ $session->register_acl_type('mod_comments', AUTH_DISALLOW, 'perm_mod_comments', Array('edit_comments'), 'Article|User|Project|Template|File|Help|System|Category');
+ $session->register_acl_type('history_view', AUTH_WIKIMODE, 'perm_history_view', Array('read'), 'Article|User|Project|Template|File|Help|System|Category');
+ $session->register_acl_type('history_rollback', AUTH_DISALLOW, 'perm_history_rollback', Array('history_view'), 'Article|User|Project|Template|File|Help|System|Category');
+ $session->register_acl_type('history_rollback_extra', AUTH_DISALLOW, 'perm_history_rollback_extra', Array('history_rollback'), 'Article|User|Project|Template|File|Help|System|Category');
+ $session->register_acl_type('protect', AUTH_DISALLOW, 'perm_protect', Array('read'), 'Article|User|Project|Template|File|Help|System|Category');
+ $session->register_acl_type('rename', AUTH_WIKIMODE, 'perm_rename', Array('read'), 'Article|User|Project|Template|File|Help|System|Category');
+ $session->register_acl_type('clear_logs', AUTH_DISALLOW, 'perm_clear_logs', Array('read', 'protect', 'even_when_protected'), 'Article|User|Project|Template|File|Help|System|Category');
+ $session->register_acl_type('vote_delete', AUTH_ALLOW, 'perm_vote_delete', Array('read'), 'Article|User|Project|Template|File|Help|System|Category');
+ $session->register_acl_type('vote_reset', AUTH_DISALLOW, 'perm_vote_reset', Array('read'), 'Article|User|Project|Template|File|Help|System|Category');
+ $session->register_acl_type('delete_page', AUTH_DISALLOW, 'perm_delete_page', Array(), 'Article|User|Project|Template|File|Help|System|Category');
+ $session->register_acl_type('tag_create', AUTH_ALLOW, 'perm_tag_create', Array('read'), 'Article|User|Project|Template|File|Help|System|Category');
+ $session->register_acl_type('tag_delete_own', AUTH_ALLOW, 'perm_tag_delete_own', Array('read', 'tag_create'), 'Article|User|Project|Template|File|Help|System|Category');
+ $session->register_acl_type('tag_delete_other', AUTH_DISALLOW, 'perm_tag_delete_other', Array('read'), 'Article|User|Project|Template|File|Help|System|Category');
+ $session->register_acl_type('set_wiki_mode', AUTH_DISALLOW, 'perm_set_wiki_mode', Array('read'), 'Article|User|Project|Template|File|Help|System|Category');
+ $session->register_acl_type('password_set', AUTH_DISALLOW, 'perm_password_set', Array('read'), 'Article|User|Project|Template|File|Help|System|Category');
+ $session->register_acl_type('password_reset', AUTH_DISALLOW, 'perm_password_reset', Array('read'), 'Article|User|Project|Template|File|Help|System|Category');
+ $session->register_acl_type('mod_misc', AUTH_DISALLOW, 'perm_mod_misc', Array(), 'All');
+ $session->register_acl_type('edit_cat', AUTH_WIKIMODE, 'perm_edit_cat', Array('read'), 'Article|User|Project|Template|File|Help|System|Category');
+ $session->register_acl_type('even_when_protected', AUTH_DISALLOW, 'perm_even_when_protected', Array('edit_page', 'rename', 'mod_comments', 'edit_cat'), 'Article|User|Project|Template|File|Help|System|Category');
+ $session->register_acl_type('create_page', AUTH_WIKIMODE, 'perm_create_page', Array(), 'All');
+ $session->register_acl_type('upload_files', AUTH_DISALLOW, 'perm_upload_files', Array('create_page'), 'All');
+ $session->register_acl_type('upload_new_version', AUTH_WIKIMODE, 'perm_upload_new_version', Array('upload_files'), 'All');
+ $session->register_acl_type('html_in_pages', AUTH_DISALLOW, 'perm_html_in_pages', Array('edit_page'), 'Article|User|Project|Template|File|Help|System|Category|Admin');
+ $session->register_acl_type('php_in_pages', AUTH_DISALLOW, 'perm_php_in_pages', Array('edit_page', 'html_in_pages'), 'Article|User|Project|Template|File|Help|System|Category|Admin');
+ $session->register_acl_type('custom_user_title', AUTH_DISALLOW, 'perm_custom_user_title', Array(), 'User|Special');
+ $session->register_acl_type('edit_acl', AUTH_DISALLOW, 'perm_edit_acl', Array());
+
+ // DO NOT add new admin pages here! Use a plugin to call $paths->addAdminNode();
+ $this->addAdminNode('adm_cat_general', 'adm_page_general_config', 'GeneralConfig', array(2, 2));
+ $this->addAdminNode('adm_cat_general', 'adm_page_file_uploads', 'UploadConfig', array(2, 5));
+ $this->addAdminNode('adm_cat_general', 'adm_page_file_types', 'UploadAllowedMimeTypes', array(1, 5));
+ $this->addAdminNode('adm_cat_content', 'adm_page_manager', 'PageManager', array(1, 4));
+ $this->addAdminNode('adm_cat_content', 'adm_page_editor', 'PageEditor', array(3, 3));
+ $this->addAdminNode('adm_cat_content', 'adm_page_pg_groups', 'PageGroups', array(4, 3));
+ $this->addAdminNode('adm_cat_appearance', 'adm_page_themes', 'ThemeManager', array(4, 4));
+ $this->addAdminNode('adm_cat_appearance', 'adm_page_plugins', 'PluginManager', array(2, 4));
+ $this->addAdminNode('adm_cat_appearance', 'adm_page_db_backup', 'DBBackup', array(1, 2));
+ $this->addAdminNode('adm_cat_appearance', 'adm_page_lang_manager', 'LangManager', array(1, 3));
+ $this->addAdminNode('adm_cat_appearance', 'adm_page_cache_manager', 'CacheManager', array(3, 1));
+ $this->addAdminNode('adm_cat_users', 'adm_page_users', 'UserManager', array(3, 5));
+ $this->addAdminNode('adm_cat_users', 'adm_page_user_groups', 'GroupManager', array(3, 2));
+ $this->addAdminNode('adm_cat_users', 'adm_page_coppa', 'COPPA', array(4, 1));
+ $this->addAdminNode('adm_cat_users', 'adm_page_mass_email', 'MassEmail', array(2, 3));
+ $this->addAdminNode('adm_cat_users', 'adm_page_user_ranks', 'UserRanks', array(4, 5));
+ $this->addAdminNode('adm_cat_security', 'adm_page_security_log', 'SecurityLog', array(3, 4));
+ $this->addAdminNode('adm_cat_security', 'adm_page_ban_control', 'BanControl', array(2, 1));
+
+ $code = $plugins->setHook('acl_rule_init');
+ foreach ( $code as $cmd )
+ {
+ eval($cmd);
+ }
+
+ $this->wiki_mode = ( getConfig('wiki_mode') == '1' ) ? 1 : 0;
+ $this->template_cache = Array();
+ }
+
+ function init($title)
+ {
+ global $db, $session, $paths, $template, $plugins; // Common objects
+ global $lang;
+ global $cache;
+
+ $code = $plugins->setHook('paths_init_before');
+ foreach ( $code as $cmd )
+ {
+ eval($cmd);
+ }
+
+ if ( defined('ENANO_INTERFACE_INDEX') || defined('ENANO_INTERFACE_AJAX') || defined('IN_ENANO_UPGRADE') )
+ {
+ if ( empty($title) )
+ $title = get_title();
+
+ if ( empty($title) && !have_blank_urlname_page() )
+ {
+ $this->main_page();
+ }
+ if ( strstr($title, ' ') || strstr($title, '+') || strstr($title, '%20') )
+ {
+ $title = sanitize_page_id($title);
+ redirect(makeUrl($title), '', '', 0);
+ }
+ $title = sanitize_page_id($title);
+ // We've got the title, pull the namespace from it
+ $namespace = 'Article';
+ $page_id = $title;
+ foreach ( $this->nslist as $ns => $prefix )
+ {
+ $prefix_len = strlen($prefix);
+ if ( substr($title, 0, $prefix_len) == $prefix )
+ {
+ $page_id = substr($title, $prefix_len);
+ $namespace = $ns;
+ }
+ }
+ $this->namespace = $namespace;
+ $this->fullpage = $title;
+ if ( $namespace == 'Special' || $namespace == 'Admin' )
+ {
+ list($page_id) = explode('/', $page_id);
+ }
+ $this->page = $this->nslist[$namespace] . $page_id;
+ $this->page_id = $page_id;
+ // die("All done setting parameters. What we've got:<br/>namespace: $namespace<br/>fullpage: $this->fullpage<br/>page: $this->page<br/>page_id: $this->page_id");
+ }
+ else
+ {
+ // Starting up Enano with the API from a page that wants to do its own thing. Generate
+ // metadata for an anonymous page and avoid redirection at all costs.
+ if ( isset($GLOBALS['title']) )
+ {
+ $title =& $GLOBALS['title'];
+ }
+ else
+ {
+ $title = basename($_SERVER['SCRIPT_NAME']);
+ }
+ $base_uri = scriptPath == '' ? ltrim($_SERVER['SCRIPT_NAME'], '/') : str_replace( scriptPath . '/', '', $_SERVER['SCRIPT_NAME'] );
+
+ $this->page = $this->nslist['API'] . sanitize_page_id($base_uri);
+ $this->fullpage = $this->nslist['API'] . sanitize_page_id($base_uri);
+ $this->namespace = 'API';
+ $this->cpage = array(
+ 'name' => $title,
+ 'urlname' => sanitize_page_id($base_uri),
+ 'namespace' => 'API',
+ 'special' => 1,
+ 'visible' => 1,
+ 'comments_on' => 1,
+ 'protected' => 1,
+ 'delvotes' => 0,
+ 'delvote_ips' => '',
+ 'page_format' => getConfig('default_page_format', 'wikitext')
+ );
+ $this->external_api_page = true;
+ $code = $plugins->setHook('paths_external_api_page');
+ foreach ( $code as $cmd )
+ {
+ eval($cmd);
+ }
+ }
+
+ $this->page = sanitize_page_id($this->page);
+ $this->fullpage = sanitize_page_id($this->fullpage);
+
+ if(isset($this->pages[$this->page]))
+ {
+ $this->page_exists = true;
+ $this->cpage = $this->pages[$this->page];
+ $this->page_id =& $this->cpage['urlname_nons'];
+ $this->namespace = $this->cpage['namespace'];
+ if(!isset($this->cpage['wiki_mode'])) $this->cpage['wiki_mode'] = 2;
+
+ // Determine the wiki mode for this page, now that we have this->cpage established
+ if($this->cpage['wiki_mode'] == 2)
+ {
+ $this->wiki_mode = (int)getConfig('wiki_mode');
+ }
+ else
+ {
+ $this->wiki_mode = $this->cpage['wiki_mode'];
+ }
+ // Allow the user to create/modify his user page uncondtionally (admins can still protect the page)
+ if($this->page == $this->nslist['User'].str_replace(' ', '_', $session->username))
+ {
+ $this->wiki_mode = true;
+ }
+ // And above all, if the site requires wiki mode to be off for non-logged-in users, disable it now
+ if(getConfig('wiki_mode_require_login')=='1' && !$session->user_logged_in)
+ {
+ $this->wiki_mode = false;
+ }
+ if($this->cpage['protected'] == 2)
+ {
+ // The page is semi-protected, determine permissions
+ if($session->user_logged_in && $session->reg_time + 60*60*24*4 < time())
+ {
+ $this->page_protected = 0;
+ }
+ else
+ {
+ $this->page_protected = 1;
+ }
+ }
+ else
+ {
+ $this->page_protected = $this->cpage['protected'];
+ }
+ }
+ else
+ {
+ $this->page_exists = false;
+ $page_name = dirtify_page_id($this->page);
+ $page_name = str_replace('_', ' ', $page_name);
+
+ $pid_cleaned = sanitize_page_id($this->page);
+ if ( $pid_cleaned != $this->page )
+ {
+ redirect(makeUrl($pid_cleaned), 'Sanitizer message', 'page id sanitized', 0);
+ }
+
+ if ( !is_array($this->cpage) )
+ {
+ $this->cpage = Array(
+ 'name' => $page_name,
+ 'urlname' => $this->page,
+ 'namespace' => 'Article',
+ 'special' => 0,
+ 'visible' => 0,
+ 'comments_on' => 1,
+ 'protected' => 0,
+ 'delvotes' => 0,
+ 'delvote_ips' => '',
+ 'wiki_mode' => 2,
+ 'page_format' => getConfig('default_page_format', 'wikitext')
+ );
+ }
+ // Look for a namespace prefix in the urlname, and assign a different namespace, if necessary
+ $k = array_keys($this->nslist);
+ for($i=0;$i<sizeof($this->nslist);$i++)
+ {
+ $ln = strlen($this->nslist[$k[$i]]);
+ if( substr($this->page, 0, $ln) == $this->nslist[$k[$i]] )
+ {
+ $this->cpage['namespace'] = $k[$i];
+ $this->cpage['urlname_nons'] = substr($this->page, strlen($this->nslist[$this->cpage['namespace']]), strlen($this->page));
+ if(!isset($this->cpage['wiki_mode']))
+ {
+ $this->cpage['wiki_mode'] = 2;
+ }
+ }
+ }
+ $this->namespace = $this->cpage['namespace'];
+ $this->page_id =& $this->cpage['urlname_nons'];
+
+ if($this->namespace=='System')
+ {
+ $this->cpage['protected'] = 1;
+ }
+ if($this->namespace == 'Special' && !$this->external_api_page)
+ {
+ // Can't load nonexistent pages
+ if( is_string(get_main_page()) )
+ {
+ $main_page = makeUrl(get_main_page());
+ }
+ else
+ {
+ $main_page = makeUrl($this->pages[0]['urlname']);
+ }
+ redirect($main_page, $lang->get('page_msg_special_404_title'), $lang->get('page_msg_special_404_body', array('sp_link' => makeUrlNS('Special', 'SpecialPages'))), 15);
+ exit;
+ }
+ // Allow the user to create/modify his user page uncondtionally (admins can still protect the page)
+ if($this->page == $this->nslist['User'].str_replace(' ', '_', $session->username))
+ {
+ $this->wiki_mode = true;
+ }
+ }
+ // This is used in the admin panel to keep track of form submission targets
+ $this->cpage['module'] = $this->cpage['urlname'];
+
+ $this->cpage['require_admin'] = ( $this->cpage['namespace'] === 'Admin' );
+
+ // Page is set up, call any hooks
+ $code = $plugins->setHook('page_set');
+ foreach ( $code as $cmd )
+ {
+ eval($cmd);
+ }
+
+ profiler_log('Paths and CMS core initted');
+ $session->init_permissions();
+ }
+
+ /**
+ * Fetch cdata (metadata) for a page.
+ * @param string Page ID
+ * @param string Namespace
+ * @return array
+ */
+
+ function get_cdata($page_id, $namespace)
+ {
+ global $db, $session, $paths, $template, $plugins; // Common objects
+
+ $pathskey = $this->get_pathskey($page_id, $namespace);
+ if ( isset($this->pages[$pathskey]) )
+ return $this->pages[$pathskey];
+
+ $page = namespace_factory($page_id, $namespace);
+ $cdata = $page->get_cdata();
+
+ $this->pages[$pathskey] = $cdata;
+ return $cdata;
+ }
+
+ /**
+ * For a given page ID and namespace, generate a flat string that can be used to access $paths->pages.
+ * @param string Page ID
+ * @param string Namespace
+ */
+
+ function get_pathskey($page_id, $namespace)
+ {
+ return ( isset($this->nslist[$namespace]) ) ? "{$this->nslist[$namespace]}{$page_id}" : "{$namespace}:{$page_id}";
+ }
+
+ function add_page($flags)
+ {
+ global $lang;
+ $flags['urlname_nons'] = $flags['urlname'];
+ $flags['urlname'] = $this->nslist[$flags['namespace']] . $flags['urlname']; // Applies the User:/File:/etc prefixes to the URL names
+
+ if ( is_object($lang) )
+ {
+ if ( preg_match('/^[a-z0-9]+_[a-z0-9_]+$/', $flags['name']) )
+ $flags['name'] = $lang->get($flags['name']);
+ }
+
+ $flags['require_admin'] = ( $flags['namespace'] === 'Admin' );
+ $flags['page_exists'] = true;
+
+ $this->pages[$flags['urlname']] = $flags;
+ }
+
+ function main_page()
+ {
+ if( is_string(get_main_page()) )
+ {
+ $main_page = makeUrl(get_main_page());
+ }
+ else
+ {
+ $main_page = makeUrl($this->pages[0]['urlname']);
+ }
+ redirect($main_page, 'Redirecting...', 'Invalid request, redirecting to main page', 0);
+ exit;
+ }
+
+ function sysmsg($n)
+ {
+ global $db, $session, $paths, $template, $plugins; // Common objects
+ static $sm_cache = array();
+
+ if ( isset($sm_cache[$n]) )
+ return $sm_cache[$n];
+
+ // 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 )
+ {
+ $db->_die('Error during generic selection of system page data.');
+ }
+ if($db->numrows() < 1)
+ {
+ return false;
+ //$db->_die('Error during generic selection of system page data: there were no rows in the text table that matched the page text query.');
+ }
+ $r = $db->fetchrow();
+ $db->free_result();
+ $message = $r['page_text'];
+
+ $message = preg_replace('/<noinclude>(.*?)<\/noinclude>/is', '', $message);
+ $message = preg_replace('/<nodisplay>(.*?)<\/nodisplay>/is', '\\1', $message);
+
+ $sm_cache[$n] = $message;
+
+ return $message;
+ }
+ function get_pageid_from_url()
+ {
+ return get_title(true, true);
+ }
+ // Parses a (very carefully formed) array into Javascript code compatible with the Tigra Tree Menu used in the admin menu
+ function parseAdminTree()
+ {
+ global $lang;
+
+ $k = array_keys($this->admin_tree);
+ $i = 0;
+ $ret = '';
+ $icon = $this->make_sprite_icon(4, 2);
+ $ret .= "var TREE_ITEMS = ";
+ $tree = array(
+ array($icon . $lang->get('adm_btn_home'), "javascript:ajaxPage('{$this->nslist['Admin']}Home');")
+ );
+
+ $root =& $tree[0];
+
+ foreach($k as $key)
+ {
+ $i++;
+ $name = $lang->get($key);
+ $group = array($name, "javascript:trees[0].toggle($i)");
+
+ foreach($this->admin_tree[$key] as $nodeid => $c)
+ {
+ $i++;
+ $name = $lang->get($c['name']);
+ if ( $c['icon'] && $c['icon'] != cdnPath . '/images/spacer.gif' )
+ {
+ if ( is_array($c['icon']) )
+ {
+ // this is a sprite reference
+ list($ix, $iy) = $c['icon'];
+ $icon = $this->make_sprite_icon($ix, $iy);
+ }
+ else
+ {
+ $icon = "<img alt=\"\" src=\"{$c['icon']}\" style=\"border-width: 0; margin-right: 3px;\" /> ";
+ }
+ }
+ else
+ {
+ $icon = '';
+ }
+ $group[] = array("$icon$name", "javascript:ajaxPage('{$this->nslist['Admin']}{$c['pageid']}');");
+ }
+
+ $root[] = $group;
+ }
+ $icon = $this->make_sprite_icon(1, 1);
+ $root[] = array(
+ $icon . $lang->get('adm_btn_logout'),
+ "javascript:ajaxPage('{$this->nslist['Admin']}AdminLogout');"
+ );
+ $root[] = array(
+ "<span id=\"keepalivestat\">" . $lang->get('adm_btn_keepalive_loading') . "</span>",
+ "javascript:ajaxToggleKeepalive();",
+ array(
+ $lang->get('adm_btn_keepalive_about'),
+ "javascript:aboutKeepAlive();"
+ )
+ );
+
+ $ret .= enano_json_encode($tree) . ';';
+
+ // I used this while I painstakingly wrote the Runt code that auto-expands certain nodes based on the value of a bitfield stored in a cookie. *shudders*
+ // $ret .= " ['(debug) Clear menu bitfield', 'javascript:createCookie(\\'admin_menu_state\\', \\'1\\', 365);'],\n";
+ return $ret;
+ }
+
+ /**
+ * Internal function to generate HTML code for an icon in the admin panel tree which is sprited.
+ * @param int X index of icon
+ * @param int Y index of icon
+ * @return string
+ */
+
+ function make_sprite_icon($ix, $iy)
+ {
+ $xpos = 16 * ( $ix - 1 );
+ $ypos = 16 * ( $iy - 1 );
+ return "<img alt=\"\" src=\"" . cdnPath . "/images/spacer.gif\" class=\"adminiconsprite\" style=\"border-width: 0; margin-right: 3px; background-position: -{$xpos}px -{$ypos}px;\" /> ";
+ }
+
+ /**
+ * Creates a new entry in the administration panel's navigation tree.
+ * @param string Section name - if this is a language string identifier, it will be sent through $lang->get()
+ * @param string The title of the page, also may be a language string identifier
+ * @param string The page ID of the admin page, the namespace Admin is assumed
+ * @param string Optional. The path to a 16x16 image that will be displayed as the icon for this admin page
+ */
+
+ function addAdminNode($section, $page_title, $url, $icon = false)
+ {
+ if ( !$icon )
+ {
+ $icon = cdnPath . '/images/spacer.gif';
+ }
+ if(!isset($this->admin_tree[$section]))
+ {
+ $this->admin_tree[$section] = Array();
+ }
+ $this->admin_tree[$section][] = Array(
+ 'name' => $page_title,
+ 'pageid' => $url,
+ 'icon' => $icon
+ );
+ }
+ function getParam($id = 0)
+ {
+ $title = $this->fullpage;
+ list(, $ns) = RenderMan::strToPageID($title);
+ $title = substr($title, strlen($this->nslist[$ns]));
+ $regex = '/^' . str_replace('/', '\\/', preg_quote($this->nslist[$ns])) . '\\/?/';
+ $title = preg_replace($regex, '', $title);
+ $title = explode('/', $title);
+ $id = $id + 1;
+ return ( isset($title[$id]) ) ? $title[$id] : false;
+ }
+
+ function getAllParams()
+ {
+ $title = $this->fullpage;
+ $regex = '/^' . str_replace('/', '\\/', preg_quote($this->nslist[$this->namespace])) . '\\/?/';
+ $title = preg_replace($regex, '', $title);
+ $title = explode('/', $title);
+ unset($title[0]);
+ return implode('/', $title);
+ }
+
+ /**
+ * Creates a new namespace in memory
+ * @param string $id the namespace ID
+ * @param string $prefix the URL prefix, must not be blank or already used
+ * @return bool true on success false on failure
+ */
+
+ function create_namespace($id, $prefix)
+ {
+ if(in_array($prefix, $this->nslist))
+ {
+ // echo '<b>Warning:</b> pathManager::create_namespace: Prefix "'.$prefix.'" is already taken<br />';
+ return false;
+ }
+ if( isset($this->nslist[$id]) )
+ {
+ // echo '<b>Warning:</b> pathManager::create_namespace: Namespace ID "'.$prefix.'" is already taken<br />';
+ return false;
+ }
+ $this->nslist[$id] = $prefix;
+ }
+
+ /**
+ * Deprecated.
+ */
+
+ function update_metadata_cache()
+ {
+ global $db, $session, $paths, $template, $plugins; // Common objects
+
+ return false;
+ }
+
+ /**
+ * Takes a result row from the pages table and calculates correct values for it.
+ * @param array
+ * @return array
+ */
+
+ function calculate_metadata_from_row($r)
+ {
+ return Namespace_Default::bake_cdata($r);
+ }
+
+ /**
+ * Registers a handler to manually process a namespace instead of the default PageProcessor behavior.
+ * The first and only parameter passed to the processing function will be the PageProcessor instance.
+ * @param string Namespace to process
+ * @param mixed Function address. Either a function name or an array of the form array(0 => mixed (string:class name or object), 1 => string:method)
+ */
+
+ function register_namespace_processor($namespace, $function)
+ {
+ if ( isset($this->namespace_processors[$namespace]) )
+ {
+ $processorname = ( is_string($this->namespace_processors[$namespace]) ) ?
+ $this->namespace_processors[$namespace] :
+ ( is_object($this->namespace_processors[$namespace][0]) ? get_class($this->namespace_processors[$namespace][0]) : $this->namespace_processors[$namespace][0] ) . '::' .
+ $this->namespace_processors[$namespace][1];
+
+ trigger_error("Namespace \"$namespace\" is already being processed by $processorname - replacing caller", E_USER_WARNING);
+ }
+ if ( !is_string($function) )
+ {
+ if ( !is_array($function) )
+ return false;
+ if ( count($function) != 2 )
+ return false;
+ if ( !is_string($function[0]) && !is_object($function[0]) )
+ return false;
+ if ( !is_string($function[1]) )
+ return false;
+ }
+
+ // security: don't allow Special or Admin namespaces to be overridden
+ if ( $namespace == 'Special' || $namespace == 'Admin' )
+ {
+ trigger_error("Security manager denied attempt to override processor for $namespace", E_USER_ERROR);
+ }
+
+ $this->namespace_processors[$namespace] = $function;
+ }
+
+ /**
+ * Returns a namespace processor if one exists, otherwise returns false.
+ * @param string Namespace
+ * @return mixed
+ */
+
+ function get_namespace_processor($namespace)
+ {
+ return ( isset($this->namespace_processors[$namespace]) ) ? $this->namespace_processors[$namespace] : false;
+ }
+
+ /**
+ * Fetches the page texts for searching
+ */
+
+ function fetch_page_search_texts()
+ {
+ global $db, $session, $paths, $template, $plugins; // Common objects
+ $texts = Array();
+ $q = $db->sql_query('SELECT t.page_id,t.namespace,t.page_text,t.char_tag FROM '.table_prefix.'page_text AS t
+ LEFT JOIN '.table_prefix.'pages AS p
+ ON t.page_id=p.urlname
+ WHERE p.namespace=t.namespace
+ AND ( p.password=\'\' OR p.password=\'da39a3ee5e6b4b0d3255bfef95601890afd80709\' )
+ AND p.visible=1;'); // Only indexes "visible" pages
+
+ if( !$q )
+ {
+ return false;
+ }
+ while($row = $db->fetchrow())
+ {
+ $pid = $this->nslist[$row['namespace']] . $row['page_id'];
+ $texts[$pid] = $row['page_text'];
+ }
+ $db->free_result();
+
+ return $texts;
+ }
+
+ /**
+ * Generates an SQL query to grab all of the text
+ */
+
+ function fetch_page_search_resource()
+ {
+ global $db, $session, $paths, $template, $plugins; // Common objects
+ // sha1('') returns "da39a3ee5e6b4b0d3255bfef95601890afd80709"
+
+ $concat_column = ( ENANO_DBLAYER == 'MYSQL' ) ?
+ 'CONCAT(\'ns=\',t.namespace,\';pid=\',t.page_id)' :
+ "'ns=' || t.namespace || ';pid=' || t.page_id";
+
+ $texts = 'SELECT t.page_text, ' . $concat_column . ' AS page_idstring, t.page_id, t.namespace FROM '.table_prefix.'page_text AS t
+ LEFT JOIN '.table_prefix.'pages AS p
+ ON ( t.page_id=p.urlname AND t.namespace=p.namespace )
+ WHERE p.namespace=t.namespace
+ AND ( p.password=\'\' OR p.password=\'da39a3ee5e6b4b0d3255bfef95601890afd80709\' )
+ AND p.visible=1;'; // Only indexes "visible" pages
+ return $texts;
+ }
+
+ /**
+ * Builds a word list for search indexing.
+ * @param string Text to index
+ * @param string Page ID of the page being indexed
+ * @param string Title of the page being indexed
+ * @return array List of words
+ */
+
+ function calculate_word_list($text, $page_id, $page_name)
+ {
+ $page_id = dirtify_page_id($page_id);
+ $text = preg_replace('/[^a-z0-9\']/i', ' ', $text);
+ $page_id = preg_replace('/[^a-z0-9\']/i', ' ', $page_id);
+ $page_name = preg_replace('/[^a-z0-9\']/i', ' ', $page_name);
+ $text .= " $page_id $page_name";
+ $text = explode(' ', $text);
+ foreach ( $text as $i => &$word )
+ {
+ if ( strstr($word, "''") )
+ $word = preg_replace("/[']{2,}/", '', $word);
+ if ( strlen($word) < 2 )
+ unset($text[$i]);
+ }
+ $text = array_unique(array_values($text));
+ // for debugging purposes (usually XSS safe because of character stripping)
+ // echo ' ' . implode(' ', $text) . '<br />';
+ return $text;
+ }
+
+ /**
+ * Rebuilds the site's entire search index. Considerably more exciting if run from the command line.
+ * @param bool If true, verbose output.
+ * @param bool If true, verbose + debugging output.
+ */
+
+ function rebuild_search_index($verbose = false, $debug = false)
+ {
+ global $db, $session, $paths, $template, $plugins; // Common objects
+ require_once(ENANO_ROOT . '/includes/search.php');
+
+ $progress = false;
+ if ( class_exists('ProgressBar') )
+ {
+ // CLI only.
+ $progress = new ProgressBar('Rebuilding search index: [', ']', 'Initializing...', 'green', 'blue', 'white', 'yellow');
+ $verbose = false;
+ $debug = false;
+ $progress->start();
+ }
+
+ @set_time_limit(0);
+
+ $q = $db->sql_query('DELETE FROM ' . table_prefix . 'search_index;');
+ if ( !$q )
+ $db->_die();
+
+ $sha1_blank = sha1('');
+
+ //
+ // Index $pages_in_batch pages at a time
+ //
+ $pages_in_batch = 15;
+
+ // First find out how many pages there are
+ $q = $db->sql_query('SELECT COUNT(p.urlname) AS num_pages FROM ' . table_prefix . "page_text AS t\n"
+ . " LEFT JOIN " . table_prefix . "pages AS p\n"
+ . " ON ( p.urlname = t.page_id AND p.namespace = t.namespace )\n"
+ . " WHERE ( p.password = '' OR p.password = '$sha1_blank' )\n"
+ . " AND ( p.visible = 1 );");
+ if ( !$q )
+ $db->_die();
+
+ list($num_pages) = $db->fetchrow_num();
+ $num_pages = intval($num_pages);
+ $loops = ceil($num_pages / $pages_in_batch);
+ $master_word_list = array();
+ $stopwords = get_stopwords();
+
+ for ( $j = 0; $j < $loops; )
+ {
+ $offset = $j * $pages_in_batch;
+
+ $j++;
+
+ if ( $verbose && $debug )
+ {
+ echo "Running indexing round $j of $loops (offset $offset)\n" . ( isset($_SERVER['REQUEST_URI']) ? '<br />' : '' );
+ }
+
+ // this is friendly to both MySQL and PostgreSQL.
+ $texts = $db->sql_query('SELECT p.name, p.visible, t.page_id, t.namespace, t.page_text FROM ' . table_prefix . "page_text AS t\n"
+ . " LEFT JOIN " . table_prefix . "pages AS p\n"
+ . " ON ( p.urlname = t.page_id AND p.namespace = t.namespace )\n"
+ . " WHERE ( p.password = '' OR p.password = '$sha1_blank' )\n"
+ . " AND ( p.visible = 1 )\n"
+ . " LIMIT $pages_in_batch OFFSET $offset;", false);
+ if ( !$texts )
+ $db->_die();
+
+ $k = $offset;
+
+ if ( $row = $db->fetchrow($texts) )
+ {
+ do
+ {
+ $k++;
+ if ( $verbose )
+ {
+ $mu = memory_get_usage();
+ echo " Indexing page $k of $num_pages: {$row['namespace']}:{$row['page_id']}";
+ if ( $debug )
+ echo ", mem = $mu...";
+ flush();
+ }
+ else if ( is_object($progress) )
+ {
+ $progress->update_text_quiet("$k/$num_pages {$row['namespace']}:{$row['page_id']}");
+ $progress->set($k, $num_pages);
+ }
+
+ // skip this page if it's not supposed to be indexed
+ if ( $row['visible'] == 0 )
+ {
+ if ( $verbose )
+ {
+ echo "skipped";
+ if ( isset($_SERVER['REQUEST_URI']) )
+ echo '<br />';
+ echo "\n";
+ }
+ continue;
+ }
+
+ // Indexing identifier for the page in the DB
+ $page_uniqid = "ns={$row['namespace']};pid=" . sanitize_page_id($row['page_id']);
+ $page_uniqid = $db->escape($page_uniqid);
+
+ // List of words on the page
+ $wordlist = $this->calculate_word_list($row['page_text'], $row['page_id'], $row['name']);
+
+ // Index calculation complete -- run inserts
+ $inserts = array();
+ foreach ( $wordlist as $word )
+ {
+ if ( in_array($word, $stopwords) || strval(intval($word)) === $word || strlen($word) < 3 )
+ continue;
+ $word_db = $db->escape($word);
+ $word_db_lc = $db->escape(strtolower($word));
+ if ( !in_array($word, $master_word_list) )
+ {
+ $inserts[] = "( '$word_db', '$word_db_lc', '$page_uniqid' )";
+ }
+ else
+ {
+ if ( $verbose && $debug )
+ echo '.';
+ $pid_col = ( ENANO_DBLAYER == 'MYSQL' ) ?
+ "CONCAT( page_names, ',$page_uniqid' )":
+ "page_names || ',$page_uniqid'";
+ $q = $db->sql_query('UPDATE ' . table_prefix . "search_index SET page_names = $pid_col WHERE word = '$word_db';", false);
+ if ( !$q )
+ $db->_die();
+ }
+ }
+ if ( count($inserts) > 0 )
+ {
+ if ( $verbose && $debug )
+ echo 'i';
+ $inserts = implode(",\n ", $inserts);
+ $q = $db->sql_query('INSERT INTO ' . table_prefix . "search_index(word, word_lcase, page_names) VALUES\n $inserts;", false);
+ if ( !$q )
+ $db->_die();
+ }
+
+ $master_word_list = array_unique(array_merge($master_word_list, $wordlist));
+ if ( $verbose )
+ {
+ if ( isset($_SERVER['REQUEST_URI']) )
+ echo '<br />';
+ echo "\n";
+ }
+ unset($inserts, $wordlist, $page_uniqid, $word_db, $q, $word, $row);
+ }
+ while ( $row = $db->fetchrow($texts) );
+ }
+ $db->free_result($texts);
+ }
+ if ( $verbose )
+ {
+ echo "Indexing complete.";
+ if ( isset($_SERVER['REQUEST_URI']) )
+ echo '<br />';
+ echo "\n";
+ }
+ else if ( is_object($progress) )
+ {
+ $progress->update_text('Complete.');
+ $progress->end();
+ }
+ return true;
+ }
+
+ /**
+ * Partially rebuilds the search index, removing/inserting entries only for the current page
+ * @param string $page_id
+ * @param string $namespace
+ */
+
+ function rebuild_page_index($page_id, $namespace)
+ {
+ global $db, $session, $paths, $template, $plugins; // Common objects
+ require_once(ENANO_ROOT . '/includes/search.php');
+
+ if(!$db->sql_query('SELECT page_text FROM '.table_prefix.'page_text
+ WHERE page_id=\''.$db->escape($page_id).'\' AND namespace=\''.$db->escape($namespace).'\';'))
+ {
+ return $db->get_error();
+ }
+ if ( $db->numrows() < 1 )
+ return 'E: No rows';
+ $idstring = $this->nslist[$namespace] . sanitize_page_id($page_id);
+ if ( !isset($this->pages[$idstring]) )
+ {
+ return 'E: Can\'t find page metadata';
+ }
+ $row = $db->fetchrow();
+ $db->free_result();
+ $search = new Searcher();
+
+ // if the page shouldn't be indexed, send a blank set of strings to the indexing engine
+ if ( $this->pages[$idstring]['visible'] == 0 )
+ {
+ $search->buildIndex(Array("ns={$namespace};pid={$page_id}"=>''));
+ }
+ else
+ {
+ $search->buildIndex(Array("ns={$namespace};pid={$page_id}"=>$row['page_text'] . ' ' . $this->pages[$idstring]['name']));
+ }
+
+ $new_index = $search->index;
+
+ if ( count($search->index) == 0 )
+ // o_O
+ // nothing indexed.
+ return true;
+
+ if ( ENANO_DBLAYER == 'MYSQL' )
+ {
+ $keys = array_keys($search->index);
+ foreach($keys as $i => $k)
+ {
+ $c =& $keys[$i];
+ $c = hexencode($c, '', '');
+ }
+ $keys = "word=0x" . implode ( " OR word=0x", $keys ) . "";
+ }
+ else
+ {
+ $keys = array_keys($search->index);
+ foreach($keys as $i => $k)
+ {
+ $c =& $keys[$i];
+ $c = $db->escape($c);
+ }
+ $keys = "word='" . implode ( "' OR word='", $keys ) . "'";
+ }
+
+ $query = $db->sql_query('SELECT word,page_names FROM '.table_prefix.'search_index WHERE '.$keys.';');
+
+ while($row = $db->fetchrow())
+ {
+ $row['word'] = rtrim($row['word'], "\0");
+ $new_index[ $row['word'] ] = $row['page_names'] . ',' . $search->index[ $row['word'] ];
+ }
+ $db->free_result();
+
+ $db->sql_query('DELETE FROM '.table_prefix.'search_index WHERE '.$keys.';');
+
+ $secs = Array();
+ $q = 'INSERT INTO '.table_prefix.'search_index(word,word_lcase,page_names) VALUES';
+ foreach($new_index as $word => $pages)
+ {
+ $secs[] = '(\''.$db->escape($word).'\', \'' . $db->escape(strtolower($word)) . '\', \''.$db->escape($pages).'\')';
+ }
+ $q .= implode(',', $secs);
+ unset($secs);
+ $q .= ';';
+ if(!$db->check_query($q))
+ {
+ die('BUG: PathManager::rebuild_page_index: Query rejected by SQL parser:<pre>'.$q.'</pre>');
+ }
+ $result = $db->sql_query($q);
+ if($result)
+ return true;
+ else
+ $db->_die('The search index was trying to rebuild itself when the error occured.');
+
+ }
+
+ /**
+ * Returns a list of groups that a given page is a member of.
+ * @param string Page ID
+ * @param string Namespace
+ * @return array
+ */
+
+ function get_page_groups($page_id, $namespace)
+ {
+ global $db, $session, $paths, $template, $plugins; // Common objects
+
+ static $cache = array();
+
+ if ( count($cache) == 0 )
+ {
+ foreach ( $this->nslist as $key => $_ )
+ {
+ $cache[$key] = array();
+ }
+ }
+
+ if ( !isset($this->nslist[$namespace]) )
+ die('$paths->get_page_groups(): HACKING ATTEMPT: namespace "'. htmlspecialchars($namespace) .'" doesn\'t exist');
+
+ $page_id_unescaped = $paths->nslist[$namespace] .
+ dirtify_page_id($page_id);
+ $page_id_str = $paths->nslist[$namespace] .
+ sanitize_page_id($page_id);
+
+ $page_id = $db->escape(sanitize_page_id($page_id));
+
+ if ( isset($cache[$namespace][$page_id]) )
+ {
+ return $cache[$namespace][$page_id];
+ }
+
+ $group_list = array();
+
+ // What linked categories have this page?
+ $q = $db->sql_unbuffered_query('SELECT g.pg_id, g.pg_type, g.pg_target FROM '.table_prefix.'page_groups AS g
+ LEFT JOIN '.table_prefix.'categories AS c
+ ON ( ( c.category_id = g.pg_target AND g.pg_type = ' . PAGE_GRP_CATLINK . ' ) OR c.category_id IS NULL )
+ LEFT JOIN '.table_prefix.'page_group_members AS m
+ ON ( ( g.pg_id = m.pg_id AND g.pg_type = ' . PAGE_GRP_NORMAL . ' ) OR ( m.pg_id IS NULL ) )
+ LEFT JOIN '.table_prefix.'tags AS t
+ ON ( ( t.tag_name = g.pg_target AND pg_type = ' . PAGE_GRP_TAGGED . ' ) OR t.tag_name IS NULL )
+ WHERE
+ ( c.page_id=\'' . $page_id . '\' AND c.namespace=\'' . $namespace . '\' ) OR
+ ( t.page_id=\'' . $page_id . '\' AND t.namespace=\'' . $namespace . '\' ) OR
+ ( m.page_id=\'' . $page_id . '\' AND m.namespace=\'' . $namespace . '\' ) OR
+ ( g.pg_type = ' . PAGE_GRP_REGEX . ' );');
+ if ( !$q )
+ $db->_die();
+
+ while ( $row = $db->fetchrow() )
+ {
+ if ( $row['pg_type'] == PAGE_GRP_REGEX )
+ {
+ //echo "<debug> matching page " . htmlspecialchars($page_id_unescaped) . " against regex <tt>" . htmlspecialchars($row['pg_target']) . "</tt>.";
+ if ( @preg_match($row['pg_target'], $page_id_unescaped) || @preg_match($row['pg_target'], $page_id_str) )
+ {
+ //echo "..matched";
+ $group_list[] = $row['pg_id'];
+ }
+ //echo "<br />";
+ }
+ else
+ {
+ $group_list[] = $row['pg_id'];
+ }
+ }
+
+ $db->free_result();
+
+ $cache[$namespace][$page_id] = $group_list;
+
+ return $group_list;
+
+ }
+
}
/**
@@ -1106,19 +1106,19 @@
function register_special_page($urlname, $name, $visible = true)
{
- global $paths;
-
- return $paths->add_page(Array(
- 'name' => $name,
- 'urlname' => $urlname,
- 'namespace' => 'Special',
- 'special' => 0,
- 'visible' => $visible ? 1 : 0,
- 'comments_on' => 0,
- 'protected' => 1,
- 'delvotes' => 0,
- 'delvote_ips' => '',
- ));
+ global $paths;
+
+ return $paths->add_page(Array(
+ 'name' => $name,
+ 'urlname' => $urlname,
+ 'namespace' => 'Special',
+ 'special' => 0,
+ 'visible' => $visible ? 1 : 0,
+ 'comments_on' => 0,
+ 'protected' => 1,
+ 'delvotes' => 0,
+ 'delvote_ips' => '',
+ ));
}
?>