? .bzr ? 140932_1.patch ? 142995_4.patch ? AUTHOR ? README.txt ? formapi_file-220944-4.patch ? generate-content.php ? generate-og-users.php ? generate-og2list-mail.php ? generate-taxonomy.php ? generate-users.php ? import-taxonomy-terms.php ? import-users.php ? link ? registry_6.patch ? update-teaser.php ? modules/gd ? sites/default/files ? sites/default/modules ? sites/default/settings.php Index: update.php =================================================================== RCS file: /cvs/drupal/drupal/update.php,v retrieving revision 1.252 diff -u -u -r1.252 update.php --- update.php 3 Feb 2008 18:41:16 -0000 1.252 +++ update.php 22 Feb 2008 02:17:35 -0000 @@ -422,6 +422,41 @@ } /** + * This is part of the Drupal 6.x to 7.x migration. + */ +function update_create_registry_tables() { + $schema['registry'] = array( + 'fields' => array( + 'name' => array('type' => 'varchar', 'length' => 255, 'not null' => TRUE, 'default' => ''), + 'type' => array('type' => 'varchar', 'length' => 9, 'not null' => TRUE, 'default' => ''), + 'module' => array('type' => 'varchar', 'length' => 255, 'not null' => TRUE, 'default' => ''), + 'hook' => array('type' => 'varchar', 'length' => 255, 'not null' => TRUE, 'default' => ''), + 'file' => array('type' => 'varchar', 'length' => 64, 'not null' => TRUE, 'default' => ''), + ), + 'primary key' => array('name', 'type'), + 'indexes' => array( + 'module_implements' => array('type', 'hook'), + ), + ); + $schema['cache_registry'] = array( + 'fields' => array( + 'cid' => array('type' => 'varchar', 'length' => 255, 'not null' => TRUE, 'default' => ''), + 'data' => array('type' => 'blob', 'not null' => FALSE, 'size' => 'big'), + 'expire' => array('type' => 'int', 'not null' => TRUE, 'default' => 0), + 'created' => array('type' => 'int', 'not null' => TRUE, 'default' => 0), + 'headers' => array('type' => 'text', 'not null' => FALSE), + 'serialized' => array('type' => 'int', 'size' => 'small', 'not null' => TRUE, 'default' => 0) + ), + 'indexes' => array('expire' => array('expire')), + 'primary key' => array('cid'), + ); + $ret = array(); + db_create_table($ret, 'cache_registry', $schema['cache_registry']); + db_create_table($ret, 'registry', $schema['registry']); + drupal_rebuild_code_registry(); +} + +/** * Disable anything in the {system} table that is not compatible with the * current version of Drupal core. */ @@ -620,6 +655,7 @@ include_once './includes/install.inc'; include_once './includes/batch.inc'; + drupal_load_updates(); update_fix_d6_requirements(); Index: includes/common.inc =================================================================== RCS file: /cvs/drupal/drupal/includes/common.inc,v retrieving revision 1.759 diff -u -u -r1.759 common.inc --- includes/common.inc 20 Feb 2008 13:38:32 -0000 1.759 +++ includes/common.inc 22 Feb 2008 02:17:36 -0000 @@ -1463,6 +1463,8 @@ if (variable_get('cache', CACHE_DISABLED) != CACHE_DISABLED) { page_set_cache(); } + // Store the used registry files. + _registry_mark_file(NULL, TRUE); module_invoke_all('exit'); } @@ -2433,6 +2435,9 @@ unicode_check(); // Undo magic quotes fix_gpc_magic(); + // Register autoload functions so that we can acess classes and interfaces. + spl_autoload_register('registry_check_class'); + spl_autoload_register('registry_check_interface'); // Load all enabled modules module_load_all(); // Let all modules take action before menu system handles the request @@ -2690,7 +2695,7 @@ // element is rendered into the final text. if (isset($elements['#pre_render'])) { foreach ($elements['#pre_render'] as $function) { - if (function_exists($function)) { + if (registry_check_function($function)) { $elements = $function($elements); } } @@ -2752,7 +2757,7 @@ // which allows the output'ed text to be filtered. if (isset($elements['#post_render'])) { foreach ($elements['#post_render'] as $function) { - if (function_exists($function)) { + if (registry_check_function($function)) { $content = $function($content, $elements); } } @@ -3228,6 +3233,7 @@ } $schema = drupal_get_schema($table); + if (empty($schema)) { return FALSE; } @@ -3518,6 +3524,7 @@ // Change query-strings on css/js files to enforce reload for all users. _drupal_flush_css_js(); + drupal_rebuild_code_registry(); drupal_clear_css_cache(); drupal_clear_js_cache(); drupal_rebuild_theme_registry(); @@ -3525,7 +3532,7 @@ node_types_rebuild(); // Don't clear cache_form - in-progress form submissions may break. // Ordered so clearing the page cache will always be the last action. - $core = array('cache', 'cache_block', 'cache_filter', 'cache_page'); + $core = array('cache', 'cache_block', 'cache_filter', 'cache_registry', 'cache_page'); $cache_tables = array_merge(module_invoke_all('flush_caches'), $core); foreach ($cache_tables as $table) { cache_clear_all('*', $table, TRUE); @@ -3550,3 +3557,238 @@ } variable_set('css_js_query_string', $new_character . substr($string_history, 0, 19)); } + +/** + * Rescan all installed modules and rebuild the registry. + */ +function drupal_rebuild_code_registry() { + // Flush the old registry. + db_query("DELETE FROM {registry}"); + // We can't use module_invoke_all here because it depends on the registry + // which is being rebuilt right now. + $list = module_list(TRUE, FALSE, FALSE); + $patterns = array(); + foreach ($list as $module) { + $function = $module .'_hooks'; + if (function_exists($function)) { + $result = (array)$function(); + foreach ($result as $pattern) { + // For example 'form__alter' + $patterns[] = '/'. str_replace('__', '_.*_', $pattern) .'/'; + } + } + } + + foreach ($list as $module) { + _registry_parse_directory(drupal_get_path('module', $module), $patterns); + } + + _registry_parse_directory('includes', $patterns); + $implementations = _registry_save_resource(); + cache_set('hooks', array('patterns' => $patterns, 'implementations' => $implementations)); +} + +/** + * Parse all loadable files in a directory and save their function listings. + */ +function _registry_parse_directory($path, $patterns) { + static $map = array(T_FUNCTION => 'function', T_CLASS => 'class', T_INTERFACE => 'interface'); + $files = file_scan_directory($path, '\.(inc|module|install)$'); + foreach ($files as $filename => $file) { + $tokens = token_get_all(file_get_contents($filename)); + while ($token = next($tokens)) { + if (is_array($token) && isset($map[$token[0]])) { + _registry_save_resource($token, $tokens, $map[$token[0]], $filename, $patterns); + // We skip the body because classes might contain functions. + _registry_skip_body($tokens); + } + } + } +} + +/** + * Save a resource into the database. + * + * @param mixed $token + * @param ArrayIterator $tokens + * @param string $type + * @param string $module_path + * @param string $filename + */ +function _registry_save_resource($token = NULL, &$tokens = NULL, $type = NULL, $filename = NULL, $patterns = NULL) { + static $implementations, $resources, $dirs; + if (!isset($token)) { + return $implementations; + } + next($tokens); // Eat a space. + $token = next($tokens); + if ($token == '&') { + $token = next($tokens); + } + $resource_name = $token[1]; + if (isset($resources[$type][$resource_name])) { + return; + } + $resources[$type][$resource_name] = TRUE; + $file_parts = explode('.', $filename); + $module = ''; + $hook = ''; + $count = count($file_parts); + if ($count == 2 && $file_parts[1] == 'inc') { + if (!isset($dirs[$filename])) { + $dir_parts = explode('/', $file_parts[0]); + array_pop($dir_parts); + $dirs[$filename] = array_pop($dir_parts); + } + $module = $dirs[$filename]; + } + if (($count == 2 && ($file_parts[1] == 'module' || $file_parts[1] == 'install')) || ($count == 3 && $file_parts[2] == 'inc')) { + $module = basename($file_parts[0]); + } + if ($module && strpos($resource_name, $module) === 0) { + $hook = substr($resource_name, strlen($module) + 1); + foreach ($patterns as $pattern) { + if (preg_match($pattern, $hook)) { + $implementations[$hook][] = $module; + } + } + } + db_query("INSERT INTO {registry} (name, type, module, hook, file) VALUES ('%s', '%s', '%s', '%s', '%s')", array($resource_name, $type, $module, $hook, "./$filename")); +} + +/** + * Skip the body of a code block, as defined by { and }. + * + * This function assumes that the body starts at the next instance + * of { from the current position. + * + * @param ArrayIterator $tokens + */ +function _registry_skip_body(&$tokens) { + $num_braces = 1; + + $token = ''; + // Get to the first open brace. + while ($token != '{' && ($token = next($tokens))); + + // Scan through the rest of the tokens until we reach the matching + // end brace. + while ($num_braces && ($token = next($tokens))) { + if ($token == '{') { + ++$num_braces; + } + elseif ($token == '}') { + --$num_braces; + } + } +} + +/** + * Collect the files included so we can preload them on the next pageload. + * + * The function collects the files as the registry includes them and at the + * end of page request it stores them per router path. + * + * @param $file + * A file or an array of files included. + * @param $store + * If true, then store the collected array of files into the database as + * necessary. + */ +function _registry_mark_file($file = NULL, $store = FALSE) { + static $used_files = array(), $original_files = array(); + +if (isset($file)) { + if (is_array($file)) { + $used_files = array_merge($used_files, $file); + } + else { + $used_files[] = $file; + } + } + elseif ($store && $used_files != $original_files) { + // files we will likely need next time. + $files = array_unique($used_files); + $files = implode(';', $files); + $menu = menu_get_item(); + cache_set($menu['path'], $files, 'cache_registry'); + } +} + +/** + * Confirm that a function is available. + * + * If the function is already available, this function does nothing. + * If it is not, it tries to load the file in which it lives. If + * the file is not available, it simply returns false. That way it + * can be used as a drop-in replacement for function_exists(). + * + * @param string $function + * The name of the function to check or load. + * @return + * TRUE if the function is now available, FALSE otherwise. + */ +function registry_check_function($function) { + static $checked = array(); + if (isset($checked[$function])) { + return $checked[$function]; + } + if (!function_exists($function)) { + $file = db_result(db_query("SELECT file FROM {registry} WHERE name = '%s' AND type = 'function'", $function)); + if (!$file) { + $checked[$function] = FALSE; + return FALSE; + } + if ($file == './includes/theme.maintenance.inc') { + var_export(debug_backtrace()); + } + require_once($file); + _registry_mark_file($file); + } + $checked[$function] = TRUE; + return TRUE; +} + +/** + * Confirm that a class is available. + * + * This function parallel's registry_check_function(), but will rarely + * be called directly. Instead, it will be registered as an spl_autoload() + * handler, and PHP will call it for us when necessary. + * + * @param string $class + * The name of the class to check or load. + * @return + * TRUE if the class is now available, FALSE otherwise. + */ +function registry_check_class($class) { + $file = db_result(db_query("SELECT file FROM {registry} WHERE name = '%s' AND type = 'class'", $class)); + if (!$file) { + return FALSE; + } + require_once($file); + _registry_mark_file($file); + return TRUE; +} + +/** + * Confirm that an interface is available. + * + * This function parallel's registry_check_function(), but will rarely + * be called directly. Instead, it will be registered as an spl_autoload() + * handler, and PHP will call it for us when necessary. + * + * @param string $interface + * The name of the interface to check or load. + * @return + * TRUE if the interface is now available, FALSE otherwise. + */ +function registry_check_interface($interface) { + $file = db_result(db_query("SELECT file FROM {registry} WHERE name = '%s' AND type = 'interface'", $interface)); + if (!$file) { + return FALSE; + } + require_once($file); + _registry_mark_file($file); + return TRUE; +} Index: includes/form.inc =================================================================== RCS file: /cvs/drupal/drupal/includes/form.inc,v retrieving revision 1.267 diff -u -u -r1.267 form.inc --- includes/form.inc 12 Feb 2008 13:52:32 -0000 1.267 +++ includes/form.inc 22 Feb 2008 02:17:36 -0000 @@ -326,7 +326,7 @@ // We first check to see if there's a function named after the $form_id. // If there is, we simply pass the arguments on to it to get the form. - if (!function_exists($form_id)) { + if (!registry_check_function($form_id)) { // In cases where many form_ids need to share a central constructor function, // such as the node editing form, modules can implement hook_forms(). It // maps one or more form_ids to the correct constructor functions. @@ -347,6 +347,7 @@ } if (isset($form_definition['callback'])) { $callback = $form_definition['callback']; + registry_check_function($callback); } } @@ -503,13 +504,13 @@ $form += _element_info('form'); if (!isset($form['#validate'])) { - if (function_exists($form_id .'_validate')) { + if (registry_check_function($form_id .'_validate')) { $form['#validate'] = array($form_id .'_validate'); } } if (!isset($form['#submit'])) { - if (function_exists($form_id .'_submit')) { + if (registry_check_function($form_id .'_submit')) { // We set submit here so that it can be altered. $form['#submit'] = array($form_id .'_submit'); } @@ -710,7 +711,7 @@ // #value data. elseif (isset($elements['#element_validate'])) { foreach ($elements['#element_validate'] as $function) { - if (function_exists($function)) { + if (registry_check_function($function)) { $function($elements, $form_state, $complete_form); } } @@ -747,7 +748,7 @@ } foreach ($handlers as $function) { - if (function_exists($function)) { + if (registry_check_function($function)) { if ($type == 'submit' && ($batch =& batch_get())) { // Some previous _submit handler has set a batch. We store the call // in a special 'control' batch set, for execution at the correct @@ -1030,7 +1031,7 @@ // checkboxes and files. if (isset($form['#process']) && !$form['#processed']) { foreach ($form['#process'] as $process) { - if (function_exists($process)) { + if (registry_check_function($process)) { $form = $process($form, isset($edit) ? $edit : NULL, $form_state, $complete_form); } } Index: includes/install.inc =================================================================== RCS file: /cvs/drupal/drupal/includes/install.inc,v retrieving revision 1.58 diff -u -u -r1.58 install.inc --- includes/install.inc 12 Feb 2008 13:45:16 -0000 1.58 +++ includes/install.inc 22 Feb 2008 02:17:36 -0000 @@ -687,8 +687,9 @@ $requirements = array(); foreach ($installs as $install) { require_once $install->filename; - if (module_hook($install->name, 'requirements')) { - $requirements = array_merge($requirements, module_invoke($install->name, 'requirements', 'install')); + $function = $install->name. '_requirements'; + if (function_exists($function)) { + $requirements = array_merge($requirements, $function('install')); } } return $requirements; Index: includes/mail.inc =================================================================== RCS file: /cvs/drupal/drupal/includes/mail.inc,v retrieving revision 1.9 diff -u -u -r1.9 mail.inc --- includes/mail.inc 6 Feb 2008 19:38:26 -0000 1.9 +++ includes/mail.inc 22 Feb 2008 02:17:36 -0000 @@ -115,7 +115,7 @@ // Build the e-mail (get subject and body, allow additional headers) by // invoking hook_mail() on this module. We cannot use module_invoke() as // we need to have $message by reference in hook_mail(). - if (function_exists($function = $module .'_mail')) { + if (registry_check_function($function = $module .'_mail')) { $function($key, $message, $params); } Index: includes/menu.inc =================================================================== RCS file: /cvs/drupal/drupal/includes/menu.inc,v retrieving revision 1.262 diff -u -u -r1.262 menu.inc --- includes/menu.inc 10 Feb 2008 19:49:37 -0000 1.262 +++ includes/menu.inc 22 Feb 2008 02:17:36 -0000 @@ -340,11 +340,18 @@ menu_rebuild(); } if ($router_item = menu_get_item($path)) { + $cache = cache_get($router_item['path'], 'cache_registry'); + if (!empty($cache->data)) { + $files = explode(';', $cache->data); + foreach($files as $file) { + require_once($file); + } + _registry_mark_file($files); + } if ($router_item['access']) { - if ($router_item['file']) { - require_once($router_item['file']); + if (registry_check_function($router_item['page_callback'])) { + return call_user_func_array($router_item['page_callback'], $router_item['page_arguments']); } - return call_user_func_array($router_item['page_callback'], $router_item['page_arguments']); } else { return MENU_ACCESS_DENIED; @@ -2169,12 +2176,12 @@ $load_functions[$k] = NULL; } else { - if (function_exists($matches[1] .'_to_arg')) { + if (registry_check_function($matches[1] .'_to_arg')) { $to_arg_functions[$k] = $matches[1] .'_to_arg'; $load_functions[$k] = NULL; $match = TRUE; } - if (function_exists($matches[1] .'_load')) { + if (registry_check_function($matches[1] .'_load')) { $function = $matches[1] .'_load'; // Create an array of arguments that will be passed to the _load // function when this menu path is checked, if 'load arguments' @@ -2260,12 +2267,6 @@ if (!isset($item['page arguments']) && isset($parent['page arguments'])) { $item['page arguments'] = $parent['page arguments']; } - if (!isset($item['file']) && isset($parent['file'])) { - $item['file'] = $parent['file']; - } - if (!isset($item['file path']) && isset($parent['file path'])) { - $item['file path'] = $parent['file path']; - } } } } @@ -2293,34 +2294,25 @@ 'tab_parent' => '', 'tab_root' => $path, 'path' => $path, - 'file' => '', - 'file path' => '', - 'include file' => '', ); - // Calculate out the file to be included for each callback, if any. - if ($item['file']) { - $file_path = $item['file path'] ? $item['file path'] : drupal_get_path('module', $item['module']); - $item['include file'] = $file_path .'/'. $item['file']; - } - $title_arguments = $item['title arguments'] ? serialize($item['title arguments']) : ''; db_query("INSERT INTO {menu_router} (path, load_functions, to_arg_functions, access_callback, access_arguments, page_callback, page_arguments, fit, number_parts, tab_parent, tab_root, title, title_callback, title_arguments, - type, block_callback, description, position, weight, file) + type, block_callback, description, position, weight) VALUES ('%s', '%s', '%s', '%s', '%s', '%s', '%s', %d, %d, '%s', '%s', '%s', '%s', '%s', - %d, '%s', '%s', '%s', %d, '%s')", + %d, '%s', '%s', '%s', %d)", $path, $item['load_functions'], $item['to_arg_functions'], $item['access callback'], serialize($item['access arguments']), $item['page callback'], serialize($item['page arguments']), $item['_fit'], $item['_number_parts'], $item['tab_parent'], $item['tab_root'], $item['title'], $item['title callback'], $title_arguments, - $item['type'], $item['block callback'], $item['description'], $item['position'], $item['weight'], $item['include file']); + $item['type'], $item['block callback'], $item['description'], $item['position'], $item['weight']); } // Sort the masks so they are in order of descending fit, and store them. $masks = array_keys($masks); Index: includes/module.inc =================================================================== RCS file: /cvs/drupal/drupal/includes/module.inc,v retrieving revision 1.115 diff -u -u -r1.115 module.inc --- includes/module.inc 27 Dec 2007 12:31:05 -0000 1.115 +++ includes/module.inc 22 Feb 2008 02:17:36 -0000 @@ -228,7 +228,7 @@ */ function module_exists($module) { $list = module_list(); - return array_key_exists($module, $list); + return isset($list[$module]); } /** @@ -308,7 +308,7 @@ // We check for the existence of node_access_needs_rebuild() since // at install time, module_enable() could be called while node.module // is not enabled yet. - if (function_exists('node_access_needs_rebuild') && !node_access_needs_rebuild() && module_hook($module, 'node_grants')) { + if (registry_check_function('node_access_needs_rebuild') && !node_access_needs_rebuild() && module_hook($module, 'node_grants')) { node_access_needs_rebuild(TRUE); } } @@ -383,7 +383,9 @@ * implemented in that module. */ function module_hook($module, $hook) { - return function_exists($module .'_'. $hook); + $function = $module .'_'. $hook; + // During bootstrap, registry_check_function might not exist. + return function_exists('registry_check_function') ? registry_check_function($function) : function_exists($function); } /** @@ -402,30 +404,62 @@ * An array with the names of the modules which are implementing this hook. */ function module_implements($hook, $sort = FALSE, $refresh = FALSE) { - static $implementations; + static $implementations, $patterns, $loaded; if ($refresh) { $implementations = array(); + cache_clear_all('hooks', 'cache'); return; } - - if (!isset($implementations[$hook])) { - $implementations[$hook] = array(); - $list = module_list(FALSE, TRUE, $sort); - foreach ($list as $module) { - if (module_hook($module, $hook)) { - $implementations[$hook][] = $module; + if (empty($implementations)) { + // During install we can not rely on the registry. + if (!db_is_active() || defined('MAINTENANCE_MODE')) { + $list = module_list(); + $return = array(); + foreach ($list as $module) { + $function = $module .'_'. $hook; + if (function_exists($function)) { + $return[] = $module; + } } + return $return; + } + $cache = cache_get('hooks'); + if (!$cache) { + drupal_rebuild_code_registry(); + $cache = cache_get('hooks'); } + $implementations = $cache->data['implementations']; + $patterns = $cache->data['patterns']; } - - // The explicit cast forces a copy to be made. This is needed because - // $implementations[$hook] is only a reference to an element of - // $implementations and if there are nested foreaches (due to nested node - // API calls, for example), they would both manipulate the same array's - // references, which causes some modules' hooks not to be called. - // See also http://www.zend.com/zend/art/ref-count.php. - return (array)$implementations[$hook]; + if (isset($implementations[$hook])) { + if (!isset($loaded[$hook])) { + $loaded[$hook] = TRUE; + array_walk($implementations[$hook], 'registry_check_function'); + } + return (array)$implementations[$hook]; + } + // We do not store empty arrays for every instance of dynamic hooks like + // hook_{$form_id}_form_alter. The actual implementations of dynamic + // hooks are cached by drupal_rebuild_code_registry so those are already + // returned. If the hook matches a pattern here then we know that there is + // no implementation. + foreach ($patterns as $pattern) { + if (preg_match($pattern, $hook)) { + return array(); + } + } + // Other hooks are fetched from the registry and cached. + $implementations[$hook] = array(); + $result = db_query("SELECT module, name, file FROM {registry} WHERE hook = '%s' AND type = 'function'", $hook); + while ($entry = db_fetch_array($result)) { + if (!function_exists($entry['name'])) { + require_once($entry['file']); + } + $implementations[$hook][] = $entry['module']; + } + cache_set('hooks', array('patterns' => $patterns, 'implementations' => $implementations)); + return $implementations[$hook]; } /** Index: includes/theme.inc =================================================================== RCS file: /cvs/drupal/drupal/includes/theme.inc,v retrieving revision 1.417 diff -u -u -r1.417 theme.inc --- includes/theme.inc 20 Feb 2008 13:39:29 -0000 1.417 +++ includes/theme.inc 22 Feb 2008 02:17:36 -0000 @@ -628,7 +628,7 @@ // call_user_func_array. $args = array(&$variables, $hook); foreach ($info['preprocess functions'] as $preprocess_function) { - if (function_exists($preprocess_function)) { + if (registry_check_function($preprocess_function)) { call_user_func_array($preprocess_function, $args); } } Index: includes/xmlrpcs.inc =================================================================== RCS file: /cvs/drupal/drupal/includes/xmlrpcs.inc,v retrieving revision 1.24 diff -u -u -r1.24 xmlrpcs.inc --- includes/xmlrpcs.inc 31 Dec 2007 08:54:36 -0000 1.24 +++ includes/xmlrpcs.inc 22 Feb 2008 02:17:36 -0000 @@ -202,7 +202,7 @@ } } - if (!function_exists($method)) { + if (!registry_check_function($method)) { return xmlrpc_error(-32601, t('Server error. Requested function %method does not exist.', array("%method" => $method))); } // Call the mapped function Index: modules/system/system.install =================================================================== RCS file: /cvs/drupal/drupal/modules/system/system.install,v retrieving revision 1.239 diff -u -u -r1.239 system.install --- modules/system/system.install 20 Feb 2008 13:46:41 -0000 1.239 +++ modules/system/system.install 22 Feb 2008 02:17:36 -0000 @@ -551,6 +551,8 @@ $schema['cache_page']['description'] = t('Cache table used to store compressed pages for anonymous users, if page caching is enabled.'); $schema['cache_menu'] = $schema['cache']; $schema['cache_menu']['description'] = t('Cache table for the menu system to store router information as well as generated link trees for various menu/page/user combinations.'); + $schema['cache_registry'] = $schema['cache']; + $schema['cache_registry']['description'] = t('Cache table for the code registry system to remember what code files need to be loaded on any given page.'); $schema['files'] = array( 'description' => t('Stores information for uploaded files.'), @@ -773,10 +775,6 @@ 'type' => 'int', 'not null' => TRUE, 'default' => 0), - 'file' => array( - 'description' => t('The file to include for this element, usually the page callback function lives in this file.'), - 'type' => 'text', - 'size' => 'medium') ), 'indexes' => array( 'fit' => array('fit'), @@ -945,6 +943,48 @@ ), 'primary key' => array('mlid'), ); + $schema['registry'] = array( + 'description' => t("Each record is a function / class / interface name and the file it is in"), + 'fields' => array( + 'name' => array( + 'description' => t('The name of the function / class / interface.'), + 'type' => 'varchar', + 'length' => 255, + 'not null' => TRUE, + 'default' => '', + ), + 'type' => array( + 'description' => t('Either function or class or interface'), + 'type' => 'varchar', + 'length' => 9, + 'not null' => TRUE, + 'default' => '', + ), + 'module' => array( + 'description' => t('Name of the module this file belongs to.'), + 'type' => 'varchar', + 'length' => 255, + 'not null' => TRUE, + 'default' => '', + ), + 'hook' => array( + 'description' => t('Name of the function minus the name of the module'), + 'type' => 'varchar', + 'length' => 255, + 'not null' => TRUE, + 'default' => '', + ), + 'file' => array( + 'description' => t('Path to the file'), + 'type' => 'text', + 'size' => 'medium', + ), + ), + 'primary key' => array('name', 'type'), + 'indexes' => array( + 'module_implements' => array('type', 'hook'), + ), + ); $schema['sessions'] = array( 'description' => t("Drupal's session handlers read and write into the sessions table. Each record represents a user session, either anonymous or authenticated."), @@ -2467,6 +2507,11 @@ return $ret; } +function system_update_7000() { + $ret = array(); + db_drop_field($ret, 'menu_router', 'file'); +} + /** * @} End of "defgroup updates-5.x-to-6.x" * The next series of updates should start at 7000. @@ -2480,7 +2525,7 @@ /** * Rename blog and forum permissions to be consistent with other content types. */ -function system_update_7000() { +function system_update_7001() { $ret = array(); $result = db_query("SELECT rid, perm FROM {permission} ORDER BY rid"); while ($role = db_fetch_object($result)) { Index: modules/system/system.module =================================================================== RCS file: /cvs/drupal/drupal/modules/system/system.module,v retrieving revision 1.592 diff -u -u -r1.592 system.module --- modules/system/system.module 20 Feb 2008 13:46:41 -0000 1.592 +++ modules/system/system.module 22 Feb 2008 02:17:36 -0000 @@ -1305,6 +1305,13 @@ } /** + * Implementation of hook_hooks(). + */ +function system_hooks() { + return array('form__alter'); +} + +/** * Menu callback. Display an overview of available and configured actions. */ function system_actions_manage() { Index: modules/update/class.update_xml_parser.inc =================================================================== RCS file: modules/update/class.update_xml_parser.inc diff -N modules/update/class.update_xml_parser.inc --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ modules/update/class.update_xml_parser.inc 22 Feb 2008 02:17:36 -0000 @@ -0,0 +1,102 @@ +projects; + } + + function start($parser, $name, $attr) { + $this->current_tag = $name; + switch ($name) { + case 'PROJECT': + unset($this->current_object); + $this->current_project = array(); + $this->current_object = &$this->current_project; + break; + case 'RELEASE': + unset($this->current_object); + $this->current_release = array(); + $this->current_object = &$this->current_release; + break; + case 'TERM': + unset($this->current_object); + $this->current_term = array(); + $this->current_object = &$this->current_term; + break; + } + } + + function end($parser, $name) { + switch ($name) { + case 'PROJECT': + unset($this->current_object); + $this->projects[$this->current_project['short_name']] = $this->current_project; + $this->current_project = array(); + break; + case 'RELEASE': + unset($this->current_object); + $this->current_project['releases'][$this->current_release['version']] = $this->current_release; + break; + case 'RELEASES': + $this->current_object = &$this->current_project; + break; + case 'TERM': + unset($this->current_object); + $term_name = $this->current_term['name']; + if (!isset($this->current_release['terms'])) { + $this->current_release['terms'] = array(); + } + if (!isset($this->current_release['terms'][$term_name])) { + $this->current_release['terms'][$term_name] = array(); + } + $this->current_release['terms'][$term_name][] = $this->current_term['value']; + break; + case 'TERMS': + $this->current_object = &$this->current_release; + break; + default: + $this->current_object[strtolower($this->current_tag)] = trim($this->current_object[strtolower($this->current_tag)]); + $this->current_tag = ''; + } + } + + function data($parser, $data) { + if ($this->current_tag && !in_array($this->current_tag, array('PROJECT', 'RELEASE', 'RELEASES', 'TERM', 'TERMS'))) { + $tag = strtolower($this->current_tag); + if (isset($this->current_object[$tag])) { + $this->current_object[$tag] .= $data; + } + else { + $this->current_object[$tag] = $data; + } + } + } +} Index: modules/update/update.fetch.inc =================================================================== RCS file: /cvs/drupal/drupal/modules/update/update.fetch.inc,v retrieving revision 1.7 diff -u -u -r1.7 update.fetch.inc --- modules/update/update.fetch.inc 30 Jan 2008 10:14:42 -0000 1.7 +++ modules/update/update.fetch.inc 22 Feb 2008 02:17:36 -0000 @@ -135,97 +135,3 @@ } } -/** - * XML Parser object to read Drupal's release history info files. - * This uses PHP4's lame XML parsing, but it works. - */ -class update_xml_parser { - var $projects = array(); - var $current_project; - var $current_release; - var $current_term; - var $current_tag; - var $current_object; - - /** - * Parse an array of XML data files. - */ - function parse($data) { - foreach ($data as $datum) { - $parser = xml_parser_create(); - xml_set_object($parser, $this); - xml_set_element_handler($parser, 'start', 'end'); - xml_set_character_data_handler($parser, "data"); - xml_parse($parser, $datum); - xml_parser_free($parser); - } - return $this->projects; - } - - function start($parser, $name, $attr) { - $this->current_tag = $name; - switch ($name) { - case 'PROJECT': - unset($this->current_object); - $this->current_project = array(); - $this->current_object = &$this->current_project; - break; - case 'RELEASE': - unset($this->current_object); - $this->current_release = array(); - $this->current_object = &$this->current_release; - break; - case 'TERM': - unset($this->current_object); - $this->current_term = array(); - $this->current_object = &$this->current_term; - break; - } - } - - function end($parser, $name) { - switch ($name) { - case 'PROJECT': - unset($this->current_object); - $this->projects[$this->current_project['short_name']] = $this->current_project; - $this->current_project = array(); - break; - case 'RELEASE': - unset($this->current_object); - $this->current_project['releases'][$this->current_release['version']] = $this->current_release; - break; - case 'RELEASES': - $this->current_object = &$this->current_project; - break; - case 'TERM': - unset($this->current_object); - $term_name = $this->current_term['name']; - if (!isset($this->current_release['terms'])) { - $this->current_release['terms'] = array(); - } - if (!isset($this->current_release['terms'][$term_name])) { - $this->current_release['terms'][$term_name] = array(); - } - $this->current_release['terms'][$term_name][] = $this->current_term['value']; - break; - case 'TERMS': - $this->current_object = &$this->current_release; - break; - default: - $this->current_object[strtolower($this->current_tag)] = trim($this->current_object[strtolower($this->current_tag)]); - $this->current_tag = ''; - } - } - - function data($parser, $data) { - if ($this->current_tag && !in_array($this->current_tag, array('PROJECT', 'RELEASE', 'RELEASES', 'TERM', 'TERMS'))) { - $tag = strtolower($this->current_tag); - if (isset($this->current_object[$tag])) { - $this->current_object[$tag] .= $data; - } - else { - $this->current_object[$tag] = $data; - } - } - } -}