Drupal: update modules (Data, Redirect)

This commit is contained in:
Robert 2021-03-22 11:00:10 +01:00
parent bf211a43bc
commit 5e2ff763cd
122 changed files with 9695 additions and 3027 deletions

File diff suppressed because it is too large Load Diff

View File

@ -1,223 +1,73 @@
INFORMATION FOR DEVELOPERS
Date
----
The Date module suite provides an API for handling date values, a field for
Drupal 7's Field API system, and a wealth of submodules for various purposes.
Once the Date API is installed, all functions in the API are available to be
used anywhere by any module.
The following modules are included in the Date package, many of which have their
own README.txt files with further information:
The API uses the PHP 5.2 date functions to create and manipulate dates.
* date_api/date_api.module:
A dependency for all of the other modules, provides a slew of APIs for the
other modules to leverage.
Example, the following will create a date for the local value in one
timezone, adjust it to a different timezone, then return the offset in seconds
in the new timezone for the input date; The offset will be adjusted for both
the timezone difference and daylight savings time, if necessary:
* date.module:
Provides the Field API integration necessary to use date fields in the Drupal
7 UI, or in custom modules.
$date = date_create('2007-03-11 02:00:00', timezone_open('America/Chicago'));
$chicago_time = date_format($date, 'Y-m-d H:i');
* date_all_day/date_all_day.module:
Provides the option to add an 'All Day' checkbox to toggle time on and off
for date fields. It also contains the theme that displays the 'All Day' text
on fields that have no time.
print 'At '. $chicago_time .' in Chicago, the timezone offset in seconds
was '. date_offset_get($date);
* date_context/date_context.module
Provides integration with the Context module [1], allowing the date to be used
as a condition on context definitions.
date_timezone_set($date, timezone_open('Europe/Berlin');
$berlin_time = date_format($date, 'Y-m-d H:i');
* date_popup/date_popup.module
Adds a field widget for use with date and time fields that uses a jQuery-based
calendar-style date picker and timepicker.
print 'It was '. $berlin_time .' in Berlin when it
was '. $chicago_time .' in Chicago.';
print 'At that time in Berlin, the timezone offset in seconds was
'. date_offset_get($date);
* date_repeat/date_repeat.module
Provides an API for calculating repeating dates and times from iCal rules.
A helper class is available, new DateObject($string, $timezone, $format), where
$string is a unixtimestamp, an ISO date, or a string like YYYY-MM-DD HH:MM:SS,
$timezone is the name of the timezone this date is in, and $format is the format
of date it is (DATE_FORMAT_UNIX, DATE_FORMAT_ISO, or DATE_FORMAT_DATETIME). It
creates and return a date object set to the right date and timezone.
* date_repeat/date_repeat_field.module
Creates the option of repeating date fields and manages Date fields that use
the Date Repeat API.
Simpletest tests for these functions are included in the package.
* date_tools/date_tools.module
Provides functionality for importing content with dates.
Available functions include the following (more documentation is provided in
the files):
* date_views/date_views.module
Adds integration with the Views module [2] for adding date fields and filters
to views.
============================================================================
Preconfigured arrays
============================================================================
Both translated and untranslated values are available. The
date_week_days_ordered() function will shift an array of week day names so it
starts with the site's first day of the week, otherwise the weekday names start
with Sunday as the first value, which is the expected order for many php and sql
functions.
date_month_names();
date_month_names_abbr();
date_month_names_untranslated();
date_week_days();
date_week_days_abbr();
date_week_days_untranslated();
date_week_days_ordered();
date_years();
date_hours();
date_minutes();
date_seconds();
date_timezone_names();
date_ampm();
Credits / contact
--------------------------------------------------------------------------------
Currently maintained by Andrii Podanenko [3], Alex Schedrov [4], Damien McKenna
[5] and Vijaya Chandran Mani [6]. Previous maintainers included Angie Byron
[7], Arlin Sandbulte [8], David Goode [9], Derek Wright [10], developer-x [11]
and Peter Lieverdink [12].
============================================================================
Miscellaneous date manipulation functions
============================================================================
Pre-defined constants and functions that will handle pre-1970 and post-2038
dates in both PHP 4 and PHP 5, in any OS. Dates can be converted from one
type to another and date parts can be extracted from any date type.
Originally written and maintained by Karen Stevenson [13].
DATE_DATETIME
DATE_ISO
DATE_UNIX
DATE_ARRAY
DATE_OBJECT
DATE_ICAL
The best way to contact the authors is to submit an issue, be it a support
request, a feature request or a bug report, in the project issue queue:
https://www.drupal.org/project/issues/date
date_convert()
date_is_valid();
date_part_is_valid();
date_part_extract();
============================================================================
Date calculation and navigation
============================================================================
date_difference() will find the time difference between any two days, measured
in seconds, minutes, hours, days, months, weeks, or years.
date_days_in_month();
date_days_in_year();
date_weeks_in_year();
date_last_day_of_month();
date_day_of_week();
date_day_of_week_name();
date_difference();
============================================================================
Date regex and format helpers
============================================================================
Pre-defined constants, an array of date format strings and their
equivalent regex strings.
DATE_REGEX_LOOSE is a very loose regex that will pull date parts out
of an ISO date with or without separators, using either 'T' or a space
to separate date and time, and with or without time.
date_format_date() is similar to format_date(), except it takes a
date object instead of a timestamp as the first parameter.
DATE_FORMAT_ISO
DATE_FORMAT_DATETIME
DATE_FORMAT_UNIX
DATE_FORMAT_ICAL
DATE_REGEX_ISO
DATE_REGEX_DATETIME
DATE_REGEX_LOOSE
date_format_date();
date_short_formats();
date_medium_formats();
date_long_formats();
date_format_patterns();
============================================================================
Standardized ical parser and creator
============================================================================
The iCal parser is found in date_api_ical.inc, which is not included by default.
Include that file if you want to use these functions:
Complete rewrite of ical imports to parse vevents, vlocations, valarms,
and all kinds of timezone options and repeat rules for ical imports.
The function now sticks to parsing the ical into an array that can be used
in various ways. It no longer trys to convert timezones while parsing,
instead a date_ical_date_format() function is provided that can be used to
convert from the ical timezone to whatever timezone is desired in the
results. Repeat rules are parsed into an array which other modules can
manipulate however they like to create additional events from the results.
date_ical_export();
date_ical_import();
date_ical_date_format();
============================================================================
Helpers for portable date SQL
============================================================================
The SQL functions are found in date_api_sql.inc, which is not included by
default. Include that file if you want to use these functions:
date_sql();
date_server_zone_adj();
date_sql_concat();
date_sql_pad();
============================================================================
Date forms and validators
============================================================================
Reusable, configurable, self-validating FAPI date elements are found in
date_api_elements.inc, which is not included by default. Include it
if you want to use these elements. To use them, create a form element
and set the '#type' to one of the following:
date_select
The date_select element will create a collection of form elements, with a
separate select or textfield for each date part. The whole collection will
get reformatted back into a date value of the requested type during validation.
date_text
The date_text element will create a textfield that can contain a whole
date or any part of a date as text. The user input value will be re-formatted
back into a date value of the requested type during validation.
date_timezone
The date_timezone element will create a drop-down selector to pick a
timezone name.
The custom date elements require a few other pieces of information to work
correctly, like #date_format and #date_type. See the internal documentation
for more information.
============================================================================
Date Popup Module
============================================================================
A new module is included in the package that will enable a popup jQuery
calendar date picker and timepicker in date and time fields.
It is implemented as a custom form element, so set '#type' to 'date_popup'
to use this element. See the internal documentation for more information.
============================================================================
Date Repeat API
============================================================================
An API for repeating dates is available if installed. It can be used by
other modules to create a form element that will allow users to select
repeat rules and store those selections in an iCal RRULE string, and a
calculation function that will parse the RRULE and return an array of dates
that match those rules. The API is implemented in the Date module as a
new date widget if the Date Repeat API is installed.
============================================================================
RDF Integration
============================================================================
To make RDF easier to use, the base date themes (date_display_single and
date_display_range) have been expanded so they pass attributes and
RDF mappings for the field, if any, to the theme. If RDF is installed
and no other mappings are provided, the theme adds RDF information
to mark both the Start and End dates as 'xsd:dateTime' datatypes with the
property of 'dc:date'. This occurs in the theme preprocess layer, in
particular via the functions template_preprocess_date_display_single() and
template_preprocess_date_display_range().
To mark these as events instead, you could install the schemaorg
module, which will load the schema.org vocabulary. The mark the content type
that contains events as an 'Event', using the UI exposed by that
module and set the event start date field with the 'dateStart'
property and tag other fields in the content type with the appropriate
property types. The Date module theme will wrap the start and end
date output with appropriate markup.
If the result is not quite what you need, you should be able to implement your
own theme preprocess functions, e.g. MYTHEME_preprocess_date_display_single()
or MYTHEME_preprocess_date_display_range() and alter the attributes to use the
values you want.
References
--------------------------------------------------------------------------------
1: https://www.drupal.org/project/context
2: https://www.drupal.org/project/views
3: https://www.drupal.org/u/podarok
4: https://www.drupal.org/u/sanchiz
5: https://www.drupal.org/u/damienmckenna
6: https://www.drupal.org/u/vijaycs85
7: https://www.drupal.org/u/webchick
8: https://www.drupal.org/u/arlinsandbulte
9: https://www.drupal.org/user/291318
10: https://www.drupal.org/u/dww
11: https://www.drupal.org/user/399625
12: https://www.drupal.org/u/cafuego
13: https://www.drupal.org/u/karens

View File

@ -40,7 +40,7 @@ function hook_date_default_argument_alter(&$argument, &$value) {
* - rdf_mapping: The RDF mapping array.
* - add_rdf: If module_exists('rdf').
*/
function hook_date_formatter_pre_view_alter(&$entity, &$variables) {
function hook_date_formatter_pre_view_alter(&$entity, array &$variables) {
if (!empty($entity->view)) {
$field = $variables['field'];
$date_id = 'date_id_' . $field['field_name'];
@ -68,7 +68,7 @@ function hook_date_formatter_pre_view_alter(&$entity, &$variables) {
* - item: The $item array.
* - display: The $display array.
*/
function hook_date_formatter_dates_alter(&$dates, $context) {
function hook_date_formatter_dates_alter(array &$dates, array $context) {
$field = $context['field'];
$instance = $context['instance'];
$format = $context['format'];
@ -90,7 +90,8 @@ function hook_date_formatter_dates_alter(&$dates, $context) {
'date2' => $date2,
'format' => $format,
'entity_type' => $entity_type,
'entity' => $entity));
'entity' => $entity,
));
$all_day2 = theme('date_all_day', array(
'field' => $field,
'instance' => $instance,
@ -99,7 +100,8 @@ function hook_date_formatter_dates_alter(&$dates, $context) {
'date2' => $date2,
'format' => $format,
'entity_type' => $entity_type,
'entity' => $entity));
'entity' => $entity,
));
$dates['value']['formatted_time'] = theme('date_all_day_label');
$dates['value2']['formatted_time'] = theme('date_all_day_label');
$dates['value']['formatted'] = $all_day1;
@ -117,7 +119,7 @@ function hook_date_formatter_dates_alter(&$dates, $context) {
* @param array $input
* The array of input values to be validated.
*/
function hook_date_text_pre_validate_alter(&$element, &$form_state, &$input) {
function hook_date_text_pre_validate_alter(array &$element, array &$form_state, array &$input) {
// Let Date module massage the format for all day values so they will pass
// validation. The All day flag, if used, actually exists on the parent
// element.
@ -134,7 +136,7 @@ function hook_date_text_pre_validate_alter(&$element, &$form_state, &$input) {
* @param array $input
* The array of input values to be validated.
*/
function hook_date_select_pre_validate_alter(&$element, &$form_state, &$input) {
function hook_date_select_pre_validate_alter(array &$element, array &$form_state, array &$input) {
// Let Date module massage the format for all day values so they will pass
// validation. The All day flag, if used, actually exists on the parent
// element.
@ -151,7 +153,7 @@ function hook_date_select_pre_validate_alter(&$element, &$form_state, &$input) {
* @param array $input
* The array of input values to be validated.
*/
function hook_date_popup_pre_validate_alter(&$element, &$form_state, &$input) {
function hook_date_popup_pre_validate_alter(array &$element, array &$form_state, array &$input) {
// Let Date module massage the format for all day values so they will pass
// validation. The All day flag, if used, actually exists on the parent
// element.
@ -173,9 +175,8 @@ function hook_date_popup_pre_validate_alter(&$element, &$form_state, &$input) {
*
* @see date_combo_element_process()
*/
function hook_date_combo_pre_validate_alter(&$element, &$form_state, $context) {
function hook_date_combo_pre_validate_alter(array &$element, array &$form_state, array $context) {
if (!empty($context['item']['all_day'])) {
$field = $context['field'];
// If we have an all day flag on this date and the time is empty, change the
@ -200,12 +201,12 @@ function hook_date_combo_pre_validate_alter(&$element, &$form_state, $context) {
* A keyed array containing the current state of the form.
* @param array $context
* An associative array containing the following keys:
* - field: The $field array.
* - instance: The $instance array.
* - item: The $item array.
* - element: The $element array.
* - field: The $field array.
* - instance: The $instance array.
* - item: The $item array.
* - element: The $element array.
*/
function hook_date_combo_validate_date_start_alter(&$date, &$form_state, $context) {
function hook_date_combo_validate_date_start_alter(object &$date, array &$form_state, array $context) {
// If this is an 'All day' value, set the time to midnight.
if (!empty($context['element']['#date_is_all_day'])) {
$date->setTime(0, 0, 0);
@ -224,12 +225,12 @@ function hook_date_combo_validate_date_start_alter(&$date, &$form_state, $contex
* A keyed array containing the current state of the form.
* @param array $context
* An associative array containing the following keys:
* - field: The $field array.
* - instance: The $instance array.
* - item: The $item array.
* - element: The $element array.
* - field: The $field array.
* - instance: The $instance array.
* - item: The $item array.
* - element: The $element array.
*/
function hook_date_combo_validate_date_end_alter(&$date, &$form_state, $context) {
function hook_date_combo_validate_date_end_alter(object &$date, array &$form_state, array $context) {
// If this is an 'All day' value, set the time to midnight.
if (!empty($context['element']['#date_is_all_day'])) {
$date->setTime(0, 0, 0);
@ -249,7 +250,7 @@ function hook_date_combo_validate_date_end_alter(&$date, &$form_state, $context)
*
* @see date_text_element_process()
*/
function hook_date_text_process_alter(&$element, &$form_state, $context) {
function hook_date_text_process_alter(array &$element, array &$form_state, array $context) {
$all_day_id = !empty($element['#date_all_day_id']) ? $element['#date_all_day_id'] : '';
if ($all_day_id != '') {
// All Day handling on text dates works only if the user leaves the time out
@ -270,7 +271,7 @@ function hook_date_text_process_alter(&$element, &$form_state, $context) {
*
* @see date_select_element_process()
*/
function hook_date_select_process_alter(&$element, &$form_state, $context) {
function hook_date_select_process_alter(array &$element, array &$form_state, array $context) {
// Hide or show the element in reaction to the all_day status for the element.
$all_day_id = !empty($element['#date_all_day_id']) ? $element['#date_all_day_id'] : '';
if ($all_day_id != '') {
@ -299,7 +300,7 @@ function hook_date_select_process_alter(&$element, &$form_state, $context) {
*
* @see date_popup_element_process()
*/
function hook_date_popup_process_alter(&$element, &$form_state, $context) {
function hook_date_popup_process_alter(array &$element, array &$form_state, array $context) {
// Hide or show the element in reaction to the all_day status for the element.
$all_day_id = !empty($element['#date_all_day_id']) ? $element['#date_all_day_id'] : '';
if ($all_day_id != '' && array_key_exists('time', $element)) {
@ -324,7 +325,7 @@ function hook_date_popup_process_alter(&$element, &$form_state, $context) {
* - instance: The $instance array.
* - form: Nested array of form elements that comprise the form.
*/
function hook_date_combo_process_alter(&$element, &$form_state, $context) {
function hook_date_combo_process_alter(array &$element, array &$form_state, array $context) {
$field = $context['field'];
$instance = $context['instance'];
$field_name = $element['#field_name'];
@ -365,7 +366,7 @@ function hook_date_combo_process_alter(&$element, &$form_state, $context) {
*
* @see date_timezone_element_process()
*/
function hook_date_timezone_process_alter(&$element, &$form_state, $context) {
function hook_date_timezone_process_alter(array &$element, array &$form_state, array $context) {
// @todo.
}
@ -382,7 +383,7 @@ function hook_date_timezone_process_alter(&$element, &$form_state, $context) {
*
* @see date_year_range_element_process()
*/
function hook_date_year_range_process_alter(&$element, &$form_state, $context) {
function hook_date_year_range_process_alter(array &$element, array &$form_state, array $context) {
// @todo.
}
@ -399,7 +400,7 @@ function hook_date_year_range_process_alter(&$element, &$form_state, $context) {
*
* @see hook_field_settings_form()
*/
function hook_date_field_settings_form_alter(&$form, $context) {
function hook_date_field_settings_form_alter(array &$form, array $context) {
$field = $context['field'];
$instance = $context['instance'];
$has_data = $context['has_data'];
@ -427,7 +428,7 @@ function hook_date_field_settings_form_alter(&$form, $context) {
*
* @see hook_field_instance_settings_form()
*/
function hook_date_field_instance_settings_form_alter(&$form, $context) {
function hook_date_field_instance_settings_form_alter(array &$form, array $context) {
$field = $context['field'];
$instance = $context['instance'];
$form['new_setting'] = array(
@ -449,7 +450,7 @@ function hook_date_field_instance_settings_form_alter(&$form, $context) {
*
* @see hook_field_widget_settings_form()
*/
function hook_date_field_widget_settings_form_alter(&$form, $context) {
function hook_date_field_widget_settings_form_alter(array &$form, array $context) {
$field = $context['field'];
$instance = $context['instance'];
$form['new_setting'] = array(
@ -474,7 +475,7 @@ function hook_date_field_widget_settings_form_alter(&$form, $context) {
*
* @see hook_field_formatter_settings_form()
*/
function hook_date_field_formatter_settings_form_alter(&$form, &$form_state, $context) {
function hook_date_field_formatter_settings_form_alter(array &$form, array &$form_state, array $context) {
$field = $context['field'];
$instance = $context['instance'];
$view_mode = $context['view_mode'];
@ -486,7 +487,8 @@ function hook_date_field_formatter_settings_form_alter(&$form, &$form_state, $co
'#type' => 'select',
'#options' => array(
'show' => t('Show repeat rule'),
'hide' => t('Hide repeat rule')),
'hide' => t('Hide repeat rule'),
),
'#default_value' => $settings['show_repeat_rule'],
'#access' => $field['settings']['repeat'],
'#weight' => 5,
@ -508,7 +510,7 @@ function hook_date_field_formatter_settings_form_alter(&$form, &$form_state, $co
*
* @see hook_field_formatter_settings_summary()
*/
function hook_date_field_formatter_settings_summary_alter(&$summary, $context) {
function hook_date_field_formatter_settings_summary_alter(array &$summary, array $context) {
$field = $context['field'];
$instance = $context['instance'];
$view_mode = $context['view_mode'];

View File

@ -7,22 +7,15 @@
/**
* Implements hook_devel_generate().
*
* Included only when needed.
*/
function date_devel_generate($entity, $field, $instance, $bundle) {
$entity_field = array();
if (isset($instance['widget']['settings']['year_range'])) {
$split = explode(':', $instance['widget']['settings']['year_range']);
// Determine how much to go back and forward depending on whether a relative
// number of years (with - or + sign) or an absolute year is given.
$back = strpos($split[0], '-') === 0
? str_replace('-', '', $split[0])
: date_format(date_now(), 'Y') - $split[0];
$forward = strpos($split[1], '+') === 0
? str_replace('+', '', $split[1])
: $split[1] - date_format(date_now(), 'Y');
$back = $split[0];
$forward = $split[1];
}
else {
$back = 2;
@ -30,7 +23,8 @@ function date_devel_generate($entity, $field, $instance, $bundle) {
}
// Pick a random year within the time range,
// and a random second within that year.
$year = date_format(date_now(), 'Y') - $back + mt_rand(0, ($forward + $back));
$this_year = date_format(date_now(), 'Y');
$year = mt_rand($this_year + $back, $this_year + $forward);
$start = new DateObject($year . '-01-01 00:00:00', date_get_timezone_db($field['settings']['tz_handling']));
$leap = date_format($start, 'L');
$max_days = $leap ? 366 : 365;
@ -39,17 +33,16 @@ function date_devel_generate($entity, $field, $instance, $bundle) {
$increment = $instance['widget']['settings']['increment'];
date_increment_round($start, $increment);
// Modify End date by 1 hour to 3 days, shorter for repeating dates
// longer for others.
$start2 = clone($start);
// Modify End date by 1 hour to 3 days, shorter for repeating dates longer
// for others.
$start2 = clone $start;
$max = !empty($field['settings']['repeat']) ? 720 : 4320;
$max = 240;
date_modify($start2, '+' . mt_rand(60, $max) . ' minutes');
date_increment_round($start2, $increment);
if ($field['settings']['tz_handling'] == 'date') {
// Choose a random timezone.
// Not all keys exist, so we have to check.
// Choose a random timezone. Not all keys exist, so we have to check.
$timezones = array_keys(date_timezone_names(TRUE));
$key = mt_rand(0, count($timezones) - 1);
if (!array_key_exists($key, $timezones)) {
@ -86,5 +79,4 @@ function date_devel_generate($entity, $field, $instance, $bundle) {
date_timezone_set($start2, timezone_open($timezone));
$entity_field['offset2'] = date_offset_get($start2);
return $entity_field;
}

View File

@ -32,7 +32,6 @@ function date_field_diff_view($items, $context) {
case 'value2':
$diff_items[$delta] = $date[$display['settings']['fromto']]['formatted'];
break;
}
}
return $diff_items;

View File

@ -15,10 +15,11 @@ function date_field_formatter_info() {
'field types' => array('date', 'datestamp', 'datetime'),
'settings' => array(
'format_type' => 'long',
'custom_date_format' => '',
'fromto' => 'both',
'multiple_number' => '',
'multiple_from' => '',
'multiple_to' => '',
'fromto' => 'both',
'show_remaining_days' => FALSE,
),
),
@ -28,7 +29,7 @@ function date_field_formatter_info() {
'settings' => array(
'interval' => 2,
'interval_display' => 'time ago',
'use_end_date' => false,
'use_end_date' => FALSE,
),
),
'date_plain' => array(
@ -53,7 +54,6 @@ function date_field_formatter_settings_form($field, $instance, $view_mode, $form
default:
$form = date_default_formatter_settings_form($field, $instance, $view_mode, $form, $form_state);
break;
}
$context = array(
'field' => $field,
@ -78,7 +78,6 @@ function date_field_formatter_settings_summary($field, $instance, $view_mode) {
default:
$summary = date_default_formatter_settings_summary($field, $instance, $view_mode);
break;
}
$context = array(
'field' => $field,
@ -92,28 +91,24 @@ function date_field_formatter_settings_summary($field, $instance, $view_mode) {
/**
* Implements hook_field_formatter_view().
*
* Useful values:
*
* $entity->date_id
* If set, this will show only an individual date on a field with
* multiple dates. The value should be a string that contains
* the following values, separated with periods:
* - module name of the module adding the item
* - node nid
* - field name
* - delta value of the field to be displayed
* - other information the module's custom theme might need
*
* Used by the calendar module and available for other uses.
* example: 'date:217:field_date:3:test'
*
* $entity->date_repeat_show
* If true, tells the theme to show all the computed values
* of a repeating date. If not true or not set, only the
* start date and the repeat rule will be displayed.
*/
function date_field_formatter_view($entity_type, $entity, $field, $instance, $langcode, $items, $display) {
// Useful values:
// - $entity->date_id
// If set, this will show only an individual date on a field with
// multiple dates. The value should be a string that contains
// the following values, separated with periods:
// - module name of the module adding the item
// - node nid
// - field name
// - delta value of the field to be displayed
// - other information the module's custom theme might need
// Used by the calendar module and available for other uses.
// example: 'date:217:field_date:3:test'
// - $entity->date_repeat_show
// If TRUE, tells the theme to show all the computed values
// of a repeating date. If not TRUE or not set, only the
// start date and the repeat rule will be displayed.
$element = array();
$settings = $display['settings'];
$formatter = $display['type'];
@ -176,8 +171,9 @@ function date_field_formatter_view($entity_type, $entity, $field, $instance, $la
$element[$delta] = array(
'#markup' => t('!start-date to !end-date', array(
'!start-date' => $item['value'],
'!end-date' => $item['value2']
)));
'!end-date' => $item['value2'],
)),
);
}
}
}
@ -215,8 +211,13 @@ function date_field_formatter_view($entity_type, $entity, $field, $instance, $la
}
}
}
break;
}
// Add the CSS stylesheet only if we have something to display.
if (!empty($element)) {
$element['#attached']['css'][] = drupal_get_path('module', 'date_api') . '/date.css';
}
return $element;
}
@ -224,8 +225,7 @@ function date_field_formatter_view($entity_type, $entity, $field, $instance, $la
* Implements hook_field_is_empty().
*/
function date_field_is_empty($item, $field) {
// Sometimes a $item is a date object.
// Coming from repeating dates. Why??
// Sometimes a $item is a date object. Coming from repeating dates. Why??
if (!is_array($item)) {
return FALSE;
}
@ -373,12 +373,13 @@ function date_field_validate($entity_type, $entity, $field, $instance, $langcode
foreach ($items as $delta => $item) {
if (is_array($item) && isset($item['value'])) {
$process = date_process_values($field, $instance);
$date1 = new DateObject($item['value'], $item['timezone'], date_type_format($field['type']));
if (count($process) == 1 || (empty($item['value2']) && $item['value2'] !== 0)) {
$date2 = clone($date1);
$timezone = date_get_timezone($field['settings']['tz_handling'], isset($item['timezone']) ? $item['timezone'] : '');
$date1 = new DateObject($item['value'], $timezone, date_type_format($field['type']));
if (count($process) === 1 || (empty($item['value2']) && $item['value2'] !== 0)) {
$date2 = clone $date1;
}
else {
$date2 = new DateObject($item['value2'], $item['timezone'], date_type_format($field['type']));
$date2 = new DateObject($item['value2'], $timezone, date_type_format($field['type']));
}
$valid1 = $date1->validGranularity($field['settings']['granularity'], $flexible);
$valid2 = $date2->validGranularity($field['settings']['granularity'], $flexible);
@ -411,6 +412,7 @@ function date_field_presave($entity_type, $entity, $field, $instance, $langcode,
if (empty($items)) {
return;
}
// Add some information needed to interpret token values.
$values = $items;
foreach ($values as $delta => $item) {
@ -454,11 +456,10 @@ function date_field_update($entity_type, $entity, $field, $instance, $langcode,
/**
* Implements hook_field_instance_settings_form().
*
* Wrapper functions for date administration, included only when processing
* field settings.
*/
function date_field_instance_settings_form($field, $instance) {
// Wrapper functions for date administration, included only when processing
// field settings.
module_load_include('inc', 'date', 'date_admin');
return _date_field_instance_settings_form($field, $instance);
}
@ -505,15 +506,14 @@ function date_field_settings_validate(&$form, &$form_state) {
/**
* Implements hook_content_migrate_field_alter().
*
* Use this to tweak the conversion of field settings from the D6 style to the
* D7 style for specific situations not handled by basic conversion, as when
* field types or settings are changed.
*
* $field_value['widget_type'] is available to see what widget type was
* originally used.
*/
function date_content_migrate_field_alter(&$field_value, $instance_value) {
// Use this to tweak the conversion of field settings from the D6 style to the
// D7 style for specific situations not handled by basic conversion, as when
// field types or settings are changed.
//
// $field_value['widget_type'] is available to see what widget type was
// originally used.
switch ($field_value['module']) {
case 'date':
// Those settings do not exist anymore, or have been moved to the instance
@ -526,15 +526,13 @@ function date_content_migrate_field_alter(&$field_value, $instance_value) {
/**
* Implements hook_content_migrate_instance_alter().
*
* Use this to tweak the conversion of instance or widget settings from the D6
* style to the D7 style for specific situations not handled by basic
* conversion, as when formatter or widget names or settings are changed.
*/
function date_content_migrate_instance_alter(&$instance_value, $field_value) {
// Use this to tweak the conversion of instance or widget settings from the D6
// style to the D7 style for specific situations not handled by basic
// conversion, as when formatter or widget names or settings are changed.
switch ($instance_value['widget']['module']) {
case 'date':
// Some settings have been moved from field to instance.
$instance_value['widget']['settings']['repeat_collapsed'] = $field_value['settings']['repeat_collapsed'];

View File

@ -1,23 +1,33 @@
name = Date
description = Makes date/time fields available.
dependencies[] = date_api
dependencies[] = date:date_api
package = Date/Time
core = 7.x
php = 5.2
files[] = date.migrate.inc
files[] = tests/date_api.test
files[] = tests/date.test
files[] = tests/date_field.test
files[] = tests/date_migrate.test
files[] = tests/date_validation.test
files[] = tests/date_timezone.test
files[] = tests/date_views_pager.test
files[] = tests/date_views_popup.test
files[] = tests/date_form.test
; Information added by Drupal.org packaging script on 2017-04-07
version = "7.x-2.10"
; Integration with the migrate module.
files[] = date.migrate.inc
; Dependencies for test coverage.
test_dependencies[] = entity:entity
test_dependencies[] = features:features (2.x)
test_dependencies[] = migrate:migrate
test_dependencies[] = views:views
; Test coverage: standalone tests.
files[] = tests/DateEmwTestCase.test
files[] = tests/DateFormTestCase.test
files[] = tests/DateMigrateTestCase.test
files[] = tests/DateNowUnitTestCase.test
; The remaining tests depend upon this one.
files[] = tests/DateFieldTestBase.test
files[] = tests/DateFieldTestCase.test
files[] = tests/DateTimezoneTestCase.test
files[] = tests/DateUiTestCase.test
files[] = tests/DateValidationTestCase.test
; Information added by Drupal.org packaging script on 2021-03-05
version = "7.x-2.11"
core = "7.x"
project = "date"
datestamp = "1491562090"
datestamp = "1614952728"

View File

@ -14,6 +14,7 @@ function date_field_schema($field) {
case 'datestamp':
$db_columns['value'] = array(
'type' => 'int',
'size' => 'big',
'not null' => FALSE,
'sortable' => TRUE,
'views' => TRUE,
@ -41,7 +42,6 @@ function date_field_schema($field) {
'sortable' => TRUE,
'views' => TRUE,
);
break;
}
// If a second date is needed for 'End date', make a copy of the first one.
@ -51,6 +51,16 @@ function date_field_schema($field) {
// We don't want Field API to create additional columns, just the first.
// We modify them our own way in views data.
$db_columns['value2']['views'] = FALSE;
// Create a compound index on value and value2 and an index on value2.
$indexes = array(
'value' => array('value', 'value2'),
'value2' => array('value2'),
);
}
else {
// Otherwise just an index on value.
$indexes = array('value' => array('value'));
}
// Timezone and offset columns are used only if date-specific dates are used.
if (isset($field['settings']['tz_handling']) && $field['settings']['tz_handling'] == 'date') {
@ -72,7 +82,7 @@ function date_field_schema($field) {
'type' => 'int',
'not null' => FALSE,
'sortable' => TRUE,
'views' => FALSE
'views' => FALSE,
);
}
}
@ -84,7 +94,7 @@ function date_field_schema($field) {
'views' => FALSE,
);
}
return array('columns' => $db_columns);
return array('columns' => $db_columns, 'indexes' => $indexes);
}
/**
@ -134,13 +144,14 @@ function date_update_7001() {
$query->condition(db_or()->condition('fc.type', 'date')->condition('fc.type', 'datestamp')->condition('fc.type', 'datetime'));
$results = $query->execute();
$allowed_types = array(
'date_popup_repeat',
'date_text_repeat',
'date_select_repeat',
);
foreach ($results as $record) {
$instance = unserialize($record['data']);
if (in_array($instance['widget']['type'], array(
'date_popup_repeat',
'date_text_repeat',
'date_select_repeat'
))) {
if (in_array($instance['widget']['type'], $allowed_types)) {
$instance['widget']['type'] = str_replace('_repeat', '', $instance['widget']['type']);
db_update('field_config_instance')
->fields(array(
@ -212,3 +223,93 @@ function date_update_7005() {
// @see https://www.drupal.org/node/1355256
date_update_7004();
}
/**
* Add date value indexes to existed field tables.
*/
function date_update_7006() {
// Get all fields provided by the Data module.
$query = db_select('field_config', 'f')
->fields('f', array('id', 'field_name', 'data'))
->condition('f.type', array('datetime', 'date', 'datestamp'));
// Special handling for the Encrypt module.
if (module_exists('field_encrypt')) {
$query->condition('f.data', '%' . db_like(' field_encrypt";a:1:{s:7:"encrypt";i:0 ') . '%', 'LIKE');
}
// Complete the query.
$fields = $query->execute()->fetchAllAssoc('id');
foreach ($fields as $id => $info) {
$field_info = field_info_field($info->field_name);
// Add indexes only for SQL storage fields.
if ($field_info['storage']['type'] != 'field_sql_storage') {
continue;
}
$tables = array(
key($field_info['storage']['details']['sql']['FIELD_LOAD_CURRENT']),
key($field_info['storage']['details']['sql']['FIELD_LOAD_REVISION']),
);
$data = unserialize($info->data);
$field_name = $info->field_name . '_value';
$field_name2 = $info->field_name . '_value2';
// Build indexes.
$indexes = $data['indexes'] = array();
if (!empty($data['settings']['todate'])) {
$indexes[$field_name] = array($field_name, $field_name2);
$indexes[$field_name2] = array($field_name2);
$data['indexes']['value'] = array('value', 'value2');
$data['indexes']['value2'] = array('value2');
}
else {
$indexes[$field_name] = array($field_name);
$data['indexes']['value'] = array('value');
}
// Add missed indexes to tables.
foreach ($indexes as $name => $index) {
foreach ($tables as $table) {
if (!db_index_exists($table, $name)) {
db_add_index($table, $name, $index);
}
}
}
// Fix date fields storage 'field_config' indexes.
db_update('field_config')
->fields(array('data' => serialize($data)))
->condition('id', $id)
->execute();
}
}
/**
* Update datestamp field schema to use 'big' integers.
*/
function date_update_7007() {
$fields = field_read_fields(array('type' => 'datestamp'));
foreach ($fields as $field_name => $field) {
foreach (array_intersect_key($field['columns'], drupal_map_assoc(array('value', 'value2'))) as $column_name => $schema) {
$schema['size'] = 'big';
$column = $field_name . '_' . $column_name;
if (db_table_exists('field_data_' . $field_name)) {
db_change_field('field_data_' . $field_name, $column, $column, $schema);
}
if (db_table_exists('field_revision_' . $field_name)) {
db_change_field('field_revision_' . $field_name, $column, $column, $schema);
}
}
}
}
/**
* The date_migrate_example module was renamed.
*/
function date_update_7200() {
db_delete('system')->condition('name', 'date_migrate_example')->execute();
}

View File

@ -1,4 +1,5 @@
<?php
/**
* @file
* Support for migration into Date fields.
@ -9,16 +10,8 @@ if (!class_exists('MigrateFieldHandler')) {
}
/**
* Implements hook_migrate_api().
* Support for migration into Date fields.
*/
function date_migrate_api() {
$api = array(
'api' => 2,
'field handlers' => array('DateMigrateFieldHandler'),
);
return $api;
}
class DateMigrateFieldHandler extends MigrateFieldHandler {
/**
@ -112,7 +105,7 @@ class DateMigrateFieldHandler extends MigrateFieldHandler {
// Legacy support for JSON containing a set of properties - deprecated
// now that we have subfields.
if (!empty($from) && $from{0} == '{') {
if (!empty($from) && $from[0] == '{') {
$properties = drupal_json_decode($from);
$from = $properties['from'];
// Properties passed in with the date override any set via arguments.
@ -127,9 +120,8 @@ class DateMigrateFieldHandler extends MigrateFieldHandler {
}
}
// Missing data? Create an empty value and return;
// Don't try to turn the empty value into a bogus
// timestamp for 'now'.
// Missing data? Create an empty value and return; Don't try to turn the
// empty value into a bogus timestamp for 'now'.
if (empty($from)) {
$return[$language][$delta]['value'] = NULL;
$return[$language][$delta]['timezone'] = NULL;
@ -171,9 +163,6 @@ class DateMigrateFieldHandler extends MigrateFieldHandler {
$to = format_date($to, 'custom', 'Y-m-d\TH:i:s', $timezone);
}
break;
default:
break;
}
// Handle repeats, coming in as RRULEs. Many field instances may be
@ -212,4 +201,5 @@ class DateMigrateFieldHandler extends MigrateFieldHandler {
'to' => t('End date date'),
);
}
}

View File

@ -20,8 +20,8 @@ function date_get_entity_bundle($entity_type, $entity) {
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)) {
@ -157,7 +157,7 @@ function date_theme() {
/**
* Implements hook_element_info().
*
* date_combo will create a 'start' and optional 'end' date, along with
* 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 or date_text.
*/
@ -170,6 +170,9 @@ function date_element_info() {
'#process' => array('date_combo_element_process'),
'#element_validate' => array('date_combo_validate'),
'#theme_wrappers' => array('date_combo'),
'#attached' => array('css' => array(
drupal_get_path('module', 'date_api') . '/date.css',
)),
);
if (module_exists('ctools')) {
$type['date_combo']['#pre_render'] = array('ctools_dependent_pre_render');
@ -185,7 +188,7 @@ function date_element_info() {
* @param string $formatter
* The date formatter.
* @param string $entity_type
* The entity_type for the instance
* The entity_type for the instance.
* @param object $entity
* The entity object.
* @param array $field
@ -215,7 +218,7 @@ function date_element_info() {
* )
* )
*/
function date_formatter_process($formatter, $entity_type, $entity, $field, $instance, $langcode, $item, $display) {
function date_formatter_process($formatter, $entity_type, $entity, array $field, array $instance, $langcode, array $item, array $display) {
$dates = array();
$timezone = date_default_timezone();
if (empty($timezone)) {
@ -338,12 +341,12 @@ function _get_custom_date_format($date, $format) {
* $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
* simple array of the turned on values.
*
* @param array $field
* The field array.
*/
function date_granularity($field) {
function date_granularity(array $field) {
if (!is_array($field) || !is_array($field['settings']['granularity'])) {
$granularity = drupal_map_assoc(array('year', 'month', 'day'));
$field['settings']['granularity'] = $granularity;
@ -352,9 +355,15 @@ function date_granularity($field) {
}
/**
* Helper function to create an array of the date values in a field that need to be processed.
* Create an array of the date values in a field that need to be processed.
*
* @param array $field
* The field being processed.
*
* @return array
* The date values which need to be processed.
*/
function date_process_values($field) {
function date_process_values(array $field) {
return $field['settings']['todate'] ? array('value', 'value2') : array('value');
}
@ -449,7 +458,6 @@ function date_formatter_format($formatter, $settings, $granularity = array(), $l
else {
$format = date_format_type_format($format_type, $langcode);
}
break;
}
// A selected format might include timezone information.
@ -471,7 +479,7 @@ function date_format_type_format($format_type, $langcode = NULL) {
// 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.
// @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
@ -493,19 +501,17 @@ function date_format_type_format($format_type, $langcode = NULL) {
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
// 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.
// 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);
}
@ -656,7 +662,7 @@ function date_entity_metadata_struct_getter($item, array $options, $name, $type,
return NULL;
}
$timezone_db = !empty($item['timezone_db']) ? $item['timezone_db'] : 'UTC';
$timezone_db = date_get_timezone_db($info['field']['settings']['tz_handling']);
$date = new DateObject($value, $timezone_db);
return !empty($date) ? date_format_date($date, 'custom', 'U') : NULL;
}
@ -714,7 +720,8 @@ function date_entity_metadata_struct_create($name, $property_info) {
*
* Based on entity_property_verbatim_set().
*
* The passed in unix timestamp (UTC) is converted to the right value and format dependent on the field.
* The passed in unix timestamp (UTC) is converted to the right value and format
* dependent on the field.
*
* $name is either 'value' or 'value2'.
*/
@ -736,25 +743,37 @@ function date_entity_metadata_struct_setter(&$item, $name, $value, $langcode, $t
}
/**
* Duplicate functionality of what is now date_all_day_field() in the Date All Day module.
* Duplicate of what is now date_all_day_field() in the Date All Day module.
*
* Copy left here to avoid breaking other modules that use this function.
* Left here to avoid breaking other modules that use this function.
*
* DEPRECATED!, will be removed at some time in the future.
* DEPRECATED! Will be removed at some time in the future.
*/
function date_field_all_day($field, $instance, $date1, $date2 = NULL) {
if (empty($date1) || !is_object($date1)) {
return FALSE;
}
elseif (!date_has_time($field['settings']['granularity'])) {
return TRUE;
return FALSE;
}
// The master setting for the 'all day' functionality.
elseif (empty($instance['settings']['widget']['settings']['display_all_day'])) {
return FALSE;
}
if (empty($date2)) {
$date2 = $date1;
}
// Use central logic for determining the granularity.
$granularity = date_granularity_precision($field['settings']['granularity']);
$increment = isset($instance['widget']['settings']['increment']) ? $instance['widget']['settings']['increment'] : 1;
// The increment is 1 by default, but it can be overridden.
$increment = 1;
if (isset($instance['widget']['settings']['increment'])) {
$increment = $instance['widget']['settings']['increment'];
}
return date_is_all_day(date_format($date1, DATE_FORMAT_DATETIME), date_format($date2, DATE_FORMAT_DATETIME), $granularity, $increment);
}
@ -771,7 +790,7 @@ function date_field_all_day($field, $instance, $date1, $date2 = NULL) {
*
* DEPRECATED!, will be removed at some time in the future.
*/
function date_field_get_sql_handler($field, $compare_tz = NULL) {
function date_field_get_sql_handler(array $field, $compare_tz = NULL) {
module_load_include('inc', 'date_api', 'date_api_sql');
$db_info = date_api_database_info($field);
@ -802,11 +821,11 @@ function date_field_get_sql_handler($field, $compare_tz = NULL) {
/**
* Implements hook_field_widget_properties_alter().
*
* Alters the widget properties of a field instance before it gets displayed.
* Used here to flag new entities so we can later tell if they need default values.
*/
function date_field_widget_properties_alter(&$widget, $context) {
function date_field_widget_properties_alter(array &$widget, $context) {
// Alters the widget properties of a field instance before it gets displayed.
// Used here to flag new entities so we can later tell if they need default
// values.
if (in_array($widget['type'], array('date_select', 'date_text', 'date_popup'))) {
$entity_type = $context['entity_type'];
$entity = $context['entity'];
@ -818,3 +837,14 @@ function date_field_widget_properties_alter(&$widget, $context) {
}
}
}
/**
* Implements hook_migrate_api().
*/
function date_migrate_api() {
$api = array(
'api' => 2,
'field handlers' => array('DateMigrateFieldHandler'),
);
return $api;
}

View File

@ -15,46 +15,46 @@
/**
* Returns HTML for a date element formatted as a Start/End combination.
*
* $entity->date_id
* If set, this will show only an individual date on a field with
* multiple dates. The value should be a string that contains
* the following values, separated with periods:
* - module name of the module adding the item
* - node nid
* - field name
* - delta value of the field to be displayed
* - other information the module's custom theme might need
* $entity->date_id
* If set, this will show only an individual date on a field with
* multiple dates. The value should be a string that contains
* the following values, separated with periods:
* - module name of the module adding the item
* - node nid
* - field name
* - delta value of the field to be displayed
* - other information the module's custom theme might need.
*
* Used by the calendar module and available for other uses.
* example: 'date.217.field_date.3.test'
* Used by the calendar module and available for other uses.
* example: 'date.217.field_date.3.test'
*
* $entity->date_repeat_show
* If true, tells the theme to show all the computed values of a repeating
* date. If not true or not set, only the start date and the repeat rule
* will be displayed.
* $entity->date_repeat_show
* If TRUE, tells the theme to show all the computed values of a repeating
* date. If not TRUE or not set, only the start date and the repeat rule
* will be displayed.
*
* $dates['format']
* The format string used on these dates
* $dates['value']['local']['object']
* The local date object for the Start date
* $dates['value2']['local']['object']
* The local date object for the End date
* $dates['value']['local']['datetime']
* The datetime value of the Start date database (GMT) value
* $dates['value2']['local']['datetime']
* The datetime value of the End date database (GMT) value
* $dates['value']['formatted']
* Formatted Start date, i.e. 'February 15, 2007 2:00 pm';
* $dates['value']['formatted_date']
* Only the date part of the formatted Start date
* $dates['value']['formatted_time']
* Only the time part of the formatted Start date
* $dates['value2']['formatted']
* Formatted End date, i.e. 'February 15, 2007 6:00 pm';
* $dates['value2']['formatted_date']
* Only the date part of the formatted End date
* $dates['value2']['formatted_time']
* Only the time part of the formatted End date
* $dates['format']
* The format string used on these dates
* $dates['value']['local']['object']
* The local date object for the Start date
* $dates['value2']['local']['object']
* The local date object for the End date
* $dates['value']['local']['datetime']
* The datetime value of the Start date database (GMT) value
* $dates['value2']['local']['datetime']
* The datetime value of the End date database (GMT) value
* $dates['value']['formatted']
* Formatted Start date, i.e. 'February 15, 2007 2:00 pm';
* $dates['value']['formatted_date']
* Only the date part of the formatted Start date
* $dates['value']['formatted_time']
* Only the time part of the formatted Start date
* $dates['value2']['formatted']
* Formatted End date, i.e. 'February 15, 2007 6:00 pm';
* $dates['value2']['formatted_date']
* Only the date part of the formatted End date
* $dates['value2']['formatted_time']
* Only the time part of the formatted End date
*/
function theme_date_display_combination($variables) {
static $repeating_ids = array();
@ -77,7 +77,6 @@ function theme_date_display_combination($variables) {
$microdata = $variables['microdata'];
$add_microdata = $variables['add_microdata'];
$precision = date_granularity_precision($field['settings']['granularity']);
$show_remaining_days = $variables['show_remaining_days'];
$output = '';
@ -117,6 +116,26 @@ function theme_date_display_combination($variables) {
$element['#entity'] = $entity;
}
// Verify these variables exist.
if (!isset($dates['value']['formatted'])) {
$dates['value']['formatted'] = '';
}
if (!isset($dates['value2']['formatted'])) {
$dates['value2']['formatted'] = '';
}
if (!isset($dates['value']['formatted_time'])) {
$dates['value']['formatted_time'] = '';
}
if (!isset($dates['value2']['formatted_time'])) {
$dates['value2']['formatted_time'] = '';
}
if (!isset($dates['value1']['formatted_timezone'])) {
$dates['value1']['formatted_timezone'] = '';
}
if (!isset($dates['value2']['formatted_timezone'])) {
$dates['value2']['formatted_timezone'] = '';
}
switch ($options['fromto']) {
case 'value':
$date1 = $dates['value']['formatted'];
@ -156,9 +175,9 @@ function theme_date_display_combination($variables) {
// Check remaining days.
$show_remaining_days = '';
if (!empty($variables['show_remaining_days'])) {
$remaining_days = floor((strtotime($variables['dates']['value']['formatted_iso'])
- strtotime('now')) / (24 * 3600));
if (!empty($variables['show_remaining_days']) && isset($variables['dates']['value']['formatted_iso'])) {
$remaining_days = $variables['dates']['value']['formatted_iso'];
$remaining_days = floor((strtotime($remaining_days) - strtotime('now')) / (24 * 3600));
// Show remaining days only for future events.
if ($remaining_days >= 0) {
@ -172,12 +191,13 @@ function theme_date_display_combination($variables) {
if (empty($date1) && empty($date2)) {
$output .= '';
}
// Start and End dates match or there is no End date, display a complete
// single date.
elseif ($date1 == $date2 || empty($date2)) {
$output .= theme('date_display_single', array(
'date' => $date1,
'timezone' => $timezone,
'timezone' => $has_time_string ? $timezone : '',
'attributes' => $attributes,
'rdf_mapping' => $rdf_mapping,
'add_rdf' => $add_rdf,
@ -187,6 +207,7 @@ function theme_date_display_combination($variables) {
'show_remaining_days' => $show_remaining_days,
));
}
// Same day, different times, don't repeat the date but show both Start and
// End times. We can NOT do this if the replacement value is an integer
// instead of a time string.
@ -216,12 +237,13 @@ function theme_date_display_combination($variables) {
'dates' => $dates,
));
}
// Different days, display both in their entirety.
else {
$output .= theme('date_display_range', array(
'date1' => $date1,
'date2' => $date2,
'timezone' => $timezone,
'timezone' => $has_time_string ? $timezone : '',
'attributes' => $attributes,
'rdf_mapping' => $rdf_mapping,
'add_rdf' => $add_rdf,
@ -251,7 +273,7 @@ function template_preprocess_date_display_single(&$variables) {
$variables['attributes'] = $variables['attributes'] + $base_attributes;
}
// Pass along microdata attributes, or set display to false if none are set.
// Pass along microdata attributes, or set display to FALSE if none are set.
if (!empty($variables['add_microdata'])) {
// Because the Entity API integration for Date has a variable data
// structure depending on whether there is an end value, the attributes
@ -299,7 +321,7 @@ function template_preprocess_date_display_range(&$variables) {
$variables['attributes_end'] += $variables['attributes'];
if ($variables['add_rdf']) {
// Pass along the rdf mapping for this field, if any. Add some default rdf
// Pass along the rdf mapping for this field, if any. Add some default RDF
// attributes that will be used if not overridden by attributes passed in.
$dates = $variables['dates'];
$base_attributes = array(
@ -315,7 +337,7 @@ function template_preprocess_date_display_range(&$variables) {
}
}
// Pass along microdata attributes, or set display to false if none are set.
// Pass along microdata attributes, or set display to FALSE if none are set.
if ($variables['add_microdata']) {
if (!empty($variables['microdata']['value']['#attributes'])) {
$variables['microdata']['value']['#attributes']['content'] = $variables['dates']['value']['formatted_iso'];
@ -336,7 +358,7 @@ function theme_date_display_range($variables) {
$timezone = $variables['timezone'];
$attributes_start = $variables['attributes_start'];
$attributes_end = $variables['attributes_end'];
$show_remaining_days = $variables['show_remaining_days'];
$show_remaining_days = isset($variables['show_remaining_days']) ? $variables['show_remaining_days'] : '';
$start_date = '<span class="date-display-start"' . drupal_attributes($attributes_start) . '>' . $date1 . '</span>';
$end_date = '<span class="date-display-end"' . drupal_attributes($attributes_end) . '>' . $date2 . $timezone . '</span>';
@ -379,7 +401,7 @@ function theme_date_display_interval($variables) {
'interval' => $options['interval'],
'interval_display' => $options['interval_display'],
'use_end_date' => !empty($options['use_end_date']) ?
$options['use_end_date'] : FALSE,
$options['use_end_date'] : FALSE,
);
if ($return = theme('date_time_ago', $time_ago_vars)) {
@ -462,11 +484,19 @@ function theme_date_form_element($variables) {
// Detect if there is more than one subfield.
$count = count(explode('<label', $element['#children'])) - 1;
if ($count == 1) {
$element['#title_display'] = 'none';
// If there is only one subfield, hide the label of the main field only if
// the label of the subfield is visible.
if (empty($variables['element']['value']['#instance'])
|| empty($variables['element']['value']['#instance']['widget'])
|| empty($variables['element']['value']['#instance']['widget']['settings'])
|| empty($variables['element']['value']['#instance']['widget']['settings']['label_position'])
|| $variables['element']['value']['#instance']['widget']['settings']['label_position'] != 'none') {
$element['#title_display'] = 'none';
}
}
// Wrap children with a div and add an extra class if element is multiline.
$element['#children'] = '<div class="date-form-element-content'. ($multiline ? ' date-form-element-content-multiline' : '') .'">'. $element['#children'] .'</div>';
$element['#children'] = '<div class="date-form-element-content' . ($multiline ? ' date-form-element-content-multiline' : '') . '">' . $element['#children'] . '</div>';
return theme('form_element', $variables);
}
@ -477,12 +507,13 @@ function theme_date_form_element($variables) {
function theme_date_display_remaining($variables) {
$remaining_days = $variables['remaining_days'];
$output = '';
$show_remaining_text = t('The upcoming date less then 1 day.');
$show_remaining_text = t('Less than 1 day remaining');
if ($remaining_days) {
$show_remaining_text = format_plural($remaining_days, 'To event remaining 1 day', 'To event remaining @count days');
$show_remaining_text = format_plural($remaining_days, '1 day remaining', '@count days remaining');
}
return '<div class="date-display-remaining"><span class="date-display-remaining">' . $show_remaining_text . '</span></div>';
}
/** @} End of addtogroup themeable */
/**
* @} End of addtogroup themeable */

View File

@ -62,8 +62,8 @@ function date_tokens($type, $tokens, array $data = array(), array $options = arr
date_timezone_set($date, timezone_open($item['timezone']));
}
$timestamp = !empty($date) ? date_format_date($date, 'custom', 'U') : '';
// Generate the token replacements, using the date token type provided
// by system.module.
// Generate the token replacements, using the date token type provided by
// system.module.
$replacements += token_generate('date', $date_tokens, array('date' => $timestamp), $options);
}
}

View File

@ -2,7 +2,7 @@
/**
* @file
* Date administration code.
* Date administration functionality.
*/
/**
@ -196,7 +196,7 @@ function date_default_formatter_settings_summary($field, $instance, $view_mode)
/**
* Settings summary for the interval formatter.
*
* @TODO Add settings later.
* @todo Add settings later.
*/
function date_interval_formatter_settings_summary($field, $instance, $view_mode) {
$summary = array();
@ -205,7 +205,7 @@ function date_interval_formatter_settings_summary($field, $instance, $view_mode)
$formatter = $display['type'];
$field = ($settings['use_end_date'] == 1) ? 'End' : 'Start';
$summary[] = t('Display time ago, showing @interval units. Using @field Date',
array('@interval' => $settings['interval'], '@field' => $field));
array('@interval' => $settings['interval'], '@field' => $field));
return $summary;
}
@ -378,14 +378,16 @@ function _date_field_widget_settings_form($field, $instance) {
'#weight' => 6,
);
$form['increment'] = array(
'#type' => 'select', '#title' => t('Time increments'),
'#type' => 'select',
'#title' => t('Time increments'),
'#default_value' => $settings['increment'],
'#options' => array(
1 => t('1 minute'),
5 => t('5 minute'),
10 => t('10 minute'),
15 => t('15 minute'),
30 => t('30 minute')),
30 => t('30 minute'),
),
'#weight' => 7,
'#fieldset' => 'date_format',
);
@ -440,7 +442,7 @@ function _date_field_widget_settings_form($field, $instance) {
'#description' => $description,
);
$form['advanced']['text_parts'] = array(
'#theme' => $widget['type'] == 'date_select' ? 'date_text_parts' : '',
'#theme' => $widget['type'] == 'date_select' ? 'date_text_parts' : NULL,
);
$text_parts = (array) $settings['text_parts'];
foreach (date_granularity_names() as $key => $value) {

View File

@ -1,81 +1,83 @@
/**
* @file
*/
(function ($) {
Drupal.behaviors.dateAdmin = {};
Drupal.behaviors.dateAdmin = {};
Drupal.behaviors.dateAdmin.attach = function (context, settings) {
// Remove timezone handling options for fields without hours granularity.
var $hour = $('#edit-field-settings-granularity-hour').once('date-admin');
if ($hour.length) {
new Drupal.dateAdmin.TimezoneHandler($hour);
}
};
Drupal.behaviors.dateAdmin.attach = function (context, settings) {
// Remove timezone handling options for fields without hours granularity.
var $hour = $('#edit-field-settings-granularity-hour').once('date-admin');
if ($hour.length) {
new Drupal.dateAdmin.TimezoneHandler($hour);
}
};
Drupal.dateAdmin = {};
Drupal.dateAdmin = {};
/**
/**
* Constructor for the TimezoneHandler object.
*
* This object is responsible for showing the timezone handling options dropdown
* when the user has chosen to collect hours as part of the date field, and
* hiding it otherwise.
*/
Drupal.dateAdmin.TimezoneHandler = function ($checkbox) {
this.$checkbox = $checkbox;
this.$dropdown = $('#edit-field-settings-tz-handling');
this.$timezoneDiv = $('.form-item-field-settings-tz-handling');
// Store the initial value of the timezone handling dropdown.
this.storeTimezoneHandling();
// Toggle the timezone handling section when the user clicks the "Hour"
// checkbox.
this.$checkbox.bind('click', $.proxy(this.clickHandler, this));
// Trigger the click handler so that if the checkbox is unchecked on initial
// page load, the timezone handling section will be hidden.
this.clickHandler();
};
Drupal.dateAdmin.TimezoneHandler = function ($checkbox) {
this.$checkbox = $checkbox;
this.$dropdown = $('#edit-field-settings-tz-handling');
this.$timezoneDiv = $('.form-item-field-settings-tz-handling');
// Store the initial value of the timezone handling dropdown.
this.storeTimezoneHandling();
// Toggle the timezone handling section when the user clicks the "Hour"
// checkbox.
this.$checkbox.bind('click', $.proxy(this.clickHandler, this));
// Trigger the click handler so that if the checkbox is unchecked on initial
// page load, the timezone handling section will be hidden.
this.clickHandler();
};
/**
* Event handler triggered when the user clicks the "Hour" checkbox.
*/
Drupal.dateAdmin.TimezoneHandler.prototype.clickHandler = function () {
if (this.$checkbox.is(':checked')) {
this.restoreTimezoneHandlingOptions();
}
else {
this.hideTimezoneHandlingOptions();
}
};
/**
* Event handler triggered when the user clicks the "Hour" checkbox.
*/
Drupal.dateAdmin.TimezoneHandler.prototype.clickHandler = function () {
if (this.$checkbox.is(':checked')) {
this.restoreTimezoneHandlingOptions();
}
else {
this.hideTimezoneHandlingOptions();
}
};
/**
* Hide the timezone handling options section of the form.
*/
Drupal.dateAdmin.TimezoneHandler.prototype.hideTimezoneHandlingOptions = function () {
this.storeTimezoneHandling();
this.$dropdown.val('none');
this.$timezoneDiv.hide();
};
/**
* Hide the timezone handling options section of the form.
*/
Drupal.dateAdmin.TimezoneHandler.prototype.hideTimezoneHandlingOptions = function () {
this.storeTimezoneHandling();
this.$dropdown.val('none');
this.$timezoneDiv.hide();
};
/**
* Show the timezone handling options section of the form.
*/
Drupal.dateAdmin.TimezoneHandler.prototype.restoreTimezoneHandlingOptions = function () {
var val = this.getTimezoneHandling();
this.$dropdown.val(val);
this.$timezoneDiv.show();
};
/**
* Show the timezone handling options section of the form.
*/
Drupal.dateAdmin.TimezoneHandler.prototype.restoreTimezoneHandlingOptions = function () {
var val = this.getTimezoneHandling();
this.$dropdown.val(val);
this.$timezoneDiv.show();
};
/**
* Store the current value of the timezone handling dropdown.
*/
Drupal.dateAdmin.TimezoneHandler.prototype.storeTimezoneHandling = function () {
this._timezoneHandling = this.$dropdown.val();
};
/**
* Store the current value of the timezone handling dropdown.
*/
Drupal.dateAdmin.TimezoneHandler.prototype.storeTimezoneHandling = function () {
this._timezoneHandling = this.$dropdown.val();
};
/**
* Return the stored value of the timezone handling dropdown.
*/
Drupal.dateAdmin.TimezoneHandler.prototype.getTimezoneHandling = function () {
return this._timezoneHandling;
};
/**
* Return the stored value of the timezone handling dropdown.
*/
Drupal.dateAdmin.TimezoneHandler.prototype.getTimezoneHandling = function () {
return this._timezoneHandling;
};
})(jQuery);

View File

@ -1,9 +1,9 @@
Date All Day
------------
This module provides the option to add an 'All Day' checkbox to toggle time on
and off for date fields. It also contains the theme that displays the 'All Day'
text on fields that have no time.
This module provides the option to add an 'All Day' checkbox to toggle time on
and off for date fields. It also contains the theme that displays the 'All Day'
text on fields that have no time.
Additionally, this module serves as an example of how other modules can inject
new functionality into date fields using various hooks provided by Date and by
the Field API.
Additionally, this module serves as an example of how other modules can inject
new functionality into date fields using various hooks provided by Date and by
the Field API.

View File

@ -1,13 +1,17 @@
name = Date All Day
description = Adds 'All Day' functionality to date fields, including an 'All Day' theme and 'All Day' checkboxes for the Date select and Date popup widgets.
dependencies[] = date_api
dependencies[] = date
package = Date/Time
core = 7.x
; Information added by Drupal.org packaging script on 2017-04-07
version = "7.x-2.10"
dependencies[] = date:date_api
dependencies[] = date:date
; Test coverage.
files[] = tests/DateAllDayUiTestCase.test
files[] = tests/DateAllDayUpdatesTest.test
; Information added by Drupal.org packaging script on 2021-03-05
version = "7.x-2.11"
core = "7.x"
project = "date"
datestamp = "1491562090"
datestamp = "1614952728"

View File

@ -0,0 +1,113 @@
<?php
/**
* @file
* Update scripts for the Date All Day module.
*/
/**
* Change old "all day" values.
*
* Old data was stored with values with the time set to "00:00:00" whereas new
* data is stored with the time set to "23:59:59" (the minutes value varies
* depending upon the field's "increment" value), while keeping the same date.
*
* @todo Run this through a sandbox.
*/
function date_all_day_update_7200() {
// Get all fields provided by the Data module. This is used instead of the
// usual field_read_fields() because we want to select three different field
// types, whereas the API only supports one at a time.
$field_names = db_select('field_config', 'f')
->fields('f', array('field_name'))
->condition('f.type', array('datetime', 'date', 'datestamp'), 'IN')
->execute()
->fetchCol();
// Keep track of the fields that were processed.
$fields_processed = array();
// Loop over each of the fields that was found.
foreach ($field_names as $field_name) {
// Get the full specs of this base field.
$field = field_read_field($field_name);
// Loop over each instance of this field.
$params = array(
'field_name' => $field_name,
);
foreach (field_read_instances($params) as $instance) {
if (!empty($instance['widget']['settings']['display_all_day'])) {
$fields_processed[] = $field_name;
// If this field has a "to" date, the column to change is the "value2"
// column, otherwise just change the primary column.
if (!empty($field['settings']['todate'])) {
$field_to_change = $field_name . '_value2';
}
else {
$field_to_change = $field_name . '_value';
}
// Manually fix each record.
$records = db_select('field_data_' . $field_name, 'f')
->fields('f')
->condition('f.entity_type', $instance['entity_type'])
->condition('f.bundle', $instance['bundle'])
->condition('f.' . $field_to_change, '%:00:00', 'LIKE')
->execute();
// The value that the widget is set to increment by. This is stored in
// minutes.
$increment = 1;
if (isset($instance['widget']['settings']['increment'])) {
$increment = $instance['widget']['settings']['increment'];
}
foreach ($records as $record) {
// Extract the value and convert it to a timestamp.
$end_date_value = $record->{$field_to_change};
$date = strtotime($end_date_value);
// The old time was stored with the value of midnight the day before
// the date that was intended, e.g. if the date was 2020-10-17 the
// time was stored as 0:00:00 thus the saved value "2020-10-17
// 00:00:00". This needs to be converted to 1 second before midnight
// on *that* day, i.e. "2020-10-17 23:59:59".
// @see hook_date_combo_validate_date_end_alter()
// @see date_all_day_date_combo_validate_date_start_alter()
$date += 23 * 60 * 60; // 23 hours.
$date += (60 - $increment) * 60; // 60 minutes - $increment, e.g. 59.
$date += 59; // 59 seconds.
// Reformat the date using an appropriate format for storing in the
// database.
$date = date(DATE_FORMAT_DATETIME, $date);
// Update the record.
db_update('field_data_' . $field_name)
->fields(array(
$field_to_change => $date,
))
->condition('entity_type', $record->entity_type)
->condition('bundle', $record->bundle)
->condition('deleted', $record->deleted)
->condition('revision_id', $record->revision_id)
->condition('language', $record->language)
->condition('delta', $record->delta)
->execute();
// Clear the caches for this entity.
entity_get_controller($record->entity_type)->resetCache(array($record->entity_id));
}
}
}
}
// If any data was converted clear the site's caches.
if ($fields_processed) {
drupal_flush_all_caches();
}
return t('Processed %fields.', array('%fields' => implode(', ', $fields_processed)));
}

View File

@ -4,16 +4,15 @@
* @file
* Adds All Day functionality to the Date field.
*
* This module implements a number of hooks in the Date field and
* Date api element processing and to add an All Day checkbox to
* date widgets, and also adds an All Day theme and
* All Day information to the formatting.
* This module implements a number of hooks in the Date field and Date API
* element processing and to add an All Day checkbox to date widgets, and also
* adds an All Day theme and All Day information to the formatting.
*
* Keep in mind that the process hooks are fired from the top down,
* first date_combo, then the date_select or date_popup.
* Keep in mind that the process hooks are fired from the top down, first
* date_combo, then the date_select or date_popup.
*
* Validation fires from the bottom up, first date_select and
* date_popup, then date_combo.
* Validation fires from the bottom up, first date_select and date_popup, then
* date_combo.
*/
/**
@ -44,12 +43,9 @@ function date_all_day_theme() {
/**
* Implements hook_date_formatter_dates_alter().
*
* This allows us to alter the $dates array created
* by date_formatter_process.
*/
function date_all_day_date_formatter_dates_alter(&$dates, $context) {
// This allows us to alter the $dates array created by date_formatter_process.
$field = $context['field'];
$instance = $context['instance'];
$format = $context['format'];
@ -57,7 +53,6 @@ function date_all_day_date_formatter_dates_alter(&$dates, $context) {
$entity = $context['entity'];
$date1 = $dates['value']['local']['object'];
$date2 = $dates['value2']['local']['object'];
$is_all_day = date_all_day_field($field, $instance, $date1, $date2);
$all_day1 = '';
@ -71,7 +66,8 @@ function date_all_day_date_formatter_dates_alter(&$dates, $context) {
'date2' => $date2,
'format' => $format,
'entity_type' => $entity_type,
'entity' => $entity));
'entity' => $entity,
));
$all_day2 = theme('date_all_day', array(
'field' => $field,
'instance' => $instance,
@ -80,7 +76,8 @@ function date_all_day_date_formatter_dates_alter(&$dates, $context) {
'date2' => $date2,
'format' => $format,
'entity_type' => $entity_type,
'entity' => $entity));
'entity' => $entity,
));
$dates['value']['formatted_time'] = theme('date_all_day_label');
$dates['value2']['formatted_time'] = theme('date_all_day_label');
$dates['value']['formatted'] = $all_day1;
@ -91,39 +88,33 @@ function date_all_day_date_formatter_dates_alter(&$dates, $context) {
/**
* Adjust start/end date format to account for 'all day' .
*
* @params array $field
* @param array $field
* The field definition for this date field.
*
* @params string $which
* @param string $which
* Which value to return, 'date1' or 'date2'.
*
* @params object $date1
* @param object $date1
* A date/time object for the 'start' date.
*
* @params object $date2
* @param object $date2
* A date/time object for the 'end' date.
*
* @params string $format
* A date/time format
*
* @params object $entity
* @param string $format
* A date/time format.
* @param object $entity
* The node this date comes from (may be incomplete, always contains nid).
*
* @params object $view
* @param object $view
* The view this node comes from, if applicable.
*
* @return string
* @return string|null
* Formatted date.
*/
function theme_date_all_day($vars) {
$field = $vars['field'];
$field = $vars['field'];
$instance = $vars['instance'];
$which = $vars['which'];
$date1 = $vars['date1'];
$date2 = $vars['date2'];
$format = $vars['format'];
$entity = $vars['entity'];
$view = !empty($vars['view']) ? $vars['view'] : NULL;
$which = $vars['which'];
$date1 = $vars['date1'];
$date2 = $vars['date2'];
$format = $vars['format'];
$entity = $vars['entity'];
$view = !empty($vars['view']) ? $vars['view'] : NULL;
if (empty($date1) || !is_object($date1) || $format == 'format_interval') {
return;
@ -156,7 +147,7 @@ function theme_date_all_day($vars) {
* Theme the way an 'all day' label will look.
*/
function theme_date_all_day_label() {
return '(' . t('All day', array(), array('context' => 'datetime')) . ')';
return t('(All day)', array(), array('context' => 'datetime'));
}
/**
@ -164,42 +155,61 @@ function theme_date_all_day_label() {
*
* @param array $field
* The field definition for this date field.
*
* @param array $instance
* The field instance for this date field.
*
* @param object $date1
* A date/time object for the 'Start' date.
*
* @param object $date2
* A date/time object for the 'End' date.
*
* @return bool
* TRUE or FALSE.
*/
function date_all_day_field($field, $instance, $date1, $date2 = NULL) {
function date_all_day_field(array $field, array $instance, $date1, $date2 = NULL) {
if (empty($date1) || !is_object($date1)) {
return FALSE;
}
elseif (!date_has_time($field['settings']['granularity'])) {
return TRUE;
return FALSE;
}
// The master setting for the 'all day' functionality.
elseif (empty($instance['widget']['settings']['display_all_day'])) {
return FALSE;
}
if (empty($date2)) {
$date2 = $date1;
}
// Several scenarios result in the two date values being the same, e.g.
// date_formatter_process().
if ($date1 == $date2) {
// Clone the date object so that changes made to the object won't affect
// both variables.
$date1 = clone $date2;
// Set the time to midnight so that the two variables can be properly
// compared later in date_is_all_day().
$date1->setTime(0, 0, 0);
}
// Use central logic for determining the granularity.
$granularity = date_granularity_precision($field['settings']['granularity']);
$increment = isset($instance['widget']['settings']['increment']) ? $instance['widget']['settings']['increment'] : 1;
// The increment is 1 by default, but it can be overridden.
$increment = 1;
if (isset($instance['widget']['settings']['increment'])) {
$increment = $instance['widget']['settings']['increment'];
}
return date_is_all_day(date_format($date1, DATE_FORMAT_DATETIME), date_format($date2, DATE_FORMAT_DATETIME), $granularity, $increment);
}
/**
* Implements hook_date_combo_process_alter().
*
* This hook lets us make changes to the date_combo element.
*/
function date_all_day_date_combo_process_alter(&$element, &$form_state, $context) {
// This hook lets us make changes to the date_combo element.
$field = $context['field'];
$instance = $context['instance'];
@ -211,13 +221,15 @@ function date_all_day_date_combo_process_alter(&$element, &$form_state, $context
$all_day_id = $first_parent . '[' . implode('][', $parents) . '][all_day]';;
foreach (array('value', 'value2') as $key) {
if (array_key_exists($key, $element)) {
$element[$key]['#date_all_day_id'] = $all_day_id;
$element[$key]['#date_all_day_id'] = $all_day_id;
}
}
$from = $element['#default_value']['value'];
$to = !empty($element['#default_value']['value2']) ? $element['#default_value']['value2'] : $element['#default_value']['value'];
$date_is_all_day = date_is_all_day($from, $to);
$granularity = date_granularity_precision($context['field']['settings']['granularity']);
$increment = isset($context['instance']['widget']['settings']['increment']) ? $context['instance']['widget']['settings']['increment'] : 1;
$date_is_all_day = date_is_all_day($from, $to, $granularity, $increment);
$all_day = !empty($form_state['values']['all_day']) || $date_is_all_day;
$element['all_day'] = array(
'#title' => t('All Day'),
@ -236,30 +248,24 @@ function date_all_day_date_combo_process_alter(&$element, &$form_state, $context
}
}
/**
* Implements hook_date_text_process_alter().
*
* This hook lets us make changes to the date_select widget.
*/
function date_all_day_date_text_process_alter(&$element, &$form_state, $context) {
// This hook lets us make changes to the date_select widget.
$all_day_id = !empty($element['#date_all_day_id']) ? $element['#date_all_day_id'] : '';
if ($all_day_id != '') {
// All Day handling on text dates works only
// if the user leaves the time out of the input value.
// There is no element to hide or show.
// All Day handling on text dates works only if the user leaves the time
// out of the input value. There is no element to hide or show.
}
}
/**
* Implements hook_date_select_process_alter().
*
* This hook lets us make changes to the date_select widget.
*/
function date_all_day_date_select_process_alter(&$element, &$form_state, $context) {
// Hide or show this element in reaction
// to the all_day status for this element.
// This hook lets us make changes to the date_select widget. Hide or show
// this element in reaction to the all_day status for this element.
$all_day_id = !empty($element['#date_all_day_id']) ? $element['#date_all_day_id'] : '';
if ($all_day_id != '') {
foreach (array('hour', 'minute', 'second', 'ampm') as $field) {
@ -267,7 +273,8 @@ function date_all_day_date_select_process_alter(&$element, &$form_state, $contex
$element[$field]['#states'] = array(
'visible' => array(
'input[name="' . $all_day_id . '"]' => array('checked' => FALSE),
));
),
);
}
}
}
@ -275,69 +282,60 @@ function date_all_day_date_select_process_alter(&$element, &$form_state, $contex
/**
* Implements hook_date_popup_process_alter().
*
* This hook lets us make changes to the date_popup element.
*/
function date_all_day_date_popup_process_alter(&$element, &$form_state, $context) {
// Hide or show this element in reaction to
// the all_day status for this element.
// This hook lets us make changes to the date_popup element. Hide or show
// this element in reaction to the all_day status for this element.
$all_day_id = !empty($element['#date_all_day_id']) ? $element['#date_all_day_id'] : '';
if ($all_day_id != '' && array_key_exists('time', $element)) {
$element['time']['#states'] = array(
'visible' => array(
'input[name="' . $all_day_id . '"]' => array('checked' => FALSE),
));
),
);
}
}
/**
* Implements hook_date_select_pre_validate_alter().
*
* This hook lets us alter the element or the form_state before the rest
* of the date_select validation gets fired.
*/
function date_all_day_date_text_pre_validate_alter(&$element, &$form_state, &$input) {
// Let Date module massage the format for all day
// values so they will pass validation.
// The All day flag, if used, actually exists on the parent element.
// This hook lets us alter the element or the form_state before the rest of
// the date_select validation gets fired. Let Date module massage the format
// for all day values so they will pass validation. The All day flag, if
// used, actually exists on the parent element.
date_all_day_value($element, $form_state);
}
/**
* Implements hook_date_select_pre_validate_alter().
*
* This hook lets us alter the element or the form_state before the rest
* of the date_select validation gets fired.
*/
function date_all_day_date_select_pre_validate_alter(&$element, &$form_state, &$input) {
// Let Date module massage the format for all
// day values so they will pass validation.
// The All day flag, if used, actually exists on the parent element.
// This hook lets us alter the element or the form_state before the rest of
// the date_select validation gets fired. Let Date module massage the format
// for all day values so they will pass validation. The All day flag, if
// used, actually exists on the parent element.
date_all_day_value($element, $form_state);
}
/**
* Implements hook_date_select_pre_validate_alter().
*
* This hook lets us alter the element or the form_state before the rest
* of the date_popup validation gets fired.
*/
function date_all_day_date_popup_pre_validate_alter(&$element, &$form_state, &$input) {
// Let Date module massage the format for all
// day values so they will pass validation.
// The All day flag, if used, actually exists on the parent element.
// This hook lets us alter the element or the form_state before the rest of
// the date_popup validation gets fired. Let Date module massage the format
// for all day values so they will pass validation. The All day flag, if
// used, actually exists on the parent element.
date_all_day_value($element, $form_state);
}
/**
* A helper function date_all_day_value().
*
* To check if the all day flag is set on the parent of an
* element, and adjust the date_format accordingly so the missing time will
* not cause validation errors.
*/
function date_all_day_value(&$element, $form_state) {
// To check if the all day flag is set on the parent of an element, and
// adjust the date_format accordingly so the missing time will not cause
// validation errors.
if (!empty($element['#date_all_day_id'])) {
$parents = $element['#parents'];
array_pop($parents);
@ -352,14 +350,11 @@ function date_all_day_value(&$element, $form_state) {
/**
* Implements hook_date_combo_pre_validate_alter().
*
* This hook lets us alter the element or the form_state before the rest
* of the date_combo validation gets fired.
*/
function date_all_day_date_combo_pre_validate_alter(&$element, &$form_state, $context) {
// This hook lets us alter the element or the form_state before the rest of
// the date_combo validation gets fired.
if (!empty($context['item']['all_day'])) {
$field = $context['field'];
// If we have an all day flag on this date and the time is empty,
@ -375,13 +370,11 @@ function date_all_day_date_combo_pre_validate_alter(&$element, &$form_state, $co
/**
* Implements hook_date_combo_validate_date_start_alter().
*
* This hook lets us alter the local date objects
* created by the date_combo validation before they are
* converted back to the database timezone and stored.
*/
function date_all_day_date_combo_validate_date_start_alter(&$date, &$form_state, $context) {
// If this is an 'All day' value, set the time to midnight.
// This hook lets us alter the local date objects created by the date_combo
// validation before they are converted back to the database timezone and
// stored. If this is an 'All day' value, set the time to midnight.
if (!empty($context['element']['#date_is_all_day'])) {
$date->setTime(0, 0, 0);
}
@ -389,24 +382,26 @@ function date_all_day_date_combo_validate_date_start_alter(&$date, &$form_state,
/**
* Implements hook_date_combo_validate_date_end_alter().
*
* This hook lets us alter the local date objects
* created by the date_combo validation before
* they are converted back to the database timezone and stored.
*/
function date_all_day_date_combo_validate_date_end_alter(&$date, &$form_state, $context) {
// If this is an 'All day' value, set the time to midnight.
// This hook lets us alter the local date objects created by the date_combo
// validation before they are converted back to the database timezone and
// stored. If this is an 'All day' value, set the time as close to midnight as
// possible.
if (!empty($context['element']['#date_is_all_day'])) {
$date->setTime(0, 0, 0);
$increment = isset($context['instance']['widget']['settings']['increment']) ? $context['instance']['widget']['settings']['increment'] : 1;
// @todo Should this reduce by an additional 1 minute if the increment is
// more than 1? For example, if the increment is 15 should the upper value
// be 23:45:59 or 23:44:59?
$date->setTime(23, 60 - $increment, 59);
}
}
/**
* Implements hook_field_widget_info_alter().
*
* This Field API hook lets us add a new setting to the widgets.
*/
function date_all_day_field_widget_info_alter(&$info) {
// This Field API hook lets us add a new setting to the widgets.
// Add a setting to a widget type.
$info['date_select']['settings'] += array(
'display_all_day' => 0,
@ -423,12 +418,10 @@ function date_all_day_field_widget_info_alter(&$info) {
/**
* Implements hook_date_field_widget_settings_form_alter().
*
* This hook lets us alter the field settings form by adding a place
* to set the value added above.
*/
function date_all_day_date_field_widget_settings_form_alter(&$form, $context) {
// This hook lets us alter the field settings form by adding a place to set
// the value added above.
$field = $context['field'];
$instance = $context['instance'];

View File

@ -0,0 +1,104 @@
<?php
/**
* @file
* Test Date All Day functionality.
*/
/**
* Test Date All Day functionality.
*/
class DateAllDayUiTestCase extends DateFieldTestBase {
/**
*
*/
public static function getInfo() {
return array(
'name' => t('Date All Day unit'),
'description' => t('Test Date All Day functions.') ,
'group' => t('Date'),
);
}
/**
* {@inheritdoc}
*/
public function setUp(array $modules = array()) {
$modules[] = 'date_all_day';
$modules[] = 'date_all_day_test_feature';
parent::setUp($modules);
// Provide a date format that includes the main values.
variable_set('date_format_long', 'D, m/d/Y - H:i');
// Turn off the option to let users change their timezone, so that all date
// values will have a fixed output.
variable_set('configurable_timezones', FALSE);
}
/**
* Test the node form, confirm the "Date all day" field exists & works.
*/
public function testAllDayField() {
// A first node, with the "all day" option disabled.
$this->drupalGet('node/add/date-all-day-test');
$this->assertResponse(200);
// Confirm the 'date all day' field exists.
$this->assertFieldByName('field_date_all_day[und][0][all_day]');
$this->assertFieldByName('field_date_all_day[und][0][value][month]');
$this->assertFieldByName('field_date_all_day[und][0][value][day]');
$this->assertFieldByName('field_date_all_day[und][0][value][year]');
$this->assertFieldByName('field_date_all_day[und][0][value][hour]');
$this->assertFieldByName('field_date_all_day[und][0][value][minute]');
// Submit the node form.
$edit = array(
'title' => 'Testing the All Day option',
'field_date_all_day[und][0][all_day]' => FALSE,
'field_date_all_day[und][0][value][month]' => 2,
'field_date_all_day[und][0][value][day]' => 11,
'field_date_all_day[und][0][value][year]' => 2021,
'field_date_all_day[und][0][value][hour]' => 18,
'field_date_all_day[und][0][value][minute]' => 15,
);
$this->drupalPost(NULL, $edit, 'Save');
$this->assertResponse(200);
// Confirm the data is displayed correctly.
$this->assertText('Testing the All Day option');
$this->assertText('Date All Day');
$this->assertText('Thu, 02/11/2021 - 18:15');
// A second node, this time with the "all day" option enabled.
$this->drupalGet('node/add/date-all-day-test');
$this->assertResponse(200);
// Submit the node form.
$edit = array(
'title' => 'Testing the All Day option again',
'field_date_all_day[und][0][all_day]' => TRUE,
'field_date_all_day[und][0][value][month]' => 2,
'field_date_all_day[und][0][value][day]' => 11,
'field_date_all_day[und][0][value][year]' => 2021,
'field_date_all_day[und][0][value][hour]' => 18,
'field_date_all_day[und][0][value][minute]' => 15,
);
$this->drupalPost(NULL, $edit, 'Save');
$this->assertResponse(200);
// Confirm the data is displayed correctly.
$this->assertText('Testing the All Day option again');
$this->assertText('Date All Day');
$this->assertText('Thu, 02/11/2021 (All day)');
// Load the node and confirm the data is as expected.
$node = node_load(2);
$this->verbose($node);
$this->assertEqual($node->title, $edit['title']);
$this->assertTrue(isset($node->field_date_all_day[LANGUAGE_NONE][0]['value']));
$this->assertEqual($node->field_date_all_day[LANGUAGE_NONE][0]['value'], '2021-02-11 23:45:59');
}
}

View File

@ -0,0 +1,151 @@
<?php
/**
* @file
* Test updates for the Date All Day module.
*/
/**
* Test updates for the Date All Day module.
*/
class DateAllDayUpdatesTest extends DrupalWebTestCase {
/**
* Define this test class.
*/
public static function getInfo() {
return array(
'name' => t('Update updates'),
'description' => t('Confirm update Date All Day updates works as intended.'),
'group' => t('Date'),
);
}
/**
* {@inheritdoc}
*/
public function setUp(array $modules = array()) {
$modules[] = 'date_all_day_test_feature';
parent::setUp($modules);
// Error logging.
variable_set('error_level', 2);
// Log in as user 1, so that permissions are irrelevant.
$this->loginUser1();
// Clear the caches so that the field specs are properly loaded.
cache_clear_all();
}
/**
* Log in as user 1.
*
* The benefit of doing this is that it ignores permissions entirely, so the
* raw functionality can be tested.
*/
protected function loginUser1() {
// Load user 1.
$account = user_load(1, TRUE);
// Reset the password.
$password = user_password();
$edit = array(
'pass' => $password,
);
user_save($account, $edit);
$account->pass_raw = $password;
// Login.
$this->drupalLogin($account);
}
/**
* {@inheritdoc}
*/
protected function verbose($message, $title = NULL) {
// Handle arrays, objects, etc.
if (!is_string($message)) {
$message = "<pre>\n" . print_r($message, TRUE) . "\n</pre>\n";
}
// Optional title to go before the output.
if (!empty($title)) {
$title = '<h2>' . check_plain($title) . "</h2>\n";
}
parent::verbose($title . $message);
}
/**
* Test update 7200.
*/
public function testUpdate7200() {
// Load the install file, so that the update script is available.
module_load_include('install', 'date_all_day');
$this->assertEqual(function_exists('date_all_day_update_7200'), TRUE, 'Update 7200 exists.');
// Create a sample node.
$this->drupalGet('node/add/date-all-day-test');
$this->assertResponse(200);
$this->assertText('Create Date Test');
$edit = array(
'title' => 'Test All Day option',
// This field is required.
// 'field_datetime_range[und][0][value2][date]' => '2020-09-07 08:00:00',
// The All-Day field.
'field_date_all_day[und][0][all_day]' => TRUE,
'field_date_all_day[und][0][value][year]' => 2020,
'field_date_all_day[und][0][value][month]' => 8,
'field_date_all_day[und][0][value][day]' => 30,
);
$this->drupalPost(NULL, $edit, 'Save');
$this->assertResponse(200);
// Make sure the form submitted.
$this->assertNoText('Create Date Test');
// Load the node.
// @todo get the node ID from the URL.
$node = node_load(1, NULL, TRUE);
$this->verbose($node);
// Update the field so it's stored the old way.
$node->field_date_all_day[LANGUAGE_NONE][0]['value'] = '2020-08-30 00:00:00';
node_save($node);
// Reload the node and confirm that it has the old value stored.
$node = node_load($node->nid, NULL, TRUE);
$this->verbose($node);
$this->assertEqual($node->field_date_all_day[LANGUAGE_NONE][0]['value'], '2020-08-30 00:00:00');
// Load the code, confirm the data is invalid.
$this->drupalGet('node/' . $node->nid);
$this->assertResponse(200);
$this->assertText($edit['title']);
$this->assertText('Date All Day');
$this->assertNoText('Sunday, August 30, 2020 (All day)');
// Load the node's edit form to confirm the values are incorrect.
$this->drupalGet('node/' . $node->nid . '/edit');
$this->assertResponse(200);
// Execute the update function.
$results = date_all_day_update_7200();
$this->assertTrue($results);
$this->verbose($results);
// Reload the node and confirm that it has the old value stored.
$node = node_load($node->nid, NULL, TRUE);
$this->verbose($node);
// The expected timestamp is relative to the increment value, which is set
// to 15 minutes for this field.
$this->assertEqual($node->field_date_all_day[LANGUAGE_NONE][0]['value'], '2020-08-30 23:45:59');
// Load the node again, confirm the data is now valid.
$this->drupalGet('node/' . $node->nid);
$this->assertText('Date All Day:');
$this->assertText('Sunday, August 30, 2020 (All day)');
}
}

View File

@ -0,0 +1,70 @@
<?php
/**
* @file
* date_all_day_test_feature.features.field_base.inc
*/
/**
* Implements hook_field_default_field_bases().
*/
function date_all_day_test_feature_field_default_field_bases() {
$field_bases = array();
// Exported field_base: 'body'.
$field_bases['body'] = array(
'active' => 1,
'cardinality' => 1,
'deleted' => 0,
'entity_types' => array(
0 => 'node',
),
'field_name' => 'body',
'indexes' => array(
'format' => array(
0 => 'format',
),
),
'locked' => 0,
'module' => 'text',
'settings' => array(),
'translatable' => 1,
'type' => 'text_with_summary',
);
// Exported field_base: 'field_date_all_day'.
$field_bases['field_date_all_day'] = array(
'active' => 1,
'cardinality' => 1,
'deleted' => 0,
'entity_types' => array(),
'field_name' => 'field_date_all_day',
'indexes' => array(
'value' => array(
0 => 'value',
),
),
'locked' => 0,
'module' => 'date',
'settings' => array(
'cache_count' => 4,
'cache_enabled' => 0,
'granularity' => array(
'day' => 'day',
'hour' => 'hour',
'minute' => 'minute',
'month' => 'month',
'second' => 0,
'year' => 'year',
),
'repeat' => 0,
'timezone_db' => '',
'todate' => '',
'tz_handling' => 'none',
),
'translatable' => 0,
'type' => 'datetime',
);
return $field_bases;
}

View File

@ -0,0 +1,123 @@
<?php
/**
* @file
* date_all_day_test_feature.features.field_instance.inc
*/
/**
* Implements hook_field_default_field_instances().
*/
function date_all_day_test_feature_field_default_field_instances() {
$field_instances = array();
// Exported field_instance: 'node-date_all_day_test-body'.
$field_instances['node-date_all_day_test-body'] = array(
'bundle' => 'date_all_day_test',
'default_value' => NULL,
'deleted' => 0,
'description' => '',
'display' => array(
'default' => array(
'label' => 'hidden',
'module' => 'text',
'settings' => array(),
'type' => 'text_default',
'weight' => 0,
),
'teaser' => array(
'label' => 'hidden',
'module' => 'text',
'settings' => array(
'trim_length' => 600,
),
'type' => 'text_summary_or_trimmed',
'weight' => 0,
),
),
'entity_type' => 'node',
'field_name' => 'body',
'label' => 'Body',
'required' => FALSE,
'settings' => array(
'display_summary' => TRUE,
'text_processing' => 1,
'user_register_form' => FALSE,
),
'widget' => array(
'module' => 'text',
'settings' => array(
'rows' => 20,
'summary_rows' => 5,
),
'type' => 'text_textarea_with_summary',
'weight' => 1,
),
);
// Exported field_instance: 'node-date_all_day_test-field_date_all_day'.
$field_instances['node-date_all_day_test-field_date_all_day'] = array(
'bundle' => 'date_all_day_test',
'deleted' => 0,
'description' => '',
'display' => array(
'default' => array(
'label' => 'above',
'module' => 'date',
'settings' => array(
'custom_date_format' => '',
'format_type' => 'long',
'fromto' => 'both',
'multiple_from' => '',
'multiple_number' => '',
'multiple_to' => '',
'show_remaining_days' => FALSE,
'show_repeat_rule' => 'show',
),
'type' => 'date_default',
'weight' => 8,
),
'teaser' => array(
'label' => 'above',
'settings' => array(),
'type' => 'hidden',
'weight' => 0,
),
),
'entity_type' => 'node',
'field_name' => 'field_date_all_day',
'label' => 'Date All Day',
'required' => FALSE,
'settings' => array(
'default_value' => 'now',
'default_value2' => 'same',
'default_value_code' => '',
'default_value_code2' => '',
'user_register_form' => FALSE,
),
'widget' => array(
'active' => 1,
'module' => 'date',
'settings' => array(
'display_all_day' => 1,
'increment' => 15,
'input_format' => 'm/d/Y - H:i:s',
'input_format_custom' => '',
'label_position' => 'above',
'no_fieldset' => 0,
'repeat_collapsed' => 0,
'text_parts' => array(),
'year_range' => '-3:+3',
),
'type' => 'date_select',
'weight' => 9,
),
);
// Translatables
// Included for use with string extractors like potx.
t('Body');
t('Date All Day');
return $field_instances;
}

View File

@ -0,0 +1,24 @@
<?php
/**
* @file
* date_all_day_test_feature.features.inc
*/
/**
* Implements hook_node_info().
*/
function date_all_day_test_feature_node_info() {
$items = array(
'date_all_day_test' => array(
'name' => t('Date Test'),
'base' => 'node_content',
'description' => t('This content type is used for testing the Date All Day module.'),
'has_title' => '1',
'title_label' => t('Title'),
'help' => '',
),
);
drupal_alter('node_info', $items);
return $items;
}

View File

@ -0,0 +1,21 @@
name = Date All Day Test Feature
description = Example content type for testing the Date All Day module.
core = 7.x
package = Date/Time
version = 7.78-dev
dependencies[] = date:date
dependencies[] = date:date_all_day
dependencies[] = features:features
features[features_api][] = api:2
features[field_base][] = body
features[field_base][] = field_date_all_day
features[field_instance][] = node-date_all_day_test-body
features[field_instance][] = node-date_all_day_test-field_date_all_day
features[node][] = date_all_day_test
hidden = 1
; Information added by Drupal.org packaging script on 2021-03-05
version = "7.x-2.11"
core = "7.x"
project = "date"
datestamp = "1614952728"

View File

@ -0,0 +1,16 @@
<?php
/**
* @file
* Uninstall functions for the Date All Day Test Feature module.
*/
/**
* Implements hook_uninstall().
*/
function date_all_day_test_feature_uninstall() {
node_type_delete('date_test_feature');
variable_del('node_preview_date_test_feature');
node_types_rebuild();
menu_rebuild();
}

View File

@ -0,0 +1,8 @@
<?php
/**
* @file
* Migration integration for Date All Day Test Example.
*/
include_once 'date_all_day_test_feature.features.inc';

View File

@ -0,0 +1,256 @@
Date API
--------
Once the Date API is installed, all functions in the API are available to be
used anywhere by any module.
The API uses the PHP 5.2 date functions to create and manipulate dates.
Example, the following will create a date for the local value in one
timezone, adjust it to a different timezone, then return the offset in seconds
in the new timezone for the input date; The offset will be adjusted for both
the timezone difference and daylight savings time, if necessary:
$date = date_create('2007-03-11 02:00:00', timezone_open('America/Chicago'));
$chicago_time = date_format($date, 'Y-m-d H:i');
print 'At '. $chicago_time .' in Chicago, the timezone offset in seconds
was '. date_offset_get($date);
date_timezone_set($date, timezone_open('Europe/Berlin');
$berlin_time = date_format($date, 'Y-m-d H:i');
print 'It was '. $berlin_time .' in Berlin when it
was '. $chicago_time .' in Chicago.';
print 'At that time in Berlin, the timezone offset in seconds was
'. date_offset_get($date);
A helper class is available, new DateObject($string, $timezone, $format), where
$string is a unixtimestamp, an ISO date, or a string like YYYY-MM-DD HH:MM:SS,
$timezone is the name of the timezone this date is in, and $format is the format
of date it is (DATE_FORMAT_UNIX, DATE_FORMAT_ISO, or DATE_FORMAT_DATETIME). It
creates and return a date object set to the right date and timezone.
Simpletest tests for these functions are included in the package.
Available functions include the following (more documentation is provided in
the files).
Preconfigured arrays
--------------------------------------------------------------------------------
Both translated and untranslated values are available. For example the
'date_week_days_ordered()' function will shift an array of week day names so it
starts with the site's first day of the week, otherwise the weekday names start
with Sunday as the first value, which is the expected order for many php and sql
functions.
date_month_names();
date_month_names_abbr();
date_month_names_untranslated();
date_week_days();
date_week_days_abbr();
date_week_days_untranslated();
date_week_days_ordered();
date_years();
date_hours();
date_minutes();
date_seconds();
date_timezone_names();
date_timezone_abbr();
date_timezone_is_valid();
date_default_timezone();
date_default_timezone_object();
date_ampm();
date_hidden_element();
date_granularity_names();
date_granularity_sorted();
date_granularity_array_from_precision();
date_granularity_precision();
date_granularity_format();
date_now();
Miscellaneous date manipulation functions
--------------------------------------------------------------------------------
Pre-defined constants and functions that will handle pre-1970 and post-2038
dates in both PHP 4 and PHP 5, in any OS. Dates can be converted from one
type to another and date parts can be extracted from any date type.
DATE_DATETIME
DATE_ISO
DATE_UNIX
DATE_ARRAY
DATE_OBJECT
DATE_ICAL
date_is_all_day();
date_increment_round();
date_is_date();
date_pad();
date_has_time();
date_has_date();
date_part_format();
date_limit_format();
date_nongranularity();
date_order_translated();
date_order();
date_range_valid();
date_range_years();
date_range_string();
date_format_type_options();
date_example_date();
date_is_all_day();
date_increment_round();
date_make_iso_valid();
Date calculation and navigation
--------------------------------------------------------------------------------
For example 'date_days_in_month()' identifies the number of days in a month for
a date.
date_days_in_month();
date_days_in_year();
date_iso_weeks_in_year();
date_iso_week_range();
date_weeks_in_year();
date_day_of_week();
date_day_of_week_name();
date_week_range();
date_week();
date_get_timezone();
date_get_timezone_db();
Date regex and format helpers
--------------------------------------------------------------------------------
Pre-defined constants, an array of date format strings and their
equivalent regex strings.
DATE_REGEX_LOOSE is a very loose regex that will pull date parts out
of an ISO date with or without separators, using either 'T' or a space
to separate date and time, and with or without time.
'date_format_date()'' is similar to 'format_date()', except it takes a
date object instead of a timestamp as the first parameter.
DATE_FORMAT_ISO
DATE_FORMAT_DATETIME
DATE_FORMAT_UNIX
DATE_FORMAT_ICAL
DATE_REGEX_ISO
DATE_REGEX_DATETIME
DATE_REGEX_LOOSE
date_format_date();
date_format_patterns();
date_format_interval();
date_format_order();
date_format_type_options();
date_type_format();
Standardized ical parser and creator
--------------------------------------------------------------------------------
The iCal parser is found in 'date_api_ical.inc', which is not included by
default. Include that file if you want to use these functions:
Complete rewrite of ical imports to parse vevents, vlocations, valarms,
and all kinds of timezone options and repeat rules for ical imports.
The function now sticks to parsing the ical into an array that can be used
in various ways. It no longer trys to convert timezones while parsing instead a
'date_ical_date()' function is provided that can be used to convert from the
ical timezone to the local timezone.
iCal properties can be parsed into an array which other modules can manipulate
however they like to create additional events from the results.
The function 'date_api_ical_build_rrule()'' can be used to build an iCal RULE
from $form_values.
date_ical_import();
date_ical_parse();
date_ical_parse_date();
date_ical_parse_rrule();
date_ical_parse_exceptions();
date_ical_parse_duration();
date_ical_parse_text();
date_ical_parse_location();
date_ical_date();
date_ical_escape_text();
date_api_ical_build_rrule();
Helpers for portable date SQL
--------------------------------------------------------------------------------
The SQL functions are found in date_api_sql.inc, which is not included by
default. Include that file if you want to use these functions:
date_sql_concat();
date_sql_coalesce();
date_sql_pad();
Date forms and validators
--------------------------------------------------------------------------------
Reusable, configurable, self-validating FAPI date elements are found in
date_api_elements.inc, which is not included by default. Include it if you want
to use these elements. To use them, create a form element and set the '#type'
to one of the following:
date_select
The date_select element will create a collection of form elements, with a
separate select or textfield for each date part. The whole collection will
get reformatted back into a date value of the requested type during
validation.
date_text
The date_text element will create a textfield that can contain a whole date
or any part of a date as text. The user input value will be re-formatted back
into a date value of the requested type during validation.
date_timezone
The date_timezone element will create a drop-down selector to pick a timezone
name.
The custom date elements require a few other pieces of information to work
correctly, like #date_format and #date_type. See the internal documentation
for more information.
Date Repeat API
--------------------------------------------------------------------------------
An API for repeating dates is available if installed. It can be used by other
modules to create a form element that will allow users to select repeat rules
and store those selections in an iCal RRULE string, and a calculation function
that will parse the RRULE and return an array of dates that match those rules.
The API is implemented in the Date module as a new date widget if the Date
Repeat API is installed.
RDF Integration
--------------------------------------------------------------------------------
To make RDF easier to use, the base date themes ('date_display_single' and
'date_display_range') have been expanded so they pass attributes and RDF
mappings for the field, if any, to the theme. If RDF is installed and no other
mappings are provided, the theme adds RDF information to mark both the Start
and End dates as 'xsd:dateTime' datatypes with the property of 'dc:date'. This
occurs in the theme preprocess layer, in particular via the functions
'template_preprocess_date_display_single()' and
'template_preprocess_date_display_range()'.
To mark these as events instead, you could install the schemaorg module [1],
which will load the schema.org vocabulary. The mark the content type that
contains events as an 'Event', using the UI exposed by that module and set the
event start date field with the 'dateStart' property and tag other fields in the
content type with the appropriate property types. The Date module theme will
wrap the start and end date output with appropriate markup.
If the result is not quite what you need, you should be able to implement your
own theme preprocess functions, e.g. 'MYTHEME_preprocess_date_display_single()'
or 'MYTHEME_preprocess_date_display_range()' and alter the attributes to use the
values you want.
References
--------------------------------------------------------------------------------
1: https://www.drupal.org/project/schemaorg

View File

@ -2,7 +2,8 @@
margin-left: 0.5em;
margin-right: 0;
}
.container-inline-date .form-item .form-item {
.container-inline-date .form-item .form-item,
.date-float {
float: right;
}

View File

@ -3,7 +3,8 @@
* Main stylesheet for Date module.
*/
/* Force start/end dates to float using inline-block, where it works, otherwise inline. */
/* Force start/end dates to float using inline-block, where it works, */
/* otherwise inline. */
.container-inline-date {
clear: both;
}
@ -52,7 +53,7 @@ fieldset.date-combo .container-inline-date > .form-item {
width: auto;
}
/* The exposed Views form doesn't need some of these styles */
/* The exposed Views form doesn't need some of these styles. */
.container-inline-date .date-padding {
float: left;
}
@ -63,7 +64,7 @@ fieldset.date-combo .container-inline-date .date-padding {
padding: 0;
}
/* Fixes for date popup css so it will behave in Drupal */
/* Fixes for date popup css so it will behave in Drupal. */
#calendar_div,
#calendar_div td,
#calendar_div th {
@ -84,7 +85,7 @@ fieldset.date-combo .container-inline-date .date-padding {
padding: 0;
}
/* formatting for start/end dates in nodes and views */
/* Formatting for start/end dates in nodes and views. */
span.date-display-single {
}
span.date-display-start {
@ -114,8 +115,8 @@ span.date-display-end {
width: auto;
}
/* Add space between date option checkboxes ('All day' & 'Collect End Date') */
.date-float .form-type-checkbox{
/* Add space between date option checkboxes ('All day' & 'Collect End Date'). */
.date-float .form-type-checkbox {
padding-right: 1em;
}
@ -142,13 +143,13 @@ span.date-display-end {
float: left;
}
/* Calendar day css */
/* Calendar day CSS. */
div.date-calendar-day {
background: #F3F3F3;
border-top: 1px solid #EEE;
border-left: 1px solid #EEE;
border-right: 1px solid #BBB;
border-bottom: 1px solid #BBB;
background: #f3f3f3;
border-top: 1px solid #eee;
border-left: 1px solid #eee;
border-right: 1px solid #bbb;
border-bottom: 1px solid #bbb;
color: #999;
float: left;
line-height: 1;
@ -162,7 +163,7 @@ div.date-calendar-day span {
text-align: center;
}
div.date-calendar-day span.month {
background-color: #B5BEBE;
background-color: #b5bebe;
color: white;
font-size: .9em;
padding: 2px;
@ -179,9 +180,10 @@ div.date-calendar-day span.year {
.date-form-element-content-multiline {
padding: 10px;
border: 1px solid #CCC;
border: 1px solid #ccc;
}
/* Admin styling */
/* Admin styling. */
.form-item.form-item-instance-widget-settings-input-format-custom,
.form-item.form-item-field-settings-enddate-required {
margin-left: 1.3em;

View File

@ -4,14 +4,15 @@ package = Date/Time
core = 7.x
php = 5.2
stylesheets[all][] = date.css
files[] = date_api.module
files[] = date_api_sql.inc
; Information added by Drupal.org packaging script on 2017-04-07
version = "7.x-2.10"
; Test coverage.
files[] = tests/DateApiTestCase.test
files[] = tests/DateApiUnitTestCase.test
; Information added by Drupal.org packaging script on 2021-03-05
version = "7.x-2.11"
core = "7.x"
project = "date"
datestamp = "1491562090"
datestamp = "1614952728"

View File

@ -128,8 +128,8 @@ function date_api_update_7000() {
// 'date_format_type' table.
if (db_table_exists('date_format_types')) {
// Find all the custom entries in the D6 table.
$result = db_select('date_format_types', 'old')
->fields('old', array('type', 'title', 'locked'))
$result = db_select('date_format_types', 'old_formats')
->fields('old_formats', array('type', 'title', 'locked'))
->condition('locked', 0)
->execute()
->fetchAll(PDO::FETCH_ASSOC);
@ -138,7 +138,7 @@ function date_api_update_7000() {
foreach ($result as $row) {
// See if this value already exists in the new table
// (it might have been added manually before this update got run).
$count = db_select('date_format_type', 'new')
$count = db_select('date_format_type', 'new_formats')
->condition('type', $row['type'])
->countQuery()
->execute()
@ -167,8 +167,8 @@ function date_api_update_7000() {
// 'd6_date_formats') to the new 'date_formats' table.
if (db_table_exists('d6_date_formats')) {
// Find all the custom entries in the D6 table.
$result = db_select('d6_date_formats', 'old')
->fields('old', array('format', 'type', 'locked'))
$result = db_select('d6_date_formats', 'old_formats')
->fields('old_formats', array('format', 'type', 'locked'))
->condition('type', 'custom')
->execute()
->fetchAll(PDO::FETCH_ASSOC);
@ -177,7 +177,7 @@ function date_api_update_7000() {
foreach ($result as $row) {
// See if this value already exists in the new table (it might have been
// added manually before this update got run).
$count = db_select('date_formats', 'new')
$count = db_select('date_formats', 'new_formats')
->condition('format', $row['format'])
->condition('type', $row['type'])
->countQuery()
@ -205,8 +205,8 @@ function date_api_update_7000() {
// to 'd6_date_format_locale') to the new 'date_format_locale' table.
if (db_table_exists('d6_date_format_locale')) {
// Find all the custom entries in the D6 table.
$result = db_select('d6_date_format_locale', 'old')
->fields('old', array('format', 'type', 'language'))
$result = db_select('d6_date_format_locale', 'old_formats')
->fields('old_formats', array('format', 'type', 'language'))
->condition('type', 'custom')
->execute()
->fetchAll(PDO::FETCH_ASSOC);
@ -215,7 +215,7 @@ function date_api_update_7000() {
foreach ($result as $row) {
// See if this value already exists in the new table (it might have been
// added manually before this update got run).
$count = db_select('date_format_locale', 'new')
$count = db_select('date_format_locale', 'new_formats')
->condition('format', $row['format'])
->condition('type', $row['type'])
->condition('language', $row['language'])

View File

@ -3,9 +3,10 @@
/**
* @file
* This module will make the date API available to other modules.
* Designed to provide a light but flexible assortment of functions
* and constants, with more functionality in additional files that
* are not loaded unless other modules specifically include them.
*
* Designed to provide a light but flexible assortment of functions and
* constants, with more functionality in additional files that are not loaded
* unless other modules specifically include them.
*/
/**
@ -14,9 +15,9 @@
* Includes standard date types, format strings, strict regex strings for ISO
* and DATETIME formats (seconds are optional).
*
* The loose regex will find any variety of ISO date and time, with or
* without time, with or without dashes and colons separating the elements,
* and with either a 'T' or a space separating date and time.
* The loose regex will find any variety of ISO date and time, with or without
* time, with or without dashes and colons separating the elements, and with
* either a 'T' or a space separating date and time.
*/
define('DATE_ISO', 'date');
define('DATE_UNIX', 'datestamp');
@ -68,7 +69,6 @@ function date_help($path, $arg) {
$output .= '<h2>More Information</h2><p>' . t('Complete documentation for the Date and Date API modules is available at <a href="@link">http://drupal.org/node/92460</a>.', array('@link' => 'http://drupal.org/node/262062')) . '</p>';
return $output;
}
}
@ -108,16 +108,14 @@ function date_api_status() {
}
return array('errors', $error_messages, 'success' => $success_messages);
}
/**
* Implements hook_menu().
*
* Creates a 'Date API' section on the administration page for Date
* modules to use for their configuration and settings.
*/
function date_api_menu() {
// Creates a 'Date API' section on the administration page for Date modules
// to use for their configuration and settings.
$items['admin/config/date'] = array(
'title' => 'Date API',
'description' => 'Settings for modules the use the Date API.',
@ -132,8 +130,10 @@ function date_api_menu() {
}
/**
* Extend PHP DateTime class with granularity handling, merge functionality and
* slightly more flexible initialization parameters.
* Extend PHP DateTime class.
*
* Adds granularity handling, merge functionality and slightly more flexible
* initialization parameters.
*
* This class is a Drupal independent extension of the >= PHP 5.2 DateTime
* class.
@ -150,7 +150,7 @@ class DateObject extends DateTime {
'hour',
'minute',
'second',
'timezone'
'timezone',
);
private $serializedTime;
private $serializedTimezone;
@ -221,7 +221,7 @@ class DateObject extends DateTime {
// Special handling for Unix timestamps expressed in the local timezone.
// Create a date object in UTC and convert it to the local timezone. Don't
// try to turn things like '2010' with a format of 'Y' into a timestamp.
if (is_numeric($time) && (empty($format) || $format == 'U')) {
if (!is_array($time) && ctype_digit((string)$time) && (empty($format) || $format == 'U')) {
// Assume timestamp.
$time = "@" . $time;
$date = new DateObject($time, 'UTC');
@ -294,7 +294,8 @@ class DateObject extends DateTime {
if (preg_match('/[a-zA-Z]/', $tz->getName())) {
$this->setTimezone($tz);
}
// We have no information about the timezone so must fallback to a default.
// We have no information about the timezone so must fallback to a
// default.
else {
$this->setTimezone(new DateTimeZone("UTC"));
$this->errors['timezone'] = t('No valid timezone name was provided.');
@ -305,13 +306,13 @@ class DateObject extends DateTime {
/**
* Merges two date objects together using the current date values as defaults.
*
* @param object $other
* @param DateObject $other
* Another date object to merge with.
*
* @return object
* @return DateObject
* A merged date object.
*/
public function merge(FeedsDateTime $other) {
public function merge(DateObject $other) {
$other_tz = $other->getTimezone();
$this_tz = $this->getTimezone();
// Figure out which timezone to use for combination.
@ -378,14 +379,14 @@ class DateObject extends DateTime {
* Overrides base format function, formats this date according to its
* available granularity, unless $force'ed not to limit to granularity.
*
* @TODO Add translation into this so translated names will be provided.
* @todo Add translation into this so translated names will be provided.
*
* @param string $format
* A date format string.
* @param bool $force
* Whether or not to limit the granularity. Defaults to FALSE.
*
* @return string|false
* @return string|bool
* Returns the formatted date string on success or FALSE on failure.
*/
public function format($format, $force = FALSE) {
@ -465,15 +466,16 @@ class DateObject extends DateTime {
public function validGranularity($granularity = NULL, $flexible = FALSE) {
$true = $this->hasGranularity() && (!$granularity || $flexible || $this->hasGranularity($granularity));
if (!$true && $granularity) {
$allowed_values = array(
'second',
'minute',
'hour',
'day',
'month',
'year',
);
foreach ((array) $granularity as $part) {
if (!$this->hasGranularity($part) && in_array($part, array(
'second',
'minute',
'hour',
'day',
'month',
'year')
)) {
if (!$this->hasGranularity($part) && in_array($part, $allowed_values)) {
switch ($part) {
case 'second':
$this->errors[$part] = t('The second is missing.');
@ -536,7 +538,7 @@ class DateObject extends DateTime {
* @param array $granularity
* An array of date parts.
*/
public function limitGranularity($granularity) {
public function limitGranularity(array $granularity) {
foreach ($this->granularity as $key => $val) {
if ($val != 'timezone' && !in_array($val, $granularity)) {
unset($this->granularity[$key]);
@ -615,10 +617,16 @@ class DateObject extends DateTime {
$regex1 = str_replace('a', '(.)', $regex1);
preg_match('`^' . $regex1 . '$`', stripslashes($format), $letters);
array_shift($letters);
// Extract values.
$regex2 = preg_replace($patterns, $repl2, $format_regexp, 1);
$regex2 = str_replace('A', '(AM|PM)', $regex2);
$regex2 = str_replace('a', '(am|pm)', $regex2);
$ampm_upper = date_ampm_options(FALSE, TRUE);
$ampm_lower = date_ampm_options(FALSE, FALSE);
$regex2 = strtr(
preg_replace($patterns, $repl2, $format_regexp, 1),
array(
'A' => '(' . preg_quote($ampm_upper['am'], '`') . '|' . preg_quote($ampm_upper['pm'], '`') . ')',
'a' => '(' . preg_quote($ampm_lower['am'], '`') . '|' . preg_quote($ampm_lower['pm'], '`') . ')',
)
);
preg_match('`^' . $regex2 . '$`u', $date, $values);
array_shift($values);
// If we did not find all the values for the patterns in the format, abort.
@ -678,8 +686,11 @@ class DateObject extends DateTime {
break;
case 'a':
$ampm = ($value == $ampm_lower['am'] ? 'am' : 'pm');
break;
case 'A':
$ampm = strtolower($value);
$ampm = ($value == $ampm_upper['am'] ? 'am' : 'pm');
break;
case 'g':
@ -712,11 +723,14 @@ class DateObject extends DateTime {
}
}
if (isset($ampm) && $ampm == 'pm' && $final_date['hour'] < 12) {
$final_date['hour'] += 12;
}
elseif (isset($ampm) && $ampm == 'am' && $final_date['hour'] == 12) {
$final_date['hour'] -= 12;
if (isset($ampm)) {
if ($ampm == 'pm' && $final_date['hour'] < 12) {
$final_date['hour'] += 12;
}
elseif ($ampm == 'am' && $final_date['hour'] == 12) {
$final_date['hour'] -= 12;
}
}
// Blank becomes current time, given TZ.
@ -797,7 +811,7 @@ class DateObject extends DateTime {
* (optional) Whether to force a full date by filling in missing values.
* Defaults to FALSE.
*/
public function toISO($arr, $full = FALSE) {
public function toISO(array $arr, $full = FALSE) {
// Add empty values to avoid errors. The empty values must create a valid
// date or we will get date slippage, i.e. a value of 2011-00-00 will get
// interpreted as November of 2010 by PHP.
@ -933,7 +947,7 @@ class DateObject extends DateTime {
* @return array
* An array of error messages, keyed by date part.
*/
public function arrayErrors($arr) {
public function arrayErrors(array $arr) {
$errors = array();
$now = date_now();
$default_month = !empty($arr['month']) ? $arr['month'] : $now->format('n');
@ -998,8 +1012,8 @@ class DateObject extends DateTime {
public function difference($date2_in, $measure = 'seconds', $absolute = TRUE) {
// Create cloned objects or original dates will be impacted by the
// date_modify() operations done in this code.
$date1 = clone($this);
$date2 = clone($date2_in);
$date1 = clone $this;
$date2 = clone $date2_in;
if (is_object($date1) && is_object($date2)) {
$diff = date_format($date2, 'U') - date_format($date1, 'U');
if ($diff == 0) {
@ -1086,6 +1100,7 @@ class DateObject extends DateTime {
}
return NULL;
}
}
/**
@ -1102,8 +1117,8 @@ class DateObject extends DateTime {
* @return bool
* TRUE if the element is effectively hidden, FALSE otherwise.
*/
function date_hidden_element($element) {
// @TODO What else needs to be tested to see if dates are hidden or disabled?
function date_hidden_element(array $element) {
// @todo What else needs to be tested to see if dates are hidden or disabled?
if ((isset($element['#access']) && empty($element['#access']))
|| !empty($element['#programmed'])
|| in_array($element['#type'], array('hidden', 'value'))) {
@ -1286,7 +1301,6 @@ function date_week_days_abbr($required = FALSE, $refresh = TRUE, $length = 3) {
default:
$context = '';
break;
}
foreach (date_week_days_untranslated() as $key => $day) {
$weekdays[$key] = t(substr($day, 0, $length), array(), array('context' => $context));
@ -1304,7 +1318,7 @@ function date_week_days_abbr($required = FALSE, $refresh = TRUE, $length = 3) {
* @return array
* An array of weekdays reordered to match the first day of the week.
*/
function date_week_days_ordered($weekdays) {
function date_week_days_ordered(array $weekdays) {
$first_day = variable_get('date_first_day', 0);
if ($first_day > 0) {
for ($i = 1; $i <= $first_day; $i++) {
@ -1459,12 +1473,38 @@ function date_seconds($format = 's', $required = FALSE, $increment = 1) {
* An array of AM and PM options.
*/
function date_ampm($required = FALSE) {
$none = array('' => '');
$ampm = array(
'am' => t('am', array(), array('context' => 'ampm')),
'pm' => t('pm', array(), array('context' => 'ampm')),
);
return !$required ? $none + $ampm : $ampm;
$ampm = date_ampm_options(FALSE, FALSE);
return !$required ? array('' => '') + $ampm : $ampm;
}
/**
* Constructs an array of AM and PM options without empty option.
*
* @param bool $key_upper
* If TRUE then the array key will be uppercase.
* @param bool $label_upper
* If TRUE then the array value will be uppercase.
*
* @return array
* An array of AM and PM options.
*/
function date_ampm_options($key_upper, $label_upper) {
$am = $key_upper ? 'AM' : 'am';
$pm = $key_upper ? 'PM' : 'pm';
if ($label_upper) {
return array(
$am => t('AM', array(), array('context' => 'ampm')),
$pm => t('PM', array(), array('context' => 'ampm')),
);
}
else {
return array(
$am => t('am', array(), array('context' => 'ampm')),
$pm => t('pm', array(), array('context' => 'ampm')),
);
}
}
/**
@ -1543,7 +1583,7 @@ function date_granularity_names() {
* @param array $granularity
* An array of date parts.
*/
function date_granularity_sorted($granularity) {
function date_granularity_sorted(array $granularity) {
return array_intersect(array(
'year',
'month',
@ -1596,7 +1636,7 @@ function date_granularity_array_from_precision($precision) {
* @return string
* The most precise element in a granularity array.
*/
function date_granularity_precision($granularity_array) {
function date_granularity_precision(array $granularity_array) {
$input = date_granularity_sorted($granularity_array);
return array_pop($input);
}
@ -1837,7 +1877,6 @@ function date_format_interval($date, $granularity = 2, $display_ago = TRUE) {
* (optional) PHP DateTimeZone object, string or NULL allowed. Optionally
* force time to a specific timezone, defaults to user timezone, if set,
* otherwise site timezone. Defaults to NULL.
*
* @param bool $reset
* (optional) Static cache reset.
*
@ -1845,10 +1884,12 @@ function date_format_interval($date, $granularity = 2, $display_ago = TRUE) {
* The current time as a date object.
*/
function date_now($timezone = NULL, $reset = FALSE) {
$static_var = __FUNCTION__ . $timezone;
if ($timezone instanceof DateTimeZone) {
$static_var = __FUNCTION__ . $timezone->getName();
}
else {
$static_var = __FUNCTION__ . $timezone;
}
if ($reset) {
drupal_static_reset($static_var);
@ -1861,7 +1902,7 @@ function date_now($timezone = NULL, $reset = FALSE) {
}
// Avoid unexpected manipulation of cached $now object
// by subsequent code execution
// by subsequent code execution.
// @see https://drupal.org/node/2261395
$clone = clone $now;
return $clone;
@ -2060,8 +2101,8 @@ function date_week_range($week, $year) {
date_modify($min_date, '-' . strval((7 + $day_wday - $first_day) % 7) . ' days');
// Move forwards to the last day of the week.
$max_date = clone($min_date);
date_modify($max_date, '+6 days');
$max_date = clone $min_date;
date_modify($max_date, '+6 days +23 hours +59 minutes +59 seconds');
if (date_format($min_date, 'Y') != $year) {
$min_date = new DateObject($year . '-01-01 00:00:00');
@ -2097,8 +2138,8 @@ function date_iso_week_range($week, $year) {
}
// Move forwards to the last day of the week.
$max_date = clone($min_date);
date_modify($max_date, '+6 days');
$max_date = clone $min_date;
date_modify($max_date, '+6 days +23 hours +59 minutes +59 seconds');
return array($min_date, $max_date);
}
@ -2148,7 +2189,7 @@ function date_week($date) {
// Remove the leap week if it's present.
if ($date_year > intval($parts[0])) {
$last_date = clone($date);
$last_date = clone $date;
date_modify($last_date, '-7 days');
$week = date_format($last_date, 'W') + 1;
}
@ -2202,7 +2243,7 @@ function date_pad($value, $size = 2) {
* @return bool
* TRUE if the granularity contains a time portion, FALSE otherwise.
*/
function date_has_time($granularity) {
function date_has_time(array $granularity) {
if (!is_array($granularity)) {
$granularity = array();
}
@ -2219,7 +2260,7 @@ function date_has_time($granularity) {
* @return bool
* TRUE if the granularity contains a date portion, FALSE otherwise.
*/
function date_has_date($granularity) {
function date_has_date(array $granularity) {
if (!is_array($granularity)) {
$granularity = array();
}
@ -2266,7 +2307,7 @@ function date_part_format($part, $format) {
* @return string
* The format string with all other elements removed.
*/
function date_limit_format($format, $granularity) {
function date_limit_format($format, array $granularity) {
// Use the advanced drupal_static() pattern to improve performance.
static $drupal_static_fast;
if (!isset($drupal_static_fast)) {
@ -2436,12 +2477,12 @@ function date_format_order($format) {
* Strips out unwanted granularity elements.
*
* @param array $granularity
* An array like ('year', 'month', 'day', 'hour', 'minute', 'second');
* An array like ('year', 'month', 'day', 'hour', 'minute', 'second').
*
* @return array
* A reduced set of granularitiy elements.
*/
function date_nongranularity($granularity) {
function date_nongranularity(array $granularity) {
$options = array(
'year',
'month',
@ -2473,7 +2514,10 @@ function date_api_theme($existing, $type, $theme, $path) {
return array(
'date_nav_title' => $base + array(
'variables' => array(
'granularity' => NULL, 'view' => NULL, 'link' => NULL, 'format' => NULL
'granularity' => NULL,
'view' => NULL,
'link' => NULL,
'format' => NULL,
),
),
'date_timezone' => $base + array('render element' => 'element'),
@ -2497,7 +2541,9 @@ function date_api_theme($existing, $type, $theme, $path) {
'date_calendar_day' => $base + array('variables' => array('date' => NULL)),
'date_time_ago' => $base + array(
'variables' => array(
'start_date' => NULL, 'end_date' => NULL, 'interval' => NULL
'start_date' => NULL,
'end_date' => NULL,
'interval' => NULL,
),
),
);
@ -2562,7 +2608,6 @@ function date_get_timezone_db($handling, $timezone = NULL) {
case ('none'):
default:
$timezone = date_default_timezone();
break;
}
return $timezone;
}
@ -2679,7 +2724,7 @@ function date_range_years($string, $date = NULL) {
* @return string
* A min and max year string like '-3:+1'.
*/
function date_range_string($years) {
function date_range_string(array $years) {
$this_year = date_format(date_now(), 'Y');
if ($years[0] < $this_year) {
@ -2776,21 +2821,32 @@ function date_example_date() {
* @param string $string2
* A string date in datetime format for the 'end' date.
* @param string $granularity
* (optional) The granularity of the date. Defaults to 'second'.
* (optional) The granularity of the date. Allowed values are:
* - 'second' (default)
* - 'minute'
* - 'hour'
* @param int $increment
* (optional) The increment of the date. Defaults to 1.
* (optional) The increment of the date. Only allows positive integers.
* Defaults to 1.
*
* @return bool
* TRUE if the date is all day, FALSE otherwise.
*/
function date_is_all_day($string1, $string2, $granularity = 'second', $increment = 1) {
// Both date strings must be present.
if (empty($string1) || empty($string2)) {
return FALSE;
}
// The granularity argument only allows three options.
elseif (!in_array($granularity, array('hour', 'minute', 'second'))) {
return FALSE;
}
// The increment must be an integer.
elseif (!is_int($increment) || $increment === 0) {
return FALSE;
}
// Verify the first date argument is a valid date string.
preg_match('/([0-9]{4}-[0-9]{2}-[0-9]{2}) (([0-9]{2}):([0-9]{2}):([0-9]{2}))/', $string1, $matches);
$count = count($matches);
$date1 = $count > 1 ? $matches[1] : '';
@ -2798,6 +2854,11 @@ function date_is_all_day($string1, $string2, $granularity = 'second', $increment
$hour1 = $count > 3 ? intval($matches[3]) : 0;
$min1 = $count > 4 ? intval($matches[4]) : 0;
$sec1 = $count > 5 ? intval($matches[5]) : 0;
if (empty($date1) || empty($time1)) {
return FALSE;
}
// Verify the second date argument is a valid date string.
preg_match('/([0-9]{4}-[0-9]{2}-[0-9]{2}) (([0-9]{2}):([0-9]{2}):([0-9]{2}))/', $string2, $matches);
$count = count($matches);
$date2 = $count > 1 ? $matches[1] : '';
@ -2805,10 +2866,7 @@ function date_is_all_day($string1, $string2, $granularity = 'second', $increment
$hour2 = $count > 3 ? intval($matches[3]) : 0;
$min2 = $count > 4 ? intval($matches[4]) : 0;
$sec2 = $count > 5 ? intval($matches[5]) : 0;
if (empty($date1) || empty($date2)) {
return FALSE;
}
if (empty($time1) || empty($time2)) {
if (empty($date2) || empty($time2)) {
return FALSE;
}
@ -2817,31 +2875,25 @@ function date_is_all_day($string1, $string2, $granularity = 'second', $increment
$tmp = date_minutes('i', TRUE, $increment);
$max_minutes = intval(array_pop($tmp));
// See if minutes and seconds are the maximum allowed for an increment or the
// See if minutes and seconds are the maximum allowed for an increment, or the
// maximum possible (59), or 0.
switch ($granularity) {
case 'second':
$min_match = $time1 == '00:00:00'
|| ($hour1 == 0 && $min1 == 0 && $sec1 == 0);
$max_match = $time2 == '00:00:00'
|| ($hour2 == 23 && in_array($min2, array($max_minutes, 59)) && in_array($sec2, array($max_seconds, 59)))
|| ($hour1 == 0 && $hour2 == 0 && $min1 == 0 && $min2 == 0 && $sec1 == 0 && $sec2 == 0);
$min_match = ($hour1 == 0 && $min1 == 0 && $sec1 == 0);
$max_match = ($hour2 == 23 && in_array($min2, array($max_minutes, 59)) && in_array($sec2, array($max_seconds, 59)))
|| ($date1 != $date2 && $hour1 == 0 && $hour2 == 0 && $min1 == 0 && $min2 == 0 && $sec1 == 0 && $sec2 == 0);
break;
case 'minute':
$min_match = $time1 == '00:00:00'
|| ($hour1 == 0 && $min1 == 0);
$max_match = $time2 == '00:00:00'
|| ($hour2 == 23 && in_array($min2, array($max_minutes, 59)))
|| ($hour1 == 0 && $hour2 == 0 && $min1 == 0 && $min2 == 0);
$min_match = ($hour1 == 0 && $min1 == 0);
$max_match = ($hour2 == 23 && in_array($min2, array($max_minutes, 59)))
|| ($date1 != $date2 && $hour1 == 0 && $hour2 == 0 && $min1 == 0 && $min2 == 0);
break;
case 'hour':
$min_match = $time1 == '00:00:00'
|| ($hour1 == 0);
$max_match = $time2 == '00:00:00'
|| ($hour2 == 23)
|| ($hour1 == 0 && $hour2 == 0);
$min_match = ($hour1 == 0);
$max_match = ($hour2 == 23)
|| ($date1 != $date2 && $hour1 == 0 && $hour2 == 0);
break;
default:
@ -2907,9 +2959,10 @@ function date_is_date($date) {
* Replace specific ISO values using patterns.
*
* Function will replace ISO values that have the pattern 9999-00-00T00:00:00
* with a pattern like 9999-01-01T00:00:00, to match the behavior of non-ISO dates
* and ensure that date objects created from this value contain a valid month
* and day.
* with a pattern like 9999-01-01T00:00:00, to match the behavior of non-ISO
* dates and ensure that date objects created from this value contain a valid
* month and day.
*
* Without this fix, the ISO date '2020-00-00T00:00:00' would be created as
* November 30, 2019 (the previous day in the previous month).
*
@ -2919,7 +2972,7 @@ function date_is_date($date) {
* @return mixed|string
* replaced value, or incoming value.
*
* @TODO Expand on this to work with all sorts of partial ISO dates.
* @todo Expand on this to work with all sorts of partial ISO dates.
*/
function date_make_iso_valid($iso_string) {
// If this isn't a value that uses an ISO pattern, there is nothing to do.

View File

@ -3,11 +3,12 @@
/**
* @file
* Date API elements themes and validation.
*
* This file is only included during the edit process to reduce memory usage.
*/
/**
* Implements hook_element_info().
* Wrapper for hook_element_info().
*
* Parameters for date form elements, designed to have sane defaults so any
* or all can be omitted.
@ -65,7 +66,8 @@
*/
function _date_api_element_info() {
$date_base = array(
'#input' => TRUE, '#tree' => TRUE,
'#input' => TRUE,
'#tree' => TRUE,
'#date_timezone' => date_default_timezone(),
'#date_flexible' => 0,
'#date_format' => variable_get('date_format_short', 'm/d/Y - H:i'),
@ -88,7 +90,8 @@ function _date_api_element_info() {
'#value_callback' => 'date_text_element_value_callback',
));
$type['date_timezone'] = array(
'#input' => TRUE, '#tree' => TRUE,
'#input' => TRUE,
'#tree' => TRUE,
'#process' => array('date_timezone_element_process'),
'#theme_wrappers' => array('date_text'),
'#value_callback' => 'date_timezone_element_value_callback',
@ -105,7 +108,7 @@ function _date_api_element_info() {
/**
* Create a date object from a datetime string value.
*/
function date_default_date($element) {
function date_default_date(array $element) {
$granularity = date_format_order($element['#date_format']);
$default_value = $element['#default_value'];
$format = DATE_FORMAT_DATETIME;
@ -270,7 +273,7 @@ function date_timezone_element_process($element, &$form_state, $form) {
/**
* Validation for timezone input.
*
* Move the timezone value from the nested field back to the original field.
* Move the timezone value from the nested field back to the original field.
*/
function date_timezone_validate($element, &$form_state) {
if (date_hidden_element($element)) {
@ -287,10 +290,10 @@ function date_text_element_value_callback($element, $input = FALSE, &$form_state
$return = array('date' => '');
$date = NULL;
// Normal input from submitting the form element.
// Check is_array() to skip the string input values created by Views pagers.
// Those string values, if present, should be interpreted as empty input.
if ($input != FALSE && is_array($input)) {
// Normal input from submitting the form element. Check is_array() to skip
// the string input values created by Views pagers. Those string values, if
// present, should be interpreted as empty input.
if ($input !== FALSE && is_array($input) && !is_null($input['date'])) {
$return = $input;
$date = date_text_input_date($element, $input);
}
@ -309,8 +312,8 @@ function date_text_element_value_callback($element, $input = FALSE, &$form_state
*
* Display all or part of a date in a single textfield.
*
* The exact parts displayed in the field are those in #date_granularity.
* The display of each part comes from #date_format.
* The exact parts displayed in the field are those in #date_granularity. The
* display of each part comes from #date_format.
*/
function date_text_element_process($element, &$form_state, $form) {
if (date_hidden_element($element)) {
@ -326,9 +329,10 @@ function date_text_element_process($element, &$form_state, $form) {
$now = date_example_date();
$element['date']['#title'] = t('Date');
$element['date']['#title_display'] = 'invisible';
$element['date']['#description'] = ' ' . t('Format: @date', array(
'@date' => date_format_date(date_example_date(), 'custom', $element['#date_format']
)));
$date_args = array(
'@date' => date_format_date(date_example_date(), 'custom', $element['#date_format']),
);
$element['date']['#description'] = ' ' . t('Format: @date', $date_args);
$element['date']['#ajax'] = !empty($element['#ajax']) ? $element['#ajax'] : FALSE;
// Make changes if instance is set to be rendered as a regular field.
@ -338,9 +342,8 @@ function date_text_element_process($element, &$form_state, $form) {
$element['date']['#required'] = $element['#required'];
}
// Keep the system from creating an error message for the sub-element.
// We'll set our own message on the parent element.
// $element['date']['#required'] = $element['#required'];
// Keep the system from creating an error message for the sub-element. We'll
// set our own message on the parent element.
$element['date']['#theme'] = 'date_textfield_element';
if (isset($element['#element_validate'])) {
array_push($element['#element_validate'], 'date_text_validate');
@ -363,9 +366,9 @@ function date_text_element_process($element, &$form_state, $form) {
/**
* Validation for text input.
*
* When used as a Views widget, the validation step always gets triggered,
* even with no form submission. Before form submission $element['#value']
* contains a string, after submission it contains an array.
* When used as a Views widget, the validation step always gets triggered, even
* with no form submission. Before form submission $element['#value'] contains a
* string, after submission it contains an array.
*/
function date_text_validate($element, &$form_state) {
if (date_hidden_element($element)) {
@ -388,9 +391,9 @@ function date_text_validate($element, &$form_state) {
$label = !empty($element['#date_title']) ? $element['#date_title'] : (!empty($element['#title']) ? $element['#title'] : '');
$date = date_text_input_date($element, $input);
// If the field has errors, display them.
// If something was input but there is no date, the date is invalid.
// If the field is empty and required, set error message and return.
// If the field has errors, display them. If something was input but there is
// no date, the date is invalid. If the field is empty and required, set
// error message and return.
$error_field = implode('][', $element['#parents']);
if (empty($date) || !empty($date->errors)) {
if (is_object($date) && !empty($date->errors)) {
@ -464,7 +467,12 @@ function date_select_element_value_callback($element, $input = FALSE, &$form_sta
);
foreach ($granularity as $field) {
if ($field != 'timezone') {
$return[$field] = date_is_date($date) ? $date->format($formats[$field]) : '';
if (date_is_date($date)) {
$return[$field] = $date->format($formats[$field]);
}
else {
$return = array();
}
}
}
return $return;
@ -488,7 +496,7 @@ function date_select_element_process($element, &$form_state, $form) {
$date = NULL;
$granularity = date_format_order($element['#date_format']);
if (is_array($element['#default_value'])) {
if (array_key_exists('#default_value', $element) && is_array($element['#default_value'])) {
$date = date_select_input_date($element, $element['#default_value']);
}
elseif (!empty($element['#default_value'])) {
@ -536,11 +544,10 @@ function date_select_element_process($element, &$form_state, $form) {
/**
* Creates form elements for one or more date parts.
*
* Get the order of date elements from the provided format.
* If the format order omits any date parts in the granularity, alter the
* granularity array to match the format, then flip the $order array
* to get the position for each element. Then iterate through the
* elements and create a sub-form for each part.
* Get the order of date elements from the provided format. If the format order
* omits any date parts in the granularity, alter the granularity array to match
* the format, then flip the $order array to get the position for each element.
* Then iterate through the elements and create a sub-form for each part.
*
* @param array $element
* The date element.
@ -552,13 +559,13 @@ function date_select_element_process($element, &$form_state, $form) {
* @return array
* The form array for the submitted date parts.
*/
function date_parts_element($element, $date, $format) {
function date_parts_element(array $element, $date, $format) {
$granularity = date_format_order($format);
$sub_element = array('#granularity' => $granularity);
$order = array_flip($granularity);
$hours_format = strpos(strtolower($element['#date_format']), 'a') ? 'g' : 'G';
$month_function = strpos($element['#date_format'], 'F') !== FALSE ? 'date_month_names' : 'date_month_names_abbr';
$hours_format = strpos(strtolower($element['#date_format']), 'a') ? 'g' : 'G';
$month_function = strpos($element['#date_format'], 'F') !== FALSE ? 'date_month_names' : 'date_month_names_abbr';
$count = 0;
$increment = min(intval($element['#date_increment']), 1);
@ -634,7 +641,8 @@ function date_parts_element($element, $date, $format) {
if ($element['#date_label_position'] == 'within') {
if (!empty($sub_element[$field]['#options']) && is_array($sub_element[$field]['#options'])) {
$sub_element[$field]['#options'] = array(
'-' . $label => '-' . $label) + $sub_element[$field]['#options'];
'-' . $label => '-' . $label,
) + $sub_element[$field]['#options'];
}
if (empty($sub_element[$field]['#default_value'])) {
$sub_element[$field]['#default_value'] = '-' . $label;
@ -648,14 +656,15 @@ function date_parts_element($element, $date, $format) {
$sub_element[$field]['#title_display'] = in_array($element['#date_label_position'], array('within', 'none')) ? 'invisible' : 'before';
if ($element['#date_label_position'] == 'within') {
$sub_element[$field]['#options'] = array(
'' => '-' . $label) + $sub_element[$field]['#options'];
'' => '-' . $label,
) + $sub_element[$field]['#options'];
}
}
}
// Views exposed filters are treated as submitted even if not,
// so force the #default value in that case. Make sure we set
// a default that is in the option list.
// Views exposed filters are treated as submitted even if not, so force the
// #default value in that case. Make sure we set a default that is in the
// option list.
if (!empty($element['#force_value'])) {
$options = $sub_element[$field]['#options'];
$default = !empty($sub_element[$field]['#default_value']) ? $sub_element[$field]['#default_value'] : array_shift($options);
@ -686,9 +695,9 @@ function date_parts_element($element, $date, $format) {
/**
* Validation function for date selector.
*
* When used as a Views widget, the validation step always gets triggered,
* even with no form submission. Before form submission $element['#value']
* contains a string, after submission it contains an array.
* When used as a Views widget, the validation step always gets triggered, even
* with no form submission. Before form submission $element['#value'] contains a
* string, after submission it contains an array.
*/
function date_select_validate($element, &$form_state) {
if (date_hidden_element($element)) {
@ -755,7 +764,6 @@ function date_select_validate($element, &$form_state) {
* Helper function for creating a date object out of user input.
*/
function date_select_input_date($element, $input) {
// Was anything entered? If not, we have no date.
if (!is_array($input)) {
return NULL;
@ -790,5 +798,6 @@ function date_select_input_date($element, $input) {
}
return $date;
}
return NULL;
}

View File

@ -10,75 +10,75 @@
/**
* Return an array of iCalendar information from an iCalendar file.
*
* No timezone adjustment is performed in the import since the timezone
* conversion needed will vary depending on whether the value is being
* imported into the database (when it needs to be converted to UTC), is being
* viewed on a site that has user-configurable timezones (when it needs to be
* converted to the user's timezone), if it needs to be converted to the
* site timezone, or if it is a date without a timezone which should not have
* any timezone conversion applied.
* No timezone adjustment is performed in the import since the timezone
* conversion needed will vary depending on whether the value is being
* imported into the database (when it needs to be converted to UTC), is being
* viewed on a site that has user-configurable timezones (when it needs to be
* converted to the user's timezone), if it needs to be converted to the site
* timezone, or if it is a date without a timezone which should not have any
* timezone conversion applied.
*
* Properties that have dates and times are converted to sub-arrays like:
* 'datetime' => date in YYYY-MM-DD HH:MM format, not timezone adjusted
* 'all_day' => whether this is an all-day event
* 'tz' => the timezone of the date, could be blank for absolute
* times that should get no timezone conversion.
* Properties that have dates and times are converted to sub-arrays like:
* 'datetime' => Date in YYYY-MM-DD HH:MM format, not timezone adjusted.
* 'all_day' => Whether this is an all-day event.
* 'tz' => The timezone of the date, could be blank for absolute times
* that should get no timezone conversion.
*
* Exception dates can have muliple values and are returned as arrays
* like the above for each exception date.
* Exception dates can have muliple values and are returned as arrayslike the
* above for each exception date.
*
* Most other properties are returned as PROPERTY => VALUE.
* Most other properties are returned as PROPERTY => VALUE.
*
* Each item in the VCALENDAR will return an array like:
* [0] => Array (
* [TYPE] => VEVENT
* [UID] => 104
* [SUMMARY] => An example event
* [URL] => http://example.com/node/1
* [DTSTART] => Array (
* [datetime] => 1997-09-07 09:00:00
* [all_day] => 0
* [tz] => US/Eastern
* Each item in the VCALENDAR will return an array like:
* [0] => Array (
* [TYPE] => VEVENT
* [UID] => 104
* [SUMMARY] => An example event
* [URL] => http://example.com/node/1
* [DTSTART] => Array (
* [datetime] => 1997-09-07 09:00:00
* [all_day] => 0
* [tz] => US/Eastern
* )
* [DTEND] => Array (
* [datetime] => 1997-09-07 11:00:00
* [all_day] => 0
* [tz] => US/Eastern
* )
* [RRULE] => Array (
* [FREQ] => Array (
* [0] => MONTHLY
* )
* [DTEND] => Array (
* [datetime] => 1997-09-07 11:00:00
* [all_day] => 0
* [tz] => US/Eastern
* )
* [RRULE] => Array (
* [FREQ] => Array (
* [0] => MONTHLY
* )
* [BYDAY] => Array (
* [0] => 1SU
* [1] => -1SU
* )
* )
* [EXDATE] => Array (
* [0] = Array (
* [datetime] => 1997-09-21 09:00:00
* [all_day] => 0
* [tz] => US/Eastern
* )
* [1] = Array (
* [datetime] => 1997-10-05 09:00:00
* [all_day] => 0
* [tz] => US/Eastern
* )
* )
* [RDATE] => Array (
* [0] = Array (
* [datetime] => 1997-09-21 09:00:00
* [all_day] => 0
* [tz] => US/Eastern
* )
* [1] = Array (
* [datetime] => 1997-10-05 09:00:00
* [all_day] => 0
* [tz] => US/Eastern
* )
* [BYDAY] => Array (
* [0] => 1SU
* [1] => -1SU
* )
* )
* [EXDATE] => Array (
* [0] = Array (
* [datetime] => 1997-09-21 09:00:00
* [all_day] => 0
* [tz] => US/Eastern
* )
* [1] = Array (
* [datetime] => 1997-10-05 09:00:00
* [all_day] => 0
* [tz] => US/Eastern
* )
* )
* [RDATE] => Array (
* [0] = Array (
* [datetime] => 1997-09-21 09:00:00
* [all_day] => 0
* [tz] => US/Eastern
* )
* [1] = Array (
* [datetime] => 1997-10-05 09:00:00
* [all_day] => 0
* [tz] => US/Eastern
* )
* )
* )
*
* @todo
* figure out how to handle this if subgroups are nested,
@ -130,7 +130,7 @@ function date_ical_import($filename) {
* @return array
* An array with all the elements from the ical.
*/
function date_ical_parse($icaldatafolded = array()) {
function date_ical_parse(array $icaldatafolded = array()) {
$items = array();
// Verify this is iCal data.
@ -249,7 +249,6 @@ function date_ical_parse($icaldatafolded = array()) {
array_pop($subgroups);
array_pop($parents);
break;
}
}
}
@ -325,7 +324,6 @@ function date_ical_parse($icaldatafolded = array()) {
// be given special treatment.
default:
$parse_result = $data;
break;
}
// Store the result of our parsing.
@ -339,14 +337,14 @@ function date_ical_parse($icaldatafolded = array()) {
* Parses a ical date element.
*
* Possible formats to parse include:
* PROPERTY:YYYYMMDD[T][HH][MM][SS][Z]
* PROPERTY;VALUE=DATE:YYYYMMDD[T][HH][MM][SS][Z]
* PROPERTY;VALUE=DATE-TIME:YYYYMMDD[T][HH][MM][SS][Z]
* PROPERTY;TZID=XXXXXXXX;VALUE=DATE:YYYYMMDD[T][HH][MM][SS]
* PROPERTY;TZID=XXXXXXXX:YYYYMMDD[T][HH][MM][SS]
* - PROPERTY:YYYYMMDD[T][HH][MM][SS][Z]
* - PROPERTY;VALUE=DATE:YYYYMMDD[T][HH][MM][SS][Z]
* - PROPERTY;VALUE=DATE-TIME:YYYYMMDD[T][HH][MM][SS][Z]
* - PROPERTY;TZID=XXXXXXXX;VALUE=DATE:YYYYMMDD[T][HH][MM][SS]
* - PROPERTY;TZID=XXXXXXXX:YYYYMMDD[T][HH][MM][SS]
*
* The property and the colon before the date are removed in the import
* process above and we are left with $field and $data.
* The property and the colon before the date are removed in the import
* process above and we are left with $field and $data.
*
* @param string $field
* The text before the colon and the date, i.e.
@ -356,22 +354,20 @@ function date_ical_parse($icaldatafolded = array()) {
* 'Z', if supplied, means the date is in UTC.
*
* @return array
* $items array, consisting of:
* 'datetime' => date in YYYY-MM-DD HH:MM format, not timezone adjusted
* 'all_day' => whether this is an all-day event with no time
* 'tz' => the timezone of the date, could be blank if the ical
* has no timezone; the ical specs say no timezone
* conversion should be done if no timezone info is
* supplied
* @todo
* Another option for dates is the format PROPERTY;VALUE=PERIOD:XXXX. The
* Consisting of:
* 'datetime' => Date in YYYY-MM-DD HH:MM format, not timezone adjusted.
* 'all_day' => Whether this is an all-day event with no time.
* 'tz' => The timezone of the date, could be blank if the iCal has no
* timezone; the ical specs say no timezone conversion should be
* done if no timezone info is supplied.
*
* @todo Another option for dates is the format PROPERTY;VALUE=PERIOD:XXXX. The
* period may include a duration, or a date and a duration, or two dates, so
* would have to be split into parts and run through date_ical_parse_date()
* and date_ical_parse_duration(). This is not commonly used, so ignored for
* now. It will take more work to figure how to support that.
*/
function date_ical_parse_date($field, $data) {
$items = array('datetime' => '', 'all_day' => '', 'tz' => '');
if (empty($data)) {
return $items;
@ -415,6 +411,7 @@ function date_ical_parse_date($field, $data) {
break;
}
}
// If no format is specified, attempt a loose match.
else {
preg_match(DATE_REGEX_LOOSE, $data, $regs);
@ -462,8 +459,9 @@ function date_ical_parse_date($field, $data) {
* Parse an ical repeat rule.
*
* @return array
* Array in the form of PROPERTY => array(VALUES)
* PROPERTIES include FREQ, INTERVAL, COUNT, BYDAY, BYMONTH, BYYEAR, UNTIL
* A nested array in the form of 'PROPERTY' => array(VALUES) where
* 'PROPERTIES' includes 'FREQ', 'INTERVAL', 'COUNT', 'BYDAY', 'BYMONTH',
* 'BYYEAR', 'UNTIL'.
*/
function date_ical_parse_rrule($field, $data) {
$data = preg_replace("/RRULE.*:/", '', $data);
@ -496,7 +494,7 @@ function date_ical_parse_rrule($field, $data) {
* Parse exception dates (can be multiple values).
*
* @return array
* an array of date value arrays.
* An array of date value arrays.
*/
function date_ical_parse_exceptions($field, $data) {
$data = str_replace($field . ':', '', $data);
@ -512,15 +510,17 @@ function date_ical_parse_exceptions($field, $data) {
* Parses the duration of the event.
*
* Example:
* DURATION:PT1H30M
* DURATION:P1Y2M
* DURATION:PT1H30M
* DURATION:P1Y2M.
*
* @param array $subgroup
* Array of other values in the vevent so we can check for DTSTART.
* @param string $value_name
* The name of the value to process; defaults to 'DURATION'.
*/
function date_ical_parse_duration(&$subgroup, $field = 'DURATION') {
$items = $subgroup[$field];
$data = $items['DATA'];
function date_ical_parse_duration(array &$subgroup, $value_name = 'DURATION') {
$items = $subgroup[$value_name];
$data = $items['DATA'];
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])?/', $data, $duration);
$items['year'] = isset($duration[1]) ? str_replace('Y', '', $duration[1]) : '';
$items['month'] = isset($duration[2]) ? str_replace('M', '', $duration[2]) : '';
@ -535,7 +535,7 @@ function date_ical_parse_duration(&$subgroup, $field = 'DURATION') {
$timezone = 'UTC';
}
$date = new DateObject($start_date, $timezone);
$date2 = clone($date);
$date2 = clone $date;
foreach ($items as $item => $count) {
if ($count > 0) {
date_modify($date2, '+' . $count . ' ' . $item);
@ -573,8 +573,8 @@ function date_ical_parse_text($field, $data) {
* Catch situations like the upcoming.org feed that uses
* LOCATION;VENUE-UID="http://upcoming.yahoo.com/venue/104/":111 First Street...
* or more normal LOCATION;UID=123:111 First Street...
* Upcoming feed would have been improperly broken on the ':' in http://
* so we paste the $field and $data back together first.
* Upcoming feed would have been improperly broken on the ':' in http:// so we
* paste the $field and $data back together first.
*
* Use non-greedy check for ':' in case there are more of them in the address.
*/
@ -603,8 +603,7 @@ function date_ical_parse_location($field, $data) {
* @return object
* A timezone-adjusted date object.
*/
function date_ical_date($ical_date, $to_tz = FALSE) {
function date_ical_date(array $ical_date, $to_tz = FALSE) {
// If the ical date has no timezone, must assume it is stateless
// so treat it as a local date.
if (empty($ical_date['datetime'])) {
@ -621,7 +620,7 @@ function date_ical_date($ical_date, $to_tz = FALSE) {
}
$date = new DateObject($ical_date['datetime'], new DateTimeZone($from_tz));
if ($to_tz && $ical_date['tz'] != '' && $to_tz != $ical_date['tz']) {
if ($to_tz && !empty($ical_date['tz']) && $to_tz != $ical_date['tz']) {
date_timezone_set($date, timezone_open($to_tz));
}
return $date;
@ -631,7 +630,7 @@ function date_ical_date($ical_date, $to_tz = FALSE) {
* Escape #text elements for safe iCal use.
*
* @param string $text
* Text to escape
* Text to escape.
*
* @return string
* Escaped text
@ -639,9 +638,9 @@ function date_ical_date($ical_date, $to_tz = FALSE) {
function date_ical_escape_text($text) {
$text = drupal_html_to_text($text);
$text = trim($text);
// TODO Per #38130 the iCal specs don't want : and " escaped
// but there was some reason for adding this in. Need to watch
// this and see if anything breaks.
// @todo Per #38130 the iCal specs don't want : and " escaped but there was
// some reason for adding this in. Need to watch this and see if anything
// breaks.
// $text = str_replace('"', '\"', $text);
// $text = str_replace(":", "\:", $text);
$text = preg_replace("/\\\b/", "\\\\", $text);
@ -695,7 +694,7 @@ function date_ical_escape_text($text) {
* )
* )
*/
function date_api_ical_build_rrule($form_values) {
function date_api_ical_build_rrule(array $form_values) {
$rrule = '';
if (empty($form_values) || !is_array($form_values)) {
return $rrule;
@ -725,9 +724,9 @@ function date_api_ical_build_rrule($form_values) {
if (array_key_exists('BYMONTHDAY', $form_values) && is_array($form_values['BYMONTHDAY']) && $bymonthday = implode(",", $form_values['BYMONTHDAY'])) {
$rrule .= ';BYMONTHDAY=' . $bymonthday;
}
// The UNTIL date is supposed to always be expressed in UTC.
// The input date values may already have been converted to a date object on a
// previous pass, so check for that.
// The UNTIL date is supposed to always be expressed in UTC. The input date
// values may already have been converted to a date object on a previous
// pass, so check for that.
if (array_key_exists('UNTIL', $form_values) && array_key_exists('datetime', $form_values['UNTIL']) && !empty($form_values['UNTIL']['datetime'])) {
// We only collect a date for UNTIL, but we need it to be inclusive, so
// force it to a full datetime element at the last second of the day.
@ -754,8 +753,8 @@ function date_api_ical_build_rrule($form_values) {
}
$rrule .= ';UNTIL=' . date_format($until, DATE_FORMAT_ICAL) . 'Z';
}
// Our form doesn't allow a value for COUNT, but it may be needed by
// modules using the API, so add it to the rule.
// Our form doesn't allow a value for COUNT, but it may be needed by modules
// using the API, so add it to the rule.
if (array_key_exists('COUNT', $form_values)) {
$rrule .= ';COUNT=' . $form_values['COUNT'];
}
@ -769,9 +768,9 @@ function date_api_ical_build_rrule($form_values) {
$rrule .= ';WKST=' . date_repeat_dow2day(variable_get('date_first_day', 0));
}
// Exceptions dates go last, on their own line.
// The input date values may already have been converted to a date
// object on a previous pass, so check for that.
// Exceptions dates go last, on their own line. The input date values may
// already have been converted to a date object on a previous pass, so check
// for that.
if (isset($form_values['EXDATE']) && is_array($form_values['EXDATE'])) {
$ex_dates = array();
foreach ($form_values['EXDATE'] as $value) {

View File

@ -4,7 +4,7 @@
* @file
* SQL helper for Date API.
*
* @TODO
* @todo
* Add experimental support for sqlite: http://www.sqlite.org/lang_datefunc.html
* and Oracle (http://psoug.org/reference/date_func.html and
* http://psoug.org/reference/datatypes.html) date/time functions.
@ -19,7 +19,7 @@
* @return string
* Correct sql string for database type.
*/
function date_sql_concat($array) {
function date_sql_concat(array $array) {
switch (Database::getConnection()->databaseType()) {
case 'mysql':
return "CONCAT(" . implode(",", $array) . ")";
@ -38,7 +38,7 @@ function date_sql_concat($array) {
* @return string
* SQL statement to return the first non-NULL value in the list.
*/
function date_sql_coalesce($array) {
function date_sql_coalesce(array $array) {
switch (Database::getConnection()->databaseType()) {
case 'mysql':
case 'pgsql':
@ -50,13 +50,13 @@ function date_sql_coalesce($array) {
* A helper function to do cross-database padding of date parts.
*
* @param string $str
* A string to apply padding to
* A string to apply padding to.
* @param int $size
* The size the final string should be
* The size the final string should be.
* @param string $pad
* The value to pad the string with
* The value to pad the string with.
* @param string $side
* The side of the string to pad
* The side of the string to pad.
*/
function date_sql_pad($str, $size = 2, $pad = '0', $side = 'l') {
switch ($side) {
@ -71,7 +71,6 @@ function date_sql_pad($str, $size = 2, $pad = '0', $side = 'l') {
/**
* A class to manipulate date SQL.
*/
// @codingStandardsIgnoreStart
class date_sql_handler {
var $db_type = NULL;
var $date_type = DATE_DATETIME;
@ -113,9 +112,17 @@ class date_sql_handler {
break;
case 'pgsql':
$test = db_query("SELECT '2008-02-15 12:00:00 UTC' AT TIME ZONE 'US/Central'")->fetchField();
if ($test == '2008-02-15 06:00:00') {
$has_support = TRUE;
// PostgreSQL doesn't always have timezone support enabled, so catch
// exceptions so they don't break the site. This is safe to do as it
// is only checking to see if timezones are actually supported.
try {
$test = db_query("SELECT '2008-02-15 12:00:00 UTC' AT TIME ZONE 'US/Central'")->fetchField();
if ($test == '2008-02-15 06:00:00') {
$has_support = TRUE;
}
}
catch (PDOException $e) {
// No support.
}
break;
}
@ -218,7 +225,7 @@ class date_sql_handler {
case 'pgsql':
switch ($this->date_type) {
case DATE_UNIX:
$field = "$field::ABSTIME";
$field = "TO_TIMESTAMP($field)";
break;
case DATE_ISO:
@ -294,8 +301,8 @@ class date_sql_handler {
* @param int $count
* The number of values to adjust.
* @param string $granularity
* The granularity of the adjustment, should be singular,
* like SECOND, MINUTE, DAY, HOUR.
* The granularity of the adjustment, should be singular, like SECOND,
* MINUTE, DAY, HOUR.
*/
function sql_date_math($field, $direction, $count, $granularity) {
$granularity = strtoupper($granularity);
@ -303,10 +310,10 @@ class date_sql_handler {
case 'mysql':
switch ($direction) {
case 'ADD':
return "DATE_ADD($field, INTERVAL $count $granularity)";
return "DATE_ADD(CAST($field AS DATETIME), INTERVAL $count $granularity)";
case 'SUB':
return "DATE_SUB($field, INTERVAL $count $granularity)";
return "DATE_SUB(CAST($field AS DATETIME), INTERVAL $count $granularity)";
}
case 'pgsql':
@ -332,23 +339,20 @@ class date_sql_handler {
}
/**
* Select a date value from the database, adjusting the value
* for the timezone.
* Select a date value from the database, adjusting for the timezone.
*
* Check whether database timezone conversion is supported in
* this system and use it if possible, otherwise use an
* offset.
* Check whether database timezone conversion is supported in this system and
* use it if possible, otherwise use an offset.
*
* @param string $field
* The field to be adjusted.
* @param bool $offset
* Set a fixed offset or offset field to use for the date.
* If set, no timezone conversion will be done and the
* offset will be used.
* Set a fixed offset or offset field to use for the date. If set, no
* timezone conversion will be done and the offset will be used.
*/
function sql_tz($field, $offset = NULL, $comp_date = NULL) {
// If the timezones are values they need to be quoted, but
// if they are field names they do not.
// If the timezones are values they need to be quoted, but if they are
// field names they do not.
$db_zone = !empty($this->db_timezone_field) ? $this->db_timezone_field : "'{$this->db_timezone}'";
$localzone = !empty($this->local_timezone_field) ? $this->local_timezone_field : "'{$this->local_timezone}'";
// If a fixed offset is required, use it.
@ -359,8 +363,8 @@ class date_sql_handler {
elseif ($db_zone == $localzone) {
return $this->sql_offset($field, 0);
}
// If the db has no timezone support, adjust by the offset,
// could be either a field name or a value.
// If the db has no timezone support, adjust by the offset, could be either
// a field name or a value.
elseif (!$this->db_tz_support() || empty($localzone)) {
if (!empty($this->offset_field)) {
return $this->sql_offset($field, $this->offset_field);
@ -441,7 +445,7 @@ class date_sql_handler {
's' => 'SS',
'A' => 'AM',
'\T' => '"T"',
// '\W' => // TODO, what should this be?
// '\W' => // @todo what should this be?
);
$format = strtr($format, $replace);
return "TO_CHAR($field, '$format')";
@ -584,6 +588,7 @@ class date_sql_handler {
case 'pgsql':
return "EXTRACT(WEEK FROM($field))";
}
case 'DOW':
switch ($this->db_type) {
case 'mysql':
@ -594,6 +599,7 @@ class date_sql_handler {
case 'pgsql':
return "EXTRACT(DOW FROM($field))";
}
case 'DOY':
switch ($this->db_type) {
case 'mysql':
@ -636,8 +642,8 @@ class date_sql_handler {
date_modify($date, $adjustment . ' seconds');
}
// When comparing a field to a date we can avoid doing timezone
// conversion by altering the comparison date to the db timezone.
// This won't work if the timezone is a field instead of a value.
// conversion by altering the comparison date to the db timezone. This
// won't work if the timezone is a field instead of a value.
if (empty($this->db_timezone_field) && empty($this->local_timezone_field) && $this->db_timezone_field != $this->local_timezone_field) {
$date->setTimezone(timezone_open($this->db_timezone));
$this->local_timezone = $this->db_timezone;
@ -706,7 +712,9 @@ class date_sql_handler {
/**
* An array of all date parts,
* optionally limited to an array of allowed parts.
*
* @param bool $limit
* Limit to an array of allowed parts.
*/
function date_parts($limit = NULL) {
$parts = array(
@ -738,8 +746,8 @@ class date_sql_handler {
* 'min', 'max', 'format', 'sep', 'empty_now', 'empty_min', 'empty_max' .
* Returns all info if empty.
* @param string $part
* 'year', 'month', 'day', 'hour', 'minute', or 'second.
* returns info for all parts if empty.
* 'year', 'month', 'day', 'hour', 'minute', or 'second. Returns info for
* all parts if empty.
*/
function part_info($op = NULL, $part = NULL) {
$info = array();
@ -813,10 +821,10 @@ class date_sql_handler {
/**
* Create a complete date/time value out of an incomplete array of 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' .
* 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') {
if (empty($selected)) {
@ -1119,8 +1127,8 @@ class date_sql_handler {
/**
* Granularity arguments handler.
*
* Use the parsed values from the ISO argument
* to determine the granularity of this period.
* Use the parsed values from the ISO argument to determine the granularity
* of this period.
*/
function arg_granularity($arg) {
$granularity = '';
@ -1133,8 +1141,9 @@ class date_sql_handler {
}
/**
* Use the parsed values from the ISO argument to determine the
* min and max date for this period.
* Determine the min and max date for this period.
*
* Uses the parsed values from the ISO argument.
*/
function arg_range($arg) {
// Parse the argument to get its parts.
@ -1143,7 +1152,7 @@ class date_sql_handler {
// 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]))) {
$min_date = date_now();
$max_date = clone($min_date);
$max_date = clone $min_date;
foreach ($parts[0]['period'] as $part => $value) {
date_modify($max_date, "+$value $part");
}
@ -1153,7 +1162,7 @@ class date_sql_handler {
// Build a range from a period to period argument.
if (empty($parts[0]['date']) && !empty($parts[0]['period']) && !empty($parts[1]['period'])) {
$min_date = date_now();
$max_date = clone($min_date);
$max_date = clone $min_date;
foreach ($parts[0]['period'] as $part => $value) {
date_modify($min_date, "+$value $part");
}
@ -1176,7 +1185,7 @@ class date_sql_handler {
// Build a range from start date + period.
elseif (!empty($parts[1]['period'])) {
foreach ($parts[1]['period'] as $part => $value) {
$max_date = clone($min_date);
$max_date = clone $min_date;
date_modify($max_date, "+$value $part");
}
date_modify($max_date, '-1 second');
@ -1204,4 +1213,3 @@ class date_sql_handler {
return array($now, $now);
}
}
// @codingStandardsIgnoreEnd

View File

@ -1,223 +1,228 @@
/**
* @file
*/
(function ($) {
Drupal.behaviors.dateYearRange = {};
Drupal.behaviors.dateYearRange = {};
Drupal.behaviors.dateYearRange.attach = function (context, settings) {
var $textfield, $textfields, i;
Drupal.behaviors.dateYearRange.attach = function (context, settings) {
var $textfield, $textfields, i;
// Turn the years back and forward fieldsets into dropdowns.
$textfields = $('input.select-list-with-custom-option', context).once('date-year-range');
for (i = 0; i < $textfields.length; i++) {
$textfield = $($textfields[i]);
new Drupal.dateYearRange.SelectListWithCustomOption($textfield);
}
};
// Turn the years back and forward fieldsets into dropdowns.
$textfields = $('input.select-list-with-custom-option', context).once('date-year-range');
for (i = 0; i < $textfields.length; i++) {
$textfield = $($textfields[i]);
new Drupal.dateYearRange.SelectListWithCustomOption($textfield);
}
};
Drupal.dateYearRange = {};
Drupal.dateYearRange = {};
/**
/**
* Constructor for the SelectListWithCustomOption object.
*
* This object is responsible for turning the years back and forward textfields
* into dropdowns with an 'other' option that lets the user enter a custom
* value.
*/
Drupal.dateYearRange.SelectListWithCustomOption = function ($textfield) {
this.$textfield = $textfield;
this.$description = $textfield.next('div.description');
this.defaultValue = $textfield.val();
this.$dropdown = this.createDropdown();
this.$dropdown.insertBefore($textfield);
};
Drupal.dateYearRange.SelectListWithCustomOption = function ($textfield) {
this.$textfield = $textfield;
this.$description = $textfield.next('div.description');
this.defaultValue = $textfield.val();
this.$dropdown = this.createDropdown();
this.$dropdown.insertBefore($textfield);
};
/**
* Get the value of the textfield as it existed on page load.
*
* @param {String} type
* The type of the variable to be returned. Defaults to string.
* @return
* The original value of the textfield. Returned as an integer, if the type
* parameter was 'int'.
*/
Drupal.dateYearRange.SelectListWithCustomOption.prototype.getOriginal = function (type) {
var original;
if (type === 'int') {
original = parseInt(this.defaultValue, 10);
if (window.isNaN(original)) {
original = 0;
/**
* Get the value of the textfield as it existed on page load.
*
* @param {String} type
* The type of the variable to be returned. Defaults to string.
*
* @return
* The original value of the textfield. Returned as an integer, if the type
* parameter was 'int'.
*/
Drupal.dateYearRange.SelectListWithCustomOption.prototype.getOriginal = function (type) {
var original;
if (type === 'int') {
original = parseInt(this.defaultValue, 10);
if (window.isNaN(original)) {
original = 0;
}
}
}
else {
original = this.defaultValue;
}
return original;
};
/**
* Get the correct first value for the dropdown.
*/
Drupal.dateYearRange.SelectListWithCustomOption.prototype.getStartValue = function () {
var direction = this.getDirection();
var start;
switch (direction) {
case 'back':
// For the 'years back' dropdown, the first option should be -10, unless
// the default value of the textfield is even smaller than that.
start = Math.min(this.getOriginal('int'), -10);
break;
case 'forward':
start = 0;
break;
}
return start;
};
/**
* Get the correct last value for the dropdown.
*/
Drupal.dateYearRange.SelectListWithCustomOption.prototype.getEndValue = function () {
var direction = this.getDirection();
var end;
var originalString = this.getOriginal();
switch (direction) {
case 'back':
end = 0;
break;
case 'forward':
// If the original value of the textfield is an absolute year such as
// 2020, don't try to include it in the dropdown.
if (originalString.indexOf('+') === -1) {
end = 10;
}
// If the original value is a relative value (+x), we want it to be
// included in the possible dropdown values.
else {
end = Math.max(this.getOriginal('int'), 10);
}
break;
}
return end;
};
/**
* Create a dropdown select list with the correct options for this textfield.
*/
Drupal.dateYearRange.SelectListWithCustomOption.prototype.createDropdown = function () {
var $dropdown = $('<select>').addClass('form-select date-year-range-select');
var $option, i, value;
var start = this.getStartValue();
var end = this.getEndValue();
var direction = this.getDirection();
for (i = start; i <= end; i++) {
// Make sure we include the +/- sign in the option value.
value = i;
if (i > 0) {
value = '+' + i;
else {
original = this.defaultValue;
}
// Zero values must have a + or - in front.
if (i === 0) {
if (direction === 'back') {
value = '-' + i;
}
else {
return original;
};
/**
* Get the correct first value for the dropdown.
*/
Drupal.dateYearRange.SelectListWithCustomOption.prototype.getStartValue = function () {
var direction = this.getDirection();
var start;
switch (direction) {
case 'back':
// For the 'years back' dropdown, the first option should be -10, unless
// the default value of the textfield is even smaller than that.
start = Math.min(this.getOriginal('int'), -10);
break;
case 'forward':
start = 0;
break;
}
return start;
};
/**
* Get the correct last value for the dropdown.
*/
Drupal.dateYearRange.SelectListWithCustomOption.prototype.getEndValue = function () {
var direction = this.getDirection();
var end;
var originalString = this.getOriginal();
switch (direction) {
case 'back':
end = 0;
break;
case 'forward':
// If the original value of the textfield is an absolute year such as
// 2020, don't try to include it in the dropdown.
if (originalString.indexOf('+') === -1) {
end = 10;
}
// If the original value is a relative value (+x), we want it to be
// included in the possible dropdown values.
else {
end = Math.max(this.getOriginal('int'), 10);
}
break;
}
return end;
};
/**
* Create a dropdown select list with the correct options for this textfield.
*/
Drupal.dateYearRange.SelectListWithCustomOption.prototype.createDropdown = function () {
var $dropdown = $('<select>').addClass('form-select date-year-range-select');
var $option, i, value;
var start = this.getStartValue();
var end = this.getEndValue();
var direction = this.getDirection();
for (i = start; i <= end; i++) {
// Make sure we include the +/- sign in the option value.
value = i;
if (i > 0) {
value = '+' + i;
}
// Zero values must have a + or - in front.
if (i === 0) {
if (direction === 'back') {
value = '-' + i;
}
else {
value = '+' + i;
}
}
$option = $('<option>' + Drupal.formatPlural(value, '@count year from now', '@count years from now') + '</option>').val(value);
$dropdown.append($option);
}
$option = $('<option>' + Drupal.formatPlural(value, '@count year from now', '@count years from now') + '</option>').val(value);
// Create an 'Other' option.
$option = $('<option class="custom-option">' + Drupal.t('Other') + '</option>').val('');
$dropdown.append($option);
}
// Create an 'Other' option.
$option = $('<option class="custom-option">' + Drupal.t('Other') + '</option>').val('');
$dropdown.append($option);
// When the user changes the selected option in the dropdown, perform
// appropriate actions (such as showing or hiding the textfield).
$dropdown.bind('change', $.proxy(this.handleDropdownChange, this));
// When the user changes the selected option in the dropdown, perform
// appropriate actions (such as showing or hiding the textfield).
$dropdown.bind('change', $.proxy(this.handleDropdownChange, this));
// Set the initial value of the dropdown.
this._setInitialDropdownValue($dropdown);
return $dropdown;
};
// Set the initial value of the dropdown.
this._setInitialDropdownValue($dropdown);
return $dropdown;
};
Drupal.dateYearRange.SelectListWithCustomOption.prototype._setInitialDropdownValue = function ($dropdown) {
var textfieldValue = this.getOriginal();
// Determine whether the original textfield value exists in the dropdown.
var possible = $dropdown.find('option[value="' + textfieldValue + '"]');
// If the original textfield value is one of the dropdown options, preselect
// it and hide the 'other' textfield.
if (possible.length) {
$dropdown.val(textfieldValue);
this.hideTextfield();
}
// If the original textfield value isn't one of the dropdown options, choose
// the 'Other' option in the dropdown.
else {
$dropdown.val('');
}
};
Drupal.dateYearRange.SelectListWithCustomOption.prototype._setInitialDropdownValue = function ($dropdown) {
var textfieldValue = this.getOriginal();
// Determine whether the original textfield value exists in the dropdown.
var possible = $dropdown.find('option[value="' + textfieldValue + '"]');
// If the original textfield value is one of the dropdown options, preselect
// it and hide the 'other' textfield.
if (possible.length) {
$dropdown.val(textfieldValue);
this.hideTextfield();
}
// If the original textfield value isn't one of the dropdown options, choose
// the 'Other' option in the dropdown.
else {
$dropdown.val('');
}
};
/**
* Determine whether this is the "years back" or "years forward" textfield.
*/
Drupal.dateYearRange.SelectListWithCustomOption.prototype.getDirection = function () {
if (this.direction) {
return this.direction;
}
var direction;
if (this.$textfield.hasClass('back')) {
direction = 'back';
}
else if (this.$textfield.hasClass('forward')) {
direction = 'forward';
}
this.direction = direction;
return direction;
};
/**
* Determine whether this is the "years back" or "years forward" textfield.
*/
Drupal.dateYearRange.SelectListWithCustomOption.prototype.getDirection = function () {
if (this.direction) {
return this.direction;
}
var direction;
if (this.$textfield.hasClass('back')) {
direction = 'back';
}
else if (this.$textfield.hasClass('forward')) {
direction = 'forward';
}
this.direction = direction;
return direction;
};
/**
* Change handler for the dropdown, to modify the textfield as appropriate.
*/
Drupal.dateYearRange.SelectListWithCustomOption.prototype.handleDropdownChange = function () {
// Since the dropdown changed, we need to make the content of the textfield
// match the (new) selected option.
this.syncTextfield();
/**
* Change handler for the dropdown, to modify the textfield as appropriate.
*/
Drupal.dateYearRange.SelectListWithCustomOption.prototype.handleDropdownChange = function () {
// Since the dropdown changed, we need to make the content of the textfield
// match the (new) selected option.
this.syncTextfield();
// Show the textfield if the 'Other' option was selected, and hide it if one
// of the preset options was selected.
if ($(':selected', this.$dropdown).hasClass('custom-option')) {
this.revealTextfield();
}
else {
this.hideTextfield();
}
};
// Show the textfield if the 'Other' option was selected, and hide it if one
// of the preset options was selected.
if ($(':selected', this.$dropdown).hasClass('custom-option')) {
this.revealTextfield();
}
else {
this.hideTextfield();
}
};
/**
* Display the textfield and its description.
*/
Drupal.dateYearRange.SelectListWithCustomOption.prototype.revealTextfield = function () {
this.$textfield.show();
this.$description.show();
};
/**
* Display the textfield and its description.
*/
Drupal.dateYearRange.SelectListWithCustomOption.prototype.revealTextfield = function () {
this.$textfield.show();
this.$description.show();
};
/**
* Hide the textfield and its description.
*/
Drupal.dateYearRange.SelectListWithCustomOption.prototype.hideTextfield = function () {
this.$textfield.hide();
this.$description.hide();
};
/**
* Hide the textfield and its description.
*/
Drupal.dateYearRange.SelectListWithCustomOption.prototype.hideTextfield = function () {
this.$textfield.hide();
this.$description.hide();
};
/**
* Copy the selected value of the dropdown to the textfield.
*
* FAPI doesn't know about the JS-only dropdown, so the textfield needs to
* reflect the value of the dropdown.
*/
Drupal.dateYearRange.SelectListWithCustomOption.prototype.syncTextfield = function () {
var value = this.$dropdown.val();
this.$textfield.val(value);
};
/**
* Copy the selected value of the dropdown to the textfield.
*
* FAPI doesn't know about the JS-only dropdown, so the textfield needs to
* reflect the value of the dropdown.
*/
Drupal.dateYearRange.SelectListWithCustomOption.prototype.syncTextfield = function () {
var value = this.$dropdown.val();
this.$textfield.val(value);
};
})(jQuery);

View File

@ -0,0 +1,438 @@
<?php
/**
* @file
* Test Date API functions.
*/
/**
* Test Date API functions.
*/
class DateApiTestCase extends DrupalWebTestCase {
/**
* @todo.
*/
public static function getInfo() {
return array(
'name' => t('Date API'),
'description' => t('Test Date API functions.') ,
'group' => t('Date'),
);
}
/**
* {@inheritdoc}
*/
public function setUp(array $modules = array()) {
$modules[] = 'date_api';
parent::setUp($modules);
variable_set('date_api_use_iso8601', FALSE);
variable_set('date_first_day', 1);
}
/**
* @todo.
*/
public function testDateAPI() {
// Test date_format_date().
$formatters = array(
'a',
'A',
'B',
'c',
'd',
'D',
'e',
'F',
'g',
'G',
'h',
'H',
'i',
'I',
'j',
'l',
'L',
'm',
'M',
'n',
'N',
'o',
'O',
'P',
'r',
'R',
's',
'S',
't',
'T',
'u',
'U',
'w',
'W',
'y',
'Y',
'z',
'Z',
);
foreach ($formatters as $formatter) {
$date_api_format = date_format_date(date_now(), 'custom', $formatter);
$php_format = date_format(date_now(), $formatter);
$this->assertEqual($date_api_format, $php_format, 'Test that the "' . $formatter . '" formatter is formatted correctly by date_format_date()');
}
// Test the order of the weeks days for a calendar that starts on Monday and
// one that starts on Sunday.
variable_set('date_first_day', 1);
$expected = array(0 => t('Mon'), 1 => t('Tue'), 2 => t('Wed'), 3 => t('Thu'), 4 => t('Fri'), 5 => t('Sat'), 6 => t('Sun'));
$days = date_week_days_ordered(date_week_days_abbr(1));
$this->assertEqual($expected, $days, 'Test that date_week_days_ordered() array starts on Monday when the site first day is on Monday.');
variable_set('date_first_day', 0);
$expected = array(0 => t('Sun'), 1 => t('Mon'), 2 => t('Tue'), 3 => t('Wed'), 4 => t('Thu'), 5 => t('Fri'), 6 => t('Sat'));
$days = date_week_days_ordered(date_week_days_abbr(1));
$this->assertEqual($expected, $days, 'Test that date_week_days_ordered() array starts on Sunday when the site first day is on Sunday.');
// Test days in February for a leap year and a non-leap year.
$expected = 28;
$value = date_days_in_month(2005, 2);
$this->assertEqual($expected, $value, "Test date_days_in_month(2, 2005): should be $expected, found $value.");
$expected = 29;
$value = date_days_in_month(2004, 2);
$this->assertEqual($expected, $value, "Test date_days_in_month(2, 2004): should be $expected, found $value.");
// Test days in year for a leap year and a non-leap year.
$expected = 365;
$value = date_days_in_year('2005-06-01 00:00:00');
$this->assertEqual($expected, $value, "Test date_days_in_year(2005-06-01): should be $expected, found $value.");
$expected = 366;
$value = date_days_in_year('2004-06-01 00:00:00');
$this->assertEqual($expected, $value, "Test date_days_in_year(2004-06-01): should be $expected, found $value.");
// Test ISO weeks for a leap year and a non-leap year.
$expected = 52;
$value = date_iso_weeks_in_year('2008-06-01 00:00:00');
$this->assertEqual($expected, $value, "Test date_iso_weeks_in_year(2008-06-01): should be $expected, found $value.");
$expected = 53;
$value = date_iso_weeks_in_year('2009-06-01 00:00:00');
$this->assertEqual($expected, $value, "Test date_iso_weeks_in_year(2009-06-01): should be $expected, found $value.");
// Test day of week for March 1, the day after leap day.
$expected = 6;
$value = date_day_of_week('2008-03-01 00:00:00');
$this->assertEqual($expected, $value, "Test date_day_of_week(2008-03-01): should be $expected, found $value.");
$expected = 0;
$value = date_day_of_week('2009-03-01 00:00:00');
$this->assertEqual($expected, $value, "Test date_day_of_week(2009-03-01): should be $expected, found $value.");
// Test day of week name for March 1, the day after leap day.
$expected = 'Sat';
$value = date_day_of_week_name('2008-03-01 00:00:00');
$this->assertEqual($expected, $value, "Test date_day_of_week_name(2008-03-01): should be $expected, found $value.");
$expected = 'Sun';
$value = date_day_of_week_name('2009-03-01 00:00:00');
$this->assertEqual($expected, $value, "Test date_day_of_week_name(2009-03-01): should be $expected, found $value.");
// Test week range with calendar weeks.
variable_set('date_first_day', 0);
variable_set('date_api_use_iso8601', FALSE);
$expected = '2008-01-27 to 2008-02-02';
$result = date_week_range(5, 2008);
$value = $result[0]->format(DATE_FORMAT_DATE) . ' to ' . $result[1]->format(DATE_FORMAT_DATE);
$this->assertEqual($expected, $value, "Test calendar date_week_range(5, 2008): should be $expected, found $value.");
$expected = '2009-01-25 to 2009-01-31';
$result = date_week_range(5, 2009);
$value = $result[0]->format(DATE_FORMAT_DATE) . ' to ' . $result[1]->format(DATE_FORMAT_DATE);
$this->assertEqual($expected, $value, "Test calendar date_week_range(5, 2009): should be $expected, found $value.");
// And now with ISO weeks.
variable_set('date_first_day', 1);
variable_set('date_api_use_iso8601', TRUE);
$expected = '2008-01-28 to 2008-02-03';
$result = date_week_range(5, 2008);
$value = $result[0]->format(DATE_FORMAT_DATE) . ' to ' . $result[1]->format(DATE_FORMAT_DATE);
$this->assertEqual($expected, $value, "Test ISO date_week_range(5, 2008): should be $expected, found $value.");
$expected = '2009-01-26 to 2009-02-01';
$result = date_week_range(5, 2009);
$value = $result[0]->format(DATE_FORMAT_DATE) . ' to ' . $result[1]->format(DATE_FORMAT_DATE);
$this->assertEqual($expected, $value, "Test ISO date_week_range(5, 2009): should be $expected, found $value.");
variable_set('date_api_use_iso8601', FALSE);
// Find calendar week for a date.
variable_set('date_first_day', 0);
$expected = '09';
$value = date_week('2008-03-01');
$this->assertEqual($expected, $value, "Test date_week(2008-03-01): should be $expected, found $value.");
$expected = '10';
$value = date_week('2009-03-01');
$this->assertEqual($expected, $value, "Test date_week(2009-03-01): should be $expected, found $value.");
// Create date object from datetime string.
$input = '2009-03-07 10:30';
$timezone = 'America/Chicago';
$date = new dateObject($input, $timezone);
$value = $date->format('c');
$expected = '2009-03-07T10:30:00-06:00';
$this->assertEqual($expected, $value, "Test new dateObject($input, $timezone): should be $expected, found $value.");
// Same during daylight savings time.
$input = '2009-06-07 10:30';
$timezone = 'America/Chicago';
$date = new dateObject($input, $timezone);
$value = $date->format('c');
$expected = '2009-06-07T10:30:00-05:00';
$this->assertEqual($expected, $value, "Test new dateObject($input, $timezone): should be $expected, found $value.");
// Create date object from date string.
$input = '2009-03-07';
$timezone = 'America/Chicago';
$date = new dateObject($input, $timezone);
$value = $date->format('c');
$expected = '2009-03-07T00:00:00-06:00';
$this->assertEqual($expected, $value, "Test new dateObject($input, $timezone): should be $expected, found $value.");
// Same during daylight savings time.
$input = '2009-06-07';
$timezone = 'America/Chicago';
$date = new dateObject($input, $timezone);
$value = $date->format('c');
$expected = '2009-06-07T00:00:00-05:00';
$this->assertEqual($expected, $value, "Test new dateObject($input, $timezone): should be $expected, found $value.");
// Create date object from date array, date only.
$input = array('year' => 2010, 'month' => 2, 'day' => 28);
$timezone = 'America/Chicago';
$date = new dateObject($input, $timezone);
$value = $date->format('c');
$expected = '2010-02-28T00:00:00-06:00';
$this->assertEqual($expected, $value, "Test new dateObject(array('year' => 2010, 'month' => 2, 'day' => 28), $timezone): should be $expected, found $value.");
// Create date object from date array with hour.
$input = array('year' => 2010, 'month' => 2, 'day' => 28, 'hour' => 10);
$timezone = 'America/Chicago';
$date = new dateObject($input, $timezone);
$value = $date->format('c');
$expected = '2010-02-28T10:00:00-06:00';
$this->assertEqual($expected, $value, "Test new dateObject(array('year' => 2010, 'month' => 2, 'day' => 28, 'hour' => 10), $timezone): should be $expected, found $value.");
// 0 = January 1, 1970 00:00:00 (UTC);
// 1000000000 = September 9, 2001 01:46:40 (UTC);
// Create date object from unix timestamp and convert it to a local date.
$input = 0;
$timezone = 'UTC';
$date = new dateObject($input, $timezone);
$value = $date->format('c');
$expected = '1970-01-01T00:00:00+00:00';
$this->assertEqual($expected, $value, "Test new dateObject($input, $timezone): should be $expected, found $value.");
$expected = 'UTC';
$value = $date->getTimeZone()->getName();
$this->assertEqual($expected, $value, "The current timezone is $value: should be $expected.");
$expected = 0;
$value = $date->getOffset();
$this->assertEqual($expected, $value, "The current offset is $value: should be $expected.");
$timezone = 'America/Los_Angeles';
$date->setTimezone(new DateTimeZone($timezone));
$value = $date->format('c');
$expected = '1969-12-31T16:00:00-08:00';
$this->assertEqual($expected, $value, "Test \$date->setTimezone(new DateTimeZone($timezone)): should be $expected, found $value.");
$expected = 'America/Los_Angeles';
$value = $date->getTimeZone()->getName();
$this->assertEqual($expected, $value, "The current timezone should be $expected, found $value.");
$expected = '-28800';
$value = $date->getOffset();
$this->assertEqual($expected, $value, "The current offset should be $expected, found $value.");
// Convert the local version of a timestamp to UTC.
$input = 0;
$timezone = 'America/Los_Angeles';
$date = new dateObject($input, $timezone);
$offset = $date->getOffset();
$value = $date->format('c');
$expected = '1969-12-31T16:00:00-08:00';
$this->assertEqual($expected, $value, "Test new dateObject($input, $timezone): should be $expected, found $value.");
$expected = 'America/Los_Angeles';
$value = $date->getTimeZone()->getName();
$this->assertEqual($expected, $value, "The current timezone should be $expected, found $value.");
$expected = '-28800';
$value = $date->getOffset();
$this->assertEqual($expected, $value, "The current offset should be $expected, found $value.");
$timezone = 'UTC';
$date->setTimezone(new DateTimeZone($timezone));
$value = $date->format('c');
$expected = '1970-01-01T00:00:00+00:00';
$this->assertEqual($expected, $value, "Test \$date->setTimezone(new DateTimeZone($timezone)): should be $expected, found $value.");
$expected = 'UTC';
$value = $date->getTimeZone()->getName();
$this->assertEqual($expected, $value, "The current timezone should be $expected, found $value.");
$expected = '0';
$value = $date->getOffset();
$this->assertEqual($expected, $value, "The current offset should be $expected, found $value.");
// Create date object from datetime string and convert it to a local date.
$input = '1970-01-01 00:00:00';
$timezone = 'UTC';
$date = new dateObject($input, $timezone);
$value = $date->format('c');
$expected = '1970-01-01T00:00:00+00:00';
$this->assertEqual($expected, $value, "Test new dateObject('$input', '$timezone'): should be $expected, found $value.");
$expected = 'UTC';
$value = $date->getTimeZone()->getName();
$this->assertEqual($expected, $value, "The current timezone is $value: should be $expected.");
$expected = 0;
$value = $date->getOffset();
$this->assertEqual($expected, $value, "The current offset is $value: should be $expected.");
$timezone = 'America/Los_Angeles';
$date->setTimezone(new DateTimeZone($timezone));
$value = $date->format('c');
$expected = '1969-12-31T16:00:00-08:00';
$this->assertEqual($expected, $value, "Test \$date->setTimezone(new DateTimeZone($timezone)): should be $expected, found $value.");
$expected = 'America/Los_Angeles';
$value = $date->getTimeZone()->getName();
$this->assertEqual($expected, $value, "The current timezone should be $expected, found $value.");
$expected = '-28800';
$value = $date->getOffset();
$this->assertEqual($expected, $value, "The current offset should be $expected, found $value.");
// Convert the local version of a datetime string to UTC.
$input = '1969-12-31 16:00:00';
$timezone = 'America/Los_Angeles';
$date = new dateObject($input, $timezone);
$offset = $date->getOffset();
$value = $date->format('c');
$expected = '1969-12-31T16:00:00-08:00';
$this->assertEqual($expected, $value, "Test new dateObject('$input', '$timezone'): should be $expected, found $value.");
$expected = 'America/Los_Angeles';
$value = $date->getTimeZone()->getName();
$this->assertEqual($expected, $value, "The current timezone should be $expected, found $value.");
$expected = '-28800';
$value = $date->getOffset();
$this->assertEqual($expected, $value, "The current offset should be $expected, found $value.");
$timezone = 'UTC';
$date->setTimezone(new DateTimeZone($timezone));
$value = $date->format('c');
$expected = '1970-01-01T00:00:00+00:00';
$this->assertEqual($expected, $value, "Test \$date->setTimezone(new DateTimeZone($timezone)): should be $expected, found $value.");
$expected = 'UTC';
$value = $date->getTimeZone()->getName();
$this->assertEqual($expected, $value, "The current timezone should be $expected, found $value.");
$expected = '0';
$value = $date->getOffset();
$this->assertEqual($expected, $value, "The current offset should be $expected, found $value.");
// Create year-only date.
$input = '2009';
$timezone = NULL;
$format = 'Y';
$date = new dateObject($input, $timezone, $format);
$value = $date->format('Y');
$expected = '2009';
$this->assertEqual($expected, $value, "Test new dateObject($input, $timezone, $format): should be $expected, found $value.");
// Create month and year-only date.
$input = '2009-10';
$timezone = NULL;
$format = 'Y-m';
$date = new dateObject($input, $timezone, $format);
$value = $date->format('Y-m');
$expected = '2009-10';
$this->assertEqual($expected, $value, "Test new dateObject($input, $timezone, $format): should be $expected, found $value.");
// Create time-only date.
$input = '0000-00-00T10:30:00';
$timezone = NULL;
$format = 'Y-m-d\TH:i:s';
$date = new dateObject($input, $timezone, $format);
$value = $date->format('H:i:s');
$expected = '10:30:00';
$this->assertEqual($expected, $value, "Test new dateObject($input, $timezone, $format): should be $expected, found $value.");
// Create time-only date.
$input = '10:30:00';
$timezone = NULL;
$format = 'H:i:s';
$date = new dateObject($input, $timezone, $format);
$value = $date->format('H:i:s');
$expected = '10:30:00';
$this->assertEqual($expected, $value, "Test new dateObject($input, $timezone, $format): should be $expected, found $value.");
// Test date ranges.
$valid = array(
'-20:+20',
'-1:+0',
'-10:-5',
'2000:2020',
'-10:2010',
'1980:-10',
'1920:+20',
);
$invalid = array(
'abc',
'abc:+20',
'1920:+20a',
'+-20:+-30',
'12:12',
'0:+20',
'-20:0',
);
foreach ($valid as $range) {
$this->assertTrue(date_range_valid($range), "$range recognized as a valid date range.");
}
foreach ($invalid as $range) {
$this->assertFalse(date_range_valid($range), "$range recognized as an invalid date range.");
}
// Test for invalid month names when we are using a short version of the
// month.
$input = '23 abc 2012';
$timezone = NULL;
$format = 'd M Y';
$date = @new dateObject($input, $timezone, $format);
$this->assertNotEqual(count($date->errors), 0, '23 abc 2012 should be an invalid date');
// Test Granularity.
$input = '2005-06-01 10:30:45';
$timezone = NULL;
$format = 'Y-m-d H:i:s';
$date = new dateObject($input, $timezone, $format);
$date->removeGranularity('hour');
$date->removeGranularity('second');
$date->removeGranularity('minute');
$value = $date->format($format);
$expected = '2005-06-01';
$this->assertEqual($expected, $value, "The date with removed granularity should be $expected, found $value.");
$date->addGranularity('hour');
$date->addGranularity('second');
$date->addGranularity('minute');
$value = $date->format($format);
$expected = '2005-06-01 10:30:45';
$this->assertEqual($expected, $value, "The date with added granularity should be $expected, found $value.");
}
/**
* @todo.
*/
public function tearDown() {
variable_del('date_first_day');
variable_del('date_api_use_iso8601');
parent::tearDown();
}
}

View File

@ -0,0 +1,267 @@
<?php
/**
* @file
* Unit tests for Date API functions.
*/
/**
* Unit tests for Date API functions.
*/
class DateApiUnitTestCase extends DrupalUnitTestCase {
/**
* @todo.
*/
public static function getInfo() {
return array(
'name' => t('Date API unit tests'),
'description' => t('Unit test coverage for Date API functions.') ,
'group' => t('Date'),
);
}
/**
* {@inheritdoc}
*/
public function setUp() {
drupal_load('module', 'date_api');
parent::setUp();
}
/**
* Tests for date_is_all_day().
*/
public function testDateIsAllDay() {
// Two empty strings, this should always return FALSE.
$string1 = '';
$string2 = '';
$response = date_is_all_day($string1, $string2);
$this->assertFalse($response, 'Two empty strings cannot represent an all-day date pair.');
// Two random date strings do not make an all-day pair.
$string1 = '2021-02-25 01:01:01';
$string2 = '2021-02-25 23:00:00';
$response = date_is_all_day($string1, $string2);
$this->assertFalse($response, 'Two random date strings do not make an "all day" pair.');
// It is not a valid "all day" date pair to start one second after midnight
// and end on midnight the next day.
$string1 = '2021-02-25 00:00:01';
$string2 = '2021-02-26 00:00:00';
$response = date_is_all_day($string1, $string2);
$this->assertFalse($response, 'Does not mark dates as "all day" if the first is 1 second after midnight on day 1 and the other is midnight on day 2.');
// A valid "all day" date pair when the granularity is 1, regardless of the
// unit.
$string1 = '2021-02-25 00:00:00';
$string2 = '2021-02-25 23:59:59';
// The granularity option must be one of a limited set of strings.
$response = date_is_all_day($string1, $string2, 'mango');
$this->assertFalse($response, 'The granularity argument must be one of a limited set of strings, so "mango" will not work.');
// A valid "all day" date pair starts at midnight and ends one second before
// midnight, both on the same day.
$response = date_is_all_day($string1, $string2);
$this->assertTrue($response, 'An "all day" date pair must start at midnight and end one time unit before midnight of the next day.');
// Confirm the granularity option works with seconds.
$response = date_is_all_day($string1, $string2, 'second');
$this->assertTrue($response, 'End time is 23:59:59 and granularity is set to "second".');
// Confirm the granularity option works with minutes.
$response = date_is_all_day($string1, $string2, 'minute');
$this->assertTrue($response, 'End time is 23:59:59 and granularity is set to "minute".');
// Confirm the granularity option works with hours.
$response = date_is_all_day($string1, $string2, 'hour');
$this->assertTrue($response, 'End time is 23:59:59 and granularity is set to "hour".');
// Test the "increment" argument with a unit of 1.
$response = date_is_all_day($string1, $string2, 'second', 1);
$this->assertTrue($response, 'End time is 23:59:59 and granularity is set to 1 "second".');
$response = date_is_all_day($string1, $string2, 'minute', 1);
$this->assertTrue($response, 'End time is 23:59:59 and granularity is set to 1 "minute".');
$response = date_is_all_day($string1, $string2, 'hour', 1);
$this->assertTrue($response, 'End time is 23:59:59 and granularity is set to 1 "hour".');
// Test the "increment" argument with a unit of 15.
$response = date_is_all_day($string1, $string2, 'second', 15);
$this->assertTrue($response, 'End time is 23:59:59 and granularity is set to 15 "second".');
$response = date_is_all_day($string1, $string2, 'minute', 15);
$this->assertTrue($response, 'End time is 23:59:59 and granularity is set to 15 "minute".');
$response = date_is_all_day($string1, $string2, 'hour', 15);
$this->assertTrue($response, 'End time is 23:59:59 and granularity is set to 15 "hour".');
// A datetime pair ending at 23:59:00.
$string1 = '2021-02-25 00:00:00';
$string2 = '2021-02-25 23:59:00';
// This pair will only be valid when the granularity is "minute" or "hour".
$response = date_is_all_day($string1, $string2, 'second');
$this->assertFalse($response, 'Not valid with an end time of 23:59:00 and granularity is set to "second".');
$response = date_is_all_day($string1, $string2, 'minute');
$this->assertTrue($response, '23:59:00 is a valid end time if the granularity is set to "minute".');
$response = date_is_all_day($string1, $string2, 'minute');
$this->assertTrue($response, '23:59:00 is a valid end time if the granularity is set to "hour".');
// A datetime pair ending at 23:59:24. This is to confirm that segments of
// the time, after the granularity option, are ignored.
$string1 = '2021-02-25 00:00:00';
$string2 = '2021-02-25 23:59:24';
// This pair will only be valid when the granularity is "minute" or "hour".
$response = date_is_all_day($string1, $string2, 'second');
$this->assertFalse($response, 'Not valid with an end time of 23:59:24 and granularity is set to "second".');
$response = date_is_all_day($string1, $string2, 'minute');
$this->assertTrue($response, '23:59:24 is a valid end time if the granularity is set to "minute".');
$response = date_is_all_day($string1, $string2, 'minute');
$this->assertTrue($response, '23:59:24 is a valid end time if the granularity is set to "hour".');
// A datetime pair ending at 23:00:00.
$string1 = '2021-02-25 00:00:00';
$string2 = '2021-02-25 23:00:00';
// This pair will only be valid when the granularity is "minute" or "hour".
$response = date_is_all_day($string1, $string2, 'second');
$this->assertFalse($response, 'Not valid with an end time of 23:00:00 and granularity is set to "second".');
$response = date_is_all_day($string1, $string2, 'minute');
$this->assertFalse($response, 'Not valid with an end time of 23:00:00 and granularity is set to "minute".');
$response = date_is_all_day($string1, $string2, 'hour');
$this->assertTrue($response, '23:00:00 is a valid end time if the granularity is set to "hour".');
// A datetime pair ending at 23:13:00.
$string1 = '2021-02-25 00:00:00';
$string2 = '2021-02-25 23:13:00';
// This pair will only be valid when the granularity is "minute" or "hour".
$response = date_is_all_day($string1, $string2, 'second');
$this->assertFalse($response, 'Not valid with an end time of 23:13:00 and granularity is set to "second".');
$response = date_is_all_day($string1, $string2, 'minute');
$this->assertFalse($response, 'Not valid with an end time of 23:13:00 and granularity is set to "minute".');
$response = date_is_all_day($string1, $string2, 'hour');
$this->assertTrue($response, '23:00:00 is a valid end time if the granularity is set to "hour".');
// A datetime pair ending at 23:59:55.
$string1 = '2021-02-25 00:00:00';
$string2 = '2021-02-25 23:59:55';
// Test the "increment" argument with 5 seconds.
$response = date_is_all_day($string1, $string2, 'second');
$this->assertFalse($response, '23:59:55 is not a valid end time when the increment is set to 1 second.');
$response = date_is_all_day($string1, $string2, 'second', 5);
$this->assertTrue($response, '23:59:55 is a valid end time when the increment is set to 5 seconds.');
$response = date_is_all_day($string1, $string2, 'minute', 5);
$this->assertTrue($response, '23:59:55 is a valid end time when the increment is set to 5 minutes.');
$response = date_is_all_day($string1, $string2, 'hour', 5);
$this->assertTrue($response, '23:59:55 is a valid end time when the increment is set to 5 hours.');
// A datetime pair ending at 23:55:00.
$string1 = '2021-02-25 00:00:00';
$string2 = '2021-02-25 23:55:00';
// Test the "increment" argument with 5 seconds.
$response = date_is_all_day($string1, $string2, 'second');
$this->assertFalse($response, '23:55:00 is not a valid end time when the increment is set to 1 second.');
$response = date_is_all_day($string1, $string2, 'second', 5);
$this->assertFalse($response, '23:55:00 is not a valid end time when the increment is set to 5 seconds.');
$response = date_is_all_day($string1, $string2, 'minute');
$this->assertFalse($response, '23:55:00 is not a valid end time when the increment is set to 1 minute.');
$response = date_is_all_day($string1, $string2, 'minute', 5);
$this->assertTrue($response, '23:55:00 is a valid end time when the increment is set to 5 minutes.');
$response = date_is_all_day($string1, $string2, 'hour', 5);
$this->assertTrue($response, '23:55:00 is a valid end time when the increment is set to 5 hours.');
// A datetime pair ending at 23:59:50.
$string1 = '2021-02-25 00:00:00';
$string2 = '2021-02-25 23:59:50';
// Test the "increment" argument with 5 seconds.
$response = date_is_all_day($string1, $string2, 'second');
$this->assertFalse($response, '23:59:50 is not a valid end time when the increment is set to 1 second.');
$response = date_is_all_day($string1, $string2, 'second', 10);
$this->assertTrue($response, '23:59:50 is a valid end time when the increment is set to 10 seconds.');
$response = date_is_all_day($string1, $string2, 'minute', 10);
$this->assertTrue($response, '23:59:50 is a valid end time when the increment is set to 10 minutes.');
$response = date_is_all_day($string1, $string2, 'hour', 10);
$this->assertTrue($response, '23:59:50 is a valid end time when the increment is set to 10 hours.');
// A datetime pair ending at 23:50:00.
$string1 = '2021-02-25 00:00:00';
$string2 = '2021-02-25 23:50:00';
// Test the "increment" argument with 5 seconds.
$response = date_is_all_day($string1, $string2, 'second');
$this->assertFalse($response, '23:50:00 is not a valid end time when the increment is set to 1 second.');
$response = date_is_all_day($string1, $string2, 'second', 10);
$this->assertFalse($response, '23:50:00 is not a valid end time when the increment is set to 10 seconds.');
$response = date_is_all_day($string1, $string2, 'minute');
$this->assertFalse($response, '23:50:00 is not a valid end time when the increment is set to 1 minute.');
$response = date_is_all_day($string1, $string2, 'minute', 10);
$this->assertTrue($response, '23:50:00 is a valid end time when the increment is set to 10 minutes.');
$response = date_is_all_day($string1, $string2, 'hour', 10);
$this->assertTrue($response, '23:50:00 is a valid end time when the increment is set to 10 hours.');
// A datetime pair ending at 23:59:45.
$string1 = '2021-02-25 00:00:00';
$string2 = '2021-02-25 23:59:45';
// Test the "increment" argument with 5 seconds.
$response = date_is_all_day($string1, $string2, 'second');
$this->assertFalse($response, '23:59:45 is not a valid end time when the increment is set to 1 second.');
$response = date_is_all_day($string1, $string2, 'second', 15);
$this->assertTrue($response, '23:59:45 is a valid end time when the increment is set to 15 seconds.');
$response = date_is_all_day($string1, $string2, 'minute', 15);
$this->assertTrue($response, '23:59:45 is a valid end time when the increment is set to 15 minutes.');
$response = date_is_all_day($string1, $string2, 'hour', 15);
$this->assertTrue($response, '23:59:45 is a valid end time when the increment is set to 15 hours.');
// A datetime pair ending at 23:45:00.
$string1 = '2021-02-25 00:00:00';
$string2 = '2021-02-25 23:45:00';
// Test the "increment" argument with 5 seconds.
$response = date_is_all_day($string1, $string2, 'second');
$this->assertFalse($response, '23:45:00 is not a valid end time when the increment is set to 1 second.');
$response = date_is_all_day($string1, $string2, 'second', 15);
$this->assertFalse($response, '23:45:00 is not a valid end time when the increment is set to 15 seconds.');
$response = date_is_all_day($string1, $string2, 'minute');
$this->assertFalse($response, '23:45:00 is not a valid end time when the increment is set to 1 minute.');
$response = date_is_all_day($string1, $string2, 'minute', 15);
$this->assertTrue($response, '23:45:00 is a valid end time when the increment is set to 15 minutes.');
$response = date_is_all_day($string1, $string2, 'hour', 15);
$this->assertTrue($response, '23:45:00 is a valid end time when the increment is set to 15 hours.');
// A datetime pair ending at 23:59:30.
$string1 = '2021-02-25 00:00:00';
$string2 = '2021-02-25 23:59:30';
// Test the "increment" argument with 5 seconds.
$response = date_is_all_day($string1, $string2, 'second');
$this->assertFalse($response, '23:59:30 is not a valid end time when the increment is set to 1 second.');
$response = date_is_all_day($string1, $string2, 'second', 30);
$this->assertTrue($response, '23:59:30 is a valid end time when the increment is set to 30 seconds.');
$response = date_is_all_day($string1, $string2, 'minute', 30);
$this->assertTrue($response, '23:59:30 is a valid end time when the increment is set to 30 minutes.');
$response = date_is_all_day($string1, $string2, 'hour', 30);
$this->assertTrue($response, '23:59:30 is a valid end time when the increment is set to 30 hours.');
// A datetime pair ending at 23:30:00.
$string1 = '2021-02-25 00:00:00';
$string2 = '2021-02-25 23:30:00';
// Test the "increment" argument with 5 seconds.
$response = date_is_all_day($string1, $string2, 'second');
$this->assertFalse($response, '23:30:00 is not a valid end time when the increment is set to 1 second.');
$response = date_is_all_day($string1, $string2, 'second', 30);
$this->assertFalse($response, '23:30:00 is not a valid end time when the increment is set to 30 seconds.');
$response = date_is_all_day($string1, $string2, 'minute');
$this->assertFalse($response, '23:30:00 is not a valid end time when the increment is set to 1 minute.');
$response = date_is_all_day($string1, $string2, 'minute', 30);
$this->assertTrue($response, '23:30:00 is a valid end time when the increment is set to 30 minutes.');
$response = date_is_all_day($string1, $string2, 'hour', 30);
$this->assertTrue($response, '23:30:00 is a valid end time when the increment is set to 30 hours.');
}
}

View File

@ -168,7 +168,6 @@ function theme_date_part_label_time($variables) {
return t('Time', array(), array('context' => 'datetime'));
}
/**
* Returns HTML for a date block that looks like a mini calendar day.
*
@ -194,7 +193,7 @@ function theme_date_calendar_day($variables) {
function theme_date_time_ago($variables) {
$start_date = $variables['start_date'];
$end_date = $variables['end_date'];
$use_end_date = isset($variables['use_end_date']) ? $variables['use_end_date'] : false;
$use_end_date = isset($variables['use_end_date']) ? $variables['use_end_date'] : FALSE;
$interval = !empty($variables['interval']) ? $variables['interval'] : 2;
$display = isset($variables['interval_display']) ? $variables['interval_display'] : 'time ago';
@ -204,7 +203,7 @@ function theme_date_time_ago($variables) {
}
// We use the end date only when the option is checked.
if ($use_end_date){
if ($use_end_date) {
$date = date_format($end_date, DATE_FORMAT_UNIX);
}
else {
@ -212,10 +211,10 @@ function theme_date_time_ago($variables) {
}
// Time to compare dates to.
$now = date_format(date_now(), DATE_FORMAT_UNIX);
// Will be positive for a datetime in the past (ago), and negative for a datetime in the future (hence).
// Will be positive for a datetime in the past (ago), and negative for a
// datetime in the future (hence).
$time_diff = $now - $date;
// Uses the same options used by Views format_interval.
@ -240,6 +239,5 @@ function theme_date_time_ago($variables) {
case 'time span':
return t(($time_diff < 0 ? '%time hence' : '%time ago'), array('%time' => format_interval(abs($time_diff), $interval)));
}
}

View File

@ -3,14 +3,13 @@ description = Adds an option to the Context module to set a context condition ba
package = Date/Time
core = 7.x
dependencies[] = date
dependencies[] = context
dependencies[] = date:date
dependencies[] = context:context
files[] = date_context.module
files[] = plugins/date_context_date_condition.inc
; Information added by Drupal.org packaging script on 2017-04-07
version = "7.x-2.10"
; Information added by Drupal.org packaging script on 2021-03-05
version = "7.x-2.11"
core = "7.x"
project = "date"
datestamp = "1491562090"
datestamp = "1614952728"

View File

@ -1,14 +1,13 @@
<?php
/**
* @file
* Add an option to set/not set the context on forms vs views.
*
* @TODO
*
* Currently only implemented for nodes. Need to add $plugin->execute()
* @todo Currently only implemented for nodes. Need to add $plugin->execute()
* for any other entities that might be supported.
*
* Cache the date processing, perhaps cache the formatted, timezone-adjusted
* @todo Cache the date processing, perhaps cache the formatted, tz-adjusted
* date strings for each entity (would have to be cached differently for each
* timezone, based on the tz_handling method for the date).
*/

View File

@ -2,13 +2,12 @@
/**
* @file
* Context date condition plugin.
* Context condition plugin for date values.
*/
/**
* Expose term views/term forms by vocabulary as a context condition.
* Context condition plugin for date values.
*/
// @codingStandardsIgnoreStart
class date_context_date_condition extends context_condition_node {
/**
@ -71,7 +70,6 @@ class date_context_date_condition extends context_condition_node {
$fields = $this->fetch_from_context($context, 'values');
foreach ($fields as $field_name => $label) {
// If this field does not exist on this entity, just move along.
if (empty($entity->{$field_name})) {
continue;
@ -79,19 +77,22 @@ class date_context_date_condition extends context_condition_node {
$items = field_get_items('node', $entity, $field_name);
// If there are no values, nothing to do unless we were looking for 'empty' or '!='.
// If there are no values, nothing to do unless we were looking for
// 'empty' or '!='.
if (empty($items)) {
if ($options['operation'] == '!=' || $options['operation'] == 'empty') {
$this->condition_met($context, $field_name);
}
}
// We don't have to construct the date values if we're just testing for 'not empty'.
// We don't have to construct the date values if we're just testing
// for 'not empty'.
elseif ($options['operation'] == 'not empty') {
$this->condition_met($context, $field_name);
}
// All other operations need to retrieve the date values for comparision.
// All other operations need to retrieve the date values for
// comparision.
else {
$field = field_info_field($field_name);
$timezone_db = date_get_timezone_db($field['settings']['tz_handling']);
@ -152,5 +153,5 @@ class date_context_date_condition extends context_condition_node {
}
}
}
}
// @codingStandardsIgnoreEnd

View File

@ -1,4 +1,5 @@
<?php
/**
* @file
* Date forms and form themes and validation.
@ -42,38 +43,46 @@
* the local timezone, are converted back to their UTC values and stored.
*/
function date_field_widget_form(&$form, &$form_state, $field, $instance, $langcode, $items, $delta, $base) {
$element = $base;
$field_name = $field['field_name'];
$entity_type = $instance['entity_type'];
// If this is a new entity, populate the field with the right default values.
// This happens early so even fields later hidden with #access get those values.
// We should only add default values to new entities, to avoid over-writing
// a value that has already been set. This means we can't just check to see
// if $items is empty, because it might have been set that way on purpose.
// @see date_field_widget_properties_alter() where we flagged if this is a new entity.
// We check !isset($items[$delta]['value']) because entity translation may create
// a new translation entity for an existing entity and we don't want to clobber
// values that were already set in that case.
// This happens early so even fields later hidden with #access get those
// values. We should only add default values to new entities, to avoid
// over-writing a value that has already been set. This means we can't just
// check to see if $items is empty, because it might have been set that way
// on purpose. In date_field_widget_properties_alter() this is flagged if
// this is a new entity. Check !isset($items[$delta]['value']) because entity
// translation may create a new translation entity for an existing entity and
// we don't want to clobber values that were already set in that case.
// @see date_field_widget_properties_alter()
// @see http://drupal.org/node/1478848.
$is_default = FALSE;
if (!empty($instance['widget']['is_new']) && !isset($items[$delta]['value'])) {
$items = date_default_value($field, $instance, $langcode);
$is_default = TRUE;
if (!isset($items[$delta]['value'])) {
if (!empty($instance['widget']['is_new'])) {
// New entity; use default values defined on instance.
$items = date_default_value($field, $instance, $langcode);
$is_default = TRUE;
}
else {
// Date is empty; create array structure for value keys.
$keys = date_process_values($field);
$items[$delta] = array_fill_keys($keys, '');
}
}
// @TODO Repeating dates should probably be made into their own field type and completely separated out.
// That will have to wait for a new branch since it may break other things, including other modules
// that have an expectation of what the date field types are.
// Since repeating dates cannot use the default Add more button, we have to handle our own behaviors here.
// Return only the first multiple value for repeating dates, then clean up the 'Add more' bits in #after_build.
// The repeating values will be re-generated when the repeat widget form is validated.
// At this point we can't tell if this form element is going to be hidden by #access, and we're going to
// lose all but the first value by doing this, so store the original values in case we need to replace them later.
// @todo Repeating dates should probably be made into their own field type
// and completely separated out. That will have to wait for a new branch
// since it may break other things, including other modules that have an
// expectation of what the date field types are. Since repeating dates cannot
// use the default Add more button, we have to handle our own behaviors here.
// Return only the first multiple value for repeating dates, then clean up
// the 'Add more' bits in #after_build. The repeating values will be
// re-generated when the repeat widget form is validated. At this point we
// can't tell if this form element is going to be hidden by #access, and
// we're going to lose all but the first value by doing this, so store the
// original values in case we need to replace them later.
if (!empty($field['settings']['repeat']) && module_exists('date_repeat_field')) {
if ($delta == 0) {
$form['#after_build'][] = 'date_repeat_after_build';
@ -101,13 +110,13 @@ function date_field_widget_form(&$form, &$form_state, $field, $instance, $langco
'#type' => 'date_combo',
'#theme_wrappers' => array('date_combo'),
'#weight' => $delta,
'#default_value' => isset($items[$delta]) ? $items[$delta] : '',
'#default_value' => isset($items[$delta]) ? $items[$delta] : array(),
'#date_timezone' => $timezone,
'#element_validate' => array('date_combo_validate'),
'#date_is_default' => $is_default,
// Store the original values, for use with disabled and hidden fields.
'#date_items' => isset($items[$delta]) ? $items[$delta] : '',
'#date_items' => isset($items[$delta]) ? $items[$delta] : array(),
);
$element['#title'] = $instance['label'];
@ -126,8 +135,8 @@ function date_field_widget_form(&$form, &$form_state, $field, $instance, $langco
// Make changes if instance is set to be rendered as a regular field.
if (!empty($instance['widget']['settings']['no_fieldset'])) {
$element['#title'] = check_plain($instance['label']);
$element['#theme_wrappers'] = ($field['cardinality'] == 1) ? array('date_form_element') : array();
$element['#title'] = check_plain($instance['label']);
$element['#theme_wrappers'] = ($field['cardinality'] == 1) ? array('date_form_element') : array();
}
return $element;
@ -136,12 +145,11 @@ function date_field_widget_form(&$form, &$form_state, $field, $instance, $langco
/**
* Create local date object.
*
* Create a date object set to local time from the field and
* widget settings and item values. Default values for new entities
* are set by the default value callback, so don't need to be accounted for here.
* Create a date object set to local time from the field and widget settings and
* item values. Default values for new entities are set by the default value
* callback, so don't need to be accounted for here.
*/
function date_local_date($item, $timezone, $field, $instance, $part = 'value') {
$value = $item[$part];
// If the value is empty, don't try to create a date object because it will
@ -150,7 +158,7 @@ function date_local_date($item, $timezone, $field, $instance, $part = 'value') {
return NULL;
}
// @TODO Figure out how to replace date_fuzzy_datetime() function.
// @todo Figure out how to replace date_fuzzy_datetime() function.
// Special case for ISO dates to create a valid date object for formatting.
// Is this still needed?
// @codingStandardsIgnoreStart
@ -200,7 +208,7 @@ function date_default_value($field, $instance, $langcode) {
}
/**
* Helper function for the date default value callback to set either 'value' or 'value2' to its default value.
* Default value callback to set either 'value', 'value2' to its default value.
*/
function date_default_value_part($item, $field, $instance, $langcode, $part = 'value') {
$timezone = date_get_timezone($field['settings']['tz_handling']);
@ -225,12 +233,13 @@ function date_default_value_part($item, $field, $instance, $langcode, $part = 'v
return NULL;
}
else {
// The date stored in 'value' has already been switched to the db timezone.
// The date stored in 'value' has already been switched to the db
// timezone.
$date = new DateObject($item[0]['value'], $timezone_db, DATE_FORMAT_DATETIME);
}
}
// Special case for 'now' when using dates with no timezone,
// make sure 'now' isn't adjusted to UTC value of 'now' .
// Special case for 'now' when using dates with no timezone, make sure 'now'
// isn't adjusted to UTC value of 'now'.
elseif ($field['settings']['tz_handling'] == 'none') {
$date = date_now();
}
@ -266,7 +275,8 @@ function date_combo_element_process($element, &$form_state, $form) {
$field = field_widget_field($element, $form_state);
$instance = field_widget_instance($element, $form_state);
// Figure out how many items are in the form, including new ones added by ajax.
// Figure out how many items are in the form, including new ones added by
// ajax.
$field_state = field_form_get_state($element['#field_parents'], $field_name, $element['#language'], $form_state);
$items_count = $field_state['items_count'];
@ -280,18 +290,19 @@ function date_combo_element_process($element, &$form_state, $form) {
$offset_field = 'offset';
$offset_field2 = 'offset2';
// Convert UTC dates to their local values in DATETIME format,
// and adjust the default values as specified in the field settings.
// It would seem to make sense to do this conversion when the data
// is loaded instead of when the form is created, but the loaded
// field data is cached and we can't cache dates that have been converted
// to the timezone of an individual user, so we cache the UTC values
// instead and do our conversion to local dates in the form and
// in the formatters.
// Convert UTC dates to their local values in DATETIME format, and adjust the
// default values as specified in the field settings. It would seem to make
// sense to do this conversion when the data is loaded instead of when the
// form is created, but the loaded field data is cached and we can't cache
// dates that have been converted to the timezone of an individual user, so
// we cache the UTC values instead and do our conversion to local dates in
// the form and in the formatters.
$process = date_process_values($field, $instance);
foreach ($process as $processed) {
if (!isset($element['#default_value'][$processed])) {
if (empty($element['#default_value']) || !is_array($element['#default_value'])) {
$element['#default_value'] = array();
}
$element['#default_value'][$processed] = '';
}
$date = date_local_date($element['#default_value'], $element['#date_timezone'], $field, $instance, $processed);
@ -299,11 +310,12 @@ function date_combo_element_process($element, &$form_state, $form) {
}
// Blank out the end date for optional end dates that match the start date,
// except when this is a new node that has default values that should be honored.
// except when this is a new node that has default values that should be
// honored.
if (!$date_is_default && $field['settings']['todate'] != 'required'
&& is_array($element['#default_value'])
&& !empty($element['#default_value'][$to_field])
&& $element['#default_value'][$to_field] == $element['#default_value'][$from_field]) {
&& is_array($element['#default_value'])
&& !empty($element['#default_value'][$to_field])
&& $element['#default_value'][$to_field] == $element['#default_value'][$from_field]) {
unset($element['#default_value'][$to_field]);
}
@ -343,8 +355,8 @@ function date_combo_element_process($element, &$form_state, $form) {
$element['#description'] = $element['value']['#instance']['description'];
}
// Give this element the right type, using a Date API
// or a Date Popup element type.
// Give this element the right type, using a Date API or a Date Popup element
// type.
$element[$from_field]['#attributes'] = array('class' => array('date-clear'));
$element[$from_field]['#wrapper_attributes'] = array('class' => array());
$element[$from_field]['#wrapper_attributes']['class'][] = 'date-no-float';
@ -360,6 +372,9 @@ function date_combo_element_process($element, &$form_state, $form) {
case 'date_popup':
$element[$from_field]['#type'] = 'date_popup';
$element[$from_field]['#theme_wrappers'] = array('date_popup');
// Disable autocomplete in browsers.
// @see https://developer.mozilla.org/en-US/docs/Web/Security/Securing_your_site/Turning_off_form_autocompletion
$element[$from_field]['#attributes']['autocomplete'] = 'off';
$element[$from_field]['#ajax'] = !empty($element['#ajax']) ? $element['#ajax'] : FALSE;
break;
@ -367,13 +382,10 @@ function date_combo_element_process($element, &$form_state, $form) {
$element[$from_field]['#type'] = 'date_text';
$element[$from_field]['#theme_wrappers'] = array('date_text');
$element[$from_field]['#ajax'] = !empty($element['#ajax']) ? $element['#ajax'] : FALSE;
break;
}
// If this field uses the 'End', add matching element
// for the 'End' date, and adapt titles to make it clear which
// is the 'Start' and which is the 'End' .
// If this field uses the 'End', add matching element for the 'End' date, and
// adapt titles to make it clear which is the 'Start' and which is the 'End' .
if (!empty($field['settings']['todate'])) {
$element[$to_field] = $element[$from_field];
$element[$from_field]['#title_display'] = 'none';
@ -423,9 +435,9 @@ function date_combo_element_process($element, &$form_state, $form) {
}
$context = array(
'field' => $field,
'instance' => $instance,
'form' => $form,
'field' => $field,
'instance' => $instance,
'form' => $form,
);
drupal_alter('date_combo_process', $element, $form_state, $context);
@ -438,8 +450,8 @@ function date_combo_element_process($element, &$form_state, $form) {
function date_element_empty($element, &$form_state) {
$item = array();
$item['value'] = NULL;
$item['value2'] = NULL;
$item['timezone'] = NULL;
$item['value2'] = NULL;
$item['timezone'] = NULL;
$item['offset'] = NULL;
$item['offset2'] = NULL;
$item['rrule'] = NULL;
@ -453,10 +465,9 @@ function date_element_empty($element, &$form_state) {
* Don't try this if there were errors before reaching this point.
*/
function date_combo_validate($element, &$form_state) {
// Disabled and hidden elements won't have any input and don't need validation,
// we just need to re-save the original values, from before they were processed into
// widget arrays and timezone-adjusted.
// Disabled and hidden elements won't have any input and don't need
// validation, we just need to re-save the original values, from before they
// were processed into widget arrays and timezone-adjusted.
if (date_hidden_element($element) || !empty($element['#disabled'])) {
form_set_value($element, $element['#date_items'], $form_state);
return;
@ -504,8 +515,8 @@ function date_combo_validate($element, &$form_state) {
$offset_field = 'offset';
$offset_field2 = 'offset2';
// Check for empty 'Start date', which could either be an empty
// value or an array of empty values, depending on the widget.
// Check for empty 'Start date', which could either be an empty value or an
// array of empty values, depending on the widget.
$empty = TRUE;
if (!empty($item[$from_field])) {
if (!is_array($item[$from_field])) {
@ -567,7 +578,6 @@ function date_combo_validate($element, &$form_state) {
// Neither the start date nor the end date should be empty at this point
// unless they held values that couldn't be evaluated.
if (!$instance['required'] && (!date_is_date($from_date) || !date_is_date($to_date))) {
$item = date_element_empty($element, $form_state);
$errors[] = t('The dates are invalid.');
@ -577,8 +587,8 @@ function date_combo_validate($element, &$form_state) {
$errors[] = t('The End date must be greater than the Start date.');
}
else {
// Convert input dates back to their UTC values and re-format to ISO
// or UNIX instead of the DATETIME format used in element processing.
// Convert input dates back to their UTC values and re-format to ISO or
// UNIX instead of the DATETIME format used in element processing.
$item[$tz_field] = $timezone;
// Update the context for changes in the $item, and allow other modules to
@ -587,7 +597,11 @@ function date_combo_validate($element, &$form_state) {
// We can only pass two additional values to drupal_alter, so $element
// needs to be included in $context.
$context['element'] = $element;
// Trigger hook_date_combo_validate_date_start_alter().
drupal_alter('date_combo_validate_date_start', $from_date, $form_state, $context);
// Trigger hook_date_combo_validate_date_end_alter().
drupal_alter('date_combo_validate_date_end', $to_date, $form_state, $context);
$item[$offset_field] = date_offset_get($from_date);
@ -604,11 +618,10 @@ function date_combo_validate($element, &$form_state) {
$item['rrule'] = $form_values[$field['field_name']]['rrule'];
}
// If the db timezone is not the same as the display timezone
// and we are using a date with time granularity,
// test a roundtrip back to the original timezone to catch
// invalid dates, like 2AM on the day that spring daylight savings
// time begins in the US.
// If the db timezone is not the same as the display timezone and we are
// using a date with time granularity, test a roundtrip back to the
// original timezone to catch invalid dates, like 2AM on the day that
// spring daylight savings time begins in the US.
$granularity = date_format_order($element[$from_field]['#date_format']);
if ($timezone != $timezone_db && date_has_time($granularity)) {
date_timezone_set($from_date, timezone_open($timezone));
@ -626,9 +639,9 @@ function date_combo_validate($element, &$form_state) {
}
}
}
// Don't show further errors if errors are already flagged
// because otherwise we'll show errors on the nested elements
// more than once.
// Don't show further errors if errors are already flagged because otherwise
// we'll show errors on the nested elements more than once.
if (!form_get_errors() && !empty($errors)) {
if ($field['cardinality']) {
form_error($element, t('There are errors in @field_name value #@delta:', array('@field_name' => $instance['label'], '@delta' => $delta + 1)) . theme('item_list', array('items' => $errors)));
@ -652,7 +665,6 @@ function date_input_format($element, $field, $instance) {
return variable_get('date_format_short', 'm/d/Y - H:i');
}
/**
* Implements hook_date_select_pre_validate_alter().
*/
@ -678,8 +690,9 @@ function date_date_popup_pre_validate_alter(&$element, &$form_state, &$input) {
* Helper function to clear out end date when not being used.
*/
function date_empty_end_date(&$element, &$form_state, &$input) {
// If this is the end date and the option to show an end date has not been selected,
// empty the end date to surpress validation errors and stop further processing.
// If this is the end date and the option to show an end date has not been
// selected, empty the end date to surpress validation errors and stop
// further processing.
$parents = $element['#parents'];
$parent = array_pop($parents);
if ($parent == 'value2') {

View File

@ -4,9 +4,8 @@ core = 7.x
package = Date/Time
hidden = TRUE
; Information added by Drupal.org packaging script on 2017-04-07
version = "7.x-2.10"
; Information added by Drupal.org packaging script on 2021-03-05
version = "7.x-2.11"
core = "7.x"
project = "date"
datestamp = "1491562090"
datestamp = "1614952728"

View File

@ -36,6 +36,10 @@ jquery.timepicker-1.1.2.js and jquery.timepicker-1.1.2.css. Rename them to
jquery.timepicker.js and jquery.timepicker.css and copy them into
'sites/all/libraries/wvega-timepicker'.
The date_popup_timepicker module is also supported:
* https://www.drupal.org/project/date_popup_timepicker
================================================================================
Usage
================================================================================

View File

@ -1,15 +1,20 @@
name = Date Popup
description = Enables jquery popup calendars and time entry widgets for selecting dates and times.
dependencies[] = date_api
package = Date/Time
core = 7.x
configure = admin/config/date/date_popup
stylesheets[all][] = themes/datepicker.1.7.css
; Only the Date API module is required.
dependencies[] = date:date_api
; Information added by Drupal.org packaging script on 2017-04-07
version = "7.x-2.10"
; Test coverage.
; @todo Replicate DateUiTestCase::testFieldUI().
files[] = tests/DatePopupFieldTestCase.test
files[] = tests/DatePopupValidationTestCase.test
files[] = tests/DatePopupWithViewsTestCase.test
; Information added by Drupal.org packaging script on 2021-03-05
version = "7.x-2.11"
core = "7.x"
project = "date"
datestamp = "1491562090"
datestamp = "1614952728"

View File

@ -1,6 +1,8 @@
/**
* Attaches the calendar behavior to all required fields
* @file
* Attaches the calendar behavior to all required fields.
*/
(function($) {
function makeFocusHandler(e) {
if (!$(this).hasClass('date-popup-init')) {
@ -10,10 +12,8 @@
case 'datepicker':
$(this)
.datepicker(datePopup.settings)
.addClass('date-popup-init');
$(this).click(function(){
$(this).focus();
});
.addClass('date-popup-init')
.focus();
if (datePopup.settings.syncEndDate) {
$('.start-date-wrapper').each(function(){
var start_date_wrapper = this;
@ -27,10 +27,8 @@
case 'timeEntry':
$(this)
.timeEntry(datePopup.settings)
.addClass('date-popup-init');
$(this).click(function(){
$(this).focus();
});
.addClass('date-popup-init')
.focus();
break;
case 'timepicker':
@ -54,10 +52,8 @@
datePopup.settings.startTime = new Date(datePopup.settings.startTime);
$(this)
.timepicker(datePopup.settings)
.addClass('date-popup-init');
$(this).click(function(){
$(this).focus();
});
.addClass('date-popup-init')
.focus();
break;
}
}
@ -66,8 +62,9 @@
Drupal.behaviors.date_popup = {
attach: function (context) {
for (var id in Drupal.settings.datePopup) {
$('#'+ id).bind('focus', Drupal.settings.datePopup[id], makeFocusHandler);
$('#' + id).bind('focus', Drupal.settings.datePopup[id], makeFocusHandler);
}
}
};
})(jQuery);

View File

@ -1,4 +1,5 @@
<?php
/**
* @file
* A module to enable jquery calendar and time entry popups.
@ -49,8 +50,10 @@ function date_popup_add() {
function date_popup_get_wvega_path() {
$path = FALSE;
if (function_exists('libraries_get_path')) {
// Try loading the wvega timepicker library.
$path = libraries_get_path('wvega-timepicker');
if (!file_exists($path)) {
// Check if the library actually exists.
if (empty($path) || !file_exists($path)) {
$path = FALSE;
}
}
@ -107,12 +110,10 @@ function date_popup_library() {
*
* @param string $id
* The CSS class prefix to search the DOM for.
* TODO : unused ?
*
* @todo unused ?
* @param string $func
* The jQuery function to invoke on each DOM element
* containing the returned CSS class.
*
* @param array $settings
* The settings array to pass to the jQuery function.
*
@ -127,9 +128,9 @@ function date_popup_js_settings_id($id, $func, $settings) {
// Make sure popup date selector grid is in correct year.
if (!empty($settings['yearRange'])) {
$parts = explode(':', $settings['yearRange']);
// Set the default date to 0 or the lowest bound if
// the date ranges do not include the current year.
// Necessary for the datepicker to render and select dates correctly.
// Set the default date to 0 or the lowest bound if the date ranges do not
// include the current year. Necessary for the datepicker to render and
// select dates correctly.
$default_date = ($parts[0] > 0 || 0 > $parts[1]) ? $parts[0] : 0;
$settings += array('defaultDate' => (string) $default_date . 'y');
}
@ -145,9 +146,8 @@ function date_popup_js_settings_id($id, $func, $settings) {
$id_count[$id] = 0;
}
// It looks like we need the additional id_count for this to
// work correctly when there are multiple values.
// $return_id = "$id-$func-popup";
// It looks like we need the additional id_count for this to work correctly
// when there are multiple values.
$return_id = "$id-$func-popup-" . $id_count[$id]++;
$js_settings['datePopup'][$return_id] = array(
'func' => $func,
@ -170,44 +170,44 @@ function date_popup_theme() {
/**
* Implements hook_element_info().
*
* Set the #type to date_popup and fill the element #default_value with
* a date adjusted to the proper local timezone in datetime format
* (YYYY-MM-DD HH:MM:SS).
*
* The element will create two textfields, one for the date and one for the
* time. The date textfield will include a jQuery popup calendar date picker,
* and the time textfield uses a jQuery timepicker.
*
* NOTE - Converting a date stored in the database from UTC to the local zone
* and converting it back to UTC before storing it is not handled by this
* element and must be done in pre-form and post-form processing!!
*
* #date_timezone
* The local timezone to be used to create this date.
*
* #date_format
* Unlike earlier versions of this popup, most formats will work.
*
* #date_increment
* Increment minutes and seconds by this amount, default is 1.
*
* #date_year_range
* The number of years to go back and forward in a year selector,
* default is -3:+3 (3 back and 3 forward).
*
* #datepicker_options
* An associative array representing the jQuery datepicker options you want
* to set for this element. Use the jQuery datepicker option names as keys.
* Hard coded defaults are:
* - changeMonth => TRUE
* - changeYear => TRUE
* - autoPopUp => 'focus'
* - closeAtTop => FALSE
* - speed => 'immediate'
*/
function date_popup_element_info() {
$timepicker = date_popup_get_preferred_timepicker();
// Set the #type to date_popup and fill the element #default_value with
// a date adjusted to the proper local timezone in datetime format
// (YYYY-MM-DD HH:MM:SS).
//
// The element will create two textfields, one for the date and one for the
// time. The date textfield will include a jQuery popup calendar date picker,
// and the time textfield uses a jQuery timepicker.
//
// NOTE - Converting a date stored in the database from UTC to the local zone
// and converting it back to UTC before storing it is not handled by this
// element and must be done in pre-form and post-form processing!!
//
// #date_timezone
// The local timezone to be used to create this date.
//
// #date_format
// Unlike earlier versions of this popup, most formats will work.
//
// #date_increment
// Increment minutes and seconds by this amount, default is 1.
//
// #date_year_range
// The number of years to go back and forward in a year selector,
// default is -3:+3 (3 back and 3 forward).
//
// #datepicker_options
// An associative array representing the jQuery datepicker options you want
// to set for this element. Use the jQuery datepicker option names as keys.
// Hard coded defaults are:
// - changeMonth => TRUE
// - changeYear => TRUE
// - autoPopUp => 'focus'
// - closeAtTop => FALSE
// - speed => 'immediate'
$type['date_popup'] = array(
'#input' => TRUE,
'#tree' => TRUE,
@ -222,10 +222,15 @@ function date_popup_element_info() {
'#process' => array('date_popup_element_process'),
'#value_callback' => 'date_popup_element_value_callback',
'#theme_wrappers' => array('date_popup'),
'#attached' => array('css' => array(
drupal_get_path('module', 'date_popup'). '/themes/datepicker.1.7.css',
)),
);
if (module_exists('ctools')) {
$type['date_popup']['#pre_render'] = array('ctools_dependent_pre_render');
}
return $type;
}
@ -262,15 +267,14 @@ function date_popup_time_format($element) {
/**
* Element value callback for date_popup element.
*/
// @codingStandardsIgnoreStart
function date_popup_element_value_callback($element, $input = FALSE, &$form_state) {
$granularity = date_format_order($element['#date_format']);
$has_time = date_has_time($granularity);
$date = NULL;
$return = $has_time ? array('date' => '', 'time' => '') : array('date' => '');
// Normal input from submitting the form element.
// Check is_array() to skip the string input values created by Views pagers.
// Those string values, if present, should be interpreted as empty input.
// Normal input from submitting the form element. Check is_array() to skip
// the string input values created by Views pagers. Those string values, if
// present, should be interpreted as empty input.
if ($input !== FALSE && is_array($input)) {
$return = $input;
$date = date_popup_input_date($element, $input);
@ -290,7 +294,6 @@ function date_popup_element_value_callback($element, $input = FALSE, &$form_stat
return $return;
}
// @codingStandardsIgnoreEnd
/**
* Javascript popup element processing.
@ -357,21 +360,32 @@ function date_popup_process_date_part(&$element) {
return array();
}
// The datepicker can't handle zero or negative values like 0:+1
// even though the Date API can handle them, so rework the value
// we pass to the datepicker to use defaults it can accept (such as +0:+1)
// The datepicker can't handle zero or negative values like 0:+1 even though
// the Date API can handle them, so rework the value we pass to the
// datepicker to use defaults it can accept (such as +0:+1)
// date_range_string() adds the necessary +/- signs to the range string.
$this_year = date_format(date_now(), 'Y');
// When used as a Views exposed filter widget, $element['#value'] contains an array instead an string.
// Fill the 'date' string in this case.
// When used as a Views exposed filter widget, $element['#value'] contains an
// array instead an string. Fill the 'date' string in this case.
$mock = NULL;
$callback_values = date_popup_element_value_callback($element, FALSE, $mock);
if (!isset($element['#value']['date']) && isset($callback_values['date'])) {
if (!is_array($element['#value'])) {
$element['#value'] = array();
}
$element['#value']['date'] = $callback_values['date'];
}
$date = '';
if (!empty($element['#value']['date'])) {
// @todo If date does not meet the granularity or format condtions, then
// $date will have error condtions and '#default_value' below will have the
// global default date (most likely formatted string from 'now'). There
// will be an validation error message referring to the actual input date
// but the form element will display formatted 'now'.
$date = new DateObject($element['#value']['date'], $element['#date_timezone'], date_popup_date_format($element));
if (!date_is_date($date)) {
$date = '';
}
}
$range = date_range_years($element['#date_year_range'], $date);
$year_range = date_range_string($range);
@ -401,8 +415,11 @@ function date_popup_process_date_part(&$element) {
// Create a unique id for each set of custom settings.
$id = date_popup_js_settings_id($element['#id'], 'datepicker', $settings);
// Manually build this element and set the value -
// this will prevent corrupting the parent value.
// Manually build this element and set the value - this will prevent
// corrupting the parent value.
// Do NOT set '#input' => FALSE as this prevents the form API from recursing
// into this element (this was useful when the '#value' property was being
// overwritten by the '#default_value' property).
$parents = array_merge($element['#parents'], array('date'));
$sub_element = array(
'#type' => 'textfield',
@ -410,17 +427,16 @@ function date_popup_process_date_part(&$element) {
'#title_display' => $element['#date_label_position'] == 'above' ? 'before' : 'invisible',
'#default_value' => date_format_date($date, 'custom', date_popup_date_format($element)),
'#id' => $id,
'#input' => FALSE,
'#size' => !empty($element['#size']) ? $element['#size'] : 20,
'#maxlength' => !empty($element['#maxlength']) ? $element['#maxlength'] : 30,
'#attributes' => $element['#attributes'],
'#parents' => $parents,
'#name' => array_shift($parents) . '[' . implode('][', $parents) . ']',
'#ajax' => !empty($element['#ajax']) ? $element['#ajax'] : FALSE,
'#required' => $element['#required'],
);
$sub_element['#value'] = $sub_element['#default_value'];
// TODO, figure out exactly when we want this description.
// In many places it is not desired.
// Do NOT overwrite the actual input with the default value.
// @todo Figure out exactly when this is needed, in many places it is not.
$sub_element['#description'] = ' ' . t('E.g., @date', array(
'@date' => date_format_date(
date_example_date(),
@ -442,8 +458,8 @@ function date_popup_process_time_part(&$element) {
return array();
}
// When used as a Views exposed filter widget, $element['#value'] contains an array instead an string.
// Fill the 'time' string in this case.
// When used as a Views exposed filter widget, $element['#value'] contains an
// array instead an string. Fill the 'time' string in this case.
$mock = NULL;
$callback_values = date_popup_element_value_callback($element, FALSE, $mock);
if (!isset($element['#value']['time']) && isset($callback_values['time'])) {
@ -464,11 +480,17 @@ function date_popup_process_time_part(&$element) {
'spinnerImage' => '',
'fromTo' => isset($fromto),
);
if (strpos($element['#date_format'], 'a') !== FALSE) {
// Then we are using lowercase am/pm.
$settings['ampmNames'] = array('am', 'pm');
$options = date_ampm_options(FALSE, FALSE);
$settings['ampmNames'] = array($options['am'], $options['pm']);
}
if (strpos($element['#date_format'], ' A') !== FALSE || strpos($element['#date_format'], ' a') !== FALSE) {
if (strpos($element['#date_format'], 'A') !== FALSE) {
// Then we are using uppercase am/pm.
$options = date_ampm_options(FALSE, TRUE);
$settings['ampmNames'] = array($options['am'], $options['pm']);
$settings['ampmPrefix'] = ' ';
}
break;
@ -496,14 +518,13 @@ function date_popup_process_time_part(&$element) {
default:
$func = '';
$settings = array();
break;
}
// Create a unique id for each set of custom settings.
$id = date_popup_js_settings_id($element['#id'], $func, $settings);
// Manually build this element and set the value -
// this will prevent corrupting the parent value.
// Manually build this element and set the value - this will prevent
// corrupting the parent value.
$parents = array_merge($element['#parents'], array('time'));
$sub_element = array(
'#type' => 'textfield',
@ -517,12 +538,12 @@ function date_popup_process_time_part(&$element) {
'#parents' => $parents,
'#name' => array_shift($parents) . '[' . implode('][', $parents) . ']',
'#ajax' => !empty($element['#ajax']) ? $element['#ajax'] : FALSE,
'#required' => $element['#required'],
);
$sub_element['#value'] = $sub_element['#default_value'];
// Do NOT overwrite the actual input with the default value.
// TODO, figure out exactly when we want this description.
// In many places it is not desired.
// @todo Figure out exactly when this is needed, in many places it is not.
$example_date = date_now();
date_increment_round($example_date, $element['#date_increment']);
$sub_element['#description'] = t('E.g., @date', array(
@ -543,7 +564,6 @@ function date_popup_process_time_part(&$element) {
* contains a string, after submission it contains an array.
*/
function date_popup_validate($element, &$form_state) {
if (date_hidden_element($element)) {
return;
}
@ -581,9 +601,9 @@ function date_popup_validate($element, &$form_state) {
// @codingStandardsIgnoreEnd
$date = date_popup_input_date($element, $input);
// If the date has errors, display them.
// If something was input but there is no date, the date is invalid.
// If the field is empty and required, set error message and return.
// If the date has errors, display them. If something was input but there is
// no date, the date is invalid. If the field is empty and required, set
// error message and return.
$error_field = implode('][', $element['#parents']);
if ((empty($element['#value']['date']) && empty($element['#value']['time'])) || !empty($date->errors)) {
if (is_object($date) && !empty($date->errors)) {
@ -617,10 +637,8 @@ function date_popup_validate($element, &$form_state) {
* Useful anytime the time value is optional.
*/
function date_popup_input_date($element, $input, $auto_complete = FALSE) {
if (empty($input) || !is_array($input) || !array_key_exists('date', $input) || empty($input['date'])) {
//check if there is no time associated in the input variable. This is the exception scenario where the user has entered only time and not date.
if(empty($input['time']))
return NULL;
if (empty($input) || !is_array($input) || !array_key_exists('date', $input) || (empty($input['date']) && empty($input['time']))) {
return NULL;
}
date_popup_add();
$granularity = date_format_order($element['#date_format']);
@ -629,12 +647,12 @@ function date_popup_input_date($element, $input, $auto_complete = FALSE) {
$format = date_popup_date_format($element);
$format .= $has_time ? ' ' . date_popup_time_format($element) : '';
//check if date is empty, if yes, then leave it blank.
// check if date is empty, if yes, then leave it blank.
$datetime = !empty($input['date']) ? trim($input['date']) : '';
$datetime .= $has_time ? ' ' . trim($input['time']) : '';
$date = new DateObject($datetime, $element['#date_timezone'], $format);
//if the variable is time only then set TimeOnly to TRUE
if(empty($input['date']) && !empty($input['time']) ){
// if the variable is time only then set TimeOnly to TRUE.
if (empty($input['date']) && !empty($input['time'])) {
$date->timeOnly = 'TRUE';
}
if (is_object($date)) {
@ -660,7 +678,7 @@ function date_popup_time_formats($with_seconds = FALSE) {
/**
* Format options array.
*
* TODO Remove any formats not supported by the widget, if any.
* @todo Remove any formats not supported by the widget, if any.
*/
function date_popup_formats() {
// Load short date formats.
@ -684,9 +702,8 @@ function date_popup_formats() {
* A normal date format string, like Y-m-d
*
* @return string
* A format string in popup format, like YMD-, for the
* earlier 'calendar' version, or m/d/Y for the later 'datepicker'
* version.
* A format string in popup format, like YMD-, for the earlier 'calendar'
* version, or m/d/Y for the later 'datepicker' version.
*/
function date_popup_format_to_popup($format) {
if (empty($format)) {
@ -754,7 +771,7 @@ function date_popup_popup_to_format($format) {
* input format into one that the given timepicker can support.
*
* @param string $timepicker
* The time entry plugin being used: either 'wvega' or 'default'.
* The time entry plugin being used: 'wvega', 'timepicker' or 'default'.
*
* @return array
* A map of replacements.
@ -765,6 +782,10 @@ function date_popup_timepicker_format_replacements($timepicker = 'default') {
// The wvega timepicker only supports uppercase AM/PM.
return array('a' => 'A');
case 'timepicker':
// The jquery_ui timepicker supports all possible date formats.
return array();
default:
// The default timeEntry plugin requires leading zeros.
return array('G' => 'H', 'g' => 'h');
@ -798,14 +819,14 @@ function theme_date_popup($vars) {
$element = $vars['element'];
$attributes = !empty($element['#wrapper_attributes']) ? $element['#wrapper_attributes'] : array('class' => array());
$attributes['class'][] = 'container-inline-date';
// If there is no description, the floating date
// elements need some extra padding below them.
// If there is no description, the floating date elements need some extra
// padding below them.
$wrapper_attributes = array('class' => array('date-padding'));
if (empty($element['date']['#description'])) {
$wrapper_attributes['class'][] = 'clearfix';
}
// Add an wrapper to mimic the way a single value field works,
// for ease in using #states.
// Add an wrapper to mimic the way a single value field works, for ease in
// using #states.
if (isset($element['#children'])) {
$element['#children'] = '<div id="' . $element['#id'] . '" ' . drupal_attributes($wrapper_attributes) . '>' . $element['#children'] . '</div>';
}
@ -824,8 +845,7 @@ function date_popup_date_field_instance_settings_form_alter(&$form, $context) {
* Implements hook_menu().
*/
function date_popup_menu() {
$items = array();
// TODO Fix this later.
// @todo Fix this later.
$items['admin/config/date/date_popup'] = array(
'title' => 'Date Popup',
'description' => 'Configure the Date Popup settings.',
@ -836,6 +856,7 @@ function date_popup_menu() {
);
return $items;
}
/**
* General configuration form for controlling the Date Popup behaviour.
*/

View File

@ -0,0 +1,64 @@
<?php
/**
* @file
* Test the custom field functionality provided by the Date Popup module.
*/
/**
* Test the custom field functionality provided by the Date Popup module.
*/
class DatePopupFieldTestCase extends DateFieldTestBase {
/**
* Define this test class.
*/
public static function getInfo() {
return array(
'name' => t('Date Popup'),
'description' => t('Test the custom functionality of the Date Popup module.'),
'group' => t('Date'),
);
}
/**
* {@inheritdoc}
*/
public function setUp(array $modules = array()) {
$modules[] = 'date_popup';
parent::setUp($modules);
// Don't load the time picker.
// @todo Add test coverage for the time picker.
variable_set('date_popup_timepicker', 'none');
}
/**
* Test the field settings.
*/
public function testField() {
// Test all base field types.
foreach (array('date', 'datestamp', 'datetime') as $field_type) {
// Add a Date Popup field to the 'story' content type, but don't remove
// it.
$this->checkDateField($field_type, 'date_popup', FALSE);
// Load the node form.
$this->drupalGet('node/add/story');
$this->assertResponse(200);
// Confirm the date field is present.
$this->assertFieldByName('field_test_date_popup[und][0][value][date]');
$this->assertFieldByName('field_test_date_popup[und][0][value][time]');
// Confirm the JS and CSS files were requested.
$this->assertRaw('date/date_popup/jquery.timeentry.pack.js');
$this->assertRaw('date/date_popup/themes/datepicker.1.7.css');
$this->assertRaw('date/date_popup/themes/jquery.timeentry.css');
// Now delete the field.
$this->deleteDateField('Test');
}
}
}

View File

@ -0,0 +1,44 @@
<?php
/**
* @file
* Data validation tests for the Date Popup module.
*/
/**
* Data validation tests for the Date Popup module.
*/
class DatePopupValidationTestCase extends DateValidationTestCase {
/**
* @todo.
*/
// public static function getInfo() {
// return array(
// 'name' => 'Date Popup Validation',
// 'description' => 'Data validation tests for the Date Popup module.',
// 'group' => 'Date',
// );
// }
/**
* {@inheritdoc}
*/
public function setUp(array $modules = array()) {
$modules[] = 'date_popup';
parent::setUp($modules);
}
/**
* Validate the Date Popup fields.
*/
public function testValidation() {
// Attempts to create text date field stored as a date with default settings
// (from input which is not valid).
foreach (array('date', 'datestamp', 'datetime') as $field_type) {
$this->checkDateField($field_type, 'date_popup', TRUE);
return;
}
}
}

View File

@ -0,0 +1,112 @@
<?php
/**
* @file
* Tests Date Popup in the Views interface.
*/
/**
* Tests Date Popup in the Views interface.
*/
class DatePopupWithViewsTestCase extends DateFieldTestBase {
/**
* Test info.
*/
public static function getInfo() {
return array(
'name' => 'Date Views - Popup Test',
'description' => 'Tests date popup in Views',
'group' => 'Date',
'dependencies' => array('ctools', 'views'),
);
}
/**
* {@inheritdoc}
*/
public function setUp(array $modules = array()) {
$modules[] = 'date_popup';
$modules[] = 'date_views';
$modules[] = 'ctools';
$modules[] = 'views';
$modules[] = 'views_ui';
parent::setUp($modules);
// Reset/rebuild all data structures after enabling the modules.
$this->resetAll();
// Create a date field.
$field_name = "field_test_date_popup";
$label = 'Test';
$options = array(
'label' => 'Test',
'widget_type' => 'date_popup',
'field_name' => $field_name,
'field_type' => 'datetime',
'input_format' => 'm/d/Y - H:i',
);
$this->createDateField($options);
// Set required permissions.
$permissions = array('administer views', 'administer site configuration');
// Create admin user and login.
$admin_user = $this->drupalCreateUser($permissions);
$this->drupalLogin($admin_user);
// Create the view.
$view = new view();
$view->name = 'test_date_popup';
$view->description = '';
$view->tag = 'default';
$view->base_table = 'node';
$view->human_name = 'Test date_popup';
$view->core = 7;
$view->api_version = '3.0';
$view->disabled = FALSE; /* Edit this to TRUE to make a default view disabled initially */
/* Display: Master */
$handler = $view->new_display('default', 'Master', 'default');
$handler->display->display_options['title'] = 'test_date_popup_page';
$handler->display->display_options['use_more_always'] = FALSE;
$handler->display->display_options['access']['type'] = 'perm';
$handler->display->display_options['cache']['type'] = 'none';
$handler->display->display_options['query']['type'] = 'views_query';
$handler->display->display_options['exposed_form']['type'] = 'basic';
$handler->display->display_options['pager']['type'] = 'none';
$handler->display->display_options['pager']['options']['offset'] = '0';
$handler->display->display_options['style_plugin'] = 'default';
$handler->display->display_options['row_plugin'] = 'node';
/* Field: Content: Title */
$handler->display->display_options['fields']['title']['id'] = 'title';
$handler->display->display_options['fields']['title']['table'] = 'node';
$handler->display->display_options['fields']['title']['field'] = 'title';
$handler->display->display_options['fields']['title']['label'] = '';
$handler->display->display_options['fields']['title']['alter']['word_boundary'] = FALSE;
$handler->display->display_options['fields']['title']['alter']['ellipsis'] = FALSE;
/* Filter criterion: Content: test_date_popup (field_test_date_popup) */
$handler->display->display_options['filters']['field_test_date_popup_value']['id'] = 'field_test_date_popup_value';
$handler->display->display_options['filters']['field_test_date_popup_value']['table'] = 'field_data_field_test_date_popup';
$handler->display->display_options['filters']['field_test_date_popup_value']['field'] = 'field_test_date_popup_value';
$handler->display->display_options['filters']['field_test_date_popup_value']['exposed'] = TRUE;
$handler->display->display_options['filters']['field_test_date_popup_value']['expose']['operator_id'] = 'field_test_date_popup_value_op';
$handler->display->display_options['filters']['field_test_date_popup_value']['expose']['label'] = 'test_date_popup (field_test_date_popup)';
$handler->display->display_options['filters']['field_test_date_popup_value']['expose']['operator'] = 'field_test_date_popup_value_op';
$handler->display->display_options['filters']['field_test_date_popup_value']['expose']['identifier'] = 'field_test_date_popup_value';
$handler->display->display_options['filters']['field_test_date_popup_value']['form_type'] = 'date_popup';
/* Display: Page */
$handler = $view->new_display('page', 'Page', 'page');
$handler->display->display_options['path'] = 'test-date-popup';
$view->save();
}
/**
* Test date popup.
*/
public function testDatePopup() {
// Go to view page.
$this->drupalGet('test-date-popup');
}
}

View File

@ -1,59 +1,188 @@
#ui-datepicker-div {
font-size: 100%;
font-family: Verdana, sans-serif;
background: #eee;
border-right:2px #666 solid;
border-bottom:2px #666 solid;
z-index: 9999;
font-size: 100%;
font-family: Verdana, sans-serif;
background: #eee;
border-right: 2px #666 solid;
border-bottom: 2px #666 solid;
z-index: 9999;
}
/* Datepicker
----------------------------------*/
.ui-datepicker { width: 17em; padding: .2em .2em 0; }
.ui-datepicker .ui-datepicker-header { position:relative; padding:.2em 0; }
.ui-datepicker .ui-datepicker-prev, .ui-datepicker .ui-datepicker-next { position:absolute; top: 2px; width: 1.8em; height: 1.8em; }
.ui-datepicker .ui-datepicker-prev-hover, .ui-datepicker .ui-datepicker-next-hover { top: 1px; }
.ui-datepicker .ui-datepicker-prev { left:2px; }
.ui-datepicker .ui-datepicker-next { right:2px; }
.ui-datepicker .ui-datepicker-prev-hover { left:1px; }
.ui-datepicker .ui-datepicker-next-hover { right:1px; }
.ui-datepicker .ui-datepicker-prev span, .ui-datepicker .ui-datepicker-next span { display: block; position: absolute; left: 50%; margin-left: -8px; top: 50%; margin-top: -8px; }
.ui-datepicker .ui-datepicker-title { margin: 0 2.3em; line-height: 1.8em; text-align: center; }
.ui-datepicker .ui-datepicker-title select { float:left; font-size:1em; margin:1px 0; }
.ui-datepicker select.ui-datepicker-month-year {width: 100%;}
.ui-datepicker {
width: auto;
padding: .2em .2em 0;
}
.ui-datepicker .ui-datepicker-header {
position: relative;
padding: .2em 0;
}
.ui-datepicker .ui-datepicker-prev,
.ui-datepicker .ui-datepicker-next {
position: absolute;
top: 2px;
width: 1.8em;
height: 1.8em;
}
.ui-datepicker .ui-datepicker-prev-hover,
.ui-datepicker .ui-datepicker-next-hover {
top: 1px;
}
.ui-datepicker .ui-datepicker-prev {
left: 2px;
}
.ui-datepicker .ui-datepicker-next {
right: 2px;
}
.ui-datepicker .ui-datepicker-prev-hover {
left: 1px;
}
.ui-datepicker .ui-datepicker-next-hover {
right: 1px;
}
.ui-datepicker .ui-datepicker-prev span,
.ui-datepicker .ui-datepicker-next span {
display: block;
position: absolute;
left: 50%;
margin-left: -8px;
top: 50%;
margin-top: -8px;
}
.ui-datepicker .ui-datepicker-title {
margin: 0 2.3em;
line-height: 1.8em;
text-align: center;
}
.ui-datepicker .ui-datepicker-title select {
float: left;
font-size: 1em;
margin: 1px 0;
}
.ui-datepicker select.ui-datepicker-month-year {
width: 100%;
}
.ui-datepicker select.ui-datepicker-month,
.ui-datepicker select.ui-datepicker-year { width: 49%;}
.ui-datepicker .ui-datepicker-title select.ui-datepicker-year { float: right; }
.ui-datepicker table {width: 100%; font-size: .9em; border-collapse: collapse; margin:0 0 .4em; }
.ui-datepicker th { padding: .7em .3em; text-align: center; font-weight: bold; border: 0; }
.ui-datepicker td { border: 0; padding: 1px; }
.ui-datepicker td span, .ui-datepicker td a { display: block; padding: .2em; text-align: right; text-decoration: none; }
.ui-datepicker .ui-datepicker-buttonpane { background-image: none; margin: .7em 0 0 0; padding:0 .2em; border-left: 0; border-right: 0; border-bottom: 0; }
.ui-datepicker .ui-datepicker-buttonpane button { float: right; margin: .5em .2em .4em; cursor: pointer; padding: .2em .6em .3em .6em; width:auto; overflow:visible; }
.ui-datepicker .ui-datepicker-buttonpane button.ui-datepicker-current { float:left; }
.ui-datepicker select.ui-datepicker-year {
width: 49%;
}
.ui-datepicker .ui-datepicker-title select.ui-datepicker-year {
float: right;
}
.ui-datepicker table {
width: 100%;
font-size: .9em;
border-collapse: collapse;
margin: 0 0 .4em;
}
.ui-datepicker th {
padding: .7em .3em;
text-align: center;
font-weight: bold;
border: 0;
}
.ui-datepicker td {
border: 0;
padding: 1px;
}
.ui-datepicker td span,
.ui-datepicker td a {
display: block;
padding: .2em;
text-align: right;
text-decoration: none;
}
.ui-datepicker .ui-datepicker-buttonpane {
background-image: none;
margin: .7em 0 0 0;
padding: 0 .2em;
border-left: 0;
border-right: 0;
border-bottom: 0;
}
.ui-datepicker .ui-datepicker-buttonpane button {
float: right;
margin: .5em .2em .4em;
cursor: pointer;
padding: .2em .6em .3em .6em;
width: auto;
overflow: visible;
}
.ui-datepicker .ui-datepicker-buttonpane button.ui-datepicker-current {
float: left;
}
/* with multiple calendars */
.ui-datepicker.ui-datepicker-multi { width:auto; }
.ui-datepicker-multi .ui-datepicker-group { float:left; }
.ui-datepicker-multi .ui-datepicker-group table { width:95%; margin:0 auto .4em; }
.ui-datepicker-multi-2 .ui-datepicker-group { width:50%; }
.ui-datepicker-multi-3 .ui-datepicker-group { width:33.3%; }
.ui-datepicker-multi-4 .ui-datepicker-group { width:25%; }
.ui-datepicker-multi .ui-datepicker-group-last .ui-datepicker-header { border-left-width:0; }
.ui-datepicker-multi .ui-datepicker-group-middle .ui-datepicker-header { border-left-width:0; }
.ui-datepicker-multi .ui-datepicker-buttonpane { clear:left; }
.ui-datepicker-row-break { clear:both; width:100%; }
.ui-datepicker.ui-datepicker-multi {
width: auto;
}
.ui-datepicker-multi .ui-datepicker-group {
float: left;
}
.ui-datepicker-multi .ui-datepicker-group table {
width: 95%;
margin: 0 auto .4em;
}
.ui-datepicker-multi-2 .ui-datepicker-group {
width: 50%;
}
.ui-datepicker-multi-3 .ui-datepicker-group {
width: 33.3%;
}
.ui-datepicker-multi-4 .ui-datepicker-group {
width: 25%;
}
.ui-datepicker-multi .ui-datepicker-group-last .ui-datepicker-header {
border-left-width: 0;
}
.ui-datepicker-multi .ui-datepicker-group-middle .ui-datepicker-header {
border-left-width: 0;
}
.ui-datepicker-multi .ui-datepicker-buttonpane {
clear: left;
}
.ui-datepicker-row-break {
clear: both;
width: 100%;
}
/* RTL support */
.ui-datepicker-rtl { direction: rtl; }
.ui-datepicker-rtl .ui-datepicker-prev { right: 2px; left: auto; }
.ui-datepicker-rtl .ui-datepicker-next { left: 2px; right: auto; }
.ui-datepicker-rtl .ui-datepicker-prev:hover { right: 1px; left: auto; }
.ui-datepicker-rtl .ui-datepicker-next:hover { left: 1px; right: auto; }
.ui-datepicker-rtl .ui-datepicker-buttonpane { clear:right; }
.ui-datepicker-rtl .ui-datepicker-buttonpane button { float: left; }
.ui-datepicker-rtl .ui-datepicker-buttonpane button.ui-datepicker-current { float:right; }
.ui-datepicker-rtl .ui-datepicker-group { float:right; }
.ui-datepicker-rtl .ui-datepicker-group-last .ui-datepicker-header { border-right-width:0; border-left-width:1px; }
.ui-datepicker-rtl .ui-datepicker-group-middle .ui-datepicker-header { border-right-width:0; border-left-width:1px; }
.ui-datepicker-rtl {
direction: rtl;
}
.ui-datepicker-rtl .ui-datepicker-prev {
right: 2px;
left: auto;
}
.ui-datepicker-rtl .ui-datepicker-next {
left: 2px;
right: auto;
}
.ui-datepicker-rtl .ui-datepicker-prev:hover {
right: 1px;
left: auto;
}
.ui-datepicker-rtl .ui-datepicker-next:hover {
left: 1px;
right: auto;
}
.ui-datepicker-rtl .ui-datepicker-buttonpane {
clear: right;
}
.ui-datepicker-rtl .ui-datepicker-buttonpane button {
float: left;
}
.ui-datepicker-rtl .ui-datepicker-buttonpane button.ui-datepicker-current {
float: right;
}
.ui-datepicker-rtl .ui-datepicker-group {
float: right;
}
.ui-datepicker-rtl .ui-datepicker-group-last .ui-datepicker-header {
border-right-width: 0;
border-left-width: 1px;
}
.ui-datepicker-rtl .ui-datepicker-group-middle .ui-datepicker-header {
border-right-width: 0;
border-left-width: 1px;
}

View File

@ -1,8 +1,8 @@
/* TimeEntry styles */
.timeEntry_control {
vertical-align: middle;
margin-left: 2px;
vertical-align: middle;
margin-left: 2px;
}
* html .timeEntry_control { /* IE only */
margin-top: -4px;
margin-top: -4px;
}

View File

@ -1,4 +1,5 @@
<?php
/**
* @file
* Empty file to avoid fatal error if it doesn't exist.

View File

@ -1,15 +1,16 @@
name = Date Repeat API
description = A Date Repeat API to calculate repeating dates and times from iCal rules.
dependencies[] = date_api
package = Date/Time
core = 7.x
php = 5.2
dependencies[] = date:date_api
files[] = tests/date_repeat.test
files[] = tests/date_repeat_form.test
; Information added by Drupal.org packaging script on 2017-04-07
version = "7.x-2.10"
; Information added by Drupal.org packaging script on 2021-03-05
version = "7.x-2.11"
core = "7.x"
project = "date"
datestamp = "1491562090"
datestamp = "1614952728"

View File

@ -1,4 +1,5 @@
<?php
/**
* @file
* This module creates a form element that allows users to select
@ -70,9 +71,9 @@ function date_repeat_interval_options() {
/**
* Helper function for FREQ options.
*
* Translated and untranslated arrays of the iCal day of week names.
* We need the untranslated values for date_modify(), translated
* values when displayed to user.
* Translated and untranslated arrays of the iCal day of week names. We need the
* untranslated values for date_modify(), translated values when displayed to
* user.
*/
function date_repeat_dow_day_options($translated = TRUE) {
return array(
@ -104,7 +105,6 @@ function date_repeat_dow_day_options_abbr($translated = TRUE, $length = 3) {
default:
$context = '';
break;
}
foreach (date_repeat_dow_day_untranslated() as $key => $day) {
$return[$key] = $translated ? t(substr($day, 0, $length), array(), array('context' => $context)) : substr($day, 0, $length);
@ -125,7 +125,7 @@ function date_repeat_dow_day_untranslated() {
'WE' => 'Wednesday',
'TH' => 'Thursday',
'FR' => 'Friday',
'SA' => 'Saturday'
'SA' => 'Saturday',
);
}
return $date_repeat_weekdays;
@ -157,7 +157,7 @@ function date_repeat_dow_count_options() {
/**
* Helper function for BYDAY options.
*
* Creates options like -1SU and 2TU
* Creates options like -1SU and 2TU.
*/
function date_repeat_dow_options() {
$options = array();
@ -172,8 +172,8 @@ function date_repeat_dow_options() {
/**
* Translate a day of week position to the iCal day name.
*
* Used with date_format($date, 'w') or get_variable('date_first_day'),
* which return 0 for Sunday, 1 for Monday, etc.
* Used with date_format($date, 'w') or get_variable('date_first_day'), which
* return 0 for Sunday, 1 for Monday, etc.
*
* dow 2 becomes 'TU', dow 3 becomes 'WE', and so on.
*/
@ -183,7 +183,9 @@ function date_repeat_dow2day($dow) {
}
/**
* Shift the array of iCal day names into the right order for a specific week start day.
* Shift the array of iCal day names into the right order.
*
* @param $week_start_day
*/
function date_repeat_days_ordered($week_start_day) {
$days = array_flip(array_keys(date_repeat_dow_day_options(FALSE)));
@ -247,7 +249,6 @@ function date_repeat_rrule_description($rrule, $format = 'D M d Y') {
default:
$description['!interval'] = format_plural($rrule['INTERVAL'], 'every day', 'every @count days') . ' ';
break;
}
if (!empty($rrule['BYDAY'])) {
@ -265,7 +266,7 @@ function date_repeat_rrule_description($rrule, $format = 'D M d Y') {
array(
'!repeats_every_interval ' => '',
'!date_order' => $order,
'!day_of_week' => $days[$day]
'!day_of_week' => $days[$day],
)));
}
else {
@ -287,14 +288,14 @@ function date_repeat_rrule_description($rrule, $format = 'D M d Y') {
array(
'!repeats_every_interval ' => '',
'!month_days' => implode(', ', $rrule['BYMONTHDAY']),
'!month_names' => implode(', ', $results)
'!month_names' => implode(', ', $results),
)));
}
else {
$description['!bymonth'] = trim(t('!repeats_every_interval on !month_names',
array(
'!repeats_every_interval ' => '',
'!month_names' => implode(', ', $results)
'!month_names' => implode(', ', $results),
)));
}
}
@ -312,7 +313,7 @@ function date_repeat_rrule_description($rrule, $format = 'D M d Y') {
$description['!until'] = trim(t('!repeats_every_interval until !until_date',
array(
'!repeats_every_interval ' => '',
'!until_date' => date_format_date($until, 'custom', $format)
'!until_date' => date_format_date($until, 'custom', $format),
)));
}
if ($exceptions) {
@ -325,7 +326,7 @@ function date_repeat_rrule_description($rrule, $format = 'D M d Y') {
$description['!except'] = trim(t('!repeats_every_interval except !except_dates',
array(
'!repeats_every_interval ' => '',
'!except_dates' => implode(', ', $values)
'!except_dates' => implode(', ', $values),
)));
}
if (!empty($rrule['WKST'])) {

View File

@ -1,4 +1,5 @@
<?php
/**
* @file
* Code to compute the dates that match an iCal RRULE.
@ -53,11 +54,10 @@ function _date_repeat_calc($rrule, $start, $end, $exceptions, $timezone, $additi
// Create a date object for the start and end dates.
$start_date = new DateObject($start, $timezone);
// Versions of PHP greater than PHP 5.3.5 require
// that we set an explicit time when using date_modify()
// or the time may not match the original value.
// Adding this modifier gives us the same results in both older
// and newer versions of PHP.
// Versions of PHP greater than PHP 5.3.5 require that we set an explicit
// time when using date_modify() or the time may not match the original value.
// Adding this modifier gives us the same results in both older and newer
// versions of PHP.
$modify_time = ' ' . $start_date->format('g:ia');
// If the rule has an UNTIL, see if that is earlier than the end date.
@ -122,17 +122,16 @@ function _date_repeat_calc($rrule, $start, $end, $exceptions, $timezone, $additi
}
$rrule = date_repeat_adjust_rrule($rrule, $start_date);
// The start date always goes into the results, whether or not it meets
// the rules. RFC 2445 includes examples where the start date DOES NOT
// meet the rules, but the expected results always include the start date.
// The start date always goes into the results, whether or not it meets the
// rules. RFC 2445 includes examples where the start date DOES NOT meet the
// rules, but the expected results always include the start date.
$days = array(date_format($start_date, DATE_FORMAT_DATETIME));
// BYMONTHDAY will look for specific days of the month in one or more months.
// This process is only valid when frequency is monthly or yearly.
if (!empty($rrule['BYMONTHDAY'])) {
$finished = FALSE;
$current_day = clone($start_date);
$current_day = clone $start_date;
$direction_days = array();
// Deconstruct the day in case it has a negative modifier.
foreach ($rrule['BYMONTHDAY'] as $day) {
@ -187,13 +186,12 @@ function _date_repeat_calc($rrule, $start, $end, $exceptions, $timezone, $additi
}
}
// This is the simple fallback case, not looking for any BYDAY,
// just repeating the start date. Because of imputed BYDAY above, this
// will only test TRUE for a DAILY or less frequency (like HOURLY).
// This is the simple fallback case, not looking for any BYDAY, just
// repeating the start date. Because of imputed BYDAY above, this will only
// test TRUE for a DAILY or less frequency (like HOURLY).
elseif (empty($rrule['BYDAY'])) {
// $current_day will keep track of where we are in the calculation.
$current_day = clone($start_date);
$current_day = clone $start_date;
$finished = FALSE;
$months = !empty($rrule['BYMONTH']) ? $rrule['BYMONTH'] : array();
while (!$finished) {
@ -204,17 +202,15 @@ function _date_repeat_calc($rrule, $start, $end, $exceptions, $timezone, $additi
}
else {
// More complex searches for day names and criteria
// like '-1SU' or '2TU,2TH', require that we interate through
// the whole time period checking each BYDAY.
// Create helper array to pull day names out of iCal day strings.
// More complex searches for day names and criteria like '-1SU' or
// '2TU,2TH', require that we interate through the whole time period
// checking each BYDAY. Create helper array to pull day names out of iCal
// day strings.
$day_names = date_repeat_dow_day_options(FALSE);
$days_of_week = array_keys($day_names);
// Parse out information about the BYDAYs and separate them
// depending on whether they have directional parameters like -1SU or 2TH.
// Parse out information about the BYDAYs and separate them depending on
// whether they have directional parameters like -1SU or 2TH.
$month_days = array();
$week_days = array();
@ -223,11 +219,11 @@ function _date_repeat_calc($rrule, $start, $end, $exceptions, $timezone, $additi
$week_start_rule = !empty($rrule['WKST']) ? trim($rrule['WKST']) : 'MO';
$week_start_day = $day_names[$week_start_rule];
// Make sure the week days array is sorted into week order,
// we use the $ordered_keys to get the right values into the key
// and force the array to that order. Needed later when we
// iterate through each week looking for days so we don't
// jump to the next week when we hit a day out of order.
// Make sure the week days array is sorted into week order, we use the
// $ordered_keys to get the right values into the key and force the array
// to that order. Needed later when we iterate through each week looking
// for days so we don't jump to the next week when we hit a day out of
// order.
$ordered = date_repeat_days_ordered($week_start_rule);
$ordered_keys = array_flip($ordered);
@ -236,8 +232,8 @@ function _date_repeat_calc($rrule, $start, $end, $exceptions, $timezone, $additi
foreach ($rrule['BYMONTH'] as $month) {
foreach ($rrule['BYDAY'] as $day) {
preg_match("@(-)?([0-9]+)?([SU|MO|TU|WE|TH|FR|SA]{2})@", trim($day), $regs);
// Convert parameters into full day name, count, and direction.
// Add leading zero to first 9 months.
// Convert parameters into full day name, count, and direction. Add
// leading zero to first 9 months.
if (!empty($regs[2])) {
$direction_days[] = array(
'day' => $day_names[$regs[3]],
@ -271,11 +267,11 @@ function _date_repeat_calc($rrule, $start, $end, $exceptions, $timezone, $additi
}
ksort($week_days);
// BYDAYs with parameters like -1SU (last Sun) or 2TH (second Thur)
// need to be processed one month or year at a time.
// BYDAYs with parameters like -1SU (last Sun) or 2TH (second Thur) need to
// be processed one month or year at a time.
if (!empty($direction_days) && in_array($rrule['FREQ'], array('MONTHLY', 'YEARLY'))) {
$finished = FALSE;
$current_day = clone($start_date);
$current_day = clone $start_date;
while (!$finished) {
foreach ($direction_days as $day) {
// Find the BYDAY date in the current month.
@ -288,9 +284,9 @@ function _date_repeat_calc($rrule, $start, $end, $exceptions, $timezone, $additi
date_repeat_add_dates($days, $current_day, $start_date, $end_date, $exceptions, $rrule);
}
$finished = date_repeat_is_finished($current_day, $days, $count, $end_date);
// Reset to beginning of period before jumping to next period.
// Needed especially when working with values like 'last Saturday'
// to be sure we don't skip months like February.
// Reset to beginning of period before jumping to next period. Needed
// especially when working with values like 'last Saturday' to be sure
// we don't skip months like February.
$year = date_format($current_day, 'Y');
$month = date_format($current_day, 'n');
if ($rrule['FREQ'] == 'MONTHLY') {
@ -304,24 +300,22 @@ function _date_repeat_calc($rrule, $start, $end, $exceptions, $timezone, $additi
}
}
// For BYDAYs without parameters,like TU,TH (every Tues and Thur),
// we look for every one of those days during the frequency period.
// Iterate through periods of a WEEK, MONTH, or YEAR, checking for
// the days of the week that match our criteria for each week in the
// period, then jumping ahead to the next week, month, or year,
// an INTERVAL at a time.
// For BYDAYs without parameters,like TU,TH (every Tues and Thur), we look
// for every one of those days during the frequency period. Iterate through
// periods of a WEEK, MONTH, or YEAR, checking for the days of the week
// that match our criteria for each week in the period, then jumping ahead
// to the next week, month, or year, an INTERVAL at a time.
if (!empty($week_days) &&
in_array($rrule['FREQ'], array('MONTHLY', 'WEEKLY', 'YEARLY'))) {
$finished = FALSE;
$current_day = clone($start_date);
$current_day = clone $start_date;
$format = $rrule['FREQ'] == 'YEARLY' ? 'Y' : 'n';
$current_period = date_format($current_day, $format);
// Back up to the beginning of the week in case we are somewhere in the
// middle of the possible week days, needed so we don't prematurely
// jump to the next week. The date_repeat_add_dates() function will
// keep dates outside the range from getting added.
// middle of the possible week days, needed so we don't prematurely jump
// to the next week. The date_repeat_add_dates() function will keep dates
// outside the range from getting added.
if (date_format($current_day, 'l') != $day_names[$day]) {
date_modify($current_day, '-1 ' . $week_start_day . $modify_time);
}
@ -330,15 +324,13 @@ function _date_repeat_calc($rrule, $start, $end, $exceptions, $timezone, $additi
while (!$period_finished) {
$moved = FALSE;
foreach ($week_days as $delta => $day) {
// Find the next occurence of each day in this week, only add it
// if we are still in the current month or year.
// The date_repeat_add_dates function is insufficient
// to test whether to include this date
// if we are using a rule like 'every other month', so we must
// explicitly test it here.
// If we're already on the right day, don't jump or we
// will prematurely move into the next week.
// Find the next occurence of each day in this week, only add it if
// we are still in the current month or year. The
// date_repeat_add_dates() function is insufficient to test whether
// to include this date if we are using a rule like 'every other
// month', so we must explicitly test it here. If we're already on
// the right day, don't jump or we will prematurely move into the
// next week.
if (date_format($current_day, 'l') != $day) {
date_modify($current_day, '+1 ' . $day . $modify_time);
$moved = TRUE;
@ -349,15 +341,15 @@ function _date_repeat_calc($rrule, $start, $end, $exceptions, $timezone, $additi
}
$finished = date_repeat_is_finished($current_day, $days, $count, $end_date);
// Make sure we don't get stuck in endless loop if the current
// day never got changed above.
// Make sure we don't get stuck in endless loop if the current day
// never got changed above.
if (!$moved) {
date_modify($current_day, '+1 day' . $modify_time);
}
// If this is a WEEKLY frequency, stop after each week,
// otherwise, stop when we've moved outside the current period.
// Jump to the end of the week, then test the period.
// If this is a WEEKLY frequency, stop after each week, otherwise,
// stop when we've moved outside the current period. Jump to the end
// of the week, then test the period.
if ($finished || $rrule['FREQ'] == 'WEEKLY') {
$period_finished = TRUE;
}
@ -370,11 +362,9 @@ function _date_repeat_calc($rrule, $start, $end, $exceptions, $timezone, $additi
continue;
}
// We'll be at the end of a week, month, or year when
// we get to this point in the code.
// Go back to the beginning of this period before we jump, to
// ensure we jump to the first day of the next period.
// We'll be at the end of a week, month, or year when we get to this
// point in the code. Go back to the beginning of this period before we
// jump, to ensure we jump to the first day of the next period.
switch ($rrule['FREQ']) {
case 'WEEKLY':
date_modify($current_day, '+1 ' . $week_start_day . $modify_time);
@ -420,7 +410,6 @@ function date_repeat_adjust_rrule($rrule, $start_date) {
// RFC 2445 says if no day or monthday is specified when creating repeats for
// weeks, months, or years, impute the value from the start date.
if (empty($rrule['BYDAY']) && $rrule['FREQ'] == 'WEEKLY') {
$rrule['BYDAY'] = array(date_repeat_dow2day(date_format($start_date, 'w')));
}
@ -433,10 +422,9 @@ function date_repeat_adjust_rrule($rrule, $start_date) {
$rrule['BYMONTH'] = array(date_format($start_date, 'n'));
}
}
// If we are processing rules for period other than YEARLY or MONTHLY
// and have BYDAYS like 2SU or -1SA, simplify them to SU or SA since the
// position rules make no sense in other periods and just add complexity.
// If we are processing rules for period other than YEARLY or MONTHLY and have
// BYDAYS like 2SU or -1SA, simplify them to SU or SA since the position
// rules make no sense in other periods and just add complexity.
elseif (!empty($rrule['BYDAY']) && !in_array($rrule['FREQ'], array('MONTHLY', 'YEARLY'))) {
foreach ($rrule['BYDAY'] as $delta => $by_day) {
$rrule['BYDAY'][$delta] = substr($by_day, -2);
@ -449,9 +437,9 @@ function date_repeat_adjust_rrule($rrule, $start_date) {
/**
* Helper function to add found date to the $dates array.
*
* Check that the date to be added is between the start and end date
* and that it is not in the $exceptions, nor already in the $days array,
* and that it meets other criteria in the RRULE.
* Check that the date to be added is between the start and end date and that it
* is not in the $exceptions, nor already in the $days array, and that it meets
* other criteria in the RRULE.
*/
function date_repeat_add_dates(&$days, $current_day, $start_date, $end_date, $exceptions, $rrule) {
if (isset($rrule['COUNT']) && count($days) >= $rrule['COUNT']) {
@ -525,20 +513,18 @@ function date_repeat_is_finished($current_day, $days, $count, $end_date) {
* Set a date object to a specific day of the month.
*
* Example,
* date_set_month_day($date, 'Sunday', 2, '-')
* will reset $date to the second to last Sunday in the month.
* If $day is empty, will set to the number of days from the
* beginning or end of the month.
* date_set_month_day($date, 'Sunday', 2, '-') will reset $date to the second
* to last Sunday in the month. If $day is empty, will set to the number of
* days from the beginning or end of the month.
*/
function date_repeat_set_month_day($date_in, $day, $count = 1, $direction = '+', $timezone = 'UTC', $modify_time = '') {
if (is_object($date_in)) {
$current_month = date_format($date_in, 'n');
// Reset to the start of the month.
// We should be able to do this with date_date_set(), but
// for some reason the date occasionally gets confused if run
// through this function multiple times. It seems to work
// reliably if we create a new object each time.
// Reset to the start of the month. We should be able to do this with
// date_date_set(), but for some reason the date occasionally gets confused
// if run through this function multiple times. It seems to work reliably
// if we create a new object each time.
$datetime = date_format($date_in, DATE_FORMAT_DATETIME);
$datetime = substr_replace($datetime, '01', 8, 2);
$date = new DateObject($datetime, $timezone);
@ -547,8 +533,8 @@ function date_repeat_set_month_day($date_in, $day, $count = 1, $direction = '+',
date_modify($date, '+1 month' . $modify_time);
}
else {
// For positive search, back up one day to get outside the
// current month, so we can catch the first of the month.
// For positive search, back up one day to get outside the current month,
// so we can catch the first of the month.
date_modify($date, '-1 day' . $modify_time);
}
@ -556,8 +542,8 @@ function date_repeat_set_month_day($date_in, $day, $count = 1, $direction = '+',
date_modify($date, $direction . $count . ' days' . $modify_time);
}
else {
// Use the English text for order, like First Sunday
// instead of +1 Sunday to overcome PHP5 bug, (see #369020).
// Use the English text for order, like First Sunday instead of +1 Sunday
// to overcome PHP5 bug, (see #369020).
$order = date_order();
$step = $count <= 5 ? $order[$direction . $count] : $count;
date_modify($date, $step . ' ' . $day . $modify_time);
@ -575,17 +561,15 @@ function date_repeat_set_month_day($date_in, $day, $count = 1, $direction = '+',
* Set a date object to a specific day of the year.
*
* Example,
* date_set_year_day($date, 'Sunday', 2, '-')
* will reset $date to the second to last Sunday in the year.
* If $day is empty, will set to the number of days from the
* beginning or end of the year.
* date_set_year_day($date, 'Sunday', 2, '-') will reset $date to the second
* to last Sunday in the year. If $day is empty, will set to the number of
* days from the beginning or end of the year.
*/
function date_repeat_set_year_day($date_in, $month, $day, $count = 1, $direction = '+', $timezone = 'UTC', $modify_time = '') {
if (is_object($date_in)) {
$current_year = date_format($date_in, 'Y');
// Reset to the start of the month.
// See note above.
// Reset to the start of the month. See note above.
$datetime = date_format($date_in, DATE_FORMAT_DATETIME);
$month_key = isset($month) ? $month : '01';
$datetime = substr_replace($datetime, $month_key . '-01', 5, 5);
@ -597,8 +581,8 @@ function date_repeat_set_year_day($date_in, $month, $day, $count = 1, $direction
$modifier = '+1 month';
}
else {
// For positive search, back up one day to get outside the
// current month, so we can catch the first of the month.
// For positive search, back up one day to get outside the current
// month, so we can catch the first of the month.
$modifier = '-1 day';
}
}
@ -608,8 +592,8 @@ function date_repeat_set_year_day($date_in, $month, $day, $count = 1, $direction
$modifier = '+1 year';
}
else {
// For positive search, back up one day to get outside the
// current year, so we can catch the first of the year.
// For positive search, back up one day to get outside the current
// year, so we can catch the first of the year.
$modifier = '-1 day';
}
}
@ -620,8 +604,8 @@ function date_repeat_set_year_day($date_in, $month, $day, $count = 1, $direction
date_modify($date, $direction . $count . ' days' . $modify_time);
}
else {
// Use the English text for order, like First Sunday
// instead of +1 Sunday to overcome PHP5 bug, (see #369020).
// Use the English text for order, like First Sunday instead of +1 Sunday
// to overcome PHP5 bug, (see #369020).
$order = date_order();
$step = $count <= 5 ? $order[$direction . $count] : $count;
date_modify($date, $step . ' ' . $day . $modify_time);

View File

@ -1,4 +1,5 @@
<?php
/**
* @file
* Code to add a date repeat selection form to a date field and create
@ -35,13 +36,10 @@
* Generate the repeat setting form.
*/
function _date_repeat_rrule_process($element, &$form_state, $form) {
// If the RRULE field is not visible to the user,
// needs no processing or validation.
// The Date field module is not adding this element to forms
// if the field is hidden,
// this test is just in case some other module attempts to do so.
// If the RRULE field is not visible to the user, needs no processing or
// validation. The Date field module is not adding this element to forms if
// the field is hidden, this test is just in case some other module attempts
// to do so.
if (date_hidden_element($element)) {
return $element;
}
@ -58,8 +56,8 @@ function _date_repeat_rrule_process($element, &$form_state, $form) {
$rrule = $element['#default_value'];
}
// Empty the original string value of the RRULE so we can create
// an array of values for the form from the RRULE's contents.
// Empty the original string value of the RRULE so we can create an array of
// values for the form from the RRULE's contents.
$element['#value'] = '';
$parts = date_repeat_split_rrule($rrule);
@ -554,8 +552,8 @@ function _date_repeat_rrule_process($element, &$form_state, $form) {
$parents = $element['#array_parents'];
$instance = implode('-', $parents);
// Make sure this will work right either in the normal
// form or in an ajax callback from the 'Add more' button.
// Make sure this will work right either in the normal form or in an AJAX
// callback from the 'Add more' button.
if (empty($form_state['num_exceptions'][$instance])) {
$form_state['num_exceptions'][$instance] = count($exceptions);
}
@ -875,9 +873,6 @@ function date_repeat_merge($form_values, $element) {
}
}
break;
default:
break;
}
}
@ -967,7 +962,6 @@ function date_repeat_merge($form_values, $element) {
* Build a RRULE out of the form values.
*/
function date_repeat_rrule_validate($element, &$form_state) {
if (date_hidden_element($element)) {
return;
}
@ -980,9 +974,9 @@ function date_repeat_rrule_validate($element, &$form_state) {
return;
}
// Clean the buttons off of the form. Needed to avoid errors when
// the date is used on a user object, which then passes the form
// through form_state_values_clean().
// Clean the buttons off of the form. Needed to avoid errors when the date is
// used on a user object, which then passes the form through
// form_state_values_clean().
foreach ($form_state['buttons'] as $delta => $item) {
if (!empty($item['#ajax']['callback']) && in_array($item['#ajax']['callback'], array('date_repeat_add_exception_callback', 'date_repeat_add_addition_callback'))) {
unset($form_state['buttons'][$delta]);
@ -1070,8 +1064,8 @@ function date_repeat_filter_non_zero_value($value) {
/**
* Helper function for transforming the return value of checkbox(es) element.
*
* Can be used for transforming the returned value of checkbox(es) element
* to the format of returned value of multiple select element.
* Can be used for transforming the returned value of checkbox(es) element to
* the format of returned value of multiple select element.
*/
function date_repeat_transform_checkbox_values_to_select_values($values) {
return array_filter($values, 'date_repeat_filter_non_zero_value');

View File

@ -4,7 +4,9 @@
* @file
* Test Date Repeat calculations.
*/
/**
*
*/
class DateRepeatTestCase extends DrupalWebTestCase {
public static function getInfo() {
return array(
@ -17,17 +19,19 @@ class DateRepeatTestCase extends DrupalWebTestCase {
/**
* Implements setUp().
*/
public function setUp() {
public function setUp(array $modules = array()) {
// Load the date_repeat module.
parent::setUp('date_api', 'date_repeat');
$modules[] = 'date_api';
$modules[] = 'date_repeat';
parent::setUp($modules);
}
public function testDateRepeat() {
require_once('./' . drupal_get_path('module', 'date_api') . '/date_api_ical.inc');
require_once('./' . drupal_get_path('module', 'date_repeat') . '/date_repeat_calc.inc');
require_once './' . drupal_get_path('module', 'date_api') . '/date_api_ical.inc';
require_once './' . drupal_get_path('module', 'date_repeat') . '/date_repeat_calc.inc';
// Examples adapted from http://www.faqs.org/rfcs/rfc2445.html and
// http://www.kanzaki.com/docs/ical/rrule.html.
// Invalid value:
$start = "1997-09-02 09:00:00";
$end = "1997-09-30 09:00:00";
@ -48,7 +52,7 @@ class DateRepeatTestCase extends DrupalWebTestCase {
$result = implode(', ', $dates);
$this->assertEqual($result, $shouldbe, $rule . '; Starting ' . $start . '; results: ' . $result);
//Daily until September 24, 1997:
// Daily until September 24, 1997:
$start = "1997-09-02 09:00:00";
$end = "1997-09-30 09:00:00";
$rule = "RRULE:FREQ=DAILY;UNTIL=19970924T000000Z";
@ -58,7 +62,7 @@ class DateRepeatTestCase extends DrupalWebTestCase {
$result = implode(', ', $dates);
$this->assertEqual($result, $shouldbe, $rule . '; Starting ' . $start . '; results: ' . $result);
//Every other day - until September 30:
// Every other day - until September 30:
$start = "1997-09-02 09:00:00";
$end = "1997-09-30 09:00:00";
$rule = "RRULE:FREQ=DAILY;INTERVAL=2";
@ -68,7 +72,7 @@ class DateRepeatTestCase extends DrupalWebTestCase {
$result = implode(', ', $dates);
$this->assertEqual($result, $shouldbe, $rule . '; Starting ' . $start . '; results: ' . $result);
//Every 10 days, 2 occurrences:
// Every 10 days, 2 occurrences:
$start = "1997-09-02 09:00:00";
$end = "1997-09-30 09:00:00";
$rule = "RRULE:FREQ=DAILY;INTERVAL=10;COUNT=2";
@ -78,7 +82,7 @@ class DateRepeatTestCase extends DrupalWebTestCase {
$result = implode(', ', $dates);
$this->assertEqual($result, $shouldbe, $rule . '; Starting ' . $start . '; results: ' . $result);
//Weekly for 3 occurrences
// Weekly for 3 occurrences.
$start = "1997-09-02 09:00:00";
$end = "1997-09-30 09:00:00";
$rule = "RRULE:FREQ=WEEKLY;COUNT=3";
@ -88,7 +92,7 @@ class DateRepeatTestCase extends DrupalWebTestCase {
$result = implode(', ', $dates);
$this->assertEqual($result, $shouldbe, $rule . '; Starting ' . $start . '; results: ' . $result);
//Weekly until September 24, 1997
// Weekly until September 24, 1997
$start = "1997-09-02 09:00:00";
$end = "1997-09-30 09:00:00";
$rule = "RRULE:FREQ=WEEKLY;UNTIL=19970924T000000Z";
@ -98,7 +102,7 @@ class DateRepeatTestCase extends DrupalWebTestCase {
$result = implode(', ', $dates);
$this->assertEqual($result, $shouldbe, $rule . '; Starting ' . $start . '; results: ' . $result);
//Every other week - forever:
// Every other week - forever:
$start = "1997-09-02 09:00:00";
$end = "1997-09-30 09:00:00";
$rule = "RRULE:FREQ=WEEKLY;INTERVAL=2;WKST=SU";
@ -108,7 +112,7 @@ class DateRepeatTestCase extends DrupalWebTestCase {
$result = implode(', ', $dates);
$this->assertEqual($result, $shouldbe, $rule . '; Starting ' . $start . '; results: ' . $result);
//Weekly on Tuesday and Thursday for 4 weeks:
// Weekly on Tuesday and Thursday for 4 weeks:
$start = "1997-09-02 09:00:00";
$end = "1997-09-30 09:00:00";
$rule = "RRULE:FREQ=WEEKLY;COUNT=8;WKST=SU;BYDAY=TU,TH";
@ -118,7 +122,7 @@ class DateRepeatTestCase extends DrupalWebTestCase {
$result = implode(', ', $dates);
$this->assertEqual($result, $shouldbe, $rule . '; Starting ' . $start . '; results: ' . $result);
//Every other week on Tuesday and Thursday, for 5 occurrences:
// Every other week on Tuesday and Thursday, for 5 occurrences:
$start = "1997-09-02 09:00:00";
$end = "1997-09-30 09:00:00";
$rule = "RRULE:FREQ=WEEKLY;INTERVAL=2;COUNT=5;WKST=SU;BYDAY=TU,TH";
@ -128,7 +132,7 @@ class DateRepeatTestCase extends DrupalWebTestCase {
$result = implode(', ', $dates);
$this->assertEqual($result, $shouldbe, $rule . '; Starting ' . $start . '; results: ' . $result);
//Every other week on Monday, Wednesday and Friday until September 24, 1997,
// Every other week on Monday, Wednesday and Friday until September 24, 1997,
$start = "1997-09-02 09:00:00";
$end = "1997-09-30 09:00:00";
$rule = "RRULE:FREQ=WEEKLY;INTERVAL=2;UNTIL=19970924T000000Z;WKST=SU;BYDAY=MO,WE,FR";
@ -138,7 +142,7 @@ class DateRepeatTestCase extends DrupalWebTestCase {
$result = implode(', ', $dates);
$this->assertEqual($result, $shouldbe, $rule . '; Starting ' . $start . '; results: ' . $result);
//Monthly on the 1st Friday for 2 occurrences:
// Monthly on the 1st Friday for 2 occurrences:
$start = "1997-09-05 09:00:00";
$end = "1997-10-31 09:00:00";
$rule = "RRULE:FREQ=MONTHLY;COUNT=2;BYDAY=1FR";
@ -148,7 +152,7 @@ class DateRepeatTestCase extends DrupalWebTestCase {
$result = implode(', ', $dates);
$this->assertEqual($result, $shouldbe, $rule . '; Starting ' . $start . '; results: ' . $result);
//Monthly on the 1st Friday until December 24, 1997:
// Monthly on the 1st Friday until December 24, 1997:
$start = "1997-09-05 09:00:00";
$end = "1998-10-01 09:00:00";
$rule = "RRULE:FREQ=MONTHLY;UNTIL=19971224T000000Z;BYDAY=1FR";
@ -157,7 +161,7 @@ class DateRepeatTestCase extends DrupalWebTestCase {
$result = implode(', ', $dates);
$this->assertEqual($result, $shouldbe, $rule . '; Starting ' . $start . '; results: ' . $result);
//Every other month on the 1st and last Sunday of the month for 10 occurrences:
// Every other month on the 1st and last Sunday of the month for 10 occurrences:
$start = "1997-09-07 09:00:00";
$end = "1998-10-01 09:00:00";
$rule = "RRULE:FREQ=MONTHLY;INTERVAL=2;COUNT=10;BYDAY=1SU,-1SU";
@ -170,11 +174,11 @@ class DateRepeatTestCase extends DrupalWebTestCase {
$result = implode(', ', $dates);
$this->assertEqual($result, $shouldbe, $rule . '; Starting ' . $start . '; results: ' . $result);
//Monthly on the second to last Monday of the month for 6 months:
// Monthly on the second to last Monday of the month for 6 months:
$start = "1997-09-22 09:00:00";
$end = "1998-10-01 09:00:00";
$rule = "RRULE:FREQ=MONTHLY;COUNT=6;BYDAY=-2MO";
//==> (1997 9:00 AM EDT)September 22;October 20
// ==> (1997 9:00 AM EDT)September 22;October 20
// (1997 9:00 AM EST)November 17;December 22
// (1998 9:00 AM EST)January 19;February 16
$dates = date_repeat_calc($rule, $start, $end, array());
@ -182,7 +186,7 @@ class DateRepeatTestCase extends DrupalWebTestCase {
$result = implode(', ', $dates);
$this->assertEqual($result, $shouldbe, $rule . '; Starting ' . $start . '; results: ' . $result);
//Every Tuesday, every other month:
// Every Tuesday, every other month:
$start = "1997-09-02 09:00:00";
$end = "1998-02-01 09:00:00";
$rule = "RRULE:FREQ=MONTHLY;INTERVAL=2;BYDAY=TU";
@ -194,7 +198,7 @@ class DateRepeatTestCase extends DrupalWebTestCase {
$result = implode(', ', $dates);
$this->assertEqual($result, $shouldbe, $rule . '; Starting ' . $start . '; results: ' . $result);
//Yearly in June and July for 10 occurrences:
// Yearly in June and July for 10 occurrences:
$start = "1997-06-10 09:00:00";
$end = "2002-01-01 09:00:00";
$rule = "RRULE:FREQ=YEARLY;COUNT=10;BYMONTH=6,7";
@ -204,13 +208,13 @@ class DateRepeatTestCase extends DrupalWebTestCase {
// (2000 9:00 AM EDT)June 10;July 10
// (2001 9:00 AM EDT)June 10;July 10
// Note: Since none of the BYDAY, BYMONTHDAY or BYYEARDAY components
// are specified, the day is gotten from DTSTART
// are specified, the day is gotten from DTSTART.
$dates = date_repeat_calc($rule, $start, $end, array());
$shouldbe = '1997-06-10 09:00:00, 1997-07-10 09:00:00, 1998-06-10 09:00:00, 1998-07-10 09:00:00, 1999-06-10 09:00:00, 1999-07-10 09:00:00, 2000-06-10 09:00:00, 2000-07-10 09:00:00, 2001-06-10 09:00:00, 2001-07-10 09:00:00';
$result = implode(', ', $dates);
$this->assertEqual($result, $shouldbe, $rule . '; Starting ' . $start . '; results: ' . $result);
//Every other year on January, February, and March for 10 occurrences:
// Every other year on January, February, and March for 10 occurrences:
$start = "1997-03-10 09:00:00";
$end = "2004-01-01 09:00:00";
$rule = "RRULE:FREQ=YEARLY;INTERVAL=2;COUNT=10;BYMONTH=1,2,3";
@ -223,7 +227,7 @@ class DateRepeatTestCase extends DrupalWebTestCase {
$result = implode(', ', $dates);
$this->assertEqual($result, $shouldbe, $rule . '; Starting ' . $start . '; results: ' . $result);
//An example where the days generated makes a difference because of WKST:
// An example where the days generated makes a difference because of WKST:
$start = "1997-08-05 09:00:00";
$end = "2004-01-01 09:00:00";
$rule = "RRULE:FREQ=WEEKLY;INTERVAL=2;COUNT=4;BYDAY=TU,SU;WKST=MO";
@ -233,7 +237,7 @@ class DateRepeatTestCase extends DrupalWebTestCase {
$result = implode(', ', $dates);
$this->assertEqual($result, $shouldbe, $rule . '; Starting ' . $start . '; results: ' . $result);
//changing only WKST from MO to SU, yields different results...
// changing only WKST from MO to SU, yields different results...
$start = "1997-08-05 09:00:00";
$end = "2004-01-01 09:00:00";
$rule = "RRULE:FREQ=WEEKLY;INTERVAL=2;COUNT=4;BYDAY=TU,SU;WKST=SU";
@ -243,7 +247,7 @@ class DateRepeatTestCase extends DrupalWebTestCase {
$result = implode(', ', $dates);
$this->assertEqual($result, $shouldbe, $rule . '; Starting ' . $start . '; results: ' . $result);
//Every 18 months on the 10th thru 15th of the month for 10 occurrences:
// Every 18 months on the 10th thru 15th of the month for 10 occurrences:
$start = "1997-09-10 09:00:00";
$end = "2004-01-01 09:00:00";
$rule = "RRULE:FREQ=MONTHLY;INTERVAL=18;COUNT=10;BYMONTHDAY=10,11,12,13,14,15";
@ -254,7 +258,7 @@ class DateRepeatTestCase extends DrupalWebTestCase {
$result = implode(', ', $dates);
$this->assertEqual($result, $shouldbe, $rule . '; Starting ' . $start . '; results: ' . $result);
//Monthly on the third to the last day of the month, forever:
// Monthly on the third to the last day of the month, forever:
$start = "1997-09-28 09:00:00";
$end = "1998-03-01 09:00:00";
$rule = "RRULE:FREQ=MONTHLY;BYMONTHDAY=-3";
@ -266,7 +270,7 @@ class DateRepeatTestCase extends DrupalWebTestCase {
$result = implode(', ', $dates);
$this->assertEqual($result, $shouldbe, $rule . '; Starting ' . $start . '; results: ' . $result);
//Every Thursday in March, forever:
// Every Thursday in March, forever:
// ==> (1997 9:00 AM EST)March 13,20,27
// (1998 9:00 AM EST)March 5,12,19,26
// (1999 9:00 AM EST)March 4,11,18,25
@ -278,7 +282,7 @@ class DateRepeatTestCase extends DrupalWebTestCase {
$result = implode(', ', $dates);
$this->assertEqual($result, $shouldbe, $rule . '; Starting ' . $start . '; results: ' . $result);
//Every Thursday, but only during June, July, and August, forever:
// Every Thursday, but only during June, July, and August, forever:
// ==> (1997 9:00 AM EDT)June 5,12,19,26;July 3,10,17,24,31;August 7,14,21,28
// (1998 9:00 AM EDT)June 4,11,18,25;July 2,9,16,23,30;August 6,13,20,27
// (1999 9:00 AM EDT)June 3,10,17,24;July 1,8,15,22,29;August 5,12,19,26
@ -290,7 +294,7 @@ class DateRepeatTestCase extends DrupalWebTestCase {
$result = implode(', ', $dates);
$this->assertEqual($result, $shouldbe, $rule . '; Starting ' . $start . '; results: ' . $result);
//Monthly on the 2nd and 15th of the month for 10 occurrences:
// Monthly on the 2nd and 15th of the month for 10 occurrences:
// ==> (1997 9:00 AM EDT)September 2,15;October 2,15
// (1997 9:00 AM EST)November 2,15;December 2,15
// (1998 9:00 AM EST)January 2,15
@ -302,7 +306,7 @@ class DateRepeatTestCase extends DrupalWebTestCase {
$result = implode(', ', $dates);
$this->assertEqual($result, $shouldbe, $rule . '; Starting ' . $start . '; results: ' . $result);
//Monthly on the first and last day of the month for 10 occurrences:
// Monthly on the first and last day of the month for 10 occurrences:
// ==> (1997 9:00 AM EDT)September 30;October 1
// (1997 9:00 AM EST)October 31;November 1,30;December 1,31
// (1998 9:00 AM EST)January 1,31;February 1
@ -314,7 +318,7 @@ class DateRepeatTestCase extends DrupalWebTestCase {
$result = implode(', ', $dates);
$this->assertEqual($result, $shouldbe, $rule . '; Starting ' . $start . '; results: ' . $result);
//Every Friday the 13th, forever:
// Every Friday the 13th, forever:
$rule = "EXDATE;TZID=US-Eastern:19970902T090000";
// ==> (1998 9:00 AM EST)February 13;March 13;November 13
// (1999 9:00 AM EDT)August 13
@ -327,7 +331,7 @@ class DateRepeatTestCase extends DrupalWebTestCase {
$result = implode(', ', $dates);
$this->assertEqual($result, $shouldbe, $rule . '; Starting ' . $start . '; results: ' . $result);
//The first Saturday that follows the first Sunday of the month, forever:
// The first Saturday that follows the first Sunday of the month, forever:
// ==> (1997 9:00 AM EDT)September 13;October 11
// (1997 9:00 AM EST)November 8;December 13
// (1998 9:00 AM EST)January 10;February 7;March 7
@ -340,8 +344,8 @@ class DateRepeatTestCase extends DrupalWebTestCase {
$result = implode(', ', $dates);
$this->assertEqual($result, $shouldbe, $rule . '; Starting ' . $start . '; results: ' . $result);
//Every four years, the first Tuesday after a Monday in November,
//forever (U.S. Presidential Election day):
// Every four years, the first Tuesday after a Monday in November,
// forever (U.S. Presidential Election day):
// ==> (1996 9:00 AM EST)November 5
// (2000 9:00 AM EST)November 7
// (2004 9:00 AM EST)November 2
@ -353,7 +357,7 @@ class DateRepeatTestCase extends DrupalWebTestCase {
$result = implode(', ', $dates);
$this->assertEqual($result, $shouldbe, $rule . '; Starting ' . $start . '; results: ' . $result);
//Every 20th Monday of the year, forever:
// Every 20th Monday of the year, forever:
$start = "1997-05-19 09:00:00";
$end = "2000-01-01 09:00:00";
$rule = "RRULE:FREQ=YEARLY;BYDAY=20MO";
@ -365,7 +369,7 @@ class DateRepeatTestCase extends DrupalWebTestCase {
$result = implode(', ', $dates);
$this->assertEqual($result, $shouldbe, $rule . '; Starting ' . $start . '; results: ' . $result);
//Every Sunday in January, every other year, forever:
// Every Sunday in January, every other year, forever:
$start = "1997-01-05 09:00:00";
$end = "2001-02-01 09:00:00";
$rule = 'RRULE:FREQ=YEARLY;INTERVAL=2;BYMONTH=1;BYDAY=SU';
@ -377,7 +381,7 @@ class DateRepeatTestCase extends DrupalWebTestCase {
$result = implode(', ', $dates);
$this->assertEqual($result, $shouldbe, $rule . '; Starting ' . $start . '; results: ' . $result);
//Every Last Thursday in November, every year, five times:
// Every Last Thursday in November, every year, five times:
$start = "2014-11-27 00:00:00";
$rule = 'FREQ=YEARLY;INTERVAL=1;BYDAY=-1TH;BYMONTH=11;COUNT=5;WKST=SU';
// ==> (2014 00:00 AM EDT)November 27
@ -390,9 +394,9 @@ class DateRepeatTestCase extends DrupalWebTestCase {
$result = implode(', ', $dates);
$this->assertEqual($result, $shouldbe, $rule . '; Starting ' . $start . '; results: ' . $result);
return;
return;
//Every Thanksgiving, forever:
// Every Thanksgiving, forever:
$start = "1997-01-01 09:00:00";
$end = "2001-02-01 09:00:00";
$rule = 'RRULE:FREQ=YEARLY;INTERVAL=1;BYMONTH=11;BYDAY=4TH';
@ -404,65 +408,57 @@ return;
$result = implode(', ', $dates);
$this->assertEqual($result, $shouldbe, $rule . '; Starting ' . $start . '; results: ' . $result);
// TODO:
// BYYEARDAY, BYSETPOS,
// BYHOUR, BYMINUTE, HOURLY, MINUTELY, SECONDLY
// have not yet been implemented in date_repeat.
//Every 3rd year on the 1st, 100th and 200th day for 10 occurrences:
$date = "DTSTART;TZID=US-Eastern:19970101T090000";
$rule = "RRULE:FREQ=YEARLY;INTERVAL=3;COUNT=10;BYYEARDAY=1,100,200";
// ==> (1997 9:00 AM EST)January 1
// (1997 9:00 AM EDT)April 10;July 19
// (2000 9:00 AM EST)January 1
// (2000 9:00 AM EDT)April 9;July 18
// (2003 9:00 AM EST)January 1
// (2003 9:00 AM EDT)April 10;July 19
// (2006 9:00 AM EST)January 1
//Monday of week number 20 (where the default start of the week is Monday), forever:
$date = "DTSTART;TZID=US-Eastern:19970512T090000";
$rule = "RRULE:FREQ=YEARLY;BYWEEKNO=20;BYDAY=MO";
// ==> (1997 9:00 AM EDT)May 12
// (1998 9:00 AM EDT)May 11
// (1999 9:00 AM EDT)May 17
//The 3rd instance into the month of one of Tuesday, Wednesday or
//Thursday, for the next 3 months:
$date = "DTSTART;TZID=US-Eastern:19970904T090000";
$rule = "RRULE:FREQ=MONTHLY;COUNT=3;BYDAY=TU,WE,TH;BYSETPOS=3";
// ==> (1997 9:00 AM EDT)September 4;October 7
// (1997 9:00 AM EST)November 6
//The 2nd to last weekday of the month:
$date = "DTSTART;TZID=US-Eastern:19970929T090000";
$rule = "RRULE:FREQ=MONTHLY;BYDAY=MO,TU,WE,TH,FR;BYSETPOS=-2";
// ==> (1997 9:00 AM EDT)September 29
// (1997 9:00 AM EST)October 30;November 27;December 30
// (1998 9:00 AM EST)January 29;February 26;March 30
//Every 3 hours from 9:00 AM to 5:00 PM on a specific day:
$date = "DTSTART;TZID=US-Eastern:19970902T090000";
$rule = "RRULE:FREQ=HOURLY;INTERVAL=3;UNTIL=19970902T170000Z";
// ==> (September 2, 1997 EDT)09:00,12:00,15:00
//Every 15 minutes for 6 occurrences:
$date = "DTSTART;TZID=US-Eastern:19970902T090000";
$rule = "RRULE:FREQ=MINUTELY;INTERVAL=15;COUNT=6";
// ==> (September 2, 1997 EDT)09:00,09:15,09:30,09:45,10:00,10:15
//Every hour and a half for 4 occurrences:
$date = "DTSTART;TZID=US-Eastern:19970902T090000";
$rule = "RRULE:FREQ=MINUTELY;INTERVAL=90;COUNT=4";
// ==> (September 2, 1997 EDT)09:00,10:30;12:00;13:30
//Every 20 minutes from 9:00 AM to 4:40 PM every day:
$date = "DTSTART;TZID=US-Eastern:19970902T090000";
$rule = "RRULE:FREQ=DAILY;BYHOUR=9,10,11,12,13,14,15,16;BYMINUTE=0,20,40";
// or
$rule = "RRULE:FREQ=MINUTELY;INTERVAL=20;BYHOUR=9,10,11,12,13,14,15,16";
// ==> (September 2, 1997 EDT)9:00,9:20,9:40,10:00,10:20,16:00,16:20,16:40
// (September 3, 1997 EDT)9:00,9:20,9:40,10:00,10:20,16:00,16:20,16:40
// TODO:
// BYYEARDAY, BYSETPOS,
// BYHOUR, BYMINUTE, HOURLY, MINUTELY, SECONDLY
// have not yet been implemented in date_repeat.
// Every 3rd year on the 1st, 100th and 200th day for 10 occurrences:
$date = "DTSTART;TZID=US-Eastern:19970101T090000";
$rule = "RRULE:FREQ=YEARLY;INTERVAL=3;COUNT=10;BYYEARDAY=1,100,200";
// ==> (1997 9:00 AM EST)January 1
// (1997 9:00 AM EDT)April 10;July 19
// (2000 9:00 AM EST)January 1
// (2000 9:00 AM EDT)April 9;July 18
// (2003 9:00 AM EST)January 1
// (2003 9:00 AM EDT)April 10;July 19
// (2006 9:00 AM EST)January 1
// Monday of week number 20 (where the default start of the week is Monday), forever:
$date = "DTSTART;TZID=US-Eastern:19970512T090000";
$rule = "RRULE:FREQ=YEARLY;BYWEEKNO=20;BYDAY=MO";
// ==> (1997 9:00 AM EDT)May 12
// (1998 9:00 AM EDT)May 11
// (1999 9:00 AM EDT)May 17
// The 3rd instance into the month of one of Tuesday, Wednesday or
// Thursday, for the next 3 months:
$date = "DTSTART;TZID=US-Eastern:19970904T090000";
$rule = "RRULE:FREQ=MONTHLY;COUNT=3;BYDAY=TU,WE,TH;BYSETPOS=3";
// ==> (1997 9:00 AM EDT)September 4;October 7
// (1997 9:00 AM EST)November 6
// The 2nd to last weekday of the month:
$date = "DTSTART;TZID=US-Eastern:19970929T090000";
$rule = "RRULE:FREQ=MONTHLY;BYDAY=MO,TU,WE,TH,FR;BYSETPOS=-2";
// ==> (1997 9:00 AM EDT)September 29
// (1997 9:00 AM EST)October 30;November 27;December 30
// (1998 9:00 AM EST)January 29;February 26;March 30
// Every 3 hours from 9:00 AM to 5:00 PM on a specific day:
$date = "DTSTART;TZID=US-Eastern:19970902T090000";
$rule = "RRULE:FREQ=HOURLY;INTERVAL=3;UNTIL=19970902T170000Z";
// ==> (September 2, 1997 EDT)09:00,12:00,15:00
// Every 15 minutes for 6 occurrences:
$date = "DTSTART;TZID=US-Eastern:19970902T090000";
$rule = "RRULE:FREQ=MINUTELY;INTERVAL=15;COUNT=6";
// ==> (September 2, 1997 EDT)09:00,09:15,09:30,09:45,10:00,10:15
// Every hour and a half for 4 occurrences:
$date = "DTSTART;TZID=US-Eastern:19970902T090000";
$rule = "RRULE:FREQ=MINUTELY;INTERVAL=90;COUNT=4";
// ==> (September 2, 1997 EDT)09:00,10:30;12:00;13:30
// Every 20 minutes from 9:00 AM to 4:40 PM every day:
$date = "DTSTART;TZID=US-Eastern:19970902T090000";
$rule = "RRULE:FREQ=DAILY;BYHOUR=9,10,11,12,13,14,15,16;BYMINUTE=0,20,40";
// or.
$rule = "RRULE:FREQ=MINUTELY;INTERVAL=20;BYHOUR=9,10,11,12,13,14,15,16";
// ==> (September 2, 1997 EDT)9:00,9:20,9:40,10:00,10:20,16:00,16:20,16:40
// (September 3, 1997 EDT)9:00,9:20,9:40,10:00,10:20,16:00,16:20,16:40
}
}

View File

@ -5,6 +5,9 @@
* Test Date Repeat form.
*/
/**
* Test Date Repeat form.
*/
class DateRepeatFormTestCase extends DrupalWebTestCase {
protected $privileged_user;
@ -17,21 +20,31 @@ class DateRepeatFormTestCase extends DrupalWebTestCase {
}
/**
* Implements setUp().
* {@inheritdoc}
*/
public function setUp() {
public function setUp(array $modules = array()) {
// Load the date_repeat module.
parent::setUp('field', 'field_ui', 'date_api', 'date_repeat', 'date', 'date_popup', 'date_repeat_field');
$modules[] = 'field';
$modules[] = 'field_ui';
$modules[] = 'date_api';
$modules[] = 'date_repeat';
$modules[] = 'date';
$modules[] = 'date_popup';
$modules[] = 'date_repeat_field';
parent::setUp($modules);
// Create and log in our privileged user.
$this->privileged_user = $this->drupalCreateUser(array(
'administer content types', 'administer nodes', 'bypass node access', 'view date repeats', 'administer fields'
'administer content types', 'administer nodes', 'bypass node access', 'view date repeats', 'administer fields',
));
$this->drupalLogin($this->privileged_user);
variable_set('date_format_short', 'Y-m-d H:i');
}
/**
* Test the Date Repeat form.
*/
public function testDateRepeatForm() {
$edit = array();
$edit['name'] = 'Date';
@ -84,8 +97,7 @@ class DateRepeatFormTestCase extends DrupalWebTestCase {
$form_edit = $this->dateForm($options, 'daily_1', FALSE, 'exclude_include');
$this->verifyDateForm($form_edit);
// Weekly tests
// Weekly tests.
$form_edit = $this->dateForm($options, 'weekly_1');
$this->verifyDateForm($form_edit);
@ -110,8 +122,7 @@ class DateRepeatFormTestCase extends DrupalWebTestCase {
$form_edit = $this->dateForm($options, 'weekly_4', TRUE);
$this->verifyDateForm($form_edit);
// Monthly tests
// Monthly tests.
$form_edit = $this->dateForm($options, 'monthly_1');
$this->verifyDateForm($form_edit);
@ -148,8 +159,7 @@ class DateRepeatFormTestCase extends DrupalWebTestCase {
$form_edit = $this->dateForm($options, 'monthly_6', TRUE);
$this->verifyDateForm($form_edit);
// Yearly tests
// Yearly tests.
$form_edit = $this->dateForm($options, 'yearly_1');
$this->verifyDateForm($form_edit);
@ -197,7 +207,6 @@ class DateRepeatFormTestCase extends DrupalWebTestCase {
$edit['body[und][0][value]'] = $this->randomName(16);
$current_year = date('Y');
switch ($options) {
case 'select':
$edit['field_test[und][0][value][year]'] = $current_year;
@ -206,59 +215,67 @@ class DateRepeatFormTestCase extends DrupalWebTestCase {
$edit['field_test[und][0][value][hour]'] = '10';
$edit['field_test[und][0][value][minute]'] = '30';
break;
case 'text':
$edit['field_test[und][0][value][date]'] = format_string('!year-10-07 10:30', array('!year' => $current_year));
break;
case 'popup':
$edit['field_test[und][0][value][date]'] = format_string('!year-10-07', array('!year' => $current_year));
$edit['field_test[und][0][value][time]'] = '10:30';
break;
}
// Tests that Date repeat settings function properly
// Tests that Date repeat settings function properly.
if ($test_id !== NULL) {
$edit['field_test[und][0][show_repeat_settings]'] = TRUE;
$count = 5;
switch ($test_id) {
// Daily test cases
// Daily test cases.
case 'daily_1':
$edit['field_test[und][0][rrule][FREQ]'] = 'DAILY';
$edit['field_test[und][0][rrule][daily][byday_radios]'] = 'INTERVAL';
$edit['field_test[und][0][rrule][daily][INTERVAL_child]'] = 2;
break;
case 'daily_2':
$edit['field_test[und][0][rrule][FREQ]'] = 'DAILY';
$edit['field_test[und][0][rrule][daily][byday_radios]'] = 'every_weekday';
break;
case 'daily_3':
$edit['field_test[und][0][rrule][FREQ]'] = 'DAILY';
$edit['field_test[und][0][rrule][daily][byday_radios]'] = 'every_mo_we_fr';
break;
case 'daily_4':
$edit['field_test[und][0][rrule][FREQ]'] = 'DAILY';
$edit['field_test[und][0][rrule][daily][byday_radios]'] = 'every_tu_th';
break;
// Weekly test cases
// Weekly test cases.
case 'weekly_1':
$edit['field_test[und][0][rrule][FREQ]'] = 'WEEKLY';
$edit['field_test[und][0][rrule][weekly][BYDAY][MO]'] = TRUE;
$edit['field_test[und][0][rrule][weekly][BYDAY][WE]'] = TRUE;
$edit['field_test[und][0][rrule][weekly][BYDAY][TH]'] = TRUE;
break;
case 'weekly_2':
$edit['field_test[und][0][rrule][FREQ]'] = 'WEEKLY';
$edit['field_test[und][0][rrule][weekly][INTERVAL]'] = 1;
$edit['field_test[und][0][rrule][weekly][BYDAY][MO]'] = TRUE;
$edit['field_test[und][0][rrule][weekly][BYDAY][FR]'] = TRUE;
break;
case 'weekly_3':
$edit['field_test[und][0][rrule][FREQ]'] = 'WEEKLY';
$edit['field_test[und][0][rrule][weekly][INTERVAL]'] = 2;
$edit['field_test[und][0][rrule][weekly][BYDAY][TU]'] = TRUE;
$edit['field_test[und][0][rrule][weekly][BYDAY][TH]'] = TRUE;
break;
case 'weekly_4':
$edit['field_test[und][0][rrule][FREQ]'] = 'WEEKLY';
$edit['field_test[und][0][rrule][weekly][INTERVAL]'] = 10;
@ -269,13 +286,14 @@ class DateRepeatFormTestCase extends DrupalWebTestCase {
$edit['field_test[und][0][rrule][weekly][BYDAY][FR]'] = TRUE;
break;
// Monthly test cases
// Monthly test cases.
case 'monthly_1':
$edit['field_test[und][0][rrule][FREQ]'] = 'MONTHLY';
$edit['field_test[und][0][rrule][monthly][day_month]'] = 'BYDAY_BYMONTH';
$edit['field_test[und][0][rrule][monthly][BYDAY_BYMONTH_child][BYDAY_COUNT]'] = '+1';
$edit['field_test[und][0][rrule][monthly][BYDAY_BYMONTH_child][BYDAY_DAY]'] = 'FR';
break;
case 'monthly_2':
$edit['field_test[und][0][rrule][FREQ]'] = 'MONTHLY';
$edit['field_test[und][0][rrule][monthly][day_month]'] = 'BYDAY_BYMONTH';
@ -288,6 +306,7 @@ class DateRepeatFormTestCase extends DrupalWebTestCase {
$edit['field_test[und][0][rrule][monthly][BYDAY_BYMONTH_child][BYMONTH][9]'] = TRUE;
$edit['field_test[und][0][rrule][monthly][BYDAY_BYMONTH_child][BYMONTH][11]'] = TRUE;
break;
case 'monthly_3':
$edit['field_test[und][0][rrule][FREQ]'] = 'MONTHLY';
$edit['field_test[und][0][rrule][monthly][day_month]'] = 'BYDAY_BYMONTH';
@ -296,11 +315,13 @@ class DateRepeatFormTestCase extends DrupalWebTestCase {
$edit['field_test[und][0][rrule][monthly][BYDAY_BYMONTH_child][BYMONTH][6]'] = TRUE;
$edit['field_test[und][0][rrule][monthly][BYDAY_BYMONTH_child][BYMONTH][12]'] = TRUE;
break;
case 'monthly_4':
$edit['field_test[und][0][rrule][FREQ]'] = 'MONTHLY';
$edit['field_test[und][0][rrule][monthly][day_month]'] = 'BYMONTHDAY_BYMONTH';
$edit['field_test[und][0][rrule][monthly][BYMONTHDAY_BYMONTH_child][BYMONTHDAY]'] = '10';
break;
case 'monthly_5':
$edit['field_test[und][0][rrule][FREQ]'] = 'MONTHLY';
$edit['field_test[und][0][rrule][monthly][day_month]'] = 'BYMONTHDAY_BYMONTH';
@ -309,6 +330,7 @@ class DateRepeatFormTestCase extends DrupalWebTestCase {
$edit['field_test[und][0][rrule][monthly][BYMONTHDAY_BYMONTH_child][BYMONTH][2]'] = TRUE;
$edit['field_test[und][0][rrule][monthly][BYMONTHDAY_BYMONTH_child][BYMONTH][3]'] = TRUE;
break;
case 'monthly_6':
$edit['field_test[und][0][rrule][FREQ]'] = 'MONTHLY';
$edit['field_test[und][0][rrule][monthly][day_month]'] = 'BYMONTHDAY_BYMONTH';
@ -318,13 +340,14 @@ class DateRepeatFormTestCase extends DrupalWebTestCase {
$edit['field_test[und][0][rrule][monthly][BYMONTHDAY_BYMONTH_child][BYMONTH][6]'] = TRUE;
break;
// Yearly test cases
// Yearly test cases.
case 'yearly_1':
$edit['field_test[und][0][rrule][FREQ]'] = 'YEARLY';
$edit['field_test[und][0][rrule][yearly][day_month]'] = 'BYDAY_BYMONTH';
$edit['field_test[und][0][rrule][yearly][BYDAY_BYMONTH_child][BYDAY_COUNT]'] = '+1';
$edit['field_test[und][0][rrule][yearly][BYDAY_BYMONTH_child][BYDAY_DAY]'] = 'FR';
break;
case 'yearly_2':
$edit['field_test[und][0][rrule][FREQ]'] = 'YEARLY';
$edit['field_test[und][0][rrule][yearly][INTERVAL]'] = 2;
@ -338,6 +361,7 @@ class DateRepeatFormTestCase extends DrupalWebTestCase {
$edit['field_test[und][0][rrule][yearly][BYDAY_BYMONTH_child][BYMONTH][9]'] = TRUE;
$edit['field_test[und][0][rrule][yearly][BYDAY_BYMONTH_child][BYMONTH][11]'] = TRUE;
break;
case 'yearly_3':
$edit['field_test[und][0][rrule][FREQ]'] = 'YEARLY';
$edit['field_test[und][0][rrule][yearly][INTERVAL]'] = 3;
@ -347,11 +371,13 @@ class DateRepeatFormTestCase extends DrupalWebTestCase {
$edit['field_test[und][0][rrule][yearly][BYDAY_BYMONTH_child][BYMONTH][6]'] = TRUE;
$edit['field_test[und][0][rrule][yearly][BYDAY_BYMONTH_child][BYMONTH][12]'] = TRUE;
break;
case 'yearly_4':
$edit['field_test[und][0][rrule][FREQ]'] = 'YEARLY';
$edit['field_test[und][0][rrule][yearly][day_month]'] = 'BYMONTHDAY_BYMONTH';
$edit['field_test[und][0][rrule][yearly][BYMONTHDAY_BYMONTH_child][BYMONTHDAY]'] = '10';
break;
case 'yearly_5':
$edit['field_test[und][0][rrule][FREQ]'] = 'YEARLY';
$edit['field_test[und][0][rrule][yearly][INTERVAL]'] = 2;
@ -361,6 +387,7 @@ class DateRepeatFormTestCase extends DrupalWebTestCase {
$edit['field_test[und][0][rrule][yearly][BYMONTHDAY_BYMONTH_child][BYMONTH][2]'] = TRUE;
$edit['field_test[und][0][rrule][yearly][BYMONTHDAY_BYMONTH_child][BYMONTH][3]'] = TRUE;
break;
case 'yearly_6':
$edit['field_test[und][0][rrule][FREQ]'] = 'YEARLY';
$edit['field_test[und][0][rrule][yearly][INTERVAL]'] = 3;
@ -372,7 +399,7 @@ class DateRepeatFormTestCase extends DrupalWebTestCase {
break;
}
// Test COUNT or UNTIL (default)
// Test COUNT or UNTIL (default).
if ($is_count) {
$edit['field_test[und][0][rrule][range_of_repeat]'] = 'COUNT';
$edit['field_test[und][0][rrule][count_child]'] = $count;
@ -382,12 +409,12 @@ class DateRepeatFormTestCase extends DrupalWebTestCase {
$date = array(
'year' => $current_year + 1,
'month' => '10',
'day' => '07'
'day' => '07',
);
$edit += $this->formatDateForRRULEInputs('field_test[und][0][rrule][until_child]', $options, $date);
}
// Test date exceptions and/or additions
// Test date exceptions and/or additions.
if ($exclude_include !== NULL) {
$exclude_include_edit = array();
switch ($exclude_include) {
@ -396,25 +423,27 @@ class DateRepeatFormTestCase extends DrupalWebTestCase {
$date = array(
'year' => $current_year,
'month' => '10',
'day' => '07'
'day' => '07',
);
$exclude_include_edit += $this->formatDateForRRULEInputs('field_test[und][0][rrule][exceptions][EXDATE][0]', $options, $date);
break;
case 'include':
$exclude_include_edit['field_test[und][0][rrule][show_additions]'] = TRUE;
$date = array(
'year' => $current_year + 3,
'month' => '10',
'day' => '07'
'day' => '07',
);
$exclude_include_edit += $this->formatDateForRRULEInputs('field_test[und][0][rrule][additions][RDATE][0]', $options, $date);
break;
case 'exclude_include':
$exclude_include_edit['field_test[und][0][rrule][show_exceptions]'] = TRUE;
$date = array(
'year' => $current_year,
'month' => '10',
'day' => '07'
'day' => '07',
);
$exclude_include_edit += $this->formatDateForRRULEInputs('field_test[und][0][rrule][exceptions][EXDATE][0]', $options, $date);
@ -422,7 +451,7 @@ class DateRepeatFormTestCase extends DrupalWebTestCase {
$date = array(
'year' => $current_year + 3,
'month' => '10',
'day' => '07'
'day' => '07',
);
$exclude_include_edit += $this->formatDateForRRULEInputs('field_test[und][0][rrule][additions][RDATE][0]', $options, $date);
break;
@ -434,7 +463,7 @@ class DateRepeatFormTestCase extends DrupalWebTestCase {
$this->drupalPost('node/add/date', $edit, t('Save'));
$this->assertText($edit['body[und][0][value]'], 'Test node has been created');
// Return the settings for later use in verification
// Return the settings for later use in verification.
return $edit;
}
@ -456,7 +485,7 @@ class DateRepeatFormTestCase extends DrupalWebTestCase {
$edit = array();
$edit['fields[_add_new_field][label]'] = 'Test';
$edit['fields[_add_new_field][field_name]'] = 'test';
$edit['fields[_add_new_field][weight]'] = '-4';
$edit['fields[_add_new_field][weight]'] = '-100';
$edit['fields[_add_new_field][type]'] = $type;
$edit['fields[_add_new_field][widget_type]'] = $widget;
@ -469,9 +498,9 @@ class DateRepeatFormTestCase extends DrupalWebTestCase {
switch ($widget) {
case 'select':
case 'popup':
$instance_edit['instance[widget][settings][year_range][years_back]'] = '-5';
$instance_edit['instance[widget][settings][year_range][years_forward]'] = '+5';
break;
$instance_edit['instance[widget][settings][year_range][years_back]'] = '-5';
$instance_edit['instance[widget][settings][year_range][years_forward]'] = '+5';
break;
}
if ($display_all_day) {
@ -510,6 +539,7 @@ class DateRepeatFormTestCase extends DrupalWebTestCase {
$return["{$form_field_name}[datetime][month]"] = $date['month'];
$return["{$form_field_name}[datetime][day]"] = ltrim($date['day'], '0');
break;
case 'text':
case 'popup':
$return["{$form_field_name}[datetime][date]"] = "{$date['year']}-{$date['month']}-{$date['day']}";
@ -518,4 +548,5 @@ class DateRepeatFormTestCase extends DrupalWebTestCase {
return $return;
}
}

View File

@ -1,4 +1,4 @@
Date Repeat Field
The functionality to integrate the Date Repeat API into date fields is being moved into this module,
which can then be enabled or disabled, depending on whether repeating date fields are needed.
-----------------
Creates the option of repeating date fields and manages Date fields that use
the Date Repeat API.

View File

@ -1,3 +1,8 @@
/**
* @file
* Custom CSS for the Date Repeat Field module.
*/
.date-repeat-input {
float: left; /* LTR */
margin-right: 5px; /* LTR */

View File

@ -1,4 +1,5 @@
<?php
/**
* @file
* Handling of devel generate functionality for repeating dates.
@ -6,11 +7,9 @@
/**
* Implements hook_date_field_insert().
*
* A substitute for hook_devel_generate to handle repeating dates.
*/
function date_repeat_field_date_field_insert(&$items, $context) {
// A substitute for hook_devel_generate to handle repeating dates.
$entity_type = $context['entity_type'];
$entity = $context['entity'];
$field = $context['field'];
@ -103,7 +102,6 @@ function date_repeat_field_date_field_insert(&$items, $context) {
$freq = $options[$freq];
$form_values['FREQ'] = $freq;
$form_values['BYDAY'] = array($dow);
break;
}
$form_values['INTERVAL'] = $interval;
@ -123,7 +121,6 @@ function date_repeat_field_date_field_insert(&$items, $context) {
default:
$period = 'day';
break;
}
$form_values['UNTIL'] = array();
@ -135,11 +132,13 @@ function date_repeat_field_date_field_insert(&$items, $context) {
$values = date_repeat_build_dates($field, $item, $rrule, $form_values);
$items += $values;
}
/**
* Generate a random content keys.
*
* @return string
* A random string generated by mt_rand().
*/
function date_content_generate_key($array) {
$keys = array_keys($array);
@ -151,8 +150,9 @@ function date_content_generate_key($array) {
/**
* Helper function for BYDAY options.
*
* Creates options like -1SU and 2TU
* Omit options that won't find many matches, like 5th Sunday.
* @return array
* Creates options like -1SU and 2TU. Omits options that won't find many
* matches, like 5th Sunday.
*/
function date_content_repeat_dow_options() {
$options = array();

View File

@ -1,15 +1,16 @@
name = Date Repeat Field
description = Creates the option of Repeating date fields and manages Date fields that use the Date Repeat API.
dependencies[] = date_api
dependencies[] = date
dependencies[] = date_repeat
stylesheets[all][] = date_repeat_field.css
package = Date/Time
core = 7.x
; Information added by Drupal.org packaging script on 2017-04-07
version = "7.x-2.10"
dependencies[] = date:date_api
dependencies[] = date:date
dependencies[] = date:date_repeat
stylesheets[all][] = date_repeat_field.css
; Information added by Drupal.org packaging script on 2021-03-05
version = "7.x-2.11"
core = "7.x"
project = "date"
datestamp = "1491562090"
datestamp = "1614952728"

View File

@ -2,7 +2,7 @@
/**
* @file
* Creates the option of Repeating date fields and manages Date fields that use the Date Repeat API.
* Creates the option of Repeating Date fields and manages Date Repeat fields.
*
* The Repeating functionality is pretty tightly intermingled with other code,
* so the process of pulling it out into this module will happen gradually.
@ -32,7 +32,7 @@ function date_repeat_field_theme() {
'item' => NULL,
'entity_type' => NULL,
'entity' => NULL,
'dates' => NULL
'dates' => NULL,
),
'function' => 'theme_date_repeat_display',
),
@ -44,9 +44,9 @@ function date_repeat_field_theme() {
/**
* Theme the human-readable description for a Date Repeat rule.
*
* TODO -
* add in ways to store the description in the date so it isn't regenerated
* over and over and find a way to allow description to be shown or hidden.
* @todo Add in ways to store the description in the date so it isn't
* regenerated over and over and find a way to allow description to be shown or
* hidden.
*/
function theme_date_repeat_display($vars) {
$field = $vars['field'];
@ -62,10 +62,9 @@ function theme_date_repeat_display($vars) {
/**
* Implements hook_menu().
*
* Add menu tabs to display pages with details about repeating date values.
*/
function date_repeat_field_menu() {
// Add menu tabs to display pages with details about repeating date values.
$items = array();
$values = date_repeat_field_bundles();
@ -126,7 +125,7 @@ function date_repeat_field_permission() {
*
* @return bool
* Return TRUE if there is at least one date field attached to this entity,
* and the current user has the permission 'view date repeats'; FALSE otherwise.
* and the current user has the permission 'view date repeats'.
*/
function date_repeat_field_show($entity_type = 'node', $entity = NULL) {
if (!user_access('view date repeats')) {
@ -181,7 +180,7 @@ function date_repeat_field_page($entity_type = 'node', $entity = NULL) {
}
/**
* Return an array of all entity types and bundles that have repeating date fields.
* All entity types and bundles that have repeating date fields.
*/
function date_repeat_field_bundles() {
$values = array();
@ -224,7 +223,6 @@ function date_is_repeat_field($field, $instance = NULL) {
* Implements hook_date_field_insert_alter().
*/
function date_repeat_field_date_field_insert_alter(&$items, $context) {
$entity = $context['entity'];
$field = $context['field'];
$instance = $context['instance'];
@ -248,16 +246,16 @@ function date_repeat_field_date_field_insert_alter(&$items, $context) {
* Implements hook_date_field_update_alter().
*/
function date_repeat_field_date_field_update_alter(&$items, $context) {
// If an RRULE with a frequency of NONE made it this far, unset it.
if (!empty($items[0]['rrule']) && strpos($items[0]['rrule'], 'FREQ=NONE')) {
$items[0]['rrule'] = NULL;
}
// If you have a repeating date field on a user and don't check the box to repeat it,
// we end up with $items[0]['rrule'] = array('additions' => '', 'exceptions' => ''));
// This will clean it up by getting rid of those bogus values.
// @TODO Figure out where that's coming from. It doesn't happen on nodes.
// If you have a repeating date field on a user and don't check the box to
// repeat it, we end up with $items[0]['rrule'] = array('additions' => '',
// 'exceptions' => '')); This will clean it up by getting rid of those bogus
// values.
// @todo Figure out where that's coming from. It doesn't happen on nodes.
if (!empty($items[0]['rrule']) && is_array($items[0]['rrule'])) {
$items[0]['rrule'] = NULL;
}
@ -267,7 +265,6 @@ function date_repeat_field_date_field_update_alter(&$items, $context) {
* Implements hook_field_widget_form_alter().
*/
function date_repeat_field_field_widget_form_alter(&$element, &$form_state, $context) {
$field = $context['field'];
$instance = $context['instance'];
$items = $context['items'];
@ -298,8 +295,7 @@ function date_repeat_field_field_widget_form_alter(&$element, &$form_state, $con
/**
* Validation for date repeat form element.
*
* Create multiple values from the RRULE results.
* Lots more work needed here.
* Create multiple values from the RRULE results. Lots more work needed here.
*/
function date_repeat_field_widget_validate($element, &$form_state) {
$field = field_widget_field($element, $form_state);
@ -314,9 +310,9 @@ function date_repeat_field_widget_validate($element, &$form_state) {
// If the widget has been hidden by #access, the RRULE will still be in its
// original string form here. Nothing to process.
if (date_hidden_element($element)) {
// If this was a hidden repeating date, we lost all the repeating values in the widget processing.
// Add them back here if that happened since we are skipping the re-creation of those values.
// If this was a hidden repeating date, we lost all the repeating values in
// the widget processing. Add them back here if that happened since we are
// skipping the re-creation of those values.
if (!empty($form_state['storage']['date_items'][$field_name])) {
array_pop($element['#parents']);
form_set_value($element, $form_state['storage']['date_items'][$field_name][$langcode], $form_state);
@ -327,15 +323,17 @@ function date_repeat_field_widget_validate($element, &$form_state) {
module_load_include('inc', 'date_repeat', 'date_repeat_form');
$instance = field_widget_instance($element, $form_state);
// Here 'values' returns an array of input values, which includes the original RRULE, as a string.
// and 'input' returns an array of the form elements created by the repeating date widget, with
// RRULE values as an array of the selected elements and their chosen values.
// Here 'values' returns an array of input values, which includes the
// original RRULE, as a string. and 'input' returns an array of the form
// elements created by the repeating date widget, with RRULE values as an
// array of the selected elements and their chosen values.
$item = drupal_array_get_nested_value($form_state['values'], $element['#parents'], $input_exists);
$input = drupal_array_get_nested_value($form_state['input'], $element['#parents'], $input_exists);
$rrule_values = date_repeat_merge($input['rrule'], $element['rrule']);
// If no repeat information was set, treat this as a normal, non-repeating value.
// If no repeat information was set, treat this as a normal, non-repeating
// value.
if ($rrule_values['FREQ'] == 'NONE' || empty($input['show_repeat_settings'])) {
$item['rrule'] = NULL;
form_set_value($element, $item, $form_state);
@ -373,6 +371,16 @@ function date_repeat_field_widget_validate($element, &$form_state) {
form_set_error($error_field_count, t("Missing value in 'Range of repeat'. (COUNT).", array(), array('context' => 'Date repeat')));
}
$error_field_count = $error_field_base . '][rrule][weekly][INTERVAL';
if (isset($rrule_values['INTERVAL']) && isset($rrule_values['FREQ']) && !empty($item['rrule']) && $rrule_values['FREQ'] == 'WEEKLY' && $rrule_values['INTERVAL'] == '') {
form_set_error($error_field_count, t('Missing value in Repeats Every # of weeks. (INTERVAL).', array(), array('context' => 'Date repeat')));
}
$error_field_count = $error_field_base . '][rrule][daily][INTERVAL_child';
if (isset($rrule_values['INTERVAL']) && isset($rrule_values['FREQ']) && !empty($item['rrule']) && $rrule_values['FREQ'] == 'DAILY' && $rrule_values['INTERVAL'] == '') {
form_set_error($error_field_count, t('Missing value in Repeats Every # of days. (INTERVAL).', array(), array('context' => 'Date repeat')));
}
if (form_get_errors()) {
return;
}
@ -380,10 +388,8 @@ function date_repeat_field_widget_validate($element, &$form_state) {
// If the rule, the start date, or the end date have changed, re-calculate
// the repeating dates, wipe out the previous values, and populate the
// field with the new values.
$rrule = $item['rrule'];
if (!empty($rrule)) {
// Avoid undefined index problems on dates that don't have all parts.
$possible_items = array('value', 'value2', 'timezone', 'offset', 'offset2');
foreach ($possible_items as $key) {
@ -392,8 +398,9 @@ function date_repeat_field_widget_validate($element, &$form_state) {
}
}
// We only collect a date for UNTIL, but we need it to be inclusive,
// so force it to a full datetime element at the last possible second of the day.
// We only collect a date for UNTIL, but we need it to be inclusive, so
// force it to a full datetime element at the last possible second of the
// day.
if (!empty($rrule_values['UNTIL'])) {
$gran = array('year', 'month', 'day', 'hour', 'minute', 'second');
$rrule_values['UNTIL']['datetime'] .= ' 23:59:59';
@ -403,17 +410,17 @@ function date_repeat_field_widget_validate($element, &$form_state) {
$value = date_repeat_build_dates($rrule, $rrule_values, $field, $item);
// Unset the delta value of the parents.
array_pop($element['#parents']);
// Set the new delta values for this item to the array of values returned by the repeat rule.
// Set the new delta values for this item to the array of values returned
// by the repeat rule.
form_set_value($element, $value, $form_state);
}
}
/**
* Implements the form after_build().
*
* Remove the 'Add more' elements from a repeating date form.
*/
function date_repeat_after_build(&$element, &$form_state) {
// Remove the 'Add more' elements from a repeating date form.
foreach ($form_state['storage']['repeat_fields'] as $field_name => $parents) {
// Remove unnecessary items in the form added by the Add more handling.
$value = drupal_array_get_nested_value($element, $parents);
@ -429,12 +436,10 @@ function date_repeat_after_build(&$element, &$form_state) {
/**
* Helper function to build repeating dates from a $node_field.
*
* Pass in either the RRULE or the $form_values array for the RRULE,
* whichever is missing will be created when needed.
* Pass in either the RRULE or the $form_values array for the RRULE, whichever
* is missing will be created when needed.
*/
// @codingStandardsIgnoreStart
function date_repeat_build_dates($rrule = NULL, $rrule_values = NULL, $field, $item) {
// @codingStandardsIgnoreEnd
include_once DRUPAL_ROOT . '/' . drupal_get_path('module', 'date_api') . '/date_api_ical.inc';
$field_name = $field['field_name'];
@ -445,9 +450,9 @@ function date_repeat_build_dates($rrule = NULL, $rrule_values = NULL, $field, $i
$rrule_values = date_ical_parse_rrule(NULL, $rrule);
}
// By the time we get here, the start and end dates have been
// adjusted back to UTC, but we want localtime dates to do
// things like '+1 Tuesday', so adjust back to localtime.
// By the time we get here, the start and end dates have been adjusted back to
// UTC, but we want localtime dates to do things like '+1 Tuesday', so adjust
// back to localtime.
$timezone = date_get_timezone($field['settings']['tz_handling'], $item['timezone']);
$timezone_db = date_get_timezone_db($field['settings']['tz_handling']);
$start = new DateObject($item['value'], $timezone_db, date_type_format($field['type']));
@ -497,13 +502,13 @@ function date_repeat_build_dates($rrule = NULL, $rrule_values = NULL, $field, $i
$dates = date_repeat_calc($rrule, $start_datetime, $end_datetime, $exceptions, $timezone, $additions);
$value = array();
foreach ($dates as $delta => $date) {
// date_repeat_calc always returns DATE_DATETIME dates, which is
// not necessarily $field['type'] dates.
// Convert returned dates back to db timezone before storing.
// date_repeat_calc always returns DATE_DATETIME dates, which is not
// necessarily $field['type'] dates. Convert returned dates back to db
// timezone before storing.
$date_start = new DateObject($date, $timezone, DATE_FORMAT_DATETIME);
$date_start->limitGranularity($field['settings']['granularity']);
date_timezone_set($date_start, timezone_open($timezone_db));
$date_end = clone($date_start);
$date_end = clone $date_start;
date_modify($date_end, '+' . $duration . ' seconds');
$value[$delta] = array(
'value' => date_format($date_start, date_type_format($field['type'])),
@ -520,20 +525,17 @@ function date_repeat_build_dates($rrule = NULL, $rrule_values = NULL, $field, $i
/**
* Implements hook_date_combo_process_alter().
*
* This hook lets us make changes to the date_combo element.
*/
function date_repeat_field_date_combo_process_alter(&$element, &$form_state, $context) {
// This hook lets us make changes to the date_combo element.
$field = $context['field'];
$instance = $context['instance'];
$field_name = $element['#field_name'];
$delta = $element['#delta'];
// Add a date repeat form element, if needed.
// We delayed until this point so we don't bother adding it to hidden fields.
// Add a date repeat form element, if needed. We delayed until this point so
// we don't bother adding it to hidden fields.
if (date_is_repeat_field($field, $instance)) {
$item = $element['#value'];
$element['rrule'] = array(
'#type' => 'date_repeat_rrule',
@ -551,26 +553,22 @@ function date_repeat_field_date_combo_process_alter(&$element, &$form_state, $co
'#weight' => $instance['widget']['weight'] + .4,
);
}
}
/**
* Implements hook_date_combo_pre_validate_alter().
*
* This hook lets us alter the element or the form_state before the rest
* of the date_combo validation gets fired.
*/
function date_repeat_field_date_combo_pre_validate_alter(&$element, &$form_state, $context) {
// This hook lets us alter the element or the form_state before the rest of
// the date_combo validation gets fired.
// Just a placeholder for now.
}
/**
* Implements hook_field_info_alter().
*
* This Field API hook lets us add a new setting to the fields.
*/
function date_repeat_field_field_info_alter(&$info) {
// This Field API hook lets us add a new setting to the fields.
$info['date']['settings'] += array(
'repeat' => 0,
);
@ -584,11 +582,9 @@ function date_repeat_field_field_info_alter(&$info) {
/**
* Implements hook_field_formatter_info_alter().
*
* This hook lets us add settings to the formatters.
*/
function date_repeat_field_field_formatter_info_alter(&$info) {
// This hook lets us add settings to the formatters.
if (isset($info['date_default'])) {
$info['date_default']['settings'] += array(
'show_repeat_rule' => 'show',
@ -598,11 +594,9 @@ function date_repeat_field_field_formatter_info_alter(&$info) {
/**
* Implements hook_field_widget_info_alter().
*
* This Field API hook lets us add a new setting to the widgets.
*/
function date_repeat_field_field_widget_info_alter(&$info) {
// This Field API hook lets us add a new setting to the widgets.
$info['date_text']['settings'] += array(
'repeat_collapsed' => 0,
);
@ -618,11 +612,9 @@ function date_repeat_field_field_widget_info_alter(&$info) {
/**
* Implements hook_date_field_settings_form_alter().
*
* This hook lets us alter the field settings form.
*/
function date_repeat_field_date_field_settings_form_alter(&$form, $context) {
// This hook lets us alter the field settings form.
$field = $context['field'];
$instance = $context['instance'];
$has_data = $context['has_data'];
@ -649,7 +641,8 @@ function date_repeat_field_form_field_ui_field_edit_form_alter(&$form, &$form_st
return;
}
// If using repeating dates, override the Field module's handling of the multiple values option.
// If using repeating dates, override the Field module's handling of the
// multiple values option.
if (date_is_repeat_field($field, $instance)) {
$form['field']['cardinality']['#disabled'] = TRUE;
$form['field']['cardinality']['#value'] = FIELD_CARDINALITY_UNLIMITED;
@ -659,7 +652,7 @@ function date_repeat_field_form_field_ui_field_edit_form_alter(&$form, &$form_st
}
/**
* Ensure the cardinality gets updated if the option to make a date repeating is checked.
* Ensure the cardinality is updated if the date can repeat.
*/
function date_repeat_field_set_cardinality($element, &$form_state) {
if (!empty($form_state['values']['field']['settings']['repeat'])) {
@ -669,20 +662,17 @@ function date_repeat_field_set_cardinality($element, &$form_state) {
/**
* Implements hook_date_field_instance_settings_form_alter().
*
* This hook lets us alter the field instance settings form.
*/
function date_repeat_field_date_field_instance_settings_form_alter(&$form, $context) {
// Just a placeholder for now.
// This hook lets us alter the field instance settings form. Just a
// placeholder for now.
}
/**
* Implements hook_date_field_widget_settings_form_alter().
*
* This hook lets us alter the field widget settings form.
*/
function date_repeat_field_date_field_widget_settings_form_alter(&$form, $context) {
// This hook lets us alter the field widget settings form.
$field = $context['field'];
$instance = $context['instance'];
@ -692,7 +682,7 @@ function date_repeat_field_date_field_widget_settings_form_alter(&$form, $contex
'#default_value' => 1,
'#options' => array(
0 => t('Expanded', array(), array('context' => 'Date repeat')),
1 => t('Collapsed', array(), array('context' => 'Date repeat'))
1 => t('Collapsed', array(), array('context' => 'Date repeat')),
),
'#title' => t('Repeat display', array(), array('context' => 'Date repeat')),
'#description' => t("Should the repeat options form start out expanded or collapsed? Set to 'Collapsed' to make those options less obtrusive.", array(), array('context' => 'Date repeat')),
@ -703,11 +693,9 @@ function date_repeat_field_date_field_widget_settings_form_alter(&$form, $contex
/**
* Implements hook_date_field_foramatter_settings_form_alter().
*
* This hook lets us alter the field formatter settings form.
*/
function date_repeat_field_date_field_formatter_settings_form_alter(&$form, &$form_state, $context) {
// This hook lets us alter the field formatter settings form.
$field = $context['field'];
$instance = $context['instance'];
$view_mode = $context['view_mode'];
@ -730,11 +718,9 @@ function date_repeat_field_date_field_formatter_settings_form_alter(&$form, &$fo
/**
* Implements hook_date_field_foramatter_settings_summary_alter().
*
* This hook lets us alter the field formatter settings summary.
*/
function date_repeat_field_date_field_formatter_settings_summary_alter(&$summary, $context) {
// This hook lets us alter the field formatter settings summary.
$field = $context['field'];
$instance = $context['instance'];
$view_mode = $context['view_mode'];

View File

@ -10,10 +10,11 @@
*
* @see date_tools_change_type_form_validate()
* @see date_tools_change_type_form_submit()
*
* @todo This is broken, still needs to be adjusted for the D6->D7 changes.
*/
function date_tools_change_type_form() {
$form = array();
// This is broken, still needs to be adjusted for the D6->D7 changes.
drupal_set_message(t('This operation does not yet work for the Drupal 7 version.'), 'error');
return $form;
$fields = content_fields();
@ -31,7 +32,7 @@ function date_tools_change_type_form() {
$date_options[$labels[$field['type']]][$field_name] = t('Field @label (@field_name)', array(
'@label' => $field['widget']['label'],
'@field_name' => $field_name,
'@type' => $labels[$field['type']]
'@type' => $labels[$field['type']],
));
}
}
@ -94,13 +95,11 @@ function date_tools_change_type_form_submit($form, &$form_state) {
$labels[$type] = $info['label'];
}
// Is there any data in this field? If not, we can
// skip some steps.
// Is there any data in this field? If not, we can skip some steps.
$has_data = db_query("SELECT COUNT(*) FROM {" . $table . "}")->fetchField();
// Create a backup copy of the original values.
// The values are going to get corrupted when we
// change the column type.
// Create a backup copy of the original values. The values are going to get
// corrupted when we change the column type.
if ($has_data) {
$temp_table = $table . '_temp';
db_query("CREATE TABLE {" . $temp_table . "} SELECT * FROM {" . $table . "}");
@ -115,7 +114,9 @@ function date_tools_change_type_form_submit($form, &$form_state) {
// If there's no data to update, we're finished.
if (!$has_data) {
drupal_set_message(t('The field @field_name has been changed from @old_type to @new_type.', array(
'@field_name' => $field['widget']['label'], '@old_type' => $labels[$old_type], '@new_type' => $labels[$new_type])));
'@field_name' => $field['widget']['label'],
'@old_type' => $labels[$old_type],
'@new_type' => $labels[$new_type])));
return;
}
@ -190,6 +191,6 @@ function date_tools_change_type_form_submit($form, &$form_state) {
drupal_set_message(t('The field @field_name has been changed from @old_type to @new_type.', array(
'@field_name' => $field['widget']['label'],
'@old_type' => $labels[$old_type],
'@new_type' => $labels[$new_type]
'@new_type' => $labels[$new_type],
)));
}

View File

@ -1,14 +1,16 @@
name = Date Tools
description = Tools to import and auto-create dates and calendars.
dependencies[] = date
package = Date/Time
core = 7.x
configure = admin/config/date/tools
files[] = tests/date_tools.test
; Information added by Drupal.org packaging script on 2017-04-07
version = "7.x-2.10"
dependencies[] = date:date
; Test coverage.
files[] = tests/DateToolsTestCase.test
; Information added by Drupal.org packaging script on 2021-03-05
version = "7.x-2.11"
core = "7.x"
project = "date"
datestamp = "1491562090"
datestamp = "1614952728"

View File

@ -2,7 +2,7 @@
/**
* @file
* @todo.
* Primary hook implementations for the Date Tools module
*/
/**
@ -17,11 +17,10 @@ function date_tools_help($section, $arg) {
return '<p>' . t('Change a date field from one type to another. Very experimental, use at your own risk!') . '</p>';
case 'admin/config/date/tools/date_wizard':
$output = t("Fill out the following form to auto-create a date content type, with a datetime field and matching pre-configured calendar. If the calendar module is enabled and the option to create a calendar is chosen, a calendar and upcoming events block will be created, an ical feed will be added to the calendar. Nodes created from this new content type will include a link to the calendar, and the calendar will have a link to the 'add new date' form. You can also add new date fields to an existing content type by entering the existing content type name instead of creating a new one.") .
'</p><p>' .
t('Only a limited set of options are displayed here to make this easy to set up. Once the date has been created you will be able to make other changes to the date settings and add other fields to your new content type on the Manage fields screen. You can also make changes to the calendar on the Views edit page.') .
'</p>';
$output = t("Fill out the following form to auto-create a date content type, with a datetime field and matching pre-configured calendar. If the calendar module is enabled and the option to create a calendar is chosen, a calendar and upcoming events block will be created, an ical feed will be added to the calendar. Nodes created from this new content type will include a link to the calendar, and the calendar will have a link to the 'add new date' form. You can also add new date fields to an existing content type by entering the existing content type name instead of creating a new one.");
$output .= '</p><p>';
$output .= t('Only a limited set of options are displayed here to make this easy to set up. Once the date has been created you will be able to make other changes to the date settings and add other fields to your new content type on the Manage fields screen. You can also make changes to the calendar on the Views edit page.');
$output .= '</p>';
return $output;
}
}
@ -41,12 +40,11 @@ function date_tools_permission() {
* Implements hook_menu().
*/
function date_tools_menu() {
$items = array();
$items['admin/config/date/tools'] = array(
'title' => 'Date Tools',
'title' => 'Date Tools',
'description' => 'Date Wizard and other tools to manage and create dates and calendars. ',
'access arguments' => array('administer date tools'),
'access arguments' => array('administer date tools'),
'page callback' => 'date_tools_page',
);
$items['admin/config/date/tools/about'] = array(
@ -68,19 +66,15 @@ function date_tools_menu() {
'file' => 'date_tools.wizard.inc',
);
// @codingStandardsIgnoreStart
/**
$items['admin/config/date/tools/change'] = array(
'title' => 'Change type',
'access arguments' => array('administer date tools'),
'page callback' => 'drupal_get_form',
'page arguments' => array('date_tools_change_type_form'),
'type' => MENU_LOCAL_TASK,
'weight' => 3,
'file' => 'date_tools.change_type.inc',
);
*/
// @codingStandardsIgnoreEnd
// $items['admin/config/date/tools/change'] = array(
// 'title' => 'Change type',
// 'access arguments' => array('administer date tools'),
// 'page callback' => 'drupal_get_form',
// 'page arguments' => array('date_tools_change_type_form'),
// 'type' => MENU_LOCAL_TASK,
// 'weight' => 3,
// 'file' => 'date_tools.change_type.inc',
// );
return $items;
}
@ -89,9 +83,5 @@ function date_tools_menu() {
* Main Date Tools page.
*/
function date_tools_page() {
$content = '';
$content .= t('Dates and calendars can be complicated to set up. The !date_wizard makes it easy to create a simple date content type and related calendar.', array('!date_wizard' => l(t('Date wizard'), 'admin/config/date/tools/date_wizard')));
return $content;
return t('Dates and calendars can be complicated to set up. The !date_wizard makes it easy to create a simple date content type and related calendar.', array('!date_wizard' => l(t('Date wizard'), 'admin/config/date/tools/date_wizard')));
}

View File

@ -7,8 +7,6 @@
/**
* Implements hook_form().
*
* @todo.
*/
function date_tools_wizard_form() {
$form = array();
@ -112,6 +110,12 @@ function date_tools_wizard_form() {
'#title' => t('Date timezone handling'),
'#description' => t("Timezone handling should be set to 'none' for granularity without time elements."),
);
$form['field']['advanced']['weight'] = array(
'#type' => 'textfield',
'#default_value' => '-4',
'#title' => t('Weight'),
'#description' => t("Set the field weight."),
);
$form['calendar'] = array(
'#type' => 'select',
'#default_value' => module_exists('calendar'),
@ -131,9 +135,7 @@ function date_tools_wizard_form() {
}
/**
* Form validate.
*
* @todo.
* Form validation.
*/
function date_tools_wizard_form_validate(&$form, &$form_state) {
$bundle = $form_state['values']['bundle'];
@ -173,8 +175,6 @@ function date_tools_wizard_form_validate(&$form, &$form_state) {
/**
* Form submit.
*
* @todo.
*/
function date_tools_wizard_form_submit(&$form, &$form_state) {
$view_name = date_tools_wizard_build($form_state['values']);
@ -189,8 +189,6 @@ function date_tools_wizard_form_submit(&$form, &$form_state) {
/**
* Wizard build.
*
* @todo.
*/
function date_tools_wizard_build($form_values) {
extract($form_values);
@ -198,9 +196,8 @@ function date_tools_wizard_build($form_values) {
$field_name = 'field_' . $field_name;
$base_table = 'node';
// Create a node type if it doesn't already exist.
// This makes it possible to add additional date fields to
// an existing type.
// Create a node type if it doesn't already exist. This makes it possible to
// add additional date fields to an existing type.
$types = node_type_get_names();
$type_settings = array();
if (!array_key_exists($bundle, $types)) {
@ -238,7 +235,7 @@ function date_tools_wizard_build($form_values) {
'label' => $label,
'bundle' => $bundle,
// Move the date right below the title.
'weight' => -4,
'weight' => $weight,
'widget' => array(
'type' => $widget_type,
// Increment for minutes and seconds, can be 1, 5, 10, 15, or 30.
@ -252,7 +249,7 @@ function date_tools_wizard_build($form_values) {
'label_position' => 'above',
'repeat_collapsed' => 0,
),
'weight' => -4,
'weight' => $weight,
),
'settings' => array(
'default_value' => 'now',
@ -295,8 +292,8 @@ function date_tools_wizard_build($form_values) {
$instance = field_create_instance($instance);
$view_name = 'calendar_node_' . $field_name;
field_info_cache_clear(TRUE);
field_cache_clear(TRUE);
field_info_cache_clear();
field_cache_clear();
drupal_set_message(t('Your date field @name has been created.', array('@name' => $label)));
@ -305,8 +302,6 @@ function date_tools_wizard_build($form_values) {
/**
* Includes handler.
*
* @todo.
*/
function date_tools_wizard_include() {
module_load_include('inc', 'node', 'content_types');
@ -317,8 +312,6 @@ function date_tools_wizard_include() {
/**
* Implements hook_field_types().
*
* @todo.
*/
function date_tools_wizard_field_types() {
$field_types = array();
@ -330,7 +323,6 @@ function date_tools_wizard_field_types() {
/**
* Implements hook_widget_types().
* @todo.
*/
function date_tools_wizard_widget_types() {
$widget_types = array();
@ -344,8 +336,6 @@ function date_tools_wizard_widget_types() {
/**
* Tz handler.
*
* @todo.
*/
function date_tools_wizard_tz_handling() {
include_once drupal_get_path('module', 'date') . '/date_admin.inc';
@ -354,11 +344,8 @@ function date_tools_wizard_tz_handling() {
/**
* Create date tools wizard content type.
*
* @todo.
*/
function date_tools_wizard_create_content_type($name, $bundle, $description, $type_settings = array()) {
date_tools_wizard_include();
// Create the content type.
@ -427,5 +414,4 @@ function date_tools_wizard_create_content_type($name, $bundle, $description, $ty
),
);
field_create_instance($instance);
}

View File

@ -0,0 +1,124 @@
<?php
/**
* @file
* Tests for Date Tools.
*/
/**
* Tests for Date Tools.
*/
class DateToolsTestCase extends DrupalWebTestCase {
protected $privileged_user;
/**
* @todo.
*/
public static function getInfo() {
return array(
'name' => 'Date Tools',
'description' => 'Test Date Wizard and other Date Tools.',
'group' => 'Date',
);
}
/**
* {@inheritdoc}
*/
public function setUp(array $modules = array()) {
$modules[] = 'field';
$modules[] = 'field_ui';
$modules[] = 'date_api';
$modules[] = 'date';
$modules[] = 'date_popup';
$modules[] = 'date_tools';
parent::setUp($modules);
// Create and log in our privileged user.
$perms = array(
'administer content types',
'administer nodes',
'bypass node access',
'administer date tools',
'administer fields',
);
$this->privileged_user = $this->drupalCreateUser($perms);
$this->drupalLogin($this->privileged_user);
variable_set('date_format_long', 'D, m/d/Y - H:i');
}
/**
* Creates a date field using the Date Wizard.
*/
public function testTools() {
$form_values = array(
'label' => 'Test',
'field_type' => 'datetime',
'widget_type' => 'date_select',
'todate' => '',
);
$this->createDateWizard($form_values);
$this->dateForm($options = 'select');
$this->assertText('Thu, 10/07/2010 - 10:30', 'Found the correct date for the Date Wizard datetime field using the date_select widget.');
$this->deleteDateField();
}
/**
* Tests that date field functions properly.
*/
function dateForm($options) {
$edit = array();
$edit['title'] = $this->randomName(8);
$edit['body[und][0][value]'] = $this->randomName(16);
if ($options == 'select') {
$edit['field_test[und][0][value][year]'] = '2010';
$edit['field_test[und][0][value][month]'] = '10';
$edit['field_test[und][0][value][day]'] = '7';
$edit['field_test[und][0][value][hour]'] = '10';
$edit['field_test[und][0][value][minute]'] = '30';
}
elseif ($options == 'text') {
$edit['field_test[und][0][value][date]'] = '10/07/2010 - 10:30';
}
elseif ($options == 'popup') {
$edit['field_test[und][0][value][date]'] = '10/07/2010';
$edit['field_test[und][0][value][time]'] = '10:30';
}
$this->drupalPost('node/add/story', $edit, t('Save'));
$this->assertText($edit['body[und][0][value]'], 'Test node has been created');
}
/**
* @todo
*/
function deleteDateField() {
$this->drupalGet('admin/structure/types/manage/story/fields');
$this->clickLink('delete');
$this->drupalPost(NULL, NULL, t('Delete'));
$this->assertText('The field Test has been deleted from the Story content type.', 'Removed date field.');
}
/**
* Creates a date field using the Date Wizard.
*/
function createDateWizard($edit) {
$edit += array(
'bundle' => 'story',
'name' => 'Story',
'type_description' => 'A test content type.',
'field_name' => 'test',
'label' => 'Test',
'widget_type' => 'date_select',
'todate' => 'optional',
'field_type' => 'date',
'granularity[]' => array('year', 'month', 'day', 'hour', 'minute'),
'tz_handling' => 'site',
'year_range' => '2010:+2',
'weight' => -100,
);
$this->drupalPost('admin/config/date/tools/date_wizard', $edit, t('Save'));
$this->assertText('Your date field Test has been created.', 'Create a date field using the Date Wizard.');
}
}

View File

@ -1,4 +1,9 @@
/* Pager plugin css */
/**
* @file
* Custom CSS for the Date Views module.
*/
/* Pager plugin. */
div.date-views-pager {
margin-left: auto;
margin-right: auto;
@ -19,7 +24,7 @@ div.date-views-pager div.next-year {
}
/* Views filter form css */
/* Views filter form. */
.views-group-box div.date-views-filter-fieldset {
margin-left: 0;
}
@ -47,9 +52,10 @@ div.date-views-pager div.next-year {
}
/**
* Style Header
* Style Header.
* Give the navigation bar a little extra padding below so it will clear the
* new contextual links overlay of the teasers below it.
*/
/* Give the navigation bar a little extra padding below so it will clear the new contextual links overlay of the teasers below it. */
.date-nav {
clear: both;
padding-bottom: 1.5em;
@ -64,7 +70,7 @@ div.date-views-pager div.next-year {
margin-bottom: 10px;
}
.date-nav-wrapper {
.date-nav-wrapper {
position: relative;
margin-top: 5px;
width: 100%;
@ -139,4 +145,4 @@ div.date-views-pager div.next-year {
.date-nav-wrapper .date-next a {
margin-right: 10px;
font-weight: bold;
}
}

View File

@ -1,10 +1,12 @@
name = Date Views
description = Views integration for date fields and date functionality.
package = Date/Time
dependencies[] = date_api
dependencies[] = views
core = 7.x
php = 5.2
dependencies[] = date:date_api
dependencies[] = views:views
files[] = includes/date_views_argument_handler.inc
files[] = includes/date_views_argument_handler_simple.inc
files[] = includes/date_views_filter_handler.inc
@ -12,9 +14,12 @@ files[] = includes/date_views_filter_handler_simple.inc
files[] = includes/date_views.views.inc
files[] = includes/date_views_plugin_pager.inc
; Information added by Drupal.org packaging script on 2017-04-07
version = "7.x-2.10"
; Test coverage.
files[] = tests/date_views_filter.test
files[] = tests/date_views_pager.test
; Information added by Drupal.org packaging script on 2021-03-05
version = "7.x-2.11"
core = "7.x"
project = "date"
datestamp = "1491562090"
datestamp = "1614952728"

View File

@ -26,7 +26,6 @@ function date_views_menu() {
* 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'),
@ -76,15 +75,13 @@ function date_views_settings($form, &$form_state) {
);
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() {
// This one is used as the base to reduce errors when updating.
$path = drupal_get_path('module', 'date_views');
$base = array(
'file' => 'theme.inc',
@ -165,23 +162,29 @@ function date_views_fields($base = 'node', $reset = FALSE) {
/**
* 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() {
// 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().
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.
* Helper function to map entity types to the Views base table they use.
*
* Views has a new handler called views_handler_field_entity() that loads entities.
* Used to make it easier to infer the entity type from a base table.
*
* 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).
* Views has a new handler called views_handler_field_entity() that loads
* entities.
*
* So it won't work here and we resort to creating information from entity_get_info().
* 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);
@ -194,7 +197,6 @@ 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) {
@ -206,7 +208,8 @@ function date_views_base_tables() {
}
}
// Then we let other modules tell us about other entity tables that hold date fields.
// Then we let other modules tell us about other entity tables that hold
// date fields.
$base_tables += module_invoke_all('date_views_extra_tables');
}
@ -215,10 +218,12 @@ function date_views_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) {
// 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.
$values = array(
// The type of date: DATE_UNIX, DATE_ISO, DATE_DATETIME.
'sql_type' => DATE_UNIX,
@ -249,11 +254,11 @@ function date_views_date_views_fields($field) {
}
/**
* A version of date_real_url that formats links correctly for the new Date pager.
* A version of date_real_url that formats links correctly for the 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 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 '';
}
@ -261,13 +266,12 @@ function date_pager_url($view, $date_type = NULL, $date_arg = NULL, $force_view_
$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.
// 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 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) {
@ -293,7 +297,6 @@ function date_pager_url($view, $date_type = NULL, $date_arg = NULL, $force_view_
default:
$args[$pos] = date_pad($view->date_info->year, 4) . '-' . date_pad($view->date_info->month);
break;
}
}
elseif (!empty($date_arg)) {
@ -302,11 +305,10 @@ function date_pager_url($view, $date_type = NULL, $date_arg = NULL, $force_view_
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))) {
// 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)) {
@ -316,8 +318,8 @@ function date_pager_url($view, $date_type = NULL, $date_arg = NULL, $force_view_
}
}
// Normal views may need querystrings appended to them
// if they use exposed filters.
// 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,
@ -337,12 +339,12 @@ function date_block_identifier($view) {
/**
* 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) {
// 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).
if ($module == 'date') {
$has_end_date = !empty($field['settings']['todate']);
if ($has_end_date) {
@ -358,8 +360,9 @@ function date_views_field_views_data_alter(&$result, $field, $module) {
// 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.
// 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;
}
@ -458,9 +461,35 @@ function date_views_form_views_ui_edit_form_alter(&$form, &$form_state, $form_id
}
/**
* 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.
* Implements hook_form_FORM_ID_alter() for views_exposed_form().
*/
function date_views_form_views_exposed_form_alter(&$form, &$form_state, $form_id) {
$children = element_children($form);
// @todo On a stock views_exposed_form, there won't be any grandchildren
// items, but will this always be the case? How about better_exposed_filters?
foreach ($children as $child) {
if (isset($form[$child]['#id']) && strpos($form[$child]['#id'], 'date_views_exposed_filter-') === 0) {
// Remove empty or scalar date input when an array was expected.
if (empty($form_state['input'][$child]) || !is_array($form_state['input'][$child])) {
unset($form_state['input'][$child]);
}
elseif (empty($form_state['input'][$child]['value']) || !is_array($form_state['input'][$child]['value'])) {
unset($form_state['input'][$child]['value']);
}
}
}
}
/**
* Work out if the plugin is a date.
*
* 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.
* 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) {
@ -476,7 +505,8 @@ function date_views_handler_is_date($handler, $type = 'argument') {
/**
* 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.
* 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.
@ -525,10 +555,10 @@ function date_views_select_validate(&$form, &$form_state) {
/**
* 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) {
// 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.
// Some views have no row index.
if (!empty($entity->view) && isset($entity->view->row_index)) {
$field = $variables['field'];

View File

@ -1,7 +1,8 @@
<?php
/**
* @file
* Defines date-related Views data and plugins:
* Defines date-related Views data and plugins:.
*
* Date argument:
* A generic date argument that has an option to select one or more
@ -126,7 +127,6 @@ function date_views_views_data() {
* Implements hook_views_data_alter().
*/
function date_views_views_data_alter(&$data) {
// Mark all the core date handlers as date fields.
// This will identify all handlers that directly use the _date handlers,
// will not pick up any that extend those handlers.
@ -146,12 +146,12 @@ function date_views_views_data_alter(&$data) {
*
* In the SQL date handler.
*
* The date handler will use this information to decide if the
* database value needs a timezone conversion.
* The date handler will use this information to decide if the database value
* needs a timezone conversion.
*
* In Views, we will always be comparing to a local date value,
* so the goal is to convert the database value to the right
* value to compare to the local value.
* In Views, we will always be comparing to a local date value, so the goal is
* to convert the database value to the right value to compare to the local
* value.
*/
function date_views_set_timezone(&$date_handler, &$view, $field) {
switch ($field['tz_handling']) {
@ -174,7 +174,6 @@ function date_views_set_timezone(&$date_handler, &$view, $field) {
default:
$date_handler->db_timezone = 'UTC';
$date_handler->local_timezone = date_default_timezone();
break;
}
}
@ -183,7 +182,6 @@ function date_views_set_timezone(&$date_handler, &$view, $field) {
*
* @param object $view
* A View object.
*
* @param array $extra_params
* An extra parameters.
*
@ -192,24 +190,29 @@ function date_views_set_timezone(&$date_handler, &$view, $field) {
*/
function date_views_querystring($view, $extra_params = array()) {
$query_params = array_merge($_GET, $extra_params);
// Allow NULL params to be removed from the query string.
foreach ($extra_params as $key => $value) {
if (!isset($value)) {
unset($query_params[$key]);
}
}
// Filter the special "q" and "view" variables out of the query string.
$exclude = array('q');
// If we don't explicitly add a value for "view", filter it out.
if (empty($extra_params['view'])) {
$exclude[] = 'view';
}
// Clear out old date pager settings if not explicitly added.
if (!empty($view->date_info->pager_id) && empty($extra_params[$view->date_info->pager_id])) {
$exclude[] = $view->date_info->pager_id;
}
$query = drupal_get_query_parameters($query_params, $exclude);
// To prevent an empty query string from adding a "?" on to the end of a URL,
// we return NULL.
return !empty($query) ? $query : NULL;

View File

@ -1,21 +1,21 @@
<?php
/**
* @file
* Date API views argument handler.
* This argument combines multiple date arguments into a single argument
* where all fields are controlled by the same date and can be combined
* with either AND or OR.
*
* This argument combines multiple date arguments into a single argument where
* all fields are controlled by the same date and can be combined with either
* AND or OR.
*/
/**
* Date API argument handler.
*/
// @codingStandardsIgnoreStart
class date_views_argument_handler extends date_views_argument_handler_simple {
/**
* Get granularity and use it to create the formula and a format
* for the results.
* {@inheritdoc}
*/
function init(&$view, &$options) {
parent::init($view, $options);
@ -30,7 +30,7 @@ class date_views_argument_handler extends date_views_argument_handler_simple {
}
/**
* Default value for the date_fields option.
* {@inheritdoc}
*/
function option_definition() {
$options = parent::option_definition();
@ -41,7 +41,7 @@ class date_views_argument_handler extends date_views_argument_handler_simple {
}
/**
* Add a form element to select date_fields for this argument.
* {@inheritdoc}
*/
function options_form(&$form, &$form_state) {
parent::options_form($form, $form_state);
@ -72,9 +72,12 @@ class date_views_argument_handler extends date_views_argument_handler_simple {
);
}
/**
* {@inheritdoc}
*/
function options_validate(&$form, &$form_state) {
// Views will whine if we don't have something for the these values even though we removed the option for summaries.
// Views will whine if we don't have something for the these values even
// though we removed the option for summaries.
$form_state['values']['options']['summary']['format'] = 'none';
$form_state['values']['options']['summary']['options']['none'] = array();
@ -89,6 +92,9 @@ class date_views_argument_handler extends date_views_argument_handler_simple {
}
}
/**
* {@inheritdoc}
*/
function options_submit(&$form, &$form_state) {
// It is very important to call the parent function here:
parent::options_submit($form, $form_state);
@ -97,7 +103,9 @@ class date_views_argument_handler extends date_views_argument_handler_simple {
}
}
// Update the summary values to show selected granularity.
/**
* {@inheritdoc}
*/
function admin_summary() {
$fields = date_views_fields($this->base_table);
if (!empty($this->options['date_fields'])) {
@ -115,15 +123,13 @@ class date_views_argument_handler extends date_views_argument_handler_simple {
}
/**
* Provide a list of default behaviors for this argument if the argument
* is not present.
*
* Override this method to provide additional (or fewer) default behaviors.
* {@inheritdoc}
*/
function default_actions($which = NULL) {
$defaults = parent::default_actions();
// There is no easy way to do summary queries on multiple fields, so remove that option.
// There is no easy way to do summary queries on multiple fields, so remove
// that option.
unset($defaults['summary']);
if ($which) {
@ -137,14 +143,11 @@ class date_views_argument_handler extends date_views_argument_handler_simple {
}
/**
* Set up the query for this argument.
*
* The argument sent may be found at $this->argument.
* {@inheritdoc}
*/
function query($group_by = FALSE) {
// @TODO Not doing anything with $group_by yet, need to figure out what has to be done.
// @todo Not doing anything with $group_by yet, need to figure out what has
// to be done.
if ($this->date_forbid()) {
return;
}
@ -156,8 +159,8 @@ class date_views_argument_handler extends date_views_argument_handler_simple {
$this->placeholders = array();
if (!empty($this->query_fields)) {
// Use set_where_group() with the selected date_method
// of 'AND' or 'OR' to create the where clause.
// Use set_where_group() with the selected date_method of 'AND' or 'OR'
// to create the where clause.
foreach ($this->query_fields as $count => $query_field) {
$field = $query_field['field'];
$this->date_handler = $query_field['date_handler'];
@ -168,8 +171,9 @@ class date_views_argument_handler extends date_views_argument_handler_simple {
if ($field['table_name'] != $this->table || !empty($this->relationship)) {
$this->table = $this->query->ensure_table($field['table_name'], $this->relationship);
}
// $this->table_alias gets set when the first field is processed if otherwise empty.
// For subsequent fields, we need to be sure it is emptied again.
// $this->table_alias gets set when the first field is processed if
// otherwise empty. For subsequent fields, we need to be sure it is
// emptied again.
elseif (empty($this->relationship)) {
$this->table_alias = NULL;
}
@ -181,7 +185,7 @@ class date_views_argument_handler extends date_views_argument_handler_simple {
}
/**
* Collect information about our fields we will need to create the right query.
* {@inheritdoc}
*/
function get_query_fields() {
$fields = date_views_fields($this->base_table);
@ -200,4 +204,3 @@ class date_views_argument_handler extends date_views_argument_handler_simple {
}
}
// @codingStandardsIgnoreEnd

View File

@ -1,19 +1,17 @@
<?php
/**
* @file
* Date API views argument handler.
*/
/**
* Date API argument handler.
* Date API views argument handler.
*/
// @codingStandardsIgnoreStart
class date_views_argument_handler_simple extends views_handler_argument_date {
/**
* Get granularity.
*
* Use it to create the formula and a format for the results.
* {@inheritdoc}
*/
function init(&$view, &$options) {
parent::init($view, $options);
@ -31,20 +29,19 @@ class date_views_argument_handler_simple extends views_handler_argument_date {
$this->date_handler->local_timezone = date_get_timezone($field['settings']['tz_handling']);
}
$this->date_handler->granularity = $this->options['granularity'];
// This value needs to be initialized so
// it exists even if the query doesn't run.
// This value needs to be initialized so it exists even if the query
// doesn't run.
$this->date_handler->placeholders = array();
$this->format = $this->date_handler->views_formats($this->date_handler->granularity, 'display');
$this->sql_format = $this->date_handler->views_formats($this->date_handler->granularity, 'sql');
// $this->arg_format is the format the parent date
// handler will use to create a default argument.
// $this->arg_format is the format the parent date handler will use to
// create a default argument.
$this->arg_format = $this->format();
// Identify the base table for this field.
// It will be used to call for the right query field options.
// Identify the base table for this field. It will be used to call for the
// right query field options.
$this->base_table = $this->table;
}
/**
@ -60,9 +57,7 @@ class date_views_argument_handler_simple extends views_handler_argument_date {
}
/**
* Set the empty argument value to the current date.
*
* Formatted appropriately for this argument.
* {@inheritdoc}
*/
function get_default_argument($raw = FALSE) {
$is_default = FALSE;
@ -87,9 +82,10 @@ class date_views_argument_handler_simple extends views_handler_argument_date {
}
/**
* Default value for the date_fields option.
* {@inheritdoc}
*/
function option_definition() {
// Default value for the date_fields option.
$options = parent::option_definition();
$options['year_range'] = array('default' => '-3:+3');
$options['granularity'] = array('default' => 'month');
@ -103,7 +99,7 @@ class date_views_argument_handler_simple extends views_handler_argument_date {
}
/**
* Add a form element to select date_fields for this argument.
* {@inheritdoc}
*/
function options_form(&$form, &$form_state) {
parent::options_form($form, $form_state);
@ -149,6 +145,7 @@ class date_views_argument_handler_simple extends views_handler_argument_date {
// Get default granularity options
$options = $this->date_handler->date_parts();
// Add the 'week' option.
$options += array(
'week' => t('Week', array(), array(
@ -214,7 +211,7 @@ class date_views_argument_handler_simple extends views_handler_argument_date {
}
/**
* Provide a link to the next level of the view from the summary.
* {@inheritdoc}
*/
function summary_name($data) {
$value = $data->{$this->name_alias};
@ -230,7 +227,7 @@ class date_views_argument_handler_simple extends views_handler_argument_date {
}
/**
* Provide a title for the view based on the argument value.
* {@inheritdoc}
*/
function title() {
$format = !empty($this->options['title_format_custom']) && !empty($this->options['title_format_custom']) ? $this->options['title_format_custom'] : $this->date_handler->views_formats($this->options['granularity'], 'display');
@ -239,13 +236,7 @@ class date_views_argument_handler_simple extends views_handler_argument_date {
}
/**
* Provide the argument to use to link from the summary to the next level.
*
* This will be called once per row of a summary, and used as part of
* $view->get_url().
*
* @param object $data
* The query results for the row.
* {@inheritdoc}
*/
function summary_argument($data) {
$format = $this->date_handler->views_formats($this->options['granularity'], 'sql');
@ -258,24 +249,25 @@ class date_views_argument_handler_simple extends views_handler_argument_date {
}
/**
* Inject a test for valid date range before the summary query.
* {@inheritdoc}
*/
function summary_query() {
// @TODO The summary values are computed by the database.
// Unless the database has built-in timezone handling it will use
// a fixed offset, which will not be right for all dates.
// The only way I can see to make this work right is to store the offset
// for each date in the database so it can be added to the base
// date value before the database formats the result. Because this is a huge
// architectural change, it won't go in until we start a new branch.
// Inject a test for valid date range before the summary query.
// @todo The summary values are computed by the database. Unless the
// database has built-in timezone handling it will use a fixed offset,
// which will not be right for all dates. The only way I can see to make
// this work right is to store the offset for each date in the database so
// it can be added to the base date value before the database formats the
// result. Because this is a huge architectural change, it won't go in
// until we start a new branch.
$this->formula = $this->date_handler->sql_format($this->sql_format, $this->date_handler->sql_field("***table***.$this->real_field"));
$this->ensure_my_table();
// Now that our table is secure, get our formula.
$formula = $this->get_formula();
// Add the field, give it an alias that does NOT match the actual
// field name or grouping won't work right.
// Add the field, give it an alias that does NOT match the actual field
// name or grouping won't work right.
$this->base_alias = $this->name_alias = $this->query->add_field(NULL, $formula, $this->field . '_summary');
$this->query->set_count_field(NULL, $formula, $this->field);
@ -283,22 +275,20 @@ class date_views_argument_handler_simple extends views_handler_argument_date {
}
/**
* Inject a test for valid date range before the regular query.
*
* Override the parent query to be able to control the $group.
* {@inheritdoc}
*/
function query($group_by = FALSE) {
// @TODO Not doing anything with $group_by yet,
// need to figure out what has to be done.
// Inject a test for valid date range before the regular query. Override
// the parent query to be able to control the $group.
// @todo Not doing anything with $group_by yet, need to figure out what has
// to be done.
if ($this->date_forbid()) {
return;
}
// See if we need to reset granularity based on an argument value.
// Make sure we don't try to reset to some bogus value if someone has
// typed in an unexpected argument.
// See if we need to reset granularity based on an argument value. Make
// sure we don't try to reset to some bogus value if someone has typed in
// an unexpected argument.
if ($this->options['granularity_reset'] && $granularity = $this->date_handler->arg_granularity($this->argument)) {
$this->date_handler->granularity = $granularity;
$this->format = $this->date_handler->views_formats($this->date_handler->granularity, 'display');
@ -308,8 +298,8 @@ class date_views_argument_handler_simple extends views_handler_argument_date {
$this->ensure_my_table();
$group = !empty($this->options['date_group']) ? $this->options['date_group'] : 0;
// If requested, add the delta field to the view so
// we can later find the value that matched our query.
// If requested, add the delta field to the view so we can later find the
// value that matched our query.
if (!empty($this->options['add_delta']) && (substr($this->real_field, -6) == '_value' || substr($this->real_field, -7) == '_value2')) {
$this->query->add_field($this->table_alias, 'delta');
$real_field_name = str_replace(array('_value', '_value2'), '', $this->real_field);
@ -324,10 +314,9 @@ class date_views_argument_handler_simple extends views_handler_argument_date {
$view_max_placeholder = $this->placeholder();
$this->date_handler->placeholders = array($view_min_placeholder => $view_min, $view_max_placeholder => $view_max);
// Are we comparing this field only or the Start/End date range
// to the view criteria?
// Are we comparing this field only or the Start/End date range to the view
// criteria?
if (!empty($this->options['use_fromto'])) {
// The simple case, match the field to the view range.
$field = $this->date_handler->sql_field($this->table_alias . '.' . $this->real_field, NULL, $this->min_date);
$field = $this->date_handler->sql_format($format, $field);
@ -335,15 +324,12 @@ class date_views_argument_handler_simple extends views_handler_argument_date {
}
else {
// Look for the intersection of the range
// of the date field with the range of the view.
// Get the Start/End values for this field.
// Retrieve using the original table name.
// Swap the current table name (adjusted for relationships)
// into the query.
// @TODO We may be able to use Views substitutions here,
// investigate that later.
// Look for the intersection of the range of the date field with the
// range of the view. Get the Start/End values for this field. Retrieve
// using the original table name. Swap the current table name (adjusted
// for relationships) into the query.
// @todo We may be able to use Views substitutions here, investigate that
// later.
$fields = date_views_fields($this->base_table);
$fields = $fields['name'];
$fromto = $fields[$this->original_table . '.' . $this->real_field]['fromto'];
@ -361,8 +347,8 @@ class date_views_argument_handler_simple extends views_handler_argument_date {
/**
* Add a callback.
*
* To determine if we have moved outside
* the valid date range for this argument.
* To determine if we have moved outside the valid date range for this
* argument.
*/
function date_forbid() {
if (empty($this->argument)) {
@ -384,4 +370,3 @@ class date_views_argument_handler_simple extends views_handler_argument_date {
}
}
// @codingStandardsIgnoreEnd

View File

@ -1,4 +1,5 @@
<?php
/**
* @file
* Helper for identifying Date API fields for views.
@ -9,10 +10,10 @@
*
* @return array
* An array with fieldname, type, and table.
*
* @see date_views_date_views_fields()
*/
function _date_views_fields($base = 'node') {
// Make sure $base is never empty.
if (empty($base)) {
$base = 'node';
@ -83,14 +84,14 @@ function _date_views_fields($base = 'node') {
default:
// If this is not a date field, nothing more to do.
continue;
continue 2;
}
$revision = in_array($base, array('node_revision')) ? FIELD_LOAD_REVISION : FIELD_LOAD_CURRENT;
$db_info = date_api_database_info($field, $revision);
$name = $table_name . "." . $field_name;
$grans = array('year', 'month', 'day', 'hour', 'minute', 'second');
$granularity = !empty($field['granularity']) ? $field['granularity'] : $grans;
$granularity = !empty($field['settings']['granularity']) ? $field['settings']['granularity'] : $grans;
$fromto = array(
$table_name . '.' . $db_info['columns'][$table_name]['value'],
@ -104,7 +105,7 @@ function _date_views_fields($base = 'node') {
$offset_field = $table_name . '.' . $db_info['columns'][$table_name]['offset'];
}
$related_fields = array(
$table_name . '.' . $db_info['columns'][$table_name]['value']
$table_name . '.' . $db_info['columns'][$table_name]['value'],
);
if (isset($db_info['columns'][$table_name]['value2'])) {
$related_fields = array_merge($related_fields, array($table_name . '.' . $db_info['columns'][$table_name]['value2']));

View File

@ -1,14 +1,22 @@
<?php
/**
* @file
* A flexible, configurable date filter.
* This filter combines multiple date filters into a single filter
* where all fields are controlled by the same date and can be combined
* with either AND or OR.
*/
// @codingStandardsIgnoreStart
/**
* A flexible, configurable date filter.
*
* This filter combines multiple date filters into a single filter where all
* fields are controlled by the same date and can be combined with either AND or
* OR.
*/
class date_views_filter_handler extends date_views_filter_handler_simple {
/**
* {@inheritdoc}
*/
function init(&$view, &$options) {
parent::init($view, $options);
@ -21,7 +29,9 @@ class date_views_filter_handler extends date_views_filter_handler_simple {
$this->view->date_info->date_fields = array_merge($this->view->date_info->date_fields, $this->options['date_fields']);
}
// Set default values for the date filter.
/**
* {@inheritdoc}
*/
function option_definition() {
$options = parent::option_definition();
$options['date_fields'] = array('default' => array());
@ -30,18 +40,30 @@ class date_views_filter_handler extends date_views_filter_handler_simple {
return $options;
}
/**
* @todo
*/
function op_between($field) {
$this->date_combine_conditions('op_between');
}
/**
* @todo
*/
function op_simple($field) {
$this->date_combine_conditions('op_simple');
}
/**
* @todo
*/
function op_contains($field) {
$this->date_combine_conditions('op_contains');
}
/**
* @todo
*/
function op_empty($field) {
$this->get_query_fields();
if (empty($this->query_fields)) {
@ -83,6 +105,7 @@ class date_views_filter_handler extends date_views_filter_handler_simple {
// Create a custom filter group for the conditions.
$this->query->set_where_group($this->options['date_method'], $this->options['date_group']);
// Add each condition to the custom filter group.
foreach ((array) $this->query_fields as $query_field) {
$field = $query_field['field'];
@ -120,6 +143,9 @@ class date_views_filter_handler extends date_views_filter_handler_simple {
$this->query->add_where_expression($this->options['group'], $conditions, $placeholders);
}
/**
* {@inheritdoc}
*/
function extra_options_form(&$form, &$form_state) {
parent::extra_options_form($form, $form_state);
@ -147,6 +173,9 @@ class date_views_filter_handler extends date_views_filter_handler_simple {
);
}
/**
* {@inheritdoc}
*/
function extra_options_validate($form, &$form_state) {
$check_fields = array_filter($form_state['values']['options']['date_fields']);
if (empty($check_fields)) {
@ -154,12 +183,16 @@ class date_views_filter_handler extends date_views_filter_handler_simple {
}
}
/**
* {@inheritdoc}
*/
function extra_options_submit($form, &$form_state) {
$form_state['values']['options']['date_fields'] = array_filter($form_state['values']['options']['date_fields']);
}
// Update the summary values to provide
// meaningful information for each option.
/**
* {@inheritdoc}
*/
function admin_summary() {
if (empty($this->options['date_fields'])) {
return t('Missing date fields!');
@ -195,6 +228,9 @@ class date_views_filter_handler extends date_views_filter_handler_simple {
return $output;
}
/**
* @todo
*/
function get_query_fields() {
$fields = date_views_fields($this->base_table);
$fields = $fields['name'];
@ -209,5 +245,5 @@ class date_views_filter_handler extends date_views_filter_handler_simple {
}
}
}
}
// @codingStandardsIgnoreEnd

View File

@ -1,15 +1,28 @@
<?php
/**
* @file
* A standard Views filter for a single date field,
* using Date API form selectors and sql handling.
* A standard Views filter for a single date field.
*/
// @codingStandardsIgnoreStart
/**
* A standard Views filter for a single date field.
*/
class date_views_filter_handler_simple extends views_handler_filter_date {
var $date_handler = NULL;
var $offset = NULL;
/**
* @var object
*/
public $date_handler = NULL;
/**
* @var int
*/
public $offset = NULL;
/**
* {@inheritdoc}
*/
function init(&$view, &$options) {
parent::init($view, $options);
module_load_include('inc', 'date_api', 'date_api_sql');
@ -26,13 +39,14 @@ class date_views_filter_handler_simple extends views_handler_filter_date {
$this->date_handler->granularity = isset($options['granularity']) ? $options['granularity'] : 'day';
$this->format = $this->date_handler->views_formats($this->options['granularity'], 'sql');
// Identify the base table for this field.
// It will be used to call for the right query field options.
// Identify the base table for this field. It will be used to call for the
// right query field options.
$this->base_table = $this->table;
}
// Set default values for the date filter.
/**
* {@inheritdoc}
*/
function option_definition() {
$options = parent::option_definition();
$options['granularity'] = array('default' => 'day');
@ -44,14 +58,24 @@ class date_views_filter_handler_simple extends views_handler_filter_date {
return $options;
}
/**
* {@inheritdoc}
*/
function operators() {
// Cache the operators that are being added.
static $new_operators;
if (!isset($new_operators)) {
$new_operators = array(
'title' => t('Contains'),
'method' => 'op_contains',
'short' => t('contains'),
'values' => 1,
);
}
// Add the new 'contains' operators.
$operators = parent::operators();
$operators['contains'] = array(
'title' => t('Contains'),
'method' => 'op_contains',
'short' => t('contains'),
'values' => 1,
);
$operators['contains'] = $new_operators;
return $operators;
}
@ -91,9 +115,9 @@ class date_views_filter_handler_simple extends views_handler_filter_date {
/**
* Helper function to see if we need to swap in the default value.
*
* Views exposed filters treat everything as submitted, so if it's an empty value we have to
* see if anything actually was submitted. If nothing has really been submitted, we need
* to swap in our default value logic.
* Views exposed filters treat everything as submitted, so if it's an empty
* value we have to see if anything actually was submitted. If nothing has
* really been submitted, we need to swap in our default value logic.
*/
function get_filter_value($prefix, $input) {
// All our date widgets provide datetime values but we use ISO in our SQL
@ -111,6 +135,9 @@ class date_views_filter_handler_simple extends views_handler_filter_date {
return str_replace(' ', 'T', $input);
}
/**
* {@inheritdoc}
*/
function accept_exposed_input($input) {
if (!empty($this->options['exposed'])) {
$element_input = $input[$this->options['expose']['identifier']];
@ -127,12 +154,14 @@ class date_views_filter_handler_simple extends views_handler_filter_date {
$input[$this->options['expose']['identifier']] = $element_input;
}
return parent::accept_exposed_input($input);
}
/**
* @todo
*/
function op_between($field) {
// Add the delta field to the view so we can later find the value that matched our query.
// Add the delta field to the view so we can later find the value that
// matched our query.
list($table_name, $field_name) = explode('.', $field);
if (!empty($this->options['add_delta']) && (substr($field_name, -6) == '_value' || substr($field_name, -7) == '_value2')) {
$this->query->add_field($table_name, 'delta');
@ -160,9 +189,12 @@ class date_views_filter_handler_simple extends views_handler_filter_date {
}
}
/**
* @todo
*/
function op_simple($field) {
// Add the delta field to the view so we can later find the value that matched our query.
// Add the delta field to the view so we can later find the value that
// matched our query.
list($table_name, $field_name) = explode('.', $field);
if (!empty($this->options['add_delta']) && (substr($field_name, -6) == '_value' || substr($field_name, -7) == '_value2')) {
$this->query->add_field($table_name, 'delta');
@ -180,9 +212,12 @@ class date_views_filter_handler_simple extends views_handler_filter_date {
$this->query->add_where_expression($group, "$field $this->operator $placeholder", array($placeholder => $value));
}
/**
* @todo
*/
function op_contains($field) {
// Add the delta field to the view so we can later find the value that matched our query.
// Add the delta field to the view so we can later find the value that
// matched our query.
list($table_name, $field_name) = explode('.', $field);
if (!empty($this->options['add_delta']) && (substr($field_name, -6) == '_value' || substr($field_name, -7) == '_value2')) {
$this->query->add_field($table_name, 'delta');
@ -204,12 +239,14 @@ class date_views_filter_handler_simple extends views_handler_filter_date {
}
/**
* Set the granularity of the date parts to use in the filter.
*/
function has_extra_options() { return TRUE; }
* {@inheritdoc}
*/
function has_extra_options() {
return TRUE;
}
/**
* Date selection options.
* {@inheritdoc}
*/
function widget_options() {
$options = array(
@ -223,6 +260,9 @@ class date_views_filter_handler_simple extends views_handler_filter_date {
return $options;
}
/**
* @todo
*/
function year_range() {
$year_range = explode(':', $this->options['year_range']);
if (substr($this->options['year_range'], 0, 1) == '-' || $year_range[0] < 0) {
@ -233,6 +273,9 @@ class date_views_filter_handler_simple extends views_handler_filter_date {
return $year_range;
}
/**
* {@inheritdoc}
*/
function extra_options_form(&$form, &$form_state) {
parent::extra_options_form($form, $form_state);
$form['form_type'] = array(
@ -264,6 +307,9 @@ class date_views_filter_handler_simple extends views_handler_filter_date {
);
}
/**
* {@inheritdoc}
*/
function extra_options_validate($form, &$form_state) {
if (!preg_match('/^(?:[\+\-][0-9]{1,4}|[0-9]{4}):(?:[\+\-][0-9]{1,4}|[0-9]{4})$/', $form_state['values']['options']['year_range'])) {
form_error($form['year_range'], t('Date year range must be in the format -9:+9, 2005:2010, -9:2010, or 2005:+9'));
@ -271,13 +317,18 @@ class date_views_filter_handler_simple extends views_handler_filter_date {
}
/**
* Add the selectors to the value form using the date handler.
* {@inheritdoc}
*/
function value_form(&$form, &$form_state) {
// We use different values than the parent form, so we must
// construct our own form element.
// We use different values than the parent form, so we must construct our
// own form element.
$form['value'] = array();
$form['value']['#tree'] = TRUE;
$form['value']['#id'] = 'date_views_exposed_filter-' . bin2hex(drupal_random_bytes(16));
$form['value']['#type'] = 'container';
if (module_exists('ctools')) {
$form['value']['#pre_render'] = array('ctools_dependent_pre_render');
}
// Below section copied from views_handler_filter_numeric.inc.
$which = 'all';
@ -288,9 +339,8 @@ class date_views_filter_handler_simple extends views_handler_filter_date {
$identifier = $this->options['expose']['identifier'];
if (!empty($form_state['exposed'])) {
if (empty($this->options['expose']['use_operator']) || empty($this->options['expose']['operator_id'])) {
// exposed and locked.
// Exposed and locked.
$which = in_array($this->operator, $this->operator_values(2)) ? 'minmax' : 'value';
}
else {
@ -307,8 +357,8 @@ class date_views_filter_handler_simple extends views_handler_filter_date {
$form['value'] += $this->date_parts_form($form_state, 'max', $source, $which, $this->operator_values(2), $identifier, 'default_to_date');
}
// Add some extra validation for the select widget to be sure that
// the user inputs all parts of the date.
// Add some extra validation for the select widget to be sure that the user
// inputs all parts of the date.
if ($this->options['form_type'] == 'date_select') {
$form['value']['#element_validate'] = array('date_views_select_validate');
}
@ -341,14 +391,15 @@ class date_views_filter_handler_simple extends views_handler_filter_date {
$label = t('Start date');
$relative_label = t('Relative start date');
break;
case 'max':
$label = t('End date');
$relative_label = t('Relative end date');
break;
default:
$label = '';
$relative_label = t('Relative date');
break;
}
$type = $this->options['form_type'];
@ -362,10 +413,12 @@ class date_views_filter_handler_simple extends views_handler_filter_date {
if (!empty($form_state['exposed'])) {
// UI when the date selector is exposed.
$label = $this->options['expose']['label'];
$default_date = $this->date_default_value($prefix);
$id = 'edit-' . str_replace('_', '-', $this->field) . '-' . $prefix;
$form[$prefix] = array(
'#title' => check_plain($label),
'#title_display' => 'invisible',
'#type' => $type,
'#size' => 20,
'#default_value' => !empty($this->value[$prefix]) ? $this->value[$prefix] : $default_date,
@ -380,7 +433,16 @@ class date_views_filter_handler_simple extends views_handler_filter_date {
$form[$prefix]['#pre_render'][] = 'ctools_dependent_pre_render';
$form[$prefix]['#dependency'] = array($source => $operator_values);
}
// Validate the input value; set form state input array.
$input = isset($form_state['input'][$identifier][$prefix]) ? $form_state['input'][$identifier][$prefix] : array();
$form_state['input'][$identifier][$prefix] = $this->input_validate($form[$prefix], $input);
if (!isset($form_state['input'][$identifier][$prefix])) {
// Handle bogus input from the query string to prevent fatal errors.
if (isset($form_state['input'][$identifier]) && !is_array($form_state['input'][$identifier])) {
$form_state['input'][$identifier] = array();
}
// Ensure these exist.
foreach ($granularity as $key) {
$form_state['input'][$identifier][$prefix][$key] = NULL;
@ -438,18 +500,67 @@ class date_views_filter_handler_simple extends views_handler_filter_date {
return $form;
}
/**
* Returns form state input array appropriate to missing, valid, and invalid input.
*/
function input_validate($element, $input) {
if (!$input) {
// No input value (e.g. initial form load), set value to NULL so Form API
// calls the element value_callback routine with FALSE to set the '#value'
// property from the '#default_value' property.
// See _form_builder_handle_input_element().
if ($element['#type'] != 'date_select') {
return array('date' => NULL);
}
$granularity = date_format_order($element['#date_format']);
if ($key = array_search('timezone', $granularity)) {
unset($granularity[$key]);
}
return array_fill_keys($granularity, NULL);
}
// Include element defaults to avoid notices in the xxx_input_date routines.
// Copied from form_builder() in form.inc.
if (isset($element['#type']) && empty($element['#defaults_loaded']) && ($info = element_info($element['#type']))) {
// Overlay $info onto $element, retaining preexisting keys in $element.
$element += $info;
$element['#defaults_loaded'] = TRUE;
}
$date = NULL;
$function = "{$element['#type']}_input_date";
if (($date = $function($element, $input)) && date_is_date($date)) {
// The input is valid including being in the expected format.
return $input;
}
if (is_object($date)) {
// Input may not meet the granularity and format but is recognizable.
return $input;
}
// Input is bogus, return empty array in granularity format.
if ($element['#type'] != 'date_select') {
return array('date' => '');
}
$granularity = date_format_order($element['#date_format']);
if ($key = array_search('timezone', $granularity)) {
unset($granularity[$key]);
}
return array_fill_keys($granularity, '');
}
/**
* Value validation.
*
* TODO add in more validation.
* @todo Add in more validation.
*
* We are setting an extra option using a value form
* because it makes more sense to set it there.
* That's not the normal method, so we have to manually
* We are setting an extra option using a value form because it makes more
* sense to set it there. That's not the normal method, so we have to manually
* transfer the selected value back to the option.
*/
function value_validate($form, &$form_state) {
$options = &$form_state['values']['options'];
if ($options['operator'] == 'between' || $options['operator'] == 'not between') {
@ -459,7 +570,8 @@ class date_views_filter_handler_simple extends views_handler_filter_date {
}
else {
$this->options['default_date'] = $options['value']['min_group']['default_date'];
// NULL out the value field, user wanted the relative value to take hold.
// NULL out the value field, user wanted the relative value to take
// hold.
$options['value']['min_group']['min'] = NULL;
}
}
@ -473,7 +585,8 @@ class date_views_filter_handler_simple extends views_handler_filter_date {
}
else {
$this->options['default_to_date'] = $options['value']['max_group']['default_to_date'];
// NULL out the value field, user wanted the relative value to take hold.
// NULL out the value field, user wanted the relative value to take
// hold.
$options['value']['max_group']['max'] = NULL;
}
}
@ -489,7 +602,8 @@ class date_views_filter_handler_simple extends views_handler_filter_date {
}
else {
$this->options['default_date'] = $options['value']['value_group']['default_date'];
// NULL out the value field, user wanted the relative value to take hold.
// NULL out the value field, user wanted the relative value to take
// hold.
$options['value']['value_group']['value'] = NULL;
}
}
@ -512,8 +626,9 @@ class date_views_filter_handler_simple extends views_handler_filter_date {
// Our date widgets do their own validation.
}
// Update the summary values to provide
// meaningful information for each option.
/**
* {@inheritdoc}
*/
function admin_summary() {
$parts = $this->date_handler->date_parts();
$widget_options = $this->widget_options();
@ -535,4 +650,3 @@ class date_views_filter_handler_simple extends views_handler_filter_date {
}
}
// @codingStandardsIgnoreEnd

View File

@ -1,100 +1,88 @@
<?php
/**
* @file
* Date pager.
* Works with a Date argument, the argument filters
* the view and the pager provides back/next navigation.
*
* Works with a Date argument, the argument filters the view and the pager
* provides back/next navigation.
*
* USER NOTES:
*
* To use this, add a pager to a view, and choose the option to 'Page by date'.
* There are several settings:
* - The pager id: Set an id to be used as the identifier
* in the url for pager values, defaults to 'date'.
* - Pager position: Choose whether to display the date
* pager above, below, or both above and below the content.
* - Link format: Choose whether the pager links will be in t
* he simple 'calendar/2011-12' format or the
* more complex 'calendar/?date=2011-12' pager format.
* The second one is more likely to work correctly
* if the pager is used in blocks and panels.
* - The pager id: Set an id to be used as the identifier in the URL for pager
* values, defaults to 'date'.
* - Pager position: Choose whether to display the date pager above, below, or
* both above and below the content.
* - Link format: Choose whether the pager links will be in the simple
* 'calendar/2011-12' format or the more complex 'calendar/?date=2011-12'
* pager format. The second one is more likely to work correctly if the pager
* is used in blocks and panels.
*
* The pager works in combination with a Date argument
* and it will use the date fields and granularity
* set in that argument to create its back/next links.
* If the view has no Date argument, the pager can
* do nothing. The argument can either be a 'Date' argument
* that lets you select one or more date fields
* in the argument, or the simple 'Content' argument for an
* individual date field. It must be an
* argument that uses the date argument handler.
* The pager works in combination with a Date argument and it will use the date
* fields and granularity set in that argument to create its back/next links. If
* the view has no Date argument, the pager can do nothing. The argument can
* either be a 'Date' argument that lets you select one or more date fields in
* the argument, or the simple 'Content' argument for an individual date field.
* It must be an argument that uses the date argument handler.
*
* DEVELOPER NOTES
*
* The pager could technically create a query of its own rather
* than depending on the date argument to
* set the query, but it has only a limited set of tools to work
* with because it is a plugin, not a handler:
* it has no knowledge about relationships, it cannot use the
* ensure_my_table() function, plugins are not even invoked in pre_query(),
* so can't do anything there.
* The pager could technically create a query of its own rather than depending
* on the date argument to set the query, but it has only a limited set of tools
* to work with because it is a plugin, not a handler: it has no knowledge about
* relationships, it cannot use the ensure_my_table() function, plugins are not
* even invoked in pre_query(), so can't do anything there.
*
* My conclusion was that the date pager simply
* is not powerful enough to create its own queries for
* date fields, which require very complex queries.
* Instead, we can combine this with a date argument and
* let the argument create the query and let the pager
* just provide the back/next links. If there is no
* date argument, the pager will do nothing.
* My conclusion was that the date pager simply is not powerful enough to create
* its own queries for date fields, which require very complex queries. Instead,
* we can combine this with a date argument and let the argument create the
* query and let the pager just provide the back/next links. If there is no date
* argument, the pager will do nothing.
*
* There are still other problems. The pager is not even
* initialized until after all the handlers
* have created their queries, so it has no chance
* to alter values ahead of that. And the argument
* has no knowledge of the pager, so it can't check
* for pager values before the query is created.
* There are still other problems. The pager is not even initialized until after
* all the handlers have created their queries, so it has no chance to alter
* values ahead of that. And the argument has no knowledge of the pager, so it
* can't check for pager values before the query is created.
*
* The solution used here is to let the argument create
* the original query. The pager query
* runs after that, so the pager checks to see
* if there is a pager value that needs to be used in the query.
* The date argument has identified the placeholders
* it used in the query. So if a change is needed,
* we can swap the pager value into the query created
* by the date argument and adjust the
* $view->date_info values set by the argument accordingly
* so the theme will pick up the new information.
* The solution used here is to let the argument create the original query. The
* pager query runs after that, so the pager checks to see if there is a pager
* value that needs to be used in the query. The date argument has identified
* the placeholders it used in the query. So if a change is needed, we can swap
* the pager value into the query created by the date argument and adjust the
* $view->date_info values set by the argument accordingly so the theme will
* pick up the new information.
*/
/**
* Example plugin to handle paging by month.
* Views pager plugin to page by month.
*/
// @codingStandardsIgnoreStart
class date_views_plugin_pager extends views_plugin_pager {
/**
* This kind of pager does not need to count the number of records.
* {@inheritdoc}
*/
function use_count_query() {
return FALSE;
}
/**
* Because we don't know how many pages there are, we never believe there are more records.
* {@inheritdoc}
*/
function has_more_records() {
return FALSE;
}
/*
* Tell Views what this pager's setting is.
/**
* {@inheritdoc}
*/
function summary_title() {
return t("Position: @position, format: @format.", array('@position' => $this->options['pager_position'], '@format' => $this->options['link_format']));
}
/**
* Tell Views what options this plugin can store.
* {@inheritdoc}
*/
function option_definition() {
$options = parent::option_definition();
@ -107,8 +95,8 @@ class date_views_plugin_pager extends views_plugin_pager {
return $options;
}
/*
* Provide the form for setting options.
/**
* {@inheritdoc}
*/
function options_form(&$form, &$form_state) {
$form['markup']['#markup'] = t('This pager works together with a Date or Content date field contextual filter. If a Date filter has been added to the view, this pager will provide back/next paging to match the granularity of that filter (i.e. paging by year, month, week, or day). The filter must also be configured to use a DATE default value. If there is no Date contextual filter on this view, or if it has not been set to use a default date, the pager will not appear.');
@ -148,13 +136,11 @@ class date_views_plugin_pager extends views_plugin_pager {
}
/**
* Transfer date information from the argument to the view so the pager theme can use it
* and update the date argument value to whatever is set by the pager.
* {@inheritdoc}
*/
function query() {
// By fetching our data from the exposed input, it is possible to
// feed pager data through some method other than $_GET.
// By fetching our data from the exposed input, it is possible to feed
// pager data through some method other than $_GET.
$input = $this->view->get_exposed_input();
$value = NULL;
if (!empty($input) && !empty($input[$this->options['date_id']])) {
@ -166,15 +152,15 @@ class date_views_plugin_pager extends views_plugin_pager {
foreach ($this->view->argument as $id => &$argument) {
if (date_views_handler_is_date($argument, 'argument')) {
// If the argument is empty, nothing to do. This could be from
// an argument that does not set a default value.
// If the argument is empty, nothing to do. This could be from an
// argument that does not set a default value.
if (empty($argument->argument) || empty($argument->date_handler)) {
continue;
}
// Storing this information in the pager so it's available for summary info.
// The view argument information is not otherwise accessible to the pager.
// Not working right yet, tho.
// Storing this information in the pager so it's available for summary
// info. The view argument information is not otherwise accessible to
// the pager. Not working right yet, tho.
$date_handler = $argument->date_handler;
$this->options['date_argument'] = $id;
$this->options['granularity'] = $argument->date_handler->granularity;
@ -189,8 +175,9 @@ class date_views_plugin_pager extends views_plugin_pager {
$this->view->build_info['fail'] = TRUE;
return;
}
// Write date_info to store information to be used
// in the theming functions.
// Write date_info to store information to be used in the theming
// functions.
if (empty($this->view->date_info)) {
$this->view->date_info = new stdClass();
}
@ -205,8 +192,9 @@ class date_views_plugin_pager extends views_plugin_pager {
$this->view->date_info->date_pager_position = $this->options['pager_position'];
$this->view->date_info->date_pager_format = $this->options['link_format'];
$this->view->date_info->skip_empty_pages = $this->options['skip_empty_pages'] == 1;
// Execute two additional queries to find
// the previous and next page with values.
// Execute two additional queries to find the previous and next page
// with values.
if ($this->view->date_info->skip_empty_pages) {
$q = clone $argument->query;
$field = $argument->table_alias . '.' . $argument->real_field;
@ -217,8 +205,8 @@ class date_views_plugin_pager extends views_plugin_pager {
$q->set_distinct(TRUE, TRUE);
// Date limits of this argument.
$datelimits = $argument->date_handler->arg_range($argument->limit[0] . '--' . $argument->limit[1]);
// Find the first two dates between the minimum date
// and the upper bound of the current value.
// Find the first two dates between the minimum date and the upper
// bound of the current value.
$q->add_orderby(NULL, $fieldsql, 'DESC', 'date');
$this->set_argument_placeholders($this->view->date_info->placeholders, $datelimits[0], $argument->max_date, $q, $format);
@ -243,7 +231,7 @@ class date_views_plugin_pager extends views_plugin_pager {
// Set the default value of the query to $prevfirst or $nextfirst
// when there is no value and $prevsecond or $nextsecond is set.
if (empty($value)) {
// @Todo find out which of $prevdate or $nextdate is closest to the
// @todo find out which of $prevdate or $nextdate is closest to the
// default argument date value and choose that one.
if ($prevdate && $prevdatealt) {
$this->set_argument_value($argument, $prevdate);
@ -269,8 +257,8 @@ class date_views_plugin_pager extends views_plugin_pager {
}
}
else {
// $prevdate and $nextdate are the same as $value, so move to
// the next values.
// $prevdate and $nextdate are the same as $value, so move to the
// next values.
$prevdate = $prevdatealt;
$nextdate = $nextdatealt;
}
@ -279,9 +267,9 @@ class date_views_plugin_pager extends views_plugin_pager {
$this->view->date_info->next_date = $nextdate ? new DateObject($nextdate, NULL, $format) : NULL;
}
else {
$this->view->date_info->prev_date = clone($argument->min_date);
$this->view->date_info->prev_date = clone $argument->min_date;
date_modify($this->view->date_info->prev_date, '-1 ' . $argument->date_handler->granularity);
$this->view->date_info->next_date = clone($argument->min_date);
$this->view->date_info->next_date = clone $argument->min_date;
date_modify($this->view->date_info->next_date, '+1 ' . $argument->date_handler->granularity);
}
// Write the date_info properties that depend on the current value.
@ -296,14 +284,17 @@ class date_views_plugin_pager extends views_plugin_pager {
$i++;
}
// Is this a view that needs to be altered based on a pager value?
// If there is pager input and the argument has set the placeholders,
// swap the pager value in for the placeholder set by the argument.
// Is this a view that needs to be altered based on a pager value? If there
// is pager input and the argument has set the placeholders, swap the pager
// value in for the placeholder set by the argument.
if (!empty($value) && !empty($this->view->date_info->placeholders)) {
$this->set_argument_placeholders($this->view->date_info->placeholders, $this->view->date_info->min_date, $this->view->date_info->max_date, $this->view->query, $format);
}
}
/**
* {@inheritdoc}
*/
function set_argument_value($argument, $value) {
$argument->argument = $value;
$argument->date_range = $argument->date_handler->arg_range($value);
@ -314,6 +305,9 @@ class date_views_plugin_pager extends views_plugin_pager {
$argument->is_default = FALSE;
}
/**
* {@inheritdoc}
*/
function set_argument_placeholders($placeholders, $mindate, $maxdate, $query, $format) {
$count = count($placeholders);
foreach ($query->where as $group => $data) {
@ -321,8 +315,8 @@ class date_views_plugin_pager extends views_plugin_pager {
if (array_key_exists('value', $condition) && is_array($condition['value'])) {
foreach ($condition['value'] as $placeholder => $placeholder_value) {
if (array_key_exists($placeholder, $placeholders)) {
// If we didn't get a match, this is a > $min < $max query that uses the view
// min and max dates as placeholders.
// If we didn't get a match, this is a > $min < $max query that
// uses the view min and max dates as placeholders.
$date = ($count == 2) ? $mindate : $maxdate;
$next_placeholder = array_shift($placeholders);
$query->where[$group]['conditions'][$delta]['value'][$placeholder] = $date->format($format);
@ -335,7 +329,7 @@ class date_views_plugin_pager extends views_plugin_pager {
}
/**
* Add a callback to determine if we have moved outside the valid date range for this argument.
* Determine if we have moved outside the valid date range for this argument.
*/
function date_forbid($argument) {
// See if we're outside the allowed date range for our argument.
@ -346,11 +340,22 @@ class date_views_plugin_pager extends views_plugin_pager {
return FALSE;
}
/**
* {@inheritdoc}
*/
function pre_render(&$result) {
// Load theme functions for the pager.
module_load_include('inc', 'date_views', 'theme/theme');
}
/**
* {@inheritdoc}
*/
function render($input) {
// This adds all of our template suggestions based upon the view name and display id.
// This adds all of our template suggestions based upon the view name and
// display id.
$pager_theme = views_theme_functions('date_views_pager', $this->view, $this->display);
return theme($pager_theme, array('plugin' => $this, 'input' => $input));
}
}
// @codingStandardsIgnoreEnd

View File

@ -0,0 +1,515 @@
<?php
/**
* @file
* Tests date filter handler in Views.
*/
class DateViewsFilterTestCase extends DateFieldTestBase {
/**
* {@inheritdoc}
*/
public static function getInfo() {
return array(
'name' => 'Date Views - Filter Test',
'description' => 'Tests date filter handler in Views, including Date Popup fields.',
'group' => 'Date Views',
);
}
/**
* {@inheritdoc}
*/
public function setUp(array $modules = array()) {
$modules[] = 'views_ui';
$modules[] = 'date_views';
$modules[] = 'date_popup';
parent::setUp($modules);
// Initialize values.
$this->initialize();
// Reset/rebuild all data structures after enabling the modules.
$this->resetAll();
// Create a date field.
$this->createDateField();
// Create sample nodes.
$this->createDateContent();
}
/**
* Initialize test values.
*/
public function initialize() {
$this->config_url = 'admin/structure/views/nojs/config-item/test_date_filters/default/filter';
$this->extra_url = 'admin/structure/views/nojs/config-item-extra/test_date_filters/default/filter';
// This is used in views configuration and on the view page.
variable_set('date_format_short', 'Y-m-d H:i');
if ($this->privileged_user) {
// Add required permissions.
$roles = $this->privileged_user->roles;
unset($roles[2]);
$rid = key($roles);
$permissions = array('administer views'/*, 'administer site configuration'*/);
user_role_grant_permissions($rid, $permissions);
}
}
/**
* Adds a date field to a node bundle.
*/
public function createDateField($values = array()) {
// Create a date field.
$values = array(
'label' => 'Sample date',
'widget_type' => 'date_select',
'field_name' => 'sample_date',
'field_type' => 'datetime',
'input_format' => 'm/d/Y - H:i:s',
'granularity' => array('year', 'month', 'day'),
);
parent::createDateField($values);
}
/**
* Saves content with a date field.
*/
public function createDateContent() {
$years = range(date('Y') - 3, date('Y') + 3);
foreach ($years as $year) {
for($month = 1; $month < 13; $month++) {
$node = array(
'type' => 'story',
'title' => "Story from $year-$month-01",
'uid' => '1',
);
$node['sample_date']['und'][0]['value'] = "$year-$month-01";
node_save((object) $node);
}
}
}
/**
* Creates a view with a date filter for each date widget type.
*/
public function createDateFilterView() {
if ($view = views_get_view('test_date_filters')) {
// Delete view, then recreate it in a known state.
views_delete_view($view);
}
// Create the view.
$view = new view();
$view->name = 'test_date_filters';
$view->description = '';
$view->tag = 'default';
$view->base_table = 'node';
$view->human_name = 'Test date filters';
$view->core = 7;
$view->api_version = '3.0';
$view->disabled = FALSE;
/* Display: Master */
$handler = $view->new_display('default', 'Master', 'default');
$handler->display->display_options['title'] = 'Date filters';
$handler->display->display_options['use_more_always'] = FALSE;
$handler->display->display_options['access']['type'] = 'none';
$handler->display->display_options['cache']['type'] = 'none';
$handler->display->display_options['query']['type'] = 'views_query';
$handler->display->display_options['exposed_form']['type'] = 'basic';
$handler->display->display_options['exposed_form']['options']['reset_button'] = TRUE;
$handler->display->display_options['pager']['type'] = 'full';
$handler->display->display_options['pager']['options']['items_per_page'] = '10';
$handler->display->display_options['style_plugin'] = 'table';
$handler->display->display_options['style_options']['columns'] = array(
'nid' => 'nid',
'title' => 'title',
'sample_date' => 'sample_date',
);
$handler->display->display_options['style_options']['default'] = '-1';
$handler->display->display_options['style_options']['info'] = array(
'nid' => array(
'sortable' => 0,
'default_sort_order' => 'asc',
'align' => '',
'separator' => '',
'empty_column' => 0,
),
'title' => array(
'sortable' => 0,
'default_sort_order' => 'asc',
'align' => '',
'separator' => '',
'empty_column' => 0,
),
'sample_date' => array(
'sortable' => 1,
'default_sort_order' => 'asc',
'align' => '',
'separator' => '',
'empty_column' => 0,
),
);
/* Field: Content: Nid */
$handler->display->display_options['fields']['nid']['id'] = 'nid';
$handler->display->display_options['fields']['nid']['table'] = 'node';
$handler->display->display_options['fields']['nid']['field'] = 'nid';
/* Field: Content: Title */
$handler->display->display_options['fields']['title']['id'] = 'title';
$handler->display->display_options['fields']['title']['table'] = 'node';
$handler->display->display_options['fields']['title']['field'] = 'title';
$handler->display->display_options['fields']['title']['label'] = '';
$handler->display->display_options['fields']['title']['alter']['word_boundary'] = FALSE;
$handler->display->display_options['fields']['title']['alter']['ellipsis'] = FALSE;
/* Field: Content: Sample date */
$handler->display->display_options['fields']['sample_date']['id'] = 'sample_date';
$handler->display->display_options['fields']['sample_date']['table'] = 'field_data_sample_date';
$handler->display->display_options['fields']['sample_date']['field'] = 'sample_date';
$handler->display->display_options['fields']['sample_date']['settings'] = array(
'format_type' => 'short',
'custom_date_format' => '',
'fromto' => 'both',
'multiple_number' => '',
'multiple_from' => '',
'multiple_to' => '',
'show_remaining_days' => 0,
'show_repeat_rule' => 'show',
);
/* Filter criterion: Content: Type */
$handler->display->display_options['filters']['type']['id'] = 'type';
$handler->display->display_options['filters']['type']['table'] = 'node';
$handler->display->display_options['filters']['type']['field'] = 'type';
$handler->display->display_options['filters']['type']['value'] = array(
'story' => 'story',
);
$handler->display->display_options['filters']['type']['group'] = 1;
/* Filter criterion: Content: Sample date (sample_date) */
$handler->display->display_options['filters']['sample_date_select']['id'] = 'sample_date_select';
$handler->display->display_options['filters']['sample_date_select']['table'] = 'field_data_sample_date';
$handler->display->display_options['filters']['sample_date_select']['field'] = 'sample_date_value';
$handler->display->display_options['filters']['sample_date_select']['group'] = 1;
$handler->display->display_options['filters']['sample_date_select']['exposed'] = TRUE;
$handler->display->display_options['filters']['sample_date_select']['expose']['operator_id'] = 'sample_date_select_op';
$handler->display->display_options['filters']['sample_date_select']['expose']['label'] = 'Select filter';
$handler->display->display_options['filters']['sample_date_select']['expose']['operator'] = 'sample_date_select_op';
$handler->display->display_options['filters']['sample_date_select']['expose']['identifier'] = 'sample_date_select';
$handler->display->display_options['filters']['sample_date_select']['expose']['remember_roles'] = array(
2 => 0,
1 => 0,
);
$handler->display->display_options['filters']['sample_date_select']['granularity'] = 'month';
$handler->display->display_options['filters']['sample_date_select']['year_range'] = '-2:+2';
/* Filter criterion: Content: Sample date (sample_date) */
$handler->display->display_options['filters']['sample_date_text']['id'] = 'sample_date_text';
$handler->display->display_options['filters']['sample_date_text']['table'] = 'field_data_sample_date';
$handler->display->display_options['filters']['sample_date_text']['field'] = 'sample_date_value';
$handler->display->display_options['filters']['sample_date_text']['group'] = 1;
$handler->display->display_options['filters']['sample_date_text']['exposed'] = TRUE;
$handler->display->display_options['filters']['sample_date_text']['expose']['operator_id'] = 'sample_date_text_op';
$handler->display->display_options['filters']['sample_date_text']['expose']['label'] = 'Text filter';
$handler->display->display_options['filters']['sample_date_text']['expose']['operator'] = 'sample_date_text_op';
$handler->display->display_options['filters']['sample_date_text']['expose']['identifier'] = 'sample_date_text';
$handler->display->display_options['filters']['sample_date_text']['expose']['remember_roles'] = array(
2 => 0,
1 => 0,
);
$handler->display->display_options['filters']['sample_date_text']['granularity'] = 'month';
$handler->display->display_options['filters']['sample_date_text']['form_type'] = 'date_text';
$handler->display->display_options['filters']['sample_date_text']['year_range'] = '-2:+2';
/* Filter criterion: Content: Sample date (sample_date) */
$handler->display->display_options['filters']['sample_date_popup']['id'] = 'sample_date_popup';
$handler->display->display_options['filters']['sample_date_popup']['table'] = 'field_data_sample_date';
$handler->display->display_options['filters']['sample_date_popup']['field'] = 'sample_date_value';
$handler->display->display_options['filters']['sample_date_popup']['group'] = 1;
$handler->display->display_options['filters']['sample_date_popup']['exposed'] = TRUE;
$handler->display->display_options['filters']['sample_date_popup']['expose']['operator_id'] = 'sample_date_popup_op';
$handler->display->display_options['filters']['sample_date_popup']['expose']['label'] = 'Popup filter';
$handler->display->display_options['filters']['sample_date_popup']['expose']['operator'] = 'sample_date_popup_op';
$handler->display->display_options['filters']['sample_date_popup']['expose']['identifier'] = 'sample_date_popup';
$handler->display->display_options['filters']['sample_date_popup']['expose']['remember_roles'] = array(
2 => 0,
1 => 0,
);
$handler->display->display_options['filters']['sample_date_popup']['granularity'] = 'month';
$handler->display->display_options['filters']['sample_date_popup']['form_type'] = 'date_popup';
$handler->display->display_options['filters']['sample_date_popup']['year_range'] = '-2:+2';
/* Display: Page */
$handler = $view->new_display('page', 'Page', 'page_samples');
$handler->display->display_options['path'] = 'test-date-filter';
$handler->display->display_options['menu']['type'] = 'normal';
$handler->display->display_options['menu']['title'] = 'Date filters';
$handler->display->display_options['menu']['weight'] = '0';
$handler->display->display_options['menu']['context'] = 0;
$handler->display->display_options['menu']['context_only_inline'] = 0;
$view->save();
}
/**
* Test date filters.
*/
public function testDateFilters() {
if (!$this->loggedInUser) {
return;
}
try {
$this->checkSelectFilter();
$this->checkTextFilter('sample_date_text');
$this->checkTextFilter('sample_date_popup');
// @todo Seems to be a bug in popup code that reuses the last filter value
// so that query condition is invalid.
// (DATE_FORMAT(field_data_sample_date.sample_date_value, '%Y') = '2018-06')
$this->validateFilters();
}
catch(Exception $e) {
}
if ($this->loggedInUser) {
$this->drupalLogout();
}
}
/**
* Test date filter using select widget.
*/
public function checkSelectFilter() {
// Restore the view.
$this->createDateFilterView();
$year = (string) (date('Y') - 1);
$month = '06';
$filter = 'sample_date_select';
// Set the filter value.
$edit = array(
'options[value][value_group][value][year]' => $year,
'options[value][value_group][value][month]' => substr($month, -1),
);
$this->setFilter($filter, $edit);
// Inspect the table.
$this->checkTable(1, $year);
// Inspect the filter value.
$this->checkSelect($filter, 'year', $year);
$this->checkSelect($filter, 'month', substr($month, -1));
// Change granularity.
$this->setGranularity($filter, 'year');
// Inspect the table.
$this->checkTable(10, $year);
// Inspect the filter value.
$this->checkSelect($filter, 'year', $year);
}
/**
* Test date filter using text or popup widget.
*
* @param string $filter
* Filter name.
*/
public function checkTextFilter($filter) {
// Restore the view.
$this->createDateFilterView();
$year = (string) (date('Y') - 1);
$month = '06';
// Set the filter value.
$edit = array(
'options[value][value_group][value][date]' => "$year-$month",
);
$this->setFilter($filter, $edit);
// Inspect the table.
$this->checkTable(1, $year);
// Inspect the filter value.
$this->checkTextField($filter, "$year-$month");
// Change granularity.
$this->setGranularity($filter, 'year');
// Set the filter value.
// This is not necessary with the text widget but is with the popup widget.
// The popup element retains the first value and the views query uses it.
$edit = array(
'options[value][value_group][value][date]' => "$year",
);
$this->setFilter($filter, $edit);
// Inspect the table.
$this->checkTable(10, $year);
// Inspect the filter value.
$this->checkTextField($filter, $year);
}
/**
* Test date filter validation using all widgets.
*/
public function validateFilters() {
// Restore the view.
$this->createDateFilterView();
$year = (string) (date('Y') - 1);
$month = '06';
// Set the filter values.
$edit = array(
'sample_date_select[value][year]' => $year,
'sample_date_text[value][date]' => $year,
'sample_date_popup[value][date]' => $year,
);
// View the page.
// The views exposed form uses a GET method not POST.
$options = array('query' => $edit);
$this->drupalGet('test-date-filter', $options);
$messages = array(
'The value input for field Select filter is invalid',
'The month is missing',
'The value input for field Text filter is invalid',
"The value $year does not match the expected format",
'The value input for field Popup filter is invalid',
"The value $year does not match the expected format",
);
foreach ($messages as $message) {
$this->assertText($message, "Found error message: {$message}");
}
}
/**
* Set value of a date filter.
*
* @param string $filter
* Filter name.
* @param string $edit
* The associative array of form input values.
*/
public function setFilter($filter, $edit) {
// Load view configuration dialog.
$this->drupalGet($this->config_url . '/' . $filter);
$this->drupalPost(NULL, $edit, t('Apply'));
// Save the view.
$edit = array();
$this->drupalPost(NULL, $edit, t('Save'));
}
/**
* Set granularity of a date filter.
*
* @param string $filter
* Filter name.
* @param string $granularity
* The granularity value.
*/
public function setGranularity($filter, $granularity) {
// Load view configuration dialog.
$this->drupalGet($this->extra_url . '/' . $filter);
$edit = array(
'options[granularity]' => $granularity,
);
$this->drupalPost(NULL, $edit, t('Apply'));
// Save the view.
$edit = array();
$this->drupalPost(NULL, $edit, t('Save'));
}
/**
* Inspect the select field[s] of a date filter.
*
* @param string $filter
* Filter name.
* @param string $part
* Date part.
* @param string $expected
* The expected field value.
*/
public function checkSelect($filter, $part, $expected) {
// Inspect the filter value.
$xpath = '//select[@name="' . $filter . '[value][' . $part . ']"]';
$elements = $this->xpath($xpath);
if (is_array($elements)) {
$select = $elements[0];
$value = $this->getSelectedItem($select);
$this->assertEqual($value, $expected, "Filter contains $expected");
}
else {
$this->assertTrue(FALSE, "Filter does NOT contain $expected");
}
}
/**
* Inspect the text field of a date filter.
*
* @param string $filter
* Filter name.
* @param string $expected
* The expected field value.
*/
public function checkTextField($filter, $expected) {
// Inspect the filter value.
$xpath = '//input[@name="' . $filter . '[value][date]"]';
$elements = $this->xpath($xpath);
if (is_array($elements)) {
$value = (array) $elements[0];
$value = $value['@attributes']['value'];
$this->assertEqual($value, $expected, "Filter contains $expected");
}
else {
$this->assertTrue(FALSE, "Filter does NOT contain $expected");
}
}
/**
* Inspect contents of view output table.
*
* @param int $count
* Expected number of rows in table.
* @param string $year
* Expected year value in cells of table.
*/
public function checkTable($count, $year) {
// View the page.
$this->drupalGet('test-date-filter');
// Count rows in table.
$xpath = '//table[contains(@class, "views-table")]//tbody//tr';
$elements = $this->xpath($xpath);
if (is_array($elements)) {
$this->assertEqual(count($elements), $count, "Table contains $count rows for $year");
}
else {
$this->assertTrue(FALSE, "Table is empty for $year");
}
// Check values in table.
// views-field views-field-sample-date
$xpath = '//td[contains(@class, "views-field-sample-date")]//span';
$elements = $this->xpath($xpath);
if (is_array($elements)) {
foreach ($elements as $key => $element) {
$row = $key + 1;
$this->assertTrue(strpos((string) $element, $year) !== FALSE, "Year in row $row is $year");
}
}
else {
$this->assertTrue(FALSE, "Table rows are empty for $year");
}
}
}

View File

@ -0,0 +1,137 @@
<?php
/**
* @file
* Views date pager test.
*/
/**
*
*/
class ViewsPagerTestCase extends DrupalWebTestCase {
/**
* Test info.
*/
public static function getInfo() {
return array(
'name' => 'Date views pager skipping test',
'description' => "Views date pager, option to skip empty pages test",
'group' => 'Date',
'dependencies' => array('ctools', 'views'),
);
}
/**
* {@inheritdoc}
*/
public function setUp(array $modules = array()) {
$modules[] = 'date_views';
$modules[] = 'ctools';
$modules[] = 'views';
$modules[] = 'views_ui';
parent::setUp($modules);
// Create admin user and login.
$perms = array('administer views', 'administer site configuration');
$admin_user = $this->drupalCreateUser($perms);
$this->drupalLogin($admin_user);
// Create a new view for test.
$view = new view();
$view->name = 'test_date_pager';
$view->description = '';
$view->tag = 'default';
$view->base_table = 'node';
$view->human_name = 'test_date_pager';
$view->core = 7;
$view->api_version = '3.0';
$view->disabled = FALSE; /* Edit this to TRUE to make a default view disabled initially */
/* Display: Master */
$handler = $view->new_display('default', 'Master', 'default');
$handler->display->display_options['title'] = 'test_date_pager';
$handler->display->display_options['use_more_always'] = FALSE;
$handler->display->display_options['access']['type'] = 'perm';
$handler->display->display_options['cache']['type'] = 'none';
$handler->display->display_options['query']['type'] = 'views_query';
$handler->display->display_options['exposed_form']['type'] = 'basic';
$handler->display->display_options['pager']['type'] = 'date_views_pager';
$handler->display->display_options['pager']['options']['skip_empty_pages'] = 1;
$handler->display->display_options['style_plugin'] = 'default';
$handler->display->display_options['row_plugin'] = 'node';
/* Field: Content: Title */
$handler->display->display_options['fields']['title']['id'] = 'title';
$handler->display->display_options['fields']['title']['table'] = 'node';
$handler->display->display_options['fields']['title']['field'] = 'title';
$handler->display->display_options['fields']['title']['label'] = '';
$handler->display->display_options['fields']['title']['alter']['word_boundary'] = FALSE;
$handler->display->display_options['fields']['title']['alter']['ellipsis'] = FALSE;
/* Sort criterion: Content: Post date */
$handler->display->display_options['sorts']['created']['id'] = 'created';
$handler->display->display_options['sorts']['created']['table'] = 'node';
$handler->display->display_options['sorts']['created']['field'] = 'created';
$handler->display->display_options['sorts']['created']['order'] = 'DESC';
/* Contextual filter: Date: Date (node) */
$handler->display->display_options['arguments']['date_argument']['id'] = 'date_argument';
$handler->display->display_options['arguments']['date_argument']['table'] = 'node';
$handler->display->display_options['arguments']['date_argument']['field'] = 'date_argument';
$handler->display->display_options['arguments']['date_argument']['default_action'] = 'default';
$handler->display->display_options['arguments']['date_argument']['default_argument_type'] = 'date';
$handler->display->display_options['arguments']['date_argument']['summary']['format'] = 'default_summary';
$handler->display->display_options['arguments']['date_argument']['granularity'] = 'hour';
$handler->display->display_options['arguments']['date_argument']['date_fields'] = array(
'node.created' => 'node.created',
);
/* Filter criterion: Content: Published */
$handler->display->display_options['filters']['status']['id'] = 'status';
$handler->display->display_options['filters']['status']['table'] = 'node';
$handler->display->display_options['filters']['status']['field'] = 'status';
$handler->display->display_options['filters']['status']['value'] = 1;
$handler->display->display_options['filters']['status']['group'] = 1;
$handler->display->display_options['filters']['status']['expose']['operator'] = FALSE;
/* Display: Page */
$handler = $view->new_display('page', 'Page', 'page_1');
$handler->display->display_options['path'] = 'test_date_pager';
$view->save();
}
/**
* Test pager skipping.
*/
public function testPagerSkipping() {
// Go to view admin page.
$this->drupalGet('admin/structure/views/view/display/test_date_pager/edit');
// Go to pager options.
$this->drupalGet('admin/structure/views/nojs/display/test_date_pager/default/pager_options');
// Check if "Skip empty pages" text - exist.
$this->assertText('Skip empty pages');
// Check if field and it's value is correct.
$this->assertFieldByName('pager_options[skip_empty_pages]', '1');
// Go back to view admin page.
$this->drupalGet('admin/structure/views/view/display/test_date_pager/edit');
// Check if pager on empty page are gone.
$this->assertNoText('« Prev', 'Previous pager does not exist');
$this->assertNoText('Next »', 'Next pager does not exist');
}
/**
* Test the view page has no PHP warnings.
*/
public function testPagerWarning() {
$this->drupalCreateNode(array('type' => 'blog'));
// Set pager to skip empty pages.
$edit = array(
'pager_options[skip_empty_pages]' => FALSE,
);
$this->drupalPost('admin/structure/views/nojs/display/test_date_pager/default/pager_options', $edit, t('Apply'));
// Save the view.
$this->drupalPost('admin/structure/views/view/test_date_pager/edit', array(), t('Save'));
// Visit view page. This will throw error, if any PHP warnings or errors.
$this->drupalGet('test_date_pager');
}
}

View File

@ -4,18 +4,18 @@
* @file
* Template to display the Views date filter form.
*
* Values available vary depending on the operator. The availability
* of date vs adjustment depending on the filter settings. It can
* be date-only, date and adjustment, or adjustment only.
* Values available vary depending on the operator. The availability of date vs
* adjustment depending on the filter settings. It can be date-only, date and
* adjustment, or adjustment only.
*
* If the operator is anything but 'Is between' or 'Is not between',
* a single date and adjustment field is available.
* If the operator is anything but 'Is between' or 'Is not between', a single
* date and adjustment field is available.
*
* $date
* $adjustment
*
* If the operator is 'Is between' or 'Is not between',
* two date and adjustment fields are available.
* If the operator is 'Is between' or 'Is not between', two date and adjustment
* fields are available.
*
* $mindate
* $minadjustment
@ -28,28 +28,27 @@
*/
?>
<div class="date-views-filter-wrapper">
<div class="container-inline-date date-clear">
<?php if (!empty($date) || !empty($adjustment)) : ?>
<div class="date-clear">
<div class="date-views-filter"><?php print $date; ?></div>
<div class="date-views-filter"><?php print $adjustment ?></div>
<div class="container-inline-date date-clear">
<?php if (!empty($date) || !empty($adjustment)): ?>
<div class="date-clear">
<div class="date-views-filter"><?php print $date; ?></div>
<div class="date-views-filter"><?php print $adjustment ?></div>
</div>
<?php endif; ?>
<?php if (!empty($mindate) || !empty($minadjustment)): ?>
<div class="date-clear">
<div class="date-views-filter"><?php print $mindate; ?></div>
<div class="date-views-filter"><?php print $minadjustment; ?></div>
</div>
<?php endif; ?>
<?php if (!empty($maxdate) || !empty($maxadjustment)): ?>
<div class="date-clear">
<div class="date-views-filter"><?php print $maxdate; ?></div>
<div class="date-views-filter"><?php print $maxadjustment; ?></div>
</div>
<?php endif; ?>
</div>
<?php endif; ?>
<?php if (!empty($mindate) || !empty($minadjustment)) : ?>
<div class="date-clear">
<div class="date-views-filter"><?php print $mindate; ?></div>
<div class="date-views-filter"><?php print $minadjustment; ?></div>
</div>
<?php endif; ?>
<?php if (!empty($maxdate) || !empty($maxadjustment)) : ?>
<div class="date-clear">
<div class="date-views-filter"><?php print $maxdate; ?></div>
<div class="date-views-filter"><?php print $maxadjustment; ?></div>
</div>
<?php endif; ?>
</div>
<div class="date-clear form-item"><div class="description">
<?php print $description; ?>
</div>
</div>
<div class="date-clear form-item"><div class="description">
<?php print $description; ?>
</div></div>
</div>

View File

@ -1,33 +1,33 @@
<?php
/**
* @file
* Template file for the example display.
*
* Variables available:
*
*
* $plugin: The pager plugin object. This contains the view.
*
* $plugin->view
* The view object for this navigation.
*
* $nav_title
* The formatted title for this view. In the case of block
* views, it will be a link to the full view, otherwise it will
* be the formatted name of the year, month, day, or week.
* The formatted title for this view. In the case of block views, it will be a
* link to the full view, 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.
* 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.
*
* $prev_options
* $next_options
* Query strings and other options for the links that need to
* be used in the l() function, including rel=nofollow.
* Query strings and other options for the links that need to be used in the
* l() function, including rel=nofollow.
*/
?>
<?php if (!empty($pager_prefix)) : ?>
<?php if (!empty($pager_prefix)): ?>
<?php print $pager_prefix; ?>
<?php endif; ?>
<div class="date-nav-wrapper clearfix<?php if (!empty($extra_classes)): print $extra_classes; endif; ?>">
@ -36,7 +36,7 @@
<h3><?php print $nav_title ?></h3>
</div>
<ul class="pager">
<?php if (!empty($prev_url)) : ?>
<?php if (!empty($prev_url)): ?>
<li class="date-prev">
<?php
$text = '&laquo;';
@ -45,7 +45,7 @@
?>
</li>
<?php endif; ?>
<?php if (!empty($next_url)) : ?>
<?php if (!empty($next_url)): ?>
<li class="date-next">
<?php print l(($mini ? '' : t('Next', array(), array('context' => 'date_nav')) . ' ') . '&raquo;', $next_url, $next_options); ?>
</li>

View File

@ -20,9 +20,6 @@ function date_views_preprocess_views_view(&$vars) {
case 'both':
$vars['header'] .= $vars['pager'];
break;
default:
// Already on the bottom.
}
}
}
@ -60,8 +57,8 @@ function template_preprocess_date_views_pager(&$vars) {
$min_date = $date_info->min_date;
$max_date = $date_info->max_date;
// Set up the pager link format. Setting the block identifier
// will force pager style links.
// Set up the pager link format. Setting the block identifier will force
// pager style links.
if ((isset($date_info->date_pager_format) && $date_info->date_pager_format != 'clean') || !empty($date_info->mini)) {
if (empty($date_info->block_identifier)) {
$date_info->block_identifier = $date_info->pager_id;
@ -79,6 +76,7 @@ function template_preprocess_date_views_pager(&$vars) {
$prev_week = date_week(date_format($prev_date, 'Y-m-d'));
$prev_arg = date_format($prev_date, 'o-\W') . date_pad($prev_week);
break;
default:
$prev_arg = date_format($prev_date, $format[$granularity]);
}
@ -92,6 +90,7 @@ function template_preprocess_date_views_pager(&$vars) {
$next_week = date_week(date_format($next_date, 'Y-m-d'));
$next_arg = date_format($next_date, 'o-\W') . date_pad($next_week);
break;
default:
$next_arg = date_format($next_date, $format[$granularity]);
}
@ -110,8 +109,8 @@ function template_preprocess_date_views_pager(&$vars) {
$vars['next_options'] = $vars['prev_options'] = array();
}
// Check whether navigation links would point to
// a date outside the allowed range.
// Check whether navigation links would point to a date outside the allowed
// range.
if (!empty($next_date) && !empty($vars['next_url']) && date_format($next_date, 'Y') > $date_info->limit[1]) {
$vars['next_url'] = '';
}
@ -145,6 +144,7 @@ function template_preprocess_date_views_pager(&$vars) {
$next_title = t('Navigate to next day');
break;
}
$vars['prev_options']['attributes'] += array('title' => $prev_title);
$vars['next_options']['attributes'] += array('title' => $next_title);
@ -183,6 +183,7 @@ function theme_date_nav_title($params) {
$format = !empty($params['format']) ? $params['format'] : NULL;
$format_with_year = variable_get('date_views_' . $granularity . '_format_with_year', 'l, F j, Y');
$format_without_year = variable_get('date_views_' . $granularity . '_format_without_year', 'l, F j');
switch ($granularity) {
case 'year':
$title = $date_info->year;
@ -213,6 +214,7 @@ function theme_date_nav_title($params) {
$date_arg = $date_info->year . '-W' . date_pad($date_info->week);
break;
}
if (!empty($date_info->mini) || $link) {
// Month navigation titles are used as links in the mini view.
$attributes = array('title' => t('View full page month'));
@ -236,4 +238,5 @@ function template_preprocess_date_views_filter_form(&$vars) {
$vars['minadjustment'] = drupal_render($form['minadjustment']);
$vars['maxadjustment'] = drupal_render($form['maxadjustment']);
$vars['description'] = drupal_render($form['description']) . drupal_render($form);
drupal_add_css(drupal_get_path('module', 'date_api') . '/date.css');
}

View File

@ -0,0 +1,143 @@
<?php
/**
* @file
* Test Date (ISO) and the Entity Metadata Wrappers play well together.
*/
/**
* Test Date (ISO) and the Entity Metadata Wrappers play well together.
*/
class DateEmwTestCase extends DrupalWebTestCase {
/**
* Info about this test.
*/
public static function getInfo() {
return array(
'name' => 'Date Entity Metadata Wrappers',
'description' => 'Ensure default timezone setting in metadata wrapper integration is working as expected.',
'group' => 'Date',
'dependencies' => array('entity'),
);
}
/**
* Set up a user and a content type with a Date (ISO) field.
*/
public function setUp(array $modules = array()) {
// Define the dependencies.
$modules[] = 'date_api';
$modules[] = 'date';
$modules[] = 'field';
$modules[] = 'field_ui';
$modules[] = 'entity';
$modules[] = 'entity_token';
parent::setUp($modules);
// Select a specific default timezone, and turn off user-configurable
// timezone handling.
variable_set('date_default_timezone', 'Pacific/Honolulu');
variable_set('configurable_timezones', 0);
// Create a privileged user and log in with it.
$this->privileged_user = $this->drupalCreateUser(array(
'administer site configuration',
'administer content types',
'administer nodes',
'bypass node access',
'administer fields',
));
$this->drupalLogin($this->privileged_user);
// Create a content type to which we can add our date fields.
$edit = array();
$edit['name'] = 'Story';
$edit['type'] = 'story';
$this->drupalPost('admin/structure/types/add', $edit, t('Save content type'));
$this->assertText('The content type Story has been added.', 'Content type added.');
// Add each of our date field types.
$this->date_types = array(
'date' => 'Date (ISO format)',
'datetime' => 'Date',
'datestamp' => 'Date (Unix timestamp)',
);
foreach ($this->date_types as $type => $label) {
$field_name_for_human = 'test_' . $type;
// Add the field.
$field = array();
$field['fields[_add_new_field][label]'] = $label;
$field['fields[_add_new_field][field_name]'] = $field_name_for_human;
$field['fields[_add_new_field][type]'] = $type;
$field['fields[_add_new_field][widget_type]'] = 'date_text';
$this->drupalPost('admin/structure/types/manage/story/fields', $field, 'Save');
$this->assertText('These settings apply to the ' . $field['fields[_add_new_field][label]'] . ' field everywhere it is used.', $label . ' field added to content type.');
// Set the timezone handling to 'none'.
$field = array();
$field['field[settings][tz_handling]'] = 'none';
$this->drupalPost('admin/structure/types/manage/story/fields/field_' . $field_name_for_human, $field, 'Save settings');
$this->assertText('Saved ' . $label . ' configuration.');
}
}
/**
* Test the edge case where timezone_db is empty.
*
* In this case the value returned by entity metadata wrappers is not the same
* as that which is stored in the node.
*
* @see https://www.drupal.org/node/2123039
*/
public function testCheckEntityMetadataWrapper() {
// Create and save a edit.
$edit = array();
$edit['title'] = 'Testing dates';
$edit['type'] = 'story';
$this->drupalCreateNode($edit);
// Fetch the Node.
$node = $this->drupalGetNodeByTitle($edit['title']);
// Prepare variables for token replacement.
$variables['node'] = $node;
// At this point we have a node with the appropriate field types available.
$wrapper = entity_metadata_wrapper('node', $node);
$original = '1482721671';
foreach ($this->date_types as $type => $label) {
$field = 'field_test_' . $type;
$wrapper->{$field} = $original;
$from_metadata_wrapper = $wrapper->{$field}->value();
$this->assertEqual(
$original,
$from_metadata_wrapper,
t(
'!type plays nicely with Entity Metadata Wrappers. !original == !fetched',
array(
'!type' => $label,
'!original' => $original,
'!fetched' => $from_metadata_wrapper,
)
)
);
$token = "[node:field-test-${type}:raw]";
$from_token = token_replace($token, $variables);
$this->assertEqual(
$original,
$from_token,
t(
'!type plays nicely with Entity tokens. !original == !fetched',
array(
'!type' => $label,
'!original' => $original,
'!fetched' => $from_token,
)
)
);
}
}
}

View File

@ -0,0 +1,406 @@
<?php
/**
* @file
* Shared test functionality.
*/
/**
* Shared test functionality.
*/
abstract class DateFieldTestBase extends DrupalWebTestCase {
protected $privileged_user;
/**
* {@inheritdoc}
*/
public function setUp(array $modules = array()) {
$modules[] = 'field';
$modules[] = 'field_ui';
$modules[] = 'date_api';
$modules[] = 'date';
$modules[] = 'date_tools';
parent::setUp($modules);
// Create and log in our privileged user.
$perms = array(
'administer content types',
'administer nodes',
'bypass node access',
'administer date tools',
'administer fields',
);
$this->privileged_user = $this->drupalCreateUser($perms);
$this->drupalLogin($this->privileged_user);
module_load_include('inc', 'node', 'content_types');
module_load_include('inc', 'node', 'node.pages');
module_load_include('inc', 'field', 'field.crud');
module_load_include('inc', 'date', 'date_admin');
$edit = array();
$edit['name'] = 'Story';
$edit['type'] = 'story';
$this->drupalPost('admin/structure/types/add', $edit, t('Save content type'));
$this->assertText('The content type Story has been added.', 'Content type added.');
}
/**
* Creates a date field from an array of settings values.
*
* All values have defaults, only need to specify values that need to be
* different.
*/
protected function createDateField($values = array()) {
$this->verbose($values);
extract($values);
$field_name = !empty($field_name) ? $field_name : 'field_test';
$entity_type = !empty($entity_type) ? $entity_type : 'node';
$bundle = !empty($bundle) ? $bundle : 'story';
$label = !empty($label) ? $label : 'Test';
$field_type = !empty($field_type) ? $field_type : 'datetime';
$repeat = !empty($repeat) ? $repeat : 0;
$todate = !empty($todate) ? $todate : 'optional';
$widget_type = !empty($widget_type) ? $widget_type : 'date_select';
$tz_handling = !empty($tz_handling) ? $tz_handling : 'site';
$granularity = !empty($granularity) ? $granularity : array('year', 'month', 'day', 'hour', 'minute');
$year_range = !empty($year_range) ? $year_range : '2010:+1';
$input_format = !empty($input_format) ? $input_format : date_default_format($widget_type);
$input_format_custom = !empty($input_format_custom) ? $input_format_custom : '';
$text_parts = !empty($text_parts) ? $text_parts : array();
$increment = !empty($increment) ? $increment : 15;
$default_value = !empty($default_value) ? $default_value : 'now';
$default_value_code = !empty($default_value_code) ? $default_value_code : NULL;
$default_value2 = !empty($default_value2) ? $default_value2 : 'blank';
$default_format = !empty($default_format) ? $default_format : 'long';
$cache_enabled = !empty($cache_enabled);
$cache_count = !empty($cache_count) ? $cache_count : 4;
$field = array(
'field_name' => $field_name,
'type' => $field_type,
'cardinality' => !empty($repeat) ? FIELD_CARDINALITY_UNLIMITED : 1,
'settings' => array(
'granularity' => $granularity,
'tz_handling' => $tz_handling,
'timezone_db' => date_get_timezone_db($tz_handling),
'repeat' => $repeat,
'todate' => $todate,
'cache_enabled' => $cache_enabled,
'cache_count' => $cache_count,
),
);
$instance = array(
'entity_type' => $entity_type,
'field_name' => $field_name,
'label' => $label,
'bundle' => $bundle,
// Move the date to the top.
'weight' => -100,
'widget' => array(
'type' => $widget_type,
// Increment for minutes and seconds, can be 1, 5, 10, 15, or 30.
'settings' => array(
'increment' => $increment,
// The number of years to go back and forward in drop-down year
// selectors.
'year_range' => $year_range,
'input_format' => $input_format,
'input_format_custom' => $input_format_custom,
'text_parts' => $text_parts,
'label_position' => 'above',
'repeat_collapsed' => 0,
),
'weight' => -100,
),
'settings' => array(
'default_value' => $default_value,
'default_value_code' => $default_value_code,
'default_value2' => $default_value2,
),
);
$instance['display'] = array(
'default' => array(
'label' => 'above',
'type' => 'date_default',
'settings' => array(
'format_type' => $default_format,
'show_repeat_rule' => 'show',
'multiple_number' => '',
'multiple_from' => '',
'multiple_to' => '',
'fromto' => 'both',
),
'module' => 'date',
'weight' => 0 ,
),
'teaser' => array(
'label' => 'above',
'type' => 'date_default',
'weight' => 0,
'settings' => array(
'format_type' => $default_format,
'show_repeat_rule' => 'show',
'multiple_number' => '',
'multiple_from' => '',
'multiple_to' => '',
'fromto' => 'both',
),
'module' => 'date',
),
);
$this->verbose($field);
$field = field_create_field($field);
$this->verbose($instance);
$instance = field_create_instance($instance);
field_info_cache_clear();
field_cache_clear();
// Look at how the field got configured.
$this->drupalGet("admin/structure/types/manage/$bundle/fields/$field_name");
$this->drupalGet("admin/structure/types/manage/$bundle/display");
}
/**
* Creates a date field from an array of settings values.
*
* All values have defaults, only need to specify values that need to be
* different.
*/
protected function createMultiDateField($values = array()) {
extract($values);
$field_name = !empty($field_name) ? $field_name : 'field_test';
$entity_type = !empty($entity_type) ? $entity_type : 'node';
$bundle = !empty($bundle) ? $bundle : 'story';
$label = !empty($label) ? $label : 'Test';
$field_type = !empty($field_type) ? $field_type : 'datetime';
$repeat = !empty($repeat) ? $repeat : 0;
$todate = !empty($todate) ? $todate : 'optional';
$widget_type = !empty($widget_type) ? $widget_type : 'date_select';
$this->verbose(!empty($tz_handling));
$tz_handling = !empty($tz_handling) ? $tz_handling : 'site';
$granularity = !empty($granularity) ? $granularity : array('year', 'month', 'day', 'hour', 'minute');
$year_range = !empty($year_range) ? $year_range : '2010:+1';
$input_format = !empty($input_format) ? $input_format : date_default_format($widget_type);
$input_format_custom = !empty($input_format_custom) ? $input_format_custom : '';
$text_parts = !empty($text_parts) ? $text_parts : array();
$increment = !empty($increment) ? $increment : 15;
$default_value = !empty($default_value) ? $default_value : 'now';
$default_value2 = !empty($default_value2) ? $default_value2 : 'blank';
$default_format = !empty($default_format) ? $default_format : 'long';
$cache_enabled = !empty($cache_enabled);
$cache_count = !empty($cache_count) ? $cache_count : 4;
$cardinality = !empty($cardinality) ? $cardinality : 1;
$field = array(
'field_name' => $field_name,
'type' => $field_type,
'cardinality' => $cardinality,
'settings' => array(
'granularity' => $granularity,
'tz_handling' => $tz_handling,
'timezone_db' => date_get_timezone_db($tz_handling),
'repeat' => $repeat,
'todate' => $todate,
'cache_enabled' => $cache_enabled,
'cache_count' => $cache_count,
),
);
$instance = array(
'entity_type' => $entity_type,
'field_name' => $field_name,
'label' => $label,
'bundle' => $bundle,
// Move the date to the top.
'weight' => -100,
'widget' => array(
'type' => $widget_type,
// Increment for minutes and seconds, can be 1, 5, 10, 15, or 30.
'settings' => array(
'increment' => $increment,
// The number of years to go back and forward in drop-down year
// selectors.
'year_range' => $year_range,
'input_format' => $input_format,
'input_format_custom' => $input_format_custom,
'text_parts' => $text_parts,
'label_position' => 'above',
'repeat_collapsed' => 0,
),
'weight' => -100,
),
'settings' => array(
'default_value' => $default_value,
'default_value2' => $default_value2,
),
);
$instance['display'] = array(
'default' => array(
'label' => 'above',
'type' => 'date_default',
'settings' => array(
'format_type' => $default_format,
'show_repeat_rule' => 'show',
'multiple_number' => '',
'multiple_from' => '',
'multiple_to' => '',
'fromto' => 'both',
),
'module' => 'date',
'weight' => 0 ,
),
'teaser' => array(
'label' => 'above',
'type' => 'date_default',
'weight' => 0,
'settings' => array(
'format_type' => $default_format,
'show_repeat_rule' => 'show',
'multiple_number' => '',
'multiple_from' => '',
'multiple_to' => '',
'fromto' => 'both',
),
'module' => 'date',
),
);
$field = field_create_field($field);
$instance = field_create_instance($instance);
field_info_cache_clear();
field_cache_clear();
// Look at how the field got configured.
$this->drupalGet("admin/structure/types/manage/$bundle/fields");
$this->drupalGet("admin/structure/types/manage/$bundle/fields/$field_name");
$this->drupalGet("admin/structure/types/manage/$bundle/display");
}
/**
* @todo.
*/
protected function deleteDateField($label, $bundle = 'story', $bundle_name = 'Story') {
$this->drupalGet("admin/structure/types/manage/$bundle/fields");
$this->clickLink('delete');
$this->drupalPost(NULL, NULL, t('Delete'));
$this->assertText("The field $label has been deleted from the $bundle_name content type.", 'Removed date field.');
}
/**
* {@inheritdoc}
*/
protected function verbose($message, $title = NULL) {
// Handle arrays, objects, etc.
if (!is_string($message)) {
$message = "<pre>\n" . print_r($message, TRUE) . "\n</pre>\n";
}
// Optional title to go before the output.
if (!empty($title)) {
$title = '<h2>' . check_plain($title) . "</h2>\n";
}
parent::verbose($title . $message);
}
/**
* Tests that date field functions properly.
*/
public function dateForm($field_name, $field_type, $widget_type, $todate = TRUE) {
$this->verbose(func_get_args());
$edit = array();
$edit['title'] = $this->randomName(8);
if ($widget_type == 'date_select') {
$edit[$field_name . '[und][0][value][year]'] = '2010';
$edit[$field_name . '[und][0][value][month]'] = '10';
$edit[$field_name . '[und][0][value][day]'] = '7';
$edit[$field_name . '[und][0][value][hour]'] = '10';
$edit[$field_name . '[und][0][value][minute]'] = '30';
if ($todate) {
$edit[$field_name . '[und][0][show_todate]'] = '1';
$edit[$field_name . '[und][0][value2][year]'] = '2010';
$edit[$field_name . '[und][0][value2][month]'] = '10';
$edit[$field_name . '[und][0][value2][day]'] = '7';
$edit[$field_name . '[und][0][value2][hour]'] = '11';
$edit[$field_name . '[und][0][value2][minute]'] = '30';
}
}
elseif ($widget_type == 'date_text') {
$edit[$field_name . '[und][0][value][date]'] = '10/07/2010 - 10:30';
if ($todate) {
$edit[$field_name . '[und][0][show_todate]'] = '1';
$edit[$field_name . '[und][0][value2][date]'] = '10/07/2010 - 11:30';
}
}
elseif ($widget_type == 'date_popup') {
$edit[$field_name . '[und][0][value][date]'] = '10/07/2010';
$edit[$field_name . '[und][0][value][time]'] = '10:30';
if ($todate) {
$edit[$field_name . '[und][0][show_todate]'] = '1';
$edit[$field_name . '[und][0][value2][date]'] = '10/07/2010';
$edit[$field_name . '[und][0][value2][time]'] = '11:30';
}
}
// Test that the date is displayed correctly using both the 'short' and
// 'long' date types.
// For the short type, save an explicit format and assert that is the one
// which is displayed.
variable_set('date_format_short', 'l, m/d/Y - H:i:s');
$instance = field_info_instance('node', $field_name, 'story');
$instance['display']['default']['settings']['format_type'] = 'short';
field_update_instance($instance);
$this->drupalPost('node/add/story', $edit, t('Save'));
$this->assertText($edit['title'], "Node has been created");
$should_be = $todate ? 'Thursday, 10/07/2010 - 10:30 to 11:30' : 'Thursday, 10/07/2010 - 10:30';
$this->assertText($should_be, "Found the correct date for a $field_type field using the $widget_type widget displayed using the short date format.");
// For the long format, do not save anything, and assert that the displayed
// date uses the expected default value of this format provided by Drupal
// core ('l, F j, Y - H:i').
$instance = field_info_instance('node', $field_name, 'story');
$instance['display']['default']['settings']['format_type'] = 'long';
field_update_instance($instance);
$this->drupalPost('node/add/story', $edit, t('Save'));
$this->assertText($edit['title'], "Node has been created");
$should_be = $todate ? 'Thursday, October 7, 2010 - 10:30 to 11:30' : 'Thursday, October 7, 2010 - 10:30';
$this->assertText($should_be, "Found the correct date for a $field_type field using the $widget_type widget displayed using the long date format.");
}
/**
* Run some tests against a specific field type/widget combination.
*
* @param string $field_type
* The field type to use.
* @param string $widget_type
* The widget type to use.
* @param bool $delete_when_done
* Whether to delete the field when it's finished; defaults to TRUE.
*/
protected function checkDateField($field_type, $widget_type, $delete_when_done = TRUE) {
$field_name = "field_test_$widget_type";
$label = 'Test';
$options = array(
'label' => $label,
'widget_type' => $widget_type,
'field_name' => $field_name,
'field_type' => $field_type,
'input_format' => 'm/d/Y - H:i',
);
$this->createDateField($options);
$this->dateForm($field_name, $field_type, $widget_type);
if ($delete_when_done) {
$this->deleteDateField($label);
}
}
}

View File

@ -0,0 +1,59 @@
<?php
/**
* @file
* Test the handling of the two included field widgets.
*/
/**
* Test the handling of the two included field widgets.
*/
class DateFieldTestCase extends DateFieldTestBase {
/**
* Test relative default values.
*/
public function testRelativeDefault() {
$edit = array();
$edit['name'] = 'Date Relative Default Test';
$edit['type'] = 'date_relative_default_test';
$this->drupalPost('admin/structure/types/add', $edit, t('Save content type'));
$this->assertText('The content type Date Relative Default Test has been added.', 'Content type added.');
$options = array(
'bundle' => 'date_relative_default_test',
'default_value' => 'strtotime',
'default_value_code' => '+1',
);
$this->createDateField($options);
$edit = array();
$edit['title'] = $this->randomName(8);
$this->drupalPost('node/add/date-relative-default-test', $edit, t('Save'));
}
/**
* @todo.
*/
public static function getInfo() {
return array(
'name' => 'Date Field',
'description' => 'Test date field settings and Start/End date interaction.',
'group' => 'Date',
);
}
/**
* Check all of the included field types for basic functionality.
*/
public function testField() {
// Create a date fields with simple values.
foreach (array('date', 'datestamp', 'datetime') as $field_type) {
foreach (array('date_select', 'date_text') as $widget_type) {
$this->checkDateField($field_type, $widget_type, TRUE);
}
}
}
}

View File

@ -0,0 +1,39 @@
<?php
/**
* @file
* Contains form specific date element test cases.
*/
/**
* Contains form specific date element test cases.
*/
class DateFormTestCase extends DrupalWebTestCase {
/**
* {@inheritdoc}
*/
public static function getInfo() {
return array(
'name' => t('Date Form test'),
'description' => t('Test Date form functions.') ,
'group' => t('Date'),
);
}
/**
* {@inheritdoc}
*/
public function setUp(array $modules = array()) {
$modules[] = 'date_test';
parent::setUp($modules);
}
/**
* Tests rendering of a date element in a form.
*/
public function testDateForm() {
$this->drupalGet('date-test/form');
}
}

View File

@ -0,0 +1,95 @@
<?php
/**
* @file
* Test for using date fields with Migrate module.
*/
/**
* Test for using date fields with Migrate module.
*/
class DateMigrateTestCase extends DrupalWebTestCase {
/**
* Provides information about this test.
*/
public static function getInfo() {
return array(
'name' => 'Date Migration',
'description' => 'Test migration into date fields.',
'group' => 'Date',
'dependencies' => array('migrate', 'features'),
);
}
/**
* {@inheritdoc}
*/
public function setUp(array $modules = array()) {
$modules[] = 'date_test_feature';
$modules[] = 'date_migrate_test';
parent::setUp($modules);
// Make sure the migration is registered.
if (function_exists('migrate_static_registration')) {
// Migrate 2.6 and later.
migrate_static_registration();
}
else {
// Migrate 2.5 and earlier.
migrate_get_module_apis(TRUE);
}
}
/**
* Verify that date fields are imported correctly.
*
* When no timezone is explicitly provided with the source data, we want the
* displayed time on the Drupal site to match that in the source data. To
* validate that, we make sure we have set a consistent timezone at the PHP
* and Drupal levels, and that the format used on the page is not locale-
* dependent (no day or month names). Then, we can just look for the desired
* date/time strings in the node page.
*/
public function testDateImport() {
date_default_timezone_set('America/Los_Angeles');
variable_set('date_default_timezone', 'America/Los_Angeles');
variable_set('date_format_medium', 'Y-m-d H:i');
$migration = Migration::getInstance('DateExample');
$result = $migration->processImport();
$this->assertEqual($result, Migration::RESULT_COMPLETED, t('Variety term import returned RESULT_COMPLETED'));
$rawnodes = node_load_multiple(FALSE, array('type' => 'date_test_feature'), TRUE);
$this->assertEqual(count($rawnodes), 2, t('Two sample nodes created'));
// Test the first imported node.
$node = reset($rawnodes);
$this->drupalGet('/node/' . $node->nid);
$this->assertText('Simple example', t('Found the first node'));
$this->assertText('This is pretty straight-forward.', t('Found the first node'));
$this->assertText('2011-05-12 19:43', t('Simple date field found'));
$this->assertText('2011-06-13 18:32 to 2011-07-23 10:32', t('Date range field found'));
$this->assertText('2011-07-22 12:13', t('Datestamp field found'));
// This needs to output the exact time and now "(All day)" because this
// field does not have the "all day" option selected.
$this->assertText('2011-08-01 00:00 to 2011-09-01 00:00', t('Datestamp range field found'));
$this->assertText('2011-11-18 15:00', t('Datetime field with +9 timezone found'));
$this->assertText('2011-10-30 14:43 to 2011-12-31 17:59', t('Datetime range field with -5 timezone found'));
$this->assertText('2011-11-25 09:01', t('First date repeat instance found'));
$this->assertText('2011-12-09 09:01', t('Second date repeat instance found'));
$this->assertNoText('2011-12-23 09:01', t('Skipped date repeat instance not found'));
$this->assertText('2012-05-11 09:01', t('Last date repeat instance found'));
// Test the second imported node.
$node = next($rawnodes);
$this->drupalGet('/node/' . $node->nid);
$this->assertText('Example with multi-value fields', t('Found the second node'));
$this->assertText('This is not as straight-forward.', t('Found the second node'));
$this->assertText('2012-06-21 15:32', t('First date value found'));
$this->assertText('2012-12-02 11:08', t('Second date value found'));
$this->assertText('2004-02-03 01:15', t('Start for first date range found'));
$this->assertText('2005-03-04 22:11', t('End for first date range found'));
$this->assertText('2014-09-01 17:21', t('Start for second date range found'));
$this->assertText('2015-12-23 00:01', t('End for first second range found'));
}
}

View File

@ -0,0 +1,64 @@
<?php
/**
* @file
* Test Date Now unit tests.
*/
/**
* Test Date Now unit tests.
*/
class DateNowUnitTestCase extends DrupalUnitTestCase {
/**
*
*/
public static function getInfo() {
return array(
'name' => t('Date Now'),
'description' => t('Test Date Now function.') ,
'group' => t('Date'),
);
}
/**
* {@inheritdoc}
*/
public function setUp() {
drupal_load('module', 'date_api');
parent::setUp();
}
public function testDateNowNoTimezone() {
// Test without passing a timezone.
$now = date_now();
$this->assertTrue(($now instanceof DateObject), 'Test date_now() returns a DateObject');
}
public function testDateNowStringTimezones() {
// Test with a string timezone.
$la_time = date_now('America/Los_Angeles');
$ny_time = date_now('America/New_York');
$this->assertTrue(($la_time instanceof DateObject), 'Test America/Los_Angeles returns a DateObject');
$this->assertTrue(($ny_time instanceof DateObject), 'Test America/New_York returns a DateObject');
$this->assertEqual($la_time->getTimestamp(), $ny_time->getTimestamp(), 'Test different timezones have same Unix timestamp');
}
public function testDateNowObjectTimezones() {
// Test with object timezones.
$la_tz = new DateTimeZone('America/Los_Angeles');
$ny_tz = new DateTimeZone('America/New_York');
$la_time = date_now($la_tz);
$ny_time = date_now($ny_tz);
$this->assertTrue(($la_time instanceof DateObject), 'Test America/Los_Angeles returns a DateObject');
$this->assertTrue(($ny_time instanceof DateObject), 'Test America/New_York returns a DateObject');
$this->assertEqual($la_time->getTimestamp(), $ny_time->getTimestamp(), 'Test different timezones have same Unix timestamp');
}
}

View File

@ -0,0 +1,264 @@
<?php
/**
* @file
* Test timezone handling.
*/
/**
* Test timezone handling.
*/
class DateTimezoneTestCase extends DateFieldTestBase {
/**
* @todo.
*/
public static function getInfo() {
return array(
'name' => 'Timezone & Granularity',
'description' => 'Test combinations of date field timezone handling and granularity.',
'group' => 'Date',
);
}
/**
* {@inheritdoc}
*/
public function setUp(array $modules = array()) {
parent::setUp($modules);
// Set the timezone explicitly. Otherwise the site's default timezone is
// used, which defaults to the server timezone when installing Drupal. This
// depends on the environment and is therefore uncertain.
// The Australia/Sydney timezone is chosen so all tests are run using an
// edge case scenario (UTC+10 and DST).
variable_set('date_default_timezone', 'Australia/Sydney');
}
/**
* @todo.
*/
public function testTimezone() {
// Create a date fields with combinations of various timezone handling and
// granularity.
foreach (array('date', 'datestamp', 'datetime') as $field_type) {
foreach (array('site', 'none', 'date', 'user', 'utc', 'Europe/Dublin') as $tz_handling) {
foreach (array('year', 'month', 'day', 'hour', 'minute', 'second') as $max_granularity) {
// Skip invalid combinations.
if (in_array($max_granularity, array('year', 'month', 'day')) && $tz_handling != 'none') {
continue;
}
$field_name = "field_test";
$label = 'Test';
$granularity = date_granularity_array_from_precision($max_granularity);
$options = array(
'label' => $label,
'widget_type' => 'date_text',
'field_name' => $field_name,
'field_type' => $field_type,
'input_format' => 'custom',
'input_format_custom' => 'm/d/Y - H:i:s',
'tz_handling' => $tz_handling,
'granularity' => $granularity,
);
$this->createDateField($options);
$this->buildDateForm($field_name, $field_type, $max_granularity, $tz_handling);
$this->deleteDateField($label);
}
}
}
}
/**
* Test timezone handling validation on the field settings form.
*/
public function testFieldWidgetSettings() {
$label = 'Test';
$options = array(
'label' => $label,
'field_type' => 'date',
'widget_type' => 'date_select',
'granularity' => array('year', 'month', 'day'),
);
$this->createDateField($options);
$this->drupalGet('admin/structure/types/manage/story/fields/field_' . strtolower($label));
$this->assertResponse(200);
$edit = array(
'field[settings][granularity][hour]' => FALSE,
);
$this->drupalPost('admin/structure/types/manage/story/fields/field_' . strtolower($label), $edit, t('Save settings'));
$this->assertText("Dates without hours granularity must not use any timezone handling.", "Dates without hours granularity required to use timezone handling of 'none.'");
$this->deleteDateField($label);
}
/**
* Validates timezone handling with a multi-value date field.
*/
public function testMultiUserTimezone() {
// Create date fields with combinations of various types and granularity
// using the "Date's Timezone" strategy.
$field_type = 'datetime';
$tz_handling = 'date';
$max_granularity = 'minute';
// Create date field.
$field_name = "field_test";
$label = 'Test';
$options = array(
'label' => $label,
'widget_type' => 'date_text',
'field_name' => $field_name,
'field_type' => $field_type,
'input_format' => 'custom',
'input_format_custom' => 'm/d/Y - H:i:s T',
'cardinality' => 3,
'tz_handling' => $tz_handling,
);
$this->createMultiDateField($options);
// Submit a date field form with multiple values.
$this->dateMultiValueForm($field_name, $field_type, $max_granularity, $tz_handling);
$this->deleteDateField($label);
}
/**
* Tests the submission of a date field's widget with unlimited cardinality.
*/
public function dateMultiValueForm($field_name, $field_type, $max_granularity, $tz_handling) {
variable_set('date_format_long', 'D, m/d/Y - H:i:s T');
$edit = array();
$should_be = array();
$edit['title'] = $this->randomName(8);
$timezones = array('America/Chicago', 'America/Los_Angeles', 'America/New_York');
switch ($max_granularity) {
case 'hour':
$edit[$field_name . '[und][0][value][date]'] = '10/07/2010 - 10:30';
$edit[$field_name . '[und][0][timezone][timezone]'] = 'America/Chicago';
$should_be[0] = 'Thu, 10/07/2010 - 10 CDT';
$edit[$field_name . '[und][1][value][date]'] = '10/07/2010 - 10:30';
$edit[$field_name . '[und][1][timezone][timezone]'] = 'America/Los_Angeles';
$should_be[1] = 'Thu, 10/07/2010 - 10 PDT';
$edit[$field_name . '[und][2][value][date]'] = '10/07/2010 - 10:30';
$edit[$field_name . '[und][2][timezone][timezone]'] = 'America/New_York';
$should_be[2] = 'Thu, 10/07/2010 - 10 EDT';
break;
case 'minute':
$edit[$field_name . '[und][0][value][date]'] = '10/07/2010 - 10:30';
$edit[$field_name . '[und][0][timezone][timezone]'] = 'America/Chicago';
$should_be[0] = 'Thu, 10/07/2010 - 10:30 CDT';
$edit[$field_name . '[und][1][value][date]'] = '10/07/2010 - 10:30';
$edit[$field_name . '[und][1][timezone][timezone]'] = 'America/Los_Angeles';
$should_be[1] = 'Thu, 10/07/2010 - 10:30 PDT';
$edit[$field_name . '[und][2][value][date]'] = '10/07/2010 - 10:30';
$edit[$field_name . '[und][2][timezone][timezone]'] = 'America/New_York';
$should_be[2] = 'Thu, 10/07/2010 - 10:30 EDT';
break;
case 'second':
$edit[$field_name . '[und][0][value][date]'] = '10/07/2010 - 10:30';
$edit[$field_name . '[und][0][timezone][timezone]'] = 'America/Chicago';
$should_be[0] = 'Thu, 10/07/2010 - 10:30:30 CDT';
$edit[$field_name . '[und][1][value][date]'] = '10/07/2010 - 10:30';
$edit[$field_name . '[und][1][timezone][timezone]'] = 'America/Los_Angeles';
$should_be[1] = 'Thu, 10/07/2010 - 10:30:30 PDT';
$edit[$field_name . '[und][2][value][date]'] = '10/07/2010 - 10:30';
$edit[$field_name . '[und][2][timezone][timezone]'] = 'America/New_York';
$should_be[2] = 'Thu, 10/07/2010 - 10:30:30 EDT';
break;
}
$this->drupalPost('node/add/story', $edit, t('Save'));
$this->assertText($edit['title'], "Node has been created");
foreach ($should_be as $assertion) {
$this->assertText($assertion, "Found the correct date for a $field_type field using $max_granularity granularity with $tz_handling timezone handling.");
}
// Goto the edit page and save the node again.
$node = $this->drupalGetNodeByTitle($edit['title']);
$this->drupalGet('node/' . $node->nid . '/edit');
// Re-assert the proper date timezones.
foreach ($timezones as $key => $timezone) {
$this->assertOptionSelected('edit-field-test-und-' . $key . '-timezone-timezone', $timezone, "Found the correct timezone $timezone for a $field_type field using $max_granularity granularity with $tz_handling timezone handling.");
}
}
/**
* Replacement for dateForm().
*/
public function buildDateForm($field_name, $field_type, $max_granularity, $tz_handling) {
variable_set('date_format_long', 'D, m/d/Y - H:i:s');
$edit = array();
$edit['title'] = $this->randomName(8);
$edit[$field_name . '[und][0][show_todate]'] = '1';
switch ($max_granularity) {
case 'year':
$edit[$field_name . '[und][0][value][date]'] = '2010';
$edit[$field_name . '[und][0][value2][date]'] = '2011';
$should_be = '2010 to 2011';
break;
case 'month':
$edit[$field_name . '[und][0][value][date]'] = '07/2010';
$edit[$field_name . '[und][0][value2][date]'] = '08/2010';
$should_be = '07/2010 to 08/2010';
break;
case 'day':
$edit[$field_name . '[und][0][value][date]'] = '10/07/2010';
$edit[$field_name . '[und][0][value2][date]'] = '10/08/2010';
$should_be = 'Thu, 10/07/2010 to Fri, 10/08/2010';
break;
case 'hour':
$edit[$field_name . '[und][0][value][date]'] = '10/07/2010 - 10';
$edit[$field_name . '[und][0][value2][date]'] = '10/07/2010 - 11';
if ($tz_handling == 'utc') {
$should_be = 'Thu, 10/07/2010 - 21 to Thu, 10/07/2010 - 22';
}
else {
$should_be = 'Thu, 10/07/2010 - 10 to Thu, 10/07/2010 - 11';
}
break;
case 'minute':
$edit[$field_name . '[und][0][value][date]'] = '10/07/2010 - 10:30';
$edit[$field_name . '[und][0][value2][date]'] = '10/07/2010 - 11:30';
if ($tz_handling == 'utc') {
$should_be = 'Thu, 10/07/2010 - 21:30 to 22:30';
}
else {
$should_be = 'Thu, 10/07/2010 - 10:30 to 11:30';
}
break;
case 'second':
$edit[$field_name . '[und][0][value][date]'] = '10/07/2010 - 10:30:30';
$edit[$field_name . '[und][0][value2][date]'] = '10/07/2010 - 11:30:30';
if ($tz_handling == 'utc') {
$should_be = 'Thu, 10/07/2010 - 21:30:30 to 22:30:30';
}
else {
$should_be = 'Thu, 10/07/2010 - 10:30:30 to 11:30:30';
}
break;
}
$this->drupalPost('node/add/story', $edit, t('Save'));
$this->assertText($edit['title'], "Node has been created");
$this->assertText($should_be, "Found the correct date for a $field_type field using $max_granularity granularity with $tz_handling timezone handling.");
}
}

View File

@ -0,0 +1,85 @@
<?php
/**
* @file
* Test date UI.
*/
/**
*
*/
class DateUiTestCase extends DateFieldTestBase {
/**
* @todo.
*/
public static function getInfo() {
return array(
'name' => 'Field UI',
'description' => 'Test creation of various date fields and widgets using Field UI.',
'group' => 'Date',
);
}
/**
* @todo.
*/
public function setUp(array $modules = array()) {
parent::setUp($modules);
variable_set('date_format_long', 'D, m/d/Y - H:i');
}
/**
* @todo.
*/
public function testFieldUI() {
$label = 'Test';
$current_year = date('Y');
// Test widgets with default settings using every widget and field type.
foreach (array('date', 'datestamp', 'datetime') as $field_type) {
// @todo Add back 'date_popup'.
foreach (array('date_select', 'date_text') as $widget_type) {
$this->createDateField(
array(
'label' => $label,
'field_type' => $field_type,
'widget_type' => $widget_type,
)
);
$this->dateForm('blah', 'blah', $widget_type);
$this->assertText(format_string('10/07/!year - 10:30', array('!year' => $current_year)), 'Found the correct date for a date field using the ' . $widget_type . ' widget.');
$this->deleteDateField($label);
}
}
}
/**
* {@inheritdoc}
*/
function dateForm($field_name, $field_type, $widget_type, $todate = TRUE) {
// Tests that date field functions properly.
$edit = array();
$edit['title'] = $this->randomName(8);
$edit['body[und][0][value]'] = $this->randomName(16);
$current_year = date('Y');
if ($widget_type == 'date_select') {
$edit['field_test[und][0][value][year]'] = $current_year;
$edit['field_test[und][0][value][month]'] = '10';
$edit['field_test[und][0][value][day]'] = '7';
$edit['field_test[und][0][value][hour]'] = '10';
$edit['field_test[und][0][value][minute]'] = '30';
}
elseif ($widget_type == 'date_text') {
$edit['field_test[und][0][value][date]'] = format_string('10/07/!year - 10:30', array('!year' => $current_year));
}
elseif ($widget_type == 'date_popup') {
$edit['field_test[und][0][value][date]'] = format_string('10/07/!year', array('!year' => $current_year));
$edit['field_test[und][0][value][time]'] = '10:30';
}
$this->drupalPost('node/add/story', $edit, t('Save'));
$this->assertText($edit['body[und][0][value]'], 'Test node has been created');
}
}

View File

@ -0,0 +1,167 @@
<?php
/**
* @file
* Validate date field values.
*/
/**
* Validate date field values.
*/
class DateValidationTestCase extends DateFieldTestBase {
/**
* @todo.
*/
public static function getInfo() {
return array(
'name' => 'Date Validation',
'description' => 'Test date validation.',
'group' => 'Date',
);
}
/**
* @todo.
*/
public function testValidation() {
// Attempts to create text date field stored as a date with default settings
// (from input which is not valid).
foreach (array('date', 'datestamp', 'datetime') as $field_type) {
foreach (array('date_select', 'date_text') as $widget_type) {
$this->checkDateField($field_type, $widget_type, TRUE);
return;
}
}
}
/**
* {@inheritdoc}
*/
protected function checkDateField($field_type, $widget_type, $delete_when_done = TRUE) {
$field_name = 'field_test';
$label = 'Test';
$options = array(
'label' => $label,
'field_name' => $field_name,
'field_type' => $field_type,
'widget_type' => $widget_type,
'input_format' => 'm/d/Y - H:i',
);
$this->createDateField($options);
// Malformed date test won't work on date_select, which won't allow
// invalid input.
if ($widget_type != 'date_select') {
$this->malFormedDate($field_name, $field_type, $widget_type);
}
$this->wrongGranularity($field_name, $field_type, $widget_type);
if ($delete_when_done) {
$this->deleteDateField($label);
}
}
/**
* @todo.
*/
function malFormedDate($field_name, $field_type, $widget_type) {
// Tests that date field filters improper dates.
$edit = array();
$edit['title'] = $this->randomName(8);
$edit['body[und][0][value]'] = $this->randomName(16);
if ($widget_type == 'date_select') {
$edit[$field_name . '[und][0][value][year]'] = '2011';
$edit[$field_name . '[und][0][value][month]'] = '15';
$edit[$field_name . '[und][0][value][day]'] = '49';
$edit[$field_name . '[und][0][value][hour]'] = '10';
$edit[$field_name . '[und][0][value][minute]'] = '30';
}
elseif ($widget_type == 'date_text') {
$edit[$field_name . '[und][0][value][date]'] = '15/49/2011 - 10:30';
}
elseif ($widget_type == 'date_popup') {
$edit[$field_name . '[und][0][value][date]'] = '15/49/2011';
$edit[$field_name . '[und][0][value][time]'] = '10:30';
}
$this->drupalPost('node/add/story', $edit, t('Save'));
$should_not_be = $edit['title'] . "has been created";
$this->assertNoText($should_not_be, "Correctly blocked creation of node with invalid month and day for a $field_type field using the $widget_type widget.");
$this->assertText('The month is invalid.', "Correctly blocked invalid month for a $field_type field using the $widget_type widget.");
$this->assertText('The day is invalid.', "Correctly blocked invalid day for a $field_type field using the $widget_type widget.");
// Test two-digit entry for year where 4-digit is expected.
if ($widget_type == 'date_select') {
$edit[$field_name . '[und][0][value][year]'] = '11';
$edit[$field_name . '[und][0][value][month]'] = '12';
$edit[$field_name . '[und][0][value][day]'] = '10';
$edit[$field_name . '[und][0][value][hour]'] = '10';
$edit[$field_name . '[und][0][value][minute]'] = '30';
}
elseif ($widget_type == 'date_text') {
$edit[$field_name . '[und][0][value][date]'] = '12/10/11 - 10:30';
}
elseif ($widget_type == 'date_popup') {
$edit[$field_name . '[und][0][value][date]'] = '12/10/11';
$edit[$field_name . '[und][0][value][time]'] = '10:30';
}
$this->drupalPost('node/add/story', $edit, t('Save'));
$should_not_be = $edit['title'] . " has been created";
$this->assertNoText($should_not_be, "Correctly blocked creation of node with invalid year for a $field_type field using the $widget_type widget.");
$should_be = 'The year is invalid. Please check that entry includes four digits.';
$this->assertText($should_be, "Correctly blocked two digit year for a $field_type field using the $widget_type widget.");
// Test invalid hour/minute entry for time.
if ($widget_type == 'date_select') {
$edit[$field_name . '[und][0][value][year]'] = '2011';
$edit[$field_name . '[und][0][value][month]'] = '12';
$edit[$field_name . '[und][0][value][day]'] = '10';
$edit[$field_name . '[und][0][value][hour]'] = '29';
$edit[$field_name . '[und][0][value][minute]'] = '95';
}
elseif ($widget_type == 'date_text') {
$edit[$field_name . '[und][0][value][date]'] = '12/10/2011 - 29:95';
}
elseif ($widget_type == 'date_popup') {
$edit[$field_name . '[und][0][value][date]'] = '12/10/2011';
$edit[$field_name . '[und][0][value][time]'] = '29:95';
}
$this->drupalPost('node/add/story', $edit, t('Save'));
$should_not_be = $edit['title'] . " has been created";
$this->assertNoText($should_not_be, "Correctly blocked creation of node with invalid time for a $field_type field using the $widget_type widget.");
$should_be = 'The hour is invalid.';
$this->assertText($should_be, "Correctly blocked invalid hour for a $field_type field using the $widget_type widget.");
$should_be = 'The minute is invalid.';
$this->assertText($should_be, "Correctly blocked invalid minute for a $field_type field using the $widget_type widget.");
}
/**
* @todo.
*/
public function wrongGranularity($field_name, $field_type, $widget_type) {
// Create a node with incorrect granularity -- missing time.
$edit = array();
$edit['title'] = $this->randomName(8);
$edit['body[und][0][value]'] = $this->randomName(16);
if ($widget_type == 'date_select') {
$edit[$field_name . '[und][0][value][year]'] = '2011';
$edit[$field_name . '[und][0][value][month]'] = '12';
$edit[$field_name . '[und][0][value][day]'] = '10';
$edit[$field_name . '[und][0][value][hour]'] = '';
$edit[$field_name . '[und][0][value][minute]'] = '';
}
elseif ($widget_type == 'date_text') {
$edit[$field_name . '[und][0][value][date]'] = '12/10/2011';
}
elseif ($widget_type == 'date_popup') {
$edit[$field_name . '[und][0][value][date]'] = '12/10/2011';
$edit[$field_name . '[und][0][value][time]'] = '';
}
$this->drupalPost('node/add/story', $edit, t('Save'));
$should_not_be = $edit['title'] . " has been created";
$this->assertNoText($should_not_be, "Correctly blocked creation of node with missing time for a $field_type field using the $widget_type widget.");
$this->assertText('invalid', "Marked form with missing time as invalid for a $field_type field using the $widget_type widget.");
}
}

View File

@ -0,0 +1,22 @@
name = Date Migration Test Helper
description = "Helper for the Date module's Migrate API integration."
package = Date/Time
version = VERSION
core = 7.x
hidden = TRUE
; Load the Date module's test feature so there are some default data structures
; to work with.
dependencies[] = date:date_test_feature
; Load the Migrate API system.
dependencies[] = migrate:migrate
; Load the custom Migrate API class.
files[] = date_migrate_test.migrate.inc
; Information added by Drupal.org packaging script on 2021-03-05
version = "7.x-2.11"
core = "7.x"
project = "date"
datestamp = "1614952728"

View File

@ -0,0 +1,13 @@
<?php
/**
* @file
* Install, update and uninstall functions for the Date Migrate Test module.
*/
/**
* Implements hook_disable().
*/
function date_migrate_test_disable() {
Migration::deregisterMigration('DateExample');
}

View File

@ -0,0 +1,132 @@
<?php
/**
* @file
* Examples and test folder for migration into date fields.
*/
/**
* Migration class to test import of various date fields.
*/
class DateExampleMigration extends XMLMigration {
/**
* Sets up the migration.
*/
public function __construct() {
parent::__construct();
$this->description = t('Example migration into date fields');
$this->map = new MigrateSQLMap($this->machineName,
array(
'id' => array(
'type' => 'int',
'unsigned' => TRUE,
'not null' => TRUE,
'description' => 'Date ID',
),
),
MigrateDestinationNode::getKeySchema()
);
// Source fields available in the XML file.
$fields = array(
'id' => t('Source id'),
'title' => t('Title'),
'body' => t('Description'),
'date' => t('A simple date'),
'date_range_from' => t('Start value for a date range'),
'datestamp' => t('Simple datestamp'),
'datestamp_range_from' => t('Start value for a datestamp range'),
'datetime' => t('Simple datetime'),
'datetime_range_from' => t('Start value for a datetime range'),
'date_repeat' => t('Sample of a repeating date field'),
);
// Our test data is in an XML file.
$xml_folder = drupal_get_path('module', 'date_migrate_test');
$items_url = $xml_folder . '/example_data.xml';
$item_xpath = '/source_data/item';
$item_id_xpath = 'id';
$items_class = new MigrateItemsXML($items_url, $item_xpath, $item_id_xpath);
$this->source = new MigrateSourceMultiItems($items_class, $fields);
$this->destination = new MigrateDestinationNode('date_test_feature');
// Basic fields.
$this->addFieldMapping('title', 'title')
->xpath('title');
$this->addFieldMapping('uid')
->defaultValue(1);
$this->addFieldMapping('body', 'body')
->xpath('body');
// For simple date fields, we just need the xpath.
$this->addFieldMapping('field_date', 'date')
->xpath('date');
// For date ranges, we add the "end" value in prepareRow() below.
$this->addFieldMapping('field_date_range', 'date_range_from');
$this->addFieldMapping('field_date_range:to', 'date_range_to');
// RRULEs on repeat fields are also done in prepareRow().
$this->addFieldMapping('field_date_repeat', 'date_repeat');
$this->addFieldMapping('field_date_repeat:rrule', 'date_repeat_rrule');
$this->addFieldMapping('field_datestamp', 'datestamp')
->xpath('datestamp');
$this->addFieldMapping('field_datestamp_range', 'datestamp_range_from');
$this->addFieldMapping('field_datestamp_range:to', 'datestamp_range_to');
// You can specify a timezone to be applied to all values going into the
// field (Tokyo is UTC+9, no DST).
$this->addFieldMapping('field_datetime', 'datetime')
->xpath('datetime');
$this->addFieldMapping('field_datetime:timezone')
->defaultValue('Asia/Tokyo');
// You can also get the timezone from the source data - it can be different
// for each instance of the field. Like To and RRULE values, it is added
// in prepareRow().
$this->addFieldMapping('field_datetime_range', 'datetime_range_from');
$this->addFieldMapping('field_datetime_range:to', 'datetime_range_to');
$this->addFieldMapping('field_datetime_range:timezone', 'datetime_range_timezone');
// Unmapped destination fields.
$this->addUnmigratedDestinations(array('is_new', 'status', 'promote',
'revision', 'language', 'sticky', 'created', 'changed', 'revision_uid'));
}
/**
* Transforms the raw migration data into the expected date formats.
*
* An advanced feature of the date field handler is that in addition to the
* basic (Start) date itself, we can add additional properties like timezone,
* encapsulating them as JSON.
*/
public function prepareRow($current_row) {
// The date range field can have multiple values.
$current_row->date_range_from = array();
foreach ($current_row->xml->date_range as $range) {
$current_row->date_range_from[] = (string) $range->from[0];
$current_row->date_range_to[] = (string) $range->to[0];
}
$current_row->datestamp_range_from
= (string) $current_row->xml->datestamp_range->from[0];
$current_row->datestamp_range_to
= (string) $current_row->xml->datestamp_range->to[0];
$current_row->datetime_range_from
= (string) $current_row->xml->datetime_range->from[0];
$current_row->datetime_range_to
= (string) $current_row->xml->datetime_range->to[0];
$current_row->datetime_range_timezone
= (string) $current_row->xml->datetime_range->timezone[0];
$current_row->date_repeat
= (string) $current_row->xml->date_repeat->date[0];
$current_row->date_repeat_rrule
= (string) $current_row->xml->date_repeat->rule[0];
}
}

View File

@ -0,0 +1,19 @@
<?php
/**
* @file
* Primary hook implementations for the Date Migrate Test module.
*/
/**
* Implements hook_migrate_api().
*/
function date_migrate_test_migrate_api() {
$api = array(
'api' => 2,
'migrations' => array(
'DateExample' => array('class_name' => 'DateExampleMigration'),
),
);
return $api;
}

View File

@ -0,0 +1,60 @@
<?xml version="1.0" encoding="UTF-8"?>
<source_data>
<item>
<id>3</id>
<title>Simple example</title>
<body>This is pretty straight-forward.</body>
<date>05/12/2011 19:43</date>
<date_range>
<from>06/13/2011 6:32pm</from>
<to>07/23/2011 10:32am</to>
</date_range>
<datestamp>07/22/2011 12:13</datestamp>
<datestamp_range>
<from>8/1/2011 00:00</from>
<to>9/1/2011 00:00</to>
</datestamp_range>
<datetime>11/18/2011 06:00</datetime>
<datetime_range>
<from>10/30/2011 19:43</from>
<to>12/31/2011 23:59</to>
<timezone>America/Chicago</timezone>
</datetime_range>
<date_repeat>
<date>11/25/2011 9:01</date>
<rule>RRULE:FREQ=WEEKLY;INTERVAL=2;UNTIL=20120512T040000Z;WKST=MO
EXDATE:20111223</rule>
</date_repeat>
</item>
<item>
<id>8</id>
<title>Example with multi-value fields</title>
<body>This is not as straight-forward.</body>
<date>06/21/2012 15:32</date>
<date>12/02/2012 11:08</date>
<date_range>
<from>02/03/2004 1:15am</from>
<to>03/04/2005 10:11pm</to>
</date_range>
<date_range>
<from>09/01/2014 5:21pm</from>
<to>12/23/2015 00:01</to>
</date_range>
<datestamp>07/22/2011 12:13</datestamp>
<datestamp_range>
<from>8/1/2011 00:00</from>
<to>9/1/2011 00:00</to>
</datestamp_range>
<datetime>11/18/2011 06:00</datetime>
<datetime_range>
<from>10/30/2011 19:43</from>
<to>12/31/2011 23:59</to>
<timezone>America/Chicago</timezone>
</datetime_range>
<date_repeat>
<date>11/25/2011 9:01</date>
<rule>RRULE:FREQ=WEEKLY;INTERVAL=2;UNTIL=20120512T040000Z;WKST=MO
EXDATE:20111223</rule>
</date_repeat>
</item>
</source_data>

View File

@ -4,11 +4,10 @@ package = Date/Time
version = VERSION
core = 7.x
hidden = TRUE
dependencies[] = date
dependencies[] = date:date
; Information added by Drupal.org packaging script on 2017-04-07
version = "7.x-2.10"
; Information added by Drupal.org packaging script on 2021-03-05
version = "7.x-2.11"
core = "7.x"
project = "date"
datestamp = "1491562090"
datestamp = "1614952728"

Some files were not shown because too many files have changed in this diff Show More