nodequeue.module 60 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183
  1. <?php
  2. /**
  3. * NOTES
  4. * Here are various notes I've taken about notable changes and/or ommissions
  5. *
  6. * - Everything has been moved to admin/structure/nodequeue which seemed the
  7. * most appropriate destination now that admin/content is out of the picture.
  8. *
  9. * - None of the PagerQueries are working, they're all just normal queries for
  10. * the time being.
  11. */
  12. /**
  13. * @file
  14. * Maintains queues of nodes in arbitrary order.
  15. */
  16. define('NODEQUEUE_OK', 0);
  17. define('NODEQUEUE_INVALID_POSITION', 1);
  18. define('NODEQUEUE_INVALID_NID', 2);
  19. define('NODEQUEUE_DUPLICATE_POSITION', 3);
  20. /* --- HOOKS ---------------------------------------------------------------- */
  21. /**
  22. * Implements hook_permission().
  23. */
  24. function nodequeue_permission() {
  25. return array(
  26. 'administer nodequeue' => array(
  27. 'title' => t('Administer nodequeue'),
  28. 'description' => t('Administer the nodequeue module.'),
  29. ),
  30. 'manipulate queues' => array(
  31. 'title' => t('Manipulate queues'),
  32. 'description' => t('Manipulate queues.'),
  33. ),
  34. 'manipulate all queues' => array(
  35. 'title' => t('Manipulate all queues'),
  36. 'description' => t('Manipulate all queues.'),
  37. ),
  38. );
  39. }
  40. /**
  41. * Implements hook_init().
  42. *
  43. * Loads subsidiary includes for other modules.
  44. */
  45. function nodequeue_init() {
  46. include_once DRUPAL_ROOT . '/' . drupal_get_path('module', 'nodequeue') . '/includes/nodequeue.actions.inc';
  47. }
  48. /**
  49. * Implements hook_menu().
  50. */
  51. function nodequeue_menu() {
  52. $items = array();
  53. $admin_access = array('administer nodequeue');
  54. $access = array('manipulate queues');
  55. // administrative items
  56. $items['admin/structure/nodequeue'] = array(
  57. 'title' => 'Nodequeues',
  58. 'page callback' => 'nodequeue_view_queues',
  59. 'access callback' => '_nodequeue_access_admin_or_manipulate',
  60. 'description' => 'Create and maintain simple nodequeues.',
  61. 'file' => 'includes/nodequeue.admin.inc',
  62. 'type' => MENU_NORMAL_ITEM
  63. );
  64. $items['admin/structure/nodequeue/list'] = array(
  65. 'title' => 'List',
  66. 'page callback' => 'nodequeue_view_queues',
  67. 'access callback' => '_nodequeue_access_admin_or_manipulate',
  68. 'file' => 'includes/nodequeue.admin.inc',
  69. 'weight' => -1,
  70. 'type' => MENU_DEFAULT_LOCAL_TASK
  71. );
  72. $items['admin/structure/nodequeue/settings'] = array(
  73. 'title' => 'Settings',
  74. 'page callback' => 'drupal_get_form',
  75. 'page arguments' => array('nodequeue_admin_settings'),
  76. 'access arguments' => $admin_access,
  77. 'file' => 'includes/nodequeue.admin.inc',
  78. 'type' => MENU_LOCAL_TASK
  79. );
  80. $items['nodequeue/autocomplete'] = array(
  81. 'title' => 'Autocomplete',
  82. 'page callback' => 'nodequeue_autocomplete',
  83. 'access arguments' => $access,
  84. 'file' => 'includes/nodequeue.admin.inc',
  85. 'type' => MENU_CALLBACK
  86. );
  87. $info = nodequeue_api_info();
  88. foreach ($info as $key => $data) {
  89. $items['admin/structure/nodequeue/add/' . $key] = array(
  90. 'title' => 'Add @type',
  91. 'title arguments' => array('@type' => strtolower($data['title'])),
  92. 'page callback' => 'drupal_get_form',
  93. 'page arguments' => array('nodequeue_edit_queue_form', $key),
  94. 'access arguments' => $admin_access,
  95. 'file' => 'includes/nodequeue.admin.inc',
  96. 'type' => MENU_LOCAL_ACTION
  97. );
  98. }
  99. $items['node/%node/nodequeue'] = array(
  100. 'title' => '@tab',
  101. 'title arguments' => array('@tab' => variable_get('nodequeue_tab_name', 'Nodequeue')),
  102. 'page callback' => 'nodequeue_node_tab',
  103. 'page arguments' => array(1),
  104. 'access callback' => 'nodequeue_node_tab_access',
  105. 'access arguments' => array(1),
  106. 'file' => 'includes/nodequeue.admin.inc',
  107. 'weight' => 5,
  108. 'type' => MENU_LOCAL_TASK
  109. );
  110. // Administrative items for an individual queue.
  111. $items['admin/structure/nodequeue/%nodequeue'] = array(
  112. 'page callback' => 'nodequeue_admin_view',
  113. 'page arguments' => array(3),
  114. 'access callback' => 'nodequeue_queue_access',
  115. 'access arguments' => array(3),
  116. 'file' => 'includes/nodequeue.admin.inc',
  117. 'type' => MENU_CALLBACK
  118. );
  119. $items['admin/structure/nodequeue/%nodequeue/view'] = array(
  120. 'title' => 'View',
  121. 'page callback' => 'nodequeue_admin_view',
  122. 'page arguments' => array(3),
  123. 'access callback' => 'nodequeue_queue_access',
  124. 'access arguments' => array(3),
  125. 'file' => 'includes/nodequeue.admin.inc',
  126. 'weight' => -10,
  127. 'type' => MENU_DEFAULT_LOCAL_TASK
  128. );
  129. $items['admin/structure/nodequeue/%nodequeue/view/%subqueue'] = array(
  130. 'title' => 'View',
  131. 'page callback' => 'nodequeue_admin_view',
  132. 'page arguments' => array(3, 5),
  133. 'access callback' => 'nodequeue_queue_access',
  134. 'access arguments' => array(3, 5),
  135. 'file' => 'includes/nodequeue.admin.inc',
  136. 'weight' => -10,
  137. 'tab parent' => 'admin/structure/nodequeue/%',
  138. 'type' => MENU_CALLBACK
  139. );
  140. // Actual administrative items.
  141. $items['admin/structure/nodequeue/%nodequeue/edit'] = array(
  142. 'title' => 'Edit queue',
  143. 'page callback' => 'drupal_get_form',
  144. 'page arguments' => array('nodequeue_edit_queue_form', 3),
  145. 'access arguments' => $admin_access,
  146. 'file' => 'includes/nodequeue.admin.inc',
  147. 'type' => MENU_LOCAL_TASK
  148. );
  149. $items['admin/structure/nodequeue/%nodequeue/delete'] = array(
  150. 'title' => 'Delete',
  151. 'page callback' => 'drupal_get_form',
  152. 'page arguments' => array('nodequeue_admin_delete', 3),
  153. 'access arguments' => $admin_access,
  154. 'file' => 'includes/nodequeue.admin.inc',
  155. 'weight' => 5,
  156. 'type' => MENU_CALLBACK
  157. );
  158. $items['nodequeue/%nodequeue/add-node/%subqueue/%node'] = array(
  159. 'page callback' => 'nodequeue_admin_add_node',
  160. 'page arguments' => array(1, 3, 4),
  161. 'access callback' => 'nodequeue_node_and_queue_access',
  162. 'access arguments' => array(4, 1, 3),
  163. 'file' => 'includes/nodequeue.admin.inc',
  164. 'type' => MENU_CALLBACK
  165. );
  166. $items['nodequeue/%nodequeue/remove-node/%subqueue/%node'] = array(
  167. 'page callback' => 'nodequeue_admin_remove_node',
  168. 'page arguments' => array(1, 3, 4),
  169. 'access callback' => 'nodequeue_node_and_queue_access',
  170. 'access arguments' => array(4, 1, 3),
  171. 'file' => 'includes/nodequeue.admin.inc',
  172. 'type' => MENU_CALLBACK
  173. );
  174. $items["admin/structure/nodequeue/%nodequeue/clear/%subqueue"] = array(
  175. 'title' => 'Clear',
  176. 'page callback' => 'drupal_get_form',
  177. 'page arguments' => array('nodequeue_clear_confirm', 3, 5),
  178. 'access callback' => 'nodequeue_queue_access',
  179. 'access arguments' => array(3, 5),
  180. 'file' => 'includes/nodequeue.admin.inc',
  181. 'type' => MENU_CALLBACK
  182. );
  183. return $items;
  184. }
  185. /**
  186. * Helper function for a _menu_translate() bug.
  187. */
  188. function subqueue_to_arg() {
  189. return '';
  190. }
  191. /**
  192. * Implements hook_admin_paths().
  193. */
  194. function nodequeue_admin_paths() {
  195. if (variable_get('node_admin_theme')) {
  196. $paths = array(
  197. 'node/*/nodequeue' => TRUE,
  198. );
  199. return $paths;
  200. }
  201. }
  202. /**
  203. * Implements hook_node_delete.
  204. */
  205. function nodequeue_node_delete($node) {
  206. // If a node is being deleted, ensure it's also removed from any queues.
  207. $result = db_query("SELECT qid, sqid FROM {nodequeue_nodes} WHERE nid =:nid", array(
  208. ':nid' => $node->nid,
  209. ));
  210. foreach ($result as $obj) {
  211. // If the queue is being tracked by translation set and the node is part
  212. // of a translation set, don't delete the queue record.
  213. // Instead, data will be updated in the 'translation_change' op, below.
  214. $queues = nodequeue_load_queues(array($obj->qid));
  215. $queue = array_shift($queues);
  216. if (!$queue->i18n || (isset($node->tnid) && empty($node->tnid))) {
  217. // This removes by nid, not position, because if we happen to have a
  218. // node in a queue twice, the 2nd position would be wrong.
  219. nodequeue_subqueue_remove_node($obj->sqid, $node->nid);
  220. }
  221. }
  222. }
  223. /**
  224. * Implements hook_node_view().
  225. */
  226. function nodequeue_node_view($node, $view_mode) {
  227. $links = nodequeue_node_links($node);
  228. if (!empty($links)) {
  229. $node->content['links']['nodequeue'] = array(
  230. '#links' => $links,
  231. '#theme' => 'links__node__nodequeue',
  232. );
  233. }
  234. }
  235. /**
  236. * Implementats hook_forms().
  237. */
  238. function nodequeue_forms($form_id) {
  239. $forms = array();
  240. if (strpos($form_id, 'nodequeue_arrange_subqueue_form_') === 0) {
  241. $forms[$form_id] = array(
  242. 'callback' => 'nodequeue_arrange_subqueue_form',
  243. );
  244. }
  245. return $forms;
  246. }
  247. /**
  248. * Implements hook_theme().
  249. */
  250. function nodequeue_theme() {
  251. return array(
  252. 'nodequeue_arrange_subqueue_form_table' => array(
  253. 'render element' => 'form',
  254. ),
  255. 'nodequeue_subqueue_empty_text' => array(
  256. 'variables' => array(),
  257. ),
  258. 'nodequeue_subqueue_full_text' => array(
  259. 'variables' => array(),
  260. ),
  261. 'nodequeue_subqueue_count_text' => array(
  262. 'variables' => array('count' => 0),
  263. ),
  264. );
  265. }
  266. /**
  267. * Implements hook_element_info().
  268. */
  269. function nodequeue_element_info() {
  270. $type = array();
  271. $type['position'] = array(
  272. '#input' => TRUE,
  273. '#delta' => 10,
  274. '#default_value' => 0,
  275. '#process' => array('process_position', 'ajax_process_form'),
  276. );
  277. return $type;
  278. }
  279. /**
  280. * Expand position elements into selects. Works like the weight element, except
  281. * only positive values are allowed.
  282. */
  283. function process_position($element) {
  284. for ($n = 1; $n <= $element['#delta']; $n++) {
  285. $positions[$n] = $n;
  286. }
  287. $element['#options'] = $positions;
  288. $element['#options']['r'] = t('Remove');
  289. $element['#type'] = 'select';
  290. // add default properties for the select element
  291. $element += element_info('select');
  292. return $element;
  293. }
  294. /**
  295. * If no default value is set for position select boxes, use 1.
  296. */
  297. function position_value(&$form) {
  298. if (isset($form['#default_value'])) {
  299. $form['#value'] = $form['#default_value'];
  300. }
  301. else {
  302. $form['#value'] = 1;
  303. }
  304. }
  305. /**
  306. * Implements hook_views_api().
  307. */
  308. function nodequeue_views_api() {
  309. return array(
  310. 'api' => 2,
  311. 'path' => drupal_get_path('module', 'nodequeue') . '/includes/views',
  312. );
  313. }
  314. // --------------------------------------------------------------------------
  315. // Nodequeue Apache Solr Search Integration
  316. /**
  317. * Implements hook_form_FORM_ID_alter().
  318. */
  319. function nodequeue_form_apachesolr_search_bias_form_alter(&$form, &$form_state, $form_id) {
  320. // Setup for the form building.
  321. $weights = drupal_map_assoc(array('21.0', '13.0', '8.0', '5.0', '3.0', '2.0', '1.0', '0.8', '0.5', '0.3', '0.2', '0.1'));
  322. $weights['0'] = t('Normal');
  323. $queues = nodequeue_load_subqueues_by_queue(array_keys(nodequeue_get_all_qids()));
  324. // Build the form.
  325. $form['biasing']['nodequeue_boost'] = array(
  326. '#type' => 'fieldset',
  327. '#title' => t('Nodequeue Biasing'),
  328. '#weight' => -5,
  329. '#collapsible' => TRUE,
  330. '#collapsed' => TRUE,
  331. );
  332. $form['biasing']['nodequeue_boost']['nodequeue_apachesolr_boost'] = array(
  333. '#type' => 'item',
  334. '#description' => t("Specify to bias the search result when a node is in a queue. Any value except <em>Normal</em> will increase the socre for the given queue in the search results"),
  335. );
  336. foreach ($queues as $sqid => $queue) {
  337. $boost = variable_get("nodequeue_apachesolr_boost_$sqid", 0);
  338. // Add in setting for each queue.
  339. $form['biasing']['nodequeue_boost']['nodequeue_apachesolr_boost']["nodequeue_apachesolr_boost_$sqid"] = array(
  340. '#type' => 'select',
  341. '#title' => t('Weight for %title nodequeue', array('%title' => $queue->title)),
  342. '#options' => $weights,
  343. '#default_value' => $boost,
  344. );
  345. }
  346. }
  347. /**
  348. * Implements hook_apachesolr_update_index().
  349. */
  350. function nodequeue_apachesolr_update_index(&$document, $node) {
  351. if (empty($document)) {
  352. return;
  353. }
  354. $queues = nodequeue_load_queues(array_keys(nodequeue_get_all_qids()));
  355. $subqueues = nodequeue_get_subqueues_by_node($queues, $node);
  356. nodequeue_set_subqueue_positions($subqueues, $node->nid);
  357. if (is_array($subqueues)) {
  358. foreach ($subqueues as $sqid => $subqueue) {
  359. if (!empty($subqueue->position)) {
  360. $key = _nodequeue_solr_qid_key();
  361. $document->setMultiValue($key, $sqid);
  362. }
  363. }
  364. }
  365. }
  366. /**
  367. * Returns the apachesolr index key for group id.
  368. */
  369. function _nodequeue_solr_qid_key() {
  370. $qid_key = array(
  371. 'index_type' => 'sint',
  372. 'multiple' => TRUE,
  373. 'name' => "nodequeue",
  374. );
  375. return apachesolr_index_key($qid_key);
  376. }
  377. /**
  378. * Implements hook_apachesolr_query_alter().
  379. */
  380. function nodequeue_apachesolr_query_alter(DrupalSolrQueryInterface $query) {
  381. $queues = nodequeue_load_subqueues_by_queue(array_keys(nodequeue_get_all_qids()));
  382. $added = FALSE;
  383. foreach ($queues as $sqid => $queue) {
  384. $boost = variable_get("nodequeue_apachesolr_boost_$sqid", 0);
  385. if (!empty($boost)) {
  386. $query->params['bq'][] = _nodequeue_solr_qid_key() . ":$sqid^$boost";
  387. if (!$added) {
  388. // Only want to add the facet.field once. no need to repeat it.
  389. $query->params['facet.field'][] = _nodequeue_solr_qid_key();
  390. $added = TRUE;
  391. }
  392. }
  393. }
  394. }
  395. // --------------------------------------------------------------------------
  396. // Nodequeue manipulation API.
  397. /**
  398. * @defgroup nodequeue_api
  399. * @{
  400. * Access to the internals of nodequeues are handled primarily through these
  401. * API functions. They allow easy loading of queues for manipulation.
  402. */
  403. /**
  404. * The nodequeue queue class; the constructor makes it so we don't have to
  405. * always check to see if our variables are empty or not.
  406. */
  407. class nodequeue_queue {
  408. var $name = '';
  409. var $title = '';
  410. var $size = 0;
  411. var $link = '';
  412. var $link_remove = '';
  413. var $roles = array();
  414. var $types = array();
  415. var $show_in_links = TRUE;
  416. var $show_in_tab = TRUE;
  417. var $show_in_ui = TRUE;
  418. var $reference = 0;
  419. var $i18n = 0;
  420. var $subqueue_title = '';
  421. var $reverse = 0;
  422. // runtime
  423. var $subqueues = array();
  424. var $subqueue = NULL;
  425. var $current = NULL;
  426. function nodequeue_queue($type) {
  427. $this->owner = $type;
  428. }
  429. }
  430. /**
  431. * Fetch a list of available queues for a given location. These queues
  432. * will be fully loaded and ready to go.
  433. */
  434. function nodequeue_load_queues_by_type($type, $location = NULL, $account = NULL, $bypass_cache = FALSE) {
  435. $qids = nodequeue_get_qids($type, $account, $bypass_cache);
  436. if ($location) {
  437. nodequeue_filter_qids($qids, $location);
  438. }
  439. return nodequeue_load_queues(array_keys($qids), $bypass_cache);
  440. }
  441. /**
  442. * Filter a list of qids returned by nodequeue_get_qids to a location.
  443. *
  444. * @param $qids
  445. * An array of $qids from @see nodequeue_get_qids()
  446. * @param $location
  447. * One of:
  448. * - 'links': Only check for queues that have node links.
  449. * - 'tab': Only check for queues that appear on the node tab.
  450. * - 'ui': Only check for queues that appear in the UI.
  451. */
  452. function nodequeue_filter_qids(&$qids, $location) {
  453. $var = "show_in_$location";
  454. foreach ($qids as $qid => $info) {
  455. if (empty($info->$var)) {
  456. unset($qids[$qid]);
  457. }
  458. }
  459. }
  460. /**
  461. * Get an array of qids applicable to this node type.
  462. *
  463. * @param $type
  464. * The node type.
  465. * @param $account
  466. * The account to test against. Defaults to the currently logged in user.
  467. *
  468. * @return $qids
  469. * An array in the format: @code { array($qid => array('qid' => $qid, 'show_in_tab' '
  470. * => true/false, 'show_in_links' => true/false }
  471. *
  472. * @param $bypass_cache
  473. * Boolean value indicating whether to bypass the cache or not.
  474. */
  475. function nodequeue_get_qids($type, $account = NULL, $bypass_cache = FALSE) {
  476. if (!isset($account)) {
  477. global $user;
  478. $account = $user;
  479. }
  480. static $cache = array();
  481. if ($bypass_cache || !isset($cache[$type])) {
  482. $roles_join = $roles_where = '';
  483. $roles = array();
  484. // superuser always has access.
  485. if (!user_access('manipulate all queues', $account)) {
  486. $roles_join = "INNER JOIN {nodequeue_roles} nr ON nr.qid = nq.qid ";
  487. $roles = array_keys((array) $account->roles) + array(DRUPAL_AUTHENTICATED_RID);
  488. $roles_where .= "AND nr.rid IN (:roles)";
  489. }
  490. $sql = 'SELECT nq.qid, nq.show_in_tab, nq.show_in_links, nq.show_in_ui, nq.i18n ' .
  491. 'FROM {nodequeue_queue} nq ' .
  492. 'INNER JOIN {nodequeue_types} nt ON nt.qid = nq.qid ' . $roles_join .
  493. "WHERE nt.type = :type " . $roles_where;
  494. $result = db_query($sql, array(':type' => $type, ':roles' => $roles));
  495. $qids = array();
  496. foreach ($result as $qid) {
  497. $qids[$qid->qid] = $qid;
  498. }
  499. $cache[$type] = $qids;
  500. }
  501. return $cache[$type];
  502. }
  503. /**
  504. * Get an array of qids using the pager query. This administrative list
  505. * does no permission checking, so should only be available to users who
  506. * have passed the 'administer queues' check.
  507. *
  508. * @param $page_size
  509. * The page size to use. If this is 0 or NULL, all queues will be returned.
  510. * Defaults to 0.
  511. * @param $pager_element
  512. * In the rare event this should use another pager element, set this..
  513. * @param $bypass_cache
  514. * Boolean value indicating whether to bypass the cache or not.
  515. *
  516. * @return $qids
  517. * An array in the format: @code { array($qid => $qid) }
  518. */
  519. function nodequeue_get_all_qids($page_size = 0, $pager_element = 0, $bypass_cache = FALSE) {
  520. $cache = &drupal_static(__FUNCTION__, array());
  521. if ($bypass_cache || empty($cache[$page_size])) {
  522. $query = db_select('nodequeue_queue', 'nq')
  523. ->fields('nq', array('qid'));
  524. if (!empty($page_size)) {
  525. $query->extend('PagerDefault')
  526. ->extend('TableSort')
  527. ->limit($page_size)
  528. ->element($pager_element);
  529. }
  530. $qids = $query->execute()->fetchAllKeyed(0, 0);
  531. $cache[$page_size] = $qids;
  532. }
  533. return $cache[$page_size];
  534. }
  535. /**
  536. * Load an array of $qids.
  537. *
  538. * This exists to provide a way of loading a bunch of queues with
  539. * the fewest queries. Loading 5 queues results in only 4 queries,
  540. * not 20. This also caches queues so that they don't get loaded
  541. * repeatedly.
  542. *
  543. * @param $qids
  544. * An array of queue IDs to load.
  545. *
  546. * @param $bypass_cache
  547. * Boolean value indicating whether to bypass the cache or not.
  548. */
  549. function nodequeue_load_queues($qids = array(), $bypass_cache = FALSE) {
  550. static $cache = array();
  551. $to_load = $loaded = array();
  552. foreach ($qids as $qid) {
  553. if ($bypass_cache || !isset($cache[$qid])) {
  554. $to_load[] = $qid;
  555. }
  556. }
  557. if (!empty($to_load)) {
  558. $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 (:to_load) GROUP BY q.qid", array(':to_load' => $to_load));
  559. foreach ($result as $queue) {
  560. $loaded[$queue->qid] = $queue;
  561. // ensure valid defaults:
  562. $loaded[$queue->qid]->types = array();
  563. $loaded[$queue->qid]->roles = array();
  564. $loaded[$queue->qid]->count = 0;
  565. }
  566. $result = db_query("SELECT qid, rid FROM {nodequeue_roles} WHERE qid IN (:to_load)", array(':to_load' => $to_load));
  567. foreach ($result as $obj) {
  568. $loaded[$obj->qid]->roles[] = $obj->rid;
  569. }
  570. $result = db_query("SELECT qid, type FROM {nodequeue_types} WHERE qid IN (:to_load)", array(':to_load' => $to_load));
  571. foreach ($result as $obj) {
  572. $loaded[$obj->qid]->types[] = $obj->type;
  573. }
  574. $context = 'load_queues';
  575. drupal_alter('nodequeue', $loaded, $context);
  576. }
  577. if ($bypass_cache) {
  578. return $loaded;
  579. }
  580. else {
  581. if (!empty($loaded)) {
  582. $cache += $loaded;
  583. }
  584. $queues = array();
  585. foreach ($qids as $qid) {
  586. if (isset($cache[$qid])) {
  587. $queues[$qid] = $cache[$qid];
  588. }
  589. }
  590. return $queues;
  591. }
  592. }
  593. /**
  594. * Load a nodequeue.
  595. *
  596. * @param $qid
  597. * The qid of the queue to load.
  598. */
  599. function nodequeue_load($qid) {
  600. $queues = nodequeue_load_queues(array($qid));
  601. return !empty($queues) ? array_shift($queues) : array();
  602. }
  603. /**
  604. * This function exists so that %subqueue will work in hook_menu.
  605. */
  606. function subqueue_load($sqid) {
  607. if (!$sqid) {
  608. return NULL;
  609. }
  610. $queues = nodequeue_load_subqueues(array($sqid));
  611. return !empty($queues) ? array_shift($queues) : array();
  612. }
  613. /**
  614. * Load a list of subqueues
  615. *
  616. * This exists to provide a way of loading a bunch of queues with
  617. * the fewest queries. Loading 5 queues results in only 4 queries,
  618. * not 20. This also caches queues so that they don't get loaded
  619. * repeatedly.
  620. *
  621. * @param $sqids
  622. * An array of subqueue IDs to load.
  623. * @param $bypass_cache
  624. * Boolean value indicating whether to bypass the cache or not.
  625. */
  626. function nodequeue_load_subqueues($sqids, $bypass_cache = FALSE) {
  627. static $cache = array();
  628. $to_load = array();
  629. foreach ($sqids as $sqid) {
  630. if ($bypass_cache || !isset($cache[$sqid])) {
  631. $to_load[] = $sqid;
  632. }
  633. }
  634. if (!empty($to_load)) {
  635. $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 (:to_load) GROUP BY s.sqid", array(':to_load' => $to_load));
  636. foreach ($result as $obj) {
  637. // Sometimes we want to get to subqueues by reference, sometimes by sqid.
  638. // sqid is always unique, but reference is sometimes more readily available.
  639. $cache[$obj->sqid] = $obj;
  640. }
  641. }
  642. foreach ($sqids as $sqid) {
  643. if (isset($cache[$sqid])) {
  644. $subqueues[$sqid] = $cache[$sqid];
  645. }
  646. }
  647. return $subqueues;
  648. }
  649. /**
  650. * Load a single subqueue.
  651. *
  652. * @param $sqid
  653. * The subqueue ID to load.
  654. * @param $bypass_cache
  655. * Boolean value indicating whether to bypass the cache or not.
  656. */
  657. function nodequeue_load_subqueue($sqid, $bypass_cache = FALSE) {
  658. $subqueues = nodequeue_load_subqueues(array($sqid), $bypass_cache);
  659. if ($subqueues) {
  660. return array_shift($subqueues);
  661. }
  662. }
  663. /**
  664. * Load the entire set of subqueues for a queue.
  665. *
  666. * This will load the entire set of subqueues for a given queue (and can
  667. * respect the pager, if desired). It does NOT cache the subqueues like
  668. * nodequeue_load_subqueues does, so beware of this mixed caching.
  669. *
  670. * @param $qids
  671. * A $qid or array of $qids
  672. * @param $page_size
  673. * If non-zero, use the pager_query and limit the page-size to the parameter.
  674. */
  675. function nodequeue_load_subqueues_by_queue($qids, $page_size = 0) {
  676. if (is_numeric($qids)) {
  677. $qids = array($qids);
  678. }
  679. if (empty($qids)) {
  680. return array();
  681. }
  682. $query = "SELECT s.*, COUNT(n.position) AS count FROM {nodequeue_subqueue} s
  683. LEFT JOIN {nodequeue_nodes} n ON n.sqid = s.sqid WHERE s.qid IN (:qids) GROUP BY s.sqid";
  684. $result = db_query($query, array(':qids' => $qids));
  685. $subqueues = array();
  686. foreach ($result as $subqueue) {
  687. $subqueues[$subqueue->sqid] = $subqueue;
  688. }
  689. return $subqueues;
  690. }
  691. /**
  692. * Load a set of subqueues by reference.
  693. *
  694. * This can be used to load a set of subqueues by reference; it will primarily
  695. * be used by plugins that are managing subqueues.
  696. *
  697. * @param $references
  698. * A keyed array of references to load. The key is the $qid and each value
  699. * is another array of references.
  700. */
  701. function nodequeue_load_subqueues_by_reference($references, $bypass_cache = FALSE) {
  702. static $cache = array();
  703. $subqueues = array();
  704. if ($bypass_cache) {
  705. $cache = array();
  706. }
  707. if (!empty($references)) {
  708. $query = db_select('nodequeue_subqueue', 's')
  709. ->groupBy('s.sqid')
  710. ->fields('s');
  711. $query->leftJoin('nodequeue_nodes', 'n', 'n.sqid = s.sqid');
  712. $query->addExpression('COUNT(n.position)', 'count');
  713. $where = db_or();
  714. foreach ($references as $qid => $reference) {
  715. $where->condition(db_and()->condition('s.qid', $qid)->condition('s.reference', $reference));
  716. }
  717. $query->condition($where);
  718. $result = $query->execute();
  719. foreach ($result as $subqueue) {
  720. $cache[$subqueue->qid][$subqueue->reference] = $subqueues[$subqueue->sqid] = $subqueue;
  721. }
  722. }
  723. return $subqueues;
  724. }
  725. /**
  726. * Return a queue by its machine name. This is obviously not ideal due to the
  727. * extra queries, but probably preferable to changing current API calls.
  728. *
  729. * @param $name
  730. * The queue machine name
  731. *
  732. * @return
  733. * The queue definition, or an empty array if no queue was found with the
  734. * given machine name.
  735. */
  736. function nodequeue_load_queue_by_name($name) {
  737. $map = nodequeue_get_qid_map();
  738. if (isset($map[$name])) {
  739. $queues = nodequeue_load_queues(array($map[$name]));
  740. if ($queues) {
  741. return current($queues);
  742. }
  743. }
  744. return array();
  745. }
  746. /**
  747. * Return a map of queue name to qid values to aid in various lookups.
  748. *
  749. * @return array
  750. * A array of qids, keyed by machine name.
  751. */
  752. function nodequeue_get_qid_map() {
  753. static $map = array();
  754. if (!$map) {
  755. $result = db_query("SELECT qid, name FROM {nodequeue_queue}");
  756. while ($get = $result->fetchObject()) {
  757. $map[$get->name] = $get->qid;
  758. }
  759. }
  760. return $map;
  761. }
  762. /**
  763. * Save a nodequeue. This does not save subqueues; those must be added separately.
  764. */
  765. function nodequeue_save(&$queue) {
  766. $nodequeue_queue_fields = array(
  767. 'name' => $queue->name,
  768. 'title' => $queue->title,
  769. 'subqueue_title' => $queue->subqueue_title,
  770. 'size' => $queue->size,
  771. 'link' => $queue->link,
  772. 'link_remove' => $queue->link_remove,
  773. 'owner' => $queue->owner,
  774. 'show_in_links' => ($queue->show_in_links) ? 1 : 0,
  775. 'show_in_tab' => $queue->show_in_tab,
  776. 'show_in_ui' => $queue->show_in_ui,
  777. 'i18n' => $queue->i18n,
  778. 'reverse' => $queue->reverse,
  779. 'reference' => $queue->reference,
  780. );
  781. if (!isset($queue->qid)) {
  782. $queue->qid = db_insert('nodequeue_queue')
  783. ->fields($nodequeue_queue_fields)
  784. ->execute();
  785. if (function_exists('views_invalidate_cache')) {
  786. views_invalidate_cache();
  787. }
  788. }
  789. else {
  790. db_update('nodequeue_queue')
  791. ->fields($nodequeue_queue_fields)
  792. ->condition('qid', $queue->qid)
  793. ->execute();
  794. db_delete('nodequeue_roles')
  795. ->condition('qid', $queue->qid)
  796. ->execute();
  797. db_delete('nodequeue_types')
  798. ->condition('qid', $queue->qid)
  799. ->execute();
  800. }
  801. if (is_array($queue->roles)) {
  802. foreach ($queue->roles as $rid) {
  803. db_insert('nodequeue_roles')
  804. ->fields(array(
  805. 'qid' => $queue->qid,
  806. 'rid' => $rid,
  807. ))
  808. ->execute();
  809. }
  810. }
  811. if (is_array($queue->types)) {
  812. foreach ($queue->types as $type) {
  813. db_insert('nodequeue_types')
  814. ->fields(array(
  815. 'qid' => $queue->qid,
  816. 'type' => $type,
  817. ))
  818. ->execute();
  819. }
  820. }
  821. // set our global that tells us whether or not we need to activate hook_link
  822. if (db_query("SELECT COUNT(*) FROM {nodequeue_queue} WHERE link <> ''")->fetchField()) {
  823. variable_set('nodequeue_links', TRUE);
  824. }
  825. else {
  826. variable_set('nodequeue_links', FALSE);
  827. }
  828. if (isset($queue->add_subqueue) && is_array($queue->add_subqueue)) {
  829. foreach ($queue->add_subqueue as $reference => $title) {
  830. // If reference is unset it should be set to the qid; this is generally
  831. // used for a single subqueue; setting the reference to the qid makes
  832. // it easy to find that one subqueue.
  833. if ($reference == 0) {
  834. $reference = $queue->qid;
  835. }
  836. nodequeue_add_subqueue($queue, $title, $reference);
  837. }
  838. }
  839. return $queue->qid;
  840. }
  841. /**
  842. * Delete a nodequeue.
  843. */
  844. function nodequeue_delete($qid) {
  845. db_delete('nodequeue_roles')
  846. ->condition('qid', $qid)
  847. ->execute();
  848. db_delete('nodequeue_types')
  849. ->condition('qid', $qid)
  850. ->execute();
  851. db_delete('nodequeue_queue')
  852. ->condition('qid', $qid)
  853. ->execute();
  854. db_delete('nodequeue_nodes')
  855. ->condition('qid', $qid)
  856. ->execute();
  857. db_delete('nodequeue_subqueue')
  858. ->condition('qid', $qid)
  859. ->execute();
  860. }
  861. /**
  862. * Add a new subqueue to a queue.
  863. *
  864. * @param $queue
  865. * The queue object that is the parent of this subqueue.
  866. * @param $title
  867. * The title of the subqueue.
  868. * @param $reference
  869. * A reference that uniquely identifies this subqueue. If NULL it will
  870. * be assigned the sqid.
  871. */
  872. function nodequeue_add_subqueue(&$queue, $title, $reference = NULL) {
  873. if (empty($reference)) {
  874. $insert_reference = "";
  875. }
  876. else {
  877. $insert_reference = $reference;
  878. }
  879. $subqueue = new stdClass();
  880. $subqueue->reference = $reference;
  881. $subqueue->qid = $queue->qid;
  882. $subqueue->title = $title;
  883. $subqueue->sqid = db_insert('nodequeue_subqueue')
  884. ->fields(array(
  885. 'qid' => $queue->qid,
  886. 'reference' => $insert_reference,
  887. 'title' => $title,
  888. ))
  889. ->execute();
  890. // If somehow the $reference is null, here we set it to the sqid.
  891. // We have to do it here, because before the insert we don't know what the sqid will be.
  892. if (empty($reference)) {
  893. db_update('nodequeue_subqueue')
  894. ->fields(array('reference' => $subqueue->sqid))
  895. ->condition('sqid', $subqueue->sqid)
  896. ->execute();
  897. }
  898. return $subqueue;
  899. }
  900. /**
  901. * Change the title of a subqueue.
  902. *
  903. * Note that only the title of a subqueue is changeable; it can change to
  904. * reflect updates in taxonomy term names, for example.
  905. */
  906. function nodequeue_subqueue_update_title($sqid, $title) {
  907. db_update('nodequeue_subqueue')
  908. ->fields(array('title' => $title))
  909. ->condition('sqid', $sqid)
  910. ->execute();
  911. }
  912. /**
  913. * Remove a subqueue.
  914. */
  915. function nodequeue_remove_subqueue($sqid) {
  916. nodequeue_queue_clear($sqid);
  917. db_delete('nodequeue_subqueue')
  918. ->condition('sqid', $sqid)
  919. ->execute();
  920. }
  921. // --------------------------------------------------------------------------
  922. // Queue position control
  923. /**
  924. * Add a node to a queue.
  925. *
  926. * @param $queue
  927. * The parent queue of the subqueue. This is required so that we can
  928. * pop nodes out if the queue breaks size limits.
  929. * @param $subqueue
  930. * The subqueue to add the node to.
  931. * @param $nid
  932. * The node ID
  933. */
  934. function nodequeue_subqueue_add($queue, &$subqueue, $nid) {
  935. if (!empty($nid)) {
  936. db_query("INSERT INTO {nodequeue_nodes} (sqid, qid, nid, position, timestamp) VALUES (:sqid, :qid, :nid, IFNULL((SELECT MAX(position)+1 FROM (SELECT * from {nodequeue_nodes} WHERE sqid = :sqid) as nn), 1), :time)", array(':sqid' => $subqueue->sqid, ':qid' => $queue->qid, ':nid' => $nid, ':time' => REQUEST_TIME));
  937. $subqueue->count = db_query("SELECT COUNT(nid) FROM {nodequeue_nodes} WHERE sqid = :sqid", array(':sqid' => $subqueue->sqid))->fetchField();
  938. // If adding this would make the queue too big, pop the front node
  939. // (or nodes) out.
  940. if (!empty($queue->size)) {
  941. // 0 means infinity so never do this if FALSE.
  942. nodequeue_check_subqueue_size($queue, $subqueue, $queue->size);
  943. }
  944. if (module_exists('apachesolr')) {
  945. apachesolr_mark_node($nid);
  946. }
  947. // Invoke the hook to notify other modules of the node addition.
  948. module_invoke_all('nodequeue_add', $subqueue->sqid, $nid);
  949. }
  950. }
  951. /**
  952. * Remove a node from the queue. If a node is in the queue more than once,
  953. * only the first (closest to 0 position, or the front of the queue) will
  954. * be removed.
  955. *
  956. * @param $sqid
  957. * The subqueue to remove nodes from.
  958. * @param $nid
  959. * The node to remove.
  960. */
  961. function nodequeue_subqueue_remove_node($sqid, $nid) {
  962. if ($pos = nodequeue_get_subqueue_position($sqid, $nid)) {
  963. nodequeue_subqueue_remove($sqid, $pos);
  964. if (module_exists('apachesolr')) {
  965. apachesolr_mark_node($nid);
  966. }
  967. }
  968. }
  969. /**
  970. * Remove a node or node(s) from a nodequeue by position.
  971. *
  972. * If you know the nid but but not the position, use
  973. * @see nodequeue_subqueue_remove_node() instead.
  974. *
  975. * @param $sqid
  976. * The subqueue to remove nodes from.
  977. * @param $start
  978. * The first position (starting from 1) to remove.
  979. * @param $end
  980. * The last position to remove. If NULL or equal to $start,
  981. * only one node will be removed. Thus if $start is 1 and $end is 2,
  982. * the first and second items will be removed from the queue.
  983. *
  984. */
  985. function nodequeue_subqueue_remove($sqid, $start, $end = NULL) {
  986. if (!isset($end)) {
  987. $end = $start;
  988. }
  989. // Retrieve the nodes that are being removed.
  990. $result = db_query("SELECT nid FROM {nodequeue_nodes} WHERE sqid = :sqid AND position >= :start AND position <= :end",
  991. array(
  992. ':sqid' => $sqid,
  993. ':start' => $start,
  994. ':end' => $end,
  995. )
  996. );
  997. $diff = $end - $start + 1;
  998. db_delete('nodequeue_nodes')
  999. ->condition('sqid', $sqid)
  1000. ->condition('position', $start, '>=')
  1001. ->condition('position', $end, '<=')
  1002. ->execute();
  1003. db_update('nodequeue_nodes')
  1004. ->expression('position', 'position - ' . $diff)
  1005. ->condition('sqid', $sqid)
  1006. ->condition('position', $end, '>')
  1007. ->execute();
  1008. // Invoke the hook to let other modules know that the nodes were removed.
  1009. foreach ($result as $node) {
  1010. module_invoke_all('nodequeue_remove', $sqid, $node->nid);
  1011. }
  1012. }
  1013. /**
  1014. * Empty a subqueue.
  1015. *
  1016. * @param $sqid
  1017. * The sqid to empty.
  1018. */
  1019. function nodequeue_queue_clear($sqid) {
  1020. db_delete('nodequeue_nodes')
  1021. ->condition('sqid', $sqid)
  1022. ->execute();
  1023. }
  1024. /**
  1025. * Guarantee that a subqueue has not gotten too big. It's important to call
  1026. * this after an operation that might have reduced a queue's maximum size.
  1027. * It stores the count to save a query if this is to be followed by an add
  1028. * operation.
  1029. *
  1030. * @param $queue
  1031. * The queue object.
  1032. * @param $reference
  1033. * The subqueue to check.
  1034. *
  1035. */
  1036. function nodequeue_check_subqueue_size($queue, &$subqueue, $size = NULL) {
  1037. if (!isset($size)) {
  1038. $size = $queue->size;
  1039. }
  1040. if ($queue->size && $subqueue->count > $size) {
  1041. nodequeue_subqueue_remove($subqueue->sqid, 1, $subqueue->count - $size);
  1042. $subqueue->count = $size;
  1043. }
  1044. }
  1045. /**
  1046. * Guarantee that all subqueues are within the size constraints set
  1047. * by $queue->size.
  1048. */
  1049. function nodequeue_check_subqueue_sizes($queue) {
  1050. // Don't check if size is 0, as that means infinite size.
  1051. if (!$queue->size) {
  1052. return;
  1053. }
  1054. $subqueues = nodequeue_load_subqueues_by_queue($queue->qid);
  1055. foreach ($subqueues as $subqueue) {
  1056. nodequeue_check_subqueue_size($queue, $subqueue);
  1057. }
  1058. }
  1059. /**
  1060. * Swap two positions within a subqueue.
  1061. */
  1062. function nodequeue_queue_swap($subqueue, $pos1, $pos2) {
  1063. // Grab the nid off one of the positions so we can more easily swap.
  1064. $nid = db_query("SELECT nid FROM {nodequeue_nodes} WHERE sqid = :sqid AND position = :position",
  1065. array(':sqid' => $subqueue->sqid, ':position' => $pos1))
  1066. ->fetchField();
  1067. if (!$nid) {
  1068. return;
  1069. }
  1070. db_update('nodequeue_nodes')
  1071. ->fields(array('position' => $pos1))
  1072. ->condition('position', $pos2)
  1073. ->condition('sqid', $subqueue->sqid)
  1074. ->execute();
  1075. db_update('nodequeue_nodes')
  1076. ->fields(array('position' => $pos2))
  1077. ->condition('nid', $nid)
  1078. ->condition('sqid', $subqueue->sqid)
  1079. ->execute();
  1080. // notify other modules of the swap
  1081. module_invoke_all('nodequeue_swap', $subqueue->sqid, $nid);
  1082. }
  1083. /**
  1084. * Move a position within a subqueue up by one.
  1085. */
  1086. function nodequeue_queue_up($subqueue, $position) {
  1087. if ($position < 2 || $position > $subqueue->count) {
  1088. return;
  1089. }
  1090. nodequeue_queue_swap($subqueue, $position - 1, $position);
  1091. }
  1092. /**
  1093. * Move a position within a subqueue down by one.
  1094. */
  1095. function nodequeue_queue_down($subqueue, $position) {
  1096. if ($position < 1 || $position >= $subqueue->count) {
  1097. return;
  1098. }
  1099. nodequeue_queue_swap($subqueue, $position + 1, $position);
  1100. }
  1101. /**
  1102. * Move an item to the front of the queue.
  1103. */
  1104. function nodequeue_queue_front($subqueue, $position) {
  1105. if ($position < 2 || $position > $subqueue->count) {
  1106. return;
  1107. }
  1108. $result = db_query("SELECT * FROM {nodequeue_nodes} WHERE sqid= :sqid AND position = :position", array(
  1109. ':sqid' => $subqueue->sqid,
  1110. ':position' => $position,
  1111. ));
  1112. $entry = $result->fetchObject();
  1113. db_delete('nodequeue_nodes')
  1114. ->condition('sqid', $subqueue->sqid)
  1115. ->condition('position', $position)
  1116. ->execute();
  1117. db_update('nodequeue_nodes')
  1118. ->expression('position', 'position + 1')
  1119. ->condition('sqid', $subqueue->sqid)
  1120. ->condition('position', $position, '<')
  1121. ->execute();
  1122. db_insert('nodequeue_nodes')
  1123. ->fields(array(
  1124. 'qid' => $entry->qid,
  1125. 'sqid' => $subqueue->sqid,
  1126. 'nid' => $entry->nid,
  1127. 'position' => 1,
  1128. 'timestamp' => $entry->timestamp,
  1129. ))
  1130. ->execute();
  1131. }
  1132. /**
  1133. * Move an item to the back of the queue.
  1134. */
  1135. function nodequeue_queue_back($subqueue, $position) {
  1136. if ($position < 1 || $position >= $subqueue->count) {
  1137. return;
  1138. }
  1139. $result = db_query("SELECT * FROM {nodequeue_nodes} WHERE sqid= :sqid AND position = :position", array(
  1140. ':sqid' => $subqueue->sqid,
  1141. ':position' => $position,
  1142. ));
  1143. $entry = $result->fetchObject();
  1144. db_delete('nodequeue_nodes')
  1145. ->condition('sqid', $subqueue->sqid)
  1146. ->condition('position', $position)
  1147. ->execute();
  1148. db_update('nodequeue_nodes')
  1149. ->expression('position', 'position - 1')
  1150. ->condition('sqid', $subqueue->sqid)
  1151. ->condition('position', $position, '<')
  1152. ->execute();
  1153. db_insert('nodequeue_nodes')
  1154. ->fields(array(
  1155. 'qid' => $entry->qid,
  1156. 'sqid' => $subqueue->sqid,
  1157. 'nid' => $entry->nid,
  1158. 'position' => $subqueue->count,
  1159. 'timestamp' => $entry->timestamp,
  1160. ))
  1161. ->execute();
  1162. }
  1163. /**
  1164. * Get the position of a node in a subqueue, or 0 if not found.
  1165. */
  1166. function nodequeue_get_subqueue_position($sqid, $nid) {
  1167. // We use MIN to make sure we always get the closes to the front of the
  1168. // queue in case the queue has nodes in it multiple times.
  1169. $pos = db_query("SELECT MIN(position) FROM {nodequeue_nodes} WHERE sqid = :sqid AND nid = :nid", array(':sqid' => $sqid, ':nid' => $nid))->fetchField();
  1170. return $pos;
  1171. }
  1172. /**
  1173. * Get the position of a node in several subqueues.
  1174. */
  1175. function nodequeue_set_subqueue_positions(&$subqueues, $nid) {
  1176. if (empty($subqueues)) {
  1177. return;
  1178. }
  1179. $query = db_select('nodequeue_nodes', 'n')
  1180. ->fields('n', array('sqid'))
  1181. ->condition('sqid', array_keys($subqueues), 'IN')
  1182. ->condition('nid', $nid)
  1183. ->groupBy('sqid');
  1184. $query->addExpression('MIN(position)', 'position');
  1185. $result = $query->execute();
  1186. foreach ($result as $obj) {
  1187. $subqueues[$obj->sqid]->position = $obj->position;
  1188. }
  1189. }
  1190. /**
  1191. * Get a list of valid subqueues for a node, along with the position of the node.
  1192. *
  1193. * @param $queues
  1194. * An array of fully loaded queue objects.
  1195. * @param $node
  1196. * A fully loaded node object.
  1197. *
  1198. */
  1199. function nodequeue_get_subqueues_by_node($queues, $node) {
  1200. // Determine which subqueues are valid for each queue.
  1201. $references = array();
  1202. static $last_nid = 0;
  1203. foreach ($queues as $queue) {
  1204. if ($result = nodequeue_api_subqueues($queue, $node)) {
  1205. $references[$queue->qid] = is_array($result) ? $result : array($result);
  1206. }
  1207. }
  1208. if (empty($references)) {
  1209. return array();
  1210. }
  1211. // only allow the static cache to be used if the nid is the same as the last
  1212. $subqueues = nodequeue_load_subqueues_by_reference($references, ($last_nid != $node->nid));
  1213. $last_nid = $node->nid;
  1214. return $subqueues;
  1215. }
  1216. /**
  1217. * Get a textual representation of a nodequeue's queue size.
  1218. */
  1219. function nodequeue_subqueue_size_text($max, $count, $long = TRUE) {
  1220. if (empty($count)) {
  1221. $message = theme('nodequeue_subqueue_empty_text');
  1222. }
  1223. elseif ($count == $max) {
  1224. $message = theme('nodequeue_subqueue_full_text');
  1225. }
  1226. else {
  1227. if ($long) {
  1228. $message = theme('nodequeue_subqueue_count_text', array('count' => $count));
  1229. }
  1230. else {
  1231. $message = $count;
  1232. }
  1233. }
  1234. return $message;
  1235. }
  1236. /**
  1237. * Substitute the subqueue title into some other string.
  1238. *
  1239. * This function does NOT check_plain the title! The output MUST be checked
  1240. * after this is complete.
  1241. */
  1242. function nodequeue_title_substitute($text, $queue, $subqueue) {
  1243. if (empty($text)) {
  1244. return $subqueue->title;
  1245. }
  1246. $text = str_replace('%subqueue', $subqueue->title, $text);
  1247. return $text;
  1248. }
  1249. /**
  1250. * Shuffle a queue.
  1251. *
  1252. * @param $subqueue
  1253. * The subqueue to shuffle. May be a sqid or the loaded object.
  1254. */
  1255. function nodequeue_subqueue_shuffle($subqueue) {
  1256. // Load the queue
  1257. if (!is_object($subqueue)) {
  1258. $subqueue = nodequeue_load_subqueue($subqueue);
  1259. }
  1260. if (empty($subqueue)) {
  1261. return;
  1262. }
  1263. $count = $subqueue->count;
  1264. // Swap each item with another randomly picked one.
  1265. foreach (range(1, $count) as $i) {
  1266. nodequeue_queue_swap($subqueue, $i, rand(1, $count));
  1267. }
  1268. }
  1269. /**
  1270. * @} End of defgroup "nodequeue_api"
  1271. */
  1272. // --------------------------------------------------------------------------
  1273. // Hooks to implement the default nodequeue type.
  1274. /**
  1275. * Implements hook_nodequeue_info().
  1276. */
  1277. function nodequeue_nodequeue_info() {
  1278. return array('nodequeue' => array(
  1279. 'title' => t('Simple queue'),
  1280. 'description' => t('Simple 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.'),
  1281. ));
  1282. }
  1283. /**
  1284. * Implements hook_nodequeue_form_submit().
  1285. */
  1286. function nodequeue_nodequeue_form_submit(&$queue, $form_state) {
  1287. // This will add a single subqueue to our new queue.
  1288. if (!isset($queue->qid) && !isset($queue->add_subqueue)) {
  1289. // A 0 will set the reference to the sqid of the queue.
  1290. $queue->add_subqueue = array(0 => $queue->title);
  1291. }
  1292. //If the qid is set at this point, we're saving an existing queue.
  1293. if (isset($queue->qid)) {
  1294. //We don't check to see if the title has been updated since the $queue object already matches $form_state['values'].
  1295. db_update('nodequeue_subqueue')
  1296. ->fields(array('title' => $form_state['values']['title']))
  1297. ->condition('qid', $queue->qid)
  1298. ->execute();
  1299. }
  1300. }
  1301. // --------------------------------------------------------------------------
  1302. // External queue fetching
  1303. /**
  1304. * In general it's preferable to use Views for this functionality.
  1305. */
  1306. function nodequeue_node_titles($sqid, $title = '', $backward = TRUE, $from = 0, $count = 0, $published_only = TRUE) {
  1307. $orderby = ($backward ? "DESC" : "ASC");
  1308. $query = db_select('node', 'n')
  1309. ->fields('n', array('nid', 'title'))
  1310. ->condition('nn.sqid', $sqid)
  1311. ->orderBy('nn.position', $orderby)
  1312. ->addTag('node_access');
  1313. $query->leftJoin('nodequeue_nodes', 'nn', 'n.nid = nn.nid');
  1314. if ($published_only) {
  1315. $query->condition('n.status', 1);
  1316. }
  1317. if ($count) {
  1318. $result = $query->range($from, $count)->execute();
  1319. }
  1320. else {
  1321. $result = $query->execute();
  1322. }
  1323. return node_title_list($result, $title);
  1324. }
  1325. /**
  1326. * Returns an array of nodequeue links for a node.
  1327. */
  1328. function nodequeue_node_links($node) {
  1329. $links = array();
  1330. if (variable_get('nodequeue_links', FALSE) &&
  1331. user_access('manipulate queues')) {
  1332. $queues = nodequeue_load_queues_by_type($node->type, 'links');
  1333. $subqueues = nodequeue_get_subqueues_by_node($queues, $node);
  1334. if (empty($subqueues)) {
  1335. return;
  1336. }
  1337. // resort the subqueues to retain consistent ordering:
  1338. ksort($subqueues);
  1339. // Due to caching, we can accidentally get positions leftover
  1340. // from previous iterations on teaser list pages, so we must
  1341. // remove any existing positions here.
  1342. foreach ($subqueues as $id => $subqueue) {
  1343. unset($subqueues[$id]->position);
  1344. }
  1345. if (!module_exists('translation')) {
  1346. nodequeue_set_subqueue_positions($subqueues, $node->nid);
  1347. }
  1348. foreach ($subqueues as $subqueue) {
  1349. $queue = $queues[$subqueue->qid];
  1350. $id = nodequeue_get_content_id($queue, $node);
  1351. if (module_exists('translation')) {
  1352. $subqueue = array($subqueue->sqid => $subqueue);
  1353. nodequeue_set_subqueue_positions($subqueue, $id);
  1354. $subqueue = array_shift($subqueue);
  1355. }
  1356. $query_string = nodequeue_get_query_string($id, TRUE);
  1357. $class = 'nodequeue-ajax-toggle nodequeue-toggle-q-' . $queue->qid . ' nodequeue-toggle-sq-' . $subqueue->sqid . ' nodequeue-toggle-ref-' . $subqueue->reference;
  1358. if (!isset($subqueue->position)) {
  1359. $links[$class] = array(
  1360. 'title' => nodequeue_title_substitute($queue->link, $queue, $subqueue),
  1361. 'href' => "nodequeue/$queue->qid/add-node/$subqueue->sqid/$id",
  1362. 'attributes' => array('class' => array($class . ' toggle-add')),
  1363. 'query' => $query_string,
  1364. 'purl' => array('disabled' => TRUE),
  1365. );
  1366. }
  1367. elseif ($queue->link_remove) {
  1368. $links[$class] = array(
  1369. 'title' => nodequeue_title_substitute($queue->link_remove, $queue, $subqueue),
  1370. 'href' => "nodequeue/$queue->qid/remove-node/$subqueue->sqid/$id",
  1371. 'attributes' => array('class' => array($class . ' toggle-remove')),
  1372. 'query' => $query_string,
  1373. 'purl' => array('disabled' => TRUE),
  1374. );
  1375. }
  1376. }
  1377. drupal_add_js(drupal_get_path('module', 'nodequeue') . '/nodequeue.js');
  1378. drupal_add_css(drupal_get_path('module', 'nodequeue') . '/nodequeue.css');
  1379. }
  1380. return $links;
  1381. }
  1382. /**
  1383. * Get node_view output from a nodequeue.
  1384. */
  1385. function nodequeue_view_nodes($sqid, $backward = TRUE, $teaser = TRUE, $links = TRUE, $from = 0, $count = 0) {
  1386. $output = array();
  1387. $nodes = nodequeue_load_nodes($sqid, $backward, $from, $count);
  1388. foreach ($nodes as $node) {
  1389. $output[] = node_view($node, $teaser, FALSE, $links);
  1390. }
  1391. return $output;
  1392. }
  1393. /**
  1394. * Load an array of node objects belonging to a particular nodequeue.
  1395. */
  1396. function nodequeue_load_nodes($sqid, $backward = FALSE, $from = 0, $count = 5, $published_only = TRUE) {
  1397. $orderby = ($backward ? "DESC" : "ASC");
  1398. $query = db_select('node', 'n')
  1399. ->fields('n', array('nid'))
  1400. ->condition('nn.sqid', $sqid)
  1401. ->orderBy('nn.position', $orderby)
  1402. ->addTag('node_access');
  1403. $query->join('nodequeue_nodes', 'nn', 'n.nid = nn.nid');
  1404. if ($published_only) {
  1405. $query->condition('n.status', 1);
  1406. }
  1407. if ($count) {
  1408. $result = $query->range($from, $count)->execute();
  1409. }
  1410. else {
  1411. $result = $query->execute();
  1412. }
  1413. $nodes = array();
  1414. foreach ($result as $nid) {
  1415. $nodes[] = node_load($nid->nid);
  1416. }
  1417. return $nodes;
  1418. }
  1419. /**
  1420. * Load the first node of a queue.
  1421. */
  1422. function nodequeue_load_front($sqid) {
  1423. return array_shift(nodequeue_load_nodes($sqid, FALSE, 0, 1));
  1424. }
  1425. /**
  1426. * Load the last node of a queue.
  1427. */
  1428. function nodequeue_load_back($sqid, $teaser = TRUE, $links = TRUE) {
  1429. return array_shift(nodequeue_load_nodes($sqid, TRUE, 0, 1));
  1430. }
  1431. /**
  1432. * View a random node from a queue.
  1433. */
  1434. function nodequeue_view_random_node($sqid, $teaser = TRUE, $links = TRUE) {
  1435. $query = db_select('node', 'n')
  1436. ->fields('n', array('nid'));
  1437. $query->join('nodequeue_nodes', 'nn', 'n.nid = nn.nid');
  1438. $count = $query->addTag('node_access')
  1439. ->condition('nn.sqid', $sqid)
  1440. ->condition('n.status', 1)
  1441. ->countQuery()
  1442. ->execute()
  1443. ->fetchField();
  1444. return nodequeue_view_nodes($sqid, FALSE, $teaser, $links, rand(0, $count - 1), 1);
  1445. }
  1446. /**
  1447. * Load a random node object from a queue.
  1448. */
  1449. function nodequeue_load_random_node($sqid) {
  1450. $query = db_select('node', 'n')
  1451. ->fields('n', array('nid'));
  1452. $query->join('nodequeue_nodes', 'nn', 'n.nid = nn.nid');
  1453. $count = $query->addTag('node_access')
  1454. ->condition('nn.sqid', $sqid)
  1455. ->condition('n.status', 1)
  1456. ->countQuery()
  1457. ->execute()
  1458. ->fetchField();
  1459. return array_shift(nodequeue_load_nodes($sqid, TRUE, rand(0, $count - 1), 1));
  1460. }
  1461. /**
  1462. * Get the position of a node in a subqueue, or FALSE if not found.
  1463. */
  1464. function nodequeue_subqueue_position($sqid, $nid) {
  1465. return db_query("SELECT position FROM {nodequeue_nodes} WHERE sqid = :sqid AND nid = :nid", array(':sqid' => $sqid, ':nid' => $nid))->fetchField();
  1466. }
  1467. /**
  1468. * Get the position of a node in a queue; this queue MUST have only one
  1469. * subqueue or the results of this function will be unpredictable.
  1470. */
  1471. function nodequeue_queue_position($qid, $nid) {
  1472. $sqid = db_select('nodequeue_subqueue', 'ns')
  1473. ->fields('ns', array('sqid'))
  1474. ->condition('qid', $qid)
  1475. ->range(0, 1)
  1476. ->execute()
  1477. ->fetchField();
  1478. return nodequeue_subqueue_position($sqid, $nid);
  1479. }
  1480. // --------------------------------------------------------------------------
  1481. // API for modules implementing subqueues.
  1482. /**
  1483. * Send the nodequeue edit form to the owning module for modification.
  1484. *
  1485. * @param $queue
  1486. * The queue being edited.
  1487. * @param &$form
  1488. * The form. This may be modified.
  1489. */
  1490. function nodequeue_api_queue_form($queue, &$form) {
  1491. $function = $queue->owner . "_nodequeue_form";
  1492. if (function_exists($function)) {
  1493. $function($queue, $form);
  1494. }
  1495. }
  1496. /**
  1497. * Validate the nodequeue edit form.
  1498. *
  1499. * @param $queue
  1500. * The queue being edited.
  1501. * @param $form_state
  1502. * The form values that were submitted.
  1503. * @param &$form
  1504. * The actual form object. This may be modified.
  1505. */
  1506. function nodequeue_api_queue_form_validate($queue, &$form_state, &$form) {
  1507. $function = $queue->owner . "_nodequeue_form_validate";
  1508. if (function_exists($function)) {
  1509. $function($queue, $form_state, $form);
  1510. }
  1511. }
  1512. /**
  1513. * Send the nodequeue edit form to the owning module upon submit.
  1514. *
  1515. * @param &$queue
  1516. * The queue being edited. This may be modified prior to being
  1517. * saved.
  1518. * @param $form_state
  1519. * The form values that were submitted.
  1520. */
  1521. function nodequeue_api_queue_form_submit(&$queue, &$form_state) {
  1522. $function = $queue->owner . "_nodequeue_form_submit";
  1523. if (function_exists($function)) {
  1524. $function($queue, $form_state);
  1525. }
  1526. }
  1527. /**
  1528. * Send the nodequeue edit form to the owning module after the queue
  1529. * has been saved.
  1530. *
  1531. * @param &$queue
  1532. * The queue being edited. This may be modified prior to being
  1533. * saved.
  1534. * @param $form_state
  1535. * The form values that were submitted.
  1536. */
  1537. function nodequeue_api_queue_form_submit_finish($queue, &$form_state) {
  1538. $function = $queue->owner . "_nodequeue_form_submit_finish";
  1539. if (function_exists($function)) {
  1540. $function($queue, $form_state);
  1541. }
  1542. }
  1543. /**
  1544. * Fetch a list of subqueues that are valid for this node from
  1545. * the owning module.
  1546. *
  1547. * @param $queue
  1548. * The queue being edited.
  1549. * @param $node
  1550. * The loaded node object being checked.
  1551. *
  1552. * @return
  1553. * An array of subqueues. This will be keyed by $sqid.
  1554. */
  1555. function nodequeue_api_subqueues(&$queue, $node) {
  1556. $function = $queue->owner . "_nodequeue_subqueues";
  1557. // This will return an array of references.
  1558. if (function_exists($function)) {
  1559. return $function($queue, $node);
  1560. }
  1561. else {
  1562. return $queue->qid;
  1563. }
  1564. }
  1565. /**
  1566. * Fetch a list of nodes available to a given subqueue
  1567. * for autocomplete.
  1568. *
  1569. * @param $queue
  1570. * The queue that owns the subqueue
  1571. * @param $subqueue
  1572. * The subqueue
  1573. * @param $string
  1574. * The string being matched.
  1575. *
  1576. * @return
  1577. * An keyed array $nid => $title
  1578. */
  1579. function nodequeue_api_autocomplete($queue, $subqueue, $string) {
  1580. $matches = array();
  1581. if (empty($string)) {
  1582. return $matches;
  1583. }
  1584. $query = db_select('node', 'n')
  1585. ->addTag('node_access')
  1586. ->fields('n', array('nid', 'tnid', 'title'))
  1587. ->range(0, variable_get('nodequeue_autocomplete_limit', 10));
  1588. if (!empty($queue->types)) {
  1589. $query->condition('n.type', $queue->types, 'IN');
  1590. }
  1591. $where_args = array();
  1592. global $user;
  1593. if (!user_access('administer nodes', $user)) {
  1594. $query->condition(db_or()->condition('n.status', 1)->condition('n.uid', $user->uid));
  1595. }
  1596. // Run a match to see if they're specifying by nid.
  1597. $preg_matches = array();
  1598. $match = preg_match('/\[nid: (\d+)\]/', $string, $preg_matches);
  1599. if (!$match) {
  1600. $match = preg_match('/^nid: (\d+)/', $string, $preg_matches);
  1601. }
  1602. if ($match) {
  1603. // If it found a nid via specification, reduce our resultset to just that nid.
  1604. $query->condition('n.nid', $preg_matches[1]);
  1605. }
  1606. else {
  1607. // Build the constant parts of the query.
  1608. $query->where('LOWER(n.title) LIKE LOWER(:string)', array(':string' => '%' . db_like($string) . '%'));
  1609. }
  1610. // Call to the API.
  1611. $function = $queue->owner . "_nodequeue_autocomplete";
  1612. if (function_exists($function)) {
  1613. return $function($queue, $subqueue, $string, $where, $where_args);
  1614. }
  1615. else {
  1616. $query->addTag('i18n_select');
  1617. $result = $query->execute();
  1618. foreach ($result as $node) {
  1619. $id = nodequeue_get_content_id($queue, $node);
  1620. $matches[$node->nid] = check_plain($node->title) . " [nid: $id]";
  1621. }
  1622. }
  1623. return $matches;
  1624. }
  1625. /**
  1626. * Collect info about all of the possible nodequeue types from owning
  1627. * modules.
  1628. */
  1629. function nodequeue_api_info() {
  1630. return module_invoke_all('nodequeue_info');
  1631. }
  1632. function nodequeue_api_queue_access($queue, $account = NULL) {
  1633. if (!$account) {
  1634. global $user;
  1635. $account = $user;
  1636. }
  1637. if ($queue->owner != 'nodequeue') { // Avoids an infinite loop.
  1638. $function = $queue->owner . '_queue_access';
  1639. if (function_exists($function)) {
  1640. $access = $function($queue, $account);
  1641. }
  1642. }
  1643. if (!isset($access)) {
  1644. $access = TRUE;
  1645. }
  1646. return $access;
  1647. }
  1648. /**
  1649. * Allows the owning module of a subqueue to restrict access to viewing and
  1650. * manipulating the queue.
  1651. */
  1652. function nodequeue_api_subqueue_access($subqueue, $account = NULL, $queue = NULL) {
  1653. if (!$account) {
  1654. global $user;
  1655. $account = $user;
  1656. }
  1657. if (!$queue) {
  1658. $queue = nodequeue_load($subqueue->qid);
  1659. }
  1660. $function = $queue->owner . '_subqueue_access';
  1661. if (function_exists($function)) {
  1662. $access = $function($subqueue, $account, $queue);
  1663. }
  1664. if (!isset($access)) {
  1665. $access = TRUE;
  1666. }
  1667. return $access;
  1668. }
  1669. /**
  1670. * Generate a query string to use on nodequeue's private links.
  1671. *
  1672. * @param $seed
  1673. * The seed to use when generating a token. If NULL no token will
  1674. * be generated.
  1675. * @param $destination
  1676. * The destination to use. If FALSE one won't be used; if TRUE
  1677. * one will be generated from drupal_get_destination().
  1678. * @param $query
  1679. * An array of additional items to add to the query.
  1680. *
  1681. * @return
  1682. * The query string suitable for use in the l() function.
  1683. */
  1684. function nodequeue_get_query_string($seed, $destination = FALSE, $query = array()) {
  1685. $dest = drupal_get_destination();
  1686. foreach ($dest as $key => $value) {
  1687. $query[$key] = $value;
  1688. }
  1689. if (isset($seed)) {
  1690. $token = explode('=', nodequeue_get_token($seed));
  1691. $query[$token[0]] = $token[1];
  1692. }
  1693. return $query;
  1694. return implode('&', $query);
  1695. }
  1696. /**
  1697. * Get a private token used to protect nodequeue's links from spoofing.
  1698. */
  1699. function nodequeue_get_token($nid) {
  1700. return 'token=' . drupal_get_token($nid);
  1701. }
  1702. /**
  1703. * Check to see if the token generated from seed matches.
  1704. */
  1705. function nodequeue_check_token($seed) {
  1706. return drupal_get_token($seed) == $_GET['token'];
  1707. }
  1708. /* --- UTILITY -------------------------------------------------------------- */
  1709. /**
  1710. * Helper function - since hook_menu now takes a function instead of a boolean,
  1711. * this function is used to compute the user's access.
  1712. *
  1713. * @return boolean
  1714. */
  1715. function _nodequeue_access_admin_or_manipulate() {
  1716. return user_access('administer nodequeue') || user_access('manipulate queues');
  1717. }
  1718. /**
  1719. * Used by menu system to determine access to the node and the queue in question.
  1720. *
  1721. * No, this isn't some odd hook_access implementation.
  1722. *
  1723. * @param unknown_type $node
  1724. * @param unknown_type $queue
  1725. * @return unknown
  1726. */
  1727. function nodequeue_node_and_queue_access($node, $queue, $subqueue = NULL) {
  1728. return nodequeue_nodequeue_access($node->type) && nodequeue_queue_access($queue, $subqueue);
  1729. }
  1730. /**
  1731. * Return TRUE if $user can queue(s) for this node.
  1732. *
  1733. * @param $type
  1734. * The node type.
  1735. * @param $location
  1736. * Optional argument. May be one of:
  1737. * - 'links': Only check for queues that have node links.
  1738. * - 'tab': Only check for queues that appear on the node tab.
  1739. * - 'ui': Only check for queues that appear in the UI.
  1740. */
  1741. function nodequeue_nodequeue_access($type, $location = NULL, $account = NULL) {
  1742. if (isset($type->type)) {
  1743. $type = $type->type;
  1744. }
  1745. $qids = nodequeue_get_qids($type, $account);
  1746. if ($location) {
  1747. nodequeue_filter_qids($qids, $location);
  1748. }
  1749. return !empty($qids);
  1750. }
  1751. /**
  1752. * Return TRUE If the specified account has access to manipulate this queue.
  1753. */
  1754. function nodequeue_queue_access($queue, $subqueue = NULL, $account = NULL) {
  1755. if (!$account) {
  1756. global $user;
  1757. $account = $user;
  1758. }
  1759. // Automatically true if all queues.
  1760. if (user_access('manipulate all queues', $account)) {
  1761. return TRUE;
  1762. }
  1763. // Automatically false if they can't manipulate queues at all.
  1764. if (!user_access('manipulate queues', $account) || empty($queue->roles)) {
  1765. return FALSE;
  1766. }
  1767. if ($subqueue) {
  1768. return nodequeue_api_subqueue_access($subqueue, $account);
  1769. }
  1770. if (!nodequeue_api_queue_access($queue, $account)) {
  1771. return FALSE;
  1772. }
  1773. $roles = array_keys((array) $account->roles) + array(DRUPAL_AUTHENTICATED_RID);
  1774. return (bool) array_intersect($roles, $queue->roles);
  1775. }
  1776. function nodequeue_node_tab_access($node) {
  1777. if (!variable_get('nodequeue_use_tab', 1) || !user_access('manipulate queues')) {
  1778. // For performance reasons: If the menu tab is disabled or the user can't
  1779. // manipulate queues, there is no reason to run the rest of these queries.
  1780. return FALSE;
  1781. }
  1782. $queues = nodequeue_load_queues_by_type($node->type, 'tab');
  1783. $subqueues = nodequeue_get_subqueues_by_node($queues, $node);
  1784. if (empty($subqueues)) {
  1785. return FALSE;
  1786. }
  1787. foreach ($subqueues as $subqueue) {
  1788. if (nodequeue_api_subqueue_access($subqueue)) {
  1789. return TRUE;
  1790. }
  1791. }
  1792. return FALSE;
  1793. }
  1794. /**
  1795. * Print the JSON output for our AJAX calls.
  1796. */
  1797. function nodequeue_js_output($label, $href, $count = NULL, $sqid = NULL) {
  1798. $return = new stdClass();
  1799. $return->status = 1;
  1800. $return->label = check_plain($label);
  1801. $return->href = $href;
  1802. if (isset($count)) {
  1803. $return->count = $count;
  1804. }
  1805. if (isset($sqid)) {
  1806. $return->sqid = $sqid;
  1807. }
  1808. drupal_json_output($return);
  1809. exit;
  1810. }
  1811. /**
  1812. * Return content id based on i18n settings
  1813. */
  1814. function nodequeue_get_content_id($queue, $node) {
  1815. return ($queue->i18n && !empty($node->tnid)) ? $node->tnid : $node->nid;
  1816. }
  1817. /**
  1818. * Determine if the machine name is in use.
  1819. */
  1820. function nodequeue_machine_name_exists($machine_name) {
  1821. $queue_exists = db_query_range('SELECT 1 FROM {nodequeue_queue} WHERE name = :name', 0, 1, array(':name' => $machine_name))->fetchField();
  1822. return $queue_exists;
  1823. }
  1824. /**
  1825. * Get the list of nodes in the subqueue, taking into account node access restrictions.
  1826. */
  1827. function nodequeue_nids_visible($sqid = -1, $account = NULL) {
  1828. $node_status_sql = '';
  1829. if (!$account) {
  1830. global $user;
  1831. $account = $user;
  1832. }
  1833. $nids_visible = array();
  1834. $query = db_select('node', 'n')
  1835. ->fields('n', array('nid'))
  1836. ->addTag('node_access')
  1837. ->distinct()
  1838. ->condition('nq.sqid', $sqid)
  1839. ->orderBy('nq.position', 'ASC');
  1840. $query->leftJoin('nodequeue_nodes', 'nq', 'nq.nid = n.nid');
  1841. if (!user_access('administer nodes', $account)) {
  1842. $query->condition(db_or()->condition('n.status', 1)->condition('n.uid', $account->uid));
  1843. }
  1844. // Disable i18n_select for this query.
  1845. if (arg(0) == 'admin') {
  1846. $query->addTag('i18n_select');
  1847. }
  1848. $query_restricted = $query->execute();
  1849. foreach ($query_restricted as $result_restricted) {
  1850. $nids_visible[$result_restricted->nid] = $result_restricted->nid;
  1851. }
  1852. return $nids_visible;
  1853. }
  1854. /* --- THEME ---------------------------------------------------------------- */
  1855. /**
  1856. * Theme the subqueue overview as a sortable list.
  1857. *
  1858. * @ingroup themeable
  1859. */
  1860. function theme_nodequeue_arrange_subqueue_form_table($variables) {
  1861. $form = $variables['form'];
  1862. $output = '';
  1863. // Get css to hide some of the help text if javascript is disabled.
  1864. drupal_add_css(drupal_get_path('module', 'nodequeue') . '/nodequeue.css');
  1865. $table_id = 'nodequeue-dragdrop-' . $form['#subqueue']['sqid'];
  1866. $table_classes = array(
  1867. 'nodequeue-dragdrop',
  1868. 'nodequeue-dragdrop-qid-' . $form['#subqueue']['qid'],
  1869. 'nodequeue-dragdrop-sqid-' . $form['#subqueue']['sqid'],
  1870. 'nodequeue-dragdrop-reference-' . $form['#subqueue']['reference'],
  1871. );
  1872. drupal_add_tabledrag($table_id, 'order', 'sibling', 'node-position');
  1873. drupal_add_js(drupal_get_path('module', 'nodequeue') . '/nodequeue_dragdrop.js');
  1874. $reverse[str_replace('-', '_', $table_id)] = (bool) $form['#queue']['reverse'];
  1875. drupal_add_js(
  1876. array(
  1877. 'nodequeue' => array(
  1878. 'reverse' => $reverse,
  1879. )
  1880. ),
  1881. array(
  1882. 'type' => 'setting',
  1883. 'scope' => JS_DEFAULT,
  1884. )
  1885. );
  1886. // Render form as table rows.
  1887. $rows = array();
  1888. $counter = 1;
  1889. foreach (element_children($form) as $key) {
  1890. if (isset($form[$key]['title'])) {
  1891. $row = array();
  1892. $row[] = drupal_render($form[$key]['title']);
  1893. $row[] = drupal_render($form[$key]['author']);
  1894. $row[] = drupal_render($form[$key]['date']);
  1895. $row[] = drupal_render($form[$key]['position']);
  1896. $row[] = (!empty($form[$key]['edit'])) ? drupal_render($form[$key]['edit']) : '&nbsp;';
  1897. $row[] = drupal_render($form[$key]['remove']);
  1898. $row[] = array(
  1899. 'data' => $counter,
  1900. 'class' => array('position')
  1901. );
  1902. $rows[] = array(
  1903. 'data' => $row,
  1904. 'class' => array('draggable'),
  1905. );
  1906. }
  1907. $counter++;
  1908. }
  1909. if (empty($rows)) {
  1910. $rows[] = array(array('data' => t('No nodes in this queue.'), 'colspan' => 7));
  1911. }
  1912. // Render the main nodequeue table.
  1913. $header = array(t('Title'), t('Author'), t('Post Date'), t('Position'), array('data' => t('Operations'), 'colspan' => 2), t('Position'));
  1914. $output .= theme('table', array('header' => $header, 'rows' => $rows, 'attributes' => array('id' => $table_id, 'class' => $table_classes)));
  1915. return $output;
  1916. }
  1917. /**
  1918. * Return a "queue is empty" message.
  1919. *
  1920. * @ingroup themeable
  1921. */
  1922. function theme_nodequeue_subqueue_empty_text() {
  1923. return t('Queue empty');
  1924. }
  1925. /**
  1926. * Return a "queue is full" message.
  1927. *
  1928. * @ingroup themeable
  1929. */
  1930. function theme_nodequeue_subqueue_full_text() {
  1931. return t('Queue full');
  1932. }
  1933. /**
  1934. * Return a count of elements in the queue.
  1935. *
  1936. * @ingroup themeable
  1937. */
  1938. function theme_nodequeue_subqueue_count_text($variables) {
  1939. return t('@count in queue', array('@count' => $variables['count']));
  1940. }