relation_add.module in Relation add 7
Relation Add module file.
File
relation_add.moduleView source
<?php
/**
* @file
* Relation Add module file.
*/
/**
* Implements hook_permission().
*/
function relation_add_permission() {
$return = array();
$return['relation add endpoint autocomplete access'] = array(
'title' => t('Endpoint autocomplete access'),
'description' => t('Endpoint autocomplete menu callback access.'),
);
return $return;
}
/**
* Implements hook_menu().
*/
function relation_add_menu() {
$items['relation_add/autocomplete/%'] = array(
'access arguments' => array(
'relation add endpoint autocomplete access',
),
'page callback' => 'relation_add_autocomplete',
'page arguments' => array(
2,
3,
4,
5,
6,
),
'type' => MENU_CALLBACK,
);
return $items;
}
/**
* Autocomplete page for listing entities appropriate for a given relation type.
*
* @param string $type
* The relation type to search for endpoints for.
* @param string $direction
* The direction for which to allow endpoint bundles.
* @param string $field
* Entity type, field and entity bundle data.
* @param string $target_bundles
* The target bundles in which to search for endpoints.
* @param string $string
* The string for which the search through entity labels will be run.
*/
function relation_add_autocomplete($type = '', $direction = 'target', $field = 'none', $target_bundles = 'all', $string = '') {
if (empty($type) || empty($direction) || empty($string)) {
exit;
}
// Removing the :reverse suffix if exists.
$type_array = explode(':', $type);
$type = $type_array[0];
$entity_infos = entity_get_info();
$relation_type = relation_type_load($type);
$entity_bundles = array();
$instance = array();
if ($field !== 'none') {
list($entity_type, $field_name, $bundle) = explode('-', $field);
$instance = field_info_instance($entity_type, $field_name, $bundle);
}
// Use source bundles unless relation type is directional and we're looking
// in the forward direction.
$direction = $relation_type->directional && $direction == 'target' ? 'target_bundles' : 'source_bundles';
if ($direction == 'target_bundles' && $target_bundles !== 'all') {
$target_bundles_ar = explode('-', $target_bundles);
foreach ($target_bundles_ar as $entity_bundle) {
list($entity_type, $bundle) = explode(':', $entity_bundle, 2);
$entity_bundles[$entity_type][] = $bundle;
}
}
else {
foreach ($relation_type->{$direction} as $entity_bundle) {
list($entity_type, $bundle) = explode(':', $entity_bundle, 2);
$entity_bundles[$entity_type][] = $bundle;
}
}
if (module_exists('relation_add_views') && isset($instance['widget']['settings']['views']) && !empty($instance['widget']['settings']['views'])) {
$suggestions = relation_add_views_autocomplet($field, $instance, $type, $direction, $target_bundles, $string);
}
else {
// Get about 12, rounded up.
$limit = ceil(12 / count(array_keys($entity_bundles)));
$suggestions = array();
foreach ($entity_bundles as $entity_type => $bundles) {
// Get the name of the column in the base table for the entity type.
if ($entity_type == 'user') {
// Special case for users.
$label_key = 'name';
}
elseif (isset($entity_infos[$entity_type]['entity keys']['label'])) {
$label_key = $entity_infos[$entity_type]['entity keys']['label'];
}
else {
if ('redhen_' == substr($entity_type, 0, 7)) {
// Special case for users.
$label_key = 'label';
}
elseif ('field_collection_item' == $entity_type) {
$label_key = $entity_infos[$entity_type]['entity keys']['id'];
}
else {
// Can't find a label to search over, give up.
continue;
}
}
$query = new EntityFieldQuery();
$query
->entityCondition('entity_type', $entity_type);
if (!empty($instance) && isset($instance['widget']['settings']['relation_endpoint_search_by_id']) && $instance['widget']['settings']['relation_endpoint_search_by_id'] && preg_match("/^[0-9]+\$/", $string)) {
// We are most likely searching for an entity ID.
$query
->entityCondition('entity_id', (int) $string);
}
else {
if ('redhen_contact' == $entity_type) {
$query
->addTag('redhen_contact_label');
}
$query
->propertyCondition($label_key, $string, 'CONTAINS');
}
$query
->range(0, $limit);
if (!in_array('*', $bundles) && 'taxonomy_term' != $entity_type && 'user' != $entity_type) {
$query
->entityCondition('bundle', $bundles, 'IN');
}
elseif (!in_array('*', $bundles) && $entity_type == 'taxonomy_term') {
$vocabularies = taxonomy_vocabulary_load_multiple(NULL, array(
'machine_name' => $bundles,
));
$bundles = array_keys($vocabularies);
$query
->propertyCondition('vid', $bundles, 'IN');
}
$query
->addTag('efq_relation_add_autocomplete');
if ($results = $query
->execute()) {
foreach (array_keys($results[$entity_type]) as $id) {
$entities = entity_load($entity_type, array(
$id,
));
$entity = reset($entities);
$label = entity_label($entity_type, $entity);
$bundle = '';
if (!empty($instance) && $instance['widget']['settings']['relation_endpoint_bundle_display']) {
list(, , $bundle) = entity_extract_ids($entity_type, $entity);
if (!empty($bundle)) {
$bundle = ' - (' . $bundle . ')';
}
}
if (!empty($instance) && $instance['widget']['settings']['relation_endpoint_iso_language_codes'] && isset($entity->language)) {
$suggestions[$label . $bundle . ' [' . $entity_type . ':' . $id . ']'] = '[' . $entity->language . '] ' . $label . $bundle . ' [' . $entity_type . ':' . $id . ']';
}
else {
$suggestions[$label . $bundle . ' [' . $entity_type . ':' . $id . ']'] = $label . $bundle . ' [' . $entity_type . ':' . $id . ']';
}
}
}
}
}
drupal_json_output($suggestions);
}
/**
* Implements hook_field_info().
*/
function relation_add_field_info() {
return array(
'relation_add' => array(
'label' => t('Relation add'),
'description' => t('Stores relationships between entities.'),
'settings' => array(),
'default_widget' => 'relation_add',
'default_formatter' => 'relation_add_endpoints_and_fields',
'instance_settings' => array(
'relation_type' => '',
),
),
);
}
/**
* Implements hook_field_is_empty().
*/
function relation_add_field_is_empty($item, $field) {
if (empty($item['relation_options'])) {
return TRUE;
}
if (!isset($item['relation_options']['rid']) && !isset($item['rid'])) {
if (isset($item['relation_options']['targets'])) {
$targets_flip = array_flip($item['relation_options']['targets']);
if (count($targets_flip) < 2) {
$target_key = array_shift($targets_flip);
if (empty($item['relation_options']['targets'][$target_key])) {
return TRUE;
}
}
}
else {
return relation_add_item_is_empty($item['relation_options']);
}
}
return FALSE;
}
/**
* Determines whether an item is empty.
*/
function relation_add_item_is_empty($fields) {
$is_empty = TRUE;
foreach ($fields as $field_name => $items) {
$field = field_info_field($field_name);
// Determine the list of languages to iterate on.
$languages = field_available_languages('relation', $field);
foreach ($languages as $langcode) {
if (!empty($items[$langcode])) {
// If at least one relation-field is not empty; the
// relation item is not empty.
foreach ($items[$langcode] as $field_item) {
if (!module_invoke($field['module'], 'field_is_empty', $field_item, $field)) {
$is_empty = FALSE;
}
}
}
}
}
return $is_empty;
}
/**
* Implements hook_field_load().
*/
function relation_add_field_load($entity_type, $entities, $field, $instances, $langcode, &$items, $age) {
$cache =& drupal_static(__FUNCTION__);
$types = relation_get_types();
foreach ($entities as $id => $entity) {
$cache[$entity_type][$instances[$id]['bundle']]['type_settings'] = array();
$cache[$entity_type][$instances[$id]['bundle']]['relation_types'] = array();
if (isset($cache[$entity_type][$instances[$id]['bundle']]) && !empty($cache[$entity_type][$instances[$id]['bundle']]['type_settings'])) {
$type_settings = $cache[$entity_type][$instances[$id]['bundle']]['type_settings'];
}
else {
if (!empty($instances[$id]['settings']['relation_type'])) {
$type_settings = $instances[$id]['settings']['relation_type'];
$cache[$entity_type][$instances[$id]['bundle']]['type_settings'] = $type_settings;
}
else {
$type_settings = array();
$source_types = relation_get_available_types($entity_type, $instances[$id]['bundle']);
foreach ($source_types as $r_type_id => $r_type) {
$type_settings[] = $r_type_id;
}
$reverse_types = relation_get_available_types($entity_type, $instances[$id]['bundle'], 'target');
foreach ($reverse_types as $r_type_id => $r_type) {
$type_settings[] = $r_type_id . ':reverse';
}
$cache[$entity_type][$instances[$id]['bundle']]['type_settings'] = $type_settings;
}
}
$target_bundles = array();
if (isset($instances[$id]['settings']['relation_target_bundles']) && !empty($instances[$id]['settings']['relation_target_bundles'])) {
foreach ($instances[$id]['settings']['relation_target_bundles'] as $target_bundle) {
$target_bundle_array = explode(':', $target_bundle);
$target_bundles[$target_bundle_array[0]][$target_bundle_array[1]] = $target_bundle_array[1];
}
}
else {
$target_bundles = 'all';
}
if (!empty($type_settings)) {
if (isset($cache[$entity_type][$instances[$id]['bundle']]) && !empty($cache[$entity_type][$instances[$id]['bundle']]['relation_types'])) {
$relation_types = $cache[$entity_type][$instances[$id]['bundle']]['relation_types'];
}
else {
foreach ($type_settings as $type) {
$type_array = explode(':', $type);
$relation_types[$type_array[0]] = $type_array[0];
}
}
$query = relation_query($entity_type, $id);
if ($relation_types) {
$query
->entityCondition('bundle', $relation_types, 'IN');
}
$relation_ids = array_keys($query
->execute());
// Who knows why but field does not like
// if the delta does not start at 0...
$items[$id] = array();
foreach (entity_load('relation', $relation_ids) as $relation) {
if (!(is_string($target_bundles) && 'all' == $target_bundles)) {
if ($relation) {
// Filter out relation items that contain
// unwanted endpoint bundle types.
foreach ($relation->endpoints[LANGUAGE_NONE] as $endpoint) {
if (!($endpoint['entity_type'] == $entity_type && $endpoint['entity_id'] == $id)) {
if (isset($target_bundles[$endpoint['entity_type']]) && is_array($target_bundles[$endpoint['entity_type']])) {
$query = new EntityFieldQuery();
$query
->entityCondition('entity_type', $endpoint['entity_type'])
->entityCondition('bundle', $target_bundles[$endpoint['entity_type']], 'IN')
->entityCondition('entity_id', $endpoint['entity_id']);
$result = $query
->execute();
if (empty($result)) {
unset($relation);
}
}
else {
unset($relation);
}
}
}
}
}
// Only add items when they are either not directional,
// or have their relation type in the field settings.
if (isset($relation)) {
$directional = $types[$relation->relation_type]->directional;
$relation_reverse = TRUE;
if ($relation->endpoints[LANGUAGE_NONE][0]['entity_id'] == $id && $relation->endpoints[LANGUAGE_NONE][0]['entity_type'] == $entity_type) {
$relation_reverse = FALSE;
}
if (!$directional || $relation_reverse && in_array($relation->relation_type . ':reverse', $type_settings) || !$relation_reverse && in_array($relation->relation_type, $type_settings)) {
$item = (array) $relation;
$item['my_entity_id'] = $id;
$items[$id][] = $item;
}
}
}
}
}
}
/**
* Implements hook_field_instance_settings_form().
*/
function relation_add_field_instance_settings_form($field, $instance) {
// 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(
'_relation_add_field_instance_settings_form',
),
'#element_validate' => array(
'_relation_add_field_instance_settings_validate',
),
'#field' => $field,
'#instance' => $instance,
);
return $form;
}
/**
* Process callback for relation_add_field_instance_settings_form().
*/
function _relation_add_field_instance_settings_form($form, $form_state) {
$field = isset($form_state['relation_add']['field']) ? $form_state['relation_add']['field'] : $form['#field'];
$instance = isset($form_state['relation_add']['instance']) ? $form_state['relation_add']['instance'] : $form['#instance'];
$relation_types = relation_get_types();
$bundle_key = $instance['entity_type'] . ':' . $instance['bundle'];
$bundle_wildcard_key = $instance['entity_type'] . ':*';
$options = array();
foreach ($relation_types as $relation_type => $relation_type_data) {
foreach ($relation_type_data->source_bundles as $relation_bundle_key) {
if ($bundle_key == $relation_bundle_key || $bundle_wildcard_key == $relation_bundle_key) {
$options[$relation_type] = $relation_type_data->label;
}
}
foreach ($relation_type_data->target_bundles as $relation_bundle_key) {
if ($bundle_key == $relation_bundle_key || $bundle_wildcard_key == $relation_bundle_key) {
$options[$relation_type . ':reverse'] = t('@relation_label (reverse)', array(
'@relation_label' => $relation_type_data->label,
));
}
}
}
ksort($options);
$form['relation_type'] = array(
'#type' => 'select',
'#title' => t('Relation types'),
'#description' => t('Select all the relation types you want to display in the relation add field. Only relation types applicable to this entity bundle are shown here. If no relation_types are selected, relations of all types will be displayed.'),
'#default_value' => $instance['settings']['relation_type'],
'#options' => $options,
'#multiple' => TRUE,
'#ajax' => array(
'event' => 'change',
'callback' => 'relation_add_settings_ajax',
'wrapper' => 'relation_target_bundles',
),
);
$relation_selected_types = !empty($instance['settings']['relation_type']) ? $instance['settings']['relation_type'] : array();
$relation_relevant_bundles = array();
foreach ($relation_selected_types as $rel_type) {
$type_array = explode(':', $rel_type);
$relation_type = relation_type_load($type_array[0]);
if (isset($type_array[1]) && $type_array[1] == 'reverse') {
foreach ($relation_type->source_bundles as $source_bundle) {
$relation_relevant_bundles[$source_bundle] = $source_bundle;
}
}
else {
foreach ($relation_type->target_bundles as $target_bundles) {
$relation_relevant_bundles[$target_bundles] = $target_bundles;
}
}
}
$entity_infos = entity_get_info();
$relation_bundles = array();
$counter = 0;
foreach ($relation_relevant_bundles as $relation_bundle) {
list($entity_type, $bundle) = explode(':', $relation_bundle);
if ('*' == $bundle) {
foreach ($entity_infos[$entity_type]['bundles'] as $bundle_name => $bundle) {
$relation_bundles[$entity_infos[$entity_type]['label']][$entity_type . ':' . $bundle_name] = $bundle['label'];
++$counter;
}
}
else {
if (isset($entity_infos[$entity_type]['bundles'][$bundle])) {
$relation_bundles[$entity_infos[$entity_type]['label']][$entity_type . ':' . $bundle] = $entity_infos[$entity_type]['bundles'][$bundle]['label'];
++$counter;
}
}
}
$form['relation_target_bundles'] = array(
'#type' => 'select',
'#title' => t('Target bundles'),
'#options' => $relation_bundles,
'#size' => max(5, $counter),
'#default_value' => isset($instance['settings']['relation_target_bundles']) ? $instance['settings']['relation_target_bundles'] : '',
'#multiple' => TRUE,
'#description' => t("Select the target bundle's type. Useful if you want to create separate fields for different possible target bundles."),
'#prefix' => '<div id="relation-add-target-bundles">',
'#suffix' => '</div>',
);
if (count($instance['settings']['relation_type']) > 1) {
// The default value can only be set
// if there are more than 1 relation types.
$form['fieldset'] = array(
'#type' => 'fieldset',
'#title' => t('Default value'),
'#collapsible' => FALSE,
);
$form['fieldset']['content'] = array(
'#pre' => '<p>',
'#markup' => t('The default value for this field, used when creating new content.'),
'#suffix' => '</p>',
);
foreach ($instance['settings']['relation_type'] as $rel_type) {
$type_array = explode(':', $rel_type);
$relation_type = relation_type_load($type_array[0]);
$types[$rel_type] = isset($type_array[1]) ? $relation_type->reverse_label : $relation_type->label;
}
$form['fieldset']['default_value'] = array(
'#type' => 'select',
'#title' => t('Relation type'),
'#options' => $types,
'#default_value' => isset($instance['settings']['fieldset']['default_value']) ? $instance['settings']['fieldset']['default_value'] : '',
'#empty_value' => '',
'#empty_option' => t('Select a relation type'),
);
}
return $form;
}
/**
* Validate for relation_add_field_instance_settings_form().
*/
function _relation_add_field_instance_settings_validate($form, &$form_state) {
// Store the new values in the form state.
$instance = $form['#instance'];
if (isset($form_state['values']['instance'])) {
$instance = $form_state['values']['instance'];
}
$form_state['relation_add']['instance'] = $instance;
}
/**
* Ajax callback for the bundle type settings form.
*
* @see relation_add_field_instance_settings_form()
*/
function relation_add_settings_ajax($form, $form_state) {
return array(
'#type' => 'ajax',
'#commands' => array(
ajax_command_replace("#relation-add-target-bundles", drupal_render($form['instance']['settings']['relation_target_bundles'])),
),
);
}
/**
* Implements hook_field_widget_settings_form().
*/
function relation_add_field_widget_settings_form($field, $instance) {
$widget = $instance['widget'];
$settings = $widget['settings'];
$options = array(
'endpoint' => 'Endpoint field',
'custom' => 'Custom',
'none' => 'None',
);
$form['relation_endpoint_label'] = array(
'#type' => 'select',
'#title' => t('Endpoint label'),
'#default_value' => isset($settings['relation_endpoint_label']) ? $settings['relation_endpoint_label'] : '',
'#options' => $options,
'#required' => TRUE,
'#weight' => 5,
);
$form['relation_endpoint_custom_label'] = array(
'#type' => 'textfield',
'#title' => t('Label'),
'#default_value' => isset($settings['relation_endpoint_custom_label']) ? $settings['relation_endpoint_custom_label'] : '',
'#states' => array(
'visible' => array(
':input[name="instance[widget][settings][relation_endpoint_label]"]' => array(
'value' => 'custom',
),
),
),
'#weight' => 5,
);
$form['relation_endpoint_label_delta'] = array(
'#type' => 'checkbox',
'#title' => t('Adding endpoint delta to the label'),
'#default_value' => isset($settings['relation_endpoint_label_delta']) ? $settings['relation_endpoint_label_delta'] : FALSE,
'#weight' => 5,
);
$form['relation_endpoint_search_by_id'] = array(
'#type' => 'checkbox',
'#title' => t('Search endpoints by entity id if the input contains only numbers'),
'#default_value' => isset($settings['relation_endpoint_search_by_id']) ? $settings['relation_endpoint_search_by_id'] : FALSE,
'#weight' => 6,
);
$form['relation_endpoint_bundle_display'] = array(
'#type' => 'checkbox',
'#title' => t('Display bundle'),
'#description' => t('Display bundle data in the ajax label.'),
'#default_value' => isset($settings['relation_endpoint_bundle_display']) ? $settings['relation_endpoint_bundle_display'] : FALSE,
'#weight' => 7,
);
$form['relation_endpoint_iso_language_codes'] = array(
'#type' => 'checkbox',
'#title' => t('Show ISO Language Codes for endpoints'),
'#default_value' => isset($settings['relation_endpoint_iso_language_codes']) ? $settings['relation_endpoint_iso_language_codes'] : FALSE,
'#description' => t('Add ISO Language Codes for the endpoints'),
'#weight' => 8,
);
if (module_exists('relation_add_views')) {
relation_add_views_extra_field_widget_settings_form($field, $instance, $form);
}
return $form;
}
/**
* Implements hook_field_widget_info().
*/
function relation_add_field_widget_info() {
return array(
'relation_add' => array(
'label' => t('Relation add widget'),
'field types' => array(
'relation_add',
),
'behaviors' => array(
// To tell field API to not display the base default value widget
// we provide our own.
'default value' => FIELD_BEHAVIOR_NONE,
),
),
);
}
/**
* Implements hook_field_widget_form().
*/
function relation_add_field_widget_form(&$form, &$form_state, $field, $instance, $langcode, $items, $delta, $element) {
$item = isset($items[$delta]) ? $items[$delta] : array();
$types = array();
if (empty($instance['settings']['relation_type'])) {
$relation_types = relation_get_available_types($instance['entity_type'], $instance['bundle']);
$reverse_types = relation_get_available_types($instance['entity_type'], $instance['bundle'], 'target');
if (empty($relation_types) && empty($reverse_types)) {
return $element;
}
// Relation type selector. On change, rest of form is loaded via ajax.
foreach ($relation_types as $relation_type) {
$types[$relation_type->relation_type] = $relation_type->label;
}
foreach ($reverse_types as $relation_type) {
// Directional n-ary relations are f@*#ing stupid.
if ($relation_type->directional && $relation_type->max_arity == 2) {
// Machine name doesn't have colons, so we add a suffix for reverse
// relations, which we explode off later.
$types[$relation_type->relation_type . ':reverse'] = $relation_type->reverse_label ? $relation_type->reverse_label : 'reverse ' . $relation_type->reverse_label;
}
}
}
elseif (count($instance['settings']['relation_type']) > 1) {
foreach ($instance['settings']['relation_type'] as $rel_type) {
$type_array = explode(':', $rel_type);
$relation_type = relation_type_load($type_array[0]);
$types[$rel_type] = !isset($type_array[1]) ? $relation_type->label : $relation_type->reverse_label;
}
}
ksort($types);
$wrapper = str_replace('_', '-', $instance['field_name']) . '-relation-add-options-' . $delta;
$form_element = array(
'#tree' => TRUE,
);
if (!empty($types)) {
if (isset($item['relation_type'])) {
if (isset($types[$item['relation_type'] . ':reverse']) && $item['endpoints'][LANGUAGE_NONE][0]['entity_id'] != $item['my_entity_id']) {
$type = $item['relation_type'] . ':reverse';
$relation_reverse = TRUE;
}
elseif (isset($types[$item['relation_type']])) {
$type = $item['relation_type'];
$relation_reverse = FALSE;
}
}
elseif (isset($instance['settings']['fieldset']['default_value'])) {
$type = $instance['settings']['fieldset']['default_value'];
$type_array = explode(':', $type);
$relation_reverse = isset($type_array[1]) && $type_array[1] == 'reverse';
}
$form_element['relation_type'] = array(
'#type' => 'select',
'#title' => t('Relation type'),
'#options' => $types,
'#default_value' => isset($type) ? $type : NULL,
'#empty_value' => '',
'#empty_option' => t('Select a relation type'),
'#ajax' => array(
'callback' => 'relation_add_widget_ajax',
'wrapper' => $wrapper,
'method' => 'replace',
'effect' => 'fade',
),
);
}
else {
$form_element['relation_type'] = array(
'#type' => 'value',
'#value' => reset($instance['settings']['relation_type']),
);
}
if (isset($form_state['triggering_element']['#ajax'])) {
if (!empty($form_state['values'][$field['field_name']][$langcode][$delta]['relation_type'])) {
$form_state_relation_type = $form_state['values'][$field['field_name']][$langcode][$delta]['relation_type'];
}
elseif (!empty($form_state['input'][$field['field_name']][$langcode][$delta]['relation_type'])) {
$form_state_relation_type = $form_state['input'][$field['field_name']][$langcode][$delta]['relation_type'];
}
if (isset($form_state_relation_type)) {
// Remove ':reverse' suffix if it exists, and set reverse flag.
$type_array = explode(':', $form_state_relation_type);
$type = $type_array[0];
$relation_reverse = isset($type_array[1]) && $type_array[1] == 'reverse';
}
}
if (empty($types)) {
$type_array = explode(':', reset($instance['settings']['relation_type']));
$type = $type_array[0];
$relation_reverse = isset($type_array[1]) && $type_array[1] == 'reverse';
}
$field_parents = $element['#field_parents'];
$field_name = $element['#field_name'];
$language = $element['#language'];
$parents = array_merge($field_parents, array(
$field_name,
$language,
$delta,
));
$parents[] = 'relation_options';
$form_element['relation_options'] = array(
'#parents' => $parents,
'#prefix' => '<div id="' . $wrapper . '">',
'#suffix' => '</div>',
);
$add_required_validation = FALSE;
if (!empty($type)) {
// Get all fields.
$info_rel_instances = field_info_instances('relation', $type);
// Remove endpoint field.
unset($info_rel_instances['endpoins']);
// Find required fields.
if (!empty($info_rel_instances)) {
foreach ($info_rel_instances as $info_rel_instance) {
if (isset($info_rel_instance['required']) && $info_rel_instance['required']) {
$add_required_validation = TRUE;
break;
}
}
}
if (isset($item) && !empty($item)) {
$relation = (object) $item;
// $relation->relation_type can also have the :reverse suffix here so we
// have to remove it.
$type_array = explode(':', $relation->relation_type);
$relation_type = relation_type_load($type_array[0]);
$default_targets = array();
$i = 2;
if (!empty($item['endpoints'][LANGUAGE_NONE])) {
foreach ($item['endpoints'][LANGUAGE_NONE] as $endpoint) {
$entities = entity_load($endpoint['entity_type'], array(
$endpoint['entity_id'],
));
$entity = reset($entities);
$label = entity_label($endpoint['entity_type'], $entity);
$bundle = '';
if (isset($instance['widget']['settings']['relation_endpoint_bundle_display'])) {
list(, , $bundle) = entity_extract_ids($endpoint['entity_type'], $entity);
if (!empty($bundle)) {
$bundle = ' - (' . $bundle . ')';
}
}
$entity_label = $label . $bundle . ' [' . $endpoint['entity_type'] . ':' . $endpoint['entity_id'] . ']';
if ($endpoint['entity_id'] == $item['my_entity_id'] && $endpoint['entity_type'] == $instance['entity_type']) {
$default_targets[1] = $entity_label;
}
else {
$default_targets[$i] = $entity_label;
$i++;
}
}
}
}
else {
// $type can also have the :reverse suffix here so we have to remove it.
$type_array = explode(':', $type);
$relation_type = relation_type_load($type_array[0]);
$relation = (object) relation_create($type_array[0], array());
}
// Create one autocomplete for each endpoint beyond the first.
$direction = $relation_reverse ? '/source' : '/target';
$endpoint_title = '';
switch ($instance['widget']['settings']['relation_endpoint_label']) {
case 'endpoint':
$relation_instance = field_info_instance('relation', 'endpoints', $relation_type->relation_type);
// @codingStandardsIgnoreStart
$endpoint_title = t(check_plain($relation_instance['label']));
// @codingStandardsIgnoreEnd
break;
case 'custom':
// @codingStandardsIgnoreStart
$endpoint_title = t($instance['widget']['settings']['relation_endpoint_custom_label']);
// @codingStandardsIgnoreEnd
break;
}
$target_bundles = 'all';
if (!empty($instance['settings']['relation_target_bundles'])) {
$target_bundles = implode('-', $instance['settings']['relation_target_bundles']);
}
if ($relation_type->max_arity == 0) {
if (isset($form_state['input'][$instance['field_name']][$element['#language']][$delta]['relation_options']['targets'])) {
$max_arity = count($form_state['input'][$instance['field_name']][$element['#language']][$delta]['relation_options']['targets']) + 1;
// Click "Add" button.
if ('targets_add_' . $instance['field_name'] . '_' . $delta == $form_state['clicked_button']['#name']) {
$max_arity += 1;
}
}
elseif (isset($default_targets)) {
$max_arity = count($default_targets) + 1;
}
else {
$max_arity = 2;
}
}
else {
$max_arity = $relation_type->max_arity;
}
$wrapper_targets = str_replace('_', '-', $instance['field_name']) . '-relation-add-targets-' . $delta;
$form_element['relation_options']['targets'] = array(
'#prefix' => '<div id="' . $wrapper_targets . '">',
'#suffix' => '</div>',
);
for ($i = 2; $i <= $max_arity; $i++) {
$endpoint_title .= $instance['widget']['settings']['relation_endpoint_label_delta'] ? ' ' . ($i - 1) : '';
$form_element['relation_options']['targets']['target_' . $i] = array(
'#type' => 'textfield',
'#maxlength' => 320,
'#title' => $endpoint_title,
'#default_value' => isset($default_targets[$i]) ? $default_targets[$i] : '',
'#autocomplete_path' => 'relation_add/autocomplete/' . $type . $direction . '/' . $instance['entity_type'] . '-' . $instance['field_name'] . '-' . $instance['bundle'] . '/' . $target_bundles,
);
}
if ($relation_type->max_arity == 0) {
$form_element['relation_options']['targets_add'] = array(
'#type' => 'button',
'#value' => t('Add'),
'#name' => 'targets_add_' . $instance['field_name'] . '_' . $delta,
'#ajax' => array(
'event' => 'click',
'callback' => 'relation_add_widget_ajax',
'wrapper' => $wrapper_targets,
'method' => 'replace',
'effect' => 'fade',
),
'#submit' => array(
'relation_add_widget_ajax',
),
'#limit_validation_errors' => array(),
);
}
if (isset($item['my_entity_id'])) {
$form_element['relation_options']['rid'] = array(
'#type' => 'value',
'#value' => $item['rid'],
);
}
field_attach_form('relation', $relation, $form_element['relation_options'], $form_state);
$form_element['delete'] = array(
'#type' => 'checkbox',
'#title' => t('Delete'),
);
$form_element['relation_options']['targets']['#weight'] = $form_element['relation_options']['endpoints']['#weight'];
if (isset($form_element['relation_options']['targets_add'])) {
$form_element['relation_options']['targets_add']['#weight'] = $form_element['relation_options']['endpoints']['#weight'] + 1;
}
unset($form_element['relation_options']['endpoints']);
}
if ($field['cardinality'] == 1) {
$form_element['relation_options']['#prefix'] = '<div class="relation-add-wrapper">' . $form_element['relation_options']['#prefix'];
$form_element['delete']['#suffix'] = '</div>';
$form_element['label'] = $element + array(
'#type' => 'item',
);
}
// Add element validate function if relation has required field.
if ($add_required_validation) {
$form_element['#element_validate'] = array(
'relation_add_field_required_validate',
);
}
return $element + $form_element;
}
/**
* AJAX callback for widget form.
*/
function relation_add_widget_ajax($form, $form_state) {
$path = $form_state['triggering_element']['#parents'];
$field_name = array_shift($path);
$language = array_shift($path);
$item = array_shift($path);
$type = array_shift($path);
switch ($type) {
case 'relation_type':
return $form[$field_name][$language][$item]['relation_options'];
case 'relation_options':
if (!empty($path)) {
$button = array_shift($path);
if ('targets_add' == $button) {
return $form[$field_name][$language][$item]['relation_options']['targets'];
}
}
break;
}
}
/**
* It is called when the relation has required fields.
*
* @param array $element
* Form element.
* @param array $form_state
* Form state value.
*/
function relation_add_field_required_validate($element, &$form_state) {
if (is_array($element)) {
$form_path = implode('][', $element['#parents']) . '][relation_options][';
$form_errors = form_get_errors();
// Clear form errors.
$drupal_errors = drupal_get_messages('error');
form_clear_error();
if (isset($drupal_errors['error']) && !empty($drupal_errors['error'])) {
foreach ($drupal_errors['error'] as $key => $error) {
if (in_array($error, $form_errors)) {
// Unset form errors.
unset($drupal_errors['error'][$key]);
}
}
// Rebuild drupal errors.
foreach ($drupal_errors['error'] as $message) {
drupal_set_message($message, 'error');
}
}
if (!empty($form_errors)) {
foreach ($form_errors as $error_path => $error_msg) {
if (strpos($error_path, $form_path) === 0) {
$element_paths = explode('][', $error_path);
$element_paths = array_slice($element_paths, count($element['#parents']));
$empty_endpoints = TRUE;
if ('targets' != $element_paths[1]) {
// Find the field which has an error.
$sub_element =& $element;
foreach ($element_paths as $element_path) {
if (isset($sub_element[$element_path])) {
$sub_element =& $sub_element[$element_path];
}
}
if (isset($sub_element['#required']) && $sub_element['#required']) {
$required_msg = t('!name field is required.', array(
'!name' => $sub_element['#title'],
));
if ($required_msg == $error_msg) {
$field_values = drupal_array_get_nested_value($form_state['values'], $element['#parents']);
foreach ($field_values['relation_options']['targets'] as $target) {
if (!empty($target)) {
$empty_endpoints = FALSE;
break;
}
}
}
if ($empty_endpoints) {
continue;
}
}
}
}
form_set_error($error_path, $error_msg);
}
}
}
}
/**
* Implements hook_field_insert().
*/
function relation_add_field_insert($entity_type, $entity, $field, $instance, $langcode, &$items) {
relation_add_field_update($entity_type, $entity, $field, $instance, $langcode, $items);
}
/**
* Implements hook_field_update().
*/
function relation_add_field_update($entity_type, $entity, $field, $instance, $langcode, &$items) {
foreach ($items as $key => $item) {
if (isset($item['relation_options'])) {
if (isset($item['delete']) && $item['delete']) {
if (isset($item['relation_options']['rid']) && $item['relation_options']['rid']) {
relation_delete($item['relation_options']['rid']);
cache_clear_all('field', 'cache_field', TRUE);
}
continue;
}
$type_array = explode(':', $item['relation_type']);
$type = $type_array[0];
$relation_reverse = isset($type_array[1]) && $type_array[1] == 'reverse';
$entity_label = entity_label($entity_type, $entity);
list($id, $vid, $bundle) = entity_extract_ids($entity_type, $entity);
$relation_add = $entity_label . ' [' . $entity_type . ':' . $id . ']';
$entity_strings = array();
if (isset($item['relation_options']['targets'])) {
$targets =& $item['relation_options']['targets'];
for ($i = 2; $i; $i++) {
if (isset($targets['target_' . $i]) && !empty($targets['target_' . $i])) {
$entity_strings[] = $targets['target_' . $i];
}
else {
// Break loop.
$i = FALSE;
}
}
}
if (!isset($item['relation_options']['targets']) || is_array($entity_strings) && count($entity_strings)) {
// Add the current entity to the endpoints array.
if ($relation_reverse) {
// For reverse relations, add the "current entity"
// to the end of the array, else to the start.
array_push($entity_strings, $relation_add);
}
else {
array_unshift($entity_strings, $relation_add);
}
$entity_keys = array();
$i = 0;
foreach ($entity_strings as $r_index => $entity_string) {
$matches = array();
preg_match('/(.*)\\[([\\w\\d]+):(\\d+)\\]/', $entity_string, $matches);
if ($matches) {
$entity_keys[] = array(
'entity_label' => $matches[1],
'entity_type' => $matches[2],
'entity_id' => $matches[3],
'r_index' => $r_index,
);
if ('redhen_' == substr($entity_keys[$i]['entity_type'], 0, 7)) {
unset($entity_keys[$i]['entity_label']);
}
++$i;
}
}
if (isset($item['relation_options']['rid'])) {
if ($relation = relation_load($item['relation_options']['rid'])) {
if ($relation->relation_type == $type) {
$relation->endpoints[LANGUAGE_NONE] = $entity_keys;
$relation->is_new = FALSE;
}
else {
// Different relation type.
relation_delete($item['relation_options']['rid']);
$relation = relation_create($type, $entity_keys);
}
}
else {
// Failed load the relation.
$relation = relation_create($type, $entity_keys);
}
}
else {
$relation = relation_create($type, $entity_keys);
}
$form = $form_state = array();
$relation_instances = field_info_instances('relation', $relation->relation_type);
foreach ($item['relation_options'] as $relation_field_name => $relation_field) {
if (isset($relation_instances[$relation_field_name])) {
$relation_keys = array_keys($relation_field);
$langcode = array_shift($relation_keys);
$relation_field_items = array_shift($relation_field);
$relation_field = field_info_field($relation_field_name);
foreach ($relation_field_items as $delta => $relation_field_item) {
if (!is_numeric($delta)) {
unset($relation_field_items[$delta]);
}
}
field_default_submit('relation', $relation, $relation_field, $relation_instances[$relation_field_name], $langcode, $relation_field_items, $form, $form_state);
$relation->{$relation_field_name}[$langcode] = $relation_field_items;
}
else {
$relation->{$relation_field_name} = $relation_field;
}
}
relation_save($relation);
$items[$key] = (array) $relation;
}
elseif (isset($item['relation_options']['rid'])) {
relation_delete($item['relation_options']['rid']);
unset($items[$key]);
}
}
else {
if (isset($item['relation_type']) && in_array($item['relation_type'], $instance['settings']['relation_type'])) {
$type_array = explode(':', $item['relation_type']);
$type = $type_array[0];
$relation_reverse = isset($type_array[1]) && $type_array[1] == 'reverse';
if (!isset($item['rid']) || empty($item['rid'])) {
$relation_type = relation_type_load($type);
$new_relation = (array) relation_create($type, array());
$item += $new_relation;
list($id, $vid, $bundle) = entity_extract_ids($entity_type, $entity);
$add_entpoint = TRUE;
foreach ($item['endpoints'][LANGUAGE_NONE] as $endpoint) {
if ($endpoint['entity_type'] == $entity_type && $endpoint['entity_id'] == $id) {
$add_entpoint = FALSE;
break;
}
}
if ($add_entpoint) {
$new_endpoint = array(
'entity_type' => $entity_type,
'entity_id' => $id,
);
if ($relation_type->directional && !$relation_reverse) {
array_unshift($item['endpoints'][LANGUAGE_NONE], $new_endpoint);
}
else {
$item['endpoints'][LANGUAGE_NONE][] = $new_endpoint;
}
}
}
else {
$item['relation_type'] = $type;
if (isset($item['is_new'])) {
$item['is_new'] = FALSE;
}
}
$relation = (object) $item;
relation_save($relation);
if (isset($relation->is_new)) {
unset($relation->is_new);
}
$items[$key] = (array) $relation;
}
}
}
}
/**
* Implements hook_field_formatter_info().
*/
function relation_add_field_formatter_info() {
return array(
'relation_add_endpoints_and_fields' => array(
'label' => t('Endpoints and fields'),
'field types' => array(
'relation_add',
),
),
);
}
/**
* Implements hook_field_formatter_info_alter().
*/
function relation_add_field_formatter_info_alter(&$info) {
$relation_dummy_formaters = array(
'relation_default',
'relation_otherendpoint',
'relation_natural',
);
foreach ($relation_dummy_formaters as $dummy_formater) {
if (isset($info[$dummy_formater])) {
$info[$dummy_formater]['field types'][] = 'relation_add';
}
}
}
/**
* Implements hook_field_formatter_view().
*/
function relation_add_field_formatter_view($entity_type, $entity, $field, $instance, $langcode, $items, $display) {
$element = array();
list($entity_id) = entity_extract_ids($entity_type, $entity);
switch ($display['type']) {
case 'relation_add_endpoints_and_fields':
foreach ($items as $delta => $item) {
$links = array();
$relation = (object) $item;
if (count($relation->endpoints[LANGUAGE_NONE]) > 1) {
foreach (array_filter($relation->endpoints[LANGUAGE_NONE]) as $endpoint) {
$related_entities = entity_load($endpoint['entity_type'], array(
$endpoint['entity_id'],
));
$related_entity = reset($related_entities);
if (!($endpoint['entity_type'] == $entity_type && $endpoint['entity_id'] == $entity_id)) {
$link = entity_uri($endpoint['entity_type'], $related_entity);
$link['href'] = $link['path'];
$link['title'] = entity_label($endpoint['entity_type'], $related_entity);
$links[] = $link;
}
}
$endpoint_title = '';
switch ($instance['widget']['settings']['relation_endpoint_label']) {
case 'endpoint':
$relation_instance = field_info_instance('relation', 'endpoints', $relation->relation_type);
// @codingStandardsIgnoreStart
$endpoint_title = t(check_plain($relation_instance['label']));
// @codingStandardsIgnoreEnd
break;
case 'custom':
// @codingStandardsIgnoreStart
$endpoint_title = t($instance['widget']['settings']['relation_endpoint_custom_label']);
// @codingStandardsIgnoreEnd
break;
}
$endpoint_title .= $instance['widget']['settings']['relation_endpoint_label_delta'] ? ' ' . ($delta + 1) : '';
// @codingStandardsIgnoreStart
$element[$delta]['relation']['heading']['#markup'] = t(check_plain($endpoint_title));
// @codingStandardsIgnoreEnd
$element[$delta]['relation']['links'] = array(
'#theme' => 'links',
'#links' => $links,
);
}
$relation_view = relation_view($relation);
$relation_instances = field_info_instances('relation', $relation->relation_type);
foreach (array_keys($relation_instances) as $relation_field_name) {
if ($relation_field_name !== 'endpoints') {
if (isset($relation_view[$relation_field_name])) {
$element[$delta]['relation']['fields'][] = $relation_view[$relation_field_name];
}
}
}
}
break;
}
return $element;
}
/**
* Implements hook_entity_presave().
*/
function relation_add_entity_presave($entity, $entity_type) {
if ('relation' == $entity_type) {
_relation_add_field_cache_clear($entity);
}
}
/**
* Implements hook_entity_delete().
*/
function relation_add_entity_delete($entity, $entity_type) {
if ('relation' == $entity_type) {
_relation_add_field_cache_clear($entity);
}
}
/**
* Clear the field cache for relation endpoints.
*
* @param object $relation
* The relation to clear endpoints for.
*/
function _relation_add_field_cache_clear($relation) {
foreach ($relation->endpoints[LANGUAGE_NONE] as $endpoint) {
$cid = "field:{$endpoint['entity_type']}:{$endpoint['entity_id']}";
cache_clear_all($cid, 'cache_field');
}
}
/**
* Entity property info getter callback for the relations.
*/
function relation_add_get_relation($entity, array $options, $property_name, $entity_type, $info) {
$field = field_info_field($property_name);
$langcode = field_language($entity_type, $entity, $property_name, isset($options['language']) ? $options['language']->language : NULL);
$values = array();
if (isset($entity->{$property_name}[$langcode])) {
foreach ($entity->{$property_name}[$langcode] as $delta => $data) {
// Wrappers do not support multiple entity references being revisions or
// not yet saved entities. In the case of a single reference we can return
// the entity object though.
if ($field['cardinality'] == 1) {
$values[$delta] = relation_load($data);
}
elseif (isset($data['rid'])) {
$values[$delta] = $data['rid'];
}
}
}
// For an empty single-valued field, we have to return NULL.
return $field['cardinality'] == 1 ? $values ? reset($values) : NULL : $values;
}
/**
* Implements hook_entity_property_info_alter().
*/
function relation_add_entity_property_info_alter(&$info) {
$fields = field_read_fields(array(
'type' => 'relation_add',
));
foreach ($fields as $field_name => $field) {
$field_type = field_info_field_types($field['type']);
$field_type['property_type'] = 'relation';
$field_info = field_info_field($field_name);
foreach ($field_info['bundles'] as $entity_type => $entity_bundles) {
foreach ($entity_bundles as $bundle) {
$instance = field_info_instance($entity_type, $field_name, $bundle);
entity_metadata_field_default_property_callback($info, $entity_type, $field, $instance, $field_type);
$info[$entity_type]['bundles'][$bundle]['properties'][$field_name]['getter callback'] = 'relation_add_get_relation';
}
}
}
}