1
|
<?php
|
2
|
|
3
|
/**
|
4
|
* @file
|
5
|
* Provide Views data and handlers for field.module.
|
6
|
*
|
7
|
* @ingroup views_module_handlers
|
8
|
*/
|
9
|
|
10
|
/**
|
11
|
* Implements hook_views_data().
|
12
|
*
|
13
|
* Field modules can implement hook_field_views_data() to override
|
14
|
* the default behavior for adding fields.
|
15
|
*/
|
16
|
function field_views_data() {
|
17
|
$data = array();
|
18
|
foreach (field_info_fields() as $field) {
|
19
|
if ($field['storage']['type'] != 'field_sql_storage') {
|
20
|
continue;
|
21
|
}
|
22
|
|
23
|
$module = $field['module'];
|
24
|
$result = (array) module_invoke($module, 'field_views_data', $field);
|
25
|
|
26
|
if (empty($result)) {
|
27
|
$result = field_views_field_default_views_data($field);
|
28
|
}
|
29
|
drupal_alter('field_views_data', $result, $field, $module);
|
30
|
|
31
|
if (is_array($result)) {
|
32
|
$data = drupal_array_merge_deep($result, $data);
|
33
|
}
|
34
|
}
|
35
|
|
36
|
return $data;
|
37
|
}
|
38
|
|
39
|
/**
|
40
|
* Implements hook_views_data_alter().
|
41
|
*
|
42
|
* Field modules can implement hook_field_views_data_views_data_alter() to
|
43
|
* alter the views data on a per field basis. This is weirdly named so as
|
44
|
* not to conflict with the drupal_alter('field_views_data') in
|
45
|
* field_views_data.
|
46
|
*/
|
47
|
function field_views_data_alter(&$data) {
|
48
|
foreach (field_info_fields() as $field) {
|
49
|
if ($field['storage']['type'] != 'field_sql_storage') {
|
50
|
continue;
|
51
|
}
|
52
|
|
53
|
$function = $field['module'] . '_field_views_data_views_data_alter';
|
54
|
if (function_exists($function)) {
|
55
|
$function($data, $field);
|
56
|
}
|
57
|
}
|
58
|
}
|
59
|
|
60
|
/**
|
61
|
* Returns the label of a certain field.
|
62
|
*
|
63
|
* Therefore it looks up in all bundles to find the most used instance.
|
64
|
*/
|
65
|
function field_views_field_label($field_name) {
|
66
|
$label_counter = array();
|
67
|
$all_labels = array();
|
68
|
// Count the amount of instances per label per field.
|
69
|
$instances = field_info_instances();
|
70
|
foreach ($instances as $entity_name => $entity_type) {
|
71
|
foreach ($entity_type as $bundle) {
|
72
|
if (isset($bundle[$field_name])) {
|
73
|
$label_counter[$bundle[$field_name]['label']] = isset($label_counter[$bundle[$field_name]['label']]) ? ++$label_counter[$bundle[$field_name]['label']] : 1;
|
74
|
$all_labels[$entity_name][$bundle[$field_name]['label']] = TRUE;
|
75
|
}
|
76
|
}
|
77
|
}
|
78
|
if (empty($label_counter)) {
|
79
|
return array($field_name, $all_labels);
|
80
|
}
|
81
|
// Sort the field lables by it most used label and return the most used one.
|
82
|
arsort($label_counter);
|
83
|
$label_counter = array_keys($label_counter);
|
84
|
return array($label_counter[0], $all_labels);
|
85
|
}
|
86
|
|
87
|
/**
|
88
|
* Default views data implementation for a field.
|
89
|
*/
|
90
|
function field_views_field_default_views_data($field) {
|
91
|
$field_types = field_info_field_types();
|
92
|
|
93
|
// Check the field module is available.
|
94
|
if (!isset($field_types[$field['type']])) {
|
95
|
return;
|
96
|
}
|
97
|
|
98
|
$data = array();
|
99
|
|
100
|
$current_table = _field_sql_storage_tablename($field);
|
101
|
$revision_table = _field_sql_storage_revision_tablename($field);
|
102
|
|
103
|
// The list of entity:bundle that this field is used in.
|
104
|
$bundles_names = array();
|
105
|
$supports_revisions = FALSE;
|
106
|
$entity_tables = array();
|
107
|
$current_tables = array();
|
108
|
$revision_tables = array();
|
109
|
$groups = array();
|
110
|
|
111
|
// Store translations to avoid being called thousands of times.
|
112
|
$translations['no_value_bracket'] = t('<No value>');
|
113
|
$translations['no_value_dash'] = t('- No value -');
|
114
|
$translations['field'] = t('Field');
|
115
|
$translations['node'] = t('Node');
|
116
|
$translations['content'] = t('Content');
|
117
|
|
118
|
$group_name = count($field['bundles']) > 1 ? $translations['field'] : NULL;
|
119
|
|
120
|
// Build the relationships between the field table and the entity tables.
|
121
|
foreach ($field['bundles'] as $entity => $bundles) {
|
122
|
$entity_info = entity_get_info($entity);
|
123
|
$groups[$entity] = $entity_info['label'];
|
124
|
|
125
|
// Override Node to Content.
|
126
|
if ($groups[$entity] == $translations['node']) {
|
127
|
$groups[$entity] = $translations['content'];
|
128
|
}
|
129
|
|
130
|
// If only one bundle use this as the default name.
|
131
|
if (empty($group_name)) {
|
132
|
$group_name = $groups[$entity];
|
133
|
}
|
134
|
|
135
|
$entity_tables[$entity_info['base table']] = $entity;
|
136
|
$current_tables[$entity] = $entity_info['base table'];
|
137
|
if (isset($entity_info['revision table'])) {
|
138
|
$entity_tables[$entity_info['revision table']] = $entity;
|
139
|
$revision_tables[$entity] = $entity_info['revision table'];
|
140
|
}
|
141
|
|
142
|
$data[$current_table]['table']['join'][$entity_info['base table']] = array(
|
143
|
'left_field' => $entity_info['entity keys']['id'],
|
144
|
'field' => 'entity_id',
|
145
|
'extra' => array(
|
146
|
array('field' => 'entity_type', 'value' => $entity),
|
147
|
array('field' => 'deleted', 'value' => 0, 'numeric' => TRUE),
|
148
|
),
|
149
|
);
|
150
|
|
151
|
if (!empty($entity_info['entity keys']['revision']) && !empty($entity_info['revision table'])) {
|
152
|
$data[$revision_table]['table']['join'][$entity_info['revision table']] = array(
|
153
|
'left_field' => $entity_info['entity keys']['revision'],
|
154
|
'field' => 'revision_id',
|
155
|
'extra' => array(
|
156
|
array('field' => 'entity_type', 'value' => $entity),
|
157
|
array('field' => 'deleted', 'value' => 0, 'numeric' => TRUE),
|
158
|
),
|
159
|
);
|
160
|
|
161
|
$supports_revisions = TRUE;
|
162
|
}
|
163
|
|
164
|
foreach ($bundles as $bundle) {
|
165
|
$bundles_names[] = t('@entity:@bundle', array('@entity' => $entity, '@bundle' => $bundle));
|
166
|
}
|
167
|
}
|
168
|
|
169
|
$tables = array();
|
170
|
$tables[FIELD_LOAD_CURRENT] = $current_table;
|
171
|
if ($supports_revisions) {
|
172
|
$tables[FIELD_LOAD_REVISION] = $revision_table;
|
173
|
}
|
174
|
|
175
|
$add_fields = array('delta', 'language', 'bundle');
|
176
|
foreach ($field['columns'] as $column_name => $attributes) {
|
177
|
$add_fields[] = _field_sql_storage_columnname($field['field_name'], $column_name);
|
178
|
}
|
179
|
|
180
|
// Note: we don't have a label available here, because we are at the field
|
181
|
// level, not at the instance level. So we just go through all instances
|
182
|
// and take the one which is used the most frequently.
|
183
|
$field_name = $field['field_name'];
|
184
|
list($label, $all_labels) = field_views_field_label($field_name);
|
185
|
|
186
|
$bundle_names_text = implode(', ', $bundles_names);
|
187
|
$translations['appears_in_help'] = t('Appears in: @bundles.', array('@bundles' => $bundle_names_text));
|
188
|
$translations['delta_appears_in_help'] = t('Delta - Appears in: @bundles.', array('@bundles' => $bundle_names_text));
|
189
|
$translations['language_appears_in_help'] = t('Language - Appears in: @bundles.', array('@bundles' => $bundle_names_text));
|
190
|
$translations['group_historical'] = t('@group (historical data)', array('@group' => $group_name));
|
191
|
$translations['alias_help'] = t('This is an alias of @group: @field.', array('@group' => $group_name, '@field' => $label));
|
192
|
$translations['label_short_language'] = t('@label:language', array('@label' => $label));
|
193
|
|
194
|
foreach ($tables as $type => $table) {
|
195
|
if ($type == FIELD_LOAD_CURRENT) {
|
196
|
$group = $group_name;
|
197
|
$old_column = 'entity_id';
|
198
|
$column = $field['field_name'];
|
199
|
}
|
200
|
else {
|
201
|
$group = $translations['group_historical'];
|
202
|
$old_column = 'revision_id';
|
203
|
$column = $field['field_name'] . '-' . $old_column;
|
204
|
}
|
205
|
|
206
|
$data[$table][$old_column]['field']['moved to'] = array($table, $column);
|
207
|
$data[$table][$column] = array(
|
208
|
'group' => $group,
|
209
|
'title' => $label,
|
210
|
'title short' => $label,
|
211
|
'help' => $translations['appears_in_help'],
|
212
|
);
|
213
|
|
214
|
// Go through and create a list of aliases for all possible combinations of
|
215
|
// entity type + name.
|
216
|
$aliases = array();
|
217
|
$also_known = array();
|
218
|
foreach ($all_labels as $entity_name => $labels) {
|
219
|
foreach ($labels as $label_name => $true) {
|
220
|
if ($type == FIELD_LOAD_CURRENT) {
|
221
|
if ($group_name != $groups[$entity_name] || $label != $label_name) {
|
222
|
$aliases[] = array(
|
223
|
'base' => $current_tables[$entity_name],
|
224
|
'group' => $groups[$entity_name],
|
225
|
'title' => $label_name,
|
226
|
'help' => $translations['alias_help'],
|
227
|
);
|
228
|
}
|
229
|
$also_known[] = t('@group: @field', array('@group' => $groups[$entity_name], '@field' => $label_name));
|
230
|
}
|
231
|
else {
|
232
|
if ($group_name != $groups[$entity_name] && $label != $label_name && isset($revision_tables[$entity_name])) {
|
233
|
$aliases[] = array(
|
234
|
'base' => $revision_tables[$entity_name],
|
235
|
'group' => t('@group (historical data)', array('@group' => $groups[$entity_name])),
|
236
|
'title' => $label_name,
|
237
|
'help' => $translations['alias_help'],
|
238
|
);
|
239
|
}
|
240
|
$also_known[] = t('@group (historical data): @field', array('@group' => $groups[$entity_name], '@field' => $label_name));
|
241
|
}
|
242
|
}
|
243
|
}
|
244
|
if ($aliases) {
|
245
|
$data[$table][$column]['aliases'] = $aliases;
|
246
|
$data[$table][$column]['help'] .= ' ' . t('Also known as: !also.', array('!also' => implode(', ', $also_known)));
|
247
|
}
|
248
|
|
249
|
$keys = array_keys($field['columns']);
|
250
|
$real_field = reset($keys);
|
251
|
$data[$table][$column]['field'] = array(
|
252
|
'table' => $table,
|
253
|
'handler' => 'views_handler_field_field',
|
254
|
'click sortable' => TRUE,
|
255
|
'field_name' => $field['field_name'],
|
256
|
// Provide a real field for group by.
|
257
|
'real field' => $column . '_' . $real_field,
|
258
|
'additional fields' => $add_fields,
|
259
|
'entity_tables' => $entity_tables,
|
260
|
// Default the element type to div, let the UI change it if necessary.
|
261
|
'element type' => 'div',
|
262
|
'is revision' => $type == FIELD_LOAD_REVISION,
|
263
|
);
|
264
|
}
|
265
|
|
266
|
foreach ($field['columns'] as $column => $attributes) {
|
267
|
$allow_sort = TRUE;
|
268
|
|
269
|
// Identify likely filters and arguments for each column based on field
|
270
|
// type.
|
271
|
switch ($attributes['type']) {
|
272
|
case 'int':
|
273
|
case 'mediumint':
|
274
|
case 'tinyint':
|
275
|
case 'bigint':
|
276
|
case 'serial':
|
277
|
case 'numeric':
|
278
|
case 'float':
|
279
|
$filter = 'views_handler_filter_numeric';
|
280
|
$argument = 'views_handler_argument_numeric';
|
281
|
$sort = 'views_handler_sort';
|
282
|
break;
|
283
|
|
284
|
case 'text':
|
285
|
case 'blob':
|
286
|
// It does not make sense to sort by blob or text.
|
287
|
$allow_sort = FALSE;
|
288
|
default:
|
289
|
$filter = 'views_handler_filter_string';
|
290
|
$argument = 'views_handler_argument_string';
|
291
|
$sort = 'views_handler_sort';
|
292
|
break;
|
293
|
}
|
294
|
|
295
|
if (count($field['columns']) == 1 || $column == 'value') {
|
296
|
$title = t('@label (!name)', array('@label' => $label, '!name' => $field['field_name']));
|
297
|
// CCK used the first 10 characters of $label. Probably doesn't matter.
|
298
|
$title_short = $label;
|
299
|
}
|
300
|
else {
|
301
|
$title = t('@label (!name:!column)', array('@label' => $label, '!name' => $field['field_name'], '!column' => $column));
|
302
|
$title_short = t('@label:!column', array('@label' => $label, '!column' => $column));
|
303
|
}
|
304
|
|
305
|
foreach ($tables as $type => $table) {
|
306
|
if ($type == FIELD_LOAD_CURRENT) {
|
307
|
$group = $group_name;
|
308
|
}
|
309
|
else {
|
310
|
$group = $translations['group_historical'];
|
311
|
}
|
312
|
$column_real_name = $field['storage']['details']['sql'][$type][$table][$column];
|
313
|
|
314
|
// Load all the fields from the table by default.
|
315
|
$additional_fields = array_values($field['storage']['details']['sql'][$type][$table]);
|
316
|
|
317
|
$data[$table][$column_real_name] = array(
|
318
|
'group' => $group,
|
319
|
'title' => $title,
|
320
|
'title short' => $title_short,
|
321
|
'help' => $translations['appears_in_help'],
|
322
|
);
|
323
|
|
324
|
// Go through and create a list of aliases for all possible combinations
|
325
|
// of entity type + name.
|
326
|
$aliases = array();
|
327
|
$also_known = array();
|
328
|
foreach ($all_labels as $entity_name => $labels) {
|
329
|
foreach ($labels as $label_name => $true) {
|
330
|
if ($group_name != $groups[$entity_name] || $label != $label_name) {
|
331
|
if (count($field['columns']) == 1 || $column == 'value') {
|
332
|
// CCK used the first 10 characters of $label. Probably doesn't
|
333
|
// matter.
|
334
|
$alias_title = t('@label (!name)', array('@label' => $label_name, '!name' => $field['field_name']));
|
335
|
}
|
336
|
else {
|
337
|
$alias_title = t('@label (!name:!column)', array('@label' => $label_name, '!name' => $field['field_name'], '!column' => $column));
|
338
|
}
|
339
|
$aliases[] = array(
|
340
|
'group' => $groups[$entity_name],
|
341
|
'title' => $alias_title,
|
342
|
'help' => t('This is an alias of @group: @field.', array('@group' => $group_name, '@field' => $title)),
|
343
|
);
|
344
|
}
|
345
|
$also_known[] = t('@group: @field', array('@group' => $groups[$entity_name], '@field' => $title));
|
346
|
}
|
347
|
}
|
348
|
if ($aliases) {
|
349
|
$data[$table][$column_real_name]['aliases'] = $aliases;
|
350
|
$data[$table][$column_real_name]['help'] .= ' ' . t('Also known as: !also.', array('!also' => implode(', ', $also_known)));
|
351
|
}
|
352
|
|
353
|
$data[$table][$column_real_name]['argument'] = array(
|
354
|
'field' => $column_real_name,
|
355
|
'table' => $table,
|
356
|
'handler' => $argument,
|
357
|
'additional fields' => $additional_fields,
|
358
|
'field_name' => $field['field_name'],
|
359
|
'empty field name' => $translations['no_value_dash'],
|
360
|
);
|
361
|
$data[$table][$column_real_name]['filter'] = array(
|
362
|
'field' => $column_real_name,
|
363
|
'table' => $table,
|
364
|
'handler' => $filter,
|
365
|
'additional fields' => $additional_fields,
|
366
|
'field_name' => $field['field_name'],
|
367
|
'allow empty' => TRUE,
|
368
|
);
|
369
|
if (!empty($allow_sort)) {
|
370
|
$data[$table][$column_real_name]['sort'] = array(
|
371
|
'field' => $column_real_name,
|
372
|
'table' => $table,
|
373
|
'handler' => $sort,
|
374
|
'additional fields' => $additional_fields,
|
375
|
'field_name' => $field['field_name'],
|
376
|
);
|
377
|
}
|
378
|
|
379
|
// Expose additional delta column for multiple value fields.
|
380
|
if ($field['cardinality'] > 1 || $field['cardinality'] == FIELD_CARDINALITY_UNLIMITED) {
|
381
|
$title_delta = t('@label (!name:delta)', array('@label' => $label, '!name' => $field['field_name']));
|
382
|
$title_short_delta = t('@label:delta', array('@label' => $label));
|
383
|
|
384
|
$data[$table]['delta'] = array(
|
385
|
'group' => $group,
|
386
|
'title' => $title_delta,
|
387
|
'title short' => $title_short_delta,
|
388
|
'help' => $translations['delta_appears_in_help'],
|
389
|
);
|
390
|
$data[$table]['delta']['field'] = array(
|
391
|
'handler' => 'views_handler_field_numeric',
|
392
|
);
|
393
|
$data[$table]['delta']['argument'] = array(
|
394
|
'field' => 'delta',
|
395
|
'table' => $table,
|
396
|
'handler' => 'views_handler_argument_numeric',
|
397
|
'additional fields' => $additional_fields,
|
398
|
'empty field name' => $translations['no_value_dash'],
|
399
|
'field_name' => $field['field_name'],
|
400
|
);
|
401
|
$data[$table]['delta']['filter'] = array(
|
402
|
'field' => 'delta',
|
403
|
'table' => $table,
|
404
|
'handler' => 'views_handler_filter_numeric',
|
405
|
'additional fields' => $additional_fields,
|
406
|
'field_name' => $field['field_name'],
|
407
|
'allow empty' => TRUE,
|
408
|
);
|
409
|
$data[$table]['delta']['sort'] = array(
|
410
|
'field' => 'delta',
|
411
|
'table' => $table,
|
412
|
'handler' => 'views_handler_sort',
|
413
|
'additional fields' => $additional_fields,
|
414
|
'field_name' => $field['field_name'],
|
415
|
);
|
416
|
}
|
417
|
|
418
|
// Expose additional language column for translatable fields.
|
419
|
if (!empty($field['translatable'])) {
|
420
|
$title_language = t('@label (!name:language)', array('@label' => $label, '!name' => $field['field_name']));
|
421
|
$title_short_language = t('@label:language', array('@label' => $label));
|
422
|
|
423
|
$data[$table]['language'] = array(
|
424
|
'group' => $group,
|
425
|
'title' => $title_language,
|
426
|
'title short' => $title_short_language,
|
427
|
'help' => t('Language - Appears in: @bundles.', array('@bundles' => implode(', ', $bundles_names))),
|
428
|
);
|
429
|
$data[$table]['language']['field'] = array(
|
430
|
'handler' => 'views_handler_field_locale_language',
|
431
|
);
|
432
|
$data[$table]['language']['argument'] = array(
|
433
|
'field' => 'language',
|
434
|
'table' => $table,
|
435
|
'handler' => 'views_handler_argument_locale_language',
|
436
|
'additional fields' => $additional_fields,
|
437
|
'empty field name' => $translations['no_value_bracket'],
|
438
|
'field_name' => $field['field_name'],
|
439
|
);
|
440
|
$data[$table]['language']['filter'] = array(
|
441
|
'field' => 'language',
|
442
|
'table' => $table,
|
443
|
'handler' => 'views_handler_filter_locale_language',
|
444
|
'additional fields' => $additional_fields,
|
445
|
'field_name' => $field['field_name'],
|
446
|
'allow empty' => TRUE,
|
447
|
);
|
448
|
$data[$table]['language']['sort'] = array(
|
449
|
'field' => 'language',
|
450
|
'table' => $table,
|
451
|
'handler' => 'views_handler_sort',
|
452
|
'additional fields' => $additional_fields,
|
453
|
'field_name' => $field['field_name'],
|
454
|
);
|
455
|
}
|
456
|
}
|
457
|
}
|
458
|
|
459
|
return $data;
|
460
|
}
|
461
|
|
462
|
/**
|
463
|
* Have a different filter handler for lists.
|
464
|
*
|
465
|
* This should allow to select values of the list.
|
466
|
*/
|
467
|
function list_field_views_data($field) {
|
468
|
$data = field_views_field_default_views_data($field);
|
469
|
foreach ($data as $table_name => $table_data) {
|
470
|
foreach ($table_data as $field_name => $field_data) {
|
471
|
if (isset($field_data['filter']) && $field_name != 'delta') {
|
472
|
if ($field['type'] == 'list_boolean') {
|
473
|
// Special handler for boolean fields.
|
474
|
$data[$table_name][$field_name]['filter']['handler'] = 'views_handler_filter_field_list_boolean';
|
475
|
}
|
476
|
else {
|
477
|
$data[$table_name][$field_name]['filter']['handler'] = 'views_handler_filter_field_list';
|
478
|
}
|
479
|
}
|
480
|
if (isset($field_data['argument']) && $field_name != 'delta') {
|
481
|
if ($field['type'] == 'list_text') {
|
482
|
$data[$table_name][$field_name]['argument']['handler'] = 'views_handler_argument_field_list_string';
|
483
|
}
|
484
|
else {
|
485
|
$data[$table_name][$field_name]['argument']['handler'] = 'views_handler_argument_field_list';
|
486
|
}
|
487
|
}
|
488
|
}
|
489
|
}
|
490
|
return $data;
|
491
|
}
|