Index: includes/module.inc =================================================================== RCS file: /cvs/drupal/drupal/includes/module.inc,v retrieving revision 1.206 diff -u -p -r1.206 module.inc --- includes/module.inc 21 Nov 2010 10:19:48 -0000 1.206 +++ includes/module.inc 22 Nov 2010 06:50:22 -0000 @@ -595,6 +595,25 @@ function module_hook($module, $hook) { } /** + * Ensures that a module hook implementation, if any, is loaded. + */ +function module_load_hook($module, $hook) { + // Nothing to do if the hook implementation already exists. + $function = $module . '_' . $hook; + if (function_exists($function)) { + return TRUE; + } + $hook_info = module_hook_info(); + if (isset($hook_info[$hook]['group'])) { + module_load_include('inc', $module, $module . '.' . $hook_info[$hook]['group']); + if (function_exists($function)) { + return TRUE; + } + } + return FALSE; +} + +/** * Determine which modules are implementing a hook. * * @param $hook @@ -695,11 +714,15 @@ function module_implements($hook, $sort * Retrieve a list of what hooks are explicitly declared. */ function module_hook_info() { - $hook_info = &drupal_static(__FUNCTION__, array()); - - if (empty($hook_info)) { - $cache = cache_get('hook_info', 'cache_bootstrap'); - if ($cache === FALSE) { + // This function is also indirectly invoked from bootstrap_invoke_all(). + // Therefore, all caches have to be specific to the current bootstrap phase. + $cid = __FUNCTION__ . ':' . drupal_bootstrap(NULL, FALSE); + $hook_info = &drupal_static($cid); + + if (!isset($hook_info)) { + $hook_info = array(); + $cache = cache_get($cid, 'cache_bootstrap'); + if (!$cache) { // Rebuild the cache and save it. // We can't use module_invoke_all() here or it would cause an infinite // loop. @@ -719,7 +742,7 @@ function module_hook_info() { $function($hook_info); } } - cache_set('hook_info', $hook_info, 'cache_bootstrap'); + cache_set($cid, $hook_info, 'cache_bootstrap'); } else { $hook_info = $cache->data; @@ -763,10 +786,14 @@ function module_invoke() { $module = $args[0]; $hook = $args[1]; unset($args[0], $args[1]); - if (module_hook($module, $hook)) { + + // If the hook implementation does not exist, check whether it may live in an + // optional include file registered via hook_hook_info(). + if (module_load_hook($module, $hook)) { return call_user_func_array($module . '_' . $hook, $args); } } + /** * Invoke a hook in all enabled modules that implement it. * Index: modules/field/field.form.inc =================================================================== RCS file: /cvs/drupal/drupal/modules/field/field.form.inc,v retrieving revision 1.54 diff -u -p -r1.54 field.form.inc --- modules/field/field.form.inc 20 Nov 2010 19:57:01 -0000 1.54 +++ modules/field/field.form.inc 22 Nov 2010 06:48:59 -0000 @@ -61,8 +61,8 @@ function field_default_form($entity_type // make it the $delta value. else { $delta = isset($get_delta) ? $get_delta : 0; - $function = $instance['widget']['module'] . '_field_widget_form'; - if (function_exists($function)) { + if (module_load_hook($instance['widget']['module'], 'field_widget_form')) { + $function = $instance['widget']['module'] . '_field_widget_form'; $element = array( '#entity_type' => $instance['entity_type'], '#bundle' => $instance['bundle'], @@ -160,8 +160,8 @@ function field_multiple_value_form($fiel $field_elements = array(); - $function = $instance['widget']['module'] . '_field_widget_form'; - if (function_exists($function)) { + if (module_load_hook($instance['widget']['module'], 'field_widget_form')) { + $function = $instance['widget']['module'] . '_field_widget_form'; for ($delta = 0; $delta <= $max; $delta++) { $multiple = $field['cardinality'] > 1 || $field['cardinality'] == FIELD_CARDINALITY_UNLIMITED; $element = array( @@ -337,7 +337,7 @@ function field_default_form_errors($enti if (!empty($field_state['errors'])) { $function = $instance['widget']['module'] . '_field_widget_error'; - $function_exists = function_exists($function); + $function_exists = module_load_hook($instance['widget']['module'], 'field_widget_error'); // Locate the correct element in the the form. $element = drupal_array_get_nested_value($form_state['complete form'], $field_state['array_parents']); Index: modules/field/field.module =================================================================== RCS file: /cvs/drupal/drupal/modules/field/field.module,v retrieving revision 1.90 diff -u -p -r1.90 field.module --- modules/field/field.module 21 Nov 2010 19:09:18 -0000 1.90 +++ modules/field/field.module 22 Nov 2010 06:13:01 -0000 @@ -110,6 +110,102 @@ define('FIELD_LOAD_REVISION', 'FIELD_LOA class FieldUpdateForbiddenException extends FieldException {} /** + * Implements hook_hook_info(). + */ +function field_hook_info() { + $field_hooks = array( + 'field_access', + 'field_attach_create_bundle', + 'field_attach_delete', + 'field_attach_delete_bundle', + 'field_attach_delete_revision', + 'field_attach_form', + 'field_attach_insert', + 'field_attach_load', + 'field_attach_prepare_translation_alter', + 'field_attach_preprocess_alter', + 'field_attach_presave', + 'field_attach_purge', + 'field_attach_rename_bundle', + 'field_attach_submit', + 'field_attach_update', + 'field_attach_validate', + 'field_attach_view_alter', + 'field_available_languages_alter', + 'field_create_field', + 'field_create_instance', + 'field_delete', + 'field_delete_field', + 'field_delete_instance', + 'field_delete_revision', + 'field_display_alter', + // @todo http://drupal.org/node/968264 + 'field_display_ENTITY_TYPE_alter', + 'field_extra_fields', + 'field_extra_fields_alter', + 'field_extra_fields_display_alter', + 'field_formatter_info', + 'field_formatter_info_alter', + 'field_formatter_prepare_view', + 'field_formatter_settings_form', + 'field_formatter_settings_summary', + 'field_formatter_view', + 'field_info', + 'field_info_alter', + 'field_info_max_weight', + 'field_insert', + 'field_instance_settings_form', + 'field_is_empty', + 'field_language_alter', + 'field_load', + 'field_prepare_translation', + 'field_prepare_view', + 'field_presave', + 'field_purge_field', + 'field_purge_field_instance', + 'field_read_field', + 'field_read_instance', + 'field_settings_form', + 'field_storage_create_field', + 'field_storage_delete', + 'field_storage_delete_field', + 'field_storage_delete_instance', + 'field_storage_delete_revision', + 'field_storage_details', + 'field_storage_details_alter', + 'field_storage_info', + 'field_storage_info_alter', + 'field_storage_load', + 'field_storage_pre_insert', + 'field_storage_pre_load', + 'field_storage_pre_update', + 'field_storage_purge', + 'field_storage_purge_field', + 'field_storage_purge_field_instance', + 'field_storage_query', + 'field_storage_update_field', + 'field_storage_write', + 'field_update', + 'field_update_field', + 'field_update_forbid', + 'field_update_instance', + 'field_validate', + 'field_widget_error', + 'field_widget_form', + 'field_widget_info', + 'field_widget_info_alter', + 'field_widget_properties_alter', + // @todo http://drupal.org/node/968264 + 'field_widget_properties_ENTITY_TYPE_alter', + 'field_widget_settings_form', + ); + $hooks = array_fill_keys($field_hooks, array( + 'group' => 'field', + )); + return $hooks; +} + +/** * Implements hook_flush_caches(). */ function field_flush_caches() { Index: modules/file/file.field.inc =================================================================== RCS file: /cvs/drupal/drupal/modules/file/file.field.inc,v retrieving revision 1.41 diff -u -p -r1.41 file.field.inc --- modules/file/file.field.inc 22 Nov 2010 04:42:09 -0000 1.41 +++ modules/file/file.field.inc 22 Nov 2010 05:51:52 -0000 @@ -42,15 +42,18 @@ function file_field_settings_form($field $form['display_field'] = array( '#type' => 'checkbox', - '#title' => t('Enable Display field'), + '#title' => t('Allow users to choose if files should be shown when viewing content'), '#default_value' => $settings['display_field'], - '#description' => t('The display option allows users to choose if a file should be shown when viewing the content.'), ); $form['display_default'] = array( '#type' => 'checkbox', - '#title' => t('Files displayed by default'), + '#title' => t('Display files by default'), '#default_value' => $settings['display_default'], - '#description' => t('This setting only has an effect if the display option is enabled.'), + '#states' => array( + 'visible' => array( + ':input[name*="display_field"]' => array('checked' => TRUE), + ), + ), ); $scheme_options = array(); @@ -486,6 +489,12 @@ function file_field_widget_form(&$form, // Allows this field to return an array instead of a single value. '#extended' => TRUE, ); + // file_field_widget_process() is defined in this file, so ensure that it is + // loaded when the form is processed. + $form_state['build_info']['files']['file'] = array( + 'module' => 'file', + 'name' => 'file.field', + ); if ($field['cardinality'] == 1) { // If there's only one field, return it as delta 0. Index: modules/file/file.module =================================================================== RCS file: /cvs/drupal/drupal/modules/file/file.module,v retrieving revision 1.46 diff -u -p -r1.46 file.module --- modules/file/file.module 13 Nov 2010 14:04:08 -0000 1.46 +++ modules/file/file.module 22 Nov 2010 05:51:52 -0000 @@ -6,9 +6,6 @@ * Defines a "managed_file" Form API field and a "file" field for Field module. */ -// Load all Field module hooks for File. -require_once DRUPAL_ROOT . '/modules/file/file.field.inc'; - /** * Implements hook_help(). */ Index: modules/image/image.field.inc =================================================================== RCS file: /cvs/drupal/drupal/modules/image/image.field.inc,v retrieving revision 1.34 diff -u -p -r1.34 image.field.inc --- modules/image/image.field.inc 31 Oct 2010 12:12:00 -0000 1.34 +++ modules/image/image.field.inc 22 Nov 2010 06:02:26 -0000 @@ -71,6 +71,7 @@ function image_field_instance_settings_f $settings = $instance['settings']; // Use the file field instance settings form as a basis. + module_load_hook('file', 'field_instance_settings_form'); $form = file_field_instance_settings_form($field, $instance); // Add maximum and minimum resolution settings. @@ -181,6 +182,7 @@ function _image_field_resolution_validat * Implements hook_field_load(). */ function image_field_load($entity_type, $entities, $field, $instances, $langcode, &$items, $age) { + module_load_hook('file', 'field_load'); file_field_load($entity_type, $entities, $field, $instances, $langcode, $items, $age); } @@ -206,6 +208,7 @@ function image_field_prepare_view($entit * Implements hook_field_presave(). */ function image_field_presave($entity_type, $entity, $field, $instance, $langcode, &$items) { + module_load_hook('file', 'field_presave'); file_field_presave($entity_type, $entity, $field, $instance, $langcode, $items); } @@ -213,6 +216,7 @@ function image_field_presave($entity_typ * Implements hook_field_insert(). */ function image_field_insert($entity_type, $entity, $field, $instance, $langcode, &$items) { + module_load_hook('file', 'field_insert'); file_field_insert($entity_type, $entity, $field, $instance, $langcode, $items); } @@ -220,6 +224,7 @@ function image_field_insert($entity_type * Implements hook_field_update(). */ function image_field_update($entity_type, $entity, $field, $instance, $langcode, &$items) { + module_load_hook('file', 'field_update'); file_field_update($entity_type, $entity, $field, $instance, $langcode, $items); } @@ -227,6 +232,7 @@ function image_field_update($entity_type * Implements hook_field_delete(). */ function image_field_delete($entity_type, $entity, $field, $instance, $langcode, &$items) { + module_load_hook('file', 'field_delete'); file_field_delete($entity_type, $entity, $field, $instance, $langcode, $items); } @@ -234,6 +240,7 @@ function image_field_delete($entity_type * Implements hook_field_delete_revision(). */ function image_field_delete_revision($entity_type, $entity, $field, $instance, $langcode, &$items) { + module_load_hook('file', 'field_delete_revision'); file_field_delete_revision($entity_type, $entity, $field, $instance, $langcode, $items); } @@ -241,6 +248,7 @@ function image_field_delete_revision($en * Implements hook_field_is_empty(). */ function image_field_is_empty($item, $field) { + module_load_hook('file', 'field_is_empty'); return file_field_is_empty($item, $field); } @@ -272,6 +280,7 @@ function image_field_widget_settings_for $settings = $widget['settings']; // Use the file widget settings form. + module_load_hook('file', 'field_widget_settings_form'); $form = file_field_widget_settings_form($field, $instance); $form['preview_image_style'] = array( @@ -295,6 +304,7 @@ function image_field_widget_form(&$form, // Add display_field setting to field because file_field_widget_form() assumes it is set. $field['settings']['display_field'] = 0; + module_load_hook('file', 'field_widget_form'); $elements = file_field_widget_form($form, $form_state, $field, $instance, $langcode, $items, $delta, $element); $settings = $instance['settings']; @@ -313,6 +323,12 @@ function image_field_widget_form(&$form, // Add all extra functionality provided by the image widget. $elements[$delta]['#process'][] = 'image_field_widget_process'; } + // image_field_widget_process() is defined in this file, so ensure that it is + // loaded when the form is processed. + $form_state['build_info']['files']['image'] = array( + 'module' => 'image', + 'name' => 'image.field', + ); if ($field['cardinality'] == 1) { // If there's only one field, return it as delta 0. Index: modules/image/image.module =================================================================== RCS file: /cvs/drupal/drupal/modules/image/image.module,v retrieving revision 1.54 diff -u -p -r1.54 image.module --- modules/image/image.module 18 Nov 2010 05:36:27 -0000 1.54 +++ modules/image/image.module 22 Nov 2010 05:59:51 -0000 @@ -31,9 +31,6 @@ define('IMAGE_STORAGE_EDITABLE', IMAGE_S */ define('IMAGE_STORAGE_MODULE', IMAGE_STORAGE_OVERRIDE | IMAGE_STORAGE_DEFAULT); -// Load all Field module hooks for Image. -require_once DRUPAL_ROOT . '/modules/image/image.field.inc'; - /** * Implement of hook_help(). */ @@ -316,6 +313,7 @@ function image_file_download($uri) { if (count($files)) { $file = reset($files); if ($file->status) { + module_load_hook('file', 'file_download'); return file_file_download($uri, 'image'); } }