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

Contents of /contributions/modules/nodequeue/nodequeue.module

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


Revision 1.97 - (show annotations) (download) (as text)
Sat Oct 24 20:35:08 2009 UTC (5 weeks ago) by ezrag
Branch: MAIN
Changes since 1.96: +3 -3 lines
File MIME type: text/x-php
bug #
Repeated nodequeue_load_queues() in nodequeue_nodeapi() and a php notice. by wulff.
1 <?php
2 // $Id: nodequeue.module,v 1.96 2009/10/24 20:25:47 ezrag Exp $
3
4 /**
5 * @file
6 * Maintains queues of nodes in arbitrary order.
7 */
8
9 define('NODEQUEUE_OK', 0);
10 define('NODEQUEUE_INVALID_POSITION', 1);
11 define('NODEQUEUE_INVALID_NID', 2);
12 define('NODEQUEUE_DUPLICATE_POSITION', 3);
13
14 /* --- HOOKS ---------------------------------------------------------------- */
15
16 /**
17 * Implementation of hook_perm
18 */
19 function nodequeue_perm() {
20 return array('manipulate queues', 'administer nodequeue', 'manipulate all queues');
21 }
22
23 /**
24 * Implementation of hook_init().
25 *
26 * Loads subsidiary includes for other modules.
27 */
28 function nodequeue_init() {
29 include_once drupal_get_path('module', 'nodequeue') .'/includes/nodequeue.actions.inc';
30 }
31
32 /**
33 * Implementation of hook_menu
34 */
35 function nodequeue_menu() {
36 $items = array();
37
38 $admin_access = array('administer nodequeue');
39 $access = array('manipulate queues');
40
41 // administrative items
42 $items['admin/content/nodequeue'] = array(
43 'title' => 'Nodequeue',
44 'access callback' => '_nodequeue_access_admin_or_manipulate',
45 'page callback' => 'nodequeue_view_queues',
46 'description' => 'Create and maintain simple nodequeues.',
47 'type' => MENU_NORMAL_ITEM
48 );
49 $items['admin/content/nodequeue/list'] = array(
50 'title' => 'List',
51 'access callback' => '_nodequeue_access_admin_or_manipulate',
52 'page callback' => 'nodequeue_view_queues',
53 'weight' => -1,
54 'type' => MENU_DEFAULT_LOCAL_TASK
55 );
56 $items['admin/content/nodequeue/settings'] = array(
57 'title' => 'Settings',
58 'access arguments' => $admin_access,
59 'page callback' => 'drupal_get_form',
60 'page arguments' => array('nodequeue_admin_settings'),
61 'type' => MENU_LOCAL_TASK
62 );
63 $items['nodequeue/autocomplete'] = array(
64 'title' => 'Autocomplete',
65 'access arguments' => $access,
66 'page callback' => 'nodequeue_autocomplete',
67 'type' => MENU_CALLBACK
68 );
69 $info = nodequeue_api_info();
70 foreach ($info as $key => $data) {
71 $items['admin/content/nodequeue/add/'. $key] = array(
72 'title' => 'Add @type',
73 'title arguments' => array('@type' => strtolower($data['title'])),
74 'access arguments' => $admin_access,
75 'page callback' => 'drupal_get_form',
76 'page arguments' => array('nodequeue_edit_queue_form', $key),
77 'type' => MENU_LOCAL_TASK
78 );
79 }
80 $items['node/%node/nodequeue'] = array(
81 'title' => '@tab',
82 'title arguments' => array('@tab' => variable_get('nodequeue_tab_name', 'Nodequeue')),
83 'access callback' => 'nodequeue_node_tab_access',
84 'access arguments' => array(1),
85 'page callback' => 'nodequeue_node_tab',
86 'page arguments' => array(1),
87 'type' => MENU_LOCAL_TASK,
88 'weight' => 5
89 );
90
91 // Administrative items for an individual queue.
92 $items['admin/content/nodequeue/%nodequeue'] = array(
93 'access arguments' => array(3),
94 'access callback' => 'nodequeue_queue_access',
95 'page callback' => 'nodequeue_admin_view',
96 'page arguments' => array(3),
97 'type' => MENU_CALLBACK
98 );
99 $items['admin/content/nodequeue/%nodequeue/view/%subqueue'] = array(
100 'title' => 'View',
101 'access arguments' => array(3, 5),
102 'access callback' => 'nodequeue_queue_access',
103 'page callback' => 'nodequeue_admin_view',
104 'page arguments' => array(3, 5),
105 'weight' => -10,
106 'type' => MENU_DEFAULT_LOCAL_TASK
107 );
108 // Actual administrative items.
109 $items['admin/content/nodequeue/%nodequeue/edit'] = array(
110 'title' => 'Edit',
111 'access arguments' => $admin_access,
112 'page callback' => 'drupal_get_form',
113 'page arguments' => array('nodequeue_edit_queue_form', 3),
114 'type' => MENU_LOCAL_TASK
115 );
116 $items['admin/content/nodequeue/%nodequeue/delete'] = array(
117 'title' => 'Delete',
118 'access arguments' => $admin_access,
119 'page callback' => 'drupal_get_form',
120 'page arguments' => array('nodequeue_admin_delete', 3),
121 'weight' => 5,
122 'type' => MENU_CALLBACK
123 );
124 $items["admin/content/nodequeue/%nodequeue/add/%subqueue/%node"] = array(
125 'access arguments' => array(6, 3, 5),
126 'access callback' => 'nodequeue_node_and_queue_access',
127 'page callback' => 'nodequeue_admin_add_node',
128 'page arguments' => array(3, 5, 6),
129 'type' => MENU_CALLBACK
130 );
131 $items["admin/content/nodequeue/%nodequeue/remove-node/%subqueue/%node"] = array(
132 'access arguments' => array(6, 3, 5),
133 'access callback' => 'nodequeue_node_and_queue_access',
134 'page callback' => 'nodequeue_admin_remove_node',
135 'page arguments' => array(3, 5, 6),
136 'type' => MENU_CALLBACK
137 );
138 $items["admin/content/nodequeue/%nodequeue/clear/%subqueue"] = array(
139 'title' => 'Clear',
140 'access arguments' => array(3, 5),
141 'access callback' => 'nodequeue_queue_access',
142 'page callback' => 'drupal_get_form',
143 'page arguments' => array('nodequeue_clear_confirm', 3, 5),
144 'type' => MENU_CALLBACK
145 );
146
147 return $items;
148 }
149 /**
150 * Implementation of hook_nodeapi
151 */
152 function nodequeue_nodeapi(&$node, $op, $teaser = NULL, $page = NULL) {
153 switch ($op) {
154 case 'delete':
155 // If a node is being deleted, ensure it's also removed from any queues.
156 $result = db_query("SELECT qid, sqid FROM {nodequeue_nodes} WHERE nid = %d", $node->nid);
157 while ($obj = db_fetch_object($result)) {
158 // If the queue is being tracked by translation set and the node is part
159 // of a translation set, don't delete the queue record.
160 // Instead, data will be updated in the 'translation_change' op, below.
161 $queues = nodequeue_load_queues(array($obj->qid));
162 $queue = array_shift($queues);
163 if (!$queue->i18n || (isset($node->tnid) && empty($node->tnid))) {
164 // This removes by nid, not position, because if we happen to have a
165 // node in a queue twice, the 2nd position would be wrong.
166 nodequeue_subqueue_remove_node($obj->sqid, $node->nid);
167 }
168 }
169 break;
170 case 'translation_change':
171 if (isset($node->translation_change)) {
172 // If there is only one node remaining, track by nid rather than tnid.
173 // Otherwise, use the new tnid.
174 $content_id = $node->translation_change['new_tnid'] == 0 ? $node->translation_change['remaining_nid'] : $node->translation_change['new_tnid'];
175 db_query("UPDATE {nodequeue_nodes} SET nid = %d WHERE nid = %d", $content_id, $node->translation_change['old_tnid']);
176 }
177 }
178 }
179
180 /**
181 * Implementation of hook_link
182 */
183 function nodequeue_link($type, $node = NULL, $teaser = FALSE) {
184 $links = array();
185
186 if ($type == 'node' &&
187 variable_get('nodequeue_links', FALSE) &&
188 user_access('manipulate queues')) {
189 $queues = nodequeue_load_queues_by_type($node->type, 'links');
190 $subqueues = nodequeue_get_subqueues_by_node($queues, $node);
191 if (empty($subqueues)) {
192 return;
193 }
194
195 // resort the subqueues to retain consistent ordering:
196
197 ksort($subqueues);
198 // Due to caching, we can accidentally get positions leftover
199 // from previous iterations on teaser list pages, so we must
200 // remove any existing positions here.
201 foreach ($subqueues as $id => $subqueue) {
202 unset($subqueues[$id]->position);
203 }
204
205 if(!module_exists('translation')) {
206 nodequeue_set_subqueue_positions($subqueues, $node->nid);
207 }
208
209 foreach ($subqueues as $subqueue) {
210 $queue = $queues[$subqueue->qid];
211 $id = nodequeue_get_content_id($queue, $node);
212 if(module_exists('translation')) {
213 $subqueue = array($subqueue->sqid => $subqueue);
214 nodequeue_set_subqueue_positions($subqueue, $id);
215 $subqueue = array_shift($subqueue);
216 }
217 $query_string = nodequeue_get_query_string($id, TRUE);
218 $class = 'nodequeue-ajax-toggle nodequeue-toggle-q-'. $queue->qid. ' nodequeue-toggle-sq-'. $subqueue->sqid .' nodequeue-toggle-ref-'. $subqueue->reference;
219 if (!isset($subqueue->position)) {
220 $links[$class] = array(
221 'title' => nodequeue_title_substitute($queue->link, $queue, $subqueue),
222 'href' => "admin/content/nodequeue/$queue->qid/add/$subqueue->sqid/$id",
223 'attributes' => array('class' => $class . ' toggle-add'),
224 'query' => $query_string);
225 }
226 else if ($queue->link_remove) {
227 $links[$class] = array(
228 'title' => nodequeue_title_substitute($queue->link_remove, $queue, $subqueue),
229 'href' => "admin/content/nodequeue/$queue->qid/remove-node/$subqueue->sqid/$id",
230 'attributes' => array('class' => $class .' toggle-remove'),
231 'query' => $query_string);
232 }
233 }
234 drupal_add_js(drupal_get_path('module', 'nodequeue') .'/nodequeue.js');
235 drupal_add_css(drupal_get_path('module', 'nodequeue') .'/nodequeue.css');
236 }
237 return $links;
238 }
239
240 /**
241 * Implementation of hook_theme().
242 */
243 function nodequeue_theme() {
244 return array(
245 'nodequeue_arrange_subqueue_form' => array(
246 'arguments' => array('form'),
247 ),
248 'nodequeue_subqueue_empty_text' => array(
249 'arguments' => array(),
250 ),
251 'nodequeue_subqueue_full_text' => array(
252 'arguments' => array(),
253 ),
254 'nodequeue_subqueue_count_text' => array(
255 'arguments' => array(),
256 ),
257 );
258 }
259
260 /**
261 * Implementation of hook_elements().
262 */
263 function nodequeue_elements() {
264 $type = array();
265
266 $type['position'] = array(
267 '#input' => TRUE,
268 '#delta' => 10,
269 '#default_value' => 0,
270 '#process' => array('process_position', 'form_expand_ahah'),
271 );
272
273 return $type;
274 }
275
276 /**
277 * Expand position elements into selects. Works like the weight element, except
278 * only positive values are allowed.
279 */
280 function process_position($element) {
281 for ($n = 1; $n <= $element['#delta']; $n++) {
282 $positions[$n] = $n;
283 }
284
285 $element['#options'] = $positions;
286 $element['#options']['r'] = t('Remove');
287 $element['#type'] = 'select';
288
289 // add default properties for the select element
290 $element += _element_info('select');
291
292 return $element;
293 }
294
295 /**
296 * If no default value is set for position select boxes, use 1.
297 */
298 function position_value(&$form) {
299 if (isset($form['#default_value'])) {
300 $form['#value'] = $form['#default_value'];
301 }
302 else {
303 $form['#value'] = 1;
304 }
305 }
306
307 /**
308 * Implementation of hook_views_api()
309 */
310 function nodequeue_views_api() {
311 return array(
312 'api' => 2,
313 'path' => drupal_get_path('module', 'nodequeue') .'/includes/views',
314 );
315 }
316
317 /**
318 * Implementation of hook_form_$form-id_alter().
319 */
320 function nodequeue_form_apachesolr_search_bias_form_alter(&$form, $form_state) {
321 // setup for the form building
322 $weights = drupal_map_assoc(array('21.0', '13.0', '8.0', '5.0', '3.0', '2.0', '1.0', '0.8', '0.5', '0.3', '0.2', '0.1'));
323 $weights['0'] = t('Normal');
324 $queues = nodequeue_load_subqueues_by_queue(array_keys(nodequeue_get_all_qids()));
325
326 // build the form
327 $form['biasing']['nodequeue_boost'] = array(
328 '#type' => 'fieldset',
329 '#title' => t('Nodequeue Biasing'),
330 '#weight' => -5,
331 '#collapsible' => TRUE,
332 '#collapsed' => TRUE,
333 );
334 $form['biasing']['nodequeue_boost']['nodequeue_apachesolr_boost'] = array(
335 '#type' => 'item',
336 '#description' => t("Specify to bias the search result when a node is in a queue. Any value except <em>Normal</em> will increase the socre for the given queue in the search results"),
337 );
338 foreach ($queues as $sqid => $queue) {
339 $boost = variable_get("nodequeue_apachesolr_boost_$sqid", 0);
340 // add in setting for each queue
341 $form['biasing']['nodequeue_boost']['nodequeue_apachesolr_boost']["nodequeue_apachesolr_boost_$sqid"] = array(
342 '#type' => 'select',
343 '#title' => t('Weight for %title nodequeue', array('%title' => $queue->title)),
344 '#options' => $weights,
345 '#default_value' => $boost,
346 );
347 }
348 }
349
350 /**
351 * Implementation of hook_apachesolr_update_index().
352 */
353 function nodequeue_apachesolr_update_index(&$document, $node) {
354 $queues = nodequeue_load_queues(array_keys(nodequeue_get_all_qids()));
355 $subqueues = nodequeue_get_subqueues_by_node($queues, $node);
356
357 nodequeue_set_subqueue_positions($subqueues, $node->nid);
358 if (is_array($subqueues)) {
359 foreach ($subqueues as $sqid => $subqueue) {
360 if (!empty($subqueue->position)) {
361 $key = _nodequeue_solr_qid_key();
362 $document->setMultiValue($key, $sqid);
363 }
364 }
365 }
366 }
367
368 /**
369 * return the apachesolr index key for group id
370 */
371 function _nodequeue_solr_qid_key() {
372 $qid_key = array(
373 'index_type' => 'sint',
374 'multiple' => TRUE,
375 'name' => "nodequeue",
376 );
377
378 return apachesolr_index_key($qid_key);
379 }
380
381 /**
382 * Implementation of hook_apachesolr_modify_query().
383 */
384 function nodequeue_apachesolr_modify_query($query, &$params, $caller) {
385 $queues = nodequeue_load_subqueues_by_queue(array_keys(nodequeue_get_all_qids()));
386 $added = FALSE;
387 foreach ($queues as $sqid => $queue) {
388 $boost = variable_get("nodequeue_apachesolr_boost_$sqid", 0);
389 if (!empty($boost)) {
390 $params['bq'][] = _nodequeue_solr_qid_key() . ":$sqid^$boost";
391 if (!$added) {
392 // Only want to add the facet.field once. no need to repeat it
393 $params['facet.field'][] = _nodequeue_solr_qid_key();
394 $added = TRUE;
395 }
396 }
397 }
398 }
399
400 // --------------------------------------------------------------------------
401 // Nodequeue Admin operations
402
403 /**
404 * Print the JSON output for our AJAX calls.
405 */
406 function nodequeue_js_output($label, $href, $count = NULL, $sqid = NULL) {
407 $return = new stdClass();
408 $return->status = 1;
409 $return->label = check_plain($label);
410 $return->href = $href;
411 if (isset($count)) {
412 $return->count = $count;
413 }
414 if (isset($sqid)) {
415 $return->sqid = $sqid;
416 }
417
418 drupal_json($return);
419 exit;
420 }
421
422 /**
423 * Return content id based on i18n settings
424 */
425 function nodequeue_get_content_id($queue, $node) {
426 return ($queue->i18n && !empty($node->tnid)) ? $node->tnid : $node->nid;
427 }
428
429 /**
430 * Page callback to add a node to a queue.
431 */
432 function nodequeue_admin_add_node($queue, $subqueue, $node) {
433 if (!nodequeue_check_token($node->nid)) {
434 return drupal_goto();
435 }
436 $id = nodequeue_get_content_id($queue, $node);
437 nodequeue_subqueue_add($queue, $subqueue, $id);
438
439 // Provide a response if this is javascript.
440 if (!empty($_POST['js'])) {
441 if (isset($_GET['tab'])) {
442 nodequeue_js_output(t('Remove from queue'),
443 url("admin/content/nodequeue/$queue->qid/remove-node/$subqueue->sqid/$node->nid", array('query' => nodequeue_get_query_string($node->nid, TRUE, array('tab')))),
444 nodequeue_subqueue_size_text($queue->size, $queue->size ? min($subqueue->count, $queue->size) : $subqueue->count, FALSE),
445 $subqueue->sqid);
446 }
447 else {
448 nodequeue_js_output(nodequeue_title_substitute($queue->link_remove, $queue, $subqueue),
449 url("admin/content/nodequeue/$queue->qid/remove-node/$subqueue->sqid/$node->nid", array('query' => nodequeue_get_query_string($node->nid, TRUE))));
450 }
451 }
452
453 // There should always be a destination set for this, so just goto wherever.
454 drupal_goto();
455 }
456
457 /**
458 * Page callback to remove a node from a queue.
459 */
460 function nodequeue_admin_remove_node($queue, $subqueue, $node) {
461 if (!nodequeue_check_token($node->nid)) {
462 return drupal_goto();
463 }
464 $id = nodequeue_get_content_id($queue, $node);
465 nodequeue_subqueue_remove_node($subqueue->sqid, $id);
466
467 // Provide a response if this is javascript.
468 if (!empty($_POST['js'])) {
469 if (isset($_GET['tab'])) {
470 nodequeue_js_output(t('Add to queue'),
471 url("admin/content/nodequeue/$queue->qid/add/$subqueue->sqid/$node->nid", array('query' => nodequeue_get_query_string($node->nid, TRUE, array('tab')))),
472 nodequeue_subqueue_size_text($queue->size, $subqueue->count - 1, FALSE),
473 $subqueue->sqid);
474 }
475 else {
476 nodequeue_js_output(nodequeue_title_substitute($queue->link, $queue, $subqueue),
477 url("admin/content/nodequeue/$queue->qid/add/$subqueue->sqid/$node->nid", array('query' => nodequeue_get_query_string($node->nid, TRUE))));
478 }
479 }
480
481 // There should always be a destination set for this, so just goto wherever.
482 drupal_goto();
483 }
484
485 /**
486 * Display the queue controls for a node.
487 *
488 * @param $node
489 * The loaded $node; will be loaded by the hook_menu.
490 */
491 function nodequeue_node_tab($node) {
492 $output = '';
493
494 // moved from hook_menu due to architecture change. This function seems to only be called from menu anyway...
495 $queues = nodequeue_load_queues_by_type($node->type, 'tab');
496 if (!$queues) {
497 return FALSE;
498 }
499 $header = array();
500 $header[] = array('data' => t('Title'), 'class' => 'nodequeue-title');
501 if (variable_get('nodequeue_tab_display_max', 1)) {
502 $header[] = array('data' => t('Max nodes'), 'class' => 'nodequeue-max-nodes');
503 }
504 $header[] = array('data' => t('In queue'), 'class' => 'nodequeue-in-queue');
505 $header[] = array('data' => t('Operation'), 'class' => 'nodequeue-operation');
506 $subqueues = nodequeue_get_subqueues_by_node($queues, $node);
507
508 nodequeue_set_subqueue_positions($subqueues, $node->nid);
509
510 $rows = array();
511 foreach ($subqueues as $subqueue) {
512 $queue = $queues[$subqueue->qid];
513 if (empty($subqueue->position)) {
514 $op = l(
515 t('Add to queue'),
516 "admin/content/nodequeue/$queue->qid/add/$subqueue->sqid/$node->nid",
517 array('attributes' => array('class' => 'nodequeue-ajax-toggle'),
518 'query' => drupal_get_destination() .'&tab&'. nodequeue_get_token($node->nid))
519 );
520 }
521 else {
522 $op = l(
523 t('Remove from queue'),
524 "admin/content/nodequeue/$queue->qid/remove-node/$subqueue->sqid/$node->nid",
525 array('attributes' => array('class' => 'nodequeue-ajax-toggle'),
526 'query' => drupal_get_destination() .'&tab&'. nodequeue_get_token($node->nid))
527 );
528 }
529 $row = array();
530 $row[] = array(
531 'class' => 'nodequeue-title',
532 'data' => l(nodequeue_title_substitute($queue->subqueue_title, $queue, $subqueue), "admin/content/nodequeue/$queue->qid/view/$subqueue->sqid"),
533 );
534 if (variable_get('nodequeue_tab_display_max', 1)) {
535 $row[] = array('class' => 'nodequeue-max-nodes', 'data' => $queue->size ? $queue->size : t('Infinite'));
536 }
537 $row[] = array(
538 'id' => 'nodequeue-count-'. $subqueue->sqid,
539 'class' => 'nodequeue-in-queue',
540 'data' => nodequeue_subqueue_size_text($queue->size, $subqueue->count, FALSE)
541 );
542 $row[] = array('class' => 'nodequeue-operation', 'data' => $op);
543 $rows[] = $row;
544 }
545
546 $output .= theme('table', $header, $rows, array('class' => 'nodequeue-table'));
547 drupal_add_js(drupal_get_path('module', 'nodequeue') .'/nodequeue.js');
548 drupal_add_css(drupal_get_path('module', 'nodequeue') .'/nodequeue.css');
549 return $output;
550 }
551
552 /**
553 * Display a list of queues and their status for the administrator.
554 */
555 function nodequeue_view_queues() {
556 $output = theme('advanced_help_topic', 'nodequeue', 'about', 'icon') . '&nbsp;' . theme('advanced_help_topic', 'nodequeue', 'about', t('Click here for information about this module'));
557 // Fetch all of the queues.
558 $queues = nodequeue_load_queues(nodequeue_get_all_qids(25));
559 foreach ($queues as $queue) {
560 if (!nodequeue_queue_access($queue)) {
561 unset($queues[$queue->qid]);
562 }
563 }
564
565 if (empty($queues)) {
566 return $output . t('No nodequeues exist.');
567 }
568
569 $header = array(
570 array('data' => t('Title'), 'field' => 'title', 'sort' => 'asc'),
571 array('data' => t('Max nodes'), 'field' => 'size'),
572 array('data' => t('Subqueues'), 'field' => 'subqueues'),
573 array('data' => t('Operation')),
574 );
575 $table_sort = tablesort_init($header);
576
577 $qids = array();
578 $sort_primary = array();
579 $sort_secondary = array();
580 $sort_direction_regular = array('asc' => SORT_ASC, 'desc' => SORT_DESC);
581 $sort_direction_reverse = array('asc' => SORT_DESC, 'desc' => SORT_ASC);
582 foreach ($queues as $queue) {
583 // If a queue has only one subqueue, store the qid so we can display
584 // the number of nodes in the subqueue.
585 if ($queue->subqueues == 1) {
586 $qids[] = $queue->qid;
587 }
588 $sort_secondary[] = $queue->title;
589 switch ($table_sort['sql']) {
590 case 'title':
591 default:
592 $sort_primary[] = $queue->title;
593 $sort_direction = $sort_direction_regular;
594 break;
595 case 'size':
596 $sort_primary[] = $queue->size;
597 $sort_direction = $sort_direction_reverse;
598 break;
599 case 'subqueues':
600 $sort_primary[] = $queue->subqueues;
601 $sort_direction = $sort_direction_regular;
602 break;
603 }
604 }
605
606 $subqueues = nodequeue_load_subqueues_by_queue($qids);
607 // Relate all the subqueues we loaded back to our queues.
608 foreach ($subqueues as $subqueue) {
609 if (nodequeue_api_subqueue_access($subqueue, NULL, $queues[$subqueue->qid])) {
610 $queues[$subqueue->qid]->subqueue = $subqueue;
611 }
612 }
613
614 if (!empty($table_sort)) {
615 if (strtolower($table_sort['sort']) == 'desc') {
616 array_multisort($sort_primary, $sort_direction['desc'], $sort_secondary, $queues); // Re-indexes array keys; key no longer equals qid.
617 }
618 else {
619 array_multisort($sort_primary, $sort_direction['asc'], $sort_secondary, $queues); // Re-indexes array keys; key no longer equals qid.
620 }
621 }
622
623 $rows = array();
624 foreach ($queues as $queue) {
625 $sub_text = $queue->subqueues;
626 if ($sub_text == 1) {
627 $sub_text .= " (". nodequeue_subqueue_size_text($queue->size, $queue->subqueue->count) .")";
628 }
629
630 $operations = array(l(t('View'), "admin/content/nodequeue/$queue->qid/view"));
631 if (user_access('administer nodequeue')) {
632 $operations[] = l(t('Edit'), "admin/content/nodequeue/$queue->qid/edit");
633 $operations[] = l(t('Delete'), "admin/content/nodequeue/$queue->qid/delete");
634 }
635
636 $rows[] = array(
637 array('class' => 'nodequeue-title', 'data' => check_plain($queue->title)),
638 array('class' => 'nodequeue-max-nodes', 'data' => $queue->size == 0 ? t('Infinite') : $queue->size),
639 array('class' => 'nodequeue-subqueues', 'data' => $sub_text),
640 array('class' => 'nodequeue-operation', 'data' => implode(' | ', $operations)),
641 );
642 }
643
644 $output .= theme('table', $header, $rows);
645 $output .= theme('pager', NULL, 25);
646
647 return $output;
648 }
649
650 /**
651 * Display a list of subqueues for a queue and their sizes
652 */
653 function nodequeue_view_subqueues($queue) {
654 // Fetch all of the subqueues.
655 $subqueues = nodequeue_load_subqueues_by_queue($queue->qid);
656
657 $header = array(t('Title'), t('In queue'), t('Operation'));
658
659 $rows = array();
660 foreach ($subqueues as $subqueue) {
661 if (nodequeue_api_subqueue_access($subqueue, NULL, $queue)) {
662 $sub_text = nodequeue_subqueue_size_text($queue->size, $subqueue->count, FALSE);
663 $rows[] = array(
664 array('class' => 'nodequeue-title', 'data' => check_plain($subqueue->title)),
665 array('class' => 'nodequeue-subqueues', 'data' => $sub_text),
666 array('class' => 'nodequeue-operation', 'data' => l(t('View'), "admin/content/nodequeue/$queue->qid/view/$subqueue->sqid"))
667 );
668 }
669 }
670
671 $output = '<p>'. t('Max nodes in queue: @size', array('@size' => $queue->size ? $queue->size : t("Infinite"))) .'</p>';
672 $output .= theme('table', $header, $rows);
673 $output .= theme('pager', NULL, 20);
674
675 return $output;
676 }
677
678 /**
679 * Add or edit a queue.
680 */
681 function nodequeue_edit_queue_form(&$form_state, $queue) {
682 $info = nodequeue_api_info();
683
684 // For adding queues.
685 if (is_string($queue)) {
686 // If the $queue is a string - name of a queue type, basically - then we test that it's a valid queue type.
687 $queue = strtolower($queue);
688 if (!isset($info[$queue])) {
689 return false;
690 }
691 drupal_set_title(t('Add @type', array('@type' => strtolower($info[$queue]['title']))));
692 $queue = new nodequeue_queue($queue);
693 }
694 else {
695 drupal_set_title(t("Nodequeue '@title'", array('@title' => $queue->title)));
696 }
697
698 $form['description'] = array(
699 '#type' => 'fieldset',
700 '#title' => $info[$queue->owner]['title'],
701 '#description' => $info[$queue->owner]['description'],
702 );
703
704 $form['title'] = array(
705 '#type' => 'textfield',
706 '#title' => t('Title'),
707 '#default_value' => $queue->title,
708 '#size' => 50,
709 '#maxlength' => 64,
710 '#description' => t('Enter the name of the queue'),
711 );
712
713 // This is a value; as the default nodequeue implementation has just one
714 // queue per nodequeue, this field is totally redundant. Plugins can
715 // override this.
716 $form['subqueue_title'] = array(
717 '#type' => 'value',
718 '#value' => $queue->subqueue_title,
719 );
720 // The placeholder is put here so that modifiers have an easy way to put
721 // additional form widgets in a prominent spot but not before the title of
722 // the queue.
723 $form['placeholder'] = array();
724
725 $form['size'] = array(
726 '#type' => 'textfield',
727 '#title' => t('Queue size'),
728 '#default_value' => $queue->size,
729 '#size' => 2,
730 '#maxlength' => 2,
731 '#description' => t('The maximum number of nodes will appear in the queue. Enter 0 for no limit'),
732 );
733
734 $form['reverse'] = array(
735 '#type' => 'checkbox',
736 '#title' => t('Reverse in admin view'),
737 '#default_value' => $queue->reverse,
738 '#description' => t('Ordinarily queues are arranged with the front of the queue (where items will be removed) on top and the back (where items will be added) on the bottom. If checked, this will display the queue such that items will be added to the top and removed from the bottom.'),
739 );
740
741 $form['link'] = array(
742 '#type' => 'textfield',
743 '#title' => t('Link "add to queue" text'),
744 '#default_value' => $queue->link,
745 '#size' => 40,
746 '#maxlength' => 40,
747 '#description' => t('If you want a link to add a node to a queue in the "links" section (next to "add new comment"), enter the text here. If left blank no link will be given; note that enabling this feature for any queue will cause an extra query to be run every time a node is loaded. "%subqueue" will be replaced with the subqueue title, if applicable.'),
748 );
749
750 $form['link_remove'] = array(
751 '#type' => 'textfield',
752 '#title' => t('Link "remove from queue" text'),
753 '#default_value' => $queue->link_remove,
754 '#size' => 40,
755 '#maxlength' => 40,
756 '#description' => t('Enter the text for the corresponding link to remove a node from a queue. This may be blank (in which case no link will appear) but a remove link will only appear if link, above, is set.'),
757 );
758
759
760 $result = db_query("SELECT r.* FROM {role} r LEFT JOIN {permission} p ON p.rid = r.rid WHERE p.perm LIKE '%manipulate queues%' ORDER BY r.name");
761
762 $roles = array();
763 while ($role = db_fetch_object($result)) {
764 $roles[$role->rid] = $role->name;
765 }
766
767 if (!empty($roles)) {
768 $form['roles'] = array(
769 '#type' => 'checkboxes',
770 '#title' => t('Roles'),
771 '#default_value' => is_array($queue->roles) ? $queue->roles : array(),
772 '#options' => $roles,
773 '#description' => t('Check each role that can add nodes to the queue. Be sure that roles you want to appear here have "manipulate queues" access in the main access control panel.'),
774 );
775 }
776 else {
777 $form['roles'] = array(
778 '#type' => 'value',
779 '#value' => array(),
780 );
781
782 $form['roles_markup'] = array(
783 '#value' => t('No roles have the "manipulate queues" permission, so only the administrator account will be able to add or remove items from this queue.'),
784 );
785 }
786
787 $nodes = node_get_types('names');
788
789 $form['types'] = array(
790 '#type' => 'checkboxes',
791 '#title' => t('Types'),
792 '#default_value' => $queue->types,
793 '#options' => $nodes,
794 '#description' => t('Check each node type that can be added to this queue.'),
795 );
796
797 $form['i18n'] = array(
798 '#type' => 'radios',
799 '#title' => t('Internationalization'),
800 '#options' => array(
801 '1' => t('Treat translation nodes as a single node'),
802 '0' => t('Manually manage translated nodes'),
803 ),
804 '#default_value' => empty($queue->qid) && module_exists('translation_helpers') ? 0 : $queue->i18n,
805 '#description' => t('Treating translations as a single node allows users to add, remove and manipulate a node
806 in the queue regardless of which translation is acted upon; nodequeue will only act on the original node.
807 When manually managing translation nodes, Nodequeue will ignore the relationship between node translations;
808 each translation node must be added to the queue separately and will occupy a separate spot in the queue.
809 Changing this setting will <strong>not</strong> update content that is already in the queue.'),
810 '#access' => module_exists('translation_helpers'),
811 );
812
813 $form['submit'] = array(
814 '#type' => 'submit',
815 '#value' => t('Submit'),
816 );
817
818 $form['owner'] = array(
819 '#type' => 'value',
820 '#value' => $queue->owner,
821 );
822
823 $form['show_in_links'] = array(
824 '#type' => 'value',
825 '#value' => $queue->show_in_links,
826 );
827
828 $form['show_in_tab'] = array(
829 '#type' => 'value',
830 '#value' => $queue->show_in_tab,
831 );
832
833 $form['show_in_ui'] = array(
834 '#type' => 'value',
835 '#value' => $queue->show_in_ui,
836 );
837
838 $form['reference'] = array(
839 '#type' => 'value',
840 '#value' => $queue->reference,
841 );
842
843 $form['subqueues'] = array(
844 '#type' => 'value',
845 '#value' => $queue->subqueues,
846 );
847
848 if (isset($queue->qid)) {
849 $form[] = array(
850 '#type' => 'submit',
851 '#value' => t('Delete'),
852 '#validate' => array('nodequeue_edit_queue_form_delete_validate'),
853 '#submit' => array('nodequeue_edit_queue_form_delete_submit'),
854 );
855 $form['qid'] = array(
856 '#type' => 'value',
857 '#value' => $queue->qid,
858 );
859 $form['count'] = array(
860 '#type' => 'value',
861 '#value' => $queue->count,
862 );
863 }
864
865 nodequeue_api_queue_form($queue, $form);
866
867 return $form;
868 }
869
870 /**
871 * Validate function for the nodequeue_queue form.
872 */
873 function nodequeue_edit_queue_form_validate($form, &$form_state) {
874 if (empty($form_state['values']['title'])) {
875 form_set_error('title', t('Please enter a title for this queue.'));
876 }
877 $queue = (object) $form_state['values'];
878 // fix checkboxes
879 $queue->roles = array_keys(array_filter($queue->roles));
880 $queue->types = array_keys(array_filter($queue->types));
881
882 if (!isset($queue->qid)) {
883 $queue->new = TRUE;
884 }
885
886 nodequeue_api_queue_form_validate($queue, $form_state, $form);
887 }
888
889 /**
890 * Submit function for the nodequeue_queue form.
891 */
892 function nodequeue_edit_queue_form_submit($formid, &$form_state) {
893 $queue = (object) $form_state['values'];
894 // fix checkboxes
895 $queue->roles = array_keys(array_filter($queue->roles));
896 $queue->types = array_keys(array_filter($queue->types));
897
898 if (!isset($queue->qid)) {
899 $queue->new = TRUE;
900 }
901
902 // Modify show_in_links based on whether or not links are available.
903 $queue->show_in_links = !empty($queue->link) || !empty($queue->link_remove);
904
905 nodequeue_api_queue_form_submit($queue, $form_state);
906
907 $qid = nodequeue_save($queue); // sets $queue->qid if needed.
908
909 nodequeue_api_queue_form_submit_finish($queue, $form_state);
910
911 nodequeue_check_subqueue_sizes($queue);
912
913 if ($queue->new) {
914 $form_state['values']['qid'] = $qid;
915 drupal_set_message(t('The queue has been created.'));
916 }
917 else {
918 drupal_set_message(t('The queue has been updated.'));
919 }
920 $form_state['redirect'] = 'admin/content/nodequeue';
921 }
922
923 /**
924 * Delete-validate function for the nodequeue_queue form.
925 */
926 function nodequeue_edit_queue_form_delete_validate($form, &$form_state) {
927 // No validation for delete step! But we need to have this so the default validation isn't called.
928 }
929
930 /**
931 * Delete-submit function for the nodequeue_queue form.
932 */
933 function nodequeue_edit_queue_form_delete_submit($formid, &$form_state) {
934 $form_state['redirect'] = "admin/content/nodequeue/". $form_state['values']['qid'] ."/delete";
935 }
936
937 /**
938 * Confirm form to delete a queue
939 */
940 function nodequeue_admin_delete(&$form_state, $queue) {
941 $form['qid'] = array('#type' => 'value', '#value' => $queue->qid);
942 return confirm_form($form,
943 t('Are you sure you want to delete "%title"?', array('%title' => $queue->title)),
944 isset($_GET['destination']) ? $_GET['destination'] : 'admin/content/nodequeue',
945 t('This action cannot be undone.'),
946 t('Delete'), t('Cancel')
947 );
948 }
949
950 /**
951 * Submit function for nodequeue delete
952 */
953 function nodequeue_admin_delete_submit($formid, &$form_state) {
954 if ($form_state['values']['confirm']) {
955 nodequeue_delete($form_state['values']['qid']);
956 drupal_set_message(t('The queue has been deleted.'));
957 }
958 $form_state['redirect'] = 'admin/content/nodequeue';
959 }
960
961 /**
962 * Page callback to view a queue.
963 */
964 function nodequeue_admin_view($queue, $subqueue = NULL) {
965 drupal_set_title(t("Nodequeue '@title'", array('@title' => $queue->title)));
966 $qid = $queue->qid;
967
968 // If the queue has just one subqueue, it gets special treatment.
969 if (empty($subqueue->sqid)) {
970 if ($queue->subqueues == 1) {
971 $subqueues = nodequeue_load_subqueues_by_queue($queue->qid);
972 $subqueue = array_shift($subqueues);
973 }
974 else {
975 // display subqueue list page.
976 return nodequeue_view_subqueues($queue);
977 }
978 }
979 else if ($subqueue->sqid) {
980 if (!nodequeue_api_subqueue_access($subqueue, NULL, $queue)) {
981 return drupal_not_found();
982 }
983 // Adjust properties of the page so our subqueue is in the right
984 // visual place.
985 drupal_set_title(t("Subqueue '@title'",
986 array('@title' => nodequeue_title_substitute($queue->subqueue_title, $queue, $subqueue))));
987 $breadcrumb = drupal_get_breadcrumb();
988 $breadcrumb[] = l($queue->title, "admin/content/nodequeue/$queue->qid");
989 drupal_set_breadcrumb($breadcrumb);
990 }
991 return nodequeue_arrange_subqueue($queue, $subqueue);
992 }
993
994 /**
995 * View the contents of a subqueue, with links to re-order the queue.
996 */
997 function nodequeue_arrange_subqueue($queue, $subqueue = NULL) {
998 // set title and load subqueue if it's not provided
999 drupal_set_title(t("Nodequeue '@title'", array('@title' => $queue->title)));
1000 if (!$subqueue->sqid) {
1001 if ($queue->subqueues == 1) {
1002 $subqueues = nodequeue_load_subqueues_by_queue($queue->qid);
1003 $subqueue = array_shift($subqueues);
1004 }
1005 else {
1006 return drupal_not_found();
1007 }
1008 }
1009 else if ($subqueue->sqid) {
1010 if (!nodequeue_api_subqueue_access($subqueue, NULL, $queue)) {
1011 return drupal_not_found();
1012 }
1013 drupal_set_title(t("Subqueue '@title'", array('@title' => nodequeue_title_substitute($queue->subqueue_title, $queue, $subqueue))));
1014 }
1015
1016 // get nodes from the queue
1017 $nodes = _nodequeue_dragdrop_get_nodes($queue, $subqueue);
1018
1019 return drupal_get_form('nodequeue_arrange_subqueue_form', $queue, $nodes, $subqueue);
1020 }
1021
1022 /**
1023 * Return a list of nodes in a specific subqueue.
1024 */
1025 function _nodequeue_dragdrop_get_nodes($queue, $subqueue) {
1026 $order = $queue->reverse ? 'DESC' : 'ASC';
1027
1028 $visible = nodequeue_nids_visible($subqueue->sqid);
1029
1030 // get a list of all nodes in the subqueue, regardless of access restrictions
1031 $result = db_query('SELECT DISTINCT(n.nid), n.title, n.uid, u.name, n.created, nq.position FROM {node} n LEFT JOIN {users} u on n.uid = u.uid LEFT JOIN {nodequeue_nodes} nq ON nq.nid = n.nid WHERE nq.sqid = %d ORDER BY nq.position '. $order, $subqueue->sqid);
1032
1033 $nodes = array();
1034 while ($node = db_fetch_object($result)) {
1035 $node->visible = isset($visible[$node->nid]) ? TRUE : FALSE;
1036 $nodes[] = $node;
1037 }
1038
1039 return $nodes;
1040 }
1041
1042 /**
1043 * Form definition for nodequeue drag'n'drop form.
1044 */
1045 function nodequeue_arrange_subqueue_form($form_state, $queue, $nodes, $subqueue) {
1046 $form = array('#tree' => TRUE);
1047
1048 // save queue and subqueue objects for later use
1049 $form['#queue'] = (array) $queue;
1050 $form['#subqueue'] = (array) $subqueue;
1051
1052 // prepare the main part of the form which will be themed as a table
1053 $count = count($nodes);
1054 foreach ($nodes as $node) {
1055 $form[$node->nid]['#node'] = (array) $node;
1056 if ($node->visible) {
1057 $form[$node->nid]['#node'] = (array) $node;
1058 $form[$node->nid]['title'] = array('#value' => l($node->title, 'node/'. $node->nid));
1059 $form[$node->nid]['author'] = array('#value' => theme('username', $node));
1060 $form[$node->nid]['date'] = array('#value' => format_date($node->created, 'small'));
1061 }
1062 else {
1063 $form[$node->nid]['title'] = array('#value' => t('Restricted node, NID: @nid', array('@nid' => $node->nid)));
1064 $form[$node->nid]['author'] = array('#value' => '');
1065 $form[$node->nid]['date'] = array('#value' => '');
1066 }
1067
1068 $form[$node->nid]['edit'] = array('#value' => l(t('edit'), 'node/'. $node->nid .'/edit', array('attributes' => array('title' => t('Edit this node')))));
1069 $form[$node->nid]['position'] = array(
1070 '#type' => 'position',
1071 '#delta' => $count,
1072 '#default_value' => $node->position,
1073 '#attributes' => array(
1074 'class' => 'node-position',
1075 ),
1076 );
1077
1078 $attr = array(
1079 'attributes' => array(
1080 'title' => t('Remove from queue'),
1081 'style' => 'display: none;',
1082 'class' => 'nodequeue-remove',
1083 'id' => 'nodequeue-remove-'. $node->nid,
1084 ),
1085 );
1086 $form[$node->nid]['remove'] = array('#value' => l(t('remove'), '', $attr));
1087 }
1088
1089 // add a textfield for adding nodes to the queue
1090 $form['add'] = array(
1091 '#type' => 'markup',
1092 );
1093 $form['add']['nid'] = array(
1094 '#type' => 'textfield',
1095 '#autocomplete_path' => 'nodequeue/autocomplete/'.