Projet

Général

Profil

Paste
Télécharger (48,4 ko) Statistiques
| Branche: | Révision:

root / drupal7 / sites / all / modules / entityreference / entityreference.module @ 3acd948f

1
<?php
2

    
3
define('ENTITYREFERENCE_DENIED', '- Restricted access -');
4

    
5
/**
6
 * @file
7
 * Entityreference primary module file.
8
 */
9

    
10
/**
11
 * Implements hook_ctools_plugin_directory().
12
 */
13
function entityreference_ctools_plugin_directory($module, $plugin) {
14
  if ($module == 'entityreference') {
15
    return 'plugins/' . $plugin;
16
  }
17
}
18

    
19
/**
20
 * Implements hook_init().
21
 */
22
function entityreference_init() {
23
  // Include feeds.module integration.
24
  if (module_exists('feeds')) {
25
    module_load_include('inc', 'entityreference', 'entityreference.feeds');
26
  }
27
}
28

    
29
/**
30
 * Implements hook_ctools_plugin_type().
31
 */
32
function entityreference_ctools_plugin_type() {
33
  $plugins['selection'] = array(
34
    'classes' => array('class'),
35
  );
36
  $plugins['behavior'] = array(
37
    'classes' => array('class'),
38
    'process' => 'entityreference_behavior_plugin_process',
39
  );
40
  return $plugins;
41
}
42

    
43
/**
44
 * CTools callback; Process the behavoir plugins.
45
 */
46
function entityreference_behavior_plugin_process(&$plugin, $info) {
47
  $plugin += array(
48
    'description' => '',
49
    'behavior type' => 'field',
50
    'access callback' => FALSE,
51
    'force enabled' => FALSE,
52
  );
53
}
54

    
55
/**
56
 * Implements hook_field_info().
57
 */
58
function entityreference_field_info() {
59
  $field_info['entityreference'] = array(
60
    'label' => t('Entity Reference'),
61
    'description' => t('This field reference another entity.'),
62
    'settings' => array(
63
      // Default to the core target entity type node.
64
      'target_type' => 'node',
65
      // The handler for this field.
66
      'handler' => 'base',
67
      // The handler settings.
68
      'handler_settings' => array(),
69
    ),
70
    'instance_settings' => array(),
71
    'default_widget' => 'entityreference_autocomplete',
72
    'default_formatter' => 'entityreference_label',
73
    'property_callbacks' => array('entityreference_field_property_callback'),
74
  );
75
  return $field_info;
76
}
77

    
78
/**
79
 * Implements hook_flush_caches().
80
 */
81
function entityreference_flush_caches() {
82
  // Because of the intricacies of the info hooks, we are forced to keep a
83
  // separate list of the base tables of each entities, so that we can use
84
  // it in entityreference_field_schema() without calling entity_get_info().
85
  // See http://drupal.org/node/1416558 for details.
86
  $base_tables = array();
87
  foreach (entity_get_info() as $entity_type => $entity_info) {
88
    if (!empty($entity_info['base table']) && !empty($entity_info['entity keys']['id'])) {
89
      $base_tables[$entity_type] = array($entity_info['base table'], $entity_info['entity keys']['id']);
90
    }
91
  }
92
  // We are using a variable because cache is going to be cleared right after
93
  // hook_flush_caches() is finished.
94
  variable_set('entityreference:base-tables', $base_tables);
95
}
96

    
97
/**
98
 * Implements hook_theme().
99
 */
100
function entityreference_theme($existing, $type, $theme, $path) {
101
  return array(
102
    'entityreference_label' => array(
103
      'variables' => array('label' => NULL, 'item' => NULL, 'settings' => NULL, 'uri' => NULL),
104
    ),
105
    'entityreference_entity_id' => array(
106
      'variables' => array('item' => NULL, 'settings' => NULL),
107
    ),
108
  );
109
}
110

    
111
/**
112
 * Implements hook_menu().
113
 */
114
function entityreference_menu() {
115
  $items = array();
116

    
117
  $items['entityreference/autocomplete/single/%/%/%'] = array(
118
    'title' => 'Entity Reference Autocomplete',
119
    'page callback' => 'entityreference_autocomplete_callback',
120
    'page arguments' => array(2, 3, 4, 5),
121
    'access callback' => 'entityreference_autocomplete_access_callback',
122
    'access arguments' => array(2, 3, 4, 5),
123
    'type' => MENU_CALLBACK,
124
  );
125
  $items['entityreference/autocomplete/tags/%/%/%'] = array(
126
    'title' => 'Entity Reference Autocomplete',
127
    'page callback' => 'entityreference_autocomplete_callback',
128
    'page arguments' => array(2, 3, 4, 5),
129
    'access callback' => 'entityreference_autocomplete_access_callback',
130
    'access arguments' => array(2, 3, 4, 5),
131
    'type' => MENU_CALLBACK,
132
  );
133

    
134
  return $items;
135
}
136

    
137
/**
138
 * Implements hook_field_is_empty().
139
 */
140
function entityreference_field_is_empty($item, $field) {
141
  $empty = !isset($item['target_id']) || !is_numeric($item['target_id']);
142

    
143
  // Invoke the behaviors to allow them to override the empty status.
144
  foreach (entityreference_get_behavior_handlers($field) as $handler) {
145
    $handler->is_empty_alter($empty, $item, $field);
146
  }
147
  return $empty;
148
}
149

    
150
/**
151
 * Get the behavior handlers for a given entityreference field.
152
 */
153
function entityreference_get_behavior_handlers($field, $instance = NULL) {
154
  $object_cache = drupal_static(__FUNCTION__);
155
  $identifier = $field['field_name'];
156
  if (!empty($instance)) {
157
    $identifier .= ':' . $instance['entity_type'] . ':' . $instance['bundle'];
158
  }
159

    
160
  if (!isset($object_cache[$identifier])) {
161
    $object_cache[$identifier] = array();
162

    
163
    // Merge in defaults.
164
    $field['settings'] += array('behaviors' => array());
165

    
166
    $object_cache[$field['field_name']] = array();
167
    $behaviors = !empty($field['settings']['handler_settings']['behaviors']) ? $field['settings']['handler_settings']['behaviors'] : array();
168
    if (!empty($instance['settings']['behaviors'])) {
169
      $behaviors = array_merge($behaviors, $instance['settings']['behaviors']);
170
    }
171
    foreach ($behaviors as $behavior => $settings) {
172
      if (empty($settings['status'])) {
173
        // Behavior is not enabled.
174
        continue;
175
      }
176

    
177
      $object_cache[$identifier][] = _entityreference_get_behavior_handler($behavior);
178
    }
179
  }
180

    
181
  return $object_cache[$identifier];
182
}
183

    
184
/**
185
 * Get the behavior handler for a given entityreference field and instance.
186
 *
187
 * @param $behavior
188
 *   The behavior handler name.
189
 */
190
function _entityreference_get_behavior_handler($behavior) {
191
  $object_cache = drupal_static(__FUNCTION__);
192

    
193
  if (!isset($object_cache[$behavior])) {
194
    ctools_include('plugins');
195
    $class = ctools_plugin_load_class('entityreference', 'behavior', $behavior, 'class');
196

    
197
    $class = class_exists($class) ? $class : 'EntityReference_BehaviorHandler_Broken';
198
    $object_cache[$behavior] = new $class($behavior);
199
  }
200

    
201
  return $object_cache[$behavior];
202
}
203

    
204
/**
205
 * Get the selection handler for a given entityreference field.
206
 */
207
function entityreference_get_selection_handler($field, $instance = NULL, $entity_type = NULL, $entity = NULL) {
208
  ctools_include('plugins');
209
  $handler = $field['settings']['handler'];
210
  $class = ctools_plugin_load_class('entityreference', 'selection', $handler, 'class');
211

    
212
  if (class_exists($class)) {
213
    return call_user_func(array($class, 'getInstance'), $field, $instance, $entity_type, $entity);
214
  }
215
  else {
216
    return EntityReference_SelectionHandler_Broken::getInstance($field, $instance, $entity_type, $entity);
217
  }
218
}
219

    
220
/**
221
 * Implements hook_field_load().
222
 */
223
function entityreference_field_load($entity_type, $entities, $field, $instances, $langcode, &$items) {
224
  // Invoke the behaviors.
225
  foreach (entityreference_get_behavior_handlers($field) as $handler) {
226
    $handler->load($entity_type, $entities, $field, $instances, $langcode, $items);
227
  }
228
}
229

    
230
/**
231
 * Implements hook_field_validate().
232
 */
233
function entityreference_field_validate($entity_type, $entity, $field, $instance, $langcode, $items, &$errors) {
234
  $ids = array();
235
  foreach ($items as $delta => $item) {
236
    if (!entityreference_field_is_empty($item, $field) && $item['target_id'] !== NULL) {
237
      $ids[$item['target_id']] = $delta;
238
    }
239
  }
240

    
241
  if ($ids) {
242
    $valid_ids = entityreference_get_selection_handler($field, $instance, $entity_type, $entity)->validateReferencableEntities(array_keys($ids));
243

    
244
    if (!empty($valid_ids)) {
245
      $invalid_entities = array_diff_key($ids, array_flip($valid_ids));
246
      if ($invalid_entities) {
247
        foreach ($invalid_entities as $id => $delta) {
248
          $errors[$field['field_name']][$langcode][$delta][] = array(
249
            'error' => 'entityreference_invalid_entity',
250
            'message' => t('The referenced entity (@type: @id) is invalid.', array('@type' => $field['settings']['target_type'], '@id' => $id)),
251
          );
252
        }
253
      }
254
    }
255
  }
256

    
257
  // Invoke the behaviors.
258
  foreach (entityreference_get_behavior_handlers($field, $instance) as $handler) {
259
    $handler->validate($entity_type, $entity, $field, $instance, $langcode, $items, $errors);
260
  }
261
}
262

    
263
/**
264
 * Implements hook_field_presave().
265
 *
266
 * Adds the target type to the field data structure when saving.
267
 */
268
function entityreference_field_presave($entity_type, $entity, $field, $instance, $langcode, &$items) {
269
  // Invoke the behaviors.
270
  foreach (entityreference_get_behavior_handlers($field, $instance) as $handler) {
271
    $handler->presave($entity_type, $entity, $field, $instance, $langcode, $items);
272
  }
273
}
274

    
275
/**
276
 * Implements hook_field_insert().
277
 */
278
function entityreference_field_insert($entity_type, $entity, $field, $instance, $langcode, &$items) {
279
  // Invoke the behaviors.
280
  foreach (entityreference_get_behavior_handlers($field, $instance) as $handler) {
281
    $handler->insert($entity_type, $entity, $field, $instance, $langcode, $items);
282
  }
283
}
284

    
285
/**
286
 * Implements hook_field_attach_insert().
287
 *
288
 * Emulates a post-insert hook.
289
 */
290
function entityreference_field_attach_insert($entity_type, $entity) {
291
  list(, , $bundle) = entity_extract_ids($entity_type, $entity);
292
  foreach (field_info_instances($entity_type, $bundle) as $field_name => $instance) {
293
    $field = field_info_field($field_name);
294
    if ($field['type'] == 'entityreference') {
295
      foreach (entityreference_get_behavior_handlers($field, $instance) as $handler) {
296
        $handler->postInsert($entity_type, $entity, $field, $instance);
297
      }
298
    }
299
  }
300
}
301

    
302
/**
303
 * Implements hook_field_update().
304
 */
305
function entityreference_field_update($entity_type, $entity, $field, $instance, $langcode, &$items) {
306
  // Invoke the behaviors.
307
  foreach (entityreference_get_behavior_handlers($field, $instance) as $handler) {
308
    $handler->update($entity_type, $entity, $field, $instance, $langcode, $items);
309
  }
310
}
311

    
312
/**
313
 * Implements hook_field_attach_update().
314
 *
315
 * Emulates a post-update hook.
316
 */
317
function entityreference_field_attach_update($entity_type, $entity) {
318
  list(, , $bundle) = entity_extract_ids($entity_type, $entity);
319
  foreach (field_info_instances($entity_type, $bundle) as $field_name => $instance) {
320
    $field = field_info_field($field_name);
321
    if ($field['type'] == 'entityreference') {
322
      foreach (entityreference_get_behavior_handlers($field, $instance) as $handler) {
323
        $handler->postUpdate($entity_type, $entity, $field, $instance);
324
      }
325
    }
326
  }
327
}
328

    
329
/**
330
 * Implements hook_field_delete().
331
 */
332
function entityreference_field_delete($entity_type, $entity, $field, $instance, $langcode, &$items) {
333
  // Invoke the behaviors.
334
  foreach (entityreference_get_behavior_handlers($field, $instance) as $handler) {
335
    $handler->delete($entity_type, $entity, $field, $instance, $langcode, $items);
336
  }
337
}
338

    
339
/**
340
 * Implements hook_field_attach_delete().
341
 *
342
 * Emulates a post-delete hook.
343
 */
344
function entityreference_field_attach_delete($entity_type, $entity) {
345
  list(, , $bundle) = entity_extract_ids($entity_type, $entity);
346
  foreach (field_info_instances($entity_type, $bundle) as $field_name => $instance) {
347
    $field = field_info_field($field_name);
348
    if ($field['type'] == 'entityreference') {
349
      foreach (entityreference_get_behavior_handlers($field, $instance) as $handler) {
350
        $handler->postDelete($entity_type, $entity, $field, $instance);
351
      }
352
    }
353
  }
354
}
355

    
356
/**
357
 * Implements hook_entity_insert().
358
 */
359
function entityreference_entity_insert($entity, $entity_type) {
360
  entityreference_entity_crud($entity, $entity_type, 'entityPostInsert');
361
}
362

    
363
/**
364
 * Implements hook_entity_update().
365
 */
366
function entityreference_entity_update($entity, $entity_type) {
367
  entityreference_entity_crud($entity, $entity_type, 'entityPostUpdate');
368
}
369

    
370
/**
371
 * Implements hook_entity_delete().
372
 */
373
function entityreference_entity_delete($entity, $entity_type) {
374
  entityreference_entity_crud($entity, $entity_type, 'entityPostDelete');
375
}
376

    
377
/**
378
 * Invoke a behavior based on entity CRUD.
379
 *
380
 * @param $entity
381
 *   The entity object.
382
 * @param $entity_type
383
 *   The entity type.
384
 * @param $method_name
385
 *   The method to invoke.
386
 */
387
function entityreference_entity_crud($entity, $entity_type, $method_name) {
388
  list(, , $bundle) = entity_extract_ids($entity_type, $entity);
389
  foreach (field_info_instances($entity_type, $bundle) as $field_name => $instance) {
390
    $field = field_info_field($field_name);
391
    if ($field['type'] == 'entityreference') {
392
      foreach (entityreference_get_behavior_handlers($field, $instance) as $handler) {
393
        $handler->{$method_name}($entity_type, $entity, $field, $instance);
394
      }
395
    }
396
  }
397
}
398

    
399
/**
400
 * Implements hook_field_settings_form().
401
 */
402
function entityreference_field_settings_form($field, $instance, $has_data) {
403
  // The field settings infrastructure is not AJAX enabled by default,
404
  // because it doesn't pass over the $form_state.
405
  // Build the whole form into a #process in which we actually have access
406
  // to the form state.
407
  $form = array(
408
    '#type' => 'container',
409
    '#attached' => array(
410
      'css' => array(drupal_get_path('module', 'entityreference') . '/entityreference.admin.css'),
411
    ),
412
    '#process' => array(
413
      '_entityreference_field_settings_process',
414
      '_entityreference_field_settings_ajax_process',
415
    ),
416
    '#element_validate' => array('_entityreference_field_settings_validate'),
417
    '#field' => $field,
418
    '#instance' => $instance,
419
    '#has_data' => $has_data,
420
  );
421
  return $form;
422
}
423

    
424
/**
425
 * Callback for custom element processing.
426
 */
427
function _entityreference_field_settings_process($form, $form_state) {
428
  $field = isset($form_state['entityreference']['field']) ? $form_state['entityreference']['field'] : $form['#field'];
429
  $instance = isset($form_state['entityreference']['instance']) ? $form_state['entityreference']['instance'] : $form['#instance'];
430
  $has_data = $form['#has_data'];
431

    
432
  $settings = $field['settings'];
433
  $settings += array('handler' => 'base');
434

    
435
  // Select the target entity type.
436
  $entity_type_options = array();
437
  foreach (entity_get_info() as $entity_type => $entity_info) {
438
    $entity_type_options[$entity_type] = $entity_info['label'];
439
  }
440

    
441
  $form['target_type'] = array(
442
    '#type' => 'select',
443
    '#title' => t('Target type'),
444
    '#options' => $entity_type_options,
445
    '#default_value' => $field['settings']['target_type'],
446
    '#required' => TRUE,
447
    '#description' => t('The entity type that can be referenced through this field.'),
448
    '#disabled' => $has_data,
449
    '#size' => 1,
450
    '#ajax' => TRUE,
451
    '#limit_validation_errors' => array(),
452
  );
453

    
454
  ctools_include('plugins');
455
  $handlers = ctools_get_plugins('entityreference', 'selection');
456
  uasort($handlers, 'ctools_plugin_sort');
457
  $handlers_options = array();
458
  foreach ($handlers as $handler => $handler_info) {
459
    $handlers_options[$handler] = check_plain($handler_info['title']);
460
  }
461

    
462
  $form['handler'] = array(
463
    '#type' => 'fieldset',
464
    '#title' => t('Entity selection'),
465
    '#tree' => TRUE,
466
    '#process' => array('_entityreference_form_process_merge_parent'),
467
  );
468

    
469
  $form['handler']['handler'] = array(
470
    '#type' => 'select',
471
    '#title' => t('Mode'),
472
    '#options' => $handlers_options,
473
    '#default_value' => $settings['handler'],
474
    '#required' => TRUE,
475
    '#ajax' => TRUE,
476
    '#limit_validation_errors' => array(),
477
  );
478
  $form['handler_submit'] = array(
479
    '#type' => 'submit',
480
    '#value' => t('Change handler'),
481
    '#limit_validation_errors' => array(),
482
    '#attributes' => array(
483
      'class' => array('js-hide'),
484
    ),
485
    '#submit' => array('entityreference_settings_ajax_submit'),
486
  );
487

    
488
  $form['handler']['handler_settings'] = array(
489
    '#type' => 'container',
490
    '#attributes' => array('class' => array('entityreference-settings')),
491
  );
492

    
493
  $handler = entityreference_get_selection_handler($field, $instance);
494
  $form['handler']['handler_settings'] += $handler->settingsForm($field, $instance);
495

    
496
  _entityreference_get_behavior_elements($form, $field, $instance, 'field');
497
  if (!empty($form['behaviors'])) {
498
    $form['behaviors'] += array(
499
      '#type' => 'fieldset',
500
      '#title' => t('Additional behaviors'),
501
      '#parents' => array_merge($form['#parents'], array('handler_settings', 'behaviors')),
502
    );
503
  }
504

    
505
  return $form;
506
}
507

    
508
/**
509
 * Custom callback for ajax processing.
510
 */
511
function _entityreference_field_settings_ajax_process($form, $form_state) {
512
  _entityreference_field_settings_ajax_process_element($form, $form);
513
  return $form;
514
}
515

    
516
/**
517
 * Helper function for custom ajax processing.
518
 */
519
function _entityreference_field_settings_ajax_process_element(&$element, $main_form) {
520
  if (isset($element['#ajax']) && $element['#ajax'] === TRUE) {
521
    $element['#ajax'] = array(
522
      'callback' => 'entityreference_settings_ajax',
523
      'wrapper' => $main_form['#id'],
524
      'element' => $main_form['#array_parents'],
525
    );
526
  }
527

    
528
  foreach (element_children($element) as $key) {
529
    _entityreference_field_settings_ajax_process_element($element[$key], $main_form);
530
  }
531
}
532

    
533
/**
534
 * Custom callback for element processing.
535
 */
536
function _entityreference_form_process_merge_parent($element) {
537
  $parents = $element['#parents'];
538
  array_pop($parents);
539
  $element['#parents'] = $parents;
540
  return $element;
541
}
542

    
543
/**
544
 * Helper function to remove blank elements.
545
 */
546
function _entityreference_element_validate_filter(&$element, &$form_state) {
547
  $element['#value'] = array_filter($element['#value']);
548
  form_set_value($element, $element['#value'], $form_state);
549
}
550

    
551
/**
552
 * Implements hook_validate().
553
 */
554
function _entityreference_field_settings_validate($form, &$form_state) {
555
  // Store the new values in the form state.
556
  $field = $form['#field'];
557
  if (isset($form_state['values']['field'])) {
558
    $field['settings'] = $form_state['values']['field']['settings'];
559
  }
560
  $form_state['entityreference']['field'] = $field;
561

    
562
  unset($form_state['values']['field']['settings']['handler_submit']);
563
}
564

    
565
/**
566
 * Implements hook_field_instance_settings_form().
567
 */
568
function entityreference_field_instance_settings_form($field, $instance) {
569
  $form['settings'] = array(
570
    '#type' => 'container',
571
    '#attached' => array(
572
      'css' => array(drupal_get_path('module', 'entityreference') . '/entityreference.admin.css'),
573
    ),
574
    '#weight' => 10,
575
    '#tree' => TRUE,
576
    '#process' => array(
577
      '_entityreference_form_process_merge_parent',
578
      '_entityreference_field_instance_settings_form',
579
      '_entityreference_field_settings_ajax_process',
580
    ),
581
    '#element_validate' => array('_entityreference_field_instance_settings_validate'),
582
    '#field' => $field,
583
    '#instance' => $instance,
584
  );
585

    
586
  return $form;
587
}
588

    
589
/**
590
 * Implements hook_field_settings_form().
591
 */
592
function _entityreference_field_instance_settings_form($form, $form_state) {
593
  $field = isset($form_state['entityreference']['field']) ? $form_state['entityreference']['field'] : $form['#field'];
594
  $instance = isset($form_state['entityreference']['instance']) ? $form_state['entityreference']['instance'] : $form['#instance'];
595

    
596
  _entityreference_get_behavior_elements($form, $field, $instance, 'instance');
597
  if (!empty($form['behaviors'])) {
598
    $form['behaviors'] += array(
599
      '#type' => 'fieldset',
600
      '#title' => t('Additional behaviors'),
601
      '#process' => array(
602
        '_entityreference_field_settings_ajax_process',
603
      ),
604
    );
605
  }
606
  return $form;
607
}
608

    
609
/**
610
 * Implements hook_validate().
611
 */
612
function _entityreference_field_instance_settings_validate($form, &$form_state) {
613
  // Store the new values in the form state.
614
  $instance = $form['#instance'];
615
  if (isset($form_state['values']['instance'])) {
616
    $instance = drupal_array_merge_deep($instance, $form_state['values']['instance']);
617
  }
618
  $form_state['entityreference']['instance'] = $instance;
619
}
620

    
621
/**
622
 * Get the field or instance elements for the field configuration.
623
 */
624
function _entityreference_get_behavior_elements(&$element, $field, $instance, $level) {
625
  // Add the accessible behavior handlers.
626
  $behavior_plugins = entityreference_get_accessible_behavior_plugins($field, $instance);
627

    
628
  if ($behavior_plugins[$level]) {
629
    $element['behaviors'] = array();
630

    
631
    foreach ($behavior_plugins[$level] as $name => $plugin) {
632
      if ($level == 'field') {
633
        $settings = !empty($field['settings']['handler_settings']['behaviors'][$name]) ? $field['settings']['handler_settings']['behaviors'][$name] : array();
634
      }
635
      else {
636
        $settings = !empty($instance['settings']['behaviors'][$name]) ? $instance['settings']['behaviors'][$name] : array();
637
      }
638
      $settings += array('status' => $plugin['force enabled']);
639

    
640
      // Render the checkbox.
641
      $element['behaviors'][$name] = array(
642
        '#tree' => TRUE,
643
      );
644
      $element['behaviors'][$name]['status'] = array(
645
        '#type' => 'checkbox',
646
        '#title' => check_plain($plugin['title']),
647
        '#description' => $plugin['description'],
648
        '#default_value' => $settings['status'],
649
        '#disabled' => $plugin['force enabled'],
650
        '#ajax' => TRUE,
651
      );
652

    
653
      if ($settings['status']) {
654
        $handler = _entityreference_get_behavior_handler($name);
655
        if ($behavior_elements = $handler->settingsForm($field, $instance)) {
656
          foreach ($behavior_elements as $key => &$behavior_element) {
657
            $behavior_element += array(
658
              '#default_value' => !empty($settings[$key]) ? $settings[$key] : NULL,
659
            );
660
          }
661

    
662
          // Get the behavior settings.
663
          $behavior_elements += array(
664
            '#type' => 'container',
665
            '#process' => array('_entityreference_form_process_merge_parent'),
666
            '#attributes' => array(
667
              'class' => array('entityreference-settings'),
668
            ),
669
          );
670
          $element['behaviors'][$name]['settings'] = $behavior_elements;
671
        }
672
      }
673
    }
674
  }
675
}
676

    
677
/**
678
 * Get all accessible behavior plugins.
679
 */
680
function entityreference_get_accessible_behavior_plugins($field, $instance) {
681
  ctools_include('plugins');
682
  $plugins = array('field' => array(), 'instance' => array());
683
  foreach (ctools_get_plugins('entityreference', 'behavior') as $name => $plugin) {
684
    $handler = _entityreference_get_behavior_handler($name);
685
    $level = $plugin['behavior type'];
686
    if ($handler->access($field, $instance)) {
687
      $plugins[$level][$name] = $plugin;
688
    }
689
  }
690
  return $plugins;
691
}
692

    
693
/**
694
 * Ajax callback for the handler settings form.
695
 *
696
 * @see entityreference_field_settings_form()
697
 */
698
function entityreference_settings_ajax($form, $form_state) {
699
  $trigger = $form_state['triggering_element'];
700
  return drupal_array_get_nested_value($form, $trigger['#ajax']['element']);
701
}
702

    
703
/**
704
 * Submit handler for the non-JS case.
705
 *
706
 * @see entityreference_field_settings_form()
707
 */
708
function entityreference_settings_ajax_submit($form, &$form_state) {
709
  $form_state['rebuild'] = TRUE;
710
}
711

    
712
/**
713
 * Property callback for the Entity Metadata framework.
714
 */
715
function entityreference_field_property_callback(&$info, $entity_type, $field, $instance, $field_type) {
716
  // Set the property type based on the targe type.
717
  $field_type['property_type'] = $field['settings']['target_type'];
718

    
719
  // Then apply the default.
720
  entity_metadata_field_default_property_callback($info, $entity_type, $field, $instance, $field_type);
721

    
722
  // Invoke the behaviors to allow them to change the properties.
723
  foreach (entityreference_get_behavior_handlers($field, $instance) as $handler) {
724
    $handler->property_info_alter($info, $entity_type, $field, $instance, $field_type);
725
  }
726
}
727

    
728
/**
729
 * Implements hook_field_widget_info().
730
 */
731
function entityreference_field_widget_info() {
732
  $widgets['entityreference_autocomplete'] = array(
733
    'label' => t('Autocomplete'),
734
    'description' => t('An autocomplete text field.'),
735
    'field types' => array('entityreference'),
736
    'settings' => array(
737
      'match_operator' => 'CONTAINS',
738
      'size' => 60,
739
      // We don't have a default here, because it's not the same between
740
      // the two widgets, and the Field API doesn't update default
741
      // settings when the widget changes.
742
      'path' => '',
743
    ),
744
  );
745

    
746
  $widgets['entityreference_autocomplete_tags'] = array(
747
    'label' => t('Autocomplete (Tags style)'),
748
    'description' => t('An autocomplete text field.'),
749
    'field types' => array('entityreference'),
750
    'settings' => array(
751
      'match_operator' => 'CONTAINS',
752
      'size' => 60,
753
      // We don't have a default here, because it's not the same between
754
      // the two widgets, and the Field API doesn't update default
755
      // settings when the widget changes.
756
      'path' => '',
757
    ),
758
    'behaviors' => array(
759
      'multiple values' => FIELD_BEHAVIOR_CUSTOM,
760
    ),
761
  );
762

    
763
  return $widgets;
764
}
765

    
766
/**
767
 * Implements hook_field_widget_info_alter().
768
 */
769
function entityreference_field_widget_info_alter(&$info) {
770
  if (module_exists('options')) {
771
    $info['options_select']['field types'][] = 'entityreference';
772
    $info['options_buttons']['field types'][] = 'entityreference';
773
  }
774
}
775

    
776
/**
777
 * Implements hook_field_widget_settings_form().
778
 */
779
function entityreference_field_widget_settings_form($field, $instance) {
780
  $widget = $instance['widget'];
781
  $settings = $widget['settings'] + field_info_widget_settings($widget['type']);
782

    
783
  $form = array();
784

    
785
  if ($widget['type'] == 'entityreference_autocomplete' || $widget['type'] == 'entityreference_autocomplete_tags') {
786
    $form['match_operator'] = array(
787
      '#type' => 'select',
788
      '#title' => t('Autocomplete matching'),
789
      '#default_value' => $settings['match_operator'],
790
      '#options' => array(
791
        'STARTS_WITH' => t('Starts with'),
792
        'CONTAINS' => t('Contains'),
793
      ),
794
      '#description' => t('Select the method used to collect autocomplete suggestions. Note that <em>Contains</em> can cause performance issues on sites with thousands of nodes.'),
795
    );
796
    $form['size'] = array(
797
      '#type' => 'textfield',
798
      '#title' => t('Size of textfield'),
799
      '#default_value' => $settings['size'],
800
      '#element_validate' => array('_element_validate_integer_positive'),
801
      '#required' => TRUE,
802
    );
803
  }
804

    
805
  return $form;
806
}
807

    
808
/**
809
 * Implements hook_options_list().
810
 */
811
function entityreference_options_list($field, $instance = NULL, $entity_type = NULL, $entity = NULL) {
812
  if (!$options = entityreference_get_selection_handler($field, $instance, $entity_type, $entity)->getReferencableEntities()) {
813
    return array();
814
  }
815

    
816
  // Rebuild the array, by changing the bundle key into the bundle label.
817
  $target_type = $field['settings']['target_type'];
818
  $entity_info = entity_get_info($target_type);
819

    
820
  $return = array();
821
  foreach ($options as $bundle => $entity_ids) {
822
    $bundle_label = check_plain($entity_info['bundles'][$bundle]['label']);
823
    $return[$bundle_label] = $entity_ids;
824
  }
825

    
826
  return count($return) == 1 ? reset($return) : $return;
827
}
828

    
829
/**
830
 * Implements hook_query_TAG_alter().
831
 */
832
function entityreference_query_entityreference_alter(QueryAlterableInterface $query) {
833
  $handler = $query->getMetadata('entityreference_selection_handler');
834
  $handler->entityFieldQueryAlter($query);
835
}
836

    
837
/**
838
 * Implements hook_field_widget_form().
839
 */
840
function entityreference_field_widget_form(&$form, &$form_state, $field, $instance, $langcode, $items, $delta, $element) {
841
  // Ensure that the entity target type exists before displaying the widget.
842
  $entity_info = entity_get_info($field['settings']['target_type']);
843
  if (empty($entity_info)) {
844
    return;
845
  }
846
  $entity_type = $instance['entity_type'];
847
  $entity = isset($element['#entity']) ? $element['#entity'] : NULL;
848
  $handler = entityreference_get_selection_handler($field, $instance, $entity_type, $entity);
849

    
850
  if ($instance['widget']['type'] == 'entityreference_autocomplete' || $instance['widget']['type'] == 'entityreference_autocomplete_tags') {
851

    
852
    if ($instance['widget']['type'] == 'entityreference_autocomplete') {
853
      // We let the Field API handles multiple values for us, only take
854
      // care of the one matching our delta.
855
      if (isset($items[$delta])) {
856
        $items = array($items[$delta]);
857
      }
858
      else {
859
        $items = array();
860
      }
861
    }
862

    
863
    $entity_ids = array();
864
    $entity_labels = array();
865

    
866
    // Build an array of entities ID.
867
    foreach ($items as $item) {
868
      if (isset($item['target_id'])) {
869
        $entity_ids[] = $item['target_id'];
870
      }
871
    }
872

    
873
    // Load those entities and loop through them to extract their labels.
874
    $entities = entity_load($field['settings']['target_type'], $entity_ids);
875

    
876
    foreach ($entities as $entity_id => $entity_item) {
877
      $label = $handler->getLabel($entity_item);
878
      $key = "$label ($entity_id)";
879
      // Labels containing commas or quotes must be wrapped in quotes.
880
      if (strpos($key, ',') !== FALSE || strpos($key, '"') !== FALSE) {
881
        $key = '"' . str_replace('"', '""', $key) . '"';
882
      }
883
      $entity_labels[] = $key;
884
    }
885

    
886
    // Prepare the autocomplete path.
887
    if (!empty($instance['widget']['settings']['path'])) {
888
      $autocomplete_path = $instance['widget']['settings']['path'];
889
    }
890
    else {
891
      $autocomplete_path = $instance['widget']['type'] == 'entityreference_autocomplete' ? 'entityreference/autocomplete/single' : 'entityreference/autocomplete/tags';
892
    }
893

    
894
    $autocomplete_path .= '/' . $field['field_name'] . '/' . $instance['entity_type'] . '/' . $instance['bundle'] . '/';
895
    // Use <NULL> as a placeholder in the URL when we don't have an entity.
896
    // Most webservers collapse two consecutive slashes.
897
    $id = 'NULL';
898
    if ($entity) {
899
      list($eid) = entity_extract_ids($entity_type, $entity);
900
      if ($eid) {
901
        $id = $eid;
902
      }
903
    }
904
    $autocomplete_path .= $id;
905

    
906
    if ($instance['widget']['type'] == 'entityreference_autocomplete') {
907
      $element += array(
908
        '#type' => 'textfield',
909
        '#maxlength' => 1024,
910
        '#default_value' => implode(', ', $entity_labels),
911
        '#autocomplete_path' => $autocomplete_path,
912
        '#size' => $instance['widget']['settings']['size'],
913
        '#element_validate' => array('_entityreference_autocomplete_validate'),
914
      );
915
      return array('target_id' => $element);
916
    }
917
    else {
918
      $element += array(
919
        '#type' => 'textfield',
920
        '#maxlength' => 1024,
921
        '#default_value' => implode(', ', $entity_labels),
922
        '#autocomplete_path' => $autocomplete_path,
923
        '#size' => $instance['widget']['settings']['size'],
924
        '#element_validate' => array('_entityreference_autocomplete_tags_validate'),
925
      );
926
      return $element;
927
    }
928
  }
929
}
930

    
931
/**
932
 * Implements hook_validate().
933
 */
934
function _entityreference_autocomplete_validate($element, &$form_state, $form) {
935
  // If a value was entered into the autocomplete...
936
  $value = '';
937
  if (!empty($element['#value'])) {
938
    // Take "label (entity id)', match the id from parenthesis.
939
    if (preg_match("/.+\((\d+)\)/", $element['#value'], $matches)) {
940
      $value = $matches[1];
941
    }
942
    else {
943
      // Try to get a match from the input string when the user didn't use the
944
      // autocomplete but filled in a value manually.
945
      $field = field_info_field($element['#field_name']);
946
      $handler = entityreference_get_selection_handler($field);
947
      $field_name = $element['#field_name'];
948
      $field = field_info_field($field_name);
949
      $instance = field_info_instance($element['#entity_type'], $field_name, $element['#bundle']);
950
      $handler = entityreference_get_selection_handler($field, $instance);
951
      $value = $handler->validateAutocompleteInput($element['#value'], $element, $form_state, $form);
952
    }
953
  }
954
  // Update the value of this element so the field can validate the product IDs.
955
  form_set_value($element, $value, $form_state);
956
}
957

    
958
/**
959
 * Implements hook_validate().
960
 */
961
function _entityreference_autocomplete_tags_validate($element, &$form_state, $form) {
962
  $value = array();
963
  // If a value was entered into the autocomplete...
964
  if (!empty($element['#value'])) {
965
    $entities = drupal_explode_tags($element['#value']);
966
    $value = array();
967
    foreach ($entities as $entity) {
968
      // Take "label (entity id)', match the id from parenthesis.
969
      if (preg_match("/.+\((\d+)\)/", $entity, $matches)) {
970
        $value[] = array(
971
          'target_id' => $matches[1],
972
        );
973
      }
974
      else {
975
        // Try to get a match from the input string when the user didn't use the
976
        // autocomplete but filled in a value manually.
977
        $field = field_info_field($element['#field_name']);
978
        $handler = entityreference_get_selection_handler($field);
979
        $value[] = array(
980
          'target_id' => $handler->validateAutocompleteInput($entity, $element, $form_state, $form),
981
        );
982
      }
983
    }
984
  }
985
  // Update the value of this element so the field can validate the product IDs.
986
  form_set_value($element, $value, $form_state);
987
}
988

    
989
/**
990
 * Implements hook_field_widget_error().
991
 */
992
function entityreference_field_widget_error($element, $error) {
993
  form_error($element, $error['message']);
994
}
995

    
996
/**
997
 * Menu Access callback for the autocomplete widget.
998
 *
999
 * @param $type
1000
 *   The widget type (i.e. 'single' or 'tags').
1001
 * @param $field_name
1002
 *   The name of the entity-reference field.
1003
 * @param $entity_type
1004
 *   The entity type.
1005
 * @param $bundle_name
1006
 *   The bundle name.
1007
 *
1008
 * @return bool
1009
 *   True if user can access this menu item.
1010
 */
1011
function entityreference_autocomplete_access_callback($type, $field_name, $entity_type, $bundle_name) {
1012
  $field = field_info_field($field_name);
1013
  $instance = field_info_instance($entity_type, $field_name, $bundle_name);
1014

    
1015
  if (!$field || !$instance || $field['type'] != 'entityreference' || !field_access('edit', $field, $entity_type)) {
1016
    return FALSE;
1017
  }
1018
  return TRUE;
1019
}
1020

    
1021
/**
1022
 * Menu callback: autocomplete the label of an entity.
1023
 *
1024
 * @param $type
1025
 *   The widget type (i.e. 'single' or 'tags').
1026
 * @param $field_name
1027
 *   The name of the entity-reference field.
1028
 * @param $entity_type
1029
 *   The entity type.
1030
 * @param $bundle_name
1031
 *   The bundle name.
1032
 * @param $entity_id
1033
 *   Optional; The entity ID the entity-reference field is attached to.
1034
 *   Defaults to ''.
1035
 * @param $string
1036
 *   The label of the entity to query by.
1037
 */
1038
function entityreference_autocomplete_callback($type, $field_name, $entity_type, $bundle_name, $entity_id = '', $string = '') {
1039
  // If the request has a '/' in the search text, then the menu system will have
1040
  // split it into multiple arguments and $string will only be a partial.
1041
  // We want to make sure we recover the intended $string.
1042
  $args = func_get_args();
1043
  // Shift off the $type, $field_name, $entity_type,
1044
  // $bundle_name, and $entity_id args.
1045
  array_shift($args);
1046
  array_shift($args);
1047
  array_shift($args);
1048
  array_shift($args);
1049
  array_shift($args);
1050
  $string = implode('/', $args);
1051

    
1052
  $field = field_info_field($field_name);
1053
  $instance = field_info_instance($entity_type, $field_name, $bundle_name);
1054

    
1055
  return entityreference_autocomplete_callback_get_matches($type, $field, $instance, $entity_type, $entity_id, $string);
1056
}
1057

    
1058
/**
1059
 * Return JSON based on given field, instance and string.
1060
 *
1061
 * This function can be used by other modules that wish to pass a mocked
1062
 * definition of the field on instance.
1063
 *
1064
 * @param $type
1065
 *   The widget type (i.e. 'single' or 'tags').
1066
 * @param $field
1067
 *   The field array defintion.
1068
 * @param $instance
1069
 *   The instance array defintion.
1070
 * @param $entity_type
1071
 *   The entity type.
1072
 * @param $entity_id
1073
 *   Optional; The entity ID the entity-reference field is attached to.
1074
 *   Defaults to ''.
1075
 * @param $string
1076
 *   The label of the entity to query by.
1077
 */
1078
function entityreference_autocomplete_callback_get_matches($type, $field, $instance, $entity_type, $entity_id = '', $string = '') {
1079
  $matches = array();
1080
  $prefix = '';
1081

    
1082
  $entity = NULL;
1083
  if ($entity_id !== 'NULL') {
1084
    $entity = entity_load_single($entity_type, $entity_id);
1085
    $has_view_access = (entity_access('view', $entity_type, $entity) !== FALSE);
1086
    $has_update_access = (entity_access('update', $entity_type, $entity) !== FALSE);
1087
    if (!$entity || !($has_view_access || $has_update_access)) {
1088
      return MENU_ACCESS_DENIED;
1089
    }
1090
  }
1091

    
1092
  $handler = entityreference_get_selection_handler($field, $instance, $entity_type, $entity);
1093

    
1094
  if ($type == 'tags') {
1095
    // The user enters a comma-separated list of tags.
1096
    // We only autocomplete the last tag.
1097
    $tags_typed = drupal_explode_tags($string);
1098
    $tag_last = drupal_strtolower(array_pop($tags_typed));
1099
    if (!empty($tag_last)) {
1100
      $prefix = count($tags_typed) ? implode(', ', $tags_typed) . ', ' : '';
1101
    }
1102
  }
1103
  else {
1104
    // The user enters a single tag.
1105
    $tag_last = $string;
1106
  }
1107

    
1108
  if (isset($tag_last)) {
1109
    // Get an array of matching entities.
1110
    $entity_labels = $handler->getReferencableEntities($tag_last, $instance['widget']['settings']['match_operator'], 10);
1111
    $denied_label = t(ENTITYREFERENCE_DENIED);
1112
    // Loop through the products and convert them into autocomplete output.
1113
    foreach ($entity_labels as $values) {
1114
      foreach ($values as $entity_id => $label) {
1115
        // Never autocomplete entities that aren't accessible.
1116
        if ($label == $denied_label) {
1117
          continue;
1118
        }
1119
        $key = "$label ($entity_id)";
1120
        // Strip starting/trailing white spaces, line breaks and tags.
1121
        $key = preg_replace('/\s\s+/', ' ', str_replace("\n", '', trim(decode_entities(strip_tags($key)))));
1122
        // Names containing commas or quotes must be wrapped in quotes.
1123
        if (strpos($key, ',') !== FALSE || strpos($key, '"') !== FALSE) {
1124
          $key = '"' . str_replace('"', '""', $key) . '"';
1125
        }
1126
        $matches[$prefix . $key] = '<div class="reference-autocomplete">' . $label . '</div>';
1127
      }
1128
    }
1129
  }
1130

    
1131
  drupal_json_output($matches);
1132
}
1133

    
1134
 /**
1135
 * Introspects field and instance settings, and determines the correct settings
1136
 * for the functioning of the formatter.
1137
 *
1138
 * Settings:
1139
 *   - entity_type - The entity_type being loaded.
1140
 *   - column - The name of the ref. field column that stores the entity id.
1141
 */
1142
function entityreference_field_type_settings($field) {
1143
  $settings = array(
1144
    'entity_type' => NULL,
1145
    'column' => NULL,
1146
  );
1147

    
1148
  if ($field['type'] == 'entityreference') {
1149
    $settings['entity_type'] = $field['settings']['target_type'];
1150
    $settings['column'] = 'target_id';
1151
  }
1152
  elseif ($field['type'] == 'taxonomy_term_reference') {
1153
    $settings['entity_type'] = 'taxonomy_term';
1154
    $settings['column'] = 'tid';
1155
  }
1156

    
1157
  return $settings;
1158
}
1159

    
1160
/**
1161
 * Implements hook_field_formatter_info().
1162
 */
1163
function entityreference_field_formatter_info() {
1164
  return array(
1165
    'entityreference_label' => array(
1166
      'label' => t('Label'),
1167
      'description' => t('Display the label of the referenced entities.'),
1168
      'field types' => array('entityreference'),
1169
      'settings' => array(
1170
        'link' => FALSE,
1171
        'bypass_access' => FALSE,
1172
      ),
1173
    ),
1174
    'entityreference_entity_id' => array(
1175
      'label' => t('Entity id'),
1176
      'description' => t('Display the id of the referenced entities.'),
1177
      'field types' => array('entityreference'),
1178
    ),
1179
    'entityreference_entity_view' => array(
1180
      'label' => t('Rendered entity'),
1181
      'description' => t('Display the referenced entities rendered by entity_view().'),
1182
      'field types' => array('entityreference', 'taxonomy_term_reference'),
1183
      'settings' => array(
1184
        'view_mode' => 'default',
1185
        'links' => TRUE,
1186
        'use_content_language' => TRUE,
1187
      ),
1188
    ),
1189
  );
1190
}
1191

    
1192
/**
1193
 * Implements hook_field_formatter_settings_form().
1194
 */
1195
function entityreference_field_formatter_settings_form($field, $instance, $view_mode, $form, &$form_state) {
1196
  $display = $instance['display'][$view_mode];
1197
  $settings = $display['settings'];
1198
  $field_type_settings = entityreference_field_type_settings($field);
1199
  $element = array();
1200

    
1201
  if ($display['type'] == 'entityreference_label') {
1202
    $element['bypass_access'] = array(
1203
      '#title' => t('Show entity labels regardless of user access'),
1204
      '#description' => t("All entities in the field will be shown, without checking them for access. If the 'Link' setting is also enabled, an entity which the user does not have access to view will show without a link."),
1205
      '#type' => 'checkbox',
1206
      '#default_value' => $settings['bypass_access'],
1207
    );
1208

    
1209
    $element['link'] = array(
1210
      '#title' => t('Link label to the referenced entity'),
1211
      '#type' => 'checkbox',
1212
      '#default_value' => $settings['link'],
1213
    );
1214
  }
1215

    
1216
  if ($display['type'] == 'entityreference_entity_view') {
1217
    $entity_info = entity_get_info($field_type_settings['entity_type']);
1218
    $options = array('default' => t('Default'));
1219
    if (!empty($entity_info['view modes'])) {
1220
      foreach ($entity_info['view modes'] as $view_mode => $view_mode_settings) {
1221
        $options[$view_mode] = $view_mode_settings['label'];
1222
      }
1223
    }
1224

    
1225
    $element['view_mode'] = array(
1226
      '#type' => 'select',
1227
      '#options' => $options,
1228
      '#title' => t('View mode'),
1229
      '#default_value' => $settings['view_mode'],
1230
      '#access' => count($options) > 1,
1231
    );
1232

    
1233
    $element['links'] = array(
1234
      '#type' => 'checkbox',
1235
      '#title' => t('Show links'),
1236
      '#default_value' => $settings['links'],
1237
    );
1238

    
1239
    $element['use_content_language'] = array(
1240
      '#type' => 'checkbox',
1241
      '#title' => t('Use current content language'),
1242
      '#default_value' => $settings['use_content_language'],
1243
    );
1244
  }
1245

    
1246
  return $element;
1247
}
1248

    
1249
/**
1250
 * Implements hook_field_formatter_settings_summary().
1251
 */
1252
function entityreference_field_formatter_settings_summary($field, $instance, $view_mode) {
1253
  $display = $instance['display'][$view_mode];
1254
  $settings = $display['settings'];
1255
  $field_type_settings = entityreference_field_type_settings($field);
1256

    
1257
  $summary = array();
1258

    
1259
  if ($display['type'] == 'entityreference_label') {
1260
    $summary[] = $settings['link'] ? t('Link to the referenced entity') : t('No link');
1261
    $summary[] = $settings['bypass_access'] ? t('Show labels regardless of access') : t('Respect entity access for label visibility');
1262
  }
1263

    
1264
  if ($display['type'] == 'entityreference_entity_view') {
1265
    $entity_info = entity_get_info($field_type_settings['entity_type']);
1266
    $view_mode_label = $settings['view_mode'] == 'default' ? t('Default') : $settings['view_mode'];
1267
    if (isset($entity_info['view modes'][$settings['view_mode']]['label'])) {
1268
      $view_mode_label = $entity_info['view modes'][$settings['view_mode']]['label'];
1269
    }
1270
    $summary[] = t('Rendered as @mode', array('@mode' => $view_mode_label));
1271
    $summary[] = !empty($settings['links']) ? t('Display links') : t('Do not display links');
1272
    $summary[] = !empty($settings['use_content_language']) ? t('Use current content language') : t('Use field language');
1273
  }
1274

    
1275
  return implode('<br />', $summary);
1276
}
1277

    
1278
/**
1279
 * Implements hook_field_formatter_prepare_view().
1280
 */
1281
function entityreference_field_formatter_prepare_view($entity_type, $entities, $field, $instances, $langcode, &$items, $displays) {
1282
  $field_type_settings = entityreference_field_type_settings($field);
1283
  $target_type = $field_type_settings['entity_type'];
1284
  $column = $field_type_settings['column'];
1285
  $target_ids = array();
1286

    
1287
  // Collect every possible entity attached to any of the entities.
1288
  foreach ($entities as $id => $entity) {
1289
    foreach ($items[$id] as $delta => $item) {
1290
      if (isset($item[$column])) {
1291
        $target_ids[] = $item[$column];
1292
      }
1293
    }
1294
  }
1295

    
1296
  if ($target_ids) {
1297
    $target_entities = entity_load($target_type, $target_ids);
1298
  }
1299
  else {
1300
    $target_entities = array();
1301
  }
1302

    
1303
  // Iterate through the fieldable entities again to attach the loaded data.
1304
  foreach ($entities as $id => $entity) {
1305
    $rekey = FALSE;
1306

    
1307
    foreach ($items[$id] as $delta => $item) {
1308
      // Check whether the referenced entity could be loaded.
1309
      if (isset($target_entities[$item[$column]]) && isset($target_entities[$item[$column]])) {
1310
        // Replace the instance value with the term data.
1311
        $items[$id][$delta]['entity'] = $target_entities[$item[$column]];
1312
        // Check whether the user has access to the referenced entity.
1313
        $has_view_access = (entity_access('view', $target_type, $target_entities[$item[$column]]) !== FALSE);
1314
        $has_update_access = (entity_access('update', $target_type, $target_entities[$item[$column]]) !== FALSE);
1315
        $items[$id][$delta]['access'] = ($has_view_access || $has_update_access);
1316
      }
1317
      // Otherwise, unset the instance value, since the entity does not exist.
1318
      else {
1319
        unset($items[$id][$delta]);
1320
        $rekey = TRUE;
1321
      }
1322
    }
1323

    
1324
    if ($rekey) {
1325
      // Rekey the items array.
1326
      $items[$id] = array_values($items[$id]);
1327
    }
1328
  }
1329
}
1330

    
1331
/**
1332
 * Implements hook_field_formatter_view().
1333
 */
1334
function entityreference_field_formatter_view($entity_type, $entity, $field, $instance, $langcode, $items, $display) {
1335
  $result = array();
1336
  $settings = $display['settings'];
1337
  $field_type_settings = entityreference_field_type_settings($field);
1338
  $target_type = $field_type_settings['entity_type'];
1339
  $column = $field_type_settings['column'];
1340

    
1341
  switch ($display['type']) {
1342
    case 'entityreference_label':
1343
      $handler = entityreference_get_selection_handler($field, $instance, $entity_type, $entity);
1344

    
1345
      foreach ($items as $delta => $item) {
1346
        // Skip an item that is not accessible, unless we're allowing output of
1347
        // entity labels without considering access.
1348
        if (empty($item['access']) && !$display['settings']['bypass_access']) {
1349
          continue;
1350
        }
1351

    
1352
        // Calling EntityReferenceHandler::getLabel() would make a repeated,
1353
        // wasteful call to entity_access().
1354
        $label = entity_label($field['settings']['target_type'], $item['entity']);
1355

    
1356
        // Check if the settings and access allow a link to be displayed.
1357
        $display_link = $display['settings']['link'] && $item['access'];
1358

    
1359
        $uri = NULL;
1360

    
1361
        // If the link is allowed and the entity has a uri, display a link.
1362
        if ($display_link) {
1363
          $uri = entity_uri($target_type, $item['entity']);
1364
        }
1365

    
1366
        $result[$delta] = array(
1367
          '#theme' => 'entityreference_label',
1368
          '#label' => $label,
1369
          '#item' => $item,
1370
          '#uri' => $uri,
1371
          '#settings' => array(
1372
            'display' => $display['settings'],
1373
            'field' => $field['settings'],
1374
          ),
1375
        );
1376
      }
1377
      break;
1378

    
1379
    case 'entityreference_entity_id':
1380
      foreach ($items as $delta => $item) {
1381
        // Skip an item that is not accessible.
1382
        if (empty($item['access'])) {
1383
          continue;
1384
        }
1385

    
1386
        $result[$delta] = array(
1387
          '#theme' => 'entityreference_entity_id',
1388
          '#item' => $item,
1389
          '#settings' => array(
1390
            'display' => $display['settings'],
1391
            'field' => $field['settings'],
1392
          ),
1393
        );
1394
      }
1395
      break;
1396

    
1397
    case 'entityreference_entity_view':
1398
      $target_langcode = $langcode;
1399
      if (!empty($settings['use_content_language']) && !empty($GLOBALS['language_content']->language)) {
1400
        $target_langcode = $GLOBALS['language_content']->language;
1401
      }
1402

    
1403
      foreach ($items as $delta => $item) {
1404
        // Skip an item that is not accessible.
1405
        if (empty($item['access'])) {
1406
          continue;
1407
        }
1408

    
1409
        // Protect ourselves from recursive rendering.
1410
        static $depth = 0;
1411
        $depth++;
1412
        if ($depth > 20) {
1413
          throw new EntityReferenceRecursiveRenderingException(t('Recursive rendering detected when rendering entity @entity_type(@entity_id). Aborting rendering.', array('@entity_type' => $target_type, '@entity_id' => $item[$column])));
1414
        }
1415

    
1416
        $target_entity = clone $item['entity'];
1417
        unset($target_entity->content);
1418
        $result[$delta] = entity_view($target_type, array($item[$column] => $target_entity), $settings['view_mode'], $target_langcode, FALSE);
1419

    
1420
        if (empty($settings['links']) && isset($result[$delta][$target_type][$column]['links'])) {
1421
          $result[$delta][$target_type][$item[$column]]['links']['#access'] = FALSE;
1422
        }
1423
        $depth = 0;
1424
      }
1425
      break;
1426
  }
1427

    
1428
  return $result;
1429
}
1430

    
1431
/**
1432
 * Exception thrown when the entity view renderer goes into a potentially infinite loop.
1433
 */
1434
class EntityReferenceRecursiveRenderingException extends Exception {}
1435

    
1436
/**
1437
 * Implements hook_views_api().
1438
 */
1439
function entityreference_views_api() {
1440
  return array(
1441
    'api' => 3,
1442
    'path' => drupal_get_path('module', 'entityreference') . '/views',
1443
  );
1444
}
1445

    
1446
/**
1447
 * Theme label.
1448
 *
1449
 * @ingroup themeable.
1450
 */
1451
function theme_entityreference_label($vars) {
1452
  $label = $vars['label'];
1453
  $settings = $vars['settings'];
1454
  $item = $vars['item'];
1455
  $uri = $vars['uri'];
1456

    
1457
  $output = '';
1458

    
1459
  // If the link is to be displayed and the entity has a uri, display a link.
1460
  // Note the assignment ($url = ) here is intended to be an assignment.
1461
  if ($settings['display']['link'] && isset($uri['path'])) {
1462
    $output .= l($label, $uri['path'], $uri['options']);
1463
  }
1464
  else {
1465
    $output .= check_plain($label);
1466
  }
1467

    
1468
  return $output;
1469
}
1470

    
1471
/**
1472
 * Theme entity_id
1473
 *
1474
 * @ingroup themeable.
1475
 */
1476
function theme_entityreference_entity_id($vars) {
1477
  $settings = $vars['settings'];
1478
  $item = $vars['item'];
1479

    
1480
  $output = '';
1481

    
1482
  $output = check_plain($item['target_id']);
1483

    
1484
  return $output;
1485
}