diff -r'uNF^f' --exclude=CVS --exclude=docs --exclude=files --exclude sites ../C-HEAD/modules/blog/blog.module ./modules/blog/blog.module --- ../C-HEAD/modules/blog/blog.module 2006-07-31 13:14:28.000000000 -0700 +++ ./modules/blog/blog.module 2006-07-31 23:44:09.000000000 -0700 @@ -10,7 +10,13 @@ * Implementation of hook_node_info(). */ function blog_node_info() { - return array('blog' => array('name' => t('blog entry'), 'base' => 'blog')); + return array( + 'blog' => array( + 'name' => t('blog entry'), + 'module' => 'blog', + 'description' => t('A blog is a regularly updated journal or diary made up of individual posts shown in reversed chronological order. Each member of the site may create and maintain a blog.'), + ) + ); } /** @@ -72,8 +78,6 @@ function blog_help($section) { return $output; case 'admin/settings/modules#description': return t('Enables keeping an easily and regularly updated web page or a blog.'); - case 'node/add#blog': - return t("A blog is a regularly updated journal or diary made up of individual posts shown in reversed chronological order. Each member of the site may create and maintain a blog."); } } @@ -201,6 +205,7 @@ function blog_page_last() { function blog_form(&$node) { global $nid; $iid = $_GET['iid']; + $type = node_get_types('type', $node); if (empty($node->body)) { @@ -221,8 +226,8 @@ function blog_form(&$node) { } - $form['title'] = array('#type' => 'textfield', '#title' => t('Title'), '#required' => TRUE, '#default_value' => $node->title, '#weight' => -5); - $form['body_filter']['body'] = array('#type' => 'textarea', '#title' => t('Body'), '#default_value' => $node->body, '#rows' => 20, '#required' => TRUE); + $form['title'] = array('#type' => 'textfield', '#title' => check_plain($type->title_label), '#required' => TRUE, '#default_value' => $node->title, '#weight' => -5); + $form['body_filter']['body'] = array('#type' => 'textarea', '#title' => check_plain($type->body_label), '#default_value' => $node->body, '#rows' => 20, '#required' => TRUE); $form['body_filter']['filter'] = filter_form($node->format); return $form; } diff -r'uNF^f' --exclude=CVS --exclude=docs --exclude=files --exclude sites ../C-HEAD/modules/book/book.module ./modules/book/book.module --- ../C-HEAD/modules/book/book.module 2006-07-31 13:14:28.000000000 -0700 +++ ./modules/book/book.module 2006-08-01 00:15:32.000000000 -0700 @@ -10,7 +10,13 @@ * Implementation of hook_node_info(). */ function book_node_info() { - return array('book' => array('name' => t('book page'), 'base' => 'book')); + return array( + 'book' => array( + 'name' => t('book page'), + 'module' => 'book', + 'description' => t("A book is a collaborative writing effort: users can collaborate writing the pages of the book, positioning the pages in the right order, and reviewing or modifying pages previously written. So when you have some information to share or when you read a page of the book and you didn't like it, or if you think a certain page could have been written better, you can do something about it."), + ) + ); } /** @@ -84,10 +90,6 @@ function book_menu($may_cache) { if ($may_cache) { $items[] = array( - 'path' => 'node/add/book', - 'title' => t('book page'), - 'access' => user_access('create book pages')); - $items[] = array( 'path' => 'admin/content/book', 'title' => t('books'), 'description' => t('Manage site\'s books and orphaned book pages.'), @@ -224,6 +226,7 @@ function book_submit(&$node) { * Implementation of hook_form(). */ function book_form(&$node) { + $type = node_get_types('type', $node); if ($node->nid && !$node->parent && !user_access('create new books')) { $form['parent'] = array('#type' => 'value', '#value' => $node->parent); } @@ -238,13 +241,13 @@ function book_form(&$node) { } $form['title'] = array('#type' => 'textfield', - '#title' => t('Title'), + '#title' => check_plain($type->title_label), '#required' => TRUE, '#default_value' => $node->title, '#weight' => -5, ); $form['body_filter']['body'] = array('#type' => 'textarea', - '#title' => t('Body'), + '#title' => check_plain($type->body_label), '#default_value' => $node->body, '#rows' => 20, '#required' => TRUE, @@ -1013,8 +1016,6 @@ function book_help($section) { return t('
The book module offers a means to organize content, authored by many users, in an online manual, outline or FAQ.
'); case 'admin/content/book/orphan': return t('Pages in a book are like a tree. As pages are edited, reorganized and removed, child pages might be left with no link to the rest of the book. Such pages are referred to as "orphan pages". On this page, administrators can review their books for orphans and reattach those pages as desired.
'); - case 'node/add#book': - return t("A book is a collaborative writing effort: users can collaborate writing the pages of the book, positioning the pages in the right order, and reviewing or modifying pages previously written. So when you have some information to share or when you read a page of the book and you didn't like it, or if you think a certain page could have been written better, you can do something about it."); } if (arg(0) == 'node' && is_numeric(arg(1)) && arg(2) == 'outline') { diff -r'uNF^f' --exclude=CVS --exclude=docs --exclude=files --exclude sites ../C-HEAD/modules/comment/comment.module ./modules/comment/comment.module --- ../C-HEAD/modules/comment/comment.module 2006-07-31 13:14:28.000000000 -0700 +++ ./modules/comment/comment.module 2006-07-31 23:44:33.000000000 -0700 @@ -265,10 +265,16 @@ function comment_link($type, $node = NUL } function comment_form_alter($form_id, &$form) { - if (isset($form['type'])) { - if ($form['type']['#value'] .'_node_settings' == $form_id) { - $form['workflow']['comment_'. $form['type']['#value']] = array('#type' => 'radios', '#title' => t('Default comment setting'), '#default_value' => variable_get('comment_'. $form['type']['#value'], COMMENT_NODE_READ_WRITE), '#options' => array(t('Disabled'), t('Read only'), t('Read/Write')), '#description' => t('Users with the administer comments permission will be able to override this setting.')); - } + if ($form_id == 'node_type_form' && isset($form['identity']['type'])) { + $form['workflow']['comment'] = array( + '#type' => 'radios', + '#title' => t('Default comment setting'), + '#default_value' => variable_get('comment_'. $form['identity']['type']['#default_value'], COMMENT_NODE_READ_WRITE), + '#options' => array(t('Disabled'), t('Read only'), t('Read/Write')), + '#description' => t('Users with the administer comments permission will be able to override this setting.'), + ); + } + elseif (isset($form['type'])) { if ($form['type']['#value'] .'_node_form' == $form_id) { $node = $form['#node']; if (user_access('administer comments')) { diff -r'uNF^f' --exclude=CVS --exclude=docs --exclude=files --exclude sites ../C-HEAD/modules/forum/forum.module ./modules/forum/forum.module --- ../C-HEAD/modules/forum/forum.module 2006-07-31 13:14:28.000000000 -0700 +++ ./modules/forum/forum.module 2006-07-31 23:44:34.000000000 -0700 @@ -33,8 +33,6 @@ function forum_help($section) { return t('Containers help you organize your forums. The job of a container is to hold, or contain, other forums that are related. For example, a container named "Food" might hold two forums named "Fruit" and "Vegetables".
'); case 'admin/content/forum/add/forum': return t('A forum holds discussion topics that are related. For example, a forum named "Fruit" might contain topics titled "Apples" and "Bananas".
'); - case 'node/add#forum': - return t('Create a new topic for discussion in the forums.'); } } @@ -107,7 +105,14 @@ function forum_menu($may_cache) { * Implementation of hook_node_info(). */ function forum_node_info() { - return array('forum' => array('name' => t('forum topic'), 'base' => 'forum')); + return array( + 'forum' => array( + 'name' => t('forum topic'), + 'module' => 'forum', + 'description' => t('Create a new topic for discussion in the forums.'), + 'title_label' => t('Subject'), + ) + ); } /** @@ -379,7 +384,8 @@ function forum_update($node) { * Implementation of hook_form(). */ function forum_form(&$node) { - $form['title'] = array('#type' => 'textfield', '#title' => t('Subject'), '#default_value' => $node->title, '#required' => TRUE, '#weight' => -5); + $type = node_get_types('type', $node); + $form['title'] = array('#type' => 'textfield', '#title' => check_plain($type->title_label), '#default_value' => $node->title, '#required' => TRUE, '#weight' => -5); if ($node->nid) { $forum_terms = taxonomy_node_get_terms_by_vocabulary(_forum_get_vid(), $node->nid); @@ -388,7 +394,7 @@ function forum_form(&$node) { $form['shadow'] = array('#type' => 'checkbox', '#title' => t('Leave shadow copy'), '#default_value' => $shadow, '#description' => t('If you move this topic, you can leave a link in the old forum to the new forum.')); } - $form['body_filter']['body'] = array('#type' => 'textarea', '#title' => t('Body'), '#default_value' => $node->body, '#rows' => 20, '#required' => TRUE); + $form['body_filter']['body'] = array('#type' => 'textarea', '#title' => check_plain($type->body_label), '#default_value' => $node->body, '#rows' => 20, '#required' => TRUE); $form['body_filter']['format'] = filter_form($node->format); return $form; diff -r'uNF^f' --exclude=CVS --exclude=docs --exclude=files --exclude sites ../C-HEAD/modules/node/content_types.inc ./modules/node/content_types.inc --- ../C-HEAD/modules/node/content_types.inc 1969-12-31 16:00:00.000000000 -0800 +++ ./modules/node/content_types.inc 2006-08-01 00:23:23.000000000 -0700 @@ -0,0 +1,388 @@ + t('Operations'), 'colspan' => '2')); + $rows = array(); + + foreach ($names as $key => $name) { + $type = $types[$key]; + if (module_exist($type->module)) { + $name = check_plain($name); + $type_url_str = str_replace('_', '-', $type->type); + // Populate the operations field. + $operations = array(); + + // Set the edit column. + $operations[] = array('data' => l(t('edit'), 'admin/content/types/'. $type_url_str)); + + // Set the delete column. + if ($type->custom) { + $operations[] = array('data' => l(t('delete'), 'admin/content/types/'. $type_url_str .'/delete')); + } + else { + $operations[] = array('data' => ''); + } + + $row = array(array('data' => l($name, 'admin/content/types/'. $type_url_str), 'class' => $class), array('data' => check_plain($type->type), 'class' => $class), array('data' => check_plain($type->description), 'class' => $class)); + foreach ($operations as $operation) { + $operation['class'] = $class; + $row[] = $operation; + } + $rows[] = $row; + } + } + + if (empty($rows)) { + $rows[] = array(array('data' => t('No content types available.'), 'colspan' => '5', 'class' => 'message')); + } + + return theme('table', $header, $rows); +} + +/** + * Generates the node type editing form. + */ +function node_type_form($type = NULL) { + if (!isset($type->type)) { + $type = new stdClass(); + $type->type = $type->name = $type->module = $type->description = $type->help = ''; + $type->min_word_count = 0; + $type->has_title = TRUE; + $type->has_body = TRUE; + $type->title_label = t('Title'); + $type->body_label = t('Body'); + $type->custom = TRUE; + $type->modified = FALSE; + $type->locked = FALSE; + } + + $form['identity'] = array( + '#type' => 'fieldset', + '#title' => t('Identification'), + ); + $form['identity']['name'] = array( + '#title' => t('Name'), + '#type' => 'textfield', + '#default_value' => $type->name, + '#description' => t('The human-readable name of this content type. This text will be displayed as part of the list on the create content page. It is recommended that this name consists only of lowercase letters, numbers, and spaces. The name must be unique to this content type.'), + '#required' => TRUE, + ); + + if (!$type->locked) { + $form['identity']['type'] = array( + '#title' => t('Type'), + '#type' => 'textfield', + '#default_value' => $type->type, + '#maxlength' => 32, + '#required' => TRUE, + '#description' => t('The machine-readable name of this content type. This text will be used for constructing the URL of the create content page for this content type. It is recommended that this name consists only of lowercase letters, numbers, and underscores. Dashes are not allowed. Underscores will be converted into dashes when constructing the URL of the create content page. The name must be unique to this content type.'), + ); + } + else { + $form['identity']['type'] = array( + '#type' => 'value', + '#value' => $type->type, + ); + $form['identity']['type_display'] = array( + '#title' => t('Type'), + '#type' => 'item', + '#value' => theme('placeholder', $type->type), + '#description' => t('The machine-readable name of this content type. This field cannot be modified for system-defined content types.'), + ); + } + + $form['submission'] = array( + '#type' => 'fieldset', + '#title' =>t('Submission form'), + '#collapsible' => TRUE, + '#collapsed' => FALSE, + ); + if ($type->has_title) { + $form['submission']['title_label'] = array( + '#title' => t('Title field label'), + '#type' => 'textfield', + '#default_value' => $type->title_label, + '#description' => t('The label for the title field of this content type.'), + '#required' => TRUE, + ); + } + if ($type->has_body) { + $form['submission']['body_label'] = array( + '#title' => t('Body field label'), + '#type' => 'textfield', + '#default_value' => $type->body_label, + '#description' => t('The label for the body field of this content type.'), + '#required' => TRUE, + ); + } + $form['submission']['description'] = array( + '#title' => t('Description'), + '#type' => 'textarea', + '#default_value' => $type->description, + '#description' => t('A brief description of this content type. This text will be displayed as part of the list on the create content page.'), + ); + $form['submission']['help'] = array( + '#type' => 'textarea', + '#title' => t('Explanation or submission guidelines'), + '#default_value' => $type->help, + '#description' => t('This text will be displayed at the top of the submission form for this content type. It is useful for helping or instructing your users.') + ); + $form['submission']['min_word_count'] = array( + '#type' => 'select', + '#title' => t('Minimum number of words'), + '#default_value' => $type->min_word_count, + '#options' => drupal_map_assoc(array(0, 10, 25, 50, 75, 100, 125, 150, 175, 200)), + '#description' => t('The minimum number of words for the body field to be considered valid for this content type. This can be useful to rule out submissions that do not meet the site\'s standards, such as short test posts.') + ); + $form['workflow'] = array( + '#type' => 'fieldset', + '#title' =>t('Workflow'), + '#collapsible' => TRUE, + ); + $form['workflow']['node_options'] = array('#type' => 'checkboxes', + '#title' => t('Default options'), + '#default_value' => variable_get('node_options_'. $type->type, array('status', 'promote')), + '#options' => array( + 'status' => t('Published'), + 'promote' => t('Promoted to front page'), + 'sticky' => t('Sticky at top of lists'), + 'revision' => t('Create new revision'), + ), + '#description' => t('Users with the administer nodes permission will be able to override these options.'), + ); + + $form['old_type'] = array( + '#type' => 'value', + '#value' => $type->type, + ); + $form['orig_type'] = array( + '#type' => 'value', + '#value' => $type->orig_type, + ); + $form['module'] = array( + '#type' => 'value', + '#value' => $type->module, + ); + $form['custom'] = array( + '#type' => 'value', + '#value' => $type->custom, + ); + $form['modified'] = array( + '#type' => 'value', + '#value' => $type->modified, + ); + $form['locked'] = array( + '#type' => 'value', + '#value' => $type->locked, + ); + + $form['submit'] = array( + '#type' => 'submit', + '#value' => t('Save content type'), + ); + + if ($type->custom) { + if (!empty($type->type)) { + $form['delete'] = array( + '#type' => 'submit', + '#value' => t('Delete content type'), + ); + } + } + else { + $form['reset'] = array( + '#type' => 'submit', + '#value' => t('Reset to defaults'), + ); + } + + return drupal_get_form('node_type_form', $form); +} + +/** + * Implementation of hook_form_validate(). + */ +function node_type_form_validate($form_id, $form_values) { + $type = new stdClass(); + $type->type = trim($form_values['type']); + $type->name = trim($form_values['name']); + + // Work out what the type was before the user submitted this form + $old_type = trim($form_values['old_type']); + if (empty($old_type)) { + $old_type = $type->type; + } + + $types = node_get_types('names'); + + if (!$form_values['locked']) { + if (isset($types[$type->type]) && $type->type != $old_type) { + form_set_error('type', t('The machine-readable name %type is already taken.', array('%type' => theme('placeholder', $type->type)))); + } + if (strpos($type->type, '-') !== FALSE) { + form_set_error('type', t('The machine-readable name cannot contain dashes.', array('%type' => theme('placeholder', $type->type)))); + } + } + + $names = array_flip($types); + + if (isset($names[$type->name]) && $names[$type->name] != $old_type) { + form_set_error('name', t('The human-readable name %name is already taken.', array('%name' => theme('placeholder', $names[$type->name])))); + break; + } +} + +/** + * Implementation of hook_form_submit(). + */ +function node_type_form_submit($form_id, $form_values) { + $op = isset($_POST['op']) ? $_POST['op'] : ''; + + $type = new stdClass(); + + $type->type = trim($form_values['type']); + $type->name = trim($form_values['name']); + $type->orig_type = trim($form_values['orig_type']); + $type->old_type = isset($form_values['old_type']) ? $form_values['old_type'] : $type->type; + + $type->description = $form_values['description']; + $type->help = $form_values['help']; + $type->min_word_count = $form_values['min_word_count']; + $type->title_label = $form_values['title_label']; + $type->body_label = $form_values['body_label']; + + $type->module = !empty($form_values['module']) ? $form_values['module'] : 'node'; + $type->has_title = $type->has_body = TRUE; + $type->custom = $form_values['custom']; + $type->modified = TRUE; + $type->locked = $form_values['locked']; + + if ($op == t('Reset to defaults')) { + node_type_reset($type); + } + elseif ($op == t('Delete node type')) { + return 'admin/content/types/'. $type->old_type .'/delete'; + } + + if (!empty($form_values['old_type'])) { + if ($type->old_type != $type->type) { + $update_count = node_type_update_nodes($type->old_type, $type->type); + + if ($update_count) { + drupal_set_message(t('Changed the content type of %update_count posts from %old_type to %type.', array('%update_count' => theme('placeholder', $update_count), '%old_type' => theme('placeholder', $type->old_type), '%type' => theme('placeholder', $type->type)))); + cache_clear_all('filter:', TRUE); + } + } + } + + $status = node_type_save($type); + + // Remove everything that's been saved already - whatever's left is assumed + // to be a persistent variable. + foreach ($form_values as $key => $value) { + if (isset($type->$key)) { + unset($form_values[$key]); + } + } + + unset($form_values['type_display'], $form_values['old_type'], $form_values['orig_type'], $form_values['submit'], $form_values['delete'], $form_values['reset'], $form_values['form_id']); + + // Save or reset persistent variable values. + foreach ($form_values as $key => $value) { + $key .= '_'. $type->type; + if ($op == t('Reset to defaults')) { + variable_del($key); + } + else { + if (is_array($value)) { + $value = array_keys(array_filter($value)); + } + variable_set($key, $value); + + if ($type->old_type != $type->type) { + $key = str_replace($type->type, $type->old_type, $key); + variable_del($key); + } + } + } + + node_types_rebuild(); + menu_rebuild(); + cache_clear_all(); + $t_args = array('%name' => theme('placeholder', $type->name)); + + if ($op == t('Reset to defaults')) { + drupal_set_message(t('The content type %name has been reset to its default values.', $t_args)); + return; + } + + if ($status == SAVED_UPDATED) { + drupal_set_message(t('The content type %name has been updated.', $t_args)); + } + elseif ($status == SAVED_NEW) { + drupal_set_message(t('The content type %name has been added.', $t_args)); + watchdog('node', t('Added content type %name.', $t_args), WATCHDOG_NOTICE, l(t('view'), 'admin/content/types')); + } + + return 'admin/content/types'; +} + +/** + * Resets all of the relevant fields of a module-defined node type to their + * default values. + * + * @param &$type + * The node type to reset. The node type is passed back by reference with its + * resetted values. If there is no module-defined info for this node type, + * then nothing happens. + */ +function node_type_reset(&$type) { + $info_array = module_invoke($type->module, 'node_info'); + if (isset($info_array[$type->orig_type])) { + $info = _node_type_set_defaults($info_array[$type->orig_type]); + $info['type'] = $type->orig_type; + + foreach ($info as $field => $value) { + $type->$field = $value; + } + } +} + +/** + * Menu callback; delete a single content type. + */ +function node_type_delete($type) { + $form['type'] = array('#type' => 'value', '#value' => $type->type); + $form['name'] = array('#type' => 'value', '#value' => $type->name); + + $message = t('Are you sure you want to delete the content type %type?', array('%type' => theme('placeholder', $type->name))); + + return confirm_form('node_type_delete_form', $form, $message, 'admin/content/types', t('This action cannot be undone.'), t('Delete')); +} + +/** + * Process content type delete form submissions. + */ +function node_type_delete_form_submit($form_id, $form_values) { + db_query("DELETE FROM {node_type} WHERE type = '%s'", $form_values['type']); + + $t_args = array('%name' => theme('placeholder', $form_values['name'])); + drupal_set_message(t('The content type %name has been deleted.', $t_args)); + watchdog('menu', t('Deleted content type %name.', $t_args), WATCHDOG_NOTICE); + + node_types_rebuild(); + menu_rebuild(); + + return 'admin/content/types'; +} diff -r'uNF^f' --exclude=CVS --exclude=docs --exclude=files --exclude sites ../C-HEAD/modules/node/node.module ./modules/node/node.module --- ../C-HEAD/modules/node/node.module 2006-07-31 13:14:28.000000000 -0700 +++ ./modules/node/node.module 2006-08-01 00:27:56.000000000 -0700 @@ -36,6 +36,10 @@ function node_help($section) { return t('Allows content to be submitted to the site and displayed on pages.'); case 'admin/content/search': return t('Enter a simple pattern to search for a post. This can include the wildcard character *.
For example, a search for "br*" might return "bread bakers", "our daily bread" and "brenda".
'. t('Below is a list of all the content types on your site. All posts that exist on your site are instances of one of these content types.') .'
'; + case 'admin/content/types/add': + return ''. t('To create a new content type, enter the human-readable name, the machine-readable name, and all other relevant fields that are on this page. Once created, users of your site will be able to create posts that are instances of this content type.'); } if (arg(0) == 'node' && is_numeric(arg(1)) && arg(2) == 'revisions') { @@ -43,7 +47,8 @@ function node_help($section) { } if (arg(0) == 'node' && arg(1) == 'add' && $type = arg(2)) { - return filter_xss_admin(variable_get($type .'_help', '')); + $type = node_get_types('type', arg(2)); + return filter_xss_admin($type->help); } } @@ -190,16 +195,32 @@ function node_teaser($body, $format = NU return truncate_utf8($body, $size); } -function _node_names($op = '', $node = NULL) { - static $node_names = array(); - static $node_list = array(); - - if (empty($node_names)) { - $node_names = module_invoke_all('node_info'); - foreach ($node_names as $type => $value) { - $node_list[$type] = $value['name']; - } +/** + * Builds a list of available node types, and returns all of part of this list + * in the specified format. + * + * @param $op + * The format in which to return the list. When this is set to 'type', + * 'module', or 'name', only the specified node type is returned. When set to + * 'types' or 'names', all node types are returned. + * @param $node + * A node object, array, or string that indicates the node type to return. + * Leave at default value (NULL) to return a list of all node types. + * @param $reset + * Whether or not to reset this function's internal cache (defaults to + * FALSE). + * + * @return + * Either an array of all available node types, or a single node type, in a + * variable format. + */ +function node_get_types($op = 'types', $node = NULL, $reset = FALSE) { + static $_node_types, $_node_names; + + if ($reset || !isset($_node_types)) { + list($_node_types, $_node_names) = _node_types_build(); } + if ($node) { if (is_array($node)) { $type = $node['type']; @@ -210,54 +231,147 @@ function _node_names($op = '', $node = N elseif (is_string($node)) { $type = $node; } - if (!isset($node_names[$type])) { + if (!isset($_node_types[$type])) { return FALSE; } } switch ($op) { - case 'base': - return $node_names[$type]['base']; - case 'list': - return $node_list; + case 'types': + return $_node_types; + case 'type': + return $_node_types[$type]; + case 'module': + return $_node_types[$type]->module; + case 'names': + return $_node_names; case 'name': - return $node_list[$type]; + return $_node_names[$type]; + } +} + +/** + * Resets the database cache of node types, and saves all new or non-modified + * module-defined node types to the database. + */ +function node_types_rebuild() { + _node_types_build(); + + $node_types = node_get_types('types', NULL, TRUE); + + foreach ($node_types as $type => $info) { + if (!empty($info->is_new)) { + node_type_save($info); + } } + + _node_types_build(); } /** - * Determine the basename for hook_load etc. + * Saves a node type to the database. + * + * @param $info + * The node type to save, as an object. * - * @param $node - * Either a node object, a node array, or a string containing the node type. * @return - * The basename for hook_load, hook_nodeapi etc. + * Status flag indicating outcome of the operation. */ -function node_get_base($node) { - return _node_names('base', $node); +function node_type_save($info) { + $is_existing = FALSE; + $existing_type = !empty($info->old_type) ? $info->old_type : $info->type; + $is_existing = db_num_rows(db_query("SELECT * FROM {node_type} WHERE type = '%s'", $existing_type)); + + if ($is_existing) { + db_query("UPDATE {node_type} SET type = '%s', name = '%s', module = '%s', has_title = %d, title_label = '%s', has_body = %d, body_label = '%s', description = '%s', help = '%s', min_word_count = %d, custom = %d, modified = %d, locked = %d WHERE type = '%s'", $info->type, $info->name, $info->module, $info->has_title, $info->title_label, $info->has_body, $info->body_label, $info->description, $info->help, $info->min_word_count, $info->custom, $info->modified, $info->locked, $existing_type); + return SAVED_UPDATED; + } + else { + db_query("INSERT INTO {node_type} (type, name, module, has_title, title_label, has_body, body_label, description, help, min_word_count, custom, modified, locked, orig_type) VALUES ('%s', '%s', '%s', %d, '%s', %d, '%s', '%s', '%s', %d, %d, %d, %d, '%s')", $info->type, $info->name, $info->module, $info->has_title, $info->title_label, $info->has_body, $info->body_label, $info->description, $info->help, $info->min_word_count, $info->custom, $info->modified, $info->locked, $info->orig_type); + return SAVED_NEW; + } } /** - * Determine the human readable name for a given type. + * Updates all nodes of one type to be of another type. + * + * @param $orig_type + * The current node type of the nodes. + * @param $type + * The new node type of the nodes. * - * @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. + * The number of nodes whose node type field was modified. */ -function node_get_name($node) { - return _node_names('name', $node); +function node_type_update_nodes($old_type, $type) { + return db_num_rows(db_query("UPDATE {node} SET type = '%s' WHERE type = '%s'", $type, $old_type)); } /** - * Return the list of available node types. + * Builds the list of available node types, by querying hook_node_info() in all + * modules, and by looking for node types in the database. * - * @param $node - * Either a node object, a node array, or a string containing the node type. - * @return - * An array consisting ('#type' => name) pairs. */ -function node_get_types() { - return _node_names('list'); +function _node_types_build() { + $_node_types = array(); + $_node_names = array(); + + $info_array = module_invoke_all('node_info'); + foreach ($info_array as $type => $info) { + $info['type'] = $type; + $_node_types[$type] = (object) _node_type_set_defaults($info); + $_node_names[$type] = $info['name']; + } + + $type_result = db_query(db_rewrite_sql('SELECT nt.type, nt.* FROM {node_type} nt ORDER BY nt.type ASC', 'nt', 'type')); + while ($type_object = db_fetch_object($type_result)) { + if (!isset($_node_types[$type_object->type]) || $type_object->modified) { + $_node_types[$type_object->type] = $type_object; + $_node_names[$type_object->type] = $type_object->name; + + if ($type_object->type != $type_object->orig_type) { + unset($_node_types[$type_object->orig_type]); + unset($_node_names[$type_object->orig_type]); + } + } + } + + asort($_node_names); + + return array($_node_types, $_node_names); +} + +/** + * Set default values for a node type defined through hook_node_info(). + */ +function _node_type_set_defaults($info) { + if (!isset($info['has_title'])) { + $info['has_title'] = TRUE; + } + if ($info['has_title'] && !isset($info['title_label'])) { + $info['title_label'] = t('Title'); + } + + if (!isset($info['has_body'])) { + $info['has_body'] = TRUE; + } + if ($info['has_body'] && !isset($info['body_label'])) { + $info['body_label'] = t('Body'); + } + + if (!isset($info['custom'])) { + $info['custom'] = FALSE; + } + if (!isset($info['modified'])) { + $info['modified'] = FALSE; + } + if (!isset($info['locked'])) { + $info['locked'] = TRUE; + } + + $info['orig_type'] = $info['type']; + $info['is_new'] = TRUE; + + return $info; } /** @@ -271,7 +385,11 @@ function node_get_types() { * TRUE iff the $hook exists in the node type of $node. */ function node_hook(&$node, $hook) { - return module_hook(node_get_base($node), $hook); + $module = node_get_types('module', $node); + if ($module == 'node') { + $module = 'node_content'; // Avoid function name collisions. + } + return module_hook($module, $hook); } /** @@ -288,7 +406,11 @@ function node_hook(&$node, $hook) { */ function node_invoke(&$node, $hook, $a2 = NULL, $a3 = NULL, $a4 = NULL) { if (node_hook($node, $hook)) { - $function = node_get_base($node) ."_$hook"; + $module = node_get_types('module', $node); + if ($module == 'node') { + $module = 'node_content'; // Avoid function name collisions. + } + $function = $module .'_'. $hook; return ($function($node, $a2, $a3, $a4)); } } @@ -581,7 +703,18 @@ function node_show($node, $cid) { * Implementation of hook_perm(). */ function node_perm() { - return array('administer nodes', 'access content', 'view revisions', 'revert revisions'); + $perms = array('administer content types', 'administer nodes', 'access content', 'view revisions', 'revert revisions'); + + foreach (node_get_types() as $type) { + if ($type->module == 'node') { + $name = check_plain($type->name); + $perms[] = 'create '. $name .' content'; + $perms[] = 'edit own '. $name .' content'; + $perms[] = 'edit '. $name .' content'; + } + } + + return $perms; } /** @@ -720,7 +853,7 @@ function node_search($op = 'search', $ke $extra = node_invoke_nodeapi($node, 'search result'); $results[] = array('link' => url('node/'. $item->sid), - 'type' => node_get_name($node), + 'type' => node_get_types('name', $node), 'title' => $node->title, 'user' => theme('username', $node), 'date' => $node->changed, @@ -846,6 +979,26 @@ function node_menu($may_cache) { $items[] = array('path' => 'admin/content/node/overview', 'title' => t('list'), 'type' => MENU_DEFAULT_LOCAL_TASK, 'weight' => -10); + $items[] = array( + 'path' => 'admin/content/types', + 'title' => t('content types'), + 'description' => t('Manage posts by content type, including default status, front page promotion, etc.'), + 'callback' => 'node_overview_types', + 'access' => user_access('administer nodes'), + ); + $items[] = array( + 'path' => 'admin/content/types/list', + 'title' => t('list'), + 'type' => MENU_DEFAULT_LOCAL_TASK, + 'weight' => -10, + ); + $items[] = array( + 'path' => 'admin/content/types/add', + 'title' => t('add content type'), + 'callback' => 'node_type_form', + 'type' => MENU_LOCAL_TASK, + ); + if (module_exist('search')) { $items[] = array('path' => 'admin/content/search', 'title' => t('search posts'), 'description' => t('Search posts by keyword.'), @@ -861,13 +1014,6 @@ function node_menu($may_cache) { 'callback' => 'node_configure', 'access' => user_access('administer nodes') ); - $items[] = array( - 'path' => 'admin/content/types', - 'title' => t('content types'), - 'description' => t('Manage posts by content type, including default status, front page promotion, etc.'), - 'callback' => 'node_types_configure', - 'access' => user_access('administer nodes') - ); $items[] = array('path' => 'node', 'title' => t('content'), 'callback' => 'node_page', @@ -882,6 +1028,18 @@ function node_menu($may_cache) { 'callback' => 'node_feed', 'access' => user_access('access content'), 'type' => MENU_CALLBACK); + + foreach (node_get_types() as $type) { + if (module_exist($type->module)) { + $name = check_plain($type->name); + $type_url_str = str_replace('_', '-', $type->type); + $items[] = array( + 'path' => 'node/add/'. $type_url_str, + 'title' => t($name), + 'access' => node_access('create', $type->type), + ); + } + } } else { if (arg(0) == 'node' && is_numeric(arg(1))) { @@ -911,10 +1069,36 @@ function node_menu($may_cache) { 'type' => MENU_LOCAL_TASK); } } - else if (arg(0) == 'admin' && arg(1) == 'settings' && arg(2) == 'content-types' && is_string(arg(3))) { - $items[] = array('path' => 'admin/content/types/'. arg(3), - 'title' => t("'%name' content type", array('%name' => node_get_name(arg(3)))), - 'type' => MENU_CALLBACK); + + // Content type configuration. + if (arg(0) == 'admin' && arg(1) == 'content' && arg(2) == 'types') { + include_once './'. drupal_get_path('module', 'node') .'/content_types.inc'; + + if (arg(3) != NULL) { + $type_name = arg(3); + $type_name = !empty($type_name) ? str_replace('-', '_', $type_name) : NULL; + $type = node_get_types('type', $type_name); + + if (!empty($type)) { + $type->name = check_plain($type->name); + $type_url_str = str_replace('_', '-', $type->type); + + $items[] = array( + 'path' => 'admin/content/types/'. $type_url_str, + 'title' => t($type->name), + 'callback' => 'node_type_form', + 'callback arguments' => array($type), + 'type' => MENU_CALLBACK, + ); + $items[] = array( + 'path' => 'admin/content/types/'. $type_url_str .'/delete', + 'title' => t('delete'), + 'callback' => 'node_type_delete', + 'callback arguments' => array($type), + 'type' => MENU_CALLBACK, + ); + } + } } } @@ -985,7 +1169,7 @@ function node_filters() { 'options' => array('status-1' => t('published'), 'status-0' => t('not published'), 'promote-1' => t('promoted'), 'promote-0' => t('not promoted'), 'sticky-1' => t('sticky'), 'sticky-0' => t('not sticky'))); - $filters['type'] = array('title' => t('type'), 'options' => node_get_types()); + $filters['type'] = array('title' => t('type'), 'options' => node_get_types('names')); // The taxonomy filter if ($taxonomy = module_invoke('taxonomy', 'form_all', 1)) { $filters['category'] = array('title' => t('category'), 'options' => $taxonomy); @@ -1198,7 +1382,7 @@ function node_admin_nodes() { while ($node = db_fetch_object($result)) { $nodes[$node->nid] = ''; $form['title'][$node->nid] = array('#value' => l($node->title, 'node/'. $node->nid) .' '. theme('mark', node_mark($node->nid, $node->changed))); - $form['name'][$node->nid] = array('#value' => node_get_name($node)); + $form['name'][$node->nid] = array('#value' => node_get_types('name', $node)); $form['username'][$node->nid] = array('#value' => theme('username', $node)); $form['status'][$node->nid] = array('#value' => ($node->status ? t('published') : t('not published'))); $form['operations'][$node->nid] = array('#value' => l(t('edit'), 'node/'. $node->nid .'/edit', array(), $destination)); @@ -1275,40 +1459,6 @@ function node_multiple_delete_confirm_su } /** - * Menu callback; presents each node type configuration page. - */ -function node_types_configure($type = NULL) { - if (isset($type)) { - $node = new stdClass(); - $node->type = $type; - $form['submission'] = array('#type' => 'fieldset', '#title' =>t('Submission form') ); - $form['submission'][$type . '_help'] = array( - '#type' => 'textarea', '#title' => t('Explanation or submission guidelines'), '#default_value' => variable_get($type .'_help', ''), - '#description' => 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))) - ); - $form['submission']['minimum_'. $type .'_size'] = array( - '#type' => 'select', '#title' => t('Minimum number of words'), '#default_value' => variable_get('minimum_'. $type .'_size', 0), '#options' => drupal_map_assoc(array(0, 10, 25, 50, 75, 100, 125, 150, 175, 200)), - '#description' => 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))) - ); - $form['workflow'] = array('#type' => 'fieldset', '#title' =>t('Workflow')); - $form['type'] = array('#type' => 'value', '#value' => $type); - - $form['array_filter'] = array('#type' => 'value', '#value' => TRUE); - return system_settings_form($type .'_node_settings', $form); - } - else { - $header = array(t('Type'), t('Operations')); - - $rows = array(); - foreach (node_get_types() as $type => $name) { - $rows[] = array($name, l(t('configure'), 'admin/content/types/'. $type)); - } - - return theme('table', $header, $rows); - } -} - -/** * Generate an overview table of older revisions of a node. */ function node_revision_overview($node) { @@ -1587,7 +1737,7 @@ function node_validate($node, $form = ar // 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_get_name($node)))); + 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_types('name', $node)))); } if (isset($node->nid) && (node_last_changed($node->nid) > $_POST['edit']['changed'])) { @@ -1643,8 +1793,8 @@ function node_form($node) { } /** -* Generate the node editing form array. -*/ + * Generate the node editing form array. + */ function node_form_array($node) { node_object_prepare($node); @@ -1783,21 +1933,25 @@ function theme_node_form($form) { function node_add($type) { global $user; + $types = node_get_types(); + $type = isset($type) ? str_replace('-', '_', $type) : NULL; // If a node type has been specified, validate its existence. - if (array_key_exists($type, node_get_types()) && node_access('create', $type)) { + if (isset($types[$type]) && node_access('create', $type)) { // Initialize settings: $node = array('uid' => $user->uid, 'name' => $user->name, 'type' => $type); $output = node_form($node); - drupal_set_title(t('Submit %name', array('%name' => node_get_name($node)))); + drupal_set_title(t('Submit %name', array('%name' => check_plain($types[$type]->name)))); } else { // If no (valid) node type has been provided, display a node type overview. - foreach (node_get_types() as $type => $name) { - if (node_access('create', $type)) { - $out = '