nodereferrer.module

Tracking 6.x-1.x branch
  1. drupal
    1. 6 contributions/nodereferrer/nodereferrer.module

Defines a field type for backlinking referencing nodes.

@todo -clear content cache with nodeapi. -query nids for access on load/view..

Functions & methods

NameDescription
nodereferrer_content_is_emptyImplementation of hook_content_is_empty().
nodereferrer_fieldImplementation of hook_field().
nodereferrer_field_formatter_infoImplementation of hook_field_formatter_info().
nodereferrer_field_infoImplementation of hook_field_info().
nodereferrer_field_settingsImplementation of hook_field_settings().
nodereferrer_helpImplementation of hook_help().
nodereferrer_nodeapiImplementation of hook_nodeapi().
nodereferrer_nodereference_field_optionsHelper function to create an options list of nodereference fields.
nodereferrer_referrers
nodereferrer_themeImplementation of hook_theme().
nodereferrer_theme_formatterGeneric formatter function
nodereferrer_views_apiImplementation of hook_views_api().
nodereferrer_widget_infoImplementation of hook_widget_info().
theme_nodereferrer_field_default
theme_nodereferrer_field_full
theme_nodereferrer_field_plain
theme_nodereferrer_field_teaser
theme_nodereferrer_formatter_countTheme function for 'count' field formatter.
theme_nodereferrer_formatter_defaultTheme functions for 'default' field formatter.
theme_nodereferrer_formatter_fullTheme function for 'full' field formatter.
theme_nodereferrer_formatter_plainTheme function for 'plain' field formatter.
theme_nodereferrer_formatter_teaserTheme function for 'teaser' field formatter.

File

View source
  1. <?php
  2. /**
  3. * @file
  4. * Defines a field type for backlinking referencing nodes.
  5. *
  6. * @todo
  7. * -clear content cache with nodeapi.
  8. * -query nids for access on load/view..
  9. */
  10. /**
  11. * Implementation of hook_help().
  12. */
  13. function nodereferrer_help($path, $arg) {
  14. switch ($path) {
  15. case 'admin/modules#description':
  16. return t('<strong>CCK:</strong> Defines a field type for displaying referrers to a node. <em>Note: Requires content.module.</em>');
  17. }
  18. }
  19. /**
  20. * Implementation of hook_field_info().
  21. */
  22. function nodereferrer_field_info() {
  23. return array(
  24. 'nodereferrer' => array('label' => t('Node Referrers')),
  25. );
  26. }
  27. /**
  28. * Implementation of hook_field_settings().
  29. */
  30. function nodereferrer_field_settings($op, $field) {
  31. switch ($op) {
  32. case 'views data':
  33. $data = content_views_field_views_data($field);
  34. if (is_array($data)) {
  35. foreach ($data as $k => $v) {
  36. $data[$k] = array();
  37. }
  38. }
  39. else {
  40. $data = array();
  41. }
  42. return $data;
  43. case 'callbacks':
  44. return array('view' => CONTENT_CALLBACK_CUSTOM);
  45. case 'form':
  46. $form = array();
  47. // Hide unused options
  48. $form['required'] = array(
  49. '#type' => 'hidden',
  50. '#value' => FALSE,
  51. );
  52. $form['multiple'] = array(
  53. '#type' => 'hidden',
  54. '#value' => 0,
  55. );
  56. $form['referrer_types'] = array(
  57. '#type' => 'checkboxes',
  58. '#title' => t('Referring Node Types'),
  59. '#multiple' => TRUE,
  60. '#default_value' => is_array($field['referrer_types']) ? $field['referrer_types'] : array(),
  61. '#options' => node_get_types('names'),
  62. );
  63. $options = nodereferrer_nodereference_field_options();
  64. $form['referrer_fields'] = array(
  65. '#type' => 'checkboxes',
  66. '#title' => t('Referring Fields'),
  67. '#multiple' => TRUE,
  68. '#default_value' => is_array($field['referrer_fields']) ? $field['referrer_fields'] : array(),
  69. '#options' => $options,
  70. );
  71. if (module_exists('translation')) {
  72. $form['referrer_translations'] = array(
  73. '#type' => 'checkbox',
  74. '#title' => t('Show on translations'),
  75. '#description' => t('If this is checked, referrers will also show on translations of the referenced node'),
  76. '#default_value' => is_int($field['referrer_translations']) ? $field['referrer_translations'] : 0,
  77. );
  78. }
  79. $form['referrer_nodes_per_page'] = array(
  80. '#type' => 'textfield',
  81. '#title' => t('Referrers Per Page'),
  82. '#description' => t('Referring nodes to display per page. 0 for no paging.'),
  83. '#default_value' => !empty($field['referrer_nodes_per_page']) ? $field['referrer_nodes_per_page'] : 0,
  84. );
  85. $form['referrer_pager_element'] = array(
  86. '#type' => 'textfield',
  87. '#title' => t('Pager element'),
  88. '#description' => t('Use this to avoid clashes if you have several pagers on one page'),
  89. '#default_value' => !empty($field['referrer_pager_element']) ? $field['referrer_pager_element'] : 0,
  90. );
  91. $form['referrer_order'] = array(
  92. '#type' => 'select',
  93. '#title' => t('Refferer Sort Order'),
  94. '#options' => array(
  95. 'CREATED_ASC' => t('Chronological Order'),
  96. 'CREATED_DESC' => t('Reverse Chronological Order'),
  97. 'TITLE_ASC' => t('Title Order'),
  98. 'TITLE_DESC' => t('Reverse Title Order'),
  99. ),
  100. '#default_value' => strlen($field['referrer_order']) ? $field['referrer_order'] : 'TITLE_ASC',
  101. );
  102. return $form;
  103. case 'save':
  104. $settings = array('referrer_types', 'referrer_fields', 'referrer_nodes_per_page', 'referrer_pager_element', 'referrer_order');
  105. if (module_exists('translation')) {
  106. $settings[] = 'referrer_translations';
  107. }
  108. return $settings;
  109. }
  110. }
  111. /**
  112. * Implementation of hook_field().
  113. */
  114. function nodereferrer_field($op, &$node, $field, &$items, $teaser, $page) {
  115. switch ($op) {
  116. case 'load':
  117. $types = isset($field['referrer_types']) ? array_values(array_filter($field['referrer_types'])) : array();
  118. $fields = isset($field['referrer_fields']) ? array_values(array_filter($field['referrer_fields'])) : array();
  119. $order = $field['referrer_order'];
  120. $translations = isset($field['referrer_translations']) ? $field['referrer_translations'] : 0;
  121. $values = nodereferrer_referrers($node->nid, $fields, $types, $translations, $order);
  122. // Pass referring node objects into CCK content_load() cache. 24/08/2006 sun
  123. $items = array();
  124. foreach ($values as $nid => $rnode) {
  125. $items[] = $rnode;
  126. }
  127. if (count($items) == 0) {
  128. return array($field['field_name'] => array());
  129. }
  130. $output = array(
  131. 'items' => $items,
  132. 'limit' => empty($field['referrer_nodes_per_page']) ? 0 : $field['referrer_nodes_per_page'],
  133. 'element' => empty($field['referrer_pager_element']) ? 0 : $field['referrer_pager_element'],
  134. 'pager' => '',
  135. );
  136. return array($field['field_name'] => array($output));
  137. case 'delete':
  138. case 'update':
  139. // clear cache on nodes that refer to me.
  140. $types = array_values(array_filter($field['referrer_types']));
  141. $fields = array_values(array_filter($field['referrer_fields']));
  142. // clear any modules referring to me as my title or other data may change.
  143. // and nodereference doesn't clear the cache yet.
  144. foreach (nodereferrer_referrers($node->nid, $fields, $types, false) as $delta => $item) {
  145. $cid = 'content:' . $item['nid'] . ':' . $item['vid'];
  146. cache_clear_all($cid, 'cache_page');
  147. }
  148. return;
  149. }
  150. }
  151. /**
  152. * Implementation of hook_field_formatter_info().
  153. */
  154. function nodereferrer_field_formatter_info() {
  155. return array(
  156. 'default' => array(
  157. 'label' => 'Node Title Link (Default)',
  158. 'field types' => array('nodereferrer'),
  159. 'multiple values' => CONTENT_HANDLE_CORE,
  160. ),
  161. 'plain' => array(
  162. 'label' => 'Node Title Plain Text',
  163. 'field types' => array('nodereferrer'),
  164. 'multiple values' => CONTENT_HANDLE_CORE,
  165. ),
  166. 'teaser' => array(
  167. 'label' => 'Node Teaser',
  168. 'field types' => array('nodereferrer'),
  169. 'multiple values' => CONTENT_HANDLE_CORE,
  170. ),
  171. 'full' => array(
  172. 'label' => 'Node Body',
  173. 'field types' => array('nodereferrer'),
  174. 'multiple values' => CONTENT_HANDLE_CORE,
  175. ),
  176. 'count' => array(
  177. 'label' => 'Node Count',
  178. 'field types' => array('nodereferrer'),
  179. 'multiple values' => CONTENT_HANDLE_CORE,
  180. ),
  181. );
  182. }
  183. /**
  184. * Implementation of hook_theme().
  185. */
  186. function nodereferrer_theme() {
  187. return array(
  188. 'nodereferrer_formatter_default' => array(
  189. 'arguments' => array('info' => NULL),
  190. ),
  191. 'nodereferrer_field_default' => array(
  192. 'arguments' => array('element' => NULL),
  193. ),
  194. 'nodereferrer_formatter_plain' => array(
  195. 'arguments' => array('element' => NULL),
  196. ),
  197. 'nodereferrer_field_plain' => array(
  198. 'arguments' => array('element' => NULL),
  199. ),
  200. 'nodereferrer_formatter_teaser' => array(
  201. 'arguments' => array('element' => NULL),
  202. ),
  203. 'nodereferrer_field_teaser' => array(
  204. 'arguments' => array('element' => NULL),
  205. ),
  206. 'nodereferrer_formatter_full' => array(
  207. 'arguments' => array('element' => NULL),
  208. ),
  209. 'nodereferrer_field_full' => array(
  210. 'arguments' => array('element' => NULL),
  211. ),
  212. 'nodereferrer_formatter_count' => array(
  213. 'arguments' => array('element' => NULL),
  214. ),
  215. );
  216. }
  217. /**
  218. * Generic formatter function
  219. */
  220. function nodereferrer_theme_formatter($formatter, $info) {
  221. $items = isset($info['#item']['items']) ? $info['#item']['items'] : array();
  222. $limit = isset($info['#item']['limit']) ? $info['#item']['limit'] : 0;
  223. $element = isset($info['#item']['element']) ? $info['#item']['element'] : array();
  224. $pager = '';
  225. if ($limit) {
  226. // Fake the values set by pager query...
  227. global $pager_page_array, $pager_total, $pager_total_items;
  228. $page = isset($_GET['page']) ? $_GET['page'] : '';
  229. // Convert comma-separated $page to an array, used by other functions.
  230. $pager_page_array = explode(',', $page);
  231. // We calculate the total of pages as ceil(items / limit).
  232. $pager_total_items[$element] = count($items);
  233. $pager_total[$element] = ceil($pager_total_items[$element] / $limit);
  234. $pager_page_array[$element] = max(0, min((int)$pager_page_array[$element], ((int)$pager_total[$element]) - 1));
  235. // only display the select elements.
  236. if (is_array($items)) {
  237. $items = array_slice($items, $pager_page_array[$element] * $limit, $limit);
  238. }
  239. $pager = theme('pager', array(), $limit, $element);
  240. }
  241. $themed_items = array();
  242. foreach ($items as $i) {
  243. $i['field'] = $info; // Add some extra information the themer might like to have
  244. $themed_items[] = theme('nodereferrer_field_' . $formatter, $i);
  245. }
  246. $out = theme('item_list', $themed_items) . $pager;
  247. return $out;
  248. }
  249. /**
  250. * Theme functions for 'default' field formatter.
  251. */
  252. function theme_nodereferrer_formatter_default($info) {
  253. return nodereferrer_theme_formatter('default', $info);
  254. }
  255. function theme_nodereferrer_field_default($element) {
  256. return l($element['title'], 'node/' . $element['nid']);
  257. }
  258. /**
  259. * Theme function for 'plain' field formatter.
  260. */
  261. function theme_nodereferrer_formatter_plain($info) {
  262. return nodereferrer_theme_formatter('plain', $info);
  263. }
  264. function theme_nodereferrer_field_plain($element) {
  265. return strip_tags($element['title']);
  266. }
  267. /**
  268. * Theme function for 'teaser' field formatter.
  269. */
  270. function theme_nodereferrer_formatter_teaser($info) {
  271. return nodereferrer_theme_formatter('teaser', $info);
  272. }
  273. function theme_nodereferrer_field_teaser($element) {
  274. return node_view(node_load($element['nid']), TRUE);
  275. }
  276. /**
  277. * Theme function for 'full' field formatter.
  278. */
  279. function theme_nodereferrer_formatter_full($info) {
  280. return nodereferrer_theme_formatter('full', $info);
  281. }
  282. function theme_nodereferrer_field_full($element) {
  283. return node_view(node_load($element['nid']));
  284. }
  285. /**
  286. * Theme function for 'count' field formatter.
  287. */
  288. function theme_nodereferrer_formatter_count($info) {
  289. return count($info['#item']['items']);
  290. }
  291. /**
  292. * Implementation of hook_widget_info().
  293. */
  294. function nodereferrer_widget_info() {
  295. return array(
  296. 'nodereferrer_list' => array(
  297. 'label' => t('Read-Only List'),
  298. 'field types' => array('nodereferrer'),
  299. 'multiple values' => CONTENT_HANDLE_MODULE,
  300. 'callbacks' => array(
  301. 'default value' => CONTENT_CALLBACK_NONE,
  302. ),
  303. ),
  304. );
  305. }
  306. /**
  307. * Implementation of hook_content_is_empty().
  308. */
  309. function nodereferrer_content_is_empty($item, $field) {
  310. return TRUE;
  311. }
  312. /**
  313. * Get an array of referrer nids, by node.type & field.type
  314. * @param nid
  315. * the nid we want to find referres for
  316. * @param fieldnames
  317. * array of fieldnames to be checked for referrers
  318. * @param nodetypes
  319. * array of node types to be checked for referrers
  320. * @param translations
  321. * boolean if true, also return nodes that referrer to translations of the given node
  322. */
  323. function nodereferrer_referrers($nid, $fieldnames = array(), $nodetypes = array(), $translations = 0, $order = 'DESC') {
  324. if ($nodetypes) {
  325. $filter_nodetypes = "AND n.type IN ('" . implode("', '", $nodetypes) . "')";
  326. }
  327. else {
  328. $filter_nodetypes = '';
  329. }
  330. $fields = content_fields();
  331. // Set default values of fieldnames.
  332. if (!count($fieldnames)) {
  333. $fieldnames = array_keys($fields);
  334. }
  335. // For Postgresql compatibility ordered fields should be in SELECT too.
  336. $select_field = '';
  337. switch ($order) {
  338. case 'TITLE_ASC':
  339. $order = 'n.title ASC';
  340. break;
  341. case 'TITLE_DESC':
  342. $order = 'n.title DESC';
  343. break;
  344. case 'ASC':
  345. case 'CREATED_ASC':
  346. $order = 'n.created ASC';
  347. $select_field = ', n.created';
  348. break;
  349. default :
  350. case 'DESC':
  351. case 'CREATED_DESC':
  352. $order = 'n.created DESC';
  353. $select_field = ', n.created';
  354. break;
  355. }
  356. $values = array();
  357. foreach ($fieldnames as $fieldname) {
  358. if ($fields[$fieldname]['type'] == 'nodereference') {
  359. $db_info = content_database_info($fields[$fieldname]);
  360. if ($translations) {
  361. $query = "SELECT n.nid, n.vid, n.title $select_field
  362. FROM {" . $db_info['table'] . "} nr
  363. INNER JOIN {node} current_node ON current_node.nid = %d
  364. INNER JOIN {node} n ON n.vid = nr.vid AND n.status = 1 " . $filter_nodetypes . "
  365. LEFT JOIN {node} translations ON current_node.tnid > 0 AND translations.tnid = current_node.tnid
  366. WHERE (current_node.tnid = 0 AND nr." . $db_info['columns']['nid']['column'] . " = current_node.nid)
  367. OR
  368. (current_node.tnid > 0 AND nr." . $db_info['columns']['nid']['column'] . " = translations.nid)
  369. ORDER BY " . $order;
  370. }
  371. else {
  372. $query = "SELECT n.nid, n.vid, n.title $select_field
  373. FROM {" . $db_info['table'] . "} nr
  374. INNER JOIN {node} n ON n.vid = nr.vid AND n.status = 1 " . $filter_nodetypes . "
  375. WHERE nr." . $db_info['columns']['nid']['column'] . " = %d
  376. ORDER BY " . $order;
  377. }
  378. $query = db_rewrite_sql($query);
  379. $result = db_query($query, $nid);
  380. while ($value = db_fetch_array($result)) {
  381. // avoid duplicate referrers by using nid as key
  382. $values[$value['nid']]['nid'] = $value['nid'];
  383. $values[$value['nid']]['vid'] = $value['vid'];
  384. $values[$value['nid']]['title'] = $value['title'];
  385. //Store the nodereference fieldnames in the nodereferrer data in the node,
  386. //so that it can be looked up without needing to load the referring node.
  387. $values[$value['nid']]['referrers'][$fieldname] = $fieldname;
  388. }
  389. }
  390. }
  391. return $values;
  392. }
  393. /**
  394. * Helper function to create an options list of nodereference fields.
  395. */
  396. function nodereferrer_nodereference_field_options() {
  397. $options = array();
  398. $types = content_fields();
  399. foreach($types as $type) {
  400. if ($type['type'] == 'nodereference') {
  401. $options[$type['field_name']] = $type['field_name'] . ' (' . $type['widget']['label'] . ')';
  402. }
  403. }
  404. return $options;
  405. }
  406. /**
  407. * Implementation of hook_nodeapi().
  408. */
  409. function nodereferrer_nodeapi($node, $op) {
  410. switch ($op) {
  411. case 'insert':
  412. case 'update':
  413. case 'delete':
  414. // Clear content cache to help maintain proper display of nodes.
  415. $nids = array();
  416. $type = content_types($node->type);
  417. if (!empty($type['fields'])) {
  418. foreach ($type['fields'] as $field) {
  419. // Add referenced nodes to nids. This will clean up nodereferrer fields
  420. // when the referencing node is updated.
  421. if ($field['type'] == 'nodereference') {
  422. $node_field = isset($node->$field['field_name']) ? $node->$field['field_name'] : array();
  423. foreach ($node_field as $delta => $item) {
  424. $nids[$item['nid']] = $item['nid'];
  425. }
  426. }
  427. }
  428. }
  429. // Clear Content cache for nodes that reference the node that is being updated.
  430. // This will keep nodereference fields up to date when referred nodes are
  431. // updated. @note this currenlty doesn't work all that well since nodereference
  432. // doesn't respect publishing states or access control.
  433. if (isset($node->nid)) {
  434. $referrers = nodereferrer_referrers($node->nid);
  435. $referrer_nids = array_keys($referrers);
  436. $nids = array_merge($nids, $referrer_nids);
  437. }
  438. foreach ($nids as $nid) {
  439. $cid = "content:$nid:";
  440. // define a table to delete from or else this complains
  441. cache_clear_all($cid, 'cache_content', TRUE);
  442. }
  443. }
  444. }
  445. /**
  446. * Implementation of hook_views_api().
  447. */
  448. function nodereferrer_views_api() {
  449. return array(
  450. 'api' => '2.0',
  451. 'path' => drupal_get_path('module', 'nodereferrer') . '/views',
  452. );
  453. }