all contrib module security updates done

This commit is contained in:
2020-09-24 22:47:32 +02:00
parent 7d87153100
commit 0fbb9c4610
1612 changed files with 116663 additions and 32269 deletions
@@ -0,0 +1,36 @@
span.autocomplete-deluxe-value-delete {
float: left;
}
div.autocomplete-deluxe-multiple {
padding: 4px 5px;
}
input.autocomplete-deluxe-form.autocomplete-deluxe-multiple {
margin-left: 0;
margin-right: 5px;
float: right;
}
div.autocomplete-deluxe-throbber {
float: left;
}
.autocomplete-deluxe-closed {
background-position: 0 6px;
}
.autocomplete-deluxe-open {
background-position: 0 -14px;
}
.autocomplete-deluxe-item {
float: right;
margin: 3px 5px 3px 0;
padding: 3px 5px 3px 20px;
}
.autocomplete-deluxe-item-delete {
position: absolute;
right: auto;
left: 3px;
}
@@ -1,5 +1,4 @@
<?php
// $Id$
/**
* @file
@@ -12,24 +11,26 @@
* When you want to use the Autocomplete Deluxe element, you have to choose
* between two types sources for the suggestion data: Ajax Callbacks or Lists.
* You can set the source type by using the appropriate options:
* - #autocomplete_deluxe_path expects a string with an url, that points to the ajax
* callback. The response should be encoded as json(like for the core
* - #autocomplete_deluxe_path expects a string with an url, that points to the
* ajax callback. The response should be encoded as json (like for the core
* autocomplete).
* - #autocomplete_options needs an array in the form of an array(similar to #options in core
* for selects or checkboxes): array('a', 'b', 'c') or array(1 => 'a', 2 =>
* 'b', 3 => 'c').
* - #autocomplete_options needs an array in the form of an array (similar to
* #options in core for selects or checkboxes): array('a', 'b', 'c') or
* array(1 => 'a', 2 => 'b', 3 => 'c').
*
* Besides this two, there are three other options, wich autocomplete deluxe
* Besides these two, there are four other options which autocomplete deluxe
* accepts:
* - #multiple Indicates whether the user may select more than one item. Expects
* TRUE or FALSE, by default it is set to FALSE.
* - #autocomplete_multiple_delimiter If #multiple is TRUE, then you can use
* this option to set a seperator for multiple values. By default a string
* with the follwing content will be used: ', '.
* - #autocomplete_min_length Indicates how many characters must be entered
* until, the suggesion list can be opened. Especially helpfull, when your
* - #delimiter If #multiple is TRUE, then you can use this option to set a
* seperator for multiple values. By default a string with the following
* content will be used: ', '.
* - #min_length Indicates how many characters must be entered
* until, the suggesion list can be opened. Especially helpful, when your
* ajax callback returns only valid suggestion for a minimum characters.
* The default is 0.
* - #not_found_message A message text which will be displayed, if the entered
* term was not found.
*/
function somefunction() {
switch ($type) {
@@ -38,16 +39,18 @@ function somefunction() {
'#type' => 'autocomplete_deluxe',
'#autocomplete_options' => $options,
'#multiple' => FALSE,
'#autocomplete_min_length' => 0,
'#min_length' => 0,
);
break;
case 'ajax':
$element = array(
'#type' => 'autocomplete_deluxe',
'#autocomplete_deluxe_path' => url('some_uri', array('absolute' => TRUE)),
'#multiple' => TRUE,
'#autocomplete_min_length' => 1,
'#autocomplete_multiple_delimiter' => '|',
'#min_length' => 1,
'#delimiter' => '|',
'#not_found_message' => "The term '@term' will be added.",
);
break;
}
@@ -12,7 +12,7 @@ a.autocomplete-deluxe-single:hover {
text-decoration: none;
}
.ui-state-hover {
.ui-autocomplete .ui-state-hover {
background-color: #3875d7;
background-image: -webkit-gradient(linear, 0% 0%, 0% 100%, color-stop(20%, #3875d7), color-stop(90%, #2a62bc));
background-image: -webkit-linear-gradient(top, #3875d7 20%, #2a62bc 90%);
@@ -114,7 +114,7 @@ div.autocomplete-deluxe-multiple {
background: no-repeat -38px -22px, -o-linear-gradient(top, #eeeeee 1%, #ffffff 15%);
background: no-repeat -38px -22px, -ms-linear-gradient(top, #eeeeee 1%, #ffffff 15%);
background: no-repeat -38px -22px, linear-gradient(top, #eeeeee 1%, #ffffff 15%);
padding: 4px 5px 4px 20px;
padding: 4px 5px;
border: 1px solid #AAAAAA;
cursor: text;
height: auto !important;
@@ -5,9 +5,9 @@ core = 7.x
files[] = autocomplete_deluxe.module
dependencies[] = taxonomy
; Information added by drupal.org packaging script on 2013-01-22
version = "7.x-2.0-beta2+22-dev"
; Information added by Drupal.org packaging script on 2017-07-25
version = "7.x-2.3"
core = "7.x"
project = "autocomplete_deluxe"
datestamp = "1358814920"
datestamp = "1501005546"
@@ -82,7 +82,26 @@
return this;
};
/**
* Unescapes the given string.
*/
Drupal.autocomplete_deluxe.unescape = function (input) {
// Unescaping is done via a textarea, since the text inside of it is never
// executed. This method also allows us to support older browsers like
// IE 9 and below.
var textArea = document.createElement('textarea');
textArea.innerHTML = input;
var decoded = textArea.value;
if ('remove' in Element.prototype) {
textArea.remove();
}
return decoded;
};
/**
* If there is no result this label will be shown.
* @type {{label: string, value: string}}
*/
Drupal.autocomplete_deluxe.empty = {label: '- ' + Drupal.t('None') + ' -', value: "" };
/**
@@ -105,6 +124,9 @@
Drupal.autocomplete_deluxe.Widget = function() {
};
/**
* Url for the callback.
*/
Drupal.autocomplete_deluxe.Widget.prototype.uri = null;
/**
@@ -119,7 +141,7 @@
};
Drupal.autocomplete_deluxe.Widget.prototype.init = function(settings) {
if ($.browser.msie && $.browser.version === "6.0") {
if(navigator.appVersion.indexOf("MSIE 6.") != -1) {
return;
}
@@ -130,7 +152,17 @@
this.multiple = settings.multiple;
this.required = settings.required;
this.limit = settings.limit;
this.synonyms = settings.use_synonyms === undefined ? false : settings.use_synonyms;
this.synonyms = typeof settings.use_synonyms == 'undefined' ? false : settings.use_synonyms;
this.not_found_message = typeof settings.use_synonyms == 'undefined' ? "The term '@term' will be added." : settings.not_found_message;
this.wrapper = '""';
if (typeof settings.delimiter == 'undefined') {
this.delimiter = true;
} else {
this.delimiter = settings.delimiter.charCodeAt(0);
}
this.items = {};
var self = this;
@@ -153,7 +185,7 @@
}
if ($.isEmptyObject(result)) {
result.push({
label: Drupal.t("The term '@term' will be added.", {'@term' : term}),
label: Drupal.t(self.not_found_message, {'@term' : term}),
value: term,
newTerm: true
});
@@ -161,7 +193,7 @@
return result;
};
var cache = {}
var cache = {};
var lastXhr = null;
this.source = function(request, response) {
@@ -193,6 +225,9 @@
});
var jqObject = this.jqObject;
var autocompleteDataKey = typeof(this.jqObject.data('autocomplete')) === 'object' ? 'item.autocomplete' : 'ui-autocomplete';
var throbber = $('<div class="autocomplete-deluxe-throbber autocomplete-deluxe-closed">&nbsp;</div>').insertAfter(jqObject);
this.jqObject.bind("autocompletesearch", function(event, ui) {
@@ -214,8 +249,9 @@
var re = new RegExp('()*""' + escapedValue + '""|' + escapedValue + '()*', 'gi');
var t = item.label.replace(re,"<span class='autocomplete-deluxe-highlight-char'>$&</span>");
}
return $( "<li></li>" )
.data( "item.autocomplete", item )
.data(autocompleteDataKey, item)
.append( "<a>" + t + "</a>" )
.appendTo( ul );
};
@@ -276,14 +312,17 @@
}
this.value = item.value;
this.element = $('<span class="autocomplete-deluxe-item">' + item.label + '</span>');
this.element = $('<span class="autocomplete-deluxe-item"></span>');
this.element.text(item.label);
this.widget = widget;
this.item = item;
var self = this;
var close = $('<a class="autocomplete-deluxe-item-delete" href="javascript:void(0)"></a>').appendTo(this.element);
// Use single quotes because of the double quote encoded stuff.
var input = $('<input type="hidden" value=\'' + this.value + '\'/>').appendTo(this.element);
var input = $('<input type="hidden"/>')
input.val(this.value);
input.appendTo(this.element);
close.mousedown(function() {
self.remove(item);
@@ -294,25 +333,28 @@
this.element.remove();
var values = this.widget.valueForm.val();
var escapedValue = Drupal.autocomplete_deluxe.escapeRegex( this.item.value );
var regex = new RegExp('()*""' + escapedValue + '""|' + escapedValue + '()*', 'gi');
var regex = new RegExp('()*""' + escapedValue + '""()*', 'gi');
this.widget.valueForm.val(values.replace(regex, ''));
delete this.widget.items[this.value];
};
Drupal.autocomplete_deluxe.MultipleWidget.prototype.setup = function() {
var jqObject = this.jqObject;
var parent = jqObject.parent();
var value_container = jqObject.parent().parent().children('.autocomplete-deluxe-value-container');
var value_input = value_container.children().children();
var parent = jqObject.parents('.autocomplete-deluxe-container');
var value_container = parent.next();
var value_input = value_container.find('input');
var items = this.items;
var self = this;
this.valueForm = value_input;
// Override the resize function, so that the suggestion list doesn't resizes
// all the time.
jqObject.data("autocomplete")._resizeMenu = function() {};
var autocompleteDataKey = typeof(this.jqObject.data('autocomplete')) === 'object' ? 'autocomplete' : 'ui-autocomplete';
jqObject.data(autocompleteDataKey)._resizeMenu = function() {};
jqObject.show();
value_container.hide();
// Add the default values to the box.
@@ -346,7 +388,7 @@
var item = new Drupal.autocomplete_deluxe.MultipleWidget.Item(self, ui_item);
item.element.insertBefore(jqObject);
items[ui_item.value] = item;
var new_value = ' ""' + ui_item.value + '""';
var new_value = ' ' + self.wrapper + ui_item.value + self.wrapper;
var values = value_input.val();
value_input.val(values + new_value);
jqObject.val('');
@@ -358,7 +400,13 @@
});
jqObject.bind("autocompleteselect", function(event, ui) {
self.addValue(ui.item);
// JQuery ui autocomplete needs the terms escaped, otherwise it would be
// open to XSS issues. Drupal.autocomplete.Item also escapes on rendering
// the DOM elements. Thus we have to unescape the label here before adding
// the new item.
var item = ui.item;
item.label = Drupal.autocomplete_deluxe.unescape(item.label);
self.addValue(item);
jqObject.width(25);
// Return false to prevent setting the last term as value for the jqObject.
return false;
@@ -375,13 +423,13 @@
var clear = false;
jqObject.keydown(function (event) {
jqObject.keypress(function (event) {
var value = jqObject.val();
// If a comma was entered and there is none or more then one comma,or the
// enter key was entered, then enter the new term.
if ((event.which == 188 && (value.split('"').length - 1) != 1) || (event.which == 13 && jqObject.val() != "")) {
if ((event.which == self.delimiter && (value.split('"').length - 1) != 1) || (event.which == 13 && jqObject.val() != "")) {
value = value.substr(0, value.length);
if (self.items[value] === undefined && value != '') {
if (typeof self.items[value] == 'undefined' && value != '') {
var ui_item = {
label: value,
value: value
@@ -27,7 +27,7 @@ function autocomplete_deluxe_field_widget_info() {
/**
* Custom taxonomy callback, which also accepts an empty string search.
*/
function taxonomy_autocomplete_deluxe($field_name, $tags_typed = '', $limit = 10) {
function autocomplete_deluxe_taxonomy_callback($field_name, $tags_typed = '', $limit = 10) {
$field = field_info_field($field_name);
$use_synonyms = !empty($_GET['synonyms']);
@@ -156,6 +156,19 @@ function autocomplete_deluxe_field_widget_settings_form($field, $instance) {
'#default_value' => isset($settings['min_length']) ? $settings['min_length'] : 0,
'#element_validate' => array('_element_validate_integer'),
);
$form['delimiter'] = array(
'#type' => 'textfield',
'#title' => t('Delimiter.'),
'#description' => t('A character which should be used beside the enter key, to seperate terms.'),
'#default_value' => isset($settings['delimiter']) ? $settings['delimiter'] : '',
'#size' => 1,
);
$form['not_found_message'] = array(
'#type' => 'textfield',
'#title' => t('Term not found message.'),
'#description' => t('A message text which will be displayed, if the entered term was not found.'),
'#default_value' => isset($settings['not_found_message']) ? $settings['not_found_message'] : "The term '@term' will be added.",
);
if (module_exists('synonyms')) {
$form['use_synonyms'] = array(
@@ -211,6 +224,8 @@ function autocomplete_deluxe_field_widget_form(&$form, &$form_state, $field, $in
'#limit' => isset($instance['widget']['settings']['limit']) ? $instance['widget']['settings']['limit'] : 10,
'#min_length' => isset($instance['widget']['settings']['min_length']) ? $instance['widget']['settings']['min_length'] : 0,
'#use_synonyms' =>isset($instance['widget']['settings']['use_synonyms']) ? $instance['widget']['settings']['use_synonyms'] : 0,
'#delimiter' =>isset($instance['widget']['settings']['delimiter']) ? $instance['widget']['settings']['delimiter'] : '',
'#not_found_message' =>isset($instance['widget']['settings']['not_found_message']) ? $instance['widget']['settings']['not_found_message'] : "The term '@term' will be added.",
);
$multiple = $field['cardinality'] > 1 || $field['cardinality'] == FIELD_CARDINALITY_UNLIMITED ? TRUE : FALSE;
@@ -262,12 +277,13 @@ function autocomplete_deluxe_element_process($element) {
$js_settings['autocomplete_deluxe'][$html_id] = array(
'input_id' => $html_id,
'min_length' => isset($element['#autocomplete_min_length']) ? $element['#autocomplete_min_length'] : 0,
'multiple' => $element['#multiple'],
'required' => $element['#required'],
'limit' => isset($element['#limit']) ? $element['#limit'] : 10,
'min_length' => isset($element['#min_length']) ? $element['#min_length'] : 0,
'use_synonyms' => isset($element['#use_synonyms']) ? $element['#use_synonyms'] : 0,
'delimiter' => isset($element['#delimiter']) ? $element['#delimiter'] : '',
'not_found_message' => isset($element['#not_found_message']) ? $element['#not_found_message'] : "The term '@term' will be added.",
);
if (isset($element['#autocomplete_deluxe_path'])) {
@@ -307,6 +323,7 @@ function autocomplete_deluxe_element_process($element) {
return $element;
}
$element['#attached']['js'][] = array('data' => $js_settings, 'type' => 'setting');
$element['#tree'] = TRUE;
return $element;
}
@@ -343,9 +360,11 @@ function autocomplete_deluxe_after_build($element, &$form_state) {
// Replace all double double quotes and space with a comma. This will allows
// us to keep entries in double quotes.
$element['#value'] = str_replace('"" ""', ',', $element['#value']);
$element['#value'] = str_replace('"" ""', ',', $element['#value']);
// Remove the double quotes at the beginning and the end from the first and
// the last term.
$element['#value'] = substr($element['#value'], 2, strlen($element['#value']) - 4);
unset($element['value_field']['#maxlength']);
}
@@ -373,7 +392,7 @@ function autocomplete_deluxe_element_info() {
function autocomplete_deluxe_menu() {
$items['autocomplete_deluxe/taxonomy'] = array(
'title' => 'Autocomplete deluxe taxonomy',
'page callback' => 'taxonomy_autocomplete_deluxe',
'page callback' => 'autocomplete_deluxe_taxonomy_callback',
'access arguments' => array('access content'),
'type' => MENU_CALLBACK,
);
@@ -0,0 +1,4 @@
*.patch
*.diff
.idea/
.idea/*
@@ -1,9 +1,67 @@
Date Module 7.x
=================
===================
Version 7.x-2.x-dev
===================
======================
Version 7.x-2.8
======================
- Issue #106713 by vijaycs85, heddn: Date Field title XSS(SA)
- Issue #2310123 by Temoor: Fixed Fatal error in Migrate UI by Date Migrate Example.
- Issue #2142277 by Temoor | lliss: Fixed Infinite Loop When Using Repeating Dates.
- Issue #1871136 by Temoor | funkimunky: Fixed Cannot remove migrate example content type.
- Issue #2261395 by minorOffense | kristofferwiklund: Fixed date_now is not respecting changes to timezone.
- Issue #1832400 by imot3k, balintk: Fixed "The year/month/... is missing" error message is not translatable.
- Issue #998076 by coredumperror, joelcollinsdc, brenk28: Fixed Problem with timezone handling (caused by date_get_timezone_db returning only UTC).
- Issue #1976014 by Harmageddon: Remove unnecessary &nbsp; in date views pager.
- Issue #2198807 by stickywes: Refactor field tests to utilize DateFieldBasic.
- Issue #2146461 by vijaycs85 | Razia_b: Fixed Translated field's label is not used in its validation messages.
- Issue #1874422 by bluetegu, brockfanning, rv0: Fixed Week pager may cause incorrect "next" link on year change.
- Issue #2167015 by schifazl: HTML output breaks validation and accessibility.
- Issue #2200513 by scor: Fix encoding issues in CHANGELOG.txt.
- Issue #2115377 by coredumperror: Notice: Undefined index: add_microdata in theme_date_display_single().
- Issue #2178299 by sandykadam | joachim: Incorrect variable name in hook_views_data_alter().
- Issue #1431952 by Cadila | Shawn DeArmond: Date field will only set in Rules if it already has a value.
- Issue #355058 by Grimreaper | prunelle: Hardcoded day/month/week output format for theme_date_nav_title().
- Issue #1968828 by pjcdawkins: Replace field_info_fields() with field_info_field_map() for Drupal >= 7.22.
- Issue #2167033 by podarok, vijaycs85: Fixing tests in 2014 .
======================
Version 7.x-2.7
======================
- Issue #1691342 by Cyberwolf: Field description is displayed multiple times.
- Issue #1668240 by ianthomas_uk: /themes/jquery.timeentry.css is not aggregated with other CSS files.
- Issue #1974056 by pjcdawkins: Add a CSS class to the date repeat rule.
- Issue #2065749 by vijaycs85, blackdog: $class in theme_date_repeat_rrule is not used.
- Issue #1840008 by cr0ss, Alan D.: Unlock "Date attributes" (granularity) field setting.
- Issue #1580032 by jmuzz | 30equals: Date values are not being saved when field is used in a nested field collection.
- Issue #2071629 by blackdog: #after_build wrongly added, overwrites other modules.
- Issue #1869962 by kaidjohnson: Date Context breaks context edit form.
- Issue #1571258 by David_Rothstein | ddalvi: Date and time form elements do not have accessible labels when the label position is set to 'Within' or 'None'.
- Issue #2130575 by kostajh: Unset() should be used with more caution in views_filter_handler_simple().inc.
- Issue #1826598 by eromba, jyee: 'c' and 'r' formatter causes date to be printed twice in views.
- Issue #1933472 by minorOffense: Added Optimize date_now() function.
- Issue #1248786 by kenneth.venken | dboulet: Fixed appearance of marker for required date fields.
- Issue #1810734 by ParisLiakos: Fixed Repeat checkboxes working reversed with updated jQuery.
- Issue #1844092 by dboulet, kardave, Spleshka: Fixed Untranslated strings: month, day, ...
- Issue #1202248 by james.williams, dawehner, mikehues | DamienMcKenna: Fixed Exported view doesn't include 'default_argument_options()' attribute.
- Issue #1409120 by anrikun, akamustang | marsbidon: Added Date format in views exposed filter does not respect configured format.
- Issue #1858112 by tomdearden: Fixed DateObject->difference not calculating correctly for future dates > 1 year away.
- Issue #1659466 by artkon: Fixed Date filter does not remember value in views if the identifier is something other than date_filter().
- Issue #1905096 by gielfeldt: Fixed Wrong usage of database API.
- Issue #1791804 by 5n00py: Errors on form validation if date field placed in sub-form.
- Issue #636208 Date range: Expose a single filter to select events that start before the chosen date and end after the chosen date by anrikun.
- Issue #2024269 Date popup documentation fix by RoySegall.
- Issue #1835184 by Steven Jones, das-peter, jwhat: Fixed date_limit_format() can have poor performance.
- Issue #991830 by milesw | restyler: Fixed validation errors when date_popup() date is localized.
- Issue #2098715 by jhodgdon: Fixed Date field not obeying widget alters for #required.
- Issue #1266688 by linclark, rbayliss, fago, colette: Support microdata in date fields.
- Issue #1863610 by maximpodorov: Fixed Invalid date formatting.
- META #2034231 #1832544 Class registration for Migrate 2.5 or later - mikeryan, [#1835214] Automated tests failing - Exception thrown in Date2 migration - PatchRanger, [#
- Issue #1350604 by Alan D., johaziel: Added Diff support for Date fields.
- Issue #2086313 by dooug: Fixed Wrong path in date_popup() README.txt
- Issue #1455558 by BTMash | mediameriquat: Fixed Missing file in date_views().info causes error 500.
- Issue #1697322 by Alan D., ianmthomasuk | pandikamal: Fixed Call to a member function getName() on a non-object in date api.
- Code cleanup, remove #prev values that were never used in repeat functionality.
- Small fix needed to ensure cardinality gets set correctly if repeat option is changed in the UI.
======================
Version 7.x-2.6
@@ -110,7 +168,7 @@ Version 7.x-2.1
- Issue #1437242 by zerbash, Remove extraneous leading slashes in module_load_include().
- Issue #1436722 by hefox: Fixed Undefined variable $form_set_error() used as function.
- Issue #1250626 by Gábor Hojtsy, B-Prod, hefox: Added start date and end date labels.
- Issue #1250626 by Gábor Hojtsy, B-Prod, hefox: Added start date and end date labels.
- Issue #1253482, Make sure $argument->is_default gets reset by the Date pager when altering results.
- Adjust Date Tools to work with changes to use Views templates to create calendars.
- Issue #1398584 by dhalbert and , Make sure groupby times is initialized.
@@ -194,7 +252,7 @@ The Date Browser has been removed. Please use the Date Pager instead. If you hav
Browser the navigation will just disappear from them. If you add a Date Pager to the view you should get it back.
Then delete the Date Browser attachment from the view, since it doesn't do anything any more.
The UNTIL date was not getting included in repeating results and that is now fixed. This is an API change of sorts
The UNTIL date was not getting included in repeating results and that is now fixed. This is an API change of sorts
for anyone who worked around the issue by setting it ahead.
The All Day checkbox and All Day themes were moved into a separate module, using new hooks added to the date
@@ -219,7 +277,7 @@ New Features/Major Changes
Bugfixes
- Fix to new default date handling, the default date has to set a date in the database timezone, not the display timezone.
- Issue #1245106 by Gábor Hojtsy, Hide the option to add the delta into the view for single value fields.
- Issue #1245106 by Gábor Hojtsy, Hide the option to add the delta into the view for single value fields.
- Issue #1370876, Make sure new Date All Day code does not try to set the popup values if Date Popup is disabled.
- Issue #874322, Add back the date_field_all_day() function to avoid breaking other modules that are using it.
- Fix Date text placeholder to display a formatted date instead of a format. Follow up to Date repeat UI changes.
@@ -316,7 +374,7 @@ Bugfixes
- Issue #1179715, Default value callback for the timezone widget was not returning an array.
- Issue #1179716, Remove value_callback for date_repeat and date_combo forms, the default behavior works fine.
- Issue #1178716 by das-peter, Use drupal_array_get_nested_value() in Date Repeat instead of trying to find it manually.
- Issue #1178716 by das-peter and KarenS, Tweak the date repeat widget to identify empty input when used on nodes with translation.
- Issue #1178716 by das-peter and KarenS, Tweak the date repeat widget to identify empty input when used on nodes with translation.
- Issue #1178716 by das-peter and KarenS, Fix date repeat form values that are not arrays when hidden on a node that has translation.
- Issue #1178176 by das-peter, Fix date_combo_value_callback to return NULL to avoid data lost on untranslatable dates used with Entity Translation.
- Date Context module was making incorrect assumptions about the $language of the field.
@@ -350,7 +408,7 @@ field settings. Previous versions did not always honor those settings, this one
New Features/UX Improvements
- Issue #1249724 by KarenS, Gábor Hojtsy, David_Rothstein, Improve usability of date and time input configuration.
- Issue #1249724 by KarenS, Gábor Hojtsy, David_Rothstein, Improve usability of date and time input configuration.
- Issue #1250784 by David_Rothstein, Add user-friendly labels for start and end date values in Views.
- Issue #742146, Add option to remove X-WR-CALNAME if VEVENT is not a feed.
- Add option to change method from PUBLISH to REQUEST in VCALENDAR.
@@ -360,8 +418,8 @@ New Features/UX Improvements
- Issue #1249724 by David_Rothstein: Improve usability of date and time input configuration
- Issue #1177198 by tim.plunkett: Allow CTools to process #dependency for date elements.
- Issue #1245562 by David_Rothstein, Rename the default date display format to something friendlier
- Issue #1239934 by David_Rothstein and Gábor Hojtsy, Reuse the "years back and forward" dropdown widget on the Views filter settings page.
- Issue #1239228 by Gábor Hojtsy, Date Views filter form UI improvements, clarify the way absolute and relative dates work.
- Issue #1239934 by David_Rothstein and Gábor Hojtsy, Reuse the "years back and forward" dropdown widget on the Views filter settings page.
- Issue #1239228 by Gábor Hojtsy, Date Views filter form UI improvements, clarify the way absolute and relative dates work.
- Issue #233047 by ksenzee and David_Rothstein, Add the Vegas jQuery timepicker as a new time selector option.
- Issue #1145976 by tim.plunkett and KarenS, Add 'is date' identifier to all date handlers.
- Issue #1234140 by arlinsundbulte, Change terminology in user-facing text from 'From/To Date' to 'Start/End Date'.
@@ -408,10 +466,10 @@ Bugfixes
- Issue #1254582 Repeat additions need to be adjusted to use the same time as the original date.
- Move vcalendar and vevent templates from Date Views to Date API modules.
- Follow up to Issue #1250344, We don't need extra space when there is a description, only when there is not.
- Issue #1239228 by Gábor Hojtsy, More tweaks to filter css.
- Issue #1244924 by Gábor Hojtsy, Minor text improvements in date filter configuration
- Issue #1239228 by Gábor Hojtsy, More tweaks to filter css.
- Issue #1244924 by Gábor Hojtsy, Minor text improvements in date filter configuration
- Issue #1245556 by David_Rothstein, Date granularity description incorrectly implies that it affects the date attributes that are displayed
- Issue #1247444 by Gábor Hojtsy, Give a little breathing space to the date year range "other" field
- Issue #1247444 by Gábor Hojtsy, Give a little breathing space to the date year range "other" field
- Issue #1250344 by jessebeach, Fix padding around date fields by adding clearfix class.
- Issue #1249116 by yched, Fix various glitches with D6 migration code.
- Issue #1243022 by fmosca and KarenS, Make sure all_day #states visibility is only set when there is a value for all_day.
@@ -419,7 +477,7 @@ Bugfixes
- Issue #1246416, Test whether libraries_get_path() returns a valid path before using it.
- Issue #1235994, Don't display 'All Day' when using a format that has no time.
- Issue #1245690 by mikeryan, Migration plugin missing seconds from date formats
- Issue #1229406 by David Rothstein, Gábor Hojtsy, and tim.plunkett Fix broken timepicker in Chrome and Safari.
- Issue #1229406 by David Rothstein, Gábor Hojtsy, and tim.plunkett Fix broken timepicker in Chrome and Safari.
- Issue #1239412 by keithm, Fix validation error when #access is false.
- Issue #1232522, Don't alter field_ui_field_edit form except on date fields.
- Issue #1243842, Make sure the All Day and Show End Date flags work correctly in unlimited value fields that use ajax.
@@ -344,9 +344,6 @@ function hook_date_combo_process_alter(&$element, &$form_state, $context) {
'#date_increment' => $instance['widget']['settings']['increment'],
'#date_year_range' => $instance['widget']['settings']['year_range'],
'#date_label_position' => $instance['widget']['settings']['label_position'],
'#prev_value' => isset($item['value']) ? $item['value'] : '',
'#prev_value2' => isset($item['value2']) ? $item['value2'] : '',
'#prev_rrule' => isset($item['rrule']) ? $item['rrule'] : '',
'#date_repeat_widget' => str_replace('_repeat', '', $instance['widget']['type']),
'#date_repeat_collapsed' => $instance['widget']['settings']['repeat_collapsed'],
'#date_flexible' => 0,
@@ -15,8 +15,14 @@ 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']);
$back = str_replace('-', '', $split[0]);
$forward = str_replace('+', '', $split[1]);
// 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');
}
else {
$back = 2;
@@ -61,9 +67,11 @@ function date_devel_generate($entity, $field, $instance, $bundle) {
case 'date':
$format = DATE_FORMAT_ISO;
break;
case 'datestamp':
$format = DATE_FORMAT_UNIX;
break;
case 'datetime':
$format = DATE_FORMAT_DATETIME;
break;
@@ -0,0 +1,79 @@
<?php
/**
* @file
* Provide diff field functions for the Date module.
*/
/**
* Diff field callback for parsing date fields comparative values.
*/
function date_field_diff_view($items, $context) {
$diff_items = array();
$display = $context['display'];
$display['settings']['format_type'] = $context['settings']['format_type'];
$display['settings']['fromto'] = $context['settings']['fromto'];
foreach ($items as $delta => $item) {
$date = date_formatter_process('date_default', $context['entity_type'], $context['entity'], $context['field'], $context['instance'], $context['language'], $item, $display);
switch ($display['settings']['fromto']) {
case 'both':
if ($date['value']['formatted'] != $date['value2']['formatted']) {
$diff_items[$delta] = t('@from to @to', array(
'@from' => $date['value']['formatted'],
'@to' => $date['value2']['formatted'],
));
}
else {
$diff_items[$delta] = $date['value']['formatted'];
}
break;
case 'value':
case 'value2':
$diff_items[$delta] = $date[$display['settings']['fromto']]['formatted'];
break;
}
}
return $diff_items;
}
/**
* Provide default field comparison options.
*/
function date_field_diff_default_options($field_type) {
return array(
'format_type' => 'long',
'fromto' => 'both',
);
}
/**
* Provide a form for setting the field comparison options.
*/
function date_field_diff_options_form($field_type, $settings) {
$options_form = array();
$form['format_type'] = array(
'#title' => t('Choose how render dates and times'),
'#type' => 'select',
'#options' => date_format_type_options(),
'#default_value' => $settings['format_type'],
'#description' => t('To add or edit options, visit <a href="@date-time-page">Date and time settings</a>.', array('@date-time-page' => url('admin/config/regional/date-time'))),
'#weight' => 0,
);
$form['fromto'] = array(
'#title' => t('Display'),
'#type' => 'select',
'#options' => array(
'both' => t('Both Start and End dates'),
'value' => t('Start date only'),
'value2' => t('End date only'),
),
'#default_value' => $settings['fromto'],
'#weight' => 1,
);
return $options_form;
}
@@ -19,6 +19,7 @@ function date_field_formatter_info() {
'multiple_from' => '',
'multiple_to' => '',
'fromto' => 'both',
'show_remaining_days' => FALSE,
),
),
'format_interval' => array(
@@ -27,6 +28,7 @@ function date_field_formatter_info() {
'settings' => array(
'interval' => 2,
'interval_display' => 'time ago',
'use_end_date' => false,
),
),
'date_plain' => array(
@@ -48,6 +50,7 @@ function date_field_formatter_settings_form($field, $instance, $view_mode, $form
case 'format_interval':
$form = date_interval_formatter_settings_form($field, $instance, $view_mode, $form, $form_state);
break;
default:
$form = date_default_formatter_settings_form($field, $instance, $view_mode, $form, $form_state);
break;
@@ -72,6 +75,7 @@ function date_field_formatter_settings_summary($field, $instance, $view_mode) {
case 'format_interval':
$summary = date_interval_formatter_settings_summary($field, $instance, $view_mode);
break;
default:
$summary = date_default_formatter_settings_summary($field, $instance, $view_mode);
break;
@@ -125,8 +129,16 @@ function date_field_formatter_view($entity_type, $entity, $field, $instance, $la
'attributes' => array(),
'rdf_mapping' => array(),
'add_rdf' => module_exists('rdf'),
'microdata' => array(),
'add_microdata' => module_exists('microdata'),
);
// If the microdata module is enabled, the microdata mapping will have been
// passed in via the entity.
if ($variables['add_microdata'] && isset($entity->microdata[$field['field_name']])) {
$variables['microdata'] = $entity->microdata[$field['field_name']];
}
// If there is an RDf mapping for this date field, pass it down to the theme.
$rdf_mapping = array();
if (!empty($entity->rdf_mapping) && function_exists('rdf_rdfa_attributes')) {
@@ -161,11 +173,16 @@ function date_field_formatter_view($entity_type, $entity, $field, $instance, $la
$element[$delta] = array('#markup' => $item['value']);
}
else {
$element[$delta] = array('#markup' => t('!start-date to !end-date', array('!start-date' => $item['value'], '!end-date' => $item['value2'])));
$element[$delta] = array(
'#markup' => t('!start-date to !end-date', array(
'!start-date' => $item['value'],
'!end-date' => $item['value2']
)));
}
}
}
break;
case 'format_interval':
foreach ($items as $delta => $item) {
if (!empty($entity->date_id) && !in_array($delta, $selected_deltas)) {
@@ -180,6 +197,7 @@ function date_field_formatter_view($entity_type, $entity, $field, $instance, $la
}
}
break;
default:
foreach ($items as $delta => $item) {
if (!empty($entity->date_id) && !in_array($delta, $selected_deltas)) {
@@ -190,6 +208,7 @@ function date_field_formatter_view($entity_type, $entity, $field, $instance, $la
$variables['item'] = $item;
$variables['dates'] = date_formatter_process($formatter, $entity_type, $entity, $field, $instance, $langcode, $item, $display);
$variables['attributes'] = !empty($rdf_mapping) ? rdf_rdfa_attributes($rdf_mapping, $item['value']) : array();
$variables['show_remaining_days'] = isset($display['settings']['show_remaining_days']) ? $display['settings']['show_remaining_days'] : FALSE;
$output = theme('date_display_combination', $variables);
if (!empty($output)) {
$element[$delta] = array('#markup' => $output);
@@ -223,10 +242,11 @@ function date_field_is_empty($item, $field) {
* Implements hook_field_info().
*/
function date_field_info() {
$granularity = array('year', 'month', 'day', 'hour', 'minute');
$settings = array(
'settings' => array(
'todate' => '',
'granularity' => drupal_map_assoc(array('year', 'month', 'day', 'hour', 'minute')),
'granularity' => drupal_map_assoc($granularity),
'tz_handling' => 'site',
'timezone_db' => 'UTC',
),
@@ -242,26 +262,26 @@ function date_field_info() {
);
return array(
'datetime' => array(
'label' => 'Date',
'label' => t('Date'),
'description' => t('Store a date in the database as a datetime field, recommended for complete dates and times that may need timezone conversion.'),
'default_widget' => 'date_select',
'default_formatter' => 'date_default',
'default_token_formatter' => 'date_plain',
) + $settings,
) + $settings,
'date' => array(
'label' => 'Date (ISO format)',
'label' => t('Date (ISO format)'),
'description' => t('Store a date in the database as an ISO date, recommended for historical or partial dates.'),
'default_widget' => 'date_select',
'default_formatter' => 'date_default',
'default_token_formatter' => 'date_plain',
) + $settings,
) + $settings,
'datestamp' => array(
'label' => 'Date (Unix timestamp)',
'label' => t('Date (Unix timestamp)'),
'description' => t('Store a date in the database as a timestamp, deprecated format to support legacy data.'),
'default_widget' => 'date_select',
'default_formatter' => 'date_default',
'default_token_formatter' => 'date_plain',
) + $settings,
) + $settings,
);
}
@@ -286,24 +306,24 @@ function date_field_widget_info() {
$info = array(
'date_select' => array(
'label' => t('Select list'),
'label' => t('Select list'),
'field types' => array('date', 'datestamp', 'datetime'),
) + $settings,
'date_text' => array(
'label' => t('Text field'),
'label' => t('Text field'),
'field types' => array('date', 'datestamp', 'datetime'),
) + $settings,
) + $settings,
);
if (module_exists('date_popup')) {
$info['date_popup'] = array(
'label' => t('Pop-up calendar'),
'label' => t('Pop-up calendar'),
'field types' => array('date', 'datestamp', 'datetime'),
) + $settings;
}
// The date text widget should use an increment of 1.
$info['date_text']['increment'] = 1;
$info['date_text']['settings']['increment'] = 1;
return $info;
}
@@ -443,6 +463,14 @@ function date_field_instance_settings_form($field, $instance) {
return _date_field_instance_settings_form($field, $instance);
}
/**
* Form validation handler for _date_field_instance_settings_form().
*/
function date_field_instance_settings_form_validate(&$form, &$form_state) {
module_load_include('inc', 'date', 'date_admin');
return _date_field_instance_settings_form_validate($form, $form_state);
}
/**
* Implements hook_field_widget_settings_form().
*/
@@ -451,6 +479,14 @@ function date_field_widget_settings_form($field, $instance) {
return _date_field_widget_settings_form($field, $instance);
}
/**
* Form validation handler for _date_field_widget_settings_form().
*/
function date_field_widget_settings_form_validate(&$form, &$form_state) {
module_load_include('inc', 'date', 'date_admin');
return _date_field_widget_settings_form_validate($form, $form_state);
}
/**
* Implements hook_field_settings_form().
*/
@@ -459,6 +495,14 @@ function date_field_settings_form($field, $instance, $has_data) {
return _date_field_settings_form($field, $instance, $has_data);
}
/**
* Form validation handler for _date_field_settings_form().
*/
function date_field_settings_validate(&$form, &$form_state) {
module_load_include('inc', 'date', 'date_admin');
return _date_field_settings_validate($form, $form_state);
}
/**
* Implements hook_content_migrate_field_alter().
*
@@ -4,15 +4,20 @@ dependencies[] = 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 2012-08-13
version = "7.x-2.6"
; Information added by Drupal.org packaging script on 2017-04-07
version = "7.x-2.10"
core = "7.x"
project = "date"
datestamp = "1344850024"
datestamp = "1491562090"
@@ -19,6 +19,7 @@ function date_field_schema($field) {
'views' => TRUE,
);
break;
case 'datetime':
$db_columns['value'] = array(
'type' => 'datetime',
@@ -31,6 +32,7 @@ function date_field_schema($field) {
'views' => TRUE,
);
break;
default:
$db_columns['value'] = array(
'type' => 'varchar',
@@ -66,7 +68,12 @@ function date_field_schema($field) {
'views' => FALSE,
);
if (!empty($field['settings']['todate'])) {
$db_columns['offset2'] = array('type' => 'int', 'not null' => FALSE, 'sortable' => TRUE, 'views' => FALSE);
$db_columns['offset2'] = array(
'type' => 'int',
'not null' => FALSE,
'sortable' => TRUE,
'views' => FALSE
);
}
}
if (isset($field['settings']['repeat']) && $field['settings']['repeat'] == 1) {
@@ -88,8 +95,9 @@ function date_update_last_removed() {
}
/**
* Get rid of the individual formatters for each format type,
* these are now settings in the default formatter.
* Get rid of the individual formatters for each format type.
*
* These are now settings in the default formatter.
*/
function date_update_7000() {
$instances = field_info_instances();
@@ -115,8 +123,9 @@ function date_update_7000() {
}
/**
* Get rid of the separate widgets for repeating dates. The code now handles
* repeating dates correctly using the regular widgets.
* Get rid of the separate widgets for repeating dates.
*
* The code now handles repeating dates correctly using the regular widgets.
*/
function date_update_7001() {
$query = db_select('field_config_instance', 'fci', array('fetch' => PDO::FETCH_ASSOC));
@@ -127,7 +136,11 @@ function date_update_7001() {
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'], array(
'date_popup_repeat',
'date_text_repeat',
'date_select_repeat'
))) {
$instance['widget']['type'] = str_replace('_repeat', '', $instance['widget']['type']);
db_update('field_config_instance')
->fields(array(
@@ -192,3 +205,10 @@ function date_update_7004() {
drupal_set_message(t('Date text widgets have been updated to use an increment of 1.'));
}
/**
* Revisited: Date text widgets should always use an increment of 1.
*/
function date_update_7005() {
// @see https://www.drupal.org/node/1355256
date_update_7004();
}
@@ -27,7 +27,7 @@ Drupal.date.EndDateHandler = function (widget) {
this.$widget = $(widget);
this.$start = this.$widget.find('.form-type-date-select[class$=value]');
this.$end = this.$widget.find('.form-type-date-select[class$=value2]');
if (this.$end.length == 0) {
if (this.$end.length === 0) {
return;
}
this.initializeSelects();
@@ -68,7 +68,7 @@ Drupal.date.EndDateHandler.prototype.endDateIsBlank = function () {
var id;
for (id in this.selects) {
if (this.selects.hasOwnProperty(id)) {
if (this.selects[id].end.val() != '') {
if (this.selects[id].end.val() !== '') {
return false;
}
}
@@ -1,10 +1,24 @@
<?php
/**
* @file
* Support for migration into Date fields.
*/
if (!class_exists('MigrateFieldHandler')) {
return;
}
/**
* Implements hook_migrate_api().
*/
function date_migrate_api() {
$api = array(
'api' => 2,
'field handlers' => array('DateMigrateFieldHandler'),
);
return $api;
}
class DateMigrateFieldHandler extends MigrateFieldHandler {
/**
@@ -29,7 +43,7 @@ class DateMigrateFieldHandler extends MigrateFieldHandler {
* @return array
* An array of the defined variables in this scope.
*/
static function arguments($timezone = 'UTC', $timezone_db = 'UTC', $rrule = NULL, $language = NULL) {
public static function arguments($timezone = 'UTC', $timezone_db = 'UTC', $rrule = NULL, $language = NULL) {
return get_defined_vars();
}
@@ -57,37 +71,47 @@ class DateMigrateFieldHandler extends MigrateFieldHandler {
$arguments = array();
}
if (isset($arguments['timezone'])) {
$default_timezone = $arguments['timezone'];
}
else {
$default_timezone = 'UTC';
}
if (isset($arguments['timezone_db'])) {
$default_timezone_db = $arguments['timezone_db'];
}
else {
$default_timezone_db = NULL;
}
if (isset($arguments['rrule'])) {
$default_rrule = $arguments['rrule'];
}
else {
$default_rrule = NULL;
}
$language = $this->getFieldLanguage($entity, $field_info, $arguments);
// Setup the standard Field API array for saving.
$delta = 0;
foreach ($values as $from) {
// Set defaults.
$to = NULL;
$timezone = $default_timezone;
$timezone_db = $default_timezone_db;
$rrule = $default_rrule;
foreach ($values as $delta => $from) {
if (!empty($arguments['timezone'])) {
if (is_array($arguments['timezone'])) {
$timezone = $arguments['timezone'][$delta];
}
else {
$timezone = $arguments['timezone'];
}
}
else {
$timezone = 'UTC';
}
// Is the value a straight datetime value, or JSON containing a set of
// properties?
if (!empty($arguments['rrule'])) {
if (is_array($arguments['rrule'])) {
$rrule = $arguments['rrule'][$delta];
}
else {
$rrule = $arguments['rrule'];
}
}
else {
$rrule = NULL;
}
if (!empty($arguments['to'])) {
if (is_array($arguments['to'])) {
$to = $arguments['to'][$delta];
}
else {
$to = $arguments['to'];
}
}
else {
$to = NULL;
}
// Legacy support for JSON containing a set of properties - deprecated
// now that we have subfields.
if (!empty($from) && $from{0} == '{') {
$properties = drupal_json_decode($from);
$from = $properties['from'];
@@ -98,9 +122,6 @@ class DateMigrateFieldHandler extends MigrateFieldHandler {
if (!empty($properties['timezone'])) {
$timezone = $properties['timezone'];
}
if (!empty($properties['timezone_db'])) {
$timezone_db = $properties['timezone_db'];
}
if (!empty($properties['rrule'])) {
$rrule = $properties['rrule'];
}
@@ -111,6 +132,7 @@ class DateMigrateFieldHandler extends MigrateFieldHandler {
// timestamp for 'now'.
if (empty($from)) {
$return[$language][$delta]['value'] = NULL;
$return[$language][$delta]['timezone'] = NULL;
if (!empty($field_info['settings']['todate'])) {
$return[$language][$delta]['value2'] = NULL;
}
@@ -133,6 +155,7 @@ class DateMigrateFieldHandler extends MigrateFieldHandler {
case 'datestamp':
// Already done.
break;
case 'datetime':
// YYYY-MM-DD HH:MM:SS.
$from = format_date($from, 'custom', 'Y-m-d H:i:s', $timezone);
@@ -140,6 +163,7 @@ class DateMigrateFieldHandler extends MigrateFieldHandler {
$to = format_date($to, 'custom', 'Y-m-d H:i:s', $timezone);
}
break;
case 'date':
// ISO date: YYYY-MM-DDTHH:MM:SS.
$from = format_date($from, 'custom', 'Y-m-d\TH:i:s', $timezone);
@@ -147,6 +171,7 @@ class DateMigrateFieldHandler extends MigrateFieldHandler {
$to = format_date($to, 'custom', 'Y-m-d\TH:i:s', $timezone);
}
break;
default:
break;
}
@@ -155,21 +180,36 @@ class DateMigrateFieldHandler extends MigrateFieldHandler {
// created.
if (function_exists('date_repeat_build_dates') && !empty($field_info['settings']['repeat']) && $rrule) {
include_once DRUPAL_ROOT . '/' . drupal_get_path('module', 'date_api') . '/date_api_ical.inc';
$item = array('value' => $from, 'value2' => $to, 'timezone' => $timezone);
$item = array(
'value' => $from,
'value2' => $to,
'timezone' => $timezone,
);
// Can be de-uglified when http://drupal.org/node/1159404 is committed.
$return[$language] = date_repeat_build_dates(NULL, date_ical_parse_rrule($field_info, $rrule), $field_info, $item);
}
else {
$return[$language][$delta]['value'] = $from;
$return[$language][$delta]['timezone'] = $timezone;
if (!empty($to)) {
$return[$language][$delta]['value2'] = $to;
}
}
$delta++;
}
if (!isset($return)) {
$return = NULL;
}
return $return;
}
/**
* {@inheritdoc}
*/
public function fields($migration = NULL) {
return array(
'timezone' => t('Timezone'),
'rrule' => t('Recurring event rule'),
'to' => t('End date date'),
);
}
}
+111 -34
View File
@@ -17,6 +17,7 @@ function date_get_entity_bundle($entity_type, $entity) {
case 'field_collection_item':
$bundle = $entity->field_name;
break;
default:
$bundle = field_extract_bundle($entity_type, $entity);
break;
@@ -42,13 +43,20 @@ function date_default_format($type) {
* Wrapper function around each of the widget types for creating a date object.
*/
function date_input_date($field, $instance, $element, $input) {
// Trim extra spacing off user input of text fields.
if (isset($input['date'])) {
$input['date'] = trim($input['date']);
}
switch ($instance['widget']['type']) {
case 'date_text':
$function = 'date_text_input_date';
break;
case 'date_popup':
$function = 'date_popup_input_date';
break;
default:
$function = 'date_select_input_date';
}
@@ -68,6 +76,7 @@ function date_theme() {
);
$themes = array(
'date_combo' => $base + array('render element' => 'element'),
'date_form_element' => $base + array('render element' => 'element'),
'date_text_parts' => $base + array('render element' => 'element'),
'date' => $base + array('render element' => 'element'),
'date_display_single' => $base + array(
@@ -78,6 +87,8 @@ function date_theme() {
'attributes' => array(),
'rdf_mapping' => NULL,
'add_rdf' => NULL,
'microdata' => NULL,
'add_microdata' => NULL,
),
),
'date_display_range' => $base + array(
@@ -95,7 +106,15 @@ function date_theme() {
'attributes_end' => array(),
'rdf_mapping' => NULL,
'add_rdf' => NULL,
)),
'microdata' => NULL,
'add_microdata' => NULL,
),
),
'date_display_remaining' => $base + array(
'variables' => array(
'remaining_days' => NULL,
),
),
'date_display_combination' => $base + array(
'variables' => array(
'entity_type' => NULL,
@@ -110,6 +129,8 @@ function date_theme() {
'attributes' => array(),
'rdf_mapping' => NULL,
'add_rdf' => NULL,
'microdata' => NULL,
'add_microdata' => NULL,
),
),
'date_display_interval' => $base + array(
@@ -126,7 +147,7 @@ function date_theme() {
'attributes' => array(),
'rdf_mapping' => NULL,
'add_rdf' => NULL,
),
),
),
);
@@ -205,8 +226,10 @@ function date_formatter_process($formatter, $entity_type, $entity, $field, $inst
$settings = $display['settings'];
$field_name = $field['field_name'];
$format = date_formatter_format($formatter, $settings, $granularity, $langcode);
$timezone = isset($item['timezone']) ? $item['timezone'] : '';
$timezone = date_get_timezone($field['settings']['tz_handling'], $timezone);
if (!isset($field['settings']['tz_handling']) || $field['settings']['tz_handling'] !== 'utc') {
$timezone = isset($item['timezone']) ? $item['timezone'] : '';
$timezone = date_get_timezone($field['settings']['tz_handling'], $timezone);
}
$timezone_db = date_get_timezone_db($field['settings']['tz_handling']);
$db_format = date_type_format($field['type']);
$process = date_process_values($field);
@@ -242,22 +265,23 @@ function date_formatter_process($formatter, $entity_type, $entity, $field, $inst
$dates[$processed]['formatted_iso'] = date_format_date($date, 'custom', 'c');
if (is_object($date)) {
if ($format == 'format_interval') {
$dates[$processed]['interval'] = date_format_interval($date);
$dates[$processed]['interval'] = date_format_interval($date);
}
elseif ($format == 'format_calendar_day') {
$dates[$processed]['calendar_day'] = date_format_calendar_day($date);
$dates[$processed]['calendar_day'] = date_format_calendar_day($date);
}
elseif ($format == 'U') {
elseif ($format == 'U' || $format == 'r' || $format == 'c') {
$dates[$processed]['formatted'] = date_format_date($date, 'custom', $format);
$dates[$processed]['formatted_date'] = date_format_date($date, 'custom', $format);
$dates[$processed]['formatted_time'] = '';
$dates[$processed]['formatted_timezone'] = '';
}
elseif (!empty($format)) {
$dates[$processed]['formatted'] = date_format_date($date, 'custom', $format);
$dates[$processed]['formatted_date'] = date_format_date($date, 'custom', date_limit_format($format, array('year', 'month', 'day')));
$dates[$processed]['formatted_time'] = date_format_date($date, 'custom', date_limit_format($format, array('hour', 'minute', 'second')));
$dates[$processed]['formatted_timezone'] = date_format_date($date, 'custom', date_limit_format($format, array('timezone')));
$formats = _get_custom_date_format($date, $format);
$dates[$processed]['formatted'] = $formats['formatted'];
$dates[$processed]['formatted_date'] = $formats['date'];
$dates[$processed]['formatted_time'] = $formats['time'];
$dates[$processed]['formatted_timezone'] = $formats['zone'];
}
}
}
@@ -284,6 +308,30 @@ function date_formatter_process($formatter, $entity_type, $entity, $field, $inst
return $dates;
}
/**
* Get a custom date format.
*/
function _get_custom_date_format($date, $format) {
$custom = array();
$custom['granularities'] = array(
'date' => array('year', 'month', 'day'),
'time' => array('hour', 'minute', 'second'),
'zone' => array('timezone'),
);
$custom['limits'] = array(
'date' => date_limit_format($format, $custom['granularities']['date']),
'time' => date_limit_format($format, $custom['granularities']['time']),
'zone' => date_limit_format($format, $custom['granularities']['zone']),
);
return array(
'formatted' => date_format_date($date, 'custom', $format),
'date' => date_format_date($date, 'custom', $custom['limits']['date']),
'time' => date_format_date($date, 'custom', $custom['limits']['time']),
'zone' => date_format_date($date, 'custom', $custom['limits']['zone']),
);
}
/**
* Retrieves the granularity for a field.
*
@@ -297,14 +345,14 @@ function date_formatter_process($formatter, $entity_type, $entity, $field, $inst
*/
function date_granularity($field) {
if (!is_array($field) || !is_array($field['settings']['granularity'])) {
$field['settings']['granularity'] = drupal_map_assoc(array('year', 'month', 'day'));
$granularity = drupal_map_assoc(array('year', 'month', 'day'));
$field['settings']['granularity'] = $granularity;
}
return array_values(array_filter($field['settings']['granularity']));
}
/**
* Helper function to create an array of the date values in a
* field that need to be processed.
* Helper function to create an array of the date values in a field that need to be processed.
*/
function date_process_values($field) {
return $field['settings']['todate'] ? array('value', 'value2') : array('value');
@@ -390,12 +438,17 @@ function date_formatter_format($formatter, $settings, $granularity = array(), $l
switch ($formatter) {
case 'format_interval':
return 'format_interval';
break;
case 'date_plain':
return 'date_plain';
break;
default:
$format = date_format_type_format($format_type, $langcode);
if ($format_type == 'custom') {
$format = $settings['custom_date_format'];
}
else {
$format = date_format_type_format($format_type, $langcode);
}
break;
}
@@ -406,6 +459,7 @@ function date_formatter_format($formatter, $settings, $granularity = array(), $l
/**
* Helper function to get the right format for a format type.
*
* Checks for locale-based format first.
*/
function date_format_type_format($format_type, $langcode = NULL) {
@@ -428,27 +482,30 @@ function date_format_type_format($format_type, $langcode = NULL) {
case 'short':
$default = 'm/d/Y - H:i';
break;
case 'long':
$default = 'l, F j, Y - H:i';
break;
// If it's not one of the core date types and isn't stored in the
// database, we'll fall back on using the same default format as the
// 'medium' type.
case 'medium':
default:
// @todo: If a non-core module provides a date type and does not
// variable_set() a default for it, the default assumed here may
// not be correct (since the default format used by 'medium' may
// not even be one of the allowed formats for the date type in
// question). To fix this properly, we should really call
// system_get_date_formats($format_type) and take the first
// format from that list as the default. However, this function
// is called often (on many different page requests), so calling
// system_get_date_formats() from here would be a performance hit
// since that function writes several records to the database
// during each page request that calls it.
// variable_set() a default for it, the default assumed here may
// not be correct (since the default format used by 'medium' may
// not even be one of the allowed formats for the date type in
// question). To fix this properly, we should really call
// system_get_date_formats($format_type) and take the first
// format from that list as the default. However, this function
// is called often (on many different page requests), so calling
// system_get_date_formats() from here would be a performance hit
// since that function writes several records to the database
// during each page request that calls it.
$default = 'D, m/d/Y - H:i';
break;
}
$format = variable_get('date_format_' . $format_type, $default);
}
@@ -502,7 +559,7 @@ function date_prepare_entity($formatter, $entity_type, $entity, $field, $instanc
elseif ((!empty($max_count) && is_numeric($max_count) && $count >= $max_count) ||
(!empty($value['value']) && $value['value'] < $start) ||
(!empty($value['value2']) && $value['value2'] > $end)) {
unset($entity->{$field_name}[$langcode][$delta]);
unset($entity->{$field_name}[$langcode][$delta]);
}
else {
$count++;
@@ -532,6 +589,7 @@ function date_entity_metadata_property_info_alter(&$info, $entity_type, $field,
if (!empty($field['settings']['todate'])) {
// Define a simple data structure containing both dates.
$property['type'] = ($field['cardinality'] != 1) ? 'list<struct>' : 'struct';
$property['auto creation'] = 'date_entity_metadata_struct_create';
$property['getter callback'] = 'entity_metadata_field_verbatim_get';
$property['setter callback'] = 'entity_metadata_field_verbatim_set';
$property['property info'] = array(
@@ -543,6 +601,8 @@ function date_entity_metadata_property_info_alter(&$info, $entity_type, $field,
// The getter and setter callbacks for 'value' and 'value2'
// will not provide the field name as $name, we'll add it to $info.
'field_name' => $field['field_name'],
// Alert Microdata module that this value can be exposed in microdata.
'microdata' => TRUE,
),
'value2' => array(
'type' => 'date',
@@ -552,6 +612,8 @@ function date_entity_metadata_property_info_alter(&$info, $entity_type, $field,
// The getter and setter callbacks for 'value' and 'value2'
// will not provide the field name as $name, we'll add it to $info.
'field_name' => $field['field_name'],
// Alert Microdata module that this value can be exposed in microdata.
'microdata' => TRUE,
),
'duration' => array(
'type' => 'duration',
@@ -566,6 +628,11 @@ function date_entity_metadata_property_info_alter(&$info, $entity_type, $field,
);
unset($property['query callback']);
}
else {
// If this doesn't have a todate, it is handled as a date rather than a
// struct. Enable microdata on the field itself rather than the properties.
$property['microdata'] = TRUE;
}
}
/**
@@ -632,12 +699,22 @@ function date_entity_metadata_field_setter(&$entity, $name, $value, $langcode, $
drupal_static_reset('field_language');
}
/**
* Auto creation callback for fields which contain two date values in one.
*/
function date_entity_metadata_struct_create($name, $property_info) {
return array(
'date_type' => $property_info['field']['columns'][$name]['type'],
'timezone_db' => $property_info['field']['settings']['timezone_db'],
);
}
/**
* Callback for setting an individual field value if a to-date may be there too.
*
* 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'.
*/
@@ -659,9 +736,9 @@ 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. Copy left here to avoid breaking other
* modules that use this function.
* Duplicate functionality 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.
*
* DEPRECATED!, will be removed at some time in the future.
*/
@@ -735,7 +812,7 @@ function date_field_widget_properties_alter(&$widget, $context) {
$entity = $context['entity'];
$info = entity_get_info($entity_type);
$id = $info['entity keys']['id'];
$widget['is_new']= FALSE;
$widget['is_new'] = FALSE;
if (empty($entity->$id)) {
$widget['is_new'] = TRUE;
}
+144 -11
View File
@@ -74,7 +74,10 @@ function theme_date_display_combination($variables) {
$attributes = $variables['attributes'];
$rdf_mapping = $variables['rdf_mapping'];
$add_rdf = $variables['add_rdf'];
$microdata = $variables['microdata'];
$add_microdata = $variables['add_microdata'];
$precision = date_granularity_precision($field['settings']['granularity']);
$show_remaining_days = $variables['show_remaining_days'];
$output = '';
@@ -119,10 +122,12 @@ function theme_date_display_combination($variables) {
$date1 = $dates['value']['formatted'];
$date2 = $date1;
break;
case 'value2':
$date2 = $dates['value2']['formatted'];
$date1 = $date2;
break;
default:
$date1 = $dates['value']['formatted'];
$date2 = $dates['value2']['formatted'];
@@ -149,6 +154,20 @@ function theme_date_display_combination($variables) {
$has_time_string = FALSE;
}
// 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));
// Show remaining days only for future events.
if ($remaining_days >= 0) {
$show_remaining_days = theme('date_display_remaining', array(
'remaining_days' => $remaining_days,
));
}
}
// No date values, display nothing.
if (empty($date1) && empty($date2)) {
$output .= '';
@@ -162,7 +181,10 @@ function theme_date_display_combination($variables) {
'attributes' => $attributes,
'rdf_mapping' => $rdf_mapping,
'add_rdf' => $add_rdf,
'microdata' => $microdata,
'add_microdata' => $add_microdata,
'dates' => $dates,
'show_remaining_days' => $show_remaining_days,
));
}
// Same day, different times, don't repeat the date but show both Start and
@@ -179,7 +201,10 @@ function theme_date_display_combination($variables) {
'attributes' => $attributes,
'rdf_mapping' => $rdf_mapping,
'add_rdf' => $add_rdf,
'microdata' => $microdata,
'add_microdata' => $add_microdata,
'dates' => $dates,
'show_remaining_days' => $show_remaining_days,
));
$replaced = str_replace($time1, $time, $date1);
$output .= theme('date_display_single', array(
@@ -200,7 +225,10 @@ function theme_date_display_combination($variables) {
'attributes' => $attributes,
'rdf_mapping' => $rdf_mapping,
'add_rdf' => $add_rdf,
'microdata' => $microdata,
'add_microdata' => $add_microdata,
'dates' => $dates,
'show_remaining_days' => $show_remaining_days,
));
}
@@ -211,7 +239,7 @@ function theme_date_display_combination($variables) {
* Template preprocess function for displaying a single date.
*/
function template_preprocess_date_display_single(&$variables) {
if ($variables['add_rdf']) {
if ($variables['add_rdf'] || !empty($variables['add_microdata'])) {
// 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.
$rdf_mapping = $variables['rdf_mapping'];
@@ -222,6 +250,24 @@ 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.
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
// could be attached to the field or to the value property.
if (!empty($variables['microdata']['#attributes']['itemprop'])) {
$variables['microdata']['value']['#attributes'] = $variables['microdata']['#attributes'];
}
// Add the machine readable time using the content attribute.
if (!empty($variables['microdata']['value']['#attributes'])) {
$variables['microdata']['value']['#attributes']['content'] = $variables['dates']['value']['formatted_iso'];
}
else {
$variables['add_microdata'] = FALSE;
}
}
}
/**
@@ -231,9 +277,17 @@ function theme_date_display_single($variables) {
$date = $variables['date'];
$timezone = $variables['timezone'];
$attributes = $variables['attributes'];
$show_remaining_days = isset($variables['show_remaining_days']) ? $variables['show_remaining_days'] : '';
// Wrap the result with the attributes.
return '<span class="date-display-single"' . drupal_attributes($attributes) . '>' . $date . $timezone . '</span>';
$output = '<span class="date-display-single"' . drupal_attributes($attributes) . '>' . $date . $timezone . '</span>';
if (!empty($variables['add_microdata'])) {
$output .= '<meta' . drupal_attributes($variables['microdata']['value']['#attributes']) . '/>';
}
// Add remaining message and return.
return $output . $show_remaining_days;
}
/**
@@ -247,7 +301,6 @@ function template_preprocess_date_display_range(&$variables) {
if ($variables['add_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.
$rdf_mapping = $variables['rdf_mapping'];
$dates = $variables['dates'];
$base_attributes = array(
'property' => array('dc:date'),
@@ -261,6 +314,17 @@ function template_preprocess_date_display_range(&$variables) {
$variables['attributes_end']['property'][$delta] = str_replace('start', 'end', $property);
}
}
// 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'];
$variables['microdata']['value2']['#attributes']['content'] = $variables['dates']['value2']['formatted_iso'];
}
else {
$variables['add_microdata'] = FALSE;
}
}
}
/**
@@ -272,12 +336,26 @@ 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'];
$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>';
// If microdata attributes for the start date property have been passed in,
// add the microdata in meta tags.
if (!empty($variables['add_microdata'])) {
$start_date .= '<meta' . drupal_attributes($variables['microdata']['value']['#attributes']) . '/>';
$end_date .= '<meta' . drupal_attributes($variables['microdata']['value2']['#attributes']) . '/>';
}
// Wrap the result with the attributes.
return t('!start-date to !end-date', array(
'!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>',
));
$output = '<span class="date-display-range">' . t('!start-date to !end-date', array(
'!start-date' => $start_date,
'!end-date' => $end_date,
)) . '</span>';
// Add remaining message and return.
return $output . $show_remaining_days;
}
/**
@@ -300,6 +378,8 @@ function theme_date_display_interval($variables) {
'end_date' => $dates['value2']['local']['object'],
'interval' => $options['interval'],
'interval_display' => $options['interval_display'],
'use_end_date' => !empty($options['use_end_date']) ?
$options['use_end_date'] : FALSE,
);
if ($return = theme('date_time_ago', $time_ago_vars)) {
@@ -320,12 +400,16 @@ function theme_date_combo($variables) {
// Group start/end items together in fieldset.
$fieldset = array(
'#title' => t($element['#title']) . ' ' . ($element['#delta'] > 0 ? intval($element['#delta'] + 1) : ''),
'#title' => field_filter_xss(t($element['#title'])) . ($element['#delta'] > 0 ? ' ' . intval($element['#delta'] + 1) : ''),
'#value' => '',
'#description' => !empty($element['#fieldset_description']) ? $element['#fieldset_description'] : '',
'#attributes' => array(),
'#description' => !empty($element['#description']) ? $element['#description'] : '',
'#attributes' => array('class' => array('date-combo')),
'#children' => $element['#children'],
);
// Add marker to required date fields.
if ($element['#required']) {
$fieldset['#title'] .= " " . theme('form_required_marker');
}
return theme('fieldset', array('element' => $fieldset));
}
@@ -340,7 +424,11 @@ function theme_date_text_parts($variables) {
$rows[] = drupal_render($element[$key]);
}
else {
$rows[] = array($part, drupal_render($element[$key][0]), drupal_render($element[$key][1]));
$rows[] = array(
$part,
drupal_render($element[$key][0]),
drupal_render($element[$key][1]),
);
}
}
if ($element['year']['#type'] == 'hidden') {
@@ -352,4 +440,49 @@ function theme_date_text_parts($variables) {
}
}
/**
* Render a date combo as a form element.
*/
function theme_date_form_element($variables) {
$element = &$variables['element'];
// Detect whether element is multiline.
$count = preg_match_all('`<(?:div|span)\b[^>]* class="[^"]*\b(?:date-no-float|date-clear)\b`', $element['#children'], $matches, PREG_OFFSET_CAPTURE);
$multiline = FALSE;
if ($count > 1) {
$multiline = TRUE;
}
elseif ($count) {
$before = substr($element['#children'], 0, $matches[0][0][1]);
if (preg_match('`<(?:div|span)\b[^>]* class="[^"]*\bdate-float\b`', $before)) {
$multiline = TRUE;
}
}
// Detect if there is more than one subfield.
$count = count(explode('<label', $element['#children'])) - 1;
if ($count == 1) {
$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>';
return theme('form_element', $variables);
}
/**
* Returns HTML for remaining message.
*/
function theme_date_display_remaining($variables) {
$remaining_days = $variables['remaining_days'];
$output = '';
$show_remaining_text = t('The upcoming date less then 1 day.');
if ($remaining_days) {
$show_remaining_text = format_plural($remaining_days, 'To event remaining 1 day', 'To event remaining @count days');
}
return '<div class="date-display-remaining"><span class="date-display-remaining">' . $show_remaining_text . '</span></div>';
}
/** @} End of addtogroup themeable */
@@ -14,15 +14,24 @@ function date_default_formatter_settings_form($field, $instance, $view_mode, $fo
$formatter = $display['type'];
$form = array();
$date_formats = date_format_type_options();
$form['format_type'] = array(
'#title' => t('Choose how users view dates and times:'),
'#type' => 'select',
'#options' => date_format_type_options(),
'#options' => $date_formats + array('custom' => t('Custom')),
'#default_value' => $settings['format_type'],
'#description' => t('To add or edit options, visit <a href="@date-time-page">Date and time settings</a>.', array('@date-time-page' => url('admin/config/regional/date-time'))),
'#weight' => 0,
);
$form['custom_date_format'] = array(
'#type' => 'textfield',
'#title' => t('Custom date format'),
'#description' => t('If "Custom", see the <a href="@url" target="_blank">PHP manual</a> for date formats. Otherwise, enter the number of different time units to display, which defaults to 2.', array('@url' => 'http://php.net/manual/function.date.php')),
'#default_value' => isset($settings['custom_date_format']) ? $settings['custom_date_format'] : '',
'#dependency' => array('edit-options-settings-format-type' => array('custom')),
);
$form['fromto'] = array(
'#title' => t('Display:'),
'#type' => 'select',
@@ -74,6 +83,12 @@ function date_default_formatter_settings_form($field, $instance, $view_mode, $fo
'#description' => t('Identify specific start and/or end dates in the format YYYY-MM-DDTHH:MM:SS, or leave blank for all available dates.'),
);
$form['show_remaining_days'] = array(
'#title' => t('Show remaining days'),
'#type' => 'checkbox',
'#default_value' => $settings['show_remaining_days'],
'#weight' => 0,
);
return $form;
}
@@ -110,6 +125,14 @@ function date_interval_formatter_settings_form($field, $instance, $view_mode, $f
'#default_value' => $settings['interval_display'],
'#weight' => 0,
);
if (!empty($field['settings']['todate'])) {
$form['use_end_date'] = array(
'#title' => t('Use End date'),
'#description' => 'Use the End date, instead of the start date',
'#type' => 'checkbox',
'#default_value' => $settings['use_end_date'],
);
}
return $form;
}
@@ -127,9 +150,11 @@ function date_default_formatter_settings_summary($field, $instance, $view_mode)
case 'date_plain':
$format = t('Plain');
break;
case 'format_interval':
$format = t('Interval');
break;
default:
if (!empty($format_types[$settings['format_type']])) {
$format = $format_types[$settings['format_type']];
@@ -148,7 +173,9 @@ function date_default_formatter_settings_summary($field, $instance, $view_mode)
'value' => t('Display Start date only'),
'value2' => t('Display End date only'),
);
$summary[] = $options[$settings['fromto']];
if (isset($options[$settings['fromto']])) {
$summary[] = $options[$settings['fromto']];
}
}
if (array_key_exists('multiple_number', $settings) && !empty($field['cardinality'])) {
@@ -159,6 +186,10 @@ function date_default_formatter_settings_summary($field, $instance, $view_mode)
));
}
if (array_key_exists('show_remaining_days', $settings)) {
$summary[] = t('Show remaining days: @value', array('@value' => ($settings['show_remaining_days'] ? 'yes' : 'no')));
}
return $summary;
}
@@ -172,7 +203,9 @@ function date_interval_formatter_settings_summary($field, $instance, $view_mode)
$display = $instance['display'][$view_mode];
$settings = $display['settings'];
$formatter = $display['type'];
$summary[] = t('Display time ago, showing @interval units.', array('@interval' => $settings['interval']));
$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));
return $summary;
}
@@ -191,7 +224,11 @@ function _date_field_instance_settings_form($field, $instance) {
'#type' => 'select',
'#title' => t('Default date'),
'#default_value' => $settings['default_value'],
'#options' => array('blank' => t('No default value'), 'now' => t('Now'), 'strtotime' => t('Relative')),
'#options' => array(
'blank' => t('No default value'),
'now' => t('Now'),
'strtotime' => t('Relative'),
),
'#weight' => 1,
'#fieldset' => 'default_values',
);
@@ -204,8 +241,11 @@ function _date_field_instance_settings_form($field, $instance) {
'#default_value' => $settings['default_value_code'],
'#states' => array(
'visible' => array(
':input[name="instance[settings][default_value]"]' => array('value' => 'strtotime')),
':input[name="instance[settings][default_value]"]' => array(
'value' => 'strtotime',
),
),
),
'#weight' => 1.1,
'#fieldset' => 'default_values',
);
@@ -213,7 +253,12 @@ function _date_field_instance_settings_form($field, $instance) {
'#type' => !empty($field['settings']['todate']) ? 'select' : 'hidden',
'#title' => t('Default end date'),
'#default_value' => $settings['default_value2'],
'#options' => array('same' => t('Same as Default date'), 'blank' => t('No default value'), 'now' => t('Now'), 'strtotime' => t('Relative')),
'#options' => array(
'same' => t('Same as Default date'),
'blank' => t('No default value'),
'now' => t('Now'),
'strtotime' => t('Relative'),
),
'#weight' => 2,
'#fieldset' => 'default_values',
);
@@ -224,8 +269,11 @@ function _date_field_instance_settings_form($field, $instance) {
'#default_value' => $settings['default_value_code2'],
'#states' => array(
'visible' => array(
':input[name="instance[settings][default_value2]"]' => array('value' => 'strtotime')),
':input[name="instance[settings][default_value2]"]' => array(
'value' => 'strtotime',
),
),
),
'#weight' => 2.1,
'#fieldset' => 'default_values',
);
@@ -244,7 +292,7 @@ function _date_field_instance_settings_form($field, $instance) {
/**
* Form validation handler for _date_field_instance_settings_form().
*/
function date_field_instance_settings_form_validate(&$form, &$form_state) {
function _date_field_instance_settings_form_validate(&$form, &$form_state) {
$settings = $form_state['values']['instance']['settings'];
if ($settings['default_value'] == 'strtotime') {
@@ -284,6 +332,7 @@ function _date_field_widget_settings_form($field, $instance) {
$formats = drupal_map_assoc($formats);
}
$now = date_example_date();
$options['site-wide'] = t('Short date format: @date', array('@date' => date_format_date($now, 'short')));
foreach ($formats as $f) {
$options[$f] = date_format_date($now, 'custom', $f);
}
@@ -369,12 +418,19 @@ function _date_field_widget_settings_form($field, $instance) {
'#weight' => 9,
);
if (in_array($widget['type'], array('date_select'))) {
$options = array('above' => t('Above'), 'within' => t('Within'), 'none' => t('None'));
$description = t("The location of date part labels, like 'Year', 'Month', or 'Day' . 'Above' displays the label as titles above each date part. 'Within' inserts the label as the first option in the select list and in blank textfields. 'None' doesn't label any of the date parts. Theme functions like 'date_part_label_year' and 'date_part_label_month' control label text.");
$options = array(
'above' => t('Above'),
'within' => t('Within'),
'none' => t('None'),
);
$description = t("The location of date part labels, like 'Year', 'Month', or 'Day' . 'Above' displays the label as titles above each date part. 'Within' inserts the label as the first option in the select list and in blank textfields. 'None' doesn't visually label any of the date parts. Theme functions like 'date_part_label_year' and 'date_part_label_month' control label text.");
}
else {
$options = array('above' => t('Above'), 'none' => t('None'));
$description = t("The location of date part labels, like 'Year', 'Month', or 'Day' . 'Above' displays the label as titles above each date part. 'None' doesn't label any of the date parts. Theme functions like 'date_part_label_year' and 'date_part_label_month' control label text.");
$options = array(
'above' => t('Above'),
'none' => t('None'),
);
$description = t("The location of date part labels, like 'Year', 'Month', or 'Day' . 'Above' displays the label as titles above each date part. 'None' doesn't visually label any of the date parts. Theme functions like 'date_part_label_year' and 'date_part_label_month' control label text.");
}
$form['advanced']['label_position'] = array(
'#type' => 'radios',
@@ -403,6 +459,13 @@ function _date_field_widget_settings_form($field, $instance) {
}
}
$form['advanced']['no_fieldset'] = array(
'#type' => 'checkbox',
'#title' => t('Render as a regular field'),
'#default_value' => !empty($settings['no_fieldset']),
'#description' => t('Whether to render this field as a regular field instead of a fieldset. The date field elements are wrapped in a fieldset by default, and may not display well without it.'),
);
$context = array(
'field' => $field,
'instance' => $instance,
@@ -415,7 +478,7 @@ function _date_field_widget_settings_form($field, $instance) {
/**
* Form validation handler for _date_field_widget_settings_form().
*/
function date_field_widget_settings_form_validate(&$form, &$form_state) {
function _date_field_widget_settings_form_validate(&$form, &$form_state) {
// The widget settings are in the wrong place in the form because of #tree on
// the top level.
$settings = $form_state['values']['instance']['widget']['settings'];
@@ -453,6 +516,9 @@ function _date_field_settings_form($field, $instance, $has_data) {
$tz_handling = $settings['tz_handling'];
$description = t('Select the date attributes to collect and store.');
if ($has_data) {
$description .= ' ' . t('Changes to date attributes only effects new or updated content.');
}
$options = date_granularity_names();
$checkbox_year = array(
'#type' => 'checkbox',
@@ -467,9 +533,10 @@ function _date_field_settings_form($field, $instance, $has_data) {
'#title' => t('Date attributes to collect'),
'#default_value' => $granularity,
'#options' => $options,
'#attributes' => array('class' => array('container-inline')),
'#attributes' => array(
'class' => array('container-inline'),
),
'#description' => $description,
'#disabled' => $has_data,
'year' => $checkbox_year,
);
@@ -499,7 +566,6 @@ function _date_field_settings_form($field, $instance, $has_data) {
'#default_value' => $tz_handling,
'#options' => date_timezone_handling_options(),
'#description' => $description,
'#disabled' => $has_data,
'#attached' => array(
'js' => array(drupal_get_path('module', 'date') . '/date_admin.js'),
),
@@ -514,7 +580,7 @@ function _date_field_settings_form($field, $instance, $has_data) {
$form['cache_enabled'] = array(
'#type' => 'checkbox',
'#title' => t('Cache dates'),
'#description' => t('Date objects can be created and cached as date fields are loaded rather than when they are displayed to improve performance.'),
'#description' => t('Date objects can be created and cached as date fields are loaded, rather than when they are displayed, to improve performance.'),
'#default_value' => !empty($settings['cache_enabled']),
'#weight' => 10,
);
@@ -527,7 +593,9 @@ function _date_field_settings_form($field, $instance, $has_data) {
'#weight' => 11,
'#states' => array(
'visible' => array(
'input[name="field[settings][cache_enabled]"]' => array('checked' => TRUE),
'input[name="field[settings][cache_enabled]"]' => array(
'checked' => TRUE,
),
),
),
);
@@ -545,7 +613,7 @@ function _date_field_settings_form($field, $instance, $has_data) {
/**
* Form validation handler for _date_field_settings_form().
*/
function date_field_settings_validate(&$form, &$form_state) {
function _date_field_settings_validate(&$form, &$form_state) {
$field = &$form_state['values']['field'];
if ($field['settings']['tz_handling'] == 'none') {
@@ -599,7 +667,7 @@ function date_timezone_handling_options() {
'site' => t("Site's time zone"),
'date' => t("Date's time zone"),
'user' => t("User's time zone"),
'utc' => 'UTC',
'utc' => 'UTC',
'none' => t('No time zone conversion'),
);
}
@@ -5,9 +5,9 @@ dependencies[] = date
package = Date/Time
core = 7.x
; Information added by drupal.org packaging script on 2012-08-13
version = "7.x-2.6"
; Information added by Drupal.org packaging script on 2017-04-07
version = "7.x-2.10"
core = "7.x"
project = "date"
datestamp = "1344850024"
datestamp = "1491562090"
@@ -31,11 +31,11 @@ function date_all_day_theme() {
'format' => NULL,
'entity_type' => NULL,
'entity' => NULL,
'view' => NULL
)
'view' => NULL,
),
),
'date_all_day_label' => array(
'variables' => array()
'variables' => array(),
),
);
@@ -91,14 +91,29 @@ function date_all_day_date_formatter_dates_alter(&$dates, $context) {
/**
* Adjust start/end date format to account for 'all day' .
*
* @param array $field, the field definition for this date field.
* @param string $which, which value to return, 'date1' or 'date2' .
* @param object $date1, a date/time object for the 'start' date.
* @param object $date2, a date/time object for the 'end' date.
* @param string $format
* @param object $entity, the node this date comes from (may be incomplete, always contains nid).
* @param object $view, the view this node comes from, if applicable.
* @return formatted date.
* @params array $field
* The field definition for this date field.
*
* @params string $which
* Which value to return, 'date1' or 'date2'.
*
* @params object $date1
* A date/time object for the 'start' date.
*
* @params object $date2
* A date/time object for the 'end' date.
*
* @params string $format
* A date/time format
*
* @params object $entity
* The node this date comes from (may be incomplete, always contains nid).
*
* @params object $view
* The view this node comes from, if applicable.
*
* @return string
* Formatted date.
*/
function theme_date_all_day($vars) {
$field = $vars['field'];
@@ -135,23 +150,32 @@ function theme_date_all_day($vars) {
}
return trim(date_format_date($$which, 'custom', $format) . $suffix);
}
/**
* 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')) . ')';
}
/**
* Determine if a Start/End date combination qualify as 'All day'.
*
* @param array $field, the field definition 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 TRUE or FALSE.
* @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) {
if (empty($date1) || !is_object($date1)) {
@@ -167,7 +191,6 @@ function date_all_day_field($field, $instance, $date1, $date2 = NULL) {
$granularity = date_granularity_precision($field['settings']['granularity']);
$increment = isset($instance['widget']['settings']['increment']) ? $instance['widget']['settings']['increment'] : 1;
return date_is_all_day(date_format($date1, DATE_FORMAT_DATETIME), date_format($date2, DATE_FORMAT_DATETIME), $granularity, $increment);
}
/**
@@ -222,7 +245,8 @@ function date_all_day_date_combo_process_alter(&$element, &$form_state, $context
function date_all_day_date_text_process_alter(&$element, &$form_state, $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 of the input value.
// 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.
}
}
@@ -234,10 +258,11 @@ function date_all_day_date_text_process_alter(&$element, &$form_state, $context)
*/
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.
// 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) {
foreach (array('hour', 'minute', 'second', 'ampm') as $field) {
if (array_key_exists($field, $element)) {
$element[$field]['#states'] = array(
'visible' => array(
@@ -255,7 +280,8 @@ function date_all_day_date_select_process_alter(&$element, &$form_state, $contex
*/
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.
// 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(
@@ -272,7 +298,8 @@ function date_all_day_date_popup_process_alter(&$element, &$form_state, $context
* 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.
// 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);
}
@@ -284,7 +311,8 @@ function date_all_day_date_text_pre_validate_alter(&$element, &$form_state, &$in
* 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.
// 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);
}
@@ -296,13 +324,16 @@ function date_all_day_date_select_pre_validate_alter(&$element, &$form_state, &$
* 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.
// 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 to check if the all day flag is set on the parent of an
* 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.
*/
@@ -332,7 +363,8 @@ function date_all_day_date_combo_pre_validate_alter(&$element, &$form_state, $co
$field = $context['field'];
// If we have an all day flag on this date and the time is empty,
// change the format to match the input value so we don't get validation errors.
// change the format to match the input value
// so we don't get validation errors.
$element['#date_is_all_day'] = TRUE;
$element['value']['#date_format'] = date_part_format('date', $element['value']['#date_format']);
if (!empty($field['settings']['todate'])) {
@@ -344,29 +376,29 @@ 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.
* 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.
if (!empty($context['element']['#date_is_all_day'])) {
$date->setTime(0, 0, 0);
}
// 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);
}
}
/**
* 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.
* 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.
if (!empty($context['element']['#date_is_all_day'])) {
$date->setTime(0, 0, 0);
}
// 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);
}
}
/**
@@ -15,9 +15,11 @@
.container-inline-date > .form-item {
display: inline-block;
margin-right: 0.5em; /* LTR */
margin-bottom: 10px;
vertical-align: top;
}
fieldset.date-combo .container-inline-date > .form-item {
margin-bottom: 10px;
}
.container-inline-date .form-item .form-item {
float: left; /* LTR */
}
@@ -52,9 +54,11 @@
/* The exposed Views form doesn't need some of these styles */
.container-inline-date .date-padding {
padding: 10px;
float: left;
}
fieldset.date-combo .container-inline-date .date-padding {
padding: 10px;
}
.views-exposed-form .container-inline-date .date-padding {
padding: 0;
}
@@ -116,7 +120,7 @@ span.date-display-end {
}
/* Add space between the date and time portions of the date_select widget. */
.form-type-date-select .form-type-select[class$=hour] {
.form-type-date-select .form-type-select[class*=hour] {
margin-left: .75em; /* LTR */
}
@@ -173,6 +177,10 @@ div.date-calendar-day span.year {
padding: 2px;
}
.date-form-element-content-multiline {
padding: 10px;
border: 1px solid #CCC;
}
/* Admin styling */
.form-item.form-item-instance-widget-settings-input-format-custom,
.form-item.form-item-field-settings-enddate-required {
@@ -10,112 +10,112 @@
*/
function _date_timezone_replacement($old) {
$replace = array(
'Brazil/Acre' => 'America/Rio_Branco',
'Brazil/DeNoronha' => 'America/Noronha',
'Brazil/East' => 'America/Recife',
'Brazil/West' => 'America/Manaus',
'Canada/Atlantic' => 'America/Halifax',
'Canada/Central' => 'America/Winnipeg',
'Canada/East-Saskatchewan' => 'America/Regina',
'Canada/Eastern' => 'America/Toronto',
'Canada/Mountain' => 'America/Edmonton',
'Canada/Newfoundland' => 'America/St_Johns',
'Canada/Pacific' => 'America/Vancouver',
'Canada/Saskatchewan' => 'America/Regina',
'Canada/Yukon' => 'America/Whitehorse',
'CET' => 'Europe/Berlin',
'Chile/Continental' => 'America/Santiago',
'Chile/EasterIsland' => 'Pacific/Easter',
'CST6CDT' => 'America/Chicago',
'Cuba' => 'America/Havana',
'EET' => 'Europe/Bucharest',
'Egypt' => 'Africa/Cairo',
'Eire' => 'Europe/Belfast',
'EST' => 'America/New_York',
'EST5EDT' => 'America/New_York',
'GB' => 'Europe/London',
'GB-Eire' => 'Europe/Belfast',
'Etc/GMT' => 'UTC',
'Etc/GMT+0' => 'UTC',
'Etc/GMT+1' => 'UTC',
'Etc/GMT+10' => 'UTC',
'Etc/GMT+11' => 'UTC',
'Etc/GMT+12' => 'UTC',
'Etc/GMT+2' => 'UTC',
'Etc/GMT+3' => 'UTC',
'Etc/GMT+4' => 'UTC',
'Etc/GMT+5' => 'UTC',
'Etc/GMT+6' => 'UTC',
'Etc/GMT+7' => 'UTC',
'Etc/GMT+8' => 'UTC',
'Etc/GMT+9' => 'UTC',
'Etc/GMT-0' => 'UTC',
'Etc/GMT-1' => 'UTC',
'Etc/GMT-10' => 'UTC',
'Etc/GMT-11' => 'UTC',
'Etc/GMT-12' => 'UTC',
'Etc/GMT-13' => 'UTC',
'Etc/GMT-14' => 'UTC',
'Etc/GMT-2' => 'UTC',
'Etc/GMT-3' => 'UTC',
'Etc/GMT-4' => 'UTC',
'Etc/GMT-5' => 'UTC',
'Etc/GMT-6' => 'UTC',
'Etc/GMT-7' => 'UTC',
'Etc/GMT-8' => 'UTC',
'Etc/GMT-9' => 'UTC',
'Etc/GMT0' => 'UTC',
'Etc/Greenwich' => 'UTC',
'Etc/UCT' => 'UTC',
'Etc/Universal' => 'UTC',
'Etc/UTC' => 'UTC',
'Etc/Zulu' => 'UTC',
'Factory' => 'UTC',
'GMT' => 'UTC',
'GMT+0' => 'UTC',
'GMT-0' => 'UTC',
'GMT0' => 'UTC',
'Hongkong' => 'Asia/Hong_Kong',
'HST' => 'Pacific/Honolulu',
'Iceland' => 'Atlantic/Reykjavik',
'Iran' => 'Asia/Tehran',
'Israel' => 'Asia/Tel_Aviv',
'Jamaica' => 'America/Jamaica',
'Japan' => 'Asia/Tokyo',
'Kwajalein' => 'Pacific/Kwajalein',
'Libya' => 'Africa/Tunis',
'MET' => 'Europe/Budapest',
'Mexico/BajaNorte' => 'America/Tijuana',
'Mexico/BajaSur' => 'America/Mazatlan',
'Mexico/General' => 'America/Mexico_City',
'MST' => 'America/Boise',
'MST7MDT' => 'America/Boise',
'Navajo' => 'America/Phoenix',
'NZ' => 'Pacific/Auckland',
'NZ-CHAT' => 'Pacific/Chatham',
'Poland' => 'Europe/Warsaw',
'Portugal' => 'Europe/Lisbon',
'PRC' => 'Asia/Chongqing',
'PST8PDT' => 'America/Los_Angeles',
'ROC' => 'Asia/Taipei',
'ROK' => 'Asia/Seoul',
'Singapore' => 'Asia/Singapore',
'Turkey' => 'Europe/Istanbul',
'US/Alaska' => 'America/Anchorage',
'US/Aleutian' => 'America/Adak',
'US/Arizona' => 'America/Phoenix',
'US/Central' => 'America/Chicago',
'US/East-Indiana' => 'America/Indianapolis',
'US/Eastern' => 'America/New_York',
'US/Hawaii' => 'Pacific/Honolulu',
'US/Indiana-Starke' => 'America/Indiana/Knox',
'US/Michigan' => 'America/Detroit',
'US/Mountain' => 'America/Boise',
'US/Pacific' => 'America/Los_Angeles',
'US/Pacific-New' => 'America/Los_Angeles',
'US/Samoa' => 'Pacific/Samoa',
'W-SU' => 'Europe/Moscow',
'WET' => 'Europe/Paris',
'Brazil/Acre' => 'America/Rio_Branco',
'Brazil/DeNoronha' => 'America/Noronha',
'Brazil/East' => 'America/Recife',
'Brazil/West' => 'America/Manaus',
'Canada/Atlantic' => 'America/Halifax',
'Canada/Central' => 'America/Winnipeg',
'Canada/East-Saskatchewan' => 'America/Regina',
'Canada/Eastern' => 'America/Toronto',
'Canada/Mountain' => 'America/Edmonton',
'Canada/Newfoundland' => 'America/St_Johns',
'Canada/Pacific' => 'America/Vancouver',
'Canada/Saskatchewan' => 'America/Regina',
'Canada/Yukon' => 'America/Whitehorse',
'CET' => 'Europe/Berlin',
'Chile/Continental' => 'America/Santiago',
'Chile/EasterIsland' => 'Pacific/Easter',
'CST6CDT' => 'America/Chicago',
'Cuba' => 'America/Havana',
'EET' => 'Europe/Bucharest',
'Egypt' => 'Africa/Cairo',
'Eire' => 'Europe/Belfast',
'EST' => 'America/New_York',
'EST5EDT' => 'America/New_York',
'GB' => 'Europe/London',
'GB-Eire' => 'Europe/Belfast',
'Etc/GMT' => 'UTC',
'Etc/GMT+0' => 'UTC',
'Etc/GMT+1' => 'UTC',
'Etc/GMT+10' => 'UTC',
'Etc/GMT+11' => 'UTC',
'Etc/GMT+12' => 'UTC',
'Etc/GMT+2' => 'UTC',
'Etc/GMT+3' => 'UTC',
'Etc/GMT+4' => 'UTC',
'Etc/GMT+5' => 'UTC',
'Etc/GMT+6' => 'UTC',
'Etc/GMT+7' => 'UTC',
'Etc/GMT+8' => 'UTC',
'Etc/GMT+9' => 'UTC',
'Etc/GMT-0' => 'UTC',
'Etc/GMT-1' => 'UTC',
'Etc/GMT-10' => 'UTC',
'Etc/GMT-11' => 'UTC',
'Etc/GMT-12' => 'UTC',
'Etc/GMT-13' => 'UTC',
'Etc/GMT-14' => 'UTC',
'Etc/GMT-2' => 'UTC',
'Etc/GMT-3' => 'UTC',
'Etc/GMT-4' => 'UTC',
'Etc/GMT-5' => 'UTC',
'Etc/GMT-6' => 'UTC',
'Etc/GMT-7' => 'UTC',
'Etc/GMT-8' => 'UTC',
'Etc/GMT-9' => 'UTC',
'Etc/GMT0' => 'UTC',
'Etc/Greenwich' => 'UTC',
'Etc/UCT' => 'UTC',
'Etc/Universal' => 'UTC',
'Etc/UTC' => 'UTC',
'Etc/Zulu' => 'UTC',
'Factory' => 'UTC',
'GMT' => 'UTC',
'GMT+0' => 'UTC',
'GMT-0' => 'UTC',
'GMT0' => 'UTC',
'Hongkong' => 'Asia/Hong_Kong',
'HST' => 'Pacific/Honolulu',
'Iceland' => 'Atlantic/Reykjavik',
'Iran' => 'Asia/Tehran',
'Israel' => 'Asia/Tel_Aviv',
'Jamaica' => 'America/Jamaica',
'Japan' => 'Asia/Tokyo',
'Kwajalein' => 'Pacific/Kwajalein',
'Libya' => 'Africa/Tunis',
'MET' => 'Europe/Budapest',
'Mexico/BajaNorte' => 'America/Tijuana',
'Mexico/BajaSur' => 'America/Mazatlan',
'Mexico/General' => 'America/Mexico_City',
'MST' => 'America/Boise',
'MST7MDT' => 'America/Boise',
'Navajo' => 'America/Phoenix',
'NZ' => 'Pacific/Auckland',
'NZ-CHAT' => 'Pacific/Chatham',
'Poland' => 'Europe/Warsaw',
'Portugal' => 'Europe/Lisbon',
'PRC' => 'Asia/Chongqing',
'PST8PDT' => 'America/Los_Angeles',
'ROC' => 'Asia/Taipei',
'ROK' => 'Asia/Seoul',
'Singapore' => 'Asia/Singapore',
'Turkey' => 'Europe/Istanbul',
'US/Alaska' => 'America/Anchorage',
'US/Aleutian' => 'America/Adak',
'US/Arizona' => 'America/Phoenix',
'US/Central' => 'America/Chicago',
'US/East-Indiana' => 'America/Indianapolis',
'US/Eastern' => 'America/New_York',
'US/Hawaii' => 'Pacific/Honolulu',
'US/Indiana-Starke' => 'America/Indiana/Knox',
'US/Michigan' => 'America/Detroit',
'US/Mountain' => 'America/Boise',
'US/Pacific' => 'America/Los_Angeles',
'US/Pacific-New' => 'America/Los_Angeles',
'US/Samoa' => 'Pacific/Samoa',
'W-SU' => 'Europe/Moscow',
'WET' => 'Europe/Paris',
);
if (array_key_exists($old, $replace)) {
return $replace[$old];
@@ -9,9 +9,9 @@ stylesheets[all][] = date.css
files[] = date_api.module
files[] = date_api_sql.inc
; Information added by drupal.org packaging script on 2012-08-13
version = "7.x-2.6"
; Information added by Drupal.org packaging script on 2017-04-07
version = "7.x-2.10"
core = "7.x"
project = "date"
datestamp = "1344850024"
datestamp = "1491562090"
@@ -96,7 +96,7 @@ function date_api_uninstall() {
'date_php_min_year',
'date_db_tz_support',
'date_api_use_iso8601',
);
);
foreach ($variables as $variable) {
variable_del($variable);
}
@@ -118,8 +118,9 @@ function date_api_update_last_removed() {
}
/**
* Move old date format data to new date format tables, and delete the old
* tables. Insert only values that don't already exist in the new tables, in
* Move old date format to new date format tables,and delete the old tables.
*
* Insert only values that don't already exist in the new tables, in
* case new version of those custom values have already been created.
*/
function date_api_update_7000() {
@@ -59,16 +59,16 @@ function date_help($path, $arg) {
}
if (module_exists('date_tools')) {
$output .= '<h3>Date Tools</h3>' . t('Dates and calendars can be complicated to set up. The !date_wizard makes it easy to create a simple date content type and with a date field. ', array('!date_wizard' => l(t('Date wizard'), 'admin/config/date/tools/date_wizard')));
$output .= '<h3>Date Tools</h3>' . t('Dates and calendars can be complicated to set up. The !date_wizard makes it easy to create a simple date content type and with a date field.', array('!date_wizard' => l(t('Date wizard'), 'admin/config/date/tools/date_wizard')));
}
else {
$output .= '<h3>Date Tools</h3>' . t('Dates and calendars can be complicated to set up. If you enable the Date Tools module, it provides a Date Wizard that makes it easy to create a simple date content type with a date field. ');
$output .= '<h3>Date Tools</h3>' . t('Dates and calendars can be complicated to set up. If you enable the Date Tools module, it provides a Date Wizard that makes it easy to create a simple date content type with a date field.');
}
$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;
break;
}
}
@@ -101,7 +101,7 @@ function date_api_status() {
$value = variable_get('date_format_medium');
if (isset($value)) {
$now = date_now();
$success_messages[] = $t('The medium date format type has been set to to @value. You may find it helpful to add new format types like Date, Time, Month, or Year, with appropriate formats, at <a href="@regional_date_time">Date and time</a> settings.', array('@value' => $now->format($value), '@regional_date_time' => url('admin/config/regional/date-time')));
$success_messages[] = $t('The medium date format type has been set to @value. You may find it helpful to add new format types like Date, Time, Month, or Year, with appropriate formats, at <a href="@regional_date_time">Date and time</a> settings.', array('@value' => $now->format($value), '@regional_date_time' => url('admin/config/regional/date-time')));
}
else {
$error_messages[] = $t('The Date API requires that you set up the <a href="@regional_date_time">system date formats</a> to function correctly.', array('@regional_date_time' => url('admin/config/regional/date-time')));
@@ -143,7 +143,15 @@ function date_api_menu() {
class DateObject extends DateTime {
public $granularity = array();
public $errors = array();
protected static $allgranularity = array('year', 'month', 'day', 'hour', 'minute', 'second', 'timezone');
protected static $allgranularity = array(
'year',
'month',
'day',
'hour',
'minute',
'second',
'timezone'
);
private $serializedTime;
private $serializedTimezone;
@@ -276,18 +284,17 @@ class DateObject extends DateTime {
$this->setGranularityFromTime($time, $tz);
}
}
// If this tz was given as just an offset or the timezone
// was invalid, we need to do some tweaking.
// If we haven't got a valid timezone name yet, we need to set one or
// we will get undefined index errors.
// This can happen if $time had an offset or no timezone.
if (!$this->getTimezone() || !preg_match('/[a-zA-Z]/', $this->getTimezone()->getName())) {
// If the timezone name is an offset and the original
// $tz has a name, use it. This happens if you pass in
// a date string with an offset along with a specific timezone name.
if (!preg_match('/[a-zA-Z]/', $this->getTimezone()->getName()) && preg_match('/[a-zA-Z]/', $tz->getName())) {
// If the original $tz has a name, use it.
if (preg_match('/[a-zA-Z]/', $tz->getName())) {
$this->setTimezone($tz);
}
// If we get this far, we have no information about the timezone name,
// but we will get undefined index errors without any name.
// 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.');
@@ -403,7 +410,7 @@ class DateObject extends DateTime {
* A single date part.
*/
public function removeGranularity($g) {
if ($key = array_search($g, $this->granularity)) {
if (($key = array_search($g, $this->granularity)) !== FALSE) {
unset($this->granularity[$key]);
}
}
@@ -459,8 +466,39 @@ class DateObject extends DateTime {
$true = $this->hasGranularity() && (!$granularity || $flexible || $this->hasGranularity($granularity));
if (!$true && $granularity) {
foreach ((array) $granularity as $part) {
if (!$this->hasGranularity($part)) {
$this->errors[$part] = t("The @part is missing.", array('@part' => $part));
if (!$this->hasGranularity($part) && in_array($part, array(
'second',
'minute',
'hour',
'day',
'month',
'year')
)) {
switch ($part) {
case 'second':
$this->errors[$part] = t('The second is missing.');
break;
case 'minute':
$this->errors[$part] = t('The minute is missing.');
break;
case 'hour':
$this->errors[$part] = t('The hour is missing.');
break;
case 'day':
$this->errors[$part] = t('The day is missing.');
break;
case 'month':
$this->errors[$part] = t('The month is missing.');
break;
case 'year':
$this->errors[$part] = t('The year is missing.');
break;
}
}
}
}
@@ -519,7 +557,14 @@ class DateObject extends DateTime {
$temp = date_parse($time);
// Special case for 'now'.
if ($time == 'now') {
$this->granularity = array('year', 'month', 'day', 'hour', 'minute', 'second');
$this->granularity = array(
'year',
'month',
'day',
'hour',
'minute',
'second',
);
}
else {
// This PHP date_parse() method currently doesn't have resolution down to
@@ -574,7 +619,7 @@ class DateObject extends DateTime {
$regex2 = preg_replace($patterns, $repl2, $format_regexp, 1);
$regex2 = str_replace('A', '(AM|PM)', $regex2);
$regex2 = str_replace('a', '(am|pm)', $regex2);
preg_match('`^' . $regex2 . '$`', $date, $values);
preg_match('`^' . $regex2 . '$`u', $date, $values);
array_shift($values);
// If we did not find all the values for the patterns in the format, abort.
if (count($letters) != count($values)) {
@@ -582,7 +627,14 @@ class DateObject extends DateTime {
return FALSE;
}
$this->granularity = array();
$final_date = array('hour' => 0, 'minute' => 0, 'second' => 0, 'month' => 1, 'day' => 1, 'year' => 0);
$final_date = array(
'hour' => 0,
'minute' => 0,
'second' => 0,
'month' => 1,
'day' => 1,
'year' => 0,
);
foreach ($letters as $i => $letter) {
$value = $values[$i];
switch ($letter) {
@@ -591,21 +643,25 @@ class DateObject extends DateTime {
$final_date['day'] = intval($value);
$this->addGranularity('day');
break;
case 'n':
case 'm':
$final_date['month'] = intval($value);
$this->addGranularity('month');
break;
case 'F':
$array_month_long = array_flip(date_month_names());
$final_date['month'] = array_key_exists($value, $array_month_long) ? $array_month_long[$value] : -1;
$this->addGranularity('month');
break;
case 'M':
$array_month = array_flip(date_month_names_abbr());
$final_date['month'] = array_key_exists($value, $array_month) ? $array_month[$value] : -1;
$this->addGranularity('month');
break;
case 'Y':
$final_date['year'] = $value;
$this->addGranularity('year');
@@ -613,16 +669,19 @@ class DateObject extends DateTime {
$this->errors['year'] = t('The year is invalid. Please check that entry includes four digits.');
}
break;
case 'y':
$year = $value;
// If no century, we add the current one ("06" => "2006").
$final_date['year'] = str_pad($year, 4, substr(date("Y"), 0, 2), STR_PAD_LEFT);
$this->addGranularity('year');
break;
case 'a':
case 'A':
$ampm = strtolower($value);
break;
case 'g':
case 'h':
case 'G':
@@ -630,14 +689,17 @@ class DateObject extends DateTime {
$final_date['hour'] = intval($value);
$this->addGranularity('hour');
break;
case 'i':
$final_date['minute'] = intval($value);
$this->addGranularity('minute');
break;
case 's':
$final_date['second'] = intval($value);
$this->addGranularity('second');
break;
case 'U':
parent::__construct($value, $tz ? $tz : new DateTimeZone("UTC"));
$this->addGranularity('year');
@@ -647,7 +709,7 @@ class DateObject extends DateTime {
$this->addGranularity('minute');
$this->addGranularity('second');
return $this;
break;
}
}
if (isset($ampm) && $ampm == 'pm' && $final_date['hour'] < 12) {
@@ -740,10 +802,24 @@ class DateObject extends DateTime {
// date or we will get date slippage, i.e. a value of 2011-00-00 will get
// interpreted as November of 2010 by PHP.
if ($full) {
$arr += array('year' => 0, 'month' => 1, 'day' => 1, 'hour' => 0, 'minute' => 0, 'second' => 0);
$arr += array(
'year' => 0,
'month' => 1,
'day' => 1,
'hour' => 0,
'minute' => 0,
'second' => 0,
);
}
else {
$arr += array('year' => '', 'month' => '', 'day' => '', 'hour' => '', 'minute' => '', 'second' => '');
$arr += array(
'year' => '',
'month' => '',
'day' => '',
'hour' => '',
'minute' => '',
'second' => '',
);
}
$datetime = '';
if ($arr['year'] !== '') {
@@ -821,28 +897,27 @@ class DateObject extends DateTime {
case 'year':
$fallback = $now->format('Y');
return !is_int($value) || empty($value) || $value < variable_get('date_min_year', 1) || $value > variable_get('date_max_year', 4000) ? $fallback : $value;
break;
case 'month':
$fallback = $default == 'first' ? 1 : $now->format('n');
return !is_int($value) || empty($value) || $value <= 0 || $value > 12 ? $fallback : $value;
break;
case 'day':
$fallback = $default == 'first' ? 1 : $now->format('j');
$max_day = isset($year) && isset($month) ? date_days_in_month($year, $month) : 31;
return !is_int($value) || empty($value) || $value <= 0 || $value > $max_day ? $fallback : $value;
break;
case 'hour':
$fallback = $default == 'first' ? 0 : $now->format('G');
return !is_int($value) || $value < 0 || $value > 23 ? $fallback : $value;
break;
case 'minute':
$fallback = $default == 'first' ? 0 : $now->format('i');
return !is_int($value) || $value < 0 || $value > 59 ? $fallback : $value;
break;
case 'second':
$fallback = $default == 'first' ? 0 : $now->format('s');
return !is_int($value) || $value < 0 || $value > 59 ? $fallback : $value;
break;
}
}
@@ -880,18 +955,23 @@ class DateObject extends DateTime {
case 'year':
$errors['year'] = t('The year is invalid.');
break;
case 'month':
$errors['month'] = t('The month is invalid.');
break;
case 'day':
$errors['day'] = t('The day is invalid.');
break;
case 'hour':
$errors['hour'] = t('The hour is invalid.');
break;
case 'minute':
$errors['minute'] = t('The minute is invalid.');
break;
case 'second':
$errors['second'] = t('The second is invalid.');
break;
@@ -911,7 +991,7 @@ class DateObject extends DateTime {
* The stop date.
* @param string $measure
* (optional) A granularity date part. Defaults to 'seconds'.
* @param boolean $absolute
* @param bool $absolute
* (optional) Indicate whether the absolute value of the difference should
* be returned or if the sign should be retained. Defaults to TRUE.
*/
@@ -937,10 +1017,13 @@ class DateObject extends DateTime {
// The easy cases first.
case 'seconds':
return $diff;
case 'minutes':
return $diff / 60;
case 'hours':
return $diff / 3600;
case 'years':
return $year_diff;
@@ -951,6 +1034,11 @@ class DateObject extends DateTime {
if ($year_diff == 0) {
return intval($item2 - $item1);
}
elseif ($year_diff < 0) {
$item_diff = 0 - $item1;
$item_diff -= intval((abs($year_diff) - 1) * 12);
return $item_diff - (12 - $item2);
}
else {
$item_diff = 12 - $item1;
$item_diff += intval(($year_diff - 1) * 12);
@@ -965,6 +1053,14 @@ class DateObject extends DateTime {
if ($year_diff == 0) {
return intval($item2 - $item1);
}
elseif ($year_diff < 0) {
$item_diff = 0 - $item1;
for ($i = 1; $i < abs($year_diff); $i++) {
date_modify($date1, '-1 year');
$item_diff -= date_days_in_year($date1);
}
return $item_diff - (date_days_in_year($date2) - $item2);
}
else {
$item_diff = date_days_in_year($date1) - $item1;
for ($i = 1; $i < $year_diff; $i++) {
@@ -978,9 +1074,12 @@ class DateObject extends DateTime {
case 'weeks':
$week_diff = date_format($date2, 'W') - date_format($date1, 'W');
$year_diff = date_format($date2, 'o') - date_format($date1, 'o');
for ($i = 1; $i <= $year_diff; $i++) {
date_modify($date1, '+1 year');
$week_diff += date_iso_weeks_in_year($date1);
$sign = ($year_diff < 0) ? -1 : 1;
for ($i = 1; $i <= abs($year_diff); $i++) {
date_modify($date1, (($sign > 0) ? '+' : '-') . '1 year');
$week_diff += (date_iso_weeks_in_year($date1) * $sign);
}
return $week_diff;
}
@@ -1026,10 +1125,13 @@ function date_type_format($type) {
switch ($type) {
case DATE_ISO:
return DATE_FORMAT_ISO;
case DATE_UNIX:
return DATE_FORMAT_UNIX;
case DATE_DATETIME:
return DATE_FORMAT_DATETIME;
case DATE_ICAL:
return DATE_FORMAT_ICAL;
}
@@ -1085,7 +1187,7 @@ function date_month_names($required = FALSE) {
}
/**
* Constructs a translated array of month name abbreviations
* Constructs a translated array of month name abbreviations.
*
* @param bool $required
* (optional) If FALSE, the returned array will include a blank value.
@@ -1177,9 +1279,11 @@ function date_week_days_abbr($required = FALSE, $refresh = TRUE, $length = 3) {
case 1:
$context = 'day_abbr1';
break;
case 2:
$context = 'day_abbr2';
break;
default:
$context = '';
break;
@@ -1214,10 +1318,10 @@ function date_week_days_ordered($weekdays) {
/**
* Constructs an array of years.
*
* @param int $min
* The minimum year in the array.
* @param int $max
* The maximum year in the array.
* @param int $start
* The start year in the array.
* @param int $end
* The end year in the array.
* @param bool $required
* (optional) If FALSE, the returned array will include a blank value.
* Defaults to FALSE.
@@ -1225,16 +1329,16 @@ function date_week_days_ordered($weekdays) {
* @return array
* An array of years in the selected range.
*/
function date_years($min = 0, $max = 0, $required = FALSE) {
function date_years($start = 0, $end = 0, $required = FALSE) {
// Ensure $min and $max are valid values.
if (empty($min)) {
$min = intval(date('Y', REQUEST_TIME) - 3);
if (empty($start)) {
$start = intval(date('Y', REQUEST_TIME) - 3);
}
if (empty($max)) {
$max = intval(date('Y', REQUEST_TIME) + 3);
if (empty($end)) {
$end = intval(date('Y', REQUEST_TIME) + 3);
}
$none = array(0 => '');
return !$required ? $none + drupal_map_assoc(range($min, $max)) : drupal_map_assoc(range($min, $max));
return !$required ? $none + drupal_map_assoc(range($start, $end)) : drupal_map_assoc(range($start, $end));
}
/**
@@ -1440,7 +1544,14 @@ function date_granularity_names() {
* An array of date parts.
*/
function date_granularity_sorted($granularity) {
return array_intersect(array('year', 'month', 'day', 'hour', 'minute', 'second'), $granularity);
return array_intersect(array(
'year',
'month',
'day',
'hour',
'minute',
'second',
), $granularity);
}
/**
@@ -1458,14 +1569,19 @@ function date_granularity_array_from_precision($precision) {
switch ($precision) {
case 'year':
return array_slice($granularity_array, -6, 1);
case 'month':
return array_slice($granularity_array, -6, 2);
case 'day':
return array_slice($granularity_array, -6, 3);
case 'hour':
return array_slice($granularity_array, -6, 4);
case 'minute':
return array_slice($granularity_array, -6, 5);
default:
return $granularity_array;
}
@@ -1499,14 +1615,19 @@ function date_granularity_format($granularity) {
switch ($granularity) {
case 'year':
return substr($format, 0, 1);
case 'month':
return substr($format, 0, 3);
case 'day':
return substr($format, 0, 5);
case 'hour';
return substr($format, 0, 7);
case 'minute':
return substr($format, 0, 9);
default:
return $format;
}
@@ -1623,40 +1744,51 @@ function date_format_date($date, $type = 'medium', $format = '', $langcode = NUL
case 'l':
$datestring .= t($date->format('l'), array(), array('context' => '', 'langcode' => $langcode));
break;
case 'D':
$datestring .= t($date->format('D'), array(), array('context' => '', 'langcode' => $langcode));
break;
case 'F':
$datestring .= t($date->format('F'), array(), array('context' => 'Long month name', 'langcode' => $langcode));
break;
case 'M':
$datestring .= t($date->format('M'), array(), array('langcode' => $langcode));
break;
case 'A':
case 'a':
$datestring .= t($date->format($c), array(), array('context' => 'ampm', 'langcode' => $langcode));
break;
// The timezone name translations can use t().
case 'e':
case 'T':
$datestring .= t($date->format($c));
break;
// Remaining date parts need no translation.
case 'O':
$datestring .= sprintf('%s%02d%02d', (date_offset_get($date) < 0 ? '-' : '+'), abs(date_offset_get($date) / 3600), abs(date_offset_get($date) % 3600) / 60);
break;
case 'P':
$datestring .= sprintf('%s%02d:%02d', (date_offset_get($date) < 0 ? '-' : '+'), abs(date_offset_get($date) / 3600), abs(date_offset_get($date) % 3600) / 60);
break;
case 'Z':
$datestring .= date_offset_get($date);
break;
case '\\':
$datestring .= $format[++$i];
break;
case 'r':
$datestring .= date_format_date($date, 'custom', 'D, d M Y H:i:s O', $langcode);
$datestring .= date_format_date($date, 'custom', 'D, d M Y H:i:s O', 'en');
break;
default:
if (strpos('BdcgGhHiIjLmnNosStTuUwWYyz', $c) !== FALSE) {
$datestring .= $date->format($c);
@@ -1701,15 +1833,38 @@ function date_format_interval($date, $granularity = 2, $display_ago = TRUE) {
/**
* A date object for the current time.
*
* @param object $timezone
* (optional) Optionally force time to a specific timezone, defaults to user
* timezone, if set, otherwise site timezone. Defaults to NULL.
* @param object|string|null $timezone
* (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.
*
* @return object
* The current time as a date object.
*/
function date_now($timezone = NULL) {
return new DateObject('now', $timezone);
function date_now($timezone = NULL, $reset = FALSE) {
$static_var = __FUNCTION__ . $timezone;
if ($timezone instanceof DateTimeZone) {
$static_var = __FUNCTION__ . $timezone->getName();
}
if ($reset) {
drupal_static_reset($static_var);
}
$now = &drupal_static($static_var);
if (!isset($now)) {
$now = new DateObject('now', $timezone);
}
// Avoid unexpected manipulation of cached $now object
// by subsequent code execution
// @see https://drupal.org/node/2261395
$clone = clone $now;
return $clone;
}
/**
@@ -1771,7 +1926,12 @@ function date_days_in_month($year, $month) {
// Pick a day in the middle of the month to avoid timezone shifts.
$datetime = date_pad($year, 4) . '-' . date_pad($month) . '-15 00:00:00';
$date = new DateObject($datetime);
return $date->format('t');
if ($date->errors) {
return FALSE;
}
else {
return $date->format('t');
}
}
/**
@@ -1780,7 +1940,7 @@ function date_days_in_month($year, $month) {
* @param mixed $date
* (optional) The current date object, or a date string. Defaults to NULL.
*
* @return integer
* @return int
* The number of days in the year.
*/
function date_days_in_year($date = NULL) {
@@ -1809,7 +1969,7 @@ function date_days_in_year($date = NULL) {
* @param mixed $date
* (optional) The current date object, or a date string. Defaults to NULL.
*
* @return integer
* @return int
* The number of ISO weeks in a year.
*/
function date_iso_weeks_in_year($date = NULL) {
@@ -1901,7 +2061,7 @@ function date_week_range($week, $year) {
// Move forwards to the last day of the week.
$max_date = clone($min_date);
date_modify($max_date, '+7 days');
date_modify($max_date, '+6 days');
if (date_format($min_date, 'Y') != $year) {
$min_date = new DateObject($year . '-01-01 00:00:00');
@@ -1926,6 +2086,9 @@ function date_iso_week_range($week, $year) {
date_timezone_set($min_date, date_default_timezone_object());
// Find the first day of the first ISO week in the year.
// If it's already a Monday, date_modify won't add a Monday,
// it will remain the same day. So add a Sunday first, then a Monday.
date_modify($min_date, '+1 Sunday');
date_modify($min_date, '+1 Monday');
// Jump ahead to the desired week for the beginning of the week range.
@@ -1935,7 +2098,7 @@ 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, '+7 days');
date_modify($max_date, '+6 days');
return array($min_date, $max_date);
}
@@ -2043,7 +2206,8 @@ function date_has_time($granularity) {
if (!is_array($granularity)) {
$granularity = array();
}
return (bool) count(array_intersect($granularity, array('hour', 'minute', 'second')));
$options = array('hour', 'minute', 'second');
return (bool) count(array_intersect($granularity, $options));
}
/**
@@ -2059,7 +2223,8 @@ function date_has_date($granularity) {
if (!is_array($granularity)) {
$granularity = array();
}
return (bool) count(array_intersect($granularity, array('year', 'month', 'day')));
$options = array('year', 'month', 'day');
return (bool) count(array_intersect($granularity, $options));
}
/**
@@ -2077,8 +2242,10 @@ function date_part_format($part, $format) {
switch ($part) {
case 'date':
return date_limit_format($format, array('year', 'month', 'day'));
case 'time':
return date_limit_format($format, array('hour', 'minute', 'second'));
default:
return date_limit_format($format, array($part));
}
@@ -2100,6 +2267,17 @@ function date_part_format($part, $format) {
* The format string with all other elements removed.
*/
function date_limit_format($format, $granularity) {
// Use the advanced drupal_static() pattern to improve performance.
static $drupal_static_fast;
if (!isset($drupal_static_fast)) {
$drupal_static_fast['formats'] = &drupal_static(__FUNCTION__);
}
$formats = &$drupal_static_fast['formats'];
$format_granularity_cid = $format . '|' . implode(',', $granularity);
if (isset($formats[$format_granularity_cid])) {
return $formats[$format_granularity_cid];
}
// If punctuation has been escaped, remove the escaping. Done using strtr()
// because it is easier than getting the escape character extracted using
// preg_replace().
@@ -2129,21 +2307,27 @@ function date_limit_format($format, $granularity) {
case 'year':
$regex[] = '([\-/\.,:]?\s?(?<!\\\\)[Yy])';
break;
case 'day':
$regex[] = '([\-/\.,:]?\s?(?<!\\\\)[l|D|d|dS|j|jS|N|w|W|z]{1,2})';
break;
case 'month':
$regex[] = '([\-/\.,:]?\s?(?<!\\\\)[FMmn])';
break;
case 'hour':
$regex[] = '([\-/\.,:]?\s?(?<!\\\\)[HhGg])';
break;
case 'minute':
$regex[] = '([\-/\.,:]?\s?(?<!\\\\)[i])';
break;
case 'second':
$regex[] = '([\-/\.,:]?\s?(?<!\\\\)[s])';
break;
case 'timezone':
$regex[] = '([\-/\.,:]?\s?(?<!\\\\)[TOZPe])';
break;
@@ -2169,11 +2353,14 @@ function date_limit_format($format, $granularity) {
// After removing the non-desired parts of the format, test if the only things
// left are escaped, non-date, characters. If so, return nothing.
// Using S instead of w to pick up non-ASCII characters.
$test = trim(preg_replace('(\\\\\S{1,3})', '', $format));
$test = trim(preg_replace('(\\\\\S{1,3})u', '', $format));
if (empty($test)) {
$format = '';
}
// Store the return value in the static array for performance.
$formats[$format_granularity_cid] = $format;
return $format;
}
@@ -2213,25 +2400,30 @@ function date_format_order($format) {
case 'j':
$order[] = 'day';
break;
case 'F':
case 'M':
case 'm':
case 'n':
$order[] = 'month';
break;
case 'Y':
case 'y':
$order[] = 'year';
break;
case 'g':
case 'G':
case 'h':
case 'H':
$order[] = 'hour';
break;
case 'i':
$order[] = 'minute';
break;
case 's':
$order[] = 'second';
break;
@@ -2250,7 +2442,16 @@ function date_format_order($format) {
* A reduced set of granularitiy elements.
*/
function date_nongranularity($granularity) {
return array_diff(array('year', 'month', 'day', 'hour', 'minute', 'second', 'timezone'), (array) $granularity);
$options = array(
'year',
'month',
'day',
'hour',
'minute',
'second',
'timezone',
);
return array_diff($options, (array) $granularity);
}
/**
@@ -2270,7 +2471,11 @@ function date_api_theme($existing, $type, $theme, $path) {
'path' => "$path/theme",
);
return array(
'date_nav_title' => $base + array('variables' => array('granularity' => NULL, 'view' => NULL, 'link' => NULL, 'format' => NULL)),
'date_nav_title' => $base + array(
'variables' => array(
'granularity' => NULL, 'view' => NULL, 'link' => NULL, 'format' => NULL
),
),
'date_timezone' => $base + array('render element' => 'element'),
'date_select' => $base + array('render element' => 'element'),
'date_text' => $base + array('render element' => 'element'),
@@ -2290,7 +2495,11 @@ function date_api_theme($existing, $type, $theme, $path) {
'date_part_label_time' => $base + array('variables' => array('date_part' => NULL, 'element' => NULL)),
'date_views_filter_form' => $base + array('template' => 'date-views-filter-form', 'render element' => 'form'),
'date_calendar_day' => $base + array('variables' => array('date' => NULL)),
'date_time_ago' => $base + array('variables' => array('start_date' => NULL, 'end_date' => NULL, 'interval' => NULL)),
'date_time_ago' => $base + array(
'variables' => array(
'start_date' => NULL, 'end_date' => NULL, 'interval' => NULL
),
),
);
}
@@ -2310,9 +2519,11 @@ function date_get_timezone($handling, $timezone = '') {
case 'date':
$timezone = !empty($timezone) ? $timezone : date_default_timezone();
break;
case 'utc':
$timezone = 'UTC';
break;
default:
$timezone = date_default_timezone();
}
@@ -2320,26 +2531,40 @@ function date_get_timezone($handling, $timezone = '') {
}
/**
* Function to figure out which db timezone applies to a date and select it.
* Function to figure out which db timezone applies to a date.
*
* @param string $handling
* The timezone handling.
* @param string $timezone
* (optional) A timezone string. Defaults to an empty string.
* (optional) When $handling is 'date', date_get_timezone_db() returns this
* value.
*
* @return string
* The timezone string.
*/
function date_get_timezone_db($handling, $timezone = '') {
function date_get_timezone_db($handling, $timezone = NULL) {
switch ($handling) {
case 'none':
$timezone = date_default_timezone();
break;
default:
case ('utc'):
case ('site'):
case ('user'):
// These handling modes all convert to UTC before storing in the DB.
$timezone = 'UTC';
break;
case ('date'):
if ($timezone == NULL) {
// This shouldn't happen, since it's meaning is undefined. But we need
// to fall back to *something* that's a legal timezone.
$timezone = date_default_timezone();
}
break;
case ('none'):
default:
$timezone = date_default_timezone();
break;
}
return $timezone > '' ? $timezone : 'UTC';
return $timezone;
}
/**
@@ -2388,12 +2613,12 @@ function date_order() {
* TRUE if the date range is valid, FALSE otherwise.
*/
function date_range_valid($string) {
$matches = preg_match('@^(\-[0-9]+|[0-9]{4}):([\+|\-][0-9]+|[0-9]{4})$@', $string);
$matches = preg_match('@^([\+\-][0-9]+|[0-9]{4}):([\+\-][0-9]+|[0-9]{4})$@', $string);
return $matches < 1 ? FALSE : TRUE;
}
/**
* Splits a string like -3:+3 or 2001:2010 into an array of min and max years.
* Splits a string like -3:+3 or 2001:2010 into an array of start and end years.
*
* Center the range around the current year, if any, but expand it far
* enough so it will pick up the year value in the field in case
@@ -2405,45 +2630,44 @@ function date_range_valid($string) {
* (optional) A date object. Defaults to NULL.
*
* @return array
* A numerically indexed array, containing a minimum and maximum year.
* A numerically indexed array, containing a start and end year.
*/
function date_range_years($string, $date = NULL) {
$this_year = date_format(date_now(), 'Y');
list($min_year, $max_year) = explode(':', $string);
list($start_year, $end_year) = explode(':', $string);
// Valid patterns would be -5:+5, 0:+1, 2008:2010.
$plus_pattern = '@[\+|\-][0-9]{1,4}@';
$plus_pattern = '@[\+\-][0-9]{1,4}@';
$year_pattern = '@^[0-9]{4}@';
if (!preg_match($year_pattern, $min_year, $matches)) {
if (preg_match($plus_pattern, $min_year, $matches)) {
$min_year = $this_year + $matches[0];
if (!preg_match($year_pattern, $start_year, $matches)) {
if (preg_match($plus_pattern, $start_year, $matches)) {
$start_year = $this_year + $matches[0];
}
else {
$min_year = $this_year;
$start_year = $this_year;
}
}
if (!preg_match($year_pattern, $max_year, $matches)) {
if (preg_match($plus_pattern, $max_year, $matches)) {
$max_year = $this_year + $matches[0];
if (!preg_match($year_pattern, $end_year, $matches)) {
if (preg_match($plus_pattern, $end_year, $matches)) {
$end_year = $this_year + $matches[0];
}
else {
$max_year = $this_year;
$end_year = $this_year;
}
}
// We expect the $min year to be less than the $max year.
// Some custom values for -99:+99 might not obey that.
if ($min_year > $max_year) {
$temp = $max_year;
$max_year = $min_year;
$min_year = $temp;
}
// If there is a current value, stretch the range to include it.
$value_year = is_object($date) ? $date->format('Y') : '';
if (!empty($value_year)) {
$min_year = min($value_year, $min_year);
$max_year = max($value_year, $max_year);
if ($start_year <= $end_year) {
$start_year = min($value_year, $start_year);
$end_year = max($value_year, $end_year);
}
else {
$start_year = max($value_year, $start_year);
$end_year = min($value_year, $end_year);
}
}
return array($min_year, $max_year);
return array($start_year, $end_year);
}
/**
@@ -2603,6 +2827,7 @@ function date_is_all_day($string1, $string2, $granularity = 'second', $increment
|| ($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);
break;
case 'minute':
$min_match = $time1 == '00:00:00'
|| ($hour1 == 0 && $min1 == 0);
@@ -2610,6 +2835,7 @@ function date_is_all_day($string1, $string2, $granularity = 'second', $increment
|| ($hour2 == 23 && in_array($min2, array($max_minutes, 59)))
|| ($hour1 == 0 && $hour2 == 0 && $min1 == 0 && $min2 == 0);
break;
case 'hour':
$min_match = $time1 == '00:00:00'
|| ($hour1 == 0);
@@ -2617,6 +2843,7 @@ function date_is_all_day($string1, $string2, $granularity = 'second', $increment
|| ($hour2 == 23)
|| ($hour1 == 0 && $hour2 == 0);
break;
default:
$min_match = TRUE;
$max_match = FALSE;
@@ -2677,15 +2904,21 @@ function date_is_date($date) {
}
/**
* This 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. Without this fix, the ISO date '2020-00-00T00:00:00' would be created as
* 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.
* 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).
*
* @param string $iso_string
* An ISO string that needs to be made into a complete, valid date.
*
* @return mixed|string
* replaced value, or incoming value.
*
* @TODO Expand on this to work with all sorts of partial ISO dates.
*/
function date_make_iso_valid($iso_string) {
@@ -111,20 +111,24 @@ function date_default_date($element) {
$format = DATE_FORMAT_DATETIME;
// The text and popup widgets might return less than a full datetime string.
if (strlen($element['#default_value']) < 19) {
if (is_string($element['#default_value']) && strlen($element['#default_value']) < 19) {
switch (strlen($element['#default_value'])) {
case 16:
$format = 'Y-m-d H:i';
break;
case 13:
$format = 'Y-m-d H';
break;
case 10:
$format = 'Y-m-d';
break;
case 7:
$format = 'Y-m';
break;
case 4:
$format = 'Y';
break;
@@ -170,7 +174,7 @@ function date_year_range_element_process($element, &$form_state, $form) {
$element['#attached']['js'][] = drupal_get_path('module', 'date_api') . '/date_year_range.js';
$context = array(
'form' => $form,
'form' => $form,
);
drupal_alter('date_year_range_process', $element, $form_state, $context);
@@ -239,7 +243,8 @@ function date_timezone_element_process($element, &$form_state, $form) {
$label = theme('date_part_label_timezone', array('part_type' => 'select', 'element' => $element));
$element['timezone'] = array(
'#type' => 'select',
'#title' => $element['#date_label_position'] == 'above' ? $label : '',
'#title' => $label,
'#title_display' => $element['#date_label_position'] == 'above' ? 'before' : 'invisible',
'#options' => date_timezone_names($element['#required']),
'#value' => $element['#value'],
'#weight' => $element['#weight'],
@@ -255,7 +260,7 @@ function date_timezone_element_process($element, &$form_state, $form) {
}
$context = array(
'form' => $form,
'form' => $form,
);
drupal_alter('date_timezone_process', $element, $form_state, $context);
@@ -263,7 +268,7 @@ function date_timezone_element_process($element, &$form_state, $form) {
}
/**
* Validation for timezone input
* Validation for timezone input.
*
* Move the timezone value from the nested field back to the original field.
*/
@@ -306,7 +311,6 @@ function date_text_element_value_callback($element, $input = FALSE, &$form_state
*
* 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)) {
@@ -315,14 +319,25 @@ function date_text_element_process($element, &$form_state, $form) {
$element['#tree'] = TRUE;
$element['#theme_wrappers'] = array('date_text');
$element['date']['#value'] = $element['#value']['date'];
$element['date']['#value'] = isset($element['#value']['date']) ? $element['#value']['date'] : '';
$element['date']['#type'] = 'textfield';
$element['date']['#weight'] = !empty($element['date']['#weight']) ? $element['date']['#weight'] : $element['#weight'];
$element['date']['#attributes'] = array('class' => isset($element['#attributes']['class']) ? $element['#attributes']['class'] += array('date-date') : array('date-date'));
$now = date_example_date();
$element['date']['#description'] = ' ' . t('Format: @date', array('@date' => date_format_date(date_example_date(), 'custom', $element['#date_format'])));
$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']
)));
$element['date']['#ajax'] = !empty($element['#ajax']) ? $element['#ajax'] : FALSE;
// Make changes if instance is set to be rendered as a regular field.
if (!empty($element['#instance']['widget']['settings']['no_fieldset']) && $element['#field']['cardinality'] == 1) {
$element['date']['#title'] = check_plain($element['#instance']['label']);
$element['date']['#title_display'] = $element['#title_display'];
$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'];
@@ -338,7 +353,7 @@ function date_text_element_process($element, &$form_state, $form) {
}
$context = array(
'form' => $form,
'form' => $form,
);
drupal_alter('date_text_process', $element, $form_state, $context);
@@ -346,12 +361,11 @@ function date_text_element_process($element, &$form_state, $form) {
}
/**
* Validation for text input.
* 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.
*
*/
function date_text_validate($element, &$form_state) {
if (date_hidden_element($element)) {
@@ -364,6 +378,11 @@ function date_text_validate($element, &$form_state) {
$input_exists = NULL;
$input = drupal_array_get_nested_value($form_state['values'], $element['#parents'], $input_exists);
// Trim extra spacing off user input of text fields.
if (isset($input['date'])) {
$input['date'] = trim($input['date']);
}
drupal_alter('date_text_pre_validate', $element, $form_state, $input);
$label = !empty($element['#date_title']) ? $element['#date_title'] : (!empty($element['#title']) ? $element['#title'] : '');
@@ -418,7 +437,14 @@ function date_text_input_date($element, $input) {
* Element value callback for date_select element.
*/
function date_select_element_value_callback($element, $input = FALSE, &$form_state = array()) {
$return = array('year' => '', 'month' => '', 'day' => '', 'hour' => '', 'minute' => '', 'second' => '');
$return = array(
'year' => '',
'month' => '',
'day' => '',
'hour' => '',
'minute' => '',
'second' => '',
);
$date = NULL;
if ($input !== FALSE) {
$return = $input;
@@ -428,7 +454,14 @@ function date_select_element_value_callback($element, $input = FALSE, &$form_sta
$date = date_default_date($element);
}
$granularity = date_format_order($element['#date_format']);
$formats = array('year' => 'Y', 'month' => 'n', 'day' => 'j', 'hour' => 'H', 'minute' => 'i', 'second' => 's');
$formats = array(
'year' => 'Y',
'month' => 'n',
'day' => 'j',
'hour' => 'H',
'minute' => 'i',
'second' => 's',
);
foreach ($granularity as $field) {
if ($field != 'timezone') {
$return[$field] = date_is_date($date) ? $date->format($formats[$field]) : '';
@@ -446,7 +479,6 @@ function date_select_element_value_callback($element, $input = FALSE, &$form_sta
*
* The exact parts displayed in the field are those in #date_granularity.
* The display of each part comes from ['#date_settings']['format'].
*
*/
function date_select_element_process($element, &$form_state, $form) {
if (date_hidden_element($element)) {
@@ -470,7 +502,14 @@ function date_select_element_process($element, &$form_state, $form) {
// Store a hidden value for all date parts not in the current display.
$granularity = date_format_order($element['#date_format']);
$formats = array('year' => 'Y', 'month' => 'n', 'day' => 'j', 'hour' => 'H', 'minute' => 'i', 'second' => 's');
$formats = array(
'year' => 'Y',
'month' => 'n',
'day' => 'j',
'hour' => 'H',
'minute' => 'i',
'second' => 's',
);
foreach (date_nongranularity($granularity) as $field) {
if ($field != 'timezone') {
$element[$field] = array(
@@ -487,7 +526,7 @@ function date_select_element_process($element, &$form_state, $form) {
}
$context = array(
'form' => $form,
'form' => $form,
);
drupal_alter('date_select_process', $element, $form_state, $context);
@@ -518,7 +557,7 @@ function date_parts_element($element, $date, $format) {
$sub_element = array('#granularity' => $granularity);
$order = array_flip($granularity);
$hours_format = strpos(strtolower($element['#date_format']), 'a') ? 'g': 'G';
$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);
@@ -536,26 +575,29 @@ function date_parts_element($element, $date, $format) {
switch ($field) {
case 'year':
$range = date_range_years($element['#date_year_range'], $date);
$min_year = $range[0];
$max_year = $range[1];
$start_year = $range[0];
$end_year = $range[1];
$sub_element[$field]['#default_value'] = is_object($date) ? $date->format('Y') : '';
if ($part_type == 'select') {
$sub_element[$field]['#options'] = drupal_map_assoc(date_years($min_year, $max_year, $part_required));
$sub_element[$field]['#options'] = drupal_map_assoc(date_years($start_year, $end_year, $part_required));
}
break;
case 'month':
$sub_element[$field]['#default_value'] = is_object($date) ? $date->format('n') : '';
if ($part_type == 'select') {
$sub_element[$field]['#options'] = $month_function($part_required);
}
break;
case 'day':
$sub_element[$field]['#default_value'] = is_object($date) ? $date->format('j') : '';
if ($part_type == 'select') {
$sub_element[$field]['#options'] = drupal_map_assoc(date_days($part_required));
}
break;
case 'hour':
$sub_element[$field]['#default_value'] = is_object($date) ? $date->format($hours_format) : '';
if ($part_type == 'select') {
@@ -563,6 +605,7 @@ function date_parts_element($element, $date, $format) {
}
$sub_element[$field]['#prefix'] = theme('date_part_hour_prefix', $element);
break;
case 'minute':
$sub_element[$field]['#default_value'] = is_object($date) ? $date->format('i') : '';
if ($part_type == 'select') {
@@ -570,6 +613,7 @@ function date_parts_element($element, $date, $format) {
}
$sub_element[$field]['#prefix'] = theme('date_part_minsec_prefix', $element);
break;
case 'second':
$sub_element[$field]['#default_value'] = is_object($date) ? $date->format('s') : '';
if ($part_type == 'select') {
@@ -585,6 +629,8 @@ function date_parts_element($element, $date, $format) {
$sub_element[$field]['#type'] = 'textfield';
$sub_element[$field]['#theme'] = 'date_textfield_element';
$sub_element[$field]['#size'] = 7;
$sub_element[$field]['#title'] = $label;
$sub_element[$field]['#title_display'] = in_array($element['#date_label_position'], array('within', 'none')) ? 'invisible' : 'before';
if ($element['#date_label_position'] == 'within') {
if (!empty($sub_element[$field]['#options']) && is_array($sub_element[$field]['#options'])) {
$sub_element[$field]['#options'] = array(
@@ -594,20 +640,16 @@ function date_parts_element($element, $date, $format) {
$sub_element[$field]['#default_value'] = '-' . $label;
}
}
elseif ($element['#date_label_position'] != 'none') {
$sub_element[$field]['#title'] = $label;
}
}
else {
$sub_element[$field]['#type'] = 'select';
$sub_element[$field]['#theme'] = 'date_select_element';
$sub_element[$field]['#title'] = $label;
$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'];
}
elseif ($element['#date_label_position'] != 'none') {
$sub_element[$field]['#title'] = $label;
}
}
}
@@ -621,9 +663,12 @@ function date_parts_element($element, $date, $format) {
}
if (($hours_format == 'g' || $hours_format == 'h') && date_has_time($granularity)) {
$label = theme('date_part_label_ampm', array('part_type' => 'ampm', 'element' => $element));
$sub_element['ampm'] = array(
'#type' => 'select',
'#theme' => 'date_select_element',
'#title' => $label,
'#title_display' => in_array($element['#date_label_position'], array('within', 'none')) ? 'invisible' : 'before',
'#default_value' => is_object($date) ? (date_format($date, 'G') >= 12 ? 'pm' : 'am') : '',
'#options' => drupal_map_assoc(date_ampm($part_required)),
'#required' => $part_required,
@@ -631,10 +676,7 @@ function date_parts_element($element, $date, $format) {
'#attributes' => array('class' => array('date-ampm')),
);
if ($element['#date_label_position'] == 'within') {
$sub_element['ampm']['#options'] = array('' => '-' . theme('date_part_label_ampm', array('part_type' => 'ampm', 'eleement' => $element))) + $sub_element['ampm']['#options'];
}
elseif ($element['#date_label_position'] != 'none') {
$sub_element['ampm']['#title'] = theme('date_part_label_ampm', array('part_type' => 'ampm', 'element' => $element));
$sub_element['ampm']['#options'] = array('' => '-' . $label) + $sub_element['ampm']['#options'];
}
}
@@ -181,6 +181,7 @@ function date_ical_parse($icaldatafolded = array()) {
$parent[array_pop($parents)][] = array_pop($subgroups);
}
break;
// Add the timezones in with their index their TZID.
case 'VTIMEZONE':
$subgroup = end($subgroups);
@@ -196,6 +197,7 @@ function date_ical_parse($icaldatafolded = array()) {
array_pop($subgroups);
array_pop($parents);
break;
// Do some fun stuff with durations and all_day events and then append
// to parent.
case 'VEVENT':
@@ -222,9 +224,9 @@ function date_ical_parse($icaldatafolded = array()) {
// assumes the end date is inclusive.
if (!empty($subgroup['DTEND']) && (!empty($subgroup['DTEND']['all_day']))) {
// Make the end date one day earlier.
$date = new DateObject ($subgroup['DTEND']['datetime'] . ' 00:00:00', $subgroup['DTEND']['tz']);
$date = new DateObject($subgroup['DTEND']['datetime'] . ' 00:00:00', $subgroup['DTEND']['tz']);
date_modify($date, '-1 day');
$subgroup['DTEND']['datetime'] = date_format($date, 'Y-m-d');
$subgroup['DTEND']['datetime'] = date_format($date, 'Y-m-d');
}
// If a start datetime is defined AND there is no definition for
// the end datetime THEN make the end datetime equal the start
@@ -239,7 +241,7 @@ function date_ical_parse($icaldatafolded = array()) {
if (!empty($subgroup['DTSTART']['all_day'])) {
$subgroup['all_day'] = TRUE;
}
// Add this element to the parent as an array under the
// Add this element to the parent as an array under the.
prev($subgroups);
$parent = &$subgroups[key($subgroups)];
@@ -264,12 +266,13 @@ function date_ical_parse($icaldatafolded = array()) {
$field = !empty($matches[2]) ? $matches[2] : '';
$data = !empty($matches[3]) ? $matches[3] : '';
$parse_result = '';
switch ($name) {
// Keep blank lines out of the results.
case '':
break;
// Lots of properties have date values that must be parsed out.
// Lots of properties have date values that must be parsed out.
case 'CREATED':
case 'LAST-MODIFIED':
case 'DTSTART':
@@ -317,9 +320,9 @@ function date_ical_parse($icaldatafolded = array()) {
$parse_result = date_ical_parse_location($field, $data);
break;
// For all other properties, just store the property and the value.
// This can be expanded on in the future if other properties should
// be given special treatment.
// For all other properties, just store the property and the value.
// This can be expanded on in the future if other properties should
// be given special treatment.
default:
$parse_result = $data;
break;
@@ -360,7 +363,7 @@ function date_ical_parse($icaldatafolded = array()) {
* has no timezone; the ical specs say no timezone
* conversion should be done if no timezone info is
* supplied
* @todo
* @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()
@@ -401,6 +404,7 @@ function date_ical_parse_date($field, $data) {
// Date.
$datetime = date_pad($regs[1]) . '-' . date_pad($regs[2]) . '-' . date_pad($regs[3]);
break;
case 'DATE-TIME':
preg_match(DATE_REGEX_ICAL_DATETIME, $data, $regs);
// Date.
@@ -519,12 +523,12 @@ function date_ical_parse_duration(&$subgroup, $field = 'DURATION') {
$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]) : '';
$items['week'] = isset($duration[3]) ?str_replace('W', '', $duration[3]) : '';
$items['day'] = isset($duration[4]) ?str_replace('D', '', $duration[4]) : '';
$items['hour'] = isset($duration[6]) ?str_replace('H', '', $duration[6]) : '';
$items['minute'] = isset($duration[7]) ?str_replace('M', '', $duration[7]) : '';
$items['second'] = isset($duration[8]) ?str_replace('S', '', $duration[8]) : '';
$items['month'] = isset($duration[2]) ? str_replace('M', '', $duration[2]) : '';
$items['week'] = isset($duration[3]) ? str_replace('W', '', $duration[3]) : '';
$items['day'] = isset($duration[4]) ? str_replace('D', '', $duration[4]) : '';
$items['hour'] = isset($duration[6]) ? str_replace('H', '', $duration[6]) : '';
$items['minute'] = isset($duration[7]) ? str_replace('M', '', $duration[7]) : '';
$items['second'] = isset($duration[8]) ? str_replace('S', '', $duration[8]) : '';
$start_date = array_key_exists('DTSTART', $subgroup) ? $subgroup['DTSTART']['datetime'] : date_format(date_now(), DATE_FORMAT_ISO);
$timezone = array_key_exists('DTSTART', $subgroup) ? $subgroup['DTSTART']['tz'] : variable_get('date_default_timezone');
if (empty($timezone)) {
@@ -542,7 +546,7 @@ function date_ical_parse_duration(&$subgroup, $field = 'DURATION') {
'datetime' => date_format($date2, DATE_FORMAT_DATETIME),
'all_day' => isset($subgroup['DTSTART']['all_day']) ? $subgroup['DTSTART']['all_day'] : 0,
'tz' => $timezone,
);
);
$duration = date_format($date2, 'U') - date_format($date, 'U');
$subgroup['DURATION'] = array('DATA' => $data, 'DURATION' => $duration);
}
@@ -631,7 +635,6 @@ function date_ical_date($ical_date, $to_tz = FALSE) {
*
* @return string
* Escaped text
*
*/
function date_ical_escape_text($text) {
$text = drupal_html_to_text($text);
@@ -693,14 +696,14 @@ function date_ical_escape_text($text) {
* )
*/
function date_api_ical_build_rrule($form_values) {
$RRULE = '';
$rrule = '';
if (empty($form_values) || !is_array($form_values)) {
return $RRULE;
return $rrule;
}
// Grab the RRULE data and put them into iCal RRULE format.
$RRULE .= 'RRULE:FREQ=' . (!array_key_exists('FREQ', $form_values) ? 'DAILY' : $form_values['FREQ']);
$RRULE .= ';INTERVAL=' . (!array_key_exists('INTERVAL', $form_values) ? 1 : $form_values['INTERVAL']);
$rrule .= 'RRULE:FREQ=' . (!array_key_exists('FREQ', $form_values) ? 'DAILY' : $form_values['FREQ']);
$rrule .= ';INTERVAL=' . (!array_key_exists('INTERVAL', $form_values) ? 1 : $form_values['INTERVAL']);
// Unset the empty 'All' values.
if (array_key_exists('BYDAY', $form_values) && is_array($form_values['BYDAY'])) {
@@ -713,14 +716,14 @@ function date_api_ical_build_rrule($form_values) {
unset($form_values['BYMONTHDAY']['']);
}
if (array_key_exists('BYDAY', $form_values) && is_array($form_values['BYDAY']) && $BYDAY = implode(",", $form_values['BYDAY'])) {
$RRULE .= ';BYDAY=' . $BYDAY;
if (array_key_exists('BYDAY', $form_values) && is_array($form_values['BYDAY']) && $byday = implode(",", $form_values['BYDAY'])) {
$rrule .= ';BYDAY=' . $byday;
}
if (array_key_exists('BYMONTH', $form_values) && is_array($form_values['BYMONTH']) && $BYMONTH = implode(",", $form_values['BYMONTH'])) {
$RRULE .= ';BYMONTH=' . $BYMONTH;
if (array_key_exists('BYMONTH', $form_values) && is_array($form_values['BYMONTH']) && $bymonth = implode(",", $form_values['BYMONTH'])) {
$rrule .= ';BYMONTH=' . $bymonth;
}
if (array_key_exists('BYMONTHDAY', $form_values) && is_array($form_values['BYMONTHDAY']) && $BYMONTHDAY = implode(",", $form_values['BYMONTHDAY'])) {
$RRULE .= ';BYMONTHDAY=' . $BYMONTHDAY;
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
@@ -731,8 +734,17 @@ function date_api_ical_build_rrule($form_values) {
if (!is_object($form_values['UNTIL']['datetime'])) {
// If this is a date without time, give it time.
if (strlen($form_values['UNTIL']['datetime']) < 11) {
$granularity_options = drupal_map_assoc(array(
'year',
'month',
'day',
'hour',
'minute',
'second',
));
$form_values['UNTIL']['datetime'] .= ' 23:59:59';
$form_values['UNTIL']['granularity'] = serialize(drupal_map_assoc(array('year', 'month', 'day', 'hour', 'minute', 'second')));
$form_values['UNTIL']['granularity'] = serialize($granularity_options);
$form_values['UNTIL']['all_day'] = FALSE;
}
$until = date_ical_date($form_values['UNTIL'], 'UTC');
@@ -740,21 +752,21 @@ function date_api_ical_build_rrule($form_values) {
else {
$until = $form_values['UNTIL']['datetime'];
}
$RRULE .= ';UNTIL=' . date_format($until, DATE_FORMAT_ICAL) . 'Z';
$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.
if (array_key_exists('COUNT', $form_values)) {
$RRULE .= ';COUNT=' . $form_values['COUNT'];
$rrule .= ';COUNT=' . $form_values['COUNT'];
}
// iCal rules presume the week starts on Monday unless otherwise specified,
// so we'll specify it.
if (array_key_exists('WKST', $form_values)) {
$RRULE .= ';WKST=' . $form_values['WKST'];
$rrule .= ';WKST=' . $form_values['WKST'];
}
else {
$RRULE .= ';WKST=' . date_repeat_dow2day(variable_get('date_first_day', 0));
$rrule .= ';WKST=' . date_repeat_dow2day(variable_get('date_first_day', 0));
}
// Exceptions dates go last, on their own line.
@@ -765,7 +777,7 @@ function date_api_ical_build_rrule($form_values) {
foreach ($form_values['EXDATE'] as $value) {
if (!empty($value['datetime'])) {
$date = !is_object($value['datetime']) ? date_ical_date($value, 'UTC') : $value['datetime'];
$ex_date = !empty($date) ? date_format($date, DATE_FORMAT_ICAL) . 'Z': '';
$ex_date = !empty($date) ? date_format($date, DATE_FORMAT_ICAL) . 'Z' : '';
if (!empty($ex_date)) {
$ex_dates[] = $ex_date;
}
@@ -773,11 +785,11 @@ function date_api_ical_build_rrule($form_values) {
}
if (!empty($ex_dates)) {
sort($ex_dates);
$RRULE .= chr(13) . chr(10) . 'EXDATE:' . implode(',', $ex_dates);
$rrule .= chr(13) . chr(10) . 'EXDATE:' . implode(',', $ex_dates);
}
}
elseif (!empty($form_values['EXDATE'])) {
$RRULE .= chr(13) . chr(10) . 'EXDATE:' . $form_values['EXDATE'];
$rrule .= chr(13) . chr(10) . 'EXDATE:' . $form_values['EXDATE'];
}
// Exceptions dates go last, on their own line.
@@ -785,19 +797,19 @@ function date_api_ical_build_rrule($form_values) {
$ex_dates = array();
foreach ($form_values['RDATE'] as $value) {
$date = !is_object($value['datetime']) ? date_ical_date($value, 'UTC') : $value['datetime'];
$ex_date = !empty($date) ? date_format($date, DATE_FORMAT_ICAL) . 'Z': '';
$ex_date = !empty($date) ? date_format($date, DATE_FORMAT_ICAL) . 'Z' : '';
if (!empty($ex_date)) {
$ex_dates[] = $ex_date;
}
}
if (!empty($ex_dates)) {
sort($ex_dates);
$RRULE .= chr(13) . chr(10) . 'RDATE:' . implode(',', $ex_dates);
$rrule .= chr(13) . chr(10) . 'RDATE:' . implode(',', $ex_dates);
}
}
elseif (!empty($form_values['RDATE'])) {
$RRULE .= chr(13) . chr(10) . 'RDATE:' . $form_values['RDATE'];
$rrule .= chr(13) . chr(10) . 'RDATE:' . $form_values['RDATE'];
}
return $RRULE;
return $rrule;
}
@@ -20,17 +20,17 @@
* Correct sql string for database type.
*/
function date_sql_concat($array) {
switch (db_driver()) {
switch (Database::getConnection()->databaseType()) {
case 'mysql':
case 'mysqli':
return "CONCAT(" . implode(",", $array) . ")";
case 'pgsql':
return implode(" || ", $array);
}
}
/**
* Helper function to do cross-database NULL replacements
* Helper function to do cross-database NULL replacements.
*
* @param array $array
* An array of values to test for NULL values.
@@ -39,9 +39,8 @@ function date_sql_concat($array) {
* SQL statement to return the first non-NULL value in the list.
*/
function date_sql_coalesce($array) {
switch (db_driver()) {
switch (Database::getConnection()->databaseType()) {
case 'mysql':
case 'mysqli':
case 'pgsql':
return "COALESCE(" . implode(',', $array) . ")";
}
@@ -63,6 +62,7 @@ function date_sql_pad($str, $size = 2, $pad = '0', $side = 'l') {
switch ($side) {
case 'r':
return "RPAD($str, $size, '$pad')";
default:
return "LPAD($str, $size, '$pad')";
}
@@ -71,6 +71,7 @@ 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;
@@ -88,8 +89,8 @@ class date_sql_handler {
/**
* The object constuctor.
*/
function __construct($date_type = DATE_DATETIME, $local_timezone = NULL, $offset = '+00:00') {
$this->db_type = db_driver();
public function __construct($date_type = DATE_DATETIME, $local_timezone = NULL, $offset = '+00:00') {
$this->db_type = Database::getConnection()->databaseType();
$this->date_type = $date_type;
$this->db_timezone = 'UTC';
$this->local_timezone = isset($local_timezone) ? $local_timezone : date_default_timezone();
@@ -99,18 +100,18 @@ class date_sql_handler {
/**
* See if the db has timezone name support.
*/
function db_tz_support($reset = FALSE) {
public function db_tz_support($reset = FALSE) {
$has_support = variable_get('date_db_tz_support', -1);
if ($has_support == -1 || $reset) {
$has_support = FALSE;
switch ($this->db_type) {
case 'mysql':
case 'mysqli':
$test = db_query("SELECT CONVERT_TZ('2008-02-15 12:00:00', 'UTC', 'US/Central')")->fetchField();
if ($test == '2008-02-15 06:00:00') {
$has_support = TRUE;
}
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') {
@@ -139,18 +140,19 @@ class date_sql_handler {
* set a fixed offset, not a timezone, so any value other than
* '+00:00' should be used with caution.
*/
function set_db_timezone($offset = '+00:00') {
public function set_db_timezone($offset = '+00:00') {
static $already_set = FALSE;
$type = db_driver();
$type = Database::getConnection()->databaseType();
if (!$already_set) {
switch ($type) {
case 'mysql':
case 'mysqli':
db_query("SET @@session.time_zone = '$offset'");
break;
case 'pgsql':
db_query("SET TIME ZONE INTERVAL '$offset' HOUR TO MINUTE");
break;
case 'sqlsrv':
// Issue #1201342, This is the wrong way to set the timezone, this
// still needs to be fixed. In the meantime, commenting this out makes
@@ -165,7 +167,7 @@ class date_sql_handler {
/**
* Return timezone offset for the date being processed.
*/
function get_offset($comp_date = NULL) {
public function get_offset($comp_date = NULL) {
if (!empty($this->db_timezone) && !empty($this->local_timezone)) {
if ($this->db_timezone != $this->local_timezone) {
if (empty($comp_date)) {
@@ -199,52 +201,61 @@ class date_sql_handler {
}
switch ($this->db_type) {
case 'mysql':
case 'mysqli':
switch ($this->date_type) {
case DATE_UNIX:
$field = "FROM_UNIXTIME($field)";
break;
case DATE_ISO:
$field = "STR_TO_DATE($field, '%Y-%m-%dT%T')";
break;
case DATE_DATETIME:
break;
}
break;
case 'pgsql':
switch ($this->date_type) {
case DATE_UNIX:
$field = "$field::ABSTIME";
break;
case DATE_ISO:
$field = "TO_DATE($field, 'FMYYYY-FMMM-FMDDTFMHH24:FMMI:FMSS')";
break;
case DATE_DATETIME:
break;
}
break;
case 'sqlite':
switch ($this->date_type) {
case DATE_UNIX:
$field = "datetime($field, 'unixepoch')";
break;
case DATE_ISO:
case DATE_DATETIME:
$field = "datetime($field)";
break;
}
break;
case 'sqlsrv':
switch ($this->date_type) {
case DATE_UNIX:
$field = "DATEADD(s, $field, '19700101 00:00:00:000')";
break;
case DATE_ISO:
case DATE_DATETIME:
$field = "CAST($field as smalldatetime)";
break;
}
break;
break;
}
// Adjust the resulting value to the right timezone/offset.
@@ -258,12 +269,14 @@ class date_sql_handler {
if (!empty($offset)) {
switch ($this->db_type) {
case 'mysql':
case 'mysqli':
return "ADDTIME($field, SEC_TO_TIME($offset))";
case 'pgsql':
return "($field + INTERVAL '$offset SECONDS')";;
return "($field + INTERVAL '$offset SECONDS')";
case 'sqlite':
return "datetime($field, '$offset seconds')";
case 'sqlsrv':
return "DATEADD(second, $offset, $field)";
}
@@ -288,10 +301,10 @@ class date_sql_handler {
$granularity = strtoupper($granularity);
switch ($this->db_type) {
case 'mysql':
case 'mysqli':
switch ($direction) {
case 'ADD':
return "DATE_ADD($field, INTERVAL $count $granularity)";
case 'SUB':
return "DATE_SUB($field, INTERVAL $count $granularity)";
}
@@ -301,6 +314,7 @@ class date_sql_handler {
switch ($direction) {
case 'ADD':
return "($field + INTERVAL '$count $granularity')";
case 'SUB':
return "($field - INTERVAL '$count $granularity')";
}
@@ -309,6 +323,7 @@ class date_sql_handler {
switch ($direction) {
case 'ADD':
return "datetime($field, '+$count $granularity')";
case 'SUB':
return "datetime($field, '-$count $granularity')";
}
@@ -358,8 +373,8 @@ class date_sql_handler {
else {
switch ($this->db_type) {
case 'mysql':
case 'mysqli':
return "CONVERT_TZ($field, $db_zone, $localzone)";
case 'pgsql':
// WITH TIME ZONE assumes the date is using the system
// timezone, which should have been set to UTC.
@@ -382,7 +397,6 @@ class date_sql_handler {
function sql_format($format, $field) {
switch ($this->db_type) {
case 'mysql':
case 'mysqli':
$replace = array(
'Y' => '%Y',
'y' => '%y',
@@ -404,6 +418,7 @@ class date_sql_handler {
);
$format = strtr($format, $replace);
return "DATE_FORMAT($field, '$format')";
case 'pgsql':
$replace = array(
'Y' => 'YYYY',
@@ -430,6 +445,7 @@ class date_sql_handler {
);
$format = strtr($format, $replace);
return "TO_CHAR($field, '$format')";
case 'sqlite':
$replace = array(
// 4 digit year number.
@@ -469,6 +485,7 @@ class date_sql_handler {
);
$format = strtr($format, $replace);
return "strftime('$format', $field)";
case 'sqlsrv':
$replace = array(
// 4 digit year number.
@@ -537,44 +554,51 @@ class date_sql_handler {
switch (strtoupper($extract_type)) {
case 'DATE':
return $field;
case 'YEAR':
return "EXTRACT(YEAR FROM($field))";
case 'MONTH':
return "EXTRACT(MONTH FROM($field))";
case 'DAY':
return "EXTRACT(DAY FROM($field))";
case 'HOUR':
return "EXTRACT(HOUR FROM($field))";
case 'MINUTE':
return "EXTRACT(MINUTE FROM($field))";
case 'SECOND':
return "EXTRACT(SECOND FROM($field))";
// ISO week number for date.
case 'WEEK':
switch ($this->db_type) {
case 'mysql':
case 'mysqli':
// WEEK using arg 3 in MySQl should return the same value as
// Postgres EXTRACT.
return "WEEK($field, 3)";
case 'pgsql':
return "EXTRACT(WEEK FROM($field))";
}
case 'DOW':
switch ($this->db_type) {
case 'mysql':
case 'mysqli':
// MySQL returns 1 for Sunday through 7 for Saturday, PHP date
// functions and Postgres use 0 for Sunday and 6 for Saturday.
return "INTEGER(DAYOFWEEK($field) - 1)";
case 'pgsql':
return "EXTRACT(DOW FROM($field))";
}
case 'DOY':
switch ($this->db_type) {
case 'mysql':
case 'mysqli':
return "DAYOFYEAR($field)";
case 'pgsql':
return "EXTRACT(DOY FROM($field))";
}
@@ -787,8 +811,7 @@ class date_sql_handler {
}
/**
* Create a complete datetime value out of an
* incomplete array of selected values.
* 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
@@ -807,9 +830,11 @@ class date_sql_handler {
case 'empty_min':
case 'min':
return date_format($dates[0], 'Y-m-d H:i:s');
case 'empty_max':
case 'max':
return date_format($dates[1], 'Y-m-d H:i:s');
default:
return;
}
@@ -852,7 +877,7 @@ class date_sql_handler {
}
/**
* A function to test the validity of various date parts
* A function to test the validity of various date parts.
*/
function part_is_valid($value, $type) {
if (!preg_match('/^[0-9]*$/', $value)) {
@@ -868,16 +893,19 @@ class date_sql_handler {
return FALSE;
}
break;
case 'month':
if ($value < 0 || $value > 12) {
return FALSE;
}
break;
case 'day':
if ($value < 0 || $value > 31) {
return FALSE;
}
break;
case 'week':
if ($value < 0 || $value > 53) {
return FALSE;
@@ -896,32 +924,42 @@ class date_sql_handler {
}
$formats = array('display', 'sql');
// Start with the site long date format and add seconds to it.
$long = str_replace(':i', ':i:s', variable_get('date_format_long', 'l, F j, Y - H:i'));
$short = str_replace(':i', ':i:s', variable_get('date_format_short', 'l, F j, Y - H:i'));
switch ($granularity) {
case 'year':
$formats['display'] = 'Y';
$formats['sql'] = 'Y';
break;
case 'month':
$formats['display'] = date_limit_format($long, array('year', 'month'));
$formats['display'] = date_limit_format($short, array('year', 'month'));
$formats['sql'] = 'Y-m';
break;
case 'day':
$formats['display'] = date_limit_format($long, array('year', 'month', 'day'));
$args = array('year', 'month', 'day');
$formats['display'] = date_limit_format($short, $args);
$formats['sql'] = 'Y-m-d';
break;
case 'hour':
$formats['display'] = date_limit_format($long, array('year', 'month', 'day', 'hour'));
$args = array('year', 'month', 'day', 'hour');
$formats['display'] = date_limit_format($short, $args);
$formats['sql'] = 'Y-m-d\TH';
break;
case 'minute':
$formats['display'] = date_limit_format($long, array('year', 'month', 'day', 'hour', 'minute'));
$args = array('year', 'month', 'day', 'hour', 'minute');
$formats['display'] = date_limit_format($short, $args);
$formats['sql'] = 'Y-m-d\TH:i';
break;
case 'second':
$formats['display'] = date_limit_format($long, array('year', 'month', 'day', 'hour', 'minute', 'second'));
$args = array('year', 'month', 'day', 'hour', 'minute', 'second');
$formats['display'] = date_limit_format($short, $args);
$formats['sql'] = 'Y-m-d\TH:i:s';
break;
case 'week':
$formats['display'] = 'F j Y (W)';
$formats['sql'] = 'Y-\WW';
@@ -939,7 +977,7 @@ class date_sql_handler {
'#type' => 'radios',
'#default_value' => $granularity,
'#options' => $this->date_parts(),
);
);
return $form;
}
@@ -1042,7 +1080,6 @@ class date_sql_handler {
$direction = $results[1];
$count = $results[2];
$item = $results[3];
$replace = array(
'now' => '@',
'+' => 'P',
@@ -1063,14 +1100,27 @@ class date_sql_handler {
'second' => 'S',
' ' => '',
' ' => '',
);
$prefix = in_array($item, array('hours', 'hour', 'minutes', 'minute', 'seconds', 'second')) ? 'T' : '';
return $prefix . strtr($direction, $replace) . $count . strtr($item, $replace);
);
$args = array('hours', 'hour', 'minutes', 'minute', 'seconds', 'second');
if (in_array($item, $args)) {
$prefix = 'T';
}
else {
$prefix = '';
}
$return = $prefix;
$return .= strtr($direction, $replace);
$return .= $count;
$return .= strtr($item, $replace);
return $return;
}
/**
* Use the parsed values from the ISO argument to determine the
* granularity of this period.
* Granularity arguments handler.
*
* Use the parsed values from the ISO argument
* to determine the granularity of this period.
*/
function arg_granularity($arg) {
$granularity = '';
@@ -1149,8 +1199,9 @@ class date_sql_handler {
}
return array($min_date, $max_date);
}
// Intercept invalid info and fall back to the current date.
// Intercept invalid info and fall back to the current date.
$now = date_now();
return array($now, $now);
}
}
// @codingStandardsIgnoreEnd
@@ -194,6 +194,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;
$interval = !empty($variables['interval']) ? $variables['interval'] : 2;
$display = isset($variables['interval_display']) ? $variables['interval_display'] : 'time ago';
@@ -202,28 +203,43 @@ function theme_date_time_ago($variables) {
return;
}
// Time to compare dates to.
$now = date_format(date_now(), DATE_FORMAT_UNIX);
$start = date_format($start_date, DATE_FORMAT_UNIX);
// We use the end date only when the option is checked.
if ($use_end_date){
$date = date_format($end_date, DATE_FORMAT_UNIX);
}
else {
$date = date_format($start_date, DATE_FORMAT_UNIX);
}
// will be positive for a datetime in the past (ago), and negative for a datetime in the future (hence)
$time_diff = $now - $start;
// 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).
$time_diff = $now - $date;
// Uses the same options used by Views format_interval.
switch ($display) {
case 'raw time ago':
return format_interval($time_diff, $interval);
case 'time ago':
return t('%time ago', array('%time' => format_interval($time_diff, $interval)));
case 'raw time hence':
return format_interval(-$time_diff, $interval);
case 'time hence':
return t('%time hence', array('%time' => format_interval(-$time_diff, $interval)));
case 'raw time span':
return ($time_diff < 0 ? '-' : '') . format_interval(abs($time_diff), $interval);
case 'inverse time span':
return ($time_diff > 0 ? '-' : '') . format_interval(abs($time_diff), $interval);
case 'time span':
return t(($time_diff < 0 ? '%time hence' : '%time ago'), array('%time' => format_interval(abs($time_diff), $interval)));
}
}
@@ -8,9 +8,9 @@ dependencies[] = context
files[] = date_context.module
files[] = plugins/date_context_date_condition.inc
; Information added by drupal.org packaging script on 2012-08-13
version = "7.x-2.6"
; Information added by Drupal.org packaging script on 2017-04-07
version = "7.x-2.10"
core = "7.x"
project = "date"
datestamp = "1344850024"
datestamp = "1491562090"
@@ -1,5 +1,8 @@
<?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()
@@ -8,8 +11,6 @@
* Cache the date processing, perhaps cache the formatted, timezone-adjusted
* date strings for each entity (would have to be cached differently for each
* timezone, based on the tz_handling method for the date).
*
* Add an option to set/not set the context on forms vs views.
*/
/**
@@ -22,7 +23,7 @@ function date_context_context_node_condition_alter($node, $op) {
}
/**
* Implements hook_context_plugins()
* Implements hook_context_plugins().
*/
function date_context_context_plugins() {
$plugins = array();
@@ -38,7 +39,7 @@ function date_context_context_plugins() {
}
/**
* Implements hook_context_registry()
* Implements hook_context_registry().
*/
function date_context_context_registry() {
return array(
@@ -51,4 +52,3 @@ function date_context_context_registry() {
),
);
}
@@ -1,10 +1,20 @@
<?php
/**
* @file
* Context date condition plugin.
*/
/**
* Expose term views/term forms by vocabulary as a context condition.
*/
// @codingStandardsIgnoreStart
class date_context_date_condition extends context_condition_node {
function condition_values() {
/**
* {@inheritdoc}
*/
public function condition_values() {
$values = array();
$fields = field_info_fields();
foreach ($fields as $field_name => $field) {
@@ -15,10 +25,13 @@ class date_context_date_condition extends context_condition_node {
return $values;
}
function options_form($context) {
/**
* {@inheritdoc}
*/
public function options_form($context) {
$defaults = $this->fetch_from_context($context, 'options');
$options = array(
'<' => t('Is less than'),
'<' => t('Is less than'),
'<=' => t('Is less than or equal to'),
'>=' => t('Is greater than or equal to'),
'>' => t('Is greater than'),
@@ -27,6 +40,8 @@ class date_context_date_condition extends context_condition_node {
'empty' => t('Is empty'),
'not empty' => t('Is not Empty'),
);
$dependency_options = array('<', '<=', '>', '>=', '=', '!=');
$form['operation'] = array(
'#title' => t('Operation'),
'#type' => 'select',
@@ -41,12 +56,15 @@ class date_context_date_condition extends context_condition_node {
'#description' => t("The value the field should contain to meet the condition. This can either be an absolute date in ISO format (YYYY-MM-DDTHH:MM:SS) or a relative string like '12AM today'. Examples: 2011-12-31T00:00:00, now, now +1 day, 12AM today, Monday next week. <a href=\"@relative_format\">More examples of relative date formats in the PHP documentation</a>.", array('@relative_format' => 'http://www.php.net/manual/en/datetime.formats.relative.php')),
'#default_value' => isset($defaults['value']) ? $defaults['value'] : '',
'#process' => array('ctools_dependent_process'),
'#dependency' => array(':input[name="conditions[plugins][date_context_date_condition][options][operation]"]' => array('<', '<=', '>', '>=', '=', '!=')),
'#dependency' => array('edit-conditions-plugins-date-context-date-condition-options-operation' => $dependency_options),
);
return $form;
}
function execute($entity, $op) {
/**
* {@inheritdoc}
*/
public function execute($entity, $op) {
if (in_array($op, array('view', 'form'))) {
foreach ($this->get_contexts() as $context) {
$options = $this->fetch_from_context($context, 'options');
@@ -91,32 +109,37 @@ class date_context_date_condition extends context_condition_node {
str_replace('now', 'today', $options['value']);
$date = date_create($options['value'], date_default_timezone_object());
$compdate = $date->format(DATE_FORMAT_DATETIME);
switch($options['operation']) {
switch ($options['operation']) {
case '=':
if ($date2 >= $compdate && $date1 <= $compdate) {
$this->condition_met($context, $field_name);
}
break;
case '>':
if ($date1 > $compdate) {
$this->condition_met($context, $field_name);
}
break;
case '>=':
if ($date1 >= $compdate) {
$this->condition_met($context, $field_name);
}
break;
case '<':
if ($date2 < $compdate) {
$this->condition_met($context, $field_name);
}
break;
case '<=':
if ($date2 <= $compdate) {
$this->condition_met($context, $field_name);
}
break;
case '!=':
if ($date1 < $compdate || $date2 > $compdate) {
$this->condition_met($context, $field_name);
@@ -130,3 +153,4 @@ class date_context_date_condition extends context_condition_node {
}
}
}
// @codingStandardsIgnoreEnd
@@ -40,7 +40,6 @@
*
* - In the field's submission processing, the new date values, which are in
* 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) {
@@ -75,9 +74,9 @@ function date_field_widget_form(&$form, &$form_state, $field, $instance, $langco
// 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'])) {
if (!empty($field['settings']['repeat']) && module_exists('date_repeat_field')) {
if ($delta == 0) {
$form['#after_build'] = array('date_repeat_after_build');
$form['#after_build'][] = 'date_repeat_after_build';
$form_state['storage']['repeat_fields'][$field_name] = array_merge($form['#parents'], array($field_name));
$form_state['storage']['date_items'][$field_name][$langcode] = $items;
}
@@ -87,7 +86,7 @@ function date_field_widget_form(&$form, &$form_state, $field, $instance, $langco
}
module_load_include('inc', 'date_api', 'date_api_elements');
$timezone = date_get_timezone($field['settings']['tz_handling'], isset($items[0]['timezone']) ? $items[0]['timezone'] : date_default_timezone());
$timezone = date_get_timezone($field['settings']['tz_handling'], isset($items[$delta]['timezone']) ? $items[$delta]['timezone'] : date_default_timezone());
// TODO see if there's a way to keep the timezone element from ever being
// nested as array('timezone' => 'timezone' => value)). After struggling
@@ -122,7 +121,13 @@ function date_field_widget_form(&$form, &$form_state, $field, $instance, $langco
'#weight' => $instance['widget']['weight'] + 1,
'#attributes' => array('class' => array('date-no-float')),
'#date_label_position' => $instance['widget']['settings']['label_position'],
);
);
}
// 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();
}
return $element;
@@ -148,6 +153,7 @@ function date_local_date($item, $timezone, $field, $instance, $part = 'value') {
// @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
/*
if ($field['type'] == DATE_ISO) {
$value = date_fuzzy_datetime($value);
@@ -157,6 +163,7 @@ function date_local_date($item, $timezone, $field, $instance, $part = 'value') {
$value = date_convert($value, $field['type'], DATE_DATETIME, $db_timezone);
}
*/
// @codingStandardsIgnoreEnd
$date = new DateObject($value, date_get_timezone_db($field['settings']['tz_handling']));
$date->limitGranularity($field['settings']['granularity']);
@@ -193,8 +200,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.
* Helper function for the date default value callback to set either 'value' or 'value2' to its default value.
*/
function date_default_value_part($item, $field, $instance, $langcode, $part = 'value') {
$timezone = date_get_timezone($field['settings']['tz_handling']);
@@ -241,7 +247,6 @@ function date_default_value_part($item, $field, $instance, $langcode, $part = 'v
* Process an individual date element.
*/
function date_combo_element_process($element, &$form_state, $form) {
if (date_hidden_element($element)) {
// A hidden value for a new entity that had its end date set to blank
// will not get processed later to populate the end date, so set it here.
@@ -296,6 +301,7 @@ 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.
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]) {
unset($element['#default_value'][$to_field]);
@@ -320,7 +326,7 @@ function date_combo_element_process($element, &$form_state, $form) {
'#field' => $field,
'#instance' => $instance,
'#weight' => $instance['widget']['weight'],
'#required' => ($instance['required'] && $delta == 0) ? 1 : 0,
'#required' => ($element['#required'] && $delta == 0) ? 1 : 0,
'#default_value' => isset($element['#default_value'][$from_field]) ? $element['#default_value'][$from_field] : '',
'#delta' => $delta,
'#date_timezone' => $element['#date_timezone'],
@@ -329,9 +335,13 @@ function date_combo_element_process($element, &$form_state, $form) {
'#date_increment' => $instance['widget']['settings']['increment'],
'#date_year_range' => $instance['widget']['settings']['year_range'],
'#date_label_position' => $instance['widget']['settings']['label_position'],
);
);
$description = !empty($instance['description']) ? t($instance['description']) : '';
// Date repeat is a multiple value field. So the description is removed from
// the single element earlier. Let's get it back.
if (isset($element['show_repeat_settings']) && !empty($element['value']['#instance']['description'])) {
$element['#description'] = $element['value']['#instance']['description'];
}
// Give this element the right type, using a Date API
// or a Date Popup element type.
@@ -346,11 +356,13 @@ function date_combo_element_process($element, &$form_state, $form) {
$element['#attached']['js'][] = drupal_get_path('module', 'date') . '/date.js';
$element[$from_field]['#ajax'] = !empty($element['#ajax']) ? $element['#ajax'] : FALSE;
break;
case 'date_popup':
$element[$from_field]['#type'] = 'date_popup';
$element[$from_field]['#theme_wrappers'] = array('date_popup');
$element[$from_field]['#ajax'] = !empty($element['#ajax']) ? $element['#ajax'] : FALSE;
break;
default:
$element[$from_field]['#type'] = 'date_text';
$element[$from_field]['#theme_wrappers'] = array('date_text');
@@ -363,8 +375,8 @@ function date_combo_element_process($element, &$form_state, $form) {
// is the 'Start' and which is the 'End' .
if (!empty($field['settings']['todate'])) {
$element[$from_field]['#title'] = '';
$element[$to_field] = $element[$from_field];
$element[$from_field]['#title_display'] = 'none';
$element[$to_field]['#title'] = t('to:');
$element[$from_field]['#wrapper_attributes']['class'][] = 'start-date-wrapper';
$element[$to_field]['#wrapper_attributes']['class'][] = 'end-date-wrapper';
@@ -374,18 +386,17 @@ function date_combo_element_process($element, &$form_state, $form) {
$element[$to_field]['#prefix'] = '';
// Users with JS enabled will never see initially blank values for the end
// date (see Drupal.date.EndDateHandler()), so hide the message for them.
$description .= '<span class="js-hide"> ' . t("Empty 'End date' values will use the 'Start date' values.") . '</span>';
$element['#fieldset_description'] = $description;
$element['#description'] .= '<span class="js-hide"> ' . t("Empty 'End date' values will use the 'Start date' values.") . '</span>';
if ($field['settings']['todate'] == 'optional') {
$element[$to_field]['#states'] = array(
'visible' => array(
'input[name="' . $show_id . '"]' => array('checked' => TRUE),
));
'input[name="' . $show_id . '"]' => array(
'checked' => TRUE,
),
),
);
}
}
else {
$element[$from_field]['#description'] = $description;
}
// Create label for error messages that make sense in multiple values
// and when the title field is left blank.
@@ -400,19 +411,30 @@ function date_combo_element_process($element, &$form_state, $form) {
$element[$to_field]['#date_title'] = t('@field_name End date', array('@field_name' => $instance['label']));
}
else {
$element[$from_field]['#date_title'] = $instance['label'];
$element[$from_field]['#date_title'] = t('@field_name', array('@field_name' => $instance['label']));
}
// Make changes if instance is set to be rendered as a regular field.
if (!empty($instance['widget']['settings']['no_fieldset'])) {
unset($element[$from_field]['#description']);
if (!empty($field['settings']['todate']) && isset($element['#description'])) {
$element['#description'] .= '<span class="js-hide"> ' . t("Empty 'End date' values will use the 'Start date' values.") . '</span>';
}
}
$context = array(
'field' => $field,
'instance' => $instance,
'form' => $form,
'field' => $field,
'instance' => $instance,
'form' => $form,
);
drupal_alter('date_combo_process', $element, $form_state, $context);
return $element;
}
/**
* Empty a date element.
*/
function date_element_empty($element, &$form_state) {
$item = array();
$item['value'] = NULL;
@@ -427,6 +449,7 @@ function date_element_empty($element, &$form_state) {
/**
* Validate and update a combo element.
*
* Don't try this if there were errors before reaching this point.
*/
function date_combo_validate($element, &$form_state) {
@@ -443,16 +466,26 @@ function date_combo_validate($element, &$form_state) {
$delta = $element['#delta'];
$langcode = $element['#language'];
// Related issue: https://drupal.org/node/2279831.
if (!is_array($element['#field_parents'])) {
$element['#field_parents'] = array();
}
$form_values = drupal_array_get_nested_value($form_state['values'], $element['#field_parents']);
$form_input = drupal_array_get_nested_value($form_state['input'], $element['#field_parents']);
// Programmatically calling drupal_submit_form() does not always add the date
// combo to $form_state['input'].
if (empty($form_input[$field_name]) && !empty($form_values[$field_name])) {
form_set_value($element, $element['#date_items'], $form_state);
return;
}
// If the whole field is empty and that's OK, stop now.
if (empty($form_input[$field_name]) && !$element['#required']) {
return;
}
$item = $form_values[$field_name][$langcode][$delta];
$posted = $form_input[$field_name][$langcode][$delta];
$item = drupal_array_get_nested_value($form_state['values'], $element['#parents']);
$posted = drupal_array_get_nested_value($form_state['input'], $element['#parents']);
$field = field_widget_field($element, $form_state);
$instance = field_widget_instance($element, $form_state);
@@ -518,11 +551,7 @@ function date_combo_validate($element, &$form_state) {
return;
}
}
// Don't look for further errors if errors are already flagged
// because otherwise we'll show errors on the nested elements
// more than once.
elseif (!form_get_errors()) {
else {
$timezone = !empty($item[$tz_field]) ? $item[$tz_field] : $element['#date_timezone'];
$timezone_db = date_get_timezone_db($field['settings']['tz_handling']);
$element[$from_field]['#date_timezone'] = $timezone;
@@ -597,7 +626,10 @@ function date_combo_validate($element, &$form_state) {
}
}
}
if (!empty($errors)) {
// 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)));
}
@@ -1,16 +1,12 @@
name = Date Migration
description = Provides support for importing into date fields with the Migrate module.
description = Obsolete data migration module. Disable if no other modules depend on it.
core = 7.x
package = Date/Time
hidden = TRUE
dependencies[] = migrate
dependencies[] = date
files[] = date.migrate.inc
files[] = date_migrate.test
; Information added by drupal.org packaging script on 2012-08-13
version = "7.x-2.6"
; Information added by Drupal.org packaging script on 2017-04-07
version = "7.x-2.10"
core = "7.x"
project = "date"
datestamp = "1344850024"
datestamp = "1491562090"
@@ -2,15 +2,5 @@
/**
* @file
* Migration integration for Date Migrate.
* Obsolete migration integration for Date - now in Date itself.
*/
/**
* Implements hook_migrate_api().
*/
function date_migrate_migrate_api() {
$api = array(
'api' => 2,
);
return $api;
}
@@ -2,7 +2,6 @@ core = "7.x"
dependencies[] = "date"
dependencies[] = "date_repeat"
dependencies[] = "date_repeat_field"
dependencies[] = "date_migrate"
dependencies[] = "features"
dependencies[] = "migrate"
description = "Examples of migrating with the Date module"
@@ -21,9 +20,9 @@ package = "Features"
project = "date_migrate_example"
version = "7.x-2.0"
; Information added by drupal.org packaging script on 2012-08-13
version = "7.x-2.6"
; Information added by Drupal.org packaging script on 2017-04-07
version = "7.x-2.10"
core = "7.x"
project = "date"
datestamp = "1344850024"
datestamp = "1491562090"
@@ -12,3 +12,12 @@ function date_migrate_example_disable() {
Migration::deregisterMigration('DateExample');
}
/**
* Implements hook_uninstall().
*/
function date_migrate_example_uninstall() {
node_type_delete('date_migrate_example');
variable_del('node_preview_date_migrate_example');
node_types_rebuild();
menu_rebuild();
}
@@ -47,8 +47,8 @@ class DateExampleMigration extends XMLMigration {
$xml_folder = drupal_get_path('module', 'date_migrate_example');
$items_url = $xml_folder . '/date_migrate_example.xml';
$item_xpath = '/source_data/item';
$item_ID_xpath = 'id';
$items_class = new MigrateItemsXML($items_url, $item_xpath, $item_ID_xpath);
$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_migrate_example');
@@ -66,28 +66,34 @@ class DateExampleMigration extends XMLMigration {
// 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)
$arguments = DateMigrateFieldHandler::arguments('Asia/Tokyo');
// field (Tokyo is UTC+9, no DST).
$this->addFieldMapping('field_datetime', 'datetime')
->xpath('datetime')
->arguments($arguments);
->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'));
$this->addUnmigratedDestinations(array('is_new', 'status', 'promote',
'revision', 'language', 'sticky', 'created', 'changed', 'revision_uid'));
}
/**
@@ -101,30 +107,25 @@ class DateExampleMigration extends XMLMigration {
// The date range field can have multiple values.
$current_row->date_range_from = array();
foreach ($current_row->xml->date_range as $range) {
$date_data = array(
'from' => (string) $range->from[0],
'to' => (string) $range->to[0],
);
$current_row->date_range_from[] = drupal_json_encode($date_data);
$current_row->date_range_from[] = (string) $range->from[0];
$current_row->date_range_to[] = (string) $range->to[0];
}
$date_data = array(
'from' => (string) $current_row->xml->datestamp_range->from[0],
'to' => (string) $current_row->xml->datestamp_range->to[0],
);
$current_row->datestamp_range_from = drupal_json_encode($date_data);
$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];
$date_data = array(
'from' => (string) $current_row->xml->datetime_range->from[0],
'to' => (string) $current_row->xml->datetime_range->to[0],
'timezone' => (string) $current_row->xml->datetime_range->timezone[0],
);
$current_row->datetime_range_from = drupal_json_encode($date_data);
$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];
$date_data = array(
'from' => (string) $current_row->xml->date_repeat->date[0],
'rrule' => (string) $current_row->xml->date_repeat->rule[0],
);
$current_row->date_repeat = drupal_json_encode($date_data);
$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];
}
}
@@ -13,6 +13,9 @@ include_once 'date_migrate_example.features.inc';
function date_migrate_example_migrate_api() {
$api = array(
'api' => 2,
'migrations' => array(
'DateExample' => array('class_name' => 'DateExampleMigration')
),
);
return $api;
}
@@ -18,7 +18,7 @@ Timepicker
================================================================================
There are three ways to let users select time in the Date Popup widgets.
You can choose between them by going to admin/config/content/date_popup.
You can choose between them by going to admin/config/date/date_popup.
The options are:
@@ -99,10 +99,10 @@ Example:
$form['date'] = array(
'#type' => 'date_popup',
'#default_value' => '2007-01-01 10:30:00,
'#default_value' => '2007-01-01 10:30:00',
'#date_type' => DATE_DATETIME,
'#date_timezone' => date_default_timezone(),
'#date_format' => 'm/d/Y - H:i',
'#date_format' => 'm-d-Y H:i',
'#date_increment' => 1,
'#date_year_range' => '-3:+3',
);
@@ -7,9 +7,9 @@ configure = admin/config/date/date_popup
stylesheets[all][] = themes/datepicker.1.7.css
; Information added by drupal.org packaging script on 2012-08-13
version = "7.x-2.6"
; Information added by Drupal.org packaging script on 2017-04-07
version = "7.x-2.10"
core = "7.x"
project = "date"
datestamp = "1344850024"
datestamp = "1491562090"
@@ -5,6 +5,7 @@
* Install, update and uninstall functions for the Date Popup module.
*/
// @codingStandardsIgnoreStart
/**
* Implements hook_install().
*/
@@ -17,6 +18,7 @@ function date_popup_install() {
function date_popup_uninstall() {
}
// @codingStandardsIgnoreEnd
/**
* Implements hook_enable().
@@ -1,62 +1,73 @@
/**
* Attaches the calendar behavior to all required fields
*/
(function ($) {
Drupal.behaviors.date_popup = {
attach: function (context) {
for (var id in Drupal.settings.datePopup) {
$('#'+ id).bind('focus', Drupal.settings.datePopup[id], function(e) {
if (!$(this).hasClass('date-popup-init')) {
var datePopup = e.data;
// Explicitely filter the methods we accept.
switch (datePopup.func) {
case 'datepicker':
$(this)
.datepicker(datePopup.settings)
.addClass('date-popup-init')
$(this).click(function(){
$(this).focus();
(function($) {
function makeFocusHandler(e) {
if (!$(this).hasClass('date-popup-init')) {
var datePopup = e.data;
// Explicitely filter the methods we accept.
switch (datePopup.func) {
case 'datepicker':
$(this)
.datepicker(datePopup.settings)
.addClass('date-popup-init');
$(this).click(function(){
$(this).focus();
});
if (datePopup.settings.syncEndDate) {
$('.start-date-wrapper').each(function(){
var start_date_wrapper = this;
$(this).find('input:eq(0)').change(function(){
$(start_date_wrapper).next('.end-date-wrapper').find('input:eq(0)').val($(this).val());
});
});
break;
}
break;
case 'timeEntry':
$(this)
.timeEntry(datePopup.settings)
.addClass('date-popup-init')
$(this).click(function(){
$(this).focus();
});
break;
case 'timepicker':
// Translate the PHP date format into the style the timepicker uses.
datePopup.settings.timeFormat = datePopup.settings.timeFormat
// 12-hour, leading zero,
.replace('h', 'hh')
// 12-hour, no leading zero.
.replace('g', 'h')
// 24-hour, leading zero.
.replace('H', 'HH')
// 24-hour, no leading zero.
.replace('G', 'H')
// AM/PM.
.replace('A', 'p')
// Minutes with leading zero.
.replace('i', 'mm')
// Seconds with leading zero.
.replace('s', 'ss');
case 'timeEntry':
$(this)
.timeEntry(datePopup.settings)
.addClass('date-popup-init');
$(this).click(function(){
$(this).focus();
});
break;
datePopup.settings.startTime = new Date(datePopup.settings.startTime);
$(this)
.timepicker(datePopup.settings)
.addClass('date-popup-init');
$(this).click(function(){
$(this).focus();
});
break;
}
case 'timepicker':
// Translate the PHP date format into the style the timepicker uses.
datePopup.settings.timeFormat = datePopup.settings.timeFormat
// 12-hour, leading zero,
.replace('h', 'hh')
// 12-hour, no leading zero.
.replace('g', 'h')
// 24-hour, leading zero.
.replace('H', 'HH')
// 24-hour, no leading zero.
.replace('G', 'H')
// AM/PM.
.replace('A', 'p')
// Minutes with leading zero.
.replace('i', 'mm')
// Seconds with leading zero.
.replace('s', 'ss');
datePopup.settings.startTime = new Date(datePopup.settings.startTime);
$(this)
.timepicker(datePopup.settings)
.addClass('date-popup-init');
$(this).click(function(){
$(this).focus();
});
break;
}
});
}
}
}
};
Drupal.behaviors.date_popup = {
attach: function (context) {
for (var id in Drupal.settings.datePopup) {
$('#'+ id).bind('focus', Drupal.settings.datePopup[id], makeFocusHandler);
}
}
};
})(jQuery);
@@ -16,7 +16,6 @@
* If no time elements are included in the format string, only the date
* textfield will be created. If no date elements are included in the format
* string, only the time textfield, will be created.
*
*/
/**
@@ -44,7 +43,7 @@ function date_popup_add() {
/**
* Get the location of the Willington Vega timepicker library.
*
* @return
* @return string
* The location of the library, or FALSE if the library isn't installed.
*/
function date_popup_get_wvega_path() {
@@ -87,16 +86,18 @@ function date_popup_library() {
$path . '/jquery.timeentry.pack.js' => array(),
),
'css' => array(
$path . '/themes/jquery.timeentry.css' => array('preprocess' => FALSE),
$path . '/themes/jquery.timeentry.css' => array(),
),
);
return $libraries;
}
/**
* Create a unique CSS id name and output a single inline JS block for
* each startup function to call and settings array to pass it. This
* used to create a unique CSS class for each unique combination of
* Create a unique CSS id name and output a single inline JS block.
*
* For each startup function to call and settings array to pass it.
*
* This used to create a unique CSS class for each unique combination of
* function and settings, but using classes requires a DOM traversal
* and is much slower than an id lookup. The new approach returns to
* requiring a duplicate copy of the settings/code for every element
@@ -104,17 +105,20 @@ function date_popup_library() {
* putting the ids for each unique function/settings combo into
* Drupal.settings and searching for each listed id.
*
* @param $pfx
* @param string $id
* The CSS class prefix to search the DOM for.
* TODO : unused ?
* @param $func
* The jQuery function to invoke on each DOM element containing the
* returned CSS class.
* @param $settings
*
* @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.
*
* @returns
* The CSS id to assign to the element that should have
* $func($settings) invoked on it.
* The CSS id to assign to the element that should have $func($settings)
* invoked on it.
*/
function date_popup_js_settings_id($id, $func, $settings) {
static $js_added = FALSE;
@@ -123,14 +127,15 @@ 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
$defaultDate = ($parts[0] > 0 || 0 > $parts[1]) ? $parts[0] : 0;
$settings += array('defaultDate' => (string) $defaultDate . 'y');
// 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');
}
if (!$js_added) {
drupal_add_js(drupal_get_path('module', 'date_popup') .'/date_popup.js');
drupal_add_js(drupal_get_path('module', 'date_popup') . '/date_popup.js');
$js_added = TRUE;
}
@@ -140,29 +145,35 @@ 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";
$return_id = "$id-$func-popup-". $id_count[$id]++;
// It looks like we need the additional id_count for this to
// work correctly when there are multiple values.
// $return_id = "$id-$func-popup";
$return_id = "$id-$func-popup-" . $id_count[$id]++;
$js_settings['datePopup'][$return_id] = array(
'func' => $func,
'settings' => $settings
'settings' => $settings,
);
drupal_add_js($js_settings, 'setting');
return $return_id;
}
/**
* Date popup theme handler.
*/
function date_popup_theme() {
return array(
'date_popup' => array('render element' => 'element'),
);
'date_popup' => array(
'render element' => 'element',
),
);
}
/**
* 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).
* 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,
@@ -218,20 +229,32 @@ function date_popup_element_info() {
return $type;
}
/**
* Date popup date granularity.
*/
function date_popup_date_granularity($element) {
$granularity = date_format_order($element['#date_format']);
return array_intersect($granularity, array('month', 'day', 'year'));
}
/**
* Date popup time granularity.
*/
function date_popup_time_granularity($element) {
$granularity = date_format_order($element['#date_format']);
return array_intersect($granularity, array('hour', 'minute', 'second'));
}
/**
* Date popup date format.
*/
function date_popup_date_format($element) {
return (date_limit_format($element['#date_format'], date_popup_date_granularity($element)));
}
/**
* Date popup time format.
*/
function date_popup_time_format($element) {
return date_popup_format_to_popup_time(date_limit_format($element['#date_format'], date_popup_time_granularity($element)), $element['#timepicker']);
}
@@ -239,6 +262,7 @@ 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);
@@ -266,9 +290,11 @@ function date_popup_element_value_callback($element, $input = FALSE, &$form_stat
return $return;
}
// @codingStandardsIgnoreEnd
/**
* Javascript popup element processing.
*
* Add popup attributes to $element.
*/
function date_popup_element_process($element, &$form_state, $form) {
@@ -284,7 +310,9 @@ function date_popup_element_process($element, &$form_state, $form) {
if (!empty($element['#ajax'])) {
$element['#ajax'] += array(
'trigger_as' => array('name' =>$element['#name']),
'trigger_as' => array(
'name' => $element['#name'],
),
'event' => 'change',
);
}
@@ -292,6 +320,18 @@ function date_popup_element_process($element, &$form_state, $form) {
$element['date'] = date_popup_process_date_part($element);
$element['time'] = date_popup_process_time_part($element);
// Make changes if instance is set to be rendered as a regular field.
if (!empty($element['#instance']['widget']['settings']['no_fieldset']) && $element['#field']['cardinality'] == 1) {
if (!empty($element['date']) && empty($element['time'])) {
$element['date']['#title'] = check_plain($element['#instance']['label']);
$element['date']['#required'] = $element['#required'];
}
elseif (empty($element['date']) && !empty($element['time'])) {
$element['time']['#title'] = check_plain($element['#instance']['label']);
$element['time']['#required'] = $element['#required'];
}
}
if (isset($element['#element_validate'])) {
array_push($element['#element_validate'], 'date_popup_validate');
}
@@ -300,7 +340,7 @@ function date_popup_element_process($element, &$form_state, $form) {
}
$context = array(
'form' => $form,
'form' => $form,
);
drupal_alter('date_popup_process', $element, $form_state, $context);
@@ -313,13 +353,22 @@ function date_popup_element_process($element, &$form_state, $form) {
function date_popup_process_date_part(&$element) {
$granularity = date_format_order($element['#date_format']);
$date_granularity = date_popup_date_granularity($element);
if (empty($date_granularity)) return array();
if (empty($date_granularity)) {
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)
// 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.
$mock = NULL;
$callback_values = date_popup_element_value_callback($element, FALSE, $mock);
if (!isset($element['#value']['date']) && isset($callback_values['date'])) {
$element['#value']['date'] = $callback_values['date'];
}
$date = '';
if (!empty($element['#value']['date'])) {
$date = new DateObject($element['#value']['date'], $element['#date_timezone'], date_popup_date_format($element));
@@ -336,36 +385,49 @@ function date_popup_process_date_part(&$element) {
'closeAtTop' => FALSE,
'speed' => 'immediate',
'firstDay' => intval(variable_get('date_first_day', 0)),
//'buttonImage' => base_path() . drupal_get_path('module', 'date_api') ."/images/calendar.png",
//'buttonImageOnly' => TRUE,
// 'buttonImage' => base_path()
// . drupal_get_path('module', 'date_api') ."/images/calendar.png",
// 'buttonImageOnly' => TRUE,
'dateFormat' => date_popup_format_to_popup(date_popup_date_format($element), 'datepicker'),
'yearRange' => $year_range,
// Custom setting, will be expanded in Drupal.behaviors.date_popup()
'fromTo' => isset($fromto),
);
if (!empty($element['#instance'])) {
$settings['syncEndDate'] = $element['#instance']['settings']['default_value2'] == 'sync';
}
// 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.
$parents = array_merge($element['#parents'], array('date'));
$sub_element = array(
'#type' => 'textfield',
'#title' => $element['#date_label_position'] == 'above' ? theme('date_part_label_date', array('part_type' => 'date', 'element' => $element)) : '',
'#default_value' => $element['#value']['date'],
'#title' => theme('date_part_label_date', array('part_type' => 'date', 'element' => $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) .']',
'#name' => array_shift($parents) . '[' . implode('][', $parents) . ']',
'#ajax' => !empty($element['#ajax']) ? $element['#ajax'] : FALSE,
);
$sub_element['#value'] = $sub_element['#default_value'];
// TODO, figure out exactly when we want this description. In many places it is not desired.
$sub_element['#description'] = ' '. t('E.g., @date', array('@date' => date_format_date(date_example_date(), 'custom', date_popup_date_format($element))));
// TODO, figure out exactly when we want this description.
// In many places it is not desired.
$sub_element['#description'] = ' ' . t('E.g., @date', array(
'@date' => date_format_date(
date_example_date(),
'custom',
date_popup_date_format($element)
),
));
return $sub_element;
}
@@ -376,7 +438,17 @@ function date_popup_process_date_part(&$element) {
function date_popup_process_time_part(&$element) {
$granularity = date_format_order($element['#date_format']);
$has_time = date_has_time($granularity);
if (empty($has_time)) return array();
if (empty($has_time)) {
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.
$mock = NULL;
$callback_values = date_popup_element_value_callback($element, FALSE, $mock);
if (!isset($element['#value']['time']) && isset($callback_values['time'])) {
$element['#value']['time'] = $callback_values['time'];
}
switch ($element['#timepicker']) {
case 'default':
@@ -384,10 +456,14 @@ function date_popup_process_time_part(&$element) {
$settings = array(
'show24Hours' => strpos($element['#date_format'], 'H') !== FALSE ? TRUE : FALSE,
'showSeconds' => (in_array('second', $granularity) ? TRUE : FALSE),
'timeSteps' => array(1, intval($element['#date_increment']), (in_array('second', $granularity) ? $element['#date_increment'] : 0)),
'timeSteps' => array(
1,
intval($element['#date_increment']),
(in_array('second', $granularity) ? $element['#date_increment'] : 0),
),
'spinnerImage' => '',
'fromTo' => isset($fromto),
);
);
if (strpos($element['#date_format'], 'a') !== FALSE) {
// Then we are using lowercase am/pm.
$settings['ampmNames'] = array('am', 'pm');
@@ -396,14 +472,17 @@ function date_popup_process_time_part(&$element) {
$settings['ampmPrefix'] = ' ';
}
break;
case 'wvega':
$func = 'timepicker';
$time_granularity = array_intersect($granularity, array('hour', 'minute', 'second'));
$grans = array('hour', 'minute', 'second');
$time_granularity = array_intersect($granularity, $grans);
$format = date_popup_format_to_popup_time(date_limit_format($element['#date_format'], $time_granularity), 'wvega');
$default_value = isset($element['#default_value']) ? $element['#default_value'] : '';
// The first value in the dropdown list should be the same as the element
// default_value, but it needs to be in JS format (i.e. milliseconds since
// the epoch).
$start_time = new DateObject($element['#default_value'], $element['#date_timezone'], DATE_FORMAT_DATETIME);
$start_time = new DateObject($default_value, $element['#date_timezone'], DATE_FORMAT_DATETIME);
date_increment_round($start_time, $element['#date_increment']);
$start_time = $start_time->format(DATE_FORMAT_UNIX) * 1000;
$settings = array(
@@ -413,6 +492,7 @@ function date_popup_process_time_part(&$element) {
'scrollbar' => TRUE,
);
break;
default:
$func = '';
$settings = array();
@@ -422,28 +502,35 @@ function date_popup_process_time_part(&$element) {
// 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',
'#title' => $element['#date_label_position'] == 'above' ? theme('date_part_label_time', array('part_type' => 'time', 'element' => $element)) : '',
'#title' => theme('date_part_label_time', array('part_type' => 'time', 'element' => $element)),
'#title_display' => $element['#date_label_position'] == 'above' ? 'before' : 'invisible',
'#default_value' => $element['#value']['time'],
'#id' => $id,
'#size' => 15,
'#maxlength' => 10,
'#attributes' => $element['#attributes'],
'#parents' => $parents,
'#name' => array_shift($parents) . '['. implode('][', $parents) .']',
'#name' => array_shift($parents) . '[' . implode('][', $parents) . ']',
'#ajax' => !empty($element['#ajax']) ? $element['#ajax'] : FALSE,
);
$sub_element['#value'] = $sub_element['#default_value'];
// TODO, figure out exactly when we want this description. In many places it is not desired.
// TODO, figure out exactly when we want this description.
// In many places it is not desired.
$example_date = date_now();
date_increment_round($example_date, $element['#date_increment']);
$sub_element['#description'] = t('E.g., @date', array('@date' => date_format_date($example_date, 'custom', date_popup_time_format($element))));
$sub_element['#description'] = t('E.g., @date', array(
'@date' => date_format_date(
$example_date,
'custom',
date_popup_time_format($element)
)));
return ($sub_element);
}
@@ -454,7 +541,6 @@ function date_popup_process_time_part(&$element) {
* 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_popup_validate($element, &$form_state) {
@@ -471,6 +557,11 @@ function date_popup_validate($element, &$form_state) {
$input_exists = NULL;
$input = drupal_array_get_nested_value($form_state['values'], $element['#parents'], $input_exists);
// If the date is a string, it is not considered valid and can cause problems
// later on, so just exit out now.
if (is_string($input)) {
return;
}
drupal_alter('date_popup_pre_validate', $element, $form_state, $input);
@@ -479,16 +570,22 @@ function date_popup_validate($element, &$form_state) {
$time_granularity = date_popup_time_granularity($element);
$has_time = date_has_time($granularity);
$label = !empty($element['#date_title']) ? $element['#date_title'] : (!empty($element['#title']) ? $element['#title'] : '');
$label = t($label);
// @codingStandardsIgnoreStart
$label = '';
if (!empty($element['#date_title'])) {
$label = t($element['#date_title']);
}
elseif (!empty($element['#title'])) {
$label = t($element['#title']);
}
// @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.
$error_field = implode('][', $element['#parents']);
if (empty($date) || !empty($date->errors)) {
if ((empty($element['#value']['date']) && empty($element['#value']['time'])) || !empty($date->errors)) {
if (is_object($date) && !empty($date->errors)) {
$message = t('The value input for field %field is invalid:', array('%field' => $label));
$message .= '<br />' . implode('<br />', $date->errors);
@@ -515,13 +612,15 @@ function date_popup_validate($element, &$form_state) {
/**
* Helper function for extracting a date value out of user input.
*
* @param autocomplete
* @param bool $auto_complete
* Should we add a time value to complete the date if there is no time?
* 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'])) {
return NULL;
//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;
}
date_popup_add();
$granularity = date_format_order($element['#date_format']);
@@ -530,9 +629,14 @@ function date_popup_input_date($element, $input, $auto_complete = FALSE) {
$format = date_popup_date_format($element);
$format .= $has_time ? ' ' . date_popup_time_format($element) : '';
$datetime = $input['date'];
$datetime .= $has_time ? ' ' . $input['time'] : '';
//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']) ){
$date->timeOnly = 'TRUE';
}
if (is_object($date)) {
$date->limitGranularity($granularity);
if ($date->validGranularity($granularity, $flexible)) {
@@ -550,7 +654,7 @@ function date_popup_time_formats($with_seconds = FALSE) {
return array(
'H:i:s',
'h:i:sA',
);
);
}
/**
@@ -559,8 +663,17 @@ function date_popup_time_formats($with_seconds = FALSE) {
* TODO Remove any formats not supported by the widget, if any.
*/
function date_popup_formats() {
$formats = str_replace('i', 'i:s', array_keys(system_get_date_formats('short')));
// Load short date formats.
$formats = system_get_date_formats('short');
// Load custom date formats.
if ($formats_custom = system_get_date_formats('custom')) {
$formats = array_merge($formats, $formats_custom);
}
$formats = str_replace('i', 'i:s', array_keys($formats));
$formats = drupal_map_assoc($formats);
return $formats;
}
@@ -568,7 +681,8 @@ function date_popup_formats() {
* Recreate a date format string so it has the values popup expects.
*
* @param string $format
* a normal date format string, like Y-m-d
* 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'
@@ -586,15 +700,34 @@ function date_popup_format_to_popup($format) {
* Recreate a time format string so it has the values popup expects.
*
* @param string $format
* a normal time format string, like h:i (a)
* A normal time format string, like h:i (a)
*
* @return string
* a format string that the popup can accept like h:i a
* A format string that the popup can accept like h:i a
*/
function date_popup_format_to_popup_time($format, $timepicker = NULL) {
if (empty($format)) {
$format = 'H:i';
}
$format = str_replace(array('/', '-', ' .', ',', 'F', 'M', 'l', 'z', 'w', 'W', 'd', 'j', 'm', 'n', 'y', 'Y'), '', $format);
$symbols = array(
'/',
'-',
' .',
',',
'F',
'M',
'l',
'z',
'w',
'W',
'd',
'j',
'm',
'n',
'y',
'Y',
);
$format = str_replace($symbols, '', $format);
$format = strtr($format, date_popup_timepicker_format_replacements($timepicker));
return $format;
}
@@ -603,9 +736,10 @@ function date_popup_format_to_popup_time($format, $timepicker = NULL) {
* Reconstruct popup format string into normal format string.
*
* @param string $format
* a string in popup format, like YMD-
* A string in popup format, like YMD-
*
* @return string
* a normal date format string, like Y-m-d
* A normal date format string, like Y-m-d
*/
function date_popup_popup_to_format($format) {
$replace = array_flip(date_popup_datepicker_format_replacements());
@@ -619,22 +753,21 @@ function date_popup_popup_to_format($format) {
* This function returns a map of format replacements required to change any
* input format into one that the given timepicker can support.
*
* @param $timepicker
* @param string $timepicker
* The time entry plugin being used: either 'wvega' or 'default'.
* @return
*
* @return array
* A map of replacements.
*/
function date_popup_timepicker_format_replacements($timepicker = 'default') {
switch ($timepicker) {
case 'wvega':
return array(
'a' => 'A', // The wvega timepicker only supports uppercase AM/PM.
);
// The wvega timepicker only supports uppercase AM/PM.
return array('a' => 'A');
default:
return array(
'G' => 'H', // The default timeEntry plugin requires leading zeros.
'g' => 'h',
);
// The default timeEntry plugin requires leading zeros.
return array('G' => 'H', 'g' => 'h');
}
}
@@ -643,16 +776,16 @@ function date_popup_timepicker_format_replacements($timepicker = 'default') {
*/
function date_popup_datepicker_format_replacements() {
return array(
'd' => 'dd',
'j' => 'd',
'l' => 'DD',
'D' => 'D',
'm' => 'mm',
'n' => 'm',
'F' => 'MM',
'M' => 'M',
'Y' => 'yy',
'y' => 'y',
'd' => 'dd',
'j' => 'd',
'l' => 'DD',
'D' => 'D',
'm' => 'mm',
'n' => 'm',
'F' => 'MM',
'M' => 'M',
'Y' => 'yy',
'y' => 'y',
);
}
@@ -665,16 +798,26 @@ 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>';
$element['#children'] = '<div id="' . $element['#id'] . '" ' . drupal_attributes($wrapper_attributes) . '>' . $element['#children'] . '</div>';
}
return '<div ' . drupal_attributes($attributes) .'>' . theme('form_element', $element) . '</div>';
return '<div ' . drupal_attributes($attributes) . '>' . theme('form_element', $element) . '</div>';
}
/**
* Implements hook_date_field_instance_settings_form_alter().
*/
function date_popup_date_field_instance_settings_form_alter(&$form, $context) {
// Add an extra option to sync the end date with the start date.
$form['default_value2']['#options']['sync'] = t('Sync with start date');
}
/**
@@ -705,8 +848,8 @@ function date_popup_settings() {
'#type' => 'select',
'#options' => array(
'default' => t('Use default jQuery timepicker'),
'wvega' => t('Use dropdown timepicker'),
'none' => t('Manual time entry, no jQuery timepicker')
'wvega' => t('Use dropdown timepicker'),
'none' => t('Manual time entry, no jQuery timepicker'),
),
'#title' => t('Timepicker'),
'#default_value' => variable_get('date_popup_timepicker', $preferred_timepicker),
@@ -732,7 +875,7 @@ function date_popup_settings() {
}
EOM;
$form['#suffix'] = t('<p>The Date Popup calendar includes some css for IE6 that breaks css validation. Since IE 6 is now superceded by IE 7, 8, and 9, the special css for IE 6 has been removed from the regular css used by the Date Popup. If you find you need that css after all, you can add it back in your theme. Look at the way the Garland theme adds special IE-only css in in its page.tpl.php file. The css you need is:</p>') .'<blockquote><PRE>' . $css .'</PRE></blockquote>';
$form['#suffix'] = t('<p>The Date Popup calendar includes some css for IE6 that breaks css validation. Since IE 6 is now superceded by IE 7, 8, and 9, the special css for IE 6 has been removed from the regular css used by the Date Popup. If you find you need that css after all, you can add it back in your theme. Look at the way the Garland theme adds special IE-only css in in its page.tpl.php file. The css you need is:</p>') . '<blockquote><PRE>' . $css . '</PRE></blockquote>';
return system_settings_form($form);
}
File diff suppressed because one or more lines are too long
@@ -2,5 +2,6 @@
/**
* @file
* Empty file to avoid fatal error if it doesn't exist.
*
* Formerly the Date Repeat field code.
*/
*/
@@ -7,9 +7,9 @@ php = 5.2
files[] = tests/date_repeat.test
files[] = tests/date_repeat_form.test
; Information added by drupal.org packaging script on 2012-08-13
version = "7.x-2.6"
; Information added by Drupal.org packaging script on 2017-04-07
version = "7.x-2.10"
core = "7.x"
project = "date"
datestamp = "1344850024"
datestamp = "1491562090"
@@ -12,21 +12,3 @@ function date_repeat_install() {
// Make sure this module loads after date_api.
db_query("UPDATE {system} SET weight = 1 WHERE name = 'date_repeat'");
}
/**
* Implements hook_uninstall().
*/
function date_repeat_uninstall() {
}
/**
* Implements hook_enable().
*/
function date_repeat_enable() {
}
/**
* Implements hook_disable().
*/
function date_repeat_disable() {
}
@@ -1,7 +1,6 @@
<?php
/**
* @file
*
* This module creates a form element that allows users to select
* repeat rules for a date, and reworks the result into an iCal
* RRULE string that can be stored in the database.
@@ -11,8 +10,8 @@
*
* Other modules can use this API to add self-validating form elements
* to their dates, and identify dates that meet the RRULE criteria.
*
*/
/**
* Implements hook_element_info().
*/
@@ -35,6 +34,9 @@ function date_repeat_element_info() {
return $type;
}
/**
* Implements hook_theme().
*/
function date_repeat_theme() {
return array(
'date_repeat_current_exceptions' => array('render element' => 'element'),
@@ -55,6 +57,9 @@ function date_repeat_freq_options() {
);
}
/**
* Helper function for interval options.
*/
function date_repeat_interval_options() {
$options = range(0, 366);
unset($options[0]);
@@ -92,9 +97,11 @@ function date_repeat_dow_day_options_abbr($translated = TRUE, $length = 3) {
case 1:
$context = 'day_abbr1';
break;
case 2:
$context = 'day_abbr2';
break;
default:
$context = '';
break;
@@ -105,16 +112,28 @@ function date_repeat_dow_day_options_abbr($translated = TRUE, $length = 3) {
return $return;
}
/**
* Helper function for weekdays translated.
*/
function date_repeat_dow_day_untranslated() {
static $date_repeat_weekdays;
if (empty($date_repeat_weekdays)) {
$date_repeat_weekdays = array('SU' => 'Sunday', 'MO' => 'Monday', 'TU' => 'Tuesday',
'WE' => 'Wednesday', 'TH' => 'Thursday', 'FR' => 'Friday',
'SA' => 'Saturday');
$date_repeat_weekdays = array(
'SU' => 'Sunday',
'MO' => 'Monday',
'TU' => 'Tuesday',
'WE' => 'Wednesday',
'TH' => 'Thursday',
'FR' => 'Friday',
'SA' => 'Saturday'
);
}
return $date_repeat_weekdays;
}
/**
* Helper function for weekdays order.
*/
function date_repeat_dow_day_options_ordered($weekdays) {
$day_keys = array_keys($weekdays);
$day_values = array_values($weekdays);
@@ -164,8 +183,7 @@ 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 for a specific week start day.
*/
function date_repeat_days_ordered($week_start_day) {
$days = array_flip(array_keys(date_repeat_dow_day_options(FALSE)));
@@ -212,18 +230,21 @@ function date_repeat_rrule_description($rrule, $format = 'D M d Y') {
'!except' => '',
'!additional' => '',
'!week_starts_on' => '',
);
);
$interval = date_repeat_interval_options();
switch ($rrule['FREQ']) {
case 'WEEKLY':
$description['!interval'] = format_plural($rrule['INTERVAL'], 'every week', 'every @count weeks') . ' ';
break;
case 'MONTHLY':
$description['!interval'] = format_plural($rrule['INTERVAL'], 'every month', 'every @count months') . ' ';
break;
case 'YEARLY':
$description['!interval'] = format_plural($rrule['INTERVAL'], 'every year', 'every @count years') . ' ';
break;
default:
$description['!interval'] = format_plural($rrule['INTERVAL'], 'every day', 'every @count days') . ' ';
break;
@@ -240,26 +261,41 @@ function date_repeat_rrule_description($rrule, $format = 'D M d Y') {
if (!empty($count)) {
// See if there is a 'pretty' option for this count, i.e. +1 => First.
$order = array_key_exists($count, $counts) ? strtolower($counts[$count]) : $count;
$results[] = trim(t('!repeats_every_interval on the !date_order !day_of_week', array('!repeats_every_interval ' => '', '!date_order' => $order, '!day_of_week' => $days[$day])));
$results[] = trim(t('!repeats_every_interval on the !date_order !day_of_week',
array(
'!repeats_every_interval ' => '',
'!date_order' => $order,
'!day_of_week' => $days[$day]
)));
}
else {
$results[] = trim(t('!repeats_every_interval every !day_of_week', array('!repeats_every_interval ' => '', '!day_of_week' => $days[$day])));
$results[] = trim(t('!repeats_every_interval every !day_of_week',
array('!repeats_every_interval ' => '', '!day_of_week' => $days[$day])));
}
}
$description['!byday'] = implode(' ' . t('and') . ' ', $results);
}
if (!empty($rrule['BYMONTH'])) {
if (sizeof($rrule['BYMONTH']) < 12) {
if (count($rrule['BYMONTH']) < 12) {
$results = array();
$months = date_month_names();
foreach ($rrule['BYMONTH'] as $month) {
$results[] = $months[$month];
}
if (!empty($rrule['BYMONTHDAY'])) {
$description['!bymonth'] = trim(t('!repeats_every_interval on the !month_days of !month_names', array('!repeats_every_interval ' => '', '!month_days' => implode(', ', $rrule['BYMONTHDAY']), '!month_names' => implode(', ', $results))));
$description['!bymonth'] = trim(t('!repeats_every_interval on the !month_days of !month_names',
array(
'!repeats_every_interval ' => '',
'!month_days' => implode(', ', $rrule['BYMONTHDAY']),
'!month_names' => implode(', ', $results)
)));
}
else {
$description['!bymonth'] = trim(t('!repeats_every_interval on !month_names', array('!repeats_every_interval ' => '', '!month_names' => implode(', ', $results))));
$description['!bymonth'] = trim(t('!repeats_every_interval on !month_names',
array(
'!repeats_every_interval ' => '',
'!month_names' => implode(', ', $results)
)));
}
}
}
@@ -267,12 +303,17 @@ function date_repeat_rrule_description($rrule, $format = 'D M d Y') {
$rrule['INTERVAL'] = 1;
}
if (!empty($rrule['COUNT'])) {
$description['!count'] = trim(t('!repeats_every_interval !count times', array('!repeats_every_interval ' => '', '!count' => $rrule['COUNT'])));
$description['!count'] = trim(t('!repeats_every_interval !count times',
array('!repeats_every_interval ' => '', '!count' => $rrule['COUNT'])));
}
if (!empty($rrule['UNTIL'])) {
$until = date_ical_date($rrule['UNTIL'], 'UTC');
date_timezone_set($until, date_default_timezone_object());
$description['!until'] = trim(t('!repeats_every_interval until !until_date', array('!repeats_every_interval ' => '', '!until_date' => date_format_date($until, 'custom', $format))));
$description['!until'] = trim(t('!repeats_every_interval until !until_date',
array(
'!repeats_every_interval ' => '',
'!until_date' => date_format_date($until, 'custom', $format)
)));
}
if ($exceptions) {
$values = array();
@@ -281,11 +322,16 @@ function date_repeat_rrule_description($rrule, $format = 'D M d Y') {
date_timezone_set($except, date_default_timezone_object());
$values[] = date_format_date($except, 'custom', $format);
}
$description['!except'] = trim(t('!repeats_every_interval except !except_dates', array('!repeats_every_interval ' => '', '!except_dates' => implode(', ', $values))));
$description['!except'] = trim(t('!repeats_every_interval except !except_dates',
array(
'!repeats_every_interval ' => '',
'!except_dates' => implode(', ', $values)
)));
}
if (!empty($rrule['WKST'])) {
$day_names = date_repeat_dow_day_options();
$description['!week_starts_on'] = trim(t('!repeats_every_interval where the week start on !day_of_week', array('!repeats_every_interval ' => '', '!day_of_week' => $day_names[trim($rrule['WKST'])])));
$description['!week_starts_on'] = trim(t('!repeats_every_interval where the week start on !day_of_week',
array('!repeats_every_interval ' => '', '!day_of_week' => $day_names[trim($rrule['WKST'])])));
}
if ($additions) {
$values = array();
@@ -294,9 +340,15 @@ function date_repeat_rrule_description($rrule, $format = 'D M d Y') {
date_timezone_set($add, date_default_timezone_object());
$values[] = date_format_date($add, 'custom', $format);
}
$description['!additional'] = trim(t('Also includes !additional_dates.', array('!additional_dates' => implode(', ', $values))));
$description['!additional'] = trim(t('Also includes !additional_dates.',
array('!additional_dates' => implode(', ', $values))));
}
return t('Repeats !interval !bymonth !byday !count !until !except. !additional', $description);
$output = t('Repeats !interval !bymonth !byday !count !until !except. !additional', $description);
// Removes double whitespaces from Repeat tile.
$output = preg_replace('/\s+/', ' ', $output);
// Removes whitespace before full stop ".", at the end of the title.
$output = str_replace(' .', '.', $output);
return $output;
}
/**
@@ -310,17 +362,17 @@ function date_repeat_split_rrule($rrule) {
$additions = array();
foreach ($parts as $part) {
if (strstr($part, 'RRULE')) {
$RRULE = str_replace('RRULE:', '', $part);
$rrule = (array) date_ical_parse_rrule('RRULE:', $RRULE);
$cleanded_part = str_replace('RRULE:', '', $part);
$rrule = (array) date_ical_parse_rrule('RRULE:', $cleanded_part);
}
elseif (strstr($part, 'EXDATE')) {
$EXDATE = str_replace('EXDATE:', '', $part);
$exceptions = (array) date_ical_parse_exceptions('EXDATE:', $EXDATE);
$exdate = str_replace('EXDATE:', '', $part);
$exceptions = (array) date_ical_parse_exceptions('EXDATE:', $exdate);
unset($exceptions['DATA']);
}
elseif (strstr($part, 'RDATE')) {
$RDATE = str_replace('RDATE:', '', $part);
$additions = (array) date_ical_parse_exceptions('RDATE:', $RDATE);
$rdate = str_replace('RDATE:', '', $part);
$additions = (array) date_ical_parse_exceptions('RDATE:', $rdate);
unset($additions['DATA']);
}
}
@@ -372,7 +424,7 @@ function date_repeat_form_element_radios_process($element) {
'#title_display' => 'invisible',
'#return_value' => $key,
'#default_value' => isset($element['#default_value']) ?
$element['#default_value'] : NULL,
$element['#default_value'] : NULL,
'#attributes' => $element['#attributes'],
'#parents' => $element['#parents'],
'#id' => drupal_html_id('edit-' . implode('-', $parents_for_id)),
@@ -53,9 +53,11 @@ 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.
@@ -91,27 +93,32 @@ function _date_repeat_calc($rrule, $start, $end, $exceptions, $timezone, $additi
}
// Make sure DAILY frequency isn't used in places it won't work;
if (!empty($rrule['BYMONTHDAY']) && !in_array($rrule['FREQ'], array('MONTHLY', 'YEARLY'))) {
if (!empty($rrule['BYMONTHDAY']) &&
!in_array($rrule['FREQ'], array('MONTHLY', 'YEARLY'))) {
$rrule['FREQ'] = 'MONTHLY';
}
elseif (!empty($rrule['BYDAY']) && !in_array($rrule['FREQ'], array('MONTHLY', 'WEEKLY', 'YEARLY'))) {
elseif (!empty($rrule['BYDAY'])
&& !in_array($rrule['FREQ'], array('MONTHLY', 'WEEKLY', 'YEARLY'))) {
$rrule['FREQ'] = 'WEEKLY';
}
}
// Find the time period to jump forward between dates.
switch ($rrule['FREQ']) {
case 'DAILY':
$jump = $interval . ' days';
break;
case 'WEEKLY':
$jump = $interval . ' weeks';
break;
case 'MONTHLY':
$jump = $interval . ' months';
break;
case 'YEARLY':
$jump = $interval . ' years';
break;
case 'DAILY':
$jump = $interval . ' days';
break;
case 'WEEKLY':
$jump = $interval . ' weeks';
break;
case 'MONTHLY':
$jump = $interval . ' months';
break;
case 'YEARLY':
$jump = $interval . ' years';
break;
}
$rrule = date_repeat_adjust_rrule($rrule, $start_date);
@@ -135,7 +142,7 @@ function _date_repeat_calc($rrule, $start, $end, $exceptions, $timezone, $additi
$direction_days[$day] = array(
'direction' => !empty($regs[1]) ? $regs[1] : '+',
'direction_count' => $regs[2],
);
);
}
}
while (!$finished) {
@@ -173,7 +180,7 @@ function _date_repeat_calc($rrule, $start, $end, $exceptions, $timezone, $additi
}
if ($rrule['FREQ'] == 'YEARLY') {
// Back up to first of year and jump.
$current_day = date_repeat_set_year_day($current_day, NULL, 1, '+', $timezone, $modify_time);
$current_day = date_repeat_set_year_day($current_day, NULL, NULL, 1, '+', $timezone, $modify_time);
date_modify($current_day, '+' . $jump . $modify_time);
}
$finished = date_repeat_is_finished($current_day, $days, $count, $end_date);
@@ -198,8 +205,9 @@ 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.
// 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);
@@ -223,18 +231,42 @@ function _date_repeat_calc($rrule, $start, $end, $exceptions, $timezone, $additi
$ordered = date_repeat_days_ordered($week_start_rule);
$ordered_keys = array_flip($ordered);
foreach ($rrule['BYDAY'] as $day) {
preg_match("@(-)?([0-9]+)?([SU|MO|TU|WE|TH|FR|SA]{2})@", trim($day), $regs);
if (!empty($regs[2])) {
// Convert parameters into full day name, count, and direction.
$direction_days[] = array(
'day' => $day_names[$regs[3]],
'direction' => !empty($regs[1]) ? $regs[1] : '+',
'direction_count' => $regs[2],
);
if ($rrule['FREQ'] == 'YEARLY' && !empty($rrule['BYMONTH'])) {
// Additional cycle to apply month preferences.
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.
if (!empty($regs[2])) {
$direction_days[] = array(
'day' => $day_names[$regs[3]],
'direction' => !empty($regs[1]) ? $regs[1] : '+',
'direction_count' => $regs[2],
'month' => strlen($month) > 1 ? $month : '0' . $month,
);
}
else {
$week_days[$ordered_keys[$regs[3]]] = $day_names[$regs[3]];
}
}
}
else {
$week_days[$ordered_keys[$regs[3]]] = $day_names[$regs[3]];
}
else {
foreach ($rrule['BYDAY'] as $day) {
preg_match("@(-)?([0-9]+)?([SU|MO|TU|WE|TH|FR|SA]{2})@", trim($day), $regs);
if (!empty($regs[2])) {
// Convert parameters into full day name, count, and direction.
$direction_days[] = array(
'day' => $day_names[$regs[3]],
'direction' => !empty($regs[1]) ? $regs[1] : '+',
'direction_count' => $regs[2],
'month' => NULL,
);
}
else {
$week_days[$ordered_keys[$regs[3]]] = $day_names[$regs[3]];
}
}
}
ksort($week_days);
@@ -251,7 +283,7 @@ function _date_repeat_calc($rrule, $start, $end, $exceptions, $timezone, $additi
$current_day = date_repeat_set_month_day($current_day, $day['day'], $day['direction_count'], $day['direction'], $timezone, $modify_time);
}
else {
$current_day = date_repeat_set_year_day($current_day, $day['day'], $day['direction_count'], $day['direction'], $timezone, $modify_time);
$current_day = date_repeat_set_year_day($current_day, $day['month'], $day['day'], $day['direction_count'], $day['direction'], $timezone, $modify_time);
}
date_repeat_add_dates($days, $current_day, $start_date, $end_date, $exceptions, $rrule);
}
@@ -279,7 +311,8 @@ function _date_repeat_calc($rrule, $start, $end, $exceptions, $timezone, $additi
// 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'))) {
if (!empty($week_days) &&
in_array($rrule['FREQ'], array('MONTHLY', 'WEEKLY', 'YEARLY'))) {
$finished = FALSE;
$current_day = clone($start_date);
$format = $rrule['FREQ'] == 'YEARLY' ? 'Y' : 'n';
@@ -298,8 +331,9 @@ function _date_repeat_calc($rrule, $start, $end, $exceptions, $timezone, $additi
$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 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.
@@ -346,10 +380,12 @@ function _date_repeat_calc($rrule, $start, $end, $exceptions, $timezone, $additi
date_modify($current_day, '+1 ' . $week_start_day . $modify_time);
date_modify($current_day, '-1 week' . $modify_time);
break;
case 'MONTHLY':
date_modify($current_day, '-' . (date_format($current_day, 'j') - 1) . ' days' . $modify_time);
date_modify($current_day, '-1 month' . $modify_time);
break;
case 'YEARLY':
date_modify($current_day, '-' . date_format($current_day, 'z') . ' days' . $modify_time);
date_modify($current_day, '-1 year' . $modify_time);
@@ -363,7 +399,7 @@ function _date_repeat_calc($rrule, $start, $end, $exceptions, $timezone, $additi
}
}
// add additional dates
// Add additional dates.
foreach ($additions as $addition) {
$date = new dateObject($addition . ' ' . $start_date->format('H:i:s'), $timezone);
$days[] = date_format($date, DATE_FORMAT_DATETIME);
@@ -402,8 +438,8 @@ function date_repeat_adjust_rrule($rrule, $start_date) {
// 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 => $BYDAY) {
$rrule['BYDAY'][$delta] = substr($BYDAY, -2);
foreach ($rrule['BYDAY'] as $delta => $by_day) {
$rrule['BYDAY'][$delta] = substr($by_day, -2);
}
}
@@ -418,7 +454,7 @@ function date_repeat_adjust_rrule($rrule, $start_date) {
* 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']) && sizeof($days) >= $rrule['COUNT']) {
if (isset($rrule['COUNT']) && count($days) >= $rrule['COUNT']) {
return FALSE;
}
$formatted = date_format($current_day, DATE_FORMAT_DATETIME);
@@ -432,13 +468,14 @@ function date_repeat_add_dates(&$days, $current_day, $start_date, $end_date, $ex
return FALSE;
}
if (!empty($rrule['BYDAY'])) {
$BYDAYS = $rrule['BYDAY'];
foreach ($BYDAYS as $delta => $BYDAY) {
$BYDAYS[$delta] = substr($BYDAY, -2);
$by_days = $rrule['BYDAY'];
foreach ($by_days as $delta => $by_day) {
$by_days[$delta] = substr($by_day, -2);
}
if (!in_array(date_repeat_dow2day(date_format($current_day, 'w')), $BYDAYS)) {
if (!in_array(date_repeat_dow2day(date_format($current_day, 'w')), $by_days)) {
return FALSE;
}}
}
}
if (!empty($rrule['BYYEAR']) && !in_array(date_format($current_day, 'Y'), $rrule['BYYEAR'])) {
return FALSE;
}
@@ -448,17 +485,17 @@ function date_repeat_add_dates(&$days, $current_day, $start_date, $end_date, $ex
if (!empty($rrule['BYMONTHDAY'])) {
// Test month days, but only if there are no negative numbers.
$test = TRUE;
$BYMONTHDAYS = array();
$by_month_days = array();
foreach ($rrule['BYMONTHDAY'] as $day) {
if ($day > 0) {
$BYMONTHDAYS[] = $day;
$by_month_days[] = $day;
}
else {
$test = FALSE;
break;
}
}
if ($test && !empty($BYMONTHDAYS) && !in_array(date_format($current_day, 'j'), $BYMONTHDAYS)) {
if ($test && !empty($by_month_days) && !in_array(date_format($current_day, 'j'), $by_month_days)) {
return FALSE;
}
}
@@ -475,7 +512,7 @@ function date_repeat_add_dates(&$days, $current_day, $start_date, $end_date, $ex
* Stop when $current_day is greater than $end_date or $count is reached.
*/
function date_repeat_is_finished($current_day, $days, $count, $end_date) {
if (($count && sizeof($days) >= $count)
if (($count && count($days) >= $count)
|| (!empty($end_date) && date_format($current_day, 'U') > date_format($end_date, 'U'))) {
return TRUE;
}
@@ -493,7 +530,7 @@ function date_repeat_is_finished($current_day, $days, $count, $end_date) {
* 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) {
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');
@@ -543,24 +580,42 @@ function date_repeat_set_month_day($date_in, $day, $count = 1, $direction = '+',
* 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, $day, $count = 1, $direction = '+', $timezone = 'UTC', $modify_time) {
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.
$datetime = date_format($date_in, DATE_FORMAT_DATETIME);
$datetime = substr_replace($datetime, '01-01', 5, 5);
$month_key = isset($month) ? $month : '01';
$datetime = substr_replace($datetime, $month_key . '-01', 5, 5);
$date = new DateObject($datetime, $timezone);
if ($direction == '-') {
// For negative search, start from the end of the year.
date_modify($date, '+1 year' . $modify_time);
if (isset($month)) {
if ($direction == '-') {
// For negative search, start from the end of the month.
$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.
$modifier = '-1 day';
}
}
else {
// For positive search, back up one day to get outside the
// current year, so we can catch the first of the year.
date_modify($date, '-1 day' . $modify_time);
if ($direction == '-') {
// For negative search, start from the end of the year.
$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.
$modifier = '-1 day';
}
}
date_modify($date, $modifier . $modify_time);
if (empty($day)) {
date_modify($date, $direction . $count . ' days' . $modify_time);
}
@@ -578,4 +633,4 @@ function date_repeat_set_year_day($date_in, $day, $count = 1, $direction = '+',
}
}
return $date_in;
}
}
@@ -30,13 +30,16 @@
* BYSETPOS
* Seldom used anywhere, so no reason to complicated the code.
*/
/**
* 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,
// 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)) {
@@ -67,16 +70,16 @@ function _date_repeat_rrule_process($element, &$form_state, $form) {
$timezone = !empty($element['#date_timezone']) ? $element['#date_timezone'] : date_default_timezone();
$merged_values = date_repeat_merge($rrule, $element);
$UNTIL = '';
$until = '';
if (!empty($merged_values['UNTIL']['datetime'])) {
$until_date = new DateObject($merged_values['UNTIL']['datetime'], $merged_values['UNTIL']['tz']);
date_timezone_set($until_date, timezone_open($timezone));
$UNTIL = date_format($until_date, DATE_FORMAT_DATETIME);
$until = date_format($until_date, DATE_FORMAT_DATETIME);
}
$COUNT = '';
$count = '';
if (!empty($merged_values['COUNT'])) {
$COUNT = $merged_values['COUNT'];
$count = $merged_values['COUNT'];
}
$element['FREQ'] = array(
@@ -137,7 +140,7 @@ function _date_repeat_rrule_process($element, &$form_state, $form) {
);
list($prefix, $suffix) = explode('@interval', t('Every @interval days', array(), array('context' => 'Date repeat')));
$DAILY_INTERVAL = array(
$daily_interval = array(
'#type' => 'textfield',
'#title' => t('Repeats', array(), array('context' => 'Date repeat')),
'#title_display' => 'invisible',
@@ -210,32 +213,34 @@ function _date_repeat_rrule_process($element, &$form_state, $form) {
'#suffix' => '</div>',
);
$DAILY_radios_default = 'INTERVAL';
$daily_radios_default = 'INTERVAL';
if (isset($rrule['FREQ']) && $rrule['FREQ'] === 'DAILY' && !empty($rrule['BYDAY'])) {
switch (count($rrule['BYDAY'])) {
case 2:
$DAILY_radios_default = 'every_tu_th';
$daily_radios_default = 'every_tu_th';
break;
case 3:
$DAILY_radios_default = 'every_mo_we_fr';
$daily_radios_default = 'every_mo_we_fr';
break;
case 5:
$DAILY_radios_default = 'every_weekday';
$daily_radios_default = 'every_weekday';
break;
}
}
$DAILY_every_weekday = array(
$daily_every_weekday = array(
'#type' => 'item',
'#markup' => '<div>' . t('Every weekday', array(), array('context' => 'Date repeat')) . '</div>',
);
$DAILY_mo_we_fr = array(
$daily_mo_we_fr = array(
'#type' => 'item',
'#markup' => '<div>' . t('Every Mon, Wed, Fri', array(), array('context' => 'Date repeat')) . '</div>',
);
$DAILY_tu_th = array(
$daily_tu_th = array(
'#type' => 'item',
'#markup' => '<div>' . t('Every Tue, Thu', array(), array('context' => 'Date repeat')) . '</div>',
);
@@ -251,17 +256,17 @@ function _date_repeat_rrule_process($element, &$form_state, $form) {
":input[name=\"{$element['#name']}[FREQ]\"]" => array('value' => 'DAILY'),
),
),
'#default_value' => $DAILY_radios_default,
'#default_value' => $daily_radios_default,
'#options' => array(
'INTERVAL' => t('interval'),
'every_weekday' => t('every weekday'),
'every_mo_we_fr' => t('monday wednesday friday'),
'every_tu_th' => t('tuesday thursday'),
),
'INTERVAL_child' => $DAILY_INTERVAL,
'every_weekday_child' => $DAILY_every_weekday,
'mo_we_fr_child' => $DAILY_mo_we_fr,
'tu_th_child' => $DAILY_tu_th,
'INTERVAL_child' => $daily_interval,
'every_weekday_child' => $daily_every_weekday,
'mo_we_fr_child' => $daily_mo_we_fr,
'tu_th_child' => $daily_tu_th,
'#div_classes' => array(
'container-inline interval',
'container-inline weekday',
@@ -270,18 +275,18 @@ function _date_repeat_rrule_process($element, &$form_state, $form) {
),
);
$MONTHLY_day_month_default = 'BYMONTHDAY_BYMONTH';
$monthly_day_month_default = 'BYMONTHDAY_BYMONTH';
if (isset($rrule['FREQ']) && $rrule['FREQ'] === 'MONTHLY' && !empty($rrule['BYDAY'])) {
$MONTHLY_day_month_default = 'BYDAY_BYMONTH';
$monthly_day_month_default = 'BYDAY_BYMONTH';
}
$MONTHLY_on_day_BYMONTHDAY_of_BYMONTH = array(
$monthly_on_day_bymonthday_of_bymonth = array(
'#type' => 'container',
'#tree' => TRUE,
);
list($bymonthday_title, $bymonthday_suffix) = explode('@bymonthday', t('On day @bymonthday of', array(), array('context' => 'Date repeat')));
$MONTHLY_on_day_BYMONTHDAY_of_BYMONTH['BYMONTHDAY'] = array(
$monthly_on_day_bymonthday_of_bymonth['BYMONTHDAY'] = array(
'#type' => 'select',
'#title' => $bymonthday_title,
'#default_value' => !empty($rrule['BYMONTHDAY']) && $rrule['FREQ'] === 'MONTHLY' ? $rrule['BYMONTHDAY'] : '',
@@ -292,11 +297,11 @@ function _date_repeat_rrule_process($element, &$form_state, $form) {
'#field_suffix' => $bymonthday_suffix,
);
$MONTHLY_on_day_BYMONTHDAY_of_BYMONTH['BYMONTH'] = array(
$monthly_on_day_bymonthday_of_bymonth['BYMONTH'] = array(
'#type' => 'checkboxes',
'#title' => t('Bymonth', array(), array('context' => 'Date repeat')),
'#title_display' => 'invisible',
'#default_value' => !empty($rrule['BYMONTH']) && $rrule['FREQ'] === 'MONTHLY' && $MONTHLY_day_month_default === 'BYMONTHDAY_BYMONTH' ? $rrule['BYMONTH'] : array(),
'#default_value' => !empty($rrule['BYMONTH']) && $rrule['FREQ'] === 'MONTHLY' && $monthly_day_month_default === 'BYMONTHDAY_BYMONTH' ? $rrule['BYMONTH'] : array(),
'#options' => date_month_names_abbr(TRUE),
'#attributes' => array('class' => array('container-inline')),
'#multiple' => TRUE,
@@ -304,45 +309,45 @@ function _date_repeat_rrule_process($element, &$form_state, $form) {
'#suffix' => '</div>',
);
$MONTHLY_on_the_BYDAY_of_BYMONTH = array(
$monthly_on_the_byday_of_bymonth = array(
'#type' => 'container',
'#tree' => TRUE,
);
$MONTHLY_BYDAY_COUNT = '';
$MONTHLY_BYDAY_DAY = '';
$monthly_byday_count = '';
$monthly_byday_day = '';
if (isset($rrule['BYDAY']) && !empty($rrule['BYDAY']) && $rrule['FREQ'] === 'MONTHLY') {
$MONTHLY_BYDAY_COUNT = substr($rrule['BYDAY'][0], 0, -2);
$MONTHLY_BYDAY_DAY = substr($rrule['BYDAY'][0], -2);;
$monthly_byday_count = substr($rrule['BYDAY'][0], 0, -2);
$monthly_byday_day = substr($rrule['BYDAY'][0], -2);;
}
list($byday_count_title, $byday_day_title) = explode('@byday', t('On the @byday of', array(), array('context' => 'Date repeat')));
$MONTHLY_on_the_BYDAY_of_BYMONTH['BYDAY_COUNT'] = array(
$monthly_on_the_byday_of_bymonth['BYDAY_COUNT'] = array(
'#type' => 'select',
'#title' => $byday_count_title,
'#default_value' => !empty($MONTHLY_BYDAY_COUNT) ? $MONTHLY_BYDAY_COUNT : '',
'#default_value' => !empty($monthly_byday_count) ? $monthly_byday_count : '',
'#options' => date_order_translated(),
'#multiple' => FALSE,
'#prefix' => '<div class="date-repeat-input byday-count">',
'#suffix' => '</div>',
);
$MONTHLY_on_the_BYDAY_of_BYMONTH['BYDAY_DAY'] = array(
$monthly_on_the_byday_of_bymonth['BYDAY_DAY'] = array(
'#type' => 'select',
'#title' => $byday_day_title,
'#title_display' => 'after',
'#default_value' => !empty($MONTHLY_BYDAY_DAY) ? $MONTHLY_BYDAY_DAY : '',
'#default_value' => !empty($monthly_byday_day) ? $monthly_byday_day : '',
'#options' => date_repeat_dow_day_options(TRUE),
'#multiple' => FALSE,
'#prefix' => '<div class="date-repeat-input byday-day">',
'#suffix' => '</div>',
);
$MONTHLY_on_the_BYDAY_of_BYMONTH['BYMONTH'] = array(
$monthly_on_the_byday_of_bymonth['BYMONTH'] = array(
'#type' => 'checkboxes',
'#title' => t('Bymonth', array(), array('context' => 'Date repeat')),
'#title_display' => 'invisible',
'#default_value' => !empty($rrule['BYMONTH']) && $rrule['FREQ'] === 'MONTHLY' && $MONTHLY_day_month_default === 'BYDAY_BYMONTH' ? $rrule['BYMONTH'] : array(),
'#default_value' => !empty($rrule['BYMONTH']) && $rrule['FREQ'] === 'MONTHLY' && $monthly_day_month_default === 'BYDAY_BYMONTH' ? $rrule['BYMONTH'] : array(),
'#options' => date_month_names_abbr(TRUE),
'#attributes' => array('class' => array('container-inline')),
'#multiple' => TRUE,
@@ -361,31 +366,31 @@ function _date_repeat_rrule_process($element, &$form_state, $form) {
),
),
'#attributes' => array('class' => array('date-repeat-radios clearfix')),
'#default_value' => $MONTHLY_day_month_default,
'#default_value' => $monthly_day_month_default,
'#options' => array(
'BYMONTHDAY_BYMONTH' => t('On day ... of ...'),
'BYDAY_BYMONTH' => t('On the ... of ...'),
),
'BYMONTHDAY_BYMONTH_child' => $MONTHLY_on_day_BYMONTHDAY_of_BYMONTH,
'BYDAY_BYMONTH_child' => $MONTHLY_on_the_BYDAY_of_BYMONTH,
'BYMONTHDAY_BYMONTH_child' => $monthly_on_day_bymonthday_of_bymonth,
'BYDAY_BYMONTH_child' => $monthly_on_the_byday_of_bymonth,
'#div_classes' => array(
'date-repeat-radios-item date-clear clearfix bymonthday-bymonth',
'date-repeat-radios-item date-clear clearfix byday-bymonth',
),
);
$YEARLY_day_month_default = 'BYMONTHDAY_BYMONTH';
$yearly_day_month_default = 'BYMONTHDAY_BYMONTH';
if (isset($rrule['FREQ']) && $rrule['FREQ'] === 'YEARLY' && !empty($rrule['BYDAY'])) {
$YEARLY_day_month_default = 'BYDAY_BYMONTH';
$yearly_day_month_default = 'BYDAY_BYMONTH';
}
$YEARLY_on_day_BYMONTHDAY_of_BYMONTH = array(
$yearly_on_day_bymonthday_of_bymonth = array(
'#type' => 'container',
'#tree' => TRUE,
);
list($bymonthday_title, $bymonthday_suffix) = explode('@bymonthday', t('On day @bymonthday of', array(), array('context' => 'Date repeat')));
$YEARLY_on_day_BYMONTHDAY_of_BYMONTH['BYMONTHDAY'] = array(
$yearly_on_day_bymonthday_of_bymonth['BYMONTHDAY'] = array(
'#type' => 'select',
'#title' => $bymonthday_title,
'#default_value' => !empty($rrule['BYMONTHDAY']) && $rrule['FREQ'] === 'YEARLY' ? $rrule['BYMONTHDAY'] : '',
@@ -396,11 +401,11 @@ function _date_repeat_rrule_process($element, &$form_state, $form) {
'#field_suffix' => $bymonthday_suffix,
);
$YEARLY_on_day_BYMONTHDAY_of_BYMONTH['BYMONTH'] = array(
$yearly_on_day_bymonthday_of_bymonth['BYMONTH'] = array(
'#type' => 'checkboxes',
'#title' => t('Bymonth', array(), array('context' => 'Date repeat')),
'#title_display' => 'invisible',
'#default_value' => !empty($rrule['BYMONTH']) && $rrule['FREQ'] === 'YEARLY' && $YEARLY_day_month_default === 'BYMONTHDAY_BYMONTH' ? $rrule['BYMONTH'] : array(),
'#default_value' => !empty($rrule['BYMONTH']) && $rrule['FREQ'] === 'YEARLY' && $yearly_day_month_default === 'BYMONTHDAY_BYMONTH' ? $rrule['BYMONTH'] : array(),
'#options' => date_month_names_abbr(TRUE),
'#attributes' => array('class' => array('container-inline')),
'#multiple' => TRUE,
@@ -408,45 +413,45 @@ function _date_repeat_rrule_process($element, &$form_state, $form) {
'#suffix' => '</div>',
);
$YEARLY_on_the_BYDAY_of_BYMONTH = array(
$yearly_on_the_byday_of_bymonth = array(
'#type' => 'container',
'#tree' => TRUE,
);
$YEARLY_BYDAY_COUNT = '';
$YEARLY_BYDAY_DAY = '';
$yearly_byday_count = '';
$yearly_byday_day = '';
if (isset($rrule['BYDAY']) && !empty($rrule['BYDAY']) && $rrule['FREQ'] === 'YEARLY') {
$YEARLY_BYDAY_COUNT = substr($rrule['BYDAY'][0], 0, -2);
$YEARLY_BYDAY_DAY = substr($rrule['BYDAY'][0], -2);;
$yearly_byday_count = substr($rrule['BYDAY'][0], 0, -2);
$yearly_byday_day = substr($rrule['BYDAY'][0], -2);;
}
list($byday_count_title, $byday_day_title) = explode('@byday', t('On the @byday of', array(), array('context' => 'Date repeat')));
$YEARLY_on_the_BYDAY_of_BYMONTH['BYDAY_COUNT'] = array(
$yearly_on_the_byday_of_bymonth['BYDAY_COUNT'] = array(
'#type' => 'select',
'#title' => $byday_count_title,
'#default_value' => !empty($YEARLY_BYDAY_COUNT) ? $YEARLY_BYDAY_COUNT : '',
'#default_value' => !empty($yearly_byday_count) ? $yearly_byday_count : '',
'#options' => date_order_translated(),
'#multiple' => FALSE,
'#prefix' => '<div class="date-repeat-input byday-count">',
'#suffix' => '</div>',
);
$YEARLY_on_the_BYDAY_of_BYMONTH['BYDAY_DAY'] = array(
$yearly_on_the_byday_of_bymonth['BYDAY_DAY'] = array(
'#type' => 'select',
'#title' => $byday_day_title,
'#title_display' => 'after',
'#default_value' => !empty($YEARLY_BYDAY_DAY) ? $YEARLY_BYDAY_DAY : '',
'#default_value' => !empty($yearly_byday_day) ? $yearly_byday_day : '',
'#options' => date_repeat_dow_day_options(TRUE),
'#multiple' => FALSE,
'#prefix' => '<div class="date-repeat-input byday-day">',
'#suffix' => '</div>',
);
$YEARLY_on_the_BYDAY_of_BYMONTH['BYMONTH'] = array(
$yearly_on_the_byday_of_bymonth['BYMONTH'] = array(
'#type' => 'checkboxes',
'#title' => t('Bymonth', array(), array('context' => 'Date repeat')),
'#title_display' => 'invisible',
'#default_value' => !empty($rrule['BYMONTH']) && $rrule['FREQ'] === 'YEARLY' && $YEARLY_day_month_default === 'BYDAY_BYMONTH' ? $rrule['BYMONTH'] : array(),
'#default_value' => !empty($rrule['BYMONTH']) && $rrule['FREQ'] === 'YEARLY' && $yearly_day_month_default === 'BYDAY_BYMONTH' ? $rrule['BYMONTH'] : array(),
'#options' => date_month_names_abbr(TRUE),
'#attributes' => array('class' => array('container-inline')),
'#multiple' => TRUE,
@@ -465,13 +470,13 @@ function _date_repeat_rrule_process($element, &$form_state, $form) {
),
),
'#attributes' => array('class' => array('date-repeat-radios clearfix')),
'#default_value' => $YEARLY_day_month_default,
'#default_value' => $yearly_day_month_default,
'#options' => array(
'BYMONTHDAY_BYMONTH' => t('On day ... of ...'),
'BYDAY_BYMONTH' => t('On the ... of ...'),
),
'BYMONTHDAY_BYMONTH_child' => $YEARLY_on_day_BYMONTHDAY_of_BYMONTH,
'BYDAY_BYMONTH_child' => $YEARLY_on_the_BYDAY_of_BYMONTH,
'BYMONTHDAY_BYMONTH_child' => $yearly_on_day_bymonthday_of_bymonth,
'BYDAY_BYMONTH_child' => $yearly_on_the_byday_of_bymonth,
'#div_classes' => array(
'date-repeat-radios-item date-clear clearfix bymonthday-bymonth',
'date-repeat-radios-item date-clear clearfix byday-bymonth',
@@ -482,7 +487,7 @@ function _date_repeat_rrule_process($element, &$form_state, $form) {
$count_form_element = array(
'#type' => 'textfield',
'#title' => t('Count', array(), array('context' => 'Date repeat')),
'#default_value' => $COUNT,
'#default_value' => $count,
'#element_validate' => array('element_validate_integer_positive'),
'#attributes' => array('placeholder' => array('#')),
'#prefix' => $prefix,
@@ -499,21 +504,26 @@ function _date_repeat_rrule_process($element, &$form_state, $form) {
'#type' => $element['#date_repeat_widget'],
'#title' => t('Until', array(), array('context' => 'Date repeat')),
'#title_display' => 'invisible',
'#default_value' => $UNTIL,
'#date_format' => !empty($element['#date_format']) ? date_limit_format($element['#date_format'], array('year', 'month', 'day')) : 'Y-m-d',
'#default_value' => $until,
'#date_format' => !empty($element['#date_format']) ?
date_limit_format($element['#date_format'], array('year', 'month', 'day')) : 'Y-m-d',
'#date_timezone' => $timezone,
'#date_text_parts' => !empty($element['#date_text_parts']) ? $element['#date_text_parts'] : array(),
'#date_year_range' => !empty($element['#date_year_range']) ? $element['#date_year_range'] : '-3:+3',
'#date_label_position' => !empty($element['#date_label_position']) ? $element['#date_label_position'] : 'within',
'#date_label_position' => !empty($element['#date_label_position']) ?
$element['#date_label_position'] : 'within',
'#date_flexible' => 0,
),
'tz' => array('#type' => 'hidden', '#value' => $element['#date_timezone']),
'all_day' => array('#type' => 'hidden', '#value' => 1),
'granularity' => array('#type' => 'hidden', '#value' => serialize(array('year', 'month', 'day'))),
'granularity' => array(
'#type' => 'hidden',
'#value' => serialize(array('year', 'month', 'day')),
),
);
$range_of_repeat_default = 'COUNT';
if (!empty($UNTIL)) {
if (!empty($until)) {
$range_of_repeat_default = 'UNTIL';
}
$element['range_of_repeat'] = array(
@@ -528,7 +538,7 @@ function _date_repeat_rrule_process($element, &$form_state, $form) {
":input[name=\"{$element['#name']}[FREQ]\"]" => array('value' => 'NONE'),
),
),
'#default_value' => $range_of_repeat_default,
'#default_value' => $range_of_repeat_default,
'#options' => array(
'COUNT' => t('Count'),
'UNTIL' => t('Until'),
@@ -544,7 +554,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);
}
@@ -576,33 +587,48 @@ function _date_repeat_rrule_process($element, &$form_state, $form) {
),
),
);
for ($i = 0; $i < max($form_state['num_exceptions'][$instance], 1) ; $i++) {
$EXCEPT = '';
for ($i = 0; $i < max($form_state['num_exceptions'][$instance], 1); $i++) {
$except = '';
if (!empty($exceptions[$i]['datetime'])) {
$ex_date = new DateObject($exceptions[$i]['datetime'], $exceptions[$i]['tz']);
date_timezone_set($ex_date, timezone_open($timezone));
$EXCEPT = date_format($ex_date, DATE_FORMAT_DATETIME);
$except = date_format($ex_date, DATE_FORMAT_DATETIME);
}
$date_format = 'Y-m-d';
if (!empty($element['#date_format'])) {
$grans = array('year', 'month', 'day');
$date_format = date_limit_format($element['#date_format'], $grans);
}
$element['exceptions']['EXDATE'][$i] = array(
'#tree' => TRUE,
'datetime' => array(
'#name' => 'exceptions|' . $instance,
'#type' => $element['#date_repeat_widget'],
'#default_value' => $EXCEPT,
'#date_timezone' => !empty($element['#date_timezone']) ? $element['#date_timezone'] : date_default_timezone(),
'#date_format' => !empty($element['#date_format']) ? date_limit_format($element['#date_format'], array('year', 'month', 'day')) : 'Y-m-d',
'#default_value' => $except,
'#date_timezone' => !empty($element['#date_timezone']) ?
$element['#date_timezone'] : date_default_timezone(),
'#date_format' => $date_format,
'#date_text_parts' => !empty($element['#date_text_parts']) ? $element['#date_text_parts'] : array(),
'#date_year_range' => !empty($element['#date_year_range']) ? $element['#date_year_range'] : '-3:+3',
'#date_label_position' => !empty($element['#date_label_position']) ? $element['#date_label_position'] : 'within',
'#date_flexible' => 0,
),
'tz' => array('#type' => 'hidden', '#value' => $element['#date_timezone']),
'all_day' => array('#type' => 'hidden', '#value' => 1),
'granularity' => array('#type' => 'hidden', '#value' => serialize(array('year', 'month', 'day'))),
);
),
'tz' => array(
'#type' => 'hidden',
'#value' => $element['#date_timezone'],
),
'all_day' => array(
'#type' => 'hidden',
'#value' => 1,
),
'granularity' => array(
'#type' => 'hidden',
'#value' => serialize(array('year', 'month', 'day')),
),
);
}
// collect additions in the same way as exceptions - implements RDATE.
// Collect additions in the same way as exceptions - implements RDATE.
if (empty($form_state['num_additions'][$instance])) {
$form_state['num_additions'][$instance] = count($additions);
}
@@ -634,30 +660,45 @@ function _date_repeat_rrule_process($element, &$form_state, $form) {
),
),
);
for ($i = 0; $i < max($form_state['num_additions'][$instance], 1) ; $i++) {
$RDATE = '';
for ($i = 0; $i < max($form_state['num_additions'][$instance], 1); $i++) {
$r_date = '';
if (!empty($additions[$i]['datetime'])) {
$rdate = new DateObject($additions[$i]['datetime'], $additions[$i]['tz']);
date_timezone_set($rdate, timezone_open($timezone));
$RDATE = date_format($rdate, DATE_FORMAT_DATETIME);
$r_date = date_format($rdate, DATE_FORMAT_DATETIME);
}
$date_format = 'Y-m-d';
if (!empty($element['#date_format'])) {
$grans = array('year', 'month', 'day');
$date_format = date_limit_format($element['#date_format'], $grans);
}
$element['additions']['RDATE'][$i] = array(
'#tree' => TRUE,
'datetime' => array(
'#type' => $element['#date_repeat_widget'],
'#name' => 'additions|' . $instance,
'#default_value' => $RDATE,
'#date_timezone' => !empty($element['#date_timezone']) ? $element['#date_timezone'] : date_default_timezone(),
'#date_format' => !empty($element['#date_format']) ? date_limit_format($element['#date_format'], array('year', 'month', 'day')) : 'Y-m-d',
'#default_value' => $r_date,
'#date_timezone' => !empty($element['#date_timezone']) ?
$element['#date_timezone'] : date_default_timezone(),
'#date_format' => $date_format,
'#date_text_parts' => !empty($element['#date_text_parts']) ? $element['#date_text_parts'] : array(),
'#date_year_range' => !empty($element['#date_year_range']) ? $element['#date_year_range'] : '-3:+3',
'#date_label_position' => !empty($element['#date_label_position']) ? $element['#date_label_position'] : 'within',
'#date_flexible' => 0,
),
'tz' => array('#type' => 'hidden', '#value' => $element['#date_timezone']),
'all_day' => array('#type' => 'hidden', '#value' => 1),
'granularity' => array('#type' => 'hidden', '#value' => serialize(array('year', 'month', 'day'))),
);
),
'tz' => array(
'#type' => 'hidden',
'#value' => $element['#date_timezone'],
),
'all_day' => array(
'#type' => 'hidden',
'#value' => 1,
),
'granularity' => array(
'#type' => 'hidden',
'#value' => serialize(array('year', 'month', 'day')),
),
);
}
$element['exceptions']['exceptions_add'] = array(
@@ -687,6 +728,9 @@ function _date_repeat_rrule_process($element, &$form_state, $form) {
return $element;
}
/**
* Add callback to date repeat.
*/
function date_repeat_add_exception_callback($form, &$form_state) {
$parents = $form_state['triggering_element']['#array_parents'];
$button_key = array_pop($parents);
@@ -694,6 +738,9 @@ function date_repeat_add_exception_callback($form, &$form_state) {
return $element;
}
/**
* Add addition callback to date repeat.
*/
function date_repeat_add_addition_callback($form, &$form_state) {
$parents = $form_state['triggering_element']['#array_parents'];
$button_key = array_pop($parents);
@@ -701,6 +748,9 @@ function date_repeat_add_addition_callback($form, &$form_state) {
return $element;
}
/**
* Add exception to date repeat.
*/
function date_repeat_add_exception($form, &$form_state) {
$parents = $form_state['triggering_element']['#array_parents'];
$instance = implode('-', array_slice($parents, 0, count($parents) - 2));
@@ -708,6 +758,9 @@ function date_repeat_add_exception($form, &$form_state) {
$form_state['rebuild'] = TRUE;
}
/**
* Add addition to date repeat.
*/
function date_repeat_add_addition($form, &$form_state) {
$parents = $form_state['triggering_element']['#array_parents'];
$instance = implode('-', array_slice($parents, 0, count($parents) - 2));
@@ -723,8 +776,14 @@ function date_repeat_merge($form_values, $element) {
return $form_values;
}
if (array_key_exists('exceptions', $form_values) || array_key_exists('additions', $form_values)) {
if (!array_key_exists('exceptions', $form_values)) $form_values['exceptions'] = array();
if (!array_key_exists('additions', $form_values)) $form_values['additions'] = array();
if (!array_key_exists('exceptions', $form_values)) {
$form_values['exceptions'] = array();
}
if (!array_key_exists('additions', $form_values)) {
$form_values['additions'] = array();
}
$form_values = array_merge($form_values, (array) $form_values['exceptions'], (array) $form_values['additions']);
unset($form_values['exceptions']);
unset($form_values['additions']);
@@ -738,18 +797,22 @@ function date_repeat_merge($form_values, $element) {
case 'INTERVAL':
$form_values['INTERVAL'] = $form_values['daily']['INTERVAL_child'];
break;
case 'every_weekday':
$form_values['BYDAY'] = array('MO', 'TU', 'WE', 'TH', 'FR');
break;
case 'every_mo_we_fr':
$form_values['BYDAY'] = array('MO', 'WE', 'FR');
break;
case 'every_tu_th':
$form_values['BYDAY'] = array('TU', 'TH');
break;
}
}
break;
case 'WEEKLY':
if (array_key_exists('weekly', $form_values)) {
$form_values = array_merge($form_values, (array) $form_values['weekly']);
@@ -758,12 +821,14 @@ function date_repeat_merge($form_values, $element) {
}
}
break;
case 'MONTHLY':
if (array_key_exists('monthly', $form_values)) {
switch ($form_values['monthly']['day_month']) {
case 'BYMONTHDAY_BYMONTH':
$form_values['monthly'] = array_merge($form_values['monthly'], (array) $form_values['monthly']['BYMONTHDAY_BYMONTH_child']);
break;
case 'BYDAY_BYMONTH':
$form_values['monthly']['BYDAY_BYMONTH_child']['BYDAY'] = $form_values['monthly']['BYDAY_BYMONTH_child']['BYDAY_COUNT'] . $form_values['monthly']['BYDAY_BYMONTH_child']['BYDAY_DAY'];
$form_values['monthly'] = array_merge($form_values['monthly'], (array) $form_values['monthly']['BYDAY_BYMONTH_child']);
@@ -783,12 +848,14 @@ function date_repeat_merge($form_values, $element) {
}
}
break;
case 'YEARLY':
if (array_key_exists('yearly', $form_values)) {
switch ($form_values['yearly']['day_month']) {
case 'BYMONTHDAY_BYMONTH':
$form_values['yearly'] = array_merge($form_values['yearly'], (array) $form_values['yearly']['BYMONTHDAY_BYMONTH_child']);
break;
case 'BYDAY_BYMONTH':
$form_values['yearly']['BYDAY_BYMONTH_child']['BYDAY'] = $form_values['yearly']['BYDAY_BYMONTH_child']['BYDAY_COUNT'] . $form_values['yearly']['BYDAY_BYMONTH_child']['BYDAY_DAY'];
$form_values['yearly'] = array_merge($form_values['yearly'], (array) $form_values['yearly']['BYDAY_BYMONTH_child']);
@@ -808,6 +875,7 @@ function date_repeat_merge($form_values, $element) {
}
}
break;
default:
break;
}
@@ -823,6 +891,7 @@ function date_repeat_merge($form_values, $element) {
case 'COUNT':
$form_values['COUNT'] = $form_values['count_child'];
break;
case 'UNTIL':
$form_values['UNTIL'] = $form_values['until_child'];
break;
@@ -832,14 +901,23 @@ function date_repeat_merge($form_values, $element) {
unset($form_values['count_child']);
unset($form_values['until_child']);
if (array_key_exists('BYDAY', $form_values) && is_array($form_values['BYDAY'])) unset($form_values['BYDAY']['']);
if (array_key_exists('BYMONTH', $form_values) && is_array($form_values['BYMONTH'])) unset($form_values['BYMONTH']['']);
if (array_key_exists('BYMONTHDAY', $form_values) && is_array($form_values['BYMONTHDAY'])) unset($form_values['BYMONTHDAY']['']);
if (array_key_exists('BYDAY', $form_values) && is_array($form_values['BYDAY'])) {
unset($form_values['BYDAY']['']);
}
if (array_key_exists('BYMONTH', $form_values) && is_array($form_values['BYMONTH'])) {
unset($form_values['BYMONTH']['']);
}
if (array_key_exists('BYMONTHDAY', $form_values) && is_array($form_values['BYMONTHDAY'])) {
unset($form_values['BYMONTHDAY']['']);
}
if (array_key_exists('UNTIL', $form_values) && is_array($form_values['UNTIL']['datetime'])) {
$function = $element['#date_repeat_widget'] . '_input_date';
$until_element = $element;
$until_element['#date_format'] = !empty($element['#date_format']) ? date_limit_format($element['#date_format'], array('year', 'month', 'day')) : 'Y-m-d';
$until_element['#date_format'] = !empty($element['#date_format']) ?
date_limit_format($element['#date_format'], array('year', 'month', 'day')) : 'Y-m-d';
$date = $function($until_element, $form_values['UNTIL']['datetime']);
$form_values['UNTIL']['datetime'] = is_object($date) ? $date->format(DATE_FORMAT_DATETIME) : '';
}
@@ -849,9 +927,14 @@ function date_repeat_merge($form_values, $element) {
if (array_key_exists('EXDATE', $form_values) && is_array($form_values['EXDATE'])) {
$function = $element['#date_repeat_widget'] . '_input_date';
$exdate_element = $element;
$date_format = 'Y-m-d';
if (!empty($element['#date_format'])) {
$grans = array('year', 'month', 'day');
$date_format = date_limit_format($element['#date_format'], $grans);
}
foreach ($form_values['EXDATE'] as $delta => $value) {
if (is_array($value['datetime'])) {
$exdate_element['#date_format'] = !empty($element['#date_format']) ? date_limit_format($element['#date_format'], array('year', 'month', 'day')) : 'Y-m-d';
$exdate_element['#date_format'] = $date_format;
$date = $function($exdate_element, $form_values['EXDATE'][$delta]['datetime']);
$form_values['EXDATE'][$delta]['datetime'] = is_object($date) ? $date->format(DATE_FORMAT_DATETIME) : '';
}
@@ -864,9 +947,14 @@ function date_repeat_merge($form_values, $element) {
if (array_key_exists('RDATE', $form_values) && is_array($form_values['RDATE'])) {
$function = $element['#date_repeat_widget'] . '_input_date';
$rdate_element = $element;
$date_format = 'Y-m-d';
if (!empty($element['#date_format'])) {
$grans = array('year', 'month', 'day');
$date_format = date_limit_format($element['#date_format'], $grans);
}
foreach ($form_values['RDATE'] as $delta => $value) {
if (is_array($value['datetime'])) {
$rdate_element['#date_format'] = !empty($element['#date_format']) ? date_limit_format($element['#date_format'], array('year', 'month', 'day')) : 'Y-m-d';
$rdate_element['#date_format'] = $date_format;
$date = $function($rdate_element, $form_values['RDATE'][$delta]['datetime']);
$form_values['RDATE'][$delta]['datetime'] = is_object($date) ? $date->format(DATE_FORMAT_DATETIME) : '';
}
@@ -910,7 +998,7 @@ function date_repeat_rrule_validate($element, &$form_state) {
}
/**
* Theme the exception list as a table so the buttons line up
* Theme the exception list as a table so the buttons line up.
*/
function theme_date_repeat_current_exceptions($vars) {
$rows = $vars['rows'];
@@ -920,11 +1008,14 @@ function theme_date_repeat_current_exceptions($vars) {
$rows_info[] = array(drupal_render($value['action']), drupal_render($value['display']));
}
}
return theme('table', array('header' => array(t('Delete'), t('Current exceptions')), 'rows' => $rows_info));
return theme('table', array(
'header' => array(t('Delete'), t('Current exceptions')),
'rows' => $rows_info)
);
}
/**
* Theme the exception list as a table so the buttons line up
/**
* Theme the exception list as a table so the buttons line up.
*/
function theme_date_repeat_current_additions($rows = array()) {
$rows_info = array();
@@ -933,7 +1024,10 @@ function theme_date_repeat_current_additions($rows = array()) {
$rows_info[] = array(drupal_render($value['action']), drupal_render($value['display']));
}
}
return theme('table', array('header' => array(t('Delete'), t('Current additions')), 'rows' => $rows_info));
return theme('table', array(
'header' => array(t('Delete'), t('Current additions')),
'rows' => $rows_info)
);
}
/**
@@ -941,19 +1035,23 @@ function theme_date_repeat_current_additions($rows = array()) {
*/
function theme_date_repeat_rrule($vars) {
$element = $vars['element'];
$class = $element['#date_repeat_collapsed'] ? array('date-no-float', 'collapsible', 'collapsed') : array('date-no-float', 'collapsible');
$id = drupal_html_id('repeat-settings-fieldset');
$parents = $element['#parents'];
$selector = "{$parents[0]}[{$parents[1]}][{$parents[2]}][show_repeat_settings]";
$selector = $parents[0];
for ($i = 1; $i < count($parents) - 1; $i++) {
$selector .= '[' . $parents[$i] . ']';
}
$selector .= '[show_repeat_settings]';
$fieldset = array(
'#type' => 'item',
'#title' => t('Repeat settings'),
'#title_display' => 'invisible',
'#attributes' => array('class' => $class),
'#markup' => $element['#children'],
'#states' => array(
'visible' => array(
":input[name=\"{$selector}\"]" => array('checked' => TRUE),
'invisible' => array(
":input[name=\"{$selector}\"]" => array('checked' => FALSE),
),
),
'#id' => $id,
@@ -962,6 +1060,9 @@ function theme_date_repeat_rrule($vars) {
return drupal_render($fieldset);
}
/**
* Filter non zero values.
*/
function date_repeat_filter_non_zero_value($value) {
return $value !== 0;
}
@@ -377,6 +377,19 @@ 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:
$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
// (2015 00:00 AM EDT)November 26
// (2016 00:00 AM EDT)November 24
// (2017 00:00 AM EDT)November 30
// (2018 00:00 AM EDT)November 29
$dates = date_repeat_calc($rule, $start, NULL, array());
$shouldbe = '2014-11-27 00:00:00, 2015-11-26 00:00:00, 2016-11-24 00:00:00, 2017-11-30 00:00:00, 2018-11-29 00:00:00';
$result = implode(', ', $dates);
$this->assertEqual($result, $shouldbe, $rule . '; Starting ' . $start . '; results: ' . $result);
return;
//Every Thanksgiving, forever:
@@ -25,7 +25,7 @@ class DateRepeatFormTestCase extends DrupalWebTestCase {
// 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 content types', 'administer nodes', 'bypass node access', 'view date repeats', 'administer fields'
));
$this->drupalLogin($this->privileged_user);
@@ -195,20 +195,22 @@ class DateRepeatFormTestCase extends DrupalWebTestCase {
$edit = array();
$edit['title'] = $this->randomName(8);
$edit['body[und][0][value]'] = $this->randomName(16);
$current_year = date('Y');
switch ($options) {
case 'select':
$edit['field_test[und][0][value][year]'] = '2010';
$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';
break;
case 'text':
$edit['field_test[und][0][value][date]'] = '2010-10-07 10:30';
$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]'] = '2010-10-07';
$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;
}
@@ -378,7 +380,7 @@ class DateRepeatFormTestCase extends DrupalWebTestCase {
else {
$edit['field_test[und][0][rrule][range_of_repeat]'] = 'UNTIL';
$date = array(
'year' => '2011',
'year' => $current_year + 1,
'month' => '10',
'day' => '07'
);
@@ -392,7 +394,7 @@ class DateRepeatFormTestCase extends DrupalWebTestCase {
case 'exclude':
$exclude_include_edit['field_test[und][0][rrule][show_exceptions]'] = TRUE;
$date = array(
'year' => '2010',
'year' => $current_year,
'month' => '10',
'day' => '07'
);
@@ -401,7 +403,7 @@ class DateRepeatFormTestCase extends DrupalWebTestCase {
case 'include':
$exclude_include_edit['field_test[und][0][rrule][show_additions]'] = TRUE;
$date = array(
'year' => '2013',
'year' => $current_year + 3,
'month' => '10',
'day' => '07'
);
@@ -410,7 +412,7 @@ class DateRepeatFormTestCase extends DrupalWebTestCase {
case 'exclude_include':
$exclude_include_edit['field_test[und][0][rrule][show_exceptions]'] = TRUE;
$date = array(
'year' => '2010',
'year' => $current_year,
'month' => '10',
'day' => '07'
);
@@ -418,7 +420,7 @@ class DateRepeatFormTestCase extends DrupalWebTestCase {
$exclude_include_edit['field_test[und][0][rrule][show_additions]'] = TRUE;
$date = array(
'year' => '2013',
'year' => $current_year + 3,
'month' => '10',
'day' => '07'
);
@@ -510,7 +512,6 @@ class DateRepeatFormTestCase extends DrupalWebTestCase {
break;
case 'text':
case 'popup':
//$return["{$field_name}[datetime][date]"] = '2011-10-07';
$return["{$form_field_name}[datetime][date]"] = "{$date['year']}-{$date['month']}-{$date['day']}";
break;
}
@@ -1,5 +1,5 @@
<?php
/*
/**
* @file
* Handling of devel generate functionality for repeating dates.
*/
@@ -42,9 +42,11 @@ function date_repeat_field_date_field_insert(&$items, $context) {
case 'date':
$format = DATE_FORMAT_ISO;
break;
case 'datestamp':
$format = DATE_FORMAT_UNIX;
break;
case 'datetime':
$format = DATE_FORMAT_DATETIME;
break;
@@ -78,6 +80,7 @@ function date_repeat_field_date_field_insert(&$items, $context) {
}
$form_values['BYMONTHDAY'] = array($mo);
break;
case 2:
$mo = mt_rand(1, 12);
$options = array('YEARLY', 'MONTHLY');
@@ -90,6 +93,7 @@ function date_repeat_field_date_field_insert(&$items, $context) {
}
$form_values['BYMONTH'] = array($mo);
break;
default:
$dows = array_keys(date_content_repeat_dow_options());
$day = date_content_generate_key($dows);
@@ -108,16 +112,18 @@ function date_repeat_field_date_field_insert(&$items, $context) {
case 'YEARLY':
$period = 'year';
break;
case 'MONTHLY':
$period = 'month';
break;
case 'WEEKLY':
$period = 'week';
break;
default:
$period = 'day';
break;
}
$form_values['UNTIL'] = array();
@@ -126,12 +132,15 @@ function date_repeat_field_date_field_insert(&$items, $context) {
$rrule = date_api_ical_build_rrule($form_values);
$items[0]['rrule'] = $rrule;
$values = date_repeat_build_dates($rrule, $form_values, $field, $item);
$values = date_repeat_build_dates($field, $item, $rrule, $form_values);
$items += $values;
}
/**
* Generate a random content keys.
*/
function date_content_generate_key($array) {
$keys = array_keys($array);
$min = array_shift($keys);
@@ -155,4 +164,4 @@ function date_content_repeat_dow_options() {
}
}
return $options;
}
}
@@ -7,9 +7,9 @@ stylesheets[all][] = date_repeat_field.css
package = Date/Time
core = 7.x
; Information added by drupal.org packaging script on 2012-08-13
version = "7.x-2.6"
; Information added by Drupal.org packaging script on 2017-04-07
version = "7.x-2.10"
core = "7.x"
project = "date"
datestamp = "1344850024"
datestamp = "1491562090"
@@ -55,7 +55,7 @@ function theme_date_repeat_display($vars) {
$output = '';
if (!empty($item['rrule'])) {
$output = date_repeat_rrule_description($item['rrule']);
$output = '<div>' . $output . '</div>';
$output = '<div class="date-repeat-rule">' . $output . '</div>';
}
return $output;
}
@@ -77,13 +77,13 @@ function date_repeat_field_menu() {
$path = field_collection_field_get_path($field);
$count = count(explode('/', $path));
$items[$path . '/%field_collection_item/repeats'] = array(
'title' => 'Repeats',
'page callback' => 'date_repeat_field_page',
'page arguments' => array($entity_type, $count),
'access callback' => 'date_repeat_field_show',
'access arguments' => array($entity_type, $count),
'type' => MENU_LOCAL_TASK,
'context' => MENU_CONTEXT_PAGE | MENU_CONTEXT_INLINE,
'title' => 'Repeats',
'page callback' => 'date_repeat_field_page',
'page arguments' => array($entity_type, $count),
'access callback' => 'date_repeat_field_show',
'access arguments' => array($entity_type, $count),
'type' => MENU_LOCAL_TASK,
'context' => MENU_CONTEXT_PAGE | MENU_CONTEXT_INLINE,
);
}
}
@@ -91,13 +91,13 @@ function date_repeat_field_menu() {
else {
$path = $entity_type . '/%' . $entity_type;
$items[$path . '/repeats'] = array(
'title' => 'Repeats',
'page callback' => 'date_repeat_field_page',
'page arguments' => array($entity_type, 1),
'access callback' => 'date_repeat_field_show',
'access arguments' => array($entity_type, 1),
'type' => MENU_LOCAL_TASK,
'context' => MENU_CONTEXT_PAGE | MENU_CONTEXT_INLINE,
'title' => 'Repeats',
'page callback' => 'date_repeat_field_page',
'page arguments' => array($entity_type, 1),
'access callback' => 'date_repeat_field_show',
'access arguments' => array($entity_type, 1),
'type' => MENU_LOCAL_TASK,
'context' => MENU_CONTEXT_PAGE | MENU_CONTEXT_INLINE,
);
}
}
@@ -108,23 +108,47 @@ function date_repeat_field_menu() {
* Implements hook_permission().
*/
function date_repeat_field_permission() {
return array('view date repeats' => array(
'title' => t('View Repeating Dates'),
'description' => t('Allow user to see a page with all the times a date repeats.'),
));
return array(
'view date repeats' => array(
'title' => t('View Repeating Dates'),
'description' => t('Allow user to see a page with all the times a date repeats.'),
),
);
}
/**
* See if the user can access repeat date info for this field.
* See if the user can access repeat date info for this entity.
*
* @param string $entity_type
* The entity type.
* @param string $entity
* The specific entity to check (optional).
*
* @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.
*/
function date_repeat_field_show($entity_type = 'node', $entity = NULL) {
if (!user_access('view date repeats')) {
return FALSE;
}
$bundle = date_get_entity_bundle($entity_type, $entity);
foreach (field_info_fields() as $field_name => $field) {
if (in_array($field['type'], array('date', 'datestamp', 'datetime'))
&& array_key_exists($entity_type, $field['bundles'])
&& in_array($bundle, $field['bundles'][$entity_type])
&& date_is_repeat_field($field)) {
return user_access('view date repeats');
// In Drupal 7.22 the field_info_field_map() function was added, which is more
// memory-efficient in certain cases than field_info_fields().
// @see https://drupal.org/node/1915646
$field_map_available = version_compare(VERSION, '7.22', '>=');
$field_list = $field_map_available ? field_info_field_map() : field_info_fields();
foreach ($field_list as $field_name => $data) {
if (in_array($data['type'], array('date', 'datestamp', 'datetime'))
&& array_key_exists($entity_type, $data['bundles'])
&& in_array($bundle, $data['bundles'][$entity_type])) {
$field_info = $field_map_available ? field_info_field($field_name) : $data;
if (date_is_repeat_field($field_info)) {
return TRUE;
}
}
}
return FALSE;
@@ -173,6 +197,9 @@ function date_repeat_field_bundles() {
return $values;
}
/**
* Check field is repeat.
*/
function date_is_repeat_field($field, $instance = NULL) {
if (is_string($field)) {
$field = field_info_field($field);
@@ -193,7 +220,7 @@ function date_is_repeat_field($field, $instance = NULL) {
}
}
/*
/**
* Implements hook_date_field_insert_alter().
*/
function date_repeat_field_date_field_insert_alter(&$items, $context) {
@@ -217,7 +244,7 @@ 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) {
@@ -257,6 +284,13 @@ function date_repeat_field_field_widget_form_alter(&$element, &$form_state, $con
'#suffix' => '</div>',
'#default_value' => isset($items[$delta]['rrule']) && !empty($items[$delta]['rrule']) ? 1 : 0,
);
// 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['#description'] = field_filter_xss($instance['description']);
$element['#theme_wrappers'] = array('date_form_element');
}
}
}
}
@@ -318,13 +352,14 @@ function date_repeat_field_widget_validate($element, &$form_state) {
// The RRULE has already been created by this point, so go back
// to the posted values to see if this was filled out.
$error_field_base = implode('][', $element['#parents']);
$error_field_until = $error_field_base . '][rrule][until_child][datetime][';
$error_field_until = $error_field_base . '][rrule][until_child][datetime][';
if (!empty($item['rrule']) && $rrule_values['range_of_repeat'] === 'UNTIL' && empty($rrule_values['UNTIL']['datetime'])) {
switch ($instance['widget']['type']) {
case 'date_text':
case 'date_popup':
form_set_error($error_field_until . 'date', t("Missing value in 'Range of repeat'. (UNTIL).", array(), array('context' => 'Date repeat')));
break;
case 'date_select':
form_set_error($error_field_until . 'year', t("Missing value in 'Range of repeat': Year (UNTIL)", array(), array('context' => 'Date repeat')));
form_set_error($error_field_until . 'month', t("Missing value in 'Range of repeat': Month (UNTIL)", array(), array('context' => 'Date repeat')));
@@ -346,15 +381,8 @@ function date_repeat_field_widget_validate($element, &$form_state) {
// the repeating dates, wipe out the previous values, and populate the
// field with the new values.
// TODO
// Is it right to not do anything unless there are changes? Will that
// confuse anyone? Commenting that out for now...
$rrule = $item['rrule'];
if (!empty($rrule)
//&& ($rrule != $element['rrule']['#prev_rrule']
//|| $item['value'] != $element['rrule']['#prev_value']
//|| $item['value2'] != $element['rrule']['#prev_value2'])
) {
if (!empty($rrule)) {
// Avoid undefined index problems on dates that don't have all parts.
$possible_items = array('value', 'value2', 'timezone', 'offset', 'offset2');
@@ -367,8 +395,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.
if (!empty($rrule_values['UNTIL'])) {
$gran = array('year', 'month', 'day', 'hour', 'minute', 'second');
$rrule_values['UNTIL']['datetime'] .= ' 23:59:59';
$rrule_values['UNTIL']['granularity'] = serialize(drupal_map_assoc(array('year', 'month', 'day', 'hour', 'minute', 'second')));
$rrule_values['UNTIL']['granularity'] = serialize(drupal_map_assoc($gran));
$rrule_values['UNTIL']['all_day'] = 0;
}
$value = date_repeat_build_dates($rrule, $rrule_values, $field, $item);
@@ -403,9 +432,10 @@ function date_repeat_after_build(&$element, &$form_state) {
* 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) {
include_once(DRUPAL_ROOT . '/' . drupal_get_path('module', 'date_api') . '/date_api_ical.inc');
// @codingStandardsIgnoreEnd
include_once DRUPAL_ROOT . '/' . drupal_get_path('module', 'date_api') . '/date_api_ical.inc';
$field_name = $field['field_name'];
if (empty($rrule)) {
@@ -482,8 +512,9 @@ function date_repeat_build_dates($rrule = NULL, $rrule_values = NULL, $field, $i
'offset2' => date_offset_get($date_end),
'timezone' => $timezone,
'rrule' => $rrule,
);
);
}
return $value;
}
@@ -514,9 +545,6 @@ function date_repeat_field_date_combo_process_alter(&$element, &$form_state, $co
'#date_increment' => $instance['widget']['settings']['increment'],
'#date_year_range' => $instance['widget']['settings']['year_range'],
'#date_label_position' => $instance['widget']['settings']['label_position'],
'#prev_value' => isset($item['value']) ? $item['value'] : '',
'#prev_value2' => isset($item['value2']) ? $item['value2'] : '',
'#prev_rrule' => isset($item['rrule']) ? $item['rrule'] : '',
'#date_repeat_widget' => str_replace('_repeat', '', $instance['widget']['type']),
'#date_repeat_collapsed' => $instance['widget']['settings']['repeat_collapsed'],
'#date_flexible' => 0,
@@ -626,6 +654,17 @@ function date_repeat_field_form_field_ui_field_edit_form_alter(&$form, &$form_st
$form['field']['cardinality']['#disabled'] = TRUE;
$form['field']['cardinality']['#value'] = FIELD_CARDINALITY_UNLIMITED;
}
// Repeating dates need unlimited values, confirm that in element_validate.
$form['field']['#element_validate'] = array('date_repeat_field_set_cardinality');
}
/**
* Ensure the cardinality gets updated if the option to make a date repeating is checked.
*/
function date_repeat_field_set_cardinality($element, &$form_state) {
if (!empty($form_state['values']['field']['settings']['repeat'])) {
form_set_value($element['cardinality'], FIELD_CARDINALITY_UNLIMITED, $form_state);
}
}
/**
@@ -658,9 +697,8 @@ function date_repeat_field_date_field_widget_settings_form_alter(&$form, $contex
'#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')),
'#fieldset' => 'date_format',
);
);
}
}
/**
@@ -28,10 +28,14 @@ function date_tools_change_type_form() {
// Get the available date fields.
foreach ($fields as $field_name => $field) {
if ($field['type'] == 'date' || $field['type'] == 'datestamp' || $field['type'] == 'datetime') {
$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']]));
$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']]
));
}
}
if (sizeof($date_options) < 1) {
if (count($date_options) < 1) {
drupal_set_message(t('There are no date fields in this database.'));
return $form;
}
@@ -142,26 +146,31 @@ function date_tools_change_type_form_submit($form, &$form_state) {
case 'datestamp':
$new_columns[] = $date_handler->sql_format('U', $db_field) . ' AS ' . $info['column'];
break;
case 'datetime':
$new_columns[] = $date_handler->sql_format('Y-m-d H:i:s', $db_field) . ' AS ' . $info['column'];
break;
}
break;
case 'datestamp':
switch ($new_type) {
case 'date':
$new_columns[] = $date_handler->sql_format('Y-m-d/TH:i:s', $db_field) . ' AS ' . $info['column'];
break;
case 'datetime':
$new_columns[] = $date_handler->sql_format('Y-m-d H:i:s', $db_field) . ' AS ' . $info['column'];
break;
}
break;
case 'datetime':
switch ($new_type) {
case 'date':
$new_columns[] = $date_handler->sql_format('Y-m-d/TH:i:s', $db_field) . ' AS ' . $info['column'];
break;
case 'datestamp':
$new_columns[] = $date_handler->sql_format('U', $db_field) . ' AS ' . $info['column'];
break;
@@ -178,5 +187,9 @@ function date_tools_change_type_form_submit($form, &$form_state) {
db_query($sql);
db_query("DROP TABLE {" . $temp_table . "}");
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])));
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]
)));
}
@@ -6,9 +6,9 @@ core = 7.x
configure = admin/config/date/tools
files[] = tests/date_tools.test
; Information added by drupal.org packaging script on 2012-08-13
version = "7.x-2.6"
; Information added by Drupal.org packaging script on 2017-04-07
version = "7.x-2.10"
core = "7.x"
project = "date"
datestamp = "1344850024"
datestamp = "1491562090"
@@ -31,7 +31,7 @@ function date_tools_help($section, $arg) {
*/
function date_tools_permission() {
return array(
'administer date tools' => array(
'administer date tools' => array(
'title' => t('Administer date tools'),
),
);
@@ -68,6 +68,7 @@ function date_tools_menu() {
'file' => 'date_tools.wizard.inc',
);
// @codingStandardsIgnoreStart
/**
$items['admin/config/date/tools/change'] = array(
'title' => 'Change type',
@@ -79,18 +80,18 @@ function date_tools_menu() {
'file' => 'date_tools.change_type.inc',
);
*/
// @codingStandardsIgnoreEnd
return $items;
}
/**
* Main Date Tools page
* 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')));
$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;
}
@@ -6,6 +6,8 @@
*/
/**
* Implements hook_form().
*
* @todo.
*/
function date_tools_wizard_form() {
@@ -59,7 +61,10 @@ function date_tools_wizard_form() {
$form['field']['repeat'] = array(
'#type' => 'select',
'#default_value' => 0,
'#options' => array(0 => t('No'), 1 => t('Yes')),
'#options' => array(
0 => t('No'),
1 => t('Yes'),
),
'#title' => t('Show repeating date options'),
'#access' => module_exists('date_repeat_field'),
);
@@ -72,7 +77,11 @@ function date_tools_wizard_form() {
$form['field']['advanced']['todate'] = array(
'#type' => 'select',
'#default_value' => 'optional',
'#options' => array('' => t('Never'), 'optional' => t('Optional'), 'required' => t('Required')),
'#options' => array(
'' => t('Never'),
'optional' => t('Optional'),
'required' => t('Required'),
),
'#title' => t('End Date'),
'#description' => t("Display a matching second date field as a 'End date'."),
);
@@ -106,7 +115,10 @@ function date_tools_wizard_form() {
$form['calendar'] = array(
'#type' => 'select',
'#default_value' => module_exists('calendar'),
'#options' => array(0 => t('No'), 1 => t('Yes')),
'#options' => array(
0 => t('No'),
1 => t('Yes'),
),
'#title' => t('Create a calendar for this date field'),
'#access' => module_exists('calendar'),
);
@@ -119,13 +131,26 @@ function date_tools_wizard_form() {
}
/**
* Form validate.
*
* @todo.
*/
function date_tools_wizard_form_validate(&$form, &$form_state) {
$bundle = $form_state['values']['bundle'];
$field_name = 'field_' . $form_state['values']['field_name'];
$existing_type = db_query("SELECT type FROM {node_type} WHERE type=:bundle", array(':bundle' => $bundle))->fetchField();
$existing_instance = db_query("SELECT field_name FROM {field_config_instance} WHERE field_name=:field_name AND bundle=:bundle AND entity_type=:entity_type", array(':field_name' => $field_name, ':bundle' => $bundle, ':entity_type' => 'node'))->fetchField();
$args = array(
':field_name' => $field_name,
':bundle' => $bundle,
':entity_type' => 'node',
);
$query = "SELECT type FROM {node_type} WHERE type=:bundle";
$existing_type = db_query($query, array(':bundle' => $args[':bundle']))->fetchField();
$query = "SELECT field_name FROM {field_config_instance} WHERE field_name=:field_name AND bundle=:bundle AND entity_type=:entity_type";
$existing_instance = db_query($query, $args)->fetchField();
if ($existing_type) {
drupal_set_message(t('This content type name already exists, adding new field to existing content type.'));
}
@@ -147,6 +172,8 @@ function date_tools_wizard_form_validate(&$form, &$form_state) {
}
/**
* Form submit.
*
* @todo.
*/
function date_tools_wizard_form_submit(&$form, &$form_state) {
@@ -161,6 +188,8 @@ function date_tools_wizard_form_submit(&$form, &$form_state) {
}
/**
* Wizard build.
*
* @todo.
*/
function date_tools_wizard_build($form_values) {
@@ -201,7 +230,7 @@ function date_tools_wizard_build($form_values) {
'timezone_db' => date_get_timezone_db($tz_handling),
'repeat' => $repeat,
'todate' => !empty($todate) ? $todate : 'optional',
),
),
);
$instance = array(
'entity_type' => 'node',
@@ -275,6 +304,8 @@ function date_tools_wizard_build($form_values) {
}
/**
* Includes handler.
*
* @todo.
*/
function date_tools_wizard_include() {
@@ -285,6 +316,8 @@ function date_tools_wizard_include() {
}
/**
* Implements hook_field_types().
*
* @todo.
*/
function date_tools_wizard_field_types() {
@@ -296,6 +329,7 @@ function date_tools_wizard_field_types() {
}
/**
* Implements hook_widget_types().
* @todo.
*/
function date_tools_wizard_widget_types() {
@@ -309,6 +343,8 @@ function date_tools_wizard_widget_types() {
}
/**
* Tz handler.
*
* @todo.
*/
function date_tools_wizard_tz_handling() {
@@ -317,6 +353,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()) {
@@ -332,8 +370,7 @@ function date_tools_wizard_create_content_type($name, $bundle, $description, $ty
'body_label' => 'Body',
'min_word_count' => '0',
'help' => '',
'node_options' =>
array(
'node_options' => array(
'status' => 1,
'promote' => 1,
'sticky' => 0,
@@ -374,8 +411,10 @@ function date_tools_wizard_create_content_type($name, $bundle, $description, $ty
'weight' => -4,
'module' => 'text',
),
'settings' => array('display_summary' => TRUE),
'display' => array(
'settings' => array(
'display_summary' => TRUE,
),
'display' => array(
'default' => array(
'label' => 'hidden',
'type' => 'text_default',
@@ -28,7 +28,7 @@ class DateToolsTestCase extends DrupalWebTestCase {
// Create and log in our privileged user.
$this->privileged_user = $this->drupalCreateUser(
array('administer content types', 'administer nodes', 'bypass node access', 'administer date tools')
array('administer content types', 'administer nodes', 'bypass node access', 'administer date tools', 'administer fields')
);
$this->drupalLogin($this->privileged_user);
@@ -9,13 +9,12 @@ files[] = includes/date_views_argument_handler.inc
files[] = includes/date_views_argument_handler_simple.inc
files[] = includes/date_views_filter_handler.inc
files[] = includes/date_views_filter_handler_simple.inc
files[] = includes/date_views.views_default.inc
files[] = includes/date_views.views.inc
files[] = includes/date_views_plugin_pager.inc
; Information added by drupal.org packaging script on 2012-08-13
version = "7.x-2.6"
; Information added by Drupal.org packaging script on 2017-04-07
version = "7.x-2.10"
core = "7.x"
project = "date"
datestamp = "1344850024"
datestamp = "1491562090"
@@ -0,0 +1,54 @@
<?php
/**
* @file
* Install, update and uninstall functions for the Date Views module.
*/
/**
* Implements hook_install().
*/
function date_views_install() {
variable_set('date_views_month_format_with_year', 'F Y');
variable_set('date_views_month_format_without_year', 'F');
variable_set('date_views_day_format_with_year', 'l, F j, Y');
variable_set('date_views_day_format_without_year', 'l, F j');
variable_set('date_views_week_format_with_year', 'F j, Y');
variable_set('date_views_week_format_without_year', 'F j');
}
/**
* Implements hook_uninstall().
*/
function date_views_uninstall() {
variable_del('date_views_month_format_with_year');
variable_del('date_views_month_format_without_year');
variable_del('date_views_day_format_with_year');
variable_del('date_views_day_format_without_year');
variable_del('date_views_week_format_with_year');
variable_del('date_views_week_format_without_year');
}
/**
* Set default date views variables.
*/
function date_views_update_7200() {
if (!variable_get('date_views_month_format_with_year', FALSE)) {
variable_set('date_views_month_format_with_year', 'F Y');
}
if (!variable_get('date_views_month_format_without_year', FALSE)) {
variable_set('date_views_month_format_without_year', 'F');
}
if (!variable_get('date_views_day_format_with_year', FALSE)) {
variable_set('date_views_day_format_with_year', 'l, F j, Y');
}
if (!variable_get('date_views_day_format_without_year', FALSE)) {
variable_set('date_views_day_format_without_year', 'l, F j');
}
if (!variable_get('date_views_week_format_with_year', FALSE)) {
variable_set('date_views_week_format_with_year', 'F j, Y');
}
if (!variable_get('date_views_week_format_without_year', FALSE)) {
variable_set('date_views_week_format_without_year', 'F j');
}
}
@@ -1,5 +1,84 @@
<?php
/**
* @file
* Date Views module.
*/
/**
* Implements hook_menu().
*/
function date_views_menu() {
// Used to import files from a local filesystem into Drupal.
$items['admin/config/regional/date-time/date-views'] = array(
'title' => 'Date views',
'description' => 'Configure settings for date views.',
'page callback' => 'drupal_get_form',
'page arguments' => array('date_views_settings'),
'access arguments' => array('administer site configuration'),
'type' => MENU_LOCAL_TASK,
);
return $items;
}
/**
* Form callback for date views settings.
*/
function date_views_settings($form, &$form_state) {
$form['date_views_month_format_with_year'] = array(
'#type' => 'textfield',
'#title' => t('Date views month format with year'),
'#size' => 10,
'#default_value' => variable_get('date_views_month_format_with_year', 'F Y'),
'#description' => t('Date views month format with year, default value : F Y'),
);
$form['date_views_month_format_without_year'] = array(
'#type' => 'textfield',
'#title' => t('Date views month format without year'),
'#size' => 10,
'#default_value' => variable_get('date_views_month_format_without_year', 'F'),
'#description' => t('Date views month format without year, default value : F'),
);
$form['date_views_day_format_with_year'] = array(
'#type' => 'textfield',
'#title' => t('Date views day format with year'),
'#size' => 10,
'#default_value' => variable_get('date_views_day_format_with_year', 'l, F j, Y'),
'#description' => t('Date views day format with year, default value : l, F j, Y'),
);
$form['date_views_day_format_without_year'] = array(
'#type' => 'textfield',
'#title' => t('Date views day format without year'),
'#size' => 10,
'#default_value' => variable_get('date_views_day_format_without_year', 'l, F j'),
'#description' => t('Date views day format without year, default value : l, F j'),
);
$form['date_views_week_format_with_year'] = array(
'#type' => 'textfield',
'#title' => t('Date views week format with year'),
'#size' => 10,
'#default_value' => variable_get('date_views_week_format_with_year', 'F j, Y'),
'#description' => t('Date views week format with year, default value : F j, Y'),
);
$form['date_views_week_format_without_year'] = array(
'#type' => 'textfield',
'#title' => t('Date views week format without year'),
'#size' => 10,
'#default_value' => variable_get('date_views_week_format_without_year', 'F j'),
'#description' => t('Date views week format without year, default value : F j'),
);
return system_settings_form($form);
}
/**
* Implements hook_views_api().
*
@@ -11,13 +90,30 @@ function date_views_theme() {
'file' => 'theme.inc',
'path' => "$path/theme",
);
return array(
'date_nav_title' => $base + array('variables' => array('granularity' => NULL, 'view' => NULL, 'link' => NULL, 'format' => NULL)),
'date_views_filter_form' => $base + array('template' => 'date-views-filter-form', 'render element' => 'form'),
'date_calendar_day' => $base + array('variables' => array('date' => NULL)),
return array(
'date_nav_title' => $base + array(
'variables' => array(
'granularity' => NULL,
'view' => NULL,
'link' => NULL,
'format' => NULL,
),
),
'date_views_filter_form' => $base + array(
'template' => 'date-views-filter-form',
'render element' => 'form',
),
'date_calendar_day' => $base + array(
'variables' => array(
'date' => NULL,
),
),
'date_views_pager' => $base + array(
'variables' => array('plugin' => NULL, 'input' => NULL),
'variables' => array(
'plugin' => NULL,
'input' => NULL,
),
// Register a pattern so that it can work like all views templates.
'pattern' => 'date_views_pager__',
'template' => 'date-views-pager',
@@ -25,6 +121,9 @@ function date_views_theme() {
);
}
/**
* Implements hook_views_api().
*/
function date_views_views_api() {
return array(
'api' => 3,
@@ -44,7 +143,7 @@ function date_views_views_fetch_fields($base, $type) {
}
/**
* Identify all potential date/timestamp fields and cache the data.
* Identify all potential date/timestamp fields and cache the data.
*/
function date_views_fields($base = 'node', $reset = FALSE) {
static $fields = array();
@@ -66,8 +165,8 @@ 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().
*
* Map extra Views tables to the entity that holds its date fields, needed for Views tables other than the primary tables identified in entity_info().
*/
function date_views_date_views_extra_tables() {
return array(
@@ -76,14 +175,13 @@ function date_views_date_views_extra_tables() {
}
/**
* 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, to make it easier to infer the entity type from a base table.
*
* Views has a new handler called views_handler_field_entity() that loads
* entities, and you can use something like the following to get the
* entity type from a view, but not all our base tables contain the
* entity information we need, (i.e. revisions) so it won't work here
* and we resort to creating information from entity_get_info().
* Views has a new handler called views_handler_field_entity() that loads entities.
*
* And you can use something like the following to get the entity type from a view, but not all our base tables contain the entity information we need, (i.e. revisions).
*
* So it won't work here and we resort to creating information from entity_get_info().
*
* // A method to get the entity type for a base table.
* $table_data = views_fetch_data($base_table);
@@ -118,11 +216,7 @@ 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.
* All modules that create custom fields that use the 'views_handler_field_date' handler can provide additional information here about the type of date they create so the date can be used by the Date API views date argument and date filter.
*/
function date_views_date_views_fields($field) {
$values = array(
@@ -188,12 +282,15 @@ function date_pager_url($view, $date_type = NULL, $date_arg = NULL, $force_view_
case 'year':
$args[$pos] = date_pad($view->date_info->year, 4);
break;
case 'week':
$args[$pos] = date_pad($view->date_info->year, 4) . '-W' . date_pad($view->date_info->week);
break;
case 'day':
$args[$pos] = date_pad($view->date_info->year, 4) . '-' . date_pad($view->date_info->month) . '-' . date_pad($view->date_info->day);
break;
default:
$args[$pos] = date_pad($view->date_info->year, 4) . '-' . date_pad($view->date_info->month);
break;
@@ -223,9 +320,14 @@ function date_pager_url($view, $date_type = NULL, $date_arg = NULL, $force_view_
// if they use exposed filters.
return url($view->get_url($args), array(
'query' => date_views_querystring($view),
'absolute' => $absolute));
'absolute' => $absolute,
)
);
}
/**
* Identifier of a date block.
*/
function date_block_identifier($view) {
if (!empty($view->block_identifier)) {
return $view->block_identifier;
@@ -236,12 +338,9 @@ 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.
* 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).
* Also use friendlier labels to distinguish the start date and end date in listings (for fields that use both).
*/
function date_views_field_views_data_alter(&$result, $field, $module) {
if ($module == 'date') {
@@ -261,8 +360,8 @@ function date_views_field_views_data_alter(&$result, $field, $module) {
$result[$table][$column]['field']['is date'] = TRUE;
// Not sure yet if we still need a custom field handler in D7 now that custom formatters are available.
// Might still need it to handle grouping of multiple value dates.
//$result[$table][$column]['field']['handler'] = 'date_handler_field_date';
//$result[$table][$column]['field']['add fields to query'] = TRUE;
// $result[$table][$column]['field']['handler'] = 'date_handler_field_date';
// $result[$table][$column]['field']['add fields to query'] = TRUE;
}
// For filters, arguments, and sorts, determine if this column is for
@@ -320,12 +419,25 @@ function date_views_field_views_data_alter(&$result, $field, $module) {
// translatable string. This is a hack to get it to appear right
// before 'end date' in the listing (i.e., in a non-alphabetical,
// but more user friendly, order).
$result[$table][$column]['title'] = t('@label - start date (!name)', array('@label' => $label, '!name' => $field['field_name']));
$result[$table][$column]['title short'] = t('@label - start date', array('@label' => $label));
$result[$table][$column]['title'] = t('@label - start date (!name)', array(
'@label' => $label,
'!name' => $field['field_name'],
));
$result[$table][$column]['title short'] = t('@label - start date', array(
'@label' => $label,
));
break;
case 'value2':
$result[$table][$column]['title'] = t('@label - end date (!name:!column)', array('@label' => $label, '!name' => $field['field_name'], '!column' => $this_column));
$result[$table][$column]['title short'] = t('@label - end date:!column', array('@label' => $label, '!column' => $this_column));
$result[$table][$column]['title'] = t('@label - end date (!name:!column)', array(
'@label' => $label,
'!name' => $field['field_name'],
'!column' => $this_column,
));
$result[$table][$column]['title short'] = t('@label - end date:!column', array(
'@label' => $label,
'!column' => $this_column,
));
break;
}
}
@@ -346,18 +458,15 @@ 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.
* 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.
*/
* The test for $handler->min_date tells us that this is an argument that not only is derived from the views date handler but also has been processed by the Date Views filter or argument code.
*/
function date_views_handler_is_date($handler, $type = 'argument') {
switch ($type) {
case 'filter':
return $handler instanceof views_handler_filter_date && !empty($handler->min_date);
case 'argument':
return $handler instanceof views_handler_argument_date && !empty($handler->min_date);
}
@@ -366,8 +475,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.
@@ -378,7 +487,7 @@ function date_views_select_validate(&$form, &$form_state) {
$filled = array();
$value = drupal_array_get_nested_value($form_state['input'], $form['#parents']);
foreach ($granularity as $part) {
if (!empty($value['value'][$part])) {
if (isset($value['value']) && is_numeric($value['value'][$part])) {
$filled[] = $part;
}
}
@@ -389,18 +498,23 @@ function date_views_select_validate(&$form, &$form_state) {
case 'year':
form_error($form['value'][$part], t('Please choose a year.'), $form_state);
break;
case 'month':
form_error($form['value'][$part], t('Please choose a month.'), $form_state);
break;
case 'day':
form_error($form['value'][$part], t('Please choose a day.'), $form_state);
break;
case 'hour':
form_error($form['value'][$part], t('Please choose an hour.'), $form_state);
break;
case 'minute':
form_error($form['value'][$part], t('Please choose a minute.'), $form_state);
break;
case 'second':
form_error($form['value'][$part], t('Please choose a second.'), $form_state);
break;
@@ -412,8 +526,7 @@ 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.
* If we are displaying a date from a view, see if we have information about which multiple value to display. If so, set the date_id in the entity.
*/
function date_views_date_formatter_pre_view_alter(&$entity, &$variables) {
// Some views have no row index.
@@ -426,4 +539,4 @@ function date_views_date_formatter_pre_view_alter(&$entity, &$variables) {
$entity->date_id = 'date.' . $date_item->$date_id . '.' . $field['field_name'] . '.' . $date_item->$date_delta . '.0';
}
}
}
}
@@ -1,6 +1,7 @@
<?php
/**
* @file
* Empty file to avoid fatal error if it doesn't exist.
* Formerly the attachment for the Date Browser.
*/
*/
@@ -27,8 +27,9 @@
* links by date, requires the date argument and uses the current
* date argument default to set a starting point for the view.
*/
/**
* Implements hook_views_plugins
* Implements hook_views_plugins().
*/
function date_views_views_plugins() {
$path = drupal_get_path('module', 'date_views');
@@ -36,7 +37,8 @@ function date_views_views_plugins() {
module_load_include('inc', 'date_views', 'theme/theme');
return array(
'module' => 'date_views', // This just tells our themes are elsewhere.
// This just tells our themes are elsewhere.
'module' => 'date_views',
'display' => array(
// Display plugin for date navigation.
'date_nav' => array(
@@ -83,7 +85,7 @@ function date_views_views_plugins() {
}
/**
* Implements hook_views_data()
* Implements hook_views_data().
*/
function date_views_views_data() {
$data = array();
@@ -95,12 +97,12 @@ function date_views_views_data() {
$data[$base_table]['date_argument'] = array(
'group' => t('Date'),
'title' => t('Date (!base_table)', array('!base_table' => $base_table)),
'help' => t('Filter any Views !base_table date field by a date argument, using any common ISO date/period format (i.e. YYYY, YYYY-MM, YYYY-MM-DD, YYYY-W99, YYYY-MM-DD--P3M, P90D, etc). ', array('!base_table' => $base_table)),
'help' => t('Filter any Views !base_table date field by a date argument, using any common ISO date/period format (i.e. YYYY, YYYY-MM, YYYY-MM-DD, YYYY-W99, YYYY-MM-DD--P3M, P90D, etc).', array('!base_table' => $base_table)),
'argument' => array(
'handler' => 'date_views_argument_handler',
'empty field name' => t('Undated'),
'is date' => TRUE,
//'skip base' => $base_table,
// 'skip base' => $base_table,
),
);
// The flexible date filter.
@@ -112,7 +114,7 @@ function date_views_views_data() {
'handler' => 'date_views_filter_handler',
'empty field name' => t('Undated'),
'is date' => TRUE,
//'skip base' => $base_table,
// 'skip base' => $base_table,
),
);
}
@@ -128,7 +130,7 @@ 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.
foreach ($data as $module => &$table) {
foreach ($data as $base_table => &$table) {
foreach ($table as $id => &$field) {
foreach (array('field', 'sort', 'filter', 'argument') as $type) {
if (isset($field[$type]) && isset($field[$type]['handler']) && ($field[$type]['handler'] == 'views_handler_' . $type . '_date')) {
@@ -140,8 +142,9 @@ function date_views_views_data_alter(&$data) {
}
/**
* Central function for setting up the right timezone values
* in the SQL date handler.
* Central function for setting up the right timezone values.
*
* In the SQL date handler.
*
* The date handler will use this information to decide if the
* database value needs a timezone conversion.
@@ -152,30 +155,45 @@ function date_views_views_data_alter(&$data) {
*/
function date_views_set_timezone(&$date_handler, &$view, $field) {
switch ($field['tz_handling']) {
case 'date' :
case 'date':
$date_handler->db_timezone = 'UTC';
$date_handler->local_timezone_field = $field['timezone_field'];
$date_handler->offset_field = $field['offset_field'];
break;
case 'none':
$date_handler->db_timezone = date_default_timezone();
$date_handler->local_timezone = date_default_timezone();
break;
case 'utc':
$date_handler->db_timezone = 'UTC';
$date_handler->local_timezone = 'UTC';
break;
default :
default:
$date_handler->db_timezone = 'UTC';
$date_handler->local_timezone = date_default_timezone();
break;
}
}
/**
* Helper function to generate a query string.
*
* @param object $view
* A View object.
*
* @param array $extra_params
* An extra parameters.
*
* @return null/string
* Return a query or NULL.
*/
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) {
foreach ($extra_params as $key => $value) {
if (!isset($value)) {
unset($query_params[$key]);
}
@@ -3,12 +3,14 @@
* @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.
* 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 {
/**
@@ -198,3 +200,4 @@ class date_views_argument_handler extends date_views_argument_handler_simple {
}
}
// @codingStandardsIgnoreEnd
@@ -7,11 +7,13 @@
/**
* Date API argument handler.
*/
// @codingStandardsIgnoreStart
class date_views_argument_handler_simple extends views_handler_argument_date {
/**
* Get granularity and use it to create the formula and a format
* for the results.
* Get granularity.
*
* Use it to create the formula and a format for the results.
*/
function init(&$view, &$options) {
parent::init($view, $options);
@@ -29,12 +31,14 @@ 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.
@@ -43,6 +47,9 @@ class date_views_argument_handler_simple extends views_handler_argument_date {
}
/**
* {@inheritdoc}
*/
function format() {
if (!empty($this->options['granularity'])) {
return $this->date_handler->views_formats($this->options['granularity']);
@@ -53,8 +60,9 @@ 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.
* Set the empty argument value to the current date.
*
* Formatted appropriately for this argument.
*/
function get_default_argument($raw = FALSE) {
$is_default = FALSE;
@@ -64,7 +72,7 @@ class date_views_argument_handler_simple extends views_handler_argument_date {
if ($granularity == 'week') {
$now = date_now();
$week = date_week(date_format($now, 'Y-m-d'));
$value = date_format($now, 'Y') . '-W' . $week;
$value = date_format($now, 'o') . '-W' . date_pad($week);
}
else {
$value = date($this->arg_format, REQUEST_TIME);
@@ -85,7 +93,8 @@ class date_views_argument_handler_simple extends views_handler_argument_date {
$options = parent::option_definition();
$options['year_range'] = array('default' => '-3:+3');
$options['granularity'] = array('default' => 'month');
$options['default_argument_type'] = array('default' => 'date');
$options['granularity_reset'] = array('default' => FALSE);
$options['default_argument_type']['default'] = 'date';
$options['add_delta'] = array('default' => '');
$options['use_fromto'] = array('default' => '');
$options['title_format'] = array('default' => '');
@@ -116,7 +125,9 @@ class date_views_argument_handler_simple extends views_handler_argument_date {
'#attributes' => array('class' => array('dependent-options')),
'#states' => array(
'visible' => array(
':input[name="options[default_action]"]' => array('value' => 'summary')
':input[name="options[default_action]"]' => array(
'value' => 'summary',
),
),
),
);
@@ -129,23 +140,37 @@ class date_views_argument_handler_simple extends views_handler_argument_date {
'#attributes' => array('class' => array('dependent-options')),
'#states' => array(
'visible' => array(
':input[name="options[title_format]"]' => array('value' => 'custom')
':input[name="options[title_format]"]' => array(
'value' => 'custom',
),
),
),
);
// Get default granularity options
$options = $this->date_handler->date_parts();
unset($options['second'], $options['minute']);
$options += array('week' => t('Week', array(), array('context' => 'datetime')));
// Add the 'week' option.
$options += array(
'week' => t('Week', array(), array(
'context' => 'datetime',
)),
);
$form['granularity'] = array(
'#title' => t('Granularity'),
'#type' => 'radios',
'#options' => $options,
'#default_value' => $this->options['granularity'],
'#multiple' => TRUE,
'#description' => t("Select the type of date value to be used in defaults, summaries, and navigation. For example, a granularity of 'month' will set the default date to the current month, summarize by month in summary views, and link to the next and previous month when using date navigation."),
);
$form['granularity_reset'] = array(
'#title' => t('Use granularity from argument value'),
'#type' => 'checkbox',
'#default_value' => $this->options['granularity_reset'],
'#description' => t("If the granularity of argument value is different from selected, use it from argument value."),
);
$form['year_range'] = array(
'#title' => t('Date year range'),
'#type' => 'textfield',
@@ -172,16 +197,18 @@ class date_views_argument_handler_simple extends views_handler_argument_date {
'#default_value' => $this->options['add_delta'],
'#options' => array('' => t('No'), 'yes' => t('Yes')),
'#description' => t('Add an identifier to the view to show which multiple value date fields meet the filter criteria. Note: This option may introduce duplicate values into the view. Required when using multiple value fields in a Calendar or any time you want the node view of multiple value dates to display only the values that match the view filters.'),
// Only let mere mortals tweak this setting for multi-value fields
// Only let mere mortals tweak this setting for multi-value fields.
'#access' => $access,
);
}
/**
* {@inheritdoc}
*/
function options_validate(&$form, &$form_state) {
// It is very important to call the parent function here:
parent::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'])) {
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'));
}
}
@@ -209,14 +236,15 @@ class date_views_argument_handler_simple extends views_handler_argument_date {
$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');
$range = $this->date_handler->arg_range($this->argument);
return date_format_date($range[0], 'custom', $format);
}
}
/**
* 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
* 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 $data
* @param object $data
* The query results for the row.
*/
function summary_argument($data) {
@@ -234,10 +262,11 @@ class date_views_argument_handler_simple extends views_handler_argument_date {
*/
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
// @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"));
@@ -245,7 +274,8 @@ class date_views_argument_handler_simple extends views_handler_argument_date {
// 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);
@@ -254,20 +284,22 @@ 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.
*/
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;
}
// 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.
$granularity = $this->date_handler->arg_granularity($this->argument);
if (!empty($granularity)) {
// 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');
$this->sql_format = $this->date_handler->views_formats($this->date_handler->granularity, 'sql');
@@ -276,7 +308,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);
@@ -291,7 +324,8 @@ 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.
@@ -302,10 +336,14 @@ 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'];
@@ -321,7 +359,10 @@ 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.
* Add a callback.
*
* To determine if we have moved outside
* the valid date range for this argument.
*/
function date_forbid() {
if (empty($this->argument)) {
@@ -343,3 +384,4 @@ class date_views_argument_handler_simple extends views_handler_argument_date {
}
}
// @codingStandardsIgnoreEnd
@@ -5,13 +5,11 @@
*/
/**
* Identify all potential date/timestamp fields.
* Identify all potential date/timestamp fields.
*
* @return
* array with fieldname, type, and table.
* @see
* date_views_date_views_fields() which implements
* the hook_date_views_fields() for the core date fields.
* @return array
* An array with fieldname, type, and table.
* @see date_views_date_views_fields()
*/
function _date_views_fields($base = 'node') {
@@ -60,7 +58,7 @@ function _date_views_fields($base = 'node') {
$handler = views_get_handler($table_name, $field_name, 'filter');
$handler_name = $handler->definition['handler'];
// We don't care about anything but date handlers
// We don't care about anything but date handlers.
if (empty($handler->definition['is date'])) {
continue;
}
@@ -72,14 +70,17 @@ function _date_views_fields($base = 'node') {
$field = field_info_field($handler->definition['field_name']);
$is_field = TRUE;
switch ($field['type']) {
case 'date':
case 'date':
$sql_type = DATE_ISO;
break;
case 'datestamp':
break;
case 'datetime':
$sql_type = DATE_DATETIME;
break;
default:
// If this is not a date field, nothing more to do.
continue;
@@ -88,7 +89,8 @@ function _date_views_fields($base = 'node') {
$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;
$granularity = !empty($field['granularity']) ? $field['granularity'] : array('year', 'month', 'day', 'hour', 'minute', 'second');
$grans = array('year', 'month', 'day', 'hour', 'minute', 'second');
$granularity = !empty($field['granularity']) ? $field['granularity'] : $grans;
$fromto = array(
$table_name . '.' . $db_info['columns'][$table_name]['value'],
@@ -3,9 +3,11 @@
* @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.
* where all fields are controlled by the same date and can be combined
* with either AND or OR.
*/
// @codingStandardsIgnoreStart
class date_views_filter_handler extends date_views_filter_handler_simple {
function init(&$view, &$options) {
parent::init($view, $options);
@@ -36,6 +38,36 @@ class date_views_filter_handler extends date_views_filter_handler_simple {
$this->date_combine_conditions('op_simple');
}
function op_contains($field) {
$this->date_combine_conditions('op_contains');
}
function op_empty($field) {
$this->get_query_fields();
if (empty($this->query_fields)) {
return;
}
// Add each condition to the custom filter group.
foreach ((array) $this->query_fields as $query_field) {
$field = $query_field['field'];
$this->date_handler = $query_field['date_handler'];
// Respect relationships when determining the table alias.
if ($field['table_name'] != $this->table || !empty($this->relationship)) {
$this->related_table_alias = $this->query->ensure_table($field['table_name'], $this->relationship);
}
else {
$this->related_table_alias = NULL;
}
$table_alias = !empty($this->related_table_alias) ? $this->related_table_alias : $field['table_name'];
$field_name = $table_alias . '.' . $field['field_name'];
parent::op_empty($field_name);
}
}
/**
* Combines multiple date WHERE expressions into a single WHERE expression.
*
@@ -60,6 +92,9 @@ class date_views_filter_handler extends date_views_filter_handler_simple {
if ($field['table_name'] != $this->table || !empty($this->relationship)) {
$this->related_table_alias = $this->query->ensure_table($field['table_name'], $this->relationship);
}
else {
$this->related_table_alias = NULL;
}
$table_alias = !empty($this->related_table_alias) ? $this->related_table_alias : $field['table_name'];
$field_name = $table_alias . '.' . $field['field_name'];
@@ -175,3 +210,4 @@ class date_views_filter_handler extends date_views_filter_handler_simple {
}
}
}
// @codingStandardsIgnoreEnd
@@ -1,9 +1,11 @@
<?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,
* using Date API form selectors and sql handling.
*/
// @codingStandardsIgnoreStart
class date_views_filter_handler_simple extends views_handler_filter_date {
var $date_handler = NULL;
var $offset = NULL;
@@ -42,6 +44,17 @@ class date_views_filter_handler_simple extends views_handler_filter_date {
return $options;
}
function operators() {
$operators = parent::operators();
$operators['contains'] = array(
'title' => t('Contains'),
'method' => 'op_contains',
'short' => t('contains'),
'values' => 1,
);
return $operators;
}
/**
* Helper function to find a default value.
*/
@@ -53,8 +66,8 @@ class date_views_filter_handler_simple extends views_handler_filter_date {
// If this is a remembered value, use the value from the SESSION.
if (!empty($this->options['expose']['remember'])) {
$display_id = ($this->view->display_handler->is_defaulted('filters')) ? 'default' : $this->view->current_display;
if (!empty($_SESSION['views'][$this->view->name][$display_id]['date_filter'][$prefix])) {
return $_SESSION['views'][$this->view->name][$display_id]['date_filter'][$prefix];
if (!empty($_SESSION['views'][$this->view->name][$display_id][$this->options['expose']['identifier']][$prefix])) {
return $_SESSION['views'][$this->view->name][$display_id][$this->options['expose']['identifier']][$prefix];
}
}
@@ -104,8 +117,12 @@ class date_views_filter_handler_simple extends views_handler_filter_date {
$element_input['value'] = $this->get_filter_value('value', !empty($element_input['value']) ? $element_input['value'] : '');
$element_input['min'] = $this->get_filter_value('min', !empty($element_input['min']) ? $element_input['min'] : '');
$element_input['max'] = $this->get_filter_value('max', !empty($element_input['max']) ? $element_input['max'] : '');
unset($element_input['default_date']);
unset($element_input['default_to_date']);
if (is_array($element_input) && isset($element_input['default_date'])) {
unset($element_input['default_date']);
}
if (is_array($element_input) && isset($element_input['default_to_date'])) {
unset($element_input['default_to_date']);
}
$input[$this->options['expose']['identifier']] = $element_input;
}
@@ -163,6 +180,29 @@ class date_views_filter_handler_simple extends views_handler_filter_date {
$this->query->add_where_expression($group, "$field $this->operator $placeholder", array($placeholder => $value));
}
function op_contains($field) {
// 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');
}
$value = $this->get_filter_value('value', $this->value['value']);
$comp_date = new DateObject($value, date_default_timezone(), $this->format);
$fields = date_views_fields($this->base_table);
$fields = $fields['name'];
$fromto = $fields[$field]['fromto'];
$field_min = $this->date_handler->sql_field($fromto[0], NULL, $comp_date);
$field_min = $this->date_handler->sql_format($this->format, $field_min);
$field_max = $this->date_handler->sql_field($fromto[1], NULL, $comp_date);
$field_max = $this->date_handler->sql_format($this->format, $field_max);
$placeholder_min = $this->placeholder();
$placeholder_max = $this->placeholder();
$group = !empty($this->options['date_group']) ? $this->options['date_group'] : $this->options['group'];
$this->query->add_where_expression($group, "$field_max >= $placeholder_min AND $field_min <= $placeholder_max", array($placeholder_min => $value, $placeholder_max => $value));
}
/**
* Set the granularity of the date parts to use in the filter.
*/
@@ -225,7 +265,7 @@ class date_views_filter_handler_simple extends views_handler_filter_date {
}
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'])) {
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'));
}
}
@@ -294,7 +334,7 @@ class date_views_filter_handler_simple extends views_handler_filter_date {
* @return
* The form date part element for this instance.
*/
function date_parts_form($form_state, $prefix, $source, $which, $operator_values, $identifier, $relative_id) {
function date_parts_form(&$form_state, $prefix, $source, $which, $operator_values, $identifier, $relative_id) {
module_load_include('inc', 'date_api', 'date_api_elements');
switch ($prefix) {
case 'min':
@@ -316,7 +356,7 @@ class date_views_filter_handler_simple extends views_handler_filter_date {
$type = 'date_text';
}
$format = $this->date_handler->views_formats($this->options['granularity'], 'sql');
$format = $this->date_handler->views_formats($this->options['granularity'], 'display');
$granularity = array_keys($this->date_handler->date_parts($this->options['granularity']));
$relative_value = ($prefix == 'max' ? $this->options['default_to_date'] : $this->options['default_date']);
@@ -333,7 +373,7 @@ class date_views_filter_handler_simple extends views_handler_filter_date {
'#date_label_position' => 'within',
'#date_year_range' => $this->options['year_range'],
'#process' => array($type . '_element_process'),
'#prefix' => '<div id="' . $id . '-wrapper"><div id="' . $id . '">',
'#prefix' => '<div id="' . $id . '-wrapper"><div id="' . $id . '-inside-wrapper">',
'#suffix' => '</div></div>',
);
if ($which == 'all') {
@@ -341,7 +381,10 @@ class date_views_filter_handler_simple extends views_handler_filter_date {
$form[$prefix]['#dependency'] = array($source => $operator_values);
}
if (!isset($form_state['input'][$identifier][$prefix])) {
$form_state['input'][$identifier][$prefix] = $this->value[$prefix];
// Ensure these exist.
foreach ($granularity as $key) {
$form_state['input'][$identifier][$prefix][$key] = NULL;
}
}
}
else {
@@ -368,7 +411,7 @@ class date_views_filter_handler_simple extends views_handler_filter_date {
'#date_label_position' => 'within',
'#date_year_range' => $this->options['year_range'],
'#process' => array($type . '_element_process'),
'#prefix' => '<div id="' . $id . '-wrapper"><div id="' . $id . '">',
'#prefix' => '<div id="' . $id . '-wrapper"><div id="' . $id . '-inside-wrapper">',
'#suffix' => '</div></div>',
'#states' => array(
'visible' => array(
@@ -492,3 +535,4 @@ class date_views_filter_handler_simple extends views_handler_filter_date {
}
}
// @codingStandardsIgnoreEnd
@@ -2,50 +2,74 @@
/**
* @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 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 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 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
* 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
* 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.
*/
// @codingStandardsIgnoreStart
class date_views_plugin_pager extends views_plugin_pager {
/**
@@ -79,6 +103,7 @@ class date_views_plugin_pager extends views_plugin_pager {
$options['link_format'] = array('default' => 'pager');
$options['date_argument'] = array('default' => 'Unknown');
$options['granularity'] = array('default' => 'Unknown');
$options['skip_empty_pages'] = array('default' => FALSE);
return $options;
}
@@ -110,6 +135,12 @@ class date_views_plugin_pager extends views_plugin_pager {
'#default_value' => $this->options['link_format'],
'#required' => TRUE,
);
$form['skip_empty_pages'] = array(
'#title' => t('Skip empty pages'),
'#type' => 'checkbox',
'#description' => t('When selected, the pager will not display pages with no result for the given date. This causes a slight performance degradation because two additional queries need to be executed.'),
'#default_value' => $this->options['skip_empty_pages'],
);
$form['date_argument']['#type'] = 'hidden';
$form['date_argument']['#value'] = $this->options['date_argument'];
$form['granularity']['#type'] = 'hidden';
@@ -150,13 +181,7 @@ class date_views_plugin_pager extends views_plugin_pager {
// Reset values set by argument if pager requires it.
if (!empty($value)) {
$argument->argument = $value;
$argument->date_range = $argument->date_handler->arg_range($value);
$argument->min_date = $argument->date_range[0];
$argument->max_date = $argument->date_range[1];
// $argument->is_default works correctly for normal arguments, but does not
// work correctly if we are swapping in a new value from the pager.
$argument->is_default = FALSE;
$this->set_argument_value($argument, $value);
}
// The pager value might move us into a forbidden range, so test it.
@@ -164,13 +189,102 @@ class date_views_plugin_pager extends views_plugin_pager {
$this->view->build_info['fail'] = TRUE;
return;
}
if (empty($this->view->date_info)) $this->view->date_info = new stdClass();
// 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();
}
$this->view->date_info->granularity = $argument->date_handler->granularity;
$format = $this->view->date_info->granularity == 'week' ? DATE_FORMAT_DATETIME : $argument->sql_format;
$this->view->date_info->placeholders = isset($argument->placeholders) ? $argument->placeholders : $argument->date_handler->placeholders;
$this->view->date_info->date_arg = $argument->argument;
$this->view->date_info->date_arg_pos = $i;
$this->view->date_info->limit = $argument->limit;
$this->view->date_info->url = $this->view->get_url();
$this->view->date_info->pager_id = $this->options['date_id'];
$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.
if ($this->view->date_info->skip_empty_pages) {
$q = clone $argument->query;
$field = $argument->table_alias . '.' . $argument->real_field;
$fieldsql = $date_handler->sql_field($field);
$fieldsql = $date_handler->sql_format($format, $fieldsql);
$q->clear_fields();
$q->orderby = array();
$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.
$q->add_orderby(NULL, $fieldsql, 'DESC', 'date');
$this->set_argument_placeholders($this->view->date_info->placeholders, $datelimits[0], $argument->max_date, $q, $format);
$compiledquery = $q->query();
$compiledquery->range(0, 2);
$results = $compiledquery->execute()->fetchCol(0);
$prevdate = array_shift($results);
$prevdatealt = array_shift($results);
// Find the first two dates between the lower bound
// of the current value and the maximum date.
$q->add_orderby(NULL, $fieldsql, 'ASC', 'date');
$this->set_argument_placeholders($this->view->date_info->placeholders, $argument->min_date, $datelimits[1], $q, $format);
$compiledquery = $q->query();
$compiledquery->range(0, 2);
$results = $compiledquery->execute()->fetchCol(0);
$nextdate = array_shift($results);
$nextdatealt = array_shift($results);
// 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
// default argument date value and choose that one.
if ($prevdate && $prevdatealt) {
$this->set_argument_value($argument, $prevdate);
$value = $prevdate;
$prevdate = $prevdatealt;
// If the first next date is the same as the first previous date,
// move to the following next date.
if ($value == $nextdate) {
$nextdate = $nextdatealt;
$nextdatealt = NULL;
}
}
elseif ($nextdate && $nextdatealt) {
$this->set_argument_value($argument, $nextdate);
$value = $nextdate;
$nextdate = $nextdatealt;
// If the first previous date is the same as the first next date,
// move to the following previous date.
if ($value == $prevdate) {
$prevdate = $prevdatealt;
$prevdatealt = NULL;
}
}
}
else {
// $prevdate and $nextdate are the same as $value, so move to
// the next values.
$prevdate = $prevdatealt;
$nextdate = $nextdatealt;
}
$this->view->date_info->prev_date = $prevdate ? new DateObject($prevdate, NULL, $format) : NULL;
$this->view->date_info->next_date = $nextdate ? new DateObject($nextdate, NULL, $format) : NULL;
}
else {
$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);
date_modify($this->view->date_info->next_date, '+1 ' . $argument->date_handler->granularity);
}
// Write the date_info properties that depend on the current value.
$this->view->date_info->year = date_format($argument->min_date, 'Y');
$this->view->date_info->month = date_format($argument->min_date, 'n');;
$this->view->date_info->day = date_format($argument->min_date, 'j');
@@ -178,11 +292,6 @@ class date_views_plugin_pager extends views_plugin_pager {
$this->view->date_info->date_range = $argument->date_range;
$this->view->date_info->min_date = $argument->min_date;
$this->view->date_info->max_date = $argument->max_date;
$this->view->date_info->limit = $argument->limit;
$this->view->date_info->url = $this->view->get_url();
$this->view->date_info->pager_id = $this->options['date_id'];
$this->view->date_info->date_pager_position = $this->options['pager_position'];
$this->view->date_info->date_pager_format = $this->options['link_format'];
}
$i++;
}
@@ -191,20 +300,33 @@ class date_views_plugin_pager extends views_plugin_pager {
// 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)) {
$placeholders = $this->view->date_info->placeholders;
$count = count($placeholders);
foreach ($this->view->query->where as $group => $data) {
foreach ($data['conditions'] as $delta => $condition) {
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.
$date = ($count == 2) ? $this->view->date_info->min_date : $this->view->date_info->max_date;
$next_placeholder = array_shift($placeholders);
$this->view->query->where[$group]['conditions'][$delta]['value'][$placeholder] = $date->format($format);
$count--;
}
$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);
}
}
function set_argument_value($argument, $value) {
$argument->argument = $value;
$argument->date_range = $argument->date_handler->arg_range($value);
$argument->min_date = $argument->date_range[0];
$argument->max_date = $argument->date_range[1];
// $argument->is_default works correctly for normal arguments, but does not
// work correctly if we are swapping in a new value from the pager.
$argument->is_default = FALSE;
}
function set_argument_placeholders($placeholders, $mindate, $maxdate, $query, $format) {
$count = count($placeholders);
foreach ($query->where as $group => $data) {
foreach ($data['conditions'] as $delta => $condition) {
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.
$date = ($count == 2) ? $mindate : $maxdate;
$next_placeholder = array_shift($placeholders);
$query->where[$group]['conditions'][$delta]['value'][$placeholder] = $date->format($format);
$count--;
}
}
}
@@ -230,4 +352,5 @@ class date_views_plugin_pager extends views_plugin_pager {
$pager_theme = views_theme_functions('date_views_pager', $this->view, $this->display);
return theme($pager_theme, array('plugin' => $this, 'input' => $input));
}
}
}
// @codingStandardsIgnoreEnd
@@ -27,8 +27,10 @@
* be used in the l() function, including rel=nofollow.
*/
?>
<?php if (!empty($pager_prefix)) print $pager_prefix; ?>
<div class="date-nav-wrapper clearfix<?php if (!empty($extra_classes)) print $extra_classes; ?>">
<?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; ?>">
<div class="date-nav item-list">
<div class="date-heading">
<h3><?php print $nav_title ?></h3>
@@ -36,14 +38,18 @@
<ul class="pager">
<?php if (!empty($prev_url)) : ?>
<li class="date-prev">
<?php print l('&laquo;' . ($mini ? '' : ' ' . t('Prev', array(), array('context' => 'date_nav'))), $prev_url, $prev_options); ?>
&nbsp;</li>
<?php
$text = '&laquo;';
$text .= $mini ? '' : ' ' . t('Prev', array(), array('context' => 'date_nav'));
print l(t($text), $prev_url, $prev_options);
?>
</li>
<?php endif; ?>
<?php if (!empty($next_url)) : ?>
<li class="date-next">&nbsp;
<li class="date-next">
<?php print l(($mini ? '' : t('Next', array(), array('context' => 'date_nav')) . ' ') . '&raquo;', $next_url, $next_options); ?>
</li>
<?php endif; ?>
</ul>
</div>
</div>
</div>
@@ -4,6 +4,7 @@
* @file
* Theme files for Date Pager.
*/
/**
* Jump in and move the pager.
*/
@@ -15,9 +16,11 @@ function date_views_preprocess_views_view(&$vars) {
$vars['header'] .= $vars['pager'];
$vars['pager'] = '';
break;
case 'both':
$vars['header'] .= $vars['pager'];
break;
default:
// Already on the bottom.
}
@@ -66,28 +69,37 @@ function template_preprocess_date_views_pager(&$vars) {
}
if (empty($date_info->hide_nav)) {
$prev_date = clone($min_date);
date_modify($prev_date, '-1 ' . $granularity);
$next_date = clone($min_date);
date_modify($next_date, '+1 ' . $granularity);
$format = array('year' => 'Y', 'month' => 'Y-m', 'day' => 'Y-m-d');
switch ($granularity) {
case 'week':
$next_week = date_week(date_format($next_date, 'Y-m-d'));
$prev_week = date_week(date_format($prev_date, 'Y-m-d'));
$next_arg = date_format($next_date, 'Y-\W') . date_pad($next_week);
$prev_arg = date_format($prev_date, 'Y-\W') . date_pad($prev_week);
break;
default:
$next_arg = date_format($next_date, $format[$granularity]);
$prev_arg = date_format($prev_date, $format[$granularity]);
$prev_date = $date_info->prev_date;
$next_date = $date_info->next_date;
$format = array('year' => 'Y', 'month' => 'Y-m', 'day' => 'Y-m-d', 'hour' => 'Y-m-d\TH');
if (!empty($prev_date)) {
switch ($granularity) {
case 'week':
$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]);
}
$prev_path = str_replace($date_info->date_arg, $prev_arg, $date_info->url);
$prev_args[$pos] = $prev_arg;
$vars['prev_url'] = date_pager_url($view, NULL, $prev_arg);
}
$next_path = str_replace($date_info->date_arg, $next_arg, $date_info->url);
$prev_path = str_replace($date_info->date_arg, $prev_arg, $date_info->url);
$next_args[$pos] = $next_arg;
$prev_args[$pos] = $prev_arg;
$vars['next_url'] = date_pager_url($view, NULL, $next_arg);
$vars['prev_url'] = date_pager_url($view, NULL, $prev_arg);
if (!empty($next_date)) {
switch ($granularity) {
case 'week':
$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]);
}
$next_path = str_replace($date_info->date_arg, $next_arg, $date_info->url);
$next_args[$pos] = $next_arg;
$vars['next_url'] = date_pager_url($view, NULL, $next_arg);
}
$vars['next_options'] = $vars['prev_options'] = array();
}
else {
@@ -117,14 +129,17 @@ function template_preprocess_date_views_pager(&$vars) {
$prev_title = t('Navigate to previous year');
$next_title = t('Navigate to next year');
break;
case 'month':
$prev_title = t('Navigate to previous month');
$next_title = t('Navigate to next month');
break;
case 'week':
$prev_title = t('Navigate to previous week');
$next_title = t('Navigate to next week');
break;
case 'day':
$prev_title = t('Navigate to previous day');
$next_title = t('Navigate to next day');
@@ -157,32 +172,44 @@ function template_preprocess_date_views_pager(&$vars) {
}
/**
* Theme the calendar title
* Theme the calendar title.
*/
function theme_date_nav_title($params) {
$title = '';
$granularity = $params['granularity'];
$view = $params['view'];
$date_info = $view->date_info;
$link = !empty($params['link']) ? $params['link'] : FALSE;
$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;
$date_arg = $date_info->year;
break;
case 'month':
$format = !empty($format) ? $format : (empty($date_info->mini) ? 'F Y' : 'F');
$format = !empty($format) ? $format : (empty($date_info->mini) ? $format_with_year : $format_without_year);
$title = date_format_date($date_info->min_date, 'custom', $format);
$date_arg = $date_info->year . '-' . date_pad($date_info->month);
break;
case 'day':
$format = !empty($format) ? $format : (empty($date_info->mini) ? 'l, F j, Y' : 'l, F j');
$format = !empty($format) ? $format : (empty($date_info->mini) ? $format_with_year : $format_without_year);
$title = date_format_date($date_info->min_date, 'custom', $format);
$date_arg = $date_info->year . '-' . date_pad($date_info->month) . '-' . date_pad($date_info->day);
$date_arg = $date_info->year;
$date_arg .= '-';
$date_arg .= date_pad($date_info->month);
$date_arg .= '-';
$date_arg .= date_pad($date_info->day);
break;
case 'week':
$format = !empty($format) ? $format : (empty($date_info->mini) ? 'F j, Y' : 'F j');
$title = t('Week of @date', array('@date' => date_format_date($date_info->min_date, 'custom', $format)));
$format = !empty($format) ? $format : (empty($date_info->mini) ? $format_with_year : $format_without_year);
$title = t('Week of @date', array(
'@date' => date_format_date($date_info->min_date, 'custom', $format),
));
$date_arg = $date_info->year . '-W' . date_pad($date_info->week);
break;
}
@@ -5,8 +5,7 @@
* Test date UI.
*/
class DateUITestCase extends DrupalWebTestCase {
protected $privileged_user;
class DateUITestCase extends DateFieldBasic {
/**
* @todo.
@@ -23,14 +22,7 @@ class DateUITestCase extends DrupalWebTestCase {
* @todo.
*/
public function setUp() {
// Load the date_api module.
parent::setUp('field', 'field_ui', 'date_api', 'date', 'date_popup', 'date_tools');
// Create and log in our privileged user.
$this->privileged_user = $this->drupalCreateUser(
array('administer content types', 'administer nodes', 'bypass node access', 'administer date tools')
);
$this->drupalLogin($this->privileged_user);
parent::setUp();
variable_set('date_format_long', 'D, m/d/Y - H:i');
}
@@ -39,82 +31,34 @@ class DateUITestCase extends DrupalWebTestCase {
* @todo.
*/
public function testFieldUI() {
$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.');
$label = 'Test';
$current_year = date('Y');
// Creates select list field stored as a date with default settings.
$this->createDateField($type = 'date', $widget = 'date_select');
$edit = array();
$this->drupalPost(NULL, $edit, t('Save field settings'));
$this->dateForm($options = 'select');
$this->assertText('Thu, 10/07/2010 - 10:30', 'Found the correct date for a date field using the date_select widget.');
$this->deleteDateField();
// Creates text field stored as a date with default settings.
$this->createDateField($type = 'date', $widget = 'date_text');
$edit = array();
$this->drupalPost(NULL, $edit, t('Save field settings'));
$this->dateForm($options = 'text');
$this->assertText('Thu, 10/07/2010 - 10:30', 'Found the correct date for a date field using the date_text widget.');
$this->deleteDateField();
// Creates popup field stored as a date with default settings.
$this->createDateField($type = 'date', $widget = 'date_popup');
$edit = array();
$this->drupalPost(NULL, $edit, t('Save field settings'));
$this->dateForm($options = 'popup');
$this->assertText('Thu, 10/07/2010 - 10:30', 'Found the correct date for a date field using the date_popup widget.');
$this->deleteDateField();
// Creates select list field stored as a datestamp with default settings.
$this->createDateField($type = 'datestamp', $widget = 'date_select');
$edit = array();
$this->drupalPost(NULL, $edit, t('Save field settings'));
$this->dateForm($options = 'select');
$this->assertText('Thu, 10/07/2010 - 10:30', 'Found the correct date for a datestamp field using the date_select widget.');
$this->deleteDateField();
// Creates text field stored as a datestamp with default settings.
$this->createDateField($type = 'datestamp', $widget = 'date_text');
$edit = array();
$this->drupalPost(NULL, $edit, t('Save field settings'));
$this->dateForm($options = 'text');
$this->assertText('Thu, 10/07/2010 - 10:30', 'Found the correct date for a datestamp field using the date_text widget.');
$this->deleteDateField();
// Creates popup field stored as a datestamp with default settings.
$this->createDateField($type = 'datestamp', $widget = 'date_popup');
$edit = array();
$this->drupalPost(NULL, $edit, t('Save field settings'));
$this->dateForm($options = 'popup');
$this->assertText('Thu, 10/07/2010 - 10:30', 'Found the correct date for a datestamp field using the date_popup widget.');
$this->deleteDateField();
// Creates select list field stored as a datetime with default settings.
$this->createDateField($type = 'datetime', $widget = 'date_select');
$edit = array();
$this->drupalPost(NULL, $edit, t('Save field settings'));
$this->dateForm($options = 'select');
$this->assertText('Thu, 10/07/2010 - 10:30', 'Found the correct date for a datetime field using the date_select widget.');
$this->deleteDateField();
// Creates text field stored as a datetime with default settings.
$this->createDateField($type = 'datetime', $widget = 'date_text');
$edit = array();
$this->drupalPost(NULL, $edit, t('Save field settings'));
$this->dateForm($options = 'text');
$this->assertText('Thu, 10/07/2010 - 10:30', 'Found the correct date for a datetime field using the date_text widget.');
$this->deleteDateField();
// Creates popup field stored as a datetime with default settings.
$this->createDateField($type = 'datetime', $widget = 'date_popup');
$edit = array();
$this->drupalPost(NULL, $edit, t('Save field settings'));
$this->dateForm($options = 'popup');
$this->assertText('Thu, 10/07/2010 - 10:30', 'Found the correct date for a datetime field using the date_popup widget.');
$this->deleteDateField();
$field_types = array('date', 'datestamp', 'datetime');
$widget_types = array('date_select', 'date_text', 'date_popup');
// Test widgets with default settings using every widget and field type.
foreach ($field_types as $field_type) {
foreach ($widget_types as $widget_type) {
$this->createDateField(
array(
'label' => $label,
'field_type' => $field_type,
'widget_type' => $widget_type,
)
);
$this->dateForm($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);
}
}
// Test timezone handling validation on the field settings form.
$this->createDateField($type = 'date', $widget = 'date_select');
$this->createDateField(array('label' => $label, 'field_type' => 'date', 'widget_type' => 'date_select', 'granularity' => array('year', 'month', 'day')));
$edit = array('field[settings][granularity][hour]' => FALSE);
$this->drupalPost(NULL, $edit, t('Save field settings'));
$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();
$this->deleteDateField($label);
}
/**
@@ -125,44 +69,23 @@ class DateUITestCase extends DrupalWebTestCase {
$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';
$current_year = date('Y');
if ($options == '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 ($options == 'text') {
$edit['field_test[und][0][value][date]'] = '10/07/2010 - 10:30';
elseif ($options == 'date_text') {
$edit['field_test[und][0][value][date]'] = format_string('10/07/!year - 10:30', array('!year' => $current_year));
}
elseif ($options == 'popup') {
$edit['field_test[und][0][value][date]'] = '10/07/2010';
elseif ($options == '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');
}
/**
* @todo.
*/
function createDateField($type, $widget) {
$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][type]'] = $type;
$edit['fields[_add_new_field][widget_type]'] = $widget;
$this->drupalPost('admin/structure/types/manage/story/fields', $edit, t('Save'));
}
/**
* @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.');
}
}
@@ -132,11 +132,11 @@ class DateAPITestCase extends DrupalWebTestCase {
// 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-03';
$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-02-01';
$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.");
@@ -144,11 +144,11 @@ class DateAPITestCase extends DrupalWebTestCase {
// 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-04';
$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-02';
$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.");
@@ -393,9 +393,31 @@ class DateAPITestCase extends DrupalWebTestCase {
$input = '23 abc 2012';
$timezone = NULL;
$format = 'd M Y';
$date = new dateObject($input, $timezone, $format);
$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.");
}
/**
@@ -16,7 +16,7 @@ abstract class DateFieldBasic extends DrupalWebTestCase {
// Create and log in our privileged user.
$this->privileged_user = $this->drupalCreateUser(
array('administer content types', 'administer nodes', 'bypass node access', 'administer date tools')
array('administer content types', 'administer nodes', 'bypass node access', 'administer date tools', 'administer fields')
);
$this->drupalLogin($this->privileged_user);
@@ -52,7 +52,7 @@ abstract class DateFieldBasic extends DrupalWebTestCase {
$repeat = !empty($repeat) ? $repeat : 0;
$todate = !empty($todate) ? $todate : 'optional';
$widget_type = !empty($widget_type) ? $widget_type : 'date_select';
$tz_handling = !empty($tz_handing) ? $tz_handling : 'site';
$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);
@@ -151,6 +151,123 @@ abstract class DateFieldBasic extends DrupalWebTestCase {
}
/**
* 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 right below the title.
'weight' => -4,
'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' => -4,
),
'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(TRUE);
field_cache_clear(TRUE);
// 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");
}
/**
* @todo.
*/
@@ -0,0 +1,30 @@
<?php
/**
* @file
* Contains form specific date element test cases.
*/
class DateFormTestCase extends DrupalWebTestCase {
public static function getInfo() {
return array(
'name' => t('Date Form test'),
'description' => t('Test Date form functions.') ,
'group' => t('Date'),
);
}
public function setUp() {
// Load the date_api module.
parent::setUp('date_test');
}
/**
* Tests rendering of a date element in a form.
*/
public function testDateForm() {
$this->drupalGet('date-test/form');
}
}
@@ -15,9 +15,10 @@ class DateMigrateExampleUnitTest extends DrupalWebTestCase {
*/
public static function getInfo() {
return array(
'name' => 'Date2 migration',
'description' => 'Testing migration of date fields',
'group' => 'Migrate',
'name' => 'Date Migration',
'description' => 'Test migration into date fields',
'group' => 'Date',
'dependencies' => array('migrate', 'features'),
);
}
@@ -25,7 +26,17 @@ class DateMigrateExampleUnitTest extends DrupalWebTestCase {
* Declars the module dependencies for the test.
*/
function setUp() {
parent::setUp('migrate', 'features', 'date', 'date_repeat', 'date_repeat_field', 'date_migrate_example');
parent::setUp('migrate', 'features', 'date', 'date_repeat',
'date_repeat_field', 'date_migrate_example');
// 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);
}
}
/**
@@ -0,0 +1,14 @@
name = "Date module tests"
description = "Support module for date related testing."
package = Date/Time
version = VERSION
core = 7.x
hidden = TRUE
dependencies[] = date
; Information added by Drupal.org packaging script on 2017-04-07
version = "7.x-2.10"
core = "7.x"
project = "date"
datestamp = "1491562090"
@@ -0,0 +1,40 @@
<?php
/**
* @file
* Contains date test implementations.
*/
/**
* Implements hook_menu().
*/
function date_test_menu() {
$items['date-test/form'] = array(
'title' => 'Test form with date element',
'description' => "Form with date element to make form related tests",
'page callback' => 'drupal_get_form',
'page arguments' => array('date_test_sample_form'),
'access arguments' => array('access content'),
'type' => MENU_CALLBACK,
);
return $items;
}
/**
* Form callback. Generates a test form with date elements.
*/
function date_test_sample_form($form, &$form_state) {
$form['date_test_select'] = array(
'#type' => 'date_select',
'#title' => t('Sample from'),
'#date_format' => 'H:i:s a',
'#default_value' => array(
'hour' => 7,
'minute' => 0,
'second' => 0,
'ampm' => 'am'
),
);
return $form;
}
@@ -16,6 +16,16 @@ class DateTimezoneTestCase extends DateFieldBasic {
);
}
public function setUp() {
parent::setUp();
// 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.
*/
@@ -23,7 +33,7 @@ class DateTimezoneTestCase extends DateFieldBasic {
// 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') as $tz_handling) {
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') {
@@ -50,6 +60,111 @@ class DateTimezoneTestCase extends DateFieldBasic {
}
}
/**
* 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 form when using 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.");
}
}
/**
* @todo.
*/
@@ -77,17 +192,32 @@ class DateTimezoneTestCase extends DateFieldBasic {
case 'hour':
$edit[$field_name . '[und][0][value][date]'] = '10/07/2010 - 10';
$edit[$field_name . '[und][0][value2][date]'] = '10/07/2010 - 11';
$should_be = 'Thu, 10/07/2010 - 10 to Thu, 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';
$should_be = 'Thu, 10/07/2010 - 10:30 to 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';
$should_be = 'Thu, 10/07/2010 - 10:30:30 to 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'));
@@ -0,0 +1,129 @@
<?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',
);
}
/**
* Test setup actions.
*/
public function setUp() {
// Load the 'date_views', 'views', 'views_ui', 'ctools' modules.
parent::setUp('date_views', 'views', 'views_ui', 'ctools');
// 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 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');
}
}
@@ -0,0 +1,106 @@
<?php
/**
* @file
* Tests date popup in Views
*/
class DateViewsPopupTestCase extends DateFieldBasic {
/**
* Test info.
*/
public static function getInfo() {
return array(
'name' => 'Date Views - Popup Test',
'description' => 'Tests date popup in Views',
'group' => 'Date',
);
}
/**
* Test setup actions.
*/
public function setUp() {
parent::setUp();
// Load the 'date_popup', 'date_views', 'views', 'views_ui', 'ctools' modules.
$modules = array('date_popup', 'date_views', 'views', 'views_ui', 'ctools');
$success = module_enable($modules, TRUE);
$this->assertTrue($success, t('Enabled modules: %modules', array('%modules' => implode(', ', $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');
}
}
@@ -19,8 +19,14 @@ function _entityreference_devel_generate($object, $field, $instance, $bundle) {
// Get all the entity that are referencable here.
$referencable_entity = entityreference_get_selection_handler($field, $instance)->getReferencableEntities();
if (is_array($referencable_entity) && !empty($referencable_entity)) {
// Get a random key.
$object_field['target_id'] = array_rand($referencable_entity);
// $referencable_entity is keyed by bundle type.
$random_bundle = array_rand($referencable_entity);
if (!empty($random_bundle)) {
$target_id = array_rand($referencable_entity[$random_bundle]);
if (!empty($referencable_entity[$random_bundle][$target_id])) {
$object_field['target_id'] = $target_id;
}
}
}
return $object_field;
}
@@ -15,14 +15,42 @@ function entityreference_feeds_processor_targets_alter(&$targets, $entity_type,
foreach (field_info_instances($entity_type, $bundle_name) as $name => $instance) {
$info = field_info_field($name);
if ($info['type'] == 'entityreference') {
// We don't use ":guid" in key, not to break existing configurations.
$targets[$name] = array(
'name' => check_plain($instance['label']),
'name' => check_plain($instance['label'] . t(' (Entity reference by Feeds GUID)')),
'callback' => 'entityreference_feeds_set_target',
'description' => t('The field instance @label of @id', array(
'description' => t('The field instance @label of @id matched by Feeds GUID.', array(
'@label' => $instance['label'],
'@id' => $name,
)),
);
$targets[$name . ':url'] = array(
'name' => check_plain($instance['label'] . t(' (Entity reference by Feeds URL)')),
'callback' => 'entityreference_feeds_set_target',
'description' => t('The field instance @label of @id matched by Feeds URL.', array(
'@label' => $instance['label'],
'@id' => $name,
)),
'real_target' => $name,
);
$targets[$name . ':etid'] = array(
'name' => check_plain($instance['label'] . t(' (Entity reference by Entity ID)')),
'callback' => 'entityreference_feeds_set_target',
'description' => t('The field instance @label of @id matched by Entity ID.', array(
'@label' => $instance['label'],
'@id' => $name,
)),
'real_target' => $name,
);
$targets[$name . ':label'] = array(
'name' => check_plain($instance['label'] . t(' (Entity reference by Entity label)')),
'callback' => 'entityreference_feeds_set_target',
'description' => t('The field instance @label of @id matched by Entity label.', array(
'@label' => $instance['label'],
'@id' => $name,
)),
'real_target' => $name,
);
}
}
}
@@ -42,12 +70,8 @@ function entityreference_feeds_processor_targets_alter(&$targets, $entity_type,
* The target key on $entity to map to.
* @param $value
* The value to map. MUST be an array.
* @param $mapping
* Array of mapping settings for current value.
* @param $input_format
* TRUE if an input format should be applied.
*/
function entityreference_feeds_set_target($source, $entity, $target, $value, $mapping, $input_format = FALSE) {
function entityreference_feeds_set_target($source, $entity, $target, $value) {
// Don't do anything if we weren't given any data.
if (empty($value)) {
@@ -62,8 +86,19 @@ function entityreference_feeds_set_target($source, $entity, $target, $value, $ma
$values = array($value);
}
// Determine the field we are matching against.
if (strpos($target, ':') === FALSE) {
$match_key = 'guid';
}
else {
list($target, $match_key) = explode(':', $target, 2);
}
// Get some useful field information.
$info = field_info_field($target);
if ($match_key == 'label') {
$handler = entityreference_get_selection_handler($info);
}
// Set the language of the field depending on the mapping.
$language = isset($mapping['language']) ? $mapping['language'] : LANGUAGE_NONE;
@@ -75,13 +110,32 @@ function entityreference_feeds_set_target($source, $entity, $target, $value, $ma
// Only process if this value was set for this instance.
if ($value) {
// Fetch the entity ID resulting from the mapping table look-up.
$entity_id = db_query(
'SELECT entity_id FROM {feeds_item} WHERE guid = :guid',
array(':guid' => $value)
)->fetchField();
switch ($match_key) {
case 'guid':
case 'url':
// Fetch the entity ID resulting from the mapping table look-up.
$entity_id = db_select('feeds_item', 'fi')
->fields('fi', array('entity_id'))
->condition($match_key, $value,'=')
->execute()
->fetchField();
break;
case 'etid':
$entity_id = $value;
break;
case 'label':
$options = $handler->getReferencableEntities($value, '=');
if ($options) {
$options = reset($options);
$etids = array_keys($options);
// Use the first matching entity.
$entity_id = reset($etids);
}
else {
$entity_id = NULL;
}
break;
}
/*
* Only add a reference to an existing entity ID if there exists a
* mapping between it and the provided GUID. In cases where no such
@@ -106,6 +160,7 @@ function entityreference_feeds_set_target($source, $entity, $target, $value, $ma
* this opportunity later, we need to destroy the hash.
*/
unset($entity->feeds_item->hash);
$source->log('entityreference', t('No existing entity found for entity @source_id entityreference to source entity @value', array('@source_id' => $entity->feeds_item->entity_id, '@value' => $value)));
}
}
@@ -1,18 +1,23 @@
name = Entity Reference
description = Provides a field that can reference other entities.
core = 7.x
package = Fields
core = 7.x
dependencies[] = entity
dependencies[] = ctools
test_dependencies[] = feeds
test_dependencies[] = views
; Migrate handler.
files[] = entityreference.migrate.inc
; Our plugins interfaces and abstract implementations.
; Plugins interfaces and abstract implementations.
files[] = plugins/selection/abstract.inc
files[] = plugins/selection/views.inc
files[] = plugins/behavior/abstract.inc
; Views integration.
files[] = views/entityreference_plugin_display.inc
files[] = views/entityreference_plugin_style.inc
files[] = views/entityreference_plugin_row_fields.inc
@@ -21,10 +26,12 @@ files[] = views/entityreference_plugin_row_fields.inc
files[] = tests/entityreference.handlers.test
files[] = tests/entityreference.taxonomy.test
files[] = tests/entityreference.admin.test
files[] = tests/entityreference.feeds.test
files[] = tests/entityreference.entity_translation.test
; Information added by drupal.org packaging script on 2012-11-18
version = "7.x-1.0"
; Information added by Drupal.org packaging script on 2017-08-16
version = "7.x-1.5"
core = "7.x"
project = "entityreference"
datestamp = "1353230808"
datestamp = "1502895850"
@@ -41,6 +41,7 @@ function entityreference_field_schema($field) {
}
// Invoke the behaviors to allow them to change the schema.
module_load_include('module', 'entityreference');
foreach (entityreference_get_behavior_handlers($field) as $handler) {
$handler->schema_alter($schema, $field);
}
@@ -161,4 +162,29 @@ function entityreference_update_7002() {
'not null' => TRUE,
));
}
}
}
/**
* Implements hook_update_N().
*
* Remove duplicate rows in the taxonomy_index table.
*/
function entityreference_update_7100() {
if (db_table_exists('taxonomy_index')) {
if (db_table_exists('taxonomy_index_tmp')) {
db_drop_table('taxonomy_index_tmp');
}
$tx_schema = drupal_get_schema('taxonomy_index');
db_create_table('taxonomy_index_tmp', $tx_schema);
$select = db_select('taxonomy_index', 'tx');
$select->fields('tx', array('nid', 'tid'));
$select->groupBy('tx.nid');
$select->groupBy('tx.tid');
$select->addExpression('MAX(sticky)', 'sticky');
$select->addExpression('MAX(created)', 'created');
db_insert('taxonomy_index_tmp')->from($select)->execute();
db_drop_table('taxonomy_index');
db_rename_table('taxonomy_index_tmp', 'taxonomy_index');
}
}
@@ -1,21 +1,27 @@
<?php
/**
* @file
* Support for processing entity reference fields in Migrate.
*/
/**
* Implement hook_migrate_api().
* Implements hook_migrate_api().
*/
function entityreference_migrate_api() {
return array(
'api' => 2,
'field_handlers' => array('MigrateEntityReferenceFieldHandler'),
'field handlers' => array('MigrateEntityReferenceFieldHandler'),
);
}
/**
* Extended class for handling entityreference fields.
*/
class MigrateEntityReferenceFieldHandler extends MigrateSimpleFieldHandler {
/**
* Constructor.
*/
public function __construct() {
parent::__construct(array(
'value_key' => 'target_id',
@@ -1,5 +1,12 @@
<?php
define('ENTITYREFERENCE_DENIED', '- Restricted access -');
/**
* @file
* Entityreference primary module file.
*/
/**
* Implements hook_ctools_plugin_directory().
*/
@@ -87,6 +94,20 @@ function entityreference_flush_caches() {
variable_set('entityreference:base-tables', $base_tables);
}
/**
* Implements hook_theme().
*/
function entityreference_theme($existing, $type, $theme, $path) {
return array(
'entityreference_label' => array(
'variables' => array('label' => NULL, 'item' => NULL, 'settings' => NULL, 'uri' => NULL),
),
'entityreference_entity_id' => array(
'variables' => array('item' => NULL, 'settings' => NULL),
),
);
}
/**
* Implements hook_menu().
*/
@@ -163,7 +184,7 @@ function entityreference_get_behavior_handlers($field, $instance = NULL) {
/**
* Get the behavior handler for a given entityreference field and instance.
*
* @param $handler
* @param $behavior
* The behavior handler name.
*/
function _entityreference_get_behavior_handler($behavior) {
@@ -220,13 +241,15 @@ function entityreference_field_validate($entity_type, $entity, $field, $instance
if ($ids) {
$valid_ids = entityreference_get_selection_handler($field, $instance, $entity_type, $entity)->validateReferencableEntities(array_keys($ids));
$invalid_entities = array_diff_key($ids, array_flip($valid_ids));
if ($invalid_entities) {
foreach ($invalid_entities as $id => $delta) {
$errors[$field['field_name']][$langcode][$delta][] = array(
'error' => 'entityreference_invalid_entity',
'message' => t('The referenced entity (@type: @id) is invalid.', array('@type' => $field['settings']['target_type'], '@id' => $id)),
);
if (!empty($valid_ids)) {
$invalid_entities = array_diff_key($ids, array_flip($valid_ids));
if ($invalid_entities) {
foreach ($invalid_entities as $id => $delta) {
$errors[$field['field_name']][$langcode][$delta][] = array(
'error' => 'entityreference_invalid_entity',
'message' => t('The referenced entity (@type: @id) is invalid.', array('@type' => $field['settings']['target_type'], '@id' => $id)),
);
}
}
}
}
@@ -398,6 +421,9 @@ function entityreference_field_settings_form($field, $instance, $has_data) {
return $form;
}
/**
* Callback for custom element processing.
*/
function _entityreference_field_settings_process($form, $form_state) {
$field = isset($form_state['entityreference']['field']) ? $form_state['entityreference']['field'] : $form['#field'];
$instance = isset($form_state['entityreference']['instance']) ? $form_state['entityreference']['instance'] : $form['#instance'];
@@ -479,11 +505,17 @@ function _entityreference_field_settings_process($form, $form_state) {
return $form;
}
/**
* Custom callback for ajax processing.
*/
function _entityreference_field_settings_ajax_process($form, $form_state) {
_entityreference_field_settings_ajax_process_element($form, $form);
return $form;
}
/**
* Helper function for custom ajax processing.
*/
function _entityreference_field_settings_ajax_process_element(&$element, $main_form) {
if (isset($element['#ajax']) && $element['#ajax'] === TRUE) {
$element['#ajax'] = array(
@@ -498,6 +530,9 @@ function _entityreference_field_settings_ajax_process_element(&$element, $main_f
}
}
/**
* Custom callback for element processing.
*/
function _entityreference_form_process_merge_parent($element) {
$parents = $element['#parents'];
array_pop($parents);
@@ -505,11 +540,17 @@ function _entityreference_form_process_merge_parent($element) {
return $element;
}
/**
* Helper function to remove blank elements.
*/
function _entityreference_element_validate_filter(&$element, &$form_state) {
$element['#value'] = array_filter($element['#value']);
form_set_value($element, $element['#value'], $form_state);
}
/**
* Implements hook_validate().
*/
function _entityreference_field_settings_validate($form, &$form_state) {
// Store the new values in the form state.
$field = $form['#field'];
@@ -545,6 +586,9 @@ function entityreference_field_instance_settings_form($field, $instance) {
return $form;
}
/**
* Implements hook_field_settings_form().
*/
function _entityreference_field_instance_settings_form($form, $form_state) {
$field = isset($form_state['entityreference']['field']) ? $form_state['entityreference']['field'] : $form['#field'];
$instance = isset($form_state['entityreference']['instance']) ? $form_state['entityreference']['instance'] : $form['#instance'];
@@ -562,6 +606,9 @@ function _entityreference_field_instance_settings_form($form, $form_state) {
return $form;
}
/**
* Implements hook_validate().
*/
function _entityreference_field_instance_settings_validate($form, &$form_state) {
// Store the new values in the form state.
$instance = $form['#instance'];
@@ -791,6 +838,11 @@ function entityreference_query_entityreference_alter(QueryAlterableInterface $qu
* Implements hook_field_widget_form().
*/
function entityreference_field_widget_form(&$form, &$form_state, $field, $instance, $langcode, $items, $delta, $element) {
// Ensure that the entity target type exists before displaying the widget.
$entity_info = entity_get_info($field['settings']['target_type']);
if (empty($entity_info)) {
return;
}
$entity_type = $instance['entity_type'];
$entity = isset($element['#entity']) ? $element['#entity'] : NULL;
$handler = entityreference_get_selection_handler($field, $instance, $entity_type, $entity);
@@ -813,7 +865,9 @@ function entityreference_field_widget_form(&$form, &$form_state, $field, $instan
// Build an array of entities ID.
foreach ($items as $item) {
$entity_ids[] = $item['target_id'];
if (isset($item['target_id'])) {
$entity_ids[] = $item['target_id'];
}
}
// Load those entities and loop through them to extract their labels.
@@ -874,6 +928,9 @@ function entityreference_field_widget_form(&$form, &$form_state, $field, $instan
}
}
/**
* Implements hook_validate().
*/
function _entityreference_autocomplete_validate($element, &$form_state, $form) {
// If a value was entered into the autocomplete...
$value = '';
@@ -898,6 +955,9 @@ function _entityreference_autocomplete_validate($element, &$form_state, $form) {
form_set_value($element, $value, $form_state);
}
/**
* Implements hook_validate().
*/
function _entityreference_autocomplete_tags_validate($element, &$form_state, $form) {
$value = array();
// If a value was entered into the autocomplete...
@@ -944,7 +1004,8 @@ function entityreference_field_widget_error($element, $error) {
* The entity type.
* @param $bundle_name
* The bundle name.
* @return
*
* @return bool
* True if user can access this menu item.
*/
function entityreference_autocomplete_access_callback($type, $field_name, $entity_type, $bundle_name) {
@@ -975,6 +1036,19 @@ function entityreference_autocomplete_access_callback($type, $field_name, $entit
* The label of the entity to query by.
*/
function entityreference_autocomplete_callback($type, $field_name, $entity_type, $bundle_name, $entity_id = '', $string = '') {
// If the request has a '/' in the search text, then the menu system will have
// split it into multiple arguments and $string will only be a partial.
// We want to make sure we recover the intended $string.
$args = func_get_args();
// Shift off the $type, $field_name, $entity_type,
// $bundle_name, and $entity_id args.
array_shift($args);
array_shift($args);
array_shift($args);
array_shift($args);
array_shift($args);
$string = implode('/', $args);
$field = field_info_field($field_name);
$instance = field_info_instance($entity_type, $field_name, $bundle_name);
@@ -1003,11 +1077,14 @@ function entityreference_autocomplete_callback($type, $field_name, $entity_type,
*/
function entityreference_autocomplete_callback_get_matches($type, $field, $instance, $entity_type, $entity_id = '', $string = '') {
$matches = array();
$prefix = '';
$entity = NULL;
if ($entity_id !== 'NULL') {
$entity = entity_load_single($entity_type, $entity_id);
if (!$entity || !entity_access('view', $entity_type, $entity)) {
$has_view_access = (entity_access('view', $entity_type, $entity) !== FALSE);
$has_update_access = (entity_access('update', $entity_type, $entity) !== FALSE);
if (!$entity || !($has_view_access || $has_update_access)) {
return MENU_ACCESS_DENIED;
}
}
@@ -1015,7 +1092,8 @@ function entityreference_autocomplete_callback_get_matches($type, $field, $insta
$handler = entityreference_get_selection_handler($field, $instance, $entity_type, $entity);
if ($type == 'tags') {
// The user enters a comma-separated list of tags. We only autocomplete the last tag.
// The user enters a comma-separated list of tags.
// We only autocomplete the last tag.
$tags_typed = drupal_explode_tags($string);
$tag_last = drupal_strtolower(array_pop($tags_typed));
if (!empty($tag_last)) {
@@ -1024,19 +1102,22 @@ function entityreference_autocomplete_callback_get_matches($type, $field, $insta
}
else {
// The user enters a single tag.
$prefix = '';
$tag_last = $string;
}
if (isset($tag_last)) {
// Get an array of matching entities.
$entity_labels = $handler->getReferencableEntities($tag_last, $instance['widget']['settings']['match_operator'], 10);
$denied_label = t(ENTITYREFERENCE_DENIED);
// Loop through the products and convert them into autocomplete output.
foreach ($entity_labels as $values) {
foreach ($values as $entity_id => $label) {
// Never autocomplete entities that aren't accessible.
if ($label == $denied_label) {
continue;
}
$key = "$label ($entity_id)";
// Strip things like starting/trailing white spaces, line breaks and tags.
// Strip starting/trailing white spaces, line breaks and tags.
$key = preg_replace('/\s\s+/', ' ', str_replace("\n", '', trim(decode_entities(strip_tags($key)))));
// Names containing commas or quotes must be wrapped in quotes.
if (strpos($key, ',') !== FALSE || strpos($key, '"') !== FALSE) {
@@ -1050,6 +1131,32 @@ function entityreference_autocomplete_callback_get_matches($type, $field, $insta
drupal_json_output($matches);
}
/**
* Introspects field and instance settings, and determines the correct settings
* for the functioning of the formatter.
*
* Settings:
* - entity_type - The entity_type being loaded.
* - column - The name of the ref. field column that stores the entity id.
*/
function entityreference_field_type_settings($field) {
$settings = array(
'entity_type' => NULL,
'column' => NULL,
);
if ($field['type'] == 'entityreference') {
$settings['entity_type'] = $field['settings']['target_type'];
$settings['column'] = 'target_id';
}
elseif ($field['type'] == 'taxonomy_term_reference') {
$settings['entity_type'] = 'taxonomy_term';
$settings['column'] = 'tid';
}
return $settings;
}
/**
* Implements hook_field_formatter_info().
*/
@@ -1061,6 +1168,7 @@ function entityreference_field_formatter_info() {
'field types' => array('entityreference'),
'settings' => array(
'link' => FALSE,
'bypass_access' => FALSE,
),
),
'entityreference_entity_id' => array(
@@ -1071,10 +1179,11 @@ function entityreference_field_formatter_info() {
'entityreference_entity_view' => array(
'label' => t('Rendered entity'),
'description' => t('Display the referenced entities rendered by entity_view().'),
'field types' => array('entityreference'),
'field types' => array('entityreference', 'taxonomy_term_reference'),
'settings' => array(
'view_mode' => '',
'view_mode' => 'default',
'links' => TRUE,
'use_content_language' => TRUE,
),
),
);
@@ -1086,8 +1195,17 @@ function entityreference_field_formatter_info() {
function entityreference_field_formatter_settings_form($field, $instance, $view_mode, $form, &$form_state) {
$display = $instance['display'][$view_mode];
$settings = $display['settings'];
$field_type_settings = entityreference_field_type_settings($field);
$element = array();
if ($display['type'] == 'entityreference_label') {
$element['bypass_access'] = array(
'#title' => t('Show entity labels regardless of user access'),
'#description' => t("All entities in the field will be shown, without checking them for access. If the 'Link' setting is also enabled, an entity which the user does not have access to view will show without a link."),
'#type' => 'checkbox',
'#default_value' => $settings['bypass_access'],
);
$element['link'] = array(
'#title' => t('Link label to the referenced entity'),
'#type' => 'checkbox',
@@ -1096,28 +1214,33 @@ function entityreference_field_formatter_settings_form($field, $instance, $view_
}
if ($display['type'] == 'entityreference_entity_view') {
$entity_info = entity_get_info($field['settings']['target_type']);
$options = array();
$entity_info = entity_get_info($field_type_settings['entity_type']);
$options = array('default' => t('Default'));
if (!empty($entity_info['view modes'])) {
foreach ($entity_info['view modes'] as $view_mode => $view_mode_settings) {
$options[$view_mode] = $view_mode_settings['label'];
}
}
if (count($options) > 1) {
$element['view_mode'] = array(
'#type' => 'select',
'#options' => $options,
'#title' => t('View mode'),
'#default_value' => $settings['view_mode'],
);
}
$element['view_mode'] = array(
'#type' => 'select',
'#options' => $options,
'#title' => t('View mode'),
'#default_value' => $settings['view_mode'],
'#access' => count($options) > 1,
);
$element['links'] = array(
'#type' => 'checkbox',
'#title' => t('Show links'),
'#default_value' => $settings['links'],
);
$element['use_content_language'] = array(
'#type' => 'checkbox',
'#title' => t('Use current content language'),
'#default_value' => $settings['use_content_language'],
);
}
return $element;
@@ -1129,17 +1252,24 @@ function entityreference_field_formatter_settings_form($field, $instance, $view_
function entityreference_field_formatter_settings_summary($field, $instance, $view_mode) {
$display = $instance['display'][$view_mode];
$settings = $display['settings'];
$field_type_settings = entityreference_field_type_settings($field);
$summary = array();
if ($display['type'] == 'entityreference_label') {
$summary[] = $settings['link'] ? t('Link to the referenced entity') : t('No link');
$summary[] = $settings['bypass_access'] ? t('Show labels regardless of access') : t('Respect entity access for label visibility');
}
if ($display['type'] == 'entityreference_entity_view') {
$entity_info = entity_get_info($field['settings']['target_type']);
$summary[] = t('Rendered as @mode', array('@mode' => isset($entity_info['view modes'][$settings['view_mode']]['label']) ? $entity_info['view modes'][$settings['view_mode']]['label'] : $settings['view_mode']));
$entity_info = entity_get_info($field_type_settings['entity_type']);
$view_mode_label = $settings['view_mode'] == 'default' ? t('Default') : $settings['view_mode'];
if (isset($entity_info['view modes'][$settings['view_mode']]['label'])) {
$view_mode_label = $entity_info['view modes'][$settings['view_mode']]['label'];
}
$summary[] = t('Rendered as @mode', array('@mode' => $view_mode_label));
$summary[] = !empty($settings['links']) ? t('Display links') : t('Do not display links');
$summary[] = !empty($settings['use_content_language']) ? t('Use current content language') : t('Use field language');
}
return implode('<br />', $summary);
@@ -1149,19 +1279,22 @@ function entityreference_field_formatter_settings_summary($field, $instance, $vi
* Implements hook_field_formatter_prepare_view().
*/
function entityreference_field_formatter_prepare_view($entity_type, $entities, $field, $instances, $langcode, &$items, $displays) {
$field_type_settings = entityreference_field_type_settings($field);
$target_type = $field_type_settings['entity_type'];
$column = $field_type_settings['column'];
$target_ids = array();
// Collect every possible entity attached to any of the entities.
foreach ($entities as $id => $entity) {
foreach ($items[$id] as $delta => $item) {
if (isset($item['target_id'])) {
$target_ids[] = $item['target_id'];
if (isset($item[$column])) {
$target_ids[] = $item[$column];
}
}
}
if ($target_ids) {
$target_entities = entity_load($field['settings']['target_type'], $target_ids);
$target_entities = entity_load($target_type, $target_ids);
}
else {
$target_entities = array();
@@ -1173,11 +1306,13 @@ function entityreference_field_formatter_prepare_view($entity_type, $entities, $
foreach ($items[$id] as $delta => $item) {
// Check whether the referenced entity could be loaded.
if (isset($target_entities[$item['target_id']])) {
if (isset($target_entities[$item[$column]]) && isset($target_entities[$item[$column]])) {
// Replace the instance value with the term data.
$items[$id][$delta]['entity'] = $target_entities[$item['target_id']];
$items[$id][$delta]['entity'] = $target_entities[$item[$column]];
// Check whether the user has access to the referenced entity.
$items[$id][$delta]['access'] = entity_access('view', $field['settings']['target_type'], $target_entities[$item['target_id']]);
$has_view_access = (entity_access('view', $target_type, $target_entities[$item[$column]]) !== FALSE);
$has_update_access = (entity_access('update', $target_type, $target_entities[$item[$column]]) !== FALSE);
$items[$id][$delta]['access'] = ($has_view_access || $has_update_access);
}
// Otherwise, unset the instance value, since the entity does not exist.
else {
@@ -1199,52 +1334,91 @@ function entityreference_field_formatter_prepare_view($entity_type, $entities, $
function entityreference_field_formatter_view($entity_type, $entity, $field, $instance, $langcode, $items, $display) {
$result = array();
$settings = $display['settings'];
// Rebuild the items list to contain only those with access.
foreach ($items as $key => $item) {
if (empty($item['access'])) {
unset($items[$key]);
}
}
$field_type_settings = entityreference_field_type_settings($field);
$target_type = $field_type_settings['entity_type'];
$column = $field_type_settings['column'];
switch ($display['type']) {
case 'entityreference_label':
$handler = entityreference_get_selection_handler($field, $instance, $entity_type, $entity);
foreach ($items as $delta => $item) {
$label = $handler->getLabel($item['entity']);
// If the link is to be displayed and the entity has a uri, display a link.
// Note the assignment ($url = ) here is intended to be an assignment.
if ($display['settings']['link'] && ($uri = entity_uri($field['settings']['target_type'], $item['entity']))) {
$result[$delta] = array('#markup' => l($label, $uri['path'], $uri['options']));
// Skip an item that is not accessible, unless we're allowing output of
// entity labels without considering access.
if (empty($item['access']) && !$display['settings']['bypass_access']) {
continue;
}
else {
$result[$delta] = array('#markup' => check_plain($label));
// Calling EntityReferenceHandler::getLabel() would make a repeated,
// wasteful call to entity_access().
$label = entity_label($field['settings']['target_type'], $item['entity']);
// Check if the settings and access allow a link to be displayed.
$display_link = $display['settings']['link'] && $item['access'];
$uri = NULL;
// If the link is allowed and the entity has a uri, display a link.
if ($display_link) {
$uri = entity_uri($target_type, $item['entity']);
}
$result[$delta] = array(
'#theme' => 'entityreference_label',
'#label' => $label,
'#item' => $item,
'#uri' => $uri,
'#settings' => array(
'display' => $display['settings'],
'field' => $field['settings'],
),
);
}
break;
case 'entityreference_entity_id':
foreach ($items as $delta => $item) {
$result[$delta] = array('#markup' => check_plain($item['target_id']));
// Skip an item that is not accessible.
if (empty($item['access'])) {
continue;
}
$result[$delta] = array(
'#theme' => 'entityreference_entity_id',
'#item' => $item,
'#settings' => array(
'display' => $display['settings'],
'field' => $field['settings'],
),
);
}
break;
case 'entityreference_entity_view':
$target_langcode = $langcode;
if (!empty($settings['use_content_language']) && !empty($GLOBALS['language_content']->language)) {
$target_langcode = $GLOBALS['language_content']->language;
}
foreach ($items as $delta => $item) {
// Skip an item that is not accessible.
if (empty($item['access'])) {
continue;
}
// Protect ourselves from recursive rendering.
static $depth = 0;
$depth++;
if ($depth > 20) {
throw new EntityReferenceRecursiveRenderingException(t('Recursive rendering detected when rendering entity @entity_type(@entity_id). Aborting rendering.', array('@entity_type' => $entity_type, '@entity_id' => $item['target_id'])));
throw new EntityReferenceRecursiveRenderingException(t('Recursive rendering detected when rendering entity @entity_type(@entity_id). Aborting rendering.', array('@entity_type' => $target_type, '@entity_id' => $item[$column])));
}
$entity = clone $item['entity'];
unset($entity->content);
$result[$delta] = entity_view($field['settings']['target_type'], array($item['target_id'] => $entity), $settings['view_mode'], $langcode, FALSE);
$target_entity = clone $item['entity'];
unset($target_entity->content);
$result[$delta] = entity_view($target_type, array($item[$column] => $target_entity), $settings['view_mode'], $target_langcode, FALSE);
if (empty($settings['links']) && isset($result[$delta][$field['settings']['target_type']][$item['target_id']]['links'])) {
$result[$delta][$field['settings']['target_type']][$item['target_id']]['links']['#access'] = FALSE;
if (empty($settings['links']) && isset($result[$delta][$target_type][$column]['links'])) {
$result[$delta][$target_type][$item[$column]]['links']['#access'] = FALSE;
}
$depth = 0;
}
@@ -1268,3 +1442,44 @@ function entityreference_views_api() {
'path' => drupal_get_path('module', 'entityreference') . '/views',
);
}
/**
* Theme label.
*
* @ingroup themeable.
*/
function theme_entityreference_label($vars) {
$label = $vars['label'];
$settings = $vars['settings'];
$item = $vars['item'];
$uri = $vars['uri'];
$output = '';
// If the link is to be displayed and the entity has a uri, display a link.
// Note the assignment ($url = ) here is intended to be an assignment.
if ($settings['display']['link'] && isset($uri['path'])) {
$output .= l($label, $uri['path'], $uri['options']);
}
else {
$output .= check_plain($label);
}
return $output;
}
/**
* Theme entity_id
*
* @ingroup themeable.
*/
function theme_entityreference_entity_id($vars) {
$settings = $vars['settings'];
$item = $vars['item'];
$output = '';
$output = check_plain($item['target_id']);
return $output;
}
@@ -4,9 +4,9 @@ core = 7.x
package = Fields
dependencies[] = entityreference
; Information added by drupal.org packaging script on 2012-11-18
version = "7.x-1.0"
; Information added by Drupal.org packaging script on 2017-08-16
version = "7.x-1.5"
core = "7.x"
project = "entityreference"
datestamp = "1353230808"
datestamp = "1502895850"
@@ -144,18 +144,20 @@ class EntityReferenceBehavior_TaxonomyIndex extends EntityReference_BehaviorHand
// already inserted in taxonomy_build_node_index().
$tid_all = array_diff($tid_all, $original_tid_all);
// Insert index entries for all the node's terms.
// Insert index entries for all the node's terms, preventing duplicates.
if (!empty($tid_all)) {
$query = db_insert('taxonomy_index')->fields(array('nid', 'tid', 'sticky', 'created'));
foreach ($tid_all as $tid) {
$query->values(array(
$row = array(
'nid' => $node->nid,
'tid' => $tid,
'sticky' => $sticky,
'created' => $node->created,
));
);
$query = db_merge('taxonomy_index')
->key($row)
->fields($row);
$query->execute();
}
$query->execute();
}
}
}
@@ -208,7 +208,11 @@ class EntityReference_SelectionHandler_Generic implements EntityReference_Select
* Implements EntityReferenceHandler::validateAutocompleteInput().
*/
public function validateAutocompleteInput($input, &$element, &$form_state, $form) {
$entities = $this->getReferencableEntities($input, '=', 6);
$bundled_entities = $this->getReferencableEntities($input, '=', 6);
$entities = array();
foreach($bundled_entities as $entities_list) {
$entities += $entities_list;
}
if (empty($entities)) {
// Error if there are no entities available for a required field.
form_error($element, t('There are no entities matching "%value"', array('%value' => $input)));
@@ -304,7 +308,8 @@ class EntityReference_SelectionHandler_Generic implements EntityReference_Select
* Implements EntityReferenceHandler::getLabel().
*/
public function getLabel($entity) {
return entity_label($this->field['settings']['target_type'], $entity);
$target_type = $this->field['settings']['target_type'];
return entity_access('view', $target_type, $entity) ? entity_label($target_type, $entity) : t(ENTITYREFERENCE_DENIED);
}
/**
@@ -338,9 +343,11 @@ class EntityReference_SelectionHandler_Generic implements EntityReference_Select
// Join the known base-table.
$target_type = $this->field['settings']['target_type'];
$entity_info = entity_get_info($target_type);
$target_type_base_table = $entity_info['base table'];
$id = $entity_info['entity keys']['id'];
// Return the alias of the table.
return $query->innerJoin($target_type, NULL, "$target_type.$id = $alias.entity_id");
return $query->innerJoin($target_type_base_table, NULL, "%alias.$id = $alias.entity_id");
}
}
@@ -540,9 +547,9 @@ class EntityReference_SelectionHandler_Generic_taxonomy_term extends EntityRefer
foreach ($bundles as $bundle) {
if ($vocabulary = taxonomy_vocabulary_machine_name_load($bundle)) {
if ($terms = taxonomy_get_tree($vocabulary->vid, 0)) {
if ($terms = taxonomy_get_tree($vocabulary->vid, 0, NULL, TRUE)) {
foreach ($terms as $term) {
$options[$vocabulary->machine_name][$term->tid] = str_repeat('-', $term->depth) . check_plain($term->name);
$options[$vocabulary->machine_name][$term->tid] = str_repeat('-', $term->depth) . check_plain(entity_label('taxonomy_term', $term));
}
}
}
@@ -9,12 +9,13 @@ class EntityReference_SelectionHandler_Views implements EntityReference_Selectio
* Implements EntityReferenceHandler::getInstance().
*/
public static function getInstance($field, $instance = NULL, $entity_type = NULL, $entity = NULL) {
return new EntityReference_SelectionHandler_Views($field, $instance);
return new EntityReference_SelectionHandler_Views($field, $instance, $entity);
}
protected function __construct($field, $instance) {
protected function __construct($field, $instance, $entity) {
$this->field = $field;
$this->instance = $instance;
$this->entity = $entity;
}
/**
@@ -52,13 +53,32 @@ class EntityReference_SelectionHandler_Views implements EntityReference_Selectio
);
$default = !empty($view_settings['args']) ? implode(', ', $view_settings['args']) : '';
$description = t('Provide a comma separated list of arguments to pass to the view.') . '<br />' . t('This field supports tokens.');
if (!module_exists('token')) {
$description .= '<br>' . t('Install the <a href="@url">token module</a> to get more tokens and display available once.', array('@url' => 'http://drupal.org/project/token'));
}
$form['view']['args'] = array(
'#type' => 'textfield',
'#title' => t('View arguments'),
'#default_value' => $default,
'#required' => FALSE,
'#description' => t('Provide a comma separated list of arguments to pass to the view.'),
'#description' => $description,
'#maxlength' => '512',
);
if (module_exists('token')) {
// Get the token type for the entity type our field is in (a type 'taxonomy_term' has a 'term' type token).
$info = entity_get_info($instance['entity_type']);
$form['view']['tokens'] = array(
'#theme' => 'token_tree',
'#token_types' => array($info['token type']),
'#global_types' => TRUE,
'#click_insert' => TRUE,
'#dialog' => TRUE,
);
}
}
else {
$form['view']['no_view_help'] = array(
@@ -84,6 +104,7 @@ class EntityReference_SelectionHandler_Views implements EntityReference_Selectio
return FALSE;
}
$this->view->set_display($display_name);
$this->view->pre_execute();
// Make sure the query is not cached.
$this->view->is_cacheable = FALSE;
@@ -104,7 +125,7 @@ class EntityReference_SelectionHandler_Views implements EntityReference_Selectio
*/
public function getReferencableEntities($match = NULL, $match_operator = 'CONTAINS', $limit = 0) {
$display_name = $this->field['settings']['handler_settings']['view']['display_name'];
$args = $this->field['settings']['handler_settings']['view']['args'];
$args = $this->handleArgs($this->field['settings']['handler_settings']['view']['args']);
$result = array();
if ($this->initializeView($match, $match_operator, $limit)) {
// Get the results.
@@ -133,12 +154,14 @@ class EntityReference_SelectionHandler_Views implements EntityReference_Selectio
function validateReferencableEntities(array $ids) {
$display_name = $this->field['settings']['handler_settings']['view']['display_name'];
$args = $this->field['settings']['handler_settings']['view']['args'];
$args = $this->handleArgs($this->field['settings']['handler_settings']['view']['args']);
$result = array();
if ($this->initializeView(NULL, 'CONTAINS', 0, $ids)) {
// Get the results.
$entities = $this->view->execute_display($display_name, $args);
$result = array_keys($entities);
if (!empty($entities)) {
$result = array_keys($entities);
}
}
return $result;
}
@@ -164,6 +187,49 @@ class EntityReference_SelectionHandler_Views implements EntityReference_Selectio
}
/**
* Handles arguments for views.
*
* Replaces tokens using token_replace().
*
* @param array $args
* Usually $this->field['settings']['handler_settings']['view']['args'].
*
* @return array
* The arguments to be send to the View.
*/
protected function handleArgs($args) {
if (!module_exists('token')) {
return $args;
}
// Parameters for token_replace().
$data = array();
$options = array('clear' => TRUE);
if ($entity = $this->entity) {
// D7 HACK: For new entities, entity and revision id are not set. This leads to
// * token replacement emitting PHP warnings
// * views choking on empty arguments
// We workaround this by filling in '0' for these IDs
// and use a clone to leave no traces of our unholy doings.
$info = entity_get_info($this->instance['entity_type']);
if (!isset($entity->{$info['entity keys']['id']})) {
$entity = clone $entity;
$entity->{$info['entity keys']['id']} = '0';
if (!empty($info['entity keys']['revision'])) {
$entity->{$info['entity keys']['revision']} = '0';
}
}
$data[$info['token type']] = $entity;
}
// Replace tokens for each argument.
foreach ($args as $key => $arg) {
$args[$key] = token_replace($arg, $data, $options);
}
return $args;
}
}
function entityreference_view_settings_validate($element, &$form_state, $form) {
@@ -21,8 +21,9 @@ interface EntityReference_SelectionHandler {
* Return a list of referencable entities.
*
* @return
* An array of referencable entities, which keys are entity ids and
* values (safe HTML) labels to be displayed to the user.
* A nested array of entities, the first level is keyed by the
* entity bundle, which contains an array of entity labels (safe HTML),
* keyed by the entity ID.
*/
public function getReferencableEntities($match = NULL, $match_operator = 'CONTAINS', $limit = 0);
@@ -21,7 +21,7 @@ class EntityReferenceAdminTestCase extends DrupalWebTestCase {
parent::setUp(array('field_ui', 'entity', 'ctools', 'entityreference'));
// Create test user.
$this->admin_user = $this->drupalCreateUser(array('access content', 'administer content types'));
$this->admin_user = $this->drupalCreateUser(array('access content', 'administer content types', 'administer fields'));
$this->drupalLogin($this->admin_user);
// Create content type, with underscores.
@@ -0,0 +1,241 @@
<?php
/**
* @file
* Test case for simple CCK field mapper mappers/content.inc.
*/
/**
* Class for testing Feeds field mapper.
*/
class FeedsMapperFieldTestCase extends DrupalWebTestCase{
/**
* Test info function.
*/
public static function getInfo() {
return array(
'name' => 'Feeds integration (field mapper)',
'description' => 'Test Feeds Mapper support for fields.',
'group' => 'Entity Reference',
'dependencies' => array('feeds'),
);
}
/**
* Set-up function.
*/
public function setUp() {
parent::setUp();
module_enable(array('entityreference_feeds_test'), TRUE);
$this->resetAll();
$permissions[] = 'access content';
$permissions[] = 'administer site configuration';
$permissions[] = 'administer content types';
$permissions[] = 'administer nodes';
$permissions[] = 'bypass node access';
$permissions[] = 'administer taxonomy';
$permissions[] = 'administer users';
$permissions[] = 'administer feeds';
// Create an admin user and log in.
$this->admin_user = $this->drupalCreateUser($permissions);
$this->drupalLogin($this->admin_user);
}
/**
* Check if mapping exists.
*
* @param string $id
* ID of the importer.
* @param integer $i
* The key of the mapping.
* @param string $source
* The source field.
* @param string $target
* The target field.
*
* @return integer
* -1 if the mapping doesn't exist, the key of the mapping otherwise.
*/
public function mappingExists($id, $i, $source, $target) {
$current_mappings = $this->getCurrentMappings($id);
if ($current_mappings) {
foreach ($current_mappings as $key => $mapping) {
if ($mapping['source'] == $source && $mapping['target'] == $target && $key == $i) {
return $key;
}
}
}
return -1;
}
/**
* Adds mappings to a given configuration.
*
* @param string $id
* ID of the importer.
* @param array $mappings
* An array of mapping arrays. Each mapping array must have a source and
* an target key and can have a unique key.
* @param bool $test_mappings
* (optional) TRUE to automatically test mapping configs. Defaults to TRUE.
*/
public function addMappings($id, $mappings, $test_mappings = TRUE) {
$path = "admin/structure/feeds/$id/mapping";
// Iterate through all mappings and add the mapping via the form.
foreach ($mappings as $i => $mapping) {
if ($test_mappings) {
$current_mapping_key = $this->mappingExists($id, $i, $mapping['source'], $mapping['target']);
$this->assertEqual($current_mapping_key, -1, 'Mapping does not exist before addition.');
}
// Get unique flag and unset it. Otherwise, drupalPost will complain that
// Split up config and mapping.
$config = $mapping;
unset($config['source'], $config['target']);
$mapping = array('source' => $mapping['source'], 'target' => $mapping['target']);
// Add mapping.
$this->drupalPost($path, $mapping, t('Add'));
// If there are other configuration options, set them.
if ($config) {
$this->drupalPostAJAX(NULL, array(), 'mapping_settings_edit_' . $i);
// Set some settings.
$edit = array();
foreach ($config as $key => $value) {
$edit["config[$i][settings][$key]"] = $value;
}
$this->drupalPostAJAX(NULL, $edit, 'mapping_settings_update_' . $i);
$this->drupalPost(NULL, array(), t('Save'));
}
if ($test_mappings) {
$current_mapping_key = $this->mappingExists($id, $i, $mapping['source'], $mapping['target']);
$this->assertTrue($current_mapping_key >= 0, 'Mapping exists after addition.');
}
}
}
/**
* Gets an array of current mappings from the feeds_importer config.
*
* @param string $id
* ID of the importer.
*
* @return bool|array
* FALSE if the importer has no mappings, or an an array of mappings.
*/
public function getCurrentMappings($id) {
$config = db_query("SELECT config FROM {feeds_importer} WHERE id = :id", array(':id' => $id))->fetchField();
$config = unserialize($config);
// We are very specific here. 'mappings' can either be an array or not
// exist.
if (array_key_exists('mappings', $config['processor']['config'])) {
$this->assertTrue(is_array($config['processor']['config']['mappings']), 'Mappings is an array.');
return $config['processor']['config']['mappings'];
}
return FALSE;
}
/**
* Basic test loading a double entry CSV file.
*/
public function test() {
$this->drupalLogin($this->admin_user);
$this->drupalGet('admin/structure/types/manage/article/fields');
$this->assertText('Ref - entity ID', t('Found Entity reference field %field.', array('%field' => 'field_er_id')));
$this->assertText('Ref - entity label', t('Found Entity reference field %field.', array('%field' => 'field_er_label')));
$this->assertText('Ref - feeds GUID', t('Found Entity reference field %field.', array('%field' => 'field_er_guid')));
$this->assertText('Ref - feeds URL', t('Found Entity reference field %field.', array('%field' => 'field_er_url')));
// Add feeds importer
$this->drupalGet('admin/structure/feeds');
$this->clickLink('Add importer');
$this->drupalPost('admin/structure/feeds/create', array('name' => 'Nodes', 'id' => 'nodes'), 'Create');
$this->assertText('Your configuration has been created with default settings.');
$this->drupalPost('admin/structure/feeds/nodes/settings/', array('content_type' => '', 'import_period' => -1), 'Save');
$this->assertText('Your changes have been saved.');
$this->drupalPost("admin/structure/feeds/nodes/fetcher", array('plugin_key' => 'FeedsFileFetcher'), 'Save');
$config = unserialize(db_query("SELECT config FROM {feeds_importer} WHERE id = :id", array(':id' => 'nodes'))->fetchField());
$this->assertEqual($config['fetcher']['plugin_key'], 'FeedsFileFetcher', 'Verified correct fetcher (FeedsFileFetcher).');
$this->drupalPost("admin/structure/feeds/nodes/parser", array('plugin_key' => 'FeedsCSVParser'), 'Save');
$config = unserialize(db_query("SELECT config FROM {feeds_importer} WHERE id = :id", array(':id' => 'nodes'))->fetchField());
$this->assertEqual($config['parser']['plugin_key'], 'FeedsCSVParser', 'Verified correct parser (FeedsCSVParser).');
$this->drupalPost('admin/structure/feeds/nodes/settings/FeedsNodeProcessor', array('content_type' => 'article'), 'Save');
$this->assertText('Your changes have been saved.');
$this->addMappings('nodes', array(
0 => array(
'source' => 'title',
'target' => 'title',
),
1 => array(
'source' => 'nid',
'target' => 'nid',
'unique' => TRUE,
),
2 => array(
'source' => 'permalink',
'target' => 'url',
'unique' => TRUE,
),
3 => array(
'source' => 'nid',
'target' => 'guid',
'unique' => TRUE,
),
4 => array(
'source' => 'parent_nid',
'target' => 'field_er_id:etid',
),
5 => array(
'source' => 'parent_title',
'target' => 'field_er_label:label',
),
6 => array(
'source' => 'parent_url',
'target' => 'field_er_url:url',
),
7 => array(
'source' => 'parent_guid',
'target' => 'field_er_guid',
),
)
);
$file = realpath(getcwd()) . '/' . drupal_get_path('module', 'entityreference') . '/tests/feeds_test.csv';
$this->assertTrue(file_exists($file), 'Source file exists');
$this->drupalPost('import/nodes', array('files[feeds]' => $file), 'Import');
$this->assertText('Created 2 nodes');
$parent = node_load(1);
$this->assertTrue(empty($parent->field_er_id['und'][0]['target_id']), t('Parent node: Import by entity ID OK.'));
$this->assertTrue(empty($parent->field_er_label['und'][0]['target_id']), t('Parent node: Import by entity label OK.'));
$this->assertTrue(empty($parent->field_er_guid['und'][0]['target_id']), t('Parent node: Import by feeds GUID OK.'));
$this->assertTrue(empty($parent->field_er_url['und'][0]['target_id']), t('Parent node: Import by feeds URL OK.'));
$child = node_load(2);
$this->assertTrue($child->field_er_id['und'][0]['target_id'] == 1, t('Child node: Import by entity ID OK.'));
$this->assertTrue($child->field_er_label['und'][0]['target_id'] == 1, t('Child node: Import by entity label OK.'));
$this->assertTrue($child->field_er_guid['und'][0]['target_id'] == 1, t('Child node: Import by feeds GUID OK.'));
$this->assertTrue($child->field_er_url['und'][0]['target_id'] == 1, t('Child node: Import by feeds URL OK.'));
}
}
@@ -84,6 +84,14 @@ class EntityReferenceHandlersTestCase extends DrupalWebTestCase {
'title' => 'Node unpublished (<&>)',
'uid' => 1,
),
// Title purposefully starts with same characters as published1 and
// published2 above but contains a slash.
'published_withslash' => (object) array(
'type' => 'article',
'status' => 1,
'title' => 'Node pub/lished1',
'uid' => 1,
),
);
$node_labels = array();
@@ -104,6 +112,7 @@ class EntityReferenceHandlersTestCase extends DrupalWebTestCase {
'article' => array(
$nodes['published1']->nid => $node_labels['published1'],
$nodes['published2']->nid => $node_labels['published2'],
$nodes['published_withslash']->nid => $node_labels['published_withslash'],
),
),
),
@@ -141,6 +150,18 @@ class EntityReferenceHandlersTestCase extends DrupalWebTestCase {
),
'result' => array(),
),
// Searching for "Node pub/" should return only the published_withslash node
// and not published1 and published2 from above.
array(
'arguments' => array(
array('Node pub/', 'CONTAINS'),
),
'result' => array(
'article' => array(
$nodes['published_withslash']->nid => $node_labels['published_withslash'],
),
),
),
);
$this->assertReferencable($field, $referencable_tests, 'Node handler');
@@ -156,6 +177,7 @@ class EntityReferenceHandlersTestCase extends DrupalWebTestCase {
'article' => array(
$nodes['published1']->nid => $node_labels['published1'],
$nodes['published2']->nid => $node_labels['published2'],
$nodes['published_withslash']->nid => $node_labels['published_withslash'],
$nodes['unpublished']->nid => $node_labels['unpublished'],
),
),
@@ -172,6 +194,21 @@ class EntityReferenceHandlersTestCase extends DrupalWebTestCase {
),
);
$this->assertReferencable($field, $referencable_tests, 'Node handler (admin)');
// Verify autocomplete input validation.
$handler = entityreference_get_selection_handler($field);
$element = array(
'#parents' => array('element_name'),
);
$form_state = array();
$form = array();
$value = $handler->validateAutocompleteInput($nodes['published1']->title, $element, $form_state, $form);
$this->assertEqual($value, $nodes['published1']->nid);
$invalid_input = $this->randomName();
$value = $handler->validateAutocompleteInput($invalid_input, $element, $form_state, $form);
$this->assertNull($value);
$this->assertEqual(form_get_error($element), t('There are no entities matching "%value"', array('%value' => $invalid_input)));
}
/**
@@ -234,7 +271,7 @@ class EntityReferenceHandlersTestCase extends DrupalWebTestCase {
),
'result' => array(
'user' => array(
$users['admin']->uid => $user_labels['admin'],
$users['admin']->uid => ENTITYREFERENCE_DENIED,
$users['non_admin']->uid => $user_labels['non_admin'],
),
),
@@ -112,4 +112,52 @@ class EntityReferenceTaxonomyTestCase extends DrupalWebTestCase {
$this->assertFalse(taxonomy_select_nodes(1));
}
/**
* Add a second ER field from node/article to taxonomy.
*
* This should not cause {taxonomy_index} to receive duplicate entries.
*/
protected function setupForIndexDuplicates() {
// Create an entity reference field.
$field = array(
'entity_types' => array('node'),
'settings' => array(
'handler' => 'base',
'target_type' => 'taxonomy_term',
'handler_settings' => array(
'target_bundles' => array(),
),
),
'field_name' => 'field_entityreference_term2',
'type' => 'entityreference',
);
$field = field_create_field($field);
$instance = array(
'field_name' => 'field_entityreference_term2',
'bundle' => 'article',
'entity_type' => 'node',
);
// Enable the taxonomy-index behavior.
$instance['settings']['behaviors']['taxonomy-index']['status'] = TRUE;
field_create_instance($instance);
}
/**
* Make sure the index only contains one entry for a given node->term
* reference, even when multiple ER fields link from the node bundle to terms.
*/
public function testIndexDuplicates() {
// Extra setup for this test: add another ER field on this content type.
$this->setupForIndexDuplicates();
// Assert node insert with reference to term in first field.
$tid = 1;
$settings = array();
$settings['type'] = 'article';
$settings['field_entityreference_term'][LANGUAGE_NONE][0]['target_id'] = $tid;
$node = $this->drupalCreateNode($settings);
$this->assertEqual(taxonomy_select_nodes($tid), array($node->nid));
}
}

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