entity_reference_multiple.module in Entity Reference Multiple 7
Same filename and directory in other branches
Primarily Drupal hooks.
This is the main module file for Entity Reference Multiple.
File
entity_reference_multiple.moduleView source
<?php
/**
* @file
* Primarily Drupal hooks.
*
* This is the main module file for Entity Reference Multiple.
*/
/**
* Implements hook_theme().
*/
function entity_reference_multiple_theme() {
return array(
'entity_reference_multiple_none' => array(
'variables' => array(
'instance' => NULL,
'option' => NULL,
),
'file' => 'theme/theme.inc',
),
);
}
/**
* Implements hook_flush_caches().
*/
function entity_reference_multiple_flush_caches() {
// Because of the intricacies of the info hooks, we are forced to keep a
// separate list of the base tables of each entities, so that we can use
// it in entityreference_field_schema() without calling entity_get_info().
// See http://drupal.org/node/1416558 for details.
$base_tables = array();
foreach (entity_get_info() as $entity_type => $entity_info) {
if (!empty($entity_info['base table']) && !empty($entity_info['entity keys']['id'])) {
$base_tables[$entity_type] = array(
$entity_info['base table'],
$entity_info['entity keys']['id'],
);
}
}
// We are using a variable because cache is going to be cleared right after
// hook_flush_caches() is finished.
variable_set('entity_reference_multiple:base-tables', $base_tables);
}
/**
* Implements hook_field_info().
*/
function entity_reference_multiple_field_info() {
return array(
'entity_reference_multiple' => array(
'label' => t('Entity Reference Multiple'),
'description' => t('This field reference another entity.'),
'settings' => array(
'target_types' => array(
'node' => 'node',
),
'target_entities_bundles' => array(),
),
'instance_settings' => array(),
'default_widget' => 'entity_reference_multiple_selects',
'default_formatter' => 'entity_reference_multiple_label',
'property_type' => 'entity_reference_multiple',
'property_callbacks' => array(
'entity_reference_multiple_field_property_callback',
),
),
);
}
/**
* Implements hook_field_settings_form().
*/
function entity_reference_multiple_field_settings_form($field, $instance, $has_data) {
// The field settings infrastructure is not AJAX enabled by default,
// because it doesn't pass over the $form_state.
// Build the whole form into a #process in which we actually have access
// to the form state.
$form = array(
'#type' => 'container',
'#process' => array(
'_entity_reference_multiple_field_settings_process',
'_entity_reference_multiple_field_settings_ajax_process',
),
'#element_validate' => array(
'_entity_reference_multiple_field_settings_validate',
),
'#field' => $field,
'#instance' => $instance,
'#has_data' => $has_data,
);
return $form;
}
/**
* Implements hook_field_validate().
*/
function entity_reference_multiple_field_validate($entity_type, $entity, $field, $instance, $langcode, $items, &$errors) {
$ids = array();
foreach ($items as $delta => $item) {
if (!entity_reference_multiple_field_is_empty($item, $field)) {
$ids[$item['target_type']][$item['target_id']] = $delta;
}
}
foreach ($ids as $target_type => $target_ids) {
$valid_ids = _entity_reference_multiple_validate_referencable_entities($field, $target_type, array_keys($target_ids));
if ($invalid_entities = array_diff_key($ids[$target_type], array_flip($valid_ids))) {
foreach ($invalid_entities as $id => $delta) {
$errors[$field['field_name']][$langcode][$delta][] = array(
'error' => 'entity_reference_multiple_invalid_entity',
'message' => t('The referenced entity (@type: @id) is invalid.', array(
'@type' => $target_type,
'@id' => $id,
)),
);
}
}
}
}
/**
* Implements hook_field_is_empty().
*/
function entity_reference_multiple_field_is_empty($item, $field) {
$empty_target_type = !isset($item['target_type']) || !entity_get_info($item['target_type']);
$empty_target_id = !isset($item['target_id']) || !is_numeric($item['target_id']);
return $empty_target_type || $empty_target_id;
}
/**
* Performs validation on form elements.
*
* @param array $form
* Nested array of form elements that comprise the form.
* @param array $form_state
* A keyed array containing the current state of the form.
*/
function _entity_reference_multiple_field_settings_validate($form, &$form_state) {
// Store the new values in the form state.
$field = $form['#field'];
if (isset($form_state['values']['field'])) {
$field['settings'] = $form_state['values']['field']['settings'];
}
$form_state['entity_reference_multiple']['field'] = $field;
}
/**
* Add settings to a field settings form.
*
* @param array $form
* Nested array of form elements that comprise the form.
* @param array $form_state
* A keyed array containing the current state of the form.
*
* @return array
* Nested array of form elements that comprise the form.
*/
function _entity_reference_multiple_field_settings_process($form, $form_state) {
$field = isset($form_state['entity_reference_multiple']['field']) ? $form_state['entity_reference_multiple']['field'] : $form['#field'];
$settings = $field['settings'];
$has_data = $form['#has_data'];
// Select the target entity type.
$entity_type_options = array();
foreach (entity_get_info() as $entity_type => $entity_info) {
$entity_type_options[$entity_type] = $entity_info['label'];
}
$form['target_types'] = array(
'#type' => 'checkboxes',
'#title' => t('Target types'),
'#options' => $entity_type_options,
'#default_value' => $settings['target_types'],
'#required' => TRUE,
'#description' => t('The entity types that can be referenced through this field.'),
'#disabled' => $has_data,
'#size' => 1,
'#ajax' => TRUE,
'#limit_validation_errors' => array(),
);
foreach ($field['settings']['target_types'] as $entity_type) {
if ($entity_type && ($entity_info = entity_get_info($entity_type))) {
$form['target_entities_bundles'][$entity_type] = array(
'#type' => 'fieldset',
'#title' => $entity_info['label'],
'#tree' => TRUE,
'#collapsible' => TRUE,
'#collapsed' => FALSE,
'#access' => FALSE,
);
$form['target_entities_bundles'][$entity_type]['target_bundles'] = array(
'#type' => 'value',
'#value' => array(),
);
if (!empty($entity_info['entity keys']['bundle'])) {
$bundles = array();
foreach ($entity_info['bundles'] as $bundle_name => $bundle_info) {
$bundles[$bundle_name] = $bundle_info['label'];
}
$form['target_entities_bundles'][$entity_type]['#access'] = TRUE;
$form['target_entities_bundles'][$entity_type]['target_bundles'] = array(
'#type' => 'checkboxes',
'#title' => t('Target bundles'),
'#options' => $bundles,
'#default_value' => isset($settings['target_entities_bundles'][$entity_type]['target_bundles']) ? $settings['target_entities_bundles'][$entity_type]['target_bundles'] : array(),
'#size' => 6,
'#multiple' => TRUE,
'#description' => t('The bundles of the entity type that can be referenced. Optional, leave empty for all bundles.'),
'#element_validate' => array(
'_entityreference_element_validate_filter',
),
);
}
}
}
return $form;
}
/**
* Form processing handler for the #ajax form property.
*
* @param array $form
* Nested array of form elements that comprise the form.
* @param array $form_state
* A keyed array containing the current state of the form.
*
* @return array
* Nested array of form elements that comprise the form.
*/
function _entity_reference_multiple_field_settings_ajax_process($form, $form_state) {
_entity_reference_multiple_field_settings_ajax_process_element($form, $form);
return $form;
}
/**
* Form element processing handler for the #ajax form property.
*
* @param array $element
* An associative array containing the properties of the element.
* @param array $main_form
* Nested array of form elements that comprise the form.
*/
function _entity_reference_multiple_field_settings_ajax_process_element(&$element, $main_form) {
if (isset($element['#ajax']) && $element['#ajax'] === TRUE) {
$element['#ajax'] = array(
'callback' => 'entity_reference_multiple_settings_ajax',
'wrapper' => $main_form['#id'],
'element' => $main_form['#array_parents'],
);
}
foreach (element_children($element) as $key) {
_entity_reference_multiple_field_settings_ajax_process_element($element[$key], $main_form);
}
}
/**
* Ajax callback for the handler settings form.
*/
function entity_reference_multiple_settings_ajax($form, $form_state) {
$trigger = $form_state['triggering_element'];
return drupal_array_get_nested_value($form, $trigger['#ajax']['element']);
}
/**
* Submit handler for the non-JS case.
*/
function entity_reference_multiple_settings_ajax_submit($form, &$form_state) {
$form_state['rebuild'] = TRUE;
}
/**
* Validate that entities can be referenced by this field.
*
* @param array $field
* The field definition.
* @param string $target_type
* The type of the entity.
* @param array $target_ids
* The ids of the entities.
*
* @return array
* An array of entity ids that are valid.
*/
function _entity_reference_multiple_validate_referencable_entities($field, $target_type, $target_ids) {
$query = _entity_reference_multiple_build_query($field, $target_type);
$query
->entityCondition('entity_id', $target_ids, 'IN');
$result = $query
->execute();
if (!empty($result[$target_type])) {
return array_keys($result[$target_type]);
}
return array();
}
/**
* Build an EntityFieldQuery to get referencable entities.
*
* @param array $field
* The field definition.
* @param string $target_type
* The type of the entity.
*
* @return EntityFieldQuery
* The built query.
*/
function _entity_reference_multiple_build_query($field, $target_type) {
$query = new EntityFieldQuery();
$query
->entityCondition('entity_type', $target_type);
if (!empty($field['settings']['target_entities_bundles'][$target_type]) && ($bundles = _entity_reference_multiple_bundles_prepare($field['settings']['target_entities_bundles'][$target_type]))) {
$query
->entityCondition('bundle', $bundles, 'IN');
}
// Add a generic entity access tag to the query.
$query
->addTag($target_type . '_access');
$query
->addMetaData('field', $field);
// Add the sort option.
$query
->propertyOrderBy('title');
return $query;
}
/**
* Property callback for the Entity Metadata framework.
*/
function entity_reference_multiple_field_property_callback(&$info, $entity_type, $field, $instance, $field_type) {
$property =& $info[$entity_type]['bundles'][$instance['bundle']]['properties'][$field['field_name']];
$property['getter callback'] = 'entity_metadata_field_verbatim_get';
$property['setter callback'] = 'entity_metadata_field_verbatim_set';
unset($property['query callback']);
$property['property info']['entity'] = array(
'type' => 'entity',
'label' => t('The entity.'),
'getter callback' => 'entity_reference_multiple_metadata_field_property_entity_get',
'translatable' => !empty($field['translatable']),
// Specify that this property stems from a field.
'field' => TRUE,
'required' => !empty($instance['required']),
);
}
/**
* Callback for getting referencable entity.
*/
function entity_reference_multiple_metadata_field_property_entity_get($item) {
if ($entity = entity_load($item['target_type'], array(
$item['target_id'],
))) {
return entity_metadata_wrapper($item['target_type'], reset($entity));
}
return NULL;
}
/**
* Implements hook_field_widget_info().
*/
function entity_reference_multiple_field_widget_info() {
return array(
'entity_reference_multiple_selects' => array(
'label' => t('Entity Reference Multiple Selects'),
'field types' => array(
'entity_reference_multiple',
),
),
);
}
/**
* Implements hook_field_widget_form().
*/
function entity_reference_multiple_field_widget_form(&$form, &$form_state, $field, $instance, $langcode, $items, $delta, $element) {
$element['#attached']['css'][] = drupal_get_path('module', 'entity_reference_multiple') . '/css/entity-reference-multiple.css';
$wrapper_id_array = array(
$field['field_name'],
$langcode,
$delta,
);
$wrapper_id = str_replace('_', '-', implode('-', $wrapper_id_array));
$element[$element['#field_name']] = array(
'#type' => 'container',
'#attributes' => array(
'id' => $wrapper_id,
'class' => array(
'entity-reference-multiple-inline',
),
),
);
// Prepare the list of options.
$column = 'target_type';
$options = _entity_reference_multiple_get_options($field, $instance, $items, $element, $column);
$target_type_value = isset($items[$delta][$column]) ? $items[$delta][$column] : NULL;
$parents = array(
$element['#field_name'],
$langcode,
$delta,
$column,
);
if (!empty($form_state['values'])) {
$target_type_value = drupal_array_get_nested_value($form_state['values'], $parents);
}
$element[$element['#field_name']][$column] = array(
'#type' => 'select',
'#parents' => $parents,
'#title' => t('Entity type'),
'#options' => $options,
'#default_value' => $target_type_value,
'#required' => $element['#required'],
'#value_key' => $column,
'#ajax' => array(
'wrapper' => $wrapper_id,
'callback' => '_entity_reference_multiple_field_widget_ajax_process',
),
);
// Prepare the list of options.
$column = 'target_id';
$options = _entity_reference_multiple_get_options($field, $instance, $items, $element, $column, $target_type_value);
$target_id_value = isset($items[$delta][$column]) ? $items[$delta][$column] : NULL;
$parents = array(
$element['#field_name'],
$langcode,
$delta,
$column,
);
if (!empty($form_state['triggering_element']) && $form_state['triggering_element']['#value_key'] == 'target_type') {
$target_id_value = NULL;
drupal_array_set_nested_value($form_state['input'], $parents, $target_id_value);
}
$element[$field['field_name']][$column] = array(
'#type' => 'select',
'#parents' => $parents,
'#title' => t('Entity'),
'#options' => $options,
'#default_value' => $target_id_value,
'#required' => !empty($target_type_value) && $target_type_value != '_none' || $element['#required'],
'#value_key' => $column,
);
return $element;
}
/**
* Implements hook_field_widget_error().
*/
function entity_reference_multiple_field_widget_error($element, $error, $form, &$form_state) {
form_error($element, $error['message']);
}
/**
* Form processing handler for the #ajax form property.
*
* @param array $form
* Nested array of form elements that comprise the form.
* @param array $form_state
* A keyed array containing the current state of the form.
*
* @return array
* The new form element.
*/
function _entity_reference_multiple_field_widget_ajax_process($form, $form_state) {
$ref =& $form;
$parents = $form_state['triggering_element']['#array_parents'];
array_pop($parents);
foreach ($parents as $parent) {
if (is_array($ref) && array_key_exists($parent, $ref)) {
$ref =& $ref[$parent];
}
}
return $ref;
}
/**
* Collects the options for a field.
*/
function _entity_reference_multiple_get_options($field, $instance, $items, $element, $column, $target_type = NULL) {
$has_value = isset($items[0][$column]);
$properties = _options_properties('select', TRUE, $element['#required'], $has_value);
$options = array();
if ($column == 'target_type') {
// Get the list of options for target_type column.
$options = _entity_reference_multiple_target_type_allowed_values($field);
}
elseif ($column == 'target_id') {
// Get the list of options for target_id column.
$options = _entity_reference_multiple_target_id_allowed_values($field, $target_type);
}
// Sanitize the options.
_options_prepare_options($options, $properties);
if (!$properties['optgroups']) {
$options = options_array_flatten($options);
}
if ($properties['empty_option'] && $column == 'target_type' || $properties['empty_option'] && (empty($target_type) || $target_type == '_none')) {
$label = theme('entity_reference_multiple_none', array(
'instance' => $instance,
'option' => $properties['empty_option'],
));
$options = array(
'_none' => $label,
) + $options;
}
return $options;
}
/**
* Returns the set of valid entity types for a entity_reference_multiple field.
*
* @param array $field
* The field definition.
*
* @return array
* The array of valid entity types for this field, keyed by entity type.
*/
function _entity_reference_multiple_target_type_allowed_values($field) {
$entity_info = entity_get_info();
$options = array();
foreach (array_keys($field['settings']['target_entities_bundles']) as $target_type) {
if (!empty($entity_info[$target_type])) {
$options[$target_type] = $entity_info[$target_type]['label'];
}
}
return $options;
}
/**
* Returns the set of valid entities for a entity_reference_multiple field.
*
* @param array $field
* The field definition.
* @param string $target_type
* The type of the entity.
*
* @return array
* The array of valid entities for this field, keyed by entity id and
* grouped by bundles.
*/
function _entity_reference_multiple_target_id_allowed_values($field, $target_type) {
$entity_info = entity_get_info();
$options = array();
if (in_array($target_type, $field['settings']['target_types'], TRUE)) {
$query = _entity_reference_multiple_build_query($field, $target_type);
$results = $query
->execute();
if (!empty($results[$target_type])) {
$entities = entity_load($target_type, array_keys($results[$target_type]));
foreach ($entities as $entity_id => $entity) {
list(, , $bundle) = entity_extract_ids('node', $entity);
$group = $entity_info[$target_type]['bundles'][$bundle]['label'];
$options[$group][$entity_id] = check_plain(entity_label($target_type, $entity));
}
}
}
return $options;
}
/**
* Get selected bundles.
*
* @param array $bundles
* An array contains all bundles.
*
* @return array
* An array contains selected bundles.
*/
function _entity_reference_multiple_bundles_prepare($bundles) {
foreach ($bundles as $key => $bundle) {
if (!$bundle) {
unset($bundles[$key]);
}
}
return $bundles;
}
/**
* Implements hook_field_formatter_info().
*/
function entity_reference_multiple_field_formatter_info() {
return array(
'entity_reference_multiple_label' => array(
'label' => t('Label'),
'description' => t('Display the label of the referenced entities.'),
'field types' => array(
'entity_reference_multiple',
),
'settings' => array(
'link' => FALSE,
),
),
);
}
/**
* Implements hook_field_formatter_settings_form().
*/
function entity_reference_multiple_field_formatter_settings_form($field, $instance, $view_mode, $form, &$form_state) {
$display = $instance['display'][$view_mode];
$settings = $display['settings'];
$element = array();
if ($display['type'] == 'entity_reference_multiple_label') {
$element['link'] = array(
'#title' => t('Link label to the referenced entity'),
'#type' => 'checkbox',
'#default_value' => $settings['link'],
);
}
return $element;
}
/**
* Implements hook_field_formatter_settings_summary().
*/
function entity_reference_multiple_field_formatter_settings_summary($field, $instance, $view_mode) {
$display = $instance['display'][$view_mode];
$settings = $display['settings'];
$summary = array();
if ($display['type'] == 'entity_reference_multiple_label') {
$summary[] = $settings['link'] ? t('Link to the referenced entity') : t('No link');
}
return implode('<br />', $summary);
}
/**
* Implements hook_field_formatter_prepare_view().
*/
function entity_reference_multiple_field_formatter_prepare_view($entity_type, $entities, $field, $instances, $langcode, &$items, $displays) {
$targets = array();
// Collect every possible entity attached to any of the entities.
foreach ($entities as $id => $entity) {
foreach ($items[$id] as $delta => $item) {
if (isset($item['target_id']) && isset($item['target_type'])) {
$targets[$item['target_type']][] = $item['target_id'];
}
}
}
$target_entities = array();
foreach ($targets as $target_type => $target_ids) {
$target_entities[$target_type] = entity_load($target_type, $target_ids);
}
// Iterate through the fieldable entities again to attach the loaded data.
foreach ($entities as $id => $entity) {
$rekey = FALSE;
foreach ($items[$id] as $delta => $item) {
// Check whether the referenced entity could be loaded.
if (isset($target_entities[$item['target_type']][$item['target_id']])) {
// Replace the instance value with the term data.
$items[$id][$delta]['entity'] = $target_entities[$item['target_type']][$item['target_id']];
// Check whether the user has access to the referenced entity.
$has_view_access = entity_access('view', $item['target_type'], $target_entities[$item['target_type']][$item['target_id']]);
$has_update_access = entity_access('update', $item['target_type'], $target_entities[$item['target_type']][$item['target_id']]);
$items[$id][$delta]['access'] = $has_view_access || $has_update_access;
}
else {
unset($items[$id][$delta]);
$rekey = TRUE;
}
}
if ($rekey) {
// Rekey the items array.
$items[$id] = array_values($items[$id]);
}
}
}
/**
* Implements hook_field_formatter_view().
*/
function entity_reference_multiple_field_formatter_view($entity_type, $entity, $field, $instance, $langcode, $items, $display) {
$result = array();
// Rebuild the items list to contain only those with access.
foreach ($items as $key => $item) {
if (empty($item['access'])) {
unset($items[$key]);
}
}
switch ($display['type']) {
case 'entity_reference_multiple_label':
foreach ($items as $delta => $item) {
$label = entity_label($item['target_type'], $item['entity']);
// If the link is to be displayed and the entity has a uri,
// display a link. Note the assignment ($url = ) here is intended to
// be an assignment.
if ($display['settings']['link'] && ($uri = entity_uri($item['target_type'], $item['entity']))) {
$result[$delta] = array(
'#markup' => l($label, $uri['path'], $uri['options']),
);
}
else {
$result[$delta] = array(
'#markup' => check_plain($label),
);
}
}
break;
}
return $result;
}