date.module in Date 8
Same filename and directory in other branches
Defines date/time field types.
File
date.moduleView source
<?php
/**
* @file
* Defines date/time field types.
*/
use Drupal\Core\Datetime\DrupalDateTime;
use Drupal\date_api\DateGranularity;
module_load_include('theme', 'date', 'date');
module_load_include('inc', 'date', 'date.field');
module_load_include('inc', 'date', 'date_elements');
/**
* Custom validation for the timestamp form element.
* The date element contains an ISO date created from user input,
* convert it back to a timestamp.
*/
function _timestamp_validate(&$element, &$form_state) {
$input_exists = FALSE;
$input = drupal_array_get_nested_value($form_state['values'], $element['#parents'], $input_exists);
if ($input_exists) {
$date = new DrupalDateTime($input['value'], $element['value']['#date_timezone']);
dsm($date);
// If this is an all day date, set the time back to midnight before saving it.
if (!empty($input['all_day'])) {
$date
->setTime(0, 0, 0);
}
$date
->setTimeZone(timezone_open('UTC'));
form_set_value($element['value'], $date
->getTimestamp(), $form_state);
}
}
/**
* Helper function to figure out the bundle name for an entity.
*/
function date_get_entity_bundle($entity_type, $entity) {
switch ($entity_type) {
case 'field_collection_item':
$bundle = $entity->field_name;
break;
default:
$bundle = field_extract_bundle($entity_type, $entity);
break;
}
// If there is no bundle name, field_info() uses the entity name as the bundle
// name in its arrays.
if (empty($bundle)) {
$bundle = $entity_type;
}
return $bundle;
}
/**
* Wrapper function around each of the widget types for creating a date object.
*/
function date_input_date($field, $instance, $element, $input) {
switch ($instance['widget']['type']) {
case 'date_popup':
$function = 'datetime_get_input_date';
break;
default:
$function = 'datelist_get_input_date';
}
return $function($element, $input);
}
/**
* Implements hook_theme().
*/
function date_theme() {
$path = drupal_get_path('module', 'date');
module_load_include('theme', 'date', 'date');
$base = array(
'file' => 'date.theme',
'path' => "{$path}",
);
$themes = array(
'date_combo' => $base + array(
'render element' => 'element',
),
'date_text_parts' => $base + array(
'render element' => 'element',
),
'date' => $base + array(
'render element' => 'element',
),
'date_display_single' => $base + array(
'variables' => array(
'date' => NULL,
'timezone' => NULL,
'dates' => NULL,
'attributes' => array(),
'rdf_mapping' => NULL,
'add_rdf' => NULL,
),
),
'date_display_range' => $base + array(
'variables' => array(
'date1' => NULL,
'date2' => NULL,
'timezone' => NULL,
'dates' => NULL,
// HTML attributes that will be applied to both the start and end dates
// (unless overridden).
'attributes' => array(),
// HTML attributes that will be applied to the start date only.
'attributes_start' => array(),
// HTML attributes that will be applied to the end date only.
'attributes_end' => array(),
'rdf_mapping' => NULL,
'add_rdf' => NULL,
),
),
'date_display_combination' => $base + array(
'variables' => array(
'entity_type' => NULL,
'entity' => NULL,
'field' => NULL,
'instance' => NULL,
'langcode' => NULL,
'item' => NULL,
'delta' => NULL,
'display' => NULL,
'dates' => NULL,
'attributes' => array(),
'rdf_mapping' => NULL,
'add_rdf' => NULL,
),
),
'date_display_interval' => $base + array(
'variables' => array(
'entity_type' => NULL,
'entity' => NULL,
'field' => NULL,
'instance' => NULL,
'langcode' => NULL,
'item' => NULL,
'delta' => NULL,
'display' => NULL,
'dates' => NULL,
'attributes' => array(),
'rdf_mapping' => NULL,
'add_rdf' => NULL,
),
),
);
return $themes;
}
/**
* Implements hook_element_info().
*
* date_combo will create a 'start' and optional 'end' date, along with
* an optional 'timezone' column for date-specific timezones. Each
* 'start' and 'end' date will be constructed from date_select.
*/
function date_element_info() {
$type = array();
$type['date_combo'] = array(
'#input' => TRUE,
'#delta' => 0,
'#columns' => array(
'value',
'value2',
'timezone',
'offset',
'offset2',
),
'#process' => array(
'date_combo_element_process',
),
'#element_validate' => array(
'date_combo_validate',
),
'#theme_wrappers' => array(
'date_combo',
),
);
if (module_exists('ctools')) {
$type['date_combo']['#pre_render'] = array(
'ctools_dependent_pre_render',
);
}
return $type;
}
/**
* Helper function for creating formatted date arrays from a formatter.
*
* Use the Date API to get an object representation of a date field.
*
* @param string $formatter
* The date formatter.
* @param string $entity_type
* The entity_type for the instance
* @param object $entity
* The entity object.
* @param array $field
* The field info array.
* @param array $instance
* The field instance array.
* @param string $langcode
* The language code used by this field.
* @param array $item
* An entity field item, like $entity->myfield[0].
* @param array $display
* The instance display settings.
*
* @return array
* An array that holds the Start and End date objects.
* Each date object looks like:
* date [value] => array (
* [db] => array ( // the value stored in the database
* [object] => the datetime object
* [datetime] => 2007-02-15 20:00:00
* )
* [local] => array ( // the local representation of that value
* [object] => the datetime object
* [datetime] => 2007-02-15 14:00:00
* [timezone] => US/Central
* [offset] => -21600
* )
* )
*/
function date_formatter_process($formatter, $entity, $field, $instance, $langcode, $item, $settings) {
$dates = array();
$timezone = drupal_get_user_timezone();
if (empty($timezone)) {
return $dates;
}
$granularity = date_granularity($field);
$field_name = $field['field_name'];
$format = date_formatter_format($formatter, $settings, $granularity, $langcode);
$timezone = isset($item['timezone']) ? $item['timezone'] : '';
$timezone = date_get_timezone($field['settings']['tz_handling'], $timezone);
$timezone_db = date_get_timezone_db($field['settings']['tz_handling']);
$db_format = date_type_format($field['type']);
$keys = date_available_values($field);
foreach ($keys as $key) {
if (empty($item[$key])) {
$dates[$key] = NULL;
}
else {
// Create a date object with a GMT timezone from the database value.
$dates[$key] = array();
// Check to see if this date was already created by date_field_load().
if (isset($item['db'][$key])) {
$date = $item['db'][$key];
}
else {
$date = new DrupalDateTime($item[$key], $timezone_db, $db_format);
$date->granularity = $granularity;
}
$dates[$key]['db']['object'] = $date;
$dates[$key]['db']['datetime'] = date_format($date, DATE_FORMAT_DATETIME);
date_timezone_set($date, timezone_open($timezone));
$dates[$key]['local']['object'] = $date;
$dates[$key]['local']['datetime'] = date_format($date, DATE_FORMAT_DATETIME);
$dates[$key]['local']['timezone'] = $timezone;
$dates[$key]['local']['offset'] = date_offset_get($date);
// Format the date, special casing the 'interval' format which doesn't
// need to be processed.
$dates[$key]['formatted'] = '';
$dates[$key]['formatted_iso'] = $date
->format('c');
if ($date instanceof DrupalDateTime) {
if ($format == 'format_interval') {
$dates[$key]['interval'] = date_format_interval($date);
}
elseif ($format == 'format_calendar_day') {
$dates[$key]['calendar_day'] = date_format_calendar_day($date);
}
elseif ($format == 'U') {
$dates[$key]['formatted'] = $date
->format($format);
$dates[$key]['formatted_date'] = $date
->format($format);
$dates[$key]['formatted_time'] = '';
$dates[$key]['formatted_timezone'] = '';
}
elseif (!empty($format)) {
$dates[$key]['formatted'] = $date
->format($format);
$dates[$key]['formatted_date'] = $date
->format(DateGranularity::limitFormat($format, DateGranularity::$date_parts));
$dates[$key]['formatted_time'] = $date
->format(DateGranularity::limitFormat($format, DateGranularity::$time_parts));
$dates[$key]['formatted_timezone'] = $date
->format(DateGranularity::limitFormat($format, array(
'timezone',
)));
}
}
}
}
if (empty($dates['value2'])) {
$dates['value2'] = $dates['value'];
}
// Allow other modules to alter the date values.
$context = array(
'field' => $field,
'instance' => $instance,
'format' => $format,
'entity' => $entity,
'langcode' => $langcode,
'item' => $item,
'settings' => $settings,
);
drupal_alter('date_formatter_dates', $dates, $context);
$dates['format'] = $format;
return $dates;
}
/**
* Retrieves the granularity for a field.
*
* $field['settings']['granularity'] will contain an array like
* ('hour' => 'hour', 'month' => 0) where the values turned on return their own
* names and the values turned off return a zero need to reconfigure this into
* simple array of the turned on values
*
* @param array $field
* The field array.
*/
function date_granularity($field) {
if (!is_array($field) || !is_array($field['settings']['granularity'])) {
$field['settings']['granularity'] = drupal_map_assoc(array(
'year',
'month',
'day',
));
}
return array_values(array_filter($field['settings']['granularity']));
}
/**
* Helper function to create an array of the date values in a
* field that need to be processed.
*/
function date_available_values($field) {
return $field['settings']['todate'] ? array(
'value',
'value2',
) : array(
'value',
);
}
/**
* Helper function to retrieve the possible keys stored in the data array.
*/
function date_data_keys() {
return array(
'all_day',
'all_day2',
'rrule',
);
}
/**
* Implements hook_form_FORM_ID_alter() for field_ui_field_edit_form().
*/
function date_form_field_ui_field_edit_form_alter(&$form, &$form_state, $form_id) {
$field = $form['#field'];
$instance = $form['#instance'];
if ($field['type'] != 'date') {
return;
}
// Reorganize the instance settings and widget settings sections into a more
// intuitive combined fieldset.
$form['instance']['defaults'] = array(
'#type' => 'fieldset',
'#title' => t('More settings and values'),
'#collapsible' => TRUE,
'#collapsed' => TRUE,
);
$form['instance']['date_format'] = array(
'#type' => 'fieldset',
'#title' => t('Date entry'),
'#collapsible' => TRUE,
'#collapsed' => FALSE,
);
$form['instance']['default_values'] = array(
'#type' => 'fieldset',
'#title' => t('Default values'),
'#collapsible' => TRUE,
'#collapsed' => FALSE,
);
$form['instance']['years_back_and_forward'] = array(
'#type' => 'fieldset',
'#title' => t('Starting and ending year'),
'#collapsible' => TRUE,
'#collapsed' => FALSE,
);
$form['instance']['#pre_render'][] = 'date_field_ui_field_edit_form_pre_render';
}
/**
* Rearrange form elements into fieldsets for presentation only.
*/
function date_field_ui_field_edit_form_pre_render($form) {
foreach ($form as $name => $element) {
if (is_array($element) && isset($element['#fieldset'])) {
$fieldset = $element['#fieldset'];
$form[$fieldset][$name] = $element;
unset($form[$name]);
}
}
foreach (array(
'date_format',
'default_values',
'years_back_and_forward',
) as $name) {
if (element_children($form[$name])) {
// Force the items in the fieldset to be resorted now that the instance
// and widget settings are combined.
$form[$name]['#sorted'] = FALSE;
$form['defaults'][$name] = $form[$name];
}
unset($form[$name]);
}
return $form;
}
/**
* Implements hook_field_widget_error().
*/
function date_field_widget_error($element, $error, $form, &$form_state) {
form_error($element[$error['error']], $error['message']);
}
/**
* Retrieve a date format string from formatter settings.
*/
function date_formatter_format($formatter, $settings, $granularity = array(), $langcode = NULL) {
$format_type = !empty($settings['format_type']) ? $settings['format_type'] : 'format_interval';
switch ($formatter) {
case 'format_interval':
return 'format_interval';
break;
case 'date_plain':
return 'date_plain';
break;
default:
$format = date_format_type_format($format_type, $langcode);
break;
}
// A selected format might include timezone information.
array_push($granularity, 'timezone');
return DateGranularity::limitFormat($format, $granularity);
}
/**
* Implements hook_field_widget_WIDGETTYPE_form_alter().
*/
function date_field_widget_date_popup_form_alter($element, $form_state, $context) {
}
/**
* Helper function to get the right format for a format type.
* Checks for locale-based format first.
*/
function date_format_type_format($format_type, $langcode = NULL) {
$static =& drupal_static(__FUNCTION__);
if (!isset($static[$langcode][$format_type])) {
$format = system_date_format_locale($langcode, $format_type);
// If locale enabled and $format_type inexistent in {date_format_locale}
// we receive (due to inconsistency of core api) an array of all (other)
// formats available for $langcode in locale table.
// However there's no guarantee that the key $format_type exists.
// See http://drupal.org/node/1302358.
if (!is_string($format)) {
// If the configuration page at admin/config/regional/date-time was
// never saved, the default core date format variables
// ('date_format_short', 'date_format_medium', and 'date_format_long')
// will not be stored in the database, so we have to define their
// expected defaults here.
switch ($format_type) {
case 'short':
$default = 'm/d/Y - H:i';
break;
case 'long':
$default = 'l, F j, Y - H:i';
break;
// If it's not one of the core date types and isn't stored in the
// database, we'll fall back on using the same default format as the
// 'medium' type.
case 'medium':
default:
// @todo: If a non-core module provides a date type and does not
// variable_set() a default for it, the default assumed here may
// not be correct (since the default format used by 'medium' may
// not even be one of the allowed formats for the date type in
// question). To fix this properly, we should really call
// system_get_date_formats($format_type) and take the first
// format from that list as the default. However, this function
// is called often (on many different page requests), so calling
// system_get_date_formats() from here would be a performance hit
// since that function writes several records to the database
// during each page request that calls it.
$default = 'D, m/d/Y - H:i';
break;
}
$format = variable_get('date_format_' . $format_type, $default);
}
$static[$langcode][$format_type] = $format;
}
return $static[$langcode][$format_type];
}
/**
* Helper function to adapt entity date fields to formatter settings.
*/
function date_prepare_entity($formatter, $entity_type, $entity, $field, $instance, $langcode, $item, $display) {
// If there are options to limit multiple values,
// alter the entity values to match.
$field_name = $field['field_name'];
$options = $display['settings'];
$max_count = $options['multiple_number'];
// If no results should be shown, empty the values and return.
if (is_numeric($max_count) && $max_count == 0) {
$entity->{$field_name} = array();
return $entity;
}
// Otherwise removed values that should not be displayed.
if (!empty($options['multiple_from']) || !empty($options['multiple_to']) || !empty($max_count)) {
$date_api_config = config('date_api.info');
$format = date_type_format($field['type']);
include_once drupal_get_path('module', 'date_api') . '/date_api_sql.inc';
$date_handler = new DateSqlHandler($field);
$arg0 = !empty($options['multiple_from']) ? $date_handler
->arg_replace($options['multiple_from']) : $date_api_config
->get('year.min') . '-01-01T00:00:00';
$arg1 = !empty($options['multiple_to']) ? $date_handler
->arg_replace($options['multiple_to']) : $date_api_config
->get('year.max') . '-12-31T23:59:59';
if (!empty($arg0) && !empty($arg1)) {
$arg = $arg0 . '--' . $arg1;
}
elseif (!empty($arg0)) {
$arg = $arg0;
}
elseif (!empty($arg1)) {
$arg = $arg1;
}
if (!empty($arg)) {
$range = $date_handler
->arg_range($arg);
$start = date_format($range[0], $format);
$end = date_format($range[1], $format);
// Empty out values we don't want to see.
$count = 0;
foreach ($entity->{$field_name}[$langcode] as $delta => $value) {
if (!empty($entity->date_repeat_show_all)) {
break;
}
elseif (!empty($max_count) && is_numeric($max_count) && $count >= $max_count || !empty($value['value']) && $value['value'] < $start || !empty($value['value2']) && $value['value2'] > $end) {
unset($entity->{$field_name}[$langcode][$delta]);
}
else {
$count++;
}
}
}
}
return $entity;
}
/**
* The callback for setting a default value for an empty date field.
*/
function date_default_value($entity_type, $entity, $field, $instance, $langcode) {
$item = array();
$db_format = date_type_format($field['type']);
$date = date_default_value_part($item, $field, $instance, $langcode, 'value');
$item[0]['value'] = $date instanceof DrupalDateTime ? date_format($date, $db_format) : '';
if (!empty($field['settings']['todate'])) {
$date = date_default_value_part($item, $field, $instance, $langcode, 'value2');
$item[0]['value2'] = $date instanceof DrupalDateTime ? date_format($date, $db_format) : '';
}
// Make sure the default value has the same construct as a loaded field value
// to avoid errors if the default value is used on a hidden element.
$item[0]['timezone'] = date_get_timezone($field['settings']['tz_handling']);
$item[0]['timezone_db'] = date_get_timezone_db($field['settings']['tz_handling']);
$item[0]['date_type'] = $field['type'];
if (!isset($item[0]['value2'])) {
$item[0]['value2'] = $item[0]['value'];
}
return $item;
}
/**
* Helper function for the date default value callback to set
* either 'value' or 'value2' to its default value.
*/
function date_default_value_part($item, $field, $instance, $langcode, $part = 'value') {
$timezone = date_get_timezone($field['settings']['tz_handling']);
$timezone_db = date_get_timezone_db($field['settings']['tz_handling']);
$date = NULL;
if ($part == 'value') {
$default_value = $instance['settings']['default_value'];
$default_value_code = $instance['settings']['default_value_code'];
}
else {
$default_value = $instance['settings']['default_value2'];
$default_value_code = $instance['settings']['default_value_code2'];
}
if (empty($default_value) || $default_value == 'blank') {
return NULL;
}
elseif ($default_value == 'strtotime' && !empty($default_value_code)) {
$date = new DrupalDateTime($default_value_code, drupal_get_user_timezone());
}
elseif ($part == 'value2' && $default_value == 'same') {
if ($instance['settings']['default_value'] == 'blank' || empty($item[0]['value'])) {
return NULL;
}
else {
// The date stored in 'value' has already been switched to the db timezone.
$date = new DrupalDateTime($item[0]['value'], $timezone_db, DATE_FORMAT_DATETIME);
}
}
elseif ($field['settings']['tz_handling'] == 'none') {
$date = new DrupalDateTime();
}
else {
$date = new DrupalDateTime('now', $timezone);
}
// The default value needs to be in the database timezone.
date_timezone_set($date, timezone_open($timezone_db));
return $date;
}
/**
* Form validation handler for _date_field_widget_settings_form().
*/
function date_field_widget_settings_form_validate(&$form, &$form_state) {
// The widget settings are in the wrong place in the form because of #tree on
// the top level.
$settings = $form_state['values']['instance']['widget']['settings'];
$settings = array_merge($settings, $settings['advanced']);
unset($settings['advanced']);
form_set_value(array(
'#parents' => array(
'instance',
'widget',
'settings',
),
), $settings, $form_state);
$widget =& $form_state['values']['instance']['widget'];
// Munge the table display for text parts back into an array of text parts.
if (is_array($widget['settings']['text_parts'])) {
form_set_value($form['text_parts'], array_keys(array_filter($widget['settings']['text_parts'])), $form_state);
}
if ($widget['settings']['input_format'] === 'custom' && empty($widget['settings']['input_format_custom'])) {
form_set_error('instance][widget][settings][input_format_custom', t('Please enter a custom date format, or choose one of the preset formats.'));
}
switch ($widget['settings']['date_date_type']) {
case 'date':
$widget['settings']['date_date_format'] = variable_get('date_format_html_date', 'Y-m-d');
break;
}
switch ($widget['settings']['date_time_type']) {
case 'time':
$widget['settings']['date_times_format'] = variable_get('date_format_html_time', 'H:i:s');
break;
}
}
Functions
Name | Description |
---|---|
date_available_values | Helper function to create an array of the date values in a field that need to be processed. |
date_data_keys | Helper function to retrieve the possible keys stored in the data array. |
date_default_value | The callback for setting a default value for an empty date field. |
date_default_value_part | Helper function for the date default value callback to set either 'value' or 'value2' to its default value. |
date_element_info | Implements hook_element_info(). |
date_field_ui_field_edit_form_pre_render | Rearrange form elements into fieldsets for presentation only. |
date_field_widget_date_popup_form_alter | Implements hook_field_widget_WIDGETTYPE_form_alter(). |
date_field_widget_error | Implements hook_field_widget_error(). |
date_field_widget_settings_form_validate | Form validation handler for _date_field_widget_settings_form(). |
date_formatter_format | Retrieve a date format string from formatter settings. |
date_formatter_process | Helper function for creating formatted date arrays from a formatter. |
date_format_type_format | Helper function to get the right format for a format type. Checks for locale-based format first. |
date_form_field_ui_field_edit_form_alter | Implements hook_form_FORM_ID_alter() for field_ui_field_edit_form(). |
date_get_entity_bundle | Helper function to figure out the bundle name for an entity. |
date_granularity | Retrieves the granularity for a field. |
date_input_date | Wrapper function around each of the widget types for creating a date object. |
date_prepare_entity | Helper function to adapt entity date fields to formatter settings. |
date_theme | Implements hook_theme(). |
_timestamp_validate | Custom validation for the timestamp form element. The date element contains an ISO date created from user input, convert it back to a timestamp. |