1
|
<?php
|
2
|
|
3
|
|
4
|
/**
|
5
|
* Implements hook_menu().
|
6
|
*/
|
7
|
function date_views_menu() {
|
8
|
// Used to import files from a local filesystem into Drupal.
|
9
|
$items['admin/config/regional/date-time/date-views'] = array(
|
10
|
'title' => 'Date views',
|
11
|
'description' => 'Configure settings for date views.',
|
12
|
'page callback' => 'drupal_get_form',
|
13
|
'page arguments' => array('date_views_settings'),
|
14
|
'access arguments' => array('administer site configuration '),
|
15
|
'type' => MENU_LOCAL_TASK,
|
16
|
);
|
17
|
|
18
|
return $items;
|
19
|
}
|
20
|
|
21
|
/**
|
22
|
* Form callback for date views settings.
|
23
|
*/
|
24
|
function date_views_settings($form, &$form_state) {
|
25
|
|
26
|
$form['date_views_month_format_with_year'] = array(
|
27
|
'#type' => 'textfield',
|
28
|
'#title' => t('Date views month format with year'),
|
29
|
'#size' => 10,
|
30
|
'#default_value' => variable_get('date_views_month_format_with_year', 'F Y'),
|
31
|
'#description' => t('Date views month format with year, default value : F Y'),
|
32
|
);
|
33
|
|
34
|
$form['date_views_month_format_without_year'] = array(
|
35
|
'#type' => 'textfield',
|
36
|
'#title' => t('Date views month format without year'),
|
37
|
'#size' => 10,
|
38
|
'#default_value' => variable_get('date_views_month_format_without_year', 'F'),
|
39
|
'#description' => t('Date views month format without year, default value : F'),
|
40
|
);
|
41
|
|
42
|
$form['date_views_day_format_with_year'] = array(
|
43
|
'#type' => 'textfield',
|
44
|
'#title' => t('Date views day format with year'),
|
45
|
'#size' => 10,
|
46
|
'#default_value' => variable_get('date_views_day_format_with_year', 'l, F j, Y'),
|
47
|
'#description' => t('Date views day format with year, default value : l, F j, Y'),
|
48
|
);
|
49
|
|
50
|
$form['date_views_day_format_without_year'] = array(
|
51
|
'#type' => 'textfield',
|
52
|
'#title' => t('Date views day format without year'),
|
53
|
'#size' => 10,
|
54
|
'#default_value' => variable_get('date_views_day_format_without_year', 'l, F j'),
|
55
|
'#description' => t('Date views day format without year, default value : l, F j'),
|
56
|
);
|
57
|
|
58
|
$form['date_views_week_format_with_year'] = array(
|
59
|
'#type' => 'textfield',
|
60
|
'#title' => t('Date views week format with year'),
|
61
|
'#size' => 10,
|
62
|
'#default_value' => variable_get('date_views_week_format_with_year', 'F j, Y'),
|
63
|
'#description' => t('Date views week format with year, default value : F j, Y'),
|
64
|
);
|
65
|
|
66
|
$form['date_views_week_format_without_year'] = array(
|
67
|
'#type' => 'textfield',
|
68
|
'#title' => t('Date views week format without year'),
|
69
|
'#size' => 10,
|
70
|
'#default_value' => variable_get('date_views_week_format_without_year', 'F j'),
|
71
|
'#description' => t('Date views week format without year, default value : F j'),
|
72
|
);
|
73
|
|
74
|
return system_settings_form($form);
|
75
|
|
76
|
}
|
77
|
|
78
|
/**
|
79
|
* Implements hook_views_api().
|
80
|
*
|
81
|
* This one is used as the base to reduce errors when updating.
|
82
|
*/
|
83
|
function date_views_theme() {
|
84
|
$path = drupal_get_path('module', 'date_views');
|
85
|
$base = array(
|
86
|
'file' => 'theme.inc',
|
87
|
'path' => "$path/theme",
|
88
|
);
|
89
|
return array(
|
90
|
'date_nav_title' => $base + array('variables' => array('granularity' => NULL, 'view' => NULL, 'link' => NULL, 'format' => NULL)),
|
91
|
'date_views_filter_form' => $base + array('template' => 'date-views-filter-form', 'render element' => 'form'),
|
92
|
'date_calendar_day' => $base + array('variables' => array('date' => NULL)),
|
93
|
|
94
|
'date_views_pager' => $base + array(
|
95
|
'variables' => array('plugin' => NULL, 'input' => NULL),
|
96
|
// Register a pattern so that it can work like all views templates.
|
97
|
'pattern' => 'date_views_pager__',
|
98
|
'template' => 'date-views-pager',
|
99
|
),
|
100
|
);
|
101
|
}
|
102
|
|
103
|
function date_views_views_api() {
|
104
|
return array(
|
105
|
'api' => 3,
|
106
|
'path' => drupal_get_path('module', 'date_views') . '/includes',
|
107
|
);
|
108
|
}
|
109
|
|
110
|
/**
|
111
|
* Wrapper function to make sure this function will always work.
|
112
|
*/
|
113
|
function date_views_views_fetch_fields($base, $type) {
|
114
|
if (!module_exists('views')) {
|
115
|
return array();
|
116
|
}
|
117
|
module_load_include('inc', 'views', 'includes/admin');
|
118
|
return views_fetch_fields($base, $type);
|
119
|
}
|
120
|
|
121
|
/**
|
122
|
* Identify all potential date/timestamp fields and cache the data.
|
123
|
*/
|
124
|
function date_views_fields($base = 'node', $reset = FALSE) {
|
125
|
static $fields = array();
|
126
|
$empty = array('name' => array(), 'alias' => array());
|
127
|
module_load_include('inc', 'date_views', 'includes/date_views_fields');
|
128
|
|
129
|
if (empty($fields[$base]) || $reset) {
|
130
|
$cid = 'date_views_fields_' . $base;
|
131
|
if (!$reset && $cached = cache_get($cid, 'cache_views')) {
|
132
|
$fields[$base] = $cached->data;
|
133
|
}
|
134
|
else {
|
135
|
$fields[$base] = _date_views_fields($base);
|
136
|
}
|
137
|
}
|
138
|
// Make sure that empty values will be arrays in he expected format.
|
139
|
return !empty($fields) && !empty($fields[$base]) ? $fields[$base] : $empty;
|
140
|
}
|
141
|
|
142
|
/**
|
143
|
* Implements hook_date_views_entities().
|
144
|
* Map extra Views tables to the entity that holds its date fields,
|
145
|
* needed for Views tables other than the primary tables identified in entity_info().
|
146
|
*/
|
147
|
function date_views_date_views_extra_tables() {
|
148
|
return array(
|
149
|
'node_revision' => 'node',
|
150
|
);
|
151
|
}
|
152
|
|
153
|
/**
|
154
|
* Helper function to map entity types to the Views base table they use,
|
155
|
* to make it easier to infer the entity type from a base table.
|
156
|
*
|
157
|
* Views has a new handler called views_handler_field_entity() that loads
|
158
|
* entities, and you can use something like the following to get the
|
159
|
* entity type from a view, but not all our base tables contain the
|
160
|
* entity information we need, (i.e. revisions) so it won't work here
|
161
|
* and we resort to creating information from entity_get_info().
|
162
|
*
|
163
|
* // A method to get the entity type for a base table.
|
164
|
* $table_data = views_fetch_data($base_table);
|
165
|
* if (!isset($table_data['table']['base']['entity type'])) {
|
166
|
* return FALSE;
|
167
|
* }
|
168
|
* $entity_type = $table_data['table']['base']['entity type'];
|
169
|
*/
|
170
|
function date_views_base_tables() {
|
171
|
$base_tables = &drupal_static(__FILE__, array());
|
172
|
|
173
|
if (empty($base_tables)) {
|
174
|
|
175
|
// First we get the base tables we can learn about from entity_info.
|
176
|
$entity_info = entity_get_info();
|
177
|
foreach ($entity_info as $entity_type => $info) {
|
178
|
if (!empty($info['base table'])) {
|
179
|
$base_tables[$info['base table']] = $entity_type;
|
180
|
}
|
181
|
if (!empty($info['revision table'])) {
|
182
|
$base_tables[$info['revision table']] = $entity_type;
|
183
|
}
|
184
|
}
|
185
|
|
186
|
// Then we let other modules tell us about other entity tables that hold date fields.
|
187
|
$base_tables += module_invoke_all('date_views_extra_tables');
|
188
|
}
|
189
|
|
190
|
return $base_tables;
|
191
|
}
|
192
|
|
193
|
/**
|
194
|
* Implements hook_date_views_fields().
|
195
|
*
|
196
|
* All modules that create custom fields that use the
|
197
|
* 'views_handler_field_date' handler can provide
|
198
|
* additional information here about the type of
|
199
|
* date they create so the date can be used by
|
200
|
* the Date API views date argument and date filter.
|
201
|
*/
|
202
|
function date_views_date_views_fields($field) {
|
203
|
$values = array(
|
204
|
// The type of date: DATE_UNIX, DATE_ISO, DATE_DATETIME.
|
205
|
'sql_type' => DATE_UNIX,
|
206
|
// Timezone handling options: 'none', 'site', 'date', 'utc' .
|
207
|
'tz_handling' => 'site',
|
208
|
// Needed only for dates that use 'date' tz_handling.
|
209
|
'timezone_field' => '',
|
210
|
// Needed only for dates that use 'date' tz_handling.
|
211
|
'offset_field' => '',
|
212
|
// Array of "table.field" values for related fields that should be
|
213
|
// loaded automatically in the Views SQL.
|
214
|
'related_fields' => array(),
|
215
|
// Granularity of this date field's db data.
|
216
|
'granularity' => array('year', 'month', 'day', 'hour', 'minute', 'second'),
|
217
|
);
|
218
|
|
219
|
switch ($field) {
|
220
|
case 'users.created':
|
221
|
case 'users.access':
|
222
|
case 'users.login':
|
223
|
case 'node.created':
|
224
|
case 'node.changed':
|
225
|
case 'node_revision.timestamp':
|
226
|
case 'file_managed.timestamp':
|
227
|
case 'comment.timestamp':
|
228
|
return $values;
|
229
|
}
|
230
|
}
|
231
|
|
232
|
/**
|
233
|
* A version of date_real_url that formats links correctly for the new Date pager.
|
234
|
*/
|
235
|
function date_pager_url($view, $date_type = NULL, $date_arg = NULL, $force_view_url = FALSE, $absolute = TRUE) {
|
236
|
|
237
|
// If someone adds a pager without a matching argument, there is not date information to work with.
|
238
|
if (empty($view->date_info) || !isset($view->date_info->date_arg_pos)) {
|
239
|
return '';
|
240
|
}
|
241
|
|
242
|
$args = $view->args;
|
243
|
$pos = $view->date_info->date_arg_pos;
|
244
|
|
245
|
// The View arguments array is indexed numerically but is not necessarily
|
246
|
// in numerical order. Sort the arguments to ensure the correct order.
|
247
|
ksort($args);
|
248
|
|
249
|
// If there are empty arguments before the date argument,
|
250
|
// pad them with the wildcard so the date argument will be in
|
251
|
// the right position.
|
252
|
if (count($args) < $pos) {
|
253
|
foreach ($view->argument as $name => $argument) {
|
254
|
if ($argument->position == $pos) {
|
255
|
break;
|
256
|
}
|
257
|
$args[] = $argument->options['exception']['value'];
|
258
|
}
|
259
|
}
|
260
|
|
261
|
if (!empty($date_type)) {
|
262
|
switch ($date_type) {
|
263
|
case 'year':
|
264
|
$args[$pos] = date_pad($view->date_info->year, 4);
|
265
|
break;
|
266
|
case 'week':
|
267
|
$args[$pos] = date_pad($view->date_info->year, 4) . '-W' . date_pad($view->date_info->week);
|
268
|
break;
|
269
|
case 'day':
|
270
|
$args[$pos] = date_pad($view->date_info->year, 4) . '-' . date_pad($view->date_info->month) . '-' . date_pad($view->date_info->day);
|
271
|
break;
|
272
|
default:
|
273
|
$args[$pos] = date_pad($view->date_info->year, 4) . '-' . date_pad($view->date_info->month);
|
274
|
break;
|
275
|
}
|
276
|
}
|
277
|
elseif (!empty($date_arg)) {
|
278
|
$args[$pos] = $date_arg;
|
279
|
}
|
280
|
else {
|
281
|
$args = $view->args;
|
282
|
}
|
283
|
// Is this an embedded or a block view?
|
284
|
// Return the pager query value.
|
285
|
if (!$force_view_url &&
|
286
|
(!empty($view->preview) || !empty($view->date_info->block_identifier))) {
|
287
|
|
288
|
$url = $args[$pos];
|
289
|
$key = date_block_identifier($view);
|
290
|
if (!empty($key)) {
|
291
|
return url($_GET['q'], array(
|
292
|
'query' => date_views_querystring($view, array($key => $url)),
|
293
|
'absolute' => $absolute));
|
294
|
}
|
295
|
}
|
296
|
|
297
|
// Normal views may need querystrings appended to them
|
298
|
// if they use exposed filters.
|
299
|
return url($view->get_url($args), array(
|
300
|
'query' => date_views_querystring($view),
|
301
|
'absolute' => $absolute));
|
302
|
}
|
303
|
|
304
|
function date_block_identifier($view) {
|
305
|
if (!empty($view->block_identifier)) {
|
306
|
return $view->block_identifier;
|
307
|
}
|
308
|
return isset($view->date_info->block_identifier) ? $view->date_info->block_identifier : NULL;
|
309
|
}
|
310
|
|
311
|
/**
|
312
|
* Implements hook_field_views_data_alter().
|
313
|
*
|
314
|
* Create a Views field for each date column we care about
|
315
|
* to supplement the generic 'entity_id' and 'revision_id'
|
316
|
* fields that are automatically created.
|
317
|
*
|
318
|
* Also use friendlier labels to distinguish the start date
|
319
|
* and end date in listings (for fields that use both).
|
320
|
*/
|
321
|
function date_views_field_views_data_alter(&$result, $field, $module) {
|
322
|
if ($module == 'date') {
|
323
|
$has_end_date = !empty($field['settings']['todate']);
|
324
|
if ($has_end_date) {
|
325
|
$labels = field_views_field_label($field['field_name']);
|
326
|
$label = array_shift($labels);
|
327
|
}
|
328
|
foreach ($result as $table => $data) {
|
329
|
$additional = array();
|
330
|
$field_name = $field['field_name'];
|
331
|
foreach ($data as $column => $value) {
|
332
|
|
333
|
// The old 'entity_id' and 'revision_id' values got rewritten in Views.
|
334
|
// The old values are still there with a 'moved to' key, so ignore them.
|
335
|
if (array_key_exists('field', $value) && !array_key_exists('moved to', $value['field'])) {
|
336
|
$result[$table][$column]['field']['is date'] = TRUE;
|
337
|
// Not sure yet if we still need a custom field handler in D7 now that custom formatters are available.
|
338
|
// Might still need it to handle grouping of multiple value dates.
|
339
|
//$result[$table][$column]['field']['handler'] = 'date_handler_field_date';
|
340
|
//$result[$table][$column]['field']['add fields to query'] = TRUE;
|
341
|
}
|
342
|
|
343
|
// For filters, arguments, and sorts, determine if this column is for
|
344
|
// the start date ('value') or the end date ('value2').
|
345
|
$this_column = NULL;
|
346
|
foreach (array_keys($field['columns']) as $candidate_column) {
|
347
|
if ($column == $field['field_name'] . '_' . $candidate_column) {
|
348
|
$this_column = $candidate_column;
|
349
|
break;
|
350
|
}
|
351
|
}
|
352
|
|
353
|
// Only alter the date fields, not timezone, rrule, offset, etc.
|
354
|
if ($this_column != 'value' && $this_column != 'value2') {
|
355
|
continue;
|
356
|
}
|
357
|
|
358
|
// We will replace the label with a friendlier name in the case of
|
359
|
// arguments, filters, and sorts (but only if this field uses an end
|
360
|
// date).
|
361
|
$replace_label = FALSE;
|
362
|
if (array_key_exists('argument', $value)) {
|
363
|
$result[$table][$column]['argument']['handler'] = 'date_views_argument_handler_simple';
|
364
|
$result[$table][$column]['argument']['empty field name'] = t('Undated');
|
365
|
$result[$table][$column]['argument']['is date'] = TRUE;
|
366
|
$replace_label = $has_end_date;
|
367
|
}
|
368
|
if (array_key_exists('filter', $value)) {
|
369
|
$result[$table][$column]['filter']['handler'] = 'date_views_filter_handler_simple';
|
370
|
$result[$table][$column]['filter']['empty field name'] = t('Undated');
|
371
|
$result[$table][$column]['filter']['is date'] = TRUE;
|
372
|
$replace_label = $has_end_date;
|
373
|
}
|
374
|
if (array_key_exists('sort', $value)) {
|
375
|
$result[$table][$column]['sort']['is date'] = TRUE;
|
376
|
$replace_label = $has_end_date;
|
377
|
}
|
378
|
if ($replace_label) {
|
379
|
// Determine if this column is for the start date ('value') or the
|
380
|
// end date ('value2').
|
381
|
$this_column = NULL;
|
382
|
foreach (array_keys($field['columns']) as $candidate_column) {
|
383
|
if ($column == $field['field_name'] . '_' . $candidate_column) {
|
384
|
$this_column = $candidate_column;
|
385
|
break;
|
386
|
}
|
387
|
}
|
388
|
// Insert the phrase "start date" or "end date" after the label, so
|
389
|
// users can distinguish them in listings (compared to the default
|
390
|
// behavior of field_views_field_default_views_data(), which only
|
391
|
// uses the 'value2' column name to distinguish them).
|
392
|
switch ($this_column) {
|
393
|
case 'value':
|
394
|
// Insert a deliberate double space before 'start date' in the
|
395
|
// translatable string. This is a hack to get it to appear right
|
396
|
// before 'end date' in the listing (i.e., in a non-alphabetical,
|
397
|
// but more user friendly, order).
|
398
|
$result[$table][$column]['title'] = t('@label - start date (!name)', array('@label' => $label, '!name' => $field['field_name']));
|
399
|
$result[$table][$column]['title short'] = t('@label - start date', array('@label' => $label));
|
400
|
break;
|
401
|
case 'value2':
|
402
|
$result[$table][$column]['title'] = t('@label - end date (!name:!column)', array('@label' => $label, '!name' => $field['field_name'], '!column' => $this_column));
|
403
|
$result[$table][$column]['title short'] = t('@label - end date:!column', array('@label' => $label, '!column' => $this_column));
|
404
|
break;
|
405
|
}
|
406
|
}
|
407
|
}
|
408
|
}
|
409
|
}
|
410
|
}
|
411
|
|
412
|
/**
|
413
|
* Implements hook_form_FORM_ID_alter() for views_ui_edit_form().
|
414
|
*/
|
415
|
function date_views_form_views_ui_edit_form_alter(&$form, &$form_state, $form_id) {
|
416
|
// This CSS is needed for the configuration form provided by the Date filter
|
417
|
// (date_views_filter_handler_simple), but we have to add it here so that
|
418
|
// it's already on the edit form the first time a Date filter is being added
|
419
|
// to the View. See http://drupal.org/node/1239228#comment-4885288.
|
420
|
$form['#attached']['css'][] = drupal_get_path('module', 'date_views') . '/css/date_views.css';
|
421
|
}
|
422
|
|
423
|
/**
|
424
|
* The instanceof function makes this work for any handler that was derived
|
425
|
* from 'views_handler_filter_date' or 'views_handler_argument_date',
|
426
|
* which includes core date fields like the node updated field.
|
427
|
*
|
428
|
* The test for $handler->min_date tells us that this is an argument that
|
429
|
* not only is derived from the views date handler but also has been processed
|
430
|
* by the Date Views filter or argument code.
|
431
|
*/
|
432
|
function date_views_handler_is_date($handler, $type = 'argument') {
|
433
|
switch ($type) {
|
434
|
case 'filter':
|
435
|
return $handler instanceof views_handler_filter_date && !empty($handler->min_date);
|
436
|
case 'argument':
|
437
|
return $handler instanceof views_handler_argument_date && !empty($handler->min_date);
|
438
|
}
|
439
|
return FALSE;
|
440
|
}
|
441
|
|
442
|
/**
|
443
|
* Validation hook for exposed filters that use the select widget.
|
444
|
* This is to ensure the the user completes all parts of the date
|
445
|
* not just some parts. Only needed for the select widget.
|
446
|
*/
|
447
|
function date_views_select_validate(&$form, &$form_state) {
|
448
|
// If there are no values just return.
|
449
|
if (empty($form['value']) && empty($form['min'])) {
|
450
|
return;
|
451
|
}
|
452
|
$granularity = (!empty($form['min']['#date_format'])) ? date_format_order($form['min']['#date_format']) : date_format_order($form['value']['#date_format']);
|
453
|
$filled = array();
|
454
|
$value = drupal_array_get_nested_value($form_state['input'], $form['#parents']);
|
455
|
foreach ($granularity as $part) {
|
456
|
if (!empty($value['value'][$part])) {
|
457
|
$filled[] = $part;
|
458
|
}
|
459
|
}
|
460
|
if (!empty($filled) && count($filled) != count($granularity)) {
|
461
|
$unfilled = array_diff($granularity, $filled);
|
462
|
foreach ($unfilled as $part) {
|
463
|
switch ($part) {
|
464
|
case 'year':
|
465
|
form_error($form['value'][$part], t('Please choose a year.'), $form_state);
|
466
|
break;
|
467
|
case 'month':
|
468
|
form_error($form['value'][$part], t('Please choose a month.'), $form_state);
|
469
|
break;
|
470
|
case 'day':
|
471
|
form_error($form['value'][$part], t('Please choose a day.'), $form_state);
|
472
|
break;
|
473
|
case 'hour':
|
474
|
form_error($form['value'][$part], t('Please choose an hour.'), $form_state);
|
475
|
break;
|
476
|
case 'minute':
|
477
|
form_error($form['value'][$part], t('Please choose a minute.'), $form_state);
|
478
|
break;
|
479
|
case 'second':
|
480
|
form_error($form['value'][$part], t('Please choose a second.'), $form_state);
|
481
|
break;
|
482
|
}
|
483
|
}
|
484
|
}
|
485
|
}
|
486
|
|
487
|
/**
|
488
|
* Implements hook_date_formatter_view_alter().
|
489
|
*
|
490
|
* If we are displaying a date from a view, see if we have information about
|
491
|
* which multiple value to display. If so, set the date_id in the entity.
|
492
|
*/
|
493
|
function date_views_date_formatter_pre_view_alter(&$entity, &$variables) {
|
494
|
// Some views have no row index.
|
495
|
if (!empty($entity->view) && isset($entity->view->row_index)) {
|
496
|
$field = $variables['field'];
|
497
|
$date_id = 'date_id_' . $field['field_name'];
|
498
|
$date_delta = 'date_delta_' . $field['field_name'];
|
499
|
$date_item = $entity->view->result[$entity->view->row_index];
|
500
|
if (!empty($date_item->$date_id)) {
|
501
|
$entity->date_id = 'date.' . $date_item->$date_id . '.' . $field['field_name'] . '.' . $date_item->$date_delta . '.0';
|
502
|
}
|
503
|
}
|
504
|
}
|