nodeapi_example.module

  1. examples
    1. 6 nodeapi_example/nodeapi_example.module
    2. 7 nodeapi_example/nodeapi_example.module
    3. 8 nodeapi_example/nodeapi_example.module
  2. drupal
    1. 4.6 developer/examples/nodeapi_example.module
    2. 4.7 developer/examples/nodeapi_example.module
    3. 5 developer/examples/nodeapi_example.module

This is an example outlining how a module can be used to extend existing content types.

We will add the ability for each node to have a "rating," which will be a number from one to five. The rating will be tracked using the revision system also, so every node revision may have different rating values.

Functions & methods

NameDescription
nodeapi_example_form_alterImplements hook_form_alter().
nodeapi_example_node_deleteImplements hook_node_delete().
nodeapi_example_node_insertImplements hook_node_insert().
nodeapi_example_node_loadImplements hook_node_load().
nodeapi_example_node_updateImplements hook_node_update().
nodeapi_example_node_validateImplements hook_node_validate().
nodeapi_example_node_viewImplements hook_node_view().
nodeapi_example_themeImplements hook_theme().
theme_nodeapi_example_ratingA custom theme function.

File

nodeapi_example/nodeapi_example.module
View source
  1. <?php
  2. /**
  3. * @file
  4. * This is an example outlining how a module can be used to extend existing
  5. * content types.
  6. *
  7. * We will add the ability for each node to have a "rating," which will be a
  8. * number from one to five. The rating will be tracked using the revision
  9. * system also, so every node revision may have different rating values.
  10. */
  11. /**
  12. * Implements hook_form_alter().
  13. *
  14. * By implementing this hook, we're able to modify any form. We'll only make
  15. * changes to two types: a node's content type configuration and edit forms.
  16. *
  17. * We need to have a way for administrators to indicate which content types
  18. * should have our rating field added. This is done by inserting radios in
  19. * the node's content type configuration page.
  20. *
  21. * Changes made by this hook will be shown when editing the settings of any
  22. * content type.
  23. *
  24. * Optionally, hook_form_FORM_ID_alter() could be used with the function name
  25. * nodeapi_example_form_node_type_form_alter
  26. */
  27. function nodeapi_example_form_alter(&$form, $form_state, $form_id) {
  28. // First, check for the node type configuration form.
  29. if ($form_id == 'node_type_form') {
  30. // Alter the node type's configuration form to add our setting. We don't
  31. // need to worry about saving this value back to the variable, the form
  32. // we're altering will do it for us.
  33. $form['rating'] = array(
  34. '#type' => 'fieldset',
  35. '#title' => t('Rating settings'),
  36. '#collapsible' => TRUE,
  37. '#collapsed' => TRUE,
  38. '#group' => 'additional_settings',
  39. '#weight' => -1,
  40. );
  41. $form['rating']['nodeapi_example_node_type'] = array(
  42. '#type' => 'radios',
  43. '#title' => t('NodeAPI Example Rating'),
  44. '#default_value' => variable_get('nodeapi_example_node_type_' . $form['#node_type']->type, FALSE),
  45. '#options' => array(
  46. FALSE => t('Disabled'),
  47. TRUE => t('Enabled')
  48. ),
  49. '#description' => t('Should this node have a rating attached to it?'),
  50. );
  51. }
  52. // Here we check to see if the type and node field are set. If so, it could
  53. // be a node edit form.
  54. elseif (isset($form['type']) && isset($form['#node']) && $form['type']['#value'] . '_node_form' == $form_id) {
  55. // If the rating is enabled for this node type, we insert our control
  56. // into the form.
  57. $node = $form['#node'];
  58. if (variable_get('nodeapi_example_node_type_' . $form['type']['#value'], FALSE)) {
  59. $form['nodeapi_example_rating'] = array(
  60. '#type' => 'select',
  61. '#title' => t('Rating'),
  62. '#default_value' => isset($node->nodeapi_example_rating) ? $node->nodeapi_example_rating : '',
  63. '#options' => array(0 => t('Unrated'), 1, 2, 3, 4, 5),
  64. '#required' => TRUE,
  65. '#weight' => 0,
  66. );
  67. }
  68. }
  69. }
  70. /**
  71. * hook_nodeapi() has been replaced in Drupal 7 with a set of different hooks
  72. * providing the same or improved functionality.
  73. *
  74. * The replacement functions providing access to events affecting nodes in
  75. * Drupal are listed below, and detailed in the following location:
  76. * http://api.drupal.org/api/group/hooks/7
  77. * or in the node API declaration file: modules/node/node.api.php
  78. *
  79. * hook_node_access() - Control access to a node.
  80. * hook_node_access_records() - Set permissions for a node to be written to the database.
  81. * hook_node_access_records_alter() - Alter permissions for a node before it is written to the database.
  82. * hook_node_build_alter() - Modify structured content after it is built.
  83. * hook_node_delete() - Act on node deletion.
  84. * hook_node_grants() - Inform the node access system what permissions the user has.
  85. * hook_node_grants_alter() - Alter user access rules when trying to view, edit or delete a node.
  86. * hook_node_info() - Define module-provided node types.
  87. * hook_node_insert() - Respond to node insertion.
  88. * hook_node_load() - Act on node objects when loaded.
  89. * hook_node_operations() - Add mass node operations.
  90. * hook_node_prepare() - The node is about to be shown on the add/edit form.
  91. * hook_node_prepare_translation() - The node is being cloned for translation.
  92. * hook_node_presave() - The node passed validation and is about to be saved.
  93. * hook_node_revision_delete() - A revision of the node is deleted.
  94. * hook_node_search_result() - The node is being displayed as a search result.
  95. * hook_node_type_delete() - Act on node type deletion.
  96. * hook_node_type_insert() - Act on node type creation.
  97. * hook_node_type_update() - Act on node type changes.
  98. * hook_node_update() - The node being updated.
  99. * hook_node_update_index() - The node is being indexed.
  100. * hook_node_validate() - The user has finished editing the node and is previewing or submitting it.
  101. * hook_node_view() - The node content is being assembled before rendering.
  102. *
  103. */
  104. /**
  105. * Implements hook_node_validate().
  106. *
  107. * Check that the rating attribute is set in the form submission, since the
  108. * field is required. If not, send error message.
  109. */
  110. function nodeapi_example_node_validate($node, $form) {
  111. if (variable_get('nodeapi_example_node_type_' . $node->type, FALSE)) {
  112. if (isset($node->nodeapi_example_rating) && !$node->nodeapi_example_rating) {
  113. form_set_error('nodeapi_example_rating', t('You must rate this content.'));
  114. }
  115. }
  116. }
  117. /**
  118. * Implements hook_node_load().
  119. *
  120. * Loads the rating information if available for any of the nodes in the
  121. * argument list.
  122. */
  123. function nodeapi_example_node_load($nodes, $types) {
  124. // We can use $types to figure out if we need to process any of these nodes.
  125. $our_types = array();
  126. foreach ($types as $type) {
  127. if (variable_get('nodeapi_example_node_type_' . $type, FALSE)) {
  128. $our_types[] = $type;
  129. }
  130. }
  131. // Now $our_types contains all the types from $types that we want
  132. // to deal with. If it's empty, we can safely return.
  133. if (!count($our_types)) {
  134. return;
  135. }
  136. // Now we need to make a list of revisions based on $our_types
  137. foreach ($nodes as $node) {
  138. // We are using the revision id instead of node id.
  139. if (variable_get('nodeapi_example_node_type_' . $node->type, FALSE)) {
  140. $vids[] = $node->vid;
  141. }
  142. }
  143. // Check if we should load rating for any of the nodes.
  144. if (!isset($vids) || !count($vids)) {
  145. return;
  146. }
  147. // When we read, we don't care about the node->nid; we look for the right
  148. // revision ID (node->vid).
  149. $result = db_select('nodeapi_example', 'e')
  150. ->fields('e', array('nid', 'vid', 'rating'))
  151. ->where('e.vid IN (:vids)', array(':vids' => $vids))
  152. ->execute();
  153. foreach ($result as $record) {
  154. $nodes[$record->nid]->nodeapi_example_rating = $record->rating;
  155. }
  156. }
  157. /**
  158. * Implements hook_node_insert().
  159. *
  160. * As a new node is being inserted into the database, we need to do our own
  161. * database inserts.
  162. */
  163. function nodeapi_example_node_insert($node) {
  164. if (variable_get('nodeapi_example_node_type_' . $node->type, FALSE)) {
  165. // Notice that we are ignoring any revision information using $node->nid
  166. db_insert('nodeapi_example')
  167. ->fields(array(
  168. 'nid' => $node->nid,
  169. 'vid' => $node->vid,
  170. 'rating' => $node->nodeapi_example_rating,
  171. ))
  172. ->execute();
  173. }
  174. }
  175. /**
  176. * Implements hook_node_delete().
  177. *
  178. * When a node is deleted, we need to remove all related records from our table,
  179. * including all revisions. For the delete operations we use node->nid.
  180. */
  181. function nodeapi_example_node_delete($node) {
  182. // Notice that we're deleting even if the content type has no rating enabled.
  183. db_delete('nodeapi_example')
  184. ->condition('nid', $node->nid)
  185. ->execute();
  186. }
  187. /**
  188. * Implements hook_node_update().
  189. *
  190. * As an existing node is being updated in the database, we need to do our own
  191. * database updates.
  192. *
  193. * This hook is called when an existing node has been changed. We can't simply
  194. * update, since the node may not have a rating saved, thus no
  195. * database field. So we first check the database for a rating. If there is one,
  196. * we update it. Otherwise, we call nodeapi_example_node_insert() to create one.
  197. */
  198. function nodeapi_example_node_update($node) {
  199. if (variable_get('nodeapi_example_node_type_' . $node->type, FALSE)) {
  200. // Check first if this node has a saved rating.
  201. $rating = db_select('nodeapi_example', 'e')
  202. ->fields('e', array(
  203. 'rating',
  204. ))
  205. ->where('e.vid = (:vid)', array(':vid' => $node->vid))
  206. ->execute()->fetchField();
  207. if ($rating) {
  208. // Node has been rated before.
  209. db_update('nodeapi_example')
  210. ->fields(array('rating' => $node->nodeapi_example_rating))
  211. ->condition('vid', $node->vid)
  212. ->execute();
  213. }
  214. else {
  215. // Node was not previously rated, so insert a new rating in database.
  216. nodeapi_example_node_insert($node);
  217. }
  218. }
  219. }
  220. /**
  221. * Implements hook_node_view().
  222. *
  223. * This is a typical implementation that simply runs the node text through
  224. * the output filters.
  225. *
  226. * Finally, we need to take care of displaying our rating when the node is
  227. * viewed. This operation is called after the node has already been prepared
  228. * into HTML and filtered as necessary, so we know we are dealing with an
  229. * HTML teaser and body. We will inject our additional information at the front
  230. * of the node copy.
  231. *
  232. * Using node API 'hook_node_view' is more appropriate than using a filter here,
  233. * because filters transform user-supplied content, whereas we are extending it
  234. * with additional information.
  235. */
  236. function nodeapi_example_node_view($node, $build_mode = 'full') {
  237. if (variable_get('nodeapi_example_node_type_' . $node->type, FALSE)) {
  238. // Make sure to set a rating, also for nodes saved previously and not yet rated.
  239. $rating = isset($node->nodeapi_example_rating) ? $node->nodeapi_example_rating : 0;
  240. $node->content['nodeapi_example'] = array(
  241. '#markup' => theme('nodeapi_example_rating', array('rating' => $rating)),
  242. '#weight' => -1,
  243. );
  244. }
  245. }
  246. /**
  247. * Implements hook_theme().
  248. *
  249. * This lets us tell Drupal about our theme functions and their arguments.
  250. */
  251. function nodeapi_example_theme() {
  252. return array(
  253. 'nodeapi_example_rating' => array(
  254. 'variables' => array('rating' => NULL),
  255. ),
  256. );
  257. }
  258. /**
  259. * A custom theme function.
  260. *
  261. * By using this function to format our rating, themes can override this
  262. * presentation if they wish; for example, they could provide a star graphic
  263. * for the rating. We also wrap the default presentation in a CSS class that
  264. * is prefixed by the module name. This way, style sheets can modify the output
  265. * without requiring theme code.
  266. */
  267. function theme_nodeapi_example_rating($variables) {
  268. $options = array(
  269. 0 => t('Unrated'),
  270. 1 => t('Poor'),
  271. 2 => t('Needs improvement'),
  272. 3 => t('Acceptable'),
  273. 4 => t('Good'),
  274. 5 => t('Excellent'),
  275. );
  276. $output = '<div class="nodeapi_example_rating">';
  277. $output .= t('Rating: %rating', array('%rating' => $options[(int) $variables['rating']]));
  278. $output .= '</div>';
  279. return $output;
  280. }
Login or register to post comments