diff --git a/core/modules/menu/menu.admin.inc b/core/modules/menu/menu.admin.inc index 5a59dab..7bcc620 100644 --- a/core/modules/menu/menu.admin.inc +++ b/core/modules/menu/menu.admin.inc @@ -477,24 +477,27 @@ function menu_edit_menu($form, &$form_state, $type, $menu = array()) { '#default_value' => $menu['description'], ); - $node_types = node_type_get_names(); - $default_value = array(); - if ($type == 'edit') { - // When editing a menu we need to collect all the node types that have this - // menu enabled. - foreach ($node_types as $node_type => $name) { - $menu_options = variable_get('menu_options_' . $node_type, array('main-menu')); - if (in_array($menu['menu_name'], $menu_options)) { - $default_value[] = $node_type; + if module_exists('node') { + $node_types = node_type_get_names(); + $default_value = array(); + // Ignore the add form case because there are no default values available. + if ($type == 'edit') { + // When editing a menu we need to collect all the node types that have + // this menu enabled. + foreach ($node_types as $node_type => $name) { + $menu_options = variable_get('menu_options_' . $node_type, array('main-menu')); + if (in_array($menu['menu_name'], $menu_options)) { + $default_value[] = $node_type; + } } } + $form['menu_node_types'] = array( + '#type' => 'checkboxes', + '#title' => t('Available for these content types'), + '#default_value' => $default_value, + '#options' => $node_types, + ); } - $form['menu_node_types'] = array( - '#type' => 'checkboxes', - '#title' => t('Make this menu available to the following content types:'), - '#default_value' => $default_value, - '#options' => $node_types, - ); $form['actions'] = array('#type' => 'actions'); $form['actions']['submit'] = array( @@ -626,7 +629,7 @@ function menu_edit_menu_submit($form, &$form_state) { } } - // Check if we need to add or remove this menu from each available node type. + // Add or remove this menu from each available node type. $node_types = $menu['menu_node_types']; foreach ($node_types as $node_type => $value) { $menu_options = variable_get('menu_options_' . $node_type, array('main-menu')); diff --git a/core/modules/node/node.module b/core/modules/node/node.module index f3fb366..a59a5c7 100644 --- a/core/modules/node/node.module +++ b/core/modules/node/node.module @@ -388,17 +388,14 @@ function _node_extract_type($node) { } /** - * Returns a list of a site's known node types. + * Returns a list of all the available node types. * - * Any node type added by hook_node_info() or node_type_save() that has not been - * explicitly removed by node_type_delete() or direct database interaction will - * appear in this list. The list can include types that are queued for addition - * or deletion. + * This list can include types that are queued for addition or deletion. + * See _node_types_build() for details. * * @return * An array of node types, as objects, keyed by the type. * - * @see _node_types_build() * @see node_type_get_type() */ function node_type_get_types() { @@ -476,8 +473,10 @@ function node_type_get_name($node) { /** * Updates the database cache of node types. * - * All new node types defined by hook_node_info() are saved to the database via - * a call to node_type_save(). + * All new module-defined node types are saved to the database via a call to + * node_type_save(), and obsolete ones are deleted via a call to + * node_type_delete(). See _node_types_build() for an explanation of the new + * and obsolete types. */ function node_types_rebuild() { _node_types_build(TRUE); @@ -500,9 +499,6 @@ function node_type_load($name) { /** * Saves a node type to the database. * - * Modules should not create node types with this function, but should use - * hook_node_info() instead. - * * @param $info * The node type to save, as an object. * @@ -513,19 +509,6 @@ function node_type_save($info) { $existing_type = !empty($info->old_type) ? $info->old_type : $info->type; $is_existing = (bool) db_query_range('SELECT 1 FROM {node_type} WHERE type = :type', 0, 1, array(':type' => $existing_type))->fetchField(); $type = node_type_set_defaults($info); - $t_arguments = array('%type' => $type->name); - if (!$type->custom && $type->base == 'node_content') { - // If a module provides a node type with a 'base' key of 'node_content', the - // node type will not be automatically disabled when the module is disabled. - drupal_set_message(t('The content type %type will not be disabled.', $t_arguments), 'warning'); - } - elseif (!$type->custom && $type->module == '') { - // The content types list is built by content_types.inc using node_hook(). - // However, when the module is disabled, the [base]_form is hidden if it was - // implemented by the same module. Under this condition, one can add content - // of the disabled module, but cannot manage the content type. - drupal_set_message(t("The content type %type must either have 'base' key set to 'node_content' or be defined via hook_node_info().", $t_arguments), 'error'); - } $fields = array( 'type' => (string) $type->type, @@ -686,38 +669,30 @@ function node_type_update_nodes($old_type, $type) { ->execute(); } - /** - * Gets a list of all node types that were not deleted by node_type_delete(). - * - * This function provides the {node_type} table state as it would appear after - * a rebuild. When the $rebuild parameter is set to TRUE, the table is also - * actually rebuilt. The returned node type's 'disabled' property indicates - * whether the module that defines it has been disabled. However, any node - * type with a value of 'node_content' for the base will never be disabled. - * - * @param $rebuild - * TRUE to rebuild {node_type} table of the database. - * @return - * An object with two properties: - * - names: An associative array of the names corresponding to active or - * disabled node types, keyed by node type. This property does not - * necessarily reflect the state of the {node_type} table unless - * $rebuild is equal to TRUE. The property is a thin version of the 'types' - * property. See below for exactly what membership in this array means. - * - types: An associative array of node type objects, keyed by node type. - * Like the 'names' array, this data structure does not necessarily reflect - * the state of the {node_type} table. The node type objects contain - * fresher data. - * - disabled: If {node_type}.base is equal to 'node_content', then this - * property is always FALSE. Otherwise, the $type->disabled flag - * takes a value of FALSE for any type in the {node_type} table whose - * module is enabled, or it takes a value of TRUE if its module is - * disabled (or possibly uninstalled). - * - is_new: The $type->is_new flag does not appear in the {node_type} - * table. The property gets set to TRUE for node types implemented by - * hook_node_info() that do not yet appear in the {node_type} table. - * The property is left unset otherwise. - */ +/** + * Builds and returns the list of available node types. + * + * The list of types is built by invoking hook_node_info() on all modules and + * comparing this information with the node types in the {node_type} table. + * These two information sources are not synchronized during module installation + * until node_types_rebuild() is called. + * + * @param $rebuild + * TRUE to rebuild node types. Equivalent to calling node_types_rebuild(). + * + * @return + * Associative array with two components: + * - names: Associative array of the names of node types, keyed by the type. + * - types: Associative array of node type objects, keyed by the type. + * Both of these arrays will include new types that have been defined by + * hook_node_info() implementations but not yet saved in the {node_type} + * table. These are indicated in the type object by $type->is_new being set + * to the value 1. These arrays will also include obsolete types: types that + * were previously defined by modules that have now been disabled, or for + * whatever reason are no longer being defined in hook_node_info() + * implementations, but are still in the database. These are indicated in the + * type object by $type->disabled being set to TRUE. + */ function _node_types_build($rebuild = FALSE) { $cid = 'node_types:' . $GLOBALS['language_interface']->langcode; @@ -732,97 +707,65 @@ function _node_types_build($rebuild = FALSE) { } } - // Gather node types from the hook_node_info() implementations of active - // modules. - $active_types = (object) array('types' => array(), 'names' => array()); + $_node_types = (object) array('types' => array(), 'names' => array()); foreach (module_implements('node_info') as $module) { - foreach (module_invoke($module, 'node_info') as $type => $node_info) { - $node_info['type'] = $type; - $active_types->types[$type] = node_type_set_defaults($node_info); - $active_types->types[$type]->module = $module; - - // Overwrite the defaults of 'is_new' and 'disabled' for the new active - // type. - $active_types->types[$type]->is_new = TRUE; - $active_types->types[$type]->disabled = FALSE; - // When rebuilding, mark this type as new to ensure it overwrites any - // existing values in the database. - if ($rebuild) { - $active_types->types[$type]->new_state = TRUE; - } + $info_array = module_invoke($module, 'node_info'); + foreach ($info_array as $type => $info) { + $info['type'] = $type; + $_node_types->types[$type] = node_type_set_defaults($info); + $_node_types->types[$type]->module = $module; + $_node_types->names[$type] = $info['name']; } } - - // Gather node types from the {node_type} table. - $db_types = db_select('node_type', 'nt') + $query = db_select('node_type', 'nt') ->addTag('translatable') ->addTag('node_type_access') ->fields('nt') - ->orderBy('nt.type', 'ASC') - ->execute(); - // Synchronize the {node_type} table against the enabled modules list. Any - // node type with a base other than 'node_content' inherits its disabled state - // from that of its module. - $enabled_modules = module_list(); - foreach ($db_types as $db_type) { - // Remove any type information from hook_node_info() from the database - // to preserve any modifications. - $is_hook_implemented = isset($active_types->types[$db_type->type]); - $active_types->types[$db_type->type] = $db_type; - $type = $active_types->types[$db_type->type]; - if ($is_hook_implemented) { - $new_state = $type->disabled; - $type->disabled = FALSE; + ->orderBy('nt.type', 'ASC'); + if (!$rebuild) { + $query->condition('disabled', 0); + } + foreach ($query->execute() as $type_object) { + $type_db = $type_object->type; + // Original disabled value. + $disabled = $type_object->disabled; + // Check for node types from disabled modules and mark their types for removal. + // Types defined by the node module in the database (rather than by a separate + // module using hook_node_info) have a base value of 'node_content'. The isset() + // check prevents errors on old (pre-Drupal 7) databases. + if (isset($type_object->base) && $type_object->base != 'node_content' && empty($_node_types->types[$type_db])) { + $type_object->disabled = TRUE; } - else { - // A hook_node_info() was unavailable for the current occurence from the - // database, so enable or disable the occurence depending on its module's - // state. - if ($type->module == '' || (isset($type->base) && $type->base == 'node_content')) { - // Special case: always enabled. - $new_state = FALSE; - $type->disabled = FALSE; - } - elseif (isset($enabled_modules[$db_type->module])) { - // The module is enabled. - $new_state = $type->disabled; - $type->disabled = FALSE; - } - else { - // The module is disabled, uninstalled, or missing entirely. - $new_state = !$type->disabled; - $type->disabled = TRUE; - } + if (isset($_node_types->types[$type_db])) { + $type_object->disabled = FALSE; } + if (!isset($_node_types->types[$type_db]) || $type_object->modified) { + $_node_types->types[$type_db] = $type_object; + $_node_types->names[$type_db] = $type_object->name; - // Do not litter the return data structure with internal data. - if ($rebuild) { - $active_types->types[$db_type->type]->new_state = $new_state; + if ($type_db != $type_object->orig_type) { + unset($_node_types->types[$type_object->orig_type]); + unset($_node_types->names[$type_object->orig_type]); + } } - - // Copy data to the names array. - $active_types->names[$db_type->type] = $active_types->types[$db_type->type]->name; + $_node_types->types[$type_db]->disabled = $type_object->disabled; + $_node_types->types[$type_db]->disabled_changed = $disabled != $type_object->disabled; } - // Save new node types if a rebuild was requested. Needs a fresh loop, since - // the loop above doesn't touch new hook_node_info() implementors. if ($rebuild) { - foreach ($active_types->types as $type) { - if (isset($type->is_new) || isset($type->new_state)) { - node_type_save($type); - // Explicitly remove the 'new_state' flag; it is for internal API use - // only. - unset($type->new_state); + foreach ($_node_types->types as $type => $type_object) { + if (!empty($type_object->is_new) || !empty($type_object->disabled_changed)) { + node_type_save($type_object); } } } - asort($active_types->names); + asort($_node_types->names); - cache()->set($cid, $active_types); + cache()->set($cid, $_node_types); - return $active_types; + return $_node_types; } /** @@ -1313,14 +1256,14 @@ function node_delete_multiple($nids) { // Remove this node from the search index if needed. // This code is implemented in node module rather than in search module, - // because node module is implementing search module's API, not the - // other way around. + // because node module is implementing search module's API, not the other + // way around. if (module_exists('search')) { search_reindex($nid, 'node'); } } - // Delete after calling hooks so they can query node tables as needed. + // Delete after calling hooks so that they can query node tables as needed. db_delete('node') ->condition('nid', $nids, 'IN') ->execute(); @@ -2143,10 +2086,10 @@ function node_menu() { 'type' => MENU_CALLBACK, ); // @todo Remove this loop when we have a 'description callback' property. + // Resets the internal static cache of _node_types_build() and forces a + // rebuild of the node type information. + node_type_cache_reset(); foreach (node_type_get_types() as $type) { - if ($type->disabled) { - continue; - } $type_url_str = str_replace('_', '-', $type->type); $items['node/add/' . $type_url_str] = array( 'title' => $type->name, @@ -3776,8 +3719,7 @@ function _node_access_rebuild_batch_finished($success, $results, $operations) { * Implements hook_form(). */ function node_content_form($node, $form_state) { - // It is impossible to define a content type with a base that is not - // 'node_content' without also implementing hook_form(). + // It is impossible to define a content type without implementing hook_form() // @todo: remove this requirement. $form = array(); $type = node_type_get_type($node); diff --git a/core/modules/node/node.test b/core/modules/node/node.test index 871d9b7..1dbcaf3 100644 --- a/core/modules/node/node.test +++ b/core/modules/node/node.test @@ -521,7 +521,7 @@ class NodeCreationTestCase extends NodeWebTestCase { } /** - * Create page node and verify that transactions roll back failed creations. + * Create a page node and verify that a transaction rolls back the failed creation */ function testFailedPageCreation() { // Create a node. @@ -1447,32 +1447,6 @@ class NodeTypeTestCase extends NodeWebTestCase { $this->assertTrue(isset($types[$type]->disabled) && empty($types[$type]->disabled), t('%type type is enabled.', array('%type' => $type))); } } - - /** - * Tests the handling of node types that are undefined. - */ - public function testNodeTypeDisabled() { - $test_type = new stdClass(); - $test_type->base = 'not_node_content'; - $test_type->name = t('Test Type'); - $test_type->type = 'test'; - - node_type_save($test_type); - $updated = db_update('node_type') - ->fields(array('disabled' => TRUE)) - ->condition('type', $test_type->type) - ->execute(); - $this->assertTrue(($updated == TRUE), 'Successfully disabled custom content type.'); - $info = node_type_get_type($test_type->type); - $this->assertTrue($info !== FALSE, 'Return data from node_type_get_type() is valid for a disabled content type with a base that is not node_content().'); - - $target_type = 'book'; - module_enable(array($target_type), FALSE); - module_disable(array($target_type)); - $info = node_type_get_type($target_type); - $this->assertTrue($info !== FALSE, 'Return data from node_type_get_type() is valid for a disabled content type with base of node_content().'); - - } } /**