array( 'title' => t('Administer nodequeue'), 'description' => t('Administer the nodequeue module.'), ), 'manipulate queues' => array( 'title' => t('Manipulate queues'), 'description' => t('Manipulate queues.'), ), 'manipulate all queues' => array( 'title' => t('Manipulate all queues'), 'description' => t('Manipulate all queues.'), ), ); } /** * Implements hook_init(). * * Loads subsidiary includes for other modules. */ function nodequeue_init() { include_once DRUPAL_ROOT . '/' . drupal_get_path('module', 'nodequeue') . '/includes/nodequeue.actions.inc'; } /** * Implements hook_menu(). */ function nodequeue_menu() { $items = array(); $admin_access = array('administer nodequeue'); $access = array('manipulate queues'); // administrative items $items['admin/structure/nodequeue'] = array( 'title' => 'Nodequeues', 'page callback' => 'nodequeue_view_queues', 'access callback' => '_nodequeue_access_admin_or_manipulate', 'description' => 'Create and maintain simple nodequeues.', 'file' => 'includes/nodequeue.admin.inc', 'type' => MENU_NORMAL_ITEM ); $items['admin/structure/nodequeue/list'] = array( 'title' => 'List', 'page callback' => 'nodequeue_view_queues', 'access callback' => '_nodequeue_access_admin_or_manipulate', 'file' => 'includes/nodequeue.admin.inc', 'weight' => -1, 'type' => MENU_DEFAULT_LOCAL_TASK ); $items['admin/structure/nodequeue/settings'] = array( 'title' => 'Settings', 'page callback' => 'drupal_get_form', 'page arguments' => array('nodequeue_admin_settings'), 'access arguments' => $admin_access, 'file' => 'includes/nodequeue.admin.inc', 'type' => MENU_LOCAL_TASK ); $items['nodequeue/autocomplete'] = array( 'title' => 'Autocomplete', 'page callback' => 'nodequeue_autocomplete', 'access arguments' => $access, 'file' => 'includes/nodequeue.admin.inc', 'type' => MENU_CALLBACK ); $info = nodequeue_api_info(); foreach ($info as $key => $data) { $items['admin/structure/nodequeue/add/' . $key] = array( 'title' => 'Add @type', 'title arguments' => array('@type' => strtolower($data['title'])), 'page callback' => 'drupal_get_form', 'page arguments' => array('nodequeue_edit_queue_form', $key), 'access arguments' => $admin_access, 'file' => 'includes/nodequeue.admin.inc', 'type' => MENU_LOCAL_ACTION ); } $items['node/%node/nodequeue'] = array( 'title' => '@tab', 'title arguments' => array('@tab' => variable_get('nodequeue_tab_name', 'Nodequeue')), 'page callback' => 'nodequeue_node_tab', 'page arguments' => array(1), 'access callback' => 'nodequeue_node_tab_access', 'access arguments' => array(1), 'file' => 'includes/nodequeue.admin.inc', 'weight' => 5, 'type' => MENU_LOCAL_TASK ); // Administrative items for an individual queue. $items['admin/structure/nodequeue/%nodequeue'] = array( 'page callback' => 'nodequeue_admin_view', 'page arguments' => array(3), 'access callback' => 'nodequeue_queue_access', 'access arguments' => array(3), 'file' => 'includes/nodequeue.admin.inc', 'type' => MENU_CALLBACK ); $items['admin/structure/nodequeue/%nodequeue/view'] = array( 'title' => 'View', 'page callback' => 'nodequeue_admin_view', 'page arguments' => array(3), 'access callback' => 'nodequeue_queue_access', 'access arguments' => array(3), 'file' => 'includes/nodequeue.admin.inc', 'weight' => -10, 'type' => MENU_DEFAULT_LOCAL_TASK ); $items['admin/structure/nodequeue/%nodequeue/view/%subqueue'] = array( 'title' => 'View', 'page callback' => 'nodequeue_admin_view', 'page arguments' => array(3, 5), 'access callback' => 'nodequeue_queue_access', 'access arguments' => array(3, 5), 'file' => 'includes/nodequeue.admin.inc', 'weight' => -10, 'tab parent' => 'admin/structure/nodequeue/%', 'type' => MENU_CALLBACK ); // Actual administrative items. $items['admin/structure/nodequeue/%nodequeue/edit'] = array( 'title' => 'Edit queue', 'page callback' => 'drupal_get_form', 'page arguments' => array('nodequeue_edit_queue_form', 3), 'access arguments' => $admin_access, 'file' => 'includes/nodequeue.admin.inc', 'type' => MENU_LOCAL_TASK ); $items['admin/structure/nodequeue/%nodequeue/delete'] = array( 'title' => 'Delete', 'page callback' => 'drupal_get_form', 'page arguments' => array('nodequeue_admin_delete', 3), 'access arguments' => $admin_access, 'file' => 'includes/nodequeue.admin.inc', 'weight' => 5, 'type' => MENU_CALLBACK ); $items['nodequeue/%nodequeue/add-node/%subqueue/%node'] = array( 'page callback' => 'nodequeue_admin_add_node', 'page arguments' => array(1, 3, 4), 'access callback' => 'nodequeue_node_and_queue_access', 'access arguments' => array(4, 1, 3), 'file' => 'includes/nodequeue.admin.inc', 'type' => MENU_CALLBACK ); $items['nodequeue/%nodequeue/remove-node/%subqueue/%node'] = array( 'page callback' => 'nodequeue_admin_remove_node', 'page arguments' => array(1, 3, 4), 'access callback' => 'nodequeue_node_and_queue_access', 'access arguments' => array(4, 1, 3), 'file' => 'includes/nodequeue.admin.inc', 'type' => MENU_CALLBACK ); $items["admin/structure/nodequeue/%nodequeue/clear/%subqueue"] = array( 'title' => 'Clear', 'page callback' => 'drupal_get_form', 'page arguments' => array('nodequeue_clear_confirm', 3, 5), 'access callback' => 'nodequeue_queue_access', 'access arguments' => array(3, 5), 'file' => 'includes/nodequeue.admin.inc', 'type' => MENU_CALLBACK ); return $items; } /** * Helper function for a _menu_translate() bug. */ function subqueue_to_arg() { return ''; } /** * Implements hook_admin_paths(). */ function nodequeue_admin_paths() { if (variable_get('node_admin_theme')) { $paths = array( 'node/*/nodequeue' => TRUE, ); return $paths; } } /** * Implements hook_node_delete. */ function nodequeue_node_delete($node) { // If a node is being deleted, ensure it's also removed from any queues. $result = db_query("SELECT qid, sqid FROM {nodequeue_nodes} WHERE nid =:nid", array( ':nid' => $node->nid, )); foreach ($result as $obj) { // If the queue is being tracked by translation set and the node is part // of a translation set, don't delete the queue record. // Instead, data will be updated in the 'translation_change' op, below. $queues = nodequeue_load_queues(array($obj->qid)); $queue = array_shift($queues); if (!$queue->i18n || (isset($node->tnid) && empty($node->tnid))) { // This removes by nid, not position, because if we happen to have a // node in a queue twice, the 2nd position would be wrong. nodequeue_subqueue_remove_node($obj->sqid, $node->nid); } } } /** * Implements hook_node_view(). */ function nodequeue_node_view($node, $view_mode) { $links = nodequeue_node_links($node); if (!empty($links)) { $node->content['links']['nodequeue'] = array( '#links' => $links, '#theme' => 'links__node__nodequeue', ); } } /** * Implementats hook_forms(). */ function nodequeue_forms($form_id) { $forms = array(); if (strpos($form_id, 'nodequeue_arrange_subqueue_form_') === 0) { $forms[$form_id] = array( 'callback' => 'nodequeue_arrange_subqueue_form', ); } return $forms; } /** * Implements hook_theme(). */ function nodequeue_theme() { return array( 'nodequeue_arrange_subqueue_form_table' => array( 'render element' => 'form', ), 'nodequeue_subqueue_empty_text' => array( 'variables' => array(), ), 'nodequeue_subqueue_full_text' => array( 'variables' => array(), ), 'nodequeue_subqueue_count_text' => array( 'variables' => array('count' => 0), ), ); } /** * Implements hook_element_info(). */ function nodequeue_element_info() { $type = array(); $type['position'] = array( '#input' => TRUE, '#delta' => 10, '#default_value' => 0, '#process' => array('process_position', 'ajax_process_form'), ); return $type; } /** * Expand position elements into selects. Works like the weight element, except * only positive values are allowed. */ function process_position($element) { for ($n = 1; $n <= $element['#delta']; $n++) { $positions[$n] = $n; } $element['#options'] = $positions; $element['#options']['r'] = t('Remove'); $element['#type'] = 'select'; // add default properties for the select element $element += element_info('select'); return $element; } /** * If no default value is set for position select boxes, use 1. */ function position_value(&$form) { if (isset($form['#default_value'])) { $form['#value'] = $form['#default_value']; } else { $form['#value'] = 1; } } /** * Implements hook_views_api(). */ function nodequeue_views_api() { return array( 'api' => 2, 'path' => drupal_get_path('module', 'nodequeue') . '/includes/views', ); } // -------------------------------------------------------------------------- // Nodequeue Apache Solr Search Integration /** * Implements hook_form_FORM_ID_alter(). */ function nodequeue_form_apachesolr_search_bias_form_alter(&$form, &$form_state, $form_id) { // Setup for the form building. $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')); $weights['0'] = t('Normal'); $queues = nodequeue_load_subqueues_by_queue(array_keys(nodequeue_get_all_qids())); // Build the form. $form['biasing']['nodequeue_boost'] = array( '#type' => 'fieldset', '#title' => t('Nodequeue Biasing'), '#weight' => -5, '#collapsible' => TRUE, '#collapsed' => TRUE, ); $form['biasing']['nodequeue_boost']['nodequeue_apachesolr_boost'] = array( '#type' => 'item', '#description' => t("Specify to bias the search result when a node is in a queue. Any value except Normal will increase the socre for the given queue in the search results"), ); foreach ($queues as $sqid => $queue) { $boost = variable_get("nodequeue_apachesolr_boost_$sqid", 0); // Add in setting for each queue. $form['biasing']['nodequeue_boost']['nodequeue_apachesolr_boost']["nodequeue_apachesolr_boost_$sqid"] = array( '#type' => 'select', '#title' => t('Weight for %title nodequeue', array('%title' => $queue->title)), '#options' => $weights, '#default_value' => $boost, ); } } /** * Implements hook_apachesolr_update_index(). */ function nodequeue_apachesolr_update_index(&$document, $node) { if (empty($document)) { return; } $queues = nodequeue_load_queues(array_keys(nodequeue_get_all_qids())); $subqueues = nodequeue_get_subqueues_by_node($queues, $node); nodequeue_set_subqueue_positions($subqueues, $node->nid); if (is_array($subqueues)) { foreach ($subqueues as $sqid => $subqueue) { if (!empty($subqueue->position)) { $key = _nodequeue_solr_qid_key(); $document->setMultiValue($key, $sqid); } } } } /** * Returns the apachesolr index key for group id. */ function _nodequeue_solr_qid_key() { $qid_key = array( 'index_type' => 'sint', 'multiple' => TRUE, 'name' => "nodequeue", ); return apachesolr_index_key($qid_key); } /** * Implements hook_apachesolr_query_alter(). */ function nodequeue_apachesolr_query_alter(DrupalSolrQueryInterface $query) { $queues = nodequeue_load_subqueues_by_queue(array_keys(nodequeue_get_all_qids())); $added = FALSE; foreach ($queues as $sqid => $queue) { $boost = variable_get("nodequeue_apachesolr_boost_$sqid", 0); if (!empty($boost)) { $query->params['bq'][] = _nodequeue_solr_qid_key() . ":$sqid^$boost"; if (!$added) { // Only want to add the facet.field once. no need to repeat it. $query->params['facet.field'][] = _nodequeue_solr_qid_key(); $added = TRUE; } } } } // -------------------------------------------------------------------------- // Nodequeue manipulation API. /** * @defgroup nodequeue_api * @{ * Access to the internals of nodequeues are handled primarily through these * API functions. They allow easy loading of queues for manipulation. */ /** * The nodequeue queue class; the constructor makes it so we don't have to * always check to see if our variables are empty or not. */ class nodequeue_queue { var $name = ''; var $title = ''; var $size = 0; var $link = ''; var $link_remove = ''; var $roles = array(); var $types = array(); var $show_in_links = TRUE; var $show_in_tab = TRUE; var $show_in_ui = TRUE; var $reference = 0; var $i18n = 0; var $subqueue_title = ''; var $reverse = 0; // runtime var $subqueues = array(); var $subqueue = NULL; var $current = NULL; function nodequeue_queue($type) { $this->owner = $type; } } /** * Fetch a list of available queues for a given location. These queues * will be fully loaded and ready to go. */ function nodequeue_load_queues_by_type($type, $location = NULL, $account = NULL, $bypass_cache = FALSE) { $qids = nodequeue_get_qids($type, $account, $bypass_cache); if ($location) { nodequeue_filter_qids($qids, $location); } return nodequeue_load_queues(array_keys($qids), $bypass_cache); } /** * Filter a list of qids returned by nodequeue_get_qids to a location. * * @param $qids * An array of $qids from @see nodequeue_get_qids() * @param $location * One of: * - 'links': Only check for queues that have node links. * - 'tab': Only check for queues that appear on the node tab. * - 'ui': Only check for queues that appear in the UI. */ function nodequeue_filter_qids(&$qids, $location) { $var = "show_in_$location"; foreach ($qids as $qid => $info) { if (empty($info->$var)) { unset($qids[$qid]); } } } /** * Get an array of qids applicable to this node type. * * @param $type * The node type. * @param $account * The account to test against. Defaults to the currently logged in user. * * @return $qids * An array in the format: @code { array($qid => array('qid' => $qid, 'show_in_tab' ' * => true/false, 'show_in_links' => true/false } * * @param $bypass_cache * Boolean value indicating whether to bypass the cache or not. */ function nodequeue_get_qids($type, $account = NULL, $bypass_cache = FALSE) { if (!isset($account)) { global $user; $account = $user; } static $cache = array(); if ($bypass_cache || !isset($cache[$type])) { $roles_join = $roles_where = ''; $roles = array(); // superuser always has access. if (!user_access('manipulate all queues', $account)) { $roles_join = "INNER JOIN {nodequeue_roles} nr ON nr.qid = nq.qid "; $roles = array_keys((array) $account->roles) + array(DRUPAL_AUTHENTICATED_RID); $roles_where .= "AND nr.rid IN (:roles)"; } $sql = 'SELECT nq.qid, nq.show_in_tab, nq.show_in_links, nq.show_in_ui, nq.i18n ' . 'FROM {nodequeue_queue} nq ' . 'INNER JOIN {nodequeue_types} nt ON nt.qid = nq.qid ' . $roles_join . "WHERE nt.type = :type " . $roles_where; $result = db_query($sql, array(':type' => $type, ':roles' => $roles)); $qids = array(); foreach ($result as $qid) { $qids[$qid->qid] = $qid; } $cache[$type] = $qids; } return $cache[$type]; } /** * Get an array of 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. * * @param $page_size * The page size to use. If this is 0 or NULL, all queues will be returned. * Defaults to 0. * @param $pager_element * In the rare event this should use another pager element, set this.. * @param $bypass_cache * Boolean value indicating whether to bypass the cache or not. * * @return $qids * An array in the format: @code { array($qid => $qid) } */ function nodequeue_get_all_qids($page_size = 0, $pager_element = 0, $bypass_cache = FALSE) { $cache = &drupal_static(__FUNCTION__, array()); if ($bypass_cache || empty($cache[$page_size])) { $query = db_select('nodequeue_queue', 'nq') ->fields('nq', array('qid')); if (!empty($page_size)) { $query->extend('PagerDefault') ->extend('TableSort') ->limit($page_size) ->element($pager_element); } $qids = $query->execute()->fetchAllKeyed(0, 0); $cache[$page_size] = $qids; } return $cache[$page_size]; } /** * Load an array of $qids. * * This exists to provide a way of loading a bunch of queues with * the fewest queries. Loading 5 queues results in only 4 queries, * not 20. This also caches queues so that they don't get loaded * repeatedly. * * @param $qids * An array of queue IDs to load. * * @param $bypass_cache * Boolean value indicating whether to bypass the cache or not. */ function nodequeue_load_queues($qids = array(), $bypass_cache = FALSE) { static $cache = array(); $to_load = $loaded = array(); foreach ($qids as $qid) { if ($bypass_cache || !isset($cache[$qid])) { $to_load[] = $qid; } } if (!empty($to_load)) { $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)); foreach ($result as $queue) { $loaded[$queue->qid] = $queue; // ensure valid defaults: $loaded[$queue->qid]->types = array(); $loaded[$queue->qid]->roles = array(); $loaded[$queue->qid]->count = 0; } $result = db_query("SELECT qid, rid FROM {nodequeue_roles} WHERE qid IN (:to_load)", array(':to_load' => $to_load)); foreach ($result as $obj) { $loaded[$obj->qid]->roles[] = $obj->rid; } $result = db_query("SELECT qid, type FROM {nodequeue_types} WHERE qid IN (:to_load)", array(':to_load' => $to_load)); foreach ($result as $obj) { $loaded[$obj->qid]->types[] = $obj->type; } $context = 'load_queues'; drupal_alter('nodequeue', $loaded, $context); } if ($bypass_cache) { return $loaded; } else { if (!empty($loaded)) { $cache += $loaded; } $queues = array(); foreach ($qids as $qid) { if (isset($cache[$qid])) { $queues[$qid] = $cache[$qid]; } } return $queues; } } /** * Load a nodequeue. * * @param $qid * The qid of the queue to load. */ function nodequeue_load($qid) { $queues = nodequeue_load_queues(array($qid)); return !empty($queues) ? array_shift($queues) : array(); } /** * This function exists so that %subqueue will work in hook_menu. */ function subqueue_load($sqid) { if (!$sqid) { return NULL; } $queues = nodequeue_load_subqueues(array($sqid)); return !empty($queues) ? array_shift($queues) : array(); } /** * Load a list of subqueues * * This exists to provide a way of loading a bunch of queues with * the fewest queries. Loading 5 queues results in only 4 queries, * not 20. This also caches queues so that they don't get loaded * repeatedly. * * @param $sqids * An array of subqueue IDs to load. * @param $bypass_cache * Boolean value indicating whether to bypass the cache or not. */ function nodequeue_load_subqueues($sqids, $bypass_cache = FALSE) { static $cache = array(); $to_load = array(); foreach ($sqids as $sqid) { if ($bypass_cache || !isset($cache[$sqid])) { $to_load[] = $sqid; } } if (!empty($to_load)) { $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)); foreach ($result as $obj) { // Sometimes we want to get to subqueues by reference, sometimes by sqid. // sqid is always unique, but reference is sometimes more readily available. $cache[$obj->sqid] = $obj; } } foreach ($sqids as $sqid) { if (isset($cache[$sqid])) { $subqueues[$sqid] = $cache[$sqid]; } } return $subqueues; } /** * Load a single subqueue. * * @param $sqid * The subqueue ID to load. * @param $bypass_cache * Boolean value indicating whether to bypass the cache or not. */ function nodequeue_load_subqueue($sqid, $bypass_cache = FALSE) { $subqueues = nodequeue_load_subqueues(array($sqid), $bypass_cache); if ($subqueues) { return array_shift($subqueues); } } /** * Load the entire set of subqueues for a queue. * * This will load the entire set of subqueues for a given queue (and can * respect the pager, if desired). It does NOT cache the subqueues like * nodequeue_load_subqueues does, so beware of this mixed caching. * * @param $qids * A $qid or array of $qids * @param $page_size * If non-zero, use the pager_query and limit the page-size to the parameter. */ function nodequeue_load_subqueues_by_queue($qids, $page_size = 0) { if (is_numeric($qids)) { $qids = array($qids); } if (empty($qids)) { return array(); } $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 (:qids) GROUP BY s.sqid"; $result = db_query($query, array(':qids' => $qids)); $subqueues = array(); foreach ($result as $subqueue) { $subqueues[$subqueue->sqid] = $subqueue; } return $subqueues; } /** * Load a set of subqueues by reference. * * This can be used to load a set of subqueues by reference; it will primarily * be used by plugins that are managing subqueues. * * @param $references * A keyed array of references to load. The key is the $qid and each value * is another array of references. */ function nodequeue_load_subqueues_by_reference($references, $bypass_cache = FALSE) { static $cache = array(); $subqueues = array(); if ($bypass_cache) { $cache = array(); } if (!empty($references)) { $query = db_select('nodequeue_subqueue', 's') ->groupBy('s.sqid') ->fields('s'); $query->leftJoin('nodequeue_nodes', 'n', 'n.sqid = s.sqid'); $query->addExpression('COUNT(n.position)', 'count'); $where = db_or(); foreach ($references as $qid => $reference) { $where->condition(db_and()->condition('s.qid', $qid)->condition('s.reference', $reference)); } $query->condition($where); $result = $query->execute(); foreach ($result as $subqueue) { $cache[$subqueue->qid][$subqueue->reference] = $subqueues[$subqueue->sqid] = $subqueue; } } return $subqueues; } /** * Return a queue by its machine name. This is obviously not ideal due to the * extra queries, but probably preferable to changing current API calls. * * @param $name * The queue machine name * * @return * The queue definition, or an empty array if no queue was found with the * given machine name. */ function nodequeue_load_queue_by_name($name) { $map = nodequeue_get_qid_map(); if (isset($map[$name])) { $queues = nodequeue_load_queues(array($map[$name])); if ($queues) { return current($queues); } } return array(); } /** * Return a map of queue name to qid values to aid in various lookups. * * @return array * A array of qids, keyed by machine name. */ function nodequeue_get_qid_map() { static $map = array(); if (!$map) { $result = db_query("SELECT qid, name FROM {nodequeue_queue}"); while ($get = $result->fetchObject()) { $map[$get->name] = $get->qid; } } return $map; } /** * Save a nodequeue. This does not save subqueues; those must be added separately. */ function nodequeue_save(&$queue) { $nodequeue_queue_fields = array( 'name' => $queue->name, 'title' => $queue->title, 'subqueue_title' => $queue->subqueue_title, 'size' => $queue->size, 'link' => $queue->link, 'link_remove' => $queue->link_remove, 'owner' => $queue->owner, 'show_in_links' => ($queue->show_in_links) ? 1 : 0, 'show_in_tab' => $queue->show_in_tab, 'show_in_ui' => $queue->show_in_ui, 'i18n' => $queue->i18n, 'reverse' => $queue->reverse, 'reference' => $queue->reference, ); if (!isset($queue->qid)) { $queue->qid = db_insert('nodequeue_queue') ->fields($nodequeue_queue_fields) ->execute(); if (function_exists('views_invalidate_cache')) { views_invalidate_cache(); } } else { db_update('nodequeue_queue') ->fields($nodequeue_queue_fields) ->condition('qid', $queue->qid) ->execute(); db_delete('nodequeue_roles') ->condition('qid', $queue->qid) ->execute(); db_delete('nodequeue_types') ->condition('qid', $queue->qid) ->execute(); } if (is_array($queue->roles)) { foreach ($queue->roles as $rid) { db_insert('nodequeue_roles') ->fields(array( 'qid' => $queue->qid, 'rid' => $rid, )) ->execute(); } } if (is_array($queue->types)) { foreach ($queue->types as $type) { db_insert('nodequeue_types') ->fields(array( 'qid' => $queue->qid, 'type' => $type, )) ->execute(); } } // set our global that tells us whether or not we need to activate hook_link if (db_query("SELECT COUNT(*) FROM {nodequeue_queue} WHERE link <> ''")->fetchField()) { variable_set('nodequeue_links', TRUE); } else { variable_set('nodequeue_links', FALSE); } if (isset($queue->add_subqueue) && is_array($queue->add_subqueue)) { foreach ($queue->add_subqueue as $reference => $title) { // If reference is unset it should be set to the qid; this is generally // used for a single subqueue; setting the reference to the qid makes // it easy to find that one subqueue. if ($reference == 0) { $reference = $queue->qid; } nodequeue_add_subqueue($queue, $title, $reference); } } return $queue->qid; } /** * Delete a nodequeue. */ function nodequeue_delete($qid) { db_delete('nodequeue_roles') ->condition('qid', $qid) ->execute(); db_delete('nodequeue_types') ->condition('qid', $qid) ->execute(); db_delete('nodequeue_queue') ->condition('qid', $qid) ->execute(); db_delete('nodequeue_nodes') ->condition('qid', $qid) ->execute(); db_delete('nodequeue_subqueue') ->condition('qid', $qid) ->execute(); } /** * Add a new subqueue to a queue. * * @param $queue * The queue object that is the parent of this subqueue. * @param $title * The title of the subqueue. * @param $reference * A reference that uniquely identifies this subqueue. If NULL it will * be assigned the sqid. */ function nodequeue_add_subqueue(&$queue, $title, $reference = NULL) { if (empty($reference)) { $insert_reference = ""; } else { $insert_reference = $reference; } $subqueue = new stdClass(); $subqueue->reference = $reference; $subqueue->qid = $queue->qid; $subqueue->title = $title; $subqueue->sqid = db_insert('nodequeue_subqueue') ->fields(array( 'qid' => $queue->qid, 'reference' => $insert_reference, 'title' => $title, )) ->execute(); // If somehow the $reference is null, here we set it to the sqid. // We have to do it here, because before the insert we don't know what the sqid will be. if (empty($reference)) { db_update('nodequeue_subqueue') ->fields(array('reference' => $subqueue->sqid)) ->condition('sqid', $subqueue->sqid) ->execute(); } return $subqueue; } /** * Change the title of a subqueue. * * Note that only the title of a subqueue is changeable; it can change to * reflect updates in taxonomy term names, for example. */ function nodequeue_subqueue_update_title($sqid, $title) { db_update('nodequeue_subqueue') ->fields(array('title' => $title)) ->condition('sqid', $sqid) ->execute(); } /** * Remove a subqueue. */ function nodequeue_remove_subqueue($sqid) { nodequeue_queue_clear($sqid); db_delete('nodequeue_subqueue') ->condition('sqid', $sqid) ->execute(); } // -------------------------------------------------------------------------- // Queue position control /** * Add a node to a queue. * * @param $queue * The parent queue of the subqueue. This is required so that we can * pop nodes out if the queue breaks size limits. * @param $subqueue * The subqueue to add the node to. * @param $nid * The node ID */ function nodequeue_subqueue_add($queue, &$subqueue, $nid) { if (!empty($nid)) { 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)); $subqueue->count = db_query("SELECT COUNT(nid) FROM {nodequeue_nodes} WHERE sqid = :sqid", array(':sqid' => $subqueue->sqid))->fetchField(); // If adding this would make the queue too big, pop the front node // (or nodes) out. if (!empty($queue->size)) { // 0 means infinity so never do this if FALSE. nodequeue_check_subqueue_size($queue, $subqueue, $queue->size); } if (module_exists('apachesolr')) { apachesolr_mark_node($nid); } // Invoke the hook to notify other modules of the node addition. module_invoke_all('nodequeue_add', $subqueue->sqid, $nid); } } /** * Remove 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. * * @param $sqid * The subqueue to remove nodes from. * @param $nid * The node to remove. */ function nodequeue_subqueue_remove_node($sqid, $nid) { if ($pos = nodequeue_get_subqueue_position($sqid, $nid)) { nodequeue_subqueue_remove($sqid, $pos); if (module_exists('apachesolr')) { apachesolr_mark_node($nid); } } } /** * Remove a node or node(s) from a nodequeue by position. * * If you know the nid but but not the position, use * @see nodequeue_subqueue_remove_node() instead. * * @param $sqid * The subqueue to remove nodes from. * @param $start * The first position (starting from 1) to remove. * @param $end * The last position to remove. If NULL or equal to $start, * only one node will be removed. Thus if $start is 1 and $end is 2, * the first and second items will be removed from the queue. * */ function nodequeue_subqueue_remove($sqid, $start, $end = NULL) { if (!isset($end)) { $end = $start; } // Retrieve the nodes that are being removed. $result = db_query("SELECT nid FROM {nodequeue_nodes} WHERE sqid = :sqid AND position >= :start AND position <= :end", array( ':sqid' => $sqid, ':start' => $start, ':end' => $end, ) ); $diff = $end - $start + 1; db_delete('nodequeue_nodes') ->condition('sqid', $sqid) ->condition('position', $start, '>=') ->condition('position', $end, '<=') ->execute(); db_update('nodequeue_nodes') ->expression('position', 'position - ' . $diff) ->condition('sqid', $sqid) ->condition('position', $end, '>') ->execute(); // Invoke the hook to let other modules know that the nodes were removed. foreach ($result as $node) { module_invoke_all('nodequeue_remove', $sqid, $node->nid); } } /** * Empty a subqueue. * * @param $sqid * The sqid to empty. */ function nodequeue_queue_clear($sqid) { db_delete('nodequeue_nodes') ->condition('sqid', $sqid) ->execute(); } /** * Guarantee 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. * * @param $queue * The queue object. * @param $reference * The subqueue to check. * */ function nodequeue_check_subqueue_size($queue, &$subqueue, $size = NULL) { if (!isset($size)) { $size = $queue->size; } if ($queue->size && $subqueue->count > $size) { nodequeue_subqueue_remove($subqueue->sqid, 1, $subqueue->count - $size); $subqueue->count = $size; } } /** * Guarantee that all subqueues are within the size constraints set * by $queue->size. */ function nodequeue_check_subqueue_sizes($queue) { // Don't check if size is 0, as that means infinite size. if (!$queue->size) { return; } $subqueues = nodequeue_load_subqueues_by_queue($queue->qid); foreach ($subqueues as $subqueue) { nodequeue_check_subqueue_size($queue, $subqueue); } } /** * Swap two positions within a subqueue. */ function nodequeue_queue_swap($subqueue, $pos1, $pos2) { // Grab the nid off one of the positions so we can more easily swap. $nid = db_query("SELECT nid FROM {nodequeue_nodes} WHERE sqid = :sqid AND position = :position", array(':sqid' => $subqueue->sqid, ':position' => $pos1)) ->fetchField(); if (!$nid) { return; } db_update('nodequeue_nodes') ->fields(array('position' => $pos1)) ->condition('position', $pos2) ->condition('sqid', $subqueue->sqid) ->execute(); db_update('nodequeue_nodes') ->fields(array('position' => $pos2)) ->condition('nid', $nid) ->condition('sqid', $subqueue->sqid) ->execute(); // notify other modules of the swap module_invoke_all('nodequeue_swap', $subqueue->sqid, $nid); } /** * Move a position within a subqueue up by one. */ function nodequeue_queue_up($subqueue, $position) { if ($position < 2 || $position > $subqueue->count) { return; } nodequeue_queue_swap($subqueue, $position - 1, $position); } /** * Move a position within a subqueue down by one. */ function nodequeue_queue_down($subqueue, $position) { if ($position < 1 || $position >= $subqueue->count) { return; } nodequeue_queue_swap($subqueue, $position + 1, $position); } /** * Move an item to the front of the queue. */ function nodequeue_queue_front($subqueue, $position) { if ($position < 2 || $position > $subqueue->count) { return; } $result = db_query("SELECT * FROM {nodequeue_nodes} WHERE sqid= :sqid AND position = :position", array( ':sqid' => $subqueue->sqid, ':position' => $position, )); $entry = $result->fetchObject(); db_delete('nodequeue_nodes') ->condition('sqid', $subqueue->sqid) ->condition('position', $position) ->execute(); db_update('nodequeue_nodes') ->expression('position', 'position + 1') ->condition('sqid', $subqueue->sqid) ->condition('position', $position, '<') ->execute(); db_insert('nodequeue_nodes') ->fields(array( 'qid' => $entry->qid, 'sqid' => $subqueue->sqid, 'nid' => $entry->nid, 'position' => 1, 'timestamp' => $entry->timestamp, )) ->execute(); } /** * Move an item to the back of the queue. */ function nodequeue_queue_back($subqueue, $position) { if ($position < 1 || $position >= $subqueue->count) { return; } $result = db_query("SELECT * FROM {nodequeue_nodes} WHERE sqid= :sqid AND position = :position", array( ':sqid' => $subqueue->sqid, ':position' => $position, )); $entry = $result->fetchObject(); db_delete('nodequeue_nodes') ->condition('sqid', $subqueue->sqid) ->condition('position', $position) ->execute(); db_update('nodequeue_nodes') ->expression('position', 'position - 1') ->condition('sqid', $subqueue->sqid) ->condition('position', $position, '<') ->execute(); db_insert('nodequeue_nodes') ->fields(array( 'qid' => $entry->qid, 'sqid' => $subqueue->sqid, 'nid' => $entry->nid, 'position' => $subqueue->count, 'timestamp' => $entry->timestamp, )) ->execute(); } /** * Get the position of a node in a subqueue, or 0 if not found. */ function nodequeue_get_subqueue_position($sqid, $nid) { // We use MIN to make sure we always get the closes to the front of the // queue in case the queue has nodes in it multiple times. $pos = db_query("SELECT MIN(position) FROM {nodequeue_nodes} WHERE sqid = :sqid AND nid = :nid", array(':sqid' => $sqid, ':nid' => $nid))->fetchField(); return $pos; } /** * Get the position of a node in several subqueues. */ function nodequeue_set_subqueue_positions(&$subqueues, $nid) { if (empty($subqueues)) { return; } $query = db_select('nodequeue_nodes', 'n') ->fields('n', array('sqid')) ->condition('sqid', array_keys($subqueues), 'IN') ->condition('nid', $nid) ->groupBy('sqid'); $query->addExpression('MIN(position)', 'position'); $result = $query->execute(); foreach ($result as $obj) { $subqueues[$obj->sqid]->position = $obj->position; } } /** * Get a list of valid subqueues for a node, along with the position of the node. * * @param $queues * An array of fully loaded queue objects. * @param $node * A fully loaded node object. * */ function nodequeue_get_subqueues_by_node($queues, $node) { // Determine which subqueues are valid for each queue. $references = array(); static $last_nid = 0; foreach ($queues as $queue) { if ($result = nodequeue_api_subqueues($queue, $node)) { $references[$queue->qid] = is_array($result) ? $result : array($result); } } if (empty($references)) { return array(); } // only allow the static cache to be used if the nid is the same as the last $subqueues = nodequeue_load_subqueues_by_reference($references, ($last_nid != $node->nid)); $last_nid = $node->nid; return $subqueues; } /** * Get a textual representation of a nodequeue's queue size. */ function nodequeue_subqueue_size_text($max, $count, $long = TRUE) { if (empty($count)) { $message = theme('nodequeue_subqueue_empty_text'); } elseif ($count == $max) { $message = theme('nodequeue_subqueue_full_text'); } else { if ($long) { $message = theme('nodequeue_subqueue_count_text', array('count' => $count)); } else { $message = $count; } } return $message; } /** * Substitute the subqueue title into some other string. * * This function does NOT check_plain the title! The output MUST be checked * after this is complete. */ function nodequeue_title_substitute($text, $queue, $subqueue) { if (empty($text)) { return $subqueue->title; } $text = str_replace('%subqueue', $subqueue->title, $text); return $text; } /** * Shuffle a queue. * * @param $subqueue * The subqueue to shuffle. May be a sqid or the loaded object. */ function nodequeue_subqueue_shuffle($subqueue) { // Load the queue if (!is_object($subqueue)) { $subqueue = nodequeue_load_subqueue($subqueue); } if (empty($subqueue)) { return; } $count = $subqueue->count; // Swap each item with another randomly picked one. foreach (range(1, $count) as $i) { nodequeue_queue_swap($subqueue, $i, rand(1, $count)); } } /** * @} End of defgroup "nodequeue_api" */ // -------------------------------------------------------------------------- // Hooks to implement the default nodequeue type. /** * Implements hook_nodequeue_info(). */ function nodequeue_nodequeue_info() { return array('nodequeue' => array( 'title' => t('Simple queue'), '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.'), )); } /** * Implements hook_nodequeue_form_submit(). */ function nodequeue_nodequeue_form_submit(&$queue, $form_state) { // This will add a single subqueue to our new queue. if (!isset($queue->qid) && !isset($queue->add_subqueue)) { // A 0 will set the reference to the sqid of the queue. $queue->add_subqueue = array(0 => $queue->title); } //If the qid is set at this point, we're saving an existing queue. if (isset($queue->qid)) { //We don't check to see if the title has been updated since the $queue object already matches $form_state['values']. db_update('nodequeue_subqueue') ->fields(array('title' => $form_state['values']['title'])) ->condition('qid', $queue->qid) ->execute(); } } // -------------------------------------------------------------------------- // External queue fetching /** * In general it's preferable to use Views for this functionality. */ function nodequeue_node_titles($sqid, $title = '', $backward = TRUE, $from = 0, $count = 0, $published_only = TRUE) { $orderby = ($backward ? "DESC" : "ASC"); $query = db_select('node', 'n') ->fields('n', array('nid', 'title')) ->condition('nn.sqid', $sqid) ->orderBy('nn.position', $orderby) ->addTag('node_access'); $query->leftJoin('nodequeue_nodes', 'nn', 'n.nid = nn.nid'); if ($published_only) { $query->condition('n.status', 1); } if ($count) { $result = $query->range($from, $count)->execute(); } else { $result = $query->execute(); } return node_title_list($result, $title); } /** * Returns an array of nodequeue links for a node. */ function nodequeue_node_links($node) { $links = array(); if (variable_get('nodequeue_links', FALSE) && user_access('manipulate queues')) { $queues = nodequeue_load_queues_by_type($node->type, 'links'); $subqueues = nodequeue_get_subqueues_by_node($queues, $node); if (empty($subqueues)) { return; } // resort the subqueues to retain consistent ordering: ksort($subqueues); // Due to caching, we can accidentally get positions leftover // from previous iterations on teaser list pages, so we must // remove any existing positions here. foreach ($subqueues as $id => $subqueue) { unset($subqueues[$id]->position); } if (!module_exists('translation')) { nodequeue_set_subqueue_positions($subqueues, $node->nid); } foreach ($subqueues as $subqueue) { $queue = $queues[$subqueue->qid]; $id = nodequeue_get_content_id($queue, $node); if (module_exists('translation')) { $subqueue = array($subqueue->sqid => $subqueue); nodequeue_set_subqueue_positions($subqueue, $id); $subqueue = array_shift($subqueue); } $query_string = nodequeue_get_query_string($id, TRUE); $class = 'nodequeue-ajax-toggle nodequeue-toggle-q-' . $queue->qid . ' nodequeue-toggle-sq-' . $subqueue->sqid . ' nodequeue-toggle-ref-' . $subqueue->reference; if (!isset($subqueue->position)) { $links[$class] = array( 'title' => nodequeue_title_substitute($queue->link, $queue, $subqueue), 'href' => "nodequeue/$queue->qid/add-node/$subqueue->sqid/$id", 'attributes' => array('class' => array($class . ' toggle-add')), 'query' => $query_string, 'purl' => array('disabled' => TRUE), ); } elseif ($queue->link_remove) { $links[$class] = array( 'title' => nodequeue_title_substitute($queue->link_remove, $queue, $subqueue), 'href' => "nodequeue/$queue->qid/remove-node/$subqueue->sqid/$id", 'attributes' => array('class' => array($class . ' toggle-remove')), 'query' => $query_string, 'purl' => array('disabled' => TRUE), ); } } drupal_add_js(drupal_get_path('module', 'nodequeue') . '/nodequeue.js'); drupal_add_css(drupal_get_path('module', 'nodequeue') . '/nodequeue.css'); } return $links; } /** * Get node_view output from a nodequeue. */ function nodequeue_view_nodes($sqid, $backward = TRUE, $teaser = TRUE, $links = TRUE, $from = 0, $count = 0) { $output = array(); $nodes = nodequeue_load_nodes($sqid, $backward, $from, $count); foreach ($nodes as $node) { $output[] = node_view($node, $teaser, FALSE, $links); } return $output; } /** * Load an array of node objects belonging to a particular nodequeue. */ function nodequeue_load_nodes($sqid, $backward = FALSE, $from = 0, $count = 5, $published_only = TRUE) { $orderby = ($backward ? "DESC" : "ASC"); $query = db_select('node', 'n') ->fields('n', array('nid')) ->condition('nn.sqid', $sqid) ->orderBy('nn.position', $orderby) ->addTag('node_access'); $query->join('nodequeue_nodes', 'nn', 'n.nid = nn.nid'); if ($published_only) { $query->condition('n.status', 1); } if ($count) { $result = $query->range($from, $count)->execute(); } else { $result = $query->execute(); } $nodes = array(); foreach ($result as $nid) { $nodes[] = node_load($nid->nid); } return $nodes; } /** * Load the first node of a queue. */ function nodequeue_load_front($sqid) { return array_shift(nodequeue_load_nodes($sqid, FALSE, 0, 1)); } /** * Load the last node of a queue. */ function nodequeue_load_back($sqid, $teaser = TRUE, $links = TRUE) { return array_shift(nodequeue_load_nodes($sqid, TRUE, 0, 1)); } /** * View a random node from a queue. */ function nodequeue_view_random_node($sqid, $teaser = TRUE, $links = TRUE) { $query = db_select('node', 'n') ->fields('n', array('nid')); $query->join('nodequeue_nodes', 'nn', 'n.nid = nn.nid'); $count = $query->addTag('node_access') ->condition('nn.sqid', $sqid) ->condition('n.status', 1) ->countQuery() ->execute() ->fetchField(); return nodequeue_view_nodes($sqid, FALSE, $teaser, $links, rand(0, $count - 1), 1); } /** * Load a random node object from a queue. */ function nodequeue_load_random_node($sqid) { $query = db_select('node', 'n') ->fields('n', array('nid')); $query->join('nodequeue_nodes', 'nn', 'n.nid = nn.nid'); $count = $query->addTag('node_access') ->condition('nn.sqid', $sqid) ->condition('n.status', 1) ->countQuery() ->execute() ->fetchField(); return array_shift(nodequeue_load_nodes($sqid, TRUE, rand(0, $count - 1), 1)); } /** * Get the position of a node in a subqueue, or FALSE if not found. */ function nodequeue_subqueue_position($sqid, $nid) { return db_query("SELECT position FROM {nodequeue_nodes} WHERE sqid = :sqid AND nid = :nid", array(':sqid' => $sqid, ':nid' => $nid))->fetchField(); } /** * Get the position of a node in a queue; this queue MUST have only one * subqueue or the results of this function will be unpredictable. */ function nodequeue_queue_position($qid, $nid) { $sqid = db_select('nodequeue_subqueue', 'ns') ->fields('ns', array('sqid')) ->condition('qid', $qid) ->range(0, 1) ->execute() ->fetchField(); return nodequeue_subqueue_position($sqid, $nid); } // -------------------------------------------------------------------------- // API for modules implementing subqueues. /** * Send the nodequeue edit form to the owning module for modification. * * @param $queue * The queue being edited. * @param &$form * The form. This may be modified. */ function nodequeue_api_queue_form($queue, &$form) { $function = $queue->owner . "_nodequeue_form"; if (function_exists($function)) { $function($queue, $form); } } /** * Validate the nodequeue edit form. * * @param $queue * The queue being edited. * @param $form_state * The form values that were submitted. * @param &$form * The actual form object. This may be modified. */ function nodequeue_api_queue_form_validate($queue, &$form_state, &$form) { $function = $queue->owner . "_nodequeue_form_validate"; if (function_exists($function)) { $function($queue, $form_state, $form); } } /** * Send the nodequeue edit form to the owning module upon submit. * * @param &$queue * The queue being edited. This may be modified prior to being * saved. * @param $form_state * The form values that were submitted. */ function nodequeue_api_queue_form_submit(&$queue, &$form_state) { $function = $queue->owner . "_nodequeue_form_submit"; if (function_exists($function)) { $function($queue, $form_state); } } /** * Send the nodequeue edit form to the owning module after the queue * has been saved. * * @param &$queue * The queue being edited. This may be modified prior to being * saved. * @param $form_state * The form values that were submitted. */ function nodequeue_api_queue_form_submit_finish($queue, &$form_state) { $function = $queue->owner . "_nodequeue_form_submit_finish"; if (function_exists($function)) { $function($queue, $form_state); } } /** * Fetch a list of subqueues that are valid for this node from * the owning module. * * @param $queue * The queue being edited. * @param $node * The loaded node object being checked. * * @return * An array of subqueues. This will be keyed by $sqid. */ function nodequeue_api_subqueues(&$queue, $node) { $function = $queue->owner . "_nodequeue_subqueues"; // This will return an array of references. if (function_exists($function)) { return $function($queue, $node); } else { return $queue->qid; } } /** * Fetch a list of nodes available to a given subqueue * for autocomplete. * * @param $queue * The queue that owns the subqueue * @param $subqueue * The subqueue * @param $string * The string being matched. * * @return * An keyed array $nid => $title */ function nodequeue_api_autocomplete($queue, $subqueue, $string) { $matches = array(); if (empty($string)) { return $matches; } $query = db_select('node', 'n') ->addTag('node_access') ->fields('n', array('nid', 'tnid', 'title')) ->range(0, variable_get('nodequeue_autocomplete_limit', 10)); if (!empty($queue->types)) { $query->condition('n.type', $queue->types, 'IN'); } $where_args = array(); global $user; if (!user_access('administer nodes', $user)) { $query->condition(db_or()->condition('n.status', 1)->condition('n.uid', $user->uid)); } // Run a match to see if they're specifying by nid. $preg_matches = array(); $match = preg_match('/\[nid: (\d+)\]/', $string, $preg_matches); if (!$match) { $match = preg_match('/^nid: (\d+)/', $string, $preg_matches); } if ($match) { // If it found a nid via specification, reduce our resultset to just that nid. $query->condition('n.nid', $preg_matches[1]); } else { // Build the constant parts of the query. $query->where('LOWER(n.title) LIKE LOWER(:string)', array(':string' => '%' . db_like($string) . '%')); } // Call to the API. $function = $queue->owner . "_nodequeue_autocomplete"; if (function_exists($function)) { return $function($queue, $subqueue, $string, $where, $where_args); } else { $query->addTag('i18n_select'); $result = $query->execute(); foreach ($result as $node) { $id = nodequeue_get_content_id($queue, $node); $matches[$node->nid] = check_plain($node->title) . " [nid: $id]"; } } return $matches; } /** * Collect info about all of the possible nodequeue types from owning * modules. */ function nodequeue_api_info() { return module_invoke_all('nodequeue_info'); } function nodequeue_api_queue_access($queue, $account = NULL) { if (!$account) { global $user; $account = $user; } if ($queue->owner != 'nodequeue') { // Avoids an infinite loop. $function = $queue->owner . '_queue_access'; if (function_exists($function)) { $access = $function($queue, $account); } } if (!isset($access)) { $access = TRUE; } return $access; } /** * Allows the owning module of a subqueue to restrict access to viewing and * manipulating the queue. */ function nodequeue_api_subqueue_access($subqueue, $account = NULL, $queue = NULL) { if (!$account) { global $user; $account = $user; } if (!$queue) { $queue = nodequeue_load($subqueue->qid); } $function = $queue->owner . '_subqueue_access'; if (function_exists($function)) { $access = $function($subqueue, $account, $queue); } if (!isset($access)) { $access = TRUE; } return $access; } /** * Generate a query string to use on nodequeue's private links. * * @param $seed * The seed to use when generating a token. If NULL no token will * be generated. * @param $destination * The destination to use. If FALSE one won't be used; if TRUE * one will be generated from drupal_get_destination(). * @param $query * An array of additional items to add to the query. * * @return * The query string suitable for use in the l() function. */ function nodequeue_get_query_string($seed, $destination = FALSE, $query = array()) { $dest = drupal_get_destination(); foreach ($dest as $key => $value) { $query[$key] = $value; } if (isset($seed)) { $token = explode('=', nodequeue_get_token($seed)); $query[$token[0]] = $token[1]; } return $query; return implode('&', $query); } /** * Get a private token used to protect nodequeue's links from spoofing. */ function nodequeue_get_token($nid) { return 'token=' . drupal_get_token($nid); } /** * Check to see if the token generated from seed matches. */ function nodequeue_check_token($seed) { return drupal_get_token($seed) == $_GET['token']; } /* --- UTILITY -------------------------------------------------------------- */ /** * Helper function - since hook_menu now takes a function instead of a boolean, * this function is used to compute the user's access. * * @return boolean */ function _nodequeue_access_admin_or_manipulate() { return user_access('administer nodequeue') || user_access('manipulate queues'); } /** * Used by menu system to determine access to the node and the queue in question. * * No, this isn't some odd hook_access implementation. * * @param unknown_type $node * @param unknown_type $queue * @return unknown */ function nodequeue_node_and_queue_access($node, $queue, $subqueue = NULL) { return nodequeue_nodequeue_access($node->type) && nodequeue_queue_access($queue, $subqueue); } /** * Return TRUE if $user can queue(s) for this node. * * @param $type * The node type. * @param $location * Optional argument. May be one of: * - 'links': Only check for queues that have node links. * - 'tab': Only check for queues that appear on the node tab. * - 'ui': Only check for queues that appear in the UI. */ function nodequeue_nodequeue_access($type, $location = NULL, $account = NULL) { if (isset($type->type)) { $type = $type->type; } $qids = nodequeue_get_qids($type, $account); if ($location) { nodequeue_filter_qids($qids, $location); } return !empty($qids); } /** * Return TRUE If the specified account has access to manipulate this queue. */ function nodequeue_queue_access($queue, $subqueue = NULL, $account = NULL) { if (!$account) { global $user; $account = $user; } // Automatically true if all queues. if (user_access('manipulate all queues', $account)) { return TRUE; } // Automatically false if they can't manipulate queues at all. if (!user_access('manipulate queues', $account) || empty($queue->roles)) { return FALSE; } if ($subqueue) { return nodequeue_api_subqueue_access($subqueue, $account); } if (!nodequeue_api_queue_access($queue, $account)) { return FALSE; } $roles = array_keys((array) $account->roles) + array(DRUPAL_AUTHENTICATED_RID); return (bool) array_intersect($roles, $queue->roles); } function nodequeue_node_tab_access($node) { if (!variable_get('nodequeue_use_tab', 1) || !user_access('manipulate queues')) { // For performance reasons: If the menu tab is disabled or the user can't // manipulate queues, there is no reason to run the rest of these queries. return FALSE; } $queues = nodequeue_load_queues_by_type($node->type, 'tab'); $subqueues = nodequeue_get_subqueues_by_node($queues, $node); if (empty($subqueues)) { return FALSE; } foreach ($subqueues as $subqueue) { if (nodequeue_api_subqueue_access($subqueue)) { return TRUE; } } return FALSE; } /** * Print the JSON output for our AJAX calls. */ function nodequeue_js_output($label, $href, $count = NULL, $sqid = NULL) { $return = new stdClass(); $return->status = 1; $return->label = check_plain($label); $return->href = $href; if (isset($count)) { $return->count = $count; } if (isset($sqid)) { $return->sqid = $sqid; } drupal_json_output($return); exit; } /** * Return content id based on i18n settings */ function nodequeue_get_content_id($queue, $node) { return ($queue->i18n && !empty($node->tnid)) ? $node->tnid : $node->nid; } /** * Determine if the machine name is in use. */ function nodequeue_machine_name_exists($machine_name) { $queue_exists = db_query_range('SELECT 1 FROM {nodequeue_queue} WHERE name = :name', 0, 1, array(':name' => $machine_name))->fetchField(); return $queue_exists; } /** * Get the list of nodes in the subqueue, taking into account node access restrictions. */ function nodequeue_nids_visible($sqid = -1, $account = NULL) { $node_status_sql = ''; if (!$account) { global $user; $account = $user; } $nids_visible = array(); $query = db_select('node', 'n') ->fields('n', array('nid')) ->addTag('node_access') ->distinct() ->condition('nq.sqid', $sqid) ->orderBy('nq.position', 'ASC'); $query->leftJoin('nodequeue_nodes', 'nq', 'nq.nid = n.nid'); if (!user_access('administer nodes', $account)) { $query->condition(db_or()->condition('n.status', 1)->condition('n.uid', $account->uid)); } // Disable i18n_select for this query. if (arg(0) == 'admin') { $query->addTag('i18n_select'); } $query_restricted = $query->execute(); foreach ($query_restricted as $result_restricted) { $nids_visible[$result_restricted->nid] = $result_restricted->nid; } return $nids_visible; } /* --- THEME ---------------------------------------------------------------- */ /** * Theme the subqueue overview as a sortable list. * * @ingroup themeable */ function theme_nodequeue_arrange_subqueue_form_table($variables) { $form = $variables['form']; $output = ''; // Get css to hide some of the help text if javascript is disabled. drupal_add_css(drupal_get_path('module', 'nodequeue') . '/nodequeue.css'); $table_id = 'nodequeue-dragdrop-' . $form['#subqueue']['sqid']; $table_classes = array( 'nodequeue-dragdrop', 'nodequeue-dragdrop-qid-' . $form['#subqueue']['qid'], 'nodequeue-dragdrop-sqid-' . $form['#subqueue']['sqid'], 'nodequeue-dragdrop-reference-' . $form['#subqueue']['reference'], ); drupal_add_tabledrag($table_id, 'order', 'sibling', 'node-position'); drupal_add_js(drupal_get_path('module', 'nodequeue') . '/nodequeue_dragdrop.js'); $reverse[str_replace('-', '_', $table_id)] = (bool) $form['#queue']['reverse']; drupal_add_js( array( 'nodequeue' => array( 'reverse' => $reverse, ) ), array( 'type' => 'setting', 'scope' => JS_DEFAULT, ) ); // Render form as table rows. $rows = array(); $counter = 1; foreach (element_children($form) as $key) { if (isset($form[$key]['title'])) { $row = array(); $row[] = drupal_render($form[$key]['title']); $row[] = drupal_render($form[$key]['author']); $row[] = drupal_render($form[$key]['date']); $row[] = drupal_render($form[$key]['position']); $row[] = (!empty($form[$key]['edit'])) ? drupal_render($form[$key]['edit']) : ' '; $row[] = drupal_render($form[$key]['remove']); $row[] = array( 'data' => $counter, 'class' => array('position') ); $rows[] = array( 'data' => $row, 'class' => array('draggable'), ); } $counter++; } if (empty($rows)) { $rows[] = array(array('data' => t('No nodes in this queue.'), 'colspan' => 7)); } // Render the main nodequeue table. $header = array(t('Title'), t('Author'), t('Post Date'), t('Position'), array('data' => t('Operations'), 'colspan' => 2), t('Position')); $output .= theme('table', array('header' => $header, 'rows' => $rows, 'attributes' => array('id' => $table_id, 'class' => $table_classes))); return $output; } /** * Return a "queue is empty" message. * * @ingroup themeable */ function theme_nodequeue_subqueue_empty_text() { return t('Queue empty'); } /** * Return a "queue is full" message. * * @ingroup themeable */ function theme_nodequeue_subqueue_full_text() { return t('Queue full'); } /** * Return a count of elements in the queue. * * @ingroup themeable */ function theme_nodequeue_subqueue_count_text($variables) { return t('@count in queue', array('@count' => $variables['count'])); }