FINAL suepr merge step : added all modules to this super repos

This commit is contained in:
Bachir Soussi Chiadmi
2015-04-19 16:46:59 +02:00
7585 changed files with 1723356 additions and 18 deletions

View File

@@ -0,0 +1,27 @@
.container-inline-date > .form-item {
margin-left: 0.5em;
margin-right: 0;
}
.container-inline-date .form-item .form-item {
float: right;
}
.container-inline-date .form-item input,
.container-inline-date .form-item select,
.container-inline-date .form-item option {
margin-left: 5px;
margin-right: 0;
}
.container-inline-date .date-spacer {
margin-left: 0;
margin-right: -5px;
}
.form-type-date-select .form-type-select[class$=hour] {
margin-right: .75em;
}
#edit-field-settings-granularity .form-type-checkbox {
margin-left: .6em;
}

View File

@@ -0,0 +1,188 @@
/**
* @file
* Main stylesheet for Date module.
*/
/* Force start/end dates to float using inline-block, where it works, otherwise inline. */
.container-inline-date {
clear: both;
}
.container-inline-date .form-item {
float: none;
margin: 0;
padding: 0;
}
.container-inline-date > .form-item {
display: inline-block;
margin-right: 0.5em; /* LTR */
margin-bottom: 10px;
vertical-align: top;
}
.container-inline-date .form-item .form-item {
float: left; /* LTR */
}
.container-inline-date .form-item,
.container-inline-date .form-item input {
width: auto;
}
.container-inline-date .description {
clear: both;
}
.container-inline-date .form-item input,
.container-inline-date .form-item select,
.container-inline-date .form-item option {
margin-right: 5px; /* LTR */
}
.container-inline-date .date-spacer {
margin-left: -5px; /* LTR */
}
.views-right-60 .container-inline-date div {
margin: 0;
padding: 0;
}
.container-inline-date .date-timezone .form-item {
clear: both;
float: none;
width: auto;
}
/* The exposed Views form doesn't need some of these styles */
.container-inline-date .date-padding {
padding: 10px;
float: left;
}
.views-exposed-form .container-inline-date .date-padding {
padding: 0;
}
/* Fixes for date popup css so it will behave in Drupal */
#calendar_div,
#calendar_div td,
#calendar_div th {
margin: 0;
padding: 0;
}
#calendar_div,
.calendar_control,
.calendar_links,
.calendar_header,
.calendar {
border-collapse: separate;
margin: 0;
width: 185px;
}
.calendar td {
padding: 0;
}
/* formatting for start/end dates in nodes and views */
span.date-display-single {
}
span.date-display-start {
}
span.date-display-end {
}
.date-prefix-inline {
display: inline-block;
}
.date-clear {
clear: both;
display: block;
float: none;
}
.date-no-float {
clear: both;
float: none;
width: 98%;
}
.date-float {
clear: none;
float: left;
width: auto;
}
/* Add space between date option checkboxes ('All day' & 'Collect End Date') */
.date-float .form-type-checkbox{
padding-right: 1em;
}
/* Add space between the date and time portions of the date_select widget. */
.form-type-date-select .form-type-select[class$=hour] {
margin-left: .75em; /* LTR */
}
.date-container .date-format-delete {
float: left;
margin-top: 1.8em;
margin-left: 1.5em;
}
.date-container .date-format-name {
float: left;
}
.date-container .date-format-type {
float: left;
padding-left: 10px;
}
.date-container .select-container {
clear: left;
float: left;
}
/* Calendar day css */
div.date-calendar-day {
background: #F3F3F3;
border-top: 1px solid #EEE;
border-left: 1px solid #EEE;
border-right: 1px solid #BBB;
border-bottom: 1px solid #BBB;
color: #999;
float: left;
line-height: 1;
margin: 6px 10px 0 0;
text-align: center;
width: 40px;
}
div.date-calendar-day span {
display: block;
text-align: center;
}
div.date-calendar-day span.month {
background-color: #B5BEBE;
color: white;
font-size: .9em;
padding: 2px;
text-transform: uppercase;
}
div.date-calendar-day span.day {
font-size: 2em;
font-weight: bold;
}
div.date-calendar-day span.year {
font-size: .9em;
padding: 2px;
}
/* Admin styling */
.form-item.form-item-instance-widget-settings-input-format-custom,
.form-item.form-item-field-settings-enddate-required {
margin-left: 1.3em;
}
#edit-field-settings-granularity .form-type-checkbox {
margin-right: .6em; /* LTR */
}
.date-year-range-select {
margin-right: 1em;
}

View File

@@ -0,0 +1,126 @@
<?php
/**
* @file
* Administrative page callbacks for the date_api module.
*/
/**
* Create replacement values for deprecated timezone names.
*/
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',
);
if (array_key_exists($old, $replace)) {
return $replace[$old];
}
else {
return $old;
}
}

View File

@@ -0,0 +1,17 @@
name = Date API
description = A Date API that can be used by other modules.
package = Date/Time
core = 7.x
php = 5.2
stylesheets[all][] = date.css
files[] = date_api.module
files[] = date_api_sql.inc
; Information added by drupal.org packaging script on 2012-08-13
version = "7.x-2.6"
core = "7.x"
project = "date"
datestamp = "1344850024"

View File

@@ -0,0 +1,252 @@
<?php
/**
* @file
* Install, update and uninstall functions for the date_api module.
*/
/**
* Helper function for setting Date variables.
*/
function date_api_set_variables() {
// Set absolute minimum and maximum year for dates on this site.
// There is actually no maximum and minimum year in PHP 5, but a date with
// a year less than 0 would result in negative ISO and DATETIME dates,
// like -1250-01-01T00:00:00, which probably won't make sense or work
// correctly anywhere.
// The odd construct of using variable_get() instead of variable_set()
// is so we don't accidentally write over an existing value. If
// no value is set, variable_get() will set it.
variable_get('date_max_year', 4000);
variable_get('date_min_year', 1);
variable_get('date_php_min_year', 1901);
// Set an API version in a way that other modules can test for compatibility.
variable_set('date_api_version', '7.2');
}
/**
* Implements hook_requirements().
*/
function date_api_requirements($phase) {
$requirements = array();
if ($phase == 'runtime') {
$t = get_t();
module_load_include('module', 'date_api');
$messages = date_api_status();
$error_messages = !empty($messages['errors']) ? $messages['errors'] : array();
$success_messages = !empty($messages['success']) ? $messages['success'] : array();
if (!empty($error_messages)) {
$requirements['date'] = array(
'title' => $t('Date API'),
'value' => $t('Missing system date settings'),
'description' => implode(' ', array_merge($error_messages, $success_messages)),
'severity' => REQUIREMENT_ERROR,
);
}
else {
$requirements['date'] = array(
'title' => $t('Date API'),
'value' => $t('System date settings'),
'description' => implode(' ', $success_messages),
);
}
}
return $requirements;
}
/**
* Implements hook_install().
*/
function date_api_install() {
// Only set the message if Drupal itself is already installed.
if (variable_get('install_task') == 'done') {
// Ensure translations don't break at install time.
$t = get_t();
// date_api_set_variables can install date_timezone. The
// date_timezone_install() function does a module_enable('date_api'). This
// means that date_api_enable() can be called before date_api_install()
// finishes! So the date_api schema needs to be installed before this line!
date_api_set_variables();
$message = $t('The Date API requires that you set up the <a href="@regional_settings">site timezone and first day of week settings</a> and the <a href="@regional_date_time">date format settings</a> to function correctly.', array('@regional_settings' => url('admin/config/regional/settings'), '@regional_date_time' => url('admin/config/regional/date-time')));
drupal_set_message(filter_xss_admin($message), 'warning');
}
}
/**
* Implements hook_enable().
*/
function date_api_enable() {
date_api_set_variables();
}
/**
* Implements hook_uninstall().
*/
function date_api_uninstall() {
cache_clear_all('date_timezone_identifiers_list', 'cache');
$variables = array(
'date_api_version',
'date_min_year',
'date_max_year',
'date_php_min_year',
'date_db_tz_support',
'date_api_use_iso8601',
);
foreach ($variables as $variable) {
variable_del($variable);
}
if (db_table_exists('views_display')) {
$displays = array(
'date_nav',
);
db_query("DELETE FROM {views_display} WHERE display_plugin IN ('" . implode("','", $displays) . "')");
db_query("DELETE FROM {cache_views}");
}
}
/**
* Implements hook_update_last_removed().
*/
function date_api_update_last_removed() {
return 6005;
}
/**
* 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
* case new version of those custom values have already been created.
*/
function date_api_update_7000() {
// Move format data from the old 'date_format_types' table to the new
// 'date_format_type' table.
if (db_table_exists('date_format_types')) {
// Find all the custom entries in the D6 table.
$result = db_select('date_format_types', 'old')
->fields('old', array('type', 'title', 'locked'))
->condition('locked', 0)
->execute()
->fetchAll(PDO::FETCH_ASSOC);
// Iterate over all the old values.
foreach ($result as $row) {
// See if this value already exists in the new table
// (it might have been added manually before this update got run).
$count = db_select('date_format_type', 'new')
->condition('type', $row['type'])
->countQuery()
->execute()
->fetchField();
// If the value is missing, insert it.
// Do nothing if it already exists, assume the value in the
// new table trumps the old values.
if (empty($count)) {
db_insert('date_format_type')
->fields(array(
'type' => $row['type'],
'title' => $row['title'],
'locked' => $row['locked'],
))
->execute();
}
}
// Drop the old table.
db_drop_table('date_format_types');
}
// Move format data from the old 'date_formats' table (which was renamed to
// 'd6_date_formats') to the new 'date_formats' table.
if (db_table_exists('d6_date_formats')) {
// Find all the custom entries in the D6 table.
$result = db_select('d6_date_formats', 'old')
->fields('old', array('format', 'type', 'locked'))
->condition('type', 'custom')
->execute()
->fetchAll(PDO::FETCH_ASSOC);
// Iterate over all the old values.
foreach ($result as $row) {
// See if this value already exists in the new table (it might have been
// added manually before this update got run).
$count = db_select('date_formats', 'new')
->condition('format', $row['format'])
->condition('type', $row['type'])
->countQuery()
->execute()
->fetchField();
// If the value is missing, insert it. Do nothing if it already exists,
// assume the value in the new table trumps the old values.
if (empty($count)) {
db_insert('date_formats')
->fields(array(
'format' => $row['format'],
'type' => $row['type'],
'locked' => $row['locked'],
))
->execute();
}
}
// Drop the old table.
db_drop_table('d6_date_formats');
}
// Move format data from the old 'date_format_locale' table (which was renamed
// to 'd6_date_format_locale') to the new 'date_format_locale' table.
if (db_table_exists('d6_date_format_locale')) {
// Find all the custom entries in the D6 table.
$result = db_select('d6_date_format_locale', 'old')
->fields('old', array('format', 'type', 'language'))
->condition('type', 'custom')
->execute()
->fetchAll(PDO::FETCH_ASSOC);
// Iterate over all the old values.
foreach ($result as $row) {
// See if this value already exists in the new table (it might have been
// added manually before this update got run).
$count = db_select('date_format_locale', 'new')
->condition('format', $row['format'])
->condition('type', $row['type'])
->condition('language', $row['language'])
->countQuery()
->execute()
->fetchField();
// If the value is missing, insert it.
// Do nothing if it already exists, assume the value in the
// new table trumps the old values.
if (empty($count)) {
db_insert('date_format_locale')
->fields(array(
'format' => $row['format'],
'type' => $row['type'],
'language' => $row['language'],
))
->execute();
}
}
// Drop the old table.
db_drop_table('d6_date_format_locale');
}
}
/**
* Drop D6 timezone_name field on {users} after upgrading to D7.
*/
function date_api_update_7001() {
if (db_field_exists('users', 'timezone_name')) {
db_drop_field('users', 'timezone_name');
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,752 @@
<?php
/**
* @file
* Date API elements themes and validation.
* This file is only included during the edit process to reduce memory usage.
*/
/**
* Implements hook_element_info().
*
* Parameters for date form elements, designed to have sane defaults so any
* or all can be omitted.
*
* Fill the element #default_value with a date in datetime format,
* (YYYY-MM-DD HH:MM:SS), adjusted to the proper local timezone.
*
* NOTE - Converting a date stored in the database from UTC to the local zone
* and converting it back to UTC before storing it is not handled by this
* element and must be done in pre-form and post-form processing!!
*
* The date_select element will create a collection of form elements, with a
* separate select or textfield for each date part. The whole collection will
* get reformatted back to a date value of the requested type during validation.
*
* The date_text element will create a textfield that can contain a whole
* date or any part of a date as text. The user input value will be re-formatted
* back into a date value of the requested type during validation.
*
* The date_timezone element will create a drop-down selector to pick a
* timezone name.
*
* The date_year_range element will create two textfields (for users with
* JavaScript enabled they will appear as drop-down selectors with an option
* for custom text entry) to pick a range of years that will be passed to form
* submit handlers as a single string (e.g., -3:+3).
*
* #date_timezone
* The local timezone to be used to create this date.
*
* #date_format
* A format string that describes the format and order of date parts to
* display in the edit form for this element. This makes it possible
* to show date parts in a custom order, or to leave some of them out.
* Be sure to add 'A' or 'a' to get an am/pm selector. Defaults to the
* short site default format.
*
* #date_label_position
* Handling option for date part labels, like 'Year', 'Month', and 'Day',
* can be 'above' the date part, 'within' it, or 'none', default is 'above' .
* The 'within' option shows the label as the first option in a select list
* or the default value for an empty textfield, taking up less screen space.
*
* #date_increment
* Increment minutes and seconds by this amount, default is 1.
*
* #date_year_range
* The number of years to go back and forward in a year selector,
* default is -3:+3 (3 back and 3 forward).
*
* #date_text_parts
* Array of date parts that should use textfields instead of selects
* i.e. array('year') will format the year as a textfield and other
* date parts as drop-down selects.
*/
function _date_api_element_info() {
$date_base = array(
'#input' => TRUE, '#tree' => TRUE,
'#date_timezone' => date_default_timezone(),
'#date_flexible' => 0,
'#date_format' => variable_get('date_format_short', 'm/d/Y - H:i'),
'#date_text_parts' => array(),
'#date_increment' => 1,
'#date_year_range' => '-3:+3',
'#date_label_position' => 'above',
);
if (module_exists('ctools')) {
$date_base['#pre_render'] = array('ctools_dependent_pre_render');
}
$type['date_select'] = array_merge($date_base, array(
'#process' => array('date_select_element_process'),
'#theme_wrappers' => array('date_select'),
'#value_callback' => 'date_select_element_value_callback',
));
$type['date_text'] = array_merge($date_base, array(
'#process' => array('date_text_element_process'),
'#theme_wrappers' => array('date_text'),
'#value_callback' => 'date_text_element_value_callback',
));
$type['date_timezone'] = array(
'#input' => TRUE, '#tree' => TRUE,
'#process' => array('date_timezone_element_process'),
'#theme_wrappers' => array('date_text'),
'#value_callback' => 'date_timezone_element_value_callback',
);
$type['date_year_range'] = array(
'#input' => TRUE,
'#process' => array('date_year_range_element_process'),
'#value_callback' => 'date_year_range_element_value_callback',
'#element_validate' => array('date_year_range_validate'),
);
return $type;
}
/**
* Create a date object from a datetime string value.
*/
function date_default_date($element) {
$granularity = date_format_order($element['#date_format']);
$default_value = $element['#default_value'];
$format = DATE_FORMAT_DATETIME;
// The text and popup widgets might return less than a full datetime string.
if (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;
}
}
$date = new DateObject($default_value, $element['#date_timezone'], $format);
if (is_object($date)) {
$date->limitGranularity($granularity);
if ($date->validGranularity($granularity, $element['#date_flexible'])) {
date_increment_round($date, $element['#date_increment']);
}
return $date;
}
return NULL;
}
/**
* Process callback which creates a date_year_range form element.
*/
function date_year_range_element_process($element, &$form_state, $form) {
// Year range is stored in the -3:+3 format, but collected as two separate
// textfields.
$element['years_back'] = array(
'#type' => 'textfield',
'#title' => t('Starting year'),
'#default_value' => $element['#value']['years_back'],
'#size' => 10,
'#maxsize' => 10,
'#attributes' => array('class' => array('select-list-with-custom-option', 'back')),
'#description' => t('Enter a relative value (-9, +9) or an absolute year such as 2015.'),
);
$element['years_forward'] = array(
'#type' => 'textfield',
'#title' => t('Ending year'),
'#default_value' => $element['#value']['years_forward'],
'#size' => 10,
'#maxsize' => 10,
'#attributes' => array('class' => array('select-list-with-custom-option', 'forward')),
'#description' => t('Enter a relative value (-9, +9) or an absolute year such as 2015.'),
);
$element['#tree'] = TRUE;
$element['#attached']['js'][] = drupal_get_path('module', 'date_api') . '/date_year_range.js';
$context = array(
'form' => $form,
);
drupal_alter('date_year_range_process', $element, $form_state, $context);
return $element;
}
/**
* Element value callback for the date_year_range form element.
*/
function date_year_range_element_value_callback($element, $input = FALSE, &$form_state = array()) {
// Convert the element's default value from a string to an array (to match
// what we will get from the two textfields when the form is submitted).
if ($input === FALSE) {
list($years_back, $years_forward) = explode(':', $element['#default_value']);
return array(
'years_back' => $years_back,
'years_forward' => $years_forward,
);
}
}
/**
* Element validation function for the date_year_range form element.
*/
function date_year_range_validate(&$element, &$form_state) {
// Recombine the two submitted form values into the -3:+3 format we will
// validate and save.
$year_range_submitted = drupal_array_get_nested_value($form_state['values'], $element['#parents']);
$year_range = $year_range_submitted['years_back'] . ':' . $year_range_submitted['years_forward'];
drupal_array_set_nested_value($form_state['values'], $element['#parents'], $year_range);
if (!date_range_valid($year_range)) {
form_error($element['years_back'], t('Starting year must be in the format -9, or an absolute year such as 1980.'));
form_error($element['years_forward'], t('Ending year must be in the format +9, or an absolute year such as 2030.'));
}
}
/**
* Element value callback for date_timezone element.
*/
function date_timezone_element_value_callback($element, $input = FALSE, &$form_state = array()) {
$return = '';
if ($input !== FALSE) {
$return = $input;
}
elseif (!empty($element['#default_value'])) {
$return = array('timezone' => $element['#default_value']);
}
return $return;
}
/**
* Creates a timezone form element.
*
* @param array $element
* The timezone form element.
*
* @return array
* the timezone form element
*/
function date_timezone_element_process($element, &$form_state, $form) {
if (date_hidden_element($element)) {
return $element;
}
$element['#tree'] = TRUE;
$label = theme('date_part_label_timezone', array('part_type' => 'select', 'element' => $element));
$element['timezone'] = array(
'#type' => 'select',
'#title' => $element['#date_label_position'] == 'above' ? $label : '',
'#options' => date_timezone_names($element['#required']),
'#value' => $element['#value'],
'#weight' => $element['#weight'],
'#required' => $element['#required'],
'#theme' => 'date_select_element',
'#theme_wrappers' => array('form_element'),
);
if (isset($element['#element_validate'])) {
array_push($element['#element_validate'], 'date_timezone_validate');
}
else {
$element['#element_validate'] = array('date_timezone_validate');
}
$context = array(
'form' => $form,
);
drupal_alter('date_timezone_process', $element, $form_state, $context);
return $element;
}
/**
* Validation for timezone input
*
* Move the timezone value from the nested field back to the original field.
*/
function date_timezone_validate($element, &$form_state) {
if (date_hidden_element($element)) {
return;
}
form_set_value($element, $element['#value']['timezone'], $form_state);
}
/**
* Element value callback for date_text element.
*/
function date_text_element_value_callback($element, $input = FALSE, &$form_state = array()) {
$return = array('date' => '');
$date = NULL;
// Normal input from submitting the form element.
// Check is_array() to skip the string input values created by Views pagers.
// Those string values, if present, should be interpreted as empty input.
if ($input != FALSE && is_array($input)) {
$return = $input;
$date = date_text_input_date($element, $input);
}
// No input? Try the default value.
elseif (!empty($element['#default_value'])) {
$date = date_default_date($element);
}
if (date_is_date($date)) {
$return['date'] = date_format_date($date, 'custom', $element['#date_format']);
}
return $return;
}
/**
* Text date input form.
*
* Display all or part of a date in a single textfield.
*
* The exact parts displayed in the field are those in #date_granularity.
* The display of each part comes from #date_format.
*
*/
function date_text_element_process($element, &$form_state, $form) {
if (date_hidden_element($element)) {
return $element;
}
$element['#tree'] = TRUE;
$element['#theme_wrappers'] = array('date_text');
$element['date']['#value'] = $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']['#ajax'] = !empty($element['#ajax']) ? $element['#ajax'] : FALSE;
// 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'];
$element['date']['#theme'] = 'date_textfield_element';
if (isset($element['#element_validate'])) {
array_push($element['#element_validate'], 'date_text_validate');
}
else {
$element['#element_validate'] = array('date_text_validate');
}
if (!empty($element['#force_value'])) {
$element['date']['#value'] = $element['date']['#default_value'];
}
$context = array(
'form' => $form,
);
drupal_alter('date_text_process', $element, $form_state, $context);
return $element;
}
/**
* 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)) {
return;
}
if (is_string($element['#value'])) {
return;
}
$input_exists = NULL;
$input = drupal_array_get_nested_value($form_state['values'], $element['#parents'], $input_exists);
drupal_alter('date_text_pre_validate', $element, $form_state, $input);
$label = !empty($element['#date_title']) ? $element['#date_title'] : (!empty($element['#title']) ? $element['#title'] : '');
$date = date_text_input_date($element, $input);
// If the field has errors, display them.
// If something was input but there is no date, the date is invalid.
// If the field is empty and required, set error message and return.
$error_field = implode('][', $element['#parents']);
if (empty($date) || !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);
form_set_error($error_field, $message);
return;
}
if (!empty($element['#required'])) {
$message = t('A valid date is required for %title.', array('%title' => $label));
form_set_error($error_field, $message);
return;
}
// Fall through, some other error.
if (!empty($input['date'])) {
form_error($element, t('%title is invalid.', array('%title' => $label)));
return;
}
}
$value = !empty($date) ? $date->format(DATE_FORMAT_DATETIME) : NULL;
form_set_value($element, $value, $form_state);
}
/**
* Helper function for creating a date object out of user input.
*/
function date_text_input_date($element, $input) {
if (empty($input) || !is_array($input) || !array_key_exists('date', $input) || empty($input['date'])) {
return NULL;
}
$granularity = date_format_order($element['#date_format']);
$date = new DateObject($input['date'], $element['#date_timezone'], $element['#date_format']);
if (is_object($date)) {
$date->limitGranularity($granularity);
if ($date->validGranularity($granularity, $element['#date_flexible'])) {
date_increment_round($date, $element['#date_increment']);
}
return $date;
}
return NULL;
}
/**
* 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' => '');
$date = NULL;
if ($input !== FALSE) {
$return = $input;
$date = date_select_input_date($element, $input);
}
elseif (!empty($element['#default_value'])) {
$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');
foreach ($granularity as $field) {
if ($field != 'timezone') {
$return[$field] = date_is_date($date) ? $date->format($formats[$field]) : '';
}
}
return $return;
}
/**
* Flexible date/time drop-down selector.
*
* Splits date into a collection of date and time sub-elements, one
* for each date part. Each sub-element can be either a textfield or a
* select, based on the value of ['#date_settings']['text_fields'].
*
* 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)) {
return $element;
}
$date = NULL;
$granularity = date_format_order($element['#date_format']);
if (is_array($element['#default_value'])) {
$date = date_select_input_date($element, $element['#default_value']);
}
elseif (!empty($element['#default_value'])) {
$date = date_default_date($element);
}
$element['#tree'] = TRUE;
$element['#theme_wrappers'] = array('date_select');
$element += (array) date_parts_element($element, $date, $element['#date_format']);
// 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');
foreach (date_nongranularity($granularity) as $field) {
if ($field != 'timezone') {
$element[$field] = array(
'#type' => 'value',
'#value' => 0,
);
}
}
if (isset($element['#element_validate'])) {
array_push($element['#element_validate'], 'date_select_validate');
}
else {
$element['#element_validate'] = array('date_select_validate');
}
$context = array(
'form' => $form,
);
drupal_alter('date_select_process', $element, $form_state, $context);
return $element;
}
/**
* Creates form elements for one or more date parts.
*
* Get the order of date elements from the provided format.
* If the format order omits any date parts in the granularity, alter the
* granularity array to match the format, then flip the $order array
* to get the position for each element. Then iterate through the
* elements and create a sub-form for each part.
*
* @param array $element
* The date element.
* @param object $date
* The date object.
* @param string $format
* A date format string.
*
* @return array
* The form array for the submitted date parts.
*/
function date_parts_element($element, $date, $format) {
$granularity = date_format_order($format);
$sub_element = array('#granularity' => $granularity);
$order = array_flip($granularity);
$hours_format = strpos(strtolower($element['#date_format']), 'a') ? 'g': 'G';
$month_function = strpos($element['#date_format'], 'F') !== FALSE ? 'date_month_names' : 'date_month_names_abbr';
$count = 0;
$increment = min(intval($element['#date_increment']), 1);
// Allow empty value as option if date is not required or there is no date.
$part_required = (bool) $element['#required'] && is_object($date);
foreach ($granularity as $field) {
$part_type = in_array($field, $element['#date_text_parts']) ? 'textfield' : 'select';
$sub_element[$field] = array(
'#weight' => $order[$field],
'#required' => $part_required,
'#attributes' => array('class' => isset($element['#attributes']['class']) ? $element['#attributes']['class'] += array('date-' . $field) : array('date-' . $field)),
'#ajax' => !empty($element['#ajax']) ? $element['#ajax'] : FALSE,
);
switch ($field) {
case 'year':
$range = date_range_years($element['#date_year_range'], $date);
$min_year = $range[0];
$max_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));
}
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') {
$sub_element[$field]['#options'] = drupal_map_assoc(date_hours($hours_format, $part_required));
}
$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') {
$sub_element[$field]['#options'] = drupal_map_assoc(date_minutes('i', $part_required, $element['#date_increment']));
}
$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') {
$sub_element[$field]['#options'] = drupal_map_assoc(date_seconds('s', $part_required, $element['#date_increment']));
}
$sub_element[$field]['#prefix'] = theme('date_part_minsec_prefix', $element);
break;
}
// Add handling for the date part label.
$label = theme('date_part_label_' . $field, array('part_type' => $part_type, 'element' => $element));
if (in_array($field, $element['#date_text_parts'])) {
$sub_element[$field]['#type'] = 'textfield';
$sub_element[$field]['#theme'] = 'date_textfield_element';
$sub_element[$field]['#size'] = 7;
if ($element['#date_label_position'] == 'within') {
if (!empty($sub_element[$field]['#options']) && is_array($sub_element[$field]['#options'])) {
$sub_element[$field]['#options'] = array(
'-' . $label => '-' . $label) + $sub_element[$field]['#options'];
}
if (empty($sub_element[$field]['#default_value'])) {
$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';
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;
}
}
}
// Views exposed filters are treated as submitted even if not,
// so force the #default value in that case. Make sure we set
// a default that is in the option list.
if (!empty($element['#force_value'])) {
$options = $sub_element[$field]['#options'];
$default = !empty($sub_element[$field]['#default_value']) ? $sub_element[$field]['#default_value'] : array_shift($options);
$sub_element[$field]['#value'] = $default;
}
if (($hours_format == 'g' || $hours_format == 'h') && date_has_time($granularity)) {
$sub_element['ampm'] = array(
'#type' => 'select',
'#theme' => 'date_select_element',
'#default_value' => is_object($date) ? (date_format($date, 'G') >= 12 ? 'pm' : 'am') : '',
'#options' => drupal_map_assoc(date_ampm($part_required)),
'#required' => $part_required,
'#weight' => 8,
'#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));
}
}
return $sub_element;
}
/**
* Validation function for date selector.
*
* When used as a Views widget, the validation step always gets triggered,
* even with no form submission. Before form submission $element['#value']
* contains a string, after submission it contains an array.
*/
function date_select_validate($element, &$form_state) {
if (date_hidden_element($element)) {
return;
}
if (is_string($element['#value'])) {
return;
}
$input_exists = NULL;
$input = drupal_array_get_nested_value($form_state['values'], $element['#parents'], $input_exists);
// Strip field labels out of the results.
foreach ($element['#value'] as $field => $field_value) {
if (substr($field_value, 0, 1) == '-') {
$input[$field] = '';
}
}
drupal_alter('date_select_pre_validate', $element, $form_state, $input);
$label = !empty($element['#date_title']) ? $element['#date_title'] : (!empty($element['#title']) ? $element['#title'] : '');
if (isset($input['ampm'])) {
if ($input['ampm'] == 'pm' && $input['hour'] < 12) {
$input['hour'] += 12;
}
elseif ($input['ampm'] == 'am' && $input['hour'] == 12) {
$input['hour'] -= 12;
}
}
unset($input['ampm']);
$date = date_select_input_date($element, $input);
// If the field has errors, display them.
$error_field = implode('][', $element['#parents']);
$entered = array_values(array_filter($input));
if (empty($date) || !empty($date->errors)) {
// The input created a date but it has 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);
form_set_error($error_field, $message);
return;
}
// Nothing was entered but the date is required.
elseif (empty($entered) && $element['#required']) {
$message = t('A valid date is required for %title.', array('%title' => $label));
form_set_error($error_field, $message);
return;
}
// Something was input but it wasn't enough to create a valid date.
elseif (!empty($entered)) {
$message = t('The value input for field %field is invalid.', array('%field' => $label));
form_set_error($error_field, $message);
return;
}
}
$value = !empty($date) ? $date->format(DATE_FORMAT_DATETIME) : NULL;
form_set_value($element, $value, $form_state);
}
/**
* Helper function for creating a date object out of user input.
*/
function date_select_input_date($element, $input) {
// Was anything entered? If not, we have no date.
if (!is_array($input)) {
return NULL;
}
else {
$entered = array_values(array_filter($input));
if (empty($entered)) {
return NULL;
}
}
$granularity = date_format_order($element['#date_format']);
if (isset($input['ampm'])) {
if ($input['ampm'] == 'pm' && $input['hour'] < 12) {
$input['hour'] += 12;
}
elseif ($input['ampm'] == 'am' && $input['hour'] == 12) {
$input['hour'] -= 12;
}
}
unset($input['ampm']);
// Make the input match the granularity.
foreach (date_nongranularity($granularity) as $part) {
unset($input[$part]);
}
$date = new DateObject($input, $element['#date_timezone']);
if (is_object($date)) {
$date->limitGranularity($granularity);
if ($date->validGranularity($granularity, $element['#date_flexible'])) {
date_increment_round($date, $element['#date_increment']);
}
return $date;
}
return NULL;
}

View File

@@ -0,0 +1,803 @@
<?php
/**
* @file
* Parse iCal data.
*
* This file must be included when these functions are needed.
*/
/**
* Return an array of iCalendar information from an iCalendar file.
*
* No timezone adjustment is performed in the import since the timezone
* conversion needed will vary depending on whether the value is being
* imported into the database (when it needs to be converted to UTC), is being
* viewed on a site that has user-configurable timezones (when it needs to be
* converted to the user's timezone), if it needs to be converted to the
* site timezone, or if it is a date without a timezone which should not have
* any timezone conversion applied.
*
* Properties that have dates and times are converted to sub-arrays like:
* 'datetime' => date in YYYY-MM-DD HH:MM format, not timezone adjusted
* 'all_day' => whether this is an all-day event
* 'tz' => the timezone of the date, could be blank for absolute
* times that should get no timezone conversion.
*
* Exception dates can have muliple values and are returned as arrays
* like the above for each exception date.
*
* Most other properties are returned as PROPERTY => VALUE.
*
* Each item in the VCALENDAR will return an array like:
* [0] => Array (
* [TYPE] => VEVENT
* [UID] => 104
* [SUMMARY] => An example event
* [URL] => http://example.com/node/1
* [DTSTART] => Array (
* [datetime] => 1997-09-07 09:00:00
* [all_day] => 0
* [tz] => US/Eastern
* )
* [DTEND] => Array (
* [datetime] => 1997-09-07 11:00:00
* [all_day] => 0
* [tz] => US/Eastern
* )
* [RRULE] => Array (
* [FREQ] => Array (
* [0] => MONTHLY
* )
* [BYDAY] => Array (
* [0] => 1SU
* [1] => -1SU
* )
* )
* [EXDATE] => Array (
* [0] = Array (
* [datetime] => 1997-09-21 09:00:00
* [all_day] => 0
* [tz] => US/Eastern
* )
* [1] = Array (
* [datetime] => 1997-10-05 09:00:00
* [all_day] => 0
* [tz] => US/Eastern
* )
* )
* [RDATE] => Array (
* [0] = Array (
* [datetime] => 1997-09-21 09:00:00
* [all_day] => 0
* [tz] => US/Eastern
* )
* [1] = Array (
* [datetime] => 1997-10-05 09:00:00
* [all_day] => 0
* [tz] => US/Eastern
* )
* )
* )
*
* @todo
* figure out how to handle this if subgroups are nested,
* like a VALARM nested inside a VEVENT.
*
* @param string $filename
* Location (local or remote) of a valid iCalendar file.
*
* @return array
* An array with all the elements from the ical.
*/
function date_ical_import($filename) {
// Fetch the iCal data. If file is a URL, use drupal_http_request. fopen
// isn't always configured to allow network connections.
if (substr($filename, 0, 4) == 'http') {
// Fetch the ical data from the specified network location.
$icaldatafetch = drupal_http_request($filename);
// Check the return result.
if ($icaldatafetch->error) {
watchdog('date ical', 'HTTP Request Error importing %filename: @error', array('%filename' => $filename, '@error' => $icaldatafetch->error));
return FALSE;
}
// Break the return result into one array entry per lines.
$icaldatafolded = explode("\n", $icaldatafetch->data);
}
else {
$icaldatafolded = @file($filename, FILE_IGNORE_NEW_LINES);
if ($icaldatafolded === FALSE) {
watchdog('date ical', 'Failed to open file: %filename', array('%filename' => $filename));
return FALSE;
}
}
// Verify this is iCal data.
if (trim($icaldatafolded[0]) != 'BEGIN:VCALENDAR') {
watchdog('date ical', 'Invalid calendar file: %filename', array('%filename' => $filename));
return FALSE;
}
return date_ical_parse($icaldatafolded);
}
/**
* Returns an array of iCalendar information from an iCalendar file.
*
* As date_ical_import() but different param.
*
* @param array $icaldatafolded
* An array of lines from an ical feed.
*
* @return array
* An array with all the elements from the ical.
*/
function date_ical_parse($icaldatafolded = array()) {
$items = array();
// Verify this is iCal data.
if (trim($icaldatafolded[0]) != 'BEGIN:VCALENDAR') {
watchdog('date ical', 'Invalid calendar file.');
return FALSE;
}
// "Unfold" wrapped lines.
$icaldata = array();
foreach ($icaldatafolded as $line) {
$out = array();
// See if this looks like the beginning of a new property or value. If not,
// it is a continuation of the previous line. The regex is to ensure that
// wrapped QUOTED-PRINTABLE data is kept intact.
if (!preg_match('/([A-Z]+)[:;](.*)/', $line, $out)) {
// Trim up to 1 leading space from wrapped line per iCalendar standard.
$line = array_pop($icaldata) . (ltrim(substr($line, 0, 1)) . substr($line, 1));
}
$icaldata[] = $line;
}
unset($icaldatafolded);
// Parse the iCal information.
$parents = array();
$subgroups = array();
$vcal = '';
foreach ($icaldata as $line) {
$line = trim($line);
$vcal .= $line . "\n";
// Deal with begin/end tags separately.
if (preg_match('/(BEGIN|END):V(\S+)/', $line, $matches)) {
$closure = $matches[1];
$type = 'V' . $matches[2];
if ($closure == 'BEGIN') {
array_push($parents, $type);
array_push($subgroups, array());
}
elseif ($closure == 'END') {
end($subgroups);
$subgroup = &$subgroups[key($subgroups)];
switch ($type) {
case 'VCALENDAR':
if (prev($subgroups) == FALSE) {
$items[] = array_pop($subgroups);
}
else {
$parent[array_pop($parents)][] = array_pop($subgroups);
}
break;
// Add the timezones in with their index their TZID.
case 'VTIMEZONE':
$subgroup = end($subgroups);
$id = $subgroup['TZID'];
unset($subgroup['TZID']);
// Append this subgroup onto the one above it.
prev($subgroups);
$parent = &$subgroups[key($subgroups)];
$parent[$type][$id] = $subgroup;
array_pop($subgroups);
array_pop($parents);
break;
// Do some fun stuff with durations and all_day events and then append
// to parent.
case 'VEVENT':
case 'VALARM':
case 'VTODO':
case 'VJOURNAL':
case 'VVENUE':
case 'VFREEBUSY':
default:
// Can't be sure whether DTSTART is before or after DURATION, so
// parse DURATION at the end.
if (isset($subgroup['DURATION'])) {
date_ical_parse_duration($subgroup, 'DURATION');
}
// Add a top-level indication for the 'All day' condition. Leave it
// in the individual date components, too, so it is always available
// even when you are working with only a portion of the VEVENT
// array, like in Feed API parsers.
$subgroup['all_day'] = FALSE;
// iCal spec states 'The "DTEND" property for a "VEVENT" calendar
// component specifies the non-inclusive end of the event'. Adjust
// multi-day events to remove the extra day because the Date code
// 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_modify($date, '-1 day');
$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
// datetime and if it is an all day event define the entire event
// as a single all day event.
if (!empty($subgroup['DTSTART']) &&
(empty($subgroup['DTEND']) && empty($subgroup['RRULE']) && empty($subgroup['RRULE']['COUNT']))) {
$subgroup['DTEND'] = $subgroup['DTSTART'];
}
// Add this element to the parent as an array under the component
// name.
if (!empty($subgroup['DTSTART']['all_day'])) {
$subgroup['all_day'] = TRUE;
}
// Add this element to the parent as an array under the
prev($subgroups);
$parent = &$subgroups[key($subgroups)];
$parent[$type][] = $subgroup;
array_pop($subgroups);
array_pop($parents);
break;
}
}
}
// Handle all other possibilities.
else {
// Grab current subgroup.
end($subgroups);
$subgroup = &$subgroups[key($subgroups)];
// Split up the line into nice pieces for PROPERTYNAME,
// PROPERTYATTRIBUTES, and PROPERTYVALUE.
preg_match('/([^;:]+)(?:;([^:]*))?:(.+)/', $line, $matches);
$name = !empty($matches[1]) ? strtoupper(trim($matches[1])) : '';
$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.
case 'CREATED':
case 'LAST-MODIFIED':
case 'DTSTART':
case 'DTEND':
case 'DTSTAMP':
case 'FREEBUSY':
case 'DUE':
case 'COMPLETED':
$parse_result = date_ical_parse_date($field, $data);
break;
case 'EXDATE':
case 'RDATE':
$parse_result = date_ical_parse_exceptions($field, $data);
break;
case 'TRIGGER':
// A TRIGGER can either be a date or in the form -PT1H.
if (!empty($field)) {
$parse_result = date_ical_parse_date($field, $data);
}
else {
$parse_result = array('DATA' => $data);
}
break;
case 'DURATION':
// Can't be sure whether DTSTART is before or after DURATION in
// the VEVENT, so store the data and parse it at the end.
$parse_result = array('DATA' => $data);
break;
case 'RRULE':
case 'EXRULE':
$parse_result = date_ical_parse_rrule($field, $data);
break;
case 'STATUS':
case 'SUMMARY':
case 'DESCRIPTION':
$parse_result = date_ical_parse_text($field, $data);
break;
case 'LOCATION':
$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.
default:
$parse_result = $data;
break;
}
// Store the result of our parsing.
$subgroup[$name] = $parse_result;
}
}
return $items;
}
/**
* Parses a ical date element.
*
* Possible formats to parse include:
* PROPERTY:YYYYMMDD[T][HH][MM][SS][Z]
* PROPERTY;VALUE=DATE:YYYYMMDD[T][HH][MM][SS][Z]
* PROPERTY;VALUE=DATE-TIME:YYYYMMDD[T][HH][MM][SS][Z]
* PROPERTY;TZID=XXXXXXXX;VALUE=DATE:YYYYMMDD[T][HH][MM][SS]
* PROPERTY;TZID=XXXXXXXX:YYYYMMDD[T][HH][MM][SS]
*
* The property and the colon before the date are removed in the import
* process above and we are left with $field and $data.
*
* @param string $field
* The text before the colon and the date, i.e.
* ';VALUE=DATE:', ';VALUE=DATE-TIME:', ';TZID='
* @param string $data
* The date itself, after the colon, in the format YYYYMMDD[T][HH][MM][SS][Z]
* 'Z', if supplied, means the date is in UTC.
*
* @return array
* $items array, consisting of:
* 'datetime' => date in YYYY-MM-DD HH:MM format, not timezone adjusted
* 'all_day' => whether this is an all-day event with no time
* 'tz' => the timezone of the date, could be blank if the ical
* has no timezone; the ical specs say no timezone
* conversion should be done if no timezone info is
* supplied
* @todo
* Another option for dates is the format PROPERTY;VALUE=PERIOD:XXXX. The
* period may include a duration, or a date and a duration, or two dates, so
* would have to be split into parts and run through date_ical_parse_date()
* and date_ical_parse_duration(). This is not commonly used, so ignored for
* now. It will take more work to figure how to support that.
*/
function date_ical_parse_date($field, $data) {
$items = array('datetime' => '', 'all_day' => '', 'tz' => '');
if (empty($data)) {
return $items;
}
// Make this a little more whitespace independent.
$data = trim($data);
// Turn the properties into a nice indexed array of
// array(PROPERTYNAME => PROPERTYVALUE);
$field_parts = preg_split('/[;:]/', $field);
$properties = array();
foreach ($field_parts as $part) {
if (strpos($part, '=') !== FALSE) {
$tmp = explode('=', $part);
$properties[$tmp[0]] = $tmp[1];
}
}
// Make this a little more whitespace independent.
$data = trim($data);
// Record if a time has been found.
$has_time = FALSE;
// If a format is specified, parse it according to that format.
if (isset($properties['VALUE'])) {
switch ($properties['VALUE']) {
case 'DATE':
preg_match(DATE_REGEX_ICAL_DATE, $data, $regs);
// 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.
$datetime = date_pad($regs[1]) . '-' . date_pad($regs[2]) . '-' . date_pad($regs[3]);
// Time.
$datetime .= ' ' . date_pad($regs[4]) . ':' . date_pad($regs[5]) . ':' . date_pad($regs[6]);
$has_time = TRUE;
break;
}
}
// If no format is specified, attempt a loose match.
else {
preg_match(DATE_REGEX_LOOSE, $data, $regs);
if (!empty($regs) && count($regs) > 2) {
// Date.
$datetime = date_pad($regs[1]) . '-' . date_pad($regs[2]) . '-' . date_pad($regs[3]);
if (isset($regs[4])) {
$has_time = TRUE;
// Time.
$datetime .= ' ' . (!empty($regs[5]) ? date_pad($regs[5]) : '00') .
':' . (!empty($regs[6]) ? date_pad($regs[6]) : '00') .
':' . (!empty($regs[7]) ? date_pad($regs[7]) : '00');
}
}
}
// Use timezone if explicitly declared.
if (isset($properties['TZID'])) {
$tz = $properties['TZID'];
// Fix alternatives like US-Eastern which should be US/Eastern.
$tz = str_replace('-', '/', $tz);
// Unset invalid timezone names.
module_load_include('inc', 'date_api', 'date_api.admin');
$tz = _date_timezone_replacement($tz);
if (!date_timezone_is_valid($tz)) {
$tz = '';
}
}
// If declared as UTC with terminating 'Z', use that timezone.
elseif (strpos($data, 'Z') !== FALSE) {
$tz = 'UTC';
}
// Otherwise this date is floating.
else {
$tz = '';
}
$items['datetime'] = $datetime;
$items['all_day'] = $has_time ? FALSE : TRUE;
$items['tz'] = $tz;
return $items;
}
/**
* Parse an ical repeat rule.
*
* @return array
* Array in the form of PROPERTY => array(VALUES)
* PROPERTIES include FREQ, INTERVAL, COUNT, BYDAY, BYMONTH, BYYEAR, UNTIL
*/
function date_ical_parse_rrule($field, $data) {
$data = preg_replace("/RRULE.*:/", '', $data);
$items = array('DATA' => $data);
$rrule = explode(';', $data);
foreach ($rrule as $key => $value) {
$param = explode('=', $value);
// Must be some kind of invalid data.
if (count($param) != 2) {
continue;
}
if ($param[0] == 'UNTIL') {
$values = date_ical_parse_date('', $param[1]);
}
else {
$values = explode(',', $param[1]);
}
// Treat items differently if they have multiple or single values.
if (in_array($param[0], array('FREQ', 'INTERVAL', 'COUNT', 'WKST'))) {
$items[$param[0]] = $param[1];
}
else {
$items[$param[0]] = $values;
}
}
return $items;
}
/**
* Parse exception dates (can be multiple values).
*
* @return array
* an array of date value arrays.
*/
function date_ical_parse_exceptions($field, $data) {
$data = str_replace($field . ':', '', $data);
$items = array('DATA' => $data);
$ex_dates = explode(',', $data);
foreach ($ex_dates as $ex_date) {
$items[] = date_ical_parse_date('', $ex_date);
}
return $items;
}
/**
* Parses the duration of the event.
*
* Example:
* DURATION:PT1H30M
* DURATION:P1Y2M
*
* @param array $subgroup
* Array of other values in the vevent so we can check for DTSTART.
*/
function date_ical_parse_duration(&$subgroup, $field = 'DURATION') {
$items = $subgroup[$field];
$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]) : '';
$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)) {
$timezone = 'UTC';
}
$date = new DateObject($start_date, $timezone);
$date2 = clone($date);
foreach ($items as $item => $count) {
if ($count > 0) {
date_modify($date2, '+' . $count . ' ' . $item);
}
}
$format = isset($subgroup['DTSTART']['type']) && $subgroup['DTSTART']['type'] == 'DATE' ? 'Y-m-d' : 'Y-m-d H:i:s';
$subgroup['DTEND'] = array(
'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);
}
/**
* Parse and clean up ical text elements.
*/
function date_ical_parse_text($field, $data) {
if (strstr($field, 'QUOTED-PRINTABLE')) {
$data = quoted_printable_decode($data);
}
// Strip line breaks within element.
$data = str_replace(array("\r\n ", "\n ", "\r "), '', $data);
// Put in line breaks where encoded.
$data = str_replace(array("\\n", "\\N"), "\n", $data);
// Remove other escaping.
$data = stripslashes($data);
return $data;
}
/**
* Parse location elements.
*
* Catch situations like the upcoming.org feed that uses
* LOCATION;VENUE-UID="http://upcoming.yahoo.com/venue/104/":111 First Street...
* or more normal LOCATION;UID=123:111 First Street...
* Upcoming feed would have been improperly broken on the ':' in http://
* so we paste the $field and $data back together first.
*
* Use non-greedy check for ':' in case there are more of them in the address.
*/
function date_ical_parse_location($field, $data) {
if (preg_match('/UID=[?"](.+)[?"][*?:](.+)/', $field . ':' . $data, $matches)) {
$location = array();
$location['UID'] = $matches[1];
$location['DESCRIPTION'] = stripslashes($matches[2]);
return $location;
}
else {
// Remove other escaping.
$location = stripslashes($data);
return $location;
}
}
/**
* Return a date object for the ical date, adjusted to its local timezone.
*
* @param array $ical_date
* An array of ical date information created in the ical import.
* @param string $to_tz
* The timezone to convert the date's value to.
*
* @return object
* A timezone-adjusted date object.
*/
function date_ical_date($ical_date, $to_tz = FALSE) {
// If the ical date has no timezone, must assume it is stateless
// so treat it as a local date.
if (empty($ical_date['datetime'])) {
return NULL;
}
elseif (empty($ical_date['tz'])) {
$from_tz = date_default_timezone();
}
else {
$from_tz = $ical_date['tz'];
}
if (strlen($ical_date['datetime']) < 11) {
$ical_date['datetime'] .= ' 00:00:00';
}
$date = new DateObject($ical_date['datetime'], new DateTimeZone($from_tz));
if ($to_tz && $ical_date['tz'] != '' && $to_tz != $ical_date['tz']) {
date_timezone_set($date, timezone_open($to_tz));
}
return $date;
}
/**
* Escape #text elements for safe iCal use.
*
* @param string $text
* Text to escape
*
* @return string
* Escaped text
*
*/
function date_ical_escape_text($text) {
$text = drupal_html_to_text($text);
$text = trim($text);
// TODO Per #38130 the iCal specs don't want : and " escaped
// but there was some reason for adding this in. Need to watch
// this and see if anything breaks.
// $text = str_replace('"', '\"', $text);
// $text = str_replace(":", "\:", $text);
$text = preg_replace("/\\\b/", "\\\\", $text);
$text = str_replace(",", "\,", $text);
$text = str_replace(";", "\;", $text);
$text = str_replace("\n", "\\n ", $text);
return trim($text);
}
/**
* Build an iCal RULE from $form_values.
*
* @param array $form_values
* An array constructed like the one created by date_ical_parse_rrule().
* [RRULE] => Array (
* [FREQ] => Array (
* [0] => MONTHLY
* )
* [BYDAY] => Array (
* [0] => 1SU
* [1] => -1SU
* )
* [UNTIL] => Array (
* [datetime] => 1997-21-31 09:00:00
* [all_day] => 0
* [tz] => US/Eastern
* )
* )
* [EXDATE] => Array (
* [0] = Array (
* [datetime] => 1997-09-21 09:00:00
* [all_day] => 0
* [tz] => US/Eastern
* )
* [1] = Array (
* [datetime] => 1997-10-05 09:00:00
* [all_day] => 0
* [tz] => US/Eastern
* )
* )
* [RDATE] => Array (
* [0] = Array (
* [datetime] => 1997-09-21 09:00:00
* [all_day] => 0
* [tz] => US/Eastern
* )
* [1] = Array (
* [datetime] => 1997-10-05 09:00:00
* [all_day] => 0
* [tz] => US/Eastern
* )
* )
*/
function date_api_ical_build_rrule($form_values) {
$RRULE = '';
if (empty($form_values) || !is_array($form_values)) {
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']);
// Unset the empty 'All' values.
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']) && $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('BYMONTHDAY', $form_values) && is_array($form_values['BYMONTHDAY']) && $BYMONTHDAY = implode(",", $form_values['BYMONTHDAY'])) {
$RRULE .= ';BYMONTHDAY=' . $BYMONTHDAY;
}
// The UNTIL date is supposed to always be expressed in UTC.
// The input date values may already have been converted to a date object on a
// previous pass, so check for that.
if (array_key_exists('UNTIL', $form_values) && array_key_exists('datetime', $form_values['UNTIL']) && !empty($form_values['UNTIL']['datetime'])) {
// We only collect a date for UNTIL, but we need it to be inclusive, so
// force it to a full datetime element at the last second of the day.
if (!is_object($form_values['UNTIL']['datetime'])) {
// If this is a date without time, give it time.
if (strlen($form_values['UNTIL']['datetime']) < 11) {
$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']['all_day'] = FALSE;
}
$until = date_ical_date($form_values['UNTIL'], 'UTC');
}
else {
$until = $form_values['UNTIL']['datetime'];
}
$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'];
}
// 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'];
}
else {
$RRULE .= ';WKST=' . date_repeat_dow2day(variable_get('date_first_day', 0));
}
// Exceptions dates go last, on their own line.
// The input date values may already have been converted to a date
// object on a previous pass, so check for that.
if (isset($form_values['EXDATE']) && is_array($form_values['EXDATE'])) {
$ex_dates = array();
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': '';
if (!empty($ex_date)) {
$ex_dates[] = $ex_date;
}
}
}
if (!empty($ex_dates)) {
sort($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'];
}
// Exceptions dates go last, on their own line.
if (isset($form_values['RDATE']) && is_array($form_values['RDATE'])) {
$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': '';
if (!empty($ex_date)) {
$ex_dates[] = $ex_date;
}
}
if (!empty($ex_dates)) {
sort($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'];
}
return $RRULE;
}

File diff suppressed because it is too large Load Diff

View File

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 440 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 357 B

View File

@@ -0,0 +1,229 @@
<?php
/**
* @file
* Theme files for Date API.
*/
/**
* Returns HTML for a date timezone element.
*/
function theme_date_timezone($variables) {
$element = $variables['element'];
$attributes = $element['#attributes'];
$wrapper_attributes = array();
// 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>';
}
return '<div ' . drupal_attributes($attributes) . '>' . theme('form_element', $element) . '</div>';
}
/**
* Returns HTML for a date select element.
*/
function theme_date_select($variables) {
$element = $variables['element'];
$attributes = !empty($element['#wrapper_attributes']) ? $element['#wrapper_attributes'] : array('class' => array());
$attributes['class'][] = 'container-inline-date';
$wrapper_attributes = array('class' => array('date-padding'));
$wrapper_attributes['class'][] = 'clearfix';
// 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>';
}
return '<div ' . drupal_attributes($attributes) . '>' . theme('form_element', $element) . '</div>';
}
/**
* Returns HTML for a date text element.
*/
function theme_date_text($variables) {
$element = $variables['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.
$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.
if (isset($element['#children'])) {
$element['#children'] = '<div id="' . $element['#id'] . '" ' . drupal_attributes($wrapper_attributes) . '>' . $element['#children'] . '</div>';
}
return '<div ' . drupal_attributes($attributes) . '>' . theme('form_element', $element) . '</div>';
}
/**
* Returns HTML for a date select input form element.
*/
function theme_date_select_element($variables) {
$element = $variables['element'];
$parents = $element['#parents'];
$part = array_pop($parents);
return '<div class="date-' . $part . '">' . theme('select', $element) . '</div>';
}
/**
* Returns HTML for a date textfield input form element.
*/
function theme_date_textfield_element($variables) {
$element = $variables['element'];
$parents = $element['#parents'];
$part = array_pop($parents);
return '<div class="date-' . $part . '">' . theme('textfield', $element) . '</div>';
}
/**
* Returns HTML for a 'hour' date part prefix.
*/
function theme_date_part_hour_prefix($variables) {
$element = $variables['element'];
if ($element['#date_label_position'] != 'above') {
return '<span class="form-item date-spacer">&nbsp;-&nbsp;</span>';
}
}
/**
* Returns HTML for a 'minutes and seconds' date part prefix.
*/
function theme_date_part_minsec_prefix($variables) {
$element = $variables['element'];
if ($element['#date_label_position'] != 'above') {
return '<span class="form-item date-spacer">:</span>';
}
}
/**
* Returns HTML for a date_select 'year' label.
*/
function theme_date_part_label_year($variables) {
return t('Year', array(), array('context' => 'datetime'));
}
/**
* Returns HTML for a date_select 'month' label.
*/
function theme_date_part_label_month($variables) {
return t('Month', array(), array('context' => 'datetime'));
}
/**
* Returns HTML for a date_select 'day' label.
*/
function theme_date_part_label_day($variables) {
return t('Day', array(), array('context' => 'datetime'));
}
/**
* Returns HTML for a date_select 'hour' label.
*/
function theme_date_part_label_hour($variables) {
return t('Hour', array(), array('context' => 'datetime'));
}
/**
* Returns HTML for a date_select 'minute' label.
*/
function theme_date_part_label_minute($variables) {
return t('Minute', array(), array('context' => 'datetime'));
}
/**
* Returns HTML for a date_select 'second' label.
*/
function theme_date_part_label_second($variables) {
return t('Second', array(), array('context' => 'datetime'));
}
/**
* Returns HTML for a date_select 'ampm' label.
*/
function theme_date_part_label_ampm($variables) {
return '&nbsp;';
}
/**
* Returns HTML for a date_select 'timezone' label.
*/
function theme_date_part_label_timezone($variables) {
return t('Timezone');
}
/**
* Returns HTML for a date_select 'date' label.
*/
function theme_date_part_label_date($variables) {
return t('Date', array(), array('context' => 'datetime'));
}
/**
* Returns HTML for a date_select 'time' label.
*/
function theme_date_part_label_time($variables) {
return t('Time', array(), array('context' => 'datetime'));
}
/**
* Returns HTML for a date block that looks like a mini calendar day.
*
* Pass in a date object already set to the right timezone, format as a calendar
* page date. The calendar styling is created in CSS.
*/
function theme_date_calendar_day($variables) {
$output = '';
$date = $variables['date'];
if (!empty($date)) {
$output .= '<div class="date-calendar-day">';
$output .= '<span class="month">' . date_format_date($date, 'custom', 'M') . '</span>';
$output .= '<span class="day">' . date_format_date($date, 'custom', 'j') . '</span>';
$output .= '<span class="year">' . date_format_date($date, 'custom', 'Y') . '</span>';
$output .= '</div>';
}
return $output;
}
/**
* Returns HTML for a date in the format 'time ago'.
*/
function theme_date_time_ago($variables) {
$start_date = $variables['start_date'];
$end_date = $variables['end_date'];
$interval = !empty($variables['interval']) ? $variables['interval'] : 2;
$display = isset($variables['interval_display']) ? $variables['interval_display'] : 'time ago';
// If no date is sent, then return nothing.
if (empty($start_date) || empty($end_date)) {
return;
}
// Time to compare dates to.
$now = date_format(date_now(), DATE_FORMAT_UNIX);
$start = 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;
// 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)));
}
}