From 25ed45aeceb9152cb12936472b7867c5df3f8486 Mon Sep 17 00:00:00 2001 From: Tim Popham Date: Tue, 13 Sep 2011 10:49:40 -0700 Subject: [PATCH] Major rewrite of _node_types_build(), some documentation cleaning, and some removed hacks. --- modules/node/node.module | 211 ++++++++++++++++++++++++++++++---------------- 1 files changed, 139 insertions(+), 72 deletions(-) diff --git a/modules/node/node.module b/modules/node/node.module index 6abfcb2..f490806 100644 --- a/modules/node/node.module +++ b/modules/node/node.module @@ -371,9 +371,12 @@ function _node_extract_type($node) { } /** - * Returns a list of all the available node types. - * - * This list can include types that are queued for addition or deletion. + * Returns a list of a site's known 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. * See _node_types_build() for details. * * @return @@ -456,10 +459,8 @@ function node_type_get_name($node) { /** * Updates the database cache of node types. * - * 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. + * All new node types defined by hook_node_info() are saved to the database via + * a call to node_type_save(). */ function node_types_rebuild() { _node_types_build(TRUE); @@ -480,7 +481,8 @@ function node_type_load($name) { } /** - * Saves a node type to the database. + * Saves a node type to the database. Modules should not implement node types + * with this function, but should use hook_node_info() instead. * * @param $info * The node type to save, as an object. @@ -492,6 +494,18 @@ 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); + $map = array('%type' => $type->name); + if (!$type->custom && $type->base == 'node_content') { + // A modular node type with base==node_content does not behave like something + // owned by its module. + drupal_set_message(t('The content type %type will not be disabled with its parent module.', $map), 'warning'); + } elseif (!$type->custom && $type->module == '') { + // content_types.inc uses node_hook() to build the `Content types' list, + // but when the module is disabled, the [base]_form vanishes 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 modular content type %type must either use base==node_content or be implemented by hook_node_info().', $map), 'error'); + } $fields = array( 'type' => (string) $type->type, @@ -645,27 +659,37 @@ function node_type_update_nodes($old_type, $type) { } /** - * Builds and returns the list of available node types. + * Get the list of all node types that have not been deleted by a call to + * node_type_delete(). * - * 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. + * This function provides the `node_type' table state as it would appear after a + * rebuild, actually rebuilding the table for $rebuild==TRUE. The returned node + * type's `disabled' property indicates if the node type's implementing module + * has been disabled, however, any node type with a `base' value of + * `node_content' always remains non-disabled (making Node Content a + * poor name choice for a module). * * @param $rebuild - * TRUE to rebuild node types. Equivalent to calling node_types_rebuild(). + * TRUE to rebuild `node_types' table of the database. * @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. + * An object with two properties: + * - names: 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==TRUE. The property is + * a thin version of the `types' property. See below for exactly what + * membership in this array means. + * - types: 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 `base==node_content', then this property does not vary from + * 0. Otherwise, the $type->disabled flag takes a value of 0 for any type + * in the `node_type' table whose module is enabled, or it takes a value of + * 1 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 1 for node types implemented by + * hook_node_info() that did not appear in the `node_type' table at + * _node_types_build() entry. The property is left unset otherwise. */ function _node_types_build($rebuild = FALSE) { $cid = 'node_types:' . $GLOBALS['language']->language; @@ -681,65 +705,108 @@ function _node_types_build($rebuild = FALSE) { } } - $_node_types = (object) array('types' => array(), 'names' => array()); - + // Gather node types from the hook_node_info() implementations of active + // modules. + $active_types = (object) array('types' => array(), 'names' => array()); foreach (module_implements('node_info') as $module) { - $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']; + 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; + // active_type not in node_type table => active_type is new, so clobber + // this default value if the type is found in the database. + $active_types->types[$type]->is_new = 1; + // active_type is new => active_type is not disabled, so be consistent + // with other defaults for now + $active_types->types[$type]->disabled = 0; + // attach flag to indicate whether the type needs to be (re)written to the + // database + if ($rebuild) { + $active_types->types[$type]->new_state = 1; + } + + // Copy subset of the types property's data to the name property + $active_types->names[$type] = $active_types->types[$type]->name; } } - $query = db_select('node_type', 'nt') + + // Gather node types from the node_type table (and I have no clue what the + // translatable tag does) + $db_types = db_select('node_type', 'nt') ->addTag('translatable') ->addTag('node_type_access') ->fields('nt') - ->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; - } - 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; - - if ($type_db != $type_object->orig_type) { - unset($_node_types->types[$type_object->orig_type]); - unset($_node_types->names[$type_object->orig_type]); + ->orderBy('nt.type', 'ASC') + ->execute(); + // Could condition on base!=node_content and/or custom=0, but then a second + // query and loop would be necessary to add these results verbatim to the + // data structure + + // Reconcile node_type table against the enabled modules list. Any node type + // with base!=node_content inherits its disabled state from that of its + // module. + $enabled_modules = module_list(); + foreach ($db_types as $db_type) { + // Clobber any type information from hook_node_info() with that 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 != 0); + $type->disabled = 0; + } else { + // A hook_node_info() was unavailable for the current iterant from the + // database, so enable or disable the iterant depending on its module's + // state + if ($type->module=='' || (isset($type->base) && $type->base=='node_content')) { + // Special case: always enabled + $new_state = 0; + $type->disabled = 0; + } elseif (isset($enabled_modules[$db_type->module])) { + // The module is enabled + $new_state = ($type->disabled != 0); + $type->disabled = 0; + } else { + // The module is disabled, uninstalled, or missing entirely + $new_state = ($type->disabled != 1); + $type->disabled = 1; } } - $_node_types->types[$type_db]->disabled = $type_object->disabled; - $_node_types->types[$type_db]->disabled_changed = $disabled != $type_object->disabled; + + // Don't litter the return data structure with internal data + if ($rebuild) { + $active_types->types[$db_type->type]->new_state = $new_state; + } + + // Copy data to names array. + // This was inside the hook_node_info()-unavailable-case above, since the + // name data from the hook_node_info() call should be fine to return, but + // changes in hook_node_info() may not have propagated into the database yet + // (and a rebuild won't fix a changed name, so the spec comes into play: + // return what the node type table would look like after a rebuild). + $active_types->names[$db_type->type] = + $active_types->types[$db_type->type]->name; } + // 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 ($_node_types->types as $type => $type_object) { - if (!empty($type_object->is_new) || !empty($type_object->disabled_changed)) { - node_type_save($type_object); + 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 or someone will start depending + // on unspec'ed behavior + unset($type->new_state); } } } - asort($_node_types->names); + asort($active_types->names); - cache_set($cid, $_node_types); + cache_set($cid, $active_types); - return $_node_types; + return $active_types; } /** @@ -1953,10 +2020,9 @@ function node_menu() { 'type' => MENU_CALLBACK, ); // @todo Remove this loop when we have a 'description callback' property. - // Reset internal static cache of _node_types_build(), forces to rebuild the - // node type information. - node_type_cache_reset(); - foreach (node_type_get_types() as $type) { + $enabled_types = array_filter(node_type_get_types(), + create_function('$t','return !$t->disabled;')); + foreach ($enabled_types as $type) { $type_url_str = str_replace('_', '-', $type->type); $items['node/add/' . $type_url_str] = array( 'title' => $type->name, @@ -3560,7 +3626,8 @@ 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 without implementing hook_form() + // It is impossible to define a content type with base!=node_content without + // also implementing hook_form() // @todo: remove this requirement. $form = array(); $type = node_type_get_type($node); -- 1.7.6.1