/[drupal]/contributions/modules/nodereferrer/nodereferrer.module
ViewVC logotype

Contents of /contributions/modules/nodereferrer/nodereferrer.module

Parent Directory Parent Directory | Revision Log Revision Log | View Revision Graph Revision Graph


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

  ViewVC Help
Powered by ViewVC 1.1.2