Refactor the Node Type API. From: Damien Tournoud --- blog/blog.info | 1 blog/blog.install | 11 ++ blog/blog.module | 41 ------ blog/blog.type.inc | 35 +++++ book/book.install | 2 forum/forum.info | 1 forum/forum.install | 9 + forum/forum.module | 39 ------ forum/forum.type.inc | 44 +++++++ node/content_types.inc | 8 + node/node.api.php | 309 +---------------------------------------------- node/node.info | 1 node/node.install | 26 +++- node/node.module | 186 +++++----------------------- node/node.pages.inc | 4 - node/node.type.inc | 285 +++++++++++++++++++++++++++++++++++++++++++ poll/poll.info | 1 poll/poll.install | 9 + poll/poll.module | 127 ------------------- poll/poll.type.inc | 133 ++++++++++++++++++++ search/search.api.php | 8 - default/default.profile | 4 - 22 files changed, 606 insertions(+), 678 deletions(-) create mode 100644 blog/blog.install create mode 100644 blog/blog.type.inc create mode 100644 forum/forum.type.inc create mode 100644 node/node.type.inc create mode 100644 poll/poll.type.inc diff --git modules/blog/blog.info modules/blog/blog.info index 9e75db2..1a2b26f 100644 --- modules/blog/blog.info +++ modules/blog/blog.info @@ -7,3 +7,4 @@ version = VERSION core = 7.x files[] = blog.module files[] = blog.pages.inc +files[] = blog.type.inc diff --git modules/blog/blog.install modules/blog/blog.install new file mode 100644 index 0000000..da5a49d --- /dev/null +++ modules/blog/blog.install @@ -0,0 +1,11 @@ + array( 'name' => t('Blog entry'), - 'base' => 'blog', + 'class' => 'DrupalBlogNodeType', 'description' => t('A blog entry is a single post to an online journal, or blog.'), ) ); @@ -27,21 +27,6 @@ function blog_perm() { } /** - * Implementation of hook_access(). - */ -function blog_access($op, $node, $account) { - switch ($op) { - case 'create': - // Anonymous users cannot post even if they have the permission. - return user_access('create blog content', $account) && $account->uid; - case 'update': - return user_access('edit any blog content', $account) || (user_access('edit own blog content', $account) && ($node->uid == $account->uid)); - case 'delete': - return user_access('delete any blog content', $account) || (user_access('delete own blog content', $account) && ($node->uid == $account->uid)); - } -} - -/** * Implementation of hook_user_view(). */ function blog_user_view(&$edit, &$user, $category) { @@ -70,30 +55,6 @@ function blog_help($path, $arg) { } /** - * Implementation of hook_form(). - */ -function blog_form($node, $form_state) { - global $nid; - $type = node_get_types('type', $node); - - $form['title'] = array('#type' => 'textfield', '#title' => check_plain($type->title_label), '#required' => TRUE, '#default_value' => !empty($node->title) ? $node->title : NULL, '#weight' => -5); - $form['body_field'] = node_body_field($node, $type->body_label, $type->min_word_count); - return $form; -} - -/** - * Implementation of hook_view(). - */ -function blog_view($node, $teaser) { - if ((bool)menu_get_object()) { - // Breadcrumb navigation. - drupal_set_breadcrumb(array(l(t('Home'), NULL), l(t('Blogs'), 'blog'), l(t("!name's blog", array('!name' => $node->name)), 'blog/' . $node->uid))); - } - - return node_prepare($node, $teaser); -} - -/** * Implementation of hook_node_view. */ function blog_node_view($node, $teaser = FALSE) { diff --git modules/blog/blog.type.inc modules/blog/blog.type.inc new file mode 100644 index 0000000..d0f6493 --- /dev/null +++ modules/blog/blog.type.inc @@ -0,0 +1,35 @@ + $node->name)), 'blog/' . $node->uid))); + } + + return parent::view($node, $teaser); + } + + function access($op, $node, $account) { + switch ($op) { + case 'create': + // Anonymous users cannot post even if they have the permission. + return user_access('create blog content', $account) && $account->uid; + case 'update': + return user_access('edit any blog content', $account) || (user_access('edit own blog content', $account) && ($node->uid == $account->uid)); + case 'delete': + return user_access('delete any blog content', $account) || (user_access('delete own blog content', $account) && ($node->uid == $account->uid)); + } + } + +} diff --git modules/book/book.install modules/book/book.install index a4326ef..aa2ad28 100644 --- modules/book/book.install +++ modules/book/book.install @@ -27,7 +27,7 @@ function _book_install_type_create() { $book_node_type = array( 'type' => 'book', 'name' => t('Book page'), - 'base' => 'node_content', + 'class' => 'DrupalNodeType', 'description' => t('A book page is a page of content, organized into a collection of related entries collectively known as a book. A book page automatically displays links to adjacent pages, providing a simple navigation system for organizing and reviewing structured content.'), 'custom' => 1, 'modified' => 1, diff --git modules/forum/forum.info modules/forum/forum.info index ab67893..c8e1cab 100644 --- modules/forum/forum.info +++ modules/forum/forum.info @@ -10,3 +10,4 @@ files[] = forum.module files[] = forum.admin.inc files[] = forum.pages.inc files[] = forum.install +files[] = forum.type.inc diff --git modules/forum/forum.install modules/forum/forum.install index cf5d5cf..8c65533 100644 --- modules/forum/forum.install +++ modules/forum/forum.install @@ -128,3 +128,12 @@ function forum_update_6000() { return $ret; } + +/** + * Update the forum type to the new node API. + */ +function forum_update_7000() { + module_load_install('node'); + update_node_type_7000('forum', 'DrupalForumNodeType'); + return array(); +} diff --git modules/forum/forum.module modules/forum/forum.module index 891fe69..0fa2158 100644 --- modules/forum/forum.module +++ modules/forum/forum.module @@ -372,7 +372,7 @@ function forum_node_info() { return array( 'forum' => array( 'name' => t('Forum topic'), - 'base' => 'forum', + 'class' => 'DrupalForumNodeType', 'description' => t('A forum topic is the initial post to a new discussion thread within a forum.'), 'title_label' => t('Subject'), ) @@ -380,20 +380,6 @@ function forum_node_info() { } /** - * Implementation of hook_access(). - */ -function forum_access($op, $node, $account) { - switch ($op) { - case 'create': - return user_access('create forum content', $account); - case 'update': - return user_access('edit any forum content', $account) || (user_access('edit own forum content', $account) && ($account->uid == $node->uid)); - case 'delete': - return user_access('delete any forum content', $account) || (user_access('delete own forum content', $account) && ($account->uid == $node->uid)); - } -} - -/** * Implementation of hook_perm(). */ function forum_perm() { @@ -536,29 +522,6 @@ function forum_block_view($delta = '') { } /** - * Implementation of hook_form(). - */ -function forum_form(&$node, $form_state) { - $type = node_get_types('type', $node); - $form['title'] = array('#type' => 'textfield', '#title' => check_plain($type->title_label), '#default_value' => !empty($node->title) ? $node->title : '', '#required' => TRUE, '#weight' => -5); - - if (!empty($node->nid)) { - $vid = variable_get('forum_nav_vocabulary', ''); - $forum_terms = taxonomy_node_get_terms_by_vocabulary($node, $vid); - // if editing, give option to leave shadows - $shadow = (count($forum_terms) > 1); - $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_field'] = node_body_field($node, $type->body_label, 1); - - $form['#submit'][] = 'forum_submit'; - // Assign the forum topic submit handler. - - return $form; -} - -/** * Implementation of hook_term_path(). */ function forum_term_path($term) { diff --git modules/forum/forum.type.inc modules/forum/forum.type.inc new file mode 100644 index 0000000..bc785c6 --- /dev/null +++ modules/forum/forum.type.inc @@ -0,0 +1,44 @@ + 'textfield', '#title' => check_plain($type->title_label), '#default_value' => !empty($node->title) ? $node->title : '', '#required' => TRUE, '#weight' => -5); + + if (!empty($node->nid)) { + $vid = variable_get('forum_nav_vocabulary', ''); + $forum_terms = taxonomy_node_get_terms_by_vocabulary($node, $vid); + // if editing, give option to leave shadows + $shadow = (count($forum_terms) > 1); + $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_field'] = node_body_field($node, $type->body_label, 1); + + // Assign the forum topic submit handler. + $form['#submit'][] = 'forum_submit'; + + return $form; + } + + function access($op, $node, $account) { + switch ($op) { + case 'create': + return user_access('create forum content', $account); + case 'update': + return user_access('edit any forum content', $account) || (user_access('edit own forum content', $account) && ($account->uid == $node->uid)); + case 'delete': + return user_access('delete any forum content', $account) || (user_access('delete own forum content', $account) && ($account->uid == $node->uid)); + } + } +} diff --git modules/node/content_types.inc modules/node/content_types.inc index dec786b..4ecf36c 100644 --- modules/node/content_types.inc +++ modules/node/content_types.inc @@ -17,7 +17,7 @@ function node_overview_types() { foreach ($names as $key => $name) { $type = $types[$key]; - if (node_hook($type, 'form')) { + if (node_type($type->type)) { $type_url_str = str_replace('_', '-', $type->type); $row = array(theme('node_admin_overview', $name, $type)); // Set the edit column. @@ -179,9 +179,9 @@ function node_type_form(&$form_state, $type = NULL) { '#type' => 'value', '#value' => isset($type->orig_type) ? $type->orig_type : '', ); - $form['base'] = array( + $form['class'] = array( '#type' => 'value', - '#value' => $type->base, + '#value' => $type->class, ); $form['custom'] = array( '#type' => 'value', @@ -280,7 +280,7 @@ function node_type_form_submit($form, &$form_state) { $type->has_title = ($type->title_label != ''); $type->has_body = ($type->body_label != ''); - $type->base = !empty($form_state['values']['base']) ? $form_state['values']['base'] : 'node_content'; + $type->class = !empty($form_state['values']['class']) ? $form_state['values']['class'] : 'DrupalNodeType'; $type->custom = $form_state['values']['custom']; $type->modified = TRUE; $type->locked = $form_state['values']['locked']; diff --git modules/node/node.api.php modules/node/node.api.php index 1838ca0..9cfd0f8 100644 --- modules/node/node.api.php +++ modules/node/node.api.php @@ -470,10 +470,10 @@ function hook_node_view($node, $teaser) { */ function hook_node_info() { 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."), + 'blog' => array( + 'name' => t('Blog entry'), + 'class' => 'DrupalBlogNodeType', + 'description' => t('A blog entry is a single post to an online journal, or blog.'), ) ); } @@ -510,306 +510,5 @@ function hook_node_type($op, $info) { } /** - * Define access restrictions. - * - * This hook allows node modules to limit access to the node types they - * define. - * - * @param $op - * The operation to be performed. Possible values: - * - "create" - * - "delete" - * - "update" - * - "view" - * @param $node - * The node on which the operation is to be performed, or, if it does - * not yet exist, the type of node to be created. - * @param $account - * A user object representing the user for whom the operation is to be - * performed. - * @return - * TRUE if the operation is to be allowed; - * FALSE if the operation is to be denied; - * NULL to not override the settings in the node_access table, or access - * control modules. - * - * The administrative account (user ID #1) always passes any access check, - * so this hook is not called in that case. If this hook is not defined for - * a node type, all access checks will fail, so only the administrator will - * be able to see content of that type. However, users with the "administer - * nodes" permission may always view and edit content through the - * administrative interface. - * @see http://api.drupal.org/api/group/node_access/7 - * - * For a detailed usage example, see node_example.module. - * - * @ingroup node_access - */ -function hook_access($op, $node, $account) { - if ($op == 'create') { - return user_access('create stories', $account); - } - - if ($op == 'update' || $op == 'delete') { - if (user_access('edit own stories', $account) && ($account->uid == $node->uid)) { - return TRUE; - } - } -} - -/** - * Respond to node deletion. - * - * This is a hook used by node modules. It is called to allow the module - * to take action when a node is being deleted from the database by, for - * example, deleting information from related tables. - * - * @param &$node - * The node being deleted. - * @return - * None. - * - * To take action when nodes of any type are deleted (not just nodes of - * the type defined by this module), use hook_node() instead. - * - * For a detailed usage example, see node_example.module. - */ -function hook_delete(&$node) { - db_delete('mytable') - ->condition('nid', $nid->nid) - ->execute(); -} - -/** - * This is a hook used by node modules. It is called after load but before the - * node is shown on the add/edit form. - * - * @param &$node - * The node being saved. - * @return - * None. - * - * For a usage example, see image.module. - */ -function hook_prepare(&$node) { - if ($file = file_check_upload($field_name)) { - $file = file_save_upload($field_name, _image_filename($file->filename, NULL, TRUE)); - if ($file) { - if (!image_get_info($file->filepath)) { - form_set_error($field_name, t('Uploaded file is not a valid image')); - return; - } - } - else { - return; - } - $node->images['_original'] = $file->filepath; - _image_build_derivatives($node, true); - $node->new_file = TRUE; - } -} - -/** - * Display a node editing form. - * - * This hook, implemented by node modules, is called to retrieve the form - * that is displayed when one attempts to "create/edit" an item. This form is - * displayed at the URI http://www.example.com/?q=node//nodetype. - * - * @param &$node - * The node being added or edited. - * @param $form_state - * The form state array. Changes made to this variable will have no effect. - * @return - * An array containing the form elements to be displayed in the node - * edit form. - * - * The submit and preview buttons, taxonomy controls, and administrative - * accoutrements are displayed automatically by node.module. This hook - * needs to return the node title, the body text area, and fields - * specific to the node type. - * - * For a detailed usage example, see node_example.module. - */ -function hook_form(&$node, $form_state) { - $type = node_get_types('type', $node); - - $form['title'] = array( - '#type' => 'textfield', - '#title' => check_plain($type->title_label), - '#required' => TRUE, - ); - $form['body'] = array( - '#type' => 'textarea', - '#title' => check_plain($type->body_label), - '#rows' => 20, - '#required' => TRUE, - ); - $form['field1'] = array( - '#type' => 'textfield', - '#title' => t('Custom field'), - '#default_value' => $node->field1, - '#maxlength' => 127, - ); - $form['selectbox'] = array( - '#type' => 'select', - '#title' => t('Select box'), - '#default_value' => $node->selectbox, - '#options' => array( - 1 => 'Option A', - 2 => 'Option B', - 3 => 'Option C', - ), - '#description' => t('Please choose an option.'), - ); - - return $form; -} - -/** - * Respond to node insertion. - * - * This is a hook used by node modules. It is called to allow the module - * to take action when a new node is being inserted in the database by, - * for example, inserting information into related tables. - * - * @param $node - * The node being inserted. - * @return - * None. - * - * To take action when nodes of any type are inserted (not just nodes of - * the type(s) defined by this module), use hook_node() instead. - * - * For a detailed usage example, see node_example.module. - */ -function hook_insert($node) { - db_query("INSERT INTO {mytable} (nid, extra) - VALUES (%d, '%s')", $node->nid, $node->extra); -} - -/** - * Load node-type-specific information. - * - * This is a hook used by node modules. It is called to allow the module - * a chance to load extra information that it stores about a node. The hook - * should not be used to replace information from the core {node} table since - * this may interfere with the way nodes are fetched from cache. - * - * @param $nodes - * An array of the nodes being loaded, keyed by nid. At call time, - * node.module has already loaded the basic information about the nodes, such - * as node ID (nid), title, and body. - * - * For a detailed usage example, see node_example.module. - */ -function hook_load($nodes) { - $result = db_query('SELECT nid, foo FROM {mytable} WHERE nid IN (:nids)', array(':nids' => array_keys($nodes))); - foreach ($result as $record) { - $nodes[$record->nid]->foo = $record->foo; - } -} - -/** - * Respond to node updating. - * - * This is a hook used by node modules. It is called to allow the module - * to take action when an edited node is being updated in the database by, - * for example, updating information in related tables. - * - * @param $node - * The node being updated. - * @return - * None. - * - * To take action when nodes of any type are updated (not just nodes of - * the type(s) defined by this module), use hook_node() instead. - * - * For a detailed usage example, see node_example.module. - */ -function hook_update($node) { - db_query("UPDATE {mytable} SET extra = '%s' WHERE nid = %d", - $node->extra, $node->nid); -} - -/** - * Verify a node editing form. - * - * This is a hook used by node modules. It is called to allow the module - * to verify that the node is in a format valid to post to the site. - * Errors should be set with form_set_error(). - * - * @param $node - * The node to be validated. - * @param $form - * The node edit form array. - * @return - * None. - * - * To validate nodes of all types (not just nodes of the type(s) defined by - * this module), use hook_node() instead. - * - * Changes made to the $node object within a hook_validate() function will - * have no effect. The preferred method to change a node's content is to use - * hook_submit() or hook_node($op='submit') instead. If it is really - * necessary to change the node at the validate stage, you can use function - * form_set_value(). - * - * For a detailed usage example, see node_example.module. - */ -function hook_validate($node, &$form) { - if (isset($node->end) && isset($node->start)) { - if ($node->start > $node->end) { - form_set_error('time', t('An event may not end before it starts.')); - } - } -} - -/** - * Display a node. - * - * This is a hook used by node modules. It allows a module to define a - * custom method of displaying its nodes, usually by displaying extra - * information particular to that node type. - * - * @param $node - * The node to be displayed. - * @param $teaser - * Whether we are to generate a "teaser" or summary of the node, rather than - * display the whole thing. - * @return - * $node. The passed $node parameter should be modified as necessary and - * returned so it can be properly presented. Nodes are prepared for display - * by assembling a structured array in $node->content, rather than directly - * manipulating $node->body and $node->teaser. The format of this array is - * the same used by the Forms API. As with FormAPI arrays, the #weight - * property can be used to control the relative positions of added elements. - * If for some reason you need to change the body or teaser returned by - * node_prepare(), you can modify $node->content['body']['#value']. Note - * that this will be the un-rendered content. To modify the rendered output, - * see hook_node($op = 'alter'). - * - * For a detailed usage example, see node_example.module. - */ -function hook_view($node, $teaser = FALSE) { - if ((bool)menu_get_object()) { - $breadcrumb = array(); - $breadcrumb[] = array('path' => 'example', 'title' => t('example')); - $breadcrumb[] = array('path' => 'example/' . $node->field1, - 'title' => t('%category', array('%category' => $node->field1))); - $breadcrumb[] = array('path' => 'node/' . $node->nid); - menu_set_location($breadcrumb); - } - - $node = node_prepare($node, $teaser); - $node->content['myfield'] = array( - '#value' => theme('mymodule_myfield', $node->myfield), - '#weight' => 1, - ); - - return $node; -} - -/** * @} End of "addtogroup hooks". */ diff --git modules/node/node.info modules/node/node.info index 227c9f6..8d31101 100644 --- modules/node/node.info +++ modules/node/node.info @@ -8,5 +8,6 @@ files[] = node.module files[] = content_types.inc files[] = node.admin.inc files[] = node.pages.inc +files[] = node.type.inc files[] = node.install required = TRUE diff --git modules/node/node.install modules/node/node.install index ba4369e..e0329c3 100644 --- modules/node/node.install +++ modules/node/node.install @@ -257,8 +257,8 @@ function node_schema() { 'not null' => TRUE, 'default' => '', ), - 'base' => array( - 'description' => 'The base string used to construct callbacks corresponding to this node type.', + 'class' => array( + 'description' => 'The base class used for this node type.', 'type' => 'varchar', 'length' => 255, 'not null' => TRUE, @@ -356,13 +356,31 @@ function node_schema() { function node_update_7000() { $ret = array(); - $ret[] = update_sql("UPDATE {node_type} SET module = 'node_content' WHERE module = 'node'"); - db_change_field($ret, 'node_type', 'module', 'base', array('type' => 'varchar', 'length' => 255, 'not null' => TRUE)); + $ret[] = update_sql("UPDATE {node_type} SET module = 'DrupalNodeType' WHERE module = 'node'"); + db_change_field($ret, 'node_type', 'module', 'class', array('type' => 'varchar', 'length' => 255, 'not null' => TRUE)); return $ret; } /** + * Helper function for other module wanting to update their node types. + * + * @param $base + * The old 'base' parameter for the node type. + * @param $class + * The new 'class' parameter. + */ +function update_node_type_7000($base, $class) { + $source_column = drupal_get_installed_schema_version('node') >= 7000 ? 'class' : 'module'; + db_update('node_type') + ->fields(array( + 'class' => $class + )) + ->condition($source_column, $base) + ->execute(); +} + +/** * Rename {node_revisions} table to {node_revision}. */ function node_update_7001() { diff --git modules/node/node.module modules/node/node.module index 34ea8ae..8300b7c 100644 --- modules/node/node.module +++ modules/node/node.module @@ -478,7 +478,7 @@ function node_teaser($body, $format = NULL, $size = NULL) { * * @param $op * The format in which to return the list. When this is set to 'type', - * 'base', or 'name', only the specified node type is returned. When set to + * 'class', 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. @@ -517,8 +517,8 @@ function node_get_types($op = 'types', $node = NULL, $reset = FALSE) { return $_node_types; case 'type': return isset($_node_types[$type]) ? $_node_types[$type] : FALSE; - case 'base': - return isset($_node_types[$type]->base) ? $_node_types[$type]->base : FALSE; + case 'class': + return isset($_node_types[$type]->class) ? $_node_types[$type]->class : FALSE; case 'names': return $_node_names; case 'name': @@ -567,7 +567,7 @@ function node_type_save($info) { $fields = array( 'type' => (string) $type->type, 'name' => (string) $type->name, - 'base' => (string) $type->base, + 'class' => (string) $type->class, 'has_title' => (int) $type->has_title, 'title_label' => (string) $type->title_label, 'has_body' => (int) $type->has_body, @@ -663,9 +663,9 @@ function _node_types_build() { foreach ($type_result as $type_object) { // 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() + // module using hook_node_info) have a base class 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($info_array[$type_object->type])) { + if (isset($type_object->class) && $type_object->class != 'DrupalNodeType' && empty($info_array[$type_object->type])) { $type_object->disabled = TRUE; } if (!isset($_node_types[$type_object->type]) || $type_object->modified) { @@ -704,7 +704,7 @@ function node_type_set_defaults($info = array()) { $type = new stdClass(); $type->type = ''; $type->name = ''; - $type->base = ''; + $type->class = ''; $type->description = ''; $type->help = ''; $type->min_word_count = 0; @@ -736,38 +736,26 @@ function node_type_set_defaults($info = array()) { } /** - * Determine whether a node hook exists. + * Return the object defining a node type. * - * @param &$node - * Either a node object, node array, or a string containing the node type. - * @param $hook - * A string containing the name of the hook. - * @return - * TRUE iff the $hook exists in the node type of $node. + * @param $node + * The type name. + * @return A DrupalNodeType. */ -function node_hook(&$node, $hook) { - $base = node_get_types('base', $node); - return module_hook($base, $hook); -} +function node_type($type) { + static $types; -/** - * Invoke a node hook. - * - * @param &$node - * Either a node object, node array, or a string containing the node type. - * @param $hook - * A string containing the name of the hook. - * @param $a2, $a3, $a4 - * Arguments to pass on to the hook, after the $node argument. - * @return - * The returned value of the invoked hook. - */ -function node_invoke(&$node, $hook, $a2 = NULL, $a3 = NULL, $a4 = NULL) { - if (node_hook($node, $hook)) { - $base = node_get_types('base', $node); - $function = $base . '_' . $hook; - return ($function($node, $a2, $a3, $a4)); + if (!isset($types[$type])) { + $class = node_get_types('class', $type); + if ($class) { + $types[$type] = new $class(); + } + else { + $types[$type] = NULL; + } } + + return $types[$type]; } /** @@ -891,10 +879,7 @@ function node_load_multiple($nids = array(), $conditions = array(), $reset = FAL // Call node type specific callbacks on each typed array of nodes. foreach ($typed_nodes as $type => $nodes_of_type) { - if (node_hook($type, 'load')) { - $function = node_get_types('base', $type) . '_load'; - $function($nodes_of_type); - } + node_type($type)->load($nodes_of_type); } // Attach fields. @@ -986,7 +971,7 @@ function node_validate($node, $form = array()) { } // Do node-type-specific validation checks. - node_invoke($node, 'validate', $form); + node_type($node->type)->validate($node, $form); module_invoke_all('node_validate', $node, $form); } @@ -1119,10 +1104,8 @@ function node_save(&$node) { ->execute(); } - // Call the node specific callback (if any). This can be - // node_invoke($node, 'insert') or - // node_invoke($node, 'update'). - node_invoke($node, $op); + // Call the node specific callback (if any). + node_type($node->type)->$op($node); // Save fields. $function = "field_attach_$op"; @@ -1177,7 +1160,7 @@ function node_delete($nid) { ->execute(); // Call the node-specific callback (if any): - node_invoke($node, 'delete'); + node_type($node->type)->delete($node); module_invoke_all('node_delete', $node); // Clear the page and block caches. @@ -1221,30 +1204,6 @@ function node_build($node, $teaser = FALSE) { } /** - * Apply filters and build the node's standard elements. - */ -function node_prepare($node, $teaser = FALSE) { - // First we'll overwrite the existing node teaser and body with - // the filtered copies! Then, we'll stick those into the content - // array and set the read more flag if appropriate. - $node->readmore = (strlen($node->teaser) < strlen($node->body)); - - if ($teaser == FALSE) { - $node->body = check_markup($node->body, $node->format, $node->language, FALSE); - } - else { - $node->teaser = check_markup($node->teaser, $node->format, $node->language, FALSE); - } - - $node->content['body'] = array( - '#markup' => $teaser ? $node->teaser : $node->body, - '#weight' => 0, - ); - - return $node; -} - -/** * Builds a structured array representing the node's content. * * @param $node @@ -1266,14 +1225,8 @@ function node_build_content($node, $teaser = FALSE) { // Remove the delimiter (if any) that separates the teaser from the body. $node->body = isset($node->body) ? str_replace('', '', $node->body) : ''; - // The 'view' hook can be implemented to overwrite the default function - // to display nodes. - if (node_hook($node, 'view')) { - $node = node_invoke($node, 'view', $teaser); - } - else { - $node = node_prepare($node, $teaser); - } + // Call the 'view' implementation of the node type. + node_type($node->type)->view($node, $teaser); // Build fields content. $node->content += field_attach_view('node', $node, $teaser); @@ -1354,7 +1307,7 @@ function node_perm() { ); foreach (node_get_types() as $type) { - if ($type->base == 'node_content') { + if ($type->class == 'DrupalNodeType') { $perms += node_list_permissions($type); } } @@ -1696,7 +1649,7 @@ function _node_revision_access($node, $op = 'view') { function _node_add_access() { $types = node_get_types(); foreach ($types as $type) { - if (node_hook($type->type, 'form') && node_access('create', $type->type)) { + if (node_type($type->type) && node_access('create', $type->type)) { return TRUE; } } @@ -1961,13 +1914,8 @@ function node_feed($nids = FALSE, $channel = array()) { if ($item_length != 'title') { $teaser = ($item_length == 'teaser'); - // Filter and prepare node teaser - if (node_hook($item, 'view')) { - $item = node_invoke($item, 'view', $teaser, FALSE); - } - else { - $item = node_prepare($item, $teaser); - } + // Filter and prepare node teaser. + node_type($item->type)->view($item, $teaser); // Allow modules to change $node->content before the node is rendered. module_invoke_all('node_view', $item, $teaser); @@ -2359,10 +2307,8 @@ function node_access($op, $node, $account = NULL) { return FALSE; } - // Can't use node_invoke('access', $node), because the access hook takes the - // $op parameter before the $node parameter. - $base = node_get_types('base', $node); - $access = module_invoke($base, 'access', $op, $node, $account); + // Check node type specific access information. + $access = node_type(is_object($node) ? $node->type : $node)->access($op, $node, $account); if (!is_null($access)) { return $access; } @@ -2804,66 +2750,6 @@ function _node_access_rebuild_batch_finished($success, $results, $operations) { * @} End of "defgroup node_access". */ - -/** - * @defgroup node_content Hook implementations for user-created content types. - * @{ - */ - -/** - * Implementation of hook_access(). - * - * Named so as not to conflict with node_access() - */ -function node_content_access($op, $node, $account) { - $type = is_string($node) ? $node : (is_array($node) ? $node['type'] : $node->type); - - if ($op == 'create') { - return user_access('create ' . $type . ' content', $account); - } - - if ($op == 'update') { - if (user_access('edit any ' . $type . ' content', $account) || (user_access('edit own ' . $type . ' content', $account) && ($account->uid == $node->uid))) { - return TRUE; - } - } - - if ($op == 'delete') { - if (user_access('delete any ' . $type . ' content', $account) || (user_access('delete own ' . $type . ' content', $account) && ($account->uid == $node->uid))) { - return TRUE; - } - } -} - -/** - * Implementation of hook_form(). - */ -function node_content_form($node, $form_state) { - $type = node_get_types('type', $node); - $form = array(); - - if ($type->has_title) { - $form['title'] = array( - '#type' => 'textfield', - '#title' => check_plain($type->title_label), - '#required' => TRUE, - '#default_value' => $node->title, - '#maxlength' => 255, - '#weight' => -5, - ); - } - - if ($type->has_body) { - $form['body_field'] = node_body_field($node, $type->body_label, $type->min_word_count); - } - - return $form; -} - -/** - * @} End of "defgroup node_content". - */ - /** * Implementation of hook_forms(). All node forms share the same form handler */ diff --git modules/node/node.pages.inc modules/node/node.pages.inc index 436dbe8..b9dd1e3 100644 --- modules/node/node.pages.inc +++ modules/node/node.pages.inc @@ -97,7 +97,7 @@ function node_object_prepare(&$node) { // Always use the default revision setting. $node->revision = in_array('revision', $node_options); - node_invoke($node, 'prepare'); + node_type($node->type)->prepare($node); module_invoke_all('node_prepare', $node); } @@ -145,7 +145,7 @@ function node_form(&$form_state, $node) { '#default_value' => isset($node->changed) ? $node->changed : NULL, ); // Get the node-specific bits. - if ($extra = node_invoke($node, 'form', $form_state)) { + if ($extra = node_type($node->type)->form($node, $form_state)) { $form = array_merge_recursive($form, $extra); } if (!isset($form['title']['#weight'])) { diff --git modules/node/node.type.inc modules/node/node.type.inc new file mode 100644 index 0000000..e665e4c --- /dev/null +++ modules/node/node.type.inc @@ -0,0 +1,285 @@ +type); + + if ($op == 'create') { + return user_access('create ' . $type . ' content', $account); + } + + if ($op == 'update') { + if (user_access('edit any ' . $type . ' content', $account) || (user_access('edit own ' . $type . ' content', $account) && ($account->uid == $node->uid))) { + return TRUE; + } + } + + if ($op == 'delete') { + if (user_access('delete any ' . $type . ' content', $account) || (user_access('delete own ' . $type . ' content', $account) && ($account->uid == $node->uid))) { + return TRUE; + } + } + } + + /** + * Display a node. + * + * This is a hook used by node modules. It allows a module to define a + * custom method of displaying its nodes, usually by displaying extra + * information particular to that node type. + * + * @param $node + * The node to be displayed. + * @param $teaser + * Whether we are to generate a "teaser" or summary of the node, rather than + * display the whole thing. + * @return + * $node. The passed $node parameter should be modified as necessary and + * returned so it can be properly presented. Nodes are prepared for display + * by assembling a structured array in $node->content, rather than directly + * manipulating $node->body and $node->teaser. The format of this array is + * the same used by the Forms API. As with FormAPI arrays, the #weight + * property can be used to control the relative positions of added elements. + * + * For a detailed usage example, see node_example.module. + */ + function view($node, $teaser = FALSE) { + // First we'll overwrite the existing node teaser and body with + // the filtered copies! Then, we'll stick those into the content + // array and set the read more flag if appropriate. + $node->readmore = (strlen($node->teaser) < strlen($node->body)); + + if ($teaser == FALSE) { + $node->body = check_markup($node->body, $node->format, $node->language, FALSE); + } + else { + $node->teaser = check_markup($node->teaser, $node->format, $node->language, FALSE); + } + + $node->content['body'] = array( + '#markup' => $teaser ? $node->teaser : $node->body, + '#weight' => 0, + ); + + return $node; + } + + /** + * This is a hook used by node modules. It is called after load but before the + * node is shown on the add/edit form. + * + * @param $node + * The node being saved. + * @return + * None. + * + * For a usage example, see image.module. + */ + function prepare($node) { + // Empty base implementation. + } + + /** + * Display a node editing form. + * + * This hook, implemented by node modules, is called to retrieve the form + * that is displayed when one attempts to "create/edit" an item. This form is + * displayed at the URI http://www.example.com/?q=node//nodetype. + * + * @param $node + * The node being added or edited. + * @param $form_state + * The form state array. Changes made to this variable will have no effect. + * @return + * An array containing the form elements to be displayed in the node + * edit form. + * + * The submit and preview buttons, taxonomy controls, and administrative + * accoutrements are displayed automatically by node.module. This hook + * needs to return the node title, the body text area, and fields + * specific to the node type. + * + * For a detailed usage example, see node_example.module. + */ + function form($node, $form_state) { + $type = node_get_types('type', $node); + $form = array(); + + if ($type->has_title) { + $form['title'] = array( + '#type' => 'textfield', + '#title' => check_plain($type->title_label), + '#required' => TRUE, + '#default_value' => $node->title, + '#maxlength' => 255, + '#weight' => -5, + ); + } + + if ($type->has_body) { + $form['body_field'] = node_body_field($node, $type->body_label, $type->min_word_count); + } + + return $form; + } + + /** + * Verify a node editing form. + * + * This is a hook used by node modules. It is called to allow the module + * to verify that the node is in a format valid to post to the site. + * Errors should be set with form_set_error(). + * + * @param $node + * The node to be validated. + * @param $form + * The node edit form array. + * @return + * None. + * + * To validate nodes of all types (not just nodes of the type(s) defined by + * this module), use node() instead. + * + * Changes made to the $node object within a validate() function will + * have no effect. The preferred method to change a node's content is to use + * submit() or node($op='submit') instead. If it is really + * necessary to change the node at the validate stage, you can use function + * form_set_value(). + * + * For a detailed usage example, see node_example.module. + */ + function validate($node, &$form) { + // Empty base implementation. + } + +} diff --git modules/poll/poll.info modules/poll/poll.info index 7b2ed5e..6b9c23d 100644 --- modules/poll/poll.info +++ modules/poll/poll.info @@ -6,4 +6,5 @@ version = VERSION core = 7.x files[] = poll.module files[] = poll.pages.inc +files[] = poll.type.inc files[] = poll.install diff --git modules/poll/poll.install modules/poll/poll.install index 34e96c1..d345070 100644 --- modules/poll/poll.install +++ modules/poll/poll.install @@ -139,6 +139,15 @@ function poll_schema() { } /** + * Update the poll type to the new node API. + */ +function poll_update_7000() { + module_load_install('node'); + update_node_type_7000('poll', 'DrupalPollNodeType'); + return array(); +} + +/** * Rename {poll_choices} table to {poll_choice} and {poll_votes} to {poll_vote}. */ function poll_update_7001() { diff --git modules/poll/poll.module modules/poll/poll.module index 04a7faa..a66e79c 100644 --- modules/poll/poll.module +++ modules/poll/poll.module @@ -74,20 +74,6 @@ function poll_perm() { } /** - * Implementation of hook_access(). - */ -function poll_access($op, $node, $account) { - switch ($op) { - case 'create': - return user_access('create poll content', $account); - case 'update': - return user_access('edit any poll content', $account) || (user_access('edit own poll content', $account) && ($node->uid == $account->uid)); - case 'delete': - return user_access('delete any poll content', $account) || (user_access('delete own poll content', $account) && ($node->uid == $account->uid)); - } -} - -/** * Implementation of hook_menu(). */ function poll_menu() { @@ -190,7 +176,7 @@ function poll_node_info() { return array( 'poll' => array( 'name' => t('Poll'), - 'base' => 'poll', + 'class' => 'DrupalPollNodeType', 'description' => t('A poll is a question with a set of possible responses. A poll, once created, automatically provides a simple running count of the number of votes received for each response.'), 'title_label' => t('Question'), 'has_body' => FALSE, @@ -199,117 +185,6 @@ function poll_node_info() { } /** - * Implementation of hook_form(). - */ -function poll_form(&$node, $form_state) { - global $user; - - $admin = user_access('administer nodes') || user_access('edit any poll content') || (user_access('edit own poll content') && $user->uid == $node->uid); - - $type = node_get_types('type', $node); - - $form = array( - '#cache' => TRUE, - ); - - $form['title'] = array( - '#type' => 'textfield', - '#title' => check_plain($type->title_label), - '#required' => TRUE, - '#default_value' => $node->title, - '#weight' => -5, - ); - - if (isset($form_state['choice_count'])) { - $choice_count = $form_state['choice_count']; - } - else { - $choice_count = max(2, empty($node->choice) ? 2 : count($node->choice)); - } - - // Add a wrapper for the choices and more button. - $form['choice_wrapper'] = array( - '#tree' => FALSE, - '#weight' => -4, - '#prefix' => '
', - '#suffix' => '
', - ); - - // Container for just the poll choices. - $form['choice_wrapper']['choice'] = array( - '#prefix' => '
', - '#suffix' => '
', - '#theme' => 'poll_choices', - ); - - // Add the current choices to the form. - $delta = 0; - $weight = 0; - if (isset($node->choice)) { - $delta = count($node->choice); - $weight = -$delta; - foreach ($node->choice as $chid => $choice) { - $key = 'chid:'. $chid; - $form['choice_wrapper']['choice'][$key] = _poll_choice_form($key, $choice['chid'], $choice['chtext'], $choice['chvotes'], $choice['weight'], $choice_count); - $weight = ($choice['weight'] > $weight) ? $choice['weight'] : $weight; - } - } - - // Add initial or additional choices. - $existing_delta = $delta; - for ($delta; $delta < $choice_count; $delta++) { - $key = 'new:'. ($delta - $existing_delta); - $form['choice_wrapper']['choice'][$key] = _poll_choice_form($key, NULL, '', 0, $weight, $choice_count); - } - - // We name our button 'poll_more' to avoid conflicts with other modules using - // AHAH-enabled buttons with the id 'more'. - $form['choice_wrapper']['poll_more'] = array( - '#type' => 'submit', - '#value' => t('More choices'), - '#description' => t("If the amount of boxes above isn't enough, click here to add more choices."), - '#weight' => 1, - '#submit' => array('poll_more_choices_submit'), // If no javascript action. - '#ahah' => array( - 'callback' => 'poll_choice_js', - 'wrapper' => 'poll-choices', - 'method' => 'replace', - 'effect' => 'fade', - ), - ); - - // Poll attributes - $duration = array(0 => t('Unlimited')) + drupal_map_assoc(array(86400, 172800, 345600, 604800, 1209600, 2419200, 4838400, 9676800, 31536000), "format_interval"); - $active = array(0 => t('Closed'), 1 => t('Active')); - - $form['settings'] = array( - '#type' => 'fieldset', - '#collapsible' => TRUE, - '#title' => t('Poll settings'), - '#weight' => -3, - '#access' => $admin, - ); - - $form['settings']['active'] = array( - '#type' => 'radios', - '#title' => t('Poll status'), - '#default_value' => isset($node->active) ? $node->active : 1, - '#options' => $active, - '#description' => t('When a poll is closed, visitors can no longer vote for it.'), - '#access' => $admin, - ); - $form['settings']['runtime'] = array( - '#type' => 'select', - '#title' => t('Poll duration'), - '#default_value' => isset($node->runtime) ? $node->runtime : 0, - '#options' => $duration, - '#description' => t('After this period, the poll will be closed automatically.'), - ); - - return $form; -} - -/** * Submit handler to add more choices to a poll form. This handler is used when * javascript is not available. It makes changes to the form state and the * entire form is rebuilt during the page reload. diff --git modules/poll/poll.type.inc modules/poll/poll.type.inc new file mode 100644 index 0000000..7021fba --- /dev/null +++ modules/poll/poll.type.inc @@ -0,0 +1,133 @@ +uid == $node->uid); + + $type = node_get_types('type', $node); + + $form = array( + '#cache' => TRUE, + ); + + $form['title'] = array( + '#type' => 'textfield', + '#title' => check_plain($type->title_label), + '#required' => TRUE, + '#default_value' => $node->title, + '#weight' => -5, + ); + + if (isset($form_state['choice_count'])) { + $choice_count = $form_state['choice_count']; + } + else { + $choice_count = max(2, empty($node->choice) ? 2 : count($node->choice)); + } + + // Add a wrapper for the choices and more button. + $form['choice_wrapper'] = array( + '#tree' => FALSE, + '#weight' => -4, + '#prefix' => '
', + '#suffix' => '
', + ); + + // Container for just the poll choices. + $form['choice_wrapper']['choice'] = array( + '#prefix' => '
', + '#suffix' => '
', + '#theme' => 'poll_choices', + ); + + // Add the current choices to the form. + $delta = 0; + $weight = 0; + if (isset($node->choice)) { + $delta = count($node->choice); + $weight = -$delta; + foreach ($node->choice as $chid => $choice) { + $key = 'chid:'. $chid; + $form['choice_wrapper']['choice'][$key] = _poll_choice_form($key, $choice['chid'], $choice['chtext'], $choice['chvotes'], $choice['weight'], $choice_count); + $weight = ($choice['weight'] > $weight) ? $choice['weight'] : $weight; + } + } + + // Add initial or additional choices. + $existing_delta = $delta; + for ($delta; $delta < $choice_count; $delta++) { + $key = 'new:'. ($delta - $existing_delta); + $form['choice_wrapper']['choice'][$key] = _poll_choice_form($key, NULL, '', 0, $weight, $choice_count); + } + + // We name our button 'poll_more' to avoid conflicts with other modules using + // AHAH-enabled buttons with the id 'more'. + $form['choice_wrapper']['poll_more'] = array( + '#type' => 'submit', + '#value' => t('More choices'), + '#description' => t("If the amount of boxes above isn't enough, click here to add more choices."), + '#weight' => 1, + '#submit' => array('poll_more_choices_submit'), // If no javascript action. + '#ahah' => array( + 'callback' => 'poll_choice_js', + 'wrapper' => 'poll-choices', + 'method' => 'replace', + 'effect' => 'fade', + ), + ); + + // Poll attributes + $duration = array(0 => t('Unlimited')) + drupal_map_assoc(array(86400, 172800, 345600, 604800, 1209600, 2419200, 4838400, 9676800, 31536000), "format_interval"); + $active = array(0 => t('Closed'), 1 => t('Active')); + + $form['settings'] = array( + '#type' => 'fieldset', + '#collapsible' => TRUE, + '#title' => t('Poll settings'), + '#weight' => -3, + '#access' => $admin, + ); + + $form['settings']['active'] = array( + '#type' => 'radios', + '#title' => t('Poll status'), + '#default_value' => isset($node->active) ? $node->active : 1, + '#options' => $active, + '#description' => t('When a poll is closed, visitors can no longer vote for it.'), + '#access' => $admin, + ); + $form['settings']['runtime'] = array( + '#type' => 'select', + '#title' => t('Poll duration'), + '#default_value' => isset($node->runtime) ? $node->runtime : 0, + '#options' => $duration, + '#description' => t('After this period, the poll will be closed automatically.'), + ); + + return $form; + } + + function access($op, $node, $account) { + switch ($op) { + case 'create': + return user_access('create poll content', $account); + case 'update': + return user_access('edit any poll content', $account) || (user_access('edit own poll content', $account) && ($node->uid == $account->uid)); + case 'delete': + return user_access('delete any poll content', $account) || (user_access('delete own poll content', $account) && ($node->uid == $account->uid)); + } + } +} \ No newline at end of file diff --git modules/search/search.api.php modules/search/search.api.php index 75b2eda..5186589 100644 --- modules/search/search.api.php +++ modules/search/search.api.php @@ -258,12 +258,8 @@ function hook_update_index() { variable_set('node_cron_last', max($last_comment, $node->changed, $node->created)); // Get node output (filtered and with module-specific fields). - if (node_hook($node, 'view')) { - node_invoke($node, 'view', false, false); - } - else { - $node = node_prepare($node, false); - } + node_type($node->type)->view($node, TRUE); + // Allow modules to change $node->body before viewing. module_invoke_all('node_view', $node, false, false); diff --git profiles/default/default.profile profiles/default/default.profile index c069f38..e29b8e6 100644 --- profiles/default/default.profile +++ profiles/default/default.profile @@ -104,7 +104,7 @@ function default_profile_tasks(&$task, $url) { array( 'type' => 'page', 'name' => st('Page'), - 'base' => 'node_content', + 'class' => 'DrupalNodeType', 'description' => st("A page, similar in form to an article, is a simple method for creating and displaying information that rarely changes, such as an \"About us\" section of a website. By default, a page entry does not allow visitor comments and is not featured on the site's initial home page."), 'custom' => 1, 'modified' => 1, @@ -113,7 +113,7 @@ function default_profile_tasks(&$task, $url) { array( 'type' => 'article', 'name' => st('Article'), - 'base' => 'node_content', + 'class' => 'DrupalNodeType', 'description' => st("An article, similar in form to a page, is ideal for creating and displaying content that informs or engages website visitors. Press releases, site announcements, and informal blog-like entries may all be created with an article entry. By default, an article entry is automatically featured on the site's initial home page, and provides the ability to post comments."), 'custom' => 1, 'modified' => 1,