Projet

Général

Profil

Paste
Télécharger (19,9 ko) Statistiques
| Branche: | Révision:

root / drupal7 / sites / all / modules / date / date_views / date_views.module @ 599a39cd

1
<?php
2

    
3
/**
4
 * @file
5
 * Date Views module.
6
 */
7

    
8
/**
9
 * Implements hook_menu().
10
 */
11
function date_views_menu() {
12
  // Used to import files from a local filesystem into Drupal.
13
  $items['admin/config/regional/date-time/date-views'] = array(
14
    'title' => 'Date views',
15
    'description' => 'Configure settings for date views.',
16
    'page callback' => 'drupal_get_form',
17
    'page arguments' => array('date_views_settings'),
18
    'access arguments' => array('administer site configuration'),
19
    'type' => MENU_LOCAL_TASK,
20
  );
21

    
22
  return $items;
23
}
24

    
25
/**
26
 * Form callback for date views settings.
27
 */
28
function date_views_settings($form, &$form_state) {
29
  $form['date_views_month_format_with_year'] = array(
30
    '#type' => 'textfield',
31
    '#title' => t('Date views month format with year'),
32
    '#size' => 10,
33
    '#default_value' => variable_get('date_views_month_format_with_year', 'F Y'),
34
    '#description' => t('Date views month format with year, default value : F Y'),
35
  );
36

    
37
  $form['date_views_month_format_without_year'] = array(
38
    '#type' => 'textfield',
39
    '#title' => t('Date views month format without year'),
40
    '#size' => 10,
41
    '#default_value' => variable_get('date_views_month_format_without_year', 'F'),
42
    '#description' => t('Date views month format without year, default value : F'),
43
  );
44

    
45
  $form['date_views_day_format_with_year'] = array(
46
    '#type' => 'textfield',
47
    '#title' => t('Date views day format with year'),
48
    '#size' => 10,
49
    '#default_value' => variable_get('date_views_day_format_with_year', 'l, F j, Y'),
50
    '#description' => t('Date views day format with year, default value : l, F j, Y'),
51
  );
52

    
53
  $form['date_views_day_format_without_year'] = array(
54
    '#type' => 'textfield',
55
    '#title' => t('Date views day format without year'),
56
    '#size' => 10,
57
    '#default_value' => variable_get('date_views_day_format_without_year', 'l, F j'),
58
    '#description' => t('Date views day format without year, default value : l, F j'),
59
  );
60

    
61
  $form['date_views_week_format_with_year'] = array(
62
    '#type' => 'textfield',
63
    '#title' => t('Date views week format with year'),
64
    '#size' => 10,
65
    '#default_value' => variable_get('date_views_week_format_with_year', 'F j, Y'),
66
    '#description' => t('Date views week format with year, default value : F j, Y'),
67
  );
68

    
69
  $form['date_views_week_format_without_year'] = array(
70
    '#type' => 'textfield',
71
    '#title' => t('Date views week format without year'),
72
    '#size' => 10,
73
    '#default_value' => variable_get('date_views_week_format_without_year', 'F j'),
74
    '#description' => t('Date views week format without year, default value : F j'),
75
  );
76

    
77
  return system_settings_form($form);
78
}
79

    
80
/**
81
 * Implements hook_views_api().
82
 */
83
function date_views_theme() {
84
  // This one is used as the base to reduce errors when updating.
85
  $path = drupal_get_path('module', 'date_views');
86
  $base = array(
87
    'file' => 'theme.inc',
88
    'path' => "$path/theme",
89
  );
90

    
91
  return array(
92
    'date_nav_title' => $base + array(
93
      'variables' => array(
94
        'granularity' => NULL,
95
        'view' => NULL,
96
        'link' => NULL,
97
        'format' => NULL,
98
      ),
99
    ),
100
    'date_views_filter_form' => $base + array(
101
      'template' => 'date-views-filter-form',
102
      'render element' => 'form',
103
    ),
104
    'date_calendar_day' => $base + array(
105
      'variables' => array(
106
        'date' => NULL,
107
      ),
108
    ),
109
    'date_views_pager' => $base + array(
110
      'variables' => array(
111
        'plugin' => NULL,
112
        'input' => NULL,
113
      ),
114
      // Register a pattern so that it can work like all views templates.
115
      'pattern' => 'date_views_pager__',
116
      'template' => 'date-views-pager',
117
    ),
118
  );
119
}
120

    
121
/**
122
 * Implements hook_views_api().
123
 */
124
function date_views_views_api() {
125
  return array(
126
    'api' => 3,
127
    'path' => drupal_get_path('module', 'date_views') . '/includes',
128
  );
129
}
130

    
131
/**
132
 * Wrapper function to make sure this function will always work.
133
 */
134
function date_views_views_fetch_fields($base, $type) {
135
  if (!module_exists('views')) {
136
    return array();
137
  }
138
  module_load_include('inc', 'views', 'includes/admin');
139
  return views_fetch_fields($base, $type);
140
}
141

    
142
/**
143
 * Identify all potential date/timestamp fields and cache the data.
144
 */
145
function date_views_fields($base = 'node', $reset = FALSE) {
146
  static $fields = array();
147
  $empty = array('name' => array(), 'alias' => array());
148
  module_load_include('inc', 'date_views', 'includes/date_views_fields');
149

    
150
  if (empty($fields[$base]) || $reset) {
151
    $cid = 'date_views_fields_' . $base;
152
    if (!$reset && $cached = cache_get($cid, 'cache_views')) {
153
      $fields[$base] = $cached->data;
154
    }
155
    else {
156
      $fields[$base] = _date_views_fields($base);
157
    }
158
  }
159
  // Make sure that empty values will be arrays in he expected format.
160
  return !empty($fields) && !empty($fields[$base]) ? $fields[$base] : $empty;
161
}
162

    
163
/**
164
 * Implements hook_date_views_entities().
165
 */
166
function date_views_date_views_extra_tables() {
167
  // Map extra Views tables to the entity that holds its date fields, needed
168
  // for Views tables other than the primary tables identified in entity_info().
169
  return array(
170
    'node_revision' => 'node',
171
  );
172
}
173

    
174
/**
175
 * Helper function to map entity types to the Views base table they use.
176
 *
177
 * Used to make it easier to infer the entity type from a base table.
178
 *
179
 * Views has a new handler called views_handler_field_entity() that loads
180
 * entities.
181
 *
182
 * And you can use something like the following to get the entity type from a
183
 * view, but not all our base tables contain the entity information we need,
184
 * (i.e. revisions).
185
 *
186
 * So it won't work here and we resort to creating information from
187
 * entity_get_info().
188
 *
189
 *   // A method to get the entity type for a base table.
190
 *   $table_data = views_fetch_data($base_table);
191
 *   if (!isset($table_data['table']['base']['entity type'])) {
192
 *     return FALSE;
193
 *   }
194
 *   $entity_type = $table_data['table']['base']['entity type'];
195
 */
196
function date_views_base_tables() {
197
  $base_tables = &drupal_static(__FILE__, array());
198

    
199
  if (empty($base_tables)) {
200
    // First we get the base tables we can learn about from entity_info.
201
    $entity_info = entity_get_info();
202
    foreach ($entity_info as $entity_type => $info) {
203
      if (!empty($info['base table'])) {
204
        $base_tables[$info['base table']] = $entity_type;
205
      }
206
      if (!empty($info['revision table'])) {
207
        $base_tables[$info['revision table']] = $entity_type;
208
      }
209
    }
210

    
211
    // Then we let other modules tell us about other entity tables that hold
212
    // date fields.
213
    $base_tables += module_invoke_all('date_views_extra_tables');
214
  }
215

    
216
  return $base_tables;
217
}
218

    
219
/**
220
 * Implements hook_date_views_fields().
221
 */
222
function date_views_date_views_fields($field) {
223
  // All modules that create custom fields that use the
224
  // 'views_handler_field_date' handler can provide additional information here
225
  // about the type of date they create so the date can be used by the Date API
226
  // views date argument and date filter.
227
  $values = array(
228
    // The type of date: DATE_UNIX, DATE_ISO, DATE_DATETIME.
229
    'sql_type' => DATE_UNIX,
230
    // Timezone handling options: 'none', 'site', 'date', 'utc' .
231
    'tz_handling' => 'site',
232
    // Needed only for dates that use 'date' tz_handling.
233
    'timezone_field' => '',
234
    // Needed only for dates that use 'date' tz_handling.
235
    'offset_field' => '',
236
    // Array of "table.field" values for related fields that should be
237
    // loaded automatically in the Views SQL.
238
    'related_fields' => array(),
239
    // Granularity of this date field's db data.
240
    'granularity' => array('year', 'month', 'day', 'hour', 'minute', 'second'),
241
  );
242

    
243
  switch ($field) {
244
    case 'users.created':
245
    case 'users.access':
246
    case 'users.login':
247
    case 'node.created':
248
    case 'node.changed':
249
    case 'node_revision.timestamp':
250
    case 'file_managed.timestamp':
251
    case 'comment.timestamp':
252
      return $values;
253
  }
254
}
255

    
256
/**
257
 * A version of date_real_url that formats links correctly for the Date pager.
258
 */
259
function date_pager_url($view, $date_type = NULL, $date_arg = NULL, $force_view_url = FALSE, $absolute = TRUE) {
260
  // If someone adds a pager without a matching argument, there is not date
261
  // information to work with.
262
  if (empty($view->date_info) || !isset($view->date_info->date_arg_pos)) {
263
    return '';
264
  }
265

    
266
  $args = $view->args;
267
  $pos = $view->date_info->date_arg_pos;
268

    
269
  // The View arguments array is indexed numerically but is not necessarily in
270
  // numerical order. Sort the arguments to ensure the correct order.
271
  ksort($args);
272

    
273
  // If there are empty arguments before the date argument, pad them with the
274
  // wildcard so the date argument will be in the right position.
275
  if (count($args) < $pos) {
276
    foreach ($view->argument as $name => $argument) {
277
      if ($argument->position == $pos) {
278
        break;
279
      }
280
      $args[] = $argument->options['exception']['value'];
281
    }
282
  }
283

    
284
  if (!empty($date_type)) {
285
    switch ($date_type) {
286
      case 'year':
287
        $args[$pos] = date_pad($view->date_info->year, 4);
288
        break;
289

    
290
      case 'week':
291
        $args[$pos] = date_pad($view->date_info->year, 4) . '-W' . date_pad($view->date_info->week);
292
        break;
293

    
294
      case 'day':
295
        $args[$pos] = date_pad($view->date_info->year, 4) . '-' . date_pad($view->date_info->month) . '-' . date_pad($view->date_info->day);
296
        break;
297

    
298
      default:
299
        $args[$pos] = date_pad($view->date_info->year, 4) . '-' . date_pad($view->date_info->month);
300
    }
301
  }
302
  elseif (!empty($date_arg)) {
303
    $args[$pos] = $date_arg;
304
  }
305
  else {
306
    $args = $view->args;
307
  }
308

    
309
  // Is this an embedded or a block view? Return the pager query value.
310
  if (!$force_view_url &&
311
      (!empty($view->preview) || !empty($view->date_info->block_identifier))) {
312
    $url = $args[$pos];
313
    $key = date_block_identifier($view);
314
    if (!empty($key)) {
315
      return url($_GET['q'], array(
316
        'query' => date_views_querystring($view, array($key => $url)),
317
        'absolute' => $absolute));
318
    }
319
  }
320

    
321
  // Normal views may need querystrings appended to them if they use exposed
322
  // filters.
323
  return url($view->get_url($args), array(
324
    'query' => date_views_querystring($view),
325
    'absolute' => $absolute,
326
    )
327
  );
328
}
329

    
330
/**
331
 * Identifier of a date block.
332
 */
333
function date_block_identifier($view) {
334
  if (!empty($view->block_identifier)) {
335
    return $view->block_identifier;
336
  }
337
  return isset($view->date_info->block_identifier) ? $view->date_info->block_identifier : NULL;
338
}
339

    
340
/**
341
 * Implements hook_field_views_data_alter().
342
 */
343
function date_views_field_views_data_alter(&$result, $field, $module) {
344
  // Create a Views field for each date column we care about to supplement the
345
  // generic 'entity_id' and 'revision_id' fields that are automatically
346
  // created. Also use friendlier labels to distinguish the start date and end
347
  // date in listings (for fields that use both).
348
  if ($module == 'date') {
349
    $has_end_date = !empty($field['settings']['todate']);
350
    if ($has_end_date) {
351
      $labels = field_views_field_label($field['field_name']);
352
      $label = array_shift($labels);
353
    }
354
    foreach ($result as $table => $data) {
355
      $additional = array();
356
      $field_name = $field['field_name'];
357
      foreach ($data as $column => $value) {
358

    
359
        // The old 'entity_id' and 'revision_id' values got rewritten in Views.
360
        // The old values are still there with a 'moved to' key, so ignore them.
361
        if (array_key_exists('field', $value) && !array_key_exists('moved to', $value['field'])) {
362
          $result[$table][$column]['field']['is date'] = TRUE;
363
          // Not sure yet if we still need a custom field handler in D7 now
364
          // that custom formatters are available. Might still need it to
365
          // handle grouping of multiple value dates.
366
          // $result[$table][$column]['field']['handler'] = 'date_handler_field_date';
367
          // $result[$table][$column]['field']['add fields to query'] = TRUE;
368
        }
369

    
370
        // For filters, arguments, and sorts, determine if this column is for
371
        // the start date ('value') or the end date ('value2').
372
        $this_column = NULL;
373
        foreach (array_keys($field['columns']) as $candidate_column) {
374
          if ($column == $field['field_name'] . '_' . $candidate_column) {
375
            $this_column = $candidate_column;
376
            break;
377
          }
378
        }
379

    
380
        // Only alter the date fields, not timezone, rrule, offset, etc.
381
        if ($this_column != 'value' && $this_column != 'value2') {
382
          continue;
383
        }
384

    
385
        // We will replace the label with a friendlier name in the case of
386
        // arguments, filters, and sorts (but only if this field uses an end
387
        // date).
388
        $replace_label = FALSE;
389
        if (array_key_exists('argument', $value)) {
390
          $result[$table][$column]['argument']['handler'] = 'date_views_argument_handler_simple';
391
          $result[$table][$column]['argument']['empty field name'] = t('Undated');
392
          $result[$table][$column]['argument']['is date'] = TRUE;
393
          $replace_label = $has_end_date;
394
        }
395
        if (array_key_exists('filter', $value)) {
396
          $result[$table][$column]['filter']['handler'] = 'date_views_filter_handler_simple';
397
          $result[$table][$column]['filter']['empty field name'] = t('Undated');
398
          $result[$table][$column]['filter']['is date'] = TRUE;
399
          $replace_label = $has_end_date;
400
        }
401
        if (array_key_exists('sort', $value)) {
402
          $result[$table][$column]['sort']['is date'] = TRUE;
403
          $replace_label = $has_end_date;
404
        }
405
        if ($replace_label) {
406
          // Determine if this column is for the start date ('value') or the
407
          // end date ('value2').
408
          $this_column = NULL;
409
          foreach (array_keys($field['columns']) as $candidate_column) {
410
            if ($column == $field['field_name'] . '_' . $candidate_column) {
411
              $this_column = $candidate_column;
412
              break;
413
            }
414
          }
415
          // Insert the phrase "start date" or "end date" after the label, so
416
          // users can distinguish them in listings (compared to the default
417
          // behavior of field_views_field_default_views_data(), which only
418
          // uses the 'value2' column name to distinguish them).
419
          switch ($this_column) {
420
            case 'value':
421
              // Insert a deliberate double space before 'start date' in the
422
              // translatable string. This is a hack to get it to appear right
423
              // before 'end date' in the listing (i.e., in a non-alphabetical,
424
              // but more user friendly, order).
425
              $result[$table][$column]['title'] = t('@label -  start date (!name)', array(
426
                '@label' => $label,
427
                '!name' => $field['field_name'],
428
              ));
429
              $result[$table][$column]['title short'] = t('@label -  start date', array(
430
                '@label' => $label,
431
              ));
432
              break;
433

    
434
            case 'value2':
435
              $result[$table][$column]['title'] = t('@label - end date (!name:!column)', array(
436
                '@label' => $label,
437
                '!name' => $field['field_name'],
438
                '!column' => $this_column,
439
              ));
440
              $result[$table][$column]['title short'] = t('@label - end date:!column', array(
441
                '@label' => $label,
442
                '!column' => $this_column,
443
              ));
444
              break;
445
          }
446
        }
447
      }
448
    }
449
  }
450
}
451

    
452
/**
453
 * Implements hook_form_FORM_ID_alter() for views_ui_edit_form().
454
 */
455
function date_views_form_views_ui_edit_form_alter(&$form, &$form_state, $form_id) {
456
  // This CSS is needed for the configuration form provided by the Date filter
457
  // (date_views_filter_handler_simple), but we have to add it here so that
458
  // it's already on the edit form the first time a Date filter is being added
459
  // to the View. See http://drupal.org/node/1239228#comment-4885288.
460
  $form['#attached']['css'][] = drupal_get_path('module', 'date_views') . '/css/date_views.css';
461
}
462

    
463
/**
464
 * Implements hook_form_FORM_ID_alter() for views_exposed_form().
465
 */
466
function date_views_form_views_exposed_form_alter(&$form, &$form_state, $form_id) {
467
  $children = element_children($form);
468
  // @todo On a stock views_exposed_form, there won't be any grandchildren
469
  // items, but will this always be the case? How about better_exposed_filters?
470
  foreach ($children as $child) {
471
    if (isset($form[$child]['#id']) && strpos($form[$child]['#id'], 'date_views_exposed_filter-') === 0) {
472
      // Remove empty or scalar date input when an array was expected.
473
      if (empty($form_state['input'][$child]) || !is_array($form_state['input'][$child])) {
474
        unset($form_state['input'][$child]);
475
      }
476
      elseif (empty($form_state['input'][$child]['value']) || !is_array($form_state['input'][$child]['value'])) {
477
        unset($form_state['input'][$child]['value']);
478
      }
479
    }
480
  }
481
}
482

    
483
/**
484
 * Work out if the plugin is a date.
485
 *
486
 * The instanceof function makes this work for any handler that was derived from
487
 * 'views_handler_filter_date' or 'views_handler_argument_date', which includes
488
 * core date fields like the node updated field.
489
 *
490
 * The test for $handler->min_date tells us that this is an argument that not
491
 * only is derived from the views date handler but also has been processed by
492
 * the Date Views filter or argument code.
493
 */
494
function date_views_handler_is_date($handler, $type = 'argument') {
495
  switch ($type) {
496
    case 'filter':
497
      return $handler instanceof views_handler_filter_date && !empty($handler->min_date);
498

    
499
    case 'argument':
500
      return $handler instanceof views_handler_argument_date && !empty($handler->min_date);
501
  }
502
  return FALSE;
503
}
504

    
505
/**
506
 * Validation hook for exposed filters that use the select widget.
507
 *
508
 * This is to ensure the the user completes all parts of the date not just some
509
 * parts. Only needed for the select widget.
510
 */
511
function date_views_select_validate(&$form, &$form_state) {
512
  // If there are no values just return.
513
  if (empty($form['value']) && empty($form['min'])) {
514
    return;
515
  }
516
  $granularity = (!empty($form['min']['#date_format'])) ? date_format_order($form['min']['#date_format']) : date_format_order($form['value']['#date_format']);
517
  $filled = array();
518
  $value = drupal_array_get_nested_value($form_state['input'], $form['#parents']);
519
  foreach ($granularity as $part) {
520
    if (isset($value['value']) && is_numeric($value['value'][$part])) {
521
      $filled[] = $part;
522
    }
523
  }
524
  if (!empty($filled) && count($filled) != count($granularity)) {
525
    $unfilled = array_diff($granularity, $filled);
526
    foreach ($unfilled as $part) {
527
      switch ($part) {
528
        case 'year':
529
          form_error($form['value'][$part], t('Please choose a year.'), $form_state);
530
          break;
531

    
532
        case 'month':
533
          form_error($form['value'][$part], t('Please choose a month.'), $form_state);
534
          break;
535

    
536
        case 'day':
537
          form_error($form['value'][$part], t('Please choose a day.'), $form_state);
538
          break;
539

    
540
        case 'hour':
541
          form_error($form['value'][$part], t('Please choose an hour.'), $form_state);
542
          break;
543

    
544
        case 'minute':
545
          form_error($form['value'][$part], t('Please choose a minute.'), $form_state);
546
          break;
547

    
548
        case 'second':
549
          form_error($form['value'][$part], t('Please choose a second.'), $form_state);
550
          break;
551
      }
552
    }
553
  }
554
}
555

    
556
/**
557
 * Implements hook_date_formatter_view_alter().
558
 */
559
function date_views_date_formatter_pre_view_alter(&$entity, &$variables) {
560
  // If we are displaying a date from a view, see if we have information about
561
  // which multiple value to display. If so, set the date_id in the entity.
562
  // Some views have no row index.
563
  if (!empty($entity->view) && isset($entity->view->row_index)) {
564
    $field = $variables['field'];
565
    $date_id = 'date_id_' . $field['field_name'];
566
    $date_delta = 'date_delta_' . $field['field_name'];
567
    $date_item = $entity->view->result[$entity->view->row_index];
568
    if (!empty($date_item->$date_id)) {
569
      $entity->date_id = 'date.' . $date_item->$date_id . '.' . $field['field_name'] . '.' . $date_item->$date_delta . '.0';
570
    }
571
  }
572
}