entity.views.inc 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723
  1. <?php
  2. /**
  3. * @file
  4. * Provide views data for modules making use of the entity CRUD API.
  5. */
  6. /**
  7. * Implements hook_views_data().
  8. *
  9. * Provides Views integration for entities if they satisfy one of these
  10. * conditions:
  11. * - hook_entity_info() specifies a 'views controller class' key.
  12. * - hook_entity_info() specifies a 'module' key, and the module does not
  13. * implement hook_views_data().
  14. *
  15. * @see entity_crud_hook_entity_info()
  16. * @see entity_views_table_definition()
  17. */
  18. function entity_views_data() {
  19. $data = array();
  20. foreach (entity_crud_get_info() as $type => $info) {
  21. // Provide default integration with the basic controller class if we know
  22. // the module providing the entity and it does not provide views integration.
  23. if (!isset($info['views controller class'])) {
  24. $info['views controller class'] = isset($info['module']) && !module_hook($info['module'], 'views_data') ? 'EntityDefaultViewsController' : FALSE;
  25. }
  26. if ($info['views controller class']) {
  27. $controller = new $info['views controller class']($type);
  28. // Relationship data may return views data for already existing tables,
  29. // so merge results on the second level.
  30. foreach ($controller->views_data() as $table => $table_data) {
  31. $data += array($table => array());
  32. $data[$table] = array_merge($data[$table], $table_data);
  33. }
  34. }
  35. }
  36. // Add tables based upon data selection "queries" for all entity types.
  37. foreach (entity_get_info() as $type => $info) {
  38. $table = entity_views_table_definition($type);
  39. if ($table) {
  40. $data['entity_' . $type] = $table;
  41. }
  42. // Generally expose properties marked as 'entity views field'.
  43. $data['views_entity_' . $type] = array();
  44. foreach (entity_get_all_property_info($type) as $key => $property) {
  45. if (!empty($property['entity views field'])) {
  46. entity_views_field_definition($key, $property, $data['views_entity_' . $type]);
  47. }
  48. }
  49. }
  50. // Expose generally usable entity-related fields.
  51. foreach (entity_get_info() as $entity_type => $info) {
  52. if (entity_type_supports($entity_type, 'view')) {
  53. // Expose a field allowing to display the rendered entity.
  54. $data['views_entity_' . $entity_type]['rendered_entity'] = array(
  55. 'title' => t('Rendered @entity-type', array('@entity-type' => $info['label'])),
  56. 'help' => t('The @entity-type of the current relationship rendered using a view mode.', array('@entity-type' => $info['label'])),
  57. 'field' => array(
  58. 'handler' => 'entity_views_handler_field_entity',
  59. 'type' => $entity_type,
  60. // The EntityFieldHandlerHelper treats the 'entity object' data
  61. // selector as special case for loading the base entity.
  62. 'real field' => 'entity object',
  63. ),
  64. );
  65. }
  66. }
  67. $data['entity__global']['table']['group'] = t('Entity');
  68. $data['entity__global']['table']['join'] = array(
  69. // #global let's it appear all the time.
  70. '#global' => array(),
  71. );
  72. $data['entity__global']['entity'] = array(
  73. 'title' => t('Rendered entity'),
  74. 'help' => t('Displays a single chosen entity.'),
  75. 'area' => array(
  76. 'handler' => 'entity_views_handler_area_entity',
  77. ),
  78. );
  79. return $data;
  80. }
  81. /**
  82. * Helper function for getting data selection based entity Views table definitions.
  83. *
  84. * This creates extra tables for each entity type that are not associated with a
  85. * query plugin (and thus are not base tables) and just rely on the entities to
  86. * retrieve the displayed data. To obtain the entities corresponding to a
  87. * certain result set, the field handlers defined on the table use a generic
  88. * interface defined for query plugins that are based on entity handling, and
  89. * which is described in the entity_views_example_query class.
  90. *
  91. * These tables are called "data selection tables".
  92. *
  93. * Other modules providing Views integration with new query plugins that are
  94. * based on entities can then use these tables as a base for their own tables
  95. * (by directly using this method and modifying the returned table) and/or by
  96. * specifying relationships to them. The tables returned here already specify
  97. * relationships to each other wherever an entity contains a reference to
  98. * another (e.g., the node author constructs a relationship from nodes to
  99. * users).
  100. *
  101. * As filtering and other query manipulation is potentially more plugin-specific
  102. * than the display, only field handlers and relationships are provided with
  103. * these tables. By providing a add_selector_orderby() method, the query plugin
  104. * can, however, support click-sorting for the field handlers in these tables.
  105. *
  106. * For a detailed discussion see http://drupal.org/node/1266036
  107. *
  108. * For example use see the Search API views module in the Search API project:
  109. * http://drupal.org/project/search_api
  110. *
  111. * @param $type
  112. * The entity type whose table definition should be returned.
  113. * @param $exclude
  114. * Whether properties already exposed as 'entity views field' should be
  115. * excluded. Defaults to TRUE, as they are available for all views tables for
  116. * the entity type anyways.
  117. *
  118. * @return
  119. * An array containing the data selection Views table definition for the
  120. * entity type.
  121. *
  122. * @see entity_views_field_definition()
  123. */
  124. function entity_views_table_definition($type, $exclude = TRUE) {
  125. // As other modules might want to copy these tables as a base for their own
  126. // Views integration, we statically cache the tables to save some time.
  127. $tables = &drupal_static(__FUNCTION__, array());
  128. if (!isset($tables[$type])) {
  129. // Work-a-round to fix updating, see http://drupal.org/node/1330874.
  130. // Views data might be rebuilt on update.php before the registry is rebuilt,
  131. // thus the class cannot be auto-loaded.
  132. if (!class_exists('EntityFieldHandlerHelper')) {
  133. module_load_include('inc', 'entity', 'views/handlers/entity_views_field_handler_helper');
  134. }
  135. $info = entity_get_info($type);
  136. $tables[$type]['table'] = array(
  137. 'group' => $info['label'],
  138. 'entity type' => $type,
  139. );
  140. foreach (entity_get_all_property_info($type) as $key => $property) {
  141. if (!$exclude || empty($property['entity views field'])) {
  142. entity_views_field_definition($key, $property, $tables[$type]);
  143. }
  144. }
  145. }
  146. return $tables[$type];
  147. }
  148. /**
  149. * Helper function for adding a Views field definition to data selection based Views tables.
  150. *
  151. * @param $field
  152. * The data selector of the field to add. E.g. "title" would derive the node
  153. * title property, "body:summary" the node body's summary.
  154. * @param array $property_info
  155. * The property information for which to create a field definition.
  156. * @param array $table
  157. * The table into which the definition should be inserted.
  158. * @param $title_prefix
  159. * Internal use only.
  160. *
  161. * @see entity_views_table_definition()
  162. */
  163. function entity_views_field_definition($field, array $property_info, array &$table, $title_prefix = '') {
  164. $additional = array();
  165. $additional_field = array();
  166. // Create a valid Views field identifier (no colons, etc.). Keep the original
  167. // data selector as real field though.
  168. $key = _entity_views_field_identifier($field, $table);
  169. if ($key != $field) {
  170. $additional['real field'] = $field;
  171. }
  172. $field_name = EntityFieldHandlerHelper::get_selector_field_name($field);
  173. $field_handlers = entity_views_get_field_handlers();
  174. $property_info += entity_property_info_defaults();
  175. $type = entity_property_extract_innermost_type($property_info['type']);
  176. $title = $title_prefix . $property_info['label'];
  177. if ($info = entity_get_info($type)) {
  178. $additional['relationship'] = array(
  179. 'handler' => $field_handlers['relationship'],
  180. 'base' => 'entity_' . $type,
  181. 'base field' => $info['entity keys']['id'],
  182. 'relationship field' => $field,
  183. 'label' => $title,
  184. );
  185. if ($property_info['type'] != $type) {
  186. // This is a list of entities, so we should mark the relationship as such.
  187. $additional['relationship']['multiple'] = TRUE;
  188. }
  189. // Implementers of the field handlers alter hook could add handlers for
  190. // specific entity types.
  191. if (!isset($field_handlers[$type])) {
  192. $type = 'entity';
  193. }
  194. }
  195. elseif (!empty($property_info['field'])) {
  196. $type = 'field';
  197. // Views' Field API field handler needs some extra definitions to work.
  198. $additional_field['field_name'] = $field_name;
  199. $additional_field['entity_tables'] = array();
  200. $additional_field['entity type'] = $table['table']['entity type'];
  201. $additional_field['is revision'] = FALSE;
  202. }
  203. // Copied from EntityMetadataWrapper::optionsList()
  204. elseif (isset($property_info['options list']) && is_callable($property_info['options list'])) {
  205. // If this is a nested property, we need to get rid of all prefixes first.
  206. $type = 'options';
  207. $additional_field['options callback'] = array(
  208. 'function' => $property_info['options list'],
  209. 'info' => $property_info,
  210. );
  211. }
  212. elseif ($type == 'decimal') {
  213. $additional_field['float'] = TRUE;
  214. }
  215. if (isset($field_handlers[$type])) {
  216. $table += array($key => array());
  217. $table[$key] += array(
  218. 'title' => $title,
  219. 'help' => empty($property_info['description']) ? t('(No information available)') : $property_info['description'],
  220. 'field' => array(),
  221. );
  222. $table[$key]['field'] += array(
  223. 'handler' => $field_handlers[$type],
  224. 'type' => $property_info['type'],
  225. );
  226. $table[$key] += $additional;
  227. $table[$key]['field'] += $additional_field;
  228. }
  229. if (!empty($property_info['property info'])) {
  230. foreach ($property_info['property info'] as $nested_key => $nested_property) {
  231. entity_views_field_definition($field . ':' . $nested_key, $nested_property, $table, $title . ' » ');
  232. }
  233. }
  234. }
  235. /**
  236. * @return array
  237. * The handlers to use for the data selection based Views tables.
  238. *
  239. * @see hook_entity_views_field_handlers_alter()
  240. */
  241. function entity_views_get_field_handlers() {
  242. $field_handlers = drupal_static(__FUNCTION__);
  243. if (!isset($field_handlers)) {
  244. // Field handlers for the entity tables, by type.
  245. $field_handlers = array(
  246. 'text' => 'entity_views_handler_field_text',
  247. 'token' => 'entity_views_handler_field_text',
  248. 'integer' => 'entity_views_handler_field_numeric',
  249. 'decimal' => 'entity_views_handler_field_numeric',
  250. 'date' => 'entity_views_handler_field_date',
  251. 'duration' => 'entity_views_handler_field_duration',
  252. 'boolean' => 'entity_views_handler_field_boolean',
  253. 'uri' => 'entity_views_handler_field_uri',
  254. 'options' => 'entity_views_handler_field_options',
  255. 'field' => 'entity_views_handler_field_field',
  256. 'entity' => 'entity_views_handler_field_entity',
  257. 'relationship' => 'entity_views_handler_relationship',
  258. );
  259. drupal_alter('entity_views_field_handlers', $field_handlers);
  260. }
  261. return $field_handlers;
  262. }
  263. /**
  264. * Helper function for creating valid Views field identifiers out of data selectors.
  265. *
  266. * Uses $table to test whether the identifier is already used, and also
  267. * recognizes if a definition for the same field is already present and returns
  268. * that definition's identifier.
  269. *
  270. * @return string
  271. * A valid Views field identifier that is not yet used as a key in $table.
  272. */
  273. function _entity_views_field_identifier($field, array $table) {
  274. $key = $base = preg_replace('/[^a-zA-Z0-9]+/S', '_', $field);
  275. $i = 0;
  276. // The condition checks whether this sanitized field identifier is already
  277. // used for another field in this table (and whether the identifier is
  278. // "table", which can never be used).
  279. // If $table[$key] is set, the identifier is already used, but this might be
  280. // already for the same field. To test that, we need the original field name,
  281. // which is either $table[$key]['real field'], if set, or $key. If this
  282. // original field name is equal to $field, we can use that key. Otherwise, we
  283. // append numeric suffixes until we reach an unused key.
  284. while ($key == 'table' || (isset($table[$key]) && (isset($table[$key]['real field']) ? $table[$key]['real field'] : $key) != $field)) {
  285. $key = $base . '_' . ++$i;
  286. }
  287. return $key;
  288. }
  289. /**
  290. * Implements hook_views_plugins().
  291. */
  292. function entity_views_plugins() {
  293. // Have views cache the table list for us so it gets
  294. // cleared at the appropriate times.
  295. $data = views_cache_get('entity_base_tables', TRUE);
  296. if (!empty($data->data)) {
  297. $base_tables = $data->data;
  298. }
  299. else {
  300. $base_tables = array();
  301. foreach (views_fetch_data() as $table => $data) {
  302. if (!empty($data['table']['entity type']) && !empty($data['table']['base'])) {
  303. $base_tables[] = $table;
  304. }
  305. }
  306. views_cache_set('entity_base_tables', $base_tables, TRUE);
  307. }
  308. if (!empty($base_tables)) {
  309. return array(
  310. 'module' => 'entity',
  311. 'row' => array(
  312. 'entity' => array(
  313. 'title' => t('Rendered entity'),
  314. 'help' => t('Renders a single entity in a specific view mode (e.g. teaser).'),
  315. 'handler' => 'entity_views_plugin_row_entity_view',
  316. 'uses fields' => FALSE,
  317. 'uses options' => TRUE,
  318. 'type' => 'normal',
  319. 'base' => $base_tables,
  320. ),
  321. ),
  322. );
  323. }
  324. }
  325. /**
  326. * Default controller for generating basic views integration.
  327. *
  328. * The controller tries to generate suiting views integration for the entity
  329. * based upon the schema information of its base table and the provided entity
  330. * property information.
  331. * For that it is possible to map a property name to its schema/views field
  332. * name by adding a 'schema field' key with the name of the field as value to
  333. * the property info.
  334. */
  335. class EntityDefaultViewsController {
  336. protected $type, $info, $relationships;
  337. public function __construct($type) {
  338. $this->type = $type;
  339. $this->info = entity_get_info($type);
  340. }
  341. /**
  342. * Defines the result for hook_views_data().
  343. */
  344. public function views_data() {
  345. $data = array();
  346. $this->relationships = array();
  347. if (!empty($this->info['base table'])) {
  348. $table = $this->info['base table'];
  349. // Define the base group of this table. Fields that don't
  350. // have a group defined will go into this field by default.
  351. $data[$table]['table']['group'] = drupal_ucfirst($this->info['label']);
  352. $data[$table]['table']['entity type'] = $this->type;
  353. // If the plural label isn't available, use the regular label.
  354. $label = isset($this->info['plural label']) ? $this->info['plural label'] : $this->info['label'];
  355. $data[$table]['table']['base'] = array(
  356. 'field' => $this->info['entity keys']['id'],
  357. 'access query tag' => $this->type . '_access',
  358. 'title' => drupal_ucfirst($label),
  359. 'help' => isset($this->info['description']) ? $this->info['description'] : '',
  360. );
  361. $data[$table]['table']['entity type'] = $this->type;
  362. $data[$table] += $this->schema_fields();
  363. // Add in any reverse-relationships which have been determined.
  364. $data += $this->relationships;
  365. }
  366. if (!empty($this->info['revision table']) && !empty($this->info['entity keys']['revision'])) {
  367. $revision_table = $this->info['revision table'];
  368. $data[$table]['table']['default_relationship'] = array(
  369. $revision_table => array(
  370. 'table' => $revision_table,
  371. 'field' => $this->info['entity keys']['revision'],
  372. ),
  373. );
  374. // Define the base group of this table. Fields that don't
  375. // have a group defined will go into this field by default.
  376. $data[$revision_table]['table']['group'] = drupal_ucfirst($this->info['label']) . ' ' . t('Revisions');
  377. $data[$revision_table]['table']['entity type'] = $this->type;
  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[$revision_table]['table']['base'] = array(
  381. 'field' => $this->info['entity keys']['revision'],
  382. 'access query tag' => $this->type . '_access',
  383. 'title' => drupal_ucfirst($label) . ' ' . t('Revisions'),
  384. 'help' => (isset($this->info['description']) ? $this->info['description'] . ' ' : '') . t('Revisions'),
  385. );
  386. $data[$revision_table]['table']['entity type'] = $this->type;
  387. $data[$revision_table] += $this->schema_revision_fields();
  388. // Add in any reverse-relationships which have been determined.
  389. $data += $this->relationships;
  390. // For other base tables, explain how we join.
  391. $data[$revision_table]['table']['join'] = array(
  392. // Directly links to base table.
  393. $table => array(
  394. 'left_field' => $this->info['entity keys']['revision'],
  395. 'field' => $this->info['entity keys']['revision'],
  396. ),
  397. );
  398. $data[$revision_table]['table']['default_relationship'] = array(
  399. $table => array(
  400. 'table' => $table,
  401. 'field' => $this->info['entity keys']['id'],
  402. ),
  403. );
  404. }
  405. return $data;
  406. }
  407. /**
  408. * Try to come up with some views fields with the help of the schema and
  409. * the entity property information.
  410. */
  411. protected function schema_fields() {
  412. $schema = drupal_get_schema($this->info['base table']);
  413. $properties = entity_get_property_info($this->type) + array('properties' => array());
  414. $data = array();
  415. foreach ($properties['properties'] as $name => $property_info) {
  416. if (isset($property_info['schema field']) && isset($schema['fields'][$property_info['schema field']])) {
  417. if ($views_info = $this->map_from_schema_info($name, $schema['fields'][$property_info['schema field']], $property_info)) {
  418. $data[$name] = $views_info;
  419. }
  420. }
  421. }
  422. return $data;
  423. }
  424. /**
  425. * Try to come up with some views fields with the help of the revision schema
  426. * and the entity property information.
  427. */
  428. protected function schema_revision_fields() {
  429. $data = array();
  430. if (!empty($this->info['revision table'])) {
  431. $schema = drupal_get_schema($this->info['revision table']);
  432. $properties = entity_get_property_info($this->type) + array('properties' => array());
  433. foreach ($properties['properties'] as $name => $property_info) {
  434. if (isset($property_info['schema field']) && isset($schema['fields'][$property_info['schema field']])) {
  435. if ($views_info = $this->map_from_schema_info($name, $schema['fields'][$property_info['schema field']], $property_info)) {
  436. $data[$name] = $views_info;
  437. }
  438. }
  439. }
  440. }
  441. return $data;
  442. }
  443. /**
  444. * Comes up with views information based on the given schema and property
  445. * info.
  446. */
  447. protected function map_from_schema_info($property_name, $schema_field_info, $property_info) {
  448. $type = isset($property_info['type']) ? $property_info['type'] : 'text';
  449. $views_field_name = $property_info['schema field'];
  450. $return = array();
  451. if (!empty($schema_field_info['serialize'])) {
  452. return FALSE;
  453. }
  454. $description = array(
  455. 'title' => $property_info['label'],
  456. 'help' => isset($property_info['description']) ? $property_info['description'] : NULL,
  457. );
  458. // Add in relationships to related entities.
  459. if (($info = entity_get_info($type)) && !empty($info['base table'])) {
  460. // Prepare reversed relationship data.
  461. $label_lowercase = drupal_strtolower($this->info['label'][0]) . drupal_substr($this->info['label'], 1);
  462. $property_label_lowercase = drupal_strtolower($property_info['label'][0]) . drupal_substr($property_info['label'], 1);
  463. // We name the field of the first reverse-relationship just with the
  464. // base table to be backward compatible, for subsequents relationships we
  465. // append the views field name in order to get a unique name.
  466. $name = !isset($this->relationships[$info['base table']][$this->info['base table']]) ? $this->info['base table'] : $this->info['base table'] . '_' . $views_field_name;
  467. $this->relationships[$info['base table']][$name] = array(
  468. 'title' => $this->info['label'],
  469. 'help' => t("Associated @label via the @label's @property.", array('@label' => $label_lowercase, '@property' => $property_label_lowercase)),
  470. 'relationship' => array(
  471. 'label' => $this->info['label'],
  472. 'handler' => $this->getRelationshipHandlerClass($this->type, $type),
  473. 'base' => $this->info['base table'],
  474. 'base field' => $views_field_name,
  475. 'relationship field' => isset($info['entity keys']['name']) ? $info['entity keys']['name'] : $info['entity keys']['id'],
  476. ),
  477. );
  478. $return['relationship'] = array(
  479. 'label' => drupal_ucfirst($info['label']),
  480. 'handler' => $this->getRelationshipHandlerClass($type, $this->type),
  481. 'base' => $info['base table'],
  482. 'base field' => isset($info['entity keys']['name']) ? $info['entity keys']['name'] : $info['entity keys']['id'],
  483. 'relationship field' => $views_field_name,
  484. );
  485. // Add in direct field/filters/sorts for the id itself too.
  486. $type = isset($info['entity keys']['name']) ? 'token' : 'integer';
  487. // Append the views-field-name to the title if it is different to the
  488. // property name.
  489. if ($property_name != $views_field_name) {
  490. $description['title'] .= ' ' . $views_field_name;
  491. }
  492. }
  493. switch ($type) {
  494. case 'token':
  495. case 'text':
  496. $return += $description + array(
  497. 'field' => array(
  498. 'real field' => $views_field_name,
  499. 'handler' => 'views_handler_field',
  500. 'click sortable' => TRUE,
  501. ),
  502. 'sort' => array(
  503. 'real field' => $views_field_name,
  504. 'handler' => 'views_handler_sort',
  505. ),
  506. 'filter' => array(
  507. 'real field' => $views_field_name,
  508. 'handler' => 'views_handler_filter_string',
  509. ),
  510. 'argument' => array(
  511. 'real field' => $views_field_name,
  512. 'handler' => 'views_handler_argument_string',
  513. ),
  514. );
  515. break;
  516. case 'decimal':
  517. case 'integer':
  518. $return += $description + array(
  519. 'field' => array(
  520. 'real field' => $views_field_name,
  521. 'handler' => 'views_handler_field_numeric',
  522. 'click sortable' => TRUE,
  523. 'float' => ($type == 'decimal'),
  524. ),
  525. 'sort' => array(
  526. 'real field' => $views_field_name,
  527. 'handler' => 'views_handler_sort',
  528. ),
  529. 'filter' => array(
  530. 'real field' => $views_field_name,
  531. 'handler' => 'views_handler_filter_numeric',
  532. ),
  533. 'argument' => array(
  534. 'real field' => $views_field_name,
  535. 'handler' => 'views_handler_argument_numeric',
  536. ),
  537. );
  538. break;
  539. case 'date':
  540. $return += $description + array(
  541. 'field' => array(
  542. 'real field' => $views_field_name,
  543. 'handler' => 'views_handler_field_date',
  544. 'click sortable' => TRUE,
  545. ),
  546. 'sort' => array(
  547. 'real field' => $views_field_name,
  548. 'handler' => 'views_handler_sort_date',
  549. ),
  550. 'filter' => array(
  551. 'real field' => $views_field_name,
  552. 'handler' => 'views_handler_filter_date',
  553. ),
  554. 'argument' => array(
  555. 'real field' => $views_field_name,
  556. 'handler' => 'views_handler_argument_date',
  557. ),
  558. );
  559. break;
  560. case 'duration':
  561. $return += $description + array(
  562. 'field' => array(
  563. 'real field' => $views_field_name,
  564. 'handler' => 'entity_views_handler_field_duration',
  565. 'click sortable' => TRUE,
  566. ),
  567. 'sort' => array(
  568. 'real field' => $views_field_name,
  569. 'handler' => 'views_handler_sort',
  570. ),
  571. 'filter' => array(
  572. 'real field' => $views_field_name,
  573. 'handler' => 'views_handler_filter_numeric',
  574. ),
  575. 'argument' => array(
  576. 'real field' => $views_field_name,
  577. 'handler' => 'views_handler_argument_numeric',
  578. ),
  579. );
  580. break;
  581. case 'uri':
  582. $return += $description + array(
  583. 'field' => array(
  584. 'real field' => $views_field_name,
  585. 'handler' => 'views_handler_field_url',
  586. 'click sortable' => TRUE,
  587. ),
  588. 'sort' => array(
  589. 'real field' => $views_field_name,
  590. 'handler' => 'views_handler_sort',
  591. ),
  592. 'filter' => array(
  593. 'real field' => $views_field_name,
  594. 'handler' => 'views_handler_filter_string',
  595. ),
  596. 'argument' => array(
  597. 'real field' => $views_field_name,
  598. 'handler' => 'views_handler_argument_string',
  599. ),
  600. );
  601. break;
  602. case 'boolean':
  603. $return += $description + array(
  604. 'field' => array(
  605. 'real field' => $views_field_name,
  606. 'handler' => 'views_handler_field_boolean',
  607. 'click sortable' => TRUE,
  608. ),
  609. 'sort' => array(
  610. 'real field' => $views_field_name,
  611. 'handler' => 'views_handler_sort',
  612. ),
  613. 'filter' => array(
  614. 'real field' => $views_field_name,
  615. 'handler' => 'views_handler_filter_boolean_operator',
  616. ),
  617. 'argument' => array(
  618. 'real field' => $views_field_name,
  619. 'handler' => 'views_handler_argument_string',
  620. ),
  621. );
  622. break;
  623. }
  624. // If there is an options list callback, add to the filter and field.
  625. if (isset($return['filter']) && !empty($property_info['options list'])) {
  626. $return['filter']['handler'] = 'views_handler_filter_in_operator';
  627. $return['filter']['options callback'] = array('EntityDefaultViewsController', 'optionsListCallback');
  628. $return['filter']['options arguments'] = array($this->type, $property_name, 'view');
  629. }
  630. // @todo: This class_exists is needed until views 3.2.
  631. if (isset($return['field']) && !empty($property_info['options list']) && class_exists('views_handler_field_machine_name')) {
  632. $return['field']['handler'] = 'views_handler_field_machine_name';
  633. $return['field']['options callback'] = array('EntityDefaultViewsController', 'optionsListCallback');
  634. $return['field']['options arguments'] = array($this->type, $property_name, 'view');
  635. }
  636. return $return;
  637. }
  638. /**
  639. * Determines the handler to use for a relationship to an entity type.
  640. *
  641. * @param $entity_type
  642. * The entity type to join to.
  643. * @param $left_type
  644. * The data type from which to join.
  645. */
  646. function getRelationshipHandlerClass($entity_type, $left_type) {
  647. // Look for an entity type which is used as bundle for the given entity
  648. // type. If there is one, allow filtering the relation by bundle by using
  649. // our own handler.
  650. foreach (entity_get_info() as $type => $info) {
  651. // In case we already join from the bundle entity we do not need to filter
  652. // by bundle entity any more, so we stay with the general handler.
  653. if (!empty($info['bundle of']) && $info['bundle of'] == $entity_type && $type != $left_type) {
  654. return 'entity_views_handler_relationship_by_bundle';
  655. }
  656. }
  657. return 'views_handler_relationship';
  658. }
  659. /**
  660. * A callback returning property options, suitable to be used as views options callback.
  661. */
  662. public static function optionsListCallback($type, $selector, $op = 'view') {
  663. $wrapper = entity_metadata_wrapper($type, NULL);
  664. $parts = explode(':', $selector);
  665. foreach ($parts as $part) {
  666. $wrapper = $wrapper->get($part);
  667. }
  668. return $wrapper->optionsList($op);
  669. }
  670. }