summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--date.css20
-rw-r--r--date/date.views.inc1312
-rw-r--r--date/date_admin.inc17
-rw-r--r--date_api.module1
-rw-r--r--date_api.views.inc964
-rw-r--r--date_api_sql.inc281
-rw-r--r--theme/date-navigation.tpl.php42
-rw-r--r--theme/theme.inc62
8 files changed, 1385 insertions, 1314 deletions
diff --git a/date.css b/date.css
index 8b65af7..7200be3 100644
--- a/date.css
+++ b/date.css
@@ -84,4 +84,24 @@ span.date-display-separator {
float:left !important;
margin-right:2px !important;
padding:0 !important;
+}
+
+.date-nav {
+ width:100%;
+ display:relative;
+}
+.date-nav div.prev {
+ text-align: left;
+ width:24%;
+ float:left;
+}
+.date-nav div.next {
+ text-align: right;
+ width:24%;
+ float:right;
+}
+.date-nav div.heading {
+ text-align:center;
+ width:50%;
+ float:left;
} \ No newline at end of file
diff --git a/date/date.views.inc b/date/date.views.inc
index e544d93..13bb990 100644
--- a/date/date.views.inc
+++ b/date/date.views.inc
@@ -1,1314 +1,2 @@
<?php
// $Id$
-
-views_include_handlers();
-
-/**
- * Implementation of hook_views_data().
- */
-function _date_views_data($field) {
- $data = content_views_field_views_data($field);
- $db_info = content_database_info($field);
- $table_alias = content_views_tablename($field);
-
- // Swap the filter and argument handlers to the date handlers.
- $data[$table_alias][$field['field_name'] .'_value']['filter']['handler'] = 'date_views_filter_handler';
- $data[$table_alias][$field['field_name'] .'_value']['argument']['handler'] = 'date_views_argument_handler';
- $data[$table_alias][$field['field_name'] .'_value']['argument']['empty name field'] = t('Undated');
-
- // Add in another set of fields, filters, and aguments for the To date.
- if ($field['todate']) {
- $data[$table_alias][$field['field_name'] .'_value2'] = $data[$table_alias][$field['field_name'] .'_value'];
- $data[$table_alias][$field['field_name'] .'_value']['title'] = t('!label (!field value)', array('!label' => t($field['widget']['label']), '!field' => t($field['field_name'])));
- $data[$table_alias][$field['field_name'] .'_value2']['title'] = t('!label (!field value2)', array('!label' => t($field['widget']['label']), '!field' => t($field['field_name'])));
- }
-
- // Create another argument for the date browser, using the configuration already created.
- $data[$table_alias][$field['field_name'] .'_browser'] = $data[$table_alias][$field['field_name'] .'_value'];
- $data[$table_alias][$field['field_name'] .'_browser']['title'] = t('Date Browser (!field value)', array('!field' => t($field['field_name'])));
- $data[$table_alias][$field['field_name'] .'_browser']['argument']['handler'] = 'date_views_browser_argument_handler';
- unset($data[$table_alias][$field['field_name'] .'_browser']['field']);
- unset($data[$table_alias][$field['field_name'] .'_browser']['filter']);
- unset($data[$table_alias][$field['field_name'] .'_browser']['sort']);
- if ($field['todate']) {
- $data[$table_alias][$field['field_name'] .'_browser2'] = $data[$table_alias][$field['field_name'] .'_value2'];
- $data[$table_alias][$field['field_name'] .'_browser2']['title'] = t('Date Browser (!field value2)', array('!field' => t($field['field_name'])));
- $data[$table_alias][$field['field_name'] .'_browser2']['argument']['handler'] = 'date_views_browser_argument_handler';
- unset($data[$table_alias][$field['field_name'] .'_browser2']['field']);
- unset($data[$table_alias][$field['field_name'] .'_browser2']['filter']);
- unset($data[$table_alias][$field['field_name'] .'_browser2']['sort']);
- }
-
- return $data;
-}
-
-/**
- * The subclass simply adds properties,
- * for field-specific subclasses to use if they need to.
- */
-class date_views_argument_handler extends views_handler_argument_date2 {
- var $content_field;
-
- function construct() {
- parent::construct();
-
- $this->content_field = content_fields($this->definition['content_field_name']);
- $this->additional_fields = $this->definition['additional fields'];
- }
-}
-
-/**
- * The subclass simply adds properties,
- * for field-specific subclasses to use if they need to.
- */
-class date_views_filter_handler extends views_handler_filter_flexible_date {
- var $content_field;
-
- function construct() {
- parent::construct();
-
- $this->content_field = content_fields($this->definition['content_field_name']);
- $this->additional_fields = $this->definition['additional fields'];
- }
-}
-
-/**
- *
- * Flexible date range argument handler
- *
- * Argument is based on ISO 8601 date duration and time interval standards
- *
- * See http://en.wikipedia.org/wiki/ISO_8601#Week_dates for definitions of ISO weeks
- * See http://en.wikipedia.org/wiki/ISO_8601#Duration for definitions of ISO duration and time interval
- *
- * Argument expects a value like 2006-01-01--2006-01-15, or 2006-W24, or @P1W
- * Separate from and to dates or date and period with a double hyphen (--)
- *
- * From and to dates in argument are ISO dates, but can be shortened and missing parts will be added
- * Omitted parts of ISO dates will be assumed to be the first possible (for the from date)
- * or the last possible (for the to date) value in that time period
- *
- * The 'to' portion of the argument can be eliminated if it is the same as the 'from' portion
- * Use @ instead of a date to substitute in the current date and time.
- *
- * Use periods (P1H, P1D, P1W, P1M, P1Y) to get next hour/day/week/month/year from now
- * Use date before P sign to get next hour/day/week/month/year from that date
- *
- * This module does not currently handle the option of using a period with an end date,
- * only a start date followed by a period.
- *
- * The groupby selector values are used only if a summary view of the argument is requested
- * possible values are by year, by month, by week, by day, and by hour
- *
- * if summaries are used, navigating to the view with no argument will display subtotals for the query,
- * grouped by the selected range, with a link to the complete query for each range
- *
- */
-/**
- * A flexible, configurable date argument.
- *
- * This argument allows you to set one or more date parts to filter on,
- * such as year, month, and day; month only; a complete date, etc.
- *
- * @ingroup views_argument_handlers
- */
-class date_views_browser_argument_handler extends views_handler_argument_formula {
- var $content_field;
- var $date_handler = NULL;
-
- /**
- * Add date handler to the argument.
- */
- function construct() {
- parent::construct();
- $this->content_field = content_fields($this->definition['content_field_name']);
- $this->additional_fields = $this->definition['additional fields'];
- $this->date_handler = new views_date_handler();
- $this->date_handler->construct();
- if (isset($this->definition['content_field'])) {
- $this->date_handler->date_type = $this->definition['content_field']['type'];
- }
- }
-
- /**
- * Get granularity and use it to create the formula and a format
- * for the results.
- */
- function init(&$view, $options) {
- parent::init($view, $options);
- $handler = $this->date_handler;
- $handler->granularity = $handler->granularity_keys($options['granularity']);
- switch ($handler->granularity[0]) {
- case('year'):
- $this->format = 'Y';
- $this->sql_format = 'Y';
- break;
- case('month'):
- $this->format = 'F Y';
- $this->sql_format = 'Y-m';
- break;
- case('day'):
- $this->format = 'F j Y';
- $this->sql_format = 'Y-m-d';
- break;
- case('hour'):
- $this->format = 'F j Y - H';
- $this->sql_format = 'Y-m-d\TH';
- break;
- case('week'):
- $this->format = 'F j Y (W)';
- $this->sql_format = 'Y-\WW';
- break;
- }
- }
-
- /**
- * Default value for the granularity option.
- */
- function options(&$options) {
- parent::options($options);
- $parts = $this->date_handler->date_parts();
- unset($parts['adjustment']);
- $options['granularity'] = 'month';
- }
-
- /**
- * Add a form element to select granularity.
- */
- function options_form(&$form, &$form_state) {
- parent::options_form($form, $form_state);
- // Select the granularity of the date parts to use in the argument.
- $handler = $this->date_handler;
- $form['granularity'] = $handler->granularity_form($this->options['granularity']);
- $form['granularity']['#description'] = t('Time range for each page, i.e. if you use a \'Month\' date range, the page will show the selected month and the back/next links will take you to the next and previous month.');
- $form['granularity']['#type'] = 'select';
- unset($form['granularity']['#options']['second']);
- unset($form['granularity']['#options']['minute']);
- }
-
- // Update the summary values to show selected granularity.
- function admin_summary() {
- $handler = $this->date_handler;
- $granularity = array_values($handler->date_parts($handler->granularity));
- return t('<br />Grouped by: @granularity', array('@granularity' => $granularity[0]));
- }
-
- /**
- * Provide a link to the next level of the view
- */
- function summary_name($data) {
- $created = $data->{$this->name_alias};
- return format_date(strtotime($created), 'custom', $this->format, 0);
- }
-
- /**
- * Provide a link to the next level of the view
- */
- function title() {
- return format_date(strtotime($this->argument), 'custom', $this->format, 0);
- }
-
- /**
- * Create a summary query that matches the granularity.
- *
- * Needed or Views will do a groupby on the complete date instead
- * of only the part of the date actually used in the argument.
- */
- function summary_query() {
- $this->ensure_my_table();
- $handler = $this->date_handler;
-
- // We need to alter the base alias so the groupby clause will group by
- // the relevant part of the date instead of a complete date.
- $this->formula = $handler->sql_format($this->sql_format, "$this->table_alias.$this->real_field");
-
- $alias = $this->name_alias = $this->table_alias ."_". $this->real_field;
- $this->query->add_field(NULL, $this->get_formula(), $alias);
- $this->base_alias = $this->get_formula();
-
- return $this->summary_basics();
- }
-
- /**
- * Need to override the basic link since base_alias is now a formula.
- */
- function summary_link($data, $url) {
- $value = $data->{$this->name_alias};
- return url("$url/$value");
- }
-
- /**
- * Create a query that matches the argument.
- *
- * Move through the arg and pick out date values to add to the query.
- */
- function query() {
- $this->ensure_my_table();
- $range = date_views_date_range($this->argument, $this->content_field);
- $handler = $this->date_handler;
- $this->query->add_where(0, $handler->sql_field("$this->table_alias.$this->real_field") .">='". str_replace('T', ' ', $range[0]) ."'");
- $this->query->add_where(0, $handler->sql_field("$this->table_alias.$this->real_field") ."<='". str_replace('T', ' ', $range[1]) ."'");
- }
-}
-
-/**
- * Deconstruct a Date Browser argument into a date range.
- *
- * @param unknown_type $arg
- * @param unknown_type $field
- * @return unknown
- */
-function date_views_date_range($arg, $field = NULL) {
- if (stristr($arg, 'P')) {
- // for a date plus value, get the min and max values
- $range = date_plus_period_range($arg);
- $min_date = $range[0];
- $max_date = $range[1];
- }
- elseif (stristr($arg, '-W') && !stristr($arg, '--')) {
- // for a specified week, get the min and max values
- $range = date_iso_week_range($arg);
- $min_date = $range[0];
- $max_date = $range[1];
- }
- else {
- // for all other get the date range from the supplied argument
- $range = (array) explode('--', $arg);
- $min_date = date_range_value($range[0], 'min');
- $max_date = date_range_value((isset($range[1]) ? $range[1] : $range[0]), 'max');
- }
- if (!empty($field)) {
- $min_date = date_limit_value($min_date, date_granularity($field), $field['type']);
- $max_date = date_limit_value($max_date, date_granularity($field), $field['type']);
- }
- return array($min_date, $max_date);
-}
-
-/**
- * Compute min and max dates for a week
- *
- * based on ISO weeks, which start counting on the first Monday in a week that
- * has at least 4 days in the current year
- *
- * January 4 is always in the first ISO week of the year. Move ahead by the
- * number of ISO weeks to find a date in the ISO week. Find the Monday of the
- * ISO week for the first second of the week, move ahead 1 week and back
- * 1 second to find last second of the week.
- *
- * @value - an argument in the format 2006-W20 (year + -W + week number)
- * @return an array of ISO dates representing the first and last day in the week
- */
-function date_iso_week_range($value) {
- $parts = explode('-W', $value);
- $year = $parts[0];
- $week = $parts[1];
- $date = date_make_date($year .'-01-04 00:00:00', 'UTC');
- date_modify($date, '+'. $week .' weeks');
- date_modify($date, '-1 Monday');
- $min_date = date_format($date, DATE_FORMAT_ISO);
- date_modify($date, '+1 week');
- date_modify($date, '-1 second');
- $max_date = date_format($date, DATE_FORMAT_ISO);
- return array($min_date, $max_date);
-}
-
-/**
- * Compute min and max dates for a P value
- *
- * Min date is whatever is to the left of the period sign, defaults to
- * current date. Use ical module to parse the period. Set end date to
- * 1 second before the end of the period, since we use <= operator.
- *
- * @value = an argument in the format (start date)P#(period type)
- * where (period type) can be Y (year), M (month), D (day), W (week), H (hour)
- * i.e. P1Y or P90D or P1Y3M2D4H
- * @return an array of ISO dates representing the first and last day in the range
- */
-function date_plus_period_range($value) {
- include_once('./'. drupal_get_path('module', 'date_api') .'/date_api_ical.inc');
- $value = str_replace('--P', 'P', $value);
- $range = explode('P', $value);
-
- $period = substr($range[1], -1);
- switch ($period) {
- case 'Y':
- $granularity = array('year');
- break;
- case 'M':
- $granularity = array('year', 'month');
- break;
- case 'D':
- $granularity = array('year', 'month', 'day');
- break;
- case 'W':
- $granularity = array('year');
- break;
- case 'H':
- $granularity = array('year', 'month', 'day', 'hour');
- }
-
- $min_date = date_range_value($range[0], 'min');
- $parsed = array('DURATION' => array(
- 'DATA' => 'P'. $range[1]),
- 'DTSTART' => array(
- 'datetime' => date_convert($min_date, DATE_ISO, DATE_DATETIME),
- 'tz' => 'UTC',
- 'granularity' => $granularity,
- ),
- );
- date_ical_parse_duration($parsed);
- $max_date = date_convert($parsed['DTEND']['datetime'], DATE_DATETIME, DATE_ISO);
- return array($min_date, $max_date);
-}
-
-/**
- * Validate and pad date range argument element
- *
- * @param $value - a full or partial ISO date from an argument
- * @param $value_type - min or max, whether it is the from or the to part of the range
- * @return complete, validated ISO date
- */
-function date_range_value($value, $value_type = 'min') {
- $now = date_format(date_now(), DATE_FORMAT_ISO);
- if (trim($value) == '@' || trim($value) == '') return $now;
-
- switch (strlen($value)) {
- case(4):
- $return = ($value_type == 'min' ? $value .'-01-01T00:00:00' : $value .'-12-31T23:59:59');
- break;
- case(7):
- $return = ($value_type == 'min' ? $value .'-01T00:00:00' : $value .'-31T23:59:59');
- break;
- case(10):
- $return = ($value_type == 'min' ? $value .'T00:00:00' : $value .'T23:59:59');
- break;
- case(13):
- $return = ($value_type == 'min' ? $value .':00:00' : $value .':59:59');
- break;
- case(16):
- $return = ($value_type == 'min' ? $value .':00' : $value .':59');
- break;
- case(19):
- $return = $value;
- break;
- default:
- $return = $now;
- }
- // use regex to test for validity of constructed date
- return (preg_match(DATE_REGEX_ISO, $return) ? $return : $now);
-}
-
-/**
- * Define groupby options for date range summaries
- */
-function date_range_arg_options() {
- return array(
- 'year' => t('summarize by year'),
- 'month' => t('summarize by month'),
- 'day' => t('summarize by day'),
- 'week' => t('summarize by week'),
- 'hour' => t('summarize by hour')
- );
-}
-
-//============================== Date Browser ================================//
-
-/**
- * Works only with views that use the date range argument
- * Adds this/next period navigation links to a date argument range view
- * Adds 'week of XXX', 'month of XXX' headings to views and blocks
- * Defaults blocks and views w/out arguments to current period to start paging
- * Choose period increments by selecting the option value of date range argument
- * (year, month, week, day, hour)
- */
-
-/**
- * Implementation of hook_views_style_plugins()
- */
-function _date_views_style_plugins() {
-
- $items = array();
- $items['date_views_browser'] = array(
- 'name' => t('Date: Date Browser'),
- 'theme' => 'date_views_browser_full_view',
- 'summary_theme' => 'date_views_browser_summary_view',
- 'needs_fields' => true,
- 'needs_table_header' => true,
- 'validate' => 'date_browser_validate',
- 'even_empty' => true,
- );
- return $items;
-}
-
-/**
- * Validate a view.
- */
-function date_browser_validate($type, $view, $form) {
- // list (and table) modes require there to be at least 1 field active.
- if (is_array($view['field'])) {
- $fields = array_filter(array_keys($view['field']), 'is_numeric');
- }
- if (!$fields) {
- form_error($form["$type-info"][$type .'_type'], t('The Date Browser requires at least one field.'));
- }
- // Make sure all arguments are set to 'Display all values'
- // and that a date argument has been provided.
- $found = FALSE;
- $options = array_keys(date_range_arg_options());
- foreach ($view['argument'] as $delta => $argument) {
- if (in_array($argument['options'], $options)) {
- $found = TRUE;
- if (is_numeric($delta) && $argument['argdefault'] != 2) {
- form_error($form['argument'][$delta]['argdefault'], t('Date Browser arguments must be set to \'Display All Values\'.'));
- }
- }
- }
- if (!$found) {
- form_error($form['argument'], t('A date argument must be added to a Date Browser view.'));
- }
-}
-
-/**
- * Implementation of hook_views_query()
- * Used to make sure view defaults to current date if no date selected
- */
-function _date_views_query_alter(&$query, &$view) {
- include_once('./'. drupal_get_path('module', 'date_api') .'/date_api_sql.inc');
- $date_views_browser_views = date_views_browser_get_views();
- if (in_array($view->name, array_keys($date_views_browser_views))) {
- $name = explode(':', $view->argument[0]['type']);
- $field_name = trim($name[1]);
- $field_name = substr($field_name, 0, 3) == 'to|' ? substr($field_name, 3) : $field_name;
- $field = content_fields($field_name);
- $field_type = $field['type'] == DATE_UNIX ? 'int' : 'iso';
- $db_info = content_database_info($field);
- $table = 'node_data_'. $field['field_name'];
-
- // Add a combo FROM|TO date field to the query
- $value = $db_info['columns']['value']['column'];
- $value2 = !empty($db_info['columns']['value2']['column']) ? $db_info['columns']['value2']['column'] : $db_info['columns']['value']['column'];
- $combo = date_sql_concat(array($table .'.'. $value, "'|'", $table .'.'. $value2)) .' AS date_combo ';
- $query->add_field($combo, NULL);
-
- $path = explode('/', $view->url);
- $pos = sizeof($path);
- if ($view->build_type == 'block' || arg($pos) == '') {
- $arg = NULL;
- }
- else {
- $arg = arg($pos);
- }
- if ($arg == NULL) {
- // if no argument specified, add the current date range to the query
- $arg = date_views_browser_period_arg($arg, $view->argument[0]['options']);
- if ($range = date_views_date_range($arg)) {
- $query->ensure_table($table);
- $query->add_field('nid', 'node');
- $query->add_field($value, $table);
- $query->add_where(date_sql('DATE', $table .'.'. $value, $field_type, $offset) .">='". str_replace('T', ' ', $range[0]) ."'");
- $query->add_where(date_sql('DATE', $table .'.'. $value, $field_type, $offset) ."<='". str_replace('T', ' ', $range[1]) ."'");
- }
- }
- }
-}
-
-/**
- * Find all the views that qualify for date browser treatment
- *
- * @param $view
- * if called from Views UI validation, $edit_view will be populated,
- * otherwise it will be empty. Use that to tell if the list
- * needs to be refreshed.
- */
-function date_views_browser_get_views($reset = FALSE) {
- static $date_views_browser_views;
- if (empty($date_views_browser_views) || $reset) {
- $cid = 'date_browser_views';
- if (!$reset && $cached = cache_get($cid, 'cache_views')) {
- $date_views_browser_views = $cached->data;
- }
- else {
- $date_views_browser_views = array();
- $arguments = array();
- $fields = content_fields();
- foreach ($fields as $field) {
- if ($field['type'] == DATE_UNIX || $field['type'] == DATE_ISO) {
- $arguments = array_merge($arguments, _date_views_arguments($field));
- }
- }
- $argument_list = "'". implode("','", array_keys($arguments)) ."'";
- if (!$argument_list) {
- return array();
- }
- $result = db_query("SELECT arg.*, view.name FROM {view_argument} arg INNER JOIN {view_view} view ON arg.vid=view.vid WHERE arg.type IN ($argument_list) AND view.page_type='date_views_browser'");
- while ($view = db_fetch_object($result)) {
- $date_views_browser_views[$view->name] = $view;
- }
- cache_set($cid, $date_views_browser_views, 'cache_views');
- }
- }
- return $date_views_browser_views;
-}
-
-/**
- * Return the correct period for the date range argument
- */
-function date_views_browser_period($period = 'month') {
- switch ($period) {
- case('year'):
- return 'P1Y';
- case('week'):
- return 'P1W';
- case('day'):
- return 'P1D';
- case('hour'):
- return 'P1H';
- default:
- return 'P1M';
- }
-}
-
-function date_views_browser_period_format($period) {
- switch ($period) {
- case 'year':
- $format = 'Y';
- break;
- case 'month':
- $format = 'Y-m';
- break;
- case 'week':
- $format = 'Y-W';
- break;
- case 'day':
- $format = 'Y-m-d';
- break;
- case 'hour':
- $format = 'Y-m-d\TH';
- break;
- }
- return $format;
-}
-
-/**
- * Format an argument for the date range
- */
-function date_views_browser_period_arg($arg = NULL, $period = 'month') {
- if (empty($arg)) {
- $now = date_now();
- $arg = date_format($now, date_views_browser_period_format($period));
- }
- $range = date_views_date_range($arg);
- $date = date_make_date($range[0], 'UTC');
- return date_format_date($date, 'custom', date_views_browser_period_format($period)) .
- date_views_browser_period($period);
-}
-
-/**
- * Return label for current date range
- */
-function date_views_browser_period_label($arg = NULL, $period = 'month') {
- $range = date_views_date_range($arg);
- $date = date_make_date($range[0], 'UTC');
- return theme('date_views_browser_period_label', $period, $date);
-}
-
-/**
- * Navigation links for the full view
- */
-function date_views_browser_navigation($view, $period) {
- $path = explode('/', $view->url);
- $pos = sizeof($path);
- if (arg($pos) == '') {
- $arg = NULL;
- }
- else {
- $arg = arg($pos);
- }
- // Create a date object for the beginning of the current range.
- $range = date_views_date_range($arg);
- $date = date_make_date($range[0], 'UTC');
- // Move backwards 1 period and create a link to that date.
- date_modify($date, '-1'. $period);
- $prev = $view->url .'/'.
- date_format_date($date, 'custom', date_views_browser_period_format($period)) .
- date_views_browser_period($period);
- // Move forward 2 periods (back to the current period and to the next period)
- // and create a link to that date.
- date_modify($date, '+2 '. $period);
- $next = $view->url .'/'.
- date_format_date($date, 'custom', date_views_browser_period_format($period)) .
- date_views_browser_period($period);
-
- $label = date_views_browser_period_label($arg, $period);
- return theme('date_views_browser_navigation', $label, $period, $prev, $next, $view);
-}
-
-//********************************************************************************//
-//********************************************************************************//
-//********************************************************************************//
-
-include_once(drupal_get_path('module', 'date_api') .'/date_api_sql.inc');
-
-
-/**
- * A class to manipulate dates.
- */
-class views_date_handler extends date_sql_handler {
- var $granularity = 'day';
- var $db_type = 'mysql';
- var $date_type = DATE_UNIX;
-
- /**
- * Make sure granularity has a sane default value.
- */
- function construct($date_type = DATE_UNIX, $local_timezone = NULL) {
- $this->granularity = 'day';
- $this->db_type = $GLOBALS['db_type'];
- if (isset($this->definition['content_field'])) {
- $this->date_handler->date_type = $this->definition['content_field']['type'];
- }
- }
-
- /**
- * An array of all date parts,
- * optionally limited to an array of allowed parts.
- */
- function date_parts($limit = NULL) {
- $parts = array(
- 'year' => t('Year'), 'month' => t('Month'), 'day' => t('Day'),
- 'hour' => t('Hour'), 'minute' => t('Minute'), 'second' => t('Second'),
- );
- if (!empty($limit)) {
- $last = FALSE;
- foreach ($parts as $key => $part) {
- if ($last) {
- unset($parts[$key]);
- }
- if ($key == $limit) {
- $last = TRUE;
- }
- }
- }
- return $parts;
- }
-
- /**
- * Part information.
- *
- * @param $op
- * 'min', 'max', 'format', 'sep', 'empty_now', 'empty_min', 'empty_max'.
- * Returns all info if empty.
- * @param $part
- * 'year', 'month', 'day', 'hour', 'minute', or 'second.
- * returns info for all parts if empty.
- */
- function part_info($op = NULL, $part = NULL) {
- $info = array();
- $info['min'] = array(
- 'year' => 100, 'month' => 1, 'day' => 1,
- 'hour' => 0, 'minute' => 0, 'second' => 0);
- $info['max'] = array(
- 'year' => 4000, 'month' => 12, 'day' => 31,
- 'hour' => 23, 'minute' => 59, 'second' => 59);
- $info['format'] = array(
- 'year' => 'Y', 'month' => 'm', 'day' => 'd',
- 'hour' => 'H', 'minute' => 'i', 'second' => 's');
- $info['sep'] = array(
- 'year' => '', 'month' => '-', 'day' => '-',
- 'hour' => ' ', 'minute' => ':', 'second' => ':');
- $info['empty_now'] = array(
- 'year' => date('Y'), 'month' => date('m'), 'day' => min('28', date('d')),
- 'hour' => date('H'), 'minute' => date('i'), 'second' => date('s'));
- $info['empty_min'] = array(
- 'year' => '1000', 'month' => '01', 'day' => '01',
- 'hour' => '00', 'minute' => '00', 'second' => '00');
- $info['empty_max'] = array(
- 'year' => '9999', 'month' => '12', 'day' => '31',
- 'hour' => '23', 'minute' => '59', 'second' => '59');
- if (!empty($op)) {
- if (!empty($part)) {
- return $info[$op][$part];
- }
- else {
- return $info[$op];
- }
- }
- return $info;
- }
-
- /**
- * Create a complete datetime value out of an
- * incomplete array of selected values.
- */
- function complete_date($selected, $type = 'now') {
- $compare = array_merge($this->part_info('empty_'. $type), $selected);
- // If this is a max date, make sure the last day of
- // the month is the right one for this date.
- if ($type == 'max') {
- $compare['day'] = date('t', mktime(0, 0, 0, $compare['month'], 1, $compare['year']));
- }
- $value = '';
- $separators = $this->part_info('sep');
- foreach ($this->date_parts() as $key => $name) {
- $value .= $separators[$key] . $compare[$key];
- }
- return $value;
- }
- /**
- * Convert a format string into help text,
- * i.e. 'Y-m-d' becomes 'YYYY-MM-DD'.
- *
- * @param unknown_type $format
- * @return unknown
- */
- function format_help($format) {
- $replace = array(
- 'Y' => 'YYYY', 'm' => 'MM', 'd' => 'DD',
- 'H' => 'HH', 'i' => 'MM', 's' => 'SS', '\T' => 'T');
- return strtr($format, $replace);
- }
-
- /**
- * Rewrite a format string so it only inludes elements from a
- * specified granularity array.
- *
- * Example:
- * date_limit_format('F j, Y - H:i', array('year', 'month', 'day'));
- * returns 'F j, Y'
- *
- * @param $format
- * a format string
- * @param $limit
- * The minimum date part to use in this format, can be
- * 'year', 'month', 'day', 'hour', 'minute', 'second';
- * @return
- * a format string with all other elements removed
- */
- function limit_format($format, $limit) {
- $granularity = array();
- foreach ($this->date_parts() as $key => $value) {
- $granularity[] = $key;
- if ($key == $limit) {
- break;
- }
- }
- // Strip out timezone formatting.
- $regex = array('([OZPe])');
- // Get rid of dash separating date and time if either is missing.
- if (!date_has_time($granularity)
- || sizeof(array_intersect($granularity, array('year', 'month', 'day')) == 0)) {
- $regex[] = '( -)';
- }
- if (!date_has_time($granularity)) {
- $regex[] = '(a|A)';
- $regex[] = '(\\\T)';
- }
- // Create regular expressions to remove selected values from string.
- $nongranularity = array_diff(array('year', 'month', 'day', 'hour', 'minute', 'second'), $granularity);
- foreach ($nongranularity as $element) {
- switch ($element) {
- case 'year':
- $regex[] = '([\-/\.]?[Yy][\-/\.,]?)';
- break;
- case 'day':
- $regex[] = '([\-/\.]?[lDdj][\-/\.,]?)';
- break;
- case 'month':
- $regex[] = '([\-/\.]?[FMmn][\-/\.,]?)';
- break;
- case 'hour':
- $regex[] = '([HhGg][:]?)';
- break;
- case 'minute':
- $regex[] = '([:]?[i])';
- break;
- case 'second':
- $regex[] = '([:]?[s])';
- break;
- }
- }
- // Remove selected values from string.
- // Don't leave any trailing punctuation behind.
- $format = trim(preg_replace($regex, array(), $format));
- return preg_replace('([\-/\.,]$)', '', $format);
- }
-
- /**
- * A form element to select the granularity.
- */
- function granularity_form($granularity) {
- $form = array(
- '#title' => t('Granularity'),
- '#type' => 'radios',
- '#default_value' => $granularity,
- '#options' => $this->date_parts(),
- );
- return $form;
- }
-}
-
-/**
- * A flexible, configurable date filter.
- *
- * This filter allows you to select a granularity of date parts to filter on,
- * such as year, month, day, etc.
- *
- * Each part can be set to blank to show all values; 'now' to filter for
- * the current value of that part, or a specific value.
- *
- * An adjustment field is provided that will adjust the selected filter
- * value by something like '+90 days' or '-1 month';
- */
-class views_handler_filter_flexible_date extends views_handler_filter_numeric {
- var $date_handler = NULL;
-
- // Add a date handler to the filter.
- function construct() {
- parent::construct();
- $this->date_handler = new views_date_handler();
- $this->date_handler->construct();
- if (isset($this->definition['content_field'])) {
- $this->date_handler->date_type = $this->definition['content_field']['type'];
- }
- $this->date_handler->local_timezone = date_default_timezone_name();
- }
-
- function init(&$view, $options) {
- parent::init($view, $options);
- $handler = $this->date_handler;
- $handler->granularity = $options['granularity'];
- $handler->adjustment_field = $options['adjustment_field'];
- }
-
- // Set default values for the date filter.
- function options(&$options) {
- parent::options($options);
- $options['granularity'] = 'day';
- $options['adjustment_field'] = 0;
-
- // We use different values than the parent form, so we must
- // construct our own value options.
- $options['value'] = array();
- foreach (array('value', 'min', 'max') as $prefix) {
- foreach ($this->date_handler->date_parts() as $key => $part) {
- $options['value'][$prefix . $key] = '';
- }
- }
- }
-
- /**
- * Set the granularity of the date parts to use in the filter.
- */
- function has_extra_options() { return TRUE; }
-
- function extra_options_form(&$form, &$form_state) {
- $form['adjustment_field'] = array(
- '#type' => 'radios',
- '#title' => t('Filter type'),
- '#default_value' => $this->options['adjustment_field'],
- '#options' => array(
- 0 => t('Date only'),
- 1 => t('Both date and adjustment'),
- 2 => t('Adjustment only'),
- ),
- '#description' => t('Choose a date to filter on, or use an adjustment field for a value like \'+1 day\'. When you use both date and adjustment, the adjustment will be added to the date. When the adjustment field is used with no date field, the adjustment will be made to the current date.'),
- );
- $form['granularity'] = $this->date_handler->granularity_form($this->options['granularity']);
- $form['granularity']['#description'] = '<p>'. t('Select a granularity for the date filter. For instance, selecting \'day\' will create a filter where you can select the year, month, and day. You will be able to choose a specific value, all values, or \'now\' for each date part in the filter.') .'</p>';
- if (!$this->date_handler->has_tz_support()) {
- $form['granularity']['#description'] .= '<p>'. t('This database does not appear to have native timezone support. Filtering using hour, minute, or second granularity is likely to return incorrect results at least some of the time on systems without native timezone support, so it is recommended to set the granularity to no more than \'day\'.') .'</p>';
- }
- }
-
- /**
- * Add the selectors to the value form using the date handler.
- */
- function value_form(&$form, &$form_state) {
- // We use different values than the parent form, so we must
- // construct our own form element.
- $form['value'] = array();
- $form['value']['#tree'] = TRUE;
- $which = 'all';
- if (!empty($form['operator'])) {
- $source = ($form['operator']['#type'] == 'radios') ? 'radio:options[operator]' : 'edit-options-operator';
- }
-
- if (!empty($form_state['exposed'])) {
- if (empty($this->options['expose']['operator'])) {
- // exposed and locked.
- $which = in_array($this->operator, $this->operator_values(2)) ? 'minmax' : 'value';
- }
- else {
- $source = 'edit-' . form_clean_id($this->options['expose']['operator']);
- }
- }
-
- $handler = $this->date_handler;
- if ($which == 'all' || $which == 'value') {
- $form['value'] += $this->date_parts_form('value', $source, $which, $this->operator_values(1));
- }
-
- if ($which == 'all' || $which == 'minmax') {
- $form['value'] += $this->date_parts_form('min', $source, $which, $this->operator_values(2));
- $form['value'] += $this->date_parts_form('max', $source, $which, $this->operator_values(2));
- }
- $form['value']['description'] = array(
- '#prefix' => '<div class=""><div class="form-item"><div class="description">',
- '#suffix' => '</div></div></div>',
- '#value' => t('Blank values do no filtering, \'now\' filters for the current value.'),
- );
- if ($this->options['adjustment_field'] == 1) {
- $form['value']['description']['#value'] .= t(' \'Adjustment\' filters for an offset like \'+1 day\' from the other values, most useful when used with \'now\'.');
- }
- elseif ($this->options['adjustment_field'] == 2) {
- $form['value']['description']['#value'] = t('\'Adjustment\' filters for an offset like \'+1 day\' from the current time.');
- }
- }
-
- /**
- * A form element to select date part values.
- *
- * @param string $prefix
- * A prefix for the date values, 'value', 'min', or 'max'.
- * @param string $source
- * The operator for this element.
- * @param string $which
- * Which element to provide, 'all', 'value', or 'minmax'.
- * @param array $operator_values
- * An array of the allowed operators for this element.
- * @param array $limit
- * An array of date parts to limit this element to.
- *
- * @return
- * The form date part element for this instance.
- */
- function date_parts_form($prefix, $source, $which, $operator_values) {
- $prefixname = $prefix == 'value' ? '' : ($prefix == 'min' ? t('From') : t('To'));
- $handler = $this->date_handler;
- $min = $handler->part_info('min');
- $max = $handler->part_info('max');
- $limit = $handler->granularity;
- switch ($this->options['adjustment_field']) {
- case 1:
- $parts = $handler->date_parts($limit) + array('adjustment' => t('Adjustment'));
- $first_item = 'year';
- $last_item = 'adjustment';
- break;
- case 2:
- $parts = array('adjustment' => t('Adjustment'));
- $first_item = 'adjustment';
- $last_item = 'adjustment';
- break;
- default:
- $parts = $handler->date_parts($limit);
- $first_item = 'year';
- $last_item = $this->options['granularity'];
- break;
- }
- foreach ($parts as $key => $name) {
- $options = array('' => '', 'now' => 'now');
- $type = 'select';
- if ($key == 'year' || $key == 'adjustment') {
- $type = 'textfield';
- }
- $form[$prefix . $key] = array(
- '#title' => t('@type @value', array('@type' => $prefixname, '@value' => $name)),
- '#type' => $type,
- '#size' => $key == 'adjustment' ? 20 : ($key == 'year' ? 6 : 1),
- '#default_value' => !empty($this->value[$prefix . $key]) ? $this->value[$prefix . $key] : '',
- '#prefix' => '<div class="views-exposed-date-filter">',
- '#suffix' => '</div>',
- );
- switch ($key) {
- case 'year':
- case 'adjustment':
- break;
- case 'month':
- $form[$prefix . $key]['#options'] = $options + drupal_map_assoc(range(1, 12), 'map_month');
- break;
- default:
- $form[$prefix . $key]['#options'] = $options + drupal_map_assoc(range($min[$key], $max[$key]));
- break;
- }
- if ($type == 'textfield') {
- unset($form[$prefix . $key]['#options']);
- }
- if ($which == 'all') {
- $dependency = array(
- '#process' => array('views_process_dependency'),
- '#dependency' => array($source => $operator_values),
- );
- $form[$prefix . $key] += $dependency;
- }
- // Add wrappers to force each date grouping to a separate line.
- if ($key == $first_item) {
- $form[$prefix . $key]['#prefix'] = '<div class="clear-block"><div class="views-left-75">' .
- $form[$prefix . $key]['#prefix'];
- }
- if ($key == $last_item) {
- $form[$prefix . $key]['#suffix'] .= '</div></div>';
- }
- }
- return $form;
- }
-
- // User the date handler to validate the form.
- function options_validate(&$form, &$form_state) {
- if (!isset($form_state['values']['options']['value'])) {
- return;
- }
- $handler = $this->date_handler;
- $parts = $handler->date_parts();
- $min = $handler->part_info('min');
- $max = $handler->part_info('max');
- $values = $form_state['values']['options']['value'];
- // Validate date values.
- unset($values['offset']);
- foreach ($values as $name => $value) {
- $part = str_replace(array('min', 'max', 'value'), '', $value);
- if (!empty($part) && $value != '' && $value != 'now' &&
- ($value < $min[$part] || $value > $max[$part])) {
- form_error($form['value'][$name], t('@value is invalid.', array('@value' => $parts[$part])));
- }
- }
- }
-
- // Update the summary values to provide
- // meaningful information for each option.
- function admin_summary() {
- $handler = $this->date_handler;
- $output = check_plain($this->operator) . ' ';
- $parts = $handler->date_parts();
-
- // If the filter is exposed, display the granularity.
- if ($this->options['exposed']) {
- return t('<strong>Exposed</strong> Granularity: @format', array('@format' => $parts[$handler->granularity]));
- }
- // If the filter is not exposed, display the selected values.
- // Check both empty and is_numeric to show all non-blank values,
- // including zero values.
- $min = '';
- $max = '';
- $handler = $this->date_handler;
- $separators = $handler->part_info('sep');
- if (in_array($this->operator, $this->operator_values(2))) {
- foreach ($handler->date_parts($this->value['granularity']) as $key => $part) {
- if (!empty($this->value['min'. $key]) || !empty($this->value['max'. $key])
- || is_numeric($this->value['min'. $key]) || is_numeric($this->value['max'. $key])) {
- $min .= $separators[$key] . check_plain($this->value['min'. $key]);
- $max .= $separators[$key] . check_plain($this->value['max'. $key]);
- }
- }
- $output .= t('@min and @max', array('@min' => $min, '@max' => $max));
- }
- else {
- foreach ($handler->date_parts($handler->granularity) as $key => $part) {
- if (!empty($this->value['value'. $key]) || is_numeric($this->value['value'. $key])) {
- $min .= $separators[$key] . check_plain($this->value['value'. $key]);
- }
- }
- $output .= $min;
- }
- return $output;
- }
-
- function op_between($field) {
- $value = $this->date_filter('min', $field, '>=');
- $value = $this->date_filter('max', $field, '<=');
- return;
- }
-
- function op_simple($field) {
- $value = $this->date_filter('value', $field, $this->operator);
- return;
- }
-
- function date_filter($prefix, $field, $operator) {
- $handler = $this->date_handler;
- $granularity = $handler->granularity;
- $parts = $handler->date_parts();
- $filter_parts = $handler->date_parts($handler->granularity);
- $adjustment = 0;
- if (!empty($this->value[$prefix .'adjustment'])) {
- $adjustment = strtotime($this->value[$prefix .'adjustment'], 0);
- // See if there are any filters other than the adjustment.
- // If not, compare to NOW() and return.
- if ($this->options['adjustment_field'] == 2) {
- $sql = $handler->sql_field($field, 0) . " $operator ". $handler->sql_field('NOW', $adjustment);
- $this->query->add_where($this->options['group'], $sql);
- return;
- }
- }
- $format = '';
- $selected = array();
- $separators = $handler->part_info('sep');
- $formats = $handler->part_info('format');
- foreach ($filter_parts as $key => $part) {
- $sep = $separators[$key];
- $pattern = $key == 'year' ? '%04d' : '%02d';
- if (is_numeric($this->value[$prefix . $key]) || $this->value[$prefix . $key] == 'now') {
- $format .= !empty($format) ? $sep : '';
- $format .= $formats[$key];
- }
- if (is_numeric($this->value[$prefix . $key])) {
- $selected[$key] = sprintf($pattern, check_plain($this->value[$prefix . $key]));
- }
- elseif ($this->value[$prefix . $key] == 'now') {
- $selected[$key] = date($formats[$key]);
- }
- else {
- // When we hit an empty (all values) option in the middle of
- // our date parts, stop and start a new query.
- if ($format > '' && $format != $sep) {
- $date = date_make_date($handler->complete_date($selected));
- $value = date_format($date, $format);
- $sql = $handler->sql_where_format($format, $field, $operator, $value);
- $this->query->add_where($this->options['group'], $sql);
- }
- $format = '';
- $selected = array();
- }
- }
- if ($format > '' && $format != $sep) {
- $date = date_make_date($handler->complete_date($selected));
- $value = date_format($date, $format);
- $sql = $handler->sql_where_format($format, $field, $operator, $value);
- $this->query->add_where($this->options['group'], $sql);
- }
- return;
- }
-}
-
-/**
- * A flexible, configurable date argument.
- *
- * This argument allows you to set one or more date parts to filter on,
- * such as year, month, and day; month only; a complete date, etc.
- *
- * @ingroup views_argument_handlers
- */
-class views_handler_argument_date2 extends views_handler_argument_formula {
- var $date_handler = NULL;
-
- /**
- * Add date handler to the argument.
- */
- function construct() {
- parent::construct();
- $this->date_handler = new views_date_handler();
- $this->date_handler->construct();
- if (isset($this->definition['date_type'])) {
- $this->date_handler->date_type = $this->definition['date_type'];
- }
- }
-
- /**
- * Get granularity and use it to create the formula and a format
- * for the results.
- */
- function init(&$view, $options) {
- parent::init($view, $options);
- $handler = $this->date_handler;
- $handler->granularity = $handler->granularity_keys($options['granularity']);
- $sql_format = $handler->limit_format('Y-m-d\TH:i:s', $handler->granularity);
- $this->formula = $handler->sql_format($sql_format, "***table***.$this->real_field");
- $this->format = $handler->limit_format(variable_get('date_format_long', 'l, F j, Y - H:i'), $handler->granularity);
- }
-
- /**
- * Default value for the granularity option.
- */
- function options(&$options) {
- parent::options($options);
- $parts = $this->date_handler->date_parts();
- unset($parts['adjustment']);
- $options['granularity'] = 'day';
- }
-
- /**
- * Add a form element to select granularity.
- */
- function options_form(&$form, &$form_state) {
- parent::options_form($form, $form_state);
- // Select the granularity of the date parts to use in the argument.
- $handler = $this->date_handler;
- $form['granularity'] = $this->options['granularity'];
- $form['granularity']['#description'] = t('Limit the argument to use only the selected date parts.');
- unset($form['granularity']['#options']['adjustment']);
- }
-
- // Update the summary values to show selected granularity.
- function admin_summary() {
- $handler = $this->date_handler;
- $format = $handler->format_help($handler->limit_format('Y-m-d\TH:i:s', $handler->granularity));
- return t('<br />Argument format: @format', array('@format' => $format));
- }
-
- /**
- * Provide a link to the next level of the view
- */
- function summary_name($data) {
- $created = $data->{$this->name_alias};
- return format_date(strtotime($created), 'custom', $this->format, 0);
- }
-
- /**
- * Provide a link to the next level of the view
- */
- function title() {
- return format_date(strtotime($this->argument), 'custom', $this->format, 0);
- }
-
- /**
- * Create a summary query that matches the granularity.
- *
- * Needed or Views will do a groupby on the complete date instead
- * of only the part of the date actually used in the argument.
- */
- function summary_query() {
- $this->ensure_my_table();
- $handler = $this->date_handler;
-
- // We need to alter the base alias so the groupby clause will group by
- // the relevant part of the date instead of a complete date.
- $alias = $this->name_alias = $this->table_alias ."_". $this->real_field;
- $this->query->add_field(NULL, $this->get_formula(), $alias);
- $this->base_alias = $this->get_formula();
-
- return $this->summary_basics();
- }
-
- /**
- * Need to override the basic link since base_alias is now a formula.
- */
- function summary_link($data, $url) {
- $value = $data->{$this->name_alias};
- return url("$url/$value");
- }
-
- /**
- * Create a query that matches the argument.
- *
- * Move through the arg and pick out date values to add to the query.
- */
- function query() {
- $this->ensure_my_table();
- $handler = $this->date_handler;
- $granularity = $handler->granularity;
- $parts = $handler->date_parts();
- unset($parts['adjustment']);
- $i = 0;
- foreach ($parts as $key => $label) {
- if (!in_array($key, $granularity)) {
- // Skip values not in the requested granularity.
- }
- else {
- // Find the next date part in the arg to evaluate
- $arg = intval(substr($this->argument, $i, $key == 'year' ? 4 : 2));
- $extract = $handler->sql_extract(strtoupper($key), "$this->table_alias.$this->real_field");
- $this->query->add_where(0, $extract. " = %s", $arg);
- }
- $i += $key == 'year' ? 5 : 3;
- }
- }
-}
diff --git a/date/date_admin.inc b/date/date_admin.inc
index 42d3b49..ec5030e 100644
--- a/date/date_admin.inc
+++ b/date/date_admin.inc
@@ -266,7 +266,22 @@ function _date_field_settings($op, $field) {
return date_columns($field);
case 'views data':
- return _date_views_data($field);
+ $data = content_views_field_views_data($field);
+ $db_info = content_database_info($field);
+ $table_alias = content_views_tablename($field);
+
+ // Unset the filter and argument handlers, dates can use the generic
+ // date argument and filter handlers created by the Date API.
+ unset($data[$table_alias][$field['field_name'] .'_value']['argument']);
+ unset($data[$table_alias][$field['field_name'] .'_value']['filter']);
+
+ // Add in another set of fields for the To date.
+ if ($field['todate']) {
+ $data[$table_alias][$field['field_name'] .'_value2'] = $data[$table_alias][$field['field_name'] .'_value'];
+ $data[$table_alias][$field['field_name'] .'_value']['title'] = t('!label (!field value)', array('!label' => t($field['widget']['label']), '!field' => t($field['field_name'])));
+ $data[$table_alias][$field['field_name'] .'_value2']['title'] = t('!label (!field value2)', array('!label' => t($field['widget']['label']), '!field' => t($field['field_name'])));
+ }
+ return $data;
}
}
diff --git a/date_api.module b/date_api.module
index 6fcd6e0..99b2020 100644
--- a/date_api.module
+++ b/date_api.module
@@ -1333,6 +1333,7 @@ function date_api_theme() {
'template' => 'date-nav',
'arguments' => array('view' => NULL)
),
+ 'date_nav_title' => array('arguments' => array('type' => NULL, 'view' => NULL)),
'date_timezone' => array('arguments' => array('element' => NULL)),
'date_select' => array('arguments' => array('element' => NULL)),
'date_text' => array('arguments' => array('element' => NULL)),
diff --git a/date_api.views.inc b/date_api.views.inc
new file mode 100644
index 0000000..31ab8a7
--- /dev/null
+++ b/date_api.views.inc
@@ -0,0 +1,964 @@
+<?php
+//$Id$
+/**
+ * @file
+ * Defines date-related Views data and plugins:
+ *
+ * Date argument:
+ * A generic date argument that has an option to select one or more
+ * Views date fields to filter on, automatically adds them to the view,
+ * and then filters the view by the value of the selected field(s).
+ * The flexible argument will accept and evaluate most ISO date
+ * and period formats, like 2009-05-01, 2008-W25, P1W.
+ *
+ * Current date argument default
+ * Adds a default option to set the argument to the current date
+ * when the argument is empty.
+ *
+ * Date navigation attachment
+ * Navigation that can be attached to any display to create back/next
+ * links by date, requires the date argument and uses the current
+ * date argument default to set a starting point for the view.
+ */
+//views_include_handlers();
+
+/**
+ * Implementation of hook_views_data()
+ */
+function date_api_views_data() {
+ $data = array();
+
+ // The flexible date argument.
+ $data['node']['date_argument'] = array(
+ 'group' => t('Date'),
+ 'title' => t('Date'),
+ 'help' => t('Filter any Views date field by a date argument, using any common ISO date/period format (i.e. YYYY, YYYY-MM, YYYY-MM-DD, YYYY-W99, YYYY-MM-DD--P3M, P90D, etc).'),
+ 'argument' => array(
+ 'handler' => 'date_api_argument_handler',
+ 'empty name field' => t('Undated'),
+ ),
+ );
+ // The flexible date fliter.
+ $data['node']['date_filter'] = array(
+ 'group' => t('Date'),
+ 'title' => t('Date'),
+ 'help' => t('Filter any Views date field.'),
+ 'filter' => array(
+ 'handler' => 'date_api_filter_handler',
+ 'empty name field' => t('Undated'),
+ ),
+ );
+ return $data;
+}
+
+/**
+ * Implementation of hook_views_plugins
+ */
+function date_api_views_plugins() {
+ $path = drupal_get_path('module', 'date_api');
+ $base = array(
+ 'file' => 'theme.inc',
+ 'path' => "$path/theme",
+ );
+ return array(
+ 'module' => 'date_api', // This just tells our themes are elsewhere.
+ 'display' => array(
+ // Display plugin for date navigation.
+ 'date_nav' => $base + array(
+ 'title' => t('Date navigation'),
+ 'help' => t('Date back/next navigation to attach to other displays. Requires the Date argument.'),
+ 'handler' => 'date_plugin_display_attachment',
+ 'theme' => 'views_view',
+ 'use ajax' => TRUE,
+ 'admin' => t('Date navigation'),
+ 'help topic' => 'display-date_navigation',
+ ),
+ ),
+ 'style' => array(
+ // Style plugin for the navigation display.
+ 'date_nav' => $base + array(
+ 'title' => t('Date navigation style'),
+ 'help' => t('Creates back/next navigation.'),
+ 'handler' => 'date_navigation_plugin_style',
+ 'theme' => 'date_navigation',
+ 'uses row plugin' => FALSE,
+ 'uses fields' => FALSE,
+ 'uses options' => TRUE,
+ 'type' => 'date_nav',
+ 'even empty' => TRUE,
+ ),
+ ),
+ // Add an option to set a default value for an empty date argument.
+ 'argument default' => array(
+ 'date' => $base + array(
+ 'title' => t('Current date'),
+ 'handler' => 'date_plugin_argument_default',
+ ),
+ ),
+ );
+}
+
+/**
+ * Date API argument handler.
+ */
+class date_api_argument_handler extends views_handler_argument_formula {
+ function construct() {
+ parent::construct();
+ include_once('./'. drupal_get_path('module', 'date_api') .'/date_api_sql.inc');
+ $this->date_handler = new date_sql_handler();
+ $this->date_handler->construct();
+ $this->date_handler->granularity = $this->options['granularity'];
+ if (isset($this->definition['content_field'])) {
+ $this->date_handler->date_type = $this->definition['content_field']['type'];
+ $this->content_field = content_fields($this->definition['content_field_name']);
+ $this->additional_fields = $this->definition['additional fields'];
+ }
+ }
+
+ /**
+ * Get granularity and use it to create the formula and a format
+ * for the results.
+ */
+ function init(&$view, $options) {
+ parent::init($view, $options);
+ $date_handler = $this->date_handler;
+ $this->format = $date_handler->views_formats($date_handler->granularity, 'display');
+ $this->sql_format = $date_handler->views_formats($date_handler->granularity, 'sql');
+ }
+
+ /**
+ * Default value for the date_fields option.
+ */
+ function options(&$options) {
+ parent::options($options);
+ $options['date_fields'] = array();
+ $options['date_method'] = 'OR';
+ $options['granularity'] = 'month';
+ }
+
+ /**
+ * Add a form element to select date_fields for this argument.
+ */
+ function options_form(&$form, &$form_state) {
+ parent::options_form($form, $form_state);
+ $options = $this->date_handler->date_parts();
+ unset($options['second'], $options['minute']);
+ $options += array('week' => t('Week'));
+ $form['granularity'] = array(
+ '#title' => t('Granularity'),
+ '#type' => 'radios',
+ '#options' => $options,
+ '#default_value' => $this->options['granularity'],
+ '#multiple' => TRUE,
+ '#description' => t('Select the type of date value to be used in defaults, summaries, and navigation. For example, a granularity of \'month\' will set the default date to the current month, summarize by month in summary views, and link to the next and previous month when using date navigation.'),
+ );
+
+ $fields = date_api_fields();
+ $options = array();
+ foreach ($fields['name'] as $name => $field) {
+ $options[$name] = $field['label'];
+ }
+ $form['date_fields'] = array(
+ '#title' => t('Date field(s)'),
+ '#type' => 'checkboxes',
+ '#options' => $options,
+ '#default_value' => $this->options['date_fields'],
+ '#multiple' => TRUE,
+ '#description' => t('Select one or more date fields to filter with this argument.'),
+ );
+ $form['date_method'] = array(
+ '#title' => t('Method'),
+ '#type' => 'radios',
+ '#options' => array('OR' => t('OR'), 'AND' => t('AND')),
+ '#default_value' => $this->options['date_method'],
+ '#description' => t('Method of handling multiple date fields in the same query. Return items that have any matching date field (date = field_1 OR field_2), or only those with matches in all selected date fields (date = field_1 AND field_2).'),
+ );
+
+ }
+
+ function options_validate($form, &$form_state) {
+ if (empty($form_state['values']['options']['date_fields'])) {
+ form_error($form, t('You must select at least one date field for this argument.'));
+ }
+ }
+
+ function options_submit($form, &$form_state) {
+ $form_state['values']['options']['date_fields'] = array_filter($form_state['values']['options']['date_fields']);
+ }
+
+ // Update the summary values to show selected granularity.
+ function admin_summary() {
+ if (!empty($this->options['date_fields'])) {
+ return ' ('. implode(', ', $this->options['date_fields']) .')';
+ }
+ else {
+ return parent::admin_summary();
+ }
+ }
+
+ /**
+ * Provide a link to the next level of the view from the summary.
+ */
+ function summary_name($data) {
+ $format = $this->date_handler->views_formats($this->options['granularity'], 'display');
+ $created = $data->{$this->name_alias};
+ return format_date(strtotime($created), 'custom', $format, 0);
+ }
+
+ /**
+ * Need to override the basic link since base_alias is now a formula.
+ */
+ function summary_link($data, $url) {
+ $value = $data->{$this->name_alias};
+ return url("$url/$value");
+ }
+
+ /**
+ * Provide a link to the next level of the view from the argument.
+ */
+ function title() {
+ $format = $this->date_handler->views_formats($this->options['granularity'], 'display');
+ return format_date(strtotime($this->argument), 'custom', $format, 0);
+ }
+
+ /**
+ * Create a summary query that matches the granularity.
+ *
+ * Needed or Views will do a groupby on the complete date instead
+ * of only the part of the date actually used in the argument.
+ */
+ function summary_query() {
+ $this->get_query_fields();
+
+ // No way to do summaries on more than one field at a time.
+ if (count($this->query_fields) > 1) {
+ return;
+ }
+
+ $field = $this->query_fields[0]['field'];
+ $date_handler = $this->query_fields[0]['date_handler'];
+
+ // Get the SQL format for this granularity, like Y-m,
+ // and use that as the grouping value.
+ $format = $date_handler->views_formats($this->options['granularity'], 'sql');
+ $this->formula = $date_handler->sql_format($format, $date_handler->sql_field($field['fullname']));
+
+ $this->ensure_my_table();
+
+ // Make sure this field is added to the query so we have all necessary tables.
+ $this->query->add_field($field['table_name'], $field['field_name']);
+
+ // Add the computed field.
+ $this->base_alias = $this->name_alias = $this->query->add_field(NULL, $this->formula, $field['queryname']);
+ $this->query->set_count_field(NULL, $this->formula, $field['queryname']);
+
+ return $this->summary_basics(FALSE);
+ }
+
+ function get_query_fields() {
+ $fields = date_api_fields();
+ $fields = $fields['name'];
+ $min_date = isset($this->min_date) ? $this->min_date : NULL;
+ $min_utc = isset($this->min_utc) ? $this->min_utc : NULL;
+ $max_date = isset($this->max_date) ? $this->max_date : NULL;
+ $max_utc = isset($this->max_utc) ? $this->max_utc : NULL;
+
+ foreach ($this->options['date_fields'] as $delta => $name) {
+ if ($field = $fields[$name]) {
+ $date_handler = new date_sql_handler();
+ $date_handler->construct($field['sql_type'], date_default_timezone_name());
+ $tz_handling = $field['tz_handling'];
+ switch ($tz_handling) {
+ case 'date' :
+ $date_handler->db_timezone = 'UTC';
+ $date_handler->local_timezone_field = $field['timezone_field'];
+ $date_handler->local_offset_field = $field['offset_field'];
+ $date_handler->min_date = $min_date;
+ $date_handler->max_date = $max_date;
+ break;
+ case 'none':
+ $date_handler->db_timezone = date_default_timezone_name();
+ $date_handler->local_timezone = date_default_timezone_name();
+ $date_handler->min_date = $min_date;
+ $date_handler->max_date = $max_date;
+ break;
+ case 'utc':
+ $date_handler->db_timezone = 'UTC';
+ $date_handler->local_timezone = 'UTC';
+ $date_handler->min_date = $min_utc;
+ $date_handler->max_date = $max_utc;
+ break;
+ default :
+ $date_handler->db_timezone = 'UTC';
+ $date_handler->local_timezone = 'UTC';
+ $date_handler->min_date = $min_utc;
+ $date_handler->max_date = $max_utc;
+ break;
+ }
+ $this->query_fields[] = array('field' => $field, 'date_handler' => $date_handler);
+ }
+ }
+ }
+
+ /**
+ * Set up the query for this argument.
+ *
+ * The argument sent may be found at $this->argument.
+ */
+ function query() {
+ $parts = $this->date_handler->arg_parts($this->argument);
+ foreach ($parts[0]['date'] as $key => $part) {
+ // The last part evaluated is the one that will 'stick'
+ // as the date type.
+ $this->date_type = $key;
+ $this->$key = $part;
+ }
+ $range = $this->date_handler->arg_range($this->argument);
+ $min_date = $range[0];
+ $max_date = $range[1];
+
+ // Create min and max dates in both local and UTC time.
+ // We'll compare fields to the UTC date whenever possible
+ // to avoid the need to do timezone conversions. When that
+ // isn't possible (the date is not stored in UTC or needs to
+ // be converted back to a time that may be different than
+ // the local timezone) we will have to do tz conversions in
+ // the database.
+ $this->min_date = $min_date;
+ $this->min_utc = drupal_clone($min_date);
+ date_timezone_set($this->min_utc, timezone_open('UTC'));
+ $this->max_date = $max_date;
+ $this->max_utc = drupal_clone($max_date);
+ date_timezone_set($this->max_utc, timezone_open('UTC'));
+
+ // Use set_where_group() with the selected date_method
+ // of 'AND' or 'OR' to create the where clause.
+ $this->query->set_where_group($this->options['date_method'], 'date');
+ $this->ensure_my_table();
+
+ $this->get_query_fields();
+ foreach ($this->query_fields as $query_field) {
+ $field = $query_field['field'];
+ $date_handler = $query_field['date_handler'];
+
+ // Make sure this field is added to the query.
+ $this->query->add_field($field['table_name'], $field['field_name']);
+ foreach ($field['related_fields'] as $related) {
+ $bits = explode('.', $related);
+ if ($bits[1] != $field['field_name']) {
+ $this->query->add_field($field['table_name'], $bits[1]);
+ }
+ }
+ $from = $date_handler->sql_where_date('DATE', $field['fullname'], '>=', date_format($date_handler->min_date, DATE_FORMAT_DATETIME));
+ $to = $date_handler->sql_where_date('DATE', $field['fullname'], '<=', date_format($date_handler->max_date, DATE_FORMAT_DATETIME));
+ $sql = str_replace('***table***', $this->table_alias, "($from AND $to)");
+ if ($sql) {
+ $this->query->add_where('date', $sql);
+ }
+ }
+ }
+}
+
+/**
+ * Default argument plugin to default to the current date.
+ */
+class date_plugin_argument_default extends views_plugin_argument_default {
+ var $option_name = 'default_argument_date';
+
+ // If used on a date argument that is tracking granularity, use that
+ // for the format, otherwise get a format to use for the argument.
+
+ function argument_form(&$form, &$form_state) {
+ if (!empty($this->argument->options['granularity'])) {
+ return;
+ }
+ else {
+ $form[$this->option_name] = array(
+ '#title' => t('Current date format'),
+ '#description' => t('Select a format to use when creating a missing argument from the current date.'),
+ '#type' => 'select',
+ '#options' => array('Y-m-d' => 'YYYY-MM-DD', 'Ymd' => 'YYYYMMDD',
+ 'Y-m' => 'YYYY-MM', 'Ym' => 'YYYYMM', 'Y' => 'YYYY',
+ 'Y-\Ww' => 'YYYY-W99', 'Y\Ww' => 'YYYYW99'),
+ '#default_value' => $this->format(),
+ '#process' => array('views_process_dependency'),
+ '#dependency' => array(
+ 'radio:options[default_action]' => array('default'),
+ 'radio:options[default_argument_type]' => array($this->id)
+ ),
+ '#dependency_count' => 2,
+ );
+ $this->check_access($form);
+ }
+ }
+
+ function format() {
+ if (!empty($this->argument->options['granularity'])) {
+ $date_handler = new date_sql_handler();
+ return $date_handler->views_formats($this->argument->options['granularity']);
+ }
+ else {
+ return !empty($this->argument->options[$this->option_name]) ? $this->argument->options[$this->option_name] : 'Y-m';
+ }
+ }
+
+ function get_argument() {
+ return date($this->format(), time());
+ }
+}
+
+/**
+ * The plugin that handles date navigation attachments.
+ *
+ * Creates a special attachment for this purpose only.
+ */
+class date_plugin_display_attachment extends views_plugin_display_attachment {
+
+ // Require the date_nav style. That style has a date_nav type
+ // so it won't show up as a style option on any other display.
+ function get_style_type() { return 'date_nav'; }
+
+ // No options to set style, force it to the right value.
+ function defaultable_sections($section = NULL) {
+ if (in_array($section, array('style_plugin', 'row_options', 'row_plugin', 'items_per_page'))) {
+ return FALSE;
+ }
+ return parent::defaultable_sections($section);
+ }
+
+ function options(&$display) {
+ parent::options($display);
+ $display->display_options['style_plugin'] = 'date_nav';
+ $display->display_options['items_per_page'] = 0;
+ $display->display_options['row_plugin'] = '';
+ $display->display_options['defaults']['style_plugin'] = FALSE;
+ $display->display_options['defaults']['style_options'] = FALSE;
+ $display->display_options['defaults']['items_per_page'] = FALSE;
+ $display->display_options['defaults']['row_plugin'] = FALSE;
+ $display->display_options['defaults']['row_options'] = FALSE;
+ }
+}
+
+/**
+ * Style plugin to create date back/next navigation.
+ *
+ * The style plugin passes some argument values to the theme, and
+ * ensures that the date argument is present and that the default
+ * value is set to the current date.
+ */
+class date_navigation_plugin_style extends views_plugin_style {
+
+ /**
+ * Style validation.
+ */
+ function validate() {
+ $errors = parent::validate();
+
+ $arguments = $this->display->handler->get_option('arguments');
+ if (!in_array('date_argument', array_keys($arguments))) {
+ $errors[] = t('The @style requires the Calendar: Date argument.', array('@style' => $this->definition['title']));
+ }
+ else {
+ if ($arguments['date_argument']['default_argument_type'] != 'date') {
+ $errors[] = t('The @style requires the Calendar: Date argument to provide a default argument set to default to the current date.', array('@style' => $this->definition['title']));
+ }
+ }
+ return $errors;
+ }
+
+ function query() {
+ include_once(drupal_get_path('module', 'date_api') .'/date_api_sql.inc');
+
+ // Bring the argument information into the view so our theme can access it.
+ $i = 0;
+ foreach ($this->view->argument as $delta => $argument) {
+ if ($argument['id'] == 'date_argument') {
+ $this->view->date_type = $argument['handler']->date_type;
+ $this->view->date_arg = $argument['handler']->argument;
+ $this->view->date_arg_pos = $i;
+ $this->view->year = $argument['handler']->year;
+ $this->view->month = $argument['handler']->month;
+ $this->view->day = $argument['handler']->day;
+ $this->view->week = $argument['handler']->week;
+ $this->view->min_date = $argument['handler']->min_date;
+ $this->view->max_date = $argument['handler']->max_date;
+ }
+ $i++;
+ }
+
+ // bring the node type into the query so we can use it in the theme
+ $this->view->query->add_field('node', 'type');
+
+ parent::query();
+ }
+
+ /**
+ * Render the calendar navigation style.
+ */
+ function render() {
+ return theme($this->theme_functions(), $this->view, $this->options, array());
+ }
+}
+
+/**
+ * A flexible, configurable date filter.
+ *
+ * This filter allows you to select a granularity of date parts to filter on,
+ * such as year, month, day, etc.
+ *
+ * Each part can be set to blank to show all values; 'now' to filter for
+ * the current value of that part, or a specific value.
+ *
+ * An adjustment field is provided that will adjust the selected filter
+ * value by something like '+90 days' or '-1 month';
+ */
+class date_api_filter_handler extends views_handler_filter_numeric {
+ var $date_handler = NULL;
+
+ // Add a date handler to the filter.
+ function construct() {
+ parent::construct();
+ include_once('./'. drupal_get_path('module', 'date_api') .'/date_api_sql.inc');
+ $this->date_handler = new views_date_handler();
+ $this->date_handler->construct();
+ $this->date_handler->granularity = $this->options['granularity'];
+ if (isset($this->definition['content_field'])) {
+ $this->date_handler->date_type = $this->definition['content_field']['type'];
+ $this->content_field = content_fields($this->definition['content_field_name']);
+ $this->additional_fields = $this->definition['additional fields'];
+ }
+ }
+
+ function init(&$view, $options) {
+ parent::init($view, $options);
+ $handler = $this->date_handler;
+ $handler->granularity = $options['granularity'];
+ $handler->adjustment_field = $options['adjustment_field'];
+ }
+
+ // Set default values for the date filter.
+ function options(&$options) {
+ parent::options($options);
+ $options['granularity'] = 'day';
+ $options['adjustment_field'] = 0;
+
+ // We use different values than the parent form, so we must
+ // construct our own value options.
+ $options['value'] = array();
+ foreach (array('value', 'min', 'max') as $prefix) {
+ foreach ($this->date_handler->date_parts() as $key => $part) {
+ $options['value'][$prefix . $key] = '';
+ }
+ }
+ }
+
+ /**
+ * Set the granularity of the date parts to use in the filter.
+ */
+ function has_extra_options() { return TRUE; }
+
+ function extra_options_form(&$form, &$form_state) {
+ $form['adjustment_field'] = array(
+ '#type' => 'radios',
+ '#title' => t('Filter type'),
+ '#default_value' => $this->options['adjustment_field'],
+ '#options' => array(
+ 0 => t('Date only'),
+ 1 => t('Both date and adjustment'),
+ 2 => t('Adjustment only'),
+ ),
+ '#description' => t('Choose a date to filter on, or use an adjustment field for a value like \'+1 day\'. When you use both date and adjustment, the adjustment will be added to the date. When the adjustment field is used with no date field, the adjustment will be made to the current date.'),
+ );
+ $form['granularity'] = $this->date_handler->granularity_form($this->options['granularity']);
+ $form['granularity']['#description'] = '<p>'. t('Select a granularity for the date filter. For instance, selecting \'day\' will create a filter where you can select the year, month, and day. You will be able to choose a specific value, all values, or \'now\' for each date part in the filter.') .'</p>';
+ if (!$this->date_handler->has_tz_support()) {
+ $form['granularity']['#description'] .= '<p>'. t('This database does not appear to have native timezone support. Filtering using hour, minute, or second granularity is likely to return incorrect results at least some of the time on systems without native timezone support, so it is recommended to set the granularity to no more than \'day\'.') .'</p>';
+ }
+ }
+
+ /**
+ * Add the selectors to the value form using the date handler.
+ */
+ function value_form(&$form, &$form_state) {
+ // We use different values than the parent form, so we must
+ // construct our own form element.
+ $form['value'] = array();
+ $form['value']['#tree'] = TRUE;
+ $which = 'all';
+ if (!empty($form['operator'])) {
+ $source = ($form['operator']['#type'] == 'radios') ? 'radio:options[operator]' : 'edit-options-operator';
+ }
+
+ if (!empty($form_state['exposed'])) {
+ if (empty($this->options['expose']['operator'])) {
+ // exposed and locked.
+ $which = in_array($this->operator, $this->operator_values(2)) ? 'minmax' : 'value';
+ }
+ else {
+ $source = 'edit-' . form_clean_id($this->options['expose']['operator']);
+ }
+ }
+
+ $handler = $this->date_handler;
+ if ($which == 'all' || $which == 'value') {
+ $form['value'] += $this->date_parts_form('value', $source, $which, $this->operator_values(1));
+ }
+
+ if ($which == 'all' || $which == 'minmax') {
+ $form['value'] += $this->date_parts_form('min', $source, $which, $this->operator_values(2));
+ $form['value'] += $this->date_parts_form('max', $source, $which, $this->operator_values(2));
+ }
+ $form['value']['description'] = array(
+ '#prefix' => '<div class=""><div class="form-item"><div class="description">',
+ '#suffix' => '</div></div></div>',
+ '#value' => t('Blank values do no filtering, \'now\' filters for the current value.'),
+ );
+ if ($this->options['adjustment_field'] == 1) {
+ $form['value']['description']['#value'] .= t(' \'Adjustment\' filters for an offset like \'+1 day\' from the other values, most useful when used with \'now\'.');
+ }
+ elseif ($this->options['adjustment_field'] == 2) {
+ $form['value']['description']['#value'] = t('\'Adjustment\' filters for an offset like \'+1 day\' from the current time.');
+ }
+ }
+
+ /**
+ * A form element to select date part values.
+ *
+ * @param string $prefix
+ * A prefix for the date values, 'value', 'min', or 'max'.
+ * @param string $source
+ * The operator for this element.
+ * @param string $which
+ * Which element to provide, 'all', 'value', or 'minmax'.
+ * @param array $operator_values
+ * An array of the allowed operators for this element.
+ * @param array $limit
+ * An array of date parts to limit this element to.
+ *
+ * @return
+ * The form date part element for this instance.
+ */
+ function date_parts_form($prefix, $source, $which, $operator_values) {
+ $prefixname = $prefix == 'value' ? '' : ($prefix == 'min' ? t('From') : t('To'));
+ $handler = $this->date_handler;
+ $min = $handler->part_info('min');
+ $max = $handler->part_info('max');
+ $limit = $handler->granularity;
+ switch ($this->options['adjustment_field']) {
+ case 1:
+ $parts = $handler->date_parts($limit) + array('adjustment' => t('Adjustment'));
+ $first_item = 'year';
+ $last_item = 'adjustment';
+ break;
+ case 2:
+ $parts = array('adjustment' => t('Adjustment'));
+ $first_item = 'adjustment';
+ $last_item = 'adjustment';
+ break;
+ default:
+ $parts = $handler->date_parts($limit);
+ $first_item = 'year';
+ $last_item = $this->options['granularity'];
+ break;
+ }
+ foreach ($parts as $key => $name) {
+ $options = array('' => '', 'now' => 'now');
+ $type = 'select';
+ if ($key == 'year' || $key == 'adjustment') {
+ $type = 'textfield';
+ }
+ $form[$prefix . $key] = array(
+ '#title' => t('@type @value', array('@type' => $prefixname, '@value' => $name)),
+ '#type' => $type,
+ '#size' => $key == 'adjustment' ? 20 : ($key == 'year' ? 6 : 1),
+ '#default_value' => !empty($this->value[$prefix . $key]) ? $this->value[$prefix . $key] : '',
+ '#prefix' => '<div class="views-exposed-date-filter">',
+ '#suffix' => '</div>',
+ );
+ switch ($key) {
+ case 'year':
+ case 'adjustment':
+ break;
+ case 'month':
+ $form[$prefix . $key]['#options'] = $options + drupal_map_assoc(range(1, 12), 'map_month');
+ break;
+ default:
+ $form[$prefix . $key]['#options'] = $options + drupal_map_assoc(range($min[$key], $max[$key]));
+ break;
+ }
+ if ($type == 'textfield') {
+ unset($form[$prefix . $key]['#options']);
+ }
+ if ($which == 'all') {
+ $dependency = array(
+ '#process' => array('views_process_dependency'),
+ '#dependency' => array($source => $operator_values),
+ );
+ $form[$prefix . $key] += $dependency;
+ }
+ // Add wrappers to force each date grouping to a separate line.
+ if ($key == $first_item) {
+ $form[$prefix . $key]['#prefix'] = '<div class="clear-block"><div class="views-left-75">' .
+ $form[$prefix . $key]['#prefix'];
+ }
+ if ($key == $last_item) {
+ $form[$prefix . $key]['#suffix'] .= '</div></div>';
+ }
+ }
+ return $form;
+ }
+
+ // User the date handler to validate the form.
+ function options_validate(&$form, &$form_state) {
+ if (!isset($form_state['values']['options']['value'])) {
+ return;
+ }
+ $handler = $this->date_handler;
+ $parts = $handler->date_parts();
+ $min = $handler->part_info('min');
+ $max = $handler->part_info('max');
+ $values = $form_state['values']['options']['value'];
+ // Validate date values.
+ unset($values['offset']);
+ foreach ($values as $name => $value) {
+ $part = str_replace(array('min', 'max', 'value'), '', $value);
+ if (!empty($part) && $value != '' && $value != 'now' &&
+ ($value < $min[$part] || $value > $max[$part])) {
+ form_error($form['value'][$name], t('@value is invalid.', array('@value' => $parts[$part])));
+ }
+ }
+ }
+
+ // Update the summary values to provide
+ // meaningful information for each option.
+ function admin_summary() {
+ $handler = $this->date_handler;
+ $output = check_plain($this->operator) . ' ';
+ $parts = $handler->date_parts();
+
+ // If the filter is exposed, display the granularity.
+ if ($this->options['exposed']) {
+ return t('<strong>Exposed</strong> Granularity: @format', array('@format' => $parts[$handler->granularity]));
+ }
+ // If the filter is not exposed, display the selected values.
+ // Check both empty and is_numeric to show all non-blank values,
+ // including zero values.
+ $min = '';
+ $max = '';
+ $handler = $this->date_handler;
+ $separators = $handler->part_info('sep');
+ if (in_array($this->operator, $this->operator_values(2))) {
+ foreach ($handler->date_parts($this->value['granularity']) as $key => $part) {
+ if (!empty($this->value['min'. $key]) || !empty($this->value['max'. $key])
+ || is_numeric($this->value['min'. $key]) || is_numeric($this->value['max'. $key])) {
+ $min .= $separators[$key] . check_plain($this->value['min'. $key]);
+ $max .= $separators[$key] . check_plain($this->value['max'. $key]);
+ }
+ }
+ $output .= t('@min and @max', array('@min' => $min, '@max' => $max));
+ }
+ else {
+ foreach ($handler->date_parts($handler->granularity) as $key => $part) {
+ if (!empty($this->value['value'. $key]) || is_numeric($this->value['value'. $key])) {
+ $min .= $separators[$key] . check_plain($this->value['value'. $key]);
+ }
+ }
+ $output .= $min;
+ }
+ return $output;
+ }
+
+ function op_between($field) {
+ $value = $this->date_filter('min', $field, '>=');
+ $value = $this->date_filter('max', $field, '<=');
+ return;
+ }
+
+ function op_simple($field) {
+ $value = $this->date_filter('value', $field, $this->operator);
+ return;
+ }
+
+ function date_filter($prefix, $field, $operator) {
+ $handler = $this->date_handler;
+ $granularity = $handler->granularity;
+ $parts = $handler->date_parts();
+ $filter_parts = $handler->date_parts($handler->granularity);
+ $adjustment = 0;
+ if (!empty($this->value[$prefix .'adjustment'])) {
+ $adjustment = strtotime($this->value[$prefix .'adjustment'], 0);
+ // See if there are any filters other than the adjustment.
+ // If not, compare to NOW() and return.
+ if ($this->options['adjustment_field'] == 2) {
+ $sql = $handler->sql_field($field, 0) . " $operator ". $handler->sql_field('NOW', $adjustment);
+ $this->query->add_where($this->options['group'], $sql);
+ return;
+ }
+ }
+ $format = '';
+ $selected = array();
+ $separators = $handler->part_info('sep');
+ $formats = $handler->part_info('format');
+ foreach ($filter_parts as $key => $part) {
+ $sep = $separators[$key];
+ $pattern = $key == 'year' ? '%04d' : '%02d';
+ if (is_numeric($this->value[$prefix . $key]) || $this->value[$prefix . $key] == 'now') {
+ $format .= !empty($format) ? $sep : '';
+ $format .= $formats[$key];
+ }
+ if (is_numeric($this->value[$prefix . $key])) {
+ $selected[$key] = sprintf($pattern, check_plain($this->value[$prefix . $key]));
+ }
+ elseif ($this->value[$prefix . $key] == 'now') {
+ $selected[$key] = date($formats[$key]);
+ }
+ else {
+ // When we hit an empty (all values) option in the middle of
+ // our date parts, stop and start a new query.
+ if ($format > '' && $format != $sep) {
+ $date = date_make_date($handler->complete_date($selected));
+ $value = date_format($date, $format);
+ $sql = $handler->sql_where_format($format, $field, $operator, $value);
+ $this->query->add_where($this->options['group'], $sql);
+ }
+ $format = '';
+ $selected = array();
+ }
+ }
+ if ($format > '' && $format != $sep) {
+ $date = date_make_date($handler->complete_date($selected));
+ $value = date_format($date, $format);
+ $sql = $handler->sql_where_format($format, $field, $operator, $value);
+ $this->query->add_where($this->options['group'], $sql);
+ }
+ return;
+ }
+}
+
+/**
+ * Identify all potential date/timestamp fields.
+ *
+ * @return
+ * array with fieldname, type, and table
+ */
+function date_api_fields($base = 'node') {
+ $cid = 'date_api_fields';
+ cache_clear_all($cid, 'cache_views');
+
+ $all_fields = views_fetch_fields($base, 'field');
+ $fields = array();
+ foreach ((array) $all_fields as $name => $val) {
+ $fromto = array();
+ $tmp = explode('.', $name);
+ $field_name = $tmp[1];
+ $table_name = $tmp[0];
+ $alias = str_replace('.', '_', $name);
+
+ $handler = views_get_handler($table_name, $field_name, 'field');
+ $type = '';
+
+ // For cck fields, get the date type.
+ if (isset($handler->content_field)) {
+ if ($handler->content_field['type'] == 'date') {
+ $type = 'cck_string';
+ }
+ elseif ($handler->content_field['type'] == 'datestamp') {
+ $type = 'cck_timestamp';
+ }
+ elseif ($handler->content_field['type'] == 'datetime') {
+ $type = 'cck_datetime';
+ }
+ }
+
+ // This is a core timestamp field.
+ elseif (strstr($field_name, 'timestamp') || strstr($field_name, 'updated')
+ || strstr($field_name, 'created') || strstr($field_name, 'changed')) {
+ $type = 'timestamp';
+ }
+
+
+ // Don't do anything if this is not a date field we can handle.
+ if (!empty($type)) {
+
+ // dates with from and to dates need to handle both fields as one
+ // add the from and to dates to the first one found and ignore the second
+ $fields[$name]['table_name'] = $table_name;
+ $fields[$name]['field_name'] = $field_name;
+ $fields[$name]['type'] = $type;
+
+ // Handling for content field dates
+ if ($handler->content_field['tz_handling']) {
+ $tz_handling = $handler->content_field['tz_handling'];
+ $db_info = content_database_info($handler->content_field);
+ if ($tz_handling == 'date') {
+ $offset_field = $table_name .'.'. $db_info['columns']['offset']['column'];
+ }
+ $related_fields = array(
+ $table_name .'.'. $field_name,
+ $table_name .'.'. $db_info['columns']['value2']['column'],
+ $table_name .'.'. $db_info['columns']['timezone']['column'],
+ );
+ $timezone_field = $table_name .'.'. $db_info['columns']['timezone']['column'];
+ }
+ // Handling for simple timestamp fields
+ else {
+ $fromto = array($alias, $alias);
+ $tz_handling = 'site';
+ $related_fields = array();
+ $timezone_field = '';
+ }
+ // Handling for cck fromto dates
+ switch ($handler->content_field['type']) {
+ case 'date':
+ case 'datetime':
+ case 'datestamp':
+ $db_info = content_database_info($handler->content_field);
+ $fromto = array(
+ $table_name .'_'. $db_info['columns']['value']['column'],
+ $table_name .'_'. ($handler->content_field['todate'] ? $db_info['columns']['value2']['column'] : $db_info['columns']['value']['column']),
+ );
+ break;
+ }
+
+ if (is_array($handler->content_field['granularity'])) {
+ $granularity = $handler->content_field['granularity'];
+ }
+ else {
+ $granularity = array('year', 'month', 'day', 'hour', 'minute');
+ }
+
+ // CCK fields append a column name to the field, others do not
+ // need a real field_name with no column name appended for cck date formatters
+ switch ($type) {
+ case 'cck_string':
+ $sql_type = DATE_ISO;
+ break;
+ case 'cck_datetime':
+ $sql_type = DATE_DATETIME;
+ break;
+ default:
+ $sql_type = DATE_UNIX;
+ break;
+ }
+ $fields['name'][$name] = array(
+ 'type' => $type,
+ 'sql_type' => $sql_type,
+ 'label' => $val['group'] .': '. $val['title'],
+ 'granularity' => $granularity,
+ 'fullname' => $name,
+ 'table_name' => $table_name,
+ 'field_name' => $field_name,
+ 'query_name' => $alias,
+ 'fromto' => $fromto,
+ 'tz_handling' => $tz_handling,
+ 'offset_field' => $offset_field,
+ 'timezone_field' => $timezone_field,
+ 'related_fields' => $related_fields,
+ );
+ $fields['alias'][$alias] = $fields['name'][$name];
+ }
+ }
+
+ //cache_set($cid, $fields, 'cache_views');
+ return $fields;
+} \ No newline at end of file
diff --git a/date_api_sql.inc b/date_api_sql.inc
index 1b16eb0..ab41345 100644
--- a/date_api_sql.inc
+++ b/date_api_sql.inc
@@ -53,6 +53,9 @@ class date_sql_handler {
$this->date_type = $date_type;
$this->db_timezone = 'UTC';
$this->local_timezone = isset($local_timezone) ? $local_timezone : date_default_timezone_name();
+ if (isset($this->definition['content_field'])) {
+ $this->date_handler->date_type = $this->definition['content_field']['type'];
+ }
date_api_set_db_timezone();
}
@@ -435,4 +438,280 @@ class date_sql_handler {
}
return $this->sql_format($format, $field) ." $operator '$value'";
}
-}
+
+ /**
+ * An array of all date parts,
+ * optionally limited to an array of allowed parts.
+ */
+ function date_parts($limit = NULL) {
+ $parts = array(
+ 'year' => t('Year'), 'month' => t('Month'), 'day' => t('Day'),
+ 'hour' => t('Hour'), 'minute' => t('Minute'), 'second' => t('Second'),
+ );
+ if (!empty($limit)) {
+ $last = FALSE;
+ foreach ($parts as $key => $part) {
+ if ($last) {
+ unset($parts[$key]);
+ }
+ if ($key == $limit) {
+ $last = TRUE;
+ }
+ }
+ }
+ return $parts;
+ }
+
+ /**
+ * Part information.
+ *
+ * @param $op
+ * 'min', 'max', 'format', 'sep', 'empty_now', 'empty_min', 'empty_max'.
+ * Returns all info if empty.
+ * @param $part
+ * 'year', 'month', 'day', 'hour', 'minute', or 'second.
+ * returns info for all parts if empty.
+ */
+ function part_info($op = NULL, $part = NULL) {
+ $info = array();
+ $info['min'] = array(
+ 'year' => 100, 'month' => 1, 'day' => 1,
+ 'hour' => 0, 'minute' => 0, 'second' => 0);
+ $info['max'] = array(
+ 'year' => 4000, 'month' => 12, 'day' => 31,
+ 'hour' => 23, 'minute' => 59, 'second' => 59);
+ $info['format'] = array(
+ 'year' => 'Y', 'month' => 'm', 'day' => 'd',
+ 'hour' => 'H', 'minute' => 'i', 'second' => 's');
+ $info['sep'] = array(
+ 'year' => '', 'month' => '-', 'day' => '-',
+ 'hour' => ' ', 'minute' => ':', 'second' => ':');
+ $info['empty_now'] = array(
+ 'year' => date('Y'), 'month' => date('m'), 'day' => min('28', date('d')),
+ 'hour' => date('H'), 'minute' => date('i'), 'second' => date('s'));
+ $info['empty_min'] = array(
+ 'year' => '1000', 'month' => '01', 'day' => '01',
+ 'hour' => '00', 'minute' => '00', 'second' => '00');
+ $info['empty_max'] = array(
+ 'year' => '9999', 'month' => '12', 'day' => '31',
+ 'hour' => '23', 'minute' => '59', 'second' => '59');
+ if (!empty($op)) {
+ if (!empty($part)) {
+ return $info[$op][$part];
+ }
+ else {
+ return $info[$op];
+ }
+ }
+ return $info;
+ }
+
+ /**
+ * Create a complete datetime value out of an
+ * incomplete array of selected values.
+ *
+ * For example, array('year' => 2008, 'month' => 05) will fill
+ * in the day, hour, minute and second with the earliest possible
+ * values if type = 'min', the latest possible values if type = 'max',
+ * and the current values if type = 'now'.
+ */
+ function complete_date($selected, $type = 'now') {
+ $compare = array_merge($this->part_info('empty_'. $type), $selected);
+ // If this is a max date, make sure the last day of
+ // the month is the right one for this date.
+ if ($type == 'max') {
+ $compare['day'] = date('t', mktime(0, 0, 0, $compare['month'], 1, $compare['year']));
+ }
+ $value = '';
+ $separators = $this->part_info('sep');
+ foreach ($this->date_parts() as $key => $name) {
+ $value .= $separators[$key] . (!empty($selected[$key]) ? $selected[$key] : $compare[$key]);
+ }
+ return $value;
+ }
+ /**
+ * Convert a format string into help text,
+ * i.e. 'Y-m-d' becomes 'YYYY-MM-DD'.
+ *
+ * @param unknown_type $format
+ * @return unknown
+ */
+ function format_help($format) {
+ $replace = array(
+ 'Y' => 'YYYY', 'm' => 'MM', 'd' => 'DD',
+ 'H' => 'HH', 'i' => 'MM', 's' => 'SS', '\T' => 'T');
+ return strtr($format, $replace);
+ }
+
+ /**
+ * A function to test the validity of various date parts
+ */
+ function part_is_valid($value, $type) {
+ if ( !preg_match('/^[0-9]*$/', $value) ) {
+ return false;
+ }
+ $value = intval($value);
+ if ($value <= 0) return false;
+ switch ($type) {
+ case 'year':
+ if ($value < DATE_MIN_YEAR) return false;
+ break;
+ case 'month':
+ if ($value < 0 || $value > 12) return false;
+ break;
+ case 'day':
+ if ($value < 0 || $value > 31) return false;
+ break;
+ case 'week':
+ if ($value < 0 || $value > 53) return false;
+ }
+ return true;
+ }
+
+ function views_formats($granularity, $type = 'sql') {
+ $formats = array('display', 'sql');
+ switch ($granularity) {
+ case('year'):
+ $formats['display'] = 'Y';
+ $formats['sql'] = 'Y';
+ break;
+ case('month'):
+ $formats['display'] = 'F Y';
+ $formats['sql'] = 'Y-m';
+ break;
+ case('day'):
+ $formats['display'] = 'F j Y';
+ $formats['sql'] = 'Y-m-d';
+ break;
+ case('hour'):
+ $formats['display'] = 'F j Y - H';
+ $formats['sql'] = 'Y-m-d\TH';
+ break;
+ case('week'):
+ $formats['display'] = 'F j Y (W)';
+ $formats['sql'] = 'Y-\WW';
+ break;
+ }
+ return $formats[$type];
+ }
+
+ /**
+ * Parse date parts from an ISO date argument.
+ *
+ * Based on ISO 8601 date duration and time interval standards.
+ *
+ * See http://en.wikipedia.org/wiki/ISO_8601#Week_dates for definitions of ISO weeks.
+ * See http://en.wikipedia.org/wiki/ISO_8601#Duration for definitions of ISO duration and time interval.
+ *
+ * Parses a value like 2006-01-01--2006-01-15, or 2006-W24, or @P1W.
+ * Separate from and to dates or date and period with a double hyphen (--).
+ *
+ * The 'to' portion of the argument can be eliminated if it is the same as the 'from' portion.
+ * Use @ instead of a date to substitute in the current date and time.
+ *
+ * Use periods (P1H, P1D, P1W, P1M, P1Y) to get next hour/day/week/month/year from now.
+ * Use date before P sign to get next hour/day/week/month/year from that date.
+ * Use period then date to get a period that ends on the date.
+ *
+ */
+ function arg_parts($argument) {
+ $fromto = explode('--', $argument);
+ $values = array();
+ foreach ($fromto as $arg) {
+ $parts = array();
+ if ($date == '@') {
+ $parts['date'] = date_array(date_now());
+ }
+ elseif (preg_match('/(\d{4})?-?(W)?(\d{1,2})?-?(\d{1,2})?[T\s]?(\d{1,2})?:?(\d{1,2})?:?(\d{1,2})?/', $arg, $matches)) {
+ $date = array();
+ if (!empty($matches[1])) $date['year'] = $matches[1];
+ if (!empty($matches[3])) {
+ if (empty($matches[2])) {
+ $date['month'] = $matches[3];
+ }
+ else {
+ $date['week'] = $matches[3];
+ }
+ }
+ if (!empty($matches[4])) $date['day'] = $matches[4];
+ if (!empty($matches[5])) $date['hour'] = $matches[5];
+ if (!empty($matches[6])) $date['minute'] = $matches[6];
+ if (!empty($matches[7])) $date['second'] = $matches[7];
+ $parts['date'] = $date;
+ }
+ if (preg_match('/^P(\d{1,4}[Y])?(\d{1,2}[M])?(\d{1,2}[W])?(\d{1,2}[D])?([T]{0,1})?(\d{1,2}[H])?(\d{1,2}[M])?(\d{1,2}[S])?/', $arg, $matches)) {
+ $period = array();
+ if (!empty($matches[1])) $period['year'] = str_replace('Y', '', $matches[1]);
+ if (!empty($matches[2])) $period['month'] = str_replace('M', '', $matches[2]);
+ if (!empty($matches[3])) $period['week'] = str_replace('W', '', $matches[3]);
+ if (!empty($matches[4])) $period['day'] = str_replace('D', '', $matches[4]);
+ if (!empty($matches[6])) $period['hour'] = str_replace('H', '', $matches[6]);
+ if (!empty($matches[7])) $period['minute'] = str_replace('M', '', $matches[7]);
+ if (!empty($matches[8])) $period['second'] = str_replace('S', '', $matches[8]);
+ $parts['period'] = $period;
+ }
+ $values[] = $parts;
+ }
+ return $values;
+ }
+
+ /**
+ * Use the parsed values from the ISO argument to determine the
+ * min and max date for this period.
+ */
+ function arg_range($arg) {
+
+ // Parse the argument to get its parts
+ $parts = $this->arg_parts($arg);
+
+ // Intercept invalid info and fall back to the current date.
+ if (empty($parts[0]) || empty($parts[0]['date']) && empty($parts[0]['period'])) {
+ $now = date_now();
+ return array($now, $now);
+ }
+
+ // Build a range from a period-only argument (assumes the min date is now.)
+ if (empty($parts[0]['date']) && !empty($parts[0]['period']) && (empty($parts[1]) || empty($parts[1]['date']))) {
+ $min_date = date_now();
+ $max_date = drupal_clone($min_date);
+ foreach ($parts[0]['period'] as $part => $value) {
+ date_modify($max_date, "+$value $part");
+ }
+ date_modify($max_date, '-1 second');
+ return array($min_date, $max_date);
+ }
+ if (!empty($parts[0]['date'])) {
+ $min_date = date_make_date($this->complete_date($parts[0]['date'], 'min'));
+
+ // Build a range from a single date-only argument.
+ if (empty($parts[1]) || (empty($parts[1]['date']) && empty($parts[1]['period']))) {
+ $max_date = date_make_date($this->complete_date($parts[0]['date'], 'max'));
+ return array($min_date, $max_date);
+ }
+ // Build a range from start date + period.
+ elseif (!empty($parts[1]['period'])) {
+ foreach ($parts[1]['period'] as $part => $value) {
+ $max_date = drupal_clone($min_date);
+ date_modify($max_date, "+$value $part");
+ }
+ date_modify($max_date, '-1 second');
+ return array($min_date, $max_date);
+ }
+ }
+ // Build a range from start date and end date.
+ if (!empty($parts[1]['date'])) {
+ $max_date = $this->complete_date($parts[1]['date'], 'max');
+ if (isset($min_date)) {
+ return array($min_date, $max_date);
+ }
+ }
+ // Build a range from period + end date.
+ if (!empty($parts[0]['period'])) {
+ foreach ($parts[0]['period'] as $part => $value) {
+ $min_date = drupal_clone($max_date);
+ date_modify($min_date, "-$value $part");
+ }
+ return array($min_date, $max_date);
+ }
+ }
+} \ No newline at end of file
diff --git a/theme/date-navigation.tpl.php b/theme/date-navigation.tpl.php
new file mode 100644
index 0000000..1442500
--- /dev/null
+++ b/theme/date-navigation.tpl.php
@@ -0,0 +1,42 @@
+<?php
+// $Id$
+/**
+ * @file
+ * Template to display date naviagion links.
+ *
+ * $nav_title
+ * The formatted title for this calendar. In the case of mini
+ * calendars, it will be a link to the full view of the calendar,
+ * otherwise it will be the formatted name of the year, month, day,
+ * or week.
+ * $prev_url
+ * $next_url
+ * Urls for the previous and next calendar pages. The links are
+ * composed in the template to make it easier to change the text,
+ * add images, etc.
+ * $mini: Whether or not this is a mini calendar.
+ * $block: Whether or not this calendar is in a block.
+ * $view
+ * The view object for this calendar.
+ *
+ * The &nbsp; in the prev and next divs is to be sure they are never
+ * completely empty, needed in some browsers to prop the header open
+ * so the title stays centered.
+ *
+ */
+?>
+<div class="date-nav clear-block">
+ <div class="prev">
+ <?php if (!empty($prev_url)) : ?>
+ <span class="next"> <?php print l($mini ? '«' : t('« prev'), $prev_url); ?></span>
+ <?php endif; ?>
+ &nbsp;</div>
+ <div class="heading">
+ <h3><?php print $nav_title ?></h3>
+ </div>
+ <div class="next">&nbsp;
+ <?php if (!empty($next_url)) : ?>
+ <span class="next"> <?php print l($mini ? '»' : t('next »'), $next_url); ?></span>
+ <?php endif; ?>
+ </div>
+</div>
diff --git a/theme/theme.inc b/theme/theme.inc
new file mode 100644
index 0000000..3c6b736
--- /dev/null
+++ b/theme/theme.inc
@@ -0,0 +1,62 @@
+<?php
+// $Id$
+/**
+ * Preprocessor to construct back and next navigation from the date argument.
+ */
+function template_preprocess_date_navigation(&$vars) {
+ $view = $vars['view'];
+
+ if (!isset($view->hide_nav)) {
+ $min_date = is_object($view->min_date) ? $view->min_date : date_now();
+ $max_date = is_object($view->max_date) ? $view->max_date : date_now();
+ $prev_date = drupal_clone($min_date);
+ date_modify($prev_date, '-1 '. $view->date_type);
+ $next_date = drupal_clone($min_date);
+ date_modify($next_date, '+1 '. $view->date_type);
+ $format = array('year' => 'Y', 'month' => 'Y-m', 'day' => 'Y-m-d');
+ switch ($view->date_type) {
+ case 'week':
+ $next_week = date_week(date_format($next_date, 'Y-m-d'));
+ $prev_week = date_week(date_format($prev_date, 'Y-m-d'));
+ $next_path = str_replace($view->date_arg, date_format($next_date, 'Y-\W') . $next_week, $view->get_url());
+ $prev_path = str_replace($view->date_arg, date_format($prev_date, 'Y-\W') . $prev_week, $view->get_url());
+ break;
+ default:
+ $next_path = str_replace($view->date_arg, date_format($next_date, $format[$view->date_type]), $view->get_url());
+ $prev_path = str_replace($view->date_arg, date_format($prev_date, $format[$view->date_type]), $view->get_url());
+ }
+ }
+ else {
+ $next_path = '';
+ $prev_path = '';
+ }
+ $vars['next_url'] = $next_path;
+ $vars['prev_url'] = $prev_path;
+
+ if ($view->mini && $view->date_type == 'month') {
+ // Month navigation titles are used as links in the mini view.
+ $nav_title = l(date_format_date($view->min_date, 'custom', 'M'), str_replace($view->date_arg, date_format($view->min_date, 'Y-m'), $view->get_url()));
+ }
+ else {
+ // Otherwise, just show the date.
+ $nav_title = theme('date_nav_title', $view->date_type, $view);
+ }
+ $vars['nav_title'] = $nav_title;
+ $vars['mini'] = $view->mini;
+ $vars['block'] = $view->block;
+}
+/**
+ * Theme the calendar title
+ */
+function theme_date_nav_title($date_type, $view) {
+ switch ($date_type) {
+ case 'year':
+ return $view->year;
+ case 'month':
+ return date_format_date($view->min_date, 'custom', 'F');
+ case 'day':
+ return date_format_date($view->min_date, 'custom', 'l, F j Y');
+ case 'week':
+ return t('Week of @date', array('@date' => date_format($view->min_date, 'F j')));
+ }
+} \ No newline at end of file