nodeapi_example.module 9.1 KB

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