Commit c9fc300b authored by Dries's avatar Dries

- Patch #29785 by Chx: multiple node types were broken so we refactored

  part of the node system!  If you have a module that implements node
  types, you'll have to udpate its CVS HEAD version.

  We replaced _node_name() and _node_types() by _node().  The new _node()
  hook let's you define one or more node types, including their names.
  The implementation of the _node() hook needs to:

   return array($type1 => array('name' => $name1, 'base' => $base1),
                $type2 => array('name' => $name2, 'base' => $base2));

  where $type is the node type, $name is the human readable name of the type
  and $base is used instead of <hook> for <hook>_load, <hook>_view, etc.

  For example, the story module's node hook looks like this:

    function story_node() {
      return array('story' => array('name' => t('story'), 'base' => 'story'));
    }

  The page module's node hook module like:

    function page_node() {
      return array('page' => array('name' => t('page'), 'base' => 'page'));
    }

  However, more complex node modules like the project module and the
  flexinode module can use the 'base' parameter to specify a different base.

  The project module implements two node types, proejcts and issues, so it
  can do:

    function project_node() {
      return array(
       array('project_project' => array('name' => t('project'), 'base' => 'project'),
       array('project_issue' => array('name' => t('issue'), 'base' => 'project_issue'));
    }

  In the flexinode module's case there can only one base ...

  This hook will simplify the CCK, and will make it easy (or easier) to merge
  the story and page module.

  In addition, node_list() became node_get_types().  In addition, we created
  the following functions: node_get_name($type) and node_get_base($type).
parent 494e5ab9
......@@ -232,7 +232,7 @@ function theme_get_settings($key = NULL) {
);
if (module_exist('node')) {
foreach (node_list() as $type) {
foreach (node_get_types() as $type) {
$defaults['toggle_node_info_' . $type] = 1;
}
}
......
......@@ -7,10 +7,10 @@
*/
/**
* Implementation of hook_node_name().
* Implementation of hook_node().
*/
function blog_node_name($node) {
return t('personal blog entry');
function blog_node() {
return array('blog' => array('name' => t('personal blog entry'), 'base' => 'blog'));
}
/**
......
......@@ -7,10 +7,10 @@
*/
/**
* Implementation of hook_node_name().
* Implementation of hook_node().
*/
function blog_node_name($node) {
return t('personal blog entry');
function blog_node() {
return array('blog' => array('name' => t('personal blog entry'), 'base' => 'blog'));
}
/**
......
......@@ -544,12 +544,8 @@ function blogapi_blogger_title(&$contents) {
function blogapi_settings() {
$output = form_select(t('XML-RPC Engine'), 'blogapi_engine', variable_get('blogapi_engine', 0), array(0 => 'Blogger', 1 => 'MetaWeblog', 2 => 'Movabletype'), t('RSD or Really-Simple-Discovery is a mechanism which allows external blogger tools to discover the APIs they can use to interact with Drupal. Here you can set the preferred method for blogger tools to interact with your site. The common XML-RPC engines are Blogger, MetaWeblog and Movabletype. If you are not sure which is the correct setting, choose Blogger.'));
foreach (node_list() as $type => $module) {
$node_types[$type] = node_invoke($type, 'node_name');
if (in_array($type, array('blog'))) {
$defaults[] = $type;
}
}
$node_types = node_get_types();
$defaults = isset($node_types['blog']) ? array('blog') : array();
$output .= form_checkboxes(t('Blog types'), "blogapi_node_types", variable_get('blogapi_node_types', $defaults), $node_types, t('Select the content types for which you wish to enable posting via blogapi. Each type will appear as a different "blog" in the client application (if supported).'), 0, 1);
return $output;
}
......@@ -716,7 +712,7 @@ function _blogapi_blogid($id) {
function _blogapi_get_node_types() {
$available_types = variable_get('blogapi_node_types', array('blog'));
$types = array();
foreach (node_list() as $type => $module) {
foreach (node_get_types() as $type => $name) {
if (node_access('create', $type) && in_array($type, $available_types)) {
$types[] = $type;
}
......
......@@ -544,12 +544,8 @@ function blogapi_blogger_title(&$contents) {
function blogapi_settings() {
$output = form_select(t('XML-RPC Engine'), 'blogapi_engine', variable_get('blogapi_engine', 0), array(0 => 'Blogger', 1 => 'MetaWeblog', 2 => 'Movabletype'), t('RSD or Really-Simple-Discovery is a mechanism which allows external blogger tools to discover the APIs they can use to interact with Drupal. Here you can set the preferred method for blogger tools to interact with your site. The common XML-RPC engines are Blogger, MetaWeblog and Movabletype. If you are not sure which is the correct setting, choose Blogger.'));
foreach (node_list() as $type => $module) {
$node_types[$type] = node_invoke($type, 'node_name');
if (in_array($type, array('blog'))) {
$defaults[] = $type;
}
}
$node_types = node_get_types();
$defaults = isset($node_types['blog']) ? array('blog') : array();
$output .= form_checkboxes(t('Blog types'), "blogapi_node_types", variable_get('blogapi_node_types', $defaults), $node_types, t('Select the content types for which you wish to enable posting via blogapi. Each type will appear as a different "blog" in the client application (if supported).'), 0, 1);
return $output;
}
......@@ -716,7 +712,7 @@ function _blogapi_blogid($id) {
function _blogapi_get_node_types() {
$available_types = variable_get('blogapi_node_types', array('blog'));
$types = array();
foreach (node_list() as $type => $module) {
foreach (node_get_types() as $type => $name) {
if (node_access('create', $type) && in_array($type, $available_types)) {
$types[] = $type;
}
......
......@@ -7,10 +7,10 @@
*/
/**
* Implementation of hook_node_name().
* Implementation of hook_node().
*/
function book_node_name($node) {
return t('book page');
function book_node() {
return array('book' => array('name' => t('book page'), 'base' => 'book'));
}
/**
......
......@@ -7,10 +7,10 @@
*/
/**
* Implementation of hook_node_name().
* Implementation of hook_node().
*/
function book_node_name($node) {
return t('book page');
function book_node() {
return array('book' => array('name' => t('book page'), 'base' => 'book'));
}
/**
......
......@@ -27,10 +27,10 @@ function forum_help($section) {
}
/**
* Implementation of hook_node_name().
* Implementation of hook_node().
*/
function forum_node_name($node) {
return t('forum topic');
function forum_node() {
return array('forum' => array('name' => t('forum topic'), 'base' => 'forum'));
}
/**
......
......@@ -27,10 +27,10 @@ function forum_help($section) {
}
/**
* Implementation of hook_node_name().
* Implementation of hook_node().
*/
function forum_node_name($node) {
return t('forum topic');
function forum_node() {
return array('forum' => array('name' => t('forum topic'), 'base' => 'forum'));
}
/**
......
......@@ -32,8 +32,8 @@ function node_help($section) {
<dt>Published</dt><dd>When using Drupal's moderation system a node remains unpublished -- unavailable to non-moderators -- until it is marked Published.</dd></dl>
<p>Now that you know what is in a node, here are some of the types of nodes available.</p>", array("%teaser" => url("admin/node/configure/settings")));
foreach (node_list() as $type => $module) {
$output .= '<h3>'. t('Node type: %module', array('%module' => node_invoke($type, 'node_name'))). '</h3>';
foreach (node_get_types() as $type => $name) {
$output .= '<h3>'. t('Node type: %name', array('%name' => $name)). '</h3>';
$output .= implode("\n", module_invoke_all('help', 'node/add#'. $type));
}
......@@ -202,55 +202,73 @@ function node_teaser($body, $format = NULL) {
return truncate_utf8($body, $size);
}
function _node_names($op = '', $node = NULL) {
static $node_names, $node_list;
if (!isset($node_names)) {
$node_names = module_invoke_all('node');
foreach ($node_names as $type => $value) {
$node_list[$type] = $value['name'];
}
}
if ($node) {
if (is_array($node)) {
$type = $node['type'];
}
elseif (is_object($node)) {
$type = $node->type;
}
elseif (is_string($node)) {
$type = $node;
}
if (!isset($node_names[$type])) {
return FALSE;
}
}
switch ($op) {
case 'base':
return $node_names[$type]['base'];
case 'list':
return $node_list;
case 'name':
return $node_list[$type];
}
}
/**
* Determine the module that defines the node type of the given node.
* Determine the basename for hook_load etc.
*
* @param &$node
* @param $node
* Either a node object, a node array, or a string containing the node type.
* @return
* A string containing the name of the defining module.
* The basename for hook_load, hook_nodeapi etc.
*/
function node_get_module_name($node) {
if (is_array($node)) {
$type = $node['type'];
}
else if (is_object($node)) {
$type = $node->type;
}
else if (is_string($node)) {
$type = $node;
}
$modules = node_list();
function node_get_base($node) {
return _node_names('base', $node);
}
return $modules[$type];
/**
* Determine the human readable name for a given type.
*
* @param $node
* Either a node object, a node array, or a string containing the node type.
* @return
* The human readable name of the node type.
*/
function node_get_name($node) {
return _node_names('name', $node);
}
/**
* Get a list of all the defined node types.
* Return the list of available node types.
*
* @param $node
* Either a node object, a node array, or a string containing the node type.
* @return
* An associate array of consisting of (node type, module name) pairs for all node types.
* An array consisting (type => name) pairs.
*/
function node_list() {
static $types = array();
if (empty($types)) {
foreach (module_list() as $module) {
if (module_hook($module, 'node_name')) {
$module_types = module_invoke($module, 'node_types');
if (is_array($module_types)) {
foreach ($module_types as $type) {
$types[$type] = $module;
}
}
else {
$types[$module] = $module;
}
}
}
}
return $types;
function node_get_types() {
return _node_names('list');
}
/**
......@@ -264,9 +282,7 @@ function node_list() {
* TRUE iff the $hook exists in the node type of $node.
*/
function node_hook(&$node, $hook) {
$function = node_get_module_name($node) ."_$hook";
return function_exists($function);
return module_hook(node_get_base($node), $hook);
}
/**
......@@ -282,9 +298,8 @@ function node_hook(&$node, $hook) {
* The returned value of the invoked hook.
*/
function node_invoke(&$node, $hook, $a2 = NULL, $a3 = NULL, $a4 = NULL) {
$function = node_get_module_name($node) ."_$hook";
if (function_exists($function)) {
if (node_hook($node, $hook)) {
$function = node_get_base($node) ."_$hook";
return ($function($node, $a2, $a3, $a4));
}
}
......@@ -303,16 +318,14 @@ function node_invoke(&$node, $hook, $a2 = NULL, $a3 = NULL, $a4 = NULL) {
*/
function node_invoke_nodeapi(&$node, $op, $a3 = NULL, $a4 = NULL) {
$return = array();
foreach (module_list() as $name) {
foreach (module_implements('nodeapi') as $name) {
$function = $name .'_nodeapi';
if (function_exists($function)) {
$result = $function($node, $op, $a3, $a4);
if (is_array($result)) {
$return = array_merge($return, $result);
}
else if (isset($result)) {
$return[] = $result;
}
$result = $function($node, $op, $a3, $a4);
if (is_array($result)) {
$return = array_merge($return, $result);
}
else if (isset($result)) {
$return[] = $result;
}
}
return $return;
......@@ -575,7 +588,7 @@ function node_search($op = 'search', $keys = null) {
$extra = node_invoke_nodeapi($node, 'search result');
$results[] = array('link' => url('node/'. $item),
'type' => node_invoke($node, 'node_name'),
'type' => node_get_name($node),
'title' => $node->title,
'user' => theme('username', $node),
'date' => $node->changed,
......@@ -700,7 +713,7 @@ function node_menu($may_cache) {
}
else if (arg(0) == 'admin' && arg(1) == 'settings' && arg(2) == 'content-types' && is_string(arg(3))) {
$items[] = array('path' => 'admin/settings/content-types/'. arg(3),
'title' => t("'%name' content type", array('%name' => node_invoke(arg(3), 'node_name'))),
'title' => t("'%name' content type", array('%name' => node_get_name(arg(3)))),
'type' => MENU_CALLBACK);
}
}
......@@ -784,10 +797,6 @@ function node_admin_nodes() {
/*
** Filters
*/
$node_types = drupal_map_assoc(array_keys(node_list()));
foreach ($node_types as $k => $v) {
$node_types[$k] = node_invoke($v, 'node_name');
}
// Regular filters
$filters = array(
'status' => array('title' => t('status'),
......@@ -796,7 +805,7 @@ function node_admin_nodes() {
'promote-1' => t('promoted'), 'promote-0' => t('not promoted'),
'sticky-1' => t('sticky'), 'sticky-0' => t('not sticky'))),
'type' => array('title' => t('type'), 'where' => "n.type = '%s'",
'options' => $node_types));
'options' => node_get_types()));
// Merge all vocabularies into one for retrieving $value below
if ($taxonomy = module_invoke('taxonomy', 'form_all')) {
$terms = array();
......@@ -912,7 +921,7 @@ function node_admin_nodes() {
while ($node = db_fetch_object($result)) {
$rows[] = array(form_checkbox(NULL, 'nodes]['. $node->nid, 1, 0),
l($node->title, 'node/'. $node->nid) .' '. theme('mark', node_mark($node->nid, $node->changed)),
node_invoke($node, 'node_name'),
node_get_name($node),
theme('username', $node),
($node->status ? t('published') : t('not published')),
l(t('edit'), 'node/'. $node->nid .'/edit', array(), $destination));
......@@ -947,8 +956,8 @@ function node_types_configure($type = NULL) {
$node = new stdClass();
$node->type = $type;
$group = form_textarea(t('Explanation or submission guidelines'), $type .'_help', variable_get($type .'_help', ''), 60, 5, t('This text will be displayed at the top of the %type submission form. It is useful for helping or instructing your users.', array('%type' => node_invoke($type, 'node_name'))));
$group .= form_select(t('Minimum number of words'), 'minimum_'. $type .'_size', variable_get('minimum_'. $type .'_size', 0), drupal_map_assoc(array(0, 10, 25, 50, 75, 100, 125, 150, 175, 200)), t('The minimum number of words a %type must be to be considered valid. This can be useful to rule out submissions that do not meet the site\'s standards, such as short test posts.', array('%type' => node_invoke($type, 'node_name'))));
$group = form_textarea(t('Explanation or submission guidelines'), $type .'_help', variable_get($type .'_help', ''), 60, 5, t('This text will be displayed at the top of the %type submission form. It is useful for helping or instructing your users.', array('%type' => node_get_name($type))));
$group .= form_select(t('Minimum number of words'), 'minimum_'. $type .'_size', variable_get('minimum_'. $type .'_size', 0), drupal_map_assoc(array(0, 10, 25, 50, 75, 100, 125, 150, 175, 200)), t('The minimum number of words a %type must be to be considered valid. This can be useful to rule out submissions that do not meet the site\'s standards, such as short test posts.', array('%type' => node_get_name($type))));
$output = form_group(t('Submission form'), $group);
$output .= form_group(t('Workflow'), implode('', node_invoke_nodeapi($node, 'settings')));
......@@ -958,8 +967,8 @@ function node_types_configure($type = NULL) {
$header = array(t('Type'), t('Operations'));
$rows = array();
foreach (node_list() as $type => $module) {
$rows[] = array(node_invoke($type, 'node_name'), l(t('configure'), 'admin/settings/content-types/'. $type));
foreach (node_get_types() as $type => $name) {
$rows[] = array($name, l(t('configure'), 'admin/settings/content-types/'. $type));
}
return theme('table', $header, $rows);
......@@ -1190,7 +1199,7 @@ function node_validate($node) {
// Make sure the body has the minimum number of words.
// todo use a better word counting algorithm that will work in other languages
if (isset($node->body) && count(explode(' ', $node->body)) < variable_get('minimum_'. $node->type .'_size', 0)) {
form_set_error('body', t('The body of your %type is too short. You need at least %words words.', array('%words' => variable_get('minimum_'. $node->type .'_size', 0), '%type' => node_invoke($node->type, 'node_name'))));
form_set_error('body', t('The body of your %type is too short. You need at least %words words.', array('%words' => variable_get('minimum_'. $node->type .'_size', 0), '%type' => node_get_name($node))));
}
// Auto-generate the teaser, but only if it hasn't been set (e.g. by a
......@@ -1279,7 +1288,7 @@ function node_form($edit) {
// Get the node-specific bits.
// We can't use node_invoke() because $param must be passed by reference.
$function = node_get_module_name($edit) .'_form';
$function = node_get_base($edit) .'_form';
$param = array();
if (function_exists($function)) {
$form .= $function($edit, $param);
......@@ -1391,7 +1400,7 @@ function node_add($type) {
$edit = $_POST['edit'];
// If a node type has been specified, validate its existence.
if (array_key_exists($type, node_list()) && node_access('create', $type)) {
if (array_key_exists($type, node_get_types()) && node_access('create', $type)) {
// Initialize settings:
$node = array('uid' => $user->uid, 'name' => $user->name, 'type' => $type);
......@@ -1403,15 +1412,15 @@ function node_add($type) {
}
}
$output = node_form($node);
drupal_set_title(t('Submit %name', array('%name' => node_invoke($node, 'node_name'))));
drupal_set_title(t('Submit %name', array('%name' => node_get_name($node))));
}
else {
// If no (valid) node type has been provided, display a node type overview.
foreach (node_list() as $type => $module) {
foreach (node_get_types() as $type => $name) {
if (node_access('create', $type)) {
$out = '<dt>'. l(node_invoke($type, 'node_name'), "node/add/$type", array('title' => t('Add a new %s.', array('%s' => node_invoke($type, 'node_name'))))) .'</dt>';
$out = '<dt>'. l($name, "node/add/$type", array('title' => t('Add a new %s.', array('%s' => $name)))) .'</dt>';
$out .= '<dd>'. implode("\n", module_invoke_all('help', 'node/add#'. $type)) .'</dd>';
$item[node_invoke($type, 'node_name')] = $out;
$item[$name] = $out;
}
}
......@@ -1485,8 +1494,7 @@ function node_preview($node) {
$output .= node_form($node);
$name = node_invoke($node, 'node_name');
drupal_set_breadcrumb(array(l(t('Home'), NULL), l(t('create content'), 'node/add'), l(t('Submit %name', array('%name' => $name)), 'node/add/'. $node->type)));
drupal_set_breadcrumb(array(l(t('Home'), NULL), l(t('create content'), 'node/add'), l(t('Submit %name', array('%name' => node_get_name($node))), 'node/add/'. $node->type)));
return $output;
}
......@@ -1543,7 +1551,7 @@ function node_submit(&$node) {
if (node_access('update', $node)) {
$node->nid = node_save($node);
watchdog('content', t('%type: updated %title.', array('%type' => theme('placeholder', t($node->type)), '%title' => theme('placeholder', $node->title))), WATCHDOG_NOTICE, l(t('view'), 'node/'. $node->nid));
$msg = t('The %post was updated.', array ('%post' => node_invoke($node, 'node_name')));
$msg = t('The %post was updated.', array ('%post' => node_get_name($node)));
}
}
else {
......@@ -1552,7 +1560,7 @@ function node_submit(&$node) {
if (node_access('create', $node)) {
$node->nid = node_save($node);
watchdog('content', t('%type: added %title.', array('%type' => theme('placeholder', t($node->type)), '%title' => theme('placeholder', $node->title))), WATCHDOG_NOTICE, l(t('view'), "node/$node->nid"));
$msg = t('Your %post was created.', array ('%post' => node_invoke($node, 'node_name')));
$msg = t('Your %post was created.', array ('%post' => node_get_name($node)));
}
}
......@@ -1865,7 +1873,7 @@ function node_access($op, $node = NULL, $uid = NULL) {
// Can't use node_invoke(), because the access hook takes the $op parameter
// before the $node parameter.
$access = module_invoke(node_get_module_name($node), 'access', $op, $node);
$access = module_invoke(node_get_base($node), 'access', $op, $node);
if (!is_null($access)) {
return $access;
}
......
......@@ -32,8 +32,8 @@ function node_help($section) {
<dt>Published</dt><dd>When using Drupal's moderation system a node remains unpublished -- unavailable to non-moderators -- until it is marked Published.</dd></dl>
<p>Now that you know what is in a node, here are some of the types of nodes available.</p>", array("%teaser" => url("admin/node/configure/settings")));
foreach (node_list() as $type => $module) {
$output .= '<h3>'. t('Node type: %module', array('%module' => node_invoke($type, 'node_name'))). '</h3>';
foreach (node_get_types() as $type => $name) {
$output .= '<h3>'. t('Node type: %name', array('%name' => $name)). '</h3>';
$output .= implode("\n", module_invoke_all('help', 'node/add#'. $type));
}
......@@ -202,55 +202,73 @@ function node_teaser($body, $format = NULL) {
return truncate_utf8($body, $size);
}
function _node_names($op = '', $node = NULL) {
static $node_names, $node_list;
if (!isset($node_names)) {
$node_names = module_invoke_all('node');
foreach ($node_names as $type => $value) {
$node_list[$type] = $value['name'];
}
}
if ($node) {
if (is_array($node)) {
$type = $node['type'];
}
elseif (is_object($node)) {
$type = $node->type;
}
elseif (is_string($node)) {
$type = $node;
}
if (!isset($node_names[$type])) {
return FALSE;
}
}
switch ($op) {
case 'base':
return $node_names[$type]['base'];
case 'list':
return $node_list;
case 'name':
return $node_list[$type];
}
}
/**
* Determine the module that defines the node type of the given node.
* Determine the basename for hook_load etc.
*
* @param &$node
* @param $node
* Either a node object, a node array, or a string containing the node type.
* @return
* A string containing the name of the defining module.
* The basename for hook_load, hook_nodeapi etc.
*/
function node_get_module_name($node) {
if (is_array($node)) {
$type = $node['type'];
}
else if (is_object($node)) {
$type = $node->type;
}
else if (is_string($node)) {
$type = $node;
}
$modules = node_list();
function node_get_base($node) {
return _node_names('base', $node);
}
return $modules[$type];
/**
* Determine the human readable name for a given type.
*
* @param $node
* Either a node object, a node array, or a string containing the node type.
* @return
* The human readable name of the node type.
*/
function node_get_name($node) {
return _node_names('name', $node);
}
/**
* Get a list of all the defined node types.
* Return the list of available node types.
*
* @param $node
* Either a node object, a node array, or a string containing the node type.
* @return
* An associate array of consisting of (node type, module name) pairs for all node types.
* An array consisting (type => name) pairs.
*/
function node_list() {
static $types = array();
if (empty($types)) {
foreach (module_list() as $module) {
if (module_hook($module, 'node_name')) {
$module_types = module_invoke($module, 'node_types');
if (is_array($module_types)) {
foreach ($module_types as $type) {
$types[$type] = $module;
}
}
else {
$types[$module] = $module;
}
}
}
}
return $types;
function node_get_types() {
return _node_names('list');
}
/**
......@@ -264,9 +282,7 @@ function node_list() {
* TRUE iff the $hook exists in the node type of $node.
*/
function node_hook(&$node, $hook) {
$function = node_get_module_name($node) ."_$hook";
return function_exists($function);
return module_hook(node_get_base($node), $hook);
}
/**
......@@ -282,9 +298,8 @@ function node_hook(&$node, $hook) {
* The returned value of the invoked hook.
*/
function node_invoke(&$node, $hook, $a2 = NULL, $a3 = NULL, $a4 = NULL) {
$function = node_get_module_name($node) ."_$hook";
if (function_exists($function)) {
if (node_hook($node, $hook)) {
$function = node_get_base($node) ."_$hook";
return ($function($node, $a2, $a3, $a4));
}
}
......@@ -303,16 +318,14 @@ function node_invoke(&$node, $hook, $a2 = NULL, $a3 = NULL, $a4 = NULL) {
*/
function node_invoke_nodeapi(&$node, $op, $a3 = NULL, $a4 = NULL) {
$return = array();
foreach (module_list() as $name) {
foreach (module_implements('nodeapi') as $name) {
$function = $name .'_nodeapi';
if (function_exists($function)) {
$result = $function($node, $op, $a3, $a4);
if (is_array($result)) {
$return = array_merge($return, $result);
}
else if (isset($result)) {
$return[] = $result;
}