diff -r de56132c008d -r bdac73ed481e includes/paths.php --- 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:
namespace: $namespace
fullpage: $this->fullpage
page: $this->page
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;$inslist);$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>/is', '', $message); - $message = preg_replace('/(.*?)<\/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 = "\"\" "; - } - } - 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( - "" . $lang->get('adm_btn_keepalive_loading') . "", - "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 "\"\" "; - } - - /** - * 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 'Warning: pathManager::create_namespace: Prefix "'.$prefix.'" is already taken
'; - return false; - } - if( isset($this->nslist[$id]) ) - { - // echo 'Warning: pathManager::create_namespace: Namespace ID "'.$prefix.'" is already taken
'; - 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) . '
'; - 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']) ? '
' : '' ); - } - - // 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 '
'; - 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 '
'; - 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 '
'; - 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:
'.$q.'
'); - } - $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 " . htmlspecialchars($row['pg_target']) . "."; - 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 "
"; - } - 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:
namespace: $namespace
fullpage: $this->fullpage
page: $this->page
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;$inslist);$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>/is', '', $message); + $message = preg_replace('/(.*?)<\/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 = "\"\" "; + } + } + 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( + "" . $lang->get('adm_btn_keepalive_loading') . "", + "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 "\"\" "; + } + + /** + * 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 'Warning: pathManager::create_namespace: Prefix "'.$prefix.'" is already taken
'; + return false; + } + if( isset($this->nslist[$id]) ) + { + // echo 'Warning: pathManager::create_namespace: Namespace ID "'.$prefix.'" is already taken
'; + 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) . '
'; + 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']) ? '
' : '' ); + } + + // 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 '
'; + 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 '
'; + 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 '
'; + 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:
'.$q.'
'); + } + $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 " . htmlspecialchars($row['pg_target']) . "."; + 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 "
"; + } + 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' => '', + )); } ?>