View source
<?php
define('REFFIELDTIMEOUT', 10800);
function ref_field_help($path, $arg) {
switch ($path) {
case 'admin/help#ref_field':
return '<p>' . t('Reference field lets you create fields to reference entities. Each field can reference one king of entity, and will optionaly let you limit the bundles that can be referenced.') . '</p>';
}
}
function ref_field_menu() {
$items = array();
$items['ref_field/autocomplete/%/%/%/%'] = array(
'page callback' => '_ref_fieldl_autocomplete',
'page arguments' => array(
2,
3,
4,
5,
),
'access callback' => '_ref_fieldl_autocomplete_access',
'access arguments' => array(
2,
3,
4,
5,
),
'type' => MENU_CALLBACK,
);
return $items;
}
function ref_field_field_info() {
return array(
'ref_field' => array(
'label' => t('Entity reference'),
'description' => t('This field stores a directional reference between two entities.'),
'settings' => array(
'entity' => '',
'bundles' => array(),
),
'default_widget' => 'ref_field_autocomplete',
'default_formatter' => 'ref_field_title',
'property_callbacks' => array(
'ref_field_field_property_callback',
),
),
);
}
function ref_field_field_settings_form($field, $instance, $has_data) {
$form = array();
$options = array(
'' => t('- Select -'),
);
foreach (entity_get_info() as $entity => $data) {
$options[$entity] = $data['label'];
}
if (!$has_data) {
$form['entity'] = array(
'#title' => t('Entity'),
'#description' => t('Select the entity type this field will reference.'),
'#type' => 'select',
'#default_value' => isset($field['settings']['entity']) ? $field['settings']['entity'] : '',
'#required' => TRUE,
'#ajax' => array(
'callback' => '_ref_field_field_settings_ajax',
'wrapper' => '',
),
'#element_validate' => array(
'_ref_field_field_entity_settings_form_validate',
),
'#options' => $options,
);
}
$form['bundles'] = array();
_ref_field_field_settings_bundles($form['bundles'], $field['settings']['entity'], $has_data);
return $form;
}
function ref_field_form_field_ui_field_settings_form_alter(&$form, &$form_state, $form_id) {
$has_data = isset($form['#field']) ? field_has_data($form['#field']) : FALSE;
if ($form['field']['type']['#value'] == 'ref_field' && isset($form_state['values']['field']['settings']['entity'])) {
_ref_field_field_settings_bundles($form['field']['settings']['bundles'], $form_state['values']['field']['settings']['entity'], $has_data);
}
$form['field']['settings']['entity']['#ajax']['wrapper'] = $form['#id'];
}
function ref_field_form_field_ui_field_edit_form_alter(&$form, &$form_state, $form_id) {
if ($form['#field']['type'] == 'ref_field' && isset($form_state['values']['field']['settings']['entity'])) {
_ref_field_field_settings_bundles($form['field']['settings']['bundles'], $form_state['values']['field']['settings']['entity'], field_has_data($form['#field']));
}
$form['field']['settings']['entity']['#ajax']['wrapper'] = $form['#id'];
}
function _ref_field_field_settings_bundles(&$element, $entity, $has_data) {
$options = array();
$entity_info = entity_get_info($entity);
switch ($entity) {
case FALSE || NULL:
case 'comment':
break;
case 'taxonomy_term':
foreach ($entity_info['bundles'] as $bundle => $data) {
$vocabulary = taxonomy_vocabulary_machine_name_load($bundle);
$options[$vocabulary->vid] = $data['label'];
}
break;
default:
foreach ($entity_info['bundles'] as $bundle => $data) {
$options[$bundle] = $data['label'];
}
break;
}
if ($options) {
$warning = t('Changes made here might cause loss of data, use with caution.');
$description = t('Select the bundles you want to reference with this field. If none is selected, every bundle will be referenceable.');
$element = array(
'#title' => t('Bundles'),
'#description' => $has_data ? $warning . ' ' . $description : $description,
'#type' => 'select',
'#multiple' => TRUE,
'#default_value' => isset($field['settings']['bundles']) ? $field['settings']['bundles'] : '',
'#element_validate' => array(
'_ref_field_field_bundles_settings_form_validate',
),
'#options' => $options,
);
}
else {
$element = array();
}
}
function _ref_field_field_settings_ajax(&$form, &$form_state) {
return $form;
}
function _ref_field_field_entity_settings_form_validate($element, $form_state, $form) {
if (!entity_get_info($element['#value'])) {
form_error($element, t('Invalid entity selected'));
}
}
function ref_field_field_instance_settings_form($field, $instance) {
$form = array();
return $form;
}
function _ref_field_field_bundles_settings_form_validate($element, &$form_state, $form) {
if (isset($form_state['values']['field']['settings']['entity'])) {
$entity = $form_state['values']['field']['settings']['entity'];
}
else {
$entity = $form['#field']['settings']['entity'];
}
$entity_info = entity_get_info($entity);
$error = FALSE;
switch ($entity) {
case FALSE || NULL:
break;
case 'comment':
break;
case 'taxonomy_term':
foreach ($element['#value'] as $vid) {
$vocabulary = taxonomy_vocabulary_load($vid);
if (!isset($entity_info['bundles'][$vocabulary->machine_name])) {
$error = TRUE;
}
}
break;
default:
foreach ($element['#value'] as $bundle) {
if (!isset($entity_info['bundles'][$bundle])) {
$error = TRUE;
}
}
break;
}
if ($error) {
form_error($element, t('Invalid bundles selected. If trying to change the entity type and are getting this error, please use the "Field Settings" tab instead.'));
}
}
function ref_field_field_validate($entity_type, $entity, $field, $instance, $langcode, $items, &$errors) {
$entity_info = entity_get_info($entity_type);
foreach ($items as $delta => $item) {
if ($item['eid'] && !entity_load($field['settings']['entity'], array(
$item['eid'],
))) {
$errors[$field['field_name']][$langcode][$delta][] = array(
'error' => 'ref_field_invalid_rel',
'message' => t('%name: you selected an invalid %entity.', array(
'%name' => $instance['label'],
'%entity' => $field['settings']['entity'],
)),
);
}
if ($field['settings']['entity'] == $entity_type && $item['eid'] && $item['eid'] == $entity->{$entity_info}['entity keys']['id']) {
$errors[$field['field_name']][$langcode][$delta][] = array(
'error' => 'ref_field_auto_reference',
'message' => t('%name: self-reference are not allowed, please change the value.', array(
'%name' => $instance['label'],
)),
);
}
}
}
function ref_field_field_widget_error($element, $error, $form, &$form_state) {
form_error($element['eid'], $error['message']);
}
function ref_field_field_is_empty($item, $field) {
if (empty($item['eid'])) {
return TRUE;
}
return FALSE;
}
function ref_field_field_formatter_info() {
return array(
'ref_field_id' => array(
'label' => t('Entity id'),
'description' => t('Display the ID of the referenced entity'),
'field types' => array(
'ref_field',
),
'settings' => array(
'linked' => TRUE,
),
),
'ref_field_title' => array(
'label' => t('Entity title'),
'description' => t('Display the title of the referenced entity'),
'field types' => array(
'ref_field',
),
'settings' => array(
'linked' => TRUE,
),
),
);
}
function ref_field_field_formatter_settings_form($field, $instance, $view_mode, $form, &$form_state) {
$element = array();
$display = $instance['display'][$view_mode];
$settings = $display['settings'];
switch ($display['type']) {
case 'ref_field_id':
case 'ref_field_title':
$element['linked'] = array(
'#type' => 'checkbox',
'#default_value' => $settings['linked'],
'#title' => t('Link to content'),
);
break;
}
return $element;
}
function ref_field_field_formatter_settings_summary($field, $instance, $view_mode) {
$display = $instance['display'][$view_mode];
$settings = $display['settings'];
$summary = '';
switch ($display['type']) {
case 'ref_field_id':
case 'ref_field_title':
if ($display['settings']['linked']) {
$summary .= t('Linked to content');
}
else {
$summary .= t('Not linked to content');
}
break;
}
return $summary;
}
function ref_field_field_formatter_prepare_view($entity_type, $entities, $field, $instances, $langcode, &$items, $displays) {
$target_ids = array();
foreach ($entities as $id => $entity) {
foreach ($items[$id] as $delta => $item) {
$target_ids[] = $item['eid'];
}
}
if ($target_ids) {
$target_entities = entity_load($field['settings']['entity'], $target_ids);
foreach ($entities as $id => $entity) {
$rekey = FALSE;
foreach ($items[$id] as $delta => $item) {
if (isset($target_entities[$item['eid']])) {
$items[$id][$delta]['entity'] = $target_entities[$item['eid']];
}
else {
unset($items[$id][$delta]);
$rekey = TRUE;
}
}
if ($rekey) {
$items[$id] = array_values($items[$id]);
}
}
}
}
function ref_field_field_formatter_view($entity_type, $entity, $field, $instance, $langcode, $items, $display) {
$element = array();
switch ($display['type']) {
case 'ref_field_id':
foreach ($items as $delta => $item) {
$e = $item['entity'];
$e_type = $field['settings']['entity'];
$output = $item['eid'];
if ($display['settings']['linked']) {
$uri = entity_uri($e_type, $e);
$output = l($output, $uri['path'], $uri['options']);
}
$element[$delta] = array(
'#markup' => $output,
);
}
break;
case 'ref_field_title':
foreach ($items as $delta => $item) {
$e = $item['entity'];
$e_type = $field['settings']['entity'];
$output = entity_label($e_type, $e);
if ($display['settings']['linked']) {
$uri = entity_uri($e_type, $e);
$output = l($output, $uri['path'], $uri['options']);
}
$element[$delta] = array(
'#markup' => $output,
);
}
break;
}
return $element;
}
function ref_field_field_widget_info_alter(&$info) {
$info['options_select']['field types'][] = 'ref_field';
$info['options_buttons']['field types'][] = 'ref_field';
}
function ref_field_options_list($field) {
return _ref_field_get_referenceable_entities($field);
}
function ref_field_field_widget_info() {
return array(
'ref_field_autocomplete' => array(
'label' => t('Autocomplete'),
'field types' => array(
'ref_field',
),
'behaviors' => array(
'default value' => FIELD_BEHAVIOR_NONE,
),
),
);
}
function ref_field_field_widget_form(&$form, &$form_state, $field, $instance, $langcode, $items, $delta, $element) {
$eid = isset($items[$delta]['eid']) ? $items[$delta]['eid'] : '';
$entity_type = $field['settings']['entity'];
$default_value = '';
if ($eid) {
$entity = entity_load($entity_type, array(
$eid,
));
$default_value = $entity ? ref_field_encode($entity_type, $entity[$eid]) : '';
}
$_SESSION['ref_field'][$instance['id']] = time();
$element += array(
'#type' => 'textfield',
'#default_value' => $default_value,
'#autocomplete_path' => 'ref_field/autocomplete/' . $instance['id'] . '/' . $instance['field_name'] . '/' . $instance['entity_type'] . '/' . $instance['bundle'],
);
$entity_info = entity_get_info($entity_type);
if ($entity_type != 'user' && !isset($entity_info['entity keys']['label'])) {
drupal_set_message(t('%instance: Autocomplete is not compatible with %entity_type entities, please contact your administrator.', array(
'%instance' => $instance['label'],
'%entity_type' => $entity_info['label'],
)), 'error');
}
$element['#element_validate'][] = 'ref_field_field_widget_validate';
return array(
'eid' => $element,
);
}
function ref_field_field_widget_validate($element, &$form_state) {
static $previous = array();
$error = TRUE;
$field = field_widget_field($element, $form_state);
$instance = field_widget_instance($element, $form_state);
$matches = ref_field_decode($element['#value']);
if (!empty($element['#value']) && isset($matches['id'])) {
if (isset($previous[$element['#field_name']]) && in_array($matches['id'], $previous[$element['#field_name']])) {
form_error($element, t('References must be unique, you already references this entity.'));
$error = TRUE;
}
elseif ($entity = entity_load($matches['entity_type'], array(
$matches['id'],
))) {
form_set_value($element, $matches['id'], $form_state);
$previous[$element['#field_name']][] = $matches['id'];
}
}
}
function _ref_fieldl_autocomplete_access($instance_id, $field_name, $entity_type, $bundle_name) {
$field = field_info_field($field_name);
$instance = field_info_instance($entity_type, $field_name, $bundle_name);
if ($field && $instance && field_access('edit', $field, $entity_type)) {
return TRUE;
}
return FALSE;
}
function _ref_fieldl_autocomplete($instance_id, $field_name, $entity_type, $bundle_name, $string = '') {
if (isset($_SESSION['ref_field'])) {
foreach ($_SESSION['ref_field'] as $id => $instance_time) {
if ($_SESSION['ref_field'][$id] < time() - REFFIELDTIMEOUT) {
unset($_SESSION['ref_field'][$id]);
}
}
}
if (isset($_SESSION['ref_field'][$instance_id])) {
$field = field_info_field($field_name);
$ref_entity_type = $field['settings']['entity'];
$ref_bundles = $field['settings']['bundles'];
$matches = array();
$ref_entity_info = entity_get_info($ref_entity_type);
if ($string && $ref_entity_info) {
$ref_base_table = $ref_entity_info['base table'];
$ref_id_column = $ref_entity_info['entity keys']['id'];
$ref_label_column = FALSE;
if (isset($ref_entity_info['entity keys']['label'])) {
$ref_label_column = $ref_entity_info['entity keys']['label'];
}
elseif ($ref_entity_type == 'user') {
$ref_label_column = 'name';
}
if ($ref_label_column) {
$query = new EntityFieldQuery();
$query
->entityCondition('entity_type', $ref_entity_type);
if ($ref_bundles) {
switch ($ref_entity_type) {
case 'taxonomy_term':
$query
->propertyCondition('vid', $ref_bundles, 'IN');
break;
default:
$query
->entityCondition('bundle', $ref_bundles, 'IN');
break;
}
}
$query
->propertyCondition($ref_label_column, db_like($string), 'CONTAINS');
$results = $query
->range(0, 10)
->execute();
$ref_entities = entity_load($ref_entity_type, array_keys($results[$ref_entity_type]));
foreach ($ref_entities as $ref_entity) {
$matches[ref_field_encode($ref_entity_type, $ref_entity)] = entity_label($ref_entity_type, $ref_entity);
}
}
else {
$matches[t('error')] = t('Autocomplete is not compatible<br />with %entity_type entities,<br />please contact your administrator.', array(
'%entity_type' => $ref_entity_info['label'],
));
}
}
}
else {
$matches[t('error')] = t('Please refresh the form to<br />get access to the autocomplete.<br />If error persist, you may not have<br />access to this autocomplete\'s content.');
}
drupal_json_output($matches);
}
function ref_field_encode($entity_type, $entity) {
$entity_info = entity_get_info($entity_type);
$id_column = $entity_info['entity keys']['id'];
return entity_label($entity_type, $entity) . ' | ' . $entity_type . ':' . $entity->{$id_column};
}
function ref_field_decode($value) {
$matches = array();
preg_match('`\\| ([a-z_]+):([0-9]+)$`', $value, $matches);
$return = FALSE;
if (isset($matches[1]) && isset($matches[2])) {
$return = array(
'entity_type' => $matches[1],
'id' => $matches[2],
);
}
return $return;
}
function _ref_field_get_referenceable_entities($field) {
$options = array();
$entity_type = $field['settings']['entity'];
$query = new EntityFieldQuery();
$query
->entityCondition('entity_type', $entity_type);
if ($field['settings']['bundles']) {
switch ($field['settings']['entity']) {
case 'comment':
break;
case 'taxonomy_term':
$query
->propertyCondition('vid', $field['settings']['bundles'], 'IN');
break;
default:
$query
->entityCondition('bundle', $field['settings']['bundles'], 'IN');
break;
}
}
$results = $query
->execute();
if (!empty($results[$entity_type])) {
$entities = entity_load($entity_type, array_keys($results[$entity_type]));
foreach ($entities as $entity_id => $entity) {
$options[$entity_id] = entity_label($entity_type, $entity);
}
}
return $options;
}
function ref_field_field_property_callback(&$info, $entity_type, $field, $instance, $field_type) {
$field_type['property_type'] = $field['settings']['entity'];
entity_metadata_field_default_property_callback($info, $entity_type, $field, $instance, $field_type);
}
function ref_field_node_type_update($info) {
if (!empty($info->old_type) && $info->old_type != $info->type) {
$fields = field_read_fields(array(
'type' => 'ref_field',
));
foreach ($fields as $field_name => $field) {
if ($field['settings']['entity'] == 'node' && isset($field['settings']['bundles'][$info->old_type])) {
$field['settings']['bundles'][$info->type] = $info->type;
unset($field['settings']['bundles'][$info->old_type]);
field_update_field($field);
}
}
}
}
function ref_field_views_api() {
return array(
'api' => 3,
'path' => drupal_get_path('module', 'ref_field') . '/includes/views',
);
}