Projet

Général

Profil

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

root / drupal7 / sites / all / modules / date / date_views / date_views.module @ b720ea3e

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

    
30
  $form['date_views_month_format_with_year'] = array(
31
    '#type' => 'textfield',
32
    '#title' => t('Date views month format with year'),
33
    '#size' => 10,
34
    '#default_value' => variable_get('date_views_month_format_with_year', 'F Y'),
35
    '#description' => t('Date views month format with year, default value : F Y'),
36
  );
37

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

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

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

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

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

    
78
  return system_settings_form($form);
79

    
80
}
81

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

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

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

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

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

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

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

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

    
196
  if (empty($base_tables)) {
197

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

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

    
213
  return $base_tables;
214
}
215

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

    
238
  switch ($field) {
239
    case 'users.created':
240
    case 'users.access':
241
    case 'users.login':
242
    case 'node.created':
243
    case 'node.changed':
244
    case 'node_revision.timestamp':
245
    case 'file_managed.timestamp':
246
    case 'comment.timestamp':
247
      return $values;
248
  }
249
}
250

    
251
/**
252
 * A version of date_real_url that formats links correctly for the new Date pager.
253
 */
254
function date_pager_url($view, $date_type = NULL, $date_arg = NULL, $force_view_url = FALSE, $absolute = TRUE) {
255

    
256
  // If someone adds a pager without a matching argument, there is not date information to work with.
257
  if (empty($view->date_info) || !isset($view->date_info->date_arg_pos)) {
258
    return '';
259
  }
260

    
261
  $args = $view->args;
262
  $pos = $view->date_info->date_arg_pos;
263

    
264
  // The View arguments array is indexed numerically but is not necessarily
265
  // in numerical order. Sort the arguments to ensure the correct order.
266
  ksort($args);
267

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

    
280
  if (!empty($date_type)) {
281
    switch ($date_type) {
282
      case 'year':
283
        $args[$pos] = date_pad($view->date_info->year, 4);
284
        break;
285

    
286
      case 'week':
287
        $args[$pos] = date_pad($view->date_info->year, 4) . '-W' . date_pad($view->date_info->week);
288
        break;
289

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

    
294
      default:
295
        $args[$pos] = date_pad($view->date_info->year, 4) . '-' . date_pad($view->date_info->month);
296
        break;
297
    }
298
  }
299
  elseif (!empty($date_arg)) {
300
    $args[$pos] = $date_arg;
301
  }
302
  else {
303
    $args = $view->args;
304
  }
305
  // Is this an embedded or a block view?
306
  // Return the pager query value.
307
  if (!$force_view_url &&
308
  (!empty($view->preview) || !empty($view->date_info->block_identifier))) {
309

    
310
    $url = $args[$pos];
311
    $key = date_block_identifier($view);
312
    if (!empty($key)) {
313
      return url($_GET['q'], array(
314
        'query' => date_views_querystring($view, array($key => $url)),
315
        'absolute' => $absolute));
316
    }
317
  }
318

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

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

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

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

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

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

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

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

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

    
460
/**
461
 * The instanceof function makes this work for any handler that was derived from 'views_handler_filter_date' or 'views_handler_argument_date', which includes core date fields like the node updated field.
462
 *
463
 * The test for $handler->min_date tells us that this is an argument that not only is derived from the views date handler but also has been processed by the Date Views filter or argument code.
464
 */
465
function date_views_handler_is_date($handler, $type = 'argument') {
466
  switch ($type) {
467
    case 'filter':
468
      return $handler instanceof views_handler_filter_date && !empty($handler->min_date);
469

    
470
    case 'argument':
471
      return $handler instanceof views_handler_argument_date && !empty($handler->min_date);
472
  }
473
  return FALSE;
474
}
475

    
476
/**
477
 * Validation hook for exposed filters that use the select widget.
478
 *
479
 * This is to ensure the the user completes all parts of the date not just some parts. Only needed for the select widget.
480
 */
481
function date_views_select_validate(&$form, &$form_state) {
482
  // If there are no values just return.
483
  if (empty($form['value']) && empty($form['min'])) {
484
    return;
485
  }
486
  $granularity = (!empty($form['min']['#date_format'])) ? date_format_order($form['min']['#date_format']) : date_format_order($form['value']['#date_format']);
487
  $filled = array();
488
  $value = drupal_array_get_nested_value($form_state['input'], $form['#parents']);
489
  foreach ($granularity as $part) {
490
    if (isset($value['value']) && is_numeric($value['value'][$part])) {
491
      $filled[] = $part;
492
    }
493
  }
494
  if (!empty($filled) && count($filled) != count($granularity)) {
495
    $unfilled = array_diff($granularity, $filled);
496
    foreach ($unfilled as $part) {
497
      switch ($part) {
498
        case 'year':
499
          form_error($form['value'][$part], t('Please choose a year.'), $form_state);
500
          break;
501

    
502
        case 'month':
503
          form_error($form['value'][$part], t('Please choose a month.'), $form_state);
504
          break;
505

    
506
        case 'day':
507
          form_error($form['value'][$part], t('Please choose a day.'), $form_state);
508
          break;
509

    
510
        case 'hour':
511
          form_error($form['value'][$part], t('Please choose an hour.'), $form_state);
512
          break;
513

    
514
        case 'minute':
515
          form_error($form['value'][$part], t('Please choose a minute.'), $form_state);
516
          break;
517

    
518
        case 'second':
519
          form_error($form['value'][$part], t('Please choose a second.'), $form_state);
520
          break;
521
      }
522
    }
523
  }
524
}
525

    
526
/**
527
 * Implements hook_date_formatter_view_alter().
528
 *
529
 * If we are displaying a date from a view, see if we have information about which multiple value to display. If so, set the date_id in the entity.
530
 */
531
function date_views_date_formatter_pre_view_alter(&$entity, &$variables) {
532
  // Some views have no row index.
533
  if (!empty($entity->view) && isset($entity->view->row_index)) {
534
    $field = $variables['field'];
535
    $date_id = 'date_id_' . $field['field_name'];
536
    $date_delta = 'date_delta_' . $field['field_name'];
537
    $date_item = $entity->view->result[$entity->view->row_index];
538
    if (!empty($date_item->$date_id)) {
539
      $entity->date_id = 'date.' . $date_item->$date_id . '.' . $field['field_name'] . '.' . $date_item->$date_delta . '.0';
540
    }
541
  }
542
}