nodequeue.module

Tracking 5.x-2.x branch
  1. drupal
    1. 5 contributions/nodequeue/nodequeue.module
    2. 6 contributions/nodequeue/nodequeue.module
    3. 7 contributions/nodequeue/nodequeue.module

Functions & methods

NameDescription
nodequeue_add_subqueueAdd a new subqueue to a queue.
nodequeue_admin_add_nodePage callback to add a node to a queue.
nodequeue_admin_backPage callback to move an item to the back of a queue. This will be used only if javascript is disabled in the client, and is a fallback technique.
nodequeue_admin_deleteConfirm form to delete a queue
nodequeue_admin_delete_submitSubmit function for nodequeue delete
nodequeue_admin_downPage callback to move an item down in a queue. This will be used only if javascript is disabled in the client, and is a fallback technique.
nodequeue_admin_frontPage callback to move an item to the front of a queue. This will be used only if javascript is disabled in the client, and is a fallback technique.
nodequeue_admin_removePage callback to remove an item from a queue. This will be used only if javascript is disabled in the client, and is a fallback technique. This differs from nodequeue_admin_remove_node in that it removes a specific position, which is necessary in case…
nodequeue_admin_remove_nodePage callback to remove a node from a queue.
nodequeue_admin_settingsForm builder for the nodequeue settings tab.
nodequeue_admin_upPage callback to move an item up in a queue. This will be used only if javascript is disabled in the client, and is a fallback technique.
nodequeue_admin_viewPage callback to view a queue.
nodequeue_ajax_addPage callback to ajaxily add a node.
nodequeue_api_autocompleteFetch a list of nodes available to a given subqueue for autocomplete.
nodequeue_api_infoCollect info about all of the possible nodequeue tyeps from owning modules.
nodequeue_api_queue_accessAllows the owning module of a queue to restrict access to viewing and manipulating the queue.
nodequeue_api_queue_formSend the nodequeue edit form to the owning module for modification.
nodequeue_api_queue_form_submitSend the nodequeue edit form to the owning module upon submit.
nodequeue_api_queue_form_submit_finishSend the nodequeue edit form to the owning module after the queue has been saved.
nodequeue_api_queue_form_validateValidate the nodequeue edit form.
nodequeue_api_subqueuesFetch a list of subqueues that are valid for this node from the owning module.
nodequeue_api_subqueue_accessAllows the owning module of a subqueue to restrict access to viewing and manipulating the queue.
nodequeue_arrange_subqueueView the contents of a subqueue, with links to re-order the queue.
nodequeue_arrange_subqueue_entry
nodequeue_arrange_subqueue_formForm used for arranging a queue
nodequeue_arrange_subqueue_form_submitSubmit function for nodequeue_arrange_subqueue_form
nodequeue_arrange_subqueue_form_validateValidate handler for nodequeue_arrange_subqueue_form
nodequeue_autocompletePage callback for autocomplete.
nodequeue_check_subqueue_sizeGuarantee that a subqueue has not gotten too big. It's important to call this after an operation that might have reduced a queue's maximum size. It stores the count to save a query if this is to be followed by an add operation.
nodequeue_check_subqueue_sizesGuarantee that all subqueues are within the size constraints set by $queue->size.
nodequeue_check_tokenCheck to see if the token generated from seed matches.
nodequeue_clear_confirmConfirm form to clear a queue.
nodequeue_clear_confirm_submitSubmit function for nodequeue clear confirm
nodequeue_deleteDelete a nodequeue.
nodequeue_edit_queue_formAdd or edit a queue.
nodequeue_edit_queue_form_submitSubmit function for the nodequeue_queue form.
nodequeue_edit_queue_form_validateSubmit function for the nodequeue_queue form.
nodequeue_fetch_back
nodequeue_fetch_front
nodequeue_fetch_random
nodequeue_filter_qidsFilter a list of qids returned by nodequeue_get_qids to a location.
nodequeue_get_all_qidsGet an array all qids using the pager query. This administrative list does no permission checking, so should only be available to users who have passed the 'administer queues' check.
nodequeue_get_qidsGet an array of qids applicable to this node type.
nodequeue_get_query_stringGenerate a query string to use on nodequeue's private links.
nodequeue_get_subqueues_by_nodeGet a list of valid subqueues for a node, along with the position of the node.
nodequeue_get_subqueue_positionGet the position of a node in a subqueue, or 0 if not found.
nodequeue_get_tokenGet a private token used to protect nodequeue's links from spoofing.
nodequeue_initImplementation of hook_init().
nodequeue_js_outputPrint the JSON output for our AJAX calls.
nodequeue_linkImplementation of hook_link
nodequeue_loadLoad a nodequeue.
nodequeue_load_queuesLoad an array of $qids.
nodequeue_load_queues_by_typeFetch a list of available queues for a given location. These queues will be fully loaded and ready to go.
nodequeue_load_subqueueLoad a single subqueue.
nodequeue_load_subqueuesLoad a list of subqueues
nodequeue_load_subqueues_by_queueLoad the entire set of subqueues for a queue.
nodequeue_load_subqueues_by_referenceLoad a set of subqueues by reference.
nodequeue_menuImplementation of hook_menu
nodequeue_nids_visible
nodequeue_nodeapiImplementation of hook_nodeapi
nodequeue_nodequeue_form_submitImplementation of hook_nodequeue_form_submit()
nodequeue_nodequeue_infoImplementation of hook_nodequeue_info()
nodequeue_nodes
nodequeue_node_accessReturn TRUE if $user can queue(s) for this node.
nodequeue_node_tabDisplay the queue controls for a node.
nodequeue_node_titlesin general it's preferable to use Views for this functionality.
nodequeue_permImplementation of hook_perm
nodequeue_queue_accessReturn TRUE If the specified account has access to manipulate this queue.
nodequeue_queue_backMove an item to the back of the queue.
nodequeue_queue_clearEmpty a subqueue.
nodequeue_queue_downMove a position within a subqueue down by one.
nodequeue_queue_frontMove an item to the front of the queue.
nodequeue_queue_positionGet the position of a node in a queue; this queue MUST have only one subqueue or the results of this function will be unpredictable.
nodequeue_queue_swapSwap two positions within a subqueue.
nodequeue_queue_upMove a position within a subqueue up by one.
nodequeue_remove_subqueueRemove a subqueue.
nodequeue_saveSave a nodequeue. This does not save subqueues; those must be added separately.
nodequeue_set_subqueue_positionsGet the position of a node in several subqueues.
nodequeue_simpletestImplementation of hook_simpletest().
nodequeue_subqueue_addAdd a node to a queue.
nodequeue_subqueue_positionGet the position of a node in a subqueue, or FALSE if not found.
nodequeue_subqueue_removeRemove a node or node(s) from a nodequeue by position.
nodequeue_subqueue_remove_nodeRemove a node from the queue. If a node is in the queue more than once, only the first (closest to 0 position, or the front of the queue) will be removed.
nodequeue_subqueue_shuffleShuffle a queue.
nodequeue_subqueue_size_textGet a textual representation of a nodequeue's queue size.
nodequeue_subqueue_update_titleChange the title of a subqueue.
nodequeue_title_substituteSubstitute the subqueue title into some others tring.
nodequeue_view_queuesDisplay a list of queues and their status for the administrator.
nodequeue_view_subqueuesDisplay a list of subqueues for a queue and their sizes
theme_nodequeue_arrange_subqueue_form
theme_nodequeue_subqueue_count_text
theme_nodequeue_subqueue_empty_text
theme_nodequeue_subqueue_full_text
_nodequeue_ajax_add
_nodequeue_autocomplete

Classes

NameDescription
nodequeue_queueThe nodequeue queue class; the constructor makes it so we don't have to always check to see if our variables are empty or not.

File

View source
  1. <?php
  2. // --------------------------------------------------------------------------
  3. // Drupal Hooks
  4. /**
  5. * Implementation of hook_perm
  6. */
  7. function nodequeue_perm() {
  8. return array ('manipulate queues', 'administer nodequeue', 'manipulate all queues');
  9. }
  10. /**
  11. * Implementation of hook_init().
  12. *
  13. * Loads subsidiary includes for other modules.
  14. */
  15. function nodequeue_init() {
  16. if (module_exists('actions')) {
  17. include_once drupal_get_path('module', 'nodequeue') . '/nodequeue.actions.inc';
  18. }
  19. if (module_exists('workflow_ng')) {
  20. if (module_exists('actions')) {
  21. // Workflow_ng and actions module can't work together becuase of API collision.
  22. drupal_set_message(t('Nodequeue\'s integration with workflow_ng module couldn\'t initialize as the Actions module is enabled. Actions module and workflow_ng can\'t be enabled at the same time.'));
  23. }
  24. else {
  25. include_once drupal_get_path('module', 'nodequeue') . '/nodequeue.workflow_ng.inc';
  26. }
  27. }
  28. if (module_exists('views')) {
  29. include_once drupal_get_path('module', 'nodequeue') . '/nodequeue.views.inc';
  30. }
  31. }
  32. /**
  33. * Implementation of hook_menu
  34. */
  35. function nodequeue_menu($may_cache) {
  36. $items = array();
  37. global $user;
  38. $admin_access = user_access('administer nodequeue');
  39. $access = user_access('manipulate queues');
  40. if ($may_cache) {
  41. $manipulate = user_access('manipulate all queues');
  42. // administrative items
  43. $items[] = array(
  44. 'path' => 'admin/content/nodequeue',
  45. 'title' => t('Node queue'),
  46. 'access' => $admin_access || $access,
  47. 'callback' => 'nodequeue_view_queues',
  48. 'description' => t('Create and maintain simple node queues.'),
  49. 'type' => MENU_NORMAL_ITEM
  50. );
  51. $items[] = array(
  52. 'path' => 'admin/content/nodequeue/list',
  53. 'title' => t('List'),
  54. 'access' => $admin_access || $access,
  55. 'callback' => 'nodequeue_view_queues',
  56. 'weight' => -1,
  57. 'type' => MENU_DEFAULT_LOCAL_TASK
  58. );
  59. $items[] = array(
  60. 'path' => 'admin/content/nodequeue/settings',
  61. 'title' => t('Settings'),
  62. 'access' => $admin_access,
  63. 'callback' => 'drupal_get_form',
  64. 'callback arguments' => array('nodequeue_admin_settings'),
  65. 'type' => MENU_LOCAL_TASK
  66. );
  67. $items[] = array(
  68. 'path' => 'nodequeue/autocomplete',
  69. 'title' => t('Autocomplete'),
  70. 'access' => $access,
  71. 'callback' => 'nodequeue_autocomplete',
  72. 'type' => MENU_CALLBACK
  73. );
  74. $items[] = array(
  75. 'path' => 'nodequeue/ajax/add',
  76. 'title' => t('ajax add'),
  77. 'access' => $access,
  78. 'callback' => 'nodequeue_ajax_add',
  79. 'type' => MENU_CALLBACK
  80. );
  81. $info = nodequeue_api_info();
  82. foreach ($info as $key => $data) {
  83. $items[] = array(
  84. 'path' => 'admin/content/nodequeue/add/' . $key,
  85. 'title' => t('Add @type', array('@type' => strtolower($data['title']))),
  86. 'access' => $admin_access,
  87. 'callback' => 'drupal_get_form',
  88. 'callback arguments' => array('nodequeue_edit_queue_form', $key),
  89. 'type' => MENU_LOCAL_TASK
  90. );
  91. }
  92. }
  93. else {
  94. if ($user && arg(0) == 'node' && is_numeric(arg(1)) && arg(1) > 0 && $access && variable_get('nodequeue_use_tab', 1)) {
  95. $node = node_load(arg(1));
  96. // nodequeue tab for a node.
  97. if ($node && $queues = nodequeue_load_queues_by_type($node->type, 'tab')) {
  98. $items[] = array(
  99. 'path' => 'node/' . arg(1) . '/nodequeue',
  100. 'title' => variable_get('nodequeue_tab_name', t('Node queue')),
  101. 'access' => $access,
  102. 'callback' => 'nodequeue_node_tab',
  103. 'callback arguments' => array($node, $queues),
  104. 'type' => MENU_LOCAL_TASK,
  105. 'weight' => 5
  106. );
  107. }
  108. }
  109. // Administrative items for an individual queue.
  110. if ($access &&
  111. arg(0) == 'admin' &&
  112. arg(1) == 'content' &&
  113. arg(2) == 'nodequeue' &&
  114. is_numeric(arg(3)) &&
  115. $queue = nodequeue_load(arg(3))) {
  116. $access = nodequeue_queue_access($queue);
  117. drupal_set_title(t("Nodequeue '@title'", array('@title' => $queue->title)));
  118. $items[] = array(
  119. 'path' => 'admin/content/nodequeue/' . $queue->qid,
  120. 'access' => $access,
  121. 'callback' => 'nodequeue_admin_view',
  122. 'callback arguments' => array($queue),
  123. 'type' => MENU_CALLBACK
  124. );
  125. $items[] = array(
  126. 'path' => 'admin/content/nodequeue/' . $queue->qid . '/view',
  127. 'title' => t('View'),
  128. 'access' => $access,
  129. 'callback' => 'nodequeue_admin_view',
  130. 'callback arguments' => array($queue),
  131. 'weight' => -10,
  132. 'type' => MENU_DEFAULT_LOCAL_TASK
  133. );
  134. // Actual administrative items.
  135. $items[] = array(
  136. 'path' => 'admin/content/nodequeue/' . $queue->qid . '/edit',
  137. 'title' => t('Edit'),
  138. 'access' => $admin_access,
  139. 'callback' => 'drupal_get_form',
  140. 'callback arguments' => array('nodequeue_edit_queue_form', $queue),
  141. 'type' => MENU_LOCAL_TASK
  142. );
  143. $items[] = array(
  144. 'path' => 'admin/content/nodequeue/' . $queue->qid . '/delete',
  145. 'title' => t('Delete'),
  146. 'access' => $admin_access,
  147. 'callback' => 'drupal_get_form',
  148. 'callback arguments' => array('nodequeue_admin_delete', $queue),
  149. 'weight' => 5,
  150. 'type' => MENU_CALLBACK
  151. );
  152. $sqid = arg(5);
  153. if (is_numeric($sqid) && $subqueue = nodequeue_load_subqueue($sqid)) {
  154. $access = ($access && nodequeue_api_subqueue_access($subqueue, $user, $queue));
  155. // The following operations require a sqid and a nid.
  156. $nid = arg(6);
  157. if (in_array(arg(4), array('add', 'remove-node')) &&
  158. is_numeric($nid)) {
  159. $node = node_load($nid);
  160. }
  161. if ($node && nodequeue_node_access($node->type)) {
  162. $items[] = array(
  163. 'path' => "admin/content/nodequeue/$queue->qid/add/$sqid/$nid",
  164. 'access' => $access,
  165. 'callback' => 'nodequeue_admin_add_node',
  166. 'callback arguments' => array($queue, $subqueue, $node),
  167. 'type' => MENU_CALLBACK
  168. );
  169. $items[] = array(
  170. 'path' => "admin/content/nodequeue/$queue->qid/remove-node/$sqid/$nid",
  171. 'access' => $access,
  172. 'callback' => 'nodequeue_admin_remove_node',
  173. 'callback arguments' => array($queue, $subqueue, $node),
  174. 'type' => MENU_CALLBACK
  175. );
  176. }
  177. if (is_numeric($nid)) {
  178. // IN this case, $nid is actually $pos but why rename the var?
  179. $items[] = array(
  180. 'path' => "admin/content/nodequeue/$queue->qid/up/$sqid",
  181. 'access' => $access,
  182. 'callback' => 'nodequeue_admin_up',
  183. 'callback arguments' => array($queue, $subqueue, $nid),
  184. 'type' => MENU_CALLBACK
  185. );
  186. $items[] = array(
  187. 'path' => "admin/content/nodequeue/$queue->qid/down/$sqid",
  188. 'access' => $access,
  189. 'callback' => 'nodequeue_admin_down',
  190. 'callback arguments' => array($queue, $subqueue, $nid),
  191. 'type' => MENU_CALLBACK
  192. );
  193. $items[] = array(
  194. 'path' => "admin/content/nodequeue/$queue->qid/front/$sqid",
  195. 'access' => $access,
  196. 'callback' => 'nodequeue_admin_front',
  197. 'callback arguments' => array($queue, $subqueue, $nid),
  198. 'type' => MENU_CALLBACK
  199. );
  200. $items[] = array(
  201. 'path' => "admin/content/nodequeue/$queue->qid/back/$sqid",
  202. 'access' => $access,
  203. 'callback' => 'nodequeue_admin_back',
  204. 'callback arguments' => array($queue, $subqueue, $nid),
  205. 'type' => MENU_CALLBACK
  206. );
  207. $items[] = array(
  208. 'path' => "admin/content/nodequeue/$queue->qid/remove/$sqid",
  209. 'access' => $access,
  210. 'callback' => 'nodequeue_admin_remove',
  211. 'callback arguments' => array($queue, $subqueue, $nid),
  212. 'type' => MENU_CALLBACK
  213. );
  214. }
  215. $items[] = array(
  216. 'path' => "admin/content/nodequeue/$queue->qid/clear/$sqid",
  217. 'title' => t('Clear'),
  218. 'access' => $access,
  219. 'callback' => 'drupal_get_form',
  220. 'callback arguments' => array('nodequeue_clear_confirm', $queue, $subqueue),
  221. 'type' => MENU_CALLBACK
  222. );
  223. }
  224. }
  225. }
  226. return $items;
  227. }
  228. /**
  229. * Implementation of hook_nodeapi
  230. */
  231. function nodequeue_nodeapi(&$node, $op, $teaser = NULL, $page = NULL) {
  232. switch($op) {
  233. case 'delete':
  234. // If a node is being deleted, ensure it's also removed from any queues.
  235. $result = db_query("SELECT * FROM {nodequeue_nodes} WHERE nid = %d", $node->nid);
  236. while ($obj = db_fetch_object($result)) {
  237. // This removes by nid, not position, because if we happen to have a
  238. // node in a queue twice, the 2nd position would be wrong.
  239. nodequeue_subqueue_remove_node($obj->sqid, $node->nid);
  240. }
  241. break;
  242. }
  243. }
  244. /**
  245. * Implementation of hook_link
  246. */
  247. function nodequeue_link($type, $node = NULL, $teaser = FALSE) {
  248. if ($type == 'node' &&
  249. variable_get('nodequeue_links', FALSE) &&
  250. user_access('manipulate queues')) {
  251. $queues = nodequeue_load_queues_by_type($node->type, 'links');
  252. $subqueues = nodequeue_get_subqueues_by_node($queues, $node);
  253. if (empty($subqueues)) {
  254. return;
  255. }
  256. // resort the subqueues to retain consistent ordering:
  257. ksort($subqueues);
  258. // Due to caching, we can accidentally get positions leftover
  259. // from previous iterations on teaser list pages, so we must
  260. // remove any existing positions here.
  261. foreach ($subqueues as $id => $subqueue) {
  262. unset($subqueues[$id]->position);
  263. }
  264. nodequeue_set_subqueue_positions($subqueues, $node->nid);
  265. foreach ($subqueues as $subqueue) {
  266. $queue = $queues[$subqueue->qid];
  267. $query_string = nodequeue_get_query_string($node->nid, TRUE);
  268. $class = 'nodequeue-ajax-toggle nodequeue-toggle-q-'. $queue->qid. ' nodequeue-toggle-sq-'. $subqueue->sqid .' nodequeue-toggle-ref-'. $subqueue->reference;
  269. if (!isset($subqueue->position)) {
  270. $links[$class] = array(
  271. 'title' => nodequeue_title_substitute($queue->link, $queue, $subqueue),
  272. 'href' => "admin/content/nodequeue/$queue->qid/add/$subqueue->sqid/$node->nid",
  273. 'attributes' => array('class' => 'toggle-add'),
  274. 'query' => $query_string);
  275. }
  276. else if ($queue->link_remove) {
  277. $links[$class] = array(
  278. 'title' => nodequeue_title_substitute($queue->link_remove, $queue, $subqueue),
  279. 'href' => "admin/content/nodequeue/$queue->qid/remove-node/$subqueue->sqid/$node->nid",
  280. 'attributes' => array('class' => 'toggle-remove'),
  281. 'query' => $query_string);
  282. }
  283. }
  284. drupal_add_js(drupal_get_path('module', 'nodequeue') .'/nodequeue.js');
  285. drupal_add_css(drupal_get_path('module', 'nodequeue') .'/nodequeue.css');
  286. }
  287. return $links;
  288. }
  289. // --------------------------------------------------------------------------
  290. // Nodequeue Admin operations
  291. /**
  292. * Print the JSON output for our AJAX calls.
  293. */
  294. function nodequeue_js_output($label, $href, $count = NULL, $sqid = NULL) {
  295. $return = new stdClass();
  296. $return->status = 1;
  297. $return->label = check_plain($label);
  298. $return->href = $href;
  299. if (isset($count)) {
  300. $return->count = $count;
  301. }
  302. if (isset($sqid)) {
  303. $return->sqid = $sqid;
  304. }
  305. // let the world know this isn't normal output.
  306. drupal_set_header('Content-type: text/javascript');
  307. print drupal_to_js($return);
  308. exit;
  309. }
  310. /**
  311. * Page callback to add a node to a queue.
  312. */
  313. function nodequeue_admin_add_node($queue, $subqueue, $node) {
  314. if (!nodequeue_check_token($node->nid)) {
  315. return drupal_goto();
  316. }
  317. nodequeue_subqueue_add($queue, $subqueue, $node->nid);
  318. // Provide a response if this is javascript.
  319. if (!empty($_POST['js'])) {
  320. if (isset($_GET['tab'])) {
  321. nodequeue_js_output(t('Remove from queue'),
  322. url("admin/content/nodequeue/$queue->qid/remove-node/$subqueue->sqid/$node->nid", nodequeue_get_query_string($node->nid, TRUE, array('tab'))),
  323. nodequeue_subqueue_size_text($queue->size, $queue->size ? min($subqueue->count + 1, $queue->size) : $subqueue->count + 1, FALSE),
  324. $subqueue->sqid);
  325. }
  326. else {
  327. nodequeue_js_output(nodequeue_title_substitute($queue->link_remove, $queue, $subqueue),
  328. url("admin/content/nodequeue/$queue->qid/remove-node/$subqueue->sqid/$node->nid", nodequeue_get_query_string($node->nid, TRUE)));
  329. }
  330. }
  331. // There should always be a destination set for this, so just goto wherever.
  332. drupal_goto();
  333. }
  334. /**
  335. * Page callback to remove a node from a queue.
  336. */
  337. function nodequeue_admin_remove_node($queue, $subqueue, $node) {
  338. if (!nodequeue_check_token($node->nid)) {
  339. return drupal_goto();
  340. }
  341. nodequeue_subqueue_remove_node($subqueue->sqid, $node->nid);
  342. // Provide a response if this is javascript.
  343. if (!empty($_POST['js'])) {
  344. if (isset($_GET['tab'])) {
  345. nodequeue_js_output(t('Add to queue'),
  346. url("admin/content/nodequeue/$queue->qid/add/$subqueue->sqid/$node->nid", nodequeue_get_query_string($node->nid, TRUE, array('tab'))),
  347. nodequeue_subqueue_size_text($queue->size, $subqueue->count - 1, FALSE),
  348. $subqueue->sqid);
  349. }
  350. else {
  351. nodequeue_js_output(nodequeue_title_substitute($queue->link, $queue, $subqueue),
  352. url("admin/content/nodequeue/$queue->qid/add/$subqueue->sqid/$node->nid", nodequeue_get_query_string($node->nid, TRUE)));
  353. }
  354. }
  355. // There should always be a destination set for this, so just goto wherever.
  356. drupal_goto();
  357. }
  358. /**
  359. * Display the queue controls for a node.
  360. *
  361. * @param $node
  362. * The loaded $node; will be loaded by the hook_menu.
  363. * @param $queues
  364. * The list of queues. Loaded by the menu in order to test access control.
  365. */
  366. function nodequeue_node_tab($node, $queues) {
  367. $header = array();
  368. $header[] = array('data' => t('Title'), 'class' => 'nodequeue-title');
  369. if (variable_get('nodequeue_tab_display_max', 1)) {
  370. $header[] = array('data' => t('Max nodes'), 'class' => 'nodequeue-max-nodes');
  371. }
  372. $header[] = array('data' => t('In queue'), 'class' => 'nodequeue-in-queue');
  373. $header[] = array('data' => t('Operation'), 'class' => 'nodequeue-operation');
  374. $subqueues = nodequeue_get_subqueues_by_node($queues, $node);
  375. nodequeue_set_subqueue_positions($subqueues, $node->nid);
  376. $rows = array();
  377. foreach ($subqueues as $subqueue) {
  378. $queue = $queues[$subqueue->qid];
  379. if (!$subqueue->position) {
  380. $op = l(
  381. t('Add to queue'),
  382. "admin/content/nodequeue/$queue->qid/add/$subqueue->sqid/$node->nid",
  383. array('class' => 'nodequeue-ajax-toggle'),
  384. drupal_get_destination() .'&tab&'. nodequeue_get_token($node->nid)
  385. );
  386. }
  387. else {
  388. $op = l(
  389. t('Remove from queue'),
  390. "admin/content/nodequeue/$queue->qid/remove-node/$subqueue->sqid/$node->nid",
  391. array('class' => 'nodequeue-ajax-toggle'),
  392. drupal_get_destination() .'&tab&'. nodequeue_get_token($node->nid)
  393. );
  394. }
  395. $row = array();
  396. $row[] = array(
  397. 'class' => 'nodequeue-title',
  398. 'data' => l(nodequeue_title_substitute($queue->subqueue_title, $queue, $subqueue), "admin/content/nodequeue/$queue->qid/view/$subqueue->sqid"),
  399. );
  400. if (variable_get('nodequeue_tab_display_max', 1)) {
  401. $row[] = array('class' => 'nodequeue-max-nodes', 'data' => $queue->size ? $queue->size : t('Infinite'));
  402. }
  403. $row[] = array(
  404. 'id' => 'nodequeue-count-' . $subqueue->sqid,
  405. 'class' => 'nodequeue-in-queue',
  406. 'data' => nodequeue_subqueue_size_text($queue->size, $subqueue->count, FALSE)
  407. );
  408. $row[] = array('class' => 'nodequeue-operation', 'data' => $op);
  409. $rows[] = $row;
  410. }
  411. $output .= theme('table', $header, $rows, array('class' => 'nodequeue-table'));
  412. drupal_add_js(drupal_get_path('module', 'nodequeue') .'/nodequeue.js');
  413. drupal_add_css(drupal_get_path('module', 'nodequeue') .'/nodequeue.css');
  414. return $output;
  415. }
  416. /**
  417. * Display a list of queues and their status for the administrator.
  418. */
  419. function nodequeue_view_queues() {
  420. // Fetch all of the queues.
  421. $queues = nodequeue_load_queues(nodequeue_get_all_qids(25));
  422. foreach ($queues as $queue) {
  423. if (!nodequeue_queue_access($queue)) {
  424. unset($queues[$queue->qid]);
  425. }
  426. }
  427. if (empty($queues)) {
  428. return t('No node queues exist.');
  429. }
  430. $qids = array();
  431. // For every queue that has exactly 1 subqueue,
  432. foreach ($queues as $queue) {
  433. if ($queue->subqueues == 1) {
  434. $qids[] = $queue->qid;
  435. }
  436. }
  437. $subqueues = nodequeue_load_subqueues_by_queue($qids);
  438. // Relate all the subqueues we loaded back to our queues.
  439. foreach ($subqueues as $subqueue) {
  440. if (nodequeue_api_subqueue_access($subqueue, NULL, $queues[$subqueue->qid]));
  441. $queues[$subqueue->qid]->subqueue = $subqueue;
  442. }
  443. $header = array(t('Title'), t('Max nodes'), t('Subqueues'), t('Operation'));
  444. $rows = array();
  445. foreach ($queues as $queue) {
  446. $sub_text = $queue->subqueues;
  447. if ($sub_text == 1) {
  448. $sub_text .= " (" . nodequeue_subqueue_size_text($queue->size, $queue->subqueue->count) . ")";
  449. }
  450. $operations = array(l(t('View'), "admin/content/nodequeue/$queue->qid/view"));
  451. if (user_access('administer nodequeue')) {
  452. $operations[] = l(t('Edit'), "admin/content/nodequeue/$queue->qid/edit");
  453. $operations[] = l(t('Delete'), "admin/content/nodequeue/$queue->qid/delete");
  454. }
  455. $rows[] = array(
  456. array('class' => 'nodequeue-title', 'data' => check_plain($queue->title)),
  457. array('class' => 'nodequeue-max-nodes', 'data' => $queue->size == 0 ? t('Infinite') : $queue->size),
  458. array('class' => 'nodequeue-subqueues', 'data' => $sub_text),
  459. array('class' => 'nodequeue-operation', 'data' => implode(' | ', $operations)),
  460. );
  461. }
  462. $output = theme('table', $header, $rows);
  463. $output .= theme('pager', NULL, 25);
  464. return $output;
  465. }
  466. /**
  467. * Display a list of subqueues for a queue and their sizes
  468. */
  469. function nodequeue_view_subqueues($queue) {
  470. // Fetch all of the subqueues.
  471. $subqueues = nodequeue_load_subqueues_by_queue($queue->qid);
  472. $header = array(t('Title'), t('In queue'), t('Operation'));
  473. $rows = array();
  474. foreach ($subqueues as $subqueue) {
  475. if (nodequeue_api_subqueue_access($subqueue, NULL, $queue)) {
  476. $sub_text = nodequeue_subqueue_size_text($queue->size, $subqueue->count, FALSE);
  477. $rows[] = array(
  478. array('class' => 'nodequeue-title', 'data' => check_plain($subqueue->title)),
  479. array('class' => 'nodequeue-subqueues', 'data' => $sub_text),
  480. array('class' => 'nodequeue-operation', 'data' => l(t('View'), "admin/content/nodequeue/$queue->qid/view/$subqueue->sqid"))
  481. );
  482. }
  483. }
  484. $output = '<p>' . t('Max nodes in queue: @size', array('@size' => $queue->size ? $queue->size : t("Infinite"))) . '</p>';
  485. $output .= theme('table', $header, $rows);
  486. $output .= theme('pager', NULL, 20);
  487. return $output;
  488. }
  489. /**
  490. * Add or edit a queue.
  491. */
  492. function nodequeue_edit_queue_form($queue) {
  493. // For adding queues.
  494. if (is_string($queue)) {
  495. $queue = new nodequeue_queue($queue);
  496. }
  497. $info = nodequeue_api_info();
  498. $form['description'] = array(
  499. '#type' => 'fieldset',
  500. '#title' => $info[$queue->owner]['title'],
  501. '#description' => $info[$queue->owner]['description'],
  502. );
  503. $form['title'] = array(
  504. '#type' => 'textfield',
  505. '#title' => t('Title'),
  506. '#default_value' => $queue->title,
  507. '#size' => 50,
  508. '#maxlength' => 64,
  509. '#description' => t('Enter the name of the queue'),
  510. );
  511. // This is a value; as the default nodequeue implementation has just one
  512. // queue per nodequeue, this field is totally redundant. Plugins can
  513. // override this.
  514. $form['subqueue_title'] = array(
  515. '#type' => 'value',
  516. '#value' => $queue->subqueue_title,
  517. );
  518. // The placeholder is put here so that modifiers have an easy way to put
  519. // additional form widgets in a prominent spot but not before the title of
  520. // the queue.
  521. $form['placeholder'] = array();
  522. $form['size'] = array(
  523. '#type' => 'textfield',
  524. '#title' => t('Queue size'),
  525. '#default_value' => $queue->size,
  526. '#size' => 2,
  527. '#maxlength' => 2,
  528. '#description' => t('The maximum number of nodes will appear in the queue. Enter 0 for no limit'),
  529. );
  530. $form['reverse'] = array(
  531. '#type' => 'checkbox',
  532. '#title' => t('Reverse in admin view'),
  533. '#default_value' => $queue->reverse,
  534. '#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.'),
  535. );
  536. $form['link'] = array(
  537. '#type' => 'textfield',
  538. '#title' => t('Link "add to queue" text'),
  539. '#default_value' => $queue->link,
  540. '#size' => 40,
  541. '#maxlength' => 40,
  542. '#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.'),
  543. );
  544. $form['link_remove'] = array(
  545. '#type' => 'textfield',
  546. '#title' => t('Link "remove from queue" text'),
  547. '#default_value' => $queue->link_remove,
  548. '#size' => 40,
  549. '#maxlength' => 40,
  550. '#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.'),
  551. );
  552. $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");
  553. while ($role = db_fetch_object($result)) {
  554. $roles[$role->rid] = $role->name;
  555. }
  556. $form['roles'] = array(
  557. '#type' => 'checkboxes',
  558. '#title' => t('Roles'),
  559. '#default_value' => $queue->roles,
  560. '#options' => $roles,
  561. '#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.'),
  562. );
  563. $nodes = node_get_types('names');
  564. $form['types'] = array(
  565. '#type' => 'checkboxes',
  566. '#title' => t('Types'),
  567. '#default_value' => $queue->types,
  568. '#options' => $nodes,
  569. '#description' => t('Check each node type that can be added to this queue.'),
  570. );
  571. $form['submit'] = array(
  572. '#type' => 'submit',
  573. '#value' => t('Submit'),
  574. );
  575. $form['owner'] = array(
  576. '#type' => 'value',
  577. '#value' => $queue->owner,
  578. );
  579. $form['show_in_links'] = array(
  580. '#type' => 'value',
  581. '#value' => $queue->show_in_links,
  582. );
  583. $form['show_in_tab'] = array(
  584. '#type' => 'value',
  585. '#value' => $queue->show_in_tab,
  586. );
  587. $form['show_in_ui'] = array(
  588. '#type' => 'value',
  589. '#value' => $queue->show_in_ui,
  590. );
  591. $form['reference'] = array(
  592. '#type' => 'value',
  593. '#value' => $queue->reference,
  594. );
  595. $form['subqueues'] = array(
  596. '#type' => 'value',
  597. '#value' => $queue->subqueues,
  598. );
  599. if (isset($queue->qid)) {
  600. $form[] = array(
  601. '#type' => 'submit',
  602. '#value' => t('Delete'),
  603. );
  604. $form['qid'] = array(
  605. '#type' => 'value',
  606. '#value' => $queue->qid,
  607. );
  608. $form['count'] = array(
  609. '#type' => 'value',
  610. '#value' => $queue->count,
  611. );
  612. }
  613. nodequeue_api_queue_form($queue, $form);
  614. return $form;
  615. }
  616. /**
  617. * Submit function for the nodequeue_queue form.
  618. */
  619. function nodequeue_edit_queue_form_validate($formid, $form_values, &$form) {
  620. if ($form_values['op'] == t('Delete')) {
  621. return;
  622. }
  623. $queue = (object) $form_values;
  624. // fix checkboxes
  625. $queue->roles = array_keys(array_filter($queue->roles));
  626. $queue->types = array_keys(array_filter($queue->types));
  627. if (!isset($queue->qid)) {
  628. $queue->new = TRUE;
  629. }
  630. nodequeue_api_queue_form_validate($queue, $form_values, $form);
  631. }
  632. /**
  633. * Submit function for the nodequeue_queue form.
  634. */
  635. function nodequeue_edit_queue_form_submit($formid, $form) {
  636. if ($form['op'] == t('Delete')) {
  637. return "admin/content/nodequeue/$form[qid]/delete";
  638. }
  639. $queue = (object) $form;
  640. // fix checkboxes
  641. $queue->roles = array_keys(array_filter($queue->roles));
  642. $queue->types = array_keys(array_filter($queue->types));
  643. if (!isset($queue->qid)) {
  644. $queue->new = TRUE;
  645. }
  646. // Modify show_in_links based on whether or not links are available.
  647. $queue->show_in_links = !empty($queue->link) && !empty($queue->link_remove);
  648. nodequeue_api_queue_form_submit($queue, $form);
  649. $qid = nodequeue_save($queue);
  650. nodequeue_api_queue_form_submit_finish($queue, $form);
  651. nodequeue_check_subqueue_sizes($queue);
  652. if ($queue->new) {
  653. drupal_set_message(t('The queue has been created.'));
  654. }
  655. else {
  656. drupal_set_message(t('The queue has been updated.'));
  657. }
  658. return 'admin/content/nodequeue';
  659. }
  660. /**
  661. * Confirm form to delete a queue
  662. */
  663. function nodequeue_admin_delete($queue) {
  664. $form['qid'] = array('#type' => 'value', '#value' => $queue->qid);
  665. return confirm_form($form,
  666. t('Are you sure you want to delete "%title"?', array('%title' => $queue->title)),
  667. $_GET['destination'] ? $_GET['destination'] : 'admin/content/nodequeue',
  668. t('This action cannot be undone.'),
  669. t('Delete'), t('Cancel')
  670. );
  671. }
  672. /**
  673. * Submit function for nodequeue delete
  674. */
  675. function nodequeue_admin_delete_submit($formid, $form) {
  676. if ($form['confirm']) {
  677. nodequeue_delete($form['qid']);
  678. drupal_set_message("The queue has been deleted.");
  679. }
  680. return 'admin/content/nodequeue';
  681. }
  682. /**
  683. * Page callback to view a queue.
  684. */
  685. function nodequeue_admin_view($queue, $sqid = NULL) {
  686. $qid = $queue->qid;
  687. // If the queue has just one subqueue, it gets special treatment.
  688. if (!$sqid) {
  689. if ($queue->subqueues == 1) {
  690. $subqueues = nodequeue_load_subqueues_by_queue($queue->qid);
  691. $subqueue = array_shift($subqueues);
  692. }
  693. else {
  694. // display subqueue list page.
  695. return nodequeue_view_subqueues($queue);
  696. }
  697. $sqid = $queue->current;
  698. }
  699. else if ($sqid) {
  700. // Adjust properties of the page so our subqueue is in the right
  701. // visual place.
  702. $subqueue = nodequeue_load_subqueue($sqid);
  703. if (!nodequeue_api_subqueue_access($subqueue, NULL, $queue)) {
  704. return drupal_not_found();
  705. }
  706. drupal_set_title(t("Subqueue '@title'",
  707. array('@title' => nodequeue_title_substitute($queue->subqueue_title, $queue, $subqueue))));
  708. $breadcrumb = drupal_get_breadcrumb();
  709. $breadcrumb[] = l($queue->title, "admin/content/nodequeue/$queue->qid");
  710. drupal_set_breadcrumb($breadcrumb);
  711. }
  712. return nodequeue_arrange_subqueue($queue, $subqueue);
  713. }
  714. function nodequeue_arrange_subqueue_entry($queue, $subqueue, $node, $nids_visible = array()) {
  715. $qid = $queue->qid;
  716. $sqid = $subqueue->sqid;
  717. $query_string = nodequeue_get_query_string($node->position, TRUE);
  718. $buttons = l(
  719. theme('image', drupal_get_path('module', 'nodequeue') .'/images/go-up.png', t('Move up')),
  720. "admin/content/nodequeue/$qid/up/$sqid/$node->position",
  721. array(
  722. 'title' => t('Move up'),
  723. 'class' => 'nodequeue-move-up',
  724. ), $query_string, NULL, FALSE, TRUE);
  725. $buttons .= l(
  726. theme('image', drupal_get_path('module', 'nodequeue') .'/images/go-down.png', t('Move down')),
  727. "admin/content/nodequeue/$qid/down/$sqid/$node->position",
  728. array(
  729. 'title' => t('Move down'),
  730. 'class' => 'nodequeue-move-down',
  731. ), $query_string, NULL, FALSE, TRUE);
  732. $buttons .= l(
  733. theme('image', drupal_get_path('module', 'nodequeue') .'/images/go-top.png', t('Move to front')),
  734. "admin/content/nodequeue/$qid/front/$sqid/$node->position",
  735. array(
  736. 'title' => t('Move to front'),
  737. 'class' => 'nodequeue-move-front',
  738. ), $query_string, NULL, FALSE, TRUE);
  739. $buttons .= l(
  740. theme('image', drupal_get_path('module', 'nodequeue') .'/images/go-bottom.png', t('Move to back')),
  741. "admin/content/nodequeue/$qid/back/$sqid/$node->position",
  742. array(
  743. 'title' => t('Move to back'),
  744. 'class' => 'nodequeue-move-back',
  745. ), $query_string, NULL, FALSE, TRUE);
  746. $buttons .= l(
  747. theme('image', drupal_get_path('module', 'nodequeue') .'/images/delete.png', t('Remove from queue')),
  748. "admin/content/nodequeue/$qid/remove/$sqid/$node->position",
  749. array(
  750. 'title' => t('Remove from queue'),
  751. 'class' => 'nodequeue-remove',
  752. ), $query_string, NULL, FALSE, TRUE);
  753. $output = '<tr id="nodequeue-row-' . $node->position . '" class="nodequeue-row ' . ($node->position % 2 ? 'odd' : 'even') . '">';
  754. if (in_array($node->nid, $nids_visible)) {
  755. $output .= '<td>'. l($node->title, "node/$node->nid") .'</td>';
  756. }
  757. else {
  758. $output .= '<td>' . t('Restricted Node - NID: @nid', array('@nid' => $node->nid)) .'</td>';
  759. }
  760. $output .= '<td>' . theme('username', $node) . '</td>';
  761. $output .= '<td>' . format_date($node->created) . '</td>';
  762. $output .= '<td>' . $buttons . '</td>';
  763. $output .= '</tr>';
  764. return $output;
  765. }
  766. /**
  767. * View the contents of a subqueue, with links to re-order the queue.
  768. */
  769. function nodequeue_arrange_subqueue($queue, $subqueue) {
  770. $qid = $queue->qid;
  771. $sqid = $subqueue->sqid;
  772. $output = '';
  773. $body = '';
  774. $order = $queue->reverse ? 'DESC' : 'ASC';
  775. //Get the list of nodes in the subqueue.
  776. //First, get a list of nodes taking into account node access restrictions.
  777. $nids_visible = nodequeue_nids_visible($subqueue->sqid);
  778. //Then, get an unrestricted list of nodes in the subqueue.
  779. //Titles are not displayed for nodes that are not present in the result set of both queries.
  780. $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", $sqid);
  781. $nids = array();
  782. while ($node = db_fetch_object($result)) {
  783. $nids[$node->position] = $node->nid;
  784. $body .= nodequeue_arrange_subqueue_entry($queue, $subqueue, $node, $nids_visible);
  785. }
  786. $output = '<p>' . t('Max nodes in queue: @size', array('@size' => $queue->size ? $queue->size : t("Infinite"))) . '</p>';
  787. $output .= '<p class="nodequeue-hide-if-not-js nodequeue-warning">';
  788. $output .= t('Changes made to the queue order and queue removals will not be active until you click Save, below. If you add more nodes than the queue can hold, they will be removed from the @end when you save!', array('@end' => $queue->reverse ? t('bottom') : t('top')));
  789. $output .= '</p>';
  790. $output .= '<table id="nodequeue-table">';
  791. $output .= '<thead>';
  792. $output .= '<tr>';
  793. $output .= '<th class="nodequeue-node">' . t('Node') . '</th>';
  794. $output .= '<th class="nodequeue-author">' . t('Author') . '</th>';
  795. $output .= '<th class="nodequeue-date">' . t('Date Created') . '</th>';
  796. $output .= '<th class="nodequeue-operation">' . t('Operation') . '</th>';
  797. $output .= '</thead>';
  798. $output .= '<tbody>' . $body . '</tbody>';
  799. $output .= '</table>';
  800. $output .= drupal_get_form('nodequeue_arrange_subqueue_form', $queue, $sqid, $nids);
  801. drupal_add_js(drupal_get_path('module', 'nodequeue') .'/nodequeue.js');
  802. return $output;
  803. }
  804. /**
  805. * Form used for arranging a queue
  806. */
  807. function nodequeue_arrange_subqueue_form($queue, $sqid, $nids) {
  808. $form['qid'] = array(
  809. '#type' => 'value',
  810. '#value' => $queue->qid,
  811. );
  812. $form['sqid'] = array(
  813. '#type' => 'hidden',
  814. '#value' => $sqid,
  815. );
  816. $form['order'] = array(
  817. '#type' => 'hidden',
  818. '#id' => 'nodequeue-order',
  819. '#default_value' => implode(',', array_keys($nids)),
  820. );
  821. $form['add'] = array(
  822. '#type' => 'textfield',
  823. '#title' => t('Select title to add'),
  824. '#autocomplete_path' => "nodequeue/autocomplete/$sqid",
  825. '#default_value' => '',
  826. );
  827. // For use by the validate handler
  828. $form['nid'] = array(
  829. '#type' => 'value',
  830. '#value' => 0,
  831. );
  832. $form['add_submit'] = array(
  833. '#type' => 'submit',
  834. '#attributes' => array('class' => 'nodequeue-add'),
  835. '#value' => t('Add'),
  836. );
  837. $form['save'] = array(
  838. '#type' => 'submit',
  839. '#attributes' => array('class' => 'nodequeue-hide-if-not-js-hide nodequeue-save'),
  840. '#value' => t('Save'),
  841. );
  842. $form['clear'] = array(
  843. '#type' => 'submit',
  844. '#attributes' => array('class' => 'nodequeue-clear'),
  845. '#value' => t('Clear'),
  846. );
  847. $form['reverse_click'] = array(
  848. '#type' => 'submit',
  849. '#attributes' => array('class' => 'nodequeue-reverse'),
  850. '#value' => t('Reverse'),
  851. );
  852. $form['shuffle'] = array(
  853. '#type' => 'submit',
  854. '#attributes' => array('class' => 'nodequeue-shuffle'),
  855. '#value' => t('Shuffle'),
  856. );
  857. // Store the original order.
  858. $form['nids'] = array(
  859. '#type' => 'value',
  860. '#value' => $nids,
  861. );
  862. $form['added_nids'] = array(
  863. '#type' => 'hidden',
  864. '#default_value' => '',
  865. );
  866. $settings = array(
  867. 'nodequeue-table' => array(
  868. // The gadget that stores our the order of items.
  869. 'order' => 'input#nodequeue-order',
  870. // The buttons that do stuff.
  871. 'up' => 'a.nodequeue-move-up',
  872. 'down' => 'a.nodequeue-move-down',
  873. 'top' => 'a.nodequeue-move-front',
  874. 'bottom' => 'a.nodequeue-move-back',
  875. 'remove' => 'a.nodequeue-remove',
  876. // The button that adds an item
  877. 'add' => 'input.nodequeue-add',
  878. // The buttom to clear the queue
  879. 'clear_list' => 'input.nodequeue-clear',
  880. // Path for js to shuffle the queue
  881. 'shuffle' => 'input.nodequeue-shuffle',
  882. // Path for js to reverse the queue
  883. 'reverse' => 'input.nodequeue-reverse',
  884. // Path for ajax on adding an item
  885. 'path' => url('nodequeue/ajax/add', NULL, NULL, TRUE),
  886. // Which items to post when adding
  887. 'post' => array('#edit-sqid', '#edit-add'),
  888. // Where to get the id of an item
  889. 'tr' => 'nodequeue-row-',
  890. 'row_class' => 'tr.nodequeue-row',
  891. // Where to put the extra (we're storing a nid)
  892. 'extra' => '#edit-added-nids',
  893. // What item to focus on ready
  894. 'focus' => '#edit-add',
  895. // What item(s) to clear after add
  896. 'clear' => array('#edit-add'),
  897. // What hidden class to show as soon as anything has changed that needs saving.
  898. 'hidden' => '.nodequeue-js-hide',
  899. // Do we add to the top or the bottom?
  900. 'add_location' => $queue->reverse ? 'top' : 'bottom',
  901. ),
  902. );
  903. drupal_add_js(array('nodequeue' => $settings), 'setting');
  904. drupal_add_css(drupal_get_path('module', 'nodequeue') .'/nodequeue.css');
  905. return $form;
  906. }
  907. function theme_nodequeue_arrange_subqueue_form($form) {
  908. $header = array(
  909. check_plain($form['add']['#title']),
  910. '',
  911. );
  912. unset($form['add']['#title']);
  913. $rows = array(
  914. drupal_render($form['add']),
  915. array('data' => drupal_render($form['add_submit']), 'width' => '80%'),
  916. );
  917. $output = theme('table', $header, array($rows));
  918. $output .= drupal_render($form);
  919. return $output;
  920. }
  921. /**
  922. * Validate handler for nodequeue_arrange_subqueue_form
  923. */
  924. function nodequeue_arrange_subqueue_form_validate($form_id, $form_values, $form) {
  925. if ($form_values['op'] == t('Add')) {
  926. $queue = nodequeue_load($form_values['qid']);
  927. $subqueue = nodequeue_load($form_values['sqid']);
  928. $nodes = nodequeue_api_autocomplete($queue, $subqueue, $form_values['add']);
  929. if (empty($nodes) || !is_array($nodes)) {
  930. form_error($form['add'], t('Invalid node'));
  931. return;
  932. }
  933. if (count($nodes) > 1) {
  934. form_error($form['add'], t('That matches too many nodes'));
  935. return;
  936. }
  937. $keys = array_keys($nodes);
  938. $nid = array_pop($keys);
  939. form_set_value($form['nid'], $nid);
  940. return;
  941. }
  942. if ($form_values['op'] == t('Save')) {
  943. $nids = $form_values['nids'];
  944. // We can't use array_merge because it'll reset our keys and we can't
  945. // use + because it will overwrite.
  946. if ($form_values['added_nids']) {
  947. foreach (explode(',', $form_values['added_nids']) as $nid) {
  948. if (empty($nids)) {
  949. $nids[1] = $nid;
  950. }
  951. else {
  952. $nids[max(array_keys($nids)) + 1] = $nid;
  953. }
  954. }
  955. }
  956. form_set_value($form['nids'], $nids);
  957. }
  958. }
  959. /**
  960. * Submit function for nodequeue_arrange_subqueue_form
  961. */
  962. function nodequeue_arrange_subqueue_form_submit($form_id, $form_values) {
  963. // Add a node to the queue if that's the intention.
  964. if ($form_values['op'] == t('Clear')) {
  965. return 'admin/content/nodequeue/' . $form_values['qid'] . '/clear/' . $form_values['sqid'];
  966. }
  967. $queue = nodequeue_load($form_values['qid']);
  968. $subqueue = nodequeue_load_subqueue($form_values['sqid']);
  969. if ($form_values['op'] == t('Add')) {
  970. nodequeue_subqueue_add($queue, $subqueue, $form_values['nid']);
  971. return;
  972. }
  973. db_query("DELETE FROM {nodequeue_nodes} WHERE sqid = %d", $form_values['sqid']);
  974. if ($form_values['order']) {
  975. $now = time();
  976. $sql = '';
  977. $args = array();
  978. $nids = $form_values['nids'];
  979. $subqueue->count = 0;
  980. $order = explode(',', $form_values['order']);
  981. if ($queue->reverse xor $form_values['op'] == t('Reverse')) {
  982. $order = array_reverse($order);
  983. }
  984. foreach ($order as $new_pos => $old_pos) {
  985. if ($sql) {
  986. $sql .= ', ';
  987. }
  988. $sql .= ' (%d, %d, %d, %d, %d)';
  989. $args[] = $form_values['sqid'];
  990. $args[] = $form_values['qid'];
  991. $args[] = $nids[$old_pos];
  992. // $new_pos starts from 0 but we start from 1.
  993. $args[] = $new_pos + 1;
  994. $args[] = $now;
  995. $subqueue->count++;
  996. }
  997. $sql = "INSERT INTO {nodequeue_nodes} (sqid, qid, nid, position, timestamp) VALUES $sql";
  998. db_query($sql, $args);
  999. if ($queue->size) {
  1000. // 0 means infinity so never do this if false
  1001. nodequeue_check_subqueue_size($queue, $subqueue);
  1002. }
  1003. if ($form_values['op'] == t('Shuffle')) {
  1004. nodequeue_subqueue_shuffle($subqueue);
  1005. }
  1006. }
  1007. drupal_set_message(t('The queue has been updated'));
  1008. }
  1009. /**
  1010. * Page callback to move an item up in a queue. This will be used only if
  1011. * javascript is disabled in the client, and is a fallback technique.
  1012. */
  1013. function nodequeue_admin_up($queue, $subqueue, $pos) {
  1014. if (!nodequeue_check_token($pos)) {
  1015. return drupal_goto();
  1016. }
  1017. // This function is safe if $pos is out of bounds.
  1018. if (!$queue->reverse) {
  1019. nodequeue_queue_up($subqueue, $pos);
  1020. } else {
  1021. nodequeue_queue_down($subqueue, $pos);
  1022. }
  1023. drupal_goto();
  1024. }
  1025. /**
  1026. * Page callback to move an item down in a queue. This will be used only if
  1027. * javascript is disabled in the client, and is a fallback technique.
  1028. */
  1029. function nodequeue_admin_down($queue, $subqueue, $pos) {
  1030. if (!nodequeue_check_token($pos)) {
  1031. return drupal_goto();
  1032. }
  1033. // This function is safe if $pos is out of bounds.
  1034. if ($queue->reverse) {
  1035. nodequeue_queue_up($subqueue, $pos);
  1036. } else {
  1037. nodequeue_queue_down($subqueue, $pos);
  1038. }
  1039. drupal_goto();
  1040. }
  1041. /**
  1042. * Page callback to move an item to the front of a queue. This will be used
  1043. * only if javascript is disabled in the client, and is a fallback technique.
  1044. */
  1045. function nodequeue_admin_front($queue, $subqueue, $pos) {
  1046. if (!nodequeue_check_token($pos)) {
  1047. return drupal_goto();
  1048. }
  1049. // This function is safe if $pos is out of bounds.
  1050. if (!$queue->reverse) {
  1051. nodequeue_queue_front($subqueue, $pos);
  1052. } else {
  1053. nodequeue_queue_back($subqueue, $pos);
  1054. }
  1055. drupal_goto();
  1056. }
  1057. /**
  1058. * Page callback to move an item to the back of a queue. This will be used
  1059. * only if javascript is disabled in the client, and is a fallback technique.
  1060. */
  1061. function nodequeue_admin_back($queue, $subqueue, $pos) {
  1062. if (!nodequeue_check_token($node->nid)) {
  1063. return drupal_goto();
  1064. }
  1065. // This function is safe if $pos is out of bounds.
  1066. if ($queue->reverse) {
  1067. nodequeue_queue_front($subqueue, $pos);
  1068. } else {
  1069. nodequeue_queue_back($subqueue, $pos);
  1070. }
  1071. drupal_goto();
  1072. }
  1073. /**
  1074. * Page callback to remove an item from a queue. This will be used only
  1075. * if javascript is disabled in the client, and is a fallback technique.
  1076. * This differs from nodequeue_admin_remove_node in that it removes a
  1077. * specific position, which is necessary in case a node is in a queue
  1078. * multiple times.
  1079. */
  1080. function nodequeue_admin_remove($queue, $subqueue, $pos) {
  1081. if (!nodequeue_check_token($node->nid)) {
  1082. return drupal_goto();
  1083. }
  1084. nodequeue_subqueue_remove($subqueue->sqid, $pos);
  1085. drupal_goto();
  1086. }
  1087. /**
  1088. * Confirm form to clear a queue.
  1089. */
  1090. function nodequeue_clear_confirm($queue, $subqueue) {
  1091. $form['sqid'] = array('#type' => 'value', '#value' => $subqueue->sqid);
  1092. $form['qid'] = array('#type' => 'value', '#value' => $queue->qid);
  1093. return confirm_form($form,
  1094. t('Clearing queue "%s" is irreversible. You sure?', array('%s' => nodequeue_title_substitute($queue->subqueue_title, $queue, $subqueue))),
  1095. $_GET['destination'] ? $_GET['destination'] : "admin/content/nodequeue/$queue->qid/view/$subqueue->sqid",
  1096. t('This action cannot be undone.'),
  1097. t('Clear'), t('Cancel')
  1098. );
  1099. }
  1100. /**
  1101. * Submit function for nodequeue clear confirm
  1102. */
  1103. function nodequeue_clear_confirm_submit($formid, $form) {
  1104. if ($form['confirm']) {
  1105. nodequeue_queue_clear($form['sqid']);
  1106. return "admin/content/nodequeue/$form[qid]/view/$form[sqid]";
  1107. }
  1108. }
  1109. /**
  1110. * Page callback for autocomplete.
  1111. */
  1112. function nodequeue_autocomplete($sqid = NULL, $string = NULL) {
  1113. $output = _nodequeue_autocomplete($sqid, $string);
  1114. // let the world know this isn't normal output.
  1115. drupal_set_header('Content-type: text/javascript');
  1116. print drupal_to_js(drupal_map_assoc($output));
  1117. exit;
  1118. }
  1119. function _nodequeue_autocomplete($sqid, $string) {
  1120. $output = array();
  1121. if (!$sqid || !is_numeric($sqid) || !$string) {
  1122. return $output;
  1123. }
  1124. $subqueue = nodequeue_load_subqueue($sqid);
  1125. if (!$subqueue) {
  1126. return $output;
  1127. }
  1128. $queue = nodequeue_load($subqueue->qid);
  1129. if (!$queue) {
  1130. return $output;
  1131. }
  1132. $nodes = nodequeue_api_autocomplete($queue, $subqueue, $string);
  1133. return $nodes;
  1134. }
  1135. /**
  1136. * Page callback to ajaxily add a node.
  1137. */
  1138. function nodequeue_ajax_add() {
  1139. $sqid = $_POST['sqid'];
  1140. $position = $_POST['position'];
  1141. $string = $_POST['add'];
  1142. $output = _nodequeue_ajax_add($sqid, $position, $string);
  1143. // let the world know this isn't normal output.
  1144. drupal_set_header('Content-type: text/javascript');
  1145. print drupal_to_js($output);
  1146. exit;
  1147. }
  1148. function _nodequeue_ajax_add($sqid, $position, $string) {
  1149. if (!$string) {
  1150. return array('error' => t('Invalid input'));
  1151. }
  1152. if (!$sqid || !is_numeric($sqid)) {
  1153. return array('error' => t('Invalid sqid'));
  1154. }
  1155. $subqueue = nodequeue_load_subqueue($sqid);
  1156. if (!$subqueue) {
  1157. return array('error' => t('Invalid sqid'));
  1158. }
  1159. if (!nodequeue_api_subqueue_access($subqueue)) {
  1160. return array('error' => t('Access denied'));
  1161. }
  1162. $queue = nodequeue_load($subqueue->qid);
  1163. if (!$queue) {
  1164. return array('error' => t('Invalid sqid'));
  1165. }
  1166. $nodes = nodequeue_api_autocomplete($queue, $subqueue, $string);
  1167. if (empty($nodes) || !is_array($nodes)) {
  1168. return array('error' => t('Invalid node'));
  1169. }
  1170. if (count($nodes) > 1) {
  1171. return array('error' => t('That matches too many nodes'));
  1172. }
  1173. $keys = array_keys($nodes);
  1174. $node = node_load(array_pop($keys));
  1175. if (!node_access('view', $node)) {
  1176. return array('error' => t('Invalid node'));
  1177. }
  1178. $node->position = $position;
  1179. $nids_visible = nodequeue_nids_visible($subqueue->sqid);
  1180. return array(
  1181. 'status' => 1,
  1182. 'extra' => $node->nid,
  1183. 'max' => $queue->size,
  1184. 'data' => nodequeue_arrange_subqueue_entry($queue, $subqueue, $node, $nids_visible),
  1185. );
  1186. }
  1187. /*
  1188. * Get the list of nodes in the subqueue, taking into account node access restrictions.
  1189. */
  1190. function nodequeue_nids_visible($sqid = -1, $account = NULL) {
  1191. if (!$account) {
  1192. global $user;
  1193. $account = $user;
  1194. }
  1195. $nids_visible = array();
  1196. if (!user_access('administer nodes', $account)) {
  1197. $node_status_sql = ' AND n.status = 1';
  1198. }
  1199. $query_restricted = db_query(db_rewrite_sql("SELECT DISTINCT(n.nid) FROM {node} n LEFT JOIN {nodequeue_nodes} nq ON nq.nid = n.nid WHERE nq.sqid = %d $node_status_sql ORDER BY nq.position $order"), $sqid);
  1200. while ($result_restricted = db_fetch_object($query_restricted)) {
  1201. $nids_visible[$result_restricted->nid] = $result_restricted->nid;
  1202. }
  1203. return $nids_visible;
  1204. }
  1205. // --------------------------------------------------------------------------
  1206. // Nodequeue manipulation API.
  1207. /**
  1208. * @defgroup nodequeue_api
  1209. * @{
  1210. * Access to the internals of nodequeues are handled primarily through these
  1211. * API functions. They allow easy loading of queues for manipulation.
  1212. */
  1213. /**
  1214. * The nodequeue queue class; the constructor makes it so we don't have to
  1215. * always check to see if our variables are empty or not.
  1216. */
  1217. class nodequeue_queue {
  1218. var $title = '';
  1219. var $size = 0;
  1220. var $link = '';
  1221. var $remove_link = '';
  1222. var $roles = array();
  1223. var $types = array();
  1224. var $show_in_links = TRUE;
  1225. var $show_in_tab = TRUE;
  1226. var $show_in_ui = TRUE;
  1227. var $reference = 0;
  1228. var $subqueue = array();
  1229. function nodequeue_queue($type) {
  1230. $this->owner = $type;
  1231. }
  1232. }
  1233. /**
  1234. * Return TRUE If the specified account has access to manipulate this queue.
  1235. */
  1236. function nodequeue_queue_access($queue, $account = NULL) {
  1237. if (!$account) {
  1238. global $user;
  1239. $account = $user;
  1240. }
  1241. // Automatically true if all queues.
  1242. if (user_access('manipulate all queues')) {
  1243. return TRUE;
  1244. }
  1245. // Automatically false if they can't manipulate queues at all.
  1246. if (!user_access('manipulate queues') || empty($queue->roles)) {
  1247. return FALSE;
  1248. }
  1249. // Return false if the queue's owner rejects access to the queue.
  1250. if (!nodequeue_api_queue_access($queue, $account)) {
  1251. return FALSE;
  1252. }
  1253. $roles = array_keys((array) $account->roles) + array(DRUPAL_AUTHENTICATED_RID);
  1254. return (bool) array_intersect($roles, $queue->roles);
  1255. }
  1256. /**
  1257. * Fetch a list of available queues for a given location. These queues
  1258. * will be fully loaded and ready to go.
  1259. */
  1260. function nodequeue_load_queues_by_type($type, $location = NULL, $account = NULL, $bypass_cache = FALSE) {
  1261. $qids = nodequeue_get_qids($type, $account, $bypass_cache);
  1262. if ($location) {
  1263. nodequeue_filter_qids($qids, $location);
  1264. }
  1265. return nodequeue_load_queues(array_keys($qids), $bypass_cache);
  1266. }
  1267. /**
  1268. * Return TRUE if $user can queue(s) for this node.
  1269. *
  1270. * @param $type
  1271. * The node type.
  1272. * @param $location
  1273. * Optional argument. May be one of:
  1274. * - 'links': Only check for queues that have node links.
  1275. * - 'tab': Only check for queues that appear on the node tab.
  1276. * - 'ui': Only check for queues that appear in the UI.
  1277. */
  1278. function nodequeue_node_access($type, $location = NULL, $account = NULL) {
  1279. $qids = nodequeue_get_qids($type, $account);
  1280. if ($location) {
  1281. nodequeue_filter_qids($qids, $location);
  1282. }
  1283. return !empty($qids);
  1284. }
  1285. /**
  1286. * Filter a list of qids returned by nodequeue_get_qids to a location.
  1287. *
  1288. * @param $qids
  1289. * An array of $qids from @see nodequeue_get_qids()
  1290. * @param $location
  1291. * One of:
  1292. * - 'links': Only check for queues that have node links.
  1293. * - 'tab': Only check for queues that appear on the node tab.
  1294. * - 'ui': Only check for queues that appear in the UI.
  1295. */
  1296. function nodequeue_filter_qids(&$qids, $location) {
  1297. $var = "show_in_$location";
  1298. foreach ($qids as $qid => $info) {
  1299. if (empty($info->$var)) {
  1300. unset($qids[$qid]);
  1301. }
  1302. }
  1303. }
  1304. /**
  1305. * Get an array of qids applicable to this node type.
  1306. *
  1307. * @param $type
  1308. * The node type.
  1309. * @param $account
  1310. * The account to test against. Defaults to the currently logged in user.
  1311. *
  1312. * @return $qids
  1313. * An array in the format: @code { array($qid => array('qid' => $qid, 'show_in_tab' '
  1314. * => true/false, 'show_in_links' => true/false }
  1315. *
  1316. * @param $bypass_cache
  1317. * Boolean value indicating whether to bypass the cache or not.
  1318. */
  1319. function nodequeue_get_qids($type, $account = NULL, $bypass_cache = FALSE) {
  1320. if (!isset($account)) {
  1321. global $user;
  1322. $account = $user;
  1323. }
  1324. static $cache = array();
  1325. if ($bypass_cache || !isset($cache[$type])) {
  1326. $roles_join = $roles_where = '';
  1327. $roles = array();
  1328. // superuser always has access.
  1329. if (!user_access('manipulate all queues', $account)) {
  1330. $roles_join = "INNER JOIN {nodequeue_roles} nr ON nr.qid = nq.qid ";
  1331. $roles = array_keys((array) $account->roles) + array(DRUPAL_AUTHENTICATED_RID);
  1332. $role_args = array_fill(0, count($roles), '%d');
  1333. $roles_where .= "AND nr.rid IN (". implode(',', $role_args) .")";
  1334. }
  1335. $sql = 'SELECT nq.qid, nq.show_in_tab, nq.show_in_links, show_in_ui ' .
  1336. 'FROM {nodequeue_queue} nq ' .
  1337. 'INNER JOIN {nodequeue_types} nt ON nt.qid = nq.qid ' . $roles_join .
  1338. "WHERE nt.type = '%s' " . $roles_where;
  1339. $result = db_query($sql, array_merge(array($type), $roles));
  1340. $qids = array();
  1341. while ($qid = db_fetch_object($result)) {
  1342. $qids[$qid->qid] = $qid;
  1343. }
  1344. $cache[$type] = $qids;
  1345. }
  1346. return $cache[$type];
  1347. }
  1348. /**
  1349. * Get an array all qids using the pager query. This administrative list
  1350. * does no permission checking, so should only be available to users who
  1351. * have passed the 'administer queues' check.
  1352. *
  1353. * @param $page_size
  1354. * The page size to use. If 0 will be all queues.
  1355. * @param $pager_element
  1356. * In the rare event this should use another pager element, set this..
  1357. *
  1358. * @return $qids
  1359. * An array in the format: @code { array($qid => array('qid' => $qid, 'show_in_tab' '
  1360. * => true/false, 'show_in_links' => true/false }
  1361. */
  1362. function nodequeue_get_all_qids($page_size = 25, $pager_element = 0) {
  1363. static $cache = NULL;
  1364. if (!isset($cache)) {
  1365. $sql = 'SELECT nq.qid ' .
  1366. 'FROM {nodequeue_queue} nq ' .
  1367. 'WHERE nq.show_in_ui = 1 ';
  1368. $count_sql = 'SELECT COUNT(q.qid) FROM {nodequeue_queue} q WHERE q.show_in_ui = 1 ';
  1369. if ($page_size) {
  1370. $result = pager_query($sql, $page_size, $pager_element, $count_sql);
  1371. }
  1372. else {
  1373. $result = db_query($sql, $count_sql);
  1374. }
  1375. $qids = array();
  1376. while ($qid = db_fetch_object($result)) {
  1377. $qids[$qid->qid] = $qid->qid;
  1378. }
  1379. $cache = $qids;
  1380. }
  1381. return $cache;
  1382. }
  1383. /**
  1384. * Load an array of $qids.
  1385. *
  1386. * This exists to provide a way of loading a bunch of queues with
  1387. * the fewest queries. Loading 5 queues results in only 4 queries,
  1388. * not 20. This also caches queues so that they don't get loaded
  1389. * repeatedly.
  1390. *
  1391. * @param $qids
  1392. * An array of queue IDs to load.
  1393. */
  1394. function nodequeue_load_queues($qids) {
  1395. static $cache = array();
  1396. $to_load = $queues = array();
  1397. foreach ($qids as $qid) {
  1398. if (!isset($cache[$qid])) {
  1399. $to_load[] = $qid;
  1400. }
  1401. }
  1402. if (!empty($to_load)) {
  1403. $load_text = implode(', ', $to_load);
  1404. $result = db_query("SELECT q.*, COUNT(s.sqid) AS subqueues FROM {nodequeue_queue} q LEFT JOIN {nodequeue_subqueue} s ON q.qid = s.qid WHERE q.qid IN (%s) GROUP BY q.qid", $load_text);
  1405. while ($queue = db_fetch_object($result)) {
  1406. $cache[$queue->qid] = $queue;
  1407. }
  1408. $result = db_query("SELECT * FROM {nodequeue_roles} WHERE qid IN (%s)", $load_text);
  1409. while ($obj = db_fetch_object($result)) {
  1410. $cache[$obj->qid]->roles[] = $obj->rid;
  1411. }
  1412. $result = db_query("SELECT * FROM {nodequeue_types} WHERE qid IN (%s)", $load_text);
  1413. while ($obj = db_fetch_object($result)) {
  1414. $cache[$obj->qid]->types[] = $obj->type;
  1415. }
  1416. }
  1417. foreach ($qids as $qid) {
  1418. if (isset($cache[$qid])) {
  1419. $queues[$qid] = $cache[$qid];
  1420. }
  1421. }
  1422. return $queues;
  1423. }
  1424. /**
  1425. * Load a nodequeue.
  1426. *
  1427. * @param $qid
  1428. * The qid of the queue to load.
  1429. */
  1430. function nodequeue_load($qid) {
  1431. $queues = nodequeue_load_queues(array($qid));
  1432. return array_shift($queues);
  1433. }
  1434. /**
  1435. * Load a list of subqueues
  1436. *
  1437. * This exists to provide a way of loading a bunch of queues with
  1438. * the fewest queries. Loading 5 queues results in only 4 queries,
  1439. * not 20. This also caches queues so that they don't get loaded
  1440. * repeatedly.
  1441. *
  1442. * @param $sqids
  1443. * An array of subqueue IDs to load.
  1444. * @param $bypass_cache
  1445. * Boolean value indicating whether to bypass the cache or not.
  1446. */
  1447. function nodequeue_load_subqueues($sqids, $bypass_cache = FALSE) {
  1448. static $cache = array();
  1449. $to_load = array();
  1450. foreach ($sqids as $sqid) {
  1451. if ($bypass_cache || !isset($cache[$sqid])) {
  1452. $to_load[] = $sqid;
  1453. }
  1454. }
  1455. if (!empty($to_load)) {
  1456. $load_text = implode(', ', $to_load);
  1457. $result = db_query("SELECT s.*, COUNT(n.position) AS count FROM {nodequeue_subqueue} s LEFT JOIN {nodequeue_nodes} n ON n.sqid = s.sqid WHERE s.sqid IN (%s) GROUP BY s.sqid", $load_text);
  1458. while ($obj = db_fetch_object($result)) {
  1459. // Sometimes we want to get to subqueues by reference, sometimes by sqid.
  1460. // sqid is always unique, but reference is sometimes more readily available.
  1461. $cache[$obj->sqid] = $obj;
  1462. }
  1463. }
  1464. foreach ($sqids as $sqid) {
  1465. if (isset($cache[$sqid])) {
  1466. $subqueues[$sqid] = $cache[$sqid];
  1467. }
  1468. }
  1469. return $subqueues;
  1470. }
  1471. /**
  1472. * Load a single subqueue.
  1473. *
  1474. * @param $sqid
  1475. * The subqueue ID to load.
  1476. * @param $bypass_cache
  1477. * Boolean value indicating whether to bypass the cache or not.
  1478. */
  1479. function nodequeue_load_subqueue($sqid, $bypass_cache = FALSE) {
  1480. $subqueues = nodequeue_load_subqueues(array($sqid), $bypass_cache);
  1481. if ($subqueues) {
  1482. return array_shift($subqueues);
  1483. }
  1484. }
  1485. /**
  1486. * Load the entire set of subqueues for a queue.
  1487. *
  1488. * This will load the entire set of subqueues for a given queue (and can
  1489. * respect the pager, if desired). It does NOT cache the subqueues like
  1490. * nodequeue_load_subqueues does, so beware of this mixed caching.
  1491. *
  1492. * @param $qids
  1493. * A $qid or array of $qids
  1494. * @param $page_size
  1495. * If non-zero, use the pager_query and limit the page-size to the parameter.
  1496. */
  1497. function nodequeue_load_subqueues_by_queue($qids, $page_size = 0) {
  1498. if (is_numeric($qids)) {
  1499. $qids = array($qids);
  1500. }
  1501. if (empty($qids)) {
  1502. return array();
  1503. }
  1504. $query = "SELECT s.*, COUNT(n.position) AS count FROM {nodequeue_subqueue} s LEFT JOIN {nodequeue_nodes} n ON n.sqid = s.sqid WHERE s.qid IN (" . implode(', ', array_fill(0, count($qids), '%d')) . ") GROUP BY s.sqid";
  1505. if ($page_size) {
  1506. $result = pager_query($query, $page_size, 0, $qids);
  1507. }
  1508. else {
  1509. $result = db_query($query, $qids);
  1510. }
  1511. $subqueues = array();
  1512. while ($subqueue = db_fetch_object($result)) {
  1513. $subqueues[$subqueue->sqid] = $subqueue;
  1514. }
  1515. return $subqueues;
  1516. }
  1517. /**
  1518. * Load a set of subqueues by reference.
  1519. *
  1520. * This can be used to load a set of subqueues by reference; it will primarily
  1521. * be used by plugins that are managing subqueues.
  1522. *
  1523. * @param $references
  1524. * A keyed array of references to load. The key is the $qid and each value
  1525. * is another array of references.
  1526. */
  1527. function nodequeue_load_subqueues_by_reference($references) {
  1528. static $cache = array();
  1529. $subqueues = array();
  1530. // build strings for the query based upon the qids and references.
  1531. $keys = $values = array();
  1532. foreach ($references as $qid => $qid_references) {
  1533. $keys[$qid] = array();
  1534. $qid_values = array();
  1535. foreach ($qid_references as $reference) {
  1536. // If we already have this qid/reference combo cached, don't add it to
  1537. // our little list.
  1538. if (isset($cache[$qid][$reference])) {
  1539. $subqueues[$cache[$qid][$reference]->sqid] = $cache[$qid][$reference];
  1540. }
  1541. else {
  1542. $keys[$qid][] = "'%s'";
  1543. $qid_values[] = $reference;
  1544. }
  1545. }
  1546. if (!empty($keys[$qid])) {
  1547. $values = array_merge($values, array($qid), $qid_values);
  1548. }
  1549. else {
  1550. unset($keys[$qid]);
  1551. }
  1552. }
  1553. if (!empty($keys)) {
  1554. $where = '';
  1555. foreach ($keys as $key_list) {
  1556. if ($where) {
  1557. $where .= ' OR ';
  1558. }
  1559. $where .= 's.qid = %d AND s.reference IN (' . implode(', ', $key_list) . ')';
  1560. }
  1561. $result = db_query("SELECT s.*, COUNT(n.position) AS count FROM {nodequeue_subqueue} s LEFT JOIN {nodequeue_nodes} n ON n.sqid = s.sqid WHERE $where GROUP BY s.sqid", $values);
  1562. while ($subqueue = db_fetch_object($result)) {
  1563. $cache[$subqueue->qid][$subqueue->reference] = $subqueues[$subqueue->sqid] = $subqueue;
  1564. }
  1565. }
  1566. return $subqueues;
  1567. }
  1568. /**
  1569. * Save a nodequeue. This does not save subqueues; those must be added separately.
  1570. */
  1571. function nodequeue_save(&$queue) {
  1572. if (!isset($queue->qid)) {
  1573. $queue->qid = db_next_id("{nodequeue_queue}_qid");
  1574. db_query("INSERT INTO {nodequeue_queue} (qid, title, subqueue_title, size, link, link_remove, owner, show_in_links, show_in_tab, show_in_ui, reverse, reference) VALUES (%d, '%s', '%s', %d, '%s', '%s', '%s', %d, %d, %d, %d, '%s')", $queue->qid, $queue->title, $queue->subqueue_title, $queue->size, $queue->link, $queue->link_remove, $queue->owner, $queue->show_in_links, $queue->show_in_tab, $queue->show_in_ui, $queue->reverse, $queue->reference);
  1575. if (function_exists('views_invalidate_cache')) {
  1576. views_invalidate_cache();
  1577. }
  1578. }
  1579. else {
  1580. db_query("UPDATE {nodequeue_queue} set size = %d, title = '%s', subqueue_title = '%s', link = '%s', link_remove = '%s', owner = '%s', show_in_links = %d, show_in_tab = %d, show_in_ui = %d, reverse = %d, reference = '%s' WHERE qid = %d", $queue->size, $queue->title, $queue->subqueue_title, $queue->link, $queue->link_remove, $queue->owner, $queue->show_in_links, $queue->show_in_tab, $queue->show_in_ui, $queue->reverse, $queue->reference, $queue->qid);
  1581. db_query("DELETE FROM {nodequeue_roles} WHERE qid = %d", $queue->qid);
  1582. db_query("DELETE FROM {nodequeue_types} WHERE qid = %d", $queue->qid);
  1583. }
  1584. if (is_array($queue->roles)) {
  1585. foreach($queue->roles as $rid)
  1586. db_query("INSERT INTO {nodequeue_roles} (qid, rid) VALUES (%d, %d)", $queue->qid, $rid);
  1587. }
  1588. if (is_array($queue->types)) {
  1589. foreach($queue->types as $type)
  1590. db_query("INSERT INTO {nodequeue_types} (qid, type) VALUES (%d, '%s')", $queue->qid, $type);
  1591. }
  1592. // set our global that tells us whether or not we need to activate hook_link
  1593. if (db_result(db_query("SELECT COUNT(*) FROM {nodequeue_queue} WHERE link != ''"))) {
  1594. variable_set('nodequeue_links', TRUE);
  1595. }
  1596. else {
  1597. variable_set('nodequeue_links', FALSE);
  1598. }
  1599. if (isset($queue->add_subqueue) && is_array($queue->add_subqueue)) {
  1600. foreach ($queue->add_subqueue as $reference => $title) {
  1601. // If reference is unset it should be set to the qid; this is generally
  1602. // used for a single subqueue; setting the reference to the qid makes
  1603. // it easy to find that one subqueue.
  1604. if ($reference == 0) {
  1605. $reference = $queue->qid;
  1606. }
  1607. nodequeue_add_subqueue($queue, $title, $reference);
  1608. }
  1609. }
  1610. return $queue->qid;
  1611. }
  1612. /**
  1613. * Delete a nodequeue.
  1614. */
  1615. function nodequeue_delete($qid) {
  1616. db_query("DELETE FROM {nodequeue_queue} WHERE qid = %d", $qid);
  1617. db_query("DELETE FROM {nodequeue_roles} WHERE qid = %d", $qid);
  1618. db_query("DELETE FROM {nodequeue_types} WHERE qid = %d", $qid);
  1619. db_query("DELETE FROM {nodequeue_nodes} WHERE qid = %d", $qid);
  1620. db_query("DELETE FROM {nodequeue_subqueue} WHERE qid = %d", $qid);
  1621. }
  1622. /**
  1623. * Add a new subqueue to a queue.
  1624. *
  1625. * @param $qid
  1626. * The queue id. This should not be the full queue object.
  1627. * @param $reference
  1628. * A reference that uniquely identifies this subqueue. If NULL it will
  1629. * be assigned to the sqid.
  1630. */
  1631. function nodequeue_add_subqueue(&$queue, $title, $reference = NULL) {
  1632. $sqid = db_next_id('{nodequeue_subqueue}_sqid');
  1633. if (empty($reference)) {
  1634. $reference = $sqid;
  1635. }
  1636. $subqueue = new stdClass();
  1637. $subqueue->sqid = $sqid;
  1638. $subqueue->reference = $reference;
  1639. $subqueue->qid = $queue->qid;
  1640. $subqueue->title = $title;
  1641. db_query("INSERT INTO {nodequeue_subqueue} (qid, sqid, reference, title) VALUES (%d, %d, '%s', '%s')", $queue->qid, $sqid, $reference, $title);
  1642. return $subqueue;
  1643. }
  1644. /**
  1645. * Change the title of a subqueue.
  1646. *
  1647. * Note that only the title of a subqueue is changeable; it can change to
  1648. * reflect updates in taxonomy term names, for example.
  1649. */
  1650. function nodequeue_subqueue_update_title($sqid, $title) {
  1651. db_query("UPDATE {nodequeue_subqueue} SET title = '%s' WHERE sqid = %d", $title, $sqid);
  1652. }
  1653. /**
  1654. * Remove a subqueue.
  1655. */
  1656. function nodequeue_remove_subqueue($sqid) {
  1657. nodequeue_queue_clear($sqid);
  1658. db_query("DELETE FROM {nodequeue_subqueue} WHERE sqid = %d", $sqid);
  1659. }
  1660. // --------------------------------------------------------------------------
  1661. // Queue position control
  1662. /**
  1663. * Add a node to a queue.
  1664. *
  1665. * @param $queue
  1666. * The parent queue of the subqueue. This is required so that we can
  1667. * pop nodes out if the queue breaks size limits.
  1668. * @param $sqid
  1669. * The subqueue ID to add the node to.
  1670. * @param $nid
  1671. * The node ID
  1672. */
  1673. function nodequeue_subqueue_add($queue, $subqueue, $nid) {
  1674. // If adding this would make the queue too big, pop the front node
  1675. // (or nodes) out.
  1676. if ($queue->size) {
  1677. // 0 means infinity so never do this if false
  1678. nodequeue_check_subqueue_size($queue, $subqueue, $queue->size - 1);
  1679. }
  1680. db_query("INSERT INTO {nodequeue_nodes} (sqid, qid, nid, position, timestamp) VALUES (%d, %d, %d, %d, %d)", $subqueue->sqid, $queue->qid, $nid, $subqueue->count + 1, time());
  1681. }
  1682. /**
  1683. * Remove a node from the queue. If a node is in the queue more than once,
  1684. * only the first (closest to 0 position, or the front of the queue) will
  1685. * be removed.
  1686. *
  1687. * @param $sqid
  1688. * The subqueue to remove nodes from.
  1689. * @param $nid
  1690. * The node to remove.
  1691. */
  1692. function nodequeue_subqueue_remove_node($sqid, $nid) {
  1693. if ($pos = nodequeue_get_subqueue_position($sqid, $nid)) {
  1694. nodequeue_subqueue_remove($sqid, $pos);
  1695. }
  1696. }
  1697. /**
  1698. * Remove a node or node(s) from a nodequeue by position.
  1699. *
  1700. * If you know the nid but but not the position, use
  1701. * @see nodequeue_subqueue_remove_node() instead.
  1702. *
  1703. * @param $sqid
  1704. * The subqueue to remove nodes from.
  1705. * @param $start
  1706. * The first position (starting from 1) to remove.
  1707. * @param $end
  1708. * The last position to remove. If NULL or equal to $start,
  1709. * only one node will be removed. Thus if $start is 1 and $end is 2,
  1710. * the first and second items will be removed from the queue.
  1711. *
  1712. */
  1713. function nodequeue_subqueue_remove($sqid, $start, $end = NULL) {
  1714. if (!isset($end)) {
  1715. $end = $start;
  1716. }
  1717. $diff = $end - $start + 1;
  1718. db_query("DELETE FROM {nodequeue_nodes} WHERE sqid = %d AND position >= %d AND position <= %d", $sqid, $start, $end);
  1719. db_query("UPDATE {nodequeue_nodes} SET position = position - %d WHERE sqid = %d AND position > %d", $diff, $sqid, $end);
  1720. }
  1721. /**
  1722. * Empty a subqueue.
  1723. *
  1724. * @param $sqid
  1725. * The sqid to empty.
  1726. */
  1727. function nodequeue_queue_clear($sqid) {
  1728. db_query("DELETE FROM {nodequeue_nodes} WHERE sqid = %d", $sqid);
  1729. }
  1730. /**
  1731. * Guarantee that a subqueue has not gotten too big. It's important to call
  1732. * this after an operation that might have reduced a queue's maximum size.
  1733. * It stores the count to save a query if this is to be followed by an add
  1734. * operation.
  1735. *
  1736. * @param $queue
  1737. * The queue object.
  1738. * @param $reference
  1739. * The subqueue to check.
  1740. *
  1741. */
  1742. function nodequeue_check_subqueue_size($queue, &$subqueue, $size = NULL) {
  1743. if (!isset($size)) {
  1744. $size = $queue->size;
  1745. }
  1746. if ($queue->size && $subqueue->count > $size) {
  1747. nodequeue_subqueue_remove($subqueue->sqid, 1, $subqueue->count - $size);
  1748. $subqueue->count = $size;
  1749. }
  1750. }
  1751. /**
  1752. * Guarantee that all subqueues are within the size constraints set
  1753. * by $queue->size.
  1754. */
  1755. function nodequeue_check_subqueue_sizes($queue) {
  1756. // Don't check if size is 0, as that means infinite size.
  1757. if (!$queue->size) {
  1758. return;
  1759. }
  1760. $subqueues = nodequeue_load_subqueues_by_queue($queue->qid);
  1761. foreach ($subqueues as $subqueue) {
  1762. nodequeue_check_subqueue_size($queue, $subqueue);
  1763. }
  1764. }
  1765. /**
  1766. * Swap two positions within a subqueue.
  1767. */
  1768. function nodequeue_queue_swap($subqueue, $pos1, $pos2) {
  1769. // Grab the nid off one of the positions so we can more easily swap.
  1770. $nid = db_result(db_query("SELECT nid FROM {nodequeue_nodes} WHERE sqid = %d AND position = %d", $subqueue->sqid, $pos1));
  1771. if (!$nid) {
  1772. return;
  1773. }
  1774. db_query("UPDATE {nodequeue_nodes} SET position = %d WHERE position = %d AND sqid = %d", $pos1, $pos2, $subqueue->sqid);
  1775. db_query("UPDATE {nodequeue_nodes} SET position = %d WHERE nid = %d AND sqid = %d", $pos2, $nid, $subqueue->sqid);
  1776. }
  1777. /**
  1778. * Move a position within a subqueue up by one.
  1779. */
  1780. function nodequeue_queue_up($subqueue, $position) {
  1781. if ($position < 2 || $position > $subqueue->count)
  1782. return;
  1783. nodequeue_queue_swap($subqueue, $position - 1, $position);
  1784. }
  1785. /**
  1786. * Move a position within a subqueue down by one.
  1787. */
  1788. function nodequeue_queue_down($subqueue, $position) {
  1789. if ($position < 1 || $position >= $subqueue->count)
  1790. return;
  1791. nodequeue_queue_swap($subqueue, $position + 1, $position);
  1792. }
  1793. /**
  1794. * Move an item to the front of the queue.
  1795. */
  1796. function nodequeue_queue_front($subqueue, $position) {
  1797. if ($position < 2 || $position > $subqueue->count)
  1798. return;
  1799. $entry = db_fetch_object(db_query("SELECT * FROM {nodequeue_nodes} WHERE sqid= %d AND position = %d", $subqueue->sqid, $position));
  1800. db_query("DELETE FROM {nodequeue_nodes} WHERE sqid = %d AND position = %d", $subqueue->sqid, $position);
  1801. db_query("UPDATE {nodequeue_nodes} SET position = position + 1 WHERE sqid= %d AND position < %d", $subqueue->sqid, $position);
  1802. db_query("INSERT INTO {nodequeue_nodes} (qid, sqid, nid, position, timestamp) VALUES (%d, %d, %d, 1, %d)", $entry->qid, $subqueue->sqid, $entry->nid, $entry->timestamp);
  1803. }
  1804. /**
  1805. * Move an item to the back of the queue.
  1806. */
  1807. function nodequeue_queue_back($subqueue, $position) {
  1808. if ($position < 1 || $position >= $subqueue->count)
  1809. return;
  1810. $entry = db_fetch_object(db_query("SELECT * FROM {nodequeue_nodes} WHERE sqid = %d AND position = %d", $subqueue->sqid, $position));
  1811. db_query("DELETE FROM {nodequeue_nodes} WHERE sqid = %d AND position = %d", $subqueue->sqid, $position);
  1812. db_query("UPDATE {nodequeue_nodes} SET position = position - 1 WHERE sqid = %d AND position > %d", $subqueue->sqid, $position);
  1813. db_query("INSERT INTO {nodequeue_nodes} (qid, sqid, nid, position, timestamp) VALUES (%d, %d, %d, %d, %d)", $entry->qid, $subqueue->sqid, $entry->nid, $subqueue->count, $entry->timestamp);
  1814. }
  1815. /**
  1816. * Get the position of a node in a subqueue, or 0 if not found.
  1817. */
  1818. function nodequeue_get_subqueue_position($sqid, $nid) {
  1819. // We use MIN to make sure we always get the closes to the front of the
  1820. // queue in case the queue has nodes in it multiple times.
  1821. $pos = db_result(db_query("SELECT MIN(position) FROM {nodequeue_nodes} WHERE sqid = %d AND nid = %d", $sqid, $nid));
  1822. return $pos;
  1823. }
  1824. /**
  1825. * Get the position of a node in several subqueues.
  1826. */
  1827. function nodequeue_set_subqueue_positions(&$subqueues, $nid) {
  1828. $result = db_query("SELECT sqid, MIN(position) AS position FROM {nodequeue_nodes} WHERE sqid IN (%s) AND nid = %d GROUP BY sqid", implode(', ', array_keys($subqueues)), $nid);
  1829. while ($obj = db_fetch_object($result)) {
  1830. $subqueues[$obj->sqid]->position = $obj->position;
  1831. }
  1832. }
  1833. /**
  1834. * Get a list of valid subqueues for a node, along with the position of the node.
  1835. *
  1836. * @param $queues
  1837. * An array of fully loaded queue objects.
  1838. * @param $node
  1839. * A fully loaded node object.
  1840. *
  1841. */
  1842. function nodequeue_get_subqueues_by_node($queues, $node) {
  1843. // Determine which subqueues are valid for each queue.
  1844. $references= array();
  1845. foreach ($queues as $queue) {
  1846. if ($result = nodequeue_api_subqueues($queue, $node)) {
  1847. $references[$queue->qid] = is_array($result) ? $result : array($result);
  1848. }
  1849. }
  1850. if (empty($references)) {
  1851. return;
  1852. }
  1853. return nodequeue_load_subqueues_by_reference($references);
  1854. }
  1855. /**
  1856. * Get a textual representation of a nodequeue's queue size.
  1857. */
  1858. function nodequeue_subqueue_size_text($max, $count, $long = TRUE) {
  1859. if (empty($count)) {
  1860. $message = theme('nodequeue_subqueue_empty_text');
  1861. }
  1862. else if ($count == $max) {
  1863. $message = theme('nodequeue_subqueue_full_text');
  1864. }
  1865. else {
  1866. if ($long) {
  1867. $message = theme('nodequeue_subqueue_count_text', $count);
  1868. }
  1869. else {
  1870. $message = $count;
  1871. }
  1872. }
  1873. return $message;
  1874. }
  1875. function theme_nodequeue_subqueue_empty_text() {
  1876. return t('Queue empty');
  1877. }
  1878. function theme_nodequeue_subqueue_full_text() {
  1879. return t('Queue full');
  1880. }
  1881. function theme_nodequeue_subqueue_count_text($count) {
  1882. return t('@count in queue', array('@count' => $count));
  1883. }
  1884. /**
  1885. * Substitute the subqueue title into some others tring.
  1886. *
  1887. * This function does NOT check_plain the title! The output MUST be checked
  1888. * after this is complete.
  1889. */
  1890. function nodequeue_title_substitute($text, $queue, $subqueue) {
  1891. if (empty($text)) {
  1892. return $subqueue->title;
  1893. }
  1894. $text = str_replace('%subqueue', $subqueue->title, $text);
  1895. return $text;
  1896. }
  1897. /**
  1898. * Shuffle a queue.
  1899. *
  1900. * @param $subqueue
  1901. * The subqueue to shuffle. May be a sqid or the loaded object.
  1902. */
  1903. function nodequeue_subqueue_shuffle($subqueue) {
  1904. // Load the queue
  1905. if (!is_object($subqueue)) {
  1906. $subqueue = nodequeue_load_subqueue($subqueue);
  1907. }
  1908. if (empty($subqueue)) {
  1909. return;
  1910. }
  1911. $count = $subqueue->count;
  1912. // Swap each item with another randomly picked one.
  1913. foreach (range(1, $count) as $i) {
  1914. nodequeue_queue_swap($subqueue, $i, rand(1, $count));
  1915. }
  1916. }
  1917. /**
  1918. * @} End of defgroup "nodequeue_api"
  1919. */
  1920. // --------------------------------------------------------------------------
  1921. // Hooks to implement the default nodequeue type.
  1922. /**
  1923. * Implementation of hook_nodequeue_info()
  1924. */
  1925. function nodequeue_nodequeue_info() {
  1926. return array('nodequeue' => array(
  1927. 'title' => t('Node queue'),
  1928. 'description' => t('Standard node queues have just one subqueue. Nodes put into a queue are added to the back of the queue; when a node is added to a full queue, the node in the front of the queue will be popped out to make room.'),
  1929. ));
  1930. }
  1931. /**
  1932. * Implementation of hook_nodequeue_form_submit()
  1933. */
  1934. function nodequeue_nodequeue_form_submit(&$queue, $form) {
  1935. // This will add a single subqueue to our new queue.
  1936. if (!isset($queue->qid) && !isset($queue->add_subqueue)) {
  1937. // A 0 will set the reference to the sqid of the queue.
  1938. $queue->add_subqueue = array(0 => $queue->title);
  1939. }
  1940. }
  1941. // --------------------------------------------------------------------------
  1942. // External queue fetching
  1943. /**
  1944. * in general it's preferable to use Views for this functionality.
  1945. */
  1946. function nodequeue_node_titles($sqid, $title = '', $backward = true, $from = 0, $count = 0) {
  1947. $orderby = ($backward ? "DESC" : "ASC");
  1948. $sql = db_rewrite_sql("SELECT n.nid, n.title FROM {node} n LEFT JOIN {nodequeue_nodes} nn ON n.nid = nn.nid WHERE nn.sqid = %d AND n.status = 1 ORDER BY nn.position $orderby");
  1949. if ($count) {
  1950. $result = db_query_range($sql, $sqid, $from, $count);
  1951. }
  1952. else {
  1953. $result = db_query($sql, $sqid);
  1954. }
  1955. return node_title_list($result, $title);
  1956. }
  1957. function nodequeue_nodes($sqid, $backward = true, $teaser = true, $links = true, $from = 0, $count = 0) {
  1958. $orderby = ($backward ? "DESC" : "ASC");
  1959. $sql = db_rewrite_sql("SELECT n.nid FROM {node} n INNER JOIN {nodequeue_nodes} nn ON n.nid = nn.nid WHERE nn.sqid = %d AND n.status = 1 ORDER BY nn.position $orderby");
  1960. if ($count) {
  1961. $result = db_query_range($sql, $sqid, $from, $count);
  1962. }
  1963. else {
  1964. $result = db_query($sql, $sqid);
  1965. }
  1966. $nodes = array();
  1967. while ($nid = db_fetch_object($result)) {
  1968. $node = node_load($nid->nid);
  1969. $output .= node_view($node, $teaser, false, $links);
  1970. }
  1971. return $output;
  1972. }
  1973. function nodequeue_fetch_front($sqid, $teaser = true, $links = true) {
  1974. return nodequeue_nodes($sqid, false, $teaser, $links, 0, 1);
  1975. }
  1976. function nodequeue_fetch_back($sqid, $teaser = true, $links = true) {
  1977. return nodequeue_nodes($sqid, true, $teaser, $links, 0, 1);
  1978. }
  1979. function nodequeue_fetch_random($sqid, $teaser = true, $links = true) {
  1980. $count = db_result(db_query(db_rewrite_sql("SELECT COUNT(n.nid) FROM {node} n INNER JOIN {nodequeue_nodes} nn ON n.nid = nn.nid WHERE nn.sqid = %d AND n.status = 1"), $sqid));
  1981. return nodequeue_nodes($sqid, false, $teaser, $links, rand(0, $count - 1), 1);
  1982. }
  1983. /**
  1984. * Get the position of a node in a subqueue, or FALSE if not found.
  1985. */
  1986. function nodequeue_subqueue_position($sqid, $nid) {
  1987. return db_result(db_query("SELECT position FROM {nodequeue_nodes} WHERE sqid = %d AND nid = %d", $sqid, $nid));
  1988. }
  1989. /**
  1990. * Get the position of a node in a queue; this queue MUST have only one
  1991. * subqueue or the results of this function will be unpredictable.
  1992. */
  1993. function nodequeue_queue_position($qid, $nid) {
  1994. $sqid = db_result(db_query("SELECT sqid FROM {nodequeue_subqueue} WHERE qid = %d LIMIT 1", $qid));
  1995. return nodequeue_subqueue_position($sqid, $nid);
  1996. }
  1997. // --------------------------------------------------------------------------
  1998. // API for modules implementing subqueues.
  1999. /**
  2000. * Send the nodequeue edit form to the owning module for modification.
  2001. *
  2002. * @param $queue
  2003. * The queue being edited.
  2004. * @param &$form
  2005. * The form. This may be modified.
  2006. */
  2007. function nodequeue_api_queue_form($queue, &$form) {
  2008. $function = $queue->owner . "_nodequeue_form";
  2009. if (function_exists($function)) {
  2010. $function($queue, $form);
  2011. }
  2012. }
  2013. /**
  2014. * Validate the nodequeue edit form.
  2015. *
  2016. * @param $queue
  2017. * The queue being edited.
  2018. * @param $form_values
  2019. * The form values that were submitted.
  2020. * @param &$form
  2021. * The actual form object. This may be modified.
  2022. */
  2023. function nodequeue_api_queue_form_validate($queue, $form_values, &$form) {
  2024. $function = $queue->owner . "_nodequeue_form_validate";
  2025. if (function_exists($function)) {
  2026. $function($queue, $form_values, $form);
  2027. }
  2028. }
  2029. /**
  2030. * Send the nodequeue edit form to the owning module upon submit.
  2031. *
  2032. * @param &$queue
  2033. * The queue being edited. This may be modified prior to being
  2034. * saved.
  2035. * @param $form_values
  2036. * The form values that were submitted.
  2037. */
  2038. function nodequeue_api_queue_form_submit(&$queue, $form_values) {
  2039. $function = $queue->owner . "_nodequeue_form_submit";
  2040. if (function_exists($function)) {
  2041. $function($queue, $form_values);
  2042. }
  2043. }
  2044. /**
  2045. * Send the nodequeue edit form to the owning module after the queue
  2046. * has been saved.
  2047. *
  2048. * @param &$queue
  2049. * The queue being edited. This may be modified prior to being
  2050. * saved.
  2051. * @param $form_values
  2052. * The form values that were submitted.
  2053. */
  2054. function nodequeue_api_queue_form_submit_finish($queue, $form_values) {
  2055. $function = $queue->owner . "_nodequeue_form_submit_finish";
  2056. if (function_exists($function)) {
  2057. $function($queue, $form_values);
  2058. }
  2059. }
  2060. /**
  2061. * Fetch a list of subqueues that are valid for this node from
  2062. * the owning module.
  2063. *
  2064. * @param $queue
  2065. * The queue being edited.
  2066. * @param $node
  2067. * The loaded node object being checked.
  2068. *
  2069. * @return
  2070. * An array of subqueues. This will be keyed by $sqid.
  2071. */
  2072. function nodequeue_api_subqueues(&$queue, $node) {
  2073. $function = $queue->owner . "_nodequeue_subqueues";
  2074. // This will return an array of references.
  2075. if (function_exists($function)) {
  2076. return $function($queue, $node);
  2077. }
  2078. else {
  2079. return $queue->qid;
  2080. }
  2081. }
  2082. /**
  2083. * Fetch a list of nodes available to a given subqueue
  2084. * for autocomplete.
  2085. *
  2086. * @param $queue
  2087. * The queue that owns the subqueue
  2088. * @param $subqueue
  2089. * The subqueue
  2090. * @param $string
  2091. * The string being matched.
  2092. *
  2093. * @return
  2094. * An keyed array $nid => $title
  2095. */
  2096. function nodequeue_api_autocomplete($queue, $subqueue, $string) {
  2097. $matches = array();
  2098. if (empty($string)) {
  2099. return $matches;
  2100. }
  2101. global $user;
  2102. if (!user_access('administer nodes', $user)) {
  2103. $where = 'n.status = 1 AND ';
  2104. }
  2105. $where .= "n.type IN (" . implode(', ', array_fill(0, count($queue->types), "'%s'")) . ')';
  2106. $where_args = $queue->types;
  2107. // Run a match to see if they're specifying by nid.
  2108. $preg_matches = array();
  2109. $match = preg_match('/\[nid: (\d+)\]/', $string, $preg_matches);
  2110. if (!$match) {
  2111. $match = preg_match('/^nid: (\d+)/', $string, $preg_matches);
  2112. }
  2113. if ($match) {
  2114. // If it found a nid via specification, reduce our resultset to just that nid.
  2115. $where .= " AND n.nid = %d";
  2116. array_push($where_args, $preg_matches[1]);
  2117. }
  2118. else {
  2119. // Build the constant parts of the query.
  2120. $where .= " AND LOWER(n.title) LIKE LOWER('%s%%')";
  2121. array_push($where_args, $string);
  2122. }
  2123. // Call to the API.
  2124. $function = $queue->owner . "_nodequeue_autocomplete";
  2125. if (function_exists($function)) {
  2126. return $function($queue, $subqueue, $string, $where, $where_args);
  2127. }
  2128. else {
  2129. $result = db_query_range(db_rewrite_sql("SELECT n.nid, n.title FROM {node} n WHERE $where"), $where_args, 0, 10);
  2130. while ($node = db_fetch_object($result)) {
  2131. $matches[$node->nid] = check_plain($node->title) . " [nid: $node->nid]";
  2132. }
  2133. }
  2134. return $matches;
  2135. }
  2136. /**
  2137. * Collect info about all of the possible nodequeue tyeps from owning
  2138. * modules.
  2139. */
  2140. function nodequeue_api_info() {
  2141. return module_invoke_all('nodequeue_info');
  2142. }
  2143. /**
  2144. * Allows the owning module of a queue to restrict access to viewing and
  2145. * manipulating the queue.
  2146. */
  2147. function nodequeue_api_queue_access($queue, $account = NULL) {
  2148. if (!$account) {
  2149. global $user;
  2150. $account = $user;
  2151. }
  2152. if ($queue->owner != 'nodequeue') { // Avoids an infinite loop.
  2153. $function = $queue->owner . '_queue_access';
  2154. if (function_exists($function)) {
  2155. $access = $function($queue, $account);
  2156. }
  2157. }
  2158. if (!isset($access)) {
  2159. $access = TRUE;
  2160. }
  2161. return $access;
  2162. }
  2163. /**
  2164. * Allows the owning module of a subqueue to restrict access to viewing and
  2165. * manipulating the queue.
  2166. */
  2167. function nodequeue_api_subqueue_access($subqueue, $account = NULL, $queue = NULL) {
  2168. if (!$account) {
  2169. global $user;
  2170. $account = $user;
  2171. }
  2172. if (!$queue) {
  2173. $queue = nodequeue_load($subqueue->qid);
  2174. }
  2175. $function = $queue->owner . '_subqueue_access';
  2176. if (function_exists($function)) {
  2177. $access = $function($subqueue, $account, $queue);
  2178. }
  2179. if (!isset($access)) {
  2180. $access = TRUE;
  2181. }
  2182. return $access;
  2183. }
  2184. /**
  2185. * Form builder for the nodequeue settings tab.
  2186. */
  2187. function nodequeue_admin_settings() {
  2188. $form = array();
  2189. $form['nodequeue_use_tab'] = array(
  2190. '#type' => 'checkbox',
  2191. '#title' => t('Create a menu tab for each node that could belong to any queues'),
  2192. '#default_value' => variable_get('nodequeue_use_tab', 1),
  2193. );
  2194. $form['nodequeue_tab_display_max'] = array(
  2195. '#type' => 'checkbox',
  2196. '#title' => t('Include a column on the nodequeue tab for the maximum number of nodes in each queue'),
  2197. '#default_value' => variable_get('nodequeue_tab_display_max', 1),
  2198. );
  2199. $form['nodequeue_tab_name'] = array(
  2200. '#type' => 'textfield',
  2201. '#title' => t('Node queue tab label'),
  2202. '#default_value' => variable_get('nodequeue_tab_name', t('Node queue')),
  2203. '#description' => t('If nodes will have a menu tab for manipulating related node queues, what should that tab be called?'),
  2204. );
  2205. return system_settings_form($form);
  2206. }
  2207. /**
  2208. * Generate a query string to use on nodequeue's private links.
  2209. *
  2210. * @param $seed
  2211. * The seed to use when generating a token. If NULL no token will
  2212. * be generated.
  2213. * @param $destination
  2214. * The destination to use. If FALSE one won't be used; if TRUE
  2215. * one will be generated from drupal_get_destination().
  2216. * @param $query
  2217. * An array of additional items to add to the query.
  2218. *
  2219. * @return
  2220. * The query string suitable for use in the l() function.
  2221. */
  2222. function nodequeue_get_query_string($seed, $destination = FALSE, $query = array()) {
  2223. if ($dest = drupal_get_destination()) {
  2224. $query[] = $dest;
  2225. }
  2226. if (isset($seed)) {
  2227. $query[] = nodequeue_get_token($seed);
  2228. }
  2229. return implode('&', $query);
  2230. }
  2231. /**
  2232. * Get a private token used to protect nodequeue's links from spoofing.
  2233. */
  2234. function nodequeue_get_token($nid) {
  2235. return 'token=' . drupal_get_token($nid);
  2236. }
  2237. /**
  2238. * Check to see if the token generated from seed matches.
  2239. */
  2240. function nodequeue_check_token($seed) {
  2241. return drupal_get_token($seed) == $_GET['token'];
  2242. }
  2243. /**
  2244. * Implementation of hook_simpletest().
  2245. *
  2246. */
  2247. function nodequeue_simpletest() {
  2248. $dir = drupal_get_path('module', 'nodequeue') .'/tests';
  2249. $tests = file_scan_directory($dir, '\.test$');
  2250. return array_keys($tests);
  2251. }