diff --git a/patches/2460643_incompatble_with_views_cache_2.patch b/patches/2460643_incompatble_with_views_cache_2.patch new file mode 100644 index 000000000..5cad736ce --- /dev/null +++ b/patches/2460643_incompatble_with_views_cache_2.patch @@ -0,0 +1,23 @@ +diff --git a/leaflet.module b/leaflet.module +index e72a3e9..aa9ba3c 100644 +--- a/leaflet.module ++++ b/leaflet.module +@@ -121,14 +121,16 @@ function leaflet_build_map($map, $features = array(), $height = '400px') { + ); + + // Load the leaflet library, which includes integration files. +- libraries_load('leaflet'); ++ $build['#attached']['libraries_load'][] = array( ++ 'leaflet' ++ ); + + return $build; + } + + /** + * DEPRECATED. Use leaflet_build_map() instead. +- * ++ * + * Load all Leaflet required client files and return markup for a map. + * + * @param array $map diff --git a/www7/sites/all/modules/contrib/date/.gitignore b/www7/sites/all/modules/contrib/date/.gitignore new file mode 100644 index 000000000..a0238825e --- /dev/null +++ b/www7/sites/all/modules/contrib/date/.gitignore @@ -0,0 +1,4 @@ +*.patch +*.diff +.idea/ +.idea/* diff --git a/www7/sites/all/modules/contrib/date/date.devel_generate.inc b/www7/sites/all/modules/contrib/date/date.devel_generate.inc index 47de0847c..6c61e23cf 100644 --- a/www7/sites/all/modules/contrib/date/date.devel_generate.inc +++ b/www7/sites/all/modules/contrib/date/date.devel_generate.inc @@ -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; diff --git a/www7/sites/all/modules/contrib/date/date.field.inc b/www7/sites/all/modules/contrib/date/date.field.inc index 46ea317b9..fa311224e 100644 --- a/www7/sites/all/modules/contrib/date/date.field.inc +++ b/www7/sites/all/modules/contrib/date/date.field.inc @@ -19,6 +19,7 @@ function date_field_formatter_info() { 'multiple_from' => '', 'multiple_to' => '', 'fromto' => 'both', + 'show_remaining_days' => FALSE, ), ), 'format_interval' => array( @@ -48,6 +49,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 +74,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; @@ -169,11 +172,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)) { @@ -188,6 +196,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)) { @@ -198,6 +207,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'] = $display['settings']['show_remaining_days']; $output = theme('date_display_combination', $variables); if (!empty($output)) { $element[$delta] = array('#markup' => $output); @@ -231,10 +241,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', ), @@ -250,26 +261,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, ); } @@ -294,18 +305,18 @@ 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; } diff --git a/www7/sites/all/modules/contrib/date/date.info b/www7/sites/all/modules/contrib/date/date.info index a1731de03..c6fdde552 100644 --- a/www7/sites/all/modules/contrib/date/date.info +++ b/www7/sites/all/modules/contrib/date/date.info @@ -11,10 +11,12 @@ 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 -; Information added by Drupal.org packaging script on 2014-07-29 -version = "7.x-2.8" +; Information added by Drupal.org packaging script on 2015-09-08 +version = "7.x-2.9" core = "7.x" project = "date" -datestamp = "1406653438" +datestamp = "1441727353" diff --git a/www7/sites/all/modules/contrib/date/date.install b/www7/sites/all/modules/contrib/date/date.install index 23fb07eb2..9a9b6d350 100644 --- a/www7/sites/all/modules/contrib/date/date.install +++ b/www7/sites/all/modules/contrib/date/date.install @@ -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( @@ -191,4 +204,3 @@ function date_update_7004() { field_cache_clear(); drupal_set_message(t('Date text widgets have been updated to use an increment of 1.')); } - diff --git a/www7/sites/all/modules/contrib/date/date.js b/www7/sites/all/modules/contrib/date/date.js index 2e60175fb..b58d3be65 100644 --- a/www7/sites/all/modules/contrib/date/date.js +++ b/www7/sites/all/modules/contrib/date/date.js @@ -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; } } diff --git a/www7/sites/all/modules/contrib/date/date.migrate.inc b/www7/sites/all/modules/contrib/date/date.migrate.inc index 095c789e1..ad2a56815 100644 --- a/www7/sites/all/modules/contrib/date/date.migrate.inc +++ b/www7/sites/all/modules/contrib/date/date.migrate.inc @@ -40,7 +40,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(); } @@ -129,6 +129,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; } @@ -151,6 +152,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); @@ -158,6 +160,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); @@ -165,6 +168,7 @@ class DateMigrateFieldHandler extends MigrateFieldHandler { $to = format_date($to, 'custom', 'Y-m-d\TH:i:s', $timezone); } break; + default: break; } @@ -173,12 +177,17 @@ 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; } @@ -190,6 +199,9 @@ class DateMigrateFieldHandler extends MigrateFieldHandler { return $return; } + /** + * {@inheritdoc} + */ public function fields($migration = NULL) { return array( 'timezone' => t('Timezone'), diff --git a/www7/sites/all/modules/contrib/date/date.module b/www7/sites/all/modules/contrib/date/date.module index 91c0b65ac..6f849dfad 100644 --- a/www7/sites/all/modules/contrib/date/date.module +++ b/www7/sites/all/modules/contrib/date/date.module @@ -1,8 +1,10 @@ field_name; break; + default: $bundle = field_extract_bundle($entity_type, $entity); break; @@ -40,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'; } @@ -66,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( @@ -97,7 +108,13 @@ function date_theme() { '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, @@ -130,7 +147,7 @@ function date_theme() { 'attributes' => array(), 'rdf_mapping' => NULL, 'add_rdf' => NULL, - ), + ), ), ); @@ -209,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); @@ -246,10 +265,10 @@ 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' || $format == 'r' || $format == 'c') { $dates[$processed]['formatted'] = date_format_date($date, 'custom', $format); @@ -258,10 +277,11 @@ function date_formatter_process($formatter, $entity_type, $entity, $field, $inst $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']; } } } @@ -288,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. * @@ -301,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'); @@ -394,10 +438,10 @@ 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); break; @@ -410,6 +454,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) { @@ -432,27 +477,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); } @@ -506,7 +554,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++; @@ -647,7 +695,7 @@ function date_entity_metadata_field_setter(&$entity, $name, $value, $langcode, $ } /** - * Auto creation callback for fields which contain two date values in one + * Auto creation callback for fields which contain two date values in one. */ function date_entity_metadata_struct_create($name, $property_info) { return array( @@ -658,10 +706,10 @@ function date_entity_metadata_struct_create($name, $property_info) { /** * 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'. */ @@ -683,9 +731,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. */ @@ -759,7 +807,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; } diff --git a/www7/sites/all/modules/contrib/date/date.theme b/www7/sites/all/modules/contrib/date/date.theme old mode 100644 new mode 100755 index a1f7d939e..5cec8b420 --- a/www7/sites/all/modules/contrib/date/date.theme +++ b/www7/sites/all/modules/contrib/date/date.theme @@ -77,6 +77,7 @@ function theme_date_display_combination($variables) { $microdata = $variables['microdata']; $add_microdata = $variables['add_microdata']; $precision = date_granularity_precision($field['settings']['granularity']); + $show_remaining_days = $variables['show_remaining_days']; $output = ''; @@ -121,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']; @@ -151,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 .= ''; @@ -167,6 +184,7 @@ function theme_date_display_combination($variables) { '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 @@ -186,6 +204,7 @@ function theme_date_display_combination($variables) { '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( @@ -209,6 +228,7 @@ function theme_date_display_combination($variables) { 'microdata' => $microdata, 'add_microdata' => $add_microdata, 'dates' => $dates, + 'show_remaining_days' => $show_remaining_days, )); } @@ -236,12 +256,12 @@ function template_preprocess_date_display_single(&$variables) { // 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'])) { + 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'])) { + if (!empty($variables['microdata']['value']['#attributes'])) { $variables['microdata']['value']['#attributes']['content'] = $variables['dates']['value']['formatted_iso']; } else { @@ -257,6 +277,7 @@ 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. $output = '' . $date . $timezone . ''; @@ -265,7 +286,8 @@ function theme_date_display_single($variables) { $output .= ''; } - return $output; + // Add remaining message and return. + return $output . $show_remaining_days; } /** @@ -314,6 +336,7 @@ function theme_date_display_range($variables) { $timezone = $variables['timezone']; $attributes_start = $variables['attributes_start']; $attributes_end = $variables['attributes_end']; + $show_remaining_days = $variables['show_remaining_days']; $start_date = '' . $date1 . ''; $end_date = '' . $date2 . $timezone . ''; @@ -326,10 +349,13 @@ function theme_date_display_range($variables) { } // Wrap the result with the attributes. - return t('!start-date to !end-date', array( + $output = '
' . t('!start-date to !end-date', array( '!start-date' => $start_date, '!end-date' => $end_date, - )); + )) . '
'; + + // Add remaining message and return. + return $output . $show_remaining_days; } /** @@ -375,7 +401,7 @@ function theme_date_combo($variables) { '#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(), + '#attributes' => array('class' => array('date-combo')), '#children' => $element['#children'], ); // Add marker to required date fields. @@ -396,7 +422,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') { @@ -408,4 +438,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(''. $element['#children'] .''; + + 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 '
' . $show_remaining_text . '
'; +} + /** @} End of addtogroup themeable */ diff --git a/www7/sites/all/modules/contrib/date/date_admin.inc b/www7/sites/all/modules/contrib/date/date_admin.inc index 0e32fc5a5..13da42ef5 100644 --- a/www7/sites/all/modules/contrib/date/date_admin.inc +++ b/www7/sites/all/modules/contrib/date/date_admin.inc @@ -74,6 +74,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; } @@ -127,9 +133,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 +156,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 +169,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; } @@ -191,7 +205,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 +222,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 +234,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 +250,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', ); @@ -284,6 +313,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,11 +399,18 @@ 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')); + $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')); + $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( @@ -403,6 +440,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, @@ -470,7 +514,9 @@ 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, 'year' => $checkbox_year, ); @@ -528,7 +574,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, + ), ), ), ); @@ -600,7 +648,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'), ); } diff --git a/www7/sites/all/modules/contrib/date/date_all_day/date_all_day.info b/www7/sites/all/modules/contrib/date/date_all_day/date_all_day.info index 8eeb7325c..df773e419 100644 --- a/www7/sites/all/modules/contrib/date/date_all_day/date_all_day.info +++ b/www7/sites/all/modules/contrib/date/date_all_day/date_all_day.info @@ -5,9 +5,9 @@ dependencies[] = date package = Date/Time core = 7.x -; Information added by Drupal.org packaging script on 2014-07-29 -version = "7.x-2.8" +; Information added by Drupal.org packaging script on 2015-09-08 +version = "7.x-2.9" core = "7.x" project = "date" -datestamp = "1406653438" +datestamp = "1441727353" diff --git a/www7/sites/all/modules/contrib/date/date_all_day/date_all_day.module b/www7/sites/all/modules/contrib/date/date_all_day/date_all_day.module index 93fc61bad..e79bc944d 100644 --- a/www7/sites/all/modules/contrib/date/date_all_day/date_all_day.module +++ b/www7/sites/all/modules/contrib/date/date_all_day/date_all_day.module @@ -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); + } } /** diff --git a/www7/sites/all/modules/contrib/date/date_api/date.css b/www7/sites/all/modules/contrib/date/date_api/date.css index 9b72ecdb7..e47a2ae22 100644 --- a/www7/sites/all/modules/contrib/date/date_api/date.css +++ b/www7/sites/all/modules/contrib/date/date_api/date.css @@ -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 { diff --git a/www7/sites/all/modules/contrib/date/date_api/date_api.admin.inc b/www7/sites/all/modules/contrib/date/date_api/date_api.admin.inc index 966b1a423..3c706aebf 100644 --- a/www7/sites/all/modules/contrib/date/date_api/date_api.admin.inc +++ b/www7/sites/all/modules/contrib/date/date_api/date_api.admin.inc @@ -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]; diff --git a/www7/sites/all/modules/contrib/date/date_api/date_api.info b/www7/sites/all/modules/contrib/date/date_api/date_api.info index 3ca86e65b..40fbb0566 100644 --- a/www7/sites/all/modules/contrib/date/date_api/date_api.info +++ b/www7/sites/all/modules/contrib/date/date_api/date_api.info @@ -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 2014-07-29 -version = "7.x-2.8" +; Information added by Drupal.org packaging script on 2015-09-08 +version = "7.x-2.9" core = "7.x" project = "date" -datestamp = "1406653438" +datestamp = "1441727353" diff --git a/www7/sites/all/modules/contrib/date/date_api/date_api.install b/www7/sites/all/modules/contrib/date/date_api/date_api.install index ce5b746dd..d5a68bbd7 100644 --- a/www7/sites/all/modules/contrib/date/date_api/date_api.install +++ b/www7/sites/all/modules/contrib/date/date_api/date_api.install @@ -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() { diff --git a/www7/sites/all/modules/contrib/date/date_api/date_api.module b/www7/sites/all/modules/contrib/date/date_api/date_api.module index b1be6e5f2..2a973ccf6 100644 --- a/www7/sites/all/modules/contrib/date/date_api/date_api.module +++ b/www7/sites/all/modules/contrib/date/date_api/date_api.module @@ -59,16 +59,16 @@ function date_help($path, $arg) { } if (module_exists('date_tools')) { - $output .= '

Date Tools

' . 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 .= '

Date Tools

' . 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 .= '

Date Tools

' . 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 .= '

Date Tools

' . 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 .= '

More Information

' . t('Complete documentation for the Date and Date API modules is available at http://drupal.org/node/92460.', array('@link' => 'http://drupal.org/node/262062')) . '

'; 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 Date and time 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 Date and time 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 system date formats 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; @@ -402,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]); } } @@ -458,23 +466,35 @@ class DateObject extends DateTime { $true = $this->hasGranularity() && (!$granularity || $flexible || $this->hasGranularity($granularity)); if (!$true && $granularity) { foreach ((array) $granularity as $part) { - if (!$this->hasGranularity($part) && in_array($part, array('second', 'minute', 'hour', 'day', 'month', 'year'))) { + if (!$this->hasGranularity($part) && in_array($part, 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; @@ -537,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 @@ -600,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) { @@ -609,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'); @@ -631,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': @@ -648,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'); @@ -665,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) { @@ -758,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'] !== '') { @@ -839,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; } } @@ -898,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; @@ -929,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. */ @@ -955,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; @@ -1013,7 +1078,7 @@ class DateObject extends DateTime { $sign = ($year_diff < 0) ? -1 : 1; for ($i = 1; $i <= abs($year_diff); $i++) { - date_modify($date1, (($sign > 0) ? '+': '-').'1 year'); + date_modify($date1, (($sign > 0) ? '+' : '-') . '1 year'); $week_diff += (date_iso_weeks_in_year($date1) * $sign); } return $week_diff; @@ -1060,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; } @@ -1119,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. @@ -1211,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; @@ -1248,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. @@ -1259,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)); } /** @@ -1474,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); } /** @@ -1492,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; } @@ -1533,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; } @@ -1657,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); @@ -1739,8 +1837,8 @@ function date_format_interval($date, $granularity = 2, $display_ago = TRUE) { * (optional) Optionally force time to a specific timezone, defaults to user * timezone, if set, otherwise site timezone. Defaults to NULL. * - * @param boolean $reset [optional] - * Static cache reset + * @param bool $reset + * (optional) Static cache reset. * * @return object * The current time as a date object. @@ -1831,7 +1929,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) { @@ -1860,7 +1958,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) { @@ -1952,7 +2050,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'); @@ -1986,7 +2084,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); } @@ -2094,7 +2192,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)); } /** @@ -2110,7 +2209,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)); } /** @@ -2128,8 +2228,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)); } @@ -2157,7 +2259,7 @@ function date_limit_format($format, $granularity) { $drupal_static_fast['formats'] = &drupal_static(__FUNCTION__); } $formats = &$drupal_static_fast['formats']; - $format_granularity_cid = $format .'|'. implode(',', $granularity); + $format_granularity_cid = $format . '|' . implode(',', $granularity); if (isset($formats[$format_granularity_cid])) { return $formats[$format_granularity_cid]; } @@ -2191,21 +2293,27 @@ function date_limit_format($format, $granularity) { case 'year': $regex[] = '([\-/\.,:]?\s?(? "$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'), @@ -2355,7 +2481,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 + ), + ), ); } @@ -2375,9 +2505,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(); } @@ -2404,6 +2536,7 @@ function date_get_timezone_db($handling, $timezone = NULL) { // 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 @@ -2411,6 +2544,7 @@ function date_get_timezone_db($handling, $timezone = NULL) { $timezone = date_default_timezone(); } break; + case ('none'): default: $timezone = date_default_timezone(); @@ -2465,12 +2599,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 @@ -2482,45 +2616,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); } /** @@ -2680,6 +2813,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); @@ -2687,6 +2821,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); @@ -2694,6 +2829,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; @@ -2754,15 +2890,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) { diff --git a/www7/sites/all/modules/contrib/date/date_api/date_api_elements.inc b/www7/sites/all/modules/contrib/date/date_api/date_api_elements.inc index 57e416153..fc91205ce 100644 --- a/www7/sites/all/modules/contrib/date/date_api/date_api_elements.inc +++ b/www7/sites/all/modules/contrib/date/date_api/date_api_elements.inc @@ -116,15 +116,19 @@ function date_default_date($element) { 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); @@ -256,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); @@ -264,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. */ @@ -307,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)) { @@ -323,9 +326,18 @@ function date_text_element_process($element, &$form_state, $form) { $now = date_example_date(); $element['date']['#title'] = t('Date'); $element['date']['#title_display'] = 'invisible'; - $element['date']['#description'] = ' ' . t('Format: @date', array('@date' => date_format_date(date_example_date(), 'custom', $element['#date_format']))); + $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']; @@ -341,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); @@ -349,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)) { @@ -367,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'] : ''); @@ -421,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; @@ -431,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]) : ''; @@ -449,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)) { @@ -473,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( @@ -490,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); @@ -521,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); @@ -539,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') { @@ -566,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') { @@ -573,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') { diff --git a/www7/sites/all/modules/contrib/date/date_api/date_api_ical.inc b/www7/sites/all/modules/contrib/date/date_api/date_api_ical.inc index 2ca484e7f..4911298c5 100644 --- a/www7/sites/all/modules/contrib/date/date_api/date_api_ical.inc +++ b/www7/sites/all/modules/contrib/date/date_api/date_api_ical.inc @@ -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; } diff --git a/www7/sites/all/modules/contrib/date/date_api/date_api_sql.inc b/www7/sites/all/modules/contrib/date/date_api/date_api_sql.inc index e02f05078..a95ca4efa 100644 --- a/www7/sites/all/modules/contrib/date/date_api/date_api_sql.inc +++ b/www7/sites/all/modules/contrib/date/date_api/date_api_sql.inc @@ -23,13 +23,14 @@ function date_sql_concat($array) { switch (Database::getConnection()->databaseType()) { case 'mysql': 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. @@ -61,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')"; } @@ -69,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; @@ -86,7 +89,7 @@ class date_sql_handler { /** * The object constuctor. */ - function __construct($date_type = DATE_DATETIME, $local_timezone = NULL, $offset = '+00:00') { + 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'; @@ -97,7 +100,7 @@ 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; @@ -108,6 +111,7 @@ class date_sql_handler { $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') { @@ -136,7 +140,7 @@ 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 = Database::getConnection()->databaseType(); if (!$already_set) { @@ -144,9 +148,11 @@ class date_sql_handler { case 'mysql': 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 @@ -161,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,47 +205,57 @@ class date_sql_handler { 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. @@ -254,10 +270,13 @@ class date_sql_handler { switch ($this->db_type) { case 'mysql': 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)"; } @@ -285,6 +304,7 @@ class date_sql_handler { switch ($direction) { case 'ADD': return "DATE_ADD($field, INTERVAL $count $granularity)"; + case 'SUB': return "DATE_SUB($field, INTERVAL $count $granularity)"; } @@ -294,6 +314,7 @@ class date_sql_handler { switch ($direction) { case 'ADD': return "($field + INTERVAL '$count $granularity')"; + case 'SUB': return "($field - INTERVAL '$count $granularity')"; } @@ -302,6 +323,7 @@ class date_sql_handler { switch ($direction) { case 'ADD': return "datetime($field, '+$count $granularity')"; + case 'SUB': return "datetime($field, '-$count $granularity')"; } @@ -352,6 +374,7 @@ class date_sql_handler { switch ($this->db_type) { case 'mysql': 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. @@ -395,6 +418,7 @@ class date_sql_handler { ); $format = strtr($format, $replace); return "DATE_FORMAT($field, '$format')"; + case 'pgsql': $replace = array( 'Y' => 'YYYY', @@ -421,6 +445,7 @@ class date_sql_handler { ); $format = strtr($format, $replace); return "TO_CHAR($field, '$format')"; + case 'sqlite': $replace = array( // 4 digit year number. @@ -460,6 +485,7 @@ class date_sql_handler { ); $format = strtr($format, $replace); return "strftime('$format', $field)"; + case 'sqlsrv': $replace = array( // 4 digit year number. @@ -528,18 +554,25 @@ 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) { @@ -547,6 +580,7 @@ class date_sql_handler { // 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))"; } @@ -556,6 +590,7 @@ class date_sql_handler { // 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))"; } @@ -563,6 +598,7 @@ class date_sql_handler { switch ($this->db_type) { case 'mysql': return "DAYOFYEAR($field)"; + case 'pgsql': return "EXTRACT(DOY FROM($field))"; } @@ -775,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 @@ -795,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; } @@ -840,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)) { @@ -856,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; @@ -890,26 +930,36 @@ class date_sql_handler { $formats['display'] = 'Y'; $formats['sql'] = 'Y'; break; + case 'month': $formats['display'] = date_limit_format($short, array('year', 'month')); $formats['sql'] = 'Y-m'; break; + case 'day': - $formats['display'] = date_limit_format($short, 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($short, 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($short, 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($short, 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'; @@ -927,7 +977,7 @@ class date_sql_handler { '#type' => 'radios', '#default_value' => $granularity, '#options' => $this->date_parts(), - ); + ); return $form; } @@ -1030,7 +1080,6 @@ class date_sql_handler { $direction = $results[1]; $count = $results[2]; $item = $results[3]; - $replace = array( 'now' => '@', '+' => 'P', @@ -1051,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 = ''; @@ -1137,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 diff --git a/www7/sites/all/modules/contrib/date/date_api/theme/theme.inc b/www7/sites/all/modules/contrib/date/date_api/theme/theme.inc index 032e3e933..a6aef2305 100644 --- a/www7/sites/all/modules/contrib/date/date_api/theme/theme.inc +++ b/www7/sites/all/modules/contrib/date/date_api/theme/theme.inc @@ -206,24 +206,31 @@ function theme_date_time_ago($variables) { $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) + // 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))); + } } diff --git a/www7/sites/all/modules/contrib/date/date_context/date_context.info b/www7/sites/all/modules/contrib/date/date_context/date_context.info index 1d8a551de..c521dd263 100644 --- a/www7/sites/all/modules/contrib/date/date_context/date_context.info +++ b/www7/sites/all/modules/contrib/date/date_context/date_context.info @@ -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 2014-07-29 -version = "7.x-2.8" +; Information added by Drupal.org packaging script on 2015-09-08 +version = "7.x-2.9" core = "7.x" project = "date" -datestamp = "1406653438" +datestamp = "1441727353" diff --git a/www7/sites/all/modules/contrib/date/date_context/date_context.module b/www7/sites/all/modules/contrib/date/date_context/date_context.module index 44e975acd..9b568e15f 100644 --- a/www7/sites/all/modules/contrib/date/date_context/date_context.module +++ b/www7/sites/all/modules/contrib/date/date_context/date_context.module @@ -1,5 +1,8 @@ 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() { ), ); } - diff --git a/www7/sites/all/modules/contrib/date/date_context/plugins/date_context_date_condition.inc b/www7/sites/all/modules/contrib/date/date_context/plugins/date_context_date_condition.inc index 573b03549..97c18b472 100644 --- a/www7/sites/all/modules/contrib/date/date_context/plugins/date_context_date_condition.inc +++ b/www7/sites/all/modules/contrib/date/date_context/plugins/date_context_date_condition.inc @@ -1,10 +1,20 @@ $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. More examples of relative date formats in the PHP documentation.", 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('edit-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 diff --git a/www7/sites/all/modules/contrib/date/date_elements.inc b/www7/sites/all/modules/contrib/date/date_elements.inc index 6908d967d..83697153b 100644 --- a/www7/sites/all/modules/contrib/date/date_elements.inc +++ b/www7/sites/all/modules/contrib/date/date_elements.inc @@ -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) { @@ -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]); @@ -329,9 +335,9 @@ 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($element['#description']) ? t($element['#description']) : ''; + $description = !empty($element['#description']) ? t($element['#description']) : ''; unset($element['#description']); // Give this element the right type, using a Date API @@ -347,11 +353,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'); @@ -380,8 +388,11 @@ function date_combo_element_process($element, &$form_state, $form) { 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 { @@ -404,16 +415,27 @@ function date_combo_element_process($element, &$form_state, $form) { $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'] .= ' ' . t("Empty 'End date' values will use the 'Start date' values.") . ''; + } + } + $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; @@ -428,6 +450,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) { @@ -444,6 +467,10 @@ 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']); diff --git a/www7/sites/all/modules/contrib/date/date_migrate/date_migrate.info b/www7/sites/all/modules/contrib/date/date_migrate/date_migrate.info index 690127117..b3a6bba61 100644 --- a/www7/sites/all/modules/contrib/date/date_migrate/date_migrate.info +++ b/www7/sites/all/modules/contrib/date/date_migrate/date_migrate.info @@ -4,9 +4,9 @@ core = 7.x package = Date/Time hidden = TRUE -; Information added by Drupal.org packaging script on 2014-07-29 -version = "7.x-2.8" +; Information added by Drupal.org packaging script on 2015-09-08 +version = "7.x-2.9" core = "7.x" project = "date" -datestamp = "1406653438" +datestamp = "1441727353" diff --git a/www7/sites/all/modules/contrib/date/date_migrate/date_migrate_example/date_migrate_example.info b/www7/sites/all/modules/contrib/date/date_migrate/date_migrate_example/date_migrate_example.info index 7223b763e..6d79df405 100644 --- a/www7/sites/all/modules/contrib/date/date_migrate/date_migrate_example/date_migrate_example.info +++ b/www7/sites/all/modules/contrib/date/date_migrate/date_migrate_example/date_migrate_example.info @@ -20,9 +20,9 @@ package = "Features" project = "date_migrate_example" version = "7.x-2.0" -; Information added by Drupal.org packaging script on 2014-07-29 -version = "7.x-2.8" +; Information added by Drupal.org packaging script on 2015-09-08 +version = "7.x-2.9" core = "7.x" project = "date" -datestamp = "1406653438" +datestamp = "1441727353" diff --git a/www7/sites/all/modules/contrib/date/date_migrate/date_migrate_example/date_migrate_example.migrate.inc b/www7/sites/all/modules/contrib/date/date_migrate/date_migrate_example/date_migrate_example.migrate.inc index e11dea663..216e1c543 100644 --- a/www7/sites/all/modules/contrib/date/date_migrate/date_migrate_example/date_migrate_example.migrate.inc +++ b/www7/sites/all/modules/contrib/date/date_migrate/date_migrate_example/date_migrate_example.migrate.inc @@ -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'); @@ -78,7 +78,7 @@ class DateExampleMigration extends XMLMigration { $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) + // field (Tokyo is UTC+9, no DST). $this->addFieldMapping('field_datetime', 'datetime') ->xpath('datetime'); $this->addFieldMapping('field_datetime:timezone') @@ -107,25 +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) { - $current_row->date_range_from[] = (string)$range->from[0]; - $current_row->date_range_to[] = (string)$range->to[0]; + $current_row->date_range_from[] = (string) $range->from[0]; + $current_row->date_range_to[] = (string) $range->to[0]; } - $current_row->datestamp_range_from = - (string) $current_row->xml->datestamp_range->from[0]; - $current_row->datestamp_range_to = - (string) $current_row->xml->datestamp_range->to[0]; - - $current_row->datetime_range_from = - (string) $current_row->xml->datetime_range->from[0]; - $current_row->datetime_range_to = - (string) $current_row->xml->datetime_range->to[0]; - $current_row->datetime_range_timezone = - (string) $current_row->xml->datetime_range->timezone[0]; - - $current_row->date_repeat = - (string) $current_row->xml->date_repeat->date[0]; - $current_row->date_repeat_rrule = - (string) $current_row->xml->date_repeat->rule[0]; + $current_row->datestamp_range_from + = (string) $current_row->xml->datestamp_range->from[0]; + $current_row->datestamp_range_to + = (string) $current_row->xml->datestamp_range->to[0]; + + $current_row->datetime_range_from + = (string) $current_row->xml->datetime_range->from[0]; + $current_row->datetime_range_to + = (string) $current_row->xml->datetime_range->to[0]; + $current_row->datetime_range_timezone + = (string) $current_row->xml->datetime_range->timezone[0]; + + $current_row->date_repeat + = (string) $current_row->xml->date_repeat->date[0]; + $current_row->date_repeat_rrule + = (string) $current_row->xml->date_repeat->rule[0]; } } diff --git a/www7/sites/all/modules/contrib/date/date_popup/date_popup.info b/www7/sites/all/modules/contrib/date/date_popup/date_popup.info index 271e1c68c..500c7ffb7 100644 --- a/www7/sites/all/modules/contrib/date/date_popup/date_popup.info +++ b/www7/sites/all/modules/contrib/date/date_popup/date_popup.info @@ -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 2014-07-29 -version = "7.x-2.8" +; Information added by Drupal.org packaging script on 2015-09-08 +version = "7.x-2.9" core = "7.x" project = "date" -datestamp = "1406653438" +datestamp = "1441727353" diff --git a/www7/sites/all/modules/contrib/date/date_popup/date_popup.install b/www7/sites/all/modules/contrib/date/date_popup/date_popup.install index 790a514a5..e13a4f5a2 100644 --- a/www7/sites/all/modules/contrib/date/date_popup/date_popup.install +++ b/www7/sites/all/modules/contrib/date/date_popup/date_popup.install @@ -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(). diff --git a/www7/sites/all/modules/contrib/date/date_popup/date_popup.js b/www7/sites/all/modules/contrib/date/date_popup/date_popup.js index 1847f84f2..bbf25e2df 100644 --- a/www7/sites/all/modules/contrib/date/date_popup/date_popup.js +++ b/www7/sites/all/modules/contrib/date/date_popup/date_popup.js @@ -1,62 +1,65 @@ /** * 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(); - }); - break; +(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(); + }); + 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); diff --git a/www7/sites/all/modules/contrib/date/date_popup/date_popup.module b/www7/sites/all/modules/contrib/date/date_popup/date_popup.module index a2d0ebb15..e91cc8a72 100644 --- a/www7/sites/all/modules/contrib/date/date_popup/date_popup.module +++ b/www7/sites/all/modules/contrib/date/date_popup/date_popup.module @@ -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() { @@ -94,9 +93,11 @@ function date_popup_library() { } /** - * 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,8 +385,9 @@ 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() @@ -347,26 +397,33 @@ function date_popup_process_date_part(&$element) { // Create a unique id for each set of custom settings. $id = date_popup_js_settings_id($element['#id'], 'datepicker', $settings); - // Manually build this element and set the value - this will prevent corrupting - // the parent value + // Manually build this element and set the value - + // this will prevent corrupting the parent value. $parents = array_merge($element['#parents'], array('date')); $sub_element = array( '#type' => 'textfield', '#title' => theme('date_part_label_date', array('part_type' => 'date', 'element' => $element)), '#title_display' => $element['#date_label_position'] == 'above' ? 'before' : 'invisible', - '#default_value' => $element['#value']['date'], + '#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; } @@ -377,7 +434,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': @@ -385,10 +452,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'); @@ -397,9 +468,11 @@ 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'); // 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 @@ -414,6 +487,7 @@ function date_popup_process_time_part(&$element) { 'scrollbar' => TRUE, ); break; + default: $func = ''; $settings = array(); @@ -423,8 +497,8 @@ 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', @@ -436,16 +510,22 @@ function date_popup_process_time_part(&$element) { '#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); } @@ -456,7 +536,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) { @@ -473,6 +552,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); @@ -481,9 +565,15 @@ 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. @@ -517,7 +607,7 @@ 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. */ @@ -532,8 +622,8 @@ 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'] : ''; + $datetime = trim($input['date']); + $datetime .= $has_time ? ' ' . trim($input['time']) : ''; $date = new DateObject($datetime, $element['#date_timezone'], $format); if (is_object($date)) { $date->limitGranularity($granularity); @@ -552,7 +642,7 @@ function date_popup_time_formats($with_seconds = FALSE) { return array( 'H:i:s', 'h:i:sA', - ); + ); } /** @@ -561,8 +651,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; } @@ -570,7 +669,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' @@ -588,15 +688,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; } @@ -605,9 +724,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()); @@ -621,22 +741,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'); } } @@ -645,16 +764,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', ); } @@ -667,16 +786,18 @@ 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'] = '
' . $element['#children'] . '
'; + $element['#children'] = '
' . $element['#children'] . '
'; } - return '
' . theme('form_element', $element) . '
'; + return '
' . theme('form_element', $element) . '
'; } /** @@ -707,8 +828,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), @@ -734,7 +855,7 @@ function date_popup_settings() { } EOM; - $form['#suffix'] = t('

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:

') .'
' . $css .'
'; + $form['#suffix'] = t('

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:

') . '
' . $css . '
'; return system_settings_form($form); } diff --git a/www7/sites/all/modules/contrib/date/date_popup/jquery.timeentry.pack.js b/www7/sites/all/modules/contrib/date/date_popup/jquery.timeentry.pack.js index e26c64bf6..658e7e49e 100644 --- a/www7/sites/all/modules/contrib/date/date_popup/jquery.timeentry.pack.js +++ b/www7/sites/all/modules/contrib/date/date_popup/jquery.timeentry.pack.js @@ -1,7 +1,8 @@ /* http://keith-wood.name/timeEntry.html - Time entry for jQuery v1.4.8. + Time entry for jQuery v1.5.2. Written by Keith Wood (kbwood{at}iinet.com.au) June 2007. - Dual licensed under the GPL (http://dev.jquery.com/browser/trunk/jquery/GPL-LICENSE.txt) and - MIT (http://dev.jquery.com/browser/trunk/jquery/MIT-LICENSE.txt) licenses. + Licensed under the MIT (https://github.com/jquery/jquery/blob/master/MIT-LICENSE.txt) license. Please attribute the author if you use it. */ -eval(function(p,a,c,k,e,r){e=function(c){return(c35?String.fromCharCode(c+29):c.toString(36))};if(!''.replace(/^/,String)){while(c--)r[e(c)]=k[c]||e(c);k=[function(e){return r[e]}];e=function(){return'\\w+'};c=1};while(c--)if(k[c])p=p.replace(new RegExp('\\b'+e(c)+'\\b','g'),k[c]);return p}('(r($){r 1n(){p.Y=[];p.26=[];p.26[\'\']={1c:B,1d:\':\',1o:\'\',Z:[\'3J\',\'3K\'],2C:[\'3L\',\'3M 2D\',\'3N 2D\',\'3O\',\'3P\']};p.1B={2E:\'\',1C:B,27:[1,1,1],2F:0,2G:L,2H:v,2I:v,2J:v,2K:\'3Q.3R\',1p:[20,20,8],2L:\'\',1D:[40,40,16],2M:B,28:[3S,3T],2N:v,2O:v};$.1q(p.1B,p.26[\'\'])}7 m=\'o\';$.1q(1n.2P,{1e:\'3U\',3V:r(a){1E(p.1B,a||{});u p},2Q:r(b,c){7 d=$(b);q(d.2R(p.1e)){u}7 e={};e.1r=$.1q({},c);e.E=0;e.13=0;e.14=0;e.x=0;e.w=$(b);$.y(b,m,e);7 f=p.t(e,\'2K\');7 g=p.t(e,\'3W\');7 h=p.t(e,\'1p\');7 i=p.t(e,\'2E\');7 j=(!f?v:$(\'<15 1F="3X" 2S="3Y: 3Z-41; \'+\'29: 2T(\\\'\'+f+\'\\\') 0 0 2U-2V; \'+\'2a: \'+h[0]+\'O; 2W: \'+h[1]+\'O;\'+($.K.2X&&$.K.42<\'1.9\'?\' 2Y-U: \'+h[0]+\'O; 2Y-43: \'+(h[1]-18)+\'O;\':\'\')+\'">\'));d.44(\'<15 1F="45">\').2Z(i?\'<15 1F="47">\'+i+\'\':\'\').2Z(j||\'\');d.49(p.1e).17(\'2b.o\',p.2c).17(\'4a.o\',p.30).17(\'4b.o\',p.31).17(\'4c.o\',p.32).17(\'4d.o\',p.33);q($.K.2X){d.17(\'w.o\',r(a){$.o.1f(e)})}q($.K.2d){d.17(\'4e.o\',r(a){1G(r(){$.o.1f(e)},1)})}q(p.t(e,\'2G\')&&$.1H.2e){d.2e(p.34)}q(j){j.3a(p.2f).2g(p.1I).4f(p.3b).2h(p.1I).3c(p.2i)}},4g:r(a){p.2j(a,B)},4h:r(a){p.2j(a,L)},2j:r(b,c){7 d=$.y(b,m);q(!d){u}b.3d=c;q(b.2k&&b.2k.1J.1g()==\'15\'){$.o.1K(d,b.2k,(c?5:-1))}$.o.Y=$.3e($.o.Y,r(a){u(a==b?v:a)});q(c){$.o.Y.4i(b)}},1s:r(a){u $.3f(a,p.Y)>-1},4j:r(a,b,c){7 d=$.y(a,m);q(d){q(1h b==\'1L\'){7 e=b;b={};b[e]=c}7 f=p.1i(d);1E(d.1r,b||{});q(f){p.19(d,F J(0,0,0,f[0],f[1],f[2]))}}$.y(a,m,d)},4k:r(b){$w=$(b);q(!$w.2R(p.1e)){u}$w.4l(p.1e).4m(\'.o\');q($.1H.2e){$w.4n()}p.Y=$.3e(p.Y,r(a){u(a==b?v:a)});$w.4o().4p($w);$.4q(b,m)},4r:r(a,b){7 c=$.y(a,m);q(c){p.19(c,b?(1h b==\'4s\'?F J(b.2l()):b):v)}},3g:r(a){7 b=$.y(a,m);7 c=(b?p.1i(b):v);u(!c?v:F J(0,0,0,c[0],c[1],c[2]))},4t:r(a){7 b=$.y(a,m);7 c=(b?p.1i(b):v);u(!c?0:(c[0]*4u+c[1]*2m+c[2])*3h)},2c:r(a){7 b=(a.1J&&a.1J.1g()==\'w\'?a:p);q($.o.P==b||$.o.1s(b)){$.o.1M=B;u}7 c=$.y(b,m);$.o.1M=L;$.o.P=b;$.o.1j=v;7 d=$.o.t(c,\'2N\');1E(c.1r,(d?d.1N(b,[b]):{}));$.y(b,m,c);$.o.1f(c);1G(r(){$.o.1a(c)},10)},30:r(a){$.o.1j=$.o.P;$.o.P=v},31:r(b){7 c=b.1k;7 d=$.y(c,m);q(!$.o.1M){7 e=$.o.t(d,\'1d\').G+2;d.x=0;q(c.3i!=v){1O(7 f=0;f<=I.1l(1,d.V,d.D);f++){7 g=(f!=d.D?(f*e)+2:(d.D*e)+$.o.t(d,\'1o\').G+$.o.t(d,\'Z\')[0].G);d.x=f;q(c.3i=48){u L}7 b=$.y(a.1k,m);2r(a.2q){A 9:u(a.4B?$.o.W(b,-1,L):$.o.W(b,+1,L));A 35:q(a.3m){$.o.1R(b,\'\')}N{b.x=I.1l(1,b.V,b.D);$.o.R(b,0)}C;A 36:q(a.3m){$.o.19(b)}N{b.x=0;$.o.R(b,0)}C;A 37:$.o.W(b,-1,B);C;A 38:$.o.R(b,+1);C;A 39:$.o.W(b,+1,B);C;A 40:$.o.R(b,-1);C;A 46:$.o.1R(b,\'\');C}u B},33:r(a){7 b=4C.4D(a.3n==4E?a.2q:a.3n);q(b<\' \'){u L}7 c=$.y(a.1k,m);$.o.3o(c,b);u B},34:r(a,b){q($.o.1s(a.1k)){u}b=($.K.3p?-b/I.1S(b):($.K.2s?b/I.1S(b):b));7 c=$.y(a.1k,m);c.w.2b();q(!c.w.T()){$.o.1f(c)}$.o.R(c,b);a.4F()},3b:r(b){7 c=$.o.1b(b);7 d=$.y($.o.1m(c),m);7 e=$.o.t(d,\'2L\');q(e){d.1T=L;7 f=$(c).1Q();7 g=v;$(c).3q().2t(r(){7 a=$(p);q(a.1v(\'1w\')==\'4G\'||a.1v(\'1w\')==\'3r\'){g=a.1Q()}u!g});7 h=$.o.t(d,\'1p\');7 i=$.o.t(d,\'1D\');$(\'<3s 1F="4H" 2S="1w: 3r; U: \'+(f.U-(i[0]-h[0])/2-(g?g.U:0))+\'O; 1x: \'+(f.1x-(i[1]-h[1])/2-(g?g.1x:0))+\'O; 2a: \'+i[0]+\'O; 2W: \'+i[1]+\'O; 29: 4I 2T(\'+e+\') 2U-2V 2u 2u; z-4J: 10;">\').3a($.o.2f).2g($.o.1I).2h($.o.3t).3c($.o.2i).4K(c)}},1m:r(a){u $(a).4L(\'.\'+$.o.1e)[0]},2i:r(a){7 b=$.o.1b(a);7 c=$.y($.o.1m(b),m);b.4M=$.o.t(c,\'2C\')[$.o.2v(c,a)]},2f:r(a){7 b=$.o.1b(a);7 c=$.o.1m(b);q($.o.1s(c)){u}q(c==$.o.1j){$.o.P=c;$.o.1j=v}7 d=$.y(c,m);$.o.2c(c);7 e=$.o.2v(d,a);$.o.1K(d,b,e);$.o.2w(d,e);$.o.X=v;$.o.1U=L;7 f=$.o.t(d,\'28\');q(e>=3&&f[0]){$.o.X=1G(r(){$.o.2x(d,e)},f[0]);$(b).3u(\'2h\',$.o.2y).3u(\'2g\',$.o.2y)}},2w:r(a,b){q(!a.w.T()){$.o.1f(a)}2r(b){A 0:p.19(a);C;A 1:p.W(a,-1,B);C;A 2:p.W(a,+1,B);C;A 3:p.R(a,+1);C;A 4:p.R(a,-1);C}},2x:r(a,b){q(!$.o.X){u}$.o.P=$.o.1j;p.2w(a,b);p.X=1G(r(){$.o.2x(a,b)},p.t(a,\'28\')[1])},2y:r(a){4N($.o.X);$.o.X=v},3t:r(a){$.o.X=v;7 b=$.o.1b(a);7 c=$.o.1m(b);7 d=$.y(c,m);$(b).4O();d.1T=B},1I:r(a){$.o.X=v;7 b=$.o.1b(a);7 c=$.o.1m(b);7 d=$.y(c,m);q(!$.o.1s(c)){$.o.1K(d,b,-1)}q($.o.1U){$.o.P=$.o.1j}q($.o.P&&$.o.1U){$.o.1a(d)}$.o.1U=B},1b:r(a){u a.1k||a.3j},2v:r(a,b){7 c=p.1b(b);7 d=($.K.3p||$.K.2s?$.o.3v(c):$(c).1Q());7 e=($.K.2s?$.o.3w(c):[1t.2n.1u||1t.3x.1u,1t.2n.1V||1t.3x.1V]);7 f=p.t(a,\'2M\');7 g=(f?3y:b.3k+e[0]-d.U-($.K.2d?2:0));7 h=b.4P+e[1]-d.1x-($.K.2d?2:0);7 i=p.t(a,(a.1T?\'1D\':\'1p\'));7 j=(f?3y:i[0]-1-g);7 k=i[1]-1-h;q(i[2]>0&&I.1S(g-j)<=i[2]&&I.1S(h-k)<=i[2]){u 0}7 l=I.3z(g,h,j,k);u(l==g?1:(l==j?2:(l==h?3:4)))},1K:r(a,b,c){$(b).1v(\'29-1w\',\'-\'+((c+1)*p.t(a,(a.1T?\'1D\':\'1p\'))[0])+\'O 2u\')},3v:r(a){7 b=1W=0;q(a.3A){b=a.2o;1W=a.3B;2z(a=a.3A){7 c=b;b+=a.2o;q(b<0){b=c}1W+=a.3B}}u{U:b,1x:1W}},3w:r(a){7 b=B;$(a).3q().2t(r(){b|=$(p).1v(\'1w\')==\'4Q\'});q(b){u[0,0]}7 c=a.1u;7 d=a.1V;2z(a=a.4R){c+=a.1u||0;d+=a.1V||0}u[c,d]},t:r(a,b){u(a.1r[b]!=v?a.1r[b]:$.o.1B[b])},1f:r(a){7 b=p.1i(a);7 c=p.t(a,\'1C\');q(b){a.E=b[0];a.13=b[1];a.14=b[2]}N{7 d=p.1y(a);a.E=d[0];a.13=d[1];a.14=(c?d[2]:0)}a.V=(c?2:-1);a.D=(p.t(a,\'1c\')?-1:(c?3:2));a.1X=\'\';a.x=I.1l(0,I.3z(I.1l(1,a.V,a.D),p.t(a,\'2F\')));q(a.w.T()!=\'\'){p.2A(a)}},1i:r(a,b){b=b||a.w.T();7 c=p.t(a,\'1d\');7 d=b.4S(c);q(c==\'\'&&b!=\'\'){d[0]=b.1z(0,2);d[1]=b.1z(2,4);d[2]=b.1z(4,6)}7 e=p.t(a,\'Z\');7 f=p.t(a,\'1c\');q(d.G>=2){7 g=!f&&(b.3C(e[0])>-1);7 h=!f&&(b.3C(e[1])>-1);7 i=Q(d[0],10);i=(2B(i)?0:i);i=((g||h)&&i==12?0:i)+(h?12:0);7 j=Q(d[1],10);j=(2B(j)?0:j);7 k=(d.G>=3?Q(d[2],10):0);k=(2B(k)||!p.t(a,\'1C\')?0:k);u p.1y(a,[i,j,k])}u v},1y:r(a,b){7 c=(b!=v);q(!c){7 d=p.1A(a,p.t(a,\'2H\'))||F J();b=[d.1Y(),d.1Z(),d.21()]}7 e=B;7 f=p.t(a,\'27\');1O(7 i=0;i1){b[i]=I.4T(b[i]/f[i])*f[i];e=L}}u b},2A:r(a){7 b=p.t(a,\'1c\');7 c=p.t(a,\'1d\');7 d=(p.22(b?a.E:((a.E+11)%12)+1)+c+p.22(a.13)+(p.t(a,\'1C\')?c+p.22(a.14):\'\')+(b?\'\':p.t(a,\'1o\')+p.t(a,\'Z\')[(a.E<12?0:1)]));p.1R(a,d);p.1a(a)},1a:r(a){7 b=a.w[0];q(a.w.4U(\':4V\')||$.o.P!=b){u}7 c=p.t(a,\'1d\');7 d=c.G+2;7 e=(a.x!=a.D?(a.x*d):(a.D*d)-c.G+p.t(a,\'1o\').G);7 f=e+(a.x!=a.D?2:p.t(a,\'Z\')[0].G);q(b.3D){b.3D(e,f)}N q(b.1P){7 g=b.1P();g.4W(\'2p\',e);g.3l(\'2p\',f-a.w.T().G);g.4X()}q(!b.3d){b.2b()}},22:r(a){u(a<10?\'0\':\'\')+a},1R:r(a,b){q(b!=a.w.T()){a.w.T(b).4Y(\'4Z\')}},W:r(a,b,c){7 d=(a.w.T()==\'\'||a.x==(b==-1?0:I.1l(1,a.V,a.D)));q(!d){a.x+=b}p.1a(a);a.1X=\'\';$.y(a.w[0],m,a);u(d&&c)},R:r(a,b){q(a.w.T()==\'\'){b=0}7 c=p.t(a,\'27\');p.19(a,F J(0,0,0,a.E+(a.x==0?b*c[0]:0)+(a.x==a.D?b*12:0),a.13+(a.x==1?b*c[1]:0),a.14+(a.x==a.V?b*c[2]:0)))},19:r(a,b){b=p.1A(a,b);7 c=p.1y(a,b?[b.1Y(),b.1Z(),b.21()]:v);b=F J(0,0,0,c[0],c[1],c[2]);7 b=p.25(b);7 d=p.25(p.1A(a,p.t(a,\'2I\')));7 e=p.25(p.1A(a,p.t(a,\'2J\')));b=(d&&be?e:b));7 f=p.t(a,\'2O\');q(f){b=f.1N(a.w[0],[p.3g(a.w[0]),b,d,e])}a.E=b.1Y();a.13=b.1Z();a.14=b.21();p.2A(a);$.y(a.w[0],m,a)},25:r(a){q(!a){u v}a.50(51);a.52(0);a.53(0);u a},1A:r(i,j){7 k=r(a){7 b=F J();b.54(b.2l()+a*3h);u b};7 l=r(a){7 b=$.o.1i(i,a);7 c=F J();7 d=(b?b[0]:c.1Y());7 e=(b?b[1]:c.1Z());7 f=(b?b[2]:c.21());q(!b){7 g=/([+-]?[0-9]+)\\s*(s|S|m|M|h|H)?/g;7 h=g.3E(a);2z(h){2r(h[2]||\'s\'){A\'s\':A\'S\':f+=Q(h[1],10);C;A\'m\':A\'M\':e+=Q(h[1],10);C;A\'h\':A\'H\':d+=Q(h[1],10);C}h=g.3E(a)}}c=F J(0,0,10,d,e,f,0);q(/^!/.55(a)){q(c.3F()>10){c=F J(0,0,10,23,59,59)}N q(c.3F()<10){c=F J(0,0,10,0,0,0)}}u c};u(j?(1h j==\'1L\'?l(j):(1h j==\'56\'?k(j):j)):v)},3o:r(a,b){q(b==p.t(a,\'1d\')){p.W(a,+1,B)}N q(b>=\'0\'&&b<=\'9\'){7 c=Q(b,10);7 d=Q(a.1X+b,10);7 e=p.t(a,\'1c\');7 f=(a.x!=0?a.E:(e?(d<24?d:c):(d>=1&&d<=12?d:(c>0?c:a.E))%12+(a.E>=12?12:0)));7 g=(a.x!=1?a.13:(d<2m?d:c));7 h=(a.x!=a.V?a.14:(d<2m?d:c));7 i=p.1y(a,[f,g,h]);p.19(a,F J(0,0,0,i[0],i[1],i[2]));a.1X=b}N q(!p.t(a,\'1c\')){b=b.1g();7 j=p.t(a,\'Z\');q((b==j[0].1z(0,1).1g()&&a.E>=12)||(b==j[1].1z(0,1).1g()&&a.E<12)){7 k=a.x;a.x=a.D;p.R(a,+1);a.x=k;p.1a(a)}}}});r 1E(a,b){$.1q(a,b);1O(7 c 57 b){q(b[c]==v){a[c]=v}}u a}7 n=[\'58\',\'2l\',\'5a\'];$.1H.o=r(c){7 d=5b.2P.5c.5d(5e,1);q(1h c==\'1L\'&&$.3f(c,n)>-1){u $.o[\'3G\'+c+\'1n\'].1N($.o,[p[0]].3H(d))}u p.2t(r(){7 a=p.1J.1g();q(a==\'w\'){q(1h c==\'1L\'){$.o[\'3G\'+c+\'1n\'].1N($.o,[p].3H(d))}N{7 b=($.1H.3I?$(p).3I():{});$.o.2Q(p,$.1q(b,c))}}})};$.o=F 1n()})(5f);',62,326,'|||||||var|||||||||||||||||timeEntry|this|if|function||_get|return|null|input|_field|data||case|false|break|_ampmField|_selectedHour|new|length||Math|Date|browser|true||else|px|_lastInput|parseInt|_adjustField||val|left|_secondField|_changeField|_timer|_disabledInputs|ampmNames||||_selectedMinute|_selectedSecond|span||bind||_setTime|_showField|_getSpinnerTarget|show24Hours|separator|markerClassName|_parseTime|toLowerCase|typeof|_extractTime|_blurredInput|target|max|_getInput|TimeEntry|ampmPrefix|spinnerSize|extend|options|_isDisabledTimeEntry|document|scrollLeft|css|position|top|_constrainTime|substring|_determineTime|_defaults|showSeconds|spinnerBigSize|extendRemove|class|setTimeout|fn|_endSpinner|nodeName|_changeSpinner|string|_focussed|apply|for|createTextRange|offset|_setValue|abs|_expanded|_handlingSpinner|scrollTop|curTop|_lastChr|getHours|getMinutes||getSeconds|_formatNumber|||_normaliseTime|regional|timeSteps|spinnerRepeat|background|width|focus|_doFocus|msie|mousewheel|_handleSpinner|mouseup|mouseout|_describeSpinner|_enableDisable|nextSibling|getTime|60|documentElement|offsetLeft|character|keyCode|switch|safari|each|0px|_getSpinnerRegion|_actionSpinner|_repeatSpinner|_releaseSpinner|while|_showTime|isNaN|spinnerTexts|field|appendText|initialField|useMouseWheel|defaultTime|minTime|maxTime|spinnerImage|spinnerBigImage|spinnerIncDecOnly|beforeShow|beforeSetTime|prototype|_connectTimeEntry|hasClass|style|url|no|repeat|height|mozilla|padding|after|_doBlur|_doClick|_doKeyDown|_doKeyPress|_doMouseWheel||||||mousedown|_expandSpinner|mousemove|disabled|map|inArray|_getTimeTimeEntry|1000|selectionStart|srcElement|clientX|moveEnd|ctrlKey|charCode|_handleKeyPress|opera|parents|absolute|div|_endExpand|one|_findPos|_findScroll|body|99|min|offsetParent|offsetTop|indexOf|setSelectionRange|exec|getDate|_|concat|metadata|AM|PM|Now|Previous|Next|Increment|Decrement|spinnerDefault|png|500|250|hasTimeEntry|setDefaults|spinnerText|timeEntry_control|display|inline||block|version|bottom|wrap|timeEntry_wrap||timeEntry_append||addClass|blur|click|keydown|keypress|paste|mouseover|_enableTimeEntry|_disableTimeEntry|push|_changeTimeEntry|_destroyTimeEntry|removeClass|unbind|unmousewheel|parent|replaceWith|removeData|_setTimeTimeEntry|object|_getOffsetTimeEntry|3600|thin|medium|thick|border|collapse|boundingWidth|shiftKey|String|fromCharCode|undefined|preventDefault|relative|timeEntry_expand|transparent|index|insertAfter|siblings|title|clearTimeout|remove|clientY|fixed|parentNode|split|round|is|hidden|moveStart|select|trigger|change|setFullYear|1900|setMonth|setDate|setTime|test|number|in|getOffset||isDisabled|Array|slice|call|arguments|jQuery'.split('|'),0,{})) \ No newline at end of file +/* jshint ignore:start */ +(function($){function TimeEntry(){this._disabledInputs=[];this.regional=[];this.regional['']={show24Hours:false,separator:':',ampmPrefix:'',ampmNames:['AM','PM'],spinnerTexts:['Now','Previous field','Next field','Increment','Decrement']};this._defaults={appendText:'',showSeconds:false,timeSteps:[1,1,1],initialField:0,noSeparatorEntry:false,useMouseWheel:true,defaultTime:null,minTime:null,maxTime:null,spinnerImage:'spinnerDefault.png',spinnerSize:[20,20,8],spinnerBigImage:'',spinnerBigSize:[40,40,16],spinnerIncDecOnly:false,spinnerRepeat:[500,250],beforeShow:null,beforeSetTime:null};$.extend(this._defaults,this.regional[''])}$.extend(TimeEntry.prototype,{markerClassName:'hasTimeEntry',propertyName:'timeEntry',_appendClass:'timeEntry_append',_controlClass:'timeEntry_control',_expandClass:'timeEntry_expand',setDefaults:function(a){$.extend(this._defaults,a||{});return this},_attachPlugin:function(b,c){var d=$(b);if(d.hasClass(this.markerClassName)){return}var e={options:$.extend({},this._defaults,c),input:d,_field:0,_selectedHour:0,_selectedMinute:0,_selectedSecond:0};d.data(this.propertyName,e).addClass(this.markerClassName).bind('focus.'+this.propertyName,this._doFocus).bind('blur.'+this.propertyName,this._doBlur).bind('click.'+this.propertyName,this._doClick).bind('keydown.'+this.propertyName,this._doKeyDown).bind('keypress.'+this.propertyName,this._doKeyPress).bind('paste.'+this.propertyName,function(a){setTimeout(function(){n._parseTime(e)},1)});this._optionPlugin(b,c)},_optionPlugin:function(a,b,c){a=$(a);var d=a.data(this.propertyName);if(!b||(typeof b=='string'&&c==null)){var e=b;b=(d||{}).options;return(b&&e?b[e]:b)}if(!a.hasClass(this.markerClassName)){return}b=b||{};if(typeof b=='string'){var e=b;b={};b[e]=c}var f=this._extractTime(d);$.extend(d.options,b);d._field=0;if(f){this._setTime(d,new Date(0,0,0,f[0],f[1],f[2]))}a.next('span.'+this._appendClass).remove();a.parent().find('span.'+this._controlClass).remove();if($.fn.mousewheel){a.unmousewheel()}var g=(!d.options.spinnerImage?null:$(''));a.after(d.options.appendText?''+d.options.appendText+'':'').after(g||'');if(d.options.useMouseWheel&&$.fn.mousewheel){a.mousewheel(this._doMouseWheel)}if(g){g.mousedown(this._handleSpinner).mouseup(this._endSpinner).mouseover(this._expandSpinner).mouseout(this._endSpinner).mousemove(this._describeSpinner)}},_enablePlugin:function(a){this._enableDisable(a,false)},_disablePlugin:function(a){this._enableDisable(a,true)},_enableDisable:function(b,c){var d=$.data(b,this.propertyName);if(!d){return}b.disabled=c;if(b.nextSibling&&b.nextSibling.nodeName.toLowerCase()=='span'){n._changeSpinner(d,b.nextSibling,(c?5:-1))}n._disabledInputs=$.map(n._disabledInputs,function(a){return(a==b?null:a)});if(c){n._disabledInputs.push(b)}},_isDisabledPlugin:function(a){return $.inArray(a,this._disabledInputs)>-1},_destroyPlugin:function(b){b=$(b);if(!b.hasClass(this.markerClassName)){return}b.removeClass(this.markerClassName).removeData(this.propertyName).unbind('.'+this.propertyName);if($.fn.mousewheel){b.unmousewheel()}this._disabledInputs=$.map(this._disabledInputs,function(a){return(a==b[0]?null:a)});b.siblings('.'+this._appendClass+',.'+this._controlClass).remove()},_setTimePlugin:function(a,b){var c=$.data(a,this.propertyName);if(c){if(b===null||b===''){c.input.val('')}else{this._setTime(c,b?(typeof b=='object'?new Date(b.getTime()):b):null)}}},_getTimePlugin:function(a){var b=$.data(a,this.propertyName);var c=(b?this._extractTime(b):null);return(!c?null:new Date(0,0,0,c[0],c[1],c[2]))},_getOffsetPlugin:function(a){var b=$.data(a,this.propertyName);var c=(b?this._extractTime(b):null);return(!c?0:(c[0]*3600+c[1]*60+c[2])*1000)},_doFocus:function(a){var b=(a.nodeName&&a.nodeName.toLowerCase()=='input'?a:this);if(n._lastInput==b||n._isDisabledPlugin(b)){n._focussed=false;return}var c=$.data(b,n.propertyName);n._focussed=true;n._lastInput=b;n._blurredInput=null;$.extend(c.options,($.isFunction(c.options.beforeShow)?c.options.beforeShow.apply(b,[b]):{}));n._parseTime(c);setTimeout(function(){n._showField(c)},10)},_doBlur:function(a){n._blurredInput=n._lastInput;n._lastInput=null},_doClick:function(b){var c=b.target;var d=$.data(c,n.propertyName);var e=d._field;if(!n._focussed){var f=d.options.separator.length+2;d._field=0;if(c.selectionStart!=null){for(var g=0;g<=Math.max(1,d._secondField,d._ampmField);g++){var h=(g!=d._ampmField?(g*f)+2:(d._ampmField*f)+d.options.ampmPrefix.length+d.options.ampmNames[0].length);d._field=g;if(c.selectionStart=48){return true}var b=$.data(a.target,n.propertyName);switch(a.keyCode){case 9:return(a.shiftKey?n._changeField(b,-1,true):n._changeField(b,+1,true));case 35:if(a.ctrlKey){n._setValue(b,'')}else{b._field=Math.max(1,b._secondField,b._ampmField);n._adjustField(b,0)}break;case 36:if(a.ctrlKey){n._setTime(b)}else{b._field=0;n._adjustField(b,0)}break;case 37:n._changeField(b,-1,false);break;case 38:n._adjustField(b,+1);break;case 39:n._changeField(b,+1,false);break;case 40:n._adjustField(b,-1);break;case 46:n._setValue(b,'');break;default:return true}return false},_doKeyPress:function(a){var b=String.fromCharCode(a.charCode==undefined?a.keyCode:a.charCode);if(b<' '){return true}var c=$.data(a.target,n.propertyName);n._handleKeyPress(c,b);return false},_doMouseWheel:function(a,b){if(n._isDisabledPlugin(a.target)){return}var c=$.data(a.target,n.propertyName);c.input.focus();if(!c.input.val()){n._parseTime(c)}n._adjustField(c,b);a.preventDefault()},_expandSpinner:function(b){var c=n._getSpinnerTarget(b);var d=$.data(n._getInput(c),n.propertyName);if(n._isDisabledPlugin(d.input[0])){return}if(d.options.spinnerBigImage){d._expanded=true;var e=$(c).offset();var f=null;$(c).parents().each(function(){var a=$(this);if(a.css('position')=='relative'||a.css('position')=='absolute'){f=a.offset()}return!f});$('
').mousedown(n._handleSpinner).mouseup(n._endSpinner).mouseout(n._endExpand).mousemove(n._describeSpinner).insertAfter(c)}},_getInput:function(a){return $(a).siblings('.'+n.markerClassName)[0]},_describeSpinner:function(a){var b=n._getSpinnerTarget(a);var c=$.data(n._getInput(b),n.propertyName);b.title=c.options.spinnerTexts[n._getSpinnerRegion(c,a)]},_handleSpinner:function(a){var b=n._getSpinnerTarget(a);var c=n._getInput(b);if(n._isDisabledPlugin(c)){return}if(c==n._blurredInput){n._lastInput=c;n._blurredInput=null}var d=$.data(c,n.propertyName);n._doFocus(c);var e=n._getSpinnerRegion(d,a);n._changeSpinner(d,b,e);n._actionSpinner(d,e);n._timer=null;n._handlingSpinner=true;if(e>=3&&d.options.spinnerRepeat[0]){n._timer=setTimeout(function(){n._repeatSpinner(d,e)},d.options.spinnerRepeat[0]);$(b).one('mouseout',n._releaseSpinner).one('mouseup',n._releaseSpinner)}},_actionSpinner:function(a,b){if(!a.input.val()){n._parseTime(a)}switch(b){case 0:this._setTime(a);break;case 1:this._changeField(a,-1,false);break;case 2:this._changeField(a,+1,false);break;case 3:this._adjustField(a,+1);break;case 4:this._adjustField(a,-1);break}},_repeatSpinner:function(a,b){if(!n._timer){return}n._lastInput=n._blurredInput;this._actionSpinner(a,b);this._timer=setTimeout(function(){n._repeatSpinner(a,b)},a.options.spinnerRepeat[1])},_releaseSpinner:function(a){clearTimeout(n._timer);n._timer=null},_endExpand:function(a){n._timer=null;var b=n._getSpinnerTarget(a);var c=n._getInput(b);var d=$.data(c,n.propertyName);$(b).remove();d._expanded=false},_endSpinner:function(a){n._timer=null;var b=n._getSpinnerTarget(a);var c=n._getInput(b);var d=$.data(c,n.propertyName);if(!n._isDisabledPlugin(c)){n._changeSpinner(d,b,-1)}if(n._handlingSpinner){n._lastInput=n._blurredInput}if(n._lastInput&&n._handlingSpinner){n._showField(d)}n._handlingSpinner=false},_getSpinnerTarget:function(a){return a.target||a.srcElement},_getSpinnerRegion:function(a,b){var c=this._getSpinnerTarget(b);var d=$(c).offset();var e=[document.documentElement.scrollLeft||document.body.scrollLeft,document.documentElement.scrollTop||document.body.scrollTop];var f=(a.options.spinnerIncDecOnly?99:b.clientX+e[0]-d.left);var g=b.clientY+e[1]-d.top;var h=a.options[a._expanded?'spinnerBigSize':'spinnerSize'];var i=(a.options.spinnerIncDecOnly?99:h[0]-1-f);var j=h[1]-1-g;if(h[2]>0&&Math.abs(f-i)<=h[2]&&Math.abs(g-j)<=h[2]){return 0}var k=Math.min(f,g,i,j);return(k==f?1:(k==i?2:(k==g?3:4)))},_changeSpinner:function(a,b,c){$(b).css('background-position','-'+((c+1)*a.options[a._expanded?'spinnerBigSize':'spinnerSize'][0])+'px 0px')},_parseTime:function(a){var b=this._extractTime(a);if(b){a._selectedHour=b[0];a._selectedMinute=b[1];a._selectedSecond=b[2]}else{var c=this._constrainTime(a);a._selectedHour=c[0];a._selectedMinute=c[1];a._selectedSecond=(a.options.showSeconds?c[2]:0)}a._secondField=(a.options.showSeconds?2:-1);a._ampmField=(a.options.show24Hours?-1:(a.options.showSeconds?3:2));a._lastChr='';a._field=Math.max(0,Math.min(Math.max(1,a._secondField,a._ampmField),a.options.initialField));if(a.input.val()!=''){this._showTime(a)}},_extractTime:function(a,b){b=b||a.input.val();var c=b.split(a.options.separator);if(a.options.separator==''&&b!=''){c[0]=b.substring(0,2);c[1]=b.substring(2,4);c[2]=b.substring(4,6)}if(c.length>=2){var d=!a.options.show24Hours&&(b.indexOf(a.options.ampmNames[0])>-1);var e=!a.options.show24Hours&&(b.indexOf(a.options.ampmNames[1])>-1);var f=parseInt(c[0],10);f=(isNaN(f)?0:f);f=((d||e)&&f==12?0:f)+(e?12:0);var g=parseInt(c[1],10);g=(isNaN(g)?0:g);var h=(c.length>=3?parseInt(c[2],10):0);h=(isNaN(h)||!a.options.showSeconds?0:h);return this._constrainTime(a,[f,g,h])}return null},_constrainTime:function(a,b){var c=(b!=null);if(!c){var d=this._determineTime(a.options.defaultTime,a)||new Date();b=[d.getHours(),d.getMinutes(),d.getSeconds()]}var e=false;for(var i=0;i1){b[i]=Math.round(b[i]/a.options.timeSteps[i])*a.options.timeSteps[i];e=true}}return b},_showTime:function(a){var b=(this._formatNumber(a.options.show24Hours?a._selectedHour:((a._selectedHour+11)%12)+1)+a.options.separator+this._formatNumber(a._selectedMinute)+(a.options.showSeconds?a.options.separator+this._formatNumber(a._selectedSecond):'')+(a.options.show24Hours?'':a.options.ampmPrefix+a.options.ampmNames[(a._selectedHour<12?0:1)]));this._setValue(a,b);this._showField(a)},_showField:function(a){var b=a.input[0];if(a.input.is(':hidden')||n._lastInput!=b){return}var c=a.options.separator.length+2;var d=(a._field!=a._ampmField?(a._field*c):(a._ampmField*c)-a.options.separator.length+a.options.ampmPrefix.length);var e=d+(a._field!=a._ampmField?2:a.options.ampmNames[0].length);if(b.setSelectionRange){b.setSelectionRange(d,e)}else if(b.createTextRange){var f=b.createTextRange();f.moveStart('character',d);f.moveEnd('character',e-a.input.val().length);f.select()}if(!b.disabled){b.focus()}},_formatNumber:function(a){return(a<10?'0':'')+a},_setValue:function(a,b){if(b!=a.input.val()){a.input.val(b).trigger('change')}},_changeField:function(a,b,c){var d=(a.input.val()==''||a._field==(b==-1?0:Math.max(1,a._secondField,a._ampmField)));if(!d){a._field+=b}this._showField(a);a._lastChr='';return(d&&c)},_adjustField:function(a,b){if(a.input.val()==''){b=0}this._setTime(a,new Date(0,0,0,a._selectedHour+(a._field==0?b*a.options.timeSteps[0]:0)+(a._field==a._ampmField?b*12:0),a._selectedMinute+(a._field==1?b*a.options.timeSteps[1]:0),a._selectedSecond+(a._field==a._secondField?b*a.options.timeSteps[2]:0)))},_setTime:function(a,b){b=this._determineTime(b,a);var c=this._constrainTime(a,b?[b.getHours(),b.getMinutes(),b.getSeconds()]:null);b=new Date(0,0,0,c[0],c[1],c[2]);var b=this._normaliseTime(b);var d=this._normaliseTime(this._determineTime(a.options.minTime,a));var e=this._normaliseTime(this._determineTime(a.options.maxTime,a));if(d&&e&&d>e){if(be){b=(Math.abs(b-d)e?e:b))}if($.isFunction(a.options.beforeSetTime)){b=a.options.beforeSetTime.apply(a.input[0],[this._getTimePlugin(a.input[0]),b,d,e])}a._selectedHour=b.getHours();a._selectedMinute=b.getMinutes();a._selectedSecond=b.getSeconds();this._showTime(a)},_determineTime:function(i,j){var k=function(a){var b=new Date();b.setTime(b.getTime()+a*1000);return b};var l=function(a){var b=n._extractTime(j,a);var c=new Date();var d=(b?b[0]:c.getHours());var e=(b?b[1]:c.getMinutes());var f=(b?b[2]:c.getSeconds());if(!b){var g=/([+-]?[0-9]+)\s*(s|S|m|M|h|H)?/g;var h=g.exec(a);while(h){switch(h[2]||'s'){case's':case'S':f+=parseInt(h[1],10);break;case'm':case'M':e+=parseInt(h[1],10);break;case'h':case'H':d+=parseInt(h[1],10);break}h=g.exec(a)}}c=new Date(0,0,10,d,e,f,0);if(/^!/.test(a)){if(c.getDate()>10){c=new Date(0,0,10,23,59,59)}else if(c.getDate()<10){c=new Date(0,0,10,0,0,0)}}return c};return(i?(typeof i=='string'?l(i):(typeof i=='number'?k(i):i)):null)},_normaliseTime:function(a){if(!a){return null}a.setFullYear(1900);a.setMonth(0);a.setDate(0);return a},_handleKeyPress:function(a,b){if(b==a.options.separator){this._changeField(a,+1,false)}else if(b>='0'&&b<='9'){var c=parseInt(b,10);var d=parseInt(a._lastChr+b,10);var e=(a._field!=0?a._selectedHour:(a.options.show24Hours?(d<24?d:c):(d>=1&&d<=12?d:(c>0?c:a._selectedHour))%12+(a._selectedHour>=12?12:0)));var f=(a._field!=1?a._selectedMinute:(d<60?d:c));var g=(a._field!=a._secondField?a._selectedSecond:(d<60?d:c));var h=this._constrainTime(a,[e,f,g]);this._setTime(a,new Date(0,0,0,h[0],h[1],h[2]));if(a.options.noSeparatorEntry&&a._lastChr){this._changeField(a,+1,false)}else{a._lastChr=b}}else if(!a.options.show24Hours){b=b.toLowerCase();if((b==a.options.ampmNames[0].substring(0,1).toLowerCase()&&a._selectedHour>=12)||(b==a.options.ampmNames[1].substring(0,1).toLowerCase()&&a._selectedHour<12)){var i=a._field;a._field=a._ampmField;this._adjustField(a,+1);a._field=i;this._showField(a)}}}});var m=['getOffset','getTime','isDisabled'];function isNotChained(a,b){if(a=='option'&&(b.length==0||(b.length==1&&typeof b[0]=='string'))){return true}return $.inArray(a,m)>-1}$.fn.timeEntry=function(b){var c=Array.prototype.slice.call(arguments,1);if(isNotChained(b,c)){return n['_'+b+'Plugin'].apply(n,[this[0]].concat(c))}return this.each(function(){if(typeof b=='string'){if(!n['_'+b+'Plugin']){throw'Unknown command: '+b;}n['_'+b+'Plugin'].apply(n,[this].concat(c))}else{var a=($.fn.metadata?$(this).metadata():{});n._attachPlugin(this,$.extend({},a,b||{}))}})};var n=$.timeEntry=new TimeEntry()})(jQuery); +/* jshint ignore:end */ \ No newline at end of file diff --git a/www7/sites/all/modules/contrib/date/date_repeat.inc b/www7/sites/all/modules/contrib/date/date_repeat.inc index 2df03dc85..046d8aeeb 100644 --- a/www7/sites/all/modules/contrib/date/date_repeat.inc +++ b/www7/sites/all/modules/contrib/date/date_repeat.inc @@ -2,5 +2,6 @@ /** * @file * Empty file to avoid fatal error if it doesn't exist. + * * Formerly the Date Repeat field code. - */ \ No newline at end of file + */ diff --git a/www7/sites/all/modules/contrib/date/date_repeat/date_repeat.info b/www7/sites/all/modules/contrib/date/date_repeat/date_repeat.info index 695a2fd97..b6afcd471 100644 --- a/www7/sites/all/modules/contrib/date/date_repeat/date_repeat.info +++ b/www7/sites/all/modules/contrib/date/date_repeat/date_repeat.info @@ -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 2014-07-29 -version = "7.x-2.8" +; Information added by Drupal.org packaging script on 2015-09-08 +version = "7.x-2.9" core = "7.x" project = "date" -datestamp = "1406653438" +datestamp = "1441727353" diff --git a/www7/sites/all/modules/contrib/date/date_repeat/date_repeat.install b/www7/sites/all/modules/contrib/date/date_repeat/date_repeat.install index 65c3adbbb..52039630e 100644 --- a/www7/sites/all/modules/contrib/date/date_repeat/date_repeat.install +++ b/www7/sites/all/modules/contrib/date/date_repeat/date_repeat.install @@ -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() { -} diff --git a/www7/sites/all/modules/contrib/date/date_repeat/date_repeat.module b/www7/sites/all/modules/contrib/date/date_repeat/date_repeat.module index d742dc889..61d971baa 100644 --- a/www7/sites/all/modules/contrib/date/date_repeat/date_repeat.module +++ b/www7/sites/all/modules/contrib/date/date_repeat/date_repeat.module @@ -1,7 +1,6 @@ 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)), diff --git a/www7/sites/all/modules/contrib/date/date_repeat/date_repeat_calc.inc b/www7/sites/all/modules/contrib/date/date_repeat/date_repeat_calc.inc index 4ff3c4a55..d840df629 100644 --- a/www7/sites/all/modules/contrib/date/date_repeat/date_repeat_calc.inc +++ b/www7/sites/all/modules/contrib/date/date_repeat/date_repeat_calc.inc @@ -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) { @@ -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); @@ -303,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'; @@ -322,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. @@ -370,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); @@ -387,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); @@ -426,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); } } @@ -442,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); @@ -456,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; } @@ -472,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; } } @@ -499,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; } @@ -517,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'); @@ -567,7 +580,7 @@ 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, $month, $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'); @@ -620,4 +633,4 @@ function date_repeat_set_year_day($date_in, $month, $day, $count = 1, $direction } } return $date_in; -} \ No newline at end of file +} diff --git a/www7/sites/all/modules/contrib/date/date_repeat/date_repeat_form.inc b/www7/sites/all/modules/contrib/date/date_repeat/date_repeat_form.inc index db7a79bc5..33525956b 100644 --- a/www7/sites/all/modules/contrib/date/date_repeat/date_repeat_form.inc +++ b/www7/sites/all/modules/contrib/date/date_repeat/date_repeat_form.inc @@ -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' => '', ); - $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' => '
' . t('Every weekday', array(), array('context' => 'Date repeat')) . '
', ); - $DAILY_mo_we_fr = array( + $daily_mo_we_fr = array( '#type' => 'item', '#markup' => '
' . t('Every Mon, Wed, Fri', array(), array('context' => 'Date repeat')) . '
', ); - $DAILY_tu_th = array( + $daily_tu_th = array( '#type' => 'item', '#markup' => '
' . t('Every Tue, Thu', array(), array('context' => 'Date repeat')) . '
', ); @@ -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' => '', ); - $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' => '
', '#suffix' => '
', ); - $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' => '
', '#suffix' => '
', ); - $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' => '', ); - $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' => '
', '#suffix' => '
', ); - $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' => '
', '#suffix' => '
', ); - $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) + ); } /** @@ -943,7 +1037,13 @@ function theme_date_repeat_rrule($vars) { $element = $vars['element']; $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'), @@ -960,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; } diff --git a/www7/sites/all/modules/contrib/date/date_repeat_field/date_repeat_field.devel_generate.inc b/www7/sites/all/modules/contrib/date/date_repeat_field/date_repeat_field.devel_generate.inc index 3880e8b4d..51c3708bd 100644 --- a/www7/sites/all/modules/contrib/date/date_repeat_field/date_repeat_field.devel_generate.inc +++ b/www7/sites/all/modules/contrib/date/date_repeat_field/date_repeat_field.devel_generate.inc @@ -1,5 +1,5 @@ '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,10 +108,12 @@ 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.'), + ), + ); } /** @@ -195,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); @@ -215,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) { @@ -239,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) { @@ -279,6 +284,13 @@ function date_repeat_field_field_widget_form_alter(&$element, &$form_state, $con '#suffix' => '', '#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'); + } } } } @@ -340,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'))); @@ -382,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); @@ -418,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)) { @@ -497,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; } @@ -681,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', - ); + ); } - } /** diff --git a/www7/sites/all/modules/contrib/date/date_tools/date_tools.change_type.inc b/www7/sites/all/modules/contrib/date/date_tools/date_tools.change_type.inc index cad2cafdc..041c5dea4 100644 --- a/www7/sites/all/modules/contrib/date/date_tools/date_tools.change_type.inc +++ b/www7/sites/all/modules/contrib/date/date_tools/date_tools.change_type.inc @@ -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] + ))); } diff --git a/www7/sites/all/modules/contrib/date/date_tools/date_tools.info b/www7/sites/all/modules/contrib/date/date_tools/date_tools.info index dbcff7d92..3ec028594 100644 --- a/www7/sites/all/modules/contrib/date/date_tools/date_tools.info +++ b/www7/sites/all/modules/contrib/date/date_tools/date_tools.info @@ -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 2014-07-29 -version = "7.x-2.8" +; Information added by Drupal.org packaging script on 2015-09-08 +version = "7.x-2.9" core = "7.x" project = "date" -datestamp = "1406653438" +datestamp = "1441727353" diff --git a/www7/sites/all/modules/contrib/date/date_tools/date_tools.module b/www7/sites/all/modules/contrib/date/date_tools/date_tools.module index a7ac5f1eb..997808df1 100644 --- a/www7/sites/all/modules/contrib/date/date_tools/date_tools.module +++ b/www7/sites/all/modules/contrib/date/date_tools/date_tools.module @@ -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; } diff --git a/www7/sites/all/modules/contrib/date/date_tools/date_tools.wizard.inc b/www7/sites/all/modules/contrib/date/date_tools/date_tools.wizard.inc index 14bc2759e..94a130a46 100644 --- a/www7/sites/all/modules/contrib/date/date_tools/date_tools.wizard.inc +++ b/www7/sites/all/modules/contrib/date/date_tools/date_tools.wizard.inc @@ -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', diff --git a/www7/sites/all/modules/contrib/date/date_views/date_views.info b/www7/sites/all/modules/contrib/date/date_views/date_views.info index 0f2d7ec39..663efd35d 100644 --- a/www7/sites/all/modules/contrib/date/date_views/date_views.info +++ b/www7/sites/all/modules/contrib/date/date_views/date_views.info @@ -12,9 +12,9 @@ files[] = includes/date_views_filter_handler_simple.inc files[] = includes/date_views.views.inc files[] = includes/date_views_plugin_pager.inc -; Information added by Drupal.org packaging script on 2014-07-29 -version = "7.x-2.8" +; Information added by Drupal.org packaging script on 2015-09-08 +version = "7.x-2.9" core = "7.x" project = "date" -datestamp = "1406653438" +datestamp = "1441727353" diff --git a/www7/sites/all/modules/contrib/date/date_views/date_views.install b/www7/sites/all/modules/contrib/date/date_views/date_views.install index c9697b2d6..e1d2e3efa 100644 --- a/www7/sites/all/modules/contrib/date/date_views/date_views.install +++ b/www7/sites/all/modules/contrib/date/date_views/date_views.install @@ -28,3 +28,27 @@ function date_views_uninstall() { 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'); + } +} diff --git a/www7/sites/all/modules/contrib/date/date_views/date_views.module b/www7/sites/all/modules/contrib/date/date_views/date_views.module index 04bad0910..c050e56e7 100644 --- a/www7/sites/all/modules/contrib/date/date_views/date_views.module +++ b/www7/sites/all/modules/contrib/date/date_views/date_views.module @@ -1,5 +1,9 @@ 'Configure settings for date views.', 'page callback' => 'drupal_get_form', 'page arguments' => array('date_views_settings'), - 'access arguments' => array('administer site configuration '), + 'access arguments' => array('administer site configuration'), 'type' => MENU_LOCAL_TASK, ); @@ -86,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', @@ -100,6 +121,9 @@ function date_views_theme() { ); } +/** + * Implements hook_views_api(). + */ function date_views_views_api() { return array( 'api' => 3, @@ -119,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(); @@ -141,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( @@ -151,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. * - * 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(). + * 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); @@ -193,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( @@ -263,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; @@ -298,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; @@ -311,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') { @@ -336,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 @@ -395,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; } } @@ -421,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); } @@ -441,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. @@ -453,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; } } @@ -464,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; @@ -487,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. @@ -501,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'; } } -} \ No newline at end of file +} diff --git a/www7/sites/all/modules/contrib/date/date_views/includes/date_plugin_display_attachment.inc b/www7/sites/all/modules/contrib/date/date_views/includes/date_plugin_display_attachment.inc index 94371f768..779f4d1f9 100644 --- a/www7/sites/all/modules/contrib/date/date_views/includes/date_plugin_display_attachment.inc +++ b/www7/sites/all/modules/contrib/date/date_views/includes/date_plugin_display_attachment.inc @@ -1,6 +1,7 @@ '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, ), ); } @@ -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]); } diff --git a/www7/sites/all/modules/contrib/date/date_views/includes/date_views_argument_handler.inc b/www7/sites/all/modules/contrib/date/date_views/includes/date_views_argument_handler.inc index 61aeafc18..20eedff0e 100644 --- a/www7/sites/all/modules/contrib/date/date_views/includes/date_views_argument_handler.inc +++ b/www7/sites/all/modules/contrib/date/date_views/includes/date_views_argument_handler.inc @@ -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 diff --git a/www7/sites/all/modules/contrib/date/date_views/includes/date_views_argument_handler_simple.inc b/www7/sites/all/modules/contrib/date/date_views/includes/date_views_argument_handler_simple.inc index 35f88e00a..2839b9593 100644 --- a/www7/sites/all/modules/contrib/date/date_views/includes/date_views_argument_handler_simple.inc +++ b/www7/sites/all/modules/contrib/date/date_views/includes/date_views_argument_handler_simple.inc @@ -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; @@ -85,6 +93,7 @@ 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['granularity_reset'] = array('default' => FALSE); $options['default_argument_type']['default'] = 'date'; $options['add_delta'] = array('default' => ''); $options['use_fromto'] = 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 diff --git a/www7/sites/all/modules/contrib/date/date_views/includes/date_views_fields.inc b/www7/sites/all/modules/contrib/date/date_views/includes/date_views_fields.inc index a002b0887..d497ef602 100644 --- a/www7/sites/all/modules/contrib/date/date_views/includes/date_views_fields.inc +++ b/www7/sites/all/modules/contrib/date/date_views/includes/date_views_fields.inc @@ -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'], diff --git a/www7/sites/all/modules/contrib/date/date_views/includes/date_views_filter_handler.inc b/www7/sites/all/modules/contrib/date/date_views/includes/date_views_filter_handler.inc index 0cfc7fcc2..f761dfa0a 100644 --- a/www7/sites/all/modules/contrib/date/date_views/includes/date_views_filter_handler.inc +++ b/www7/sites/all/modules/contrib/date/date_views/includes/date_views_filter_handler.inc @@ -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); @@ -64,6 +66,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']; @@ -179,3 +184,4 @@ class date_views_filter_handler extends date_views_filter_handler_simple { } } } +// @codingStandardsIgnoreEnd diff --git a/www7/sites/all/modules/contrib/date/date_views/includes/date_views_filter_handler_simple.inc b/www7/sites/all/modules/contrib/date/date_views/includes/date_views_filter_handler_simple.inc index d41f30b64..4fa4c4090 100644 --- a/www7/sites/all/modules/contrib/date/date_views/includes/date_views_filter_handler_simple.inc +++ b/www7/sites/all/modules/contrib/date/date_views/includes/date_views_filter_handler_simple.inc @@ -1,9 +1,11 @@ $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 { @@ -530,3 +535,4 @@ class date_views_filter_handler_simple extends views_handler_filter_date { } } +// @codingStandardsIgnoreEnd diff --git a/www7/sites/all/modules/contrib/date/date_views/includes/date_views_plugin_pager.inc b/www7/sites/all/modules/contrib/date/date_views/includes/date_views_plugin_pager.inc index f9594a72f..5caca4e5b 100644 --- a/www7/sites/all/modules/contrib/date/date_views/includes/date_views_plugin_pager.inc +++ b/www7/sites/all/modules/contrib/date/date_views/includes/date_views_plugin_pager.inc @@ -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->max_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)); } -} \ No newline at end of file +} +// @codingStandardsIgnoreEnd diff --git a/www7/sites/all/modules/contrib/date/date_views/theme/date-views-pager.tpl.php b/www7/sites/all/modules/contrib/date/date_views/theme/date-views-pager.tpl.php index b12c2e1c0..ac8cfb4ee 100644 --- a/www7/sites/all/modules/contrib/date/date_views/theme/date-views-pager.tpl.php +++ b/www7/sites/all/modules/contrib/date/date_views/theme/date-views-pager.tpl.php @@ -27,8 +27,10 @@ * be used in the l() function, including rel=nofollow. */ ?> - -
+ + + +

@@ -36,7 +38,11 @@
  • - 'date_nav'))), $prev_url, $prev_options); ?> + 'date_nav')); + print l(t($text), $prev_url, $prev_options); + ?>
  • @@ -46,4 +52,4 @@
-
\ No newline at end of file +
diff --git a/www7/sites/all/modules/contrib/date/date_views/theme/theme.inc b/www7/sites/all/modules/contrib/date/date_views/theme/theme.inc index 8c42a527b..9e7debc74 100644 --- a/www7/sites/all/modules/contrib/date/date_views/theme/theme.inc +++ b/www7/sites/all/modules/contrib/date/date_views/theme/theme.inc @@ -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, 'o-\W') . date_pad($next_week); - $prev_arg = date_format($prev_date, 'o-\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, 'Y-\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, 'Y-\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,34 +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'); + $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) ? $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) ? $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) ? $format_with_year : $format_without_year); - $title = t('Week of @date', array('@date' => date_format_date($date_info->min_date, 'custom', $format))); + $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; } diff --git a/www7/sites/all/modules/contrib/date/tests/date_api.test b/www7/sites/all/modules/contrib/date/tests/date_api.test index f50020c15..0924ebaa9 100644 --- a/www7/sites/all/modules/contrib/date/tests/date_api.test +++ b/www7/sites/all/modules/contrib/date/tests/date_api.test @@ -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."); @@ -396,6 +396,28 @@ class DateAPITestCase extends DrupalWebTestCase { $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."); } /** diff --git a/www7/sites/all/modules/contrib/date/tests/date_field.test b/www7/sites/all/modules/contrib/date/tests/date_field.test index b2a45a0de..2dd3f0518 100644 --- a/www7/sites/all/modules/contrib/date/tests/date_field.test +++ b/www7/sites/all/modules/contrib/date/tests/date_field.test @@ -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. */ diff --git a/www7/sites/all/modules/contrib/date/tests/date_timezone.test b/www7/sites/all/modules/contrib/date/tests/date_timezone.test index ddbdcef99..54d882fd8 100644 --- a/www7/sites/all/modules/contrib/date/tests/date_timezone.test +++ b/www7/sites/all/modules/contrib/date/tests/date_timezone.test @@ -50,6 +50,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. */ diff --git a/www7/sites/all/modules/contrib/date/tests/date_views_pager.test b/www7/sites/all/modules/contrib/date/tests/date_views_pager.test new file mode 100644 index 000000000..4f5dd97d8 --- /dev/null +++ b/www7/sites/all/modules/contrib/date/tests/date_views_pager.test @@ -0,0 +1,129 @@ + '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'); + } + +} diff --git a/www7/sites/all/modules/contrib/date/tests/date_views_popup.test b/www7/sites/all/modules/contrib/date/tests/date_views_popup.test new file mode 100644 index 000000000..e3d4306d3 --- /dev/null +++ b/www7/sites/all/modules/contrib/date/tests/date_views_popup.test @@ -0,0 +1,106 @@ + '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'); + } +} diff --git a/www7/sites/all/modules/contrib/honeypot/honeypot.info b/www7/sites/all/modules/contrib/honeypot/honeypot.info index 19626a300..92ba90d34 100644 --- a/www7/sites/all/modules/contrib/honeypot/honeypot.info +++ b/www7/sites/all/modules/contrib/honeypot/honeypot.info @@ -6,9 +6,9 @@ package = "Spam control" files[] = honeypot.test -; Information added by Drupal.org packaging script on 2015-07-24 -version = "7.x-1.19" +; Information added by Drupal.org packaging script on 2015-09-04 +version = "7.x-1.21" core = "7.x" project = "honeypot" -datestamp = "1437709441" +datestamp = "1441334340" diff --git a/www7/sites/all/modules/contrib/honeypot/honeypot.module b/www7/sites/all/modules/contrib/honeypot/honeypot.module index e524ea907..3fb54ad02 100644 --- a/www7/sites/all/modules/contrib/honeypot/honeypot.module +++ b/www7/sites/all/modules/contrib/honeypot/honeypot.module @@ -233,7 +233,7 @@ function honeypot_add_form_protection(&$form, &$form_state, $options = array()) // Disable page caching to make sure timestamp isn't cached. if (user_is_anonymous()) { - $GLOBALS['conf']['cache'] = 0; + drupal_page_is_cacheable(FALSE); } } @@ -447,6 +447,11 @@ function honeypot_get_signed_timestamp($time) { function honeypot_get_time_from_signed_timestamp($signed_timestamp) { $honeypot_time = 0; + // Fail fast if timestamp was forged or saved with an older Honeypot version. + if (strpos($signed_timestamp, '|') === FALSE) { + return $honeypot_time; + } + list($timestamp, $received_hmac) = explode('|', $signed_timestamp); if ($timestamp && $received_hmac) { diff --git a/www7/sites/all/modules/contrib/leaflet/leaflet.module b/www7/sites/all/modules/contrib/leaflet/leaflet.module index dabae4faa..35ac4c793 100644 --- a/www7/sites/all/modules/contrib/leaflet/leaflet.module +++ b/www7/sites/all/modules/contrib/leaflet/leaflet.module @@ -118,14 +118,14 @@ function leaflet_build_map($map, $features = array(), $height = '400px') { ); // Load the leaflet library, which includes integration files. - libraries_load('leaflet'); + $build['#attached']['libraries_load'][] = array('leaflet'); return $build; } /** * DEPRECATED. Use leaflet_build_map() instead. - * + * * Load all Leaflet required client files and return markup for a map. * * @param array $map diff --git a/www7/sites/all/modules/contrib/mailchimp/includes/mailchimp.admin.inc b/www7/sites/all/modules/contrib/mailchimp/includes/mailchimp.admin.inc index 73a9b5b5b..fe71189f7 100644 --- a/www7/sites/all/modules/contrib/mailchimp/includes/mailchimp.admin.inc +++ b/www7/sites/all/modules/contrib/mailchimp/includes/mailchimp.admin.inc @@ -2,7 +2,7 @@ /** * @file - * Mailchimp module admin settings. + * MailChimp module admin settings. */ /** @@ -11,15 +11,15 @@ function mailchimp_admin_settings() { $form['mailchimp_api_key'] = array( '#type' => 'textfield', - '#title' => t('Mailchimp API Key'), + '#title' => t('MailChimp API Key'), '#required' => TRUE, '#default_value' => variable_get('mailchimp_api_key', ''), '#description' => t('The API key for your MailChimp account. Get or generate a valid API key at your !apilink.', array('!apilink' => l(t('MailChimp API Dashboard'), 'http://admin.mailchimp.com/account/api'))), ); $form['mailchimp_cron'] = array( '#type' => 'checkbox', - '#title' => 'Use batch processing.', - '#description' => 'Puts all Mailchimp subscription operations into the cron queue. (Includes subscribe, update, and unsubscribe operations.) Note: May cause confusion if caches are cleared, as requested changes will appear to have failed until cron is run.', + '#title' => t('Use batch processing.'), + '#description' => t('Puts all MailChimp subscription operations into the cron queue. (Includes subscribe, update, and unsubscribe operations.) Note: May cause confusion if caches are cleared, as requested changes will appear to have failed until cron is run.'), '#default_value' => variable_get('mailchimp_cron', FALSE), ); $form['mailchimp_batch_limit'] = array( @@ -41,7 +41,7 @@ function mailchimp_admin_settings() { '10000' => '10000', ), '#title' => t('Batch limit'), - '#description' => t('Maximum number of entities to process in a single cron run. Mailchimp suggest keeping this at 5000 or below. This value is also used for batch Merge Variable updates on the Fields tab (part of mailchimp_lists).'), + '#description' => t('Maximum number of entities to process in a single cron run. MailChimp suggest keeping this at 5000 or below. This value is also used for batch Merge Variable updates on the Fields tab (part of mailchimp_lists).'), '#default_value' => variable_get('mailchimp_batch_limit', 100), ); @@ -49,14 +49,14 @@ function mailchimp_admin_settings() { } /** - * Mailchimp List cache clear form. + * MailChimp List cache clear form. */ function mailchimp_clear_list_cache_form($form, &$form_state) { $cancel_destination = 'admin/config/services/mailchimp'; return confirm_form($form, - t('Reset Mailchimp List Cache'), + t('Reset MailChimp List Cache'), $cancel_destination, - t('Confirm clearing of Mailchimp list cache.'), + t('Confirm clearing of MailChimp list cache.'), 'Confirm' ); } @@ -66,5 +66,5 @@ function mailchimp_clear_list_cache_form($form, &$form_state) { */ function mailchimp_clear_list_cache_form_submit($form, &$form_state) { mailchimp_get_lists(array(), TRUE); - drupal_set_message(t('Mailchimp List cache cleared.')); + drupal_set_message(t('MailChimp List cache cleared.')); } diff --git a/www7/sites/all/modules/contrib/mailchimp/mailchimp.info b/www7/sites/all/modules/contrib/mailchimp/mailchimp.info index 9cb4895d7..09885cf86 100644 --- a/www7/sites/all/modules/contrib/mailchimp/mailchimp.info +++ b/www7/sites/all/modules/contrib/mailchimp/mailchimp.info @@ -11,9 +11,9 @@ dependencies[] = libraries (>=2.0) configure = admin/config/services/mailchimp -; Information added by Drupal.org packaging script on 2015-06-22 -version = "7.x-3.4" +; Information added by Drupal.org packaging script on 2015-09-10 +version = "7.x-3.6" core = "7.x" project = "mailchimp" -datestamp = "1434940404" +datestamp = "1441921145" diff --git a/www7/sites/all/modules/contrib/mailchimp/mailchimp.module b/www7/sites/all/modules/contrib/mailchimp/mailchimp.module index a9cbc9e77..10b02ed3f 100644 --- a/www7/sites/all/modules/contrib/mailchimp/mailchimp.module +++ b/www7/sites/all/modules/contrib/mailchimp/mailchimp.module @@ -44,7 +44,7 @@ function mailchimp_menu() { $items['admin/config/services/mailchimp'] = array( 'title' => 'MailChimp', - 'description' => 'Manage MailChimp Settings.', + 'description' => t('Manage MailChimp Settings.'), 'page callback' => 'drupal_get_form', 'page arguments' => array('mailchimp_admin_settings'), 'access arguments' => array('administer mailchimp'), @@ -52,7 +52,7 @@ function mailchimp_menu() { 'type' => MENU_NORMAL_ITEM, ); $items['admin/config/services/mailchimp/global'] = array( - 'title' => 'Global Settings', + 'title' => t('Global Settings'), 'type' => MENU_DEFAULT_LOCAL_TASK, 'weight' => -10, ); @@ -83,7 +83,7 @@ function mailchimp_permission() { return array( 'administer mailchimp' => array( 'title' => t('administer mailchimp'), - 'description' => t('Access the mailchimp configuration options.'), + 'description' => t('Access the MailChimp configuration options.'), ), ); } @@ -99,7 +99,7 @@ function mailchimp_apikey_ready_access($permission) { } /** - * Get a Mailchimp API object for communication with the mailchimp server. + * Get a MailChimp API object for communication with the MailChimp server. * * @return DrupalMailchimp * The default return value is an instance of DrupalMailchimp, although that @@ -126,7 +126,7 @@ function mailchimp_get_api_object() { $api_key = variable_get('mailchimp_api_key', ''); if (!strlen($api_key)) { - watchdog('mailchimp', 'Mailchimp Error: API Key cannot be blank.', array(), WATCHDOG_ERROR); + watchdog('mailchimp', t('MailChimp Error: API Key cannot be blank.'), array(), WATCHDOG_ERROR); return NULL; } // Set the timeout to something that won't take down the Drupal site: @@ -174,7 +174,7 @@ function mailchimp_get_lists($list_ids = array(), $reset = FALSE) { try { $mcapi = mailchimp_get_api_object(); if (!$mcapi) { - throw new MailchimpException('Cannot get lists without Mailchimp API. Check API key has been entered.'); + throw new MailchimpException('Cannot get lists without MailChimp API. Check API key has been entered.'); } $result = $mcapi->lists->getList(array(), 0, 100); if ($result['total'] > 0) { @@ -195,7 +195,7 @@ function mailchimp_get_lists($list_ids = array(), $reset = FALSE) { cache_set('lists', $lists, 'cache_mailchimp', CACHE_TEMPORARY); } catch (Exception $e) { - watchdog('mailchimp', 'An error occurred requesting list information from Mailchimp. "%message"', array( + watchdog('mailchimp', 'An error occurred requesting list information from MailChimp. "%message"', array( '%message' => $e->getMessage(), ), WATCHDOG_ERROR); } @@ -263,7 +263,7 @@ function mailchimp_get_mergevars($list_ids, $reset = FALSE) { $last_list_id = NULL; try { if (!$mcapi) { - throw new MailchimpException('Cannot get merge vars without Mailchimp API. Check API key has been entered.'); + throw new MailchimpException('Cannot get merge vars without MailChimp API. Check API key has been entered.'); } $result = $mcapi->lists->mergeVars($list_ids); if ($result['success_count']) { @@ -314,7 +314,7 @@ function mailchimp_get_memberinfo($list_id, $email, $reset = FALSE) { $mcapi = mailchimp_get_api_object(); try { if (!$mcapi) { - throw new MailchimpException('Cannot get member info without Mailchimp API. Check API key has been entered.'); + throw new MailchimpException('Cannot get member info without MailChimp API. Check API key has been entered.'); } $result = $mcapi->lists->memberInfo($list_id, array(array('email' => $email))); if ($result['success_count']) { @@ -341,7 +341,7 @@ function mailchimp_get_memberinfo($list_id, $email, $reset = FALSE) { * @param string $list_id * Unique string identifier for the list on your MailChimp account. * @param string $email - * Email address to check for on the identified Mailchimp List + * Email address to check for on the identified MailChimp List * @param bool $reset * Set to TRUE to ignore the cache. (Used heavily in testing functions.) * @@ -397,7 +397,7 @@ function mailchimp_subscribe_process($list_id, $email, $merge_vars = NULL, $doub try { $mcapi = mailchimp_get_api_object(); if (!$mcapi) { - throw new MailchimpException('Cannot subscribe to list without Mailchimp API. Check API key has been entered.'); + throw new MailchimpException('Cannot subscribe to list without MailChimp API. Check API key has been entered.'); } $result = $mcapi->lists->subscribe($list_id, array('email' => $email), $merge_vars, $format, $double_optin, $update_existing, $replace_interests, $confirm); if (isset($result['email'])) { @@ -442,12 +442,96 @@ function mailchimp_subscribe_process($list_id, $email, $merge_vars = NULL, $doub function mailchimp_addto_queue($function, $args) { $queue = DrupalQueue::get(MAILCHIMP_QUEUE_CRON); $queue->createQueue(); + mailchimp_update_local_cache($function, $args); return $queue->createItem(array( 'function' => $function, 'args' => $args, )); } +/** + * Updates the local cache for a user as though a queued request had been processed. + * + * If we don't do this, then users can make changes, but not have them shown on the site until + * cron runs, which is intensely confusing. See https://www.drupal.org/node/2503597 + * + * @string $function + * The name of the function that the queue runner will call when the update is processed. + * @array $args + * The list of args that will be passed to the queue runner. + */ +function mailchimp_update_local_cache($function, $args) { + $list_id = isset($args['list_id']) ? $args['list_id'] : NULL; + $email = isset($args['email']) ? $args['email'] : NULL; + if (empty($list_id) || empty($email)) { + return FALSE; + } + $cache = mailchimp_get_memberinfo($list_id, $email); + if (empty($cache)) { + // Create a new entry. + cache_set($list_id . '-' . $email, array('merges' => array()), 'cache_mailchimp', CACHE_TEMPORARY); + $cache = cache_get($list_id . '-' . $email, 'cache_mailchimp'); + $cache = $cache->data; + } + // Handle unsubscribes. + if ($function == 'mailchimp_unsubscribe_process') { + $cache['status'] = 'unsubscribed'; + $cache['merges']['GROUPINGS'] = array(); + } + // Handle subscribes. + if ($function == 'mailchimp_subscribe_process') { + $cache['status'] = 'subscribed'; + } + // Handle member updates. + if ($function == 'mailchimp_update_member_process' || + $function == 'mailchimp_subscribe_process') { + // Update cached merge vars. + foreach ($args['merge_vars'] as $key => $value) { + // Groups are handled separately since the format needs re-written. + if ($key == 'GROUPINGS') { + continue; + } + $cache['merges'][$key] = $value; + } + // Update cached groupings. + mailchimp_update_local_cached_groupings($args, $cache); + } + // Store the data back in the local cache. + cache_set($list_id . '-' . $email, $cache, 'cache_mailchimp', CACHE_TEMPORARY); +} + +/** + * Updates the locally cached interest group data. + * + * @array $args + * The args that would be sent if this was a MailChimp API call. + * @array &$cache + * The current cache information for the user. + */ +function mailchimp_update_local_cached_groupings($args, &$cache) { + $lists = cache_get('lists', 'cache_mailchimp'); + $list_data = $lists->data[$args['list_id']]; + foreach ($list_data['intgroups'] as $idx => $group_settings) { + $interests = array(); + foreach ($group_settings['groups'] as $group) { + if (!empty($args['merge_vars']['GROUPINGS'][$idx]['groups'][$group['name']])) { + $interested = TRUE; + } + else { + $interested = FALSE; + } + $interests[] = array( + 'name' => $group['name'], + 'interested' => $interested, + ); + } + if (empty($cache['merges']['GROUPINGS'][$idx])) { + $cache['merges']['GROUPINGS'][$idx] = $group_settings; + } + $cache['merges']['GROUPINGS'][$idx]['groups'] = $interests; + } +} + /** * Update a members list subscription in real time or by adding to the queue. * @@ -484,7 +568,7 @@ function mailchimp_update_member_process($list_id, $email, $merge_vars, $format, try { $mcapi = mailchimp_get_api_object(); if (!$mcapi) { - throw new MailchimpException('Cannot update member without Mailchimp API. Check API key has been entered.'); + throw new MailchimpException('Cannot update member without MailChimp API. Check API key has been entered.'); } $result = $mcapi->lists->updateMember($list_id, array('email' => $email), $merge_vars, $format, $replace_interests); if (isset($result['email'])) { @@ -524,7 +608,7 @@ function mailchimp_get_members($list_id, $status = 'subscribed', $options = arra try { $mcapi = mailchimp_get_api_object(); if (!$mcapi) { - throw new MailchimpException('Cannot get members without Mailchimp API. Check API key has been entered.'); + throw new MailchimpException('Cannot get members without MailChimp API. Check API key has been entered.'); } $results = $mcapi->lists->members($list_id, $status, $options); } @@ -551,7 +635,7 @@ function mailchimp_batch_update_members($list_id, $batch, $double_in = FALSE, $u try { $mcapi = mailchimp_get_api_object(); if (!$mcapi) { - throw new MailchimpException('Cannot batch subscribe to list without Mailchimp API. Check API key has been entered.'); + throw new MailchimpException('Cannot batch subscribe to list without MailChimp API. Check API key has been entered.'); } $results = $mcapi->lists->batchSubscribe($list_id, $batch, $double_in, $update_existing, $replace_interests); } @@ -578,7 +662,7 @@ function mailchimp_batch_update_members($list_id, $batch, $double_in = FALSE, $u * Indicates whether to send the unsubscribe notification email to the address * defined in the list email notification settings. * @param object $mcapi - * Mailchimp API object if one is already loaded. + * MailChimp API object if one is already loaded. * @param bool $allow_async * Set to TRUE to allow asynchronous processing using DrupalQueue. * @@ -622,7 +706,7 @@ function mailchimp_unsubscribe_process($list_id, $email, $delete, $goodbye, $not try { $mcapi = mailchimp_get_api_object(); if (!$mcapi) { - throw new MailchimpException('Cannot unsubscribe from list without Mailchimp API. Check API key has been entered.'); + throw new MailchimpException('Cannot unsubscribe from list without MailChimp API. Check API key has been entered.'); } $result = $mcapi->lists->unsubscribe($list_id, array('email' => $email), $delete, $goodbye, $notify); if ($result) { @@ -664,13 +748,13 @@ function mailchimp_get_segments($list_id, $reset = NULL) { try { $mcapi = mailchimp_get_api_object(); if (!$mcapi) { - throw new MailchimpException('Cannot get list segments without Mailchimp API. Check API key has been entered.'); + throw new MailchimpException('Cannot get list segments without MailChimp API. Check API key has been entered.'); } $segments = $mcapi->lists->segments($list_id); cache_set($list_id . '-segments', $segments, 'cache_mailchimp', CACHE_TEMPORARY); } catch (Exception $e) { - watchdog('mailchimp', 'An error occurred requesting list segment information from Mailchimp. "%message"', array( + watchdog('mailchimp', 'An error occurred requesting list segment information from MailChimp. "%message"', array( '%message' => $e->getMessage(), ), WATCHDOG_ERROR); } @@ -688,7 +772,7 @@ function mailchimp_get_segments($list_id, $reset = NULL) { * @param string $type * 'static' or 'saved' * @param array $segment_options - * Array of options for 'saved' segments. See Mailchimp API docs. + * Array of options for 'saved' segments. See MailChimp API docs. * * @return int * ID of the new segment. @@ -698,7 +782,7 @@ function mailchimp_segment_create($list_id, $name, $type, $segment_options = NUL try { $mcapi = mailchimp_get_api_object(); if (!$mcapi) { - throw new MailchimpException('Cannot add list segment without Mailchimp API. Check API key has been entered.'); + throw new MailchimpException('Cannot add list segment without MailChimp API. Check API key has been entered.'); } $options = array( 'type' => $type, @@ -757,7 +841,7 @@ function mailchimp_segment_add_subscriber($list_id, $segment_id, $email, $batch 'item' => $item, )); if (!$success) { - watchdog('mailchimp', 'A problem occurred adding a mailchimp segment subscribe to the queue. Email: @email List: @list Segment: @segment.', array( + watchdog('mailchimp', 'A problem occurred adding a MailChimp segment subscribe to the queue. Email: @email List: @list Segment: @segment.', array( '@email' => $email, '@list' => $list_id, '@segment' => $segment_id, @@ -785,7 +869,7 @@ function mailchimp_segment_batch_add_subscribers($list_id, $segment_id, $batch) try { $mcapi = mailchimp_get_api_object(); if (!$mcapi) { - throw new MailchimpException('Cannot batch add segment subscribers without Mailchimp API. Check API key has been entered.'); + throw new MailchimpException('Cannot batch add segment subscribers without MailChimp API. Check API key has been entered.'); } $results = $mcapi->lists->staticSegmentMembersAdd($list_id, $segment_id, $batch); $count = isset($results['success_count']) ? $results['success_count'] : 0; @@ -824,7 +908,7 @@ function mailchimp_get_campaign_data($campaign_id, $reset = FALSE) { try { $mcapi = mailchimp_get_api_object(); if (!$mcapi) { - throw new MailchimpException('Cannot get list without Mailchimp API. Check API key has been entered.'); + throw new MailchimpException('Cannot get list without MailChimp API. Check API key has been entered.'); } $filters = array( 'campaign_id' => $campaign_id, @@ -863,7 +947,7 @@ function mailchimp_get_campaigns_for_email($email) { try { $mcapi = mailchimp_get_api_object(); if (!$mcapi) { - throw new MailchimpException('Cannot get campaigns without Mailchimp API. Check API key has been entered.'); + throw new MailchimpException('Cannot get campaigns without MailChimp API. Check API key has been entered.'); } $campaign_list = $mcapi->helper->campaignsForEmail(array('email' => $email)); } @@ -892,7 +976,7 @@ function mailchimp_get_lists_for_email($email) { try { $mcapi = mailchimp_get_api_object(); if (!$mcapi) { - throw new MailchimpException('Cannot get lists without Mailchimp API. Check API key has been entered.'); + throw new MailchimpException('Cannot get lists without MailChimp API. Check API key has been entered.'); } $lists = $mcapi->helper->listsForEmail(array('email' => $email)); } @@ -919,7 +1003,7 @@ function mailchimp_webhook_get($list_id) { try { $mcapi = mailchimp_get_api_object(); if (!$mcapi) { - throw new MailchimpException('Cannot get webhook without Mailchimp API. Check API key has been entered.'); + throw new MailchimpException('Cannot get webhook without MailChimp API. Check API key has been entered.'); } $result = $mcapi->lists->webhooks($list_id); } @@ -943,12 +1027,12 @@ function mailchimp_webhook_add($list_id, $url, $actions = array(), $sources = ar try { $mcapi = mailchimp_get_api_object(); if (!$mcapi) { - throw new MailchimpException('Cannot add webhook without Mailchimp API. Check API key has been entered.'); + throw new MailchimpException('Cannot add webhook without MailChimp API. Check API key has been entered.'); } $result = $mcapi->lists->webhookAdd($list_id, $url, $actions, $sources); } catch (Exception $e) { - watchdog('mailchimp', 'An error occurred adding webhook for list @list. "%message"', array( + watchdog('mailchimp', t('An error occurred adding webhook for list @list. "%message"'), array( '@list' => $list_id, '%message' => $e->getMessage(), ), WATCHDOG_ERROR); @@ -967,7 +1051,7 @@ function mailchimp_webhook_delete($list_id, $url) { try { $mcapi = mailchimp_get_api_object(); if (!$mcapi) { - throw new MailchimpException('Cannot delete webhook without Mailchimp API. Check API key has been entered.'); + throw new MailchimpException('Cannot delete webhook without MailChimp API. Check API key has been entered.'); } $result = $mcapi->lists->webhookDel($list_id, $url); } @@ -1030,7 +1114,7 @@ function mailchimp_process_webhook_access($key) { */ function mailchimp_process_webhook() { if (empty($_POST)) { - return "Mailchimp Webhook Endpoint."; + return "MailChimp Webhook Endpoint."; } $data = $_POST['data']; $type = $_POST['type']; @@ -1095,7 +1179,7 @@ function mailchimp_webhook_url() { * mailchimp_get_list() * @param array $default_values * Array of default values to use if no group subscription values already - * exist at Mailchimp. + * exist at MailChimp. * @param string $email * Optional email address to pass to the MCAPI and retrieve existing values * for use as defaults. @@ -1318,8 +1402,8 @@ function mailchimp_cron() { */ function mailchimp_variable_group_info() { $groups['mailchimp'] = array( - 'title' => t('Mailchimp'), - 'description' => t('Settings related to Mailchimp.'), + 'title' => t('MailChimp'), + 'description' => t('Settings related to MailChimp.'), 'access' => 'administer mailchimp', 'path' => array('admin/config/services/mailchimp'), ); @@ -1331,7 +1415,7 @@ function mailchimp_variable_group_info() { */ function mailchimp_variable_info($options) { $variable['mailchimp_api_key'] = array( - 'title' => t('Mailchimp API Key', array(), $options), + 'title' => t('MailChimp API Key', array(), $options), 'group' => 'mailchimp', ); $variable['mailchimp_cron'] = array( @@ -1377,4 +1461,4 @@ function mailchimp_mergevars_populate($mergefields, $entity, $entity_type) { } return $mergevars[$bundle . ':' . $id]; -} \ No newline at end of file +} diff --git a/www7/sites/all/modules/contrib/mailchimp/modules/mailchimp_activity/mailchimp_activity.info b/www7/sites/all/modules/contrib/mailchimp/modules/mailchimp_activity/mailchimp_activity.info index 544c05a5c..76a4d4392 100644 --- a/www7/sites/all/modules/contrib/mailchimp/modules/mailchimp_activity/mailchimp_activity.info +++ b/www7/sites/all/modules/contrib/mailchimp/modules/mailchimp_activity/mailchimp_activity.info @@ -9,9 +9,9 @@ dependencies[] = entity files[] = includes/mailchimp_activity.entity.inc files[] = includes/mailchimp_activity.ui_controller.inc -; Information added by Drupal.org packaging script on 2015-06-22 -version = "7.x-3.4" +; Information added by Drupal.org packaging script on 2015-09-10 +version = "7.x-3.6" core = "7.x" project = "mailchimp" -datestamp = "1434940404" +datestamp = "1441921145" diff --git a/www7/sites/all/modules/contrib/mailchimp/modules/mailchimp_activity/mailchimp_activity.install b/www7/sites/all/modules/contrib/mailchimp/modules/mailchimp_activity/mailchimp_activity.install index d7c22f99e..dc7d3d886 100644 --- a/www7/sites/all/modules/contrib/mailchimp/modules/mailchimp_activity/mailchimp_activity.install +++ b/www7/sites/all/modules/contrib/mailchimp/modules/mailchimp_activity/mailchimp_activity.install @@ -11,48 +11,48 @@ function mailchimp_activity_schema() { $schema['mailchimp_activity_entity'] = array( - 'description' => 'MailChimp activity enabled entities.', + 'description' => t('MailChimp activity enabled entities.'), 'fields' => array( 'mailchimp_activity_entity_id' => array( 'type' => 'serial', 'not null' => TRUE, - 'description' => 'Primary Key: Unique mailchimp_activity_entity entity ID.', + 'description' => t('Primary Key: Unique mailchimp_activity_entity entity ID.'), ), 'name' => array( - 'description' => 'The machine-readable name of this mailchimp_activity_entity.', + 'description' => t('The machine-readable name of this mailchimp_activity_entity.'), 'type' => 'varchar', 'length' => 128, 'not null' => TRUE, ), 'label' => array( - 'description' => 'The human-readable name of this mailchimp_activity_entity.', + 'description' => t('The human-readable name of this mailchimp_activity_entity.'), 'type' => 'varchar', 'length' => 128, 'not null' => TRUE, ), 'entity_type' => array( - 'description' => 'The Drupal entity type (e.g. "node", "user").', + 'description' => t('The Drupal entity type (e.g. "node", "user").'), 'type' => 'varchar', 'length' => 128, 'not null' => TRUE, 'default' => '', ), 'bundle' => array( - 'description' => 'The Drupal bundle (e.g. "page", "user")', + 'description' => t('The Drupal bundle (e.g. "page", "user")'), 'type' => 'varchar', 'length' => 128, 'not null' => TRUE, 'default' => '', ), 'entity_path' => array( - 'description' => 'The path to view individual entities of the selected type.', + 'description' => t('The path to view individual entities of the selected type.'), 'type' => 'varchar', 'length' => 128, 'not null' => TRUE, 'default' => '', ), 'email_property' => array( - 'description' => 'The property that contains the email address to track', + 'description' => t('The property that contains the email address to track'), 'type' => 'varchar', 'length' => 128, 'not null' => TRUE, @@ -63,11 +63,11 @@ function mailchimp_activity_schema() { 'not null' => TRUE, 'default' => 0, 'size' => 'tiny', - 'description' => 'Whether or not this mailchimp activity stream is enabled.', + 'description' => t('Whether or not this MailChimp activity stream is enabled.'), ), // The following fields are for supporting exportable status. 'locked' => array( - 'description' => 'A boolean indicating whether the administrator may delete this mapping.', + 'description' => t('A boolean indicating whether the administrator may delete this mapping.'), 'type' => 'int', 'not null' => TRUE, 'default' => 0, @@ -80,10 +80,10 @@ function mailchimp_activity_schema() { // not safe to use it at this point. 'default' => 0x01, 'size' => 'tiny', - 'description' => 'The exportable status of the entity.', + 'description' => t('The exportable status of the entity.'), ), 'module' => array( - 'description' => 'The name of the providing module if the entity has been defined in code.', + 'description' => t('The name of the providing module if the entity has been defined in code.'), 'type' => 'varchar', 'length' => 255, 'not null' => FALSE, diff --git a/www7/sites/all/modules/contrib/mailchimp/modules/mailchimp_campaign/includes/mailchimp_campaign.admin.inc b/www7/sites/all/modules/contrib/mailchimp/modules/mailchimp_campaign/includes/mailchimp_campaign.admin.inc index 39275f89f..d54d134e2 100644 --- a/www7/sites/all/modules/contrib/mailchimp/modules/mailchimp_campaign/includes/mailchimp_campaign.admin.inc +++ b/www7/sites/all/modules/contrib/mailchimp/modules/mailchimp_campaign/includes/mailchimp_campaign.admin.inc @@ -421,7 +421,7 @@ function mailchimp_campaign_get_merge_vars_form_elements($merge_vars, $list_name $form['merge_vars']['content'] = array( '#type' => 'item', - '#title' => 'MailChimp merge variables', + '#title' => t('MailChimp merge variables'), '#markup' => _mailchimp_campaign_build_merge_vars_html($merge_vars), '#description' => t( 'Insert merge variables from the %list_name list or one of the !standard_link.', @@ -523,7 +523,7 @@ function _mailchimp_campaign_get_entities_for_content_import() { */ function _mailchimp_campaign_build_entity_option_list($entity_info) { $options = array( - '' => '-- Select --', + '' => t('-- Select --'), ); foreach ($entity_info as $entity_id => $entity_data) { // Exclude MailChimp entities. @@ -656,3 +656,42 @@ function mailchimp_campaign_send_form_submit($form, &$form_state) { ); drupal_goto('admin/config/services/mailchimp/campaigns'); } + +/** + * Send test campaign form. + */ +function mailchimp_campaign_send_test_form($form, &$form_state, MailChimpCampaign $campaign) { + $form_state['campaign'] = $campaign; + + $form['recipients'] = array( + '#type' => 'textfield', + '#title' => t('Test recipients'), + '#size' => 100, + '#default_value' => isset($_SESSION['mailchimp_campaign_test']) ? $_SESSION['mailchimp_campaign_test'] : '', + '#description' => t('Enter one or more e-mail addresses separated by commas.'), + ); + + return confirm_form($form, + t('Confirm test for %name?', array('%name' => $campaign->label())), + 'admin/config/services/mailchimp/campaigns/' . $campaign->mc_campaign_id, + t('This action will send a test message through MailChimp to the recipients specified above.'), + t('Send test') + ); +} + +/** + * Submit handler for mailchimp_campaign_send_test_form(). + */ +function mailchimp_campaign_send_test_form_submit($form, &$form_state) { + $campaign = $form_state['campaign']; + $recipients = explode(',', $form_state['values']['recipients']); + $recipients = array_map('trim', $recipients); + + mailchimp_campaign_send_test_campaign($campaign, $recipients); + $_SESSION['mailchimp_campaign_test'] = implode(', ', $recipients); + + drupal_set_message(t('Campaign %name test message sent.', + array('%name' => $campaign->label())) + ); + drupal_goto('admin/config/services/mailchimp/campaigns/' . $campaign->mc_campaign_id); +} diff --git a/www7/sites/all/modules/contrib/mailchimp/modules/mailchimp_campaign/mailchimp_campaign.info b/www7/sites/all/modules/contrib/mailchimp/modules/mailchimp_campaign/mailchimp_campaign.info index 742d8a86b..7c8d36c12 100644 --- a/www7/sites/all/modules/contrib/mailchimp/modules/mailchimp_campaign/mailchimp_campaign.info +++ b/www7/sites/all/modules/contrib/mailchimp/modules/mailchimp_campaign/mailchimp_campaign.info @@ -13,9 +13,9 @@ files[] = tests/mailchimp_campaigns.test configure = admin/config/services/mailchimp/campaigns -; Information added by Drupal.org packaging script on 2015-06-22 -version = "7.x-3.4" +; Information added by Drupal.org packaging script on 2015-09-10 +version = "7.x-3.6" core = "7.x" project = "mailchimp" -datestamp = "1434940404" +datestamp = "1441921145" diff --git a/www7/sites/all/modules/contrib/mailchimp/modules/mailchimp_campaign/mailchimp_campaign.module b/www7/sites/all/modules/contrib/mailchimp/modules/mailchimp_campaign/mailchimp_campaign.module index 296b8db02..2df95b5f3 100644 --- a/www7/sites/all/modules/contrib/mailchimp/modules/mailchimp_campaign/mailchimp_campaign.module +++ b/www7/sites/all/modules/contrib/mailchimp/modules/mailchimp_campaign/mailchimp_campaign.module @@ -51,8 +51,8 @@ function mailchimp_campaign_permission() { */ function mailchimp_campaign_menu() { $items['admin/config/services/mailchimp/campaigns'] = array( - 'title' => 'Campaigns', - 'description' => 'Manage MailChimp Campaigns.', + 'title' => t('Campaigns'), + 'description' => t('Manage MailChimp Campaigns.'), 'page callback' => 'mailchimp_campaign_overview_page', 'access callback' => 'mailchimp_apikey_ready_access', 'access arguments' => array('administer mailchimp campaigns'), @@ -60,8 +60,8 @@ function mailchimp_campaign_menu() { 'weight' => 10, ); $items['admin/config/services/mailchimp/campaigns/add'] = array( - 'title' => 'Add a Campaign', - 'description' => 'Add a new MailChimp campaign.', + 'title' => t('Add a Campaign'), + 'description' => t('Add a new MailChimp campaign.'), 'page callback' => 'drupal_get_form', 'page arguments' => array('mailchimp_campaign_campaign_form'), 'access callback' => 'mailchimp_apikey_ready_access', @@ -78,12 +78,12 @@ function mailchimp_campaign_menu() { 'load arguments' => array(5), ); $items['admin/config/services/mailchimp/campaigns/%mailchimp_campaign/view'] = array( - 'title' => 'View', + 'title' => t('View'), 'type' => MENU_DEFAULT_LOCAL_TASK, 'weight' => 0, ); $items['admin/config/services/mailchimp/campaigns/%mailchimp_campaign/send'] = array( - 'title' => 'Send', + 'title' => t('Send'), 'page callback' => 'drupal_get_form', 'page arguments' => array('mailchimp_campaign_send_form', 5), 'access callback' => 'mailchimp_campaign_access', @@ -92,9 +92,19 @@ function mailchimp_campaign_menu() { 'type' => MENU_LOCAL_TASK, 'weight' => 1, ); + $items['admin/config/services/mailchimp/campaigns/%mailchimp_campaign/send-test'] = array( + 'title' => 'Send Test', + 'page callback' => 'drupal_get_form', + 'page arguments' => array('mailchimp_campaign_send_test_form', 5), + 'access callback' => 'mailchimp_campaign_access', + 'access arguments' => array(5, FALSE), + 'file' => 'includes/mailchimp_campaign.admin.inc', + 'type' => MENU_LOCAL_TASK, + 'weight' => 1, + ); $items['admin/config/services/mailchimp/campaigns/%mailchimp_campaign/edit'] = array( - 'title' => 'Edit', - 'description' => 'Edit a MailChimp campaign.', + 'title' => t('Edit'), + 'description' => t('Edit a MailChimp campaign.'), 'page callback' => 'drupal_get_form', 'page arguments' => array('mailchimp_campaign_campaign_form', 5), 'access callback' => 'mailchimp_campaign_access', @@ -104,7 +114,7 @@ function mailchimp_campaign_menu() { 'weight' => 2, ); $items['admin/config/services/mailchimp/campaigns/%mailchimp_campaign/delete'] = array( - 'title' => 'Delete', + 'title' => t('Delete'), 'page callback' => 'drupal_get_form', 'page arguments' => array('mailchimp_campaign_delete_form', 5), 'access callback' => 'mailchimp_campaign_access', @@ -114,7 +124,7 @@ function mailchimp_campaign_menu() { 'weight' => 3, ); $items['admin/config/services/mailchimp/campaigns/%mailchimp_campaign/stats'] = array( - 'title' => 'Stats', + 'title' => t('Stats'), 'page callback' => 'mailchimp_campaign_stats_page', 'page arguments' => array(5), 'access callback' => 'mailchimp_campaign_access', @@ -411,6 +421,41 @@ function mailchimp_campaign_send_campaign(MailChimpCampaign $campaign) { return FALSE; } +/** + * Send MailChimp campaign. + */ +function mailchimp_campaign_send_test_campaign(MailChimpCampaign $campaign, $recipients = array()) { + $mcapi = mailchimp_get_api_object(); + + // Send campaign. + try { + if (!$mcapi) { + throw new MailchimpException('Cannot send campaign without Mailchimp API. Check API key has been entered.'); + } + $result = $mcapi->campaigns->sendTest($campaign->mc_campaign_id, $recipients); + } + catch (Mailchimp_Error $e) { + drupal_set_message($e->getMessage(), 'error'); + watchdog('mailchimp_campaign', + 'An error occurred while sending a test of this campaign: @msg', array('@msg' => $e->getMessage()), WATCHDOG_ERROR); + return FALSE; + } + + if (isset($result['complete']) && ($result['complete'] === TRUE)) { + // Log action, and notify the user. + watchdog('mailchimp_campaign', 'MailChimp test message for campaign %name has been sent to %recipients.', + array('%name' => $campaign->label(), '%recipients' => implode(', ', $recipients)) + ); + return TRUE; + } + elseif (isset($result['status']) && ($result['status'] == 'error')) { + // Display and log error, if any. + $message = 'MailChimp error. The campaign was not sent.'; + _mailchimp_campaign_mcapi_error_message($result, $message); + } + return FALSE; +} + /** * Delete a MailChimp campaign and the local entity. */ @@ -645,10 +690,19 @@ function mailchimp_campaign_process_callback($matches = array()) { $entities = entity_load($entity_type, array($entity_id)); if (!empty($entities)) { + // Prevent session information from being rendered. + drupal_save_session(FALSE); + // Force the current user to anonymous to prevent access bypass. + $original_user = $GLOBALS['user']; + $GLOBALS['user'] = drupal_anonymous_user(); + // Render the entity. $render_array = entity_view($entity_type, $entities, $view_mode, NULL, TRUE); // Remove contextual links. _mailchimp_campaign_recursive_unset($render_array[$entity_type][$entity_id], '#contextual_links'); $content = render($render_array); + // Restore the user. + $GLOBALS['user'] = $original_user; + drupal_save_session(TRUE); } return $content; diff --git a/www7/sites/all/modules/contrib/mailchimp/modules/mailchimp_lists/includes/mailchimp_lists.admin.inc b/www7/sites/all/modules/contrib/mailchimp/modules/mailchimp_lists/includes/mailchimp_lists.admin.inc index 7571922a6..1a11072d4 100644 --- a/www7/sites/all/modules/contrib/mailchimp/modules/mailchimp_lists/includes/mailchimp_lists.admin.inc +++ b/www7/sites/all/modules/contrib/mailchimp/modules/mailchimp_lists/includes/mailchimp_lists.admin.inc @@ -24,11 +24,11 @@ function mailchimp_lists_overview_page() { } } if ($enabled) { - // This is a webhook for this Mailchimp Module. - $webhook_status = "ENABLED (" . l(t('disable'), 'admin/config/services/mailchimp/lists/' . $mc_list['id'] . '/webhook/disable') . ')'; + // This is a webhook for this MailChimp Module. + $webhook_status = t('ENABLED') . '(' . l(t('disable'), 'admin/config/services/mailchimp/lists/' . $mc_list['id'] . '/webhook/disable') . ')'; } else { - $webhook_status = "disabled (" . l(t('enable'), 'admin/config/services/mailchimp/lists/' . $mc_list['id'] . '/webhook/enable') . ')'; + $webhook_status = t('disabled') . ' (' . l(t('enable'), 'admin/config/services/mailchimp/lists/' . $mc_list['id'] . '/webhook/enable') . ')'; } $rows[] = array( l($mc_list['name'], 'https://admin.mailchimp.com/lists/dashboard/overview?id=' . $mc_list['web_id']), @@ -45,7 +45,7 @@ function mailchimp_lists_overview_page() { ), 'rows' => $rows, ); - $refresh_link = l(t('Refresh lists from Mailchimp'), + $refresh_link = l(t('Refresh lists from MailChimp'), 'admin/config/services/mailchimp/list_cache_clear', array('query' => array('destination' => 'admin/config/services/mailchimp/lists'))); if (empty($mc_lists)) { @@ -54,7 +54,7 @@ function mailchimp_lists_overview_page() { array('!link' => l(t('MailChimp'), 'https://admin.mailchimp.com/lists/'))), 'warning'); } else { - $options = 'Currently Available MailChimp lists:'; + $options = t('Currently Available MailChimp lists').':'; foreach ($mc_lists as $mc_list) { $options .= ' ' . $mc_list['name'] . ','; } @@ -81,7 +81,7 @@ function mailchimp_lists_fields_overview_page() { $entity_type, $bundle, $field['field_name'], - l(t('Update Mailchimp Mergevar Values'), $link), + l(t('Update MailChimp Mergevar Values'), $link), ); } } @@ -97,12 +97,12 @@ function mailchimp_lists_fields_overview_page() { 'rows' => $rows, ); - $table['caption'] = t("This displays a list of all Mailchimp Subscription Fields configured on your system, with a row for each unique Instance of that field. + $table['caption'] = t("This displays a list of all MailChimp Subscription Fields configured on your system, with a row for each unique Instance of that field. To edit each field's settings, go to the Entity Bundle's configuration screen and use the Field UI. When entities with MailChimp Subscription Fields are updated, the Merge Variables configured through Field UI are automatically updated if necessary. However, if you have existing subscribers on MailChimp and matching Entities on Drupal when you configure your Merge Variables, the existing values are not synced automatically, as this could be a slow process. You can manually force updates of all existing Merge Values to existing MailChimp subscribers for each field configuration using the 'Batch Update' - option on this table. The MailChimp Subscription Field is provided by the Mailchimp Lists (mailchimp_lists) module.

+ option on this table. The MailChimp Subscription Field is provided by the MailChimp Lists (mailchimp_lists) module.

The following MailChimp Subscription Field instances were found:"); return theme('table', $table); @@ -126,11 +126,11 @@ function mailchimp_lists_webhook_form($form, &$form_state, $list_id, $action) { default: return array(); } - $label = $action . " webhooks"; + $label = t($action) . " webhooks"; return confirm_form($form, t('Are you sure you want to &action webhooks for %name?', array( - '&action' => $action, + '&action' => t($action), '%name' => $list['name'], )), 'admin/config/services/mailchimp/lists', @@ -209,7 +209,7 @@ function mailchimp_lists_update_mergevars_form($form, &$form_state, $entity_type '@bundle' => $bundle_name, )), 'admin/config/services/mailchimp/lists', - t('This can overwrite values configured directly on your Mailchimp Account.'), + t('This can overwrite values configured directly on your MailChimp Account.'), "Update Merge Values" ); } diff --git a/www7/sites/all/modules/contrib/mailchimp/modules/mailchimp_lists/includes/mailchimp_lists.field.inc b/www7/sites/all/modules/contrib/mailchimp/modules/mailchimp_lists/includes/mailchimp_lists.field.inc index 958f5e2cf..3f0fbddcd 100644 --- a/www7/sites/all/modules/contrib/mailchimp/modules/mailchimp_lists/includes/mailchimp_lists.field.inc +++ b/www7/sites/all/modules/contrib/mailchimp/modules/mailchimp_lists/includes/mailchimp_lists.field.inc @@ -98,18 +98,28 @@ function mailchimp_lists_field_settings_form($this_field, $instance, $has_data) ); $form['double_opt_in'] = array( '#type' => 'checkbox', - '#title' => 'Require subscribers to Double Opt-in', - '#description' => 'New subscribers will be sent a link with an email they must follow to confirm their subscription.', + '#title' => t('Require subscribers to Double Opt-in'), + '#description' => t('New subscribers will be sent a link with an email they must follow to confirm their subscription.'), '#default_value' => isset($this_field['settings']['double_opt_in']) ? $this_field['settings']['double_opt_in'] : FALSE, '#disabled' => $disabled, ); $form['send_welcome'] = array( '#type' => 'checkbox', - '#title' => 'Send a welcome email to new subscribers', - '#description' => 'New subscribers will be sent a welcome email once they are confirmed.', + '#title' => t('Send a welcome email to new subscribers'), + '#description' => t('New subscribers will be sent a welcome email once they are confirmed.'), '#default_value' => isset($this_field['settings']['send_welcome']) ? $this_field['settings']['send_welcome'] : FALSE, '#disabled' => $disabled, ); + $form['unsubscribe_action'] = array( + '#type' => 'radios', + '#title' => t('Action to take when unchecking this field'), + '#description' => t('When this field gets unchecked you can choose if the subscriber gets unsubscribed (to keep the subscriber data in MailChimp) or completely removed from the Mailchimp list.'), + '#options' => array( + 'unsubscribe' => t('Unsubscribe from list'), + 'remove' => t('Remove from list'), + ), + '#default_value' => isset($this_field['settings']['unsubscribe_action']) ? $this_field['settings']['unsubscribe_action'] : 'remove', + ); return $form; } @@ -121,13 +131,13 @@ function mailchimp_lists_field_instance_settings_form($field, $instance) { $form = array(); $mc_list_id = $field['settings']['mc_list_id']; $form['show_interest_groups'] = array( - '#title' => "Enable Interest Groups", - '#type' => "checkbox", + '#title' => t('Enable Interest Groups'), + '#type' => 'checkbox', '#default_value' => $instance['settings']['show_interest_groups'], ); $form['interest_groups_title'] = array( - '#title' => "Interest Groups Label", - '#type' => "textfield", + '#title' => t('Interest Groups Label'), + '#type' => 'textfield', '#default_value' => isset($instance['settings']['interest_groups_title']) ? $instance['settings']['interest_groups_title'] : "Interest Groups", ); @@ -146,8 +156,8 @@ function mailchimp_lists_field_instance_settings_form($field, $instance) { ); $form['unsubscribe_on_delete'] = array( - '#title' => "Unsubscribe on deletion", - '#type' => "checkbox", + '#title' => t('Unsubscribe on deletion'), + '#type' => 'checkbox', '#description' => t('Unsubscribe entities from this list when they are deleted.'), '#default_value' => $instance['settings']['unsubscribe_on_delete'], ); @@ -358,9 +368,42 @@ function mailchimp_lists_field_widget_form(&$form, &$form_state, $field, $instan $element['interest_groups'] += mailchimp_interest_groups_form_elements($mc_list, $groups_default, $email); } } + + // Make a distinction between whether the field is edited by the system or the user. + // This is important to prevent unwanted subscription overwrites. + // @see _mailchimp_lists_field_postsave() + if (isset($element['#entity'])) { + // The field is edited via the UI. + $element['is_default'] = array( + '#type' => 'value', + '#value' => FALSE, + ); + } + else { + // The field is NOT edited via the UI. + $element['is_default'] = array( + '#type' => 'value', + '#value' => TRUE, + ); + } + + $element['#element_validate'] = array('mailchimp_lists_field_widget_form_validate'); + return $element; } +/** + * Validation callback for mailchimp_lists_select widget. + */ +function mailchimp_lists_field_widget_form_validate(&$element, &$form_state) { + if (isset($element['#access']) && !$element['#access']) { + // When the field is not accessible, do not accept any values. + // Else the default value will be send to MailChimp, which causes + // an existing subscription to be overwritten. + form_set_value($element, NULL, $form_state); + } +} + /** * Implements hook_field_formatter_info(). */ @@ -593,7 +636,17 @@ function _mailchimp_lists_field_postsave($entity_type, $entity, $field, $instanc $entity->is_new = (($entity_wrapper->getIdentifier() === FALSE) || ($entity_wrapper->getIdentifier() === NULL)); } - if ($entity->is_new && empty($entity->{$instance['field_name']})) { + // Check if the field is empty or is set by the system via field_get_default_value(). + if (empty($items[0]) || !empty($item[0]['is_default'])) { + // Check if this email is already subscribed. + $email = mailchimp_lists_load_email($instance, $entity); + $list_id = $field['settings']['mc_list_id']; + $subscribed = mailchimp_is_subscribed($list_id, $email); + if ($subscribed) { + // The mail address is already subscribed. Do not overwrite the subscription. + return; + } + $choices = $instance['default_value'][0]; } diff --git a/www7/sites/all/modules/contrib/mailchimp/modules/mailchimp_lists/mailchimp_lists.info b/www7/sites/all/modules/contrib/mailchimp/modules/mailchimp_lists/mailchimp_lists.info index 2705efd71..375c0d90e 100644 --- a/www7/sites/all/modules/contrib/mailchimp/modules/mailchimp_lists/mailchimp_lists.info +++ b/www7/sites/all/modules/contrib/mailchimp/modules/mailchimp_lists/mailchimp_lists.info @@ -11,9 +11,9 @@ configure = admin/config/services/mailchimp/lists files[] = tests/mailchimp_lists.test -; Information added by Drupal.org packaging script on 2015-06-22 -version = "7.x-3.4" +; Information added by Drupal.org packaging script on 2015-09-10 +version = "7.x-3.6" core = "7.x" project = "mailchimp" -datestamp = "1434940404" +datestamp = "1441921145" diff --git a/www7/sites/all/modules/contrib/mailchimp/modules/mailchimp_lists/mailchimp_lists.module b/www7/sites/all/modules/contrib/mailchimp/modules/mailchimp_lists/mailchimp_lists.module index 23d7151db..5b0628ae7 100644 --- a/www7/sites/all/modules/contrib/mailchimp/modules/mailchimp_lists/mailchimp_lists.module +++ b/www7/sites/all/modules/contrib/mailchimp/modules/mailchimp_lists/mailchimp_lists.module @@ -14,8 +14,8 @@ function mailchimp_lists_menu() { $items = array(); $items['admin/config/services/mailchimp/lists'] = array( - 'title' => 'Lists', - 'description' => 'Display MailChimp Lists and configure Webhooks.', + 'title' => t('Lists'), + 'description' => t('Display MailChimp Lists and configure Webhooks.'), 'page callback' => 'mailchimp_lists_overview_page', 'access callback' => 'mailchimp_apikey_ready_access', 'access arguments' => array('administer mailchimp'), @@ -24,8 +24,8 @@ function mailchimp_lists_menu() { 'weight' => 10, ); $items['admin/config/services/mailchimp/fields'] = array( - 'title' => 'Fields', - 'description' => 'Display MailChimp Subscription Fields and trigger merge variable updates.', + 'title' => t('Fields'), + 'description' => t('Display MailChimp Subscription Fields and trigger merge variable updates.'), 'page callback' => 'mailchimp_lists_fields_overview_page', 'access callback' => 'mailchimp_apikey_ready_access', 'access arguments' => array('administer mailchimp'), @@ -34,7 +34,7 @@ function mailchimp_lists_menu() { 'weight' => 10, ); $items['admin/config/services/mailchimp/lists/%/webhook/%'] = array( - 'title' => 'Enable/disable Webhooks', + 'title' => t('Enable/disable Webhooks'), 'page callback' => 'drupal_get_form', 'page arguments' => array('mailchimp_lists_webhook_form', 5, 7), 'access callback' => 'mailchimp_apikey_ready_access', @@ -42,7 +42,7 @@ function mailchimp_lists_menu() { 'file' => 'includes/mailchimp_lists.admin.inc', ); $items['admin/config/services/mailchimp/lists/update_mergevars/%/%/%'] = array( - 'title' => 'Mass Update Merge Variables', + 'title' => t('Mass Update Merge Variables'), 'page callback' => 'drupal_get_form', 'page arguments' => array('mailchimp_lists_update_mergevars_form', 6, 7, 8), 'access callback' => 'mailchimp_apikey_ready_access', @@ -88,7 +88,7 @@ function mailchimp_lists_load_email($instance, $entity, $log_errors = TRUE) { if (!isset($instance['settings']['mergefields']['EMAIL'])) { if ($log_errors) { - watchdog('mailchimp_lists', 'Mailchimp Lists field "@field" on @entity -> @bundle has no EMAIL field configured, subscription actions cannot take place.', array( + watchdog('mailchimp_lists', 'MailChimp Lists field "@field" on @entity -> @bundle has no EMAIL field configured, subscription actions cannot take place.', array( '@field' => $instance['field_name'], '@entity' => $instance['entity_type'], '@bundle' => $instance['bundle'], @@ -170,7 +170,7 @@ function mailchimp_lists_subscribe_form($form, &$form_state, $instance, $setting // Add the title and description to lists for anonymous users or if requested: $form[$wrapper_key]['subscribe'] = array( '#type' => 'checkbox', - '#title' => 'Subscribe', + '#title' => t('Subscribe'), '#disabled' => $instance['required'], '#required' => $instance['required'], '#default_value' => $instance['required'] || $is_subscribed, @@ -238,6 +238,9 @@ function mailchimp_lists_process_subscribe_form_choices($choices, $instance, $fi if ($choices['subscribe']) { $function = 'add'; } + elseif (isset($field['settings']['unsubscribe_action'])) { + $function = $field['settings']['unsubscribe_action']; + } else { $function = 'remove'; } @@ -260,6 +263,10 @@ function mailchimp_lists_process_subscribe_form_choices($choices, $instance, $fi $ret = mailchimp_subscribe($list_id, $email, $mergevars, $field['settings']['double_opt_in'], $field['settings']['send_welcome']); break; + case 'unsubscribe': + $ret = mailchimp_unsubscribe($list_id, $email, FALSE); + break; + case 'remove': $ret = mailchimp_unsubscribe($list_id, $email, TRUE); break; @@ -384,7 +391,7 @@ function mailchimp_lists_update_member_merge_values($entity_type, $bundle_name, ), 'finished' => 'mailchimp_lists_populate_member_batch_complete', 'title' => t('Processing Merge Variable Updates'), - 'init_message' => t('Starting Mailchimp Merge Variable Update.'), + 'init_message' => t('Starting MailChimp Merge Variable Update.'), 'progress_message' => t('Processed @current out of @total.'), 'error_message' => t('MailChimp Merge Variable Update Failed.'), ); @@ -690,7 +697,7 @@ function mailchimp_lists_add_to_segment_action_form($context, &$form_state) { } $form['mailchimp_field'] = array( '#type' => 'select', - '#title' => "Select a Mailchimp List", + '#title' => t('Select a MailChimp List'), '#options' => $mailchimp_field_options, '#required' => TRUE, '#ajax' => array( @@ -732,7 +739,7 @@ function mailchimp_lists_add_to_segment_action_form($context, &$form_state) { ':input[name="segment"]' => array('value' => 0), ), 'invisible' => array( - ':input[name="mc_list_id"]' => array('value' => ''), + ':input[name="mailchimp_field"]' => array('value' => ''), ), ), ); @@ -794,10 +801,10 @@ function mailchimp_lists_add_to_segment_action_views_bulk_operations_form($optio } $form['mailchimp_field'] = array( '#type' => 'select', - '#title' => t('REQUIRED: Mailchimp List Field(s)'), + '#title' => t('REQUIRED: MailChimp List Field(s)'), '#options' => $field_options, '#multiple' => TRUE, - '#description' => t('Select the mailchimp fields for which to allow segmentation.'), + '#description' => t('Select the MailChimp fields for which to allow segmentation.'), '#default_value' => isset($options['mailchimp_field']) ? $options['mailchimp_field'] : NULL, '#states' => array( 'required' => array( diff --git a/www7/sites/all/modules/contrib/mailchimp/modules/mailchimp_lists/mailchimp_lists.rules.inc b/www7/sites/all/modules/contrib/mailchimp/modules/mailchimp_lists/mailchimp_lists.rules.inc index d7dca5108..4a8f39a84 100644 --- a/www7/sites/all/modules/contrib/mailchimp/modules/mailchimp_lists/mailchimp_lists.rules.inc +++ b/www7/sites/all/modules/contrib/mailchimp/modules/mailchimp_lists/mailchimp_lists.rules.inc @@ -10,7 +10,7 @@ function mailchimp_lists_rules_action_info() { $items = array(); $items['mailchimp_lists_user_subscribe'] = array( - 'label' => t('Subscribe or unsubscribe entity from a mailchimp list'), + 'label' => t('Subscribe or unsubscribe entity from a MailChimp list'), 'parameter' => array( 'entity' => array( 'type' => 'entity', @@ -19,7 +19,7 @@ function mailchimp_lists_rules_action_info() { ), 'field' => array( 'type' => '*', - 'label' => t('Mailchimp List Subscription Field'), + 'label' => t('MailChimp List Subscription Field'), 'description' => t('Subscription Field connected to the desired Mailchimp List.'), 'restriction' => 'selector', 'wrapped' => TRUE, @@ -31,7 +31,7 @@ function mailchimp_lists_rules_action_info() { 'description' => t('True to subscribe, False to unsubscribe'), ), ), - 'group' => t('Mailchimp'), + 'group' => t('MailChimp'), 'access callback' => 'mailchimp_lists_rules_access_callback', 'base' => 'mailchimp_lists_rules_action_entity_subscribe', ); @@ -45,7 +45,7 @@ function mailchimp_lists_rules_action_entity_subscribe($entity, $field, $subscri $field_info = $field->info(); $choices = reset($field_info['instance']['default_value']); $choices['subscribe'] = $subscribe; - mailchimp_lists_process_subscribe_form_choices($choices, $field_info['instance'], $field_info['field'], $entity->raw()); + mailchimp_lists_process_subscribe_form_choices($choices, $field_info['instance'], $field_info['field'], $entity->value()); } /** diff --git a/www7/sites/all/modules/contrib/mailchimp/modules/mailchimp_signup/includes/mailchimp_signup.ui_controller.inc b/www7/sites/all/modules/contrib/mailchimp/modules/mailchimp_signup/includes/mailchimp_signup.ui_controller.inc index 845ea1f2b..3a608c52b 100644 --- a/www7/sites/all/modules/contrib/mailchimp/modules/mailchimp_signup/includes/mailchimp_signup.ui_controller.inc +++ b/www7/sites/all/modules/contrib/mailchimp/modules/mailchimp_signup/includes/mailchimp_signup.ui_controller.inc @@ -15,8 +15,8 @@ class MailChimpSignupUIController extends EntityDefaultUIController { */ public function hook_menu() { $items = parent::hook_menu(); - $items[$this->path]['title'] = 'Signup Forms'; - $items[$this->path]['description'] = 'Manage MailChimp Signup blocks and pages.'; + $items[$this->path]['title'] = t('Signup Forms'); + $items[$this->path]['description'] = t('Manage MailChimp Signup blocks and pages.'); $items[$this->path]['type'] = MENU_LOCAL_TASK; $items[$this->path]['weight'] = 10; $items[$this->path]['access callback'] = 'mailchimp_signup_entity_access'; @@ -54,7 +54,7 @@ class MailChimpSignupUIController extends EntityDefaultUIController { $list_labels[] = l($mc_lists[$list_id]['name'], 'https://admin.mailchimp.com/lists/dashboard/overview?id=' . $mc_lists[$list_id]['web_id']); } if ($block_only) { - $access = 'N/A - this form only exists as a block'; + $access = t('N/A - this form only exists as a block'); } else { $all_roles_allowed = user_roles(FALSE, 'mailchimp_signup_all_forms' . $signup->name); diff --git a/www7/sites/all/modules/contrib/mailchimp/modules/mailchimp_signup/mailchimp_signup.info b/www7/sites/all/modules/contrib/mailchimp/modules/mailchimp_signup/mailchimp_signup.info index ce647797e..211d3e05d 100644 --- a/www7/sites/all/modules/contrib/mailchimp/modules/mailchimp_signup/mailchimp_signup.info +++ b/www7/sites/all/modules/contrib/mailchimp/modules/mailchimp_signup/mailchimp_signup.info @@ -10,9 +10,9 @@ configure = admin/config/services/mailchimp/signup files[] = includes/mailchimp_signup.entity.inc files[] = includes/mailchimp_signup.ui_controller.inc -; Information added by Drupal.org packaging script on 2015-06-22 -version = "7.x-3.4" +; Information added by Drupal.org packaging script on 2015-09-10 +version = "7.x-3.6" core = "7.x" project = "mailchimp" -datestamp = "1434940404" +datestamp = "1441921145" diff --git a/www7/sites/all/modules/contrib/mailchimp/modules/mailchimp_signup/mailchimp_signup.module b/www7/sites/all/modules/contrib/mailchimp/modules/mailchimp_signup/mailchimp_signup.module index 946caf6a5..71c1966c9 100644 --- a/www7/sites/all/modules/contrib/mailchimp/modules/mailchimp_signup/mailchimp_signup.module +++ b/www7/sites/all/modules/contrib/mailchimp/modules/mailchimp_signup/mailchimp_signup.module @@ -251,7 +251,9 @@ function mailchimp_signup_forms($form_id, $args) { function mailchimp_signup_subscribe_form($form, &$form_state, $signup, $type) { $form['#attributes'] = array('class' => array('mailchimp-signup-subscribe-form')); $form['description'] = array( - '#markup' => filter_xss($signup->settings['description']), + '#markup' => filter_xss_admin($signup->settings['description']), + '#prefix' => '' ); $form['mailchimp_lists'] = array('#tree' => TRUE); $lists = mailchimp_get_lists($signup->mc_lists); @@ -304,6 +306,7 @@ function mailchimp_signup_subscribe_form($form, &$form_state, $signup, $type) { ); foreach ($signup->settings['mergefields'] as $tag => $mergevar) { if (!empty($mergevar)) { + $mergevar['name'] = mailchimp_signup_tt("field:mailchimp_signup:form:$signup->mcs_id:mergefield:$tag", $mergevar['name']); $form['mergevars'][$tag] = mailchimp_insert_drupal_form_tag($mergevar); if (empty($lists)) { $form['mergevars'][$tag]['#disabled'] = TRUE; @@ -311,7 +314,8 @@ function mailchimp_signup_subscribe_form($form, &$form_state, $signup, $type) { } } - $form['submit'] = array( + $form['actions']['#type'] = 'actions'; + $form['actions']['submit'] = array( '#type' => 'submit', '#weight' => 10, '#value' => $signup->settings['submit_button'], @@ -382,7 +386,8 @@ function mailchimp_signup_subscribe_form_submit($form, &$form_state) { } } if (count($successes) && isset($signup->settings['confirmation_message']) && strlen($signup->settings['confirmation_message'])) { - drupal_set_message(check_plain($signup->settings['confirmation_message']), 'status'); + $message = mailchimp_signup_tt("field:mailchimp_signup:form:$signup->mcs_id:confirmation_message", check_plain($signup->settings['confirmation_message'])); + drupal_set_message($message, 'status'); } if (!empty($signup->settings['destination'])) { $form_state['redirect'] = $signup->settings['destination']; @@ -400,6 +405,22 @@ function mailchimp_signup_entity_delete($entity, $type) { } } +/** + * Wrapper function for i18n_string() if i18nstrings enabled. + */ +function mailchimp_signup_tt($name, $string, $langcode = NULL, $update = FALSE) { + if (function_exists('i18n_string')) { + $options = array( + 'langcode' => $langcode, + 'update' => $update, + ); + return i18n_string($name, $string, $options); + } + else { + return $string; + } +} + /** * Implements hook_feeds_node_processor_targets_alter(). */ diff --git a/www7/sites/all/modules/contrib/scald/includes/ScaldAtom.inc b/www7/sites/all/modules/contrib/scald/includes/ScaldAtom.inc index d1a573792..86f98a8e9 100644 --- a/www7/sites/all/modules/contrib/scald/includes/ScaldAtom.inc +++ b/www7/sites/all/modules/contrib/scald/includes/ScaldAtom.inc @@ -20,6 +20,13 @@ class ScaldAtom { 'data' => array(), ); + if (module_exists('entity_translation')) { + unset($values['language']); + $handler = entity_translation_get_handler('scald_atom',(object) $values); + $langcode = $handler->getLanguage(); + $values['language'] = $langcode; + } + foreach ($values as $key => $value) { $this->$key = $value; } diff --git a/www7/sites/all/modules/contrib/scald/includes/scald.debug.inc b/www7/sites/all/modules/contrib/scald/includes/scald.debug.inc deleted file mode 100644 index 8184e4879..000000000 --- a/www7/sites/all/modules/contrib/scald/includes/scald.debug.inc +++ /dev/null @@ -1,25 +0,0 @@ - TRUE, '#default_value' => $atom->title, '#parents' => array('atom' . $delta, 'title'), + '#maxlength' => 255, '#weight' => -10, ); $form['language'] = array( @@ -351,8 +352,17 @@ function scald_atom_add_form_options_submit(&$form, &$form_state) { // Let entity add its properties to the atom. entity_form_submit_build_entity('scald_atom', $atom, $form['atom' . $delta], $form_state); - $atom->title = $form_state['values']['atom' . $delta]['title']; $atom->actions = $form_state['values']['atom' . $delta]['actions']; + + // Our form structure is different from standard Entity form because we + // support multiple entities in a single form, so we have to process special + // stuffs here. + // Except when comes the Title module, which is a field and it takes care of + // the sync in title_field_attach_submit() so we don't have to do anything. + if (!(module_exists('title') && title_field_replacement_enabled('scald_atom', $atom->type, 'title'))) { + $atom->title = $form_state['values']['atom' . $delta]['title']; + } + // Then save it... scald_atom_save($atom); } @@ -492,7 +502,7 @@ function scald_atom_fetch_atoms($sids) { $atoms = scald_atom_load_multiple(explode(',', $sids)); // Context can be passed via the querystring. - $context = isset($_GET['context']) && array_key_exists($_GET['context'], scald_contexts()) ? $_GET['context'] : ''; + $context = isset($_GET['context']) && array_key_exists($_GET['context'], scald_contexts_public()) ? $_GET['context'] : ''; foreach ($atoms as $sid => $atom) { $output[$sid] = array( diff --git a/www7/sites/all/modules/contrib/scald/includes/scald.translation_handler.inc b/www7/sites/all/modules/contrib/scald/includes/scald.translation_handler.inc new file mode 100644 index 000000000..5dc706174 --- /dev/null +++ b/www7/sites/all/modules/contrib/scald/includes/scald.translation_handler.inc @@ -0,0 +1,50 @@ +entity, array('fetch', 'view')); + } + + /** + * Changes the editPath when necessary. + */ + public function getEditPath($langcode = NULL) { + $edit_path = parent::getEditPath($langcode); + return str_replace('/%ctools_js', '/nojs', $edit_path); + } + + /** + * Tweaks the product form to support multilingual elements. + */ + public function entityForm(&$form, &$form_state) { + parent::entityForm($form, $form_state); + $translations = $this->getTranslations(); + $is_translation = $this->isTranslationForm(); + $form_langcode = $this->getFormLanguage(); + $new_translation = !isset($translations->data[$form_langcode]); + if ($is_translation && !$new_translation) { + $form['actions']['delete_translation'] = array( + '#type' => 'submit', + '#value' => t('Delete translation'), + '#weight' => 50, + '#submit' => array('entity_translation_entity_form_delete_translation_submit'), + ); + } + } +} diff --git a/www7/sites/all/modules/contrib/scald/includes/scald_views_handler_field_representation.inc b/www7/sites/all/modules/contrib/scald/includes/scald_views_handler_field_representation.inc index e125a6856..fc763d3b6 100644 --- a/www7/sites/all/modules/contrib/scald/includes/scald_views_handler_field_representation.inc +++ b/www7/sites/all/modules/contrib/scald/includes/scald_views_handler_field_representation.inc @@ -29,11 +29,9 @@ class scald_views_handler_field_representation extends views_handler_field { parent::options_form($form, $form_state); $options = array(); - $contexts = scald_contexts(); + $contexts = scald_contexts_public(); foreach ($contexts as $name => $context) { - if (empty($context['hidden'])) { - $options[$name] = $context['title']; - } + $options[$name] = $context['title']; } $form['context'] = array( '#type' => 'select', diff --git a/www7/sites/all/modules/contrib/scald/modules/fields/atom_reference/atom_reference.info b/www7/sites/all/modules/contrib/scald/modules/fields/atom_reference/atom_reference.info index 84a16f9dc..861f7a1fe 100644 --- a/www7/sites/all/modules/contrib/scald/modules/fields/atom_reference/atom_reference.info +++ b/www7/sites/all/modules/contrib/scald/modules/fields/atom_reference/atom_reference.info @@ -11,9 +11,9 @@ files[] = atom_reference.test files[] = atom_reference.migrate.inc -; Information added by Drupal.org packaging script on 2015-05-21 -version = "7.x-1.4" +; Information added by Drupal.org packaging script on 2015-09-16 +version = "7.x-1.5" core = "7.x" project = "scald" -datestamp = "1432202882" +datestamp = "1442418555" diff --git a/www7/sites/all/modules/contrib/scald/modules/fields/atom_reference/atom_reference.js b/www7/sites/all/modules/contrib/scald/modules/fields/atom_reference/atom_reference.js index b07dc6080..5b92ec24d 100644 --- a/www7/sites/all/modules/contrib/scald/modules/fields/atom_reference/atom_reference.js +++ b/www7/sites/all/modules/contrib/scald/modules/fields/atom_reference/atom_reference.js @@ -16,14 +16,14 @@ Drupal.behaviors.atom_reference = { var this_behavior_attach = this; // Record if the edit target link modal frame is updated - $('.ctools-modal-content form').bind('formUpdated', function() { + $('.ctools-modal-content form', context).bind('formUpdated', function() { this_behavior_attach['update_atom_reference_drop_zone'] = true; }); // Update drop zone (especially when returning from the edit modal frame). if (typeof(this.update_atom_reference_drop_zone) !== 'undefined') { this.update_atom_reference_drop_zone = undefined; - $('div.atom_reference_drop_zone.atom_reference_processed').each(function() { + $('div.atom_reference_drop_zone.atom_reference_processed', context).each(function() { var $this = $(this); var rendering_context = $this.attr('data-rendering-context'); var match_atom_id = //g.exec($this.html()); diff --git a/www7/sites/all/modules/contrib/scald/modules/fields/atom_reference/atom_reference.module b/www7/sites/all/modules/contrib/scald/modules/fields/atom_reference/atom_reference.module index 70ef2578d..0acdce1e3 100644 --- a/www7/sites/all/modules/contrib/scald/modules/fields/atom_reference/atom_reference.module +++ b/www7/sites/all/modules/contrib/scald/modules/fields/atom_reference/atom_reference.module @@ -39,7 +39,7 @@ function atom_reference_field_info() { 'instance_settings' => array( 'referencable_types' => array(), ), - 'default_widget' => 'textfield', + 'default_widget' => 'atom_reference_textfield', 'default_formatter' => 'title', 'property_type' => 'scald_atom', ) @@ -157,15 +157,13 @@ function atom_reference_field_is_empty($item, $field) { function atom_reference_field_formatter_info() { // Expose all the Scald Contexts as formatters for the Atom Reference field. $formatters = array(); - $contexts = scald_contexts(); + $contexts = scald_contexts_public(); foreach ($contexts as $name => $context) { - if (empty($context['hidden'])) { - $formatters[$name] = array( - 'label' => $context['title'], - 'field types' => array('atom_reference'), - 'settings' => array('link' => 0, 'override' => 0), - ); - } + $formatters[$name] = array( + 'label' => $context['title'], + 'field types' => array('atom_reference'), + 'settings' => array('link' => 0, 'override' => 0), + ); } return $formatters; @@ -276,9 +274,12 @@ function atom_reference_field_widget_settings_form($field, $instance) { '#description' => t('Scald preview context to be displayed in the edit form.'), ); - $contexts = scald_contexts(); + $contexts = scald_contexts_public(); foreach ($contexts as $name => $context) { - if (empty($context['hidden']) && !empty($context['parseable'])) { + // TODO: Atom reference shouldn't need the context to be parsable, there's + // nothing converting back and forth between the SAS and the rendered + // representation. + if (!empty($context['parseable'])) { $form['context']['#options'][$name] = $context['title']; } } @@ -338,10 +339,10 @@ function atom_reference_field_widget_form(&$form, &$form_state, $field, $instanc ); $context_element['#weight'] = 20; - $contexts = scald_contexts(); + $contexts = scald_contexts_public(); $context_element['#options']['use_the_default'] = t('Use the default'); foreach ($contexts as $name => $context) { - if (empty($context['hidden']) && $name !== 'sdl_library_item') { + if ($name !== 'sdl_library_item') { $context_element['#options'][$name] = $context['title']; } } @@ -405,3 +406,103 @@ function atom_reference_field_widget_error($element, $error, $form, &$form_state $name = implode('][', $element['sid']['#array_parents']); form_set_error($name, $error['message']); } + +/** + * Provide default field comparison options. + */ +function atom_reference_field_diff_default_options($field_type) { + return array( + 'show_id' => 0, + 'show_type' => 1, + 'entity_title' => 'Atom', + ); +} + +/** + * Provide a form for setting the field comparison options. + */ +function atom_reference_field_diff_options_form($field_type, $settings) { + $options_form = array(); + $options_form['show_id'] = array( + '#type' => 'checkbox', + '#title' => t('Show atom id'), + '#default_value' => $settings['show_id'], + ); + + $options_form['show_type'] = array( + '#type' => 'checkbox', + '#title' => t('Show atom type'), + '#default_value' => $settings['show_type'], + ); + + $options_form['entity_title'] = array( + '#type' => 'textfield', + '#title' => t('The title to use for the atom entity'), + '#default_value' => $settings['entity_title'], + '#description' => t('This can be useful if you call Atoms differently on your website, such as Resources.') + ); + + return $options_form; +} + +/** + * Diff field callback for preloading the scald atom entities. + */ +function atom_reference_field_diff_view_prepare(&$old_items, &$new_items, $context) { + $sids = array(); + foreach (array_merge_recursive($old_items, $new_items) as $info) { + $sids[$info['sid']] = $info['sid']; + } + $atoms = scald_atom_load_multiple($sids); + + foreach ($old_items as $delta => $info) { + $old_items[$delta]['atom'] = isset($atoms[$info['sid']]) ? $atoms[$info['sid']] : NULL; + } + foreach ($new_items as $delta => $info) { + $new_items[$delta]['atom'] = isset($atoms[$info['sid']]) ? $atoms[$info['sid']] : NULL; + } +} + +/** + * Diff field callback for parsing atom_reference field comparative values. + */ +function atom_reference_field_diff_view($items, $context) { + $instance = $context['instance']; + $settings = $context['settings']; + + $diff_items = array(); + foreach ($items as $delta => $item) { + if (!isset($item['atom'])) { + continue; + } + + $diff_items[$delta] = $item['atom']->title; + + if ($settings['show_id'] || $settings['show_type']) { + $diff_items[$delta] .= ' ('; + } + + if ($settings['show_type']) { + $diff_items[$delta] .= t('!type', array('!type' => ucfirst($item['atom']->type))); + } + + if ($settings['show_id']) { + if ($settings['show_type']) { + $diff_items[$delta] .= ', '; + } + $diff_items[$delta] .= t( + '@entity_name ID: !id', + array( + '@entity_name' => $settings['entity_title'], + '!id' => $item['atom']->sid + ) + ); + } + + if ($settings['show_id'] || $settings['show_type']) { + $diff_items[$delta] .= ')'; + } + } + + return $diff_items; +} diff --git a/www7/sites/all/modules/contrib/scald/modules/fields/mee/mee.info b/www7/sites/all/modules/contrib/scald/modules/fields/mee/mee.info index 2a1fab4e1..52edc6561 100644 --- a/www7/sites/all/modules/contrib/scald/modules/fields/mee/mee.info +++ b/www7/sites/all/modules/contrib/scald/modules/fields/mee/mee.info @@ -7,9 +7,9 @@ dependencies[] = dnd dependencies[] = scald core = 7.x -; Information added by Drupal.org packaging script on 2015-05-21 -version = "7.x-1.4" +; Information added by Drupal.org packaging script on 2015-09-16 +version = "7.x-1.5" core = "7.x" project = "scald" -datestamp = "1432202882" +datestamp = "1442418555" diff --git a/www7/sites/all/modules/contrib/scald/modules/fields/mee/mee.module b/www7/sites/all/modules/contrib/scald/modules/fields/mee/mee.module index f93d9471a..43d91c8c0 100644 --- a/www7/sites/all/modules/contrib/scald/modules/fields/mee/mee.module +++ b/www7/sites/all/modules/contrib/scald/modules/fields/mee/mee.module @@ -112,7 +112,7 @@ function mee_form_alter(&$form, &$form_state, $form_id) { $settings = $form['#instance']['settings']; $context_options = array(); - foreach (scald_contexts() as $name => $context) { + foreach (scald_contexts_public() as $name => $context) { $context_options[$name] = $context['title']; } @@ -288,7 +288,7 @@ function mee_field_attach_view_alter(&$output, $context) { $output[$name][$key]['#markup'] = mee_filter_process($output[$name][$key]['#markup']); } } - $output[$name][$key]['#markup'] = scald_sas_to_rendered($output[$name][$key]['#markup'], $field['settings']['context']); + $output[$name][$key]['#markup'] = scald_sas_to_rendered($output[$name][$key]['#markup'], $field['settings']['context'], FALSE, dnd_scald_wysiwyg_context_slugs()); } } } @@ -312,7 +312,7 @@ function mee_panels_pane_content_alter($content, $pane, $args, $context) { $content->content = mee_filter_process($content->content); } } - $content->content = scald_sas_to_rendered($content->content); + $content->content = scald_sas_to_rendered($content->content, NULL, FALSE, dnd_scald_wysiwyg_context_slugs()); } } @@ -334,7 +334,7 @@ function mee_field_widget_form_alter(&$element, &$form_state, $context) { // In any case, convert SAS into rendered for format textarea. if (in_array($context['field']['type'], mee_field_types()) && isset($element['#default_value'])) { if (mee_store_format() == 'sas') { - $element['#default_value'] = scald_sas_to_rendered($element['#default_value'], $context['instance']['settings']['context'], FALSE, TRUE); + $element['#default_value'] = scald_sas_to_rendered($element['#default_value'], $context['instance']['settings']['context'], FALSE, dnd_scald_wysiwyg_context_slugs()); } } @@ -544,10 +544,19 @@ function theme_mee_resource_manager($variables) { * The HTML for the atom embed. */ function theme_mee_widget_embed($vars) { + $options = array(); + if (isset($vars['options'])) { + $options = drupal_json_decode($vars['options']); + } $classes = array('dnd-widget-wrapper', 'context-' . $vars['context'], 'type-' . $vars['atom']->type); if ($vars['align'] != 'none') { $classes[] = 'atom-align-' . $vars['align']; } + if (!empty($options['additionalClasses'])) { + foreach (explode(' ', $options['additionalClasses']) as $class) { + $classes[] = $class; + } + } $output = '
'; $output .= '
' . $vars['content'] . '
'; @@ -555,7 +564,7 @@ function theme_mee_widget_embed($vars) { // When displaying a widget within a CKEditor, always include a container div // for the editable. Otherwise, only display the caption container if there // is a caption. - if ($vars['caption'] || $vars['wysiwyg']) { + if (!empty($vars['caption']) || $vars['wysiwyg']) { // Note: The 'dnd-caption-wrapper' class is used by the CKEditor plugin to // identify the editable zone and should not be modified by theme overrides. $output .= '
' . $vars['caption'] . '
'; @@ -577,6 +586,26 @@ function theme_mee_widget_embed($vars) { * Other parameters, such as context, could also be passed via the querystring. */ function mee_ajax_widget_expand($atom) { + $context = (isset($_GET['context']) && in_array($_GET['context'], dnd_scald_wysiwyg_context_slugs())) ? $_GET['context'] : NULL; + if ($atom->type == 'scald_atom_fallback') { + $context = 'invalid-id'; + } + $options = isset($_GET['options']) ? urldecode($_GET['options']) : ''; + $align = (isset($_GET['align']) && in_array($_GET['align'], array('left', 'right', 'center'))) ? $_GET['align'] : 'none'; + + // The legend call needs at least the basic atom meta-data + // to be pre-rendered, so ensure they are present by doing + // an early render, eventually in the lightweight 'title' + // context if no explicit context is given. + $output = $context ? scald_render($atom, $context, $options) : scald_render($atom, 'title'); + + if (empty($atom->omit_legend)) { + $legend = theme('sdl_editor_legend', array('atom' => $atom)); + } + else { + $legend = ''; + } + $commands = array(); $commands[] = array( @@ -588,17 +617,12 @@ function mee_ajax_widget_expand($atom) { 'type' => $atom->type, 'data' => !empty($atom->data) ? $atom->data : array(), 'provider' => $atom->provider, + 'legend' => $legend, ), 'actions' => array_keys(scald_atom_actions_available($atom)), ), ); - $context = (isset($_GET['context']) && in_array($_GET['context'], array_keys(scald_contexts()))) ? $_GET['context'] : NULL; - if ($atom->type == 'scald_atom_fallback') { - $context = 'invalid-id'; - } - $options = isset($_GET['options']) ? urldecode($_GET['options']) : ''; - $align = (isset($_GET['align']) && in_array($_GET['align'], array('left', 'right', 'center'))) ? $_GET['align'] : 'none'; if ($context) { $commands[] = array( @@ -608,7 +632,7 @@ function mee_ajax_widget_expand($atom) { 'context' => $context, 'options' => $options, 'align' => $align, - 'content' => scald_render($atom->sid, $context, $options), + 'content' => $output, 'wysiwyg' => TRUE, )), ); diff --git a/www7/sites/all/modules/contrib/scald/modules/library/dnd/css/dnd-library.css b/www7/sites/all/modules/contrib/scald/modules/library/dnd/css/dnd-library.css index c906785d2..a38398e09 100644 --- a/www7/sites/all/modules/contrib/scald/modules/library/dnd/css/dnd-library.css +++ b/www7/sites/all/modules/contrib/scald/modules/library/dnd/css/dnd-library.css @@ -377,6 +377,7 @@ padding-top: 50px; padding-bottom: 20px; width: 42px; + float: left; } .scald-menu .add-buttons .item-list { diff --git a/www7/sites/all/modules/contrib/scald/modules/library/dnd/dnd.admin.inc b/www7/sites/all/modules/contrib/scald/modules/library/dnd/dnd.admin.inc index cc333cfda..7a185541e 100644 --- a/www7/sites/all/modules/contrib/scald/modules/library/dnd/dnd.admin.inc +++ b/www7/sites/all/modules/contrib/scald/modules/library/dnd/dnd.admin.inc @@ -37,6 +37,14 @@ function dnd_admin_form() { } $mee_store_format = (module_exists('mee')) ? mee_store_format() : ''; if (!empty($mee_store_format)) { + if ($mee_store_format === 'embed_div') { + $form['dnd_uses_caption_default'] = array( + '#type' => 'checkbox', + '#title' => t('Enable captions by default'), + '#default_value' => variable_get('dnd_uses_caption_default', TRUE), + '#description' => t('If enabled, captions in CKEditor will show up under Atoms by default.'), + ); + } $form['mee_store_format'] = array( '#type' => 'select', '#options' => array( diff --git a/www7/sites/all/modules/contrib/scald/modules/library/dnd/dnd.info b/www7/sites/all/modules/contrib/scald/modules/library/dnd/dnd.info index a75c9e494..991228c30 100644 --- a/www7/sites/all/modules/contrib/scald/modules/library/dnd/dnd.info +++ b/www7/sites/all/modules/contrib/scald/modules/library/dnd/dnd.info @@ -7,9 +7,9 @@ dependencies[] = scald stylesheets[all][] = css/editor-global.css -; Information added by Drupal.org packaging script on 2015-05-21 -version = "7.x-1.4" +; Information added by Drupal.org packaging script on 2015-09-16 +version = "7.x-1.5" core = "7.x" project = "scald" -datestamp = "1432202882" +datestamp = "1442418555" diff --git a/www7/sites/all/modules/contrib/scald/modules/library/dnd/dnd.module b/www7/sites/all/modules/contrib/scald/modules/library/dnd/dnd.module index 0494560e3..1f890934a 100644 --- a/www7/sites/all/modules/contrib/scald/modules/library/dnd/dnd.module +++ b/www7/sites/all/modules/contrib/scald/modules/library/dnd/dnd.module @@ -42,30 +42,62 @@ function dnd_theme() { } /** - * Implements hook_library(). + * Get the list of Scald contexts that can be used in a WYSIWYG, keyed by type. + * + * @return array + * Returns an associative array, keyed by the atom type machine name and whose + * values are associative arrays, each keyed by the context machine name and + * whose values are the user facing name of this context for this atom type. */ -function dnd_library() { - $path = drupal_get_path('module', 'dnd'); - $types = scald_types(); - $contexts = array(); - $config = array(); +function dnd_scald_wysiwyg_context_list() { + $contexts = &drupal_static(__FUNCTION__, NULL); + if (!isset($contexts)) { + $types = scald_types(); + foreach (scald_contexts_public() as $name => $definition) { + if (empty($definition['parseable'])) { + continue; + } - foreach (scald_contexts() as $name => $definition) { - if (!empty($definition['hidden']) || empty($definition['parseable'])) { - continue; + // There "formats" is actually used nowhere in Scald. Every context is + // available to all atom types. + $definition['formats'] = $types; + + foreach ($definition['formats'] as $type => $data) { + $contexts[$type][$name] = $definition['title']; + } } + drupal_alter('scald_wysiwyg_context_list', $contexts); + } - // There "formats" is actually used nowhere in Scald. Every context is - // available to all atom types. - $definition['formats'] = $types; + return $contexts; +} - foreach ($definition['formats'] as $type => $data) { - $contexts[$type][$name] = $definition['title']; +/** + * Get the list of Scald contexts machine names that are allowed in WYSIWYG. + */ +function dnd_scald_wysiwyg_context_slugs() { + $contexts_type = dnd_scald_wysiwyg_context_list(); + $slugs = array(); + foreach ($contexts_type as $contexts) { + foreach ($contexts as $slug => $label) { + $slugs[$slug] = $slug; } + } + + return array_values($slugs); +} + +/** + * Implements hook_library(). + */ +function dnd_library() { + $path = drupal_get_path('module', 'dnd'); + $contexts = dnd_scald_wysiwyg_context_list(); + $config = array(); - $config[$name] = scald_context_config_load($name); + foreach (dnd_scald_wysiwyg_context_slugs() as $slug) { + $config[$slug] = scald_context_config_load($slug); } - drupal_alter('scald_wysiwyg_context_list', $contexts); $qtip_settings = ''; if (function_exists('qtip_fetch_instances_field')) { @@ -75,6 +107,8 @@ function dnd_library() { } } + $plugin = mee_store_format() !== 'embed_div' ? 'ckeditor' : 'dndck4'; + $libraries['library'] = array( 'title' => 'DnD Library', 'website' => 'http://drupal.org/project/scald', @@ -90,7 +124,7 @@ function dnd_library() { // This file is included automatically if the CKEditor plugin is enabled. // However we need to load it directly in Drupal so that strings can be // translated. - $path . '/plugins/ckeditor/lang/en.js' => array(), + $path . '/plugins/' . $plugin . '/lang/en.js' => array(), // Settings for the library url. array( 'type' => 'setting', @@ -99,6 +133,7 @@ function dnd_library() { 'url' => url(dnd_get_library()), 'contexts' => $contexts, 'contextDefault' => variable_get('dnd_context_default', 'sdl_editor_representation'), + 'usesCaptionDefault' => variable_get('dnd_uses_caption_default', TRUE), 'contexts_config' => $config, 'qTipSettings' => $qtip_settings, ), @@ -208,26 +243,43 @@ function dnd_library_alter(&$libraries, $module) { } /** - * Implements hook_edit_editor_attachments_alter(). + * Implements hook_entity_view_alter(). + * + * Adds the dnd library in case we have quickedit enabled and dnd enabled on the entity. */ -function dnd_edit_editor_attachments_alter(&$attachments, $editor, $metadata) { - // When Edit module uses CKEditor editor widget, add DnD library if necessary. - if ($editor == 'ckeditor') { - // If there is at least one field with CKEditor + DnD enabled, add the DnD - // library. - foreach ($metadata as $field => $data) { - if ($data['editor'] == 'ckeditor') { - list($entity_type, $entity_id, $field_name, $language, $view_mode) = explode('/', $field); - $entity = entity_load_single($entity_type, $entity_id); - list($id, $vid, $bundle) = entity_extract_ids($entity_type, $entity); - $instance_info = field_info_instance($entity_type, $field_name, $bundle); - if (!empty($instance_info['settings']['dnd_enabled'])) { - $attachments['library'][] = array('dnd', 'library'); - return; - } +function dnd_entity_view_alter(&$build, $type) { + if (!module_exists('quickedit')) { + return; + } + + if (!user_access('access in-place editing')) { + return; + } + + // In-place editing is only supported on the front-end. + if (path_is_admin(current_path())) { + return; + } + + $dnd_enabled = FALSE; + foreach ($build as $item) { + if (!is_array($item)) { + continue; + } + if (isset($item['#field_name'])) { + $instance_info = field_info_instance($type, $item['#field_name'], $build['#bundle']); + if (!empty($instance_info['settings']['dnd_enabled'])) { + $dnd_enabled = TRUE; + } + if (isset($item['#field_type']) && $item['#field_type'] === 'atom_reference') { + $dnd_enabled = TRUE; } } } + if ($dnd_enabled) { + $build['#attached']['library'][] = array('dnd', 'library'); + } + return; } /** diff --git a/www7/sites/all/modules/contrib/scald/modules/library/dnd/js/dnd-library.js b/www7/sites/all/modules/contrib/scald/modules/library/dnd/js/dnd-library.js index 6f5e6e64f..437990e6f 100644 --- a/www7/sites/all/modules/contrib/scald/modules/library/dnd/js/dnd-library.js +++ b/www7/sites/all/modules/contrib/scald/modules/library/dnd/js/dnd-library.js @@ -220,7 +220,8 @@ Drupal.theme.prototype.scaldEmbed = function(atom, context, options) { // If there are options, update the SAS representation. if (options) { - output = output.replace(//, ''); + options = (typeof options === 'string') ? options.trim() : JSON.stringify(options); + output = output.replace(//, ''); } return output; diff --git a/www7/sites/all/modules/contrib/scald/modules/library/dnd/plugins/ckeditor/plugin.js b/www7/sites/all/modules/contrib/scald/modules/library/dnd/plugins/ckeditor/plugin.js index abb3fd9d8..e1a9afd4c 100644 --- a/www7/sites/all/modules/contrib/scald/modules/library/dnd/plugins/ckeditor/plugin.js +++ b/www7/sites/all/modules/contrib/scald/modules/library/dnd/plugins/ckeditor/plugin.js @@ -40,7 +40,8 @@ CKEDITOR.plugins.add('dnd', { // Assign the "insert atom into editor" method to be used for this editor. editor.dndInsertAtom = function(sid) { - var markup = Drupal.theme('scaldEmbed', Drupal.dnd.Atoms[sid]); + var atom = Drupal.dnd.sas2array(Drupal.dnd.Atoms[sid].sas); + var markup = Drupal.theme('scaldEmbed', Drupal.dnd.Atoms[sid], atom.context, atom.options); editor.insertElement(CKEDITOR.dom.element.createFromHtml(markup)); }; @@ -145,6 +146,13 @@ CKEDITOR.plugins.add('dnd', { dnd.protectAtom($(editor.document.$).find('.dnd-atom-wrapper')); }); + // Prevent paste, so the new clipboard plugin will not double insert the Atom. + editor.on('paste', function (evt) { + if (Drupal.dnd.sas2array(evt.data.dataTransfer.getData('Text'))) { + return false; + } + }); + editor.document.on('click', function (evt) { var element = dnd.getWrapperElement(evt.data.getTarget()); if (element) { diff --git a/www7/sites/all/modules/contrib/scald/modules/library/dnd/plugins/dndck4/dialogs/atomProperties.js b/www7/sites/all/modules/contrib/scald/modules/library/dnd/plugins/dndck4/dialogs/atomProperties.js index 9580824fb..61ec4a19e 100644 --- a/www7/sites/all/modules/contrib/scald/modules/library/dnd/plugins/dndck4/dialogs/atomProperties.js +++ b/www7/sites/all/modules/contrib/scald/modules/library/dnd/plugins/dndck4/dialogs/atomProperties.js @@ -31,7 +31,7 @@ CKEDITOR.dialog.add('atomProperties', function(editor) { contents: [ { id: 'info', - label: '', + label: lang.tab_info, title: '', expand: true, padding: 0, @@ -84,6 +84,31 @@ CKEDITOR.dialog.add('atomProperties', function(editor) { } } ] + }, + { + id: 'advanced', + label: lang.tab_advanced, + title: '', + expand: true, + padding: 0, + elements: [ + { + id: 'txtClasses', + type: 'text', + label: lang.properties_classes, + setup: function(widget) { + var options = JSON.parse(widget.data.options); + if (options.additionalClasses) { + this.setValue(options.additionalClasses); + } + }, + commit: function(widget) { + var options = JSON.parse(widget.data.options); + options.additionalClasses = this.getValue(); + widget.setData('options', JSON.stringify(options)); + } + } + ] } ] }; diff --git a/www7/sites/all/modules/contrib/scald/modules/library/dnd/plugins/dndck4/lang/en.js b/www7/sites/all/modules/contrib/scald/modules/library/dnd/plugins/dndck4/lang/en.js index 9ac3bf6c8..6e6dd4133 100644 --- a/www7/sites/all/modules/contrib/scald/modules/library/dnd/plugins/dndck4/lang/en.js +++ b/www7/sites/all/modules/contrib/scald/modules/library/dnd/plugins/dndck4/lang/en.js @@ -13,8 +13,11 @@ if (typeof CKEDITOR !== 'undefined' && typeof CKEDITOR.plugins !== 'undefined') atom_delete: Drupal.t('Delete'), atom_none: Drupal.t('Please select an atom first'), properties_has_caption: Drupal.t('Add a caption'), + properties_classes: Drupal.t('CSS Classes'), properties_context: Drupal.t('Context'), properties_alignment: Drupal.t('Alignment'), + tab_advanced: Drupal.t('Advanced'), + tab_info: Drupal.t('Atom Properties'), alignment_none: Drupal.t('None'), alignment_left: Drupal.t('Left'), alignment_right: Drupal.t('Right'), diff --git a/www7/sites/all/modules/contrib/scald/modules/library/dnd/plugins/dndck4/plugin.js b/www7/sites/all/modules/contrib/scald/modules/library/dnd/plugins/dndck4/plugin.js index f347494ef..afe3af6ed 100644 --- a/www7/sites/all/modules/contrib/scald/modules/library/dnd/plugins/dndck4/plugin.js +++ b/www7/sites/all/modules/contrib/scald/modules/library/dnd/plugins/dndck4/plugin.js @@ -1,5 +1,5 @@ (function ($) { -// Drop out if for some reason Drupal.dnd is not available +// Drop out if for some reason Drupal.dnd is not available. if (typeof Drupal.dnd === 'undefined') { CKEDITOR.plugins.add('dndck4', {}); return; @@ -88,7 +88,10 @@ CKEDITOR.plugins.add('dndck4', { * initial creation or upcast). */ data: function() { - this.refreshAtom(); + // Don't refresh dragged atom until it's actually dropped. + if (this.element.$.parentNode.parentNode) { + this.refreshAtom(); + } } }); @@ -105,9 +108,14 @@ CKEDITOR.plugins.add('dndck4', { // Listen to atom drags from the Library. Namespace the event so that we // can unbind it when the editor is disabled. $(document).bind('dragstart.dndck4_' + editor.name, function (evt) { - if (Drupal.dnd.currentAtom) { + var editable = editor.editable(); + if (Drupal.dnd.currentAtom && $(editable.$).is(':visible')) { var dragInfo = Drupal.dnd.sas2array(Drupal.dnd.currentAtom); - Drupal.dndck4.onLibraryAtomDrag(editor, Drupal.dnd.Atoms[dragInfo.sid]); + var atomInfo = Drupal.dnd.Atoms[dragInfo.sid]; + var data = Drupal.dndck4.getDefaultInsertData(editor, atomInfo); + var caption = atomInfo.meta.legend || ''; + var widget = Drupal.dndck4.createNewWidget(editor, data, caption); + Drupal.dndck4.onLibraryAtomDrag.call(widget, atomInfo); } }); }); @@ -297,20 +305,51 @@ CKEDITOR.plugins.add('dndck4', { editor.addCommand('atomPaste', { exec: function (editor) { if (Drupal.dndck4.atomPaste) { - editor.fire('saveSnapshot'); - editor.fire('lockSnapshot', {dontUpdate: 1}); - var range = editor.getSelection().getRanges()[0]; + // insertNewWidget already handles snapshot. Drupal.dndck4.insertNewWidget(editor, range, Drupal.dndck4.atomPaste.data, Drupal.dndck4.atomPaste.caption); - - editor.fire('unlockSnapshot'); - editor.fire('saveSnapshot'); } }, canUndo: false, editorFocus: CKEDITOR.env.ie || CKEDITOR.env.webkit }); + }, + + afterInit: function (editor) { + function setupAlignCommand(value) { + var command = editor.getCommand('justify' + value); + if (command) { + if (value in {right: 1, left: 1, center: 1}) { + command.on('exec', function (event) { + var widget = editor.widgets.focused; + if (widget && widget.name === 'dndck4') { + widget.setData({align: value}); + } + }); + } + + command.on('refresh', function (event) { + var widget = editor.widgets.focused, + allowed = { left: 1, center: 1, right: 1 }, + align; + + if (widget && widget.name === 'dndck4') { + align = widget.data.align; + + this.setState( + (align === value) ? CKEDITOR.TRISTATE_ON : (value in allowed) ? CKEDITOR.TRISTATE_OFF : CKEDITOR.TRISTATE_DISABLED); + + event.cancel(); + } + }); + } + } + + // Customize the behavior of the alignment commands. + setupAlignCommand('left'); + setupAlignCommand('right'); + setupAlignCommand('center'); } }); @@ -361,6 +400,36 @@ Drupal.dndck4 = { } }, + addOption: function(id, type, mode, name, callback) { + $('body').once(id, function() { + if (typeof CKEDITOR === 'undefined') { + // CKEditor is not available yet, lets try a little bit later. + setTimeout(function() { + // If It's still not available, stop trying. + if (typeof CKEDITOR !== 'undefined') { + Drupal.dndck4.processOption(id, type, mode, name, callback); + } + }, 1000); + } + else { + Drupal.dndck4.processOption(id, type, mode, name, callback); + } + }); + }, + + processOption: function(id, type, mode, name, callback) { + CKEDITOR.on('dialogDefinition', function(ev) { + if (typeof Drupal.dndck4 !== 'undefined') { + if (ev.data.name == 'atomProperties') { + var dialogDefinition = ev.data.definition; + var infoTab = dialogDefinition.getContents('info'); + callback.call(this, infoTab, dialogDefinition); + Drupal.dndck4.registerOptions(id, type, mode, name); + } + } + }); + }, + dataFromAttributes: function (attributes) { return { sid : attributes['data-scald-sid'], @@ -409,26 +478,29 @@ Drupal.dndck4 = { // options in the sas code for the atom. options : sasData.options || '{}', align : 'none', - usesCaption : true + usesCaption : Drupal.settings.dnd.usesCaptionDefault }; }, - insertNewWidget: function(editor, range, data, caption) { - // Group all following operations in one snapshot. - editor.fire('saveSnapshot'); - editor.fire('lockSnapshot', {dontUpdate: 1}); - + createNewWidget: function(editor, data, caption) { // Generate the downcasted HTML for the widget, and run upcast() on it. var html = Drupal.dndck4.downcastedHtml(data, caption); var element = CKEDITOR.htmlParser.fragment.fromHtml(html).children[0]; element = editor.widgets.registered.dndck4.upcast(element); // Turn it into a proper DOM element, and insert it. element = CKEDITOR.dom.element.createFromHtml(element.getOuterHtml()); - range.select(); - editor.insertElement(element); // Promote it to a widget. This runs the init() / data() methods, which // fetches the expanded HTML for the atom embed. - var widget = editor.widgets.initOn(element, 'dndck4', data); + return editor.widgets.initOn(element, 'dndck4', data); + }, + + insertWidget: function(widget, editor, range) { + // Group all following operations in one snapshot. + editor.fire('saveSnapshot'); + editor.fire('lockSnapshot', {dontUpdate: 1}); + + editor.editable().insertElementIntoRange(widget.wrapper, range); + widget.ready = true; widget.fire('ready'); widget.focus(); @@ -439,16 +511,27 @@ Drupal.dndck4 = { editor.fire('saveSnapshot'); }, + insertNewWidget: function(editor, range, data, caption) { + var widget = Drupal.dndck4.createNewWidget(editor, data, caption); + Drupal.dndck4.insertWidget(widget, editor, range); + }, + // Heavily inspired from CKE widget plugin's onBlockWidgetDrag(). - onLibraryAtomDrag: function (editor, atomInfo) { - var finder = editor.widgets.finder, - locator = editor.widgets.locator, - liner = editor.widgets.liner, + onLibraryAtomDrag: function (atomInfo) { + var widget = this, + finder = widget.repository.finder, + locator = widget.repository.locator, + liner = widget.repository.liner, + editor = widget.editor, editable = editor.editable(), listeners = [], - sorted = []; + sorted = [], + dropRange = null; var relations, locations, y; + // Mark dragged widget for repository#finder. + this.repository._.draggedWidget = widget; + // Dropping into an empty CKEditor requires special logic. var editableHasContent = (editable.getFirst() != null); @@ -477,6 +560,7 @@ Drupal.dndck4 = { liner.placeLine(sorted[0]); liner.cleanup(); } + dropRange = finder.getRange(sorted[0]); }); // Let's have the "dragging cursor" over entire editable. @@ -506,8 +590,8 @@ Drupal.dndck4 = { listeners.push(dropElement.on('drop', function (evt) { evt.data.preventDefault(); var range; - if (editableHasContent && !CKEDITOR.tools.isEmpty(liner.visible)) { - range = finder.getRange(sorted[0]); + if (dropRange && editableHasContent && !CKEDITOR.tools.isEmpty(liner.visible)) { + range = dropRange; } else { // If no liner position was determined, insert at the end of the @@ -515,10 +599,14 @@ Drupal.dndck4 = { range = editor.createRange(); range.moveToElementEditablePosition(editable, true); } - var data = Drupal.dndck4.getDefaultInsertData(editor, atomInfo); - var caption = atomInfo.meta.legend || ''; - Drupal.dndck4.insertNewWidget(editor, range, data, caption); - cleanupDrag(); + Drupal.dndck4.insertWidget(widget, editor, range); + widget.refreshAtom(); + cleanupDrag(true); + })); + + // Prevent paste, so the new clipboard plugin will not double insert the Atom. + listeners.push(editor.on('paste', function (evt) { + return false; })); // On dragend (without drop), cleanup the events. @@ -534,13 +622,18 @@ Drupal.dndck4 = { // console.log('dragleave'); // })); - function cleanupDrag() { + function cleanupDrag(dropped) { // Stop observing events. eventBuffer.reset(); var l; while (l = listeners.pop()) { l.removeListener(); } + // Clean-up unused widget. + if (!dropped) { + widget.repository._.draggedWidget = null; + widget.repository.destroy(widget, true); + } // Clean-up all remaining lines. liner.hideVisible(); // Clean-up custom cursor for editable. @@ -605,6 +698,10 @@ Drupal.dndck4 = { var caption = ''; if (widget.editables.caption) { caption = widget.editables.caption.getHtml(); + + if (caption == '') { + caption = Drupal.dnd.Atoms[widget.data.sid].meta.legend || ''; + } widget.destroyEditable('caption'); } @@ -616,7 +713,7 @@ Drupal.dndck4 = { // Replace the inner HTML. widgetElement.setHtml(newElement.getHtml()); - // Notify external scripts of new atom rendering + // Notify external scripts of new atom rendering. Drupal.dndck4.invokeCallbacks('AjaxExpandWidget', widget); // Initialize the new caption editable, and fill it with the previous diff --git a/www7/sites/all/modules/contrib/scald/modules/library/scald_dnd_library/scald_dnd_library.info b/www7/sites/all/modules/contrib/scald/modules/library/scald_dnd_library/scald_dnd_library.info index c624ba845..39096d9c5 100644 --- a/www7/sites/all/modules/contrib/scald/modules/library/scald_dnd_library/scald_dnd_library.info +++ b/www7/sites/all/modules/contrib/scald/modules/library/scald_dnd_library/scald_dnd_library.info @@ -10,9 +10,9 @@ files[] = includes/scald_dnd_library.views.inc files[] = includes/scald_plugin_display_library.inc files[] = includes/scald_plugin_style_library.inc -; Information added by Drupal.org packaging script on 2015-05-21 -version = "7.x-1.4" +; Information added by Drupal.org packaging script on 2015-09-16 +version = "7.x-1.5" core = "7.x" project = "scald" -datestamp = "1432202882" +datestamp = "1442418555" diff --git a/www7/sites/all/modules/contrib/scald/modules/providers/scald_audio/scald_audio.info b/www7/sites/all/modules/contrib/scald/modules/providers/scald_audio/scald_audio.info index ad49703c6..1603dc685 100644 --- a/www7/sites/all/modules/contrib/scald/modules/providers/scald_audio/scald_audio.info +++ b/www7/sites/all/modules/contrib/scald/modules/providers/scald_audio/scald_audio.info @@ -5,9 +5,9 @@ core = 7.x dependencies[] = scald -; Information added by Drupal.org packaging script on 2015-05-21 -version = "7.x-1.4" +; Information added by Drupal.org packaging script on 2015-09-16 +version = "7.x-1.5" core = "7.x" project = "scald" -datestamp = "1432202882" +datestamp = "1442418555" diff --git a/www7/sites/all/modules/contrib/scald/modules/providers/scald_flash/scald_flash.info b/www7/sites/all/modules/contrib/scald/modules/providers/scald_flash/scald_flash.info index 25ef4dac6..578500b0d 100644 --- a/www7/sites/all/modules/contrib/scald/modules/providers/scald_flash/scald_flash.info +++ b/www7/sites/all/modules/contrib/scald/modules/providers/scald_flash/scald_flash.info @@ -6,9 +6,9 @@ dependencies[] = scald stylesheets[all][] = scald_flash.css -; Information added by Drupal.org packaging script on 2015-05-21 -version = "7.x-1.4" +; Information added by Drupal.org packaging script on 2015-09-16 +version = "7.x-1.5" core = "7.x" project = "scald" -datestamp = "1432202882" +datestamp = "1442418555" diff --git a/www7/sites/all/modules/contrib/scald/modules/providers/scald_flash/scald_flash.module b/www7/sites/all/modules/contrib/scald/modules/providers/scald_flash/scald_flash.module index c7f4d2248..81927a320 100644 --- a/www7/sites/all/modules/contrib/scald/modules/providers/scald_flash/scald_flash.module +++ b/www7/sites/all/modules/contrib/scald/modules/providers/scald_flash/scald_flash.module @@ -72,6 +72,9 @@ function scald_flash_scald_fetch($atom, $type) { if ($items = field_get_items('scald_atom', $atom, 'scald_thumbnail')) { $atom->thumbnail_source = $items[0]['uri']; } + else { + $atom->thumbnail_source = drupal_get_path('module', 'scald_flash') . '/icons/flash.png'; + } } /** @@ -93,17 +96,42 @@ function scald_flash_scald_atom_insert($atom) { */ function scald_flash_scald_prerender($atom, $context, $options, $mode) { if ($mode == 'atom') { - $langcode = field_language('scald_atom', $atom, 'scald_width'); - $atom->rendered->player = theme('scald_flash_object', - array('vars' => - array( - 'flash_uri' => $atom->rendered->file_source_url, - 'flash_width' => ($atom->scald_width[$langcode][0]['value'] ? $atom->scald_width[$langcode][0]['value'] : 480), - 'flash_height' => ($atom->scald_height[$langcode][0]['value'] ? $atom->scald_height[$langcode][0]['value'] : 365), - 'thumbnail' => $atom->thumbnail_source, - ), - ) - ); + if ($context === 'sdl_library_item') { + $scald_thumbnail = field_get_items('scald_atom', $atom, 'scald_thumbnail'); + if (empty($scald_thumbnail)) { + $atom->rendered->thumbnail_transcoded_url = file_create_url($atom->thumbnail_source); + } + } + else { + $flash_width = 480; + $flash_height = 365; + + $scald_width = field_get_items('scald_atom', $atom, 'scald_width'); + if (!empty($scald_width)) { + $flash_width = $scald_width[0]['value']; + } + $scald_height = field_get_items('scald_atom', $atom, 'scald_height'); + if (!empty($scald_height)) { + $flash_height = $scald_height[0]['value']; + } + + // Allow context configuration to override flash dimension variables. + $context_config = scald_context_config_load($context); + if (!empty($context_config->data['width']) && !empty($context_config->data['height'])) { + $flash_width = $context_config->data['width']; + $flash_height = $context_config->data['height']; + } + $atom->rendered->player = theme('scald_flash_object', + array('vars' => + array( + 'flash_uri' => $atom->rendered->file_source_url, + 'flash_width' => $flash_width, + 'flash_height' => $flash_height, + 'thumbnail' => $atom->thumbnail_source, + ), + ) + ); + } } } diff --git a/www7/sites/all/modules/contrib/scald/modules/providers/scald_flash/scald_flash_object.tpl.php b/www7/sites/all/modules/contrib/scald/modules/providers/scald_flash/scald_flash_object.tpl.php index 1d89d50cd..507905570 100644 --- a/www7/sites/all/modules/contrib/scald/modules/providers/scald_flash/scald_flash_object.tpl.php +++ b/www7/sites/all/modules/contrib/scald/modules/providers/scald_flash/scald_flash_object.tpl.php @@ -3,11 +3,17 @@ */ ?> - - - - - + + + + + + + + + + + diff --git a/www7/sites/all/modules/contrib/scald/modules/providers/scald_image/scald_image.info b/www7/sites/all/modules/contrib/scald/modules/providers/scald_image/scald_image.info index cf6f50ec7..253623dbb 100644 --- a/www7/sites/all/modules/contrib/scald/modules/providers/scald_image/scald_image.info +++ b/www7/sites/all/modules/contrib/scald/modules/providers/scald_image/scald_image.info @@ -6,9 +6,9 @@ dependencies[] = scald dependencies[] = image -; Information added by Drupal.org packaging script on 2015-05-21 -version = "7.x-1.4" +; Information added by Drupal.org packaging script on 2015-09-16 +version = "7.x-1.5" core = "7.x" project = "scald" -datestamp = "1432202882" +datestamp = "1442418555" diff --git a/www7/sites/all/modules/contrib/scald/modules/providers/scald_image/scald_image.js b/www7/sites/all/modules/contrib/scald/modules/providers/scald_image/scald_image.js index e7dd03ee9..ddeb2acf3 100644 --- a/www7/sites/all/modules/contrib/scald/modules/providers/scald_image/scald_image.js +++ b/www7/sites/all/modules/contrib/scald/modules/providers/scald_image/scald_image.js @@ -1,44 +1,34 @@ (function ($) { Drupal.behaviors.scaldImage = { attach: function (context, settings) { - $('body').once('scald-image', function() { - if (typeof CKEDITOR !== 'undefined') { - CKEDITOR.on('dialogDefinition', function(ev) { - if (typeof Drupal.dndck4 !== 'undefined') { - if (ev.data.name == 'atomProperties') { - var dialogDefinition = ev.data.definition; - var infoTab = dialogDefinition.getContents( 'info' ); - - infoTab.add( { - id: 'txtLink', - type: 'text', - label: 'Link', - // "Link" edits the 'link' property in the options JSON string. - setup: function(widget) { - var options = JSON.parse(widget.data.options); - if (options.link) { - this.setValue(decodeURIComponent(options.link)); - } - }, - commit: function(widget) { - // Copy the current options into a new object, - var options = JSON.parse(widget.data.options); - var value = this.getValue(); - if (value != '') { - options.link = encodeURIComponent(value); - } - else { - delete options.link; - } - widget.setData('options', JSON.stringify(options)); - } - }); - Drupal.dndck4.registerOptions('txtLink', 'image', 'atom', 'scald_image'); + if (typeof Drupal.dndck4 !== 'undefined') { + Drupal.dndck4.addOption('txtLink', 'image', 'atom', 'scald_image', function (infoTab, dialogDefinition) { + infoTab.add({ + id: 'txtLink', + type: 'text', + label: 'Link', + // "Link" edits the 'link' property in the options JSON string. + setup: function (widget) { + var options = JSON.parse(widget.data.options); + if (options.link) { + this.setValue(decodeURIComponent(options.link)); } + }, + commit: function (widget) { + // Copy the current options into a new object, + var options = JSON.parse(widget.data.options); + var value = this.getValue(); + if (value != '') { + options.link = encodeURIComponent(value); + } + else { + delete options.link; + } + widget.setData('options', JSON.stringify(options)); } }); - } - }); + }); + } } }; })(jQuery); diff --git a/www7/sites/all/modules/contrib/scald/modules/providers/scald_video/scald_video.info b/www7/sites/all/modules/contrib/scald/modules/providers/scald_video/scald_video.info index 705f6459a..53099d421 100644 --- a/www7/sites/all/modules/contrib/scald/modules/providers/scald_video/scald_video.info +++ b/www7/sites/all/modules/contrib/scald/modules/providers/scald_video/scald_video.info @@ -4,9 +4,9 @@ package = Scald Providers core = 7.x dependencies[] = scald -; Information added by Drupal.org packaging script on 2015-05-21 -version = "7.x-1.4" +; Information added by Drupal.org packaging script on 2015-09-16 +version = "7.x-1.5" core = "7.x" project = "scald" -datestamp = "1432202882" +datestamp = "1442418555" diff --git a/www7/sites/all/modules/contrib/scald/modules/providers/scald_video/scald_video.module b/www7/sites/all/modules/contrib/scald/modules/providers/scald_video/scald_video.module index 6a5f86bdf..9a6482934 100644 --- a/www7/sites/all/modules/contrib/scald/modules/providers/scald_video/scald_video.module +++ b/www7/sites/all/modules/contrib/scald/modules/providers/scald_video/scald_video.module @@ -132,6 +132,7 @@ function scald_video_scald_prerender($atom, $context, $options, $mode) { 'video_height' => check_plain($atom->data['video_height']), 'thumbnail' => $atom->thumbnail_source, 'class' => 'scald_video', + 'context' => $context, ), ) ); diff --git a/www7/sites/all/modules/contrib/scald/modules/scald_quickstart/scald_quickstart.info b/www7/sites/all/modules/contrib/scald/modules/scald_quickstart/scald_quickstart.info index 82eb85139..7897fce00 100644 --- a/www7/sites/all/modules/contrib/scald/modules/scald_quickstart/scald_quickstart.info +++ b/www7/sites/all/modules/contrib/scald/modules/scald_quickstart/scald_quickstart.info @@ -6,9 +6,9 @@ dependencies[] = scald dependencies[] = scald_image dependencies[] = scald_dnd_library -; Information added by Drupal.org packaging script on 2015-05-21 -version = "7.x-1.4" +; Information added by Drupal.org packaging script on 2015-09-16 +version = "7.x-1.5" core = "7.x" project = "scald" -datestamp = "1432202882" +datestamp = "1442418555" diff --git a/www7/sites/all/modules/contrib/scald/scald.api.php b/www7/sites/all/modules/contrib/scald/scald.api.php index 810399c21..aad9bf291 100644 --- a/www7/sites/all/modules/contrib/scald/scald.api.php +++ b/www7/sites/all/modules/contrib/scald/scald.api.php @@ -377,6 +377,19 @@ function hook_scald_wysiwyg_context_list_alter(&$contexts) { $contexts['image']['sdl_editor_representation'] = t('Default'); } +/** + * Alters links that show up in the drag and drop library. + * + * @param array $links + * List of built action links. + * + * @param $atom + * The atom that user action links are being built. + */ +function hook_scald_atom_user_build_actions_links_alter(&$links, $atom) { + unset($links['delete']); +} + /** * Control access to an atom. * @@ -398,6 +411,54 @@ function hook_scald_wysiwyg_context_list_alter(&$contexts) { function hook_scald_atom_access($atom, $action, $account = NULL) { } +/** + * Act on an atom being inserted or updated. + * + * This hook is invoked from ScaldAtomController::save() before the atom is + * saved to the database. Like any other hook_ENTITY_TYPE_presave() hook, it is + * invoked before hook_entity_presave(). + * + * @param $atom + */ +function hook_scald_atom_presave($atom) { +} + +/** + * Act on an atom being inserted. + * + * This hook is invoked from ScaldAtomController::save() after a new atom is + * saved to the database, after field_attach_insert() and before + * hook_entity_insert() is called. + * + * @param $atom + */ +function hook_scald_atom_insert($atom) { +} + +/** + * Act on an atom being updated. + * + * This hook is invoked from ScaldAtomController::save() after an existing atom + * is saved to the database, after field_attach_update() and before + * hook_entity_update() is called. + * + * @param $atom + */ +function hook_scald_atom_update($atom) { +} + +/** + * Act on an atom being deleted. + * + * This hook is invoked from scald_atom_delete_multiple() after the atom is + * unregistered, before hook_entity_delete() is called and before the atom is + * removed from scald_atoms table in the database. + * + * @param $atom + */ +function hook_scald_atom_delete($atom) { +} + /** * @defgroup scald_atom_provider Hooks related to Atom provider modules. * @{ diff --git a/www7/sites/all/modules/contrib/scald/scald.info b/www7/sites/all/modules/contrib/scald/scald.info index 1ca432444..a68d4230b 100644 --- a/www7/sites/all/modules/contrib/scald/scald.info +++ b/www7/sites/all/modules/contrib/scald/scald.info @@ -11,6 +11,7 @@ dependencies[] = text test_dependencies[] = i18n test_dependencies[] = i18n_string +files[] = includes/scald.translation_handler.inc files[] = includes/scald_views_handler_field_actions.inc files[] = includes/scald_views_handler_field_data.inc files[] = includes/scald_views_handler_field_representation.inc @@ -22,9 +23,9 @@ files[] = includes/ScaldAtomController.inc files[] = tests/scald.test files[] = scald.features.inc -; Information added by Drupal.org packaging script on 2015-05-21 -version = "7.x-1.4" +; Information added by Drupal.org packaging script on 2015-09-16 +version = "7.x-1.5" core = "7.x" project = "scald" -datestamp = "1432202882" +datestamp = "1442418555" diff --git a/www7/sites/all/modules/contrib/scald/scald.module b/www7/sites/all/modules/contrib/scald/scald.module index 44fca3190..dd2dd8318 100644 --- a/www7/sites/all/modules/contrib/scald/scald.module +++ b/www7/sites/all/modules/contrib/scald/scald.module @@ -120,6 +120,21 @@ function scald_contexts($reset = FALSE) { return _scald_get_info('contexts', $reset); } +/** + * Returns the list of public context. + */ +function scald_contexts_public($reset = FALSE) { + $public = array(); + $contexts = scald_contexts($reset); + foreach ($contexts as $name => $context) { + if (empty($context['hidden'])) { + $public[$name] = $context; + } + } + + return $public; +} + /** * Implements hook_scald_contexts_alter(). * @@ -812,6 +827,9 @@ function scald_search($query = array(), $fuzzy = FALSE, $singular = FALSE) { * @param bool $override * A boolean used to determine if the Scald Context specified in the SAS * should be used or not. + * @param string[] $allowed_contexts + * An array of contexts slugs that are allowed to be used in the passed-in + * string. If empty, any context that's not hidden will be allowed. * * @return string * The same text string, but with all the Scald Atom Shorthands replaced with @@ -820,33 +838,51 @@ function scald_search($query = array(), $fuzzy = FALSE, $singular = FALSE) { * * @see scald_rendered_to_sas() */ -function scald_sas_to_rendered($string, $context = NULL, $override = FALSE) { - if (empty($context)) { +function scald_sas_to_rendered($string, $context = NULL, $override = FALSE, $allowed_contexts = array()) { + if (empty($context) || !array_key_exists($context, scald_contexts())) { $context = 'title'; } - global $_scald_override; - $_scald_override = $override; + if (empty($allowed_contexts)) { + $allowed_contexts = array_keys(scald_contexts_public()); + } + + _scald_sas_to_rendered_callback('set', $context, $override, $allowed_contexts); $rendered = preg_replace_callback( SCALD_SAS_MATCH_PATTERN, - create_function( - '$matches', - ' - global $_scald_override; - return scald_render( - $matches[1], - (!empty($matches[2]) && !$_scald_override ? $matches[2] : \'' . $context . '\'), - (!empty($matches[3]) ? $matches[3] : NULL) - ); - ' - ), + '_scald_sas_to_rendered_callback', $string ); - unset($_scald_override); return $rendered; } +function _scald_sas_to_rendered_callback($matches, $context = NULL, $override = NULL, $allowed_contexts = NULL) { + // Not using drupal_static here, because those static will + // always be reset by the parent using a 'set' call before + // they're value is actually used. + static $callback_context, $callback_override, $callback_allowed_contexts; + + if ($matches === 'set') { + $callback_context = $context; + $callback_override = $override; + $callback_allowed_contexts = $allowed_contexts; + } + else { + if ($callback_override || empty($matches[2]) || !in_array($matches[2], $callback_allowed_contexts, TRUE)) { + $render_context = $callback_context; + } + else { + $render_context = $matches[2]; + } + return scald_render( + $matches[1], + $render_context, + !empty($matches[3]) ? $matches[3] : NULL + ); + } +} + /** * Determine atoms (expressed as SAS) embedded in a string. * @@ -1445,16 +1481,6 @@ function _scald_context_fallback($type, $context) { */ function _scald_system_contexts() { return array( - 'debug' => array( - 'title' => t('Debugging Display'), - 'description' => t('A useful debugging display of a Scald Atom.'), - 'render_language' => 'XHTML', - 'parseable' => FALSE, - 'hidden' => TRUE, - 'formats' => array( - '' => 'passthrough', - ), - ), 'no-access' => array( 'title' => t('No Access'), 'description' => t('Built-in Context used when an Atom cannot be viewed by the current User.'), @@ -1557,8 +1583,8 @@ function scald_scald_actions() { * Implements hook_scald_prerender(). * * Scald Core implements this hook for its role as a Scald Context Provider of - * Scald Contexts 'debug', 'no-access', 'invalid-id', 'deleted', 'title' and - * UI-created contexts, and for its role as a Scald Transcoder Provider of Scald + * Scald Contexts 'no-access', 'invalid-id', 'deleted', 'title' and UI-created + * contexts, and for its role as a Scald Transcoder Provider of Scald * Transcoder 'passthrough'. */ function scald_scald_prerender($atom, $context, $options, $mode) { @@ -1589,10 +1615,6 @@ function scald_scald_prerender($atom, $context, $options, $mode) { */ function scald_scald_render($atom, $context, $options = array()) { switch ($context) { - case 'debug': - $output = '
' . print_r($atom, TRUE) . '
'; - break; - case 'no-access': $output = theme('scald_render_error', array('context' => $context, 'message' => t('You do not have access to view this Atom.'), 'atom' => $atom)); break; @@ -1649,6 +1671,27 @@ function scald_scald_atom_access($atom, $action, $account = NULL) { * Define a new entity type for the Atoms. */ function scald_entity_info() { + $translatable_field = array( + 'type' => 'text', + 'cardinality' => 1, + 'translatable' => TRUE, + ); + + $translatable_field_instance = array( + 'required' => TRUE, + 'settings' => array( + 'text_processing' => 0, + ), + 'widget' => array( + 'weight' => -5, + ), + 'display' => array( + 'default' => array( + 'type' => 'hidden', + ), + ), + ); + $return = array( 'scald_atom' => array( 'label' => t('Atoms'), @@ -1674,11 +1717,22 @@ function scald_entity_info() { 'save callback' => 'scald_atom_save', 'translation' => array( 'entity_translation' => array( + 'class' => 'EntityTranslationScaldHandler', 'base path' => 'atom/%scald_atom', 'edit path' => 'atom/%scald_atom/edit/%ctools_js', 'edit form' => 'atom', ), ), + 'field replacement' => array( + 'title' => array( + 'field' => $translatable_field, + 'instance' => array( + 'label' => t('Title'), + 'description' => '', + ) + $translatable_field_instance, + ), + ), + 'efq bundle conditions' => TRUE, ), ); @@ -2085,17 +2139,6 @@ function scald_menu() { 'theme callback' => 'ajax_base_page_theme', ); - $items['atom/%/debug'] = array( - 'title' => 'Debug', - 'page callback' => 'scald_debug_console', - 'page arguments' => array(1), - 'access callback' => 'user_access', - 'access arguments' => array('administer scald'), - 'file' => 'includes/scald.debug.inc', - 'weight' => 90, - 'type' => MENU_LOCAL_TASK, - ); - // Optional devel module integration. if (module_exists('devel')) { $items['atom/%scald_atom/devel'] = array( @@ -2509,6 +2552,29 @@ function scald_form_user_admin_permissions_alter(&$form, &$form_state, $form_id) $form['#submit'][] = 'scald_permissions_submit'; } +/* + * Implements hook_form_FORM_ID_alter(). + * In case the title module is used the title field has to be populated from the title + * attribute when the user comes from the add step. + */ +function scald_form_scald_atom_add_form_options_alter(&$form, &$form_state, $form_id) { + if (module_exists('title')) { + $scald = $form_state['scald']; + if (isset($scald['type']) && title_field_replacement_enabled('scald_atom', $scald['type']->type, 'title')) { + // Setting default values for each created atom + $fr_info = title_field_replacement_info('scald_atom', 'title'); + foreach($form as $key => $data) { + if (strpos($key, 'atom') === 0) { + if (!empty($form[$key]['title']['#default_value'])) { + $langcode = $form['language']['#value']; + $form[$key][$fr_info['field']['field_name']][$langcode][0]['value']['#default_value'] = $form[$key]['title']['#default_value']; + } + } + } + } + } +} + /** * Handles the permissions form submission. */ @@ -2561,6 +2627,8 @@ function scald_atom_user_build_actions_links($atom, $query = NULL) { } } + drupal_alter('scald_atom_user_build_actions_links', $links, $atom); + return $links; } diff --git a/www7/sites/all/modules/contrib/scald/tests/scald_test/scald_test.info b/www7/sites/all/modules/contrib/scald/tests/scald_test/scald_test.info index 84058373d..19fae5deb 100644 --- a/www7/sites/all/modules/contrib/scald/tests/scald_test/scald_test.info +++ b/www7/sites/all/modules/contrib/scald/tests/scald_test/scald_test.info @@ -8,9 +8,9 @@ dependencies[] = i18n dependencies[] = i18n_string -; Information added by Drupal.org packaging script on 2015-05-21 -version = "7.x-1.4" +; Information added by Drupal.org packaging script on 2015-09-16 +version = "7.x-1.5" core = "7.x" project = "scald" -datestamp = "1432202882" +datestamp = "1442418555" diff --git a/www7/sites/all/modules/contrib/scald_file/scald_file.info b/www7/sites/all/modules/contrib/scald_file/scald_file.info index 0e40d5d55..06f76aec6 100644 --- a/www7/sites/all/modules/contrib/scald_file/scald_file.info +++ b/www7/sites/all/modules/contrib/scald_file/scald_file.info @@ -8,9 +8,9 @@ stylesheets[all][] = scald_file.css files[] = includes/scald_file_views_handler_field_filesize.inc -; Information added by Drupal.org packaging script on 2015-04-22 -version = "7.x-1.1" +; Information added by Drupal.org packaging script on 2015-09-18 +version = "7.x-1.2" core = "7.x" project = "scald_file" -datestamp = "1429721933" +datestamp = "1442566750" diff --git a/www7/sites/all/modules/contrib/scald_file/scald_file.module b/www7/sites/all/modules/contrib/scald_file/scald_file.module index 399fdd9e4..a3bc9e71e 100644 --- a/www7/sites/all/modules/contrib/scald_file/scald_file.module +++ b/www7/sites/all/modules/contrib/scald_file/scald_file.module @@ -40,12 +40,12 @@ function scald_file_scald_add_form(&$form, &$form_state) { $validators = array( 'file_validate_extensions' => array(0 => 'ppt pptx xls xlsx doc docx odt pdf txt csv odg sxw ods rtf zip rar gz 7z tar'), ); - $location = 'public://atoms/files/'; + $location = 'public://atoms/files'; if ($field && $instance) { $validators = file_field_widget_upload_validators($field, $instance); $directory = file_field_widget_uri($field, $instance); if (file_prepare_directory($directory, FILE_CREATE_DIRECTORY)) { - $location = $directory . '/'; + $location = $directory; } } $form['file'] = array( @@ -249,8 +249,9 @@ function _scald_file_get_default_icon($file) { * Atom. */ function _scald_file_sync_file($atom) { - if ($atom->scald_file) { - $atom->base_id = $atom->scald_file[LANGUAGE_NONE][0]['fid']; + if (!empty($atom->scald_file)) { + $items = field_get_items('scald_atom', $atom, 'scald_file'); + $atom->base_id = $items[0]['fid']; } } @@ -435,6 +436,31 @@ function scald_file_scald_render($atom, $context, $options = array()) { } } +/** + * Implements hook_action_info(). + */ +function scald_file_action_info() { + return array( + 'scald_file_generate_thumbnail' => array( + 'type' => 'scald_atom', + 'label' => t('Generate Thumbnail'), + 'configurable' => FALSE, + 'behavior' => array('changes_property'), + 'triggers' => array('any'), + ), + ); +} + +/** + * Action callback for the thumbnail generation action. + */ +function scald_file_generate_thumbnail($entity, $context) { + if ($entity->provider === 'scald_file') { + $entity->data['scald_thumbnail_default'] = TRUE; + scald_atom_save($entity); + } +} + /** * Determines if a command exists on the current environment * diff --git a/www7/sites/all/modules/contrib/search_api/CHANGELOG.txt b/www7/sites/all/modules/contrib/search_api/CHANGELOG.txt index 27b18df93..7f4a8bffc 100644 --- a/www7/sites/all/modules/contrib/search_api/CHANGELOG.txt +++ b/www7/sites/all/modules/contrib/search_api/CHANGELOG.txt @@ -1,3 +1,21 @@ +Search API 1.16 (2015-08-30): +----------------------------- +- #2502819: Fixed example code for hook_search_api_query_alter(). +- #2491175 by ptmkenny, drunken monkey: Added a data alteration for filtering + out blocked users. +- #1197538 by thePanz, k4v, drunken monkey, ayalon, nadavoid, timodwhit, becw, + Elvar: Added support for the "Global: Random" sort in Views. +- #2520934 by drunken monkey: Added an item type for indexing several types of + entities in one index. +- #2533096 by drunken monkey: Fixed uncaught exception when deleting a server. +- #2479453 by prics, drunken monkey: Added a Drush command to + list/enable/disable servers. +- #2520684 by drunken monkey: Fixed "bundles" setting on indexes with "Index + immediately". +- #2489882 by dww: Fixed Views taxonomy filter with "is (not) empty" operator +- #2447213 by drunken monkey: Fixed issues with stale field settings for MLT + contextual filter. + Search API 1.15 (2015-06-03): ----------------------------- - #2190627 by m1n0, drunken monkey: Fixed fatal errors for views of disabled diff --git a/www7/sites/all/modules/contrib/search_api/contrib/search_api_facetapi/search_api_facetapi.info b/www7/sites/all/modules/contrib/search_api/contrib/search_api_facetapi/search_api_facetapi.info index 92bd4d487..844bc8427 100644 --- a/www7/sites/all/modules/contrib/search_api/contrib/search_api_facetapi/search_api_facetapi.info +++ b/www7/sites/all/modules/contrib/search_api/contrib/search_api_facetapi/search_api_facetapi.info @@ -9,9 +9,9 @@ files[] = plugins/facetapi/adapter.inc files[] = plugins/facetapi/query_type_term.inc files[] = plugins/facetapi/query_type_date.inc -; Information added by Drupal.org packaging script on 2015-06-03 -version = "7.x-1.15" +; Information added by Drupal.org packaging script on 2015-08-30 +version = "7.x-1.16" core = "7.x" project = "search_api" -datestamp = "1433352300" +datestamp = "1440962813" diff --git a/www7/sites/all/modules/contrib/search_api/contrib/search_api_views/README.txt b/www7/sites/all/modules/contrib/search_api/contrib/search_api_views/README.txt index bae140fe9..b36a7b55b 100644 --- a/www7/sites/all/modules/contrib/search_api/contrib/search_api_views/README.txt +++ b/www7/sites/all/modules/contrib/search_api/contrib/search_api_views/README.txt @@ -24,6 +24,22 @@ When these are present, the normal keywords should be ignored and the related items be returned as results instead. Sorting, filtering and range restriction should all work normally. +"Random sort" feature +--------------------- +This module defines the "Random sort" feature (feature key: +"search_api_random_sort") that allows to randomly sort the results returned by a +search. With a server supporting this, you can use the "Global: Random" sort to +sort the view's results randomly. Every time the query is run a different +sorting will be provided. + +For developers: +A service class that wants to support this feature has to check for a +"search_api_random" field in the search query's sorts and insert a random sort +in that position. If the query is sorted in this way, then the +"search_api_random_sort" query option can contain additional options for the +random sort, as an associative array with any of the following keys: +- seed: A numeric seed value to use for the random sort. + "Facets block" display ---------------------- Most features should be clear to users of Views. However, the module also diff --git a/www7/sites/all/modules/contrib/search_api/contrib/search_api_views/includes/handler_argument_more_like_this.inc b/www7/sites/all/modules/contrib/search_api/contrib/search_api_views/includes/handler_argument_more_like_this.inc index 69e4a54c7..9708851cc 100644 --- a/www7/sites/all/modules/contrib/search_api/contrib/search_api_views/includes/handler_argument_more_like_this.inc +++ b/www7/sites/all/modules/contrib/search_api/contrib/search_api_views/includes/handler_argument_more_like_this.inc @@ -71,11 +71,12 @@ class SearchApiViewsHandlerArgumentMoreLikeThis extends SearchApiViewsHandlerArg $this->query->abort(); return; } - $fields = $this->options['fields'] ? $this->options['fields'] : array(); - if (empty($fields)) { - foreach ($this->query->getIndex()->options['fields'] as $key => $field) { - $fields[] = $key; - } + $index_fields = array_keys($this->query->getIndex()->options['fields']); + if (empty($this->options['fields'])) { + $fields = $index_fields; + } + else { + $fields = array_intersect($this->options['fields'], $index_fields); } $mlt = array( 'id' => $this->argument, diff --git a/www7/sites/all/modules/contrib/search_api/contrib/search_api_views/includes/handler_filter_taxonomy_term.inc b/www7/sites/all/modules/contrib/search_api/contrib/search_api_views/includes/handler_filter_taxonomy_term.inc index 02d306861..efa685afc 100644 --- a/www7/sites/all/modules/contrib/search_api/contrib/search_api_views/includes/handler_filter_taxonomy_term.inc +++ b/www7/sites/all/modules/contrib/search_api/contrib/search_api_views/includes/handler_filter_taxonomy_term.inc @@ -178,12 +178,25 @@ class SearchApiViewsHandlerFilterTaxonomyTerm extends SearchApiViewsHandlerFilte return TRUE; } + // We need to know the operator, which is normally set in + // views_handler_filter::accept_exposed_input(), before we actually call + // the parent version of ourselves. + if (!empty($this->options['expose']['use_operator']) && !empty($this->options['expose']['operator_id']) && isset($input[$this->options['expose']['operator_id']])) { + $this->operator = $input[$this->options['expose']['operator_id']]; + } + // If view is an attachment and is inheriting exposed filters, then assume // exposed input has already been validated. if (!empty($this->view->is_attachment) && $this->view->display_handler->uses_exposed()) { $this->validated_exposed_input = (array) $this->view->exposed_raw_input[$this->options['expose']['identifier']]; } + // If we're checking for EMPTY or NOT, we don't need any input, and we can + // say that our input conditions are met by just having the right operator. + if ($this->operator == 'empty' || $this->operator == 'not empty') { + return TRUE; + } + // If it's non-required and there's no value don't bother filtering. if (!$this->options['expose']['required'] && empty($this->validated_exposed_input)) { return FALSE; diff --git a/www7/sites/all/modules/contrib/search_api/contrib/search_api_views/includes/query.inc b/www7/sites/all/modules/contrib/search_api/contrib/search_api_views/includes/query.inc index 82588a630..923570bb7 100644 --- a/www7/sites/all/modules/contrib/search_api/contrib/search_api_views/includes/query.inc +++ b/www7/sites/all/modules/contrib/search_api/contrib/search_api_views/includes/query.inc @@ -122,19 +122,63 @@ class SearchApiViewsQuery extends views_plugin_query { } /** - * Add a sort to the query. + * Adds a sort to the query. * - * @param $selector + * @param string $selector * The field to sort on. All indexed fields of the index are valid values. - * In addition, the special fields 'search_api_relevance' (sort by - * relevance) and 'search_api_id' (sort by item id) may be used. - * @param $order + * In addition, these special fields may be used: + * - search_api_relevance: sort by relevance; + * - search_api_id: sort by item id; + * - search_api_random: random sort (available only if the server supports + * the "search_api_random_sort" feature). + * @param string $order * The order to sort items in - either 'ASC' or 'DESC'. Defaults to 'ASC'. */ public function add_selector_orderby($selector, $order = 'ASC') { $this->query->sort($selector, $order); } + /** + * Provides a sorting method as present in the Views default query plugin. + * + * This is provided so that the "Global: Random" sort included in Views will + * work properly with Search API Views. Random sorting is only supported if + * the active search server supports the "search_api_random_sort" feature, + * though, otherwise the call will be ignored. + * + * This method can only be used to sort randomly, as would be done with the + * default query plugin. All other calls are ignored. + * + * @param string|null $table + * Only "rand" is recognized here, all other calls are ignored. + * @param string|null $field + * Is ignored and only present for compatibility reasons. + * @param string $order + * Either "ASC" or "DESC". + * @param string|null $alias + * Is ignored and only present for compatibility reasons. + * @param array $params + * The following optional parameters are recognized: + * - seed: a predefined seed for the random generator. + * + * @see views_plugin_query_default::add_orderby() + */ + public function add_orderby($table, $field = NULL, $order = 'ASC', $alias = '', $params = array()) { + $server = $this->getIndex()->server(); + if ($table == 'rand') { + if ($server->supportsFeature('search_api_random_sort')) { + $this->add_selector_orderby('search_api_random', $order); + if ($params) { + $this->setOption('search_api_random_sort', $params); + } + } + else { + $variables['%server'] = $server->label(); + watchdog('search_api_views', 'Tried to sort results randomly on server %server which does not support random sorting.', $variables, WATCHDOG_WARNING); + } + } + } + /** * Defines the options used by this query plugin. * @@ -494,31 +538,31 @@ class SearchApiViewsQuery extends views_plugin_query { * query backend. */ public function get_result_wrappers($results, $relationship = NULL, $field = NULL) { - $entity_type = $this->index->getEntityType(); + $type = $this->index->item_type; $wrappers = array(); - $load_entities = array(); + $load_items = array(); foreach ($results as $row_index => $row) { - if ($entity_type && isset($row->entity)) { + if (isset($row->entity)) { // If this entity isn't load, register it for pre-loading. if (!is_object($row->entity)) { - $load_entities[$row->entity] = $row_index; + $load_items[$row->entity] = $row_index; + } + else { + $wrappers[$row_index] = $this->index->entityWrapper($row->entity); } - - $wrappers[$row_index] = $this->index->entityWrapper($row->entity); } } // If the results are entities, we pre-load them to make use of a multiple // load. (Otherwise, each result would be loaded individually.) - if (!empty($load_entities)) { - $entities = entity_load($entity_type, array_keys($load_entities)); - foreach ($entities as $entity_id => $entity) { - $wrappers[$load_entities[$entity_id]] = $this->index->entityWrapper($entity); + if (!empty($load_items)) { + $items = $this->index->loadItems(array_keys($load_items)); + foreach ($items as $id => $item) { + $wrappers[$load_items[$id]] = $this->index->entityWrapper($item); } } // Apply the relationship, if necessary. - $type = $entity_type ? $entity_type : $this->index->item_type; $selector_suffix = ''; if ($field && ($pos = strrpos($field, ':'))) { $selector_suffix = substr($field, 0, $pos); diff --git a/www7/sites/all/modules/contrib/search_api/contrib/search_api_views/search_api_views.info b/www7/sites/all/modules/contrib/search_api/contrib/search_api_views/search_api_views.info index c6ada7c5a..8dd362986 100644 --- a/www7/sites/all/modules/contrib/search_api/contrib/search_api_views/search_api_views.info +++ b/www7/sites/all/modules/contrib/search_api/contrib/search_api_views/search_api_views.info @@ -27,9 +27,9 @@ files[] = includes/handler_sort.inc files[] = includes/plugin_cache.inc files[] = includes/query.inc -; Information added by Drupal.org packaging script on 2015-06-03 -version = "7.x-1.15" +; Information added by Drupal.org packaging script on 2015-08-30 +version = "7.x-1.16" core = "7.x" project = "search_api" -datestamp = "1433352300" +datestamp = "1440962813" diff --git a/www7/sites/all/modules/contrib/search_api/includes/callback_bundle_filter.inc b/www7/sites/all/modules/contrib/search_api/includes/callback_bundle_filter.inc index 08fcba458..50a9dc9fb 100644 --- a/www7/sites/all/modules/contrib/search_api/includes/callback_bundle_filter.inc +++ b/www7/sites/all/modules/contrib/search_api/includes/callback_bundle_filter.inc @@ -14,24 +14,40 @@ class SearchApiAlterBundleFilter extends SearchApiAbstractAlterCallback { * {@inheritdoc} */ public function supportsIndex(SearchApiIndex $index) { - $support = $index->getEntityType() && ($info = entity_get_info($index->getEntityType())) && self::hasBundles($info); - $support &= empty($index->options['datasource']['bundles']) || count($index->options['datasource']['bundles']) > 1; - return $support; + if ($this->isMultiEntityIndex($index)) { + $info = entity_get_info(); + foreach ($index->options['datasource']['types'] as $type) { + if (isset($info[$type]) && self::hasBundles($info[$type])) { + return TRUE; + } + } + return FALSE; + } + return $index->getEntityType() && ($info = entity_get_info($index->getEntityType())) && self::hasBundles($info); } /** * {@inheritdoc} */ public function alterItems(array &$items) { - $info = entity_get_info($this->index->getEntityType()); - if (self::hasBundles($info) && isset($this->options['bundles'])) { - $bundles = array_flip($this->options['bundles']); - $default = (bool) $this->options['default']; + if (!$this->supportsIndex($this->index) || !isset($this->options['bundles'])) { + return; + } + + if ($this->isMultiEntityIndex()) { + $bundle_prop = 'item_bundle'; + } + else { + $info = entity_get_info($this->index->getEntityType()); $bundle_prop = $info['entity keys']['bundle']; - foreach ($items as $id => $item) { - if (isset($bundles[$item->$bundle_prop]) == $default) { - unset($items[$id]); - } + } + + $bundles = array_flip($this->options['bundles']); + $default = (bool) $this->options['default']; + + foreach ($items as $id => $item) { + if (isset($bundles[$item->$bundle_prop]) == $default) { + unset($items[$id]); } } } @@ -40,20 +56,35 @@ class SearchApiAlterBundleFilter extends SearchApiAbstractAlterCallback { * {@inheritdoc} */ public function configurationForm() { - $info = entity_get_info($this->index->getEntityType()); - if (self::hasBundles($info)) { + if ($this->supportsIndex($this->index)) { $options = array(); - foreach ($info['bundles'] as $bundle => $bundle_info) { - $options[$bundle] = isset($bundle_info['label']) ? $bundle_info['label'] : $bundle; + if ($this->isMultiEntityIndex()) { + $info = entity_get_info(); + $unsupported_types = array(); + foreach ($this->index->options['datasource']['types'] as $type) { + if (isset($info[$type]) && self::hasBundles($info[$type])) { + foreach ($info[$type]['bundles'] as $bundle => $bundle_info) { + $options["$type:$bundle"] = $info[$type]['label'] . ' » ' . $bundle_info['label']; + } + } + else { + $unsupported_types[] = isset($info[$type]['label']) ? $info[$type]['label'] : $type; + } + } + if ($unsupported_types) { + $form['unsupported_types']['#markup'] = '

' . t('The following entity types do not contain any bundles: @types. All items of those types will therefore be included in the index.', array('@types' => implode(', ', $unsupported_types))) . '

'; + } + } + else { + $info = entity_get_info($this->index->getEntityType()); + foreach ($info['bundles'] as $bundle => $bundle_info) { + $options[$bundle] = isset($bundle_info['label']) ? $bundle_info['label'] : $bundle; + } } if (!empty($this->index->options['datasource']['bundles'])) { $form['message']['#markup'] = '

' . t("Note: This index is already restricted to certain bundles. If you use this data alteration, those will be reduced further. However, the index setting is better supported in the user interface and should therefore be prefered. For example, using this data alteration will not reduce the displayed total number of items to index (even though some of them will not be indexed). Consider creating a new index with appropriate bundle settings instead.") . '

'; - $included_bundles = drupal_map_assoc($this->index->options['datasource']['bundles']); - foreach ($options as $bundle => $label) { - if (!isset($included_bundles[$bundle])) { - unset($options[$bundle]); - } - } + $included_bundles = array_flip($this->index->options['datasource']['bundles']); + $options = array_intersect_key($options, $included_bundles); } $form['default'] = array( '#type' => 'radios', @@ -90,10 +121,25 @@ class SearchApiAlterBundleFilter extends SearchApiAbstractAlterCallback { * The entity type's entity_get_info() array. * * @return bool - * TRUE if the entity type has bundles, FASLE otherwise. + * TRUE if the entity type has bundles, FALSE otherwise. */ protected static function hasBundles(array $entity_info) { return !empty($entity_info['entity keys']['bundle']) && !empty($entity_info['bundles']); } + /** + * Determines whether the given index contains multiple types of entities. + * + * @param SearchApiIndex|null $index + * (optional) The index to examine. Defaults to the index set for this + * plugin. + * + * @return bool + * TRUE if the index is a multi-entity index, FALSE otherwise. + */ + protected function isMultiEntityIndex(SearchApiIndex $index = NULL) { + $index = $index ? $index : $this->index; + return $index->datasource() instanceof SearchApiCombinedEntityDataSourceController; + } + } diff --git a/www7/sites/all/modules/contrib/search_api/includes/callback_user_status.inc b/www7/sites/all/modules/contrib/search_api/includes/callback_user_status.inc new file mode 100644 index 000000000..541f1b2d1 --- /dev/null +++ b/www7/sites/all/modules/contrib/search_api/includes/callback_user_status.inc @@ -0,0 +1,31 @@ +getEntityType() == 'user'; + } + + /** + * {@inheritdoc} + */ + public function alterItems(array &$items) { + foreach ($items as $id => $account) { + if (empty($account->status)) { + unset($items[$id]); + } + } + } + +} diff --git a/www7/sites/all/modules/contrib/search_api/includes/datasource.inc b/www7/sites/all/modules/contrib/search_api/includes/datasource.inc index bac2a658b..0c72f0143 100644 --- a/www7/sites/all/modules/contrib/search_api/includes/datasource.inc +++ b/www7/sites/all/modules/contrib/search_api/includes/datasource.inc @@ -183,6 +183,10 @@ interface SearchApiDataSourceControllerInterface { * The concept of queued items will be removed in the Drupal 8 version of * this module. * + * @return SearchApiIndex[]|null + * All indexes for which any items were updated; or NULL if items were + * updated for all of them. + * * @throws SearchApiDataSourceException * If any error state was encountered. */ @@ -231,6 +235,10 @@ interface SearchApiDataSourceControllerInterface { * @param SearchApiIndex[] $indexes * The indexes for which the deletions should be tracked. * + * @return SearchApiIndex[]|null + * All indexes for which any items were deleted; or NULL if items were + * deleted for all of them. + * * @throws SearchApiDataSourceException * If any error state was encountered. */ @@ -615,23 +623,28 @@ abstract class SearchApiAbstractDataSourceController implements SearchApiDataSou */ public function trackItemChange($item_ids, array $indexes, $dequeue = FALSE) { if (!$this->table) { - return; + return NULL; } - $index_ids = array(); + + $ret = array(); + foreach ($indexes as $index) { $this->checkIndex($index); - $index_ids[] = $index->id; - } - $update = db_update($this->table) - ->fields(array( - $this->changedColumn => REQUEST_TIME, - )) - ->condition($this->indexIdColumn, $index_ids, 'IN') - ->condition($this->changedColumn, 0, $dequeue ? '<=' : '='); - if ($item_ids !== FALSE) { - $update->condition($this->itemIdColumn, $item_ids, 'IN'); + $update = db_update($this->table) + ->fields(array( + $this->changedColumn => REQUEST_TIME, + )) + ->condition($this->indexIdColumn, $index->id) + ->condition($this->changedColumn, 0, $dequeue ? '<=' : '='); + if ($item_ids !== FALSE) { + $update->condition($this->itemIdColumn, $item_ids, 'IN'); + } + if ($update->execute()) { + $ret[] = $index; + } } - $update->execute(); + + return $ret; } /** @@ -675,17 +688,22 @@ abstract class SearchApiAbstractDataSourceController implements SearchApiDataSou */ public function trackItemDelete(array $item_ids, array $indexes) { if (!$this->table) { - return; + return NULL; } - $index_ids = array(); + + $ret = array(); + foreach ($indexes as $index) { $this->checkIndex($index); - $index_ids[] = $index->id; + $delete = db_delete($this->table) + ->condition($this->indexIdColumn, $index->id) + ->condition($this->itemIdColumn, $item_ids, 'IN'); + if ($delete->execute()) { + $ret[] = $index; + } } - db_delete($this->table) - ->condition($this->itemIdColumn, $item_ids, 'IN') - ->condition($this->indexIdColumn, $index_ids, 'IN') - ->execute(); + + return $ret; } /** diff --git a/www7/sites/all/modules/contrib/search_api/includes/datasource_entity.inc b/www7/sites/all/modules/contrib/search_api/includes/datasource_entity.inc index ddfd34ac1..59f5bd937 100644 --- a/www7/sites/all/modules/contrib/search_api/includes/datasource_entity.inc +++ b/www7/sites/all/modules/contrib/search_api/includes/datasource_entity.inc @@ -229,7 +229,7 @@ class SearchApiEntityDataSourceController extends SearchApiAbstractDataSourceCon '#attributes' => array('class' => array('search-api-checkboxes-list')), '#disabled' => !empty($form_state['index']), ); - if (!empty($form_state['index'])) { + if (!empty($form_state['index']->options['datasource'])) { $form['bundles']['#default_value'] = drupal_map_assoc($form_state['index']->options['datasource']['bundles']); } return $form; diff --git a/www7/sites/all/modules/contrib/search_api/includes/datasource_multiple.inc b/www7/sites/all/modules/contrib/search_api/includes/datasource_multiple.inc new file mode 100644 index 000000000..75adf4591 --- /dev/null +++ b/www7/sites/all/modules/contrib/search_api/includes/datasource_multiple.inc @@ -0,0 +1,357 @@ + 'item_id', + 'type' => 'string', + ); + } + + /** + * {@inheritdoc} + */ + public function loadItems(array $ids) { + $ids_by_type = array(); + foreach ($ids as $id) { + list($type, $entity_id) = explode('/', $id); + $ids_by_type[$type][$entity_id] = $id; + } + + $items = array(); + foreach ($ids_by_type as $type => $type_ids) { + foreach (entity_load($type, array_keys($type_ids)) as $entity_id => $entity) { + $id = $type_ids[$entity_id]; + $item = (object) array($type => $entity); + $item->item_id = $id; + $item->item_type = $type; + $item->item_entity_id = $entity_id; + $item->item_bundle = NULL; + try { + list(, , $bundle) = entity_extract_ids($type, $entity); + $item->item_bundle = $bundle ? "$type:$bundle" : NULL; + } + catch (EntityMalformedException $e) { + // Will probably make problems at some other place, but for extracting + // the bundle it is really not critical enough to fail on – just + // ignore this exception. + } + $items[$id] = $item; + unset($type_ids[$entity_id]); + } + if ($type_ids) { + search_api_track_item_delete($type, array_keys($type_ids)); + } + } + + return $items; + } + + /** + * {@inheritdoc} + */ + protected function getPropertyInfo() { + $info = array( + 'item_id' => array( + 'label' => t('ID'), + 'description' => t('The combined ID of the item, containing both entity type and entity ID.'), + 'type' => 'token', + ), + 'item_type' => array( + 'label' => t('Entity type'), + 'description' => t('The entity type of the item.'), + 'type' => 'token', + 'options list' => 'search_api_entity_type_options_list', + ), + 'item_entity_id' => array( + 'label' => t('Entity ID'), + 'description' => t('The entity ID of the item.'), + 'type' => 'token', + ), + 'item_bundle' => array( + 'label' => t('Bundle'), + 'description' => t('The bundle of the item, if applicable.'), + 'type' => 'token', + 'options list' => 'search_api_combined_bundle_options_list', + ), + 'item_label' => array( + 'label' => t('Label'), + 'description' => t('The label of the item.'), + 'type' => 'text', + // Since this needs a bit more computation than the others, we don't + // include it always when loading the item but use a getter callback. + 'getter callback' => 'search_api_get_multi_type_item_label', + ), + ); + + foreach ($this->getSelectedEntityTypeOptions() as $type => $label) { + $info[$type] = array( + 'label' => $label, + 'description' => t('The indexed entity, if it is of type %type.', array('%type' => $label)), + 'type' => $type, + ); + } + + return array('property info' => $info); + } + + /** + * {@inheritdoc} + */ + public function getItemId($item) { + return isset($item->item_id) ? $item->item_id : NULL; + } + + /** + * {@inheritdoc} + */ + public function getItemLabel($item) { + return search_api_get_multi_type_item_label($item); + } + + /** + * {@inheritdoc} + */ + public function getItemUrl($item) { + if ($item->item_type == 'file') { + return array( + 'path' => file_create_url($item->file->uri), + 'options' => array( + 'entity_type' => 'file', + 'entity' => $item, + ), + ); + } + $url = entity_uri($item->item_type, $item->{$item->item_type}); + return $url ? $url : NULL; + } + + /** + * {@inheritdoc} + */ + public function startTracking(array $indexes) { + if (!$this->table) { + return; + } + // We first clear the tracking table for all indexes, so we can just insert + // all items again without any key conflicts. + $this->stopTracking($indexes); + + foreach ($indexes as $index) { + $types = $this->getEntityTypes($index); + + // Wherever possible, use a sub-select instead of the much slower + // entity_load(). + foreach ($types as $type) { + $entity_info = entity_get_info($type); + + if (!empty($entity_info['base table'])) { + // Assumes that all entities use the "base table" property and the + // "entity keys[id]" in the same way as the default controller. + $id_field = $entity_info['entity keys']['id']; + $table = $entity_info['base table']; + + // Select all entity ids. + $query = db_select($table, 't'); + $query->addExpression("CONCAT(:prefix, t.$id_field)", 'item_id', array(':prefix' => $type . '/')); + $query->addExpression(':index_id', 'index_id', array(':index_id' => $index->id)); + $query->addExpression('1', 'changed'); + + // INSERT ... SELECT ... + db_insert($this->table) + ->from($query) + ->execute(); + + unset($types[$type]); + } + } + + // In the absence of a "base table", use the slow entity_load(). + if ($types) { + foreach ($types as $type) { + $query = new EntityFieldQuery(); + $query->entityCondition('entity_type', $type); + $result = $query->execute(); + $ids = !empty($result[$type]) ? array_keys($result[$type]) : array(); + if ($ids) { + foreach ($ids as $i => $id) { + $ids[$i] = $type . '/' . $id; + } + $this->trackItemInsert($ids, array($index), TRUE); + } + } + } + } + } + + /** + * Starts tracking the index status for the given items on the given indexes. + * + * @param array $item_ids + * The IDs of new items to track. + * @param SearchApiIndex[] $indexes + * The indexes for which items should be tracked. + * @param bool $skip_type_check + * (optional) If TRUE, don't check whether the type matches the index's + * datasource configuration. Internal use only. + * + * @return SearchApiIndex[]|null + * All indexes for which any items were added; or NULL if items were added + * for all of them. + * + * @throws SearchApiDataSourceException + * If any error state was encountered. + */ + public function trackItemInsert(array $item_ids, array $indexes, $skip_type_check = FALSE) { + $ret = array(); + + foreach ($indexes as $index_id => $index) { + $ids = drupal_map_assoc($item_ids); + + if (!$skip_type_check) { + $types = $this->getEntityTypes($index); + foreach ($ids as $id) { + list($type) = explode('/', $id); + if (!isset($types[$type])) { + unset($ids[$id]); + } + } + } + + if ($ids) { + parent::trackItemInsert($ids, array($index)); + $ret[$index_id] = $index; + } + } + + return $ret; + } + + /** + * {@inheritdoc} + */ + public function configurationForm(array $form, array &$form_state) { + $form['types'] = array( + '#type' => 'checkboxes', + '#title' => t('Entity types'), + '#description' => t('Select the entity types which should be included in this index.'), + '#options' => search_api_entity_type_options_list(), + '#attributes' => array('class' => array('search-api-checkboxes-list')), + '#disabled' => !empty($form_state['index']), + '#required' => TRUE, + ); + if (!empty($form_state['index']->options['datasource']['types'])) { + $form['types']['#default_value'] = $this->getEntityTypes($form_state['index']); + } + return $form; + } + + /** + * {@inheritdoc} + */ + public function configurationFormSubmit(array $form, array &$values, array &$form_state) { + if (!empty($values['types'])) { + $values['types'] = array_keys(array_filter($values['types'])); + } + } + + /** + * {@inheritdoc} + */ + public function getConfigurationSummary(SearchApiIndex $index) { + if ($type_labels = $this->getSelectedEntityTypeOptions($index)) { + $args['!types'] = implode(', ', $type_labels); + return format_plural(count($type_labels), 'Indexed entity types: !types.', 'Indexed entity types: !types.', $args); + } + return NULL; + } + + /** + * Retrieves the index for which the current method was called. + * + * Very ugly method which uses the stack trace to find the right object. + * + * @return SearchApiIndex + * The active index. + * + * @throws SearchApiException + * Thrown if the active index could not be determined. + */ + protected function getCallingIndex() { + foreach (debug_backtrace() as $trace) { + if (isset($trace['object']) && $trace['object'] instanceof SearchApiIndex) { + return $trace['object']; + } + } + // If there's only a single index on the site, it's also easy. + $indexes = search_api_index_load_multiple(FALSE); + if (count($indexes) === 1) { + return reset($indexes); + } + throw new SearchApiException('Could not determine the active index of the datasource.'); + } + + /** + * Returns the entity types for which this datasource is configured. + * + * Depends on the index from which this method is (indirectly) called. + * + * @param SearchApiIndex $index + * (optional) The index for which to get the enabled entity types. If not + * given, will be determined automatically. + * + * @return string[] + * The machine names of the datasource's enabled entity types, as both keys + * and values. + * + * @throws SearchApiException + * Thrown if the active index could not be determined. + */ + protected function getEntityTypes(SearchApiIndex $index = NULL) { + if (!$index) { + $index = $this->getCallingIndex(); + } + if (isset($index->options['datasource']['types'])) { + return drupal_map_assoc($index->options['datasource']['types']); + } + return array(); + } + + /** + * Returns the selected entity type options for this datasource. + * + * Depends on the index from which this method is (indirectly) called. + * + * @param SearchApiIndex $index + * (optional) The index for which to get the enabled entity types. If not + * given, will be determined automatically. + * + * @return string[] + * An associative array, mapping the machine names of the enabled entity + * types to their labels. + * + * @throws SearchApiException + * Thrown if the active index could not be determined. + */ + protected function getSelectedEntityTypeOptions(SearchApiIndex $index = NULL) { + return array_intersect_key(search_api_entity_type_options_list(), $this->getEntityTypes($index)); + } + +} diff --git a/www7/sites/all/modules/contrib/search_api/includes/index_entity.inc b/www7/sites/all/modules/contrib/search_api/includes/index_entity.inc index 9fc1f68bb..824bf1294 100644 --- a/www7/sites/all/modules/contrib/search_api/includes/index_entity.inc +++ b/www7/sites/all/modules/contrib/search_api/includes/index_entity.inc @@ -439,7 +439,6 @@ class SearchApiIndex extends Entity { return $this->server()->query($this, $options); } - /** * Indexes items on this index. * diff --git a/www7/sites/all/modules/contrib/search_api/includes/query.inc b/www7/sites/all/modules/contrib/search_api/includes/query.inc index b005cf731..1935ff031 100644 --- a/www7/sites/all/modules/contrib/search_api/includes/query.inc +++ b/www7/sites/all/modules/contrib/search_api/includes/query.inc @@ -153,7 +153,9 @@ interface SearchApiQueryInterface { * * @param string $field * The field to sort by. The special fields 'search_api_relevance' (sort by - * relevance) and 'search_api_id' (sort by item id) may be used. + * relevance) and 'search_api_id' (sort by item id) may be used. Also, if + * the search server supports the "search_api_random_sort" feature, the + * "search_api_random_sort" special field can be used to sort randomly. * @param string $order * The order to sort items in - either 'ASC' or 'DESC'. * @@ -586,6 +588,10 @@ class SearchApiQuery implements SearchApiQueryInterface { 'search_api_relevance' => array('type' => 'decimal'), 'search_api_id' => array('type' => 'integer'), ); + if ($this->getIndex()->server()->supportsFeature('search_api_random_sort')) { + $fields['search_api_random'] = array('type' => 'integer'); + } + if (empty($fields[$field])) { throw new SearchApiException(t('Trying to sort on unknown field @field.', array('@field' => $field))); } diff --git a/www7/sites/all/modules/contrib/search_api/includes/service.inc b/www7/sites/all/modules/contrib/search_api/includes/service.inc index b8d8ca884..046e5d399 100644 --- a/www7/sites/all/modules/contrib/search_api/includes/service.inc +++ b/www7/sites/all/modules/contrib/search_api/includes/service.inc @@ -420,7 +420,15 @@ abstract class SearchApiAbstractService implements SearchApiServiceInterface { public function preDelete() { $indexes = search_api_index_load_multiple(FALSE, array('server' => $this->server->machine_name)); foreach ($indexes as $index) { - $this->removeIndex($index); + // removeIndex() might throw exceptions, but this method mustn't. + try { + $this->removeIndex($index); + } + catch (SearchApiException $e) { + $variables['%index'] = $index->name; + $variables['%server'] = $this->server->name; + watchdog_exception('search_api', $e, '%type while trying to remove index %index from deleted server %server: !message in %function (line %line of %file).', $variables); + } } } diff --git a/www7/sites/all/modules/contrib/search_api/search_api.api.php b/www7/sites/all/modules/contrib/search_api/search_api.api.php index f98210819..e5c2ca620 100644 --- a/www7/sites/all/modules/contrib/search_api/search_api.api.php +++ b/www7/sites/all/modules/contrib/search_api/search_api.api.php @@ -341,7 +341,7 @@ function hook_search_api_query_alter(SearchApiQueryInterface $query) { // Exclude entities with ID 0. (Assume the ID field is always indexed.) if ($query->getIndex()->getEntityType()) { $info = entity_get_info($query->getIndex()->getEntityType()); - $query->condition($info['entity keys']['id'], 0, '!='); + $query->condition($info['entity keys']['id'], 0, '<>'); } } diff --git a/www7/sites/all/modules/contrib/search_api/search_api.drush.inc b/www7/sites/all/modules/contrib/search_api/search_api.drush.inc index 927514d0f..9edefe94c 100644 --- a/www7/sites/all/modules/contrib/search_api/search_api.drush.inc +++ b/www7/sites/all/modules/contrib/search_api/search_api.drush.inc @@ -121,6 +121,39 @@ function search_api_drush_command() { 'aliases' => array('sapi-sis'), ); + $items['search-api-server-list'] = array( + 'description' => 'List all search servers.', + 'examples' => array( + 'drush search-api-server-list' => dt('List all search servers.'), + 'drush sapi-sl' => dt('Alias to list all search servers.'), + ), + 'aliases' => array('sapi-sl'), + ); + + $items['search-api-server-enable'] = array( + 'description' => 'Enable a search server.', + 'examples' => array( + 'drush search-api-server-e my_solr_server' => dt('Enable the !server search server.', array('!server' => 'my_solr_server')), + 'drush sapi-se my_solr_server' => dt('Alias to enable the !server search server.', array('!server' => 'my_solr_server')), + ), + 'arguments' => array( + 'server_id' => dt('The numeric ID or machine name of a search server to enable.'), + ), + 'aliases' => array('sapi-se'), + ); + + $items['search-api-server-disable'] = array( + 'description' => 'Disable a search server.', + 'examples' => array( + 'drush search-api-server-disable' => dt('Disable the !server search server.', array('!server' => 'my_solr_server')), + 'drush sapi-sd' => dt('Alias to disable the !server search server.', array('!server' => 'my_solr_server')), + ), + 'arguments' => array( + 'server_id' => dt('The numeric ID or machine name of a search server to disable.'), + ), + 'aliases' => array('sapi-sd'), + ); + return $items; } @@ -508,7 +541,8 @@ function search_api_drush_get_server($server_id = NULL) { $servers = search_api_server_load_multiple($ids); if (empty($servers)) { drush_set_error(dt('Invalid server_id or no servers present.')); - // @todo: Maybe add logic to print table of all servers. + drush_print(); + drush_search_api_server_list(); } return $servers; } @@ -533,3 +567,75 @@ function search_api_drush_static($function) { $index[$function] = TRUE; return FALSE; } + +/** + * Lists all search servers. + */ +function drush_search_api_server_list() { + if (search_api_drush_static(__FUNCTION__)) { + return; + } + $servers = search_api_server_load_multiple(FALSE); + if (empty($servers)) { + drush_print(dt('There are no servers present.')); + return; + } + $rows[] = array( + dt('Machine name'), + dt('Name'), + dt('Status'), + ); + foreach ($servers as $server) { + $row = array( + $server->machine_name, + $server->name, + $server->enabled ? dt('enabled') : dt('disabled'), + ); + $rows[] = $row; + } + drush_print_table($rows); +} + +/** + * Enables a search server. + * + * @param int|string $server_id + * The numeric ID or machine name of the server to enable. + */ +function drush_search_api_server_enable($server_id = NULL) { + if (!isset($server_id)) { + drush_print(dt('Please provide a valid server id.')); + return; + } + $server = search_api_server_load($server_id); + if (empty($server)) { + drush_print(dt('The server was not able to load.')); + return; + } + else { + $server->update(array('enabled' => 1)); + drush_print(dt('The server was enabled successfully.')); + } +} + +/** + * Disables a search server. + * + * @param int|string $server_id + * The numeric ID or machine name of the server to disable. + */ +function drush_search_api_server_disable($server_id = NULL) { + if (!isset($server_id)) { + drush_print(dt('Please provide a valid server id.')); + return; + } + $server = search_api_server_load($server_id); + if (empty($server)) { + drush_print(dt('The server was not able to load.')); + return; + } + else { + $server->update(array('enabled' => 0)); + drush_print(dt('The server was disabled successfully.')); + } +} diff --git a/www7/sites/all/modules/contrib/search_api/search_api.info b/www7/sites/all/modules/contrib/search_api/search_api.info index f73aa706e..1c44e69e7 100644 --- a/www7/sites/all/modules/contrib/search_api/search_api.info +++ b/www7/sites/all/modules/contrib/search_api/search_api.info @@ -16,9 +16,11 @@ files[] = includes/callback_language_control.inc files[] = includes/callback_node_access.inc files[] = includes/callback_node_status.inc files[] = includes/callback_role_filter.inc +files[] = includes/callback_user_status.inc files[] = includes/datasource.inc files[] = includes/datasource_entity.inc files[] = includes/datasource_external.inc +files[] = includes/datasource_multiple.inc files[] = includes/exception.inc files[] = includes/index_entity.inc files[] = includes/processor.inc @@ -34,9 +36,9 @@ files[] = includes/service.inc configure = admin/config/search/search_api -; Information added by Drupal.org packaging script on 2015-06-03 -version = "7.x-1.15" +; Information added by Drupal.org packaging script on 2015-08-30 +version = "7.x-1.16" core = "7.x" project = "search_api" -datestamp = "1433352300" +datestamp = "1440962813" diff --git a/www7/sites/all/modules/contrib/search_api/search_api.install b/www7/sites/all/modules/contrib/search_api/search_api.install index 850b82804..3650a0338 100644 --- a/www7/sites/all/modules/contrib/search_api/search_api.install +++ b/www7/sites/all/modules/contrib/search_api/search_api.install @@ -191,6 +191,35 @@ function search_api_schema() { 'primary key' => array('item_id', 'index_id'), ); + $schema['search_api_item_string_id'] = array( + 'description' => 'Stores the items which should be indexed for each index, and their status. Used only for items with string IDs.', + 'fields' => array( + 'item_id' => array( + 'description' => "The item's ID.", + 'type' => 'varchar', + 'length' => 64, + 'not null' => TRUE, + ), + 'index_id' => array( + 'description' => 'The {search_api_index}.id this item belongs to.', + 'type' => 'int', + 'unsigned' => TRUE, + 'not null' => TRUE, + ), + 'changed' => array( + 'description' => 'Either a flag or a timestamp to indicate if or when the item was changed since it was last indexed.', + 'type' => 'int', + 'size' => 'big', + 'not null' => TRUE, + 'default' => 1, + ), + ), + 'indexes' => array( + 'indexing' => array('index_id', 'changed'), + ), + 'primary key' => array('item_id', 'index_id'), + ); + $schema['search_api_task'] = array( 'description' => 'Stores pending tasks for servers.', 'fields' => array( @@ -1001,3 +1030,38 @@ function search_api_update_7117() { ->execute(); } } + +/** + * Adds the {search_api_item_string_id} table for items with string IDs. + */ +function search_api_update_7118() { + $table = array( + 'description' => 'Stores the items which should be indexed for each index, and their status. Used only for items with string IDs.', + 'fields' => array( + 'item_id' => array( + 'description' => "The item's ID.", + 'type' => 'varchar', + 'length' => 64, + 'not null' => TRUE, + ), + 'index_id' => array( + 'description' => 'The {search_api_index}.id this item belongs to.', + 'type' => 'int', + 'unsigned' => TRUE, + 'not null' => TRUE, + ), + 'changed' => array( + 'description' => 'Either a flag or a timestamp to indicate if or when the item was changed since it was last indexed.', + 'type' => 'int', + 'size' => 'big', + 'not null' => TRUE, + 'default' => 1, + ), + ), + 'indexes' => array( + 'indexing' => array('index_id', 'changed'), + ), + 'primary key' => array('item_id', 'index_id'), + ); + db_create_table('search_api_item_string_id', $table); +} diff --git a/www7/sites/all/modules/contrib/search_api/search_api.module b/www7/sites/all/modules/contrib/search_api/search_api.module index 6253221b4..f87ced8dd 100644 --- a/www7/sites/all/modules/contrib/search_api/search_api.module +++ b/www7/sites/all/modules/contrib/search_api/search_api.module @@ -647,8 +647,9 @@ function search_api_search_api_index_insert(SearchApiIndex $index) { * Implements hook_search_api_index_update(). */ function search_api_search_api_index_update(SearchApiIndex $index) { - // Call the datasource update function with the table this module provides. + // Call the datasource update function with the tables this module provides. search_api_index_update_datasource($index, 'search_api_item'); + search_api_index_update_datasource($index, 'search_api_item_string_id'); // If the server was changed, we have to call the appropriate service class // hook methods. @@ -857,6 +858,8 @@ function search_api_entity_insert($entity, $type) { list($id) = entity_extract_ids($type, $entity); if (isset($id)) { search_api_track_item_insert($type, array($id)); + $combined_id = $type . '/' . $id; + search_api_track_item_insert('multiple', array($combined_id)); } } @@ -889,6 +892,8 @@ function search_api_entity_update($entity, $type) { if (isset($id)) { search_api_track_item_change($type, array($id)); + $combined_id = $type . '/' . $id; + search_api_track_item_change('multiple', array($combined_id)); } } @@ -910,6 +915,8 @@ function search_api_entity_delete($entity, $type) { list($id) = entity_extract_ids($type, $entity); if (isset($id)) { search_api_track_item_delete($type, array($id)); + $combined_id = $type . '/' . $id; + search_api_track_item_delete('multiple', array($combined_id)); } } @@ -967,16 +974,19 @@ function search_api_flush_caches() { function search_api_search_api_item_type_info() { $types = array(); - foreach (entity_get_property_info() as $type => $property_info) { - if ($info = entity_get_info($type)) { - $types[$type] = array( - 'name' => $info['label'], - 'datasource controller' => 'SearchApiEntityDataSourceController', - 'entity_type' => $type, - ); - } + foreach (search_api_entity_type_options_list() as $type => $label) { + $types[$type] = array( + 'name' => $label, + 'datasource controller' => 'SearchApiEntityDataSourceController', + 'entity_type' => $type, + ); } + $types['multiple'] = array( + 'name' => t('Multiple types'), + 'datasource controller' => 'SearchApiCombinedEntityDataSourceController', + ); + return $types; } @@ -1058,6 +1068,11 @@ function search_api_search_api_alter_callback_info() { 'description' => t('Exclude unpublished nodes from the index. Caution: This only affects the indexed nodes themselves. If an enabled node has references to disabled nodes, those will still be indexed (or displayed) normally.'), 'class' => 'SearchApiAlterNodeStatus', ); + $callbacks['search_api_alter_user_status'] = array( + 'name' => t('Exclude blocked users'), + 'description' => t('Exclude blocked users from the index. Caution: This only affects the indexed users themselves. If an active user account includes a reference to a disabled user, that reference will still be indexed (or displayed) normally.'), + 'class' => 'SearchApiAlterUserStatus', + ); return $callbacks; } @@ -1172,7 +1187,10 @@ function search_api_track_item_change($type, array $item_ids) { return; } try { - search_api_get_datasource_controller($type)->trackItemChange($item_ids, $indexes); + $returned_indexes = search_api_get_datasource_controller($type)->trackItemChange($item_ids, $indexes); + if (isset($returned_indexes)) { + $indexes = $returned_indexes; + } foreach ($indexes as $index) { if (!empty($index->options['index_directly'])) { // For indexes with the index_directly option set, queue the items to be @@ -1247,7 +1265,10 @@ function search_api_track_item_delete($type, array $item_ids) { $indexes = search_api_index_load_multiple(FALSE, $conditions); if ($indexes) { try { - search_api_get_datasource_controller($type)->trackItemDelete($item_ids, $indexes); + $changed_indexes = search_api_get_datasource_controller($type)->trackItemDelete($item_ids, $indexes); + if (isset($changed_indexes)) { + $indexes = $changed_indexes; + } } catch (SearchApiException $e) { $vars['%item_type'] = $type; @@ -1257,8 +1278,9 @@ function search_api_track_item_delete($type, array $item_ids) { // Then, delete it from all servers. Servers of disabled indexes have to be // considered, too! - unset($conditions['enabled']); - foreach (search_api_index_load_multiple(FALSE, $conditions) as $index) { + $conditions['enabled'] = 0; + $indexes = array_merge($indexes, search_api_index_load_multiple(FALSE, $conditions)); + foreach ($indexes as $index) { try { if ($server = $index->server()) { $server->deleteItems($item_ids, $index); @@ -2824,6 +2846,66 @@ function search_api_index_options_list() { return $ret; } +/** + * Options list callback for entity types. + * + * Will only include entity types which specify entity property information. + * + * @return string[] + * An array of entity type machine names mapped to their human-readable + * names. + */ +function search_api_entity_type_options_list() { + $types = array(); + foreach (array_keys(entity_get_property_info()) as $type) { + $info = entity_get_info($type); + if ($info) { + $types[$type] = $info['label']; + } + } + return $types; +} + +/** + * Options list callback for entity type bundles. + * + * Will include all bundles for all entity types which specify entity property + * information, in a format combining both entity type and bundle. + * + * @return string[] + * An array of bundle identifiers mapped to their human-readable names. + */ +function search_api_combined_bundle_options_list() { + $types = array(); + foreach (array_keys(entity_get_property_info()) as $type) { + $info = entity_get_info($type); + if (!empty($info['bundles'])) { + foreach ($info['bundles'] as $bundle => $bundle_info) { + $types["$type:$bundle"] = $bundle_info['label']; + } + } + } + return $types; +} + +/** + * Retrieves a human-readable label for a multi-type index item. + * + * Provided as a non-object alternative to + * SearchApiCombinedEntityDataSourceController::getItemLabel() so it can be used + * as a getter callback. + * + * @param object $item + * An item of the "multiple" item type. + * + * @return string|null + * Either a human-readable label for the item, or NULL if none is available. + */ +function search_api_get_multi_type_item_label($item) { + $label = entity_label($item->item_type, $item->{$item->item_type}); + return $label ? $label : NULL; +} + /** * Shutdown function which indexes all queued items, if any. */ diff --git a/www7/sites/all/modules/contrib/search_api/tests/search_api_test.info b/www7/sites/all/modules/contrib/search_api/tests/search_api_test.info index 74fb5f5da..9e398fc96 100644 --- a/www7/sites/all/modules/contrib/search_api/tests/search_api_test.info +++ b/www7/sites/all/modules/contrib/search_api/tests/search_api_test.info @@ -10,9 +10,9 @@ files[] = search_api_test.module hidden = TRUE -; Information added by Drupal.org packaging script on 2015-06-03 -version = "7.x-1.15" +; Information added by Drupal.org packaging script on 2015-08-30 +version = "7.x-1.16" core = "7.x" project = "search_api" -datestamp = "1433352300" +datestamp = "1440962813" diff --git a/www7/sites/all/modules/contrib/search_api_solr/CHANGELOG.txt b/www7/sites/all/modules/contrib/search_api_solr/CHANGELOG.txt index b22b3ef20..6de51cc31 100644 --- a/www7/sites/all/modules/contrib/search_api_solr/CHANGELOG.txt +++ b/www7/sites/all/modules/contrib/search_api_solr/CHANGELOG.txt @@ -1,10 +1,24 @@ -Search API Solr search 1.8 (2015-06-15): +Search API Solr Search 1.9 (2015-08-30): +---------------------------------------- +- #2503829 by das-peter: Added support for grouping on "magic" fields. +- #2503617 by tobiasb: Fixed undefined "status_message" property in HTTP + response object. +- #2313591 by thePanz, nadavoid: Added support for random sorting. +- #2004596 by drunken monkey: Fixed workarounds for MLT problems. +- #2486533 by drunken monkey: Fixed the default operator in the Solr 5 configs. +- #2466897 by drunken monkey: Fixed Solr version options in server settings. +- #1918904 by arnested, ramlev, drunken monkey: Added an alter hook for + autocomplete suggestions. +- #2532812 by drunken monkey: Improved performance for filter-only queries. +- #2463523 by bmunslow: Fixed field boosts in multi-index searches. + +Search API Solr Search 1.8 (2015-06-15): ---------------------------------------- - #2502511 by drunken monkey: Fixed index removal on Solr 5. - #2441117 by drunken monkey: Fixed unnecessary reindexing after changing fields' boosts. -Search API Solr search 1.7 (2015-06-08): +Search API Solr Search 1.7 (2015-06-08): ---------------------------------------- - #2466489 by drunken monkey: Changed installation instructions to point to the handbook. @@ -20,7 +34,7 @@ Search API Solr search 1.7 (2015-06-08): with a bounding box. - #2333133 by drunken monkey: Fixed behavior of filters on fulltext fields. -Search API Solr search 1.6 (2014-09-08): +Search API Solr Search 1.6 (2014-09-08): ---------------------------------------- - #2050961 by das-peter, drunken monkey: Added proximity/distance information to search results. @@ -32,7 +46,7 @@ Search API Solr search 1.6 (2014-09-08): - #2270767 by RaF: Fixed search_api_solr_views_data_alter() not always returning all virtual fields. -Search API Solr search 1.5 (2014-05-23): +Search API Solr Search 1.5 (2014-05-23): ---------------------------------------- - #2216895 by das-peter: Added support for empty/non-empty conditions on location field types. @@ -52,7 +66,7 @@ Search API Solr search 1.5 (2014-05-23): only” queries. - #2147573 by drunken monkey: Improved error handling. -Search API Solr search 1.4 (2013-12-25): +Search API Solr Search 1.4 (2013-12-25): ---------------------------------------- - #2157839 by drunken monkey, Nick_vh: Updated config files to the newest version. @@ -70,7 +84,7 @@ Search API Solr search 1.4 (2013-12-25): - #2064377 by Nick_vh: Made configuration files compatible with Solr Cloud. - #2107417 by Nick_vh: Fixed config files for Solr 4.5. -Search API Solr search 1.3 (2013-10-23): +Search API Solr Search 1.3 (2013-10-23): ---------------------------------------- - #2099683 by drunken monkey: Added support for 'virtual fields' in Views. - #1997702 by ianthomas_uk, drunken monkey: Added "AUTO" mode for HTTP method. @@ -80,7 +94,7 @@ Search API Solr search 1.3 (2013-10-23): - #1882190 by corvus_ch, arnested, drunken monkey: Added optional index ID prefixes. -Search API Solr search 1.2 (2013-09-01): +Search API Solr Search 1.2 (2013-09-01): ---------------------------------------- - #1246730 by febbraro, maciej.zgadzaj, drunken monkey: Added a way to alter the Solr document when indexing. @@ -91,7 +105,7 @@ Search API Solr search 1.2 (2013-09-01): - #2045355 by drunken monkey, arpieb: Fixed result mapping of item IDs. - #2050157 by izus: Fixed typo in stopwords.txt. -Search API Solr search 1.1 (2013-07-21): +Search API Solr Search 1.1 (2013-07-21): ---------------------------------------- - #1957730 by drunken monkey: Fixed filter query strings for negated filters. - #2010818 by kenorb, drunken monkey: Added new Files tab showing all used solr @@ -106,7 +120,7 @@ Search API Solr search 1.1 (2013-07-21): multi-valued fields. - #2008034 by bdecarne: Fixed highlighting in multi-index searches. -Search API Solr search 1.0 (2013-06-09): +Search API Solr Search 1.0 (2013-06-09): ---------------------------------------- - #1896080 by drunken monkey: Included additional required config files in the module. @@ -120,7 +134,7 @@ Search API Solr search 1.0 (2013-06-09): support. - #1549244 by cferthorney, drunken monkey: Added SSL Support for Solr servers. -Search API Solr search 1.0, RC 5 (2013-05-17): +Search API Solr Search 1.0, RC 5 (2013-05-17): ---------------------------------------------- - #1190462 by drunken monkey: Documented that enabling HTML filter makes sense. - #1986284 by drunken monkey: Updated common configs to the latest version. @@ -134,7 +148,7 @@ Search API Solr search 1.0, RC 5 (2013-05-17): - #1978600 by chaby: Fixed hook_requirements() for install phase. - #1976930 by drunken monkey: Fixed duplicate method in SearchApiSolrField. -Search API Solr search 1.0, RC 4 (2013-04-22): +Search API Solr Search 1.0, RC 4 (2013-04-22): ---------------------------------------------- - #1744250 by mollux, drunken monkey, das-peter: Added support for location-based searches. @@ -144,7 +158,7 @@ Search API Solr search 1.0, RC 4 (2013-04-22): - #1900644 by Deciphered: Fixed facet handling for multi-index searches. - #1897386 by drunken monkey, NIck_vh: Update the common schema. -Search API Solr search 1.0, RC 3 (2013-01-06): +Search API Solr Search 1.0, RC 3 (2013-01-06): ---------------------------------------------- - #1828260 by drunken monkey: Fixed filtering by index in multi-index searches. - #1509380 by drunken monkey: Adopt common config files. @@ -163,7 +177,7 @@ Search API Solr search 1.0, RC 3 (2013-01-06): - #1299940 by drunken monkey: Fixed handling of empty response. - #1507818 by larowlan: Fixed field boosts for standard request handler. -Search API Solr search 1.0, RC 2 (2012-05-23): +Search API Solr Search 1.0, RC 2 (2012-05-23): ---------------------------------------------- - Fixed escaping of error messages. - #1480170 by kotnik: Fixed return value of hook_requirements(). @@ -173,14 +187,14 @@ Search API Solr search 1.0, RC 2 (2012-05-23): - #1302406 by Steven Jones: Fixed autoload problem during installation. - #1340244 by drunken monkey, alanomaly: Added more helpful error messages. -Search API Solr search 1.0, RC 1 (2011-11-10): +Search API Solr Search 1.0, RC 1 (2011-11-10): ---------------------------------------------- - #1308638 by drunken monkey: Adapted to new structure of field settings. - #1308498 by zenlan, drunken monkey: Added flexibility for facet fields. - #1319544 by drunken monkey: Fixed never delete contents of read-only indexes. - #1309650 by jonhattan, drunken monkey: Added support for the Libraries API. -Search API Solr search 1.0, Beta 4 (2011-09-08): +Search API Solr Search 1.0, Beta 4 (2011-09-08): ------------------------------------------------ - #1230536 by thegreat, drunken monkey: Added support for OR facets. - #1184002 by drunken monkey: Fixed support of the latest SolrPhpClient version. @@ -203,7 +217,7 @@ Search API Solr search 1.0, Beta 4 (2011-09-08): - #1184002 by drunken monkey: Fixed INSTALL.txt to reflect that the module doesn't work with the latest Solr PHP Client version. -Search API Solr search 1.0, Beta 3 (2011-06-06): +Search API Solr Search 1.0, Beta 3 (2011-06-06): ------------------------------------------------ - #1111852 by miiimooo, drunken monkey: Added a 'More like this' feature. - #1153306 by JoeMcGuire, drunken monkey: Added spellchecking support. @@ -213,7 +227,7 @@ Search API Solr search 1.0, Beta 3 (2011-06-06): - #1110820 by becw, drunken monkey: Added support for the Luke request handler. - #1095956 by drunken monkey: Added Solr-specific index alter hook. -Search API Solr search 1.0, Beta 2 (2011-03-04): +Search API Solr Search 1.0, Beta 2 (2011-03-04): ------------------------------------------------ - #1071894 by drunken monkey: Fixed incorrect handling of boolean facets. - #1071796: Add additional help for Solr-specific extensions. @@ -226,7 +240,7 @@ Search API Solr search 1.0, Beta 2 (2011-03-04): - #915174: Remove unnecessary files[] declarations from .info file. - #984134: Add Solr-specific query alter hooks. -Search API Solr search 1.0, Beta 1 (2010-11-29): +Search API Solr Search 1.0, Beta 1 (2010-11-29): ------------------------------------------------ Basic functionality is in place and quite well-tested, including support for facets and for multi-index searches. diff --git a/www7/sites/all/modules/contrib/search_api_solr/includes/service.inc b/www7/sites/all/modules/contrib/search_api_solr/includes/service.inc index 7e728f200..105cdd216 100644 --- a/www7/sites/all/modules/contrib/search_api_solr/includes/service.inc +++ b/www7/sites/all/modules/contrib/search_api_solr/includes/service.inc @@ -222,9 +222,9 @@ class SearchApiSolrService extends SearchApiAbstractService { '#description' => t('Specify the Solr version manually in case it cannot be retrived automatically. The version can be found in the Solr admin interface under "Solr Specification Version" or "solr-spec"'), '#options' => array( '' => t('Determine automatically'), - '1' => '1.4', '3' => '3.x', '4' => '4.x', + '5' => '5.x', ), '#default_value' => $options['solr_version'], ); @@ -321,6 +321,7 @@ class SearchApiSolrService extends SearchApiAbstractService { 'search_api_spellcheck', 'search_api_data_type_location', 'search_api_data_type_geohash', + 'search_api_random_sort', )); if (isset($supported[$feature])) { return TRUE; @@ -643,6 +644,7 @@ class SearchApiSolrService extends SearchApiAbstractService { $ret = array( 'search_api_id' => 'item_id', 'search_api_relevance' => 'score', + 'search_api_random' => 'random', ); // Add the names of any fields configured on the index. @@ -826,10 +828,12 @@ class SearchApiSolrService extends SearchApiAbstractService { $options = $query->getOptions(); $search_fields = $query->getFields(); // Get the index fields to be able to retrieve boosts. - $index_fields = $index->getFields(); + $index_fields = $index->getFields() + array( + 'search_api_relevance' => array('type' => 'decimal', 'indexed' => TRUE), + 'search_api_id' => array('type' => 'integer', 'indexed' => TRUE), + ); $qf = array(); foreach ($search_fields as $f) { - $boost = ''; $boost = isset($index_fields[$f]['boost']) ? '^' . $index_fields[$f]['boost'] : ''; $qf[] = $fields[$f] . $boost; } @@ -851,6 +855,17 @@ class SearchApiSolrService extends SearchApiAbstractService { if (substr($f, 0, 3) == 'ss_') { $f = 'sort_' . substr($f, 3); } + + // The default Solr schema provides a virtual field named "random_SEED" + // that can be used to randomly sort the results; the field is available + // only at query-time. + if ($field == 'search_api_random') { + $params = $query->getOption('search_api_random_sort', array()); + // Random seed: getting the value from parameters or computing a new one. + $seed = !empty($params['seed']) ? $params['seed'] : mt_rand(); + $f = 'random_' . $seed; + } + $order = strtolower($order); $sort[$field] = "$f $order"; } @@ -868,11 +883,16 @@ class SearchApiSolrService extends SearchApiAbstractService { $mlt_params['qt'] = 'mlt'; // The fields to look for similarities in. $mlt_fl = array(); + // Solr 4 (before 4.6) has a bug which results in numeric fields not being + // supported in MLT queries. + $mlt_no_numeric_fields = FALSE; + if ($version == 4) { + $system_info = $this->solr->getSystemInfo(); + $mlt_no_numeric_fields = !isset($system_info->lucene->{'solr-spec-version'}) || version_compare($system_info->lucene->{'solr-spec-version'}, '4.6.0', '<'); + } foreach($mlt['fields'] as $f) { - // Solr 4 has a bug which results in numeric fields not being supported - // in MLT queries. // Date fields don't seem to be supported at all. - if ($fields[$f][0] === 'd' || ($version == 4 && in_array($fields[$f][0], array('i', 'f')))) { + if ($fields[$f][0] === 'd' || ($mlt_no_numeric_fields && in_array($fields[$f][0], array('i', 'f')))) { continue; } $mlt_fl[] = $fields[$f]; @@ -885,6 +905,10 @@ class SearchApiSolrService extends SearchApiAbstractService { $id = $this->createId($index_id, $mlt['id']); $id = call_user_func(array($this->connection_class, 'phrase'), $id); $keys = 'id:' . $id; + // In (early versions of) Solr 5, facets aren't supported with MLT. + if ($version >= 5) { + $facet_params = array(); + } } // Handle spatial filters. @@ -1864,6 +1888,14 @@ class SearchApiSolrService extends SearchApiAbstractService { $this->setRequestHandler($this->request_handler, $call_args); } $second_pass = !isset($this->options['autocorrect_suggest_words']) || $this->options['autocorrect_suggest_words']; + + $alter_data = array( + 'search' => $search, + 'query' => $query, + 'incomplete_key' => $incomplete_key, + 'user_input' => $user_input, + ); + for ($i = 0; $i < ($second_pass ? 2 : 1); ++$i) { try { // Send search request @@ -1872,6 +1904,8 @@ class SearchApiSolrService extends SearchApiAbstractService { $this->preQuery($call_args, $query); $response = $this->solr->search($keys, $params, $http_method); + $alter_data['responses'][] = $response; + if (!empty($response->spellcheck->suggestions)) { $replace = array(); foreach ($response->spellcheck->suggestions as $word => $data) { @@ -1960,9 +1994,11 @@ class SearchApiSolrService extends SearchApiAbstractService { } // Change parameters for second query. unset($params['facet.prefix']); - $keys = trim ($keys . ' ' . $incomplete_key); + $keys = trim($keys . ' ' . $incomplete_key); } + drupal_alter('search_api_solr_autocomplete_suggestions', $suggestions, $alter_data); + return $suggestions; } @@ -2018,7 +2054,8 @@ class SearchApiSolrService extends SearchApiAbstractService { $search_fields = $query->getFields(); $qf = array(); foreach ($search_fields as $f) { - $qf[] = $solr_fields[$f]; + $boost = isset($fields[$f]['boost']) ? '^' . $fields[$f]['boost'] : ''; + $qf[] = $solr_fields[$f] . $boost; } // Extract filters diff --git a/www7/sites/all/modules/contrib/search_api_solr/includes/solr_connection.inc b/www7/sites/all/modules/contrib/search_api_solr/includes/solr_connection.inc index 63db3b592..13eeeddf4 100644 --- a/www7/sites/all/modules/contrib/search_api_solr/includes/solr_connection.inc +++ b/www7/sites/all/modules/contrib/search_api_solr/includes/solr_connection.inc @@ -392,7 +392,7 @@ class SearchApiSolrConnection implements SearchApiSolrConnectionInterface { $system_info = $this->getSystemInfo(); // Get our solr version number if (isset($system_info->lucene->{'solr-spec-version'})) { - return $system_info->lucene->{'solr-spec-version'}[0]; + return (int) $system_info->lucene->{'solr-spec-version'}; } return 0; } @@ -603,6 +603,7 @@ class SearchApiSolrConnection implements SearchApiSolrConnectionInterface { $result = drupal_http_request($url, $options); + $result->status_message = isset($result->status_message) ? $result->status_message : ''; if (!isset($result->code) || $result->code < 0) { $result->code = 0; $result->status_message = 'Request failed'; @@ -900,26 +901,6 @@ class SearchApiSolrConnection implements SearchApiSolrConnectionInterface { // search keys here. (With our normal service class, empty keys won't be // set, but another module using this connection class might do that.) unset($params['q'], $params['qf']); - - // If we have filters set (which will nearly always be the case, since we - // have to filter by index), move them to the q.alt parameter where - // possible. - if (!empty($params['fq'])) { - $qalt = array(); - foreach ($params['fq'] as $i => $fq) { - // Tagged and negative filters cannot be moved to q.alt. - if ($fq[0] !== '{' && $fq[0] !== '-') { - $qalt[] = "($fq)"; - unset($params['fq'][$i]); - } - } - if ($qalt) { - $params['q.alt'] = implode(' AND ', $qalt); - } - if (empty($params['fq'])) { - unset($params['fq']); - } - } } // Build the HTTP query string. We have our own method for that since PHP's diff --git a/www7/sites/all/modules/contrib/search_api_solr/search_api_solr.api.php b/www7/sites/all/modules/contrib/search_api_solr/search_api_solr.api.php index c20d63232..bcbb114d7 100644 --- a/www7/sites/all/modules/contrib/search_api_solr/search_api_solr.api.php +++ b/www7/sites/all/modules/contrib/search_api_solr/search_api_solr.api.php @@ -161,6 +161,24 @@ function search_api_solr_hook_search_api_data_type_info() { ); } +/** + * Alter autocomplete suggestions returned from Solr servers. + * + * @param array $suggestions + * An array of suggestions to be altered, in the structure documented in + * SearchApiAutocompleteSuggesterInterface::getAutocompleteSuggestions(). + * @param array $alter_data + * An associative array of data about the search, with the following keys: + * "search", "query", "incomplete_key", "user_input", which correspond to the + * arguments to SearchApiAutocompleteInterface::getAutocompleteSuggestions(); + * and "responses", an array containing the Solr response objects used for + * constructing the suggestions. + */ +function hook_search_api_solr_autocomplete_suggestions_alter(array &$suggestions, array &$alter_data) { + // Always also suggest the original user input. + array_unshift($suggestions, trim($alter_data['user_input'])); +} + /** * @} End of "addtogroup hooks". */ diff --git a/www7/sites/all/modules/contrib/search_api_solr/search_api_solr.info b/www7/sites/all/modules/contrib/search_api_solr/search_api_solr.info index f00b47ee4..4dffa53d5 100644 --- a/www7/sites/all/modules/contrib/search_api_solr/search_api_solr.info +++ b/www7/sites/all/modules/contrib/search_api_solr/search_api_solr.info @@ -11,9 +11,9 @@ files[] = includes/solr_connection.interface.inc files[] = includes/solr_field.inc files[] = includes/spellcheck.inc -; Information added by Drupal.org packaging script on 2015-06-15 -version = "7.x-1.8" +; Information added by Drupal.org packaging script on 2015-08-30 +version = "7.x-1.9" core = "7.x" project = "search_api_solr" -datestamp = "1434376081" +datestamp = "1440962648" diff --git a/www7/sites/all/modules/contrib/search_api_solr/solr-conf/5.x/solrconfig.xml b/www7/sites/all/modules/contrib/search_api_solr/solr-conf/5.x/solrconfig.xml index bfe1c03ea..c378343ab 100644 --- a/www7/sites/all/modules/contrib/search_api_solr/solr-conf/5.x/solrconfig.xml +++ b/www7/sites/all/modules/contrib/search_api_solr/solr-conf/5.x/solrconfig.xml @@ -866,6 +866,7 @@ json true text + AND @@ -996,6 +997,7 @@ content + AND 1 1 3 @@ -1013,6 +1015,7 @@ content explicit true + AND @@ -1248,6 +1251,7 @@ true json true + AND diff --git a/www7/sites/all/modules/contrib/twitter_block/twitter_block.admin.inc b/www7/sites/all/modules/contrib/twitter_block/twitter_block.admin.inc index 272d45906..3e67da2e7 100644 --- a/www7/sites/all/modules/contrib/twitter_block/twitter_block.admin.inc +++ b/www7/sites/all/modules/contrib/twitter_block/twitter_block.admin.inc @@ -5,6 +5,28 @@ * Admin page callbacks for the Twitter Block module. */ +/** + * Implements hook_admin_settings() for module settings configuration. + */ +function twitter_block_admin_settings_form($form, &$form_state) { + // Advanced feature configurations. + $form['advanced'] = array( + '#type' => 'fieldset', + '#title' => t('Advanced settings'), + '#collapsible' => TRUE, + '#collapsed' => TRUE, + ); + + $form['advanced']['twitter_block_cache'] = array( + '#type' => 'checkbox', + '#title' => t('Locally cache widget code'), + '#description' => t("If checked, the widget code is retrieved from Twitter and cached locally. It is updated daily from Twitter's servers to ensure updates to widget code are reflected in the local copy."), + '#default_value' => variable_get('twitter_block_cache', 0), + ); + + return system_settings_form($form); +} + /** * Form constructor for the add block form. * diff --git a/www7/sites/all/modules/contrib/twitter_block/twitter_block.info b/www7/sites/all/modules/contrib/twitter_block/twitter_block.info index bb127f0db..19aebfc2a 100644 --- a/www7/sites/all/modules/contrib/twitter_block/twitter_block.info +++ b/www7/sites/all/modules/contrib/twitter_block/twitter_block.info @@ -8,9 +8,9 @@ configure = admin/structure/block dependencies[] = block -; Information added by Drupal.org packaging script on 2014-07-09 -version = "7.x-2.2" +; Information added by Drupal.org packaging script on 2015-09-15 +version = "7.x-2.3" core = "7.x" project = "twitter_block" -datestamp = "1404893628" +datestamp = "1442349540" diff --git a/www7/sites/all/modules/contrib/twitter_block/twitter_block.install b/www7/sites/all/modules/contrib/twitter_block/twitter_block.install index 0b4cbecd4..9c9691823 100644 --- a/www7/sites/all/modules/contrib/twitter_block/twitter_block.install +++ b/www7/sites/all/modules/contrib/twitter_block/twitter_block.install @@ -72,12 +72,15 @@ function twitter_block_uninstall() { // Remove variables. variable_del('twitter_block_cache'); variable_del('twitter_block_last_cache'); +} - // Remove the cache directory. +/** + * Implements hook_disable(). + * + * Remove cache directory if module is disabled (or uninstalled). + */ +function twitter_block_disable() { twitter_block_clear_js_cache(); - - // Clear the site cache. - cache_clear_all(); } /** diff --git a/www7/sites/all/modules/contrib/twitter_block/twitter_block.module b/www7/sites/all/modules/contrib/twitter_block/twitter_block.module index bca3a5656..c7e349e03 100644 --- a/www7/sites/all/modules/contrib/twitter_block/twitter_block.module +++ b/www7/sites/all/modules/contrib/twitter_block/twitter_block.module @@ -10,7 +10,7 @@ */ function twitter_block_cron() { // Regenerate the JavaScript file every day. - if (REQUEST_TIME - variable_get('twitter_block_last_cache', 0) >= 86400 && variable_get('twitter_block_cache', TRUE)) { + if (REQUEST_TIME - variable_get('twitter_block_last_cache', 0) >= 86400 && variable_get('twitter_block_cache', 0)) { // Synchronize the widget code and update it if remote file have changed. twitter_block_cache(TRUE); @@ -26,9 +26,23 @@ function twitter_block_help($path, $arg) { switch ($path) { case 'admin/structure/block/add-twitter-block': return '

' . t('Use this page to create a new custom Twitter block.') . '

'; + case 'admin/config/system/twitter-block': + return '

' . t('Configure global settings for Twitter blocks.') . '

'; } } +/** + * Implements hook_permission(). + */ +function twitter_block_permission() { + return array( + 'administer twitter block' => array( + 'title' => t('Administer Twitter Block'), + 'description' => t('Perform maintenance tasks for Twitter Block.'), + ), + ); +} + /** * Implements hook_menu(). */ @@ -69,6 +83,17 @@ function twitter_block_menu() { 'type' => MENU_CALLBACK, 'file' => 'twitter_block.admin.inc', ); + + $items['admin/config/system/twitter-block'] = array( + 'title' => 'Twitter Block', + 'description' => 'Configure cache settings for Twitter blocks.', + 'page callback' => 'drupal_get_form', + 'page arguments' => array('twitter_block_admin_settings_form'), + 'access arguments' => array('administer twitter block'), + 'type' => MENU_NORMAL_ITEM, + 'file' => 'twitter_block.admin.inc', + ); + return $items; } @@ -406,7 +431,7 @@ function twitter_block_block_view($delta) { ); // Use a locally cached copy of widgets.js by default. - if (variable_get('twitter_block_cache', TRUE) && $url = twitter_block_cache()) { + if (variable_get('twitter_block_cache', 0) && $url = twitter_block_cache()) { $block['content']['#attached']['js'] = array( array( 'data' => $url, diff --git a/www7/sites/all/modules/contrib/views_data_export/.gitignore b/www7/sites/all/modules/contrib/views_data_export/.gitignore new file mode 100644 index 000000000..e43b0f988 --- /dev/null +++ b/www7/sites/all/modules/contrib/views_data_export/.gitignore @@ -0,0 +1 @@ +.DS_Store diff --git a/www7/sites/all/modules/contrib/views_data_export/LICENSE.txt b/www7/sites/all/modules/contrib/views_data_export/LICENSE.txt new file mode 100644 index 000000000..d159169d1 --- /dev/null +++ b/www7/sites/all/modules/contrib/views_data_export/LICENSE.txt @@ -0,0 +1,339 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Lesser General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. diff --git a/www7/sites/all/modules/contrib/views_data_export/README.txt b/www7/sites/all/modules/contrib/views_data_export/README.txt new file mode 100644 index 000000000..7b3685181 --- /dev/null +++ b/www7/sites/all/modules/contrib/views_data_export/README.txt @@ -0,0 +1,45 @@ +Views Data Export +================= + +Introduction +------------ + +This module is designed to provide a way to export large amounts of data from +views. It provides a display plugin that can rendered progressively in a batch. +Style plugins are include that support exporting in the following types: + +* CSV +* Microsoft XLS +* Microsoft Doc +* Basic txt +* Simple xml. + +Using the "Views Data Export" module +------------------------------------ + +1. Add a new "Data export" display to your view. +2. Change its "Style" to the desired export type. e.g. "CSV file". +3. Configure the options (such as name, quote, etc.). You can go back and do + this at any time by clicking the gear icon next to the style plugin you just + selected. +4. Give it a path in the Feed settings such as "path/to/view/csv". +5. Optionally, you can choose to attach this to another of your displays by + updating the "Attach to:" option in feed settings. + +Advanced usage +-------------- + +This module also exposes a drush command that can execute the view and save its +results to a file. + +drush views-data-export [view-name] [display-id] [output-file] + + +History +------- + +This module has its roots in the export module that was part of the views bonus +pack (http://drupal.org/project/views_bonus). However, massive changes were +needed to make the batch export functionality work, and so this fork was +created. See: http://drupal.org/node/805960 + diff --git a/www7/sites/all/modules/contrib/views_data_export/images/csv.png b/www7/sites/all/modules/contrib/views_data_export/images/csv.png new file mode 100644 index 000000000..732d9376c Binary files /dev/null and b/www7/sites/all/modules/contrib/views_data_export/images/csv.png differ diff --git a/www7/sites/all/modules/contrib/views_data_export/images/doc.png b/www7/sites/all/modules/contrib/views_data_export/images/doc.png new file mode 100644 index 000000000..f12c9ef60 Binary files /dev/null and b/www7/sites/all/modules/contrib/views_data_export/images/doc.png differ diff --git a/www7/sites/all/modules/contrib/views_data_export/images/txt.png b/www7/sites/all/modules/contrib/views_data_export/images/txt.png new file mode 100644 index 000000000..6d282e2e6 Binary files /dev/null and b/www7/sites/all/modules/contrib/views_data_export/images/txt.png differ diff --git a/www7/sites/all/modules/contrib/views_data_export/images/views_data_export.xcf b/www7/sites/all/modules/contrib/views_data_export/images/views_data_export.xcf new file mode 100644 index 000000000..ef8f47f34 Binary files /dev/null and b/www7/sites/all/modules/contrib/views_data_export/images/views_data_export.xcf differ diff --git a/www7/sites/all/modules/contrib/views_data_export/images/xls.png b/www7/sites/all/modules/contrib/views_data_export/images/xls.png new file mode 100644 index 000000000..2d256ca3b Binary files /dev/null and b/www7/sites/all/modules/contrib/views_data_export/images/xls.png differ diff --git a/www7/sites/all/modules/contrib/views_data_export/images/xml.png b/www7/sites/all/modules/contrib/views_data_export/images/xml.png new file mode 100644 index 000000000..e677a2585 Binary files /dev/null and b/www7/sites/all/modules/contrib/views_data_export/images/xml.png differ diff --git a/www7/sites/all/modules/contrib/views_data_export/plugins/views_data_export_plugin_display_export.inc b/www7/sites/all/modules/contrib/views_data_export/plugins/views_data_export_plugin_display_export.inc new file mode 100644 index 000000000..a461566a3 --- /dev/null +++ b/www7/sites/all/modules/contrib/views_data_export/plugins/views_data_export_plugin_display_export.inc @@ -0,0 +1,1022 @@ + 'no_batch'); + $options['items_per_page'] = array('default' => '0'); + $options['return_path'] = array('default' => ''); + $options['style_plugin']['default'] = 'views_data_export_csv'; + + // This is the default size of a segment when doing a batched export. + $options['segment_size']['default'] = 100; + + if (isset($options['defaults']['default']['items_per_page'])) { + $options['defaults']['default']['items_per_page'] = FALSE; + } + + return $options; + } + + /** + * Provide the summary for page options in the views UI. + * + * This output is returned as an array. + */ + function options_summary(&$categories, &$options) { + // It is very important to call the parent function here: + parent::options_summary($categories, $options); + + $categories['page']['title'] = t('Data export settings'); + + $options['use_batch'] = array( + 'category' => 'page', + 'title' => t('Batched export'), + 'value' => ($this->get_option('use_batch') == 'batch' ? t('Yes') : t('No')), + ); + + if (!$this->is_compatible() && $this->get_option('use_batch')) { + $options['use_batch']['value'] .= ' ' . t('(Warning: incompatible)') . ''; + } + } + + /** + * Provide the default form for setting options. + */ + function options_form(&$form, &$form_state) { + // It is very important to call the parent function here: + parent::options_form($form, $form_state); + + switch ($form_state['section']) { + case 'use_batch': + $form['#title'] .= t('Batched export'); + $form['use_batch'] = array( + '#type' => 'radios', + '#description' => t(''), + '#default_value' => $this->get_option('use_batch'), + '#options' => array( + 'no_batch' => t('Export data all in one segment. Possible time and memory limit issues.'), + 'batch' => t('Export data in small segments to build a complete export. Recommended for large exports sets (1000+ rows)'), + ), + ); + // Allow the administrator to configure the number of items exported per batch. + $form['segment_size'] = array( + '#type' => 'select', + '#title' => t('Segment size'), + '#description' => t('If each row of your export consumes a lot of memory to render, then reduce this value. Higher values will generally mean that the export completes in less time but will have a higher peak memory usage.'), + '#options' => drupal_map_assoc(range(1, 500)), + '#default_value' => $this->get_option('segment_size'), + '#process' => array('ctools_dependent_process'), + '#dependency' => array( + 'radio:use_batch' => array('batch') + ), + ); + $form['return_path'] = array( + '#title' => t('Return path'), + '#type' => 'textfield', + '#description' => t('Return path after the batched operation, leave empty for default. This path will only be used if the export URL is visited directly, and not by following a link when attached to another view display.'), + '#default_value' => $this->get_option('return_path'), + '#dependency' => array( + 'radio:use_batch' => array('batch') + ), + ); + if (!$this->is_compatible()) { + $form['use_batch']['#disabled'] = TRUE; + $form['use_batch']['#default_value'] = 'no_batch'; + $form['use_batch']['message'] = array ( + '#type' => 'markup', + '#markup' => theme('views_data_export_message', array('message' => t('The underlying database (!db_driver) is incompatible with the batched export option and it has been disabled.', array('!db_driver' => $this->_get_database_driver())), 'type' => 'warning')), + '#weight' => -10, + ); + } + break; + + case 'cache': + // We're basically going to disable using cache plugins, by disabling + // the UI. + if (isset($form['cache']['type']['#options'])) { + foreach ($form['cache']['type']['#options'] as $id => $v) { + if ($id != 'none') { + unset($form['cache']['type']['#options'][$id]); + } + $form['cache']['type']['#description'] = t("Views data export isn't currently compatible with caching plugins."); + } + } + break; + + } + } + + function get_option($option) { + // Force people to never use caching with Views data export. Sorry folks, + // but it causes too many issues for our workflow. If you really want to add + // caching back, then you can subclass this display handler and override + // this method to add it back. + if ($option == 'cache') { + return array('type' => 'none'); + } + + return parent::get_option($option); + } + + /** + * Save the options from the options form. + */ + function options_submit(&$form, &$form_state) { + // It is very important to call the parent function here: + parent::options_submit($form, $form_state); + switch ($form_state['section']) { + case 'use_batch': + $this->set_option('use_batch', $form_state['values']['use_batch']); + $this->set_option('segment_size', $form_state['values']['segment_size']); + $this->set_option('return_path', $form_state['values']['return_path']); + break; + } + } + + /** + * Determine if this view should run as a batch or not. + */ + function is_batched() { + // The source of this option may change in the future. + return ($this->get_option('use_batch') == 'batch') && empty($this->view->live_preview); + } + + /** + * Add HTTP headers for the file export. + */ + function add_http_headers() { + // Ask the style plugin to add any HTTP headers if it wants. + if (method_exists($this->view->style_plugin, 'add_http_headers')) { + $this->view->style_plugin->add_http_headers(); + } + } + + /** + * Execute this display handler. + * + * This is the main entry point for this display. We do different things based + * on the stage in the rendering process. + * + * If we are being called for the very first time, the user has usually just + * followed a link to our view. For this phase we: + * - Register a new batched export with our parent module. + * - Build and execute the view, redirecting the output into a temporary table. + * - Set up the batch. + * + * If we are being called during batch processing we: + * - Set up our variables from the context into the display. + * - Call the rendering layer. + * - Return with the appropriate progress value for the batch. + * + * If we are being called after the batch has completed we: + * - Remove the index table. + * - Show the complete page with a download link. + * - Transfer the file if the download link was clicked. + */ + function execute() { + if (!$this->is_batched()) { + return parent::execute(); + } + + // Try and get a batch context if possible. + $eid = !empty($_GET['eid']) ? $_GET['eid'] : + (!empty($this->batched_execution_state->eid) ? $this->batched_execution_state->eid : FALSE); + if ($eid) { + $this->batched_execution_state = views_data_export_get($eid); + } + + // First time through + if (empty($this->batched_execution_state)) { + $output = $this->execute_initial(); + } + + // Call me on the cached version of this view please + // This allows this view to be programatically executed with nothing + // more than the eid in $_GET in order for it to execute the next chunk + // TODO: What is going on here? + /* + Jamsilver tells me this might be useful one day. + if (!$this->views_data_export_cached_view_loaded) { + $view = views_data_export_view_retrieve($this->batched_execution_state->eid); + $view->set_display($this->view->current_display); + $view->display_handler->batched_execution_state->eid = $this->batched_execution_state->eid; + $view->display_handler->views_data_export_cached_view_loaded = TRUE; + $ret = $view->execute_display($this->view->current_display); + $this->batched_execution_state = &$view->display_handler->batched_execution_state; + return $ret; + }*/ + + // Last time through + if ($this->batched_execution_state->batch_state == VIEWS_DATA_EXPORT_FINISHED) { + $output = $this->execute_final(); + } + // In the middle of processing + else { + $output = $this->execute_normal(); + } + + //Ensure any changes we made to the database sandbox are saved + views_data_export_update($this->batched_execution_state); + + return $output; + } + + + /** + * Initializes the whole export process and starts off the batch process. + * + * Page execution will be ended at the end of this function. + */ + function execute_initial() { + + // Register this export with our central table - get a unique eid + // Also store our view in a cache to be retrieved with each batch call + $this->batched_execution_state = views_data_export_new($this->view->name, $this->view->current_display, $this->outputfile_create()); + views_data_export_view_store($this->batched_execution_state->eid, $this->view); + + // We need to build the index right now, before we lose $_GET etc. + $this->initialize_index(); + //$this->batched_execution_state->fid = $this->outputfile_create(); + + // Initialize the progress counter + $this->batched_execution_state->sandbox['max'] = db_query('SELECT COUNT(*) FROM {' . $this->index_tablename() . '}')->fetchField(); + // Record the time we started. + list($usec, $sec) = explode(' ', microtime()); + $this->batched_execution_state->sandbox['started'] = (float) $usec + (float) $sec; + + // Build up our querystring for the final page callback. + $querystring = array( + 'eid' => $this->batched_execution_state->eid, + 'return-url' => NULL, + ); + + // If we have a configured return path, use that. + if ($this->get_option('return_path')) { + $querystring['return-url'] = $this->get_option('return_path'); + } + // Else if we were attached to another view, grab that for the final URL. + else if (!empty($_GET['attach']) && isset($this->view->display[$_GET['attach']])) { + // Get the path of the attached display: + $querystring['return-url'] = $this->view->get_url(NULL, $this->view->display[$_GET['attach']]->handler->get_path()); + } + + //Set the batch off + $batch = array( + 'operations' => array ( + array('_views_data_export_batch_process', array($this->batched_execution_state->eid, $this->view->current_display, $this->view->get_exposed_input())), + ), + 'title' => t('Building export'), + 'init_message' => t('Export is starting up.'), + 'progress_message' => t('Exporting @percentage% complete,'), + 'error_message' => t('Export has encountered an error.'), + ); + + // We do not return, so update database sandbox now + views_data_export_update($this->batched_execution_state); + + $final_destination = $this->view->get_url(); + + // Provide a way in for others at this point + // e.g. Drush to grab this batch and yet execute it in + // it's own special way + $batch['view_name'] = $this->view->name; + $batch['exposed_filters'] = $this->view->get_exposed_input(); + $batch['display_id'] = $this->view->current_display; + $batch['eid'] = $this->batched_execution_state->eid; + $batch_redirect = array($final_destination, array('query' => $querystring)); + drupal_alter('views_data_export_batch', $batch, $batch_redirect); + + // Modules may have cleared out $batch, indicating that we shouldn't process further. + if (!empty($batch)) { + batch_set($batch); + // batch_process exits + batch_process($batch_redirect); + } + } + + + /** + * Compiles the next chunk of the output file + */ + function execute_normal() { + + // Pass through to our render method, + $output = $this->view->render(); + + // Append what was rendered to the output file. + $this->outputfile_write($output); + + // Store for convenience. + $state = &$this->batched_execution_state; + $sandbox = &$state->sandbox; + + // Update progress measurements & move our state forward + switch ($state->batch_state) { + + case VIEWS_DATA_EXPORT_BODY: + // Remove rendered results from our index + if (count($this->view->result) && ($sandbox['weight_field_alias'])) { + $last = end($this->view->result); + db_delete($this->index_tablename()) + ->condition($sandbox['weight_field_alias'], $last->{$sandbox['weight_field_alias']}, '<=') + ->execute(); + + // Update progress. + $progress = db_query('SELECT COUNT(*) FROM {' . $this->index_tablename() . '}')->fetchField(); + // TODO: These next few lines are messy, clean them up. + $progress = 0.99 - ($progress / $sandbox['max'] * 0.99); + $progress = ((int)floor($progress * 100000)); + $progress = $progress / 100000; + $sandbox['finished'] = $progress; + } + else { + // No more results. + $progress = 0.99; + $state->batch_state = VIEWS_DATA_EXPORT_FOOTER; + } + break; + + case VIEWS_DATA_EXPORT_HEADER: + $sandbox['finished'] = 0; + $state->batch_state = VIEWS_DATA_EXPORT_BODY; + break; + + case VIEWS_DATA_EXPORT_FOOTER: + $sandbox['finished'] = 1; + $state->batch_state = VIEWS_DATA_EXPORT_FINISHED; + break; + } + + // Create a more helpful exporting message. + $sandbox['message'] = $this->compute_time_remaining($sandbox['started'], $sandbox['finished']); + } + + + /** + * Renders the final page + * We should be free of the batch at this point + */ + function execute_final() { + // Should we download the file. + if (!empty($_GET['download'])) { + // This next method will exit. + $this->transfer_file(); + } + else { + // Remove the index table. + $this->remove_index(); + return $this->render_complete(); + } + } + + + /** + * Render the display. + * + * We basically just work out if we should be rendering the header, body or + * footer and call the appropriate functions on the style plugins. + */ + function render() { + + if (!$this->is_batched()) { + $result = parent::render(); + if (empty($this->view->live_preview)) { + $this->add_http_headers(); + } + return $result; + } + + $this->view->build(); + + switch ($this->batched_execution_state->batch_state) { + case VIEWS_DATA_EXPORT_BODY: + $output = $this->view->style_plugin->render_body(); + break; + case VIEWS_DATA_EXPORT_HEADER: + $output = $this->view->style_plugin->render_header(); + break; + case VIEWS_DATA_EXPORT_FOOTER: + $output = $this->view->style_plugin->render_footer(); + break; + } + + return $output; + } + + + + /** + * Trick views into thinking that we have executed the query and got results. + * + * We are called in the build phase of the view, but short circuit straight to + * getting the results and making the view think it has already executed the + * query. + */ + function query() { + + if (!$this->is_batched()) { + return parent::query(); + } + + // Make the query distinct if the option was set. + if ($this->get_option('distinct')) { + $this->view->query->set_distinct(); + } + + if (!empty($this->batched_execution_state->batch_state) && !empty($this->batched_execution_state->sandbox['weight_field_alias'])) { + + switch ($this->batched_execution_state->batch_state) { + case VIEWS_DATA_EXPORT_BODY: + case VIEWS_DATA_EXPORT_HEADER: + case VIEWS_DATA_EXPORT_FOOTER: + // Tell views its been executed. + $this->view->executed = TRUE; + + // Grab our results from the index, and push them into the view result. + // TODO: Handle external databases. + $result = db_query_range('SELECT * FROM {' . $this->index_tablename() . '} ORDER BY ' . $this->batched_execution_state->sandbox['weight_field_alias'] . ' ASC', 0, $this->get_option('segment_size')); + $this->view->result = array(); + foreach ($result as $item_hashed) { + $item = new stdClass(); + // We had to shorten some of the column names in the index, restore + // those now. + foreach ($item_hashed as $hash => $value) { + if (isset($this->batched_execution_state->sandbox['field_aliases'][$hash])) { + $item->{$this->batched_execution_state->sandbox['field_aliases'][$hash]} = $value; + } + else { + $item->{$hash} = $value; + } + } + // Push the restored $item in the views result array. + $this->view->result[] = $item; + } + $this->view->_post_execute(); + break; + } + } + } + + + /** + * Render the 'Export Finished' page with the link to the file on it. + */ + function render_complete() { + $return_path = empty($_GET['return-url']) ? '' : $_GET['return-url']; + + $query = array( + 'download' => 1, + 'eid' => $this->batched_execution_state->eid, + ); + + return theme('views_data_export_complete_page', array( + 'file' => url($this->view->get_url(), array('query' => $query)), + 'errors' => $this->errors, + 'return_url' => $return_path)); + } + + /** + * TBD - What does 'preview' mean for bulk exports? + * According to doc: + * "Fully render the display for the purposes of a live preview or + * some other AJAXy reason. [views_plugin_display.inc:1877]" + * + * Not sure it makes sense for Bulk exports to be previewed in this manner? + * We need the user's full attention to run the batch. Suggestions: + * 1) Provide a link to execute the view? + * 2) Provide a link to the last file we generated?? + * 3) Show a table of the first 20 results? + */ + function preview() { + if (!$this->is_batched()) { + // Can replace with return parent::preview() when views 2.12 lands. + if (!empty($this->view->live_preview)) { + // Change the items per page. + $this->view->set_items_per_page(20); + // Force a pager to be used. + $this->set_option('pager', array('type' => 'some', 'options' => array())); + return '

' . t('A maximum of 20 items will be shown here, all results will be shown on export.') . '

' . check_plain($this->view->render()) . '
'; + } + return $this->view->render(); + } + return ''; + } + + /** + * Transfer the output file to the client. + */ + function transfer_file() { + // Build the view so we can set the headers. + $this->view->build(); + // Arguments can cause the style to not get built. + if (!$this->view->init_style()) { + $this->view->build_info['fail'] = TRUE; + } + // Set the headers. + $this->add_http_headers(); + file_transfer($this->outputfile_path(), array()); + } + + /** + * Called on export initialization. + * + * Modifies the view query to insert the results into a table, which we call + * the 'index', this means we essentially have a snapshot of the results, + * which we can then take time over rendering. + * + * This method is essentially all the best bits of the view::execute() method. + */ + protected function initialize_index() { + $view = &$this->view; + // Get views to build the query. + $view->build(); + + // Change the query object to use our custom one. + switch ($this->_get_database_driver()) { + case 'pgsql': + $query_class = 'views_data_export_plugin_query_pgsql_batched'; + break; + + default: + $query_class = 'views_data_export_plugin_query_default_batched'; + break; + } + $query = new $query_class(); + // Copy the query over: + foreach ($view->query as $property => $value) { + $query->$property = $value; + } + // Replace the query object. + $view->query = $query; + + $view->execute(); + } + + /** + * Given a view, construct an map of hashed aliases to aliases. + * + * The keys of the returned array will have a maximum length of 33 characters. + */ + function field_aliases_create(&$view) { + $all_aliases = array(); + foreach ($view->query->fields as $field) { + if (strlen($field['alias']) > 32) { + $all_aliases['a' . md5($field['alias'])] = $field['alias']; + } + else { + $all_aliases[$field['alias']] = $field['alias']; + } + } + return $all_aliases; + } + + /** + * Create an alias for the weight field in the index. + * + * This method ensures that it isn't the same as any other alias in the + * supplied view's fields. + */ + function _weight_alias_create(&$view) { + $alias = 'vde_weight'; + $all_aliases = array(); + foreach ($view->query->fields as $field) { + $all_aliases[] = $field['alias']; + } + // Keep appending '_' until we are unique. + while (in_array($alias, $all_aliases)) { + $alias .= '_'; + } + return $alias; + } + + /** + * Remove the index. + */ + function remove_index() { + $ret = array(); + if (db_table_exists($this->index_tablename())) { + db_drop_table($this->index_tablename()); + } + } + + /** + * Return the name of the unique table to store the index in. + */ + function index_tablename() { + return VIEWS_DATA_EXPORT_INDEX_TABLE_PREFIX . $this->batched_execution_state->eid; + } + + /** + * Get the output file path. + */ + function outputfile_path() { + if (empty($this->_output_file)) { + if (!empty($this->batched_execution_state->fid)) { + // Return the filename associated with this file. + $this->_output_file = $this->file_load($this->batched_execution_state->fid); + } + else { + return NULL; + } + } + return $this->_output_file->uri; + } + + /** + * Called on export initialization + * Creates the output file, registers it as a temporary file with Drupal + * and returns the fid + */ + protected function outputfile_create() { + + $dir = variable_get('views_data_export_directory', 'temporary://views_plugin_display'); + + // Make sure the directory exists first. + if (!file_prepare_directory($dir, FILE_CREATE_DIRECTORY | FILE_MODIFY_PERMISSIONS)) { + $this->abort_export(t('Could not create temporary directory for result export (@dir). Check permissions.', array ('@dir' => $dir))); + } + + $path = drupal_tempnam($dir, 'views_data_export'); + + // Save the file into the DB. + $file = $this->file_save_file($path); + + return $file->fid; + } + + /** + * Write to the output file. + */ + protected function outputfile_write($string) { + $output_file = $this->outputfile_path(); + if (file_put_contents($output_file, $string, FILE_APPEND) === FALSE) { + $this->abort_export(t('Could not write to temporary output file for result export (@file). Check permissions.', array ('@file' => $output_file))); + } + } + + function abort_export($errors) { + // Just cause the next batch to do the clean-up + if (!is_array($errors)) { + $errors = array($errors); + } + foreach ($errors as $error) { + drupal_set_message($error . ' ['. t('Export Aborted') . ']', 'error'); + } + $this->batched_execution_state->batch_state = VIEWS_DATA_EXPORT_FINISHED; + } + + /** + * Load a file from the database. + * + * @param $fid + * A numeric file id or string containing the file path. + * @return + * A file object. + */ + function file_load($fid) { + return file_load($fid); + } + + /** + * Save a file into a file node after running all the associated validators. + * + * This function is usually used to move a file from the temporary file + * directory to a permanent location. It may be used by import scripts or other + * modules that want to save an existing file into the database. + * + * @param $filepath + * The local file path of the file to be saved. + * @return + * An array containing the file information, or 0 in the event of an error. + */ + function file_save_file($filepath) { + return file_save_data('', $filepath, FILE_EXISTS_REPLACE); + } + + /** + * Helper function that computes the time remaining + */ + function compute_time_remaining($started, $finished) { + list($usec, $sec) = explode(' ', microtime()); + $now = (float) $usec + (float) $sec; + $diff = round(($now - $started), 0); + // So we've taken $diff seconds to get this far. + if ($finished > 0) { + $estimate_total = $diff / $finished; + $stamp = max(1, $estimate_total - $diff); + // Round up to nearest 30 seconds. + $stamp = ceil($stamp / 30) * 30; + // Set the message in the batch context. + return t('Time remaining: about @interval.', array('@interval' => format_interval($stamp))); + } + } + + /** + * Checks the driver of the database underlying + * this query and returns FALSE if it is imcompatible + * with the approach taken in this display. + * Basically mysql & mysqli will be fine, pg will not + */ + function is_compatible() { + $incompatible_drivers = array ( + //'pgsql', + ); + $db_driver = $this->_get_database_driver(); + return !in_array($db_driver, $incompatible_drivers); + } + + function _get_database_driver() { + $name = !empty($this->view->base_database) ? $this->view->base_database : 'default'; + $conn_info = Database::getConnectionInfo($name); + return $conn_info['default']['driver']; + } +} + +class views_data_export_plugin_query_default_batched extends views_plugin_query_default { + + + /** + * Executes the query and fills the associated view object with according + * values. + * + * Values to set: $view->result, $view->total_rows, $view->execute_time, + * $view->current_page. + */ + function execute(&$view) { + $display_handler = &$view->display_handler; + $external = FALSE; // Whether this query will run against an external database. + $query = $view->build_info['query']; + $count_query = $view->build_info['count_query']; + + $query->addMetaData('view', $view); + $count_query->addMetaData('view', $view); + + if (empty($this->options['disable_sql_rewrite'])) { + $base_table_data = views_fetch_data($this->base_table); + if (isset($base_table_data['table']['base']['access query tag'])) { + $access_tag = $base_table_data['table']['base']['access query tag']; + $query->addTag($access_tag); + $count_query->addTag($access_tag); + } + } + + $items = array(); + if ($query) { + $additional_arguments = module_invoke_all('views_query_substitutions', $view); + + // Count queries must be run through the preExecute() method. + // If not, then hook_query_node_access_alter() may munge the count by + // adding a distinct against an empty query string + // (e.g. COUNT DISTINCT(1) ...) and no pager will return. + // See pager.inc > PagerDefault::execute() + // http://api.drupal.org/api/drupal/includes--pager.inc/function/PagerDefault::execute/7 + // See http://drupal.org/node/1046170. + $count_query->preExecute(); + + // Build the count query. + $count_query = $count_query->countQuery(); + + // Add additional arguments as a fake condition. + // XXX: this doesn't work... because PDO mandates that all bound arguments + // are used on the query. TODO: Find a better way to do this. + if (!empty($additional_arguments)) { + // $query->where('1 = 1', $additional_arguments); + // $count_query->where('1 = 1', $additional_arguments); + } + + $start = microtime(TRUE); + + if ($this->pager->use_count_query() || !empty($view->get_total_rows)) { + $this->pager->execute_count_query($count_query); + } + + // Let the pager modify the query to add limits. + $this->pager->pre_execute($query); + + if (!empty($this->limit) || !empty($this->offset)) { + // We can't have an offset without a limit, so provide a very large limit instead. + $limit = intval(!empty($this->limit) ? $this->limit : 999999); + $offset = intval(!empty($this->offset) ? $this->offset : 0); + $query->range($offset, $limit); + } + + try { + // The $query is final and ready to go, we are going to redirect it to + // become an insert into our table, sneaky! + // Our query will look like: + // CREATE TABLE {idx} SELECT @row := @row + 1 AS weight_alias, cl.* FROM + // (-query-) AS cl, (SELECT @row := 0) AS r + // We do some magic to get the row count. + + $display_handler->batched_execution_state->sandbox['weight_field_alias'] = $display_handler->_weight_alias_create($view); + + $display_handler->batched_execution_state->sandbox['field_aliases'] = $display_handler->field_aliases_create($view); + $select_aliases = array(); + foreach ($display_handler->batched_execution_state->sandbox['field_aliases'] as $hash => $alias) { + $select_aliases[] = "cl.$alias AS $hash"; + } + + // TODO: this could probably be replaced with a query extender and new query type. + $query->preExecute(); + $args = $query->getArguments(); + $insert_query = 'CREATE TABLE {' . $display_handler->index_tablename() . '} SELECT @row := @row + 1 AS ' . $display_handler->batched_execution_state->sandbox['weight_field_alias'] . ', ' . implode(', ', $select_aliases) . ' FROM (' . (string)$query . ') AS cl, (SELECT @row := 0) AS r'; + db_query($insert_query, $args); + + + $view->result = array(); + + $this->pager->post_execute($view->result); + + if ($this->pager->use_pager()) { + $view->total_rows = $this->pager->get_total_items(); + } + + // Now create an index for the weight field, otherwise the queries on the + // index will take a long time to execute. + db_add_unique_key($display_handler->index_tablename(), $display_handler->batched_execution_state->sandbox['weight_field_alias'], array($display_handler->batched_execution_state->sandbox['weight_field_alias'])); + } + catch (Exception $e) { + $view->result = array(); + debug('Exception: ' . $e->getMessage()); + } + + } + $view->execute_time = microtime(TRUE) - $start; + } +} + +class views_data_export_plugin_query_pgsql_batched extends views_data_export_plugin_query_default_batched { + + + /** + * Executes the query and fills the associated view object with according + * values. + * + * Values to set: $view->result, $view->total_rows, $view->execute_time, + * $view->current_page. + */ + function execute(&$view) { + $display_handler = &$view->display_handler; + $external = FALSE; // Whether this query will run against an external database. + $query = $view->build_info['query']; + $count_query = $view->build_info['count_query']; + + $query->addMetaData('view', $view); + $count_query->addMetaData('view', $view); + + if (empty($this->options['disable_sql_rewrite'])) { + $base_table_data = views_fetch_data($this->base_table); + if (isset($base_table_data['table']['base']['access query tag'])) { + $access_tag = $base_table_data['table']['base']['access query tag']; + $query->addTag($access_tag); + $count_query->addTag($access_tag); + } + } + + $items = array(); + if ($query) { + $additional_arguments = module_invoke_all('views_query_substitutions', $view); + + // Count queries must be run through the preExecute() method. + // If not, then hook_query_node_access_alter() may munge the count by + // adding a distinct against an empty query string + // (e.g. COUNT DISTINCT(1) ...) and no pager will return. + // See pager.inc > PagerDefault::execute() + // http://api.drupal.org/api/drupal/includes--pager.inc/function/PagerDefault::execute/7 + // See http://drupal.org/node/1046170. + $count_query->preExecute(); + + // Build the count query. + $count_query = $count_query->countQuery(); + + // Add additional arguments as a fake condition. + // XXX: this doesn't work... because PDO mandates that all bound arguments + // are used on the query. TODO: Find a better way to do this. + if (!empty($additional_arguments)) { + // $query->where('1 = 1', $additional_arguments); + // $count_query->where('1 = 1', $additional_arguments); + } + + $start = microtime(TRUE); + + if ($this->pager->use_count_query() || !empty($view->get_total_rows)) { + $this->pager->execute_count_query($count_query); + } + + // Let the pager modify the query to add limits. + $this->pager->pre_execute($query); + + if (!empty($this->limit) || !empty($this->offset)) { + // We can't have an offset without a limit, so provide a very large limit instead. + $limit = intval(!empty($this->limit) ? $this->limit : 999999); + $offset = intval(!empty($this->offset) ? $this->offset : 0); + $query->range($offset, $limit); + } + + try { + // The $query is final and ready to go, we are going to redirect it to + // become an insert into our table, sneaky! + // Our query will look like: + // CREATE TABLE {idx} SELECT @row := @row + 1 AS weight_alias, cl.* FROM + // (-query-) AS cl, (SELECT @row := 0) AS r + // We do some magic to get the row count. + + $display_handler->batched_execution_state->sandbox['weight_field_alias'] = $display_handler->_weight_alias_create($view); + + $display_handler->batched_execution_state->sandbox['field_aliases'] = $display_handler->field_aliases_create($view); + $select_aliases = array(); + foreach ($display_handler->batched_execution_state->sandbox['field_aliases'] as $hash => $alias) { + $select_aliases[] = "cl.$alias AS $hash"; + } + + // TODO: this could probably be replaced with a query extender and new query type. + $query->preExecute(); + $args = $query->getArguments(); + // Create temporary sequence + $seq_name = $display_handler->index_tablename() . '_seq'; + db_query('CREATE TEMP sequence ' . $seq_name); + // Query uses sequence to create row number + $insert_query = 'CREATE TABLE {' . $display_handler->index_tablename() . "} AS SELECT nextval('". $seq_name . "') AS " . $display_handler->batched_execution_state->sandbox['weight_field_alias'] . ', ' . implode(', ', $select_aliases) . ' FROM (' . (string)$query . ') AS cl'; + db_query($insert_query, $args); + + + $view->result = array(); + + $this->pager->post_execute($view->result); + + if ($this->pager->use_pager()) { + $view->total_rows = $this->pager->get_total_items(); + } + + // Now create an index for the weight field, otherwise the queries on the + // index will take a long time to execute. + db_add_unique_key($display_handler->index_tablename(), $display_handler->batched_execution_state->sandbox['weight_field_alias'], array($display_handler->batched_execution_state->sandbox['weight_field_alias'])); + } + catch (Exception $e) { + $view->result = array(); + debug('Exception: ' . $e->getMessage()); + } + + } + $view->execute_time = microtime(TRUE) - $start; + } +} diff --git a/www7/sites/all/modules/contrib/views_data_export/plugins/views_data_export_plugin_style_export.inc b/www7/sites/all/modules/contrib/views_data_export/plugins/views_data_export_plugin_style_export.inc new file mode 100644 index 000000000..493796f65 --- /dev/null +++ b/www7/sites/all/modules/contrib/views_data_export/plugins/views_data_export_plugin_style_export.inc @@ -0,0 +1,384 @@ + $this->definition['export feed text'], + 'translatable' => TRUE, + ); + $options['provide_file'] = array( + 'default' => FALSE, + 'translatable' => FALSE, + ); + $options['filename'] = array( + 'default' => $this->definition['export feed file'], + 'translatable' => FALSE, + ); + $options['parent_sort'] = array( + 'default' => FALSE, + 'translatable' => FALSE, + ); + return $options; + } + + /** + * Options form mini callback. + * + * @param $form + * Form array to add additional fields to. + * @param $form_state + * State of the form. + * @return + * None. + */ + function options_form(&$form, &$form_state) { + $form['attach_text'] = array( + '#type' => 'textfield', + '#title' => t('Attach text'), + '#default_value' => $this->options['attach_text'], + '#description' => t('This text is used in building the feed link. By default it is the "alt" text for the feed image.'), + ); + $form['provide_file'] = array( + '#type' => 'checkbox', + '#title' => t('Provide as file'), + '#default_value' => $this->options['provide_file'], + '#description' => t('By deselecting this, the xml file will be provided as a feed instead of a file for download.'), + ); + $form['filename'] = array( + '#type' => 'textfield', + '#title' => t('Filename'), + '#default_value' => $this->options['filename'], + '#description' => t('The filename that will be suggested to the browser for downloading purposes. You may include replacement patterns from the list below.'), + '#process' => array('ctools_dependent_process'), + '#dependency' => array( + 'edit-style-options-provide-file' => array(TRUE), + ), + ); + + // General token replacement. + $output = t('

The following substitution patterns are available for this display. Use the pattern shown on the left to display the value indicated on the right.

'); + $items = array( + '%view == ' . t('View name'), + '%display == ' . t('Display name'), + ); + + $output .= theme('item_list', array('items' => $items)); + + // Get a list of the available arguments for token replacement. + $options = array(); + + $count = 0; // This lets us prepare the key as we want it printed. + foreach ($this->view->display_handler->get_handlers('argument') as $arg => $handler) { + $options[t('Arguments')]['%' . ++$count . '-title'] = t('@argument title', array('@argument' => $handler->ui_name())); + $options[t('Arguments')]['%' . $count . '-value'] = t('@argument value', array('@argument' => $handler->ui_name())); + } + + // Append the list with exposed filters stuff. + $options[t('Exposed filters')]['%exposed'] = t('effective exposed filters, like filter1_foo-filter2_bar'); + + // ...and datestamp. + $time = REQUEST_TIME; + $parts = array( + 'full' => 'Y-m-d\TH-i-s', + 'yy' => 'y', + 'yyyy' => 'Y', + 'mm' => 'm', + 'mmm' => 'M', + 'dd' => 'd', + 'ddd' => 'D', + 'hh' => 'H', + 'ii' => 'i', + 'ss' => 's', + ); + foreach ($parts as $part => $format) { + $options[t('Timestamp')]['%timestamp-' . $part] = format_date($time, 'custom', $format); + } + + // We have some options, so make a list. + if (!empty($options)) { + foreach (array_keys($options) as $type) { + if (!empty($options[$type])) { + $items = array(); + foreach ($options[$type] as $key => $value) { + $items[] = $key . ' == ' . $value; + } + $output .= theme('item_list', array('items' => $items, 'title' => $type)); + } + } + } + $form['help'] = array( + '#type' => 'fieldset', + '#title' => t('Replacement patterns'), + '#collapsible' => TRUE, + '#collapsed' => TRUE, + '#value' => $output, + '#dependency' => array( + 'edit-style-options-provide-file' => array(1), + ), + ); + $form['parent_sort'] = array( + '#type' => 'checkbox', + '#title' => t('Parent sort'), + '#default_value' => $this->options['parent_sort'], + '#description' => t('Try to apply any additional sorting from the attached display like table sorting to the exported feed.'), + ); + } + + /** + * Attach this view to another display as a feed. + * + * Provide basic functionality for all export style views like attaching a + * feed image link. + */ + function attach_to($display_id, $path, $title) { + if ($this->display->handler->access()) { + $type = $this->definition['export feed type']; + $theme_pattern = array( + 'views_data_export_feed_icon__' . $this->view->name . '__' . $display_id . '__' . $type, + 'views_data_export_feed_icon__' . $this->view->name . '__' . $display_id, + 'views_data_export_feed_icon__' . $this->view->name . '__' . $type, + 'views_data_export_feed_icon__' . $display_id . '__' . $type, + 'views_data_export_feed_icon__' . $display_id, + 'views_data_export_feed_icon__' . $type, + 'views_data_export_feed_icon', + ); + $query = $this->view->get_exposed_input(); + // Stash the display id we're coming form in the url so we can hijack it later. + if ($this->options['parent_sort']) { + $query['attach'] = $display_id; + } + if (!isset($this->view->feed_icon)) { + $this->view->feed_icon = ''; + } + $this->view->feed_icon .= theme($theme_pattern, array( + 'image_path' => $this->definition['export feed icon'], + 'url' => $this->view->get_url(NULL, $path), + 'query' => $query, + 'text' => $this->options['attach_text'], + ) + ); + } + } + + function build_sort() { + + // Bypass doing any sort of testing if parent sorting is disabled. + if (!$this->options['parent_sort']) { + return parent::build_sort(); + } + + $displays = $this->display->handler->get_option('displays'); + + // Here is later. We can get the passed argument and use it to know which + // display we can from and then do some addition processing. + // If the display exists and is attached these two tests will succeed. + if (isset($_GET['attach']) && isset($displays[$_GET['attach']]) && $displays[$_GET['attach']]) { + // Setup the second style we're going to be using to sort on. + $plugin_id = $displays[$_GET['attach']]; + $parent_display = $this->view->display[$plugin_id]; + $style_name = $parent_display->handler->get_option('style_plugin'); + $style_options = $parent_display->handler->get_option('style_options'); + $this->extra_style = views_get_plugin('style', $style_name); + $this->extra_style->init($this->view, $parent_display, $style_options); + + // Call the second styles sort funciton and return the value. + return $this->extra_style->build_sort(); + } + } + + function build_sort_post() { + // If we found an extra style plugin earlier, pass off the build_sort_post call to it. + if (isset($this->extra_style)) { + return $this->extra_style->build_sort_post(); + } + else { + return parent::build_sort_post(); + } + } + + /** + * Render the display in this style. + */ + function render() { + if ($this->uses_row_plugin() && empty($this->row_plugin)) { + vpr('views_plugin_style_default: Missing row plugin'); + return; + } + + $output = ''; + $rows['header'] = $this->render_header(); + $rows['body'] = $this->render_body(); + $rows['footer'] = $this->render_footer(); + $title = ''; + $output .= theme($this->theme_functions(), array('view' => $this->view, 'options' => $this->options, 'rows' => $rows, 'title' => $title)); + return $output; + } + + function render_header() { + $rows = array(); + $title = ''; + $output = ''; + $output .= theme($this->theme_functions($this->definition['additional themes base'] . '_header'), array('view' => $this->view, 'options' => $this->options, 'rows' => $rows, 'title' => $title)); + return $output; + } + + function render_footer() { + $rows = array(); + $title = ''; + $output = ''; + $output .= theme($this->theme_functions($this->definition['additional themes base'] . '_footer'), array('view' => $this->view, 'options' => $this->options, 'rows' => $rows, 'title' => $title)); + + return $output; + } + + function render_body() { + if ($this->uses_row_plugin() && empty($this->row_plugin)) { + vpr('views_plugin_style_default: Missing row plugin'); + return; + } + + // Group the rows according to the grouping field, if specified. + $sets = $this->render_grouping($this->view->result, $this->options['grouping']); + + // Render each group separately and concatenate. Plugins may override this + // method if they wish some other way of handling grouping. + $output = ''; + foreach ($sets as $title => $records) { + if ($this->uses_row_plugin()) { + $rows = array(); + foreach ($records as $row_index => $row) { + $this->view->row_index = $row_index; + $rows[] = $this->row_plugin->render($row); + } + } + else { + $rows = $records; + } + + $title = ''; + $output .= theme($this->theme_functions($this->definition['additional themes base'] . '_body'), array('view' => $this->view, 'options' => $this->options, 'rows' => $rows, 'title' => $title)); + } + unset($this->view->row_index); + return $output; + + } + + /** + * Provide a full list of possible theme templates used by this style. + */ + function theme_functions($hook = NULL) { + if (is_null($hook)) { + $hook = $this->definition['theme']; + } + return views_theme_functions($hook, $this->view, $this->display); + } + + /** + * Add any HTTP headers that this style plugin wants to. + */ + function add_http_headers() { + + drupal_add_http_header('Cache-Control', 'max-age=60, must-revalidate'); + + if (!empty($this->definition['export headers'])) { + foreach ($this->definition['export headers'] as $name => $value) { + drupal_add_http_header($name, $value); + } + } + + if (isset($this->options['filename']) && !empty($this->options['provide_file'])) { + $filename = $this->generate_filename(); + + if ($filename) { + drupal_add_http_header('Content-Disposition', 'attachment; filename="'. $filename .'"'); + } + } + } + + /** + * Generate the filename for the export. + */ + function generate_filename() { + $view = $this->view; + $filename = ''; + + if (isset($this->options['filename']) && !empty($this->options['provide_file'])) { + // General tokens. + $tokens = array( + '%view' => check_plain($view->name), + '%display' => check_plain($view->current_display), + ); + // Argument tokens. + $count = 0; + foreach ($view->display_handler->get_handlers('argument') as $arg => $handler) { + $token = '%' . ++$count; + $tokens[$token . '-title'] = $handler->get_title(); + $tokens[$token . '-value'] = isset($view->args[$count - 1]) ? check_plain($view->args[$count - 1]) : ''; + } + + // Effective exposed filters token. + $exposed = array(); + foreach ($view->display_handler->get_handlers('filter') as $arg => $handler) { + if (!$handler->options['exposed']) { + continue; + } + if (!empty($view->exposed_input[$handler->options['expose']['identifier']])) { + $identifier = $handler->options['expose']['identifier']; + $option = $view->exposed_input[$identifier]; + // The option may be a string or an array, depending on whether the + // widget is a text box/area or a select box. + if (is_array($option)) { + $option = implode('--', $option); + } + $exposed[] = check_plain($identifier) . '_' . check_plain($option); + } + } + if (!empty($exposed)) { + $tokens['%exposed'] = implode('-', $exposed); + } + else { + $tokens['%exposed'] = 'default' ; + } + + // Timestamp token. + $time = REQUEST_TIME; + $parts = array( + 'full' => 'Y-m-d\TH-i-s', + 'yy' => 'y', + 'yyyy' => 'Y', + 'mm' => 'm', + 'mmm' => 'M', + 'dd' => 'd', + 'ddd' => 'D', + 'hh' => 'H', + 'ii' => 'i', + 'ss' => 's', + ); + foreach ($parts as $part => $format) { + $tokens['%timestamp-' . $part] = format_date($time, 'custom', $format); + } + + $filename = strtr($this->options['filename'], $tokens); + } + + return $filename; + } +} diff --git a/www7/sites/all/modules/contrib/views_data_export/plugins/views_data_export_plugin_style_export_csv.inc b/www7/sites/all/modules/contrib/views_data_export/plugins/views_data_export_plugin_style_export_csv.inc new file mode 100644 index 000000000..daebeafd1 --- /dev/null +++ b/www7/sites/all/modules/contrib/views_data_export/plugins/views_data_export_plugin_style_export_csv.inc @@ -0,0 +1,120 @@ + ',', + 'translatable' => TRUE, + ); + $options['quote'] = array( + 'default' => TRUE, + 'translatable' => TRUE, + ); + $options['trim'] = array( + 'default' => FALSE, + 'translatable' => FALSE, + ); + $options['replace_newlines'] = array( + 'default' => FALSE, + 'translatable' => FALSE, + ); + $options['newline_replacement'] = array( + 'default' => ', ', + 'translatable' => FALSE, + ); + $options['header'] = array( + 'default' => TRUE, + 'translatable' => FALSE, + ); + $options['keep_html'] = array( + 'default' => FALSE, + 'translatable' => FALSE, + ); + $options['encoding'] = array( + 'default' => '', + 'translatable' => FALSE, + ); + + return $options; + } + + /** + * Options form mini callback. + * + * @param $form + * Form array to add additional fields to. + * @param $form_state + * State of the form. + * @return + * None. + */ + function options_form(&$form, &$form_state) { + parent::options_form($form, $form_state); + + $form['separator'] = array( + '#type' => 'textfield', + '#title' => t('Separator'), + '#default_value' => !empty($this->options['separator']) ? $this->options['separator'] : ',', + '#description' => t('This is the separator that is used to separate fields. CSV implies comma separated fields so this should not be changed unless you have specific requirements'), + ); + $form['quote'] = array( + '#type' => 'checkbox', + '#default_value' => !empty($this->options['quote']), + '#title' => t('Quote values. Useful for output that might contain your separator as part of one of the values.'), + ); + $form['trim'] = array( + '#type' => 'checkbox', + '#default_value' => !empty($this->options['trim']), + '#title' => t('Trim whitespace from rendered fields. Can be useful for some themes where output results in extra newlines.'), + ); + $form['replace_newlines'] = array( + '#type' => 'checkbox', + '#default_value' => !empty($this->options['replace_newlines']), + '#title' => t('Replace newlines in rendered fields.'), + ); + $form['newline_replacement'] = array( + '#prefix' => '
', + '#suffix' => '
', + '#type' => 'textfield', + '#title' => t('Replacement'), + '#default_value' => $this->options['newline_replacement'], + '#process' => array('ctools_dependent_process'), + '#dependency' => array('edit-style-options-replace-newlines' => array(TRUE)), + ); + $form['header'] = array( + '#type' => 'checkbox', + '#title' => t('Make first row a list of column headers.'), + '#default_value' => !empty($this->options['header']), + ); + $form['keep_html'] = array( + '#type' => 'checkbox', + '#default_value' => !empty($this->options['keep_html']), + '#title' => t('Keep HTML tags.'), + ); + $form['encoding'] = array( + '#type' => 'textfield', + '#default_value' => !empty($this->options['encoding']) ? $this->options['encoding'] : '', + '#title' => t('Character encoding conversion'), + '#description' => t('Optionally specify a character conversion that some CSV file readers need. Use "utf8_decode" for ISO-8859-1 via utf8_decode PHP function, other values will be passed iconv (this requires the iconv PHP extension), empty or illegal values will leave the output as UTF-8.', + array('@iconv' => 'http://www.php.net/manual/en/function.iconv.php', '@utf8_decode' => 'http://www.php.net/manual/en/function.utf8-decode.php')), + ); + } +} diff --git a/www7/sites/all/modules/contrib/views_data_export/plugins/views_data_export_plugin_style_export_xml.inc b/www7/sites/all/modules/contrib/views_data_export/plugins/views_data_export_plugin_style_export_xml.inc new file mode 100644 index 000000000..f01f65c44 --- /dev/null +++ b/www7/sites/all/modules/contrib/views_data_export/plugins/views_data_export_plugin_style_export_xml.inc @@ -0,0 +1,169 @@ +base_table, 's'); + + if (empty($options['root_node'])) { + $options['root_node'] = $base_object_name . 's'; + } + if (empty($options['item_node'])) { + $options['item_node'] = $base_object_name; + } + } + + parent::init($view, $display, $options); + } + + /** + * Set options fields and default values. + * + * @return + * An array of options information. + */ + function option_definition() { + $options = parent::option_definition(); + + $options['transform'] = array( + 'default' => TRUE, + 'translatable' => FALSE, + ); + $options['transform_type'] = array( + 'default' => 'dash', + 'translatable' => FALSE, + ); + $options['root_node'] = array( + 'default' => '', + 'translatable' => FALSE, + ); + $options['item_node'] = array( + 'default' => '', + 'translatable' => FALSE, + ); + $options['no_entity_encode'] = array( + 'default' => array(), + 'translatable' => FALSE, + ); + $options['cdata_wrapper'] = array( + 'default' => array(), + 'translatable' => FALSE, + ); + + return $options; + } + + /** + * Options form mini callback. + * + * @param $form + * Form array to add additional fields to. + * @param $form_state + * State of the form. + * @return + * None. + */ + function options_form(&$form, &$form_state) { + parent::options_form($form, $form_state); + + $form['transform'] = array( + '#type' => 'checkbox', + '#title' => t('Transform spaces'), + '#default_value' => $this->options['transform'], + '#description' => t('Transform spaces to valid XML in field labels (spaces create invalid XML markup). Note that invalid XML tag characters will always be converted.'), + ); + $form['transform_type'] = array( + '#type' => 'select', + '#title' => t('Transform type'), + '#default_value' => $this->options['transform_type'], + '#options' => array( + 'dash' => t('Dash'), + 'underline' => t('Underline'), + 'camel' => t('camelCase'), + 'pascal' => t('PascalCase'), + ), + '#process' => array('ctools_dependent_process'), + '#dependency' => array( + 'edit-style-options-transform' => array(TRUE), + ), + ); + $form['root_node'] = array( + '#type' => 'textfield', + '#title' => t('Root node'), + '#default_value' => $this->options['root_node'], + '#description' => t('The XML tag for the root node.'), + ); + $form['item_node'] = array( + '#type' => 'textfield', + '#title' => t('Item node'), + '#default_value' => $this->options['item_node'], + '#description' => t('The XML tag for an item node.'), + ); + + $field_labels = $this->display->handler->get_field_labels(); + + if (!empty($field_labels)) { + $options = $field_labels; + if (empty($this->options['no_entity_encode'])) { + $this->options['no_entity_encode'] = array(); + } + + $form['no_entity_encode'] = array( + '#type' => 'checkboxes', + '#title' => t('Disable encoding of XML entities for these fields'), + '#options' => $options, + '#default_value' => $this->options['no_entity_encode'], + '#description' => t('If checked field contents will be outputted '. + 'without encoding of XML entities. This is '. + 'useful when when used in conjunction with a field '. + 'formatter that outputs properly formatted and '. + 'encoded XML data.'), + ); + + if (empty($this->options['cdata_wrapper'])) { + $this->options['cdata_wrapper'] = array(); + } + + $form['cdata_wrapper'] = array( + '#type' => 'checkboxes', + '#title' => t('Fields value to wrapped using CDATA'), + '#options' => $options, + '#default_value' => $this->options['cdata_wrapper'], + '#description' => t('If checked the fields content will be wrapped using the CDATA tag.'), + ); + } + } + + /** + * Perform any necessary changes to the form values prior to storage. + * There is no need for this function to actually store the data. + * + * @param $form + * @param $form_state + */ + function options_submit(&$form, &$form_state) { + if (isset($form_state['values']['style_options']['no_entity_encode'])) { + // Remove any options values set to 0 + $form_state['values']['style_options']['no_entity_encode'] = array_filter($form_state['values']['style_options']['no_entity_encode']); + } + if (isset($form_state['values']['style_options']['cdata_wrapper'])) { + // Remove any options values set to 0 + $form_state['values']['style_options']['cdata_wrapper'] = array_filter($form_state['values']['style_options']['cdata_wrapper']); + } + } +} diff --git a/www7/sites/all/modules/contrib/views_data_export/tests/base.test b/www7/sites/all/modules/contrib/views_data_export/tests/base.test new file mode 100644 index 000000000..455f5d166 --- /dev/null +++ b/www7/sites/all/modules/contrib/views_data_export/tests/base.test @@ -0,0 +1,508 @@ +schemaDefinition()); + variable_set('views_test_views_data', $this->viewsData()); + variable_set('views_test_views_plugins', $this->viewsPlugins()); + + module_enable(array('views_test')); + $this->resetAll(); + + // Load the test dataset. + $data_set = $this->dataSet(); + $query = db_insert('views_test') + ->fields(array_keys($data_set[0])); + foreach ($data_set as $record) { + $query->values($record); + } + $query->execute(); + $this->checkPermissions(array(), TRUE); + } + + /** + * This function allows to enable views ui from a higher class which can't change the setup function anymore. + * + * @TODO + * Convert existing setUp functions. + */ + function enableViewsUi() { + module_enable(array('views_ui')); + // @TODO Figure out why it's required to clear the cache here. + views_module_include('views_default', TRUE); + views_get_all_views(TRUE); + menu_rebuild(); + } + + /** + * The schema definition. + */ + protected function schemaDefinition() { + $schema['views_test'] = array( + 'description' => 'Basic test table for Views tests.', + 'fields' => array( + 'id' => array( + 'type' => 'serial', + 'unsigned' => TRUE, + 'not null' => TRUE, + ), + 'name' => array( + 'description' => "A person's name", + 'type' => 'varchar', + 'length' => 255, + 'not null' => TRUE, + 'default' => '', + ), + 'age' => array( + 'description' => "The person's age", + 'type' => 'int', + 'unsigned' => TRUE, + 'not null' => TRUE, + 'default' => 0), + 'job' => array( + 'description' => "The person's job", + 'type' => 'varchar', + 'length' => 255, + 'not null' => TRUE, + 'default' => 'Undefined', + ), + 'created' => array( + 'description' => "The creation date of this record", + 'type' => 'int', + 'unsigned' => TRUE, + 'not null' => TRUE, + 'default' => 0, + ), + ), + 'primary key' => array('id'), + 'unique keys' => array( + 'name' => array('name') + ), + 'indexes' => array( + 'ages' => array('age'), + ), + ); + return $schema; + } + + /** + * The views data definition. + */ + protected function viewsData() { + // Declaration of the base table. + $data['views_test']['table'] = array( + 'group' => t('Views test'), + 'base' => array( + 'field' => 'id', + 'title' => t('Views test'), + 'help' => t('Users who have created accounts on your site.'), + ), + ); + + // Declaration of fields. + $data['views_test']['id'] = array( + 'title' => t('ID'), + 'help' => t('The test data ID'), + 'field' => array( + 'handler' => 'views_handler_field_numeric', + 'click sortable' => TRUE, + ), + 'argument' => array( + 'handler' => 'views_handler_argument_numeric', + ), + 'filter' => array( + 'handler' => 'views_handler_filter_numeric', + ), + 'sort' => array( + 'handler' => 'views_handler_sort', + ), + ); + $data['views_test']['name'] = array( + 'title' => t('Name'), + 'help' => t('The name of the person'), + 'field' => array( + 'handler' => 'views_handler_field', + 'click sortable' => TRUE, + ), + 'argument' => array( + 'handler' => 'views_handler_argument_string', + ), + 'filter' => array( + 'handler' => 'views_handler_filter_string', + ), + 'sort' => array( + 'handler' => 'views_handler_sort', + ), + ); + $data['views_test']['age'] = array( + 'title' => t('Age'), + 'help' => t('The age of the person'), + 'field' => array( + 'handler' => 'views_handler_field_numeric', + 'click sortable' => TRUE, + ), + 'argument' => array( + 'handler' => 'views_handler_argument_numeric', + ), + 'filter' => array( + 'handler' => 'views_handler_filter_numeric', + ), + 'sort' => array( + 'handler' => 'views_handler_sort', + ), + ); + $data['views_test']['job'] = array( + 'title' => t('Job'), + 'help' => t('The job of the person'), + 'field' => array( + 'handler' => 'views_handler_field', + 'click sortable' => TRUE, + ), + 'argument' => array( + 'handler' => 'views_handler_argument_string', + ), + 'filter' => array( + 'handler' => 'views_handler_filter_string', + ), + 'sort' => array( + 'handler' => 'views_handler_sort', + ), + ); + $data['views_test']['created'] = array( + 'title' => t('Created'), + 'help' => t('The creation date of this record'), + 'field' => array( + 'handler' => 'views_handler_field_date', + 'click sortable' => TRUE, + ), + 'argument' => array( + 'handler' => 'views_handler_argument_date', + ), + 'filter' => array( + 'handler' => 'views_handler_filter_date', + ), + 'sort' => array( + 'handler' => 'views_handler_sort_date', + ), + ); + return $data; + } + + protected function viewsPlugins() { + return array(); + } + + /** + * A very simple test dataset. + */ + protected function dataSet() { + return array( + array( + 'name' => 'John', + 'age' => 25, + 'job' => 'Singer', + 'created' => gmmktime(0, 0, 0, 1, 1, 2000), + ), + array( + 'name' => 'George', + 'age' => 27, + 'job' => 'Singer', + 'created' => gmmktime(0, 0, 0, 1, 2, 2000), + ), + array( + 'name' => 'Ringo', + 'age' => 28, + 'job' => 'Drummer', + 'created' => gmmktime(6, 30, 30, 1, 1, 2000), + ), + array( + 'name' => 'Paul', + 'age' => 26, + 'job' => 'Songwriter', + 'created' => gmmktime(6, 0, 0, 1, 1, 2000), + ), + array( + 'name' => 'Meredith', + 'age' => 30, + 'job' => 'Speaker', + 'created' => gmmktime(6, 30, 10, 1, 1, 2000), + ), + ); + } + + /** + * Build and return a basic view of the views_test table. + * + * @return view + */ + protected function getBasicView() { + views_include('view'); + + // Create the basic view. + $view = new view(); + $view->vid = 'test_view'; + $view->add_display('default'); + $view->base_table = 'views_test'; + + // Set up the fields we need. + $display = $view->new_display('default', 'Master', 'default'); + $display->override_option('fields', array( + 'id' => array( + 'id' => 'id', + 'table' => 'views_test', + 'field' => 'id', + 'relationship' => 'none', + ), + 'name' => array( + 'id' => 'name', + 'table' => 'views_test', + 'field' => 'name', + 'relationship' => 'none', + ), + 'age' => array( + 'id' => 'age', + 'table' => 'views_test', + 'field' => 'age', + 'relationship' => 'none', + ), + )); + + // Set up the sort order. + $display->override_option('sorts', array( + 'id' => array( + 'order' => 'ASC', + 'id' => 'id', + 'table' => 'views_test', + 'field' => 'id', + 'relationship' => 'none', + ), + )); + + // Set up the pager. + $display->override_option('pager', array( + 'type' => 'none', + 'options' => array('offset' => 0), + )); + + return $view; + } + + public function logViewResult($result, $prefix = 'View result:
') { + $this->verbose($prefix . '
' . check_plain($result) .'
'); + } + + public function assertBatchedExportEqual($path, $expected, $message) { + $this->drupalGet($path); + $output = $this->drupalGetContent(); + $this->logViewResult($output); + $this->logViewResult($expected, 'Expected result:
'); + $this->assertEqual($this->normaliseString($output), $this->normaliseString($expected), $message); + } + + public function assertExportEqual($a, $b, $message) { + $this->logViewResult($a); + $this->logViewResult($b, 'Expected result:
'); + $this->assertEqual($this->normaliseString($a), $this->normaliseString($b), $message); + } + + protected function normaliseString($s) { + // Normalize line endings + // Convert all line-endings to UNIX format + $s = str_replace("\r\n", "\n", $s); + $s = str_replace("\r", "\n", $s); + $s = trim($s); + return $s; + } + +} + +abstract class ViewsDataExportSimpleExportTest extends ViewsDataExportBaseTest { + + protected $vde_export_type; + + /** + * Tests the non-batched export functionality for this style. + */ + public function testNonBatchedExport() { + $path = 'vde_test/' . $this->randomName(); + list($view, $expected) = $this->getExportView($path); + // Save this view so we can hit the path. + $view->save(); + // Ensure that the menu router system is rebuilt on the next page load. + variable_set('menu_rebuild_needed', TRUE); + + $this->drupalGet($path); + $result = $this->drupalGetContent(); + + $this->assertExportEqual($result, $expected, 'Non batched ' . $this->vde_export_type . ' export matched expected output.'); + } + + /** + * Tests the batched export functionality for this style. + */ + public function testBatchedExport() { + $path = 'vde_test/' . $this->randomName(); + list($view, $expected) = $this->getExportView($path); + $display = &$view->display['vde_test']->handler; + // Set this view to be batched. + $display->override_option('use_batch', 'batch'); + // Save this view so we can hit the path. + $view->save(); + // Ensure that the menu router system is rebuilt on the next page load. + variable_set('menu_rebuild_needed', TRUE); + + $this->assertBatchedExportEqual($path, $expected, 'Batched ' . $this->vde_export_type . ' export matched expected output.'); + } + + /** + * Get a very basic view and expected output for this style. + * + * @return + * An array containing two elements: + * - A View object, for the export. + * - The expected out from that view, if is was executed without further + * changes. + */ + abstract protected function getExportView($path = 'vde_test'); + + /** + * Build and return a basic view of the views_test table. + * + * @return view + */ + protected function getBasicExportView() { + views_include('view'); + + // Create the basic view. + $view = new view(); + $view->vid = 'new'; + $view->base_table = 'views_test'; + + // Set up the fields we need. + $display = $view->new_display('default', 'Master', 'default'); + + $display->override_option('fields', array( + 'id' => array( + 'id' => 'id', + 'table' => 'views_test', + 'field' => 'id', + 'relationship' => 'none', + ), + 'name' => array( + 'id' => 'name', + 'table' => 'views_test', + 'field' => 'name', + 'relationship' => 'none', + ), + 'age' => array( + 'id' => 'age', + 'table' => 'views_test', + 'field' => 'age', + 'relationship' => 'none', + ), + )); + + // Set up the sort order. + $display->override_option('sorts', array( + 'id' => array( + 'order' => 'ASC', + 'id' => 'id', + 'table' => 'views_test', + 'field' => 'id', + 'relationship' => 'none', + ), + )); + + // Set up the pager. + $display->override_option('pager', array( + 'type' => 'none', + 'options' => array('offset' => 0), + )); + + return $view; + } + + /** + * Get a view that's our basic view, but with hide if empty/0 support. + * + * We add this to the 'age' field. + */ + protected function getHideIfEmptyExportView() { + $view = $this->getBasicExportView(); + + $display = $view->display['default']->handler; + + $display->override_option('fields', array( + 'id' => array( + 'id' => 'id', + 'table' => 'views_test', + 'field' => 'id', + 'relationship' => 'none', + ), + 'name' => array( + 'id' => 'name', + 'table' => 'views_test', + 'field' => 'name', + 'relationship' => 'none', + // Hide their name if its empty. + 'hide_empty' => TRUE, + // But we don't hide it if it's: 0. + 'empty_zero' => FALSE, + ), + 'age' => array( + 'id' => 'age', + 'table' => 'views_test', + 'field' => 'age', + 'relationship' => 'none', + // Hide their age if it's empty. + 'hide_empty' => TRUE, + 'empty_zero' => TRUE, + ), + )); + + return $view; + } + + /** + * Execute a given view very simply. + * + * This will take a view, and add a display plugin of the correct export type, + * and then run it and compare it with the expected output. + */ + protected function executeAndCompareGivenView(view $view, $expected, $message = '', $style_options = array()) { + $path = 'vde_test/' . $this->randomName(); + + $display = $view->new_display('views_data_export', 'Data export', 'vde_test'); + $display->override_option('style_plugin', $this->getStylePluginName()); + $display->override_option('style_options', $style_options); + $display->override_option('path', $path); + + // Save this view so we can hit the path. + $view->save(); + + // Ensure that the menu router system is rebuilt on the next page load. + variable_set('menu_rebuild_needed', TRUE); + + $this->drupalGet($path); + $result = $this->drupalGetContent(); + + $this->assertExportEqual($result, $expected, $message); + } + + /** + * Return the name of the style plugin represented by this test. + */ + abstract protected function getStylePluginName(); +} diff --git a/www7/sites/all/modules/contrib/views_data_export/tests/csv_export.test b/www7/sites/all/modules/contrib/views_data_export/tests/csv_export.test new file mode 100644 index 000000000..9852b7783 --- /dev/null +++ b/www7/sites/all/modules/contrib/views_data_export/tests/csv_export.test @@ -0,0 +1,211 @@ + 'CSV Export', + 'description' => 'Various tests for exporting to CSV.', + 'group' => 'Views Data Export', + ); + } + + protected $vde_export_type = 'CSV'; + + protected function getStylePluginName() { + return 'views_data_export_csv'; + } + + protected function getExportView($path = 'vde_test') { + // Create the basic view. + $view = $this->getBasicExportView(); + + $display = $view->new_display('views_data_export', 'Data export', 'vde_test'); + $display->override_option('style_plugin', $this->getStylePluginName()); + $display->override_option('path', $path); + + $expected = '"ID","Name","Age" +"1","John","25" +"2","George","27" +"3","Ringo","28" +"4","Paul","26" +"5","Meredith","30"'; + + return array(&$view, $expected); + } + + /** + * Test to ensure that HTML tags are kept in CSV files when requested. + */ + protected function testKeepHTML() { + $view = $this->getBasicExportView(); + + $display = $view->display['default']->handler; + + $display->override_option('fields', array( + 'id' => array( + 'id' => 'id', + 'table' => 'views_test', + 'field' => 'id', + 'relationship' => 'none', + // Add a label to include HTML + 'label' => 'ID', + ), + 'name' => array( + 'id' => 'name', + 'table' => 'views_test', + 'field' => 'name', + 'relationship' => 'none', + // Alter this field to include HTML. + 'alter' => array( + 'alter_text' => TRUE, + 'text' => '[name]', + ), + ), + 'age' => array( + 'id' => 'age', + 'table' => 'views_test', + 'field' => 'age', + 'relationship' => 'none', + ), + )); + + $style_options = array( + 'keep_html' => TRUE, + ); + + $expected = '"ID","Name","Age" +"1","John","25" +"2","George","27" +"3","Ringo","28" +"4","Paul","26" +"5","Meredith","30"'; + + $message = 'Keep HTML test in ' . $this->vde_export_type . ' export matched expected output.'; + + $this->executeAndCompareGivenView($view, $expected, $message, $style_options); + + + // And now make sure that HTML tags are stripped correctly. + $style_options = array( + 'keep_html' => FALSE, + ); + + $expected = '"ID","Name","Age" +"1","John","25" +"2","George","27" +"3","Ringo","28" +"4","Paul","26" +"5","Meredith","30"'; + + $message = 'Keep HTML reverse test in ' . $this->vde_export_type . ' export matched expected output.'; + + $this->executeAndCompareGivenView($view, $expected, $message, $style_options); + } + + /** + * Test to ensure that HTML tags are kept in CSV files when requested. + */ + protected function testTrimFields() { + $view = $this->getBasicExportView(); + + $display = $view->display['default']->handler; + + $display->override_option('fields', array( + 'id' => array( + 'id' => 'id', + 'table' => 'views_test', + 'field' => 'id', + 'relationship' => 'none', + 'label' => 'ID', + ), + 'name' => array( + 'id' => 'name', + 'table' => 'views_test', + 'field' => 'name', + 'relationship' => 'none', + // Alter this field to include HTML. + 'alter' => array( + 'alter_text' => TRUE, + 'text' => ' [name] ', + ), + ), + 'age' => array( + 'id' => 'age', + 'table' => 'views_test', + 'field' => 'age', + 'relationship' => 'none', + ), + )); + + $style_options = array( + 'trim' => FALSE, + ); + + $expected = '"ID","Name","Age" +"1"," John ","25" +"2"," George ","27" +"3"," Ringo ","28" +"4"," Paul ","26" +"5"," Meredith ","30"'; + + $message = 'Trim reverse test in ' . $this->vde_export_type . ' export matched expected output.'; + + $this->executeAndCompareGivenView($view, $expected, $message, $style_options); + + // And now make sure that trimming works as expected. + $style_options = array( + 'trim' => TRUE, + ); + + $expected = '"ID","Name","Age" +"1","John","25" +"2","George","27" +"3","Ringo","28" +"4","Paul","26" +"5","Meredith","30"'; + + $message = 'Trim test in ' . $this->vde_export_type . ' export matched expected output.'; + + $this->executeAndCompareGivenView($view, $expected, $message, $style_options); + } + + /** + * Test to ensure that Encoding options work. + */ + protected function testIconvEncoding() { + $view = $this->getBasicExportView(); + + db_update('views_test') + ->fields(array('name' => 'Jo€hn')) + ->condition('name', 'John') + ->execute(); + + $encodings = array( + 'ISO-8859-1//TRANSLIT' => 'EUR', + 'utf8_decode' => '?', + ); + + foreach ($encodings as $encoding => $conversion) { + + $style_options = array( + 'encoding' => $encoding, + ); + + $expected = '"ID","Name","Age" +"1","Jo' . $conversion . 'hn","25" +"2","George","27" +"3","Ringo","28" +"4","Paul","26" +"5","Meredith","30"'; + + $message = 'Character encoding ' . $encoding . ' worked correctly.'; + + $this->executeAndCompareGivenView($view, $expected, $message, $style_options); + } + + } + +} diff --git a/www7/sites/all/modules/contrib/views_data_export/tests/doc_export.test b/www7/sites/all/modules/contrib/views_data_export/tests/doc_export.test new file mode 100644 index 000000000..deed57dbd --- /dev/null +++ b/www7/sites/all/modules/contrib/views_data_export/tests/doc_export.test @@ -0,0 +1,48 @@ + 'DOC Export', + 'description' => 'Various tests for exporting to DOC.', + 'group' => 'Views Data Export', + ); + } + + protected $vde_export_type = 'DOC'; + + protected function getStylePluginName() { + return 'views_data_export_doc'; + } + + protected function getExportView($path = 'vde_test') { + // Create the basic view. + $view = $this->getBasicExportView(); + + $display = $view->new_display('views_data_export', 'Data export', 'vde_test'); + $display->override_option('style_plugin', $this->getStylePluginName()); + $display->override_option('path', $path); + + $expected = ' + + + + + + + + + + + +
IDNameAge
1John25
2George27
3Ringo28
4Paul26
5Meredith30
+ +'; + + return array(&$view, $expected); + } + +} diff --git a/www7/sites/all/modules/contrib/views_data_export/tests/txt_export.test b/www7/sites/all/modules/contrib/views_data_export/tests/txt_export.test new file mode 100644 index 000000000..e7484e972 --- /dev/null +++ b/www7/sites/all/modules/contrib/views_data_export/tests/txt_export.test @@ -0,0 +1,164 @@ + 'TXT Export', + 'description' => 'Various tests for exporting to TXT.', + 'group' => 'Views Data Export', + ); + } + + protected $vde_export_type = 'TXT'; + + protected function getStylePluginName() { + return 'views_data_export_txt'; + } + + protected function getExportView($path = 'vde_test') { + // Create the basic view. + $view = $this->getBasicExportView(); + + $display = $view->new_display('views_data_export', 'Data export', 'vde_test'); + $display->override_option('style_plugin', $this->getStylePluginName()); + $display->override_option('path', $path); + + $expected = '[ID] + +1 +[Name] + +John +[Age] + +25 +---------------------------------------- + +[ID] + +2 +[Name] + +George +[Age] + +27 +---------------------------------------- + +[ID] + +3 +[Name] + +Ringo +[Age] + +28 +---------------------------------------- + +[ID] + +4 +[Name] + +Paul +[Age] + +26 +---------------------------------------- + +[ID] + +5 +[Name] + +Meredith +[Age] + +30 +----------------------------------------'; + + return array(&$view, $expected); + } + + /** + * Test to check if empty fields are correctly hidden. + */ + protected function testHideEmptySupport() { + $view = $this->getHideIfEmptyExportView(); + + // We need to ensure that the test fields are actually empty/0. + db_update('views_test') + ->fields(array('age' => 0,)) + ->condition('name', 'Paul') + ->execute(); + + db_update('views_test') + ->fields(array('name' => '',)) + ->condition('name', 'George') + ->execute(); + + db_update('views_test') + ->fields(array('name' => 0,)) + ->condition('name', 'John') + ->execute(); + + $expected = '[ID] + +1 +[Name] + +0 +[Age] + +25 +---------------------------------------- + +[ID] + +2 +[Age] + +27 +---------------------------------------- + +[ID] + +3 +[Name] + +Ringo +[Age] + +28 +---------------------------------------- + +[ID] + +4 +[Name] + +Paul +---------------------------------------- + +[ID] + +5 +[Name] + +Meredith +[Age] + +30 +----------------------------------------'; + + $message = 'Hide if empty support for ' . $this->vde_export_type . ' export matched expected output.'; + + $this->executeAndCompareGivenView($view, $expected, $message); + + } + +} diff --git a/www7/sites/all/modules/contrib/views_data_export/tests/xls_export.test b/www7/sites/all/modules/contrib/views_data_export/tests/xls_export.test new file mode 100644 index 000000000..ae47de764 --- /dev/null +++ b/www7/sites/all/modules/contrib/views_data_export/tests/xls_export.test @@ -0,0 +1,48 @@ + 'XLS Export', + 'description' => 'Various tests for exporting to XLS.', + 'group' => 'Views Data Export', + ); + } + + protected $vde_export_type = 'XLS'; + + protected function getStylePluginName() { + return 'views_data_export_xls'; + } + + protected function getExportView($path = 'vde_test') { + // Create the basic view. + $view = $this->getBasicExportView(); + + $display = $view->new_display('views_data_export', 'Data export', 'vde_test'); + $display->override_option('style_plugin', $this->getStylePluginName()); + $display->override_option('path', $path); + + $expected = ' + + + + + + + + + + + +
IDNameAge
1John25
2George27
3Ringo28
4Paul26
5Meredith30
+ +'; + + return array(&$view, $expected); + } + +} diff --git a/www7/sites/all/modules/contrib/views_data_export/tests/xml_export.test b/www7/sites/all/modules/contrib/views_data_export/tests/xml_export.test new file mode 100644 index 000000000..6e52d5063 --- /dev/null +++ b/www7/sites/all/modules/contrib/views_data_export/tests/xml_export.test @@ -0,0 +1,386 @@ + 'XML Export', + 'description' => 'Various tests for exporting to XML.', + 'group' => 'Views Data Export', + ); + } + + protected $vde_export_type = 'XML'; + + protected function getStylePluginName() { + return 'views_data_export_xml'; + } + + protected function getExportView($path = 'vde_test') { + // Create the basic view. + $view = $this->getBasicExportView(); + + $display = $view->new_display('views_data_export', 'Data export', 'vde_test'); + $display->override_option('style_plugin', 'views_data_export_xml'); + $display->override_option('path', $path); + + $expected = ' + + + 1 + John + 25 + + + 2 + George + 27 + + + 3 + Ringo + 28 + + + 4 + Paul + 26 + + + 5 + Meredith + 30 + +'; + + return array(&$view, $expected); + } + + /** + * Test to check if empty fields are correctly hidden. + */ + protected function testHideEmptySupport() { + $view = $this->getHideIfEmptyExportView(); + + // We need to ensure that the test fields are actually empty/0. + db_update('views_test') + ->fields(array('age' => 0,)) + ->condition('name', 'Paul') + ->execute(); + + db_update('views_test') + ->fields(array('name' => '',)) + ->condition('name', 'George') + ->execute(); + + db_update('views_test') + ->fields(array('name' => 0,)) + ->condition('name', 'John') + ->execute(); + + $expected = ' + + + 1 + 0 + 25 + + + 2 + 27 + + + 3 + Ringo + 28 + + + 4 + Paul + + + 5 + Meredith + 30 + +'; + + $message = 'Hide if empty support for ' . $this->vde_export_type . ' export matched expected output.'; + + $this->executeAndCompareGivenView($view, $expected, $message); + + } + + /** + * Test to ensure that valid XML is produced when someone doesn't specify a label. + */ + protected function testEmptyLabels() { + $view = $this->getBasicExportView(); + + $display = $view->display['default']->handler; + + $display->override_option('fields', array( + 'id' => array( + 'id' => 'id', + 'table' => 'views_test', + 'field' => 'id', + 'relationship' => 'none', + ), + 'name' => array( + 'id' => 'name', + 'table' => 'views_test', + 'field' => 'name', + 'relationship' => 'none', + // Remove the label from the name field. + 'label' => '', + ), + 'age' => array( + 'id' => 'age', + 'table' => 'views_test', + 'field' => 'age', + 'relationship' => 'none', + // Make this label intentially invalid XML. + 'label' => '.', + ), + )); + + $expected = ' + + + 1 + John + 25 + + + 2 + George + 27 + + + 3 + Ringo + 28 + + + 4 + Paul + 26 + + + 5 + Meredith + 30 + +'; + + $message = 'Empty label test in ' . $this->vde_export_type . ' export matched expected output.'; + + $this->executeAndCompareGivenView($view, $expected, $message); + } + + /** + * Test to ensure that XML nodes names can be manually specified. + */ + protected function testCustomiseXMLNodes() { + $view = $this->getBasicExportView(); + + // Load the include that contains the _views_data_export_xml_tag_clean function. + module_load_include('inc', 'views_data_export', 'theme/views_data_export.theme'); + + + $root_node = _views_data_export_xml_tag_clean($this->randomName()); + $item_node = _views_data_export_xml_tag_clean($this->randomName()); + + $style_options = array( + 'root_node' => $root_node, + 'item_node' => $item_node, + ); + + $expected = ' +<' . $root_node . '> + <' . $item_node . '> + 1 + John + 25 + + <' . $item_node . '> + 2 + George + 27 + + <' . $item_node . '> + 3 + Ringo + 28 + + <' . $item_node . '> + 4 + Paul + 26 + + <' . $item_node . '> + 5 + Meredith + 30 + +'; + + $message = 'Custom XML nodes test in ' . $this->vde_export_type . ' export matched expected output.'; + + $this->executeAndCompareGivenView($view, $expected, $message, $style_options); + } + + /** + * Test to ensure certain fields can be optionally non-escaped. + */ + protected function testXMLNoEntityEncode() { + $view = $this->getBasicExportView(); + + $display = $view->display['default']->handler; + + $style_options = array( + 'no_entity_encode' => array( + 'id' => 'id', + ), + ); + + $display->override_option('fields', array( + 'id' => array( + 'id' => 'id', + 'table' => 'views_test', + 'field' => 'id', + 'relationship' => 'none', + 'alter' => array( + 'alter_text' => TRUE, + 'text' => '[id]', + ), + ), + 'name' => array( + 'id' => 'name', + 'table' => 'views_test', + 'field' => 'name', + 'relationship' => 'none', + 'alter' => array( + 'alter_text' => TRUE, + 'text' => '[name]', + ), + ), + 'age' => array( + 'id' => 'age', + 'table' => 'views_test', + 'field' => 'age', + 'relationship' => 'none', + ), + )); + + $expected = ' + + + 1 + <em>John</em> + 25 + + + 2 + <em>George</em> + 27 + + + 3 + <em>Ringo</em> + 28 + + + 4 + <em>Paul</em> + 26 + + + 5 + <em>Meredith</em> + 30 + +'; + + $message = 'XML in values test in ' . $this->vde_export_type . ' export matched expected output.'; + + $this->executeAndCompareGivenView($view, $expected, $message, $style_options); + } + + /** + * Test to ensure certain fields can be optionally non-escaped. + */ + protected function testXMLCDATAWrapper() { + $view = $this->getBasicExportView(); + + $style_options = array( + 'cdata_wrapper' => array( + 'id' => 'id', + 'name' => 'name', + ), + ); + + $display = $view->display['default']->handler; + + $display->override_option('fields', array( + 'id' => array( + 'id' => 'id', + 'table' => 'views_test', + 'field' => 'id', + 'relationship' => 'none', + ), + 'name' => array( + 'id' => 'name', + 'table' => 'views_test', + 'field' => 'name', + 'relationship' => 'none', + 'alter' => array( + 'alter_text' => TRUE, + 'text' => '[name]', + ), + ), + 'age' => array( + 'id' => 'age', + 'table' => 'views_test', + 'field' => 'age', + 'relationship' => 'none', + ), + )); + + $expected = ' + + + + John]]> + 25 + + + + George]]> + 27 + + + + Ringo]]> + 28 + + + + Paul]]> + 26 + + + + Meredith]]> + 30 + +'; + + $message = 'XML in values test in ' . $this->vde_export_type . ' export matched expected output.'; + + $this->executeAndCompareGivenView($view, $expected, $message, $style_options); + } + +} diff --git a/www7/sites/all/modules/contrib/views_data_export/theme/views-data-export-csv-body.tpl.php b/www7/sites/all/modules/contrib/views_data_export/theme/views-data-export-csv-body.tpl.php new file mode 100644 index 000000000..edd20d03a --- /dev/null +++ b/www7/sites/all/modules/contrib/views_data_export/theme/views-data-export-csv-body.tpl.php @@ -0,0 +1,6 @@ + $item_row): + print implode($separator, $item_row) . "\r\n"; +endforeach; \ No newline at end of file diff --git a/www7/sites/all/modules/contrib/views_data_export/theme/views-data-export-csv-footer.tpl.php b/www7/sites/all/modules/contrib/views_data_export/theme/views-data-export-csv-footer.tpl.php new file mode 100644 index 000000000..8be952ed8 --- /dev/null +++ b/www7/sites/all/modules/contrib/views_data_export/theme/views-data-export-csv-footer.tpl.php @@ -0,0 +1,5 @@ + \ No newline at end of file diff --git a/www7/sites/all/modules/contrib/views_data_export/theme/views-data-export-doc-footer.tpl.php b/www7/sites/all/modules/contrib/views_data_export/theme/views-data-export-doc-footer.tpl.php new file mode 100644 index 000000000..d75c23d29 --- /dev/null +++ b/www7/sites/all/modules/contrib/views_data_export/theme/views-data-export-doc-footer.tpl.php @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/www7/sites/all/modules/contrib/views_data_export/theme/views-data-export-doc-header.tpl.php b/www7/sites/all/modules/contrib/views_data_export/theme/views-data-export-doc-header.tpl.php new file mode 100644 index 000000000..86b824ce9 --- /dev/null +++ b/www7/sites/all/modules/contrib/views_data_export/theme/views-data-export-doc-header.tpl.php @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/www7/sites/all/modules/contrib/views_data_export/theme/views-data-export-txt-body.tpl.php b/www7/sites/all/modules/contrib/views_data_export/theme/views-data-export-txt-body.tpl.php new file mode 100644 index 000000000..d9f1ba3d9 --- /dev/null +++ b/www7/sites/all/modules/contrib/views_data_export/theme/views-data-export-txt-body.tpl.php @@ -0,0 +1,24 @@ + $row): + foreach ($row as $field => $content): +?> +[] + + + + +---------------------------------------- + + \ No newline at end of file diff --git a/www7/sites/all/modules/contrib/views_data_export/theme/views-data-export-xls-footer.tpl.php b/www7/sites/all/modules/contrib/views_data_export/theme/views-data-export-xls-footer.tpl.php new file mode 100644 index 000000000..d75c23d29 --- /dev/null +++ b/www7/sites/all/modules/contrib/views_data_export/theme/views-data-export-xls-footer.tpl.php @@ -0,0 +1,4 @@ + +
+ + \ No newline at end of file diff --git a/www7/sites/all/modules/contrib/views_data_export/theme/views-data-export-xls-header.tpl.php b/www7/sites/all/modules/contrib/views_data_export/theme/views-data-export-xls-header.tpl.php new file mode 100644 index 000000000..86b824ce9 --- /dev/null +++ b/www7/sites/all/modules/contrib/views_data_export/theme/views-data-export-xls-header.tpl.php @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/www7/sites/all/modules/contrib/views_data_export/theme/views-data-export-xml-body.tpl.php b/www7/sites/all/modules/contrib/views_data_export/theme/views-data-export-xml-body.tpl.php new file mode 100644 index 000000000..b97e8adb3 --- /dev/null +++ b/www7/sites/all/modules/contrib/views_data_export/theme/views-data-export-xml-body.tpl.php @@ -0,0 +1,20 @@ + + $row): ?> + <> + $content): ?> + <>> + + > + diff --git a/www7/sites/all/modules/contrib/views_data_export/theme/views-data-export-xml-footer.tpl.php b/www7/sites/all/modules/contrib/views_data_export/theme/views-data-export-xml-footer.tpl.php new file mode 100644 index 000000000..f845bbcea --- /dev/null +++ b/www7/sites/all/modules/contrib/views_data_export/theme/views-data-export-xml-footer.tpl.php @@ -0,0 +1 @@ +> diff --git a/www7/sites/all/modules/contrib/views_data_export/theme/views-data-export-xml-header.tpl.php b/www7/sites/all/modules/contrib/views_data_export/theme/views-data-export-xml-header.tpl.php new file mode 100644 index 000000000..808e8098e --- /dev/null +++ b/www7/sites/all/modules/contrib/views_data_export/theme/views-data-export-xml-header.tpl.php @@ -0,0 +1,18 @@ +'; +?> + +<> diff --git a/www7/sites/all/modules/contrib/views_data_export/theme/views-data-export.tpl.php b/www7/sites/all/modules/contrib/views_data_export/theme/views-data-export.tpl.php new file mode 100644 index 000000000..c5504880d --- /dev/null +++ b/www7/sites/all/modules/contrib/views_data_export/theme/views-data-export.tpl.php @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/www7/sites/all/modules/contrib/views_data_export/theme/views_data_export.theme.inc b/www7/sites/all/modules/contrib/views_data_export/theme/views_data_export.theme.inc new file mode 100644 index 000000000..8416d96e4 --- /dev/null +++ b/www7/sites/all/modules/contrib/views_data_export/theme/views_data_export.theme.inc @@ -0,0 +1,517 @@ +'; + $output .= $var['message']; + $output .= ''; + return $output; +} + +/** + * Theme a feed link. + * + * This theme function uses the theme pattern system to allow it to be + * overidden in a more specific manner. The options for overiding this include + * providing per display id; per type; per display id and per type. + * + * e.g. + * For the view "export_test" with the display "page_1" and the type "csv" you + * would have the following options. + * views_data_export_feed_icon__export_test__page_1__csv + * views_data_export_feed_icon__export_test__page_1 + * views_data_export_feed_icon__export_test__csv + * views_data_export_feed_icon__page_1__csv + * views_data_export_feed_icon__page_1 + * views_data_export_feed_icon__csv + * views_data_export_feed_icon + * + * @ingroup themeable + */ +function theme_views_data_export_feed_icon($variables) { + extract($variables, EXTR_SKIP); + $url_options = array('html' => TRUE); + if ($query) { + $url_options['query'] = $query; + } + $image = theme('image', array('path' => $image_path, 'alt' => $text, 'title' => $text)); + return l($image, $url, $url_options); +} + +/** + * Theme callback for the export complete page. + * + * @param $file + * Link to output file + */ +function theme_views_data_export_complete_page($variables) { + extract($variables, EXTR_SKIP); + drupal_set_title(t('Data export successful')); + drupal_add_html_head(array('#tag' => 'meta', '#attributes' => array('http-equiv' =>"Refresh", 'content' => '3;url='. $file)), 'views_data_export_download'); + $output = ''; + $output .= '

'; + $output .= t('Your export has been created. View/download the file here (will automatically download in 3 seconds.)', array('@link' => $file)); + $output .= '

'; + + if (!empty($return_url)) { + $output .= '

'; + $output .= l(t('Return to previous page'), $return_url); + $output .= '

'; + } + return $output; +} + + +function template_preprocess_views_data_export(&$vars) { + $vars['header'] = $vars['rows']['header']; + $vars['body'] = $vars['rows']['body']; + $vars['footer'] = $vars['rows']['footer']; + + $view = $vars['view']; + $fields = &$view->field; +} + +function template_preprocess_views_data_export_csv_header(&$vars) { + _views_data_export_header_shared_preprocess($vars); + + // Make sure we catch saved options that are misspelled. LEGACY + if (isset($vars['options']['seperator'])) { + $vars['options']['separator'] = $vars['options']['seperator']; + } + // Support old misspelled templates. LEGACY + $vars['seperator'] = + $vars['separator'] = $vars['options']['separator']; + + // Special handling when quoted values are involved. + if ($vars['options']['quote']) { + $wrap = '"'; + $replace_value = '""'; + } + else { + $wrap = ''; + $replace_value = ''; + } + + // Format header values. + foreach ($vars['header'] as $key => $value) { + $output = decode_entities($value); + $output = (empty($vars['options']['keep_html'])) ? strip_tags($output) : $output; + if (!empty($vars['options']['trim'])) { + $output = trim($output); + } + if (!empty($vars['options']['encoding']) && function_exists('iconv')) { + switch($vars['options']['encoding']) { + case 'utf8_decode': + $converted = utf8_decode($output); + break; + default: + $converted = iconv("UTF-8", $vars['options']['encoding'], $output); + break; + } + if ($converted !== FALSE) { + $output = $converted; + } + } + $vars['header'][$key] = $wrap . str_replace('"', $replace_value, $output) . $wrap; + } +} + +function template_preprocess_views_data_export_csv_body(&$vars) { + _views_data_export_body_shared_preprocess($vars); + + // Make sure we catch saved options that are misspelled. LEGACY + if (isset($vars['options']['seperator'])) { + $vars['options']['separator'] = $vars['options']['seperator']; + } + // Support old misspelled templates. LEGACY + $vars['seperator'] = + $vars['separator'] = $vars['options']['separator']; + + // Special handling when quoted values are involved. + if ($vars['options']['quote']) { + $wrap = '"'; + $replace_value = '""'; + } + else { + $wrap = ''; + $replace_value = ''; + } + + // Format row values. + foreach ($vars['themed_rows'] as $i => $values) { + foreach ($values as $j => $value) { + $output = decode_entities($value); + $output = (empty($vars['options']['keep_html'])) ? strip_tags($output) : $output; + if (!empty($vars['options']['trim'])) { + $output = trim($output); + } + + if (!empty($vars['options']['encoding']) && function_exists('iconv')) { + switch($vars['options']['encoding']) { + case 'utf8_decode': + $converted = utf8_decode($output); + break; + default: + $converted = iconv("UTF-8", $vars['options']['encoding'], $output); + break; + } + if ($converted !== FALSE) { + $output = $converted; + } + } + if (!empty($vars['options']['replace_newlines'])) { + $output = str_replace("\n", $vars['options']['newline_replacement'], $output); + } + $vars['themed_rows'][$i][$j] = $wrap . str_replace('"', $replace_value, $output) . $wrap; + } + } +} + +/** + * Preprocess csv output template. + */ +function template_preprocess_views_data_export_csv(&$vars) { + // TODO Replace items with themed_rows. + _views_data_export_shared_preprocess($vars); + + // Make sure we catch saved options that are misspelled. LEGACY + if (isset($vars['options']['separator'])) { + $vars['options']['separator'] = $vars['options']['seperator']; + } + // Support old misspelled templates. LEGACY + $vars['seperator'] = + $vars['separator'] = $vars['options']['separator']; + + // Special handling when quoted values are involved. + if ($vars['options']['quote']) { + $wrap = '"'; + $replace_value = '""'; + } + else { + $wrap = ''; + $replace_value = ''; + } + + // Format header values. + foreach ($vars['header'] as $key => $value) { + $output = decode_entities(strip_tags($value)); + if ($vars['options']['trim']) { + $output = trim($output); + } + if (!empty($vars['options']['encoding']) && function_exists('iconv')) { + switch($vars['options']['encoding']) { + case 'ASCII': + $converted = iconv("UTF-8", "ASCII//TRANSLIT", $output); + if ($converted !== FALSE) { + $output = $converted; + } + break; + } + } + $vars['header'][$key] = $wrap . str_replace('"', $replace_value, $output) . $wrap; + } + + // Format row values. + foreach ($vars['themed_rows'] as $i => $values) { + foreach ($values as $j => $value) { + $output = decode_entities(strip_tags($value)); + if ($vars['options']['trim']) { + $output = trim($output); + } + if (!empty($vars['options']['encoding']) && function_exists('iconv')) { + switch($vars['options']['encoding']) { + case 'ASCII': + $converted = iconv("UTF-8", "ASCII//TRANSLIT", $output); + if ($converted !== FALSE) { + $output = $converted; + } + break; + } + } + $vars['themed_rows'][$i][$j] = $wrap . str_replace('"', $replace_value, $output) . $wrap; + } + } +} + +/** + * Preprocess txt output template. + */ +function template_preprocess_views_data_export_txt_body(&$vars) { + _views_data_export_header_shared_preprocess($vars); + // We support not outputting fields when they are empty, so indicate so. + $vars['hide_empty_support'] = TRUE; + _views_data_export_body_shared_preprocess($vars); +} + +function template_preprocess_views_data_export_doc_body(&$vars) { + // Pass through the generic MS Office preprocess. + template_preprocess_views_data_export_msoffice_body($vars); +} + +function template_preprocess_views_data_export_xls_body(&$vars) { + // Pass through the generic MS Office preprocess. + template_preprocess_views_data_export_msoffice_body($vars); +} + +function template_preprocess_views_data_export_msoffice_body(&$vars) { + _views_data_export_header_shared_preprocess($vars); + _views_data_export_body_shared_preprocess($vars); + + $output = ''; + + // Construct the tbody of a table, see theme_table(). + + $ts = tablesort_init($vars['header']); + + $flip = array( + 'even' => 'odd', + 'odd' => 'even', + ); + $class = 'even'; + foreach ($vars['themed_rows'] as $number => $row) { + $attributes = array(); + + // Check if we're dealing with a simple or complex row + if (isset($row['data'])) { + foreach ($row as $key => $value) { + if ($key == 'data') { + $cells = $value; + } + else { + $attributes[$key] = $value; + } + } + } + else { + $cells = $row; + } + if (count($cells)) { + // Add odd/even class + $class = $flip[$class]; + if (isset($attributes['class'])) { + $attributes['class'] .= ' ' . $class; + } + else { + $attributes['class'] = $class; + } + + // Build row + $output .= ' '; + $i = 0; + foreach ($cells as $cell) { + $cell = tablesort_cell($cell, $vars['header'], $ts, $i++); + $output .= _theme_table_cell($cell); + } + $output .= " \n"; + } + } + + + $vars['tbody'] = preg_replace('/<\/?(a|span) ?.*?>/', '', $output); // strip 'a' and 'span' tags + +} + +function template_preprocess_views_data_export_doc_header(&$vars) { + // Pass through the generic MS Office preprocess. + template_preprocess_views_data_export_msoffice_header($vars); +} + +function template_preprocess_views_data_export_xls_header(&$vars) { + // Pass through the generic MS Office preprocess. + template_preprocess_views_data_export_msoffice_header($vars); +} + +function template_preprocess_views_data_export_msoffice_header(&$vars) { + _views_data_export_header_shared_preprocess($vars); + + // Need to do a little work to construct the table header, see theme_table(). + $vars['header_row'] = ''; + $vars['header_row'] .= ''; + + $ts = tablesort_init($vars['header']); + + foreach ($vars['header'] as $cell) { + $cell = tablesort_header($cell, $vars['header'], $ts); + $vars['header_row'] .= _theme_table_cell($cell, TRUE); + } + + $vars['header_row'] .= ''; + + $vars['header_row'] = preg_replace('/<\/?(a|span) ?.*?>/', '', $vars['header_row']); // strip 'a' and 'span' tags +} + +/** + * Preprocess xml output template. + */ +function template_preprocess_views_data_export_xml_header(&$vars) { + $vars['root_node'] = _views_data_export_xml_tag_clean($vars['options']['root_node']); +} + +/** + * Preprocess xml output template. + */ +function template_preprocess_views_data_export_xml_footer(&$vars) { + $vars['root_node'] = _views_data_export_xml_tag_clean($vars['options']['root_node']); +} + +/** + * Preprocess xml output template. + */ +function template_preprocess_views_data_export_xml_body(&$vars) { + _views_data_export_header_shared_preprocess($vars); + // We support not outputting fields when they are empty, so indicate so. + $vars['hide_empty_support'] = TRUE; + _views_data_export_body_shared_preprocess($vars); + + $view = $vars['view']; + $style_options = $view->display_handler->get_option('style_options'); + + $no_encode = isset($style_options['no_entity_encode']) ? $style_options['no_entity_encode'] : array(); + + $cdata_wrapper = isset($style_options['cdata_wrapper']) ? $style_options['cdata_wrapper'] : array(); + + $vars['item_node'] = _views_data_export_xml_tag_clean($vars['options']['item_node']); + + foreach ($vars['themed_rows'] as $num => $row) { + foreach ($row as $field => $content) { + + // Perform xml entity encoding unless excluded by style options. + if (empty($no_encode[$field]) && empty($cdata_wrapper[$field])) { + + // Prevent double encoding of the ampersand. Look for the entities produced by check_plain(). + $content = preg_replace('/&(?!(amp|quot|#039|lt|gt);)/', '&', $content); + // Convert < and > to HTML entities. + $content = str_replace( + array('<', '>'), + array('<', '>'), + $content); + } + + // Perform wrapping the field data using the CDATA tag + // unless excluded by style options. + if (!empty($cdata_wrapper[$field])) { + // Escape CDATA end sequence only. + $content = '', ']]]]>', $content) . ']]>'; + } + + $vars['themed_rows'][$num][$field] = $content; + } + } + + foreach ($vars['header'] as $field => $header) { + // If there is no field label, use 'no name'. + if (empty($header)) { + $header = 'no name'; + } + if ($vars['options']['transform']) { + switch ($vars['options']['transform_type']) { + case 'dash': + $vars['xml_tag'][$field] = str_replace(' ', '-', $header); + break; + case 'underline': + $vars['xml_tag'][$field] = str_replace(' ', '_', $header); + break; + case 'camel': + $vars['xml_tag'][$field] = str_replace(' ', '', ucwords(strtolower($header))); + // Convert the very first character of the string to lowercase. + $vars['xml_tag'][$field][0] = strtolower($vars['xml_tag'][$field][0]); + break; + case 'pascal': + $vars['xml_tag'][$field] = str_replace(' ', '', ucwords(strtolower($header))); + break; + } + } + // We should always try to output valid XML. + $vars['xml_tag'][$field] = _views_data_export_xml_tag_clean($vars['xml_tag'][$field]); + } +} + +/** + * Returns a valid XML tag formed from the given input. + * + * @param $tag The string that should be made into a valid XML tag. + * @return The valid XML tag or an empty string if the string contained no valid + * XML tag characters. + */ +function _views_data_export_xml_tag_clean($tag) { + + // This regex matches characters that are not valid in XML tags, and the + // unicode ones that are. We don't bother with unicode, because it would so + // the preg_replace down a lot. + static $invalid_tag_chars_regex = '#[^\:A-Za-z_\-.0-9]+#'; + + // These characters are not valid at the start of an XML tag: + static $invalid_start_chars = '-.0123456789'; + + // Convert invalid chars to '-': + $tag = preg_replace($invalid_tag_chars_regex, '-', $tag); + + // Need to trim invalid characters from the start of the string: + $tag = ltrim($tag, $invalid_start_chars); + + // As a last line of defense, if we've stripped out everything, set it to + // something. + if (empty($tag)) { + $tag = 'invalid-tag-name'; + } + + return $tag; +} + +/** + * Shared helper function for export preprocess functions. + */ +function _views_data_export_header_shared_preprocess(&$vars) { + $view = $vars['view']; + $fields = &$view->field; + + $vars['header'] = array(); + foreach ($fields as $key => $field) { + if (empty($field->options['exclude'])) { + $vars['header'][$key] = check_plain($field->label()); + } + } + +} + +/** + * Shared helper function for export preprocess functions. + */ +function _views_data_export_body_shared_preprocess(&$vars) { + $view = $vars['view']; + $fields = &$view->field; + $hide_empty_support = !empty($vars['hide_empty_support']); + + $rows = $vars['rows']; + + $vars['themed_rows'] = array(); + $keys = array_keys($fields); + foreach ($rows as $num => $row) { + $vars['themed_rows'][$num] = array(); + + foreach ($keys as $id) { + if (empty($fields[$id]->options['exclude'])) { + $content = $view->style_plugin->rendered_fields[$num][$id]; + + if ($hide_empty_support && !empty($fields[$id]->options['hide_empty'])) { + if ($fields[$id]->is_value_empty($content, $fields[$id]->options['empty_zero'])) { + continue; + } + } + + $vars['themed_rows'][$num][$id] = $content; + } + } + } +} diff --git a/www7/sites/all/modules/contrib/views_data_export/views_data_export.drush.inc b/www7/sites/all/modules/contrib/views_data_export/views_data_export.drush.inc new file mode 100644 index 000000000..ce99aa5dc --- /dev/null +++ b/www7/sites/all/modules/contrib/views_data_export/views_data_export.drush.inc @@ -0,0 +1,394 @@ + array ( + 'vde', + ), + 'description' => 'Fully executes a views_data_export display of a view and writes the output to file.', + 'arguments' => array ( + 'view_name' => 'The name of the view', + 'display_id' => 'The id of the views_data_export display to execute on the view', + 'output_file' => 'The file to write the results to - will be overwritten if it already exists', + ), + 'options' => array ( + '--arguments' => 'Comma separated list of arguments to be passed to the view.', + '--format' => 'csv,doc,txt,xls or xml. These options are ignored if the display_id passed is a "views_data_export" display.', + '--separator' => 'csv only: What character separates the fields (default:,)', + '--trim-whitespace' => 'csv only: Trim whitespace from either side of fields (default:1)', + '--header-row' => 'csv only: Make the first row a row of headers (default:1)', + '--quote-values' => 'csv only: Surround each field in quotes (default:1)', + ), + 'examples' => array ( + 'drush views-data-export myviewname views_data_export_1 output.csv' => 'Export myviewname:views_data_export_1 and write the output to output.csv in the current directory', + ), + 'drupal dependencies' => array ( + 'views_data_export', + ), + 'core' => array('7'), + ); + + return $items; +} + + +/** + * Implementation of hook_drush_help(). + * + * This function is called whenever a drush user calls + * 'drush help ' + * + * @param + * A string with the help section (prepend with 'drush:') + * + * @return + * A string with the help text for your command. + */ +function views_data_export_drush_help($section) { + switch ($section) { + case 'drush:views-data-export': + return dt("This command may be used to fully execute a views_data_export display of a view, batched if need be, and write the output to a file."); + } +} + +/** + * Implementation of drush_hook_COMMAND_validate(). + */ +function drush_views_data_export_validate() { + // Because of a bug in the way that Drush 4 computes the name of functions to + // call from a Drush command, we may end up getting called twice, so we just + // don't do anything on subsequent invocations. + static $already_run = FALSE; + if ($already_run) { + return; + } + $already_run = TRUE; + + $args = drush_get_arguments(); + array_shift($args); + + if (count($args) !== 3) { + return drush_set_error('ARGUMENTS_REQUIRED', dt('All arguments are required.')); + } + + if (!$view = views_get_view($args[0])) { + return drush_set_error('VIEW_DOES_NOT_EXIST', dt('The view !view does not exist.', array ('!view' => $args[0]))); + } + + if (!$view->set_display($args[1])) { + return drush_set_error('VIEW_DOES_NOT_EXIST', dt('The view !view does not have the !display display.', array ('!view' => $args[0], '!display' => $args[1]))); + } + else { + if ($view->current_display != $args[1]) { + drush_log(dt('Using different display from specified display: @display', array('@display' => $view->current_display)), 'notice'); + } + drush_set_option('views_data_export_display_id', $view->current_display); + } + + $format = drush_get_option('format'); + $valid_formats = array('csv', 'doc', 'txt', 'xls', 'xml'); + if (!empty($format) && !in_array($format, $valid_formats)) { + return drush_set_error('VIEWS_DATA_EXPORT_INVALID_OPTION', dt('The "--format" option is invalid, please supply one of the following: !formats', array('!formats' => implode(', ', $valid_formats)))); + } +} + +/** + * Drush command callback to export a views data to a file. + * + * @see drush_views_data_export_validate(). + * @see views_data_export_views_data_export_batch_alter(). + */ +function drush_views_data_export($view_name, $display_id, $output_file) { + // Because of a bug in the way that Drush 4 computes the name of functions to + // call from a Drush command, we may end up getting called twice, so we just + // don't do anything on subsequent invocations. + static $already_run = FALSE; + if ($already_run) { + return; + } + $already_run = TRUE; + + // Set the display to the one that we computed earlier. + $display_id = drush_get_option('views_data_export_display_id', 'default'); + + $view = views_get_view($view_name); + + // If the given display_id is not views_data_alter then + // we programatically clone it to a views_data_alter display + // and then execute that one instead + if ($view->display[$display_id]->display_plugin != 'views_data_export') { + //drush_log("Display '$display_id' is not views_data_export. Making one that is and executing that instead =).", 'success'); + + $format = drush_get_option('format'); + $settings = array(); + switch ($format) { + case 'doc': + case 'xls': + case 'xml': + case 'txt': + $settings['display_options']['style_plugin'] = 'views_data_export_' . $format; + break; + case 'csv': + default: + $settings['display_options']['style_plugin'] = 'views_data_export_csv'; + if ($separator = drush_get_option('separator')) { + $settings['display_options']['style_options']['separator'] = $separator; + } + if (!$trim = drush_get_option('trim-whitespace')) { + $settings['display_options']['style_options']['trim'] = 0; + } + if (!$header = drush_get_option('header-row')) { + $settings['display_options']['style_options']['header'] = 0; + } + if (!$quote = drush_get_option('quote-values')) { + $settings['display_options']['style_options']['quote'] = 0; + } + // Seperator + } + + $display_id = _drush_views_data_export_clone_display($view, $display_id, $settings); + } + + $view->set_display($display_id); + + // We execute the view normally, and take advantage + // of an alter function to interject later and batch it ourselves + + $options = array(); + // Let's see if the given $output_file is a absolute path. + if (strpos($output_file, '/') === 0) { + $options['output_file'] = $output_file; + } + else { + $options['output_file'] = realpath(drush_get_context('DRUSH_OLDCWD', getcwd())) . '/' . $output_file; + } + + + if ($view->display_handler->is_batched()) { + // This is a batched export, and needs to be handled as such. + _drush_views_data_export_override_batch($view_name, $display_id, $options); + + $arguments = drush_get_option('arguments', ''); + $arguments = explode(',', $arguments); + $view->execute_display($display_id, $arguments); + } + else { + // This export isn't batched. + ob_start(); + $view->execute_display($display_id); + // Get the results, and clean the output buffer. + $result = ob_get_contents(); + // Clean the buffer. + ob_end_clean(); + // Save the results to file. + // Copy file over + if (file_put_contents($options['output_file'], $result)) { + drush_log("Data export saved to " . $options['output_file'], 'success'); + } + else { + drush_set_error('VIEWS_DATA_EXPORT_COPY_ERROR', dt("The file could not be copied to the selected destination")); + } + } + +} + + +/** + * Helper function that indicates that we want to + * override the batch that the views_data_export view creates + * on it's initial time through. + * + * Also provides a place to stash options that need to stay around + * until the end of the batch + */ +function _drush_views_data_export_override_batch($view = NULL, $display = NULL, $options = TRUE) { + static $_views; + if (isset($view)) { + $_views[$view][$display] = $options; + } + return $_views; +} + +/** + * Implementation of hook_views_data_export_batch_alter() + */ +function views_data_export_views_data_export_batch_alter(&$batch, &$final_destination, &$querystring) { + + // Copy the batch, because we're going to monkey with it, a lot! + $new_batch = $batch; + + $view_name = $new_batch['view_name']; + $display_id = $new_batch['display_id']; + + $ok_to_override = _drush_views_data_export_override_batch(); + + // Make sure we do nothing if we are called not following the execution of + // our drush command. This could happen if the file with this function in it + // is included during the normal execution of the view + if (!$ok_to_override[$view_name][$display_id]) { + return; + } + + $options = $ok_to_override[$view_name][$display_id]; + + // We actually never return from the drupal_alter, but + // use drush's batch system to run the same batch + + // Add a final callback + $new_batch['operations'][] = array( + '_drush_views_data_export_batch_finished', array($batch['eid'], $options['output_file']), + ); + + batch_set($new_batch); + $new_batch =& batch_get(); + // Drush handles the different processes, so instruct BatchAPI not to. + $new_batch['progressive'] = FALSE; + // Process the batch using drush. + drush_backend_batch_process(); + + // Instruct the view display plugin that it shouldn't set a batch. + $batch = array(); +} + + +/** + * Get's called at the end of the drush batch process that generated our export + */ +function _drush_views_data_export_batch_finished($eid, $output_file, &$context) { + // Fetch export info + $export = views_data_export_get($eid); + + // Perform cleanup + $view = views_data_export_view_retrieve($eid); + $view->set_display($export->view_display_id); + $view->display_handler->batched_execution_state = $export; + $view->display_handler->remove_index(); + + // Get path to temp file + $temp_file = $view->display_handler->outputfile_path(); + + // Copy file over + if (@drush_op('copy', $temp_file, $output_file)) { + drush_log("Data export saved to " . $output_file, 'success'); + } + else { + drush_set_error('VIEWS_DATA_EXPORT_COPY_ERROR', dt("The file could not be copied to the selected destination")); + } + +} + + +/** + * Helper function that takes a view and returns a clone of it + * that has cloned a given display to one of type views_data_export + * + * @param &$view + * Modified to contain the new display + * + * @return + * The new display_id + */ +function _drush_views_data_export_clone_display(&$view, $display_id, $settings = array()) { + + // Create the new display + $new_display_id = _drush_views_data_export_generate_display_id($view, 'views_data_export'); + $view->display[$new_display_id] = clone $view->display[$display_id]; + + // Ensure we have settings we'll need for our display + $default_settings = array ( + 'id' => $new_display_id, + 'display_plugin' => 'views_data_export', + 'position' => 99, + 'display_options' => array ( + 'style_plugin' => 'views_data_export_csv', + 'style_options' => array( + 'attach_text' => 'CSV', + 'provide_file' => 1, + 'filename' => 'view-%view.csv', + 'parent_sort' => 1, + 'separator' => ',', + 'quote' => 1, + 'trim' => 1, + 'header' => 1, + ), + 'use_batch' => 'batch', + 'path' => '', + 'displays' => array ( + 'default' => 'default', + ), + ), + ); + $settings = array_replace_recursive($default_settings, $settings); + + $view->display[$new_display_id] = (object)array_replace_recursive((array)$view->display[$new_display_id], $settings); + + return $new_display_id; +} + + +/** + * Generate a display id of a certain plugin type. + * See http://drupal.org/files/issues/348975-clone-display.patch + * + * @param $type + * Which plugin should be used for the new display id. + */ +function _drush_views_data_export_generate_display_id($view, $type) { + // 'default' is singular and is unique, so just go with 'default' + // for it. For all others, start counting. + if ($type == 'default') { + return 'default'; + } + // Initial id. + $id = $type . '_1'; + $count = 1; + + // Loop through IDs based upon our style plugin name until + // we find one that is unused. + while (!empty($view->display[$id])) { + $id = $type . '_' . ++$count; + } + + return $id; +} + +/** + * If we're using PHP < 5.3.0 then we'll need + * to define this function ourselves. + * See: http://phpmyanmar.com/phpcodes/manual/function.array-replace-recursive.php + */ +if (!function_exists('array_replace_recursive')) { +function array_replace_recursive($array, $array1) { + // Get array arguments + $arrays = func_get_args(); + + // Define the original array + $original = array_shift($arrays); + + // Loop through arrays + foreach ($arrays as $array) { + // Loop through array key/value pairs + foreach ($array as $key => $value) { + // Value is an array + if (is_array($value)) { + // Traverse the array; replace or add result to original array + $original[$key] = array_replace_recursive($original[$key], $array[$key]); + } + + // Value is not an array + else { + // Replace or add current value to original array + $original[$key] = $value; + } + } + } + + // Return the joined array + return $original; +} +} diff --git a/www7/sites/all/modules/contrib/views_data_export/views_data_export.info b/www7/sites/all/modules/contrib/views_data_export/views_data_export.info new file mode 100644 index 000000000..478b4fb87 --- /dev/null +++ b/www7/sites/all/modules/contrib/views_data_export/views_data_export.info @@ -0,0 +1,28 @@ +name = Views Data Export +description = "Plugin to export views data into various file formats" + +package = Views +core = 7.x + +dependencies[] = views + +; Plugins +files[] = plugins/views_data_export_plugin_display_export.inc +files[] = plugins/views_data_export_plugin_style_export.inc +files[] = plugins/views_data_export_plugin_style_export_csv.inc +files[] = plugins/views_data_export_plugin_style_export_xml.inc + +; Tests +files[] = "tests/base.test" +files[] = "tests/csv_export.test" +files[] = "tests/doc_export.test" +files[] = "tests/txt_export.test" +files[] = "tests/xls_export.test" +files[] = "tests/xml_export.test" + +; Information added by Drupal.org packaging script on 2014-08-27 +version = "7.x-3.0-beta8" +core = "7.x" +project = "views_data_export" +datestamp = "1409118835" + diff --git a/www7/sites/all/modules/contrib/views_data_export/views_data_export.install b/www7/sites/all/modules/contrib/views_data_export/views_data_export.install new file mode 100644 index 000000000..9bd0dd107 --- /dev/null +++ b/www7/sites/all/modules/contrib/views_data_export/views_data_export.install @@ -0,0 +1,205 @@ + t('Keep track of currently executing exports.'), + 'fields' => array( + 'eid' => array( + 'description' => 'Unique id for each on-going export.', + 'type' => 'serial', + 'unsigned' => TRUE, + 'not null' => TRUE, + ), + 'view_name' => array( + 'type' => 'varchar', + 'length' => '128', + 'default' => '', + 'not null' => TRUE, + 'description' => 'The unique name of the view. This is the primary field views are loaded from, and is used so that views may be internal and not necessarily in the database. May only be alphanumeric characters plus underscores.', + ), + 'view_display_id' => array( + 'type' => 'varchar', + 'length' => '64', + 'default' => '', + 'not null' => TRUE, + 'description' => 'The unique name of the view. This is the primary field views are loaded from, and is used so that views may be internal and not necessarily in the database. May only be alphanumeric characters plus underscores.', + ), + 'time_stamp' => array( + 'type' => 'int', + 'unsigned' => TRUE, + 'not null' => TRUE, + 'default' => 0, + 'description' => 'The time this export started', + ), + 'fid' => array( + 'description' => 'Files ID.', + 'type' => 'int', + 'unsigned' => TRUE, + 'not null' => TRUE + ), + 'batch_state' => array( + 'type' => 'varchar', + 'length' => '32', + 'default' => 'init', + 'not null' => TRUE, + 'description' => 'The current state of the batch.', + ), + 'sandbox' => array( + 'type' => 'blob', + 'not null' => FALSE, + 'size' => 'big', + 'serialize' => TRUE, + ), + ), + 'primary key' => array('eid'), + ); + + $schema['views_data_export_object_cache'] = array( + 'description' => 'A modified version of the views_object_cache that ignores session id.', + 'fields' => array( + 'eid' => array( + 'type' => 'varchar', + 'length' => '64', + 'description' => 'The export ID this view equates too.', + ), + 'updated' => array( + 'type' => 'int', + 'unsigned' => TRUE, + 'not null' => TRUE, + 'default' => 0, + 'description' => 'The time this cache was created or updated.', + ), + 'data' => array( + 'type' => 'blob', + 'size' => 'big', + 'description' => 'Serialized data being stored.', + 'serialize' => TRUE, + ), + ), + 'indexes' => array( + 'eid' => array('eid'), + 'updated' => array('updated'), + ), + ); + return $schema; +} + +/** + * Implementation of hook_uninstall() + */ +function views_data_export_uninstall() { + // Clean up any tables we may have left around. + module_load_include('module', 'views_data_export'); + views_data_export_garbage_collect(0, -1); +} + +/** + * Convert the data column in the object cache. + */ +function views_data_export_update_7100() { + $new_field = array( + 'type' => 'text', + 'size' => 'big', + 'description' => 'Serialized data being stored.', + 'serialize' => TRUE, + ); + + // Drop and re-add this field because there is a bug in + // db_change_field that causes this to fail when trying to cast the data. + db_drop_field('views_data_export_object_cache', 'data'); + db_add_field('views_data_export_object_cache', 'data', $new_field); +} + +/** + * Increase the length of the view_name field to match views_schema_7301(). + */ +function views_data_export_update_7300() { + $fields = array(); + + $fields['view_name'] = array( + 'type' => 'varchar', + 'length' => '128', + 'default' => '', + 'not null' => TRUE, + 'description' => 'The unique name of the view. This is the primary field views are loaded from, and is used so that views may be internal and not necessarily in the database. May only be alphanumeric characters plus underscores.', + ); + + $fields['view_display_id'] = array( + 'type' => 'varchar', + 'length' => '64', + 'default' => '', + 'not null' => TRUE, + 'description' => 'An identifier for this display; usually generated from the display_plugin, so should be something like page or page_1 or block_2, etc.', + ); + + // Apply the updates to the DB. + foreach ($fields as $field_name => $spec) { + db_change_field('views_data_export', $field_name, $field_name, $spec); + } +} +/** + * Change the object cache table to support postgres installations + */ +function views_data_export_update_7301(){ + $spec = array( + 'type' => 'blob', + 'size' => 'big', + 'description' => 'Serialized data being stored.', + 'serialize' => TRUE, + ); + db_change_field('views_data_export_object_cache', 'data', 'data', $spec); +} + + +/** + * Implements hook_requirements(). + */ +function views_data_export_requirements($phase) { + $requirements = array(); + + // Ensure translations don't break at install time + $t = get_t(); + + switch ($phase) { + case 'runtime': + + $requirements['views_data_export_temp'] = array( + 'title' => t('Views Data Export temporary directory'), + 'severity' => REQUIREMENT_OK, + 'value' => t('Exists'), + ); + + $path = variable_get('views_data_export_directory', 'temporary://views_plugin_display'); + if (!file_prepare_directory($path, FILE_CREATE_DIRECTORY)) { + $requirements['views_data_export_temp']['description'] = t('The Views Data Export temporary directory, %path could not be created due to a misconfigured directory. Please ensure that the temporary directory is correctly configured and that the webserver has permission to create directories.', array('%path' => file_uri_target($path))); + $requirements['views_data_export_temp']['severity'] = REQUIREMENT_ERROR; + $requirements['views_data_export_temp']['value'] = t('Unable to create'); + } + + $db_type = Database::getConnection()->databaseType(); + switch ($db_type) { + case 'mysql': + // Check the max allowed packet size. + $max_allowed_packet = db_query('SHOW VARIABLES WHERE variable_name = :name', array(':name' => 'max_allowed_packet'))->fetchField(1); + if (is_numeric($max_allowed_packet)) { + if ($max_allowed_packet < (16 * 1024 * 1024)) { + $requirements['views_data_export'] = array( + 'title' => $t('MySQL - max allowed packet'), + 'value' => format_size($max_allowed_packet), + 'description' => $t("Your MySQL 'max_allowed_packet' setting may be too low for Views data export to function correctly, Drupal's requirements recommend setting it to at least 16M. See: !link", array('!link' => l('http://drupal.org/requirements', 'http://drupal.org/requirements'))), + 'severity' => REQUIREMENT_WARNING, + ); + } + } + break; + } + break; + } + + return $requirements; +} diff --git a/www7/sites/all/modules/contrib/views_data_export/views_data_export.module b/www7/sites/all/modules/contrib/views_data_export/views_data_export.module new file mode 100644 index 000000000..92d9bf2b7 --- /dev/null +++ b/www7/sites/all/modules/contrib/views_data_export/views_data_export.module @@ -0,0 +1,282 @@ + 2, + ); +} + +/** + * Implementation of hook_theme(). + */ +function views_data_export_theme() { + // Make sure that views picks up the preprocess functions. + module_load_include('inc', 'views_data_export', 'theme/views_data_export.theme'); + $hooks = array(); + $hooks['views_data_export_feed_icon'] = array( + 'pattern' => 'views_data_export_feed_icon__', + 'variables' => array( + 'image_path' => NULL, + 'url' => NULL, + 'query' => '', + 'text' => '', + ), + 'file' => 'theme/views_data_export.theme.inc', + ); + + $hooks['views_data_export_complete_page'] = array ( + 'variables' => array( + 'file' => '', + 'errors' => array(), + 'return_url'=> '', + ), + 'file' => 'theme/views_data_export.theme.inc', + ); + + $hooks['views_data_export_message'] = array ( + 'variables' => array( + 'message' => '', + 'type' => 'info', + ), + 'file' => 'theme/views_data_export.theme.inc', + ); + + return $hooks; +} + + +/** + * Implementation of hook_cron(). + */ +function views_data_export_cron() { + views_data_export_garbage_collect(); +} + +/** + * Removes any temporary index tables that have been left + * behind. This is caused by batch processes which are + * started but never finished. + * + * Removes all trace of exports from the database that + * were created more than $expires seconds ago + * + * @param $expires + * Seconds ago. Defaults to that given in the settings. + * @param $chunk + * The number of tables to test for and delete. + * Defaults to that given in the settings. Pass -1 + * for this setting to remove any restriction and to + * garbage collect all exports. + */ +function views_data_export_garbage_collect($expires = NULL, $chunk = NULL) { + if (lock_acquire('views_data_export_gc')) { + if (!isset($expires)) { + $expires = variable_get('views_data_export_gc_expires', 604800); // one week + } + if (!isset($chunk)) { + $chunk = variable_get('views_data_export_gc_chunk', 30); + } + + if ($chunk == -1) { + $result = db_query("SELECT eid FROM {views_data_export} WHERE time_stamp <= :timestamp ORDER BY time_stamp ASC", array(':timestamp' => REQUEST_TIME - $expires)); + } + else { + $result = db_query_range("SELECT eid FROM {views_data_export} WHERE time_stamp <= :timestamp ORDER BY time_stamp ASC", 0, $chunk, array(':timestamp' => REQUEST_TIME - $expires)); + } + + $eids_to_clear = array(); + foreach ($result as $row) { + $eids_to_clear[] = $row->eid; + } + + // We do two things to exports we want to garbage collect + // 1. Delete the index table for it, if it is still around + // 2. Delete the row from the exports table + // 3. Delete the view from the object_cache + if (count($eids_to_clear)) { + foreach ($eids_to_clear as $eid) { + // 1. Delete index table, if it is still around for some reason + $table = VIEWS_DATA_EXPORT_INDEX_TABLE_PREFIX . $eid; + if (db_table_exists($table)) { + db_drop_table($table); + } + } + + // 2. Delete the entries in the exports table. + db_delete('views_data_export') + ->condition('eid', $eids_to_clear, 'IN') + ->execute(); + + // 3. Clear the cached views + views_data_export_view_clear($eids_to_clear); + } + + lock_release('views_data_export_gc'); + } +} + + +/** + * Batch API callback. + * Handles all batching operations by executing the appropriate view. + */ +function _views_data_export_batch_process($export_id, $display_id, $exposed_input, &$context) { + // Don't show the admin menu on batch page, some people don't like it. + if (module_exists('admin_menu')) { + module_invoke('admin_menu', 'suppress'); + } + + // Fetch the view in question from our cache + $view = views_data_export_view_retrieve($export_id); + $view->set_display($display_id); + if (!empty($exposed_input)) { + $view->set_exposed_input($exposed_input); + } + // Inform the data_export display which export it corresponds to and execute + if (!isset($view->display_handler->batched_execution_state)) { + $view->display_handler->batched_execution_state = new stdClass(); + } + $view->display_handler->batched_execution_state->eid = $export_id; + $view->display_handler->views_data_export_cached_view_loaded = TRUE; + $view->execute_display($display_id); + + // Update batch api progress information + $sandbox = $view->display_handler->batched_execution_state->sandbox; + $context['finished'] = $sandbox['finished']; + $context['message'] = $sandbox['message']; + + views_data_export_view_store($export_id, $view); +} + + + +/**********/ +/** CRUD **/ +/**********/ + +/** + * Save a new export into the database. + */ +function views_data_export_new($view_name, $view_display_id, $file) { + // Insert new row into exports table + $record = (object) array( + 'view_name' => $view_name, + 'view_display_id' => $view_display_id, + 'time_stamp' => REQUEST_TIME, + 'fid' => $file, + 'batch_state' => VIEWS_DATA_EXPORT_HEADER, + 'sandbox' => array(), + ); + drupal_write_record('views_data_export', $record); + return $record; +} + + +/** + * Update an export row in the database + */ +function views_data_export_update($state) { + // Note, drupal_write_record handles serializing + // the sandbox field as per our schema definition + drupal_write_record('views_data_export', $state, 'eid'); +} + + + +/** + * Get the information about a previous export. + */ +function views_data_export_get($export_id) { + $object = db_query("SELECT * FROM {views_data_export} WHERE eid = :eid", array(':eid' => (int)$export_id))->fetch(); + if ($object) { + $object->sandbox = unserialize($object->sandbox); + } + return $object; +} + +/** + * Remove the information about an export. + */ +function views_data_export_clear($export_id) { + db_delete('views_data_export') + ->condition('eid', $export_id) + ->execute(); + views_data_export_view_clear($export_id); +} + + +/** + * Store a view in the object cache. + */ +function views_data_export_view_store($export_id, $view) { + // Store a clean copy of the view. + $_view = $view->clone_view(); + + views_data_export_view_clear($export_id); + $record = array( + 'eid' => $export_id, + 'data' => $_view, + 'updated' => REQUEST_TIME, + ); + drupal_write_record('views_data_export_object_cache', $record); +} + +/** + * Retrieve a view from the object cache. + */ +function views_data_export_view_retrieve($export_id) { + views_include('view'); + $data = db_query("SELECT * FROM {views_data_export_object_cache} WHERE eid = :eid", array(':eid' => $export_id))->fetch(); + if ($data) { + $view = unserialize($data->data); + } + return $view; +} + +/** + * Clear a view from the object cache. + * + * @param $export_id + * An export ID or an array of export IDs to clear from the object cache. + */ +function views_data_export_view_clear($export_id) { + db_delete('views_data_export_object_cache') + ->condition('eid', $export_id) + ->execute(); +} + +/** + * Implements hook_file_presave(). + */ +function views_data_export_file_presave($file) { + // Ensure temporary files really are temporary. + // @see: https://drupal.org/node/2198399 + if (strpos($file->filename, 'views_data_export') === 0) { + // There is no FILE_STATUS_TEMPORARY. + $file->status = 0; + } +} diff --git a/www7/sites/all/modules/contrib/views_data_export/views_data_export.views.inc b/www7/sites/all/modules/contrib/views_data_export/views_data_export.views.inc new file mode 100644 index 000000000..c433c79b6 --- /dev/null +++ b/www7/sites/all/modules/contrib/views_data_export/views_data_export.views.inc @@ -0,0 +1,136 @@ + $path . '/plugins', + 'parent' => 'views_data_export', + 'theme' => 'views_data_export', + 'theme path' => $path . '/theme', + 'theme file' => 'views_data_export.theme.inc', + 'uses row plugin' => FALSE, + 'uses fields' => TRUE, + 'uses options' => TRUE, + 'type' => 'data_export', + ); + + return array( + 'display' => array ( + 'views_data_export' => array( + 'title' => t('Data export'), + 'help' => t('Export the view results to a file. Can handle very large result sets.'), + 'path' => $path . '/plugins', + 'handler' => 'views_data_export_plugin_display_export', + 'parent' => 'feed', + 'uses hook menu' => TRUE, + 'use ajax' => FALSE, + 'use pager' => FALSE, + 'accept attachments' => FALSE, + 'admin' => t('Data export'), + 'help topic' => 'display-data-export', + ), + ), + 'style' => array( + 'views_data_export' => array( + // this isn't really a display but is necessary so the file can + // be included. + 'no ui' => TRUE, + 'handler' => 'views_data_export_plugin_style_export', + 'path' => $path . '/plugins', + 'theme path' => $path . '/theme', + 'theme file' => 'views_data_export.theme.inc', + 'type' => 'normal', + ), + 'views_data_export_csv' => array( + 'title' => t('CSV file'), + 'help' => t('Display the view as a comma separated list.'), + 'handler' => 'views_data_export_plugin_style_export_csv', + // Views Data Export element that will be used to set additional headers when serving the feed. + 'export headers' => array('Content-type' => 'text/csv; charset=utf-8'), + // Views Data Export element mostly used for creating some additional classes and template names. + 'export feed type' => 'csv', + 'export feed text' => 'CSV', + 'export feed file' => '%view.csv', + 'export feed icon' => drupal_get_path('module', 'views_data_export') . '/images/csv.png', + 'additional themes' => array( + 'views_data_export_csv_header' => 'style', + 'views_data_export_csv_body' => 'style', + 'views_data_export_csv_footer' => 'style', + ), + 'additional themes base' => 'views_data_export_csv', + ) + $style_defaults, + 'views_data_export_doc' => array( + 'title' => t('DOC file'), + 'help' => t('Display the view as a doc file.'), + 'handler' => 'views_data_export_plugin_style_export', + 'export headers' => array('Content-Type' => 'application/msword'), + 'export feed type' => 'doc', + 'export feed text' => 'Word Document', + 'export feed file' => '%view.doc', + 'export feed icon' => drupal_get_path('module', 'views_data_export') . '/images/doc.png', + 'additional themes' => array( + 'views_data_export_doc_header' => 'style', + 'views_data_export_doc_body' => 'style', + 'views_data_export_doc_footer' => 'style', + ), + 'additional themes base' => 'views_data_export_doc', + ) + $style_defaults, + 'views_data_export_txt' => array( + 'title' => t('TXT file'), + 'help' => t('Display the view as a txt file.'), + 'handler' => 'views_data_export_plugin_style_export', + 'export headers' => array('Content-Type' => 'text/plain'), + 'export feed type' => 'txt', + 'export feed text' => 'Plain Text Document', + 'export feed file' => '%view.txt', + 'export feed icon' => drupal_get_path('module', 'views_data_export') . '/images/txt.png', + 'additional themes' => array( + 'views_data_export_txt_header' => 'style', + 'views_data_export_txt_body' => 'style', + 'views_data_export_txt_footer' => 'style', + ), + 'additional themes base' => 'views_data_export_txt', + ) + $style_defaults, + 'views_data_export_xls' => array( + 'title' => t('XLS file'), + 'help' => t('Display the view as a xls file.'), + 'handler' => 'views_data_export_plugin_style_export', + 'export headers' => array('Content-Type' => 'application/vnd.ms-excel'), + 'export feed type' => 'xls', + 'export feed text' => 'XLS', + 'export feed file' => '%view.xls', + 'export feed icon' => drupal_get_path('module', 'views_data_export') . '/images/xls.png', + 'additional themes' => array( + 'views_data_export_xls_header' => 'style', + 'views_data_export_xls_body' => 'style', + 'views_data_export_xls_footer' => 'style', + ), + 'additional themes base' => 'views_data_export_xls', + ) + $style_defaults, + 'views_data_export_xml' => array( + 'title' => t('XML file'), + 'help' => t('Display the view as a txt file.'), + 'handler' => 'views_data_export_plugin_style_export_xml', + 'export headers' => array('Content-Type' => 'text/xml'), + 'export feed type' => 'xml', + 'export feed text' => 'XML', + 'export feed file' => '%view.xml', + 'export feed icon' => drupal_get_path('module', 'views_data_export') . '/images/xml.png', + 'additional themes' => array( + 'views_data_export_xml_header' => 'style', + 'views_data_export_xml_body' => 'style', + 'views_data_export_xml_footer' => 'style', + ), + 'additional themes base' => 'views_data_export_xml', + ) + $style_defaults, + ), + ); +} diff --git a/www7/sites/all/modules/custom/drupalfr/dfr_emploi/dfr_emploi.module b/www7/sites/all/modules/custom/drupalfr/dfr_emploi/dfr_emploi.module index a8ea8347a..c38aff9c4 100644 --- a/www7/sites/all/modules/custom/drupalfr/dfr_emploi/dfr_emploi.module +++ b/www7/sites/all/modules/custom/drupalfr/dfr_emploi/dfr_emploi.module @@ -99,7 +99,8 @@ function dfr_emploi_url_inbound_alter(&$path, $original_path, $path_language) { global $base_url; // On arrive sur emploi.* if (preg_match('|^https?:\/\/emploi\.(.+)|', $base_url)) { - // On veut que le path interne remette emploi en front, sauf s'il y est déjà + // On veut que le path interne remette emploi en front, sauf s'il y est + // déjà. if (0 !== strpos($original_path, 'emploi')) { if (!$original_path || $original_path == variable_get('site_frontpage', 'node')) { $path = 'emploi'; @@ -117,7 +118,8 @@ function dfr_emploi_url_inbound_alter(&$path, $original_path, $path_language) { 0 === strpos($alias, 'emploi')) ) { // Quand on est pas sur emploi.*, on regarde si notre path interne ou son - // alias commence par emploi, si oui, on tronque et on redirige sur emploi.* + // alias commence par emploi, si oui, on tronque et on redirige sur + // emploi.* if (!isset($alias)) { $alias = drupal_lookup_path('alias', $path, $path_language); } @@ -137,7 +139,7 @@ function dfr_emploi_url_inbound_alter(&$path, $original_path, $path_language) { */ function dfr_emploi_block_info() { $blocks['creer-offre'] = array( - 'info' => t('Lien création offre d\'emploi'), + 'info' => t("Lien création offre d'emploi"), 'cache' => DRUPAL_CACHE_GLOBAL, ); @@ -151,7 +153,7 @@ function dfr_emploi_block_view($delta = '') { $block = array(); $block['subject'] = ''; - $block['content'] = l('Déposer une offre', 'node/add/offre', array( + $block['content'] = l(t('Déposer une offre'), 'node/add/offre', array( 'attributes' => array('class' => 'btn-link'), )); if (user_is_anonymous()) { @@ -173,7 +175,7 @@ function dfr_emploi_cron() { ->fetchAllKeyed(); $nodes = node_load_multiple(array_keys($results)); foreach ($nodes as $node) { - $lifetime = (int)$node->field_temps_publication['und'][0]['value'] * 30 * 24 * 3600; + $lifetime = (int) $node->field_temps_publication['und'][0]['value'] * 30 * 24 * 3600; if (REQUEST_TIME > $node->changed + $lifetime) { $node->status = 0; node_save($node); @@ -229,3 +231,45 @@ function dfr_emploi_approve_job_offer(&$node, $context) { $node->field_flag_moderation[LANGUAGE_NONE][0]['value'] = 'approved'; $node->status = 1; } + +/** + * Implements hook_comment_view(). + * + * Removes comments and comment links on job offer for non-moderator. + */ +function dfr_emploi_comment_view($comment, $view_mode, $langcode) { + if ($comment->node_type == 'comment_node_offre' && !user_access('access job offer comments')) { + $comment->content['#access'] = FALSE; + } +} + +/** + * Implements hook_node_view_alter(). + * + * Hides comments and comment links on job offer for non-moderator. + */ +function dfr_emploi_node_view_alter(&$build) { + if ($build['#bundle'] == 'offre' && !user_access('access job offer comments')) { + // Hides comment links. + if (isset($build['links']) && isset($build['links']['comment'])) { + $build['links']['comment']['#access'] = FALSE; + } + + // Hides comments. + if (isset($build['comments'])) { + $build['comments']['#access'] = FALSE; + } + } +} + +/** + * Implements hook_permission(). + */ +function dfr_emploi_permission() { + return array( + 'access job offer comments' => array( + 'title' => t('Access job offer comments'), + 'description' => t('Display the comments on job offers for moderators.'), + ), + ); +} diff --git a/www7/sites/all/modules/custom/drupalfr/drupalfr_events/drupalfr_events.module b/www7/sites/all/modules/custom/drupalfr/drupalfr_events/drupalfr_events.module index 79350cf0a..7a11ba545 100644 --- a/www7/sites/all/modules/custom/drupalfr/drupalfr_events/drupalfr_events.module +++ b/www7/sites/all/modules/custom/drupalfr/drupalfr_events/drupalfr_events.module @@ -149,9 +149,7 @@ function drupalfr_events_upcoming_events() { // Keep only 5 meetups for the list. $list_events = array_slice($events, 0, 5); - $content['map'] = array( - '#markup' => leaflet_render_map($map, $features, '600px'), - ); + $content['map'] = leaflet_build_map($map, $features, '600px'); $content['events'] = drupalfr_events_display_events($list_events, 'full'); $content['infos'] = array( '#markup' => '

' . l(t('Voir tous les évènements'), 'http://www.meetup.com/drupal-france-francophonie') . '

', @@ -176,6 +174,11 @@ function drupalfr_events_upcoming_events() { function drupalfr_events_prepare_leaflet_features($events) { $features = array(); + // Prepare city name extraction. + $main_cities = drupalfr_events_get_main_cities(); + $main_cities_names = array_keys($main_cities); + $regex = '/(' . implode('|', $main_cities_names) . ')/i'; + foreach ($events as $event) { if (isset($event['venue'])) { $features[] = array( @@ -185,7 +188,68 @@ function drupalfr_events_prepare_leaflet_features($events) { 'popup' => l($event['name'], $event['event_url'], array('absolute' => TRUE)), ); } + else { + // Extract coordinates from city name. + preg_match($regex, $event['name'], $event_city); + + if (!empty($event_city)) { + $event_city = array_shift($event_city); + $event_city = drupal_strtolower($event_city); + + $features[] = array( + 'type' => 'point', + 'lat' => $main_cities[$event_city]['lat'], + 'lon' => $main_cities[$event_city]['lon'], + 'popup' => l($event['name'], $event['event_url'], array('absolute' => TRUE)) . ' : ' . t("Ce meetup n'a pas encore de lieu précisément défini."), + ); + } + } } return $features; } + +/** + * Helper function. + * + * Coordinates from http://www.coordonnees-gps.fr + * + * @return array + * An array containing the coordinates of the main french cities. + */ +function drupalfr_events_get_main_cities() { + return array( + 'bordeaux' => array( + 'lat' => '44.837789', + 'lon' => '-0.57918', + ), + 'lille' => array( + 'lat' => '50.62925', + 'lon' => '3.057256', + ), + 'lyon' => array( + 'lat' => '45.764043', + 'lon' => '4.835659', + ), + 'marseille' => array( + 'lat' => '43.296482', + 'lon' => '5.36978', + ), + 'montpellier' => array( + 'lat' => '43.610769', + 'lon' => '3.876716', + ), + 'nantes' => array( + 'lat' => '47.218371', + 'lon' => '-1.553621', + ), + 'paris' => array( + 'lat' => '48.856614', + 'lon' => '2.352222', + ), + 'toulouse' => array( + 'lat' => '43.604652', + 'lon' => '1.444209', + ), + ); +} diff --git a/www7/sites/all/themes/dfrtheme/template.php b/www7/sites/all/themes/dfrtheme/template.php index 101c90f6d..1acc24029 100644 --- a/www7/sites/all/themes/dfrtheme/template.php +++ b/www7/sites/all/themes/dfrtheme/template.php @@ -1,7 +1,7 @@ '
', '#weight' => 100, @@ -132,7 +138,8 @@ function dfrtheme_preprocess_page(&$variables) { * Implements template_preprocess_maintenance_page(). */ function dfrtheme_preprocess_maintenance_page(&$variables) { - // Manually include these as they're not available outside template_preprocess_page(). + // Manually include these as they're not available outside + // template_preprocess_page(). $variables['grddl_profile'] = 'http://www.w3.org/1999/xhtml/vocab'; $variables['doctype'] = _dfrtheme_doctype(); @@ -187,7 +194,7 @@ function dfrtheme_preprocess_block(&$variables) { $variables['title_attributes_array']['class'][] = 'block-title'; // In the header region visually hide block titles. - if (in_array($variables['block']->region, array('menu'))) { + if (in_array($variables['block']->region, array('menu'))) { $variables['title_attributes_array']['class'][] = 'element-invisible'; } } @@ -200,7 +207,7 @@ function dfrtheme_menu_tree($variables) { } /** - * Return a themed breadcrumb trail + * Return a themed breadcrumb trail. */ function dfrtheme_breadcrumb($variables) { $breadcrumb = isset($variables['breadcrumb']) ? $variables['breadcrumb'] : array(); @@ -211,7 +218,8 @@ function dfrtheme_breadcrumb($variables) { $breadcrumb[] = '' . $title . ''; } - return implode(' » ', $breadcrumb); // >>. + // >>. + return implode(' » ', $breadcrumb); } /** @@ -249,10 +257,12 @@ function dfrtheme_id_safe($string) { // Strip accents. $accents = '/&([A-Za-z]{1,2})(tilde|grave|acute|circ|cedil|uml|lig);/'; $string = preg_replace($accents, '$1', htmlentities(utf8_decode($string))); - // Replace with dashes anything that isn't A-Z, numbers, dashes, or underscores. + // Replace with dashes anything that isn't A-Z, numbers, dashes, or + // underscores. $string = strtolower(preg_replace('/[^a-zA-Z0-9_-]+/', '-', $string)); // If the first character is not a-z, add 'n' in front. - if (strlen($string) > 0 && !ctype_lower($string{0})) { // Don't use ctype_alpha since its locale aware. + // Don't use ctype_alpha since its locale aware. + if (strlen($string) > 0 && !ctype_lower($string{0})) { $string = 'id' . $string; } return $string; @@ -268,7 +278,7 @@ function _dfrtheme_doctype() { /** * Generate the HTML output for a menu link and submenu. * - * @param $variables + * @param array $variables * An associative array containing: * - element: Structured array data for a menu link. * @@ -389,14 +399,14 @@ function dfrtheme_preprocess_views_view(&$variables) { $options['query'] = array('destination' => 'node/add/offre'); } - $variables['empty'] = "

Il n'y a aucune offre pour le moment, pourquoi ne pas " . l('proposer la vôtre', $path, $options) . " ?

"; - $link_create_offer = l('Déposer une offre', 'node/add/offre', array('attributes' => array('class' => array('btn-link')))); - $link_see_offers = l('Voir toutes les offres', 'emploi/', array('attributes' => array('class' => array('btn-link')))); + $variables['empty'] = "

Il n'y a aucune offre pour le moment, pourquoi ne pas " . l(t('proposer la vôtre'), $path, $options) . " ?

"; + $link_create_offer = l(t('Déposer une offre'), 'node/add/offre', array('attributes' => array('class' => array('btn-link')))); + $link_see_offers = l(t('Voir toutes les offres'), 'emploi/', array('attributes' => array('class' => array('btn-link')))); // Update link path if you are logged in or not. if ($variables['view']->total_rows > 0) { if (user_is_anonymous()) { - $link_create_offer = l('Déposer une offre', 'user/login', array('query' => array('destination' => 'node/add/offre'), 'attributes' => array('class' => array('btn-link')))); + $link_create_offer = l(t('Déposer une offre'), 'user/login', array('query' => array('destination' => 'node/add/offre'), 'attributes' => array('class' => array('btn-link')))); } } $footer = $link_create_offer . ' ' . $link_see_offers; diff --git a/www7/sites/all/themes/dfrtheme/templates/node/node.tpl.php b/www7/sites/all/themes/dfrtheme/templates/node/node.tpl.php index ae7175c24..9e398b7a1 100644 --- a/www7/sites/all/themes/dfrtheme/templates/node/node.tpl.php +++ b/www7/sites/all/themes/dfrtheme/templates/node/node.tpl.php @@ -77,7 +77,7 @@ */ ?>
> - +
@@ -89,7 +89,7 @@ - +