entityqueue.module in Entityqueue 7
Same filename and directory in other branches
Allows users to collect entities in arbitrarily ordered lists.
File
entityqueue.moduleView source
<?php
/**
* @file
* Allows users to collect entities in arbitrarily ordered lists.
*/
/**
* Implements hook_ctools_plugin_directory().
*/
function entityqueue_ctools_plugin_directory($module, $plugin) {
return 'plugins/' . $module . '/' . $plugin;
}
/**
* Returns the hook to use in order to determine if modules support the
* entityqueue API.
*
* @return string
*/
function entityqueue_ctools_plugin_api_hook_name() {
return 'entityqueue_api';
}
/**
* Implements hook_ctools_plugin_type().
*/
function entityqueue_ctools_plugin_type() {
$plugins['handler'] = array(
'classes' => array(
'class',
),
// The default behavior of handler plugins is to have multiple subqueues per
// queue.
'defaults' => array(
'queue type' => 'multiple',
),
);
return $plugins;
}
/**
* Gets the handler for a given queue.
*
* @param EntityQueue $queue
* An EntityQueue object.
*
* @return EntityQueueHandlerInterface
*/
function entityqueue_get_handler(EntityQueue $queue) {
$object_cache =& drupal_static(__FUNCTION__);
if (!isset($object_cache[$queue->name])) {
ctools_include('plugins');
$class = ctools_plugin_load_class('entityqueue', 'handler', $queue->handler, 'class');
if (class_exists($class)) {
$object_cache[$queue->name] = call_user_func(array(
$class,
'getInstance',
), $queue);
}
else {
$object_cache[$queue->name] = BrokenEntityQueueHandler::getInstance($queue);
}
}
return $object_cache[$queue->name];
}
/**
* Implements hook_permission().
*/
function entityqueue_permission() {
$permissions = array(
'administer entityqueue' => array(
'title' => t('Administer entityqueue'),
'description' => t('Administer entityqueue configuration and create, update and delete all queues.'),
'restrict access' => TRUE,
),
'manipulate entityqueues' => array(
'title' => t('Manipulate queues'),
'description' => t('Access the entityqueues list.'),
),
'manipulate all entityqueues' => array(
'title' => t('Manipulate all queues'),
'description' => t('Access to update all queues.'),
),
);
$queues = entityqueue_queue_load_multiple(array(), TRUE);
$handlers = ctools_get_plugins('entityqueue', 'handler');
foreach ($queues as $name => $queue) {
if ($handlers[$queue->handler]['queue type'] == 'multiple') {
$permissions["create {$name} entityqueue"] = array(
'title' => t('Add %queue subqueues', array(
'%queue' => $queue
->label(),
)),
'description' => t('Access to create new subqueue to the %queue queue.', array(
'%queue' => $queue
->label(),
)),
);
$permissions["delete {$name} entityqueue"] = array(
'title' => t('Delete %queue subqueues', array(
'%queue' => $queue
->label(),
)),
'description' => t('Access to delete subqueues of the %queue queue.', array(
'%queue' => $queue
->label(),
)),
);
}
$permissions["update {$name} entityqueue"] = array(
'title' => t('Manipulate %queue queue', array(
'%queue' => $queue
->label(),
)),
'description' => t('Access to update the %queue queue.', array(
'%queue' => $queue
->label(),
)),
);
}
return $permissions;
}
/**
* Implements hook_menu().
*/
function entityqueue_menu() {
$items = array();
return $items;
}
/**
* Constructs a new EntityQueue object, without saving it to the database.
*
* @param array $values
* An array of queue properties. Defaults to an empty array.
*
* @return EntityQueue|false
* An EntityQueue object, or FALSE if creation fails.
*
* @see EntityQueue
*/
function entityqueue_queue_create($values = array()) {
$values = (array) $values;
// Add default properties if they are not set.
$values += array(
'is_new' => TRUE,
'language' => language_default()->language,
'export_type' => EXPORT_IN_CODE,
);
$queue = new EntityQueue($values);
// Invoke the queue handler in order to allow it to alter the created queue.
entityqueue_get_handler($queue)
->create();
return $queue;
}
/**
* Saves a queue.
*
* @param EntityQueue $queue
* EntityQueue object with queue properties. See entityqueue_queue_create().
* @param bool $rebuild_menu
* Boolean indicating whether the the database tables used by various menu
* functions should be rebuilt. Setting this to FALSE is useful if multiple
* queues are being created programmatically. Defaults to TRUE.
*
* @return int|bool
* If the queue insert or update failed, returns FALSE. If it succeeded,
* returns SAVED_NEW or SAVED_UPDATED, depending on the operation performed.
*/
function entityqueue_queue_save(EntityQueue $queue, $rebuild_menu = TRUE) {
// Make sure all keys are populated.
$queue = entityqueue_queue_create($queue);
if ($queue->export_type & EXPORT_IN_DATABASE) {
// Existing queue.
$write_record_keys = array(
'name',
);
$queue->is_new = FALSE;
}
else {
// New queue.
$write_record_keys = array();
$queue->export_type = EXPORT_IN_DATABASE;
$queue->is_new = TRUE;
}
entityqueue_get_handler($queue)
->preSave();
$transaction = db_transaction();
try {
$return = drupal_write_record('entityqueue_queue', $queue, $write_record_keys);
_entityqueue_queue_ensure_instance($queue);
} catch (Exception $e) {
$transaction
->rollback();
watchdog_exception('Entityqueue', $e);
throw $e;
}
if ($queue->is_new) {
entityqueue_get_handler($queue)
->insert();
}
else {
entityqueue_get_handler($queue)
->update();
}
if ($rebuild_menu) {
menu_rebuild();
}
return $return;
}
/**
* Loads a queue.
*
* @param string $name
* The machine name of the queue (bundle) to be loaded.
*
* @return EntityQueue|false
* A EntityQueue object in the same format as expected by
* entityqueue_queue_save(), or FALSE if the queue doesn't exist.
*/
function entityqueue_queue_load($name) {
$queues = entityqueue_queue_load_multiple(array(
$name,
));
return isset($queues[$name]) ? $queues[$name] : FALSE;
}
/**
* Loads multiple queues.
*
* @param array $names
* An array of machine names of the queues to be loaded. If $names is empty,
* load all queues.
*
* @return EntityQueue[]
* An array of EntityQueue objects, keyed by queue name.
*/
function entityqueue_queue_load_multiple($names = array(), $reset = FALSE) {
ctools_include('export');
$queues = !empty($names) ? ctools_export_load_object('entityqueue_queue', 'names', $names) : ctools_export_crud_load_all('entityqueue_queue', $reset);
// Bail out early if we haven't found any queues.
if (empty($queues)) {
return array();
}
static $recursion = FALSE;
if (!$recursion && !drupal_static('entityqueue_install')) {
$recursion = TRUE;
foreach ($queues as $name => $queue) {
if (!empty($queue->in_code_only)) {
_entityqueue_queue_ensure_instance($queue);
// Invoke a special queue handler method for queues that are stored only
// in code (e.g. a hook_entityqueue_default_queues() implementation).
entityqueue_get_handler($queue)
->loadFromCode();
}
entityqueue_get_handler($queue)
->load();
}
}
$recursion = FALSE;
return $queues;
}
/**
* Loads multiple queues with a specific target type.
*
* @param string $target_type
* An entity type (e.g. 'node', 'comment', 'user').
*
* @return array
* An array of EntityQueue objects, keyed by queue name.
*/
function entityqueue_queue_load_by_target_type($target_type) {
ctools_include('export');
return ctools_export_load_object('entityqueue_queue', 'conditions', array(
'target_type' => $target_type,
));
}
/**
* Deletes a queue.
*
* @param EntityQueue|string $queue
* An EntityQueue object or the machine name of a queue.
*/
function entityqueue_queue_delete($queue) {
// If the argument is not an EntityQueue object, load it now.
if (!is_object($queue)) {
$queue = entityqueue_queue_load($queue);
}
entityqueue_get_handler($queue)
->preDelete();
// Delete this queue's subqueues first.
$query = new EntityFieldQuery();
$query
->entityCondition('entity_type', 'entityqueue_subqueue')
->entityCondition('bundle', $queue->name);
$result = $query
->execute();
if (!empty($result['entityqueue_subqueue'])) {
if ($queue->export_type == EXPORT_IN_CODE + EXPORT_IN_DATABASE) {
db_delete('entityqueue_queue')
->condition('name', $queue->name)
->execute();
return;
}
entity_delete_multiple('entityqueue_subqueue', array_keys($result['entityqueue_subqueue']));
}
// Delete the entity reference field instance that was created for this queue.
$field_name = _entityqueue_get_target_field_name($queue->target_type);
$entityreference_field = field_read_instance('entityqueue_subqueue', $field_name, $queue->name);
field_delete_instance($entityreference_field, FALSE);
field_attach_delete_bundle('entityqueue_subqueue', $queue->name);
// And finally we can delete the queue.
db_delete('entityqueue_queue')
->condition('name', $queue->name)
->execute();
entityqueue_get_handler($queue)
->postDelete();
}
/**
* Implements hook_entity_info().
*/
function entityqueue_entity_info() {
$return = array(
'entityqueue_subqueue' => array(
'label' => t('Subqueue'),
'plural label' => t('Subqueues'),
'entity class' => 'EntitySubqueue',
'controller class' => 'EntitySubqueueEntityController',
'module' => 'entityqueue',
'base table' => 'entityqueue_subqueue',
'load hook' => 'entityqueue_subqueue_load',
'access callback' => 'entityqueue_access',
'fieldable' => TRUE,
'entity keys' => array(
'id' => 'subqueue_id',
'bundle' => 'queue',
'label' => 'label',
),
'bundles' => array(),
'bundle keys' => array(
'bundle' => 'name',
),
'view modes' => array(
'full' => array(
'label' => t('queue'),
'custom settings' => FALSE,
),
),
'metadata controller class' => '',
),
);
foreach (entityqueue_queue_load_multiple() as $name => $queue) {
$return['entityqueue_subqueue']['bundles'][$name] = array(
'label' => $queue->label,
'admin' => array(
'path' => 'admin/structure/entityqueue/list/%entityqueue_queue',
'real path' => 'admin/structure/entityqueue/list/' . $name,
'bundle argument' => 4,
'access callback' => 'entityqueue_queue_access',
'access arguments' => array(
'view',
$name,
),
),
);
}
// Support the Entity cache module.
// if (module_exists('entitycache')) {
// $return['entityqueue']['field cache'] = FALSE;
// $return['entityqueue']['entity cache'] = TRUE;
// }
return $return;
}
/**
* Implements hook_entity_property_info().
*/
function entityqueue_entity_property_info() {
$info = array();
$properties =& $info['entityqueue_subqueue']['properties'];
$properties['subqueue_id'] = array(
'label' => t('Subqueue ID'),
'type' => 'integer',
'description' => t('The unique subqueue ID.'),
'schema field' => 'subqueue_id',
);
// @todo convert to a token type, like the language property
$properties['queue'] = array(
'label' => t('Queue Name'),
'description' => t('The entityqueue machine name.'),
'schema field' => 'queue',
'required' => TRUE,
);
$properties['name'] = array(
'label' => t('Name'),
'description' => t('The subqueue machine name.'),
'schema field' => 'name',
'required' => TRUE,
);
$properties['label'] = array(
'label' => t('Label'),
'description' => t('The subqueue label.'),
'schema field' => 'label',
'required' => TRUE,
);
$properties['language'] = array(
'label' => t('Language'),
'type' => 'token',
'description' => t('The language the subqueue is written in.'),
'setter callback' => 'entity_property_verbatim_set',
'options list' => 'entity_metadata_language_list',
'schema field' => 'language',
'setter permission' => 'administer entityqueue',
);
// @todo figure out how other modules do this
$properties['module'] = array(
'label' => t('Module'),
'description' => t('The machine name of the module that defines the subqueue.'),
'schema field' => 'module',
'required' => TRUE,
);
$properties['uid'] = array(
'label' => t('Author'),
'type' => 'user',
'description' => t('The creator of the subqueue.'),
'setter callback' => 'entity_property_verbatim_set',
'setter permission' => 'administer entityqueue',
'required' => TRUE,
'schema field' => 'uid',
);
return $info;
}
/**
* Access callback for the entity API.
* @see entity_access()
*
* @param $op
* The operation being performed. One of 'view', 'update', 'create' or
* 'delete'.
* @param $entity
* Optionally an entity to check access for. If no entity is given, it will be
* determined whether access is allowed for all entities of the given type.
* @param $account
* The user to check for. Leave it to NULL to check for the global user.
* @param $entity_type
* The entity type of the entity to check for.
*
* @return bool
* TRUE if the user has permission for $op, FALSE otherwise.
*/
function entityqueue_access($op, $entity = NULL, $account = NULL, $entity_type = NULL) {
if (empty($account)) {
global $user;
$account = $user;
}
$administer = user_access('administer entityqueue', $account);
if ($administer || user_access('manipulate all entityqueues', $account)) {
return TRUE;
}
if ($op == 'view' && user_access('manipulate entityqueues', $account)) {
return TRUE;
}
if (!(isset($entity) && is_object($entity))) {
return FALSE;
}
if (!isset($entity->queue)) {
watchdog('entityqueue', 'Missing queue property of entity object in entityqueue_access().', NULL, WATCHDOG_DEBUG);
return FALSE;
}
// For view, if they don't have the 'manipulate entityqueues' permission,
// check all other operations.
if ($op == 'view') {
foreach (array(
'update',
'create',
'delete',
) as $subop) {
if (user_access("{$subop} {$entity->queue} entityqueue", $account)) {
return TRUE;
}
}
}
return user_access("{$op} {$entity->queue} entityqueue", $account);
}
/**
* Menu access callback.
*/
function entityqueue_queue_access($op, $queue) {
if (is_object($queue) && $queue instanceof EntityQueue) {
$queue_name = $queue->name;
}
elseif (is_string($queue)) {
$queue_name = $queue;
}
else {
return;
}
$stub = (object) array(
'queue' => $queue_name,
);
return entity_access($op, 'entityqueue_subqueue', $stub);
}
/**
* Constructs a new EntitySubqueue object, without saving it to the database.
*
* @param array $values
* An array of values to set, keyed by property name.
*
* @return EntitySubqueue
* A new EntitySubqueue object.
*/
function entityqueue_subqueue_create($values = array()) {
return entity_get_controller('entityqueue_subqueue')
->create($values);
}
/**
* Saves a subqueue.
*
* @param EntitySubqueue $subqueue
* The full EntitySubqueue object to save.
*
* @return int
* SAVED_NEW or SAVED_UPDATED depending on the operation performed.
*/
function entityqueue_subqueue_save(EntitySubqueue $subqueue) {
return entity_get_controller('entityqueue_subqueue')
->save($subqueue);
}
/**
* Loads a subqueue by name or by ID.
*
* @param string|int $name
* The subqueue_id or machine name of a EntitySubqueue.
*
* @return EntitySubqueue|false
* An EntitySubqueue object.
*/
function entityqueue_subqueue_load($name) {
$subqueues = entityqueue_subqueue_load_multiple(array(
$name,
));
return $subqueues ? reset($subqueues) : FALSE;
}
/**
* Loads multiple subqueues by ID, name or based on a set of matching conditions.
*
* @param array $names
* An array of queue names or IDs.
* @param array $conditions
* An array of conditions on the {entityqueue_subqueue} table in the form
* 'field' => $value.
* @param bool $reset
* Whether to reset the internal queue loading cache.
*
* @return array
* An array of EntitySubqueue objects, keyed by subuque_id. When no results
* are found, an empty array is returned.
*/
function entityqueue_subqueue_load_multiple($names = FALSE, $conditions = array(), $reset = FALSE) {
if (!empty($names) && !is_numeric(reset($names))) {
$conditions += array(
'name' => $names,
);
$names = FALSE;
}
$queues = entity_load('entityqueue_subqueue', $names, $conditions, $reset);
return !empty($queues) ? $queues : array();
}
/**
* Loads multiple subqueues with a specific target type.
*
* @param string $target_type
* An entity type (e.g. 'node', 'comment', 'user').
*
* @return array
* An array of EntitySubqueue objects, keyed by subqueue_id.
*/
function entityqueue_subqueue_load_by_target_type($target_type) {
$queues = entityqueue_queue_load_by_target_type($target_type);
return entity_load('entityqueue_subqueue', FALSE, array(
'queue' => array_keys($queues),
));
}
/**
* Removes an item from a subqueue.
*
* @param \EntitySubqueue $subqueue
* An entity subqueue.
* @param string|int $reference_id
* An entity ID to remove from the subqueue.
*/
function entityqueue_subqueue_remove_item(EntitySubqueue $subqueue, $reference_id) {
// Get the corresponding queue for this subqueue.
$queue = entityqueue_queue_load($subqueue->queue);
$field_name = _entityqueue_get_target_field_name($queue->target_type);
// Filter the field items and remove the referenced ID.
$field_items = field_get_items('entityqueue_subqueue', $subqueue, $field_name);
$field_items = array_filter($field_items, function ($value) use ($reference_id) {
return $value['target_id'] != $reference_id;
});
// Set the filtered item list on the subqueue.
$langcode = field_language('entityqueue_subqueue', $subqueue, $field_name, NULL);
$subqueue->{$field_name}[$langcode] = $field_items;
$subqueue
->save();
}
/**
* Implements hook_contextual_links_view_alter().
*/
function entityqueue_contextual_links_view_alter(&$element, $items) {
// Do not add contextual link on view preview.
if (module_exists('views_ui') && views_ui_contextual_links_suppress()) {
return;
}
// Add contextual link "Edit entityqueue".
$views_ui_element = array();
if (isset($element['#element']['#views_contextual_links_info']['views_ui'])) {
$views_ui_element = $element['#element']['#views_contextual_links_info']['views_ui'];
}
// In case of block #views_contextual_links_info element is inside of
// 'content' and not '#element' directly.
// @see http://drupal.org/node/1413596#comment-5912688
if (empty($views_ui_element) && isset($element['#element']['content']['#views_contextual_links_info']['views_ui'])) {
$views_ui_element = $element['#element']['content']['#views_contextual_links_info']['views_ui'];
}
if (!empty($views_ui_element['view_display_id']) && isset($views_ui_element['view'])) {
$display_id = $views_ui_element['view_display_id'];
$view = $views_ui_element['view'];
$view
->build($display_id);
// Proceed only if there is entityqueue sort criteria available.
if (!($sort_key = entityqueue_get_entityqueue_sort($view))) {
return;
}
// Get view display relationships.
$relationships = $view->display[$display_id]->handler
->get_option('relationships');
foreach ($relationships as $relationship) {
if ($relationship['field'] == 'entityqueue_relationship') {
$referenced_subqueues = array_keys(array_filter($relationship['queues']));
if (!empty($referenced_subqueues)) {
// Contextual links can handle only one set of links coming from a module,
// so we'll have to settle for the first referenced queue.
$subqueue = entityqueue_subqueue_load(reset($referenced_subqueues));
if ($subqueue) {
$path = 'admin/structure/entityqueue/list/' . $subqueue->queue . '/subqueues/' . $subqueue->subqueue_id . '/edit';
$element['#links']['entityqueue-order'] = array(
'title' => t('Edit subqueue'),
'href' => $path,
'query' => array(
'destination' => current_path(),
),
);
}
}
}
}
}
}
/**
* Get the entityqueue position sort of a view if there is one and return its
* ID. If there are multiple of these sorts the first is returned.
*
* @param $view
* The view object.
*
* @return
* The ID of the sort or FALSE if there isn't one.
*/
function entityqueue_get_entityqueue_sort($view) {
foreach ($view->sort as $id => $sort) {
if ($sort->definition['handler'] == 'entityqueue_handler_sort_position') {
return $id;
}
}
return FALSE;
}
/**
* Implements hook_views_api().
*/
function entityqueue_views_api() {
return array(
'api' => 3,
'path' => drupal_get_path('module', 'entityqueue') . '/includes/views',
);
}
/**
* Implements hook_theme().
*/
function entityqueue_theme() {
return array(
'entityqueue_overview_item' => array(
'variables' => array(
'label' => NULL,
'name' => FALSE,
'status' => FALSE,
),
'file' => 'includes/entityqueue.theme.inc',
),
'entityqueue_status' => array(
'variables' => array(
'status' => NULL,
'html' => TRUE,
),
'file' => 'includes/entityqueue.theme.inc',
),
'entityqueue_dragtable' => array(
'render element' => 'form',
'file' => 'includes/entityqueue.theme.inc',
),
);
}
/**
* Returns all queues or subqueues in a way which can be used on form options.
*
* @param array $objects
* (optional) An array of fully loaded objects to display.
* @param string $object_type
* (optional) A string representing what needs to be loaded, queues or
* subqueues. Defaults to 'subqueue';
*
* @return array
* An array of EntityQueue or EntitySubqueue objects, keyed by name.
*/
function entityqueue_get_options($objects = array(), $object_type = 'subqueue') {
if (empty($objects)) {
switch ($object_type) {
case 'subqueue':
$objects = entityqueue_subqueue_load_multiple();
break;
case 'queue':
default:
$objects = entityqueue_queue_load_multiple();
break;
}
}
$options = array();
foreach ($objects as $object) {
$options[$object->name] = $object->label;
}
return $options;
}
/**
* Helper function for getting the name of a entityreference field.
*
* @param string $entity_type
* A Drupal entity type.
*
* @return string
* The name of the entityreference field for the given entity type.
*/
function _entityqueue_get_target_field_name($entity_type) {
if (drupal_strlen($entity_type) <= 29) {
return 'eq_' . $entity_type;
}
else {
// Field names cannot be longer than 32 characters, so have to use a hashing
// trick in order to get a human-readable field name for long entity types.
return 'eq_' . substr($entity_type, 0, 20) . '_' . substr(md5($entity_type), 0, 8);
}
}
/**
* Makes sure that a entityreference field instance exists for a queue.
*
* @param EntityQueue $queue
* An EntityQueue object.
*/
function _entityqueue_queue_ensure_instance(EntityQueue $queue) {
$all_queue_names = variable_get('entityqueue_queue_names', array());
if (!in_array($queue->name, $all_queue_names)) {
$all_queue_names[] = $queue->name;
variable_set('entityqueue_queue_names', $all_queue_names);
}
$field_name = _entityqueue_get_target_field_name($queue->target_type);
$all_eq_fields = variable_get('entityqueue_field_names', array());
if (!in_array($field_name, $all_eq_fields)) {
$all_eq_fields[] = $field_name;
variable_set('entityqueue_field_names', $all_eq_fields);
}
$handler_settings = array(
'behaviors' => array(
'entityqueue' => array(
'status' => 1,
),
),
);
if (!field_info_instance('entityqueue_subqueue', $field_name, $queue->name)) {
_entityqueue_create_entityreference_field($queue, $field_name, 'entityqueue_subqueue', $queue->name, t('Queue items'), 0, array(), $handler_settings);
}
}
/**
* Creates an instance of a entityreference field on the specified bundle.
*
* @param EntityQueue $queue
* An EntityQueue object.
* @param string $field_name
* The name of the field; if it already exists, a new instance of the existing
* field will be created.
* @param string $entity_type
* The type of entity the field instance will be attached to.
* @param string $bundle
* The bundle name of the entity the field instance will be attached to.
* @param string $label
* The label of the field instance.
* @param int $weight
* The default weight of the field instance widget and display.
* @param array $display
* An array of default display data used for the entity's current view modes.
* @param array $handler_settings
* An array of Entityrefence field handler settings.
*/
function _entityqueue_create_entityreference_field($queue, $field_name, $entity_type, $bundle, $label, $weight = 0, $display = array(), $handler_settings = array()) {
// If a field type we know should exist isn't found, clear the Field cache.
if (!field_info_field_types('entityreference')) {
field_cache_clear();
}
// Look for or add the specified entityreference field to the requested entity
// bundle.
$field = field_read_field($field_name);
if (empty($field)) {
$field = array(
'field_name' => $field_name,
'type' => 'entityreference',
'cardinality' => FIELD_CARDINALITY_UNLIMITED,
'entity_types' => array(
$entity_type,
),
'translatable' => FALSE,
'settings' => array(
'target_type' => $queue->target_type,
'handler' => 'entityqueue',
'handler_settings' => $handler_settings,
),
);
field_create_field($field);
}
$instance = field_read_instance($entity_type, $field_name, $bundle);
if (empty($instance)) {
$instance = array(
'field_name' => $field_name,
'entity_type' => $entity_type,
'bundle' => $bundle,
'label' => $label,
'required' => FALSE,
'settings' => array(),
'widget' => array(
'type' => 'entityqueue_dragtable',
'weight' => $weight,
'settings' => array(),
),
'display' => $display,
);
field_create_instance($instance);
}
}
/**
* Implements hook_field_widget_info().
*/
function entityqueue_field_widget_info() {
return array(
'entityqueue_dragtable' => array(
'label' => t('Draggable table'),
'field types' => array(
'entityreference',
),
'settings' => array(
'match_operator' => 'CONTAINS',
'size' => 60,
'add_position' => 'bottom',
),
'behaviors' => array(
'multiple values' => FIELD_BEHAVIOR_CUSTOM,
),
),
);
}
/**
* Implements hook_field_widget_settings_form().
*/
function entityqueue_field_widget_settings_form($field, $instance) {
$widget = $instance['widget'];
$settings = $widget['settings'] + field_info_widget_settings($widget['type']);
$form = array();
if ($widget['type'] == 'entityqueue_dragtable') {
$target_type = $field['settings']['target_type'];
$info = entity_get_info($target_type);
$target_label = isset($info['plural label']) ? $info['plural label'] : $info['label'];
$form['match_operator'] = array(
'#type' => 'select',
'#title' => t('Autocomplete matching'),
'#default_value' => $settings['match_operator'],
'#options' => array(
'STARTS_WITH' => t('Starts with'),
'CONTAINS' => t('Contains'),
),
'#description' => t('Select the method used to collect autocomplete suggestions. Note that <em>Contains</em> can cause performance issues on sites with thousands of @entities.', array(
'@entities' => $target_label,
)),
);
$form['size'] = array(
'#type' => 'textfield',
'#title' => t('Size of textfield'),
'#default_value' => $settings['size'],
'#element_validate' => array(
'_element_validate_integer_positive',
),
'#required' => TRUE,
);
$form['add_position'] = array(
'#type' => 'radios',
'#title' => t('Position for new items'),
'#options' => array(
'top' => t('Top of the queue'),
'bottom' => t('Bottom of the queue'),
),
'#default_value' => $settings['add_position'],
'#description' => t('Changing this setting will move the autocomplete field above or below the draggable table.'),
);
}
return $form;
}
/**
* Implements hook_field_widget_form().
*/
function entityqueue_field_widget_form(&$form, &$form_state, $field, $instance, $langcode, $items, $delta, $element) {
if ($instance['widget']['type'] == 'entityqueue_dragtable') {
$subform = array();
$entity_type = $element['#entity_type'];
$field_name = $element['#field_name'];
$target_type = $field['settings']['target_type'];
// The referenced entity_type
$entity = isset($element['#entity']) ? $element['#entity'] : FALSE;
// Abort early if we don't have a reference to the parent entity.
if (!$entity) {
return $element;
}
list($entity_id) = entity_extract_ids($entity_type, $entity);
$value_key = key($field['columns']);
$subform['#value_key'] = $value_key;
$subform['#add_position'] = $instance['widget']['settings']['add_position'];
// We don't use drupal_html_id() here because our ajax callback needs
// to be able to know what the table id is. We should never have
// the same form field multiple times on a page anyway.
$table_id = drupal_clean_css_identifier('entityqueue-dragtable-' . $field_name);
$table_classes = array(
'entityqueue-dragtable',
'entityqueue-dragtable-field-' . $field_name,
'entityqueue-dragtable-entity-type-' . $entity_type,
);
drupal_add_tabledrag($table_id, 'order', 'sibling', 'item-weight');
$subform['items'] = array(
'#theme' => 'entityqueue_dragtable',
'#attributes' => array(
'id' => $table_id,
'class' => $table_classes,
),
'#attached' => array(
'js' => array(
drupal_get_path('module', 'entityqueue') . '/js/entityqueue.widget.js' => array(
'type' => 'file',
),
),
),
);
$rows = array();
$values = isset($form_state['values'][$field_name][$langcode]) ? $form_state['values'][$field_name][$langcode] : $items;
if (!empty($values)) {
$entity_ids = array();
foreach ($values as $key => $item) {
$entity_ids[] = $item[$value_key];
}
$entities = entity_load($target_type, $entity_ids);
}
$count = count($values);
// When ajax element is clicked, don't lose the destination.
if (current_path() == 'system/ajax') {
if (isset($form_state['destination'])) {
$destination = $form_state['destination'];
}
}
else {
$destination = drupal_get_destination();
$form_state['destination'] = $destination;
}
$weight = 0;
// Keeps track of existing items index
foreach ($values as $key => $item) {
$target_id = $item[$value_key];
$actions = array();
if (!isset($entities[$target_id])) {
// Skip entities that no longer exist.
continue;
}
$uri = entity_uri($target_type, $entities[$target_id]);
if (entity_access('view', $target_type, $entities[$target_id])) {
$label = entity_label($target_type, $entities[$target_id]);
$label = l($label, $uri['path']);
}
else {
$label = t('- Restricted access -');
}
$actions['remove'] = array(
'#type' => 'button',
'#value' => t('Remove'),
'#name' => $field_name . '_remove_' . $weight,
'#validate' => array(),
'#ajax' => array(
'callback' => 'entityqueue_field_widget_ajax_callback',
'wrapper' => $table_id,
),
);
$entity_actions = array();
if (entity_access('edit', $target_type, $entities[$target_id])) {
$entity_actions[] = array(
'title' => t('edit'),
'href' => drupal_get_normal_path($uri['path']) . '/edit',
'query' => drupal_get_destination(),
);
}
if (entity_access('delete', $target_type, $entities[$target_id])) {
$entity_actions[] = array(
'title' => t('delete'),
'href' => drupal_get_normal_path($uri['path']) . '/delete',
'query' => drupal_get_destination(),
);
}
$subform['items'][$weight] = array(
'label' => array(
'#type' => 'markup',
'#markup' => $label,
),
'entity_actions' => array(
'#type' => 'actions',
'separator' => array(
'#type' => 'markup',
'#markup' => '—',
),
'actions' => array(
'#type' => 'markup',
'#markup' => theme('links__ctools_dropbutton', array(
'title' => t('Actions'),
'links' => $entity_actions,
)),
),
'#access' => count($entity_actions) ? 1 : 0,
),
$value_key => array(
'#type' => 'value',
'#value' => $target_id,
),
'actions' => array(
'#type' => 'container',
$actions,
),
'weight' => array(
'#type' => 'weight',
'#delta' => $count,
'#default_value' => $weight,
'#title' => '',
'#attributes' => array(
'class' => array(
'item-weight',
),
),
),
);
$weight++;
}
// This is stolen from entityreference_field_widget_form() and trimmed down
// for our purposes.
$autocomplete_path = 'entityreference/autocomplete/single/';
$autocomplete_path .= $field_name . '/' . $entity_type . '/' . $instance['bundle'] . '/';
$id = 'NULL';
if ($entity_id) {
$id = $entity_id;
}
$autocomplete_path .= $id;
$subform['add'] = array(
'#type' => 'container',
'#attributes' => array(
'class' => array(
'container-inline',
),
),
'#weight' => $subform['#add_position'] === 'top' ? -1 : 10,
'entity' => array(
'#type' => 'textfield',
'#maxlength' => 1024,
'#default_value' => '',
'#autocomplete_path' => $autocomplete_path,
'#size' => $instance['widget']['settings']['size'],
'#entity_type' => $element['#entity_type'],
'#bundle' => $element['#bundle'],
'#field_name' => $element['#field_name'],
'#element_validate' => array(
'_entityreference_autocomplete_validate',
),
'#attributes' => array(
'class' => array(
$table_id . '-add',
),
),
),
'add' => array(
'#type' => 'submit',
'#value' => t('Add item'),
'#ajax' => array(
'callback' => 'entityqueue_field_widget_ajax_callback',
'wrapper' => $table_id,
),
),
);
if (!empty($instance['description'])) {
$subform['description'] = array(
'#markup' => field_filter_xss($instance['description']),
'#prefix' => '<div class="description">',
'#suffix' => '</div>',
'#weight' => 11,
);
}
$subform['#element_validate'] = array(
'entityqueue_widget_dragtable_element_validate',
);
return $subform;
}
}
/**
* Element validate callback.
* @see entityqueue_field_widget_form()
*/
function entityqueue_widget_dragtable_element_validate($element, &$form_state) {
$items = array();
$value_key = $element['#value_key'];
$field_name = $element['#field_name'];
$lang = $element['#language'];
if (isset($form_state['values'][$field_name][$lang]['items'])) {
$existing_values = $form_state['values'][$field_name][$lang]['items'];
}
else {
$existing_values = array();
}
// Determine which button has been clicked.
$triggering_element = $form_state['triggering_element'];
$triggering_key = end($triggering_element['#array_parents']);
$triggering_field = reset($triggering_element['#array_parents']);
// Remove an item from the queue through the Remove button.
if ($triggering_field === $field_name && $triggering_key === 'remove') {
$remove_key = $triggering_element['#array_parents'][3];
unset($existing_values[$remove_key]);
$form_state['entityqueue_changed'] = TRUE;
}
$values = array();
$weights = array();
foreach ($existing_values as $key => $row) {
$values[] = $row[$value_key];
$weights[] = $row['weight'];
}
array_multisort($weights, SORT_ASC, $existing_values);
foreach ($existing_values as $key => $row) {
$items[] = array(
$value_key => $row[$value_key],
);
}
// Add new items to the queue.
$new_value = $form_state['values'][$field_name][$lang]['add'];
if ($triggering_field === $field_name && $triggering_key === 'add' && !empty($new_value['entity'])) {
if ($element['#add_position'] === 'top') {
array_unshift($items, array(
$value_key => $new_value['entity'],
));
}
else {
$items[] = array(
$value_key => $new_value['entity'],
);
}
$form_state['entityqueue_changed'] = TRUE;
}
form_set_value($element, $items, $form_state);
// Rebuild form if ajax callback button was clicked.
if (current_path() == 'system/ajax' && $triggering_element['#ajax']['callback'] == 'entityqueue_field_widget_ajax_callback') {
$form_state['rebuild'] = TRUE;
}
}
/**
* Ajax form callback.
*/
function entityqueue_field_widget_ajax_callback($form, $form_state) {
$field_name = $form_state['triggering_element']['#parents'][0];
$lang = $form[$field_name]['#language'];
$form_state['rebuild'] = TRUE;
$form[$field_name][$lang]['add']['#value'] = '';
$markup = theme('status_messages') . drupal_render($form[$field_name]);
$commands[] = ajax_command_replace('.' . drupal_clean_css_identifier('field-name-' . $field_name), $markup);
$add_id = drupal_clean_css_identifier('entityqueue-dragtable-' . $field_name . '-add');
$commands[] = ajax_command_invoke('.' . $add_id, 'val', array(
'',
));
$settings = array(
drupal_clean_css_identifier('entityqueue-dragtable-' . $field_name) => TRUE,
);
if (!empty($form_state['entityqueue_changed'])) {
drupal_add_js(array(
'entityqueue_changed' => $settings,
), 'setting');
}
return array(
'#type' => 'ajax',
'#commands' => $commands,
);
}
/**
* Ajax form callback.
*/
function entityqueue_subqueue_ajax_callback($form, $form_state) {
$settings = array(
drupal_clean_css_identifier('entityqueue-dragtable-' . $form_state['field_name']) => TRUE,
);
drupal_add_js(array(
'entityqueue_changed' => $settings,
), 'setting');
$markup = drupal_render($form);
$commands[] = ajax_command_replace('#' . $form_state['form_wrapper_id'], $markup);
return array(
'#type' => 'ajax',
'#commands' => $commands,
);
}
/**
* Workaround for missing breadcrumbs on callback and action paths.
*
* Shamelessly stolen from http://drupal.org/project/xmlsitemap
*
* @todo Remove when http://drupal.org/node/576290 is fixed.
*/
function _entityqueue_set_breadcrumb($path = 'admin/structure/entityqueue/list') {
$breadcrumb = array();
$path = explode('/', $path);
do {
$menu_path = implode('/', $path);
$menu_item = menu_get_item($menu_path);
// Skip default tabs such as the "list" page, which is the same as it's parent.
if ($menu_item['type'] == MENU_DEFAULT_LOCAL_TASK) {
continue;
}
$last_item = isset($menu_item['map']) ? end($menu_item['map']) : NULL;
if (is_object($last_item) && method_exists($last_item, 'label')) {
$menu_item['title'] = $last_item
->label();
}
if (isset($last_item)) {
array_unshift($breadcrumb, l($menu_item['title'], $menu_path));
}
else {
array_unshift($breadcrumb, $menu_item['title']);
}
} while (array_pop($path) && !empty($path));
array_unshift($breadcrumb, l(t('Home'), NULL));
array_pop($breadcrumb);
drupal_set_breadcrumb($breadcrumb);
}
Functions
Name | Description |
---|---|
entityqueue_access | Access callback for the entity API. |
entityqueue_contextual_links_view_alter | Implements hook_contextual_links_view_alter(). |
entityqueue_ctools_plugin_api_hook_name | Returns the hook to use in order to determine if modules support the entityqueue API. |
entityqueue_ctools_plugin_directory | Implements hook_ctools_plugin_directory(). |
entityqueue_ctools_plugin_type | Implements hook_ctools_plugin_type(). |
entityqueue_entity_info | Implements hook_entity_info(). |
entityqueue_entity_property_info | Implements hook_entity_property_info(). |
entityqueue_field_widget_ajax_callback | Ajax form callback. |
entityqueue_field_widget_form | Implements hook_field_widget_form(). |
entityqueue_field_widget_info | Implements hook_field_widget_info(). |
entityqueue_field_widget_settings_form | Implements hook_field_widget_settings_form(). |
entityqueue_get_entityqueue_sort | Get the entityqueue position sort of a view if there is one and return its ID. If there are multiple of these sorts the first is returned. |
entityqueue_get_handler | Gets the handler for a given queue. |
entityqueue_get_options | Returns all queues or subqueues in a way which can be used on form options. |
entityqueue_menu | Implements hook_menu(). |
entityqueue_permission | Implements hook_permission(). |
entityqueue_queue_access | Menu access callback. |
entityqueue_queue_create | Constructs a new EntityQueue object, without saving it to the database. |
entityqueue_queue_delete | Deletes a queue. |
entityqueue_queue_load | Loads a queue. |
entityqueue_queue_load_by_target_type | Loads multiple queues with a specific target type. |
entityqueue_queue_load_multiple | Loads multiple queues. |
entityqueue_queue_save | Saves a queue. |
entityqueue_subqueue_ajax_callback | Ajax form callback. |
entityqueue_subqueue_create | Constructs a new EntitySubqueue object, without saving it to the database. |
entityqueue_subqueue_load | Loads a subqueue by name or by ID. |
entityqueue_subqueue_load_by_target_type | Loads multiple subqueues with a specific target type. |
entityqueue_subqueue_load_multiple | Loads multiple subqueues by ID, name or based on a set of matching conditions. |
entityqueue_subqueue_remove_item | Removes an item from a subqueue. |
entityqueue_subqueue_save | Saves a subqueue. |
entityqueue_theme | Implements hook_theme(). |
entityqueue_views_api | Implements hook_views_api(). |
entityqueue_widget_dragtable_element_validate | Element validate callback. |
_entityqueue_create_entityreference_field | Creates an instance of a entityreference field on the specified bundle. |
_entityqueue_get_target_field_name | Helper function for getting the name of a entityreference field. |
_entityqueue_queue_ensure_instance | Makes sure that a entityreference field instance exists for a queue. |
_entityqueue_set_breadcrumb | Workaround for missing breadcrumbs on callback and action paths. |