Projet

Général

Profil

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

root / drupal7 / sites / all / modules / entityreference / entityreference.module @ 59ae487e

1
<?php
2

    
3
/**
4
 * @file
5
 * Entityreference primary module file.
6
 */
7

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

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

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

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

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

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

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

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

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

    
132
  return $items;
133
}
134

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

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

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

    
158
  if (!isset($object_cache[$identifier])) {
159
    $object_cache[$identifier] = array();
160

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

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

    
175
      $object_cache[$identifier][] = _entityreference_get_behavior_handler($behavior);
176
    }
177
  }
178

    
179
  return $object_cache[$identifier];
180
}
181

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

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

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

    
199
  return $object_cache[$behavior];
200
}
201

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
503
  return $form;
504
}
505

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

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

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

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

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

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

    
560
  unset($form_state['values']['field']['settings']['handler_submit']);
561
}
562

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

    
584
  return $form;
585
}
586

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
761
  return $widgets;
762
}
763

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

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

    
781
  $form = array();
782

    
783
  if ($widget['type'] == 'entityreference_autocomplete' || $widget['type'] == 'entityreference_autocomplete_tags') {
784
    $form['match_operator'] = array(
785
      '#type' => 'select',
786
      '#title' => t('Autocomplete matching'),
787
      '#default_value' => $settings['match_operator'],
788
      '#options' => array(
789
        'STARTS_WITH' => t('Starts with'),
790
        'CONTAINS' => t('Contains'),
791
      ),
792
      '#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.'),
793
    );
794
    $form['size'] = array(
795
      '#type' => 'textfield',
796
      '#title' => t('Size of textfield'),
797
      '#default_value' => $settings['size'],
798
      '#element_validate' => array('_element_validate_integer_positive'),
799
      '#required' => TRUE,
800
    );
801
  }
802

    
803
  return $form;
804
}
805

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

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

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

    
824
  return count($return) == 1 ? reset($return) : $return;
825
}
826

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

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

    
848
  if ($instance['widget']['type'] == 'entityreference_autocomplete' || $instance['widget']['type'] == 'entityreference_autocomplete_tags') {
849

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

    
861
    $entity_ids = array();
862
    $entity_labels = array();
863

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

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

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

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

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

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

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

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

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

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

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

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

    
1050
  $field = field_info_field($field_name);
1051
  $instance = field_info_instance($entity_type, $field_name, $bundle_name);
1052

    
1053
  return entityreference_autocomplete_callback_get_matches($type, $field, $instance, $entity_type, $entity_id, $string);
1054
}
1055

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

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

    
1090
  $handler = entityreference_get_selection_handler($field, $instance, $entity_type, $entity);
1091

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

    
1106
  if (isset($tag_last)) {
1107
    // Get an array of matching entities.
1108
    $entity_labels = $handler->getReferencableEntities($tag_last, $instance['widget']['settings']['match_operator'], 10);
1109

    
1110
    // Loop through the products and convert them into autocomplete output.
1111
    foreach ($entity_labels as $values) {
1112
      foreach ($values as $entity_id => $label) {
1113
        $key = "$label ($entity_id)";
1114
        // Strip starting/trailing white spaces, line breaks and tags.
1115
        $key = preg_replace('/\s\s+/', ' ', str_replace("\n", '', trim(decode_entities(strip_tags($key)))));
1116
        // Names containing commas or quotes must be wrapped in quotes.
1117
        if (strpos($key, ',') !== FALSE || strpos($key, '"') !== FALSE) {
1118
          $key = '"' . str_replace('"', '""', $key) . '"';
1119
        }
1120
        $matches[$prefix . $key] = '<div class="reference-autocomplete">' . $label . '</div>';
1121
      }
1122
    }
1123
  }
1124

    
1125
  drupal_json_output($matches);
1126
}
1127

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

    
1142
  if ($field['type'] == 'entityreference') {
1143
    $settings['entity_type'] = $field['settings']['target_type'];
1144
    $settings['column'] = 'target_id';
1145
  }
1146
  elseif ($field['type'] == 'taxonomy_term_reference') {
1147
    $settings['entity_type'] = 'taxonomy_term';
1148
    $settings['column'] = 'tid';
1149
  }
1150

    
1151
  return $settings;
1152
}
1153

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

    
1186
/**
1187
 * Implements hook_field_formatter_settings_form().
1188
 */
1189
function entityreference_field_formatter_settings_form($field, $instance, $view_mode, $form, &$form_state) {
1190
  $display = $instance['display'][$view_mode];
1191
  $settings = $display['settings'];
1192
  $field_type_settings = entityreference_field_type_settings($field);
1193
  $element = array();
1194

    
1195
  if ($display['type'] == 'entityreference_label') {
1196
    $element['bypass_access'] = array(
1197
      '#title' => t('Show entity labels regardless of user access'),
1198
      '#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."),
1199
      '#type' => 'checkbox',
1200
      '#default_value' => $settings['bypass_access'],
1201
    );
1202

    
1203
    $element['link'] = array(
1204
      '#title' => t('Link label to the referenced entity'),
1205
      '#type' => 'checkbox',
1206
      '#default_value' => $settings['link'],
1207
    );
1208
  }
1209

    
1210
  if ($display['type'] == 'entityreference_entity_view') {
1211
    $entity_info = entity_get_info($field_type_settings['entity_type']);
1212
    $options = array('default' => t('Default'));
1213
    if (!empty($entity_info['view modes'])) {
1214
      foreach ($entity_info['view modes'] as $view_mode => $view_mode_settings) {
1215
        $options[$view_mode] = $view_mode_settings['label'];
1216
      }
1217
    }
1218

    
1219
    $element['view_mode'] = array(
1220
      '#type' => 'select',
1221
      '#options' => $options,
1222
      '#title' => t('View mode'),
1223
      '#default_value' => $settings['view_mode'],
1224
      '#access' => count($options) > 1,
1225
    );
1226

    
1227
    $element['links'] = array(
1228
      '#type' => 'checkbox',
1229
      '#title' => t('Show links'),
1230
      '#default_value' => $settings['links'],
1231
    );
1232

    
1233
    $element['use_content_language'] = array(
1234
      '#type' => 'checkbox',
1235
      '#title' => t('Use current content language'),
1236
      '#default_value' => $settings['use_content_language'],
1237
    );
1238
  }
1239

    
1240
  return $element;
1241
}
1242

    
1243
/**
1244
 * Implements hook_field_formatter_settings_summary().
1245
 */
1246
function entityreference_field_formatter_settings_summary($field, $instance, $view_mode) {
1247
  $display = $instance['display'][$view_mode];
1248
  $settings = $display['settings'];
1249
  $field_type_settings = entityreference_field_type_settings($field);
1250

    
1251
  $summary = array();
1252

    
1253
  if ($display['type'] == 'entityreference_label') {
1254
    $summary[] = $settings['link'] ? t('Link to the referenced entity') : t('No link');
1255
    $summary[] = $settings['bypass_access'] ? t('Show labels regardless of access') : t('Respect entity access for label visibility');
1256
  }
1257

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

    
1269
  return implode('<br />', $summary);
1270
}
1271

    
1272
/**
1273
 * Implements hook_field_formatter_prepare_view().
1274
 */
1275
function entityreference_field_formatter_prepare_view($entity_type, $entities, $field, $instances, $langcode, &$items, $displays) {
1276
  $field_type_settings = entityreference_field_type_settings($field);
1277
  $target_type = $field_type_settings['entity_type'];
1278
  $column = $field_type_settings['column'];
1279
  $target_ids = array();
1280

    
1281
  // Collect every possible entity attached to any of the entities.
1282
  foreach ($entities as $id => $entity) {
1283
    foreach ($items[$id] as $delta => $item) {
1284
      if (isset($item[$column])) {
1285
        $target_ids[] = $item[$column];
1286
      }
1287
    }
1288
  }
1289

    
1290
  if ($target_ids) {
1291
    $target_entities = entity_load($target_type, $target_ids);
1292
  }
1293
  else {
1294
    $target_entities = array();
1295
  }
1296

    
1297
  // Iterate through the fieldable entities again to attach the loaded data.
1298
  foreach ($entities as $id => $entity) {
1299
    $rekey = FALSE;
1300

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

    
1318
    if ($rekey) {
1319
      // Rekey the items array.
1320
      $items[$id] = array_values($items[$id]);
1321
    }
1322
  }
1323
}
1324

    
1325
/**
1326
 * Implements hook_field_formatter_view().
1327
 */
1328
function entityreference_field_formatter_view($entity_type, $entity, $field, $instance, $langcode, $items, $display) {
1329
  $result = array();
1330
  $settings = $display['settings'];
1331
  $field_type_settings = entityreference_field_type_settings($field);
1332
  $target_type = $field_type_settings['entity_type'];
1333
  $column = $field_type_settings['column'];
1334

    
1335
  switch ($display['type']) {
1336
    case 'entityreference_label':
1337
      $handler = entityreference_get_selection_handler($field, $instance, $entity_type, $entity);
1338

    
1339
      foreach ($items as $delta => $item) {
1340
        // Skip an item that is not accessible, unless we're allowing output of
1341
        // entity labels without considering access.
1342
        if (empty($item['access']) && !$display['settings']['bypass_access']) {
1343
          continue;
1344
        }
1345

    
1346
        // Calling EntityReferenceHandler::getLabel() would make a repeated,
1347
        // wasteful call to entity_access().
1348
        $label = entity_label($field['settings']['target_type'], $item['entity']);
1349

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

    
1353
        $uri = NULL;
1354

    
1355
        // If the link is allowed and the entity has a uri, display a link.
1356
        if ($display_link) {
1357
          $uri = entity_uri($target_type, $item['entity']);
1358
        }
1359

    
1360
        $result[$delta] = array(
1361
          '#theme' => 'entityreference_label',
1362
          '#label' => $label,
1363
          '#item' => $item,
1364
          '#uri' => $uri,
1365
          '#settings' => array(
1366
            'display' => $display['settings'],
1367
            'field' => $field['settings'],
1368
          ),
1369
        );
1370
      }
1371
      break;
1372

    
1373
    case 'entityreference_entity_id':
1374
      foreach ($items as $delta => $item) {
1375
        // Skip an item that is not accessible.
1376
        if (empty($item['access'])) {
1377
          continue;
1378
        }
1379

    
1380
        $result[$delta] = array(
1381
          '#theme' => 'entityreference_entity_id',
1382
          '#item' => $item,
1383
          '#settings' => array(
1384
            'display' => $display['settings'],
1385
            'field' => $field['settings'],
1386
          ),
1387
        );
1388
      }
1389
      break;
1390

    
1391
    case 'entityreference_entity_view':
1392
      $target_langcode = $langcode;
1393
      if (!empty($settings['use_content_language']) && !empty($GLOBALS['language_content']->language)) {
1394
        $target_langcode = $GLOBALS['language_content']->language;
1395
      }
1396

    
1397
      foreach ($items as $delta => $item) {
1398
        // Skip an item that is not accessible.
1399
        if (empty($item['access'])) {
1400
          continue;
1401
        }
1402

    
1403
        // Protect ourselves from recursive rendering.
1404
        static $depth = 0;
1405
        $depth++;
1406
        if ($depth > 20) {
1407
          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])));
1408
        }
1409

    
1410
        $target_entity = clone $item['entity'];
1411
        unset($target_entity->content);
1412
        $result[$delta] = entity_view($target_type, array($item[$column] => $target_entity), $settings['view_mode'], $target_langcode, FALSE);
1413

    
1414
        if (empty($settings['links']) && isset($result[$delta][$target_type][$column]['links'])) {
1415
          $result[$delta][$target_type][$item[$column]]['links']['#access'] = FALSE;
1416
        }
1417
        $depth = 0;
1418
      }
1419
      break;
1420
  }
1421

    
1422
  return $result;
1423
}
1424

    
1425
/**
1426
 * Exception thrown when the entity view renderer goes into a potentially infinite loop.
1427
 */
1428
class EntityReferenceRecursiveRenderingException extends Exception {}
1429

    
1430
/**
1431
 * Implements hook_views_api().
1432
 */
1433
function entityreference_views_api() {
1434
  return array(
1435
    'api' => 3,
1436
    'path' => drupal_get_path('module', 'entityreference') . '/views',
1437
  );
1438
}
1439

    
1440
/**
1441
 * Theme label.
1442
 *
1443
 * @ingroup themeable.
1444
 */
1445
function theme_entityreference_label($vars) {
1446
  $label = $vars['label'];
1447
  $settings = $vars['settings'];
1448
  $item = $vars['item'];
1449
  $uri = $vars['uri'];
1450

    
1451
  $output = '';
1452

    
1453
  // If the link is to be displayed and the entity has a uri, display a link.
1454
  // Note the assignment ($url = ) here is intended to be an assignment.
1455
  if ($settings['display']['link'] && isset($uri['path'])) {
1456
    $output .= l($label, $uri['path'], $uri['options']);
1457
  }
1458
  else {
1459
    $output .= check_plain($label);
1460
  }
1461

    
1462
  return $output;
1463
}
1464

    
1465
/**
1466
 * Theme entity_id
1467
 *
1468
 * @ingroup themeable.
1469
 */
1470
function theme_entityreference_entity_id($vars) {
1471
  $settings = $vars['settings'];
1472
  $item = $vars['item'];
1473

    
1474
  $output = '';
1475

    
1476
  $output = check_plain($item['target_id']);
1477

    
1478
  return $output;
1479
}