1 |
85ad3d82
|
Assos Assos
|
<?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 |
503b3f7b
|
Assos Assos
|
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 |
85ad3d82
|
Assos Assos
|
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 |
503b3f7b
|
Assos Assos
|
/**
|
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 |
85ad3d82
|
Assos Assos
|
/**
|
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 |
|
|
} |