'Date views', 'description' => 'Configure settings for date views.', 'page callback' => 'drupal_get_form', 'page arguments' => array('date_views_settings'), 'access arguments' => array('administer site configuration '), 'type' => MENU_LOCAL_TASK, ); return $items; } /** * Form callback for date views settings. */ function date_views_settings($form, &$form_state) { $form['date_views_month_format_with_year'] = array( '#type' => 'textfield', '#title' => t('Date views month format with year'), '#size' => 10, '#default_value' => variable_get('date_views_month_format_with_year', 'F Y'), '#description' => t('Date views month format with year, default value : F Y'), ); $form['date_views_month_format_without_year'] = array( '#type' => 'textfield', '#title' => t('Date views month format without year'), '#size' => 10, '#default_value' => variable_get('date_views_month_format_without_year', 'F'), '#description' => t('Date views month format without year, default value : F'), ); $form['date_views_day_format_with_year'] = array( '#type' => 'textfield', '#title' => t('Date views day format with year'), '#size' => 10, '#default_value' => variable_get('date_views_day_format_with_year', 'l, F j, Y'), '#description' => t('Date views day format with year, default value : l, F j, Y'), ); $form['date_views_day_format_without_year'] = array( '#type' => 'textfield', '#title' => t('Date views day format without year'), '#size' => 10, '#default_value' => variable_get('date_views_day_format_without_year', 'l, F j'), '#description' => t('Date views day format without year, default value : l, F j'), ); $form['date_views_week_format_with_year'] = array( '#type' => 'textfield', '#title' => t('Date views week format with year'), '#size' => 10, '#default_value' => variable_get('date_views_week_format_with_year', 'F j, Y'), '#description' => t('Date views week format with year, default value : F j, Y'), ); $form['date_views_week_format_without_year'] = array( '#type' => 'textfield', '#title' => t('Date views week format without year'), '#size' => 10, '#default_value' => variable_get('date_views_week_format_without_year', 'F j'), '#description' => t('Date views week format without year, default value : F j'), ); return system_settings_form($form); } /** * Implements hook_views_api(). * * This one is used as the base to reduce errors when updating. */ function date_views_theme() { $path = drupal_get_path('module', 'date_views'); $base = array( 'file' => 'theme.inc', 'path' => "$path/theme", ); return array( 'date_nav_title' => $base + array('variables' => array('granularity' => NULL, 'view' => NULL, 'link' => NULL, 'format' => NULL)), 'date_views_filter_form' => $base + array('template' => 'date-views-filter-form', 'render element' => 'form'), 'date_calendar_day' => $base + array('variables' => array('date' => NULL)), 'date_views_pager' => $base + array( 'variables' => array('plugin' => NULL, 'input' => NULL), // Register a pattern so that it can work like all views templates. 'pattern' => 'date_views_pager__', 'template' => 'date-views-pager', ), ); } function date_views_views_api() { return array( 'api' => 3, 'path' => drupal_get_path('module', 'date_views') . '/includes', ); } /** * Wrapper function to make sure this function will always work. */ function date_views_views_fetch_fields($base, $type) { if (!module_exists('views')) { return array(); } module_load_include('inc', 'views', 'includes/admin'); return views_fetch_fields($base, $type); } /** * Identify all potential date/timestamp fields and cache the data. */ function date_views_fields($base = 'node', $reset = FALSE) { static $fields = array(); $empty = array('name' => array(), 'alias' => array()); module_load_include('inc', 'date_views', 'includes/date_views_fields'); if (empty($fields[$base]) || $reset) { $cid = 'date_views_fields_' . $base; if (!$reset && $cached = cache_get($cid, 'cache_views')) { $fields[$base] = $cached->data; } else { $fields[$base] = _date_views_fields($base); } } // Make sure that empty values will be arrays in he expected format. return !empty($fields) && !empty($fields[$base]) ? $fields[$base] : $empty; } /** * Implements hook_date_views_entities(). * Map extra Views tables to the entity that holds its date fields, * needed for Views tables other than the primary tables identified in entity_info(). */ function date_views_date_views_extra_tables() { return array( 'node_revision' => 'node', ); } /** * Helper function to map entity types to the Views base table they use, * to make it easier to infer the entity type from a base table. * * Views has a new handler called views_handler_field_entity() that loads * entities, and you can use something like the following to get the * entity type from a view, but not all our base tables contain the * entity information we need, (i.e. revisions) so it won't work here * and we resort to creating information from entity_get_info(). * * // A method to get the entity type for a base table. * $table_data = views_fetch_data($base_table); * if (!isset($table_data['table']['base']['entity type'])) { * return FALSE; * } * $entity_type = $table_data['table']['base']['entity type']; */ function date_views_base_tables() { $base_tables = &drupal_static(__FILE__, array()); if (empty($base_tables)) { // First we get the base tables we can learn about from entity_info. $entity_info = entity_get_info(); foreach ($entity_info as $entity_type => $info) { if (!empty($info['base table'])) { $base_tables[$info['base table']] = $entity_type; } if (!empty($info['revision table'])) { $base_tables[$info['revision table']] = $entity_type; } } // Then we let other modules tell us about other entity tables that hold date fields. $base_tables += module_invoke_all('date_views_extra_tables'); } return $base_tables; } /** * Implements hook_date_views_fields(). * * All modules that create custom fields that use the * 'views_handler_field_date' handler can provide * additional information here about the type of * date they create so the date can be used by * the Date API views date argument and date filter. */ function date_views_date_views_fields($field) { $values = array( // The type of date: DATE_UNIX, DATE_ISO, DATE_DATETIME. 'sql_type' => DATE_UNIX, // Timezone handling options: 'none', 'site', 'date', 'utc' . 'tz_handling' => 'site', // Needed only for dates that use 'date' tz_handling. 'timezone_field' => '', // Needed only for dates that use 'date' tz_handling. 'offset_field' => '', // Array of "table.field" values for related fields that should be // loaded automatically in the Views SQL. 'related_fields' => array(), // Granularity of this date field's db data. 'granularity' => array('year', 'month', 'day', 'hour', 'minute', 'second'), ); switch ($field) { case 'users.created': case 'users.access': case 'users.login': case 'node.created': case 'node.changed': case 'node_revision.timestamp': case 'file_managed.timestamp': case 'comment.timestamp': return $values; } } /** * A version of date_real_url that formats links correctly for the new Date pager. */ function date_pager_url($view, $date_type = NULL, $date_arg = NULL, $force_view_url = FALSE, $absolute = TRUE) { // If someone adds a pager without a matching argument, there is not date information to work with. if (empty($view->date_info) || !isset($view->date_info->date_arg_pos)) { return ''; } $args = $view->args; $pos = $view->date_info->date_arg_pos; // The View arguments array is indexed numerically but is not necessarily // in numerical order. Sort the arguments to ensure the correct order. ksort($args); // If there are empty arguments before the date argument, // pad them with the wildcard so the date argument will be in // the right position. if (count($args) < $pos) { foreach ($view->argument as $name => $argument) { if ($argument->position == $pos) { break; } $args[] = $argument->options['exception']['value']; } } if (!empty($date_type)) { switch ($date_type) { case 'year': $args[$pos] = date_pad($view->date_info->year, 4); break; case 'week': $args[$pos] = date_pad($view->date_info->year, 4) . '-W' . date_pad($view->date_info->week); break; case 'day': $args[$pos] = date_pad($view->date_info->year, 4) . '-' . date_pad($view->date_info->month) . '-' . date_pad($view->date_info->day); break; default: $args[$pos] = date_pad($view->date_info->year, 4) . '-' . date_pad($view->date_info->month); break; } } elseif (!empty($date_arg)) { $args[$pos] = $date_arg; } else { $args = $view->args; } // Is this an embedded or a block view? // Return the pager query value. if (!$force_view_url && (!empty($view->preview) || !empty($view->date_info->block_identifier))) { $url = $args[$pos]; $key = date_block_identifier($view); if (!empty($key)) { return url($_GET['q'], array( 'query' => date_views_querystring($view, array($key => $url)), 'absolute' => $absolute)); } } // Normal views may need querystrings appended to them // if they use exposed filters. return url($view->get_url($args), array( 'query' => date_views_querystring($view), 'absolute' => $absolute)); } function date_block_identifier($view) { if (!empty($view->block_identifier)) { return $view->block_identifier; } return isset($view->date_info->block_identifier) ? $view->date_info->block_identifier : NULL; } /** * Implements hook_field_views_data_alter(). * * Create a Views field for each date column we care about * to supplement the generic 'entity_id' and 'revision_id' * fields that are automatically created. * * Also use friendlier labels to distinguish the start date * and end date in listings (for fields that use both). */ function date_views_field_views_data_alter(&$result, $field, $module) { if ($module == 'date') { $has_end_date = !empty($field['settings']['todate']); if ($has_end_date) { $labels = field_views_field_label($field['field_name']); $label = array_shift($labels); } foreach ($result as $table => $data) { $additional = array(); $field_name = $field['field_name']; foreach ($data as $column => $value) { // The old 'entity_id' and 'revision_id' values got rewritten in Views. // The old values are still there with a 'moved to' key, so ignore them. if (array_key_exists('field', $value) && !array_key_exists('moved to', $value['field'])) { $result[$table][$column]['field']['is date'] = TRUE; // Not sure yet if we still need a custom field handler in D7 now that custom formatters are available. // Might still need it to handle grouping of multiple value dates. //$result[$table][$column]['field']['handler'] = 'date_handler_field_date'; //$result[$table][$column]['field']['add fields to query'] = TRUE; } // For filters, arguments, and sorts, determine if this column is for // the start date ('value') or the end date ('value2'). $this_column = NULL; foreach (array_keys($field['columns']) as $candidate_column) { if ($column == $field['field_name'] . '_' . $candidate_column) { $this_column = $candidate_column; break; } } // Only alter the date fields, not timezone, rrule, offset, etc. if ($this_column != 'value' && $this_column != 'value2') { continue; } // We will replace the label with a friendlier name in the case of // arguments, filters, and sorts (but only if this field uses an end // date). $replace_label = FALSE; if (array_key_exists('argument', $value)) { $result[$table][$column]['argument']['handler'] = 'date_views_argument_handler_simple'; $result[$table][$column]['argument']['empty field name'] = t('Undated'); $result[$table][$column]['argument']['is date'] = TRUE; $replace_label = $has_end_date; } if (array_key_exists('filter', $value)) { $result[$table][$column]['filter']['handler'] = 'date_views_filter_handler_simple'; $result[$table][$column]['filter']['empty field name'] = t('Undated'); $result[$table][$column]['filter']['is date'] = TRUE; $replace_label = $has_end_date; } if (array_key_exists('sort', $value)) { $result[$table][$column]['sort']['is date'] = TRUE; $replace_label = $has_end_date; } if ($replace_label) { // Determine if this column is for the start date ('value') or the // end date ('value2'). $this_column = NULL; foreach (array_keys($field['columns']) as $candidate_column) { if ($column == $field['field_name'] . '_' . $candidate_column) { $this_column = $candidate_column; break; } } // Insert the phrase "start date" or "end date" after the label, so // users can distinguish them in listings (compared to the default // behavior of field_views_field_default_views_data(), which only // uses the 'value2' column name to distinguish them). switch ($this_column) { case 'value': // Insert a deliberate double space before 'start date' in the // translatable string. This is a hack to get it to appear right // before 'end date' in the listing (i.e., in a non-alphabetical, // but more user friendly, order). $result[$table][$column]['title'] = t('@label - start date (!name)', array('@label' => $label, '!name' => $field['field_name'])); $result[$table][$column]['title short'] = t('@label - start date', array('@label' => $label)); break; case 'value2': $result[$table][$column]['title'] = t('@label - end date (!name:!column)', array('@label' => $label, '!name' => $field['field_name'], '!column' => $this_column)); $result[$table][$column]['title short'] = t('@label - end date:!column', array('@label' => $label, '!column' => $this_column)); break; } } } } } } /** * Implements hook_form_FORM_ID_alter() for views_ui_edit_form(). */ function date_views_form_views_ui_edit_form_alter(&$form, &$form_state, $form_id) { // This CSS is needed for the configuration form provided by the Date filter // (date_views_filter_handler_simple), but we have to add it here so that // it's already on the edit form the first time a Date filter is being added // to the View. See http://drupal.org/node/1239228#comment-4885288. $form['#attached']['css'][] = drupal_get_path('module', 'date_views') . '/css/date_views.css'; } /** * The instanceof function makes this work for any handler that was derived * from 'views_handler_filter_date' or 'views_handler_argument_date', * which includes core date fields like the node updated field. * * The test for $handler->min_date tells us that this is an argument that * not only is derived from the views date handler but also has been processed * by the Date Views filter or argument code. */ function date_views_handler_is_date($handler, $type = 'argument') { switch ($type) { case 'filter': return $handler instanceof views_handler_filter_date && !empty($handler->min_date); case 'argument': return $handler instanceof views_handler_argument_date && !empty($handler->min_date); } return FALSE; } /** * Validation hook for exposed filters that use the select widget. * This is to ensure the the user completes all parts of the date * not just some parts. Only needed for the select widget. */ function date_views_select_validate(&$form, &$form_state) { // If there are no values just return. if (empty($form['value']) && empty($form['min'])) { return; } $granularity = (!empty($form['min']['#date_format'])) ? date_format_order($form['min']['#date_format']) : date_format_order($form['value']['#date_format']); $filled = array(); $value = drupal_array_get_nested_value($form_state['input'], $form['#parents']); foreach ($granularity as $part) { if (!empty($value['value'][$part])) { $filled[] = $part; } } if (!empty($filled) && count($filled) != count($granularity)) { $unfilled = array_diff($granularity, $filled); foreach ($unfilled as $part) { switch ($part) { case 'year': form_error($form['value'][$part], t('Please choose a year.'), $form_state); break; case 'month': form_error($form['value'][$part], t('Please choose a month.'), $form_state); break; case 'day': form_error($form['value'][$part], t('Please choose a day.'), $form_state); break; case 'hour': form_error($form['value'][$part], t('Please choose an hour.'), $form_state); break; case 'minute': form_error($form['value'][$part], t('Please choose a minute.'), $form_state); break; case 'second': form_error($form['value'][$part], t('Please choose a second.'), $form_state); break; } } } } /** * Implements hook_date_formatter_view_alter(). * * If we are displaying a date from a view, see if we have information about * which multiple value to display. If so, set the date_id in the entity. */ function date_views_date_formatter_pre_view_alter(&$entity, &$variables) { // Some views have no row index. if (!empty($entity->view) && isset($entity->view->row_index)) { $field = $variables['field']; $date_id = 'date_id_' . $field['field_name']; $date_delta = 'date_delta_' . $field['field_name']; $date_item = $entity->view->result[$entity->view->row_index]; if (!empty($date_item->$date_id)) { $entity->date_id = 'date.' . $date_item->$date_id . '.' . $field['field_name'] . '.' . $date_item->$date_delta . '.0'; } } }