Index: includes/bootstrap.inc
===================================================================
RCS file: /cvs/drupal/drupal/includes/bootstrap.inc,v
retrieving revision 1.163
diff -u -p -r1.163 bootstrap.inc
--- includes/bootstrap.inc	30 Apr 2007 14:37:36 -0000	1.163
+++ includes/bootstrap.inc	5 May 2007 09:14:41 -0000
@@ -535,6 +535,46 @@ function drupal_load($type, $name) {
 }
 
 /**
+ * Includes a library file provided by a module.  This allows
+ * modules to be broken up into functional components while still
+ * providing access to its functions to other modules.
+ *
+ * @param $module
+ *   The module in which to load this component.
+ * @param $component
+ *   The non-module part of the name of the function we're trying to load.  
+ *   The corresponding library file is the part of the function stub after the
+ *   first underscore.  So for example to ensure that the function 'system_admin_theme_settings'
+ *   is available, call drupal_include('system', 'admin_theme_settings').  This function will then
+ *   include system/admin.inc if it exists.
+ *
+ * @return
+ *   TRUE if the corresponding file was includable and included successfully, FALSE otherwise.
+ */
+function drupal_include($module, $component) {
+  static $files = array();
+
+  $underscore = strpos($component, '_');
+  if ($underscore !== FALSE) {
+    $include = substr($component, 0, $underscore);
+    $filename = dirname(drupal_get_filename('module', $module)) . "/$module.$include.inc";
+    if (isset($files[$module][$filename])) {
+      return $files[$module][$filename];
+    }
+
+    $files[$module][$filename] = FALSE;
+    if (file_exists($filename)) {
+      include_once($filename);
+       $files[$module][$filename] = TRUE;
+    }
+    return $files[$module][$filename];
+  }
+
+  return FALSE;
+}
+
+
+/**
  * Set HTTP headers in preparation for a page response.
  *
  * Authenticated users are always given a 'no-cache' header, and will
Index: includes/form.inc
===================================================================
RCS file: /cvs/drupal/drupal/includes/form.inc,v
retrieving revision 1.191
diff -u -p -r1.191 form.inc
--- includes/form.inc	4 May 2007 09:41:36 -0000	1.191
+++ includes/form.inc	5 May 2007 09:14:41 -0000
@@ -202,19 +202,35 @@ function drupal_retrieve_form($form_id) 
     // So, we call the hook if $forms isn't yet populated, OR if it doesn't
     // yet have an entry for the requested form_id.
     if (!isset($forms) || !isset($forms[$form_id])) {
-      $forms = module_invoke_all('forms', $saved_args);
+      $forms = array();
+      // We need to manually call each module so that we can know which module a given form came from.
+      foreach (module_implements('forms') as $module) {
+        $module_forms = call_user_func_array($module . '_forms', $saved_args);
+        if (isset($module_forms) && is_array($module_forms)) {
+          foreach (array_keys($module_forms) as $form) {
+            $module_forms[$form]['module'] = $module;
+          }
+          //dpr($module_forms);
+          $forms = array_merge($forms, $module_forms);
+        }
+      }
     }
     $form_definition = $forms[$form_id];
     if (isset($form_definition['callback arguments'])) {
       $args = array_merge($form_definition['callback arguments'], $args);
     }
-    if (isset($form_definition['callback'])) {
-      $callback = $form_definition['callback'];
+
+    // If 'callback' was returned by a hook_forms() implementation, use that as the callback.
+    // Otherwise, use the function named after the form id.
+    $callback = isset($form_definition['callback']) ? $form_definition['callback'] : $form_id;
+
+    // There may be a file that needs to be included.
+    if (strpos($callback, $form_definition['module']) === 0) {
+      drupal_include($form_definition['module'], substr($callback, strlen($form_definition['module'])+1));
     }
   }
-  // If $callback was returned by a hook_forms() implementation, call it.
-  // Otherwise, call the function named after the form id.
-  $form = call_user_func_array(isset($callback) ? $callback : $form_id, $args);
+
+  $form = call_user_func_array($callback, $args);
 
   // We store the original function arguments, rather than the final $arg
   // value, so that form_alter functions can see what was originally
Index: includes/menu.inc
===================================================================
RCS file: /cvs/drupal/drupal/includes/menu.inc,v
retrieving revision 1.164
diff -u -p -r1.164 menu.inc
--- includes/menu.inc	30 Apr 2007 17:03:22 -0000	1.164
+++ includes/menu.inc	5 May 2007 09:14:42 -0000
@@ -343,7 +343,15 @@ function menu_get_item($path = NULL, $it
  */
 function menu_execute_active_handler() {
   if ($item = menu_get_item()) {
-    return $item->access ? call_user_func_array($item->page_callback, $item->page_arguments) : MENU_ACCESS_DENIED;
+    if (!$item->access) {
+      return MENU_ACCESS_DENIED;
+    }
+    $include = $item->page_callback;
+    if (strpos($include, $item->module) === 0) {
+      $include = substr($include, strlen($item->module)+1);
+    }
+    drupal_include($item->module, $include);
+    return call_user_func_array($item->page_callback, $item->page_arguments);
   }
   return MENU_NOT_FOUND;
 }
@@ -626,7 +634,22 @@ function menu_path_is_external($path) {
  */
 function menu_rebuild() {
   // TODO: split menu and menu links storage.
-  $menu = module_invoke_all('menu');
+
+  // We need to manually call each module so that we can know which module a given item came from.
+  // If a given menu item doesn't specify what module its callback lives in, assume it's the 
+  // defining module.
+  $menu = array();
+  foreach (module_implements('menu') as $module) {
+    $items = call_user_func($module . '_menu');
+    if (isset($items) && is_array($items)) {
+      foreach (array_keys($items) as $path) {
+        if (!isset($items[$path]['module'])) {
+          $items[$path]['module'] = $module;
+        }
+      }
+      $menu = array_merge($menu, $items);
+    }
+  }
 
   // Alter the menu as defined in modules, keys are like user/%user.
   drupal_alter('menu', $menu, MENU_ALTER_MODULE_DEFINED);
@@ -887,10 +910,10 @@ function menu_rebuild() {
       title_callback, title_arguments, fit, number_parts, visible,
       parents, depth, has_children, tab, title, parent,
       type, mleft, mright, block_callback, description, position,
-      link_path, attributes, query, fragment, absolute, html)
+      link_path, attributes, query, fragment, absolute, html, module)
       VALUES (%d, %d, '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s',
       %d, %d, %d, '%s', %d, %d, %d, '%s', '%s', '%s', %d, %d, '%s', '%s',
-      '%s', '%s', '%s', '%s', '%s', %d, %d)",
+      '%s', '%s', '%s', '%s', '%s', %d, %d, '%s')",
       $item['_mid'], $item['_pid'], $path, $item['load_functions'],
       $item['to_arg_functions'], $item['access callback'],
       serialize($item['access arguments']), $item['page callback'],
@@ -902,7 +925,7 @@ function menu_rebuild() {
       $item['_mright'], $item['block callback'], $item['description'],
       $item['position'], $link_path,
       $item['attributes'], $item['query'], $item['fragment'],
-      $item['absolute'], $item['html']);
+      $item['absolute'], $item['html'], $item['module']);
   }
 }
 
Index: includes/module.inc
===================================================================
RCS file: /cvs/drupal/drupal/includes/module.inc,v
retrieving revision 1.100
diff -u -p -r1.100 module.inc
--- includes/module.inc	17 Apr 2007 07:19:38 -0000	1.100
+++ includes/module.inc	5 May 2007 09:14:42 -0000
@@ -345,6 +345,7 @@ function module_invoke() {
   $module = array_shift($args);
   $hook = array_shift($args);
   $function = $module .'_'. $hook;
+  drupal_include($module, $hook);
   if (module_hook($module, $hook)) {
     return call_user_func_array($function, $args);
   }
@@ -366,6 +367,7 @@ function module_invoke_all() {
   $return = array();
   foreach (module_implements($hook) as $module) {
     $function = $module .'_'. $hook;
+    drupal_include($module, $hook);
     $result = call_user_func_array($function, $args);
     if (isset($result) && is_array($result)) {
       $return = array_merge($return, $result);
Index: modules/node/node.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/node/node.module,v
retrieving revision 1.806
diff -u -p -r1.806 node.module
--- modules/node/node.module	30 Apr 2007 17:03:26 -0000	1.806
+++ modules/node/node.module	5 May 2007 09:14:42 -0000
@@ -1128,6 +1128,7 @@ function node_menu() {
     'weight' => -10,
     'page callback' => 'system_admin_menu_block_page',
     'access arguments' => array('administer site configuration'),
+    'module' => 'system',
   );
 
   $items['admin/content/node'] = array(
Index: modules/system/system.admin.inc
===================================================================
RCS file: modules/system/system.admin.inc
diff -N modules/system/system.admin.inc
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ modules/system/system.admin.inc	5 May 2007 09:14:42 -0000
@@ -0,0 +1,128 @@
+<?php
+
+
+/**
+ * Provide the administration overview page.
+ */
+function system_admin_main_page($arg = NULL) {
+  // If we received an argument, they probably meant some other page.
+  // Let's 404 them since the menu system cannot be told we do not
+  // accept arguments.
+  if (isset($arg) && substr($arg, 0, 3) != 'by-') {
+    return drupal_not_found();
+  }
+
+  // Check for status report errors.
+  if (system_status(TRUE)) {
+    drupal_set_message(t('One or more problems were detected with your Drupal installation. Check the <a href="@status">status report</a> for more information.', array('@status' => url('admin/logs/status'))), 'error');
+  }
+
+  $map = arg(NULL);
+  $result = db_query("SELECT * FROM {menu} WHERE path LIKE 'admin/%%' AND depth = 2 AND visible = 1 AND path != 'admin/help' ORDER BY mleft");
+  while ($item = db_fetch_object($result)) {
+    _menu_translate($item, $map, MENU_RENDER_LINK);
+    if (!$item->access) {
+      continue;
+    }
+    $block = (array)$item;
+    $block['content'] = '';
+    if ($item->block_callback && function_exists($item->block_callback)) {
+      $function = $item->block_callback;
+      $block['content'] .= $function();
+    }
+    $block['content'] .= theme('admin_block_content', system_admin_menu_block($item));
+    $blocks[] = $block;
+  }
+  return theme('admin_page', $blocks);
+}
+
+function system_admin_compact_page($mode = 'off') {
+  global $user;
+  user_save($user, array('admin_compact_mode' => ($mode == 'on')));
+  drupal_goto('admin');
+}
+
+/**
+ * Menu callback; prints a listing of admin tasks for each installed module.
+ */
+function system_admin_by_module() {
+  return 'This page awaits rewrite'; // TODO: this needs to be rewritten for the new menu system.
+  $modules = module_rebuild_cache();
+  $menu_items = array();
+  foreach ($modules as $file) {
+    $module = $file->name;
+    if ($module == 'help') {
+      continue;
+    }
+
+    $admin_tasks = system_get_module_admin_tasks($module);
+
+    // Only display a section if there are any available tasks.
+    if (count($admin_tasks)) {
+
+      // Check for help links.
+      if (module_invoke($module, 'help', "admin/help#$module")) {
+        $admin_tasks[100] = l(t('Get help'), "admin/help/$module");
+      }
+
+      // Sort.
+      ksort($admin_tasks);
+
+      $menu_items[$file->info['name']] = array($file->info['description'], $admin_tasks);
+    }
+  }
+  return theme('system_admin_by_module', $menu_items);
+}
+
+/**
+ * This function allows selection of the theme to show in administration sections.
+ */
+function system_admin_theme_settings() {
+  $themes = system_theme_data();
+  ksort($themes);
+  $options[0] = t('System default');
+  foreach ($themes as $theme) {
+    $options[$theme->name] = $theme->info['name'];
+  }
+
+  $form['admin_theme'] = array(
+    '#type' => 'select',
+    '#options' => $options,
+    '#title' => t('Administration theme'),
+    '#description' => t('Choose which theme the administration pages should display in. If you choose "System default" the administration pages will use the same theme as the rest of the site.'),
+    '#default_value' => variable_get('admin_theme', '0'),
+  );
+
+  // In order to give it our own submit, we have to give it the default submit
+  // too because the presence of a #submit will prevent the default #submit
+  // from being used. Also we want ours first.
+  $form['#submit']['system_admin_theme_submit'] = array();
+  $form['#submit']['system_settings_form_submit'] = array();
+  return system_settings_form($form);
+}
+
+
+function system_admin_theme_submit($form_id, $form_values) {
+  // If we're changing themes, make sure the theme has its blocks initialized.
+  if ($form_values['admin_theme'] != variable_get('admin_theme', '0')) {
+    $result = db_query("SELECT status FROM {blocks} WHERE theme = '%s'", $form_values['admin_theme']);
+    if (!db_num_rows($result)) {
+      system_initialize_theme_blocks($form_values['admin_theme']);
+    }
+  }
+}
+
+/**
+ * Provide a single block from the administration menu as a page.
+ * This function is often a destination for these blocks.
+ * For example, 'admin/content/types' needs to have a destination to be valid
+ * in the Drupal menu system, but too much information there might be
+ * hidden, so we supply the contents of the block.
+ */
+function system_admin_menu_block_page() {
+  $item = menu_get_item();
+  $content = system_admin_menu_block($item);
+
+  $output = theme('admin_block_content', $content);
+  return $output;
+}
Index: modules/system/system.install
===================================================================
RCS file: /cvs/drupal/drupal/modules/system/system.install,v
retrieving revision 1.100
diff -u -p -r1.100 system.install
--- modules/system/system.install	4 May 2007 09:41:36 -0000	1.100
+++ modules/system/system.install	5 May 2007 09:14:43 -0000
@@ -368,6 +368,7 @@ function system_install() {
         fragment varchar(255) NOT NULL default '',
         absolute INT NOT NULL default 0,
         html INT NOT NULL default 0,
+        module varchar(255) NOT NULL default '',
         PRIMARY KEY  (path),
         KEY fit (fit),
         KEY visible (visible),
@@ -853,6 +854,7 @@ function system_install() {
         fragment varchar(255) NOT NULL default '',
         absolute INT NOT NULL default 0,
         html INT NOT NULL default 0,
+        module varchar(255) NOT NULL default '',
         PRIMARY KEY (path)
       )");
 
@@ -3865,6 +3867,25 @@ function system_update_6012() {
 }
 
 /**
+ * Add module column to menu table
+ */
+function system_update_6013() {
+  $ret = array();
+
+  switch ($GLOBALS['db_type']) {
+    case 'pgsql':
+      db_add_column($ret, 'menu', 'module', 'varchar(255)', array('default' => "''", 'not null' => TRUE));
+      break;
+    case 'mysql':
+    case 'mysqli':
+      $ret[] = update_sql("ALTER TABLE {menu} ADD module varchar(255) NOT NULL default ''");
+      break;
+  }
+
+  return $ret;
+}
+
+/**
  * @} End of "defgroup updates-5.x-to-6.x"
  * The next series of updates should start at 7000.
  */
Index: modules/system/system.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/system/system.module,v
retrieving revision 1.473
diff -u -p -r1.473 system.module
--- modules/system/system.module	4 May 2007 09:41:37 -0000	1.473
+++ modules/system/system.module	5 May 2007 09:14:43 -0000
@@ -119,6 +119,11 @@ function system_elements() {
   return $type;
 }
 
+function system_forms() {
+  $forms['system_admin_theme_settings'] = array();
+  return $forms; 
+}
+
 /**
  * Implementation of hook_menu().
  */
@@ -132,7 +137,7 @@ function system_menu() {
   $items['admin'] = array(
     'title' => 'Administer',
     'access arguments' => array('access administration pages'),
-    'page callback' => 'system_main_admin_page',
+    'page callback' => 'system_admin_main_page',
     'weight' => 9,
   );
   $items['admin/compact'] = array(
@@ -142,7 +147,7 @@ function system_menu() {
   );
   $items['admin/by-task'] = array(
     'title' => 'By task',
-    'page callback' => 'system_main_admin_page',
+    'page callback' => 'system_admin_main_page',
     'type' => MENU_DEFAULT_LOCAL_TASK,
   );
   $items['admin/by-module'] = array(
@@ -380,41 +385,6 @@ function system_user($type, $edit, &$use
 }
 
 /**
- * Provide the administration overview page.
- */
-function system_main_admin_page($arg = NULL) {
-  // If we received an argument, they probably meant some other page.
-  // Let's 404 them since the menu system cannot be told we do not
-  // accept arguments.
-  if (isset($arg) && substr($arg, 0, 3) != 'by-') {
-    return drupal_not_found();
-  }
-
-  // Check for status report errors.
-  if (system_status(TRUE)) {
-    drupal_set_message(t('One or more problems were detected with your Drupal installation. Check the <a href="@status">status report</a> for more information.', array('@status' => url('admin/logs/status'))), 'error');
-  }
-
-  $map = arg(NULL);
-  $result = db_query("SELECT * FROM {menu} WHERE path LIKE 'admin/%%' AND depth = 2 AND visible = 1 AND path != 'admin/help' ORDER BY mleft");
-  while ($item = db_fetch_object($result)) {
-    _menu_translate($item, $map, MENU_RENDER_LINK);
-    if (!$item->access) {
-      continue;
-    }
-    $block = (array)$item;
-    $block['content'] = '';
-    if ($item->block_callback && function_exists($item->block_callback)) {
-      $function = $item->block_callback;
-      $block['content'] .= $function();
-    }
-    $block['content'] .= theme('admin_block_content', system_admin_menu_block($item));
-    $blocks[] = $block;
-  }
-  return theme('admin_page', $blocks);
-}
-
-/**
  * Provide a single block on the administration overview page.
  */
 function system_admin_menu_block($item) {
@@ -431,65 +401,6 @@ function system_admin_menu_block($item) 
   return $content;
 }
 
-/**
- * Provide a single block from the administration menu as a page.
- * This function is often a destination for these blocks.
- * For example, 'admin/content/types' needs to have a destination to be valid
- * in the Drupal menu system, but too much information there might be
- * hidden, so we supply the contents of the block.
- */
-function system_admin_menu_block_page() {
-  $item = menu_get_item();
-  $content = system_admin_menu_block($item);
-
-  $output = theme('admin_block_content', $content);
-  return $output;
-}
-
-function system_admin_compact_page($mode = 'off') {
-  global $user;
-  user_save($user, array('admin_compact_mode' => ($mode == 'on')));
-  drupal_goto('admin');
-}
-
-/**
- * This function allows selection of the theme to show in administration sections.
- */
-function system_admin_theme_settings() {
-  $themes = system_theme_data();
-  ksort($themes);
-  $options[0] = t('System default');
-  foreach ($themes as $theme) {
-    $options[$theme->name] = $theme->info['name'];
-  }
-
-  $form['admin_theme'] = array(
-    '#type' => 'select',
-    '#options' => $options,
-    '#title' => t('Administration theme'),
-    '#description' => t('Choose which theme the administration pages should display in. If you choose "System default" the administration pages will use the same theme as the rest of the site.'),
-    '#default_value' => variable_get('admin_theme', '0'),
-  );
-
-  // In order to give it our own submit, we have to give it the default submit
-  // too because the presence of a #submit will prevent the default #submit
-  // from being used. Also we want ours first.
-  $form['#submit']['system_admin_theme_submit'] = array();
-  $form['#submit']['system_settings_form_submit'] = array();
-  return system_settings_form($form);
-}
-
-
-function system_admin_theme_submit($form_id, $form_values) {
-  // If we're changing themes, make sure the theme has its blocks initialized.
-  if ($form_values['admin_theme'] != variable_get('admin_theme', '0')) {
-    $result = db_query("SELECT status FROM {blocks} WHERE theme = '%s'", $form_values['admin_theme']);
-    if (!db_num_rows($result)) {
-      system_initialize_theme_blocks($form_values['admin_theme']);
-    }
-  }
-}
-
 /*
  * Returns a fieldset containing the theme select form.
  *
@@ -2360,38 +2271,6 @@ function theme_admin_block_content($cont
   return $output;
 }
 
-/**
- * Menu callback; prints a listing of admin tasks for each installed module.
- */
-function system_admin_by_module() {
-  return 'This page awaits rewrite'; // TODO: this needs to be rewritten for the new menu system.
-  $modules = module_rebuild_cache();
-  $menu_items = array();
-  foreach ($modules as $file) {
-    $module = $file->name;
-    if ($module == 'help') {
-      continue;
-    }
-
-    $admin_tasks = system_get_module_admin_tasks($module);
-
-    // Only display a section if there are any available tasks.
-    if (count($admin_tasks)) {
-
-      // Check for help links.
-      if (module_invoke($module, 'help', "admin/help#$module")) {
-        $admin_tasks[100] = l(t('Get help'), "admin/help/$module");
-      }
-
-      // Sort.
-      ksort($admin_tasks);
-
-      $menu_items[$file->info['name']] = array($file->info['description'], $admin_tasks);
-    }
-  }
-  return theme('system_admin_by_module', $menu_items);
-}
-
 function system_get_module_admin_tasks($module) {
   return array(); // TODO: this needs to be rewritten for the new menu system.
   $admin_access = user_access('administer access control');
k
