Projet

Général

Profil

Paste
Télécharger (26,7 ko) Statistiques
| Branche: | Révision:

root / drupal7 / sites / all / modules / entity / views / entity.views.inc @ a45e4bc1

1
<?php
2

    
3
/**
4
 * @file
5
 * Provide views data for modules making use of the entity CRUD API.
6
 */
7

    
8
/**
9
 * Implements hook_views_data().
10
 *
11
 * Provides Views integration for entities if they satisfy one of these
12
 * conditions:
13
 *  - hook_entity_info() specifies a 'views controller class' key.
14
 *  - hook_entity_info() specifies a 'module' key, and the module does not
15
 *    implement hook_views_data().
16
 *
17
 * @see entity_crud_hook_entity_info()
18
 * @see entity_views_table_definition()
19
 */
20
function entity_views_data() {
21
  $data = array();
22

    
23
  foreach (entity_crud_get_info() as $type => $info) {
24
    // Provide default integration with the basic controller class if we know
25
    // the module providing the entity and it does not provide views integration.
26
    if (!isset($info['views controller class'])) {
27
      $info['views controller class'] = isset($info['module']) && !module_hook($info['module'], 'views_data') ? 'EntityDefaultViewsController' : FALSE;
28
    }
29
    if ($info['views controller class']) {
30
      $controller = new $info['views controller class']($type);
31
      // Relationship data may return views data for already existing tables,
32
      // so merge results on the second level.
33
      foreach ($controller->views_data() as $table => $table_data) {
34
        $data += array($table => array());
35
        $data[$table] = array_merge($data[$table], $table_data);
36
      }
37
    }
38
  }
39

    
40
  // Add tables based upon data selection "queries" for all entity types.
41
  foreach (entity_get_info() as $type => $info) {
42
    $table = entity_views_table_definition($type);
43
    if ($table) {
44
      $data['entity_' . $type] = $table;
45
    }
46
    // Generally expose properties marked as 'entity views field'.
47
    $data['views_entity_' . $type] = array();
48
    foreach (entity_get_all_property_info($type) as $key => $property) {
49
      if (!empty($property['entity views field'])) {
50
        entity_views_field_definition($key, $property, $data['views_entity_' . $type]);
51
      }
52
    }
53
  }
54

    
55
  // Expose generally usable entity-related fields.
56
  foreach (entity_get_info() as $entity_type => $info) {
57
    if (entity_type_supports($entity_type, 'view')) {
58
      // Expose a field allowing to display the rendered entity.
59
      $data['views_entity_' . $entity_type]['rendered_entity'] = array(
60
        'title' => t('Rendered @entity-type', array('@entity-type' => $info['label'])),
61
        'help' => t('The @entity-type of the current relationship rendered using a view mode.', array('@entity-type' => $info['label'])),
62
        'field' => array(
63
          'handler' => 'entity_views_handler_field_entity',
64
          'type' => $entity_type,
65
          // The EntityFieldHandlerHelper treats the 'entity object' data
66
          // selector as special case for loading the base entity.
67
          'real field' => 'entity object',
68
        ),
69
      );
70
    }
71
  }
72

    
73
  $data['entity__global']['table']['group'] = t('Entity');
74
  $data['entity__global']['table']['join'] = array(
75
    // #global let's it appear all the time.
76
    '#global' => array(),
77
  );
78
  $data['entity__global']['entity'] = array(
79
    'title' => t('Rendered entity'),
80
    'help' => t('Displays a single chosen entity.'),
81
    'area' => array(
82
      'handler' => 'entity_views_handler_area_entity',
83
    ),
84
  );
85

    
86
  return $data;
87
}
88

    
89
/**
90
 * Helper function for getting data selection based entity Views table definitions.
91
 *
92
 * This creates extra tables for each entity type that are not associated with a
93
 * query plugin (and thus are not base tables) and just rely on the entities to
94
 * retrieve the displayed data. To obtain the entities corresponding to a
95
 * certain result set, the field handlers defined on the table use a generic
96
 * interface defined for query plugins that are based on entity handling, and
97
 * which is described in the entity_views_example_query class.
98
 *
99
 * These tables are called "data selection tables".
100
 *
101
 * Other modules providing Views integration with new query plugins that are
102
 * based on entities can then use these tables as a base for their own tables
103
 * (by directly using this method and modifying the returned table) and/or by
104
 * specifying relationships to them. The tables returned here already specify
105
 * relationships to each other wherever an entity contains a reference to
106
 * another (e.g., the node author constructs a relationship from nodes to
107
 * users).
108
 *
109
 * As filtering and other query manipulation is potentially more plugin-specific
110
 * than the display, only field handlers and relationships are provided with
111
 * these tables. By providing a add_selector_orderby() method, the query plugin
112
 * can, however, support click-sorting for the field handlers in these tables.
113
 *
114
 * For a detailed discussion see http://drupal.org/node/1266036
115
 *
116
 * For example use see the Search API views module in the Search API project:
117
 * http://drupal.org/project/search_api
118
 *
119
 * @param $type
120
 *   The entity type whose table definition should be returned.
121
 * @param $exclude
122
 *   Whether properties already exposed as 'entity views field' should be
123
 *   excluded. Defaults to TRUE, as they are available for all views tables for
124
 *   the entity type anyways.
125
 *
126
 * @return
127
 *   An array containing the data selection Views table definition for the
128
 *   entity type.
129
 *
130
 * @see entity_views_field_definition()
131
 */
132
function entity_views_table_definition($type, $exclude = TRUE) {
133
  // As other modules might want to copy these tables as a base for their own
134
  // Views integration, we statically cache the tables to save some time.
135
  $tables = &drupal_static(__FUNCTION__, array());
136

    
137
  if (!isset($tables[$type])) {
138
    // Work-a-round to fix updating, see http://drupal.org/node/1330874.
139
    // Views data might be rebuilt on update.php before the registry is rebuilt,
140
    // thus the class cannot be auto-loaded.
141
    if (!class_exists('EntityFieldHandlerHelper')) {
142
      module_load_include('inc', 'entity', 'views/handlers/entity_views_field_handler_helper');
143
    }
144

    
145
    $info = entity_get_info($type);
146
    $tables[$type]['table'] = array(
147
      'group' => $info['label'],
148
      'entity type' => $type,
149
    );
150
    foreach (entity_get_all_property_info($type) as $key => $property) {
151
      if (!$exclude || empty($property['entity views field'])) {
152
        entity_views_field_definition($key, $property, $tables[$type]);
153
      }
154
    }
155
  }
156

    
157
  return $tables[$type];
158
}
159

    
160
/**
161
 * Helper function for adding a Views field definition to data selection based Views tables.
162
 *
163
 * @param $field
164
 *   The data selector of the field to add. E.g. "title" would derive the node
165
 *   title property, "body:summary" the node body's summary.
166
 * @param array $property_info
167
 *   The property information for which to create a field definition.
168
 * @param array $table
169
 *   The table into which the definition should be inserted.
170
 * @param $title_prefix
171
 *   Internal use only.
172
 *
173
 * @see entity_views_table_definition()
174
 */
175
function entity_views_field_definition($field, array $property_info, array &$table, $title_prefix = '') {
176
  $additional = array();
177
  $additional_field = array();
178

    
179
  // Create a valid Views field identifier (no colons, etc.). Keep the original
180
  // data selector as real field though.
181
  $key = _entity_views_field_identifier($field, $table);
182
  if ($key != $field) {
183
    $additional['real field'] = $field;
184
  }
185
  $field_name = EntityFieldHandlerHelper::get_selector_field_name($field);
186

    
187
  $field_handlers = entity_views_get_field_handlers();
188

    
189
  $property_info += entity_property_info_defaults();
190
  $type = entity_property_extract_innermost_type($property_info['type']);
191
  $title = $title_prefix . $property_info['label'];
192
  if ($info = entity_get_info($type)) {
193
    $additional['relationship'] = array(
194
      'handler' => $field_handlers['relationship'],
195
      'base' => 'entity_' . $type,
196
      'base field' => $info['entity keys']['id'],
197
      'relationship field' => $field,
198
      'label' => $title,
199
    );
200
    if ($property_info['type'] != $type) {
201
      // This is a list of entities, so we should mark the relationship as such.
202
      $additional['relationship']['multiple'] = TRUE;
203
    }
204
    // Implementers of the field handlers alter hook could add handlers for
205
    // specific entity types.
206
    if (!isset($field_handlers[$type])) {
207
      $type = 'entity';
208
    }
209
  }
210
  elseif (!empty($property_info['field'])) {
211
    $type = 'field';
212
    // Views' Field API field handler needs some extra definitions to work.
213
    $additional_field['field_name'] = $field_name;
214
    $additional_field['entity_tables'] = array();
215
    $additional_field['entity type'] = $table['table']['entity type'];
216
    $additional_field['is revision'] = FALSE;
217
  }
218
  // Copied from EntityMetadataWrapper::optionsList()
219
  elseif (isset($property_info['options list']) && is_callable($property_info['options list'])) {
220
    // If this is a nested property, we need to get rid of all prefixes first.
221
    $type = 'options';
222
    $additional_field['options callback'] = array(
223
      'function' => $property_info['options list'],
224
      'info' => $property_info,
225
    );
226
  }
227
  elseif ($type == 'decimal') {
228
    $additional_field['float'] = TRUE;
229
  }
230

    
231
  if (isset($field_handlers[$type])) {
232
    $table += array($key => array());
233
    $table[$key] += array(
234
      'title' => $title,
235
      'help' => empty($property_info['description']) ? t('(No information available)') : $property_info['description'],
236
      'field' => array(),
237
    );
238
    $table[$key]['field'] += array(
239
      'handler' => $field_handlers[$type],
240
      'type' => $property_info['type'],
241
    );
242
    $table[$key] += $additional;
243
    $table[$key]['field'] += $additional_field;
244
  }
245
  if (!empty($property_info['property info'])) {
246
    foreach ($property_info['property info'] as $nested_key => $nested_property) {
247
      entity_views_field_definition($field . ':' . $nested_key, $nested_property, $table, $title . ' » ');
248
    }
249
  }
250
}
251

    
252
/**
253
 * @return array
254
 *   The handlers to use for the data selection based Views tables.
255
 *
256
 * @see hook_entity_views_field_handlers_alter()
257
 */
258
function entity_views_get_field_handlers() {
259
  $field_handlers = drupal_static(__FUNCTION__);
260
  if (!isset($field_handlers)) {
261
    // Field handlers for the entity tables, by type.
262
    $field_handlers = array(
263
      'text'         => 'entity_views_handler_field_text',
264
      'token'        => 'entity_views_handler_field_text',
265
      'integer'      => 'entity_views_handler_field_numeric',
266
      'decimal'      => 'entity_views_handler_field_numeric',
267
      'date'         => 'entity_views_handler_field_date',
268
      'duration'     => 'entity_views_handler_field_duration',
269
      'boolean'      => 'entity_views_handler_field_boolean',
270
      'uri'          => 'entity_views_handler_field_uri',
271
      'options'      => 'entity_views_handler_field_options',
272
      'field'        => 'entity_views_handler_field_field',
273
      'entity'       => 'entity_views_handler_field_entity',
274
      'relationship' => 'entity_views_handler_relationship',
275
    );
276
    drupal_alter('entity_views_field_handlers', $field_handlers);
277
  }
278
  return $field_handlers;
279
}
280

    
281
/**
282
 * Helper function for creating valid Views field identifiers out of data selectors.
283
 *
284
 * Uses $table to test whether the identifier is already used, and also
285
 * recognizes if a definition for the same field is already present and returns
286
 * that definition's identifier.
287
 *
288
 * @return string
289
 *   A valid Views field identifier that is not yet used as a key in $table.
290
 */
291
function _entity_views_field_identifier($field, array $table) {
292
  $key = $base = preg_replace('/[^a-zA-Z0-9]+/S', '_', $field);
293
  $i = 0;
294
  // The condition checks whether this sanitized field identifier is already
295
  // used for another field in this table (and whether the identifier is
296
  // "table", which can never be used).
297
  // If $table[$key] is set, the identifier is already used, but this might be
298
  // already for the same field. To test that, we need the original field name,
299
  // which is either $table[$key]['real field'], if set, or $key. If this
300
  // original field name is equal to $field, we can use that key. Otherwise, we
301
  // append numeric suffixes until we reach an unused key.
302
  while ($key == 'table' || (isset($table[$key]) && (isset($table[$key]['real field']) ? $table[$key]['real field'] : $key) != $field)) {
303
    $key = $base . '_' . ++$i;
304
  }
305
  return $key;
306
}
307

    
308
/**
309
 * Implements hook_views_plugins().
310
 */
311
function entity_views_plugins() {
312
  // Have views cache the table list for us so it gets
313
  // cleared at the appropriate times.
314
  $data = views_cache_get('entity_base_tables', TRUE);
315
  if (!empty($data->data)) {
316
    $base_tables = $data->data;
317
  }
318
  else {
319
    $base_tables = array();
320
    foreach (views_fetch_data() as $table => $data) {
321
      if (!empty($data['table']['entity type']) && !empty($data['table']['base'])) {
322
        $base_tables[] = $table;
323
      }
324
    }
325
    views_cache_set('entity_base_tables', $base_tables, TRUE);
326
  }
327
  if (!empty($base_tables)) {
328
    return array(
329
      'module' => 'entity',
330
      'row' => array(
331
        'entity' => array(
332
          'title' => t('Rendered entity'),
333
          'help' => t('Renders a single entity in a specific view mode (e.g. teaser).'),
334
          'handler' => 'entity_views_plugin_row_entity_view',
335
          'uses fields' => FALSE,
336
          'uses options' => TRUE,
337
          'type' => 'normal',
338
          'base' => $base_tables,
339
        ),
340
      ),
341
    );
342
  }
343
}
344

    
345
/**
346
 * Default controller for generating basic views integration.
347
 *
348
 * The controller tries to generate suiting views integration for the entity
349
 * based upon the schema information of its base table and the provided entity
350
 * property information.
351
 * For that it is possible to map a property name to its schema/views field
352
 * name by adding a 'schema field' key with the name of the field as value to
353
 * the property info.
354
 */
355
class EntityDefaultViewsController {
356

    
357
  protected $type, $info, $relationships;
358

    
359
  public function __construct($type) {
360
    $this->type = $type;
361
    $this->info = entity_get_info($type);
362
  }
363

    
364
  /**
365
   * Defines the result for hook_views_data().
366
   */
367
  public function views_data() {
368
    $data = array();
369
    $this->relationships = array();
370

    
371
    if (!empty($this->info['base table'])) {
372
      $table = $this->info['base table'];
373
      // Define the base group of this table. Fields that don't
374
      // have a group defined will go into this field by default.
375
      $data[$table]['table']['group']  = drupal_ucfirst($this->info['label']);
376
      $data[$table]['table']['entity type'] = $this->type;
377

    
378
      // If the plural label isn't available, use the regular label.
379
      $label = isset($this->info['plural label']) ? $this->info['plural label'] : $this->info['label'];
380
      $data[$table]['table']['base'] = array(
381
        'field' => $this->info['entity keys']['id'],
382
        'access query tag' => $this->type . '_access',
383
        'title' => drupal_ucfirst($label),
384
        'help' => isset($this->info['description']) ? $this->info['description'] : '',
385
      );
386
      $data[$table]['table']['entity type'] = $this->type;
387
      $data[$table] += $this->schema_fields();
388

    
389
      // Add in any reverse-relationships which have been determined.
390
      $data += $this->relationships;
391
    }
392
    if (!empty($this->info['revision table']) && !empty($this->info['entity keys']['revision'])) {
393
      $revision_table = $this->info['revision table'];
394

    
395
      $data[$table]['table']['default_relationship'] = array(
396
        $revision_table => array(
397
          'table' => $revision_table,
398
          'field' => $this->info['entity keys']['revision'],
399
        ),
400
      );
401

    
402
      // Define the base group of this table. Fields that don't
403
      // have a group defined will go into this field by default.
404
      $data[$revision_table]['table']['group']  = drupal_ucfirst($this->info['label']) . ' ' . t('Revisions');
405
      $data[$revision_table]['table']['entity type'] = $this->type;
406

    
407
      // If the plural label isn't available, use the regular label.
408
      $label = isset($this->info['plural label']) ? $this->info['plural label'] : $this->info['label'];
409
      $data[$revision_table]['table']['base'] = array(
410
        'field' => $this->info['entity keys']['revision'],
411
        'access query tag' => $this->type . '_access',
412
        'title' => drupal_ucfirst($label) . ' ' . t('Revisions'),
413
        'help' => (isset($this->info['description']) ? $this->info['description'] . ' ' : '') . t('Revisions'),
414
      );
415
      $data[$revision_table]['table']['entity type'] = $this->type;
416
      $data[$revision_table] += $this->schema_revision_fields();
417

    
418
      // Add in any reverse-relationships which have been determined.
419
      $data += $this->relationships;
420

    
421
      // For other base tables, explain how we join.
422
      $data[$revision_table]['table']['join'] = array(
423
        // Directly links to base table.
424
        $table => array(
425
          'left_field' => $this->info['entity keys']['revision'],
426
          'field' => $this->info['entity keys']['revision'],
427
        ),
428
      );
429
      $data[$revision_table]['table']['default_relationship'] = array(
430
        $table => array(
431
          'table' => $table,
432
          'field' => $this->info['entity keys']['id'],
433
        ),
434
      );
435
    }
436
    return $data;
437
  }
438

    
439
  /**
440
   * Try to come up with some views fields with the help of the schema and
441
   * the entity property information.
442
   */
443
  protected function schema_fields() {
444
    $schema = drupal_get_schema($this->info['base table']);
445
    $properties = entity_get_property_info($this->type) + array('properties' => array());
446
    $data = array();
447

    
448
    foreach ($properties['properties'] as $name => $property_info) {
449
      if (isset($property_info['schema field']) && isset($schema['fields'][$property_info['schema field']])) {
450
        if ($views_info = $this->map_from_schema_info($name, $schema['fields'][$property_info['schema field']], $property_info)) {
451
          $data[$name] = $views_info;
452
        }
453
      }
454
    }
455
    return $data;
456
  }
457

    
458
  /**
459
   * Try to come up with some views fields with the help of the revision schema
460
   * and the entity property information.
461
   */
462
  protected function schema_revision_fields() {
463
    $data = array();
464
    if (!empty($this->info['revision table'])) {
465
      $schema = drupal_get_schema($this->info['revision table']);
466
      $properties = entity_get_property_info($this->type) + array('properties' => array());
467

    
468
      foreach ($properties['properties'] as $name => $property_info) {
469
        if (isset($property_info['schema field']) && isset($schema['fields'][$property_info['schema field']])) {
470
          if ($views_info = $this->map_from_schema_info($name, $schema['fields'][$property_info['schema field']], $property_info)) {
471
            $data[$name] = $views_info;
472
          }
473
        }
474
      }
475
    }
476
    return $data;
477
  }
478

    
479
  /**
480
   * Comes up with views information based on the given schema and property
481
   * info.
482
   */
483
  protected function map_from_schema_info($property_name, $schema_field_info, $property_info) {
484
    $type = isset($property_info['type']) ? $property_info['type'] : 'text';
485
    $views_field_name = $property_info['schema field'];
486

    
487
    $return = array();
488

    
489
    if (!empty($schema_field_info['serialize'])) {
490
      return FALSE;
491
    }
492

    
493
    $description = array(
494
      'title' => $property_info['label'],
495
      'help' => isset($property_info['description']) ? $property_info['description'] : NULL,
496
    );
497

    
498
      // Add in relationships to related entities.
499
    if (($info = entity_get_info($type)) && !empty($info['base table'])) {
500

    
501
      // Prepare reversed relationship data.
502
      $label_lowercase = drupal_strtolower($this->info['label'][0]) . drupal_substr($this->info['label'], 1);
503
      $property_label_lowercase = drupal_strtolower($property_info['label'][0]) . drupal_substr($property_info['label'], 1);
504

    
505
      // We name the field of the first reverse-relationship just with the
506
      // base table to be backward compatible, for subsequents relationships we
507
      // append the views field name in order to get a unique name.
508
      $name = !isset($this->relationships[$info['base table']][$this->info['base table']]) ? $this->info['base table'] : $this->info['base table'] . '_' . $views_field_name;
509
      $this->relationships[$info['base table']][$name] = array(
510
        'title' => $this->info['label'],
511
        'help' => t("Associated @label via the @label's @property.", array('@label' => $label_lowercase, '@property' => $property_label_lowercase)),
512
        'relationship' => array(
513
          'label' => $this->info['label'],
514
          'handler' => $this->getRelationshipHandlerClass($this->type, $type),
515
          'base' => $this->info['base table'],
516
          'base field' => $views_field_name,
517
          'relationship field' => isset($info['entity keys']['name']) ? $info['entity keys']['name'] : $info['entity keys']['id'],
518
        ),
519
      );
520

    
521
      $return['relationship'] = array(
522
        'label' => drupal_ucfirst($info['label']),
523
        'handler' => $this->getRelationshipHandlerClass($type, $this->type),
524
        'base' => $info['base table'],
525
        'base field' => isset($info['entity keys']['name']) ? $info['entity keys']['name'] : $info['entity keys']['id'],
526
        'relationship field' => $views_field_name,
527
      );
528

    
529
      // Add in direct field/filters/sorts for the id itself too.
530
      $type = isset($info['entity keys']['name']) ? 'token' : 'integer';
531
      // Append the views-field-name to the title if it is different to the
532
      // property name.
533
      if ($property_name != $views_field_name) {
534
        $description['title'] .= ' ' . $views_field_name;
535
      }
536
    }
537

    
538
    switch ($type) {
539
      case 'token':
540
      case 'text':
541
        $return += $description + array(
542
          'field' => array(
543
            'real field' => $views_field_name,
544
            'handler' => 'views_handler_field',
545
            'click sortable' => TRUE,
546
           ),
547
          'sort' => array(
548
            'real field' => $views_field_name,
549
            'handler' => 'views_handler_sort',
550
          ),
551
          'filter' => array(
552
            'real field' => $views_field_name,
553
            'handler' => 'views_handler_filter_string',
554
          ),
555
          'argument' => array(
556
            'real field' => $views_field_name,
557
            'handler' => 'views_handler_argument_string',
558
          ),
559
        );
560
      break;
561

    
562
      case 'decimal':
563
      case 'integer':
564
        $return += $description + array(
565
          'field' => array(
566
            'real field' => $views_field_name,
567
            'handler' => 'views_handler_field_numeric',
568
            'click sortable' => TRUE,
569
            'float' => ($type == 'decimal'),
570
           ),
571
          'sort' => array(
572
            'real field' => $views_field_name,
573
            'handler' => 'views_handler_sort',
574
          ),
575
          'filter' => array(
576
            'real field' => $views_field_name,
577
            'handler' => 'views_handler_filter_numeric',
578
          ),
579
          'argument' => array(
580
            'real field' => $views_field_name,
581
            'handler' => 'views_handler_argument_numeric',
582
          ),
583
        );
584
      break;
585

    
586
      case 'date':
587
        $return += $description + array(
588
          'field' => array(
589
            'real field' => $views_field_name,
590
            'handler' => 'views_handler_field_date',
591
            'click sortable' => TRUE,
592
           ),
593
          'sort' => array(
594
            'real field' => $views_field_name,
595
            'handler' => 'views_handler_sort_date',
596
          ),
597
          'filter' => array(
598
            'real field' => $views_field_name,
599
            'handler' => 'views_handler_filter_date',
600
          ),
601
          'argument' => array(
602
            'real field' => $views_field_name,
603
            'handler' => 'views_handler_argument_date',
604
          ),
605
        );
606
      break;
607

    
608
      case 'uri':
609
        $return += $description + array(
610
          'field' => array(
611
            'real field' => $views_field_name,
612
            'handler' => 'views_handler_field_url',
613
            'click sortable' => TRUE,
614
           ),
615
          'sort' => array(
616
            'real field' => $views_field_name,
617
            'handler' => 'views_handler_sort',
618
          ),
619
          'filter' => array(
620
            'real field' => $views_field_name,
621
            'handler' => 'views_handler_filter_string',
622
          ),
623
          'argument' => array(
624
            'real field' => $views_field_name,
625
            'handler' => 'views_handler_argument_string',
626
          ),
627
        );
628
      break;
629

    
630
      case 'boolean':
631
        $return += $description + array(
632
          'field' => array(
633
            'real field' => $views_field_name,
634
            'handler' => 'views_handler_field_boolean',
635
            'click sortable' => TRUE,
636
           ),
637
          'sort' => array(
638
            'real field' => $views_field_name,
639
            'handler' => 'views_handler_sort',
640
          ),
641
          'filter' => array(
642
            'real field' => $views_field_name,
643
            'handler' => 'views_handler_filter_boolean_operator',
644
          ),
645
          'argument' => array(
646
            'real field' => $views_field_name,
647
            'handler' => 'views_handler_argument_string',
648
          ),
649
        );
650
      break;
651
    }
652

    
653
    // If there is an options list callback, add to the filter and field.
654
    if (isset($return['filter']) && !empty($property_info['options list'])) {
655
      $return['filter']['handler'] = 'views_handler_filter_in_operator';
656
      $return['filter']['options callback'] = array('EntityDefaultViewsController', 'optionsListCallback');
657
      $return['filter']['options arguments'] = array($this->type, $property_name, 'view');
658
    }
659
    // @todo: This class_exists is needed until views 3.2.
660
    if (isset($return['field']) && !empty($property_info['options list']) && class_exists('views_handler_field_machine_name')) {
661
      $return['field']['handler'] = 'views_handler_field_machine_name';
662
      $return['field']['options callback'] = array('EntityDefaultViewsController', 'optionsListCallback');
663
      $return['field']['options arguments'] = array($this->type, $property_name, 'view');
664
    }
665
    return $return;
666
  }
667

    
668
  /**
669
   * Determines the handler to use for a relationship to an entity type.
670
   *
671
   * @param $entity_type
672
   *   The entity type to join to.
673
   * @param $left_type
674
   *   The data type from which to join.
675
   */
676
  function getRelationshipHandlerClass($entity_type, $left_type) {
677
    // Look for an entity type which is used as bundle for the given entity
678
    // type. If there is one, allow filtering the relation by bundle by using
679
    // our own handler.
680
    foreach (entity_get_info() as $type => $info) {
681
      // In case we already join from the bundle entity we do not need to filter
682
      // by bundle entity any more, so we stay with the general handler.
683
      if (!empty($info['bundle of']) && $info['bundle of'] == $entity_type && $type != $left_type) {
684
        return 'entity_views_handler_relationship_by_bundle';
685
      }
686
    }
687
    return 'views_handler_relationship';
688
  }
689

    
690
  /**
691
   * A callback returning property options, suitable to be used as views options callback.
692
   */
693
  public static function optionsListCallback($type, $selector, $op = 'view') {
694
    $wrapper = entity_metadata_wrapper($type, NULL);
695
    $parts = explode(':', $selector);
696
    foreach ($parts as $part) {
697
      $wrapper = $wrapper->get($part);
698
    }
699
    return $wrapper->optionsList($op);
700
  }
701
}