diff --git a/sites/all/modules/contrib/date/date.field.inc b/sites/all/modules/contrib/date/date.field.inc index fa311224e..7aef97bee 100644 --- a/sites/all/modules/contrib/date/date.field.inc +++ b/sites/all/modules/contrib/date/date.field.inc @@ -28,6 +28,7 @@ function date_field_formatter_info() { 'settings' => array( 'interval' => 2, 'interval_display' => 'time ago', + 'use_end_date' => false, ), ), 'date_plain' => array( @@ -207,7 +208,7 @@ function date_field_formatter_view($entity_type, $entity, $field, $instance, $la $variables['item'] = $item; $variables['dates'] = date_formatter_process($formatter, $entity_type, $entity, $field, $instance, $langcode, $item, $display); $variables['attributes'] = !empty($rdf_mapping) ? rdf_rdfa_attributes($rdf_mapping, $item['value']) : array(); - $variables['show_remaining_days'] = $display['settings']['show_remaining_days']; + $variables['show_remaining_days'] = isset($display['settings']['show_remaining_days']) ? $display['settings']['show_remaining_days'] : FALSE; $output = theme('date_display_combination', $variables); if (!empty($output)) { $element[$delta] = array('#markup' => $output); @@ -322,7 +323,7 @@ function date_field_widget_info() { } // The date text widget should use an increment of 1. - $info['date_text']['increment'] = 1; + $info['date_text']['settings']['increment'] = 1; return $info; } @@ -462,6 +463,14 @@ function date_field_instance_settings_form($field, $instance) { return _date_field_instance_settings_form($field, $instance); } +/** + * Form validation handler for _date_field_instance_settings_form(). + */ +function date_field_instance_settings_form_validate(&$form, &$form_state) { + module_load_include('inc', 'date', 'date_admin'); + return _date_field_instance_settings_form_validate($form, $form_state); +} + /** * Implements hook_field_widget_settings_form(). */ @@ -470,6 +479,14 @@ function date_field_widget_settings_form($field, $instance) { return _date_field_widget_settings_form($field, $instance); } +/** + * Form validation handler for _date_field_widget_settings_form(). + */ +function date_field_widget_settings_form_validate(&$form, &$form_state) { + module_load_include('inc', 'date', 'date_admin'); + return _date_field_widget_settings_form_validate($form, $form_state); +} + /** * Implements hook_field_settings_form(). */ @@ -478,6 +495,14 @@ function date_field_settings_form($field, $instance, $has_data) { return _date_field_settings_form($field, $instance, $has_data); } +/** + * Form validation handler for _date_field_settings_form(). + */ +function date_field_settings_validate(&$form, &$form_state) { + module_load_include('inc', 'date', 'date_admin'); + return _date_field_settings_validate($form, $form_state); +} + /** * Implements hook_content_migrate_field_alter(). * diff --git a/sites/all/modules/contrib/date/date.info b/sites/all/modules/contrib/date/date.info index c6fdde552..28263b420 100644 --- a/sites/all/modules/contrib/date/date.info +++ b/sites/all/modules/contrib/date/date.info @@ -13,10 +13,11 @@ files[] = tests/date_validation.test files[] = tests/date_timezone.test files[] = tests/date_views_pager.test files[] = tests/date_views_popup.test +files[] = tests/date_form.test -; Information added by Drupal.org packaging script on 2015-09-08 -version = "7.x-2.9" +; Information added by Drupal.org packaging script on 2017-04-07 +version = "7.x-2.10" core = "7.x" project = "date" -datestamp = "1441727353" +datestamp = "1491562090" diff --git a/sites/all/modules/contrib/date/date.install b/sites/all/modules/contrib/date/date.install index 9a9b6d350..765b90cf8 100644 --- a/sites/all/modules/contrib/date/date.install +++ b/sites/all/modules/contrib/date/date.install @@ -204,3 +204,11 @@ function date_update_7004() { field_cache_clear(); drupal_set_message(t('Date text widgets have been updated to use an increment of 1.')); } + +/** + * Revisited: Date text widgets should always use an increment of 1. + */ +function date_update_7005() { + // @see https://www.drupal.org/node/1355256 + date_update_7004(); +} diff --git a/sites/all/modules/contrib/date/date.migrate.inc b/sites/all/modules/contrib/date/date.migrate.inc index ad2a56815..90c1ec51f 100644 --- a/sites/all/modules/contrib/date/date.migrate.inc +++ b/sites/all/modules/contrib/date/date.migrate.inc @@ -1,10 +1,13 @@ ' . 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; @@ -378,6 +378,8 @@ function theme_date_display_interval($variables) { 'end_date' => $dates['value2']['local']['object'], 'interval' => $options['interval'], 'interval_display' => $options['interval_display'], + 'use_end_date' => !empty($options['use_end_date']) ? + $options['use_end_date'] : FALSE, ); if ($return = theme('date_time_ago', $time_ago_vars)) { @@ -398,9 +400,9 @@ function theme_date_combo($variables) { // Group start/end items together in fieldset. $fieldset = array( - '#title' => field_filter_xss(t($element['#title'])) . ' ' . ($element['#delta'] > 0 ? intval($element['#delta'] + 1) : ''), + '#title' => field_filter_xss(t($element['#title'])) . ($element['#delta'] > 0 ? ' ' . intval($element['#delta'] + 1) : ''), '#value' => '', - '#description' => !empty($element['#fieldset_description']) ? $element['#fieldset_description'] : '', + '#description' => !empty($element['#description']) ? $element['#description'] : '', '#attributes' => array('class' => array('date-combo')), '#children' => $element['#children'], ); diff --git a/sites/all/modules/contrib/date/date_admin.inc b/sites/all/modules/contrib/date/date_admin.inc index 13da42ef5..b800201e0 100644 --- a/sites/all/modules/contrib/date/date_admin.inc +++ b/sites/all/modules/contrib/date/date_admin.inc @@ -14,15 +14,24 @@ function date_default_formatter_settings_form($field, $instance, $view_mode, $fo $formatter = $display['type']; $form = array(); + $date_formats = date_format_type_options(); $form['format_type'] = array( '#title' => t('Choose how users view dates and times:'), '#type' => 'select', - '#options' => date_format_type_options(), + '#options' => $date_formats + array('custom' => t('Custom')), '#default_value' => $settings['format_type'], '#description' => t('To add or edit options, visit Date and time settings.', array('@date-time-page' => url('admin/config/regional/date-time'))), '#weight' => 0, ); + $form['custom_date_format'] = array( + '#type' => 'textfield', + '#title' => t('Custom date format'), + '#description' => t('If "Custom", see the PHP manual for date formats. Otherwise, enter the number of different time units to display, which defaults to 2.', array('@url' => 'http://php.net/manual/function.date.php')), + '#default_value' => isset($settings['custom_date_format']) ? $settings['custom_date_format'] : '', + '#dependency' => array('edit-options-settings-format-type' => array('custom')), + ); + $form['fromto'] = array( '#title' => t('Display:'), '#type' => 'select', @@ -116,6 +125,14 @@ function date_interval_formatter_settings_form($field, $instance, $view_mode, $f '#default_value' => $settings['interval_display'], '#weight' => 0, ); + if (!empty($field['settings']['todate'])) { + $form['use_end_date'] = array( + '#title' => t('Use End date'), + '#description' => 'Use the End date, instead of the start date', + '#type' => 'checkbox', + '#default_value' => $settings['use_end_date'], + ); + } return $form; } @@ -186,7 +203,9 @@ function date_interval_formatter_settings_summary($field, $instance, $view_mode) $display = $instance['display'][$view_mode]; $settings = $display['settings']; $formatter = $display['type']; - $summary[] = t('Display time ago, showing @interval units.', array('@interval' => $settings['interval'])); + $field = ($settings['use_end_date'] == 1) ? 'End' : 'Start'; + $summary[] = t('Display time ago, showing @interval units. Using @field Date', + array('@interval' => $settings['interval'], '@field' => $field)); return $summary; } @@ -273,7 +292,7 @@ function _date_field_instance_settings_form($field, $instance) { /** * Form validation handler for _date_field_instance_settings_form(). */ -function date_field_instance_settings_form_validate(&$form, &$form_state) { +function _date_field_instance_settings_form_validate(&$form, &$form_state) { $settings = $form_state['values']['instance']['settings']; if ($settings['default_value'] == 'strtotime') { @@ -459,7 +478,7 @@ function _date_field_widget_settings_form($field, $instance) { /** * Form validation handler for _date_field_widget_settings_form(). */ -function date_field_widget_settings_form_validate(&$form, &$form_state) { +function _date_field_widget_settings_form_validate(&$form, &$form_state) { // The widget settings are in the wrong place in the form because of #tree on // the top level. $settings = $form_state['values']['instance']['widget']['settings']; @@ -561,7 +580,7 @@ function _date_field_settings_form($field, $instance, $has_data) { $form['cache_enabled'] = array( '#type' => 'checkbox', '#title' => t('Cache dates'), - '#description' => t('Date objects can be created and cached as date fields are loaded rather than when they are displayed to improve performance.'), + '#description' => t('Date objects can be created and cached as date fields are loaded, rather than when they are displayed, to improve performance.'), '#default_value' => !empty($settings['cache_enabled']), '#weight' => 10, ); @@ -594,7 +613,7 @@ function _date_field_settings_form($field, $instance, $has_data) { /** * Form validation handler for _date_field_settings_form(). */ -function date_field_settings_validate(&$form, &$form_state) { +function _date_field_settings_validate(&$form, &$form_state) { $field = &$form_state['values']['field']; if ($field['settings']['tz_handling'] == 'none') { diff --git a/sites/all/modules/contrib/date/date_all_day/date_all_day.info b/sites/all/modules/contrib/date/date_all_day/date_all_day.info index df773e419..fcdc673a0 100644 --- a/sites/all/modules/contrib/date/date_all_day/date_all_day.info +++ b/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 2015-09-08 -version = "7.x-2.9" +; Information added by Drupal.org packaging script on 2017-04-07 +version = "7.x-2.10" core = "7.x" project = "date" -datestamp = "1441727353" +datestamp = "1491562090" diff --git a/sites/all/modules/contrib/date/date_api/date_api.info b/sites/all/modules/contrib/date/date_api/date_api.info index 40fbb0566..c537ca040 100644 --- a/sites/all/modules/contrib/date/date_api/date_api.info +++ b/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 2015-09-08 -version = "7.x-2.9" +; Information added by Drupal.org packaging script on 2017-04-07 +version = "7.x-2.10" core = "7.x" project = "date" -datestamp = "1441727353" +datestamp = "1491562090" diff --git a/sites/all/modules/contrib/date/date_api/date_api.module b/sites/all/modules/contrib/date/date_api/date_api.module index 2a973ccf6..160bc9fbd 100644 --- a/sites/all/modules/contrib/date/date_api/date_api.module +++ b/sites/all/modules/contrib/date/date_api/date_api.module @@ -1833,9 +1833,10 @@ function date_format_interval($date, $granularity = 2, $display_ago = TRUE) { /** * A date object for the current time. * - * @param object $timezone - * (optional) Optionally force time to a specific timezone, defaults to user - * timezone, if set, otherwise site timezone. Defaults to NULL. + * @param object|string|null $timezone + * (optional) PHP DateTimeZone object, string or NULL allowed. Optionally + * force time to a specific timezone, defaults to user timezone, if set, + * otherwise site timezone. Defaults to NULL. * * @param bool $reset * (optional) Static cache reset. @@ -1844,11 +1845,16 @@ function date_format_interval($date, $granularity = 2, $display_ago = TRUE) { * The current time as a date object. */ function date_now($timezone = NULL, $reset = FALSE) { + $static_var = __FUNCTION__ . $timezone; + if ($timezone instanceof DateTimeZone) { + $static_var = __FUNCTION__ . $timezone->getName(); + } + if ($reset) { - drupal_static_reset(__FUNCTION__ . $timezone); + drupal_static_reset($static_var); } - $now = &drupal_static(__FUNCTION__ . $timezone); + $now = &drupal_static($static_var); if (!isset($now)) { $now = new DateObject('now', $timezone); @@ -1920,7 +1926,12 @@ function date_days_in_month($year, $month) { // Pick a day in the middle of the month to avoid timezone shifts. $datetime = date_pad($year, 4) . '-' . date_pad($month) . '-15 00:00:00'; $date = new DateObject($datetime); - return $date->format('t'); + if ($date->errors) { + return FALSE; + } + else { + return $date->format('t'); + } } /** @@ -2075,6 +2086,9 @@ function date_iso_week_range($week, $year) { date_timezone_set($min_date, date_default_timezone_object()); // Find the first day of the first ISO week in the year. + // If it's already a Monday, date_modify won't add a Monday, + // it will remain the same day. So add a Sunday first, then a Monday. + date_modify($min_date, '+1 Sunday'); date_modify($min_date, '+1 Monday'); // Jump ahead to the desired week for the beginning of the week range. diff --git a/sites/all/modules/contrib/date/date_api/date_api_elements.inc b/sites/all/modules/contrib/date/date_api/date_api_elements.inc index fc91205ce..638fcda5b 100644 --- a/sites/all/modules/contrib/date/date_api/date_api_elements.inc +++ b/sites/all/modules/contrib/date/date_api/date_api_elements.inc @@ -111,7 +111,7 @@ function date_default_date($element) { $format = DATE_FORMAT_DATETIME; // The text and popup widgets might return less than a full datetime string. - if (strlen($element['#default_value']) < 19) { + if (is_string($element['#default_value']) && strlen($element['#default_value']) < 19) { switch (strlen($element['#default_value'])) { case 16: $format = 'Y-m-d H:i'; @@ -319,7 +319,7 @@ function date_text_element_process($element, &$form_state, $form) { $element['#tree'] = TRUE; $element['#theme_wrappers'] = array('date_text'); - $element['date']['#value'] = $element['#value']['date']; + $element['date']['#value'] = isset($element['#value']['date']) ? $element['#value']['date'] : ''; $element['date']['#type'] = 'textfield'; $element['date']['#weight'] = !empty($element['date']['#weight']) ? $element['date']['#weight'] : $element['#weight']; $element['date']['#attributes'] = array('class' => isset($element['#attributes']['class']) ? $element['#attributes']['class'] += array('date-date') : array('date-date')); diff --git a/sites/all/modules/contrib/date/date_api/theme/theme.inc b/sites/all/modules/contrib/date/date_api/theme/theme.inc index a6aef2305..0a57ecd07 100644 --- a/sites/all/modules/contrib/date/date_api/theme/theme.inc +++ b/sites/all/modules/contrib/date/date_api/theme/theme.inc @@ -194,6 +194,7 @@ function theme_date_calendar_day($variables) { function theme_date_time_ago($variables) { $start_date = $variables['start_date']; $end_date = $variables['end_date']; + $use_end_date = isset($variables['use_end_date']) ? $variables['use_end_date'] : false; $interval = !empty($variables['interval']) ? $variables['interval'] : 2; $display = isset($variables['interval_display']) ? $variables['interval_display'] : 'time ago'; @@ -202,12 +203,20 @@ function theme_date_time_ago($variables) { return; } + // We use the end date only when the option is checked. + if ($use_end_date){ + $date = date_format($end_date, DATE_FORMAT_UNIX); + } + else { + $date = date_format($start_date, DATE_FORMAT_UNIX); + } + // Time to compare dates to. + $now = date_format(date_now(), DATE_FORMAT_UNIX); - $start = date_format($start_date, DATE_FORMAT_UNIX); // Will be positive for a datetime in the past (ago), and negative for a datetime in the future (hence). - $time_diff = $now - $start; + $time_diff = $now - $date; // Uses the same options used by Views format_interval. switch ($display) { diff --git a/sites/all/modules/contrib/date/date_context/date_context.info b/sites/all/modules/contrib/date/date_context/date_context.info index c521dd263..97cb8c9d9 100644 --- a/sites/all/modules/contrib/date/date_context/date_context.info +++ b/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 2015-09-08 -version = "7.x-2.9" +; Information added by Drupal.org packaging script on 2017-04-07 +version = "7.x-2.10" core = "7.x" project = "date" -datestamp = "1441727353" +datestamp = "1491562090" diff --git a/sites/all/modules/contrib/date/date_elements.inc b/sites/all/modules/contrib/date/date_elements.inc index 83697153b..656d49989 100644 --- a/sites/all/modules/contrib/date/date_elements.inc +++ b/sites/all/modules/contrib/date/date_elements.inc @@ -74,7 +74,7 @@ function date_field_widget_form(&$form, &$form_state, $field, $instance, $langco // The repeating values will be re-generated when the repeat widget form is validated. // At this point we can't tell if this form element is going to be hidden by #access, and we're going to // lose all but the first value by doing this, so store the original values in case we need to replace them later. - if (!empty($field['settings']['repeat'])) { + if (!empty($field['settings']['repeat']) && module_exists('date_repeat_field')) { if ($delta == 0) { $form['#after_build'][] = 'date_repeat_after_build'; $form_state['storage']['repeat_fields'][$field_name] = array_merge($form['#parents'], array($field_name)); @@ -337,8 +337,11 @@ function date_combo_element_process($element, &$form_state, $form) { '#date_label_position' => $instance['widget']['settings']['label_position'], ); - $description = !empty($element['#description']) ? t($element['#description']) : ''; - unset($element['#description']); + // Date repeat is a multiple value field. So the description is removed from + // the single element earlier. Let's get it back. + if (isset($element['show_repeat_settings']) && !empty($element['value']['#instance']['description'])) { + $element['#description'] = $element['value']['#instance']['description']; + } // Give this element the right type, using a Date API // or a Date Popup element type. @@ -383,8 +386,7 @@ function date_combo_element_process($element, &$form_state, $form) { $element[$to_field]['#prefix'] = ''; // Users with JS enabled will never see initially blank values for the end // date (see Drupal.date.EndDateHandler()), so hide the message for them. - $description .= ' ' . t("Empty 'End date' values will use the 'Start date' values.") . ''; - $element['#fieldset_description'] = $description; + $element['#description'] .= ' ' . t("Empty 'End date' values will use the 'Start date' values.") . ''; if ($field['settings']['todate'] == 'optional') { $element[$to_field]['#states'] = array( 'visible' => array( @@ -395,9 +397,6 @@ function date_combo_element_process($element, &$form_state, $form) { ); } } - else { - $element[$from_field]['#description'] = $description; - } // Create label for error messages that make sense in multiple values // and when the title field is left blank. @@ -474,6 +473,12 @@ function date_combo_validate($element, &$form_state) { $form_values = drupal_array_get_nested_value($form_state['values'], $element['#field_parents']); $form_input = drupal_array_get_nested_value($form_state['input'], $element['#field_parents']); + // Programmatically calling drupal_submit_form() does not always add the date + // combo to $form_state['input']. + if (empty($form_input[$field_name]) && !empty($form_values[$field_name])) { + form_set_value($element, $element['#date_items'], $form_state); + return; + } // If the whole field is empty and that's OK, stop now. if (empty($form_input[$field_name]) && !$element['#required']) { return; @@ -546,11 +551,7 @@ function date_combo_validate($element, &$form_state) { return; } } - // Don't look for further errors if errors are already flagged - // because otherwise we'll show errors on the nested elements - // more than once. - elseif (!form_get_errors()) { - + else { $timezone = !empty($item[$tz_field]) ? $item[$tz_field] : $element['#date_timezone']; $timezone_db = date_get_timezone_db($field['settings']['tz_handling']); $element[$from_field]['#date_timezone'] = $timezone; @@ -625,7 +626,10 @@ function date_combo_validate($element, &$form_state) { } } } - if (!empty($errors)) { + // Don't show further errors if errors are already flagged + // because otherwise we'll show errors on the nested elements + // more than once. + if (!form_get_errors() && !empty($errors)) { if ($field['cardinality']) { form_error($element, t('There are errors in @field_name value #@delta:', array('@field_name' => $instance['label'], '@delta' => $delta + 1)) . theme('item_list', array('items' => $errors))); } diff --git a/sites/all/modules/contrib/date/date_migrate/date_migrate.info b/sites/all/modules/contrib/date/date_migrate/date_migrate.info index b3a6bba61..41e416a36 100644 --- a/sites/all/modules/contrib/date/date_migrate/date_migrate.info +++ b/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 2015-09-08 -version = "7.x-2.9" +; Information added by Drupal.org packaging script on 2017-04-07 +version = "7.x-2.10" core = "7.x" project = "date" -datestamp = "1441727353" +datestamp = "1491562090" diff --git a/sites/all/modules/contrib/date/date_migrate/date_migrate_example/date_migrate_example.info b/sites/all/modules/contrib/date/date_migrate/date_migrate_example/date_migrate_example.info index 6d79df405..ba07220d4 100644 --- a/sites/all/modules/contrib/date/date_migrate/date_migrate_example/date_migrate_example.info +++ b/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 2015-09-08 -version = "7.x-2.9" +; Information added by Drupal.org packaging script on 2017-04-07 +version = "7.x-2.10" core = "7.x" project = "date" -datestamp = "1441727353" +datestamp = "1491562090" diff --git a/sites/all/modules/contrib/date/date_popup/date_popup.info b/sites/all/modules/contrib/date/date_popup/date_popup.info index 500c7ffb7..41945a636 100644 --- a/sites/all/modules/contrib/date/date_popup/date_popup.info +++ b/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 2015-09-08 -version = "7.x-2.9" +; Information added by Drupal.org packaging script on 2017-04-07 +version = "7.x-2.10" core = "7.x" project = "date" -datestamp = "1441727353" +datestamp = "1491562090" diff --git a/sites/all/modules/contrib/date/date_popup/date_popup.js b/sites/all/modules/contrib/date/date_popup/date_popup.js index bbf25e2df..f38dcb59d 100644 --- a/sites/all/modules/contrib/date/date_popup/date_popup.js +++ b/sites/all/modules/contrib/date/date_popup/date_popup.js @@ -14,6 +14,14 @@ $(this).click(function(){ $(this).focus(); }); + if (datePopup.settings.syncEndDate) { + $('.start-date-wrapper').each(function(){ + var start_date_wrapper = this; + $(this).find('input:eq(0)').change(function(){ + $(start_date_wrapper).next('.end-date-wrapper').find('input:eq(0)').val($(this).val()); + }); + }); + } break; case 'timeEntry': diff --git a/sites/all/modules/contrib/date/date_popup/date_popup.module b/sites/all/modules/contrib/date/date_popup/date_popup.module index e91cc8a72..a03132e99 100644 --- a/sites/all/modules/contrib/date/date_popup/date_popup.module +++ b/sites/all/modules/contrib/date/date_popup/date_popup.module @@ -394,6 +394,10 @@ function date_popup_process_date_part(&$element) { 'fromTo' => isset($fromto), ); + if (!empty($element['#instance'])) { + $settings['syncEndDate'] = $element['#instance']['settings']['default_value2'] == 'sync'; + } + // Create a unique id for each set of custom settings. $id = date_popup_js_settings_id($element['#id'], 'datepicker', $settings); @@ -474,10 +478,11 @@ function date_popup_process_time_part(&$element) { $grans = array('hour', 'minute', 'second'); $time_granularity = array_intersect($granularity, $grans); $format = date_popup_format_to_popup_time(date_limit_format($element['#date_format'], $time_granularity), 'wvega'); + $default_value = isset($element['#default_value']) ? $element['#default_value'] : ''; // The first value in the dropdown list should be the same as the element // default_value, but it needs to be in JS format (i.e. milliseconds since // the epoch). - $start_time = new DateObject($element['#default_value'], $element['#date_timezone'], DATE_FORMAT_DATETIME); + $start_time = new DateObject($default_value, $element['#date_timezone'], DATE_FORMAT_DATETIME); date_increment_round($start_time, $element['#date_increment']); $start_time = $start_time->format(DATE_FORMAT_UNIX) * 1000; $settings = array( @@ -580,7 +585,7 @@ function date_popup_validate($element, &$form_state) { // If something was input but there is no date, the date is invalid. // If the field is empty and required, set error message and return. $error_field = implode('][', $element['#parents']); - if (empty($date) || !empty($date->errors)) { + if ((empty($element['#value']['date']) && empty($element['#value']['time'])) || !empty($date->errors)) { if (is_object($date) && !empty($date->errors)) { $message = t('The value input for field %field is invalid:', array('%field' => $label)); $message .= '
' . implode('
', $date->errors); @@ -613,7 +618,9 @@ function date_popup_validate($element, &$form_state) { */ function date_popup_input_date($element, $input, $auto_complete = FALSE) { if (empty($input) || !is_array($input) || !array_key_exists('date', $input) || empty($input['date'])) { - return NULL; + //check if there is no time associated in the input variable. This is the exception scenario where the user has entered only time and not date. + if(empty($input['time'])) + return NULL; } date_popup_add(); $granularity = date_format_order($element['#date_format']); @@ -622,9 +629,14 @@ function date_popup_input_date($element, $input, $auto_complete = FALSE) { $format = date_popup_date_format($element); $format .= $has_time ? ' ' . date_popup_time_format($element) : ''; - $datetime = trim($input['date']); + //check if date is empty, if yes, then leave it blank. + $datetime = !empty($input['date']) ? trim($input['date']) : ''; $datetime .= $has_time ? ' ' . trim($input['time']) : ''; $date = new DateObject($datetime, $element['#date_timezone'], $format); + //if the variable is time only then set TimeOnly to TRUE + if(empty($input['date']) && !empty($input['time']) ){ + $date->timeOnly = 'TRUE'; + } if (is_object($date)) { $date->limitGranularity($granularity); if ($date->validGranularity($granularity, $flexible)) { @@ -800,6 +812,14 @@ function theme_date_popup($vars) { return '
' . theme('form_element', $element) . '
'; } +/** + * Implements hook_date_field_instance_settings_form_alter(). + */ +function date_popup_date_field_instance_settings_form_alter(&$form, $context) { + // Add an extra option to sync the end date with the start date. + $form['default_value2']['#options']['sync'] = t('Sync with start date'); +} + /** * Implements hook_menu(). */ diff --git a/sites/all/modules/contrib/date/date_repeat/date_repeat.info b/sites/all/modules/contrib/date/date_repeat/date_repeat.info index b6afcd471..0a63175c7 100644 --- a/sites/all/modules/contrib/date/date_repeat/date_repeat.info +++ b/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 2015-09-08 -version = "7.x-2.9" +; Information added by Drupal.org packaging script on 2017-04-07 +version = "7.x-2.10" core = "7.x" project = "date" -datestamp = "1441727353" +datestamp = "1491562090" diff --git a/sites/all/modules/contrib/date/date_repeat/tests/date_repeat_form.test b/sites/all/modules/contrib/date/date_repeat/tests/date_repeat_form.test index 0c5460bab..22d652961 100644 --- a/sites/all/modules/contrib/date/date_repeat/tests/date_repeat_form.test +++ b/sites/all/modules/contrib/date/date_repeat/tests/date_repeat_form.test @@ -25,7 +25,7 @@ class DateRepeatFormTestCase extends DrupalWebTestCase { // Create and log in our privileged user. $this->privileged_user = $this->drupalCreateUser(array( - 'administer content types', 'administer nodes', 'bypass node access', 'view date repeats' + 'administer content types', 'administer nodes', 'bypass node access', 'view date repeats', 'administer fields' )); $this->drupalLogin($this->privileged_user); diff --git a/sites/all/modules/contrib/date/date_repeat_field/date_repeat_field.info b/sites/all/modules/contrib/date/date_repeat_field/date_repeat_field.info index 31c99af56..c9aa9d67b 100644 --- a/sites/all/modules/contrib/date/date_repeat_field/date_repeat_field.info +++ b/sites/all/modules/contrib/date/date_repeat_field/date_repeat_field.info @@ -7,9 +7,9 @@ stylesheets[all][] = date_repeat_field.css package = Date/Time core = 7.x -; Information added by Drupal.org packaging script on 2015-09-08 -version = "7.x-2.9" +; Information added by Drupal.org packaging script on 2017-04-07 +version = "7.x-2.10" core = "7.x" project = "date" -datestamp = "1441727353" +datestamp = "1491562090" diff --git a/sites/all/modules/contrib/date/date_tools/date_tools.info b/sites/all/modules/contrib/date/date_tools/date_tools.info index 3ec028594..04bfb03f9 100644 --- a/sites/all/modules/contrib/date/date_tools/date_tools.info +++ b/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 2015-09-08 -version = "7.x-2.9" +; Information added by Drupal.org packaging script on 2017-04-07 +version = "7.x-2.10" core = "7.x" project = "date" -datestamp = "1441727353" +datestamp = "1491562090" diff --git a/sites/all/modules/contrib/date/date_tools/tests/date_tools.test b/sites/all/modules/contrib/date/date_tools/tests/date_tools.test index 47c056424..07af4a3d4 100644 --- a/sites/all/modules/contrib/date/date_tools/tests/date_tools.test +++ b/sites/all/modules/contrib/date/date_tools/tests/date_tools.test @@ -28,7 +28,7 @@ class DateToolsTestCase extends DrupalWebTestCase { // Create and log in our privileged user. $this->privileged_user = $this->drupalCreateUser( - array('administer content types', 'administer nodes', 'bypass node access', 'administer date tools') + array('administer content types', 'administer nodes', 'bypass node access', 'administer date tools', 'administer fields') ); $this->drupalLogin($this->privileged_user); diff --git a/sites/all/modules/contrib/date/date_views/date_views.info b/sites/all/modules/contrib/date/date_views/date_views.info index 663efd35d..68b32cc2e 100644 --- a/sites/all/modules/contrib/date/date_views/date_views.info +++ b/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 2015-09-08 -version = "7.x-2.9" +; Information added by Drupal.org packaging script on 2017-04-07 +version = "7.x-2.10" core = "7.x" project = "date" -datestamp = "1441727353" +datestamp = "1491562090" diff --git a/sites/all/modules/contrib/date/date_views/includes/date_views_filter_handler.inc b/sites/all/modules/contrib/date/date_views/includes/date_views_filter_handler.inc index f761dfa0a..ae6944ee1 100644 --- a/sites/all/modules/contrib/date/date_views/includes/date_views_filter_handler.inc +++ b/sites/all/modules/contrib/date/date_views/includes/date_views_filter_handler.inc @@ -42,6 +42,32 @@ class date_views_filter_handler extends date_views_filter_handler_simple { $this->date_combine_conditions('op_contains'); } + function op_empty($field) { + $this->get_query_fields(); + if (empty($this->query_fields)) { + return; + } + + // Add each condition to the custom filter group. + foreach ((array) $this->query_fields as $query_field) { + $field = $query_field['field']; + $this->date_handler = $query_field['date_handler']; + + // Respect relationships when determining the table alias. + if ($field['table_name'] != $this->table || !empty($this->relationship)) { + $this->related_table_alias = $this->query->ensure_table($field['table_name'], $this->relationship); + } + else { + $this->related_table_alias = NULL; + } + + $table_alias = !empty($this->related_table_alias) ? $this->related_table_alias : $field['table_name']; + $field_name = $table_alias . '.' . $field['field_name']; + + parent::op_empty($field_name); + } + } + /** * Combines multiple date WHERE expressions into a single WHERE expression. * @@ -67,7 +93,7 @@ class date_views_filter_handler extends date_views_filter_handler_simple { $this->related_table_alias = $this->query->ensure_table($field['table_name'], $this->relationship); } else { - $this->related_table_alias = null; + $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']; diff --git a/sites/all/modules/contrib/date/date_views/includes/date_views_plugin_pager.inc b/sites/all/modules/contrib/date/date_views/includes/date_views_plugin_pager.inc index 5caca4e5b..1addd2000 100644 --- a/sites/all/modules/contrib/date/date_views/includes/date_views_plugin_pager.inc +++ b/sites/all/modules/contrib/date/date_views/includes/date_views_plugin_pager.inc @@ -281,7 +281,7 @@ class date_views_plugin_pager extends views_plugin_pager { 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); + $this->view->date_info->next_date = clone($argument->min_date); date_modify($this->view->date_info->next_date, '+1 ' . $argument->date_handler->granularity); } // Write the date_info properties that depend on the current value. diff --git a/sites/all/modules/contrib/date/date_views/theme/theme.inc b/sites/all/modules/contrib/date/date_views/theme/theme.inc index 9e7debc74..7ca03f3a4 100644 --- a/sites/all/modules/contrib/date/date_views/theme/theme.inc +++ b/sites/all/modules/contrib/date/date_views/theme/theme.inc @@ -77,7 +77,7 @@ function template_preprocess_date_views_pager(&$vars) { 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); + $prev_arg = date_format($prev_date, 'o-\W') . date_pad($prev_week); break; default: $prev_arg = date_format($prev_date, $format[$granularity]); @@ -90,7 +90,7 @@ function template_preprocess_date_views_pager(&$vars) { 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); + $next_arg = date_format($next_date, 'o-\W') . date_pad($next_week); break; default: $next_arg = date_format($next_date, $format[$granularity]); diff --git a/sites/all/modules/contrib/date/tests/date_api.test b/sites/all/modules/contrib/date/tests/date_api.test index 0924ebaa9..03c9081ba 100644 --- a/sites/all/modules/contrib/date/tests/date_api.test +++ b/sites/all/modules/contrib/date/tests/date_api.test @@ -393,7 +393,7 @@ class DateAPITestCase extends DrupalWebTestCase { $input = '23 abc 2012'; $timezone = NULL; $format = 'd M Y'; - $date = new dateObject($input, $timezone, $format); + $date = @new dateObject($input, $timezone, $format); $this->assertNotEqual(count($date->errors), 0, '23 abc 2012 should be an invalid date'); // Test Granularity. diff --git a/sites/all/modules/contrib/date/tests/date_field.test b/sites/all/modules/contrib/date/tests/date_field.test index 2dd3f0518..b8aad5945 100644 --- a/sites/all/modules/contrib/date/tests/date_field.test +++ b/sites/all/modules/contrib/date/tests/date_field.test @@ -16,7 +16,7 @@ abstract class DateFieldBasic extends DrupalWebTestCase { // Create and log in our privileged user. $this->privileged_user = $this->drupalCreateUser( - array('administer content types', 'administer nodes', 'bypass node access', 'administer date tools') + array('administer content types', 'administer nodes', 'bypass node access', 'administer date tools', 'administer fields') ); $this->drupalLogin($this->privileged_user); diff --git a/sites/all/modules/contrib/date/tests/date_form.test b/sites/all/modules/contrib/date/tests/date_form.test new file mode 100644 index 000000000..56b89e438 --- /dev/null +++ b/sites/all/modules/contrib/date/tests/date_form.test @@ -0,0 +1,30 @@ + t('Date Form test'), + 'description' => t('Test Date form functions.') , + 'group' => t('Date'), + ); + } + + public function setUp() { + // Load the date_api module. + parent::setUp('date_test'); + } + + /** + * Tests rendering of a date element in a form. + */ + public function testDateForm() { + $this->drupalGet('date-test/form'); + } + +} diff --git a/sites/all/modules/contrib/date/tests/date_migrate.test b/sites/all/modules/contrib/date/tests/date_migrate.test index cdde115f9..ec2ae7d88 100644 --- a/sites/all/modules/contrib/date/tests/date_migrate.test +++ b/sites/all/modules/contrib/date/tests/date_migrate.test @@ -18,6 +18,7 @@ class DateMigrateExampleUnitTest extends DrupalWebTestCase { 'name' => 'Date Migration', 'description' => 'Test migration into date fields', 'group' => 'Date', + 'dependencies' => array('migrate', 'features'), ); } diff --git a/sites/all/modules/contrib/date/tests/date_test/date_test.info b/sites/all/modules/contrib/date/tests/date_test/date_test.info new file mode 100644 index 000000000..2f265fa75 --- /dev/null +++ b/sites/all/modules/contrib/date/tests/date_test/date_test.info @@ -0,0 +1,14 @@ +name = "Date module tests" +description = "Support module for date related testing." +package = Date/Time +version = VERSION +core = 7.x +hidden = TRUE +dependencies[] = date + +; Information added by Drupal.org packaging script on 2017-04-07 +version = "7.x-2.10" +core = "7.x" +project = "date" +datestamp = "1491562090" + diff --git a/sites/all/modules/contrib/date/tests/date_test/date_test.module b/sites/all/modules/contrib/date/tests/date_test/date_test.module new file mode 100644 index 000000000..185408913 --- /dev/null +++ b/sites/all/modules/contrib/date/tests/date_test/date_test.module @@ -0,0 +1,40 @@ + 'Test form with date element', + 'description' => "Form with date element to make form related tests", + 'page callback' => 'drupal_get_form', + 'page arguments' => array('date_test_sample_form'), + 'access arguments' => array('access content'), + 'type' => MENU_CALLBACK, + ); + return $items; +} + +/** + * Form callback. Generates a test form with date elements. + */ +function date_test_sample_form($form, &$form_state) { + $form['date_test_select'] = array( + '#type' => 'date_select', + '#title' => t('Sample from'), + '#date_format' => 'H:i:s a', + '#default_value' => array( + 'hour' => 7, + 'minute' => 0, + 'second' => 0, + 'ampm' => 'am' + ), + ); + + return $form; +} diff --git a/sites/all/modules/contrib/date/tests/date_timezone.test b/sites/all/modules/contrib/date/tests/date_timezone.test index 54d882fd8..9f2905a28 100644 --- a/sites/all/modules/contrib/date/tests/date_timezone.test +++ b/sites/all/modules/contrib/date/tests/date_timezone.test @@ -16,6 +16,16 @@ class DateTimezoneTestCase extends DateFieldBasic { ); } + public function setUp() { + parent::setUp(); + // Set the timezone explicitly. Otherwise the site's default timezone is + // used, which defaults to the server timezone when installing Drupal. This + // depends on the environment and is therefore uncertain. + // The Australia/Sydney timezone is chosen so all tests are run using an + // edge case scenario (UTC+10 and DST). + variable_set('date_default_timezone', 'Australia/Sydney'); + } + /** * @todo. */ @@ -23,7 +33,7 @@ class DateTimezoneTestCase extends DateFieldBasic { // Create a date fields with combinations of various timezone handling and // granularity. foreach (array('date', 'datestamp', 'datetime') as $field_type) { - foreach (array('site', 'none', 'date', 'user', 'utc') as $tz_handling) { + foreach (array('site', 'none', 'date', 'user', 'utc', 'Europe/Dublin') as $tz_handling) { foreach (array('year', 'month', 'day', 'hour', 'minute', 'second') as $max_granularity) { // Skip invalid combinations. if (in_array($max_granularity, array('year', 'month', 'day')) && $tz_handling != 'none') { @@ -182,17 +192,32 @@ class DateTimezoneTestCase extends DateFieldBasic { case 'hour': $edit[$field_name . '[und][0][value][date]'] = '10/07/2010 - 10'; $edit[$field_name . '[und][0][value2][date]'] = '10/07/2010 - 11'; - $should_be = 'Thu, 10/07/2010 - 10 to Thu, 10/07/2010 - 11'; + if ($tz_handling == 'utc') { + $should_be = 'Thu, 10/07/2010 - 21 to Thu, 10/07/2010 - 22'; + } + else { + $should_be = 'Thu, 10/07/2010 - 10 to Thu, 10/07/2010 - 11'; + } break; case 'minute': $edit[$field_name . '[und][0][value][date]'] = '10/07/2010 - 10:30'; $edit[$field_name . '[und][0][value2][date]'] = '10/07/2010 - 11:30'; - $should_be = 'Thu, 10/07/2010 - 10:30 to 11:30'; + if ($tz_handling == 'utc') { + $should_be = 'Thu, 10/07/2010 - 21:30 to 22:30'; + } + else { + $should_be = 'Thu, 10/07/2010 - 10:30 to 11:30'; + } break; case 'second': $edit[$field_name . '[und][0][value][date]'] = '10/07/2010 - 10:30:30'; $edit[$field_name . '[und][0][value2][date]'] = '10/07/2010 - 11:30:30'; - $should_be = 'Thu, 10/07/2010 - 10:30:30 to 11:30:30'; + if ($tz_handling == 'utc') { + $should_be = 'Thu, 10/07/2010 - 21:30:30 to 22:30:30'; + } + else { + $should_be = 'Thu, 10/07/2010 - 10:30:30 to 11:30:30'; + } break; } $this->drupalPost('node/add/story', $edit, t('Save')); diff --git a/sites/all/modules/contrib/entityreference/entityreference.info b/sites/all/modules/contrib/entityreference/entityreference.info index cb0301bdc..2695e4650 100644 --- a/sites/all/modules/contrib/entityreference/entityreference.info +++ b/sites/all/modules/contrib/entityreference/entityreference.info @@ -29,9 +29,9 @@ files[] = tests/entityreference.admin.test files[] = tests/entityreference.feeds.test files[] = tests/entityreference.entity_translation.test -; Information added by Drupal.org packaging script on 2016-09-19 -version = "7.x-1.2" +; Information added by Drupal.org packaging script on 2017-05-23 +version = "7.x-1.4" core = "7.x" project = "entityreference" -datestamp = "1474306740" +datestamp = "1495557187" diff --git a/sites/all/modules/contrib/entityreference/entityreference.install b/sites/all/modules/contrib/entityreference/entityreference.install index cc7bce591..ce6101852 100644 --- a/sites/all/modules/contrib/entityreference/entityreference.install +++ b/sites/all/modules/contrib/entityreference/entityreference.install @@ -163,3 +163,28 @@ function entityreference_update_7002() { )); } } + +/** + * Implements hook_update_N(). + * + * Remove duplicate rows in the taxonomy_index table. + */ +function entityreference_update_7100() { + if (db_table_exists('taxonomy_index')) { + if (db_table_exists('taxonomy_index_tmp')) { + db_drop_table('taxonomy_index_tmp'); + } + + $tx_schema = drupal_get_schema('taxonomy_index'); + db_create_table('taxonomy_index_tmp', $tx_schema); + $select = db_select('taxonomy_index', 'tx'); + $select->fields('tx', array('nid', 'tid')); + $select->groupBy('tx.nid'); + $select->groupBy('tx.tid'); + $select->addExpression('MAX(sticky)', 'sticky'); + $select->addExpression('MAX(created)', 'created'); + db_insert('taxonomy_index_tmp')->from($select)->execute(); + db_drop_table('taxonomy_index'); + db_rename_table('taxonomy_index_tmp', 'taxonomy_index'); + } +} diff --git a/sites/all/modules/contrib/entityreference/entityreference.module b/sites/all/modules/contrib/entityreference/entityreference.module index 14d932a5f..42af2a302 100644 --- a/sites/all/modules/contrib/entityreference/entityreference.module +++ b/sites/all/modules/contrib/entityreference/entityreference.module @@ -1,5 +1,10 @@ array( + 'variables' => array('label' => NULL, 'item' => NULL, 'settings' => NULL, 'uri' => NULL), + ), + 'entityreference_entity_id' => array( + 'variables' => array('item' => NULL, 'settings' => NULL), + ), + ); +} + /** * Implements hook_menu(). */ @@ -163,7 +182,7 @@ function entityreference_get_behavior_handlers($field, $instance = NULL) { /** * Get the behavior handler for a given entityreference field and instance. * - * @param $handler + * @param $behavior * The behavior handler name. */ function _entityreference_get_behavior_handler($behavior) { @@ -400,6 +419,9 @@ function entityreference_field_settings_form($field, $instance, $has_data) { return $form; } +/** + * Callback for custom element processing. + */ function _entityreference_field_settings_process($form, $form_state) { $field = isset($form_state['entityreference']['field']) ? $form_state['entityreference']['field'] : $form['#field']; $instance = isset($form_state['entityreference']['instance']) ? $form_state['entityreference']['instance'] : $form['#instance']; @@ -481,11 +503,17 @@ function _entityreference_field_settings_process($form, $form_state) { return $form; } +/** + * Custom callback for ajax processing. + */ function _entityreference_field_settings_ajax_process($form, $form_state) { _entityreference_field_settings_ajax_process_element($form, $form); return $form; } +/** + * Helper function for custom ajax processing. + */ function _entityreference_field_settings_ajax_process_element(&$element, $main_form) { if (isset($element['#ajax']) && $element['#ajax'] === TRUE) { $element['#ajax'] = array( @@ -500,6 +528,9 @@ function _entityreference_field_settings_ajax_process_element(&$element, $main_f } } +/** + * Custom callback for element processing. + */ function _entityreference_form_process_merge_parent($element) { $parents = $element['#parents']; array_pop($parents); @@ -507,11 +538,17 @@ function _entityreference_form_process_merge_parent($element) { return $element; } +/** + * Helper function to remove blank elements. + */ function _entityreference_element_validate_filter(&$element, &$form_state) { $element['#value'] = array_filter($element['#value']); form_set_value($element, $element['#value'], $form_state); } +/** + * Implements hook_validate(). + */ function _entityreference_field_settings_validate($form, &$form_state) { // Store the new values in the form state. $field = $form['#field']; @@ -547,6 +584,9 @@ function entityreference_field_instance_settings_form($field, $instance) { return $form; } +/** + * Implements hook_field_settings_form(). + */ function _entityreference_field_instance_settings_form($form, $form_state) { $field = isset($form_state['entityreference']['field']) ? $form_state['entityreference']['field'] : $form['#field']; $instance = isset($form_state['entityreference']['instance']) ? $form_state['entityreference']['instance'] : $form['#instance']; @@ -564,6 +604,9 @@ function _entityreference_field_instance_settings_form($form, $form_state) { return $form; } +/** + * Implements hook_validate(). + */ function _entityreference_field_instance_settings_validate($form, &$form_state) { // Store the new values in the form state. $instance = $form['#instance']; @@ -795,7 +838,7 @@ function entityreference_query_entityreference_alter(QueryAlterableInterface $qu function entityreference_field_widget_form(&$form, &$form_state, $field, $instance, $langcode, $items, $delta, $element) { // Ensure that the entity target type exists before displaying the widget. $entity_info = entity_get_info($field['settings']['target_type']); - if (empty($entity_info)){ + if (empty($entity_info)) { return; } $entity_type = $instance['entity_type']; @@ -820,7 +863,9 @@ function entityreference_field_widget_form(&$form, &$form_state, $field, $instan // Build an array of entities ID. foreach ($items as $item) { - $entity_ids[] = $item['target_id']; + if (isset($item['target_id'])) { + $entity_ids[] = $item['target_id']; + } } // Load those entities and loop through them to extract their labels. @@ -881,6 +926,9 @@ function entityreference_field_widget_form(&$form, &$form_state, $field, $instan } } +/** + * Implements hook_validate(). + */ function _entityreference_autocomplete_validate($element, &$form_state, $form) { // If a value was entered into the autocomplete... $value = ''; @@ -905,6 +953,9 @@ function _entityreference_autocomplete_validate($element, &$form_state, $form) { form_set_value($element, $value, $form_state); } +/** + * Implements hook_validate(). + */ function _entityreference_autocomplete_tags_validate($element, &$form_state, $form) { $value = array(); // If a value was entered into the autocomplete... @@ -951,7 +1002,8 @@ function entityreference_field_widget_error($element, $error) { * The entity type. * @param $bundle_name * The bundle name. - * @return + * + * @return bool * True if user can access this menu item. */ function entityreference_autocomplete_access_callback($type, $field_name, $entity_type, $bundle_name) { @@ -983,10 +1035,11 @@ function entityreference_autocomplete_access_callback($type, $field_name, $entit */ function entityreference_autocomplete_callback($type, $field_name, $entity_type, $bundle_name, $entity_id = '', $string = '') { // If the request has a '/' in the search text, then the menu system will have - // split it into multiple arguments and $string will only be a partial. We want - // to make sure we recover the intended $string. + // split it into multiple arguments and $string will only be a partial. + // We want to make sure we recover the intended $string. $args = func_get_args(); - // Shift off the $type, $field_name, $entity_type, $bundle_name, and $entity_id args. + // Shift off the $type, $field_name, $entity_type, + // $bundle_name, and $entity_id args. array_shift($args); array_shift($args); array_shift($args); @@ -1022,6 +1075,7 @@ function entityreference_autocomplete_callback($type, $field_name, $entity_type, */ function entityreference_autocomplete_callback_get_matches($type, $field, $instance, $entity_type, $entity_id = '', $string = '') { $matches = array(); + $prefix = ''; $entity = NULL; if ($entity_id !== 'NULL') { @@ -1036,7 +1090,8 @@ function entityreference_autocomplete_callback_get_matches($type, $field, $insta $handler = entityreference_get_selection_handler($field, $instance, $entity_type, $entity); if ($type == 'tags') { - // The user enters a comma-separated list of tags. We only autocomplete the last tag. + // The user enters a comma-separated list of tags. + // We only autocomplete the last tag. $tags_typed = drupal_explode_tags($string); $tag_last = drupal_strtolower(array_pop($tags_typed)); if (!empty($tag_last)) { @@ -1045,7 +1100,6 @@ function entityreference_autocomplete_callback_get_matches($type, $field, $insta } else { // The user enters a single tag. - $prefix = ''; $tag_last = $string; } @@ -1057,7 +1111,7 @@ function entityreference_autocomplete_callback_get_matches($type, $field, $insta foreach ($entity_labels as $values) { foreach ($values as $entity_id => $label) { $key = "$label ($entity_id)"; - // Strip things like starting/trailing white spaces, line breaks and tags. + // Strip starting/trailing white spaces, line breaks and tags. $key = preg_replace('/\s\s+/', ' ', str_replace("\n", '', trim(decode_entities(strip_tags($key))))); // Names containing commas or quotes must be wrapped in quotes. if (strpos($key, ',') !== FALSE || strpos($key, '"') !== FALSE) { @@ -1071,6 +1125,32 @@ function entityreference_autocomplete_callback_get_matches($type, $field, $insta drupal_json_output($matches); } + /** + * Introspects field and instance settings, and determines the correct settings + * for the functioning of the formatter. + * + * Settings: + * - entity_type - The entity_type being loaded. + * - column - The name of the ref. field column that stores the entity id. + */ +function entityreference_field_type_settings($field) { + $settings = array( + 'entity_type' => NULL, + 'column' => NULL, + ); + + if ($field['type'] == 'entityreference') { + $settings['entity_type'] = $field['settings']['target_type']; + $settings['column'] = 'target_id'; + } + elseif ($field['type'] == 'taxonomy_term_reference') { + $settings['entity_type'] = 'taxonomy_term'; + $settings['column'] = 'tid'; + } + + return $settings; +} + /** * Implements hook_field_formatter_info(). */ @@ -1082,6 +1162,7 @@ function entityreference_field_formatter_info() { 'field types' => array('entityreference'), 'settings' => array( 'link' => FALSE, + 'bypass_access' => FALSE, ), ), 'entityreference_entity_id' => array( @@ -1092,7 +1173,7 @@ function entityreference_field_formatter_info() { 'entityreference_entity_view' => array( 'label' => t('Rendered entity'), 'description' => t('Display the referenced entities rendered by entity_view().'), - 'field types' => array('entityreference'), + 'field types' => array('entityreference', 'taxonomy_term_reference'), 'settings' => array( 'view_mode' => 'default', 'links' => TRUE, @@ -1108,9 +1189,17 @@ function entityreference_field_formatter_info() { function entityreference_field_formatter_settings_form($field, $instance, $view_mode, $form, &$form_state) { $display = $instance['display'][$view_mode]; $settings = $display['settings']; + $field_type_settings = entityreference_field_type_settings($field); $element = array(); if ($display['type'] == 'entityreference_label') { + $element['bypass_access'] = array( + '#title' => t('Show entity labels regardless of user access'), + '#description' => t("All entities in the field will be shown, without checking them for access. If the 'Link' setting is also enabled, an entity which the user does not have access to view will show without a link."), + '#type' => 'checkbox', + '#default_value' => $settings['bypass_access'], + ); + $element['link'] = array( '#title' => t('Link label to the referenced entity'), '#type' => 'checkbox', @@ -1119,7 +1208,7 @@ function entityreference_field_formatter_settings_form($field, $instance, $view_ } if ($display['type'] == 'entityreference_entity_view') { - $entity_info = entity_get_info($field['settings']['target_type']); + $entity_info = entity_get_info($field_type_settings['entity_type']); $options = array('default' => t('Default')); if (!empty($entity_info['view modes'])) { foreach ($entity_info['view modes'] as $view_mode => $view_mode_settings) { @@ -1157,15 +1246,17 @@ function entityreference_field_formatter_settings_form($field, $instance, $view_ function entityreference_field_formatter_settings_summary($field, $instance, $view_mode) { $display = $instance['display'][$view_mode]; $settings = $display['settings']; + $field_type_settings = entityreference_field_type_settings($field); $summary = array(); if ($display['type'] == 'entityreference_label') { $summary[] = $settings['link'] ? t('Link to the referenced entity') : t('No link'); + $summary[] = $settings['bypass_access'] ? t('Show labels regardless of access') : t('Respect entity access for label visibility'); } if ($display['type'] == 'entityreference_entity_view') { - $entity_info = entity_get_info($field['settings']['target_type']); + $entity_info = entity_get_info($field_type_settings['entity_type']); $view_mode_label = $settings['view_mode'] == 'default' ? t('Default') : $settings['view_mode']; if (isset($entity_info['view modes'][$settings['view_mode']]['label'])) { $view_mode_label = $entity_info['view modes'][$settings['view_mode']]['label']; @@ -1182,19 +1273,22 @@ function entityreference_field_formatter_settings_summary($field, $instance, $vi * Implements hook_field_formatter_prepare_view(). */ function entityreference_field_formatter_prepare_view($entity_type, $entities, $field, $instances, $langcode, &$items, $displays) { + $field_type_settings = entityreference_field_type_settings($field); + $target_type = $field_type_settings['entity_type']; + $column = $field_type_settings['column']; $target_ids = array(); // Collect every possible entity attached to any of the entities. foreach ($entities as $id => $entity) { foreach ($items[$id] as $delta => $item) { - if (isset($item['target_id'])) { - $target_ids[] = $item['target_id']; + if (isset($item[$column])) { + $target_ids[] = $item[$column]; } } } if ($target_ids) { - $target_entities = entity_load($field['settings']['target_type'], $target_ids); + $target_entities = entity_load($target_type, $target_ids); } else { $target_entities = array(); @@ -1206,12 +1300,12 @@ function entityreference_field_formatter_prepare_view($entity_type, $entities, $ foreach ($items[$id] as $delta => $item) { // Check whether the referenced entity could be loaded. - if (isset($item['target_id']) && isset($target_entities[$item['target_id']])) { + if (isset($target_entities[$item[$column]]) && isset($target_entities[$item[$column]])) { // Replace the instance value with the term data. - $items[$id][$delta]['entity'] = $target_entities[$item['target_id']]; + $items[$id][$delta]['entity'] = $target_entities[$item[$column]]; // Check whether the user has access to the referenced entity. - $has_view_access = (entity_access('view', $field['settings']['target_type'], $target_entities[$item['target_id']]) !== FALSE); - $has_update_access = (entity_access('update', $field['settings']['target_type'], $target_entities[$item['target_id']]) !== FALSE); + $has_view_access = (entity_access('view', $target_type, $target_entities[$item[$column]]) !== FALSE); + $has_update_access = (entity_access('update', $target_type, $target_entities[$item[$column]]) !== FALSE); $items[$id][$delta]['access'] = ($has_view_access || $has_update_access); } // Otherwise, unset the instance value, since the entity does not exist. @@ -1234,34 +1328,63 @@ function entityreference_field_formatter_prepare_view($entity_type, $entities, $ function entityreference_field_formatter_view($entity_type, $entity, $field, $instance, $langcode, $items, $display) { $result = array(); $settings = $display['settings']; - - // Rebuild the items list to contain only those with access. - foreach ($items as $key => $item) { - if (empty($item['access'])) { - unset($items[$key]); - } - } + $field_type_settings = entityreference_field_type_settings($field); + $target_type = $field_type_settings['entity_type']; + $column = $field_type_settings['column']; switch ($display['type']) { case 'entityreference_label': $handler = entityreference_get_selection_handler($field, $instance, $entity_type, $entity); foreach ($items as $delta => $item) { - $label = $handler->getLabel($item['entity']); - // If the link is to be displayed and the entity has a uri, display a link. - // Note the assignment ($url = ) here is intended to be an assignment. - if ($display['settings']['link'] && ($uri = entity_uri($field['settings']['target_type'], $item['entity']))) { - $result[$delta] = array('#markup' => l($label, $uri['path'], $uri['options'])); + // Skip an item that is not accessible, unless we're allowing output of + // entity labels without considering access. + if (empty($item['access']) && !$display['settings']['bypass_access']) { + continue; } - else { - $result[$delta] = array('#markup' => check_plain($label)); + + // Calling EntityReferenceHandler::getLabel() would make a repeated, + // wasteful call to entity_access(). + $label = entity_label($field['settings']['target_type'], $item['entity']); + + // Check if the settings and access allow a link to be displayed. + $display_link = $display['settings']['link'] && $item['access']; + + $uri = NULL; + + // If the link is allowed and the entity has a uri, display a link. + if ($display_link) { + $uri = entity_uri($target_type, $item['entity']); } + + $result[$delta] = array( + '#theme' => 'entityreference_label', + '#label' => $label, + '#item' => $item, + '#uri' => $uri, + '#settings' => array( + 'display' => $display['settings'], + 'field' => $field['settings'], + ), + ); } break; case 'entityreference_entity_id': foreach ($items as $delta => $item) { - $result[$delta] = array('#markup' => check_plain($item['target_id'])); + // Skip an item that is not accessible. + if (empty($item['access'])) { + continue; + } + + $result[$delta] = array( + '#theme' => 'entityreference_entity_id', + '#item' => $item, + '#settings' => array( + 'display' => $display['settings'], + 'field' => $field['settings'], + ), + ); } break; @@ -1272,19 +1395,24 @@ function entityreference_field_formatter_view($entity_type, $entity, $field, $in } foreach ($items as $delta => $item) { + // Skip an item that is not accessible. + if (empty($item['access'])) { + continue; + } + // Protect ourselves from recursive rendering. static $depth = 0; $depth++; if ($depth > 20) { - throw new EntityReferenceRecursiveRenderingException(t('Recursive rendering detected when rendering entity @entity_type(@entity_id). Aborting rendering.', array('@entity_type' => $entity_type, '@entity_id' => $item['target_id']))); + throw new EntityReferenceRecursiveRenderingException(t('Recursive rendering detected when rendering entity @entity_type(@entity_id). Aborting rendering.', array('@entity_type' => $target_type, '@entity_id' => $item[$column]))); } $target_entity = clone $item['entity']; unset($target_entity->content); - $result[$delta] = entity_view($field['settings']['target_type'], array($item['target_id'] => $target_entity), $settings['view_mode'], $target_langcode, FALSE); + $result[$delta] = entity_view($target_type, array($item[$column] => $target_entity), $settings['view_mode'], $target_langcode, FALSE); - if (empty($settings['links']) && isset($result[$delta][$field['settings']['target_type']][$item['target_id']]['links'])) { - $result[$delta][$field['settings']['target_type']][$item['target_id']]['links']['#access'] = FALSE; + if (empty($settings['links']) && isset($result[$delta][$target_type][$column]['links'])) { + $result[$delta][$target_type][$item[$column]]['links']['#access'] = FALSE; } $depth = 0; } @@ -1308,3 +1436,44 @@ function entityreference_views_api() { 'path' => drupal_get_path('module', 'entityreference') . '/views', ); } + +/** + * Theme label. + * + * @ingroup themeable. + */ +function theme_entityreference_label($vars) { + $label = $vars['label']; + $settings = $vars['settings']; + $item = $vars['item']; + $uri = $vars['uri']; + + $output = ''; + + // If the link is to be displayed and the entity has a uri, display a link. + // Note the assignment ($url = ) here is intended to be an assignment. + if ($settings['display']['link'] && isset($uri['path'])) { + $output .= l($label, $uri['path'], $uri['options']); + } + else { + $output .= check_plain($label); + } + + return $output; +} + +/** + * Theme entity_id + * + * @ingroup themeable. + */ +function theme_entityreference_entity_id($vars) { + $settings = $vars['settings']; + $item = $vars['item']; + + $output = ''; + + $output = check_plain($item['target_id']); + + return $output; +} diff --git a/sites/all/modules/contrib/entityreference/examples/entityreference_behavior_example/entityreference_behavior_example.info b/sites/all/modules/contrib/entityreference/examples/entityreference_behavior_example/entityreference_behavior_example.info index 33fe2036e..6f21bac57 100644 --- a/sites/all/modules/contrib/entityreference/examples/entityreference_behavior_example/entityreference_behavior_example.info +++ b/sites/all/modules/contrib/entityreference/examples/entityreference_behavior_example/entityreference_behavior_example.info @@ -4,9 +4,9 @@ core = 7.x package = Fields dependencies[] = entityreference -; Information added by Drupal.org packaging script on 2016-09-19 -version = "7.x-1.2" +; Information added by Drupal.org packaging script on 2017-05-23 +version = "7.x-1.4" core = "7.x" project = "entityreference" -datestamp = "1474306740" +datestamp = "1495557187" diff --git a/sites/all/modules/contrib/entityreference/plugins/behavior/EntityReferenceBehavior_TaxonomyIndex.class.php b/sites/all/modules/contrib/entityreference/plugins/behavior/EntityReferenceBehavior_TaxonomyIndex.class.php index 43ac693f2..075b54d79 100644 --- a/sites/all/modules/contrib/entityreference/plugins/behavior/EntityReferenceBehavior_TaxonomyIndex.class.php +++ b/sites/all/modules/contrib/entityreference/plugins/behavior/EntityReferenceBehavior_TaxonomyIndex.class.php @@ -144,18 +144,20 @@ protected function buildNodeIndex($node) { // already inserted in taxonomy_build_node_index(). $tid_all = array_diff($tid_all, $original_tid_all); - // Insert index entries for all the node's terms. + // Insert index entries for all the node's terms, preventing duplicates. if (!empty($tid_all)) { - $query = db_insert('taxonomy_index')->fields(array('nid', 'tid', 'sticky', 'created')); foreach ($tid_all as $tid) { - $query->values(array( + $row = array( 'nid' => $node->nid, 'tid' => $tid, 'sticky' => $sticky, 'created' => $node->created, - )); + ); + $query = db_merge('taxonomy_index') + ->key($row) + ->fields($row); + $query->execute(); } - $query->execute(); } } } diff --git a/sites/all/modules/contrib/entityreference/plugins/selection/EntityReference_SelectionHandler_Generic.class.php b/sites/all/modules/contrib/entityreference/plugins/selection/EntityReference_SelectionHandler_Generic.class.php index 902c55ced..e0dff00bb 100644 --- a/sites/all/modules/contrib/entityreference/plugins/selection/EntityReference_SelectionHandler_Generic.class.php +++ b/sites/all/modules/contrib/entityreference/plugins/selection/EntityReference_SelectionHandler_Generic.class.php @@ -208,7 +208,11 @@ public function validateReferencableEntities(array $ids) { * Implements EntityReferenceHandler::validateAutocompleteInput(). */ public function validateAutocompleteInput($input, &$element, &$form_state, $form) { - $entities = $this->getReferencableEntities($input, '=', 6); + $bundled_entities = $this->getReferencableEntities($input, '=', 6); + $entities = array(); + foreach($bundled_entities as $entities_list) { + $entities += $entities_list; + } if (empty($entities)) { // Error if there are no entities available for a required field. form_error($element, t('There are no entities matching "%value"', array('%value' => $input))); diff --git a/sites/all/modules/contrib/entityreference/plugins/selection/EntityReference_SelectionHandler_Views.class.php b/sites/all/modules/contrib/entityreference/plugins/selection/EntityReference_SelectionHandler_Views.class.php index 859e1e133..cbed33b17 100644 --- a/sites/all/modules/contrib/entityreference/plugins/selection/EntityReference_SelectionHandler_Views.class.php +++ b/sites/all/modules/contrib/entityreference/plugins/selection/EntityReference_SelectionHandler_Views.class.php @@ -9,12 +9,13 @@ class EntityReference_SelectionHandler_Views implements EntityReference_Selectio * Implements EntityReferenceHandler::getInstance(). */ public static function getInstance($field, $instance = NULL, $entity_type = NULL, $entity = NULL) { - return new EntityReference_SelectionHandler_Views($field, $instance); + return new EntityReference_SelectionHandler_Views($field, $instance, $entity); } - protected function __construct($field, $instance) { + protected function __construct($field, $instance, $entity) { $this->field = $field; $this->instance = $instance; + $this->entity = $entity; } /** @@ -52,13 +53,32 @@ public static function settingsForm($field, $instance) { ); $default = !empty($view_settings['args']) ? implode(', ', $view_settings['args']) : ''; + $description = t('Provide a comma separated list of arguments to pass to the view.') . '
' . t('This field supports tokens.'); + + if (!module_exists('token')) { + $description .= '
' . t('Install the token module to get more tokens and display available once.', array('@url' => 'http://drupal.org/project/token')); + } + $form['view']['args'] = array( '#type' => 'textfield', '#title' => t('View arguments'), '#default_value' => $default, '#required' => FALSE, - '#description' => t('Provide a comma separated list of arguments to pass to the view.'), + '#description' => $description, + '#maxlength' => '512', ); + if (module_exists('token')) { + // Get the token type for the entity type our field is in (a type 'taxonomy_term' has a 'term' type token). + $info = entity_get_info($instance['entity_type']); + + $form['view']['tokens'] = array( + '#theme' => 'token_tree', + '#token_types' => array($info['token type']), + '#global_types' => TRUE, + '#click_insert' => TRUE, + '#dialog' => TRUE, + ); + } } else { $form['view']['no_view_help'] = array( @@ -84,6 +104,7 @@ protected function initializeView($match = NULL, $match_operator = 'CONTAINS', $ return FALSE; } $this->view->set_display($display_name); + $this->view->pre_execute(); // Make sure the query is not cached. $this->view->is_cacheable = FALSE; @@ -104,7 +125,7 @@ protected function initializeView($match = NULL, $match_operator = 'CONTAINS', $ */ public function getReferencableEntities($match = NULL, $match_operator = 'CONTAINS', $limit = 0) { $display_name = $this->field['settings']['handler_settings']['view']['display_name']; - $args = $this->field['settings']['handler_settings']['view']['args']; + $args = $this->handleArgs($this->field['settings']['handler_settings']['view']['args']); $result = array(); if ($this->initializeView($match, $match_operator, $limit)) { // Get the results. @@ -133,7 +154,7 @@ function countReferencableEntities($match = NULL, $match_operator = 'CONTAINS') function validateReferencableEntities(array $ids) { $display_name = $this->field['settings']['handler_settings']['view']['display_name']; - $args = $this->field['settings']['handler_settings']['view']['args']; + $args = $this->handleArgs($this->field['settings']['handler_settings']['view']['args']); $result = array(); if ($this->initializeView(NULL, 'CONTAINS', 0, $ids)) { // Get the results. @@ -166,6 +187,49 @@ public function entityFieldQueryAlter(SelectQueryInterface $query) { } + /** + * Handles arguments for views. + * + * Replaces tokens using token_replace(). + * + * @param array $args + * Usually $this->field['settings']['handler_settings']['view']['args']. + * + * @return array + * The arguments to be send to the View. + */ + protected function handleArgs($args) { + if (!module_exists('token')) { + return $args; + } + + // Parameters for token_replace(). + $data = array(); + $options = array('clear' => TRUE); + + if ($entity = $this->entity) { + // D7 HACK: For new entities, entity and revision id are not set. This leads to + // * token replacement emitting PHP warnings + // * views choking on empty arguments + // We workaround this by filling in '0' for these IDs + // and use a clone to leave no traces of our unholy doings. + $info = entity_get_info($this->instance['entity_type']); + if (!isset($entity->{$info['entity keys']['id']})) { + $entity = clone $entity; + $entity->{$info['entity keys']['id']} = '0'; + if (!empty($info['entity keys']['revision'])) { + $entity->{$info['entity keys']['revision']} = '0'; + } + } + + $data[$info['token type']] = $entity; + } + // Replace tokens for each argument. + foreach ($args as $key => $arg) { + $args[$key] = token_replace($arg, $data, $options); + } + return $args; + } } function entityreference_view_settings_validate($element, &$form_state, $form) { diff --git a/sites/all/modules/contrib/entityreference/plugins/selection/abstract.inc b/sites/all/modules/contrib/entityreference/plugins/selection/abstract.inc index 1d2ea0d30..a4805b1f5 100644 --- a/sites/all/modules/contrib/entityreference/plugins/selection/abstract.inc +++ b/sites/all/modules/contrib/entityreference/plugins/selection/abstract.inc @@ -21,8 +21,9 @@ interface EntityReference_SelectionHandler { * Return a list of referencable entities. * * @return - * An array of referencable entities, which keys are entity ids and - * values (safe HTML) labels to be displayed to the user. + * A nested array of entities, the first level is keyed by the + * entity bundle, which contains an array of entity labels (safe HTML), + * keyed by the entity ID. */ public function getReferencableEntities($match = NULL, $match_operator = 'CONTAINS', $limit = 0); diff --git a/sites/all/modules/contrib/entityreference/tests/entityreference.admin.test b/sites/all/modules/contrib/entityreference/tests/entityreference.admin.test index 6c886975e..ec78e7bca 100644 --- a/sites/all/modules/contrib/entityreference/tests/entityreference.admin.test +++ b/sites/all/modules/contrib/entityreference/tests/entityreference.admin.test @@ -68,7 +68,7 @@ class EntityReferenceAdminTestCase extends DrupalWebTestCase { 'fields[_add_new_field][type]' => 'entityreference', 'fields[_add_new_field][widget_type]' => 'entityreference_autocomplete', ), t('Save')); - return; + // Node should be selected by default. $this->assertFieldByName('field[settings][target_type]', 'node'); // The base handler should be selected by default. diff --git a/sites/all/modules/contrib/entityreference/tests/entityreference.handlers.test b/sites/all/modules/contrib/entityreference/tests/entityreference.handlers.test index b88e10693..22569324c 100644 --- a/sites/all/modules/contrib/entityreference/tests/entityreference.handlers.test +++ b/sites/all/modules/contrib/entityreference/tests/entityreference.handlers.test @@ -194,6 +194,21 @@ class EntityReferenceHandlersTestCase extends DrupalWebTestCase { ), ); $this->assertReferencable($field, $referencable_tests, 'Node handler (admin)'); + + // Verify autocomplete input validation. + $handler = entityreference_get_selection_handler($field); + $element = array( + '#parents' => array('element_name'), + ); + $form_state = array(); + $form = array(); + $value = $handler->validateAutocompleteInput($nodes['published1']->title, $element, $form_state, $form); + $this->assertEqual($value, $nodes['published1']->nid); + + $invalid_input = $this->randomName(); + $value = $handler->validateAutocompleteInput($invalid_input, $element, $form_state, $form); + $this->assertNull($value); + $this->assertEqual(form_get_error($element), t('There are no entities matching "%value"', array('%value' => $invalid_input))); } /** diff --git a/sites/all/modules/contrib/entityreference/tests/entityreference.taxonomy.test b/sites/all/modules/contrib/entityreference/tests/entityreference.taxonomy.test index 6e4afb780..94b3f5682 100644 --- a/sites/all/modules/contrib/entityreference/tests/entityreference.taxonomy.test +++ b/sites/all/modules/contrib/entityreference/tests/entityreference.taxonomy.test @@ -112,4 +112,52 @@ class EntityReferenceTaxonomyTestCase extends DrupalWebTestCase { $this->assertFalse(taxonomy_select_nodes(1)); } + /** + * Add a second ER field from node/article to taxonomy. + * + * This should not cause {taxonomy_index} to receive duplicate entries. + */ + protected function setupForIndexDuplicates() { + // Create an entity reference field. + $field = array( + 'entity_types' => array('node'), + 'settings' => array( + 'handler' => 'base', + 'target_type' => 'taxonomy_term', + 'handler_settings' => array( + 'target_bundles' => array(), + ), + ), + 'field_name' => 'field_entityreference_term2', + 'type' => 'entityreference', + ); + $field = field_create_field($field); + $instance = array( + 'field_name' => 'field_entityreference_term2', + 'bundle' => 'article', + 'entity_type' => 'node', + ); + + // Enable the taxonomy-index behavior. + $instance['settings']['behaviors']['taxonomy-index']['status'] = TRUE; + field_create_instance($instance); + } + + /** + * Make sure the index only contains one entry for a given node->term + * reference, even when multiple ER fields link from the node bundle to terms. + */ + public function testIndexDuplicates() { + // Extra setup for this test: add another ER field on this content type. + $this->setupForIndexDuplicates(); + + // Assert node insert with reference to term in first field. + $tid = 1; + $settings = array(); + $settings['type'] = 'article'; + $settings['field_entityreference_term'][LANGUAGE_NONE][0]['target_id'] = $tid; + $node = $this->drupalCreateNode($settings); + + $this->assertEqual(taxonomy_select_nodes($tid), array($node->nid)); + } } diff --git a/sites/all/modules/contrib/entityreference/tests/modules/entityreference_feeds_test/entityreference_feeds_test.info b/sites/all/modules/contrib/entityreference/tests/modules/entityreference_feeds_test/entityreference_feeds_test.info index 6a7a2ec60..dace68b70 100644 --- a/sites/all/modules/contrib/entityreference/tests/modules/entityreference_feeds_test/entityreference_feeds_test.info +++ b/sites/all/modules/contrib/entityreference/tests/modules/entityreference_feeds_test/entityreference_feeds_test.info @@ -8,9 +8,9 @@ dependencies[] = feeds dependencies[] = feeds_ui dependencies[] = entityreference -; Information added by Drupal.org packaging script on 2016-09-19 -version = "7.x-1.2" +; Information added by Drupal.org packaging script on 2017-05-23 +version = "7.x-1.4" core = "7.x" project = "entityreference" -datestamp = "1474306740" +datestamp = "1495557187" diff --git a/sites/all/modules/contrib/entityreference/views/entityreference_plugin_display.inc b/sites/all/modules/contrib/entityreference/views/entityreference_plugin_display.inc index f13e88a86..cca365090 100644 --- a/sites/all/modules/contrib/entityreference/views/entityreference_plugin_display.inc +++ b/sites/all/modules/contrib/entityreference/views/entityreference_plugin_display.inc @@ -81,8 +81,9 @@ class entityreference_plugin_display extends views_plugin_display { $field = $this->view->query->fields[$this->view->field[$field_alias]->field_alias]; } else { - $this->view->query->add_field($this->view->field[$field_alias]->options['table'], $this->view->field[$field_alias]->real_field, $this->view->field[$field_alias]->options['field'], array()); - $field = $this->view->query->fields[$this->view->field[$field_alias]->options['field']]; + $field_table = $this->view->query->ensure_table($this->view->field[$field_alias]->table, $this->view->field[$field_alias]->relationship); + $this->view->query->add_field($field_table, $this->view->field[$field_alias]->real_field, $this->view->field[$field_alias]->field, array()); + $field = $this->view->query->fields[$this->view->field[$field_alias]->field]; } // Add an OR condition for the field $conditions->condition($field['table'] . '.' . $field['field'], $value, 'LIKE'); diff --git a/sites/all/modules/contrib/entityreference/views/entityreference_plugin_style.inc b/sites/all/modules/contrib/entityreference/views/entityreference_plugin_style.inc index fadaa9ee2..b72a23578 100644 --- a/sites/all/modules/contrib/entityreference/views/entityreference_plugin_style.inc +++ b/sites/all/modules/contrib/entityreference/views/entityreference_plugin_style.inc @@ -26,7 +26,7 @@ class entityreference_plugin_style extends views_plugin_style { '#title' => t('Search fields'), '#options' => $options, '#required' => TRUE, - '#default_value' => $this->options['search_fields'], + '#default_value' => isset($this->options['search_fields']) ? $this->options['search_fields'] : array(), '#description' => t('Select the field(s) that will be searched when using the autocomplete widget.'), '#weight' => -3, ); diff --git a/sites/all/modules/contrib/file_entity/file_entity.field.inc b/sites/all/modules/contrib/file_entity/file_entity.field.inc index 1701c88f5..66a5c490c 100644 --- a/sites/all/modules/contrib/file_entity/file_entity.field.inc +++ b/sites/all/modules/contrib/file_entity/file_entity.field.inc @@ -284,45 +284,58 @@ function file_entity_field_formatter_view($entity_type, $entity, $field, $instan foreach ($items as $delta => $item) { // Protect ourselves from recursive rendering. static $recursive_render_depth = array(); - $recursive_render_id = $entity_type . $field['field_name'] . $item['fid']; + if (!empty($item)) { + $recursive_render_id = $entity_type . $field['field_name'] . $item['fid']; + if (isset($recursive_render_depth[$recursive_render_id])) { + $recursive_render_depth[$recursive_render_id]++; + } + else { + $recursive_render_depth[$recursive_render_id] = 1; + } - if (isset($recursive_render_depth[$recursive_render_id])) { - $recursive_render_depth[$recursive_render_id]++; - } - else { - $recursive_render_depth[$recursive_render_id] = 1; - } + if ($recursive_render_depth[$recursive_render_id] > 20) { + watchdog( + 'file_entity', + 'Recursive rendering detected when rendering entity %entity_type: %entity_id, using the %field_name field. Aborting rendering.', + array( + '%entity_type' => 'file', + '%entity_id' => $item['fid'], + '%field_name' => $field['field_name'], + ), + WATCHDOG_ERROR + ); + return $element; + } + + $file = file_load($item['fid']); + + // Add some references to the referencing entity. + // @see https://www.drupal.org/node/2333107 + $file->referencing_entity = $entity; + $file->referencing_entity_type = $entity_type; + $file->referencing_field = $field['field_name']; - if ($recursive_render_depth[$recursive_render_id] > 20) { + $element[$delta] = file_view($file, $settings['file_view_mode'], $langcode); + } else { watchdog( 'file_entity', - 'Recursive rendering detected when rendering entity %entity_type: %entity_id, using the %field_name field. Aborting rendering.', + 'In the %referencing_entity_type, the %field_name field refers to a %entity_type which does not exist. Aborting the render for it.', array( - '%entity_type' => 'file', - '%entity_id' => $item['fid'], + '%referencing_entity_type' => $entity_type, '%field_name' => $field['field_name'], + '%entity_type' => 'file', ), WATCHDOG_ERROR ); - return $element; } - - $file = (object) $item; - - // Add some references to the referencing entity. - // @see https://www.drupal.org/node/2333107 - $file->referencing_entity = $entity; - $file->referencing_entity_type = $entity_type; - $file->referencing_field = $field['field_name']; - - $element[$delta] = file_view($file, $settings['file_view_mode'], $langcode); } break; case 'file_download_link': + // Prevent 'empty' fields from causing a WSOD. + $items = array_filter($items); foreach ($items as $delta => $item) { - $file = (object) $item; - if (file_entity_access('download', $file)) { + if (!empty($item['fid']) && ($file = file_load($item['fid'])) && file_entity_access('download', $file)) { $element[$delta] = array( '#theme' => 'file_entity_download_link', '#file' => $file, @@ -342,14 +355,14 @@ function file_entity_field_formatter_view($entity_type, $entity, $field, $instan $source_lists = array(); if ($multiple_file_behavior == 'tags') { foreach ($items as $delta => $item) { - if ($item['type'] == 'audio') { + if (file_entity_file_get_mimetype_type($item) == 'audio') { $source_lists[$delta] = array($item); } } } else { foreach ($items as $delta => $item) { - if ($item['type'] == 'audio') { + if (file_entity_file_get_mimetype_type($item) == 'audio') { $source_lists[0][$delta] = $item; } } @@ -378,14 +391,14 @@ function file_entity_field_formatter_view($entity_type, $entity, $field, $instan $source_lists = array(); if ($multiple_file_behavior == 'tags') { foreach ($items as $delta => $item) { - if ($item['type'] == 'video') { + if (file_entity_file_get_mimetype_type($item) == 'video') { $source_lists[$delta] = array($item); } } } else { foreach ($items as $delta => $item) { - if ($item['type'] == 'video') { + if (file_entity_file_get_mimetype_type($item) == 'video') { $source_lists[0][$delta] = $item; } } diff --git a/sites/all/modules/contrib/file_entity/file_entity.info b/sites/all/modules/contrib/file_entity/file_entity.info index 03afc1791..5476c7f1c 100644 --- a/sites/all/modules/contrib/file_entity/file_entity.info +++ b/sites/all/modules/contrib/file_entity/file_entity.info @@ -9,6 +9,7 @@ dependencies[] = ctools dependencies[] = system (>=7.33) test_dependencies[] = token +test_dependencies[] = views files[] = views/views_handler_argument_file_type.inc files[] = views/views_handler_field_file_rendered.inc @@ -24,15 +25,16 @@ files[] = views/views_handler_field_file_link_usage.inc files[] = views/views_plugin_row_file_rss.inc files[] = views/views_plugin_row_file_view.inc files[] = file_entity.test +files[] = file_entity_views.test configure = admin/config/media/file-settings ; We have to add a fake version so Git checkouts do not fail Media dependencies version = 7.x-2.x-dev -; Information added by Drupal.org packaging script on 2016-05-31 -version = "7.x-2.0-beta3" +; Information added by Drupal.org packaging script on 2017-05-22 +version = "7.x-2.0" core = "7.x" project = "file_entity" -datestamp = "1464653173" +datestamp = "1495456387" diff --git a/sites/all/modules/contrib/file_entity/file_entity.module b/sites/all/modules/contrib/file_entity/file_entity.module index 36f828308..bbd4d2f1c 100644 --- a/sites/all/modules/contrib/file_entity/file_entity.module +++ b/sites/all/modules/contrib/file_entity/file_entity.module @@ -1794,7 +1794,7 @@ function file_entity_query_file_access_alter(QueryAlterableInterface $query) { * access conditions are added for field values belonging to files only. */ function file_entity_query_entity_field_access_alter(QueryAlterableInterface $query) { - //_file_entity_query_file_entity_access_alter($query, 'entity'); + _file_entity_query_file_entity_access_alter($query, 'entity'); } /** @@ -1822,6 +1822,12 @@ function _file_entity_query_file_entity_access_alter($query, $type) { $tables = $query->getTables(); $base_table = $query->getMetaData('base_table'); + // Do not use the base table for general entity queries unless this is + // querying the file_managed table directly. + if ($base_table && $type == 'entity' && $base_table != 'file_managed') { + $base_table = ''; + } + // If no base table is specified explicitly, search for one. if (!$base_table) { $fallback = ''; @@ -1853,6 +1859,10 @@ function _file_entity_query_file_entity_access_alter($query, $type) { $fallback = $table; } } + elseif (isset($schema['fields']['entity_id']) && isset($schema['fields']['entity_type']) && isset($schema['fields']['deleted']) && isset($schema['fields']['delta'])) { + // The table is a field data table, use it as fallback. + $base_table = $table; + } } } } @@ -1863,6 +1873,11 @@ function _file_entity_query_file_entity_access_alter($query, $type) { $base_table = $fallback; } else { + // Ignore this query as it was a general field query and no + // relationships were found to the file_managed table. + if ($type == 'entity') { + return; + } throw new Exception(t('Query tagged for file access but there is no fid. Add foreign keys to file_managed.fid in schema to fix.')); } } @@ -1925,33 +1940,36 @@ function _file_entity_query_file_entity_access_alter($query, $type) { } } - if ($subquery_conditions->count()) { + // If there is no subquery conditions, the query is likely for file usage. + // Or user can only access public files. + // If there are subquery conditions then add them to the subquery. + if ($subquery_conditions->count() >= 1) { $subquery->condition($subquery_conditions); + } - $field = 'fid'; - // Now handle entities. - if ($type == 'entity') { - // Set a common alias for entities. - $base_alias = $falias; - $field = 'entity_id'; - } - $subquery->where("$falias.$field = fm_access.fid"); + $field = 'fid'; + // Now handle entities. + if ($type == 'entity') { + // Set a common alias for entities. + $base_alias = $falias; + $field = ($falias == 'file_managed' ? 'fid' : 'entity_id'); + } + $subquery->where("$falias.$field = fm_access.fid"); - // For an entity query, attach the subquery to entity conditions. - if ($type == 'entity') { - $file_conditions->exists($subquery); - } - // Otherwise attach it to the node query itself. - elseif ($table == 'file_managed') { - // Fix for https://drupal.org/node/2073085 - $db_or = db_or(); - $db_or->exists($subquery); - $db_or->isNull($falias . '.' . $field); - $query->condition($db_or); - } - else { - $query->exists($subquery); - } + // For an entity query, attach the subquery to entity conditions. + if ($type == 'entity') { + $file_conditions->exists($subquery); + } + // Otherwise attach it to the node query itself. + elseif ($table == 'file_managed') { + // Fix for https://drupal.org/node/2073085 + $db_or = db_or(); + $db_or->exists($subquery); + $db_or->isNull($falias . '.' . $field); + $query->condition($db_or); + } + else { + $query->exists($subquery); } } } @@ -1959,14 +1977,19 @@ function _file_entity_query_file_entity_access_alter($query, $type) { if ($type == 'entity' && $file_conditions->count()) { // All the file access conditions are only for field values belonging to // files. - $file_conditions->condition("$base_alias.entity_type", 'file'); - $or = db_or(); - $or->condition($file_conditions); - // If the field value belongs to a non-file entity type then this function - // does not do anything with it. - $or->condition("$base_alias.entity_type", 'file', '<>'); - // Add the compiled set of rules to the query. - $query->condition($or); + if ($base_alias !== 'file_managed') { + $file_conditions->condition("$base_alias.entity_type", 'file'); + $or = db_or(); + $or->condition($file_conditions); + // If the field value belongs to a non-file entity type then this function + // does not do anything with it. + $or->condition("$base_alias.entity_type", 'file', '<>'); + // Add the compiled set of rules to the query. + $query->condition($or); + } + else { + $query->condition($file_conditions); + } } } @@ -2479,6 +2502,9 @@ function file_entity_download_uri($file) { } function file_entity_file_get_mimetype_type($file) { + if (is_array($file)) { + $file = (object) $file; + } list($type, $subtype) = explode('/', $file->filemime, 2); return $type; } @@ -2600,3 +2626,4 @@ function file_entity_features_pipe_file_type_alter(&$pipe, $data, $export) { $pipe['variable'][] = "pathauto_file_{$file_type}_pattern"; } } + diff --git a/sites/all/modules/contrib/file_entity/file_entity.pages.inc b/sites/all/modules/contrib/file_entity/file_entity.pages.inc index 9d6e359bd..e10cfe614 100644 --- a/sites/all/modules/contrib/file_entity/file_entity.pages.inc +++ b/sites/all/modules/contrib/file_entity/file_entity.pages.inc @@ -35,10 +35,12 @@ function file_entity_download_page($file) { if (!is_file($file->uri)) { return MENU_NOT_FOUND; } - + // @todo Remove this once drupal_basename is fixed for PHP versions greater + // than '5.3.29'. + $basename_function = version_compare(PHP_VERSION,'5.3.29','>') ? 'basename' : 'drupal_basename'; $headers = array( 'Content-Type' => mime_header_encode($file->filemime), - 'Content-Disposition' => 'attachment; filename="' . mime_header_encode(drupal_basename($file->uri)) . '"', + 'Content-Disposition' => 'attachment; filename="' . mime_header_encode($basename_function($file->uri)) . '"', 'Content-Length' => $file->filesize, 'Content-Transfer-Encoding' => 'binary', 'Pragma' => 'no-cache', @@ -77,7 +79,10 @@ function file_entity_download_page($file) { * - Edit fields * Skip this step if there are no fields on this entity type. */ -function file_entity_add_upload($form, &$form_state, array $options = array()) { +function file_entity_add_upload($form, &$form_state, $options = array()) { + if (!is_array($options)) { + $options = array($options); + } $step = (isset($form_state['step']) && in_array($form_state['step'], array(1, 2, 3, 4))) ? $form_state['step'] : 1; $form['#step'] = $step; $form['#options'] = $options; @@ -398,6 +403,7 @@ function file_entity_add_upload_submit($form, &$form_state) { // We have the filetype, check if we can skip step 4. if (($form['#step'] == 3 && $trigger == 'edit-next')) { $file = file_load($form_state['storage']['upload']); + $form_state['file'] = $file; if (!field_info_instances('file', $form_state['storage']['type'])) { // This filetype doesn't have fields, save the file. $save = TRUE; @@ -800,21 +806,25 @@ function file_entity_edit_submit($form, &$form_state) { if (!empty($form_state['values']['replace_upload'])) { $replacement = $form_state['values']['replace_upload']; // Move file from temp to permanent home. - $destination_uri = rtrim($file->uri, drupal_basename($file->uri)) . drupal_basename($replacement->uri); - $replace_mode = $destination_uri == $file->uri ? FILE_EXISTS_REPLACE : FILE_EXISTS_RENAME; - if ($new_file_uri = file_unmanaged_copy($replacement->uri, $destination_uri, $replace_mode)) { - // @todo Add watchdog() about replaced file here? - - // Remove temporary file. - file_delete($replacement); - - // Update if the uri target has changed. - if ($new_file_uri != $file->uri) { - // Store the original file uri to delete if save is successful. - $orphaned_uri = $file->uri; - - // Update file entity uri. - $file->uri = $new_file_uri; + if (pathinfo($replacement->uri, PATHINFO_EXTENSION) == pathinfo($file->uri, PATHINFO_EXTENSION)) { + file_unmanaged_copy($replacement->uri, $file->uri, FILE_EXISTS_REPLACE); + } else { + $destination_uri = rtrim($file->uri, drupal_basename($file->uri)) . drupal_basename($replacement->uri); + $replace_mode = $destination_uri == $file->uri ? FILE_EXISTS_REPLACE : FILE_EXISTS_RENAME; + if ($new_file_uri = file_unmanaged_copy($replacement->uri, $destination_uri, $replace_mode)) { + // @todo Add watchdog() about replaced file here? + + // Remove temporary file. + file_delete($replacement); + + // Update if the uri target has changed. + if ($new_file_uri != $file->uri) { + // Store the original file uri to delete if save is successful. + $orphaned_uri = $file->uri; + + // Update file entity uri. + $file->uri = $new_file_uri; + } } } } diff --git a/sites/all/modules/contrib/file_entity/file_entity.pathauto.inc b/sites/all/modules/contrib/file_entity/file_entity.pathauto.inc index 3b3d6957e..98befe3ac 100644 --- a/sites/all/modules/contrib/file_entity/file_entity.pathauto.inc +++ b/sites/all/modules/contrib/file_entity/file_entity.pathauto.inc @@ -27,7 +27,7 @@ function file_entity_pathauto($op) { $settings['token_type'] = 'file'; $settings['groupheader'] = t('File paths'); $settings['patterndescr'] = t('Default path pattern (applies to all file types with blank patterns below)'); - $settings['patterndefault'] = 'files/[file:name]'; + $settings['patterndefault'] = ''; $settings['batch_update_callback'] = 'file_entity_pathauto_bulk_update_batch_process'; $settings['batch_file'] = drupal_get_path('module', 'file_entity') . '/file_entity.pathauto.inc'; diff --git a/sites/all/modules/contrib/file_entity/file_entity.test b/sites/all/modules/contrib/file_entity/file_entity.test index 20f47cabb..96a451023 100644 --- a/sites/all/modules/contrib/file_entity/file_entity.test +++ b/sites/all/modules/contrib/file_entity/file_entity.test @@ -1205,7 +1205,7 @@ class FileEntityTypeTestCase extends FileEntityTestHelper { $this->drupalGet('admin/structure/file-types'); $this->assertResponse(403, 'File types UI page is not accessible to unauthorized users.'); - $user = $this->drupalCreateUser(array('administer file types')); + $user = $this->drupalCreateUser(array('administer file types', 'administer fields')); $this->drupalLogin($user); $this->drupalGet('admin/structure/file-types'); @@ -1671,4 +1671,132 @@ class FileEntityAttributeOverrideTestCase extends FileEntityTestHelper { $this->assertEqual($build['#file']->$attribute, $expected_value, format_string('The %attribute was overridden correctly.', array('%attribute' => $attribute))); } } + + /** + * @param EntityFieldQuery $query + * @param $expected + * An associative array of expected result. Keys are file ids, values are + * booleans to indicate if the result should include the file. + */ + function assertEntityFieldQueryAccess(EntityFieldQuery $query, $expected, $account = NULL, $query_name = 'unnamed') { + if ($account) { + $query->addMetaData('account', $account); + } + $query->addTag('entity_field_access'); + $results = $query->execute(); + $fids = isset($results['file']) ? array_keys($results['file']) : array(); + foreach ($expected as $fid => $in_result) { + if ($in_result) { + $this->assertTrue(in_array($fid, $fids), format_string("For the %name query, the result should contain %fid", array('%name' => $query_name, '%fid' => $fid))); + } + else { + $this->assertFalse(in_array($fid, $fids), format_string("For the %name query, the result should not contain %fid", array('%name' => $query_name, '%fid' => $fid))); + } + } + } + + /** + * Test file entity access for entity field queries. + */ + function testEntityFieldQueryAccess() { + // Attach a text field to the default image file type. + $field = array( + 'field_name' => drupal_strtolower($this->randomName()), + 'type' => 'text', + 'settings' => array( + 'max_length' => 255, + ) + ); + field_create_field($field); + $instance = array( + 'field_name' => $field['field_name'], + 'entity_type' => 'file', + 'bundle' => 'document', + 'widget' => array( + 'type' => 'text_textfield', + ), + 'display' => array( + 'default' => array( + 'type' => 'text_default', + ), + ), + ); + field_create_instance($instance); + // Create test files. + $file_owner = $this->drupalCreateUser(array('view own files', 'view own private files')); + $public_file = $this->createFileEntity(array( + 'status' => 0, + )); + $private_file = $this->createFileEntity(array('scheme' => 'private')); + $owned_public_file = $this->createFileEntity(array( + 'uid' => $file_owner->uid, + 'scheme' => 'public', + )); + $owned_private_file = $this->createFileEntity(array( + 'uid' => $file_owner->uid, + 'scheme' => 'private', + )); + $fids = array( + $public_file->fid, + $private_file->fid, + $owned_public_file->fid, + $owned_private_file->fid, + ); + foreach (file_load_multiple($fids) as $file) { + $file->{$field['field_name']}[LANGUAGE_NONE][0] = array('value' => 'find me'); + file_save($file); + } + + $efq_fids = new EntityFieldQuery(); + $queries['entity type and id conditions'] = $efq_fids + ->entityCondition('entity_type', 'file') + ->entityCondition('entity_id', $fids); + $efq_field_name = new EntityFieldQuery(); + $queries['single field condition'] = $efq_field_name + ->fieldCondition($field['field_name'], 'value', 'find me'); + + foreach($queries as $name => $query) { + $message = format_string(''); + // User should not see private files, only his own public files. + $this->assertEntityFieldQueryAccess(clone $query, array( + $public_file->fid => TRUE, + $private_file->fid => FALSE, + $owned_public_file->fid => TRUE, + $owned_private_file->fid => FALSE, + ), $this->drupalCreateUser(array('create files')), $name); + + // A user with the 'view own files' and 'view own private files' permissions should only see owned files and public files. + $this->drupalLogin($file_owner); + $this->assertEntityFieldQueryAccess(clone $query, array( + $public_file->fid => TRUE, + $private_file->fid => FALSE, + $owned_public_file->fid => TRUE, + $owned_private_file->fid => TRUE, + ), $file_owner, $name); + + // User with the 'view files' permission should only see public files but cannot create files. + $this->assertEntityFieldQueryAccess(clone $query, array( + $public_file->fid => TRUE, + $private_file->fid => FALSE, + $owned_public_file->fid => TRUE, + $owned_private_file->fid => FALSE, + ), $this->drupalCreateUser(array('view files')), $name); + + // User with the 'view files' and 'view private files' permissions should only see all files. + $this->assertEntityFieldQueryAccess(clone $query, array( + $public_file->fid => TRUE, + $private_file->fid => TRUE, + $owned_public_file->fid => TRUE, + $owned_private_file->fid => TRUE, + ), $this->drupalCreateUser(array('view files', 'view private files')), $name); + + // User with the 'bypass file access' permissions should only see all files. + $this->assertEntityFieldQueryAccess(clone $query, array( + $public_file->fid => TRUE, + $private_file->fid => TRUE, + $owned_public_file->fid => TRUE, + $owned_private_file->fid => TRUE, + ), $this->drupalCreateUser(array('bypass file access')), $name); + } + } } diff --git a/sites/all/modules/contrib/file_entity/file_entity.theme.inc b/sites/all/modules/contrib/file_entity/file_entity.theme.inc index c05d33201..aa78d5d22 100644 --- a/sites/all/modules/contrib/file_entity/file_entity.theme.inc +++ b/sites/all/modules/contrib/file_entity/file_entity.theme.inc @@ -12,18 +12,37 @@ */ function theme_file_entity_file_link($variables) { $file = $variables['file']; - $icon_directory = $variables['icon_directory']; + $uri = entity_uri('file', $file); + + // Human-readable names, for use as text-alternatives to icons. + $mime_name = array( + 'application/msword' => t('Microsoft Office document icon'), + 'application/vnd.ms-excel' => t('Office spreadsheet icon'), + 'application/vnd.ms-powerpoint' => t('Office presentation icon'), + 'application/pdf' => t('PDF icon'), + 'video/quicktime' => t('Movie icon'), + 'audio/mpeg' => t('Audio icon'), + 'audio/wav' => t('Audio icon'), + 'image/jpeg' => t('Image icon'), + 'image/png' => t('Image icon'), + 'image/gif' => t('Image icon'), + 'application/zip' => t('Package icon'), + 'text/html' => t('HTML icon'), + 'text/plain' => t('Plain text icon'), + 'application/octet-stream' => t('Binary Data'), + ); + + $mimetype = file_get_mimetype($file->uri); - $url = 'file/' . $file->fid; - $icon = theme('file_icon', array('file' => $file, 'icon_directory' => $icon_directory)); + $icon = theme('file_icon', array( + 'file' => $file, + 'icon_directory' => $variables['icon_directory'], + 'alt' => !empty($mime_name[$mimetype]) ? $mime_name[$mimetype] : t('File'), + )); // Set options as per anchor format described at // http://microformats.org/wiki/file-format-examples - $options = array( - 'attributes' => array( - 'type' => $file->filemime . '; length=' . $file->filesize, - ), - ); + $uri['options']['attributes']['type'] = $file->filemime . '; length=' . $file->filesize; // Use the description as the link text if available. if (empty($file->description)) { @@ -44,10 +63,33 @@ function theme_file_entity_file_link($variables) { */ function theme_file_entity_download_link($variables) { $file = $variables['file']; - $icon_directory = $variables['icon_directory']; - $uri = file_entity_download_uri($file); - $icon = theme('file_icon', array('file' => $file, 'icon_directory' => $icon_directory)); + + // Human-readable names, for use as text-alternatives to icons. + $mime_name = array( + 'application/msword' => t('Microsoft Office document icon'), + 'application/vnd.ms-excel' => t('Office spreadsheet icon'), + 'application/vnd.ms-powerpoint' => t('Office presentation icon'), + 'application/pdf' => t('PDF icon'), + 'video/quicktime' => t('Movie icon'), + 'audio/mpeg' => t('Audio icon'), + 'audio/wav' => t('Audio icon'), + 'image/jpeg' => t('Image icon'), + 'image/png' => t('Image icon'), + 'image/gif' => t('Image icon'), + 'application/zip' => t('Package icon'), + 'text/html' => t('HTML icon'), + 'text/plain' => t('Plain text icon'), + 'application/octet-stream' => t('Binary Data'), + ); + + $mimetype = file_get_mimetype($file->uri); + + $icon = theme('file_icon', array( + 'file' => $file, + 'icon_directory' => $variables['icon_directory'], + 'alt' => !empty($mime_name[$mimetype]) ? $mime_name[$mimetype] : t('File'), + )); // Set options as per anchor format described at // http://microformats.org/wiki/file-format-examples diff --git a/sites/all/modules/contrib/file_entity/file_entity_views.test b/sites/all/modules/contrib/file_entity/file_entity_views.test new file mode 100644 index 000000000..5e0bac558 --- /dev/null +++ b/sites/all/modules/contrib/file_entity/file_entity_views.test @@ -0,0 +1,114 @@ + 'File entity views integration: file link usage', + 'description' => 'Test file usage Views field.', + 'group' => 'File entity', + ); + } + + /** + * Test views file usage handlers. + */ + public function testViewsUsageField() { + $file = $this->createFileEntity(); + file_usage_add($file, 'foo', 'bar', 1,1); + file_usage_add($file, 'foo', 'bar', 1,2); + $file2 = $this->createFileEntity(); + file_usage_add($file2, 'foo', 'bar', 1, 99); + + $view = $this->getTestView(); + $view->set_display(); + $view->pre_execute(); + $view->execute(); + + $row = $view->result[0]; + $render = $view->field['usage']->render_link($row, $row); + $this->assertEqual($render, '1 place'); + + $row = $view->result[1]; + $render = $view->field['usage']->render_link($row, $row); + $this->assertEqual($render, '1 place'); + + $view->field['usage']->options['count_entities_once'] = 0; + + $row = $view->result[0]; + $render = $view->field['usage']->render_link($row, $row); + $this->assertEqual($render, '3 places'); + + $row = $view->result[1]; + $render = $view->field['usage']->render_link($row, $row); + $this->assertEqual($render, '99 places'); + } + + /** + * Creates a test view containing a file usage field. + */ + protected function getTestView() { + $view = new view(); + $view->name = 'file_list'; + $view->description = ''; + $view->tag = 'default'; + $view->base_table = 'file_managed'; + $view->human_name = 'File List'; + $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'] = 'File List'; + $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['style_plugin'] = 'default'; + $handler->display->display_options['row_plugin'] = 'fields'; + /* Field: File: Name */ + $handler->display->display_options['fields']['filename']['id'] = 'filename'; + $handler->display->display_options['fields']['filename']['table'] = 'file_managed'; + $handler->display->display_options['fields']['filename']['field'] = 'filename'; + $handler->display->display_options['fields']['filename']['label'] = ''; + $handler->display->display_options['fields']['filename']['alter']['word_boundary'] = FALSE; + $handler->display->display_options['fields']['filename']['alter']['ellipsis'] = FALSE; + /* Field: File: Usage link */ + $handler->display->display_options['fields']['usage']['id'] = 'usage'; + $handler->display->display_options['fields']['usage']['table'] = 'file_managed'; + $handler->display->display_options['fields']['usage']['field'] = 'usage'; + $handler->display->display_options['fields']['usage']['count_entities_once'] = 1; + /* Field: File Usage: Use count */ + $handler->display->display_options['fields']['count']['id'] = 'count'; + $handler->display->display_options['fields']['count']['table'] = 'file_usage'; + $handler->display->display_options['fields']['count']['field'] = 'count'; + + /* Display: Page */ + $handler = $view->new_display('page', 'Page', 'page'); + $handler->display->display_options['path'] = 'file-list'; + + return $view; + } + +} diff --git a/sites/all/modules/contrib/file_entity/plugins/tasks/file_view.inc b/sites/all/modules/contrib/file_entity/plugins/tasks/file_view.inc index 5b8237581..be14e5a48 100644 --- a/sites/all/modules/contrib/file_entity/plugins/tasks/file_view.inc +++ b/sites/all/modules/contrib/file_entity/plugins/tasks/file_view.inc @@ -105,6 +105,7 @@ function file_entity_file_view_page($file) { } // Otherwise, fall back. + module_load_include('inc', 'file_entity', 'file_entity.pages'); return $function($file); } diff --git a/sites/all/modules/contrib/file_entity/tests/file_entity_test.info b/sites/all/modules/contrib/file_entity/tests/file_entity_test.info index e9b51c516..5ea94f730 100644 --- a/sites/all/modules/contrib/file_entity/tests/file_entity_test.info +++ b/sites/all/modules/contrib/file_entity/tests/file_entity_test.info @@ -5,9 +5,9 @@ core = 7.x dependencies[] = file_entity hidden = TRUE -; Information added by Drupal.org packaging script on 2016-05-31 -version = "7.x-2.0-beta3" +; Information added by Drupal.org packaging script on 2017-05-22 +version = "7.x-2.0" core = "7.x" project = "file_entity" -datestamp = "1464653173" +datestamp = "1495456387" diff --git a/sites/all/modules/contrib/file_entity/views/views_handler_field_file_link_usage.inc b/sites/all/modules/contrib/file_entity/views/views_handler_field_file_link_usage.inc index 49e1df8d7..aa461c82f 100644 --- a/sites/all/modules/contrib/file_entity/views/views_handler_field_file_link_usage.inc +++ b/sites/all/modules/contrib/file_entity/views/views_handler_field_file_link_usage.inc @@ -12,6 +12,28 @@ */ class views_handler_field_file_link_usage extends views_handler_field_file_link { + /** + * {@inheritdoc} + */ + function option_definition() { + $options = parent::option_definition(); + $options['count_entities_once'] = array('default' => TRUE); + return $options; + } + + /** + * {@inheritdoc} + */ + function options_form(&$form, &$form_state) { + $form['count_entities_once'] = array( + '#type' => 'checkbox', + '#title' => t('Count each unique entity once'), + '#description' => t('Files can be used multiple times for an entity, especially when an entity is revisionable.'), + '#default_value' => !empty($this->options['count_entities_once']), + ); + parent::options_form($form, $form_state); + } + /** * Renders the link. */ @@ -27,10 +49,19 @@ class views_handler_field_file_link_usage extends views_handler_field_file_link // Get total count for each file. $total_count = 0; - foreach (file_usage_list($file) as $module => $usage) { + $file_usage = file_usage_list($file); + $count_entities_once = !empty($this->options['count_entities_once']); + foreach ($file_usage as $module => $usage) { foreach ($usage as $entity_type => $entity_ids) { - foreach ($entity_ids as $id => $count) { - $total_count += $count; + if ($count_entities_once) { + // Just count each unique entity once. + $total_count += count($entity_ids); + } + else { + // Count multiple usages for each entity. + foreach ($entity_ids as $id => $count) { + $total_count += $count; + } } } } diff --git a/sites/all/modules/contrib/media/includes/MediaEntityTranslationHandler.inc b/sites/all/modules/contrib/media/includes/MediaEntityTranslationHandler.inc index 7543faa50..f9b9cf6a4 100644 --- a/sites/all/modules/contrib/media/includes/MediaEntityTranslationHandler.inc +++ b/sites/all/modules/contrib/media/includes/MediaEntityTranslationHandler.inc @@ -37,7 +37,7 @@ class MediaEntityTranslationHandler extends EntityTranslationDefaultHandler { if ($this->getPathScheme() == 'media') { $language = $GLOBALS[LANGUAGE_TYPE_CONTENT]; - $form_langcode = $this->getFormLanguage(); + $form_langcode = $this->getActiveLanguage(); $source_langcode = $this->getSourceLanguage(); $translations = $this->getTranslations(); diff --git a/sites/all/modules/contrib/media/includes/media.fields.inc b/sites/all/modules/contrib/media/includes/media.fields.inc index e3ed2d1a6..9105fd1b7 100644 --- a/sites/all/modules/contrib/media/includes/media.fields.inc +++ b/sites/all/modules/contrib/media/includes/media.fields.inc @@ -126,7 +126,7 @@ function media_field_widget_form(&$form, &$form_state, $field, $instance, $langc // on the elements for further usage in media_element_process(). if (module_invoke('entity_translation', 'enabled', $element['#entity_type'], $element['#entity'])) { $translation_handler = entity_translation_get_handler($element['#entity_type'], $element['#entity']); - $element['#media_parent_entity_form_langcode'] = $translation_handler->getFormLanguage(); + $element['#media_parent_entity_form_langcode'] = $translation_handler->getActiveLanguage(); if ($source_langcode = $translation_handler->getSourceLanguage()) { $element['#media_parent_entity_source_langcode'] = $source_langcode; } diff --git a/sites/all/modules/contrib/media/includes/media.pages.inc b/sites/all/modules/contrib/media/includes/media.pages.inc index 9bef54a1c..3f5b27de4 100644 --- a/sites/all/modules/contrib/media/includes/media.pages.inc +++ b/sites/all/modules/contrib/media/includes/media.pages.inc @@ -19,7 +19,7 @@ function media_file_edit_modal($form, &$form_state, $file, $js) { if (isset($languages[$_GET['media_parent_entity_form_langcode']])) { $langcode = $_GET['media_parent_entity_form_langcode']; $translation_handler = entity_translation_get_handler('file', $file); - $translation_handler->setFormLanguage($langcode); + $translation_handler->setActiveLanguage($langcode); $translations = $translation_handler->getTranslations(); if (!isset($translations->data[$langcode])) { if (!empty($_GET['media_parent_entity_source_langcode']) && is_string($_GET['media_parent_entity_source_langcode']) && isset($translations->data[$_GET['media_parent_entity_source_langcode']])) { diff --git a/sites/all/modules/contrib/media/js/media.popups.js b/sites/all/modules/contrib/media/js/media.popups.js index 2c8c775f2..49f56c0ee 100644 --- a/sites/all/modules/contrib/media/js/media.popups.js +++ b/sites/all/modules/contrib/media/js/media.popups.js @@ -213,6 +213,7 @@ Drupal.media.popups.mediaStyleSelector = function (mediaFile, onSelect, options) dialogOptions.buttons[ok] = function () { // Find the current file selection. var formattedMedia = this.contentWindow.Drupal.media.formatForm.getFormattedMedia(); + formattedMedia.options = $.extend({}, mediaFile.attributes, formattedMedia.options); // Alert the user if a selection has yet to be made. if (!formattedMedia) { diff --git a/sites/all/modules/contrib/media/media.info b/sites/all/modules/contrib/media/media.info index 783d280ad..785a7aab4 100644 --- a/sites/all/modules/contrib/media/media.info +++ b/sites/all/modules/contrib/media/media.info @@ -24,9 +24,9 @@ configure = admin/config/media/browser ; We have to add a fake version so Git checkouts do not fail Media dependencies version = 7.x-2.x-dev -; Information added by Drupal.org packaging script on 2017-03-05 -version = "7.x-2.0-rc12" +; Information added by Drupal.org packaging script on 2017-05-25 +version = "7.x-2.4" core = "7.x" project = "media" -datestamp = "1488724088" +datestamp = "1495749189" diff --git a/sites/all/modules/contrib/media/media.install b/sites/all/modules/contrib/media/media.install index b6efdd44c..6aa7981dd 100644 --- a/sites/all/modules/contrib/media/media.install +++ b/sites/all/modules/contrib/media/media.install @@ -87,8 +87,8 @@ function media_update_dependencies() { 'rules' => 7205, ); // Those updates require {file_type} table created. - $dependencies['media'][7204] = array( - 'file_entity' => 7201, + $dependencies['media'][7200] = array( + 'file_entity' => 7207, ); // Require {file_type}.mimetypes column before updating them. $dependencies['media'][7208] = array( @@ -122,6 +122,26 @@ function media_requirements($phase) { } } + if (module_exists('entity_translation')) { + if ($phase == 'update' || $phase == 'install' || $phase == 'runtime' ) { + $entity_translation_info = system_get_info('module', 'entity_translation'); + $et_installed_version = $entity_translation_info['version']; + $et_installed_datestamp = $entity_translation_info['datestamp']; + $march3rd_entity_translation_timestamp = 1488530885; + if (!isset($entity_translation_info['version']) || !isset($entity_translation_info['datestamp'])) { + $et_installed_datestamp = 1488530884; + } + + if ($et_installed_datestamp < $march3rd_entity_translation_timestamp) { + $description = $t('Your entity_translation installation version: %version is too old. media requires entity_translation at least beta6 from march 3 2017 or newer. Your choice is to either upgrade entity_translation or to disable it.', array('%version' => $et_installed_version)); + $requirements['entity_translation']['description'] = $description; + $requirements['entity_translation']['severity'] = REQUIREMENT_ERROR; + $requirements['entity_translation']['value'] = $et_installed_version; + $requirements['entity_translation']['title'] = $t('Entity translation (when installed) with Media'); + drupal_set_message($description, 'error', TRUE); + } + } + } return $requirements; } @@ -929,7 +949,7 @@ function media_update_7211() { // Clear the image cache to remove any old image styles that only exist in // code. - cache_clear_all('*', 'cache_image', TRUE); + cache_clear_all('image_styles', 'cache_image', TRUE); // Check if the square_thumbnail image style exists. // The style will only exist if the user has customized it, otherwise it would @@ -945,8 +965,8 @@ function media_update_7211() { $effect = array( 'name' => 'image_scale_and_crop', 'data' => array( - 'width' => 180, - 'height' => 180, + 'width' => 100, + 'height' => 100, 'weight' => 0, ), 'isid' => $style['isid'], @@ -1187,3 +1207,10 @@ function media_update_7226() { user_role_grant_permissions($rid, array('access media browser')); } } + +/** + * Make sure that the image style square_thumbnail is created. + */ +function media_update_7227() { + media_update_7211(); +} diff --git a/sites/all/modules/contrib/media/media.module b/sites/all/modules/contrib/media/media.module index 9c9cb0d37..4035de5e8 100644 --- a/sites/all/modules/contrib/media/media.module +++ b/sites/all/modules/contrib/media/media.module @@ -368,7 +368,7 @@ function media_form_field_ui_field_edit_form_alter(&$form, &$form_state) { // Add a validation function to any field instance which uses the media widget // to ensure that the upload destination scheme is one of the allowed schemes // if any defined by settings. - if ($form['instance']['widget']['type']['#value'] == 'media_generic' && isset($form['#field']['settings']['uri_scheme'])) { + if (isset($form['instance']['widget']) && $form['instance']['widget']['type']['#value'] == 'media_generic' && isset($form['#field']['settings']['uri_scheme'])) { $form['#validate'][] = 'media_field_instance_validate'; } } @@ -833,11 +833,14 @@ function media_element_process($element, &$form_state, $form) { $element_js_class = drupal_html_class('js-media-element-' . $element['#id']); $element['upload']['#attributes']['class'][] = $element_js_class; - // Add the media options to the page as JavaScript settings. + // Cache the media options and pass the cache ID as a JavaScript setting. + $cid = drupal_get_token(drupal_random_bytes(32)); + cache_set('media_options:' . $cid, $element['#media_options']['global'], 'cache_form', REQUEST_TIME + 21600); + $element['browse_button']['#attached']['js'] = array( array( 'type' => 'setting', - 'data' => array('media' => array('elements' => array('.' . $element_js_class => $element['#media_options']))) + 'data' => array('media' => array('elements' => array('.' . $element_js_class => array('global' => array('options' => $cid))))), ) ); @@ -1202,11 +1205,32 @@ function media_set_browser_params() { if (empty($params)) { // Build out browser settings. Permissions- and security-related behaviors // should not rely on these parameters, since they come from the HTTP query. - // @TODO make sure we treat parameters as user input. - $params = drupal_get_query_parameters() + array( - 'types' => array(), - 'multiselect' => FALSE, - ); + // There are two ways of passing secure data: + // - Store the options in the 'cache_form' cache bin, using a random key + // prefixed with 'media_options:'. Pass the random key in the 'options' + // query argument. + // - Inject the options by altering the browser parameters. + // @see hook_media_browser_params_alter() + $params = drupal_get_query_parameters(); + + // Filter out everything except a whitelist of known safe options. + $safe_options = array( + 'activePlugins', + 'fid', + 'id', + 'multiselect', + 'options', + 'plugins', + 'render', + 'types', + 'render_multi_edit_form', + ); + $params = array_intersect_key($params, array_flip($safe_options)); + + // Retrieve the security sensitive options from the cache. + if (!empty($params['options']) && is_string($params['options']) && $options = cache_get('media_options:' . $params['options'], 'cache_form')) { + $params = array_merge($options->data, $params); + } // Transform text 'true' and 'false' to actual booleans. foreach ($params as $k => $v) { @@ -1220,6 +1244,12 @@ function media_set_browser_params() { array_walk_recursive($params, 'media_recursive_check_plain'); + // Provide some default parameters. + $params += array( + 'types' => array(), + 'multiselect' => FALSE, + ); + // Allow modules to alter the parameters. drupal_alter('media_browser_params', $params); } diff --git a/sites/all/modules/contrib/media/media.views.inc b/sites/all/modules/contrib/media/media.views.inc index 70d5c2f1b..9640dde2c 100644 --- a/sites/all/modules/contrib/media/media.views.inc +++ b/sites/all/modules/contrib/media/media.views.inc @@ -33,6 +33,7 @@ function media_views_plugins() { 'help' => t('Display as a tab in the media browser.'), 'handler' => 'media_views_plugin_display_media_browser', 'theme' => 'views_view', + 'theme path' => drupal_get_path('module', 'views') . '/theme', 'base' => $base, 'use ajax' => TRUE, 'use pager' => TRUE, diff --git a/sites/all/modules/contrib/media/modules/media_bulk_upload/media_bulk_upload.info b/sites/all/modules/contrib/media/modules/media_bulk_upload/media_bulk_upload.info index 40aaff9ae..9760dc8f8 100644 --- a/sites/all/modules/contrib/media/modules/media_bulk_upload/media_bulk_upload.info +++ b/sites/all/modules/contrib/media/modules/media_bulk_upload/media_bulk_upload.info @@ -15,9 +15,9 @@ test_dependencies[] = plupload files[] = includes/MediaBrowserBulkUpload.inc files[] = tests/media_bulk_upload.test -; Information added by Drupal.org packaging script on 2017-03-05 -version = "7.x-2.0-rc12" +; Information added by Drupal.org packaging script on 2017-05-25 +version = "7.x-2.4" core = "7.x" project = "media" -datestamp = "1488724088" +datestamp = "1495749189" diff --git a/sites/all/modules/contrib/media/modules/media_internet/media_internet.info b/sites/all/modules/contrib/media/modules/media_internet/media_internet.info index 563d5bfc2..75560b3d9 100644 --- a/sites/all/modules/contrib/media/modules/media_internet/media_internet.info +++ b/sites/all/modules/contrib/media/modules/media_internet/media_internet.info @@ -12,9 +12,9 @@ files[] = includes/MediaInternetNoHandlerException.inc files[] = includes/MediaInternetValidationException.inc files[] = tests/media_internet.test -; Information added by Drupal.org packaging script on 2017-03-05 -version = "7.x-2.0-rc12" +; Information added by Drupal.org packaging script on 2017-05-25 +version = "7.x-2.4" core = "7.x" project = "media" -datestamp = "1488724088" +datestamp = "1495749189" diff --git a/sites/all/modules/contrib/media/modules/media_internet/media_internet.module b/sites/all/modules/contrib/media/modules/media_internet/media_internet.module index d94b47926..a6bf5f29c 100644 --- a/sites/all/modules/contrib/media/modules/media_internet/media_internet.module +++ b/sites/all/modules/contrib/media/modules/media_internet/media_internet.module @@ -111,6 +111,7 @@ function media_internet_get_providers() { */ function media_internet_get_provider($embed_string) { foreach (media_internet_get_providers() as $class_name => $nothing) { + module_load_include('inc', $nothing['module'], 'includes/' . $class_name); $p = new $class_name($embed_string); if ($p->claim($embed_string)) { return $p; diff --git a/sites/all/modules/contrib/media/modules/media_internet/tests/media_internet_test.info b/sites/all/modules/contrib/media/modules/media_internet/tests/media_internet_test.info index 749a92dd2..2538656ae 100644 --- a/sites/all/modules/contrib/media/modules/media_internet/tests/media_internet_test.info +++ b/sites/all/modules/contrib/media/modules/media_internet/tests/media_internet_test.info @@ -7,9 +7,9 @@ hidden = TRUE files[] = includes/MediaInternetTestStreamWrapper.inc files[] = includes/MediaInternetTestHandler.inc -; Information added by Drupal.org packaging script on 2017-03-05 -version = "7.x-2.0-rc12" +; Information added by Drupal.org packaging script on 2017-05-25 +version = "7.x-2.4" core = "7.x" project = "media" -datestamp = "1488724088" +datestamp = "1495749189" diff --git a/sites/all/modules/contrib/media/modules/media_migrate_file_types/media_migrate_file_types.info b/sites/all/modules/contrib/media/modules/media_migrate_file_types/media_migrate_file_types.info index 7da7b75aa..13ccd4d6c 100644 --- a/sites/all/modules/contrib/media/modules/media_migrate_file_types/media_migrate_file_types.info +++ b/sites/all/modules/contrib/media/modules/media_migrate_file_types/media_migrate_file_types.info @@ -8,9 +8,9 @@ dependencies[] = media configure = admin/structure/file-types/upgrade -; Information added by Drupal.org packaging script on 2017-03-05 -version = "7.x-2.0-rc12" +; Information added by Drupal.org packaging script on 2017-05-25 +version = "7.x-2.4" core = "7.x" project = "media" -datestamp = "1488724088" +datestamp = "1495749189" diff --git a/sites/all/modules/contrib/media/modules/media_wysiwyg/css/media_wysiwyg.css b/sites/all/modules/contrib/media/modules/media_wysiwyg/css/media_wysiwyg.css index 7a0f3b704..a04734c5a 100644 --- a/sites/all/modules/contrib/media/modules/media_wysiwyg/css/media_wysiwyg.css +++ b/sites/all/modules/contrib/media/modules/media_wysiwyg/css/media_wysiwyg.css @@ -10,11 +10,15 @@ margin: 20px; } -#media-wysiwyg-format-form .media-item { +#media-wysiwyg-format-form .media-preview-group { float: left; margin-right: 10px; } +#media-wysiwyg-format-form .media-preview-group .edit-file-link { + text-align: center; +} + #media-wysiwyg-format-form .form-item-format label { display: inline; } diff --git a/sites/all/modules/contrib/media/modules/media_wysiwyg/includes/media_wysiwyg.filter.inc b/sites/all/modules/contrib/media/modules/media_wysiwyg/includes/media_wysiwyg.filter.inc index ef7261f37..25cee6bb0 100644 --- a/sites/all/modules/contrib/media/modules/media_wysiwyg/includes/media_wysiwyg.filter.inc +++ b/sites/all/modules/contrib/media/modules/media_wysiwyg/includes/media_wysiwyg.filter.inc @@ -5,7 +5,7 @@ * Functions related to the WYSIWYG editor and the media input filter. */ -define('MEDIA_WYSIWYG_TOKEN_REGEX', '/\[\[.*?\]\]/s'); +define('MEDIA_WYSIWYG_TOKEN_REGEX', '/\[\[.+?"type":"media".+?\]\]/s'); /** * Filter callback for media markup filter. @@ -27,21 +27,70 @@ function media_wysiwyg_filter($text, $filter = NULL, $format = NULL, $langcode = return $rendered_text; } +/** + * Filter callback to configure media_filter_paragraph_fix filter. + */ +function _media_filter_paragraph_fix_settings($form, &$form_state, $filter, $format, $defaults) { + $filter->settings += $defaults; + $settings['replace'] = array( + '#type' => 'checkbox', + '#title' => t('Replace paragraph tags with DIV.media-p tags'), + '#default_value' => $filter->settings['replace'], + '#description' => t('Default behaviour is to strip out parent P tags of media elements rather than replacing these.'), + ); + return $settings; +} + /** * Filter callback to remove paragraph tags surrounding embedded media. */ -function media_wysiwyg_filter_paragraph_fix($text) { +function media_wysiwyg_filter_paragraph_fix($text, $filter) { $html_dom = filter_dom_load($text); + // Store Nodes to remove to avoid inferferring with the NodeList iteration. + $dom_nodes_to_remove = array(); foreach ($html_dom->getElementsByTagName('p') as $paragraph) { if (preg_match(MEDIA_WYSIWYG_TOKEN_REGEX, $paragraph->nodeValue)) { - $sibling = $paragraph->firstChild; - do { - $next = $sibling->nextSibling; - $paragraph->parentNode->insertBefore($sibling, $paragraph); - } while ($sibling = $next); - $paragraph->parentNode->removeChild($paragraph); + if (empty($filter->settings['replace'])) { + $sibling = $paragraph->firstChild; + do { + $next = $sibling->nextSibling; + $paragraph->parentNode->insertBefore($sibling, $paragraph); + } while ($sibling = $next); + $dom_nodes_to_remove[] = $paragraph; + } + else { + // Clone the P node into a DIV node. + $div = $html_dom->createElement('div'); + $sibling = $paragraph->firstChild; + do { + $next = $sibling->nextSibling; + $div->appendChild($sibling); + } while ($sibling = $next); + + $classes = array('media-p'); + if ($paragraph->hasAttributes()) { + foreach ($paragraph->attributes as $attr) { + $name = $attr->nodeName; + $value = $attr->nodeValue; + if (strtolower($name) == 'class') { + $classes[] = $value; + } + else { + // Supressing errors with ID attribute or duplicate properties. + @$div->setAttribute($name, $value); + } + } + } + $div->setAttribute('class', implode(' ', $classes)); + + $paragraph->parentNode->insertBefore($div, $paragraph); + $dom_nodes_to_remove[] = $paragraph; + } } } + foreach ($dom_nodes_to_remove as $paragraph) { + $paragraph->parentNode->removeChild($paragraph); + } $text = filter_dom_serialize($html_dom); return $text; } diff --git a/sites/all/modules/contrib/media/modules/media_wysiwyg/includes/media_wysiwyg.pages.inc b/sites/all/modules/contrib/media/modules/media_wysiwyg/includes/media_wysiwyg.pages.inc index a2ca6474e..41e4430cb 100644 --- a/sites/all/modules/contrib/media/modules/media_wysiwyg/includes/media_wysiwyg.pages.inc +++ b/sites/all/modules/contrib/media/modules/media_wysiwyg/includes/media_wysiwyg.pages.inc @@ -164,8 +164,8 @@ function media_wysiwyg_format_form_view_mode(&$form, $form_state, $file) { } $form['preview'] = array(); - $form['preview']['#prefix'] = '
'; - $form['preview']['#suffix'] = '
'; + $form['preview']['#prefix'] = '
'; + $form['preview']['#suffix'] = '
'; $form['preview']['thumbnail'] = file_view_file($file, $view_mode); $form['preview']['thumbnail']['#prefix'] = '
'; $form['preview']['thumbnail']['#suffix'] = '
'; diff --git a/sites/all/modules/contrib/media/modules/media_wysiwyg/js/media_wysiwyg.filter.js b/sites/all/modules/contrib/media/modules/media_wysiwyg/js/media_wysiwyg.filter.js index 02e8cd2da..f3df9363c 100644 --- a/sites/all/modules/contrib/media/modules/media_wysiwyg/js/media_wysiwyg.filter.js +++ b/sites/all/modules/contrib/media/modules/media_wysiwyg/js/media_wysiwyg.filter.js @@ -175,7 +175,8 @@ Drupal.media.filter.ensure_tagmap(); // Locate and process all the media placeholders in the WYSIWYG content. - var contentElements = $('
').html(content); // TODO: once baseline jQuery is 1.8+, switch to using $.parseHTML(content) + var contentElements = $('
'); // TODO: once baseline jQuery is 1.8+, switch to using $.parseHTML(content) + contentElements.get(0).innerHTML = content; var mediaElements = contentElements.find('.media-element'); if (mediaElements) { $(mediaElements).each(function (i) { @@ -238,6 +239,11 @@ if (info.attributes[a]) { element.attr(a, info.attributes[a]); } + else if (element.attr(a)) { + // If the element has the attribute, but the value is empty, be + // sure to clear it. + element.removeAttr(a); + } }); delete(info.attributes); diff --git a/sites/all/modules/contrib/media/modules/media_wysiwyg/media_wysiwyg.info b/sites/all/modules/contrib/media/modules/media_wysiwyg/media_wysiwyg.info index 482500d2a..05622b88e 100644 --- a/sites/all/modules/contrib/media/modules/media_wysiwyg/media_wysiwyg.info +++ b/sites/all/modules/contrib/media/modules/media_wysiwyg/media_wysiwyg.info @@ -12,12 +12,13 @@ test_dependencies[] = wysiwyg files[] = media_wysiwyg.test files[] = tests/media_wysiwyg.file_usage.test files[] = tests/media_wysiwyg.macro.test +files[] = tests/media_wysiwyg.paragraph_fix_filter.test configure = admin/config/media/browser -; Information added by Drupal.org packaging script on 2017-03-05 -version = "7.x-2.0-rc12" +; Information added by Drupal.org packaging script on 2017-05-25 +version = "7.x-2.4" core = "7.x" project = "media" -datestamp = "1488724088" +datestamp = "1495749189" diff --git a/sites/all/modules/contrib/media/modules/media_wysiwyg/media_wysiwyg.module b/sites/all/modules/contrib/media/modules/media_wysiwyg/media_wysiwyg.module index ceab9d120..483e2d0f2 100644 --- a/sites/all/modules/contrib/media/modules/media_wysiwyg/media_wysiwyg.module +++ b/sites/all/modules/contrib/media/modules/media_wysiwyg/media_wysiwyg.module @@ -333,7 +333,7 @@ function media_wysiwyg_admin_config_browser_pre_submit(&$form, &$form_state) { function media_wysiwyg_filter_info() { $filters['media_filter'] = array( 'title' => t('Convert Media tags to markup'), - 'description' => t('This filter will convert [[{type:media... ]] tags into markup. This must be enabled for the Media WYSIWYG integration to work with this input format.'), + 'description' => t('This filter will convert [[{type:media... ]] tags into markup. This must be enabled for the Media WYSIWYG integration to work with this input format. It is recommended to run this before the "@convert_urls" filter.', array('@convert_urls' => 'Convert URLs into links')), 'process callback' => 'media_wysiwyg_filter', 'weight' => 2, // @TODO not implemented @@ -342,8 +342,12 @@ function media_wysiwyg_filter_info() { $filters['media_filter_paragraph_fix'] = array( 'title' => t('Ensure that embedded Media tags are not contained in paragraphs'), - 'description' => t('This filter will strip any paragraph tags surrounding embedded Media tags. This helps to avoid the chopped up markup that can result from unexpectedly closed paragraph tags. This filter should be positioned above (before) the "Convert Media tags to markup" filter.'), + 'description' => t('This filter will fix any paragraph tags surrounding embedded Media tags. This helps to avoid the chopped up markup that can result from unexpectedly closed paragraph tags. This filter should be positioned above (before) the "Convert Media tags to markup" filter.'), 'process callback' => 'media_wysiwyg_filter_paragraph_fix', + 'settings callback' => '_media_filter_paragraph_fix_settings', + 'default settings' => array( + 'replace' => 0, + ), 'weight' => 1, ); @@ -389,7 +393,7 @@ function media_wysiwyg_allowed_attributes() { 'data-delta', )); drupal_alter('media_wysiwyg_allowed_attributes', $allowed_attributes); - return $allowed_attributes; + return $allowed_attributes; } /** @@ -603,7 +607,7 @@ function media_wysiwyg_form_file_entity_file_type_form_alter(&$form, &$form_stat if (empty($form_state['build_info']['args'][0])) { return; } - + $options = array(); // Add an option allowing users not to use a view mode. @@ -694,3 +698,18 @@ function media_wysiwyg_form_file_entity_file_display_form_alter_submit(&$form, & drupal_write_record('media_restrict_wysiwyg', $record); } } + +/** + * Implements hook_media_browser_params_alter(). + */ +function media_wysiwyg_media_browser_params_alter(&$params) { + // Set the media browser options as defined in the interface. + if (!empty($params['id']) && $params['id'] === 'media_wysiwyg') { + $params = array( + 'enabledPlugins' => variable_get('media_wysiwyg_wysiwyg_browser_plugins', array()), + 'file_directory' => variable_get('media_wysiwyg_wysiwyg_upload_directory', ''), + 'types' => variable_get('media_wysiwyg_wysiwyg_allowed_types', array('audio', 'image', 'video', 'document')), + 'id' => 'media_wysiwyg', + ) + $params; + } +} diff --git a/sites/all/modules/contrib/media/modules/media_wysiwyg/media_wysiwyg.test b/sites/all/modules/contrib/media/modules/media_wysiwyg/media_wysiwyg.test index 6b0aaa377..117aab57b 100644 --- a/sites/all/modules/contrib/media/modules/media_wysiwyg/media_wysiwyg.test +++ b/sites/all/modules/contrib/media/modules/media_wysiwyg/media_wysiwyg.test @@ -58,9 +58,11 @@ abstract class MediaWYSIWYGTestHelper extends DrupalWebTestCase { ); // Create the file usage markup. + $markup .= '

Intro paragraph

'; for ($i = 1; $i <= $count; $i++) { $markup .= '

[[' . drupal_json_encode($data) . ']]

'; } + $markup .= '

Finish paragraph

'; return $markup; } diff --git a/sites/all/modules/contrib/media/modules/media_wysiwyg/tests/media_wysiwyg.paragraph_fix_filter.test b/sites/all/modules/contrib/media/modules/media_wysiwyg/tests/media_wysiwyg.paragraph_fix_filter.test new file mode 100644 index 000000000..5c6f6c590 --- /dev/null +++ b/sites/all/modules/contrib/media/modules/media_wysiwyg/tests/media_wysiwyg.paragraph_fix_filter.test @@ -0,0 +1,165 @@ +]*media\-element\-container[^>]*>/i'; + + /** + * Defines the regex to test for in the raw body field source. + * + * @var string + */ + protected $regexpPWrapped = '/]*>
]*media\-element\-container[^>]*>/i'; + + /** + * Defines the regex to test for the P replacement filter. + * + * @var string + */ + protected $regexpReplaced = '/
t('Media WYSIWYG Paragraph Filter Test'), + 'description' => t('Tests that this media filter is working.'), + 'group' => t('Media WYSIWYG'), + 'dependencies' => array('token'), + ); + } + + /** + * Set-up the system for testing without the filter enabled. + */ + public function setUp() { + parent::setUp('token'); + + // Create and log in a user. + $account = $this->drupalCreateUser(array( + 'create article content', + 'administer filters', + 'use text format filtered_html', + )); + $this->drupalLogin($account); + + // Enable the media filter for full html. + $edit = array( + 'filters[media_filter][status]' => TRUE, + 'filters[filter_autop][status]' => FALSE, + 'filters[filter_html][status]' => FALSE, + 'filters[filter_htmlcorrector][status]' => FALSE, + ); + $this->drupalPost('admin/config/content/formats/filtered_html', $edit, t('Save configuration')); + } + + /** + * Test image media overrides. + */ + public function testMediaFilterParagraphFixMultipleImages() { + $files = $this->drupalGetTestFiles('image'); + $file = file_save($files[0]); + + // Create a node to test with 3 images. + $nid = $this->createNode($file->fid); + $node = node_load($nid); + $node->body[LANGUAGE_NONE][0]['value'] = $this->generateJsonTokenMarkup($file->fid, 3); + node_save($node); + + // Check without the filter enabled. + $html = $this->drupalGet('node/' . $nid); + $count = preg_match_all($this->regexpMediaTag, $html); + $this->assertEqual($count, 3, t('Three media tags found, found @count.', array('@count' => $count))); + + $count = preg_match_all($this->regexpPWrapped, $html); + $this->assertEqual($count, 3, t('Three media tags with original wrapping HTML present, found @count.', array('@count' => $count))); + + $count = preg_match_all($this->regexpReplaced, $html); + $this->assertEqual($count, 0, t('No media tags with P replaced present, found @count.', array('@count' => $count))); + + // Enable the default P fix filter. + $edit = array( + 'filters[media_filter_paragraph_fix][status]' => TRUE, + ); + $this->drupalPost('admin/config/content/formats/filtered_html', $edit, t('Save configuration')); + $html = $this->drupalGet('node/' . $nid); + + $count = preg_match_all($this->regexpMediaTag, $html); + $this->assertEqual($count, 3, t('Three media tags found, found @count.', array('@count' => $count))); + + $count = preg_match_all($this->regexpPWrapped, $html); + $this->assertEqual($count, 0, t('No media tags with original wrapping HTML present, found @count.', array('@count' => $count))); + + $count = preg_match_all($this->regexpReplaced, $html); + $this->assertEqual($count, 0, t('No media tags with P replaced present, found @count.', array('@count' => $count))); + + // Enable the replace P fix filter option. + $edit = array( + 'filters[media_filter_paragraph_fix][settings][replace]' => TRUE, + ); + $this->drupalPost('admin/config/content/formats/filtered_html', $edit, t('Save configuration')); + $html = $this->drupalGet('node/' . $nid); + + $count = preg_match_all($this->regexpMediaTag, $html); + $this->assertEqual($count, 3, t('Three media tags found, found @count.', array('@count' => $count))); + + $count = preg_match_all($this->regexpPWrapped, $html); + $this->assertEqual($count, 0, t('No media tags with original wrapping HTML present, found @count.', array('@count' => $count))); + + $count = preg_match_all($this->regexpReplaced, $html); + $this->assertEqual($count, 3, t('Three media tags with P replaced present, found @count.', array('@count' => $count))); + } + + /** + * Test image media overrides. + */ + public function testMediaFilterParagraphFixDefault() { + $files = $this->drupalGetTestFiles('image'); + $file = file_save($files[0]); + + // Create a node to test with. + $nid = $this->createNode($file->fid); + + // Check without the filter enabled. + $this->drupalGet('node/' . $nid); + $this->assertPattern($this->regexpPWrapped, t('Nested media DIV tags within paragraphs without filter.')); + $this->assertNoPattern($this->regexpReplaced, t('No replacement DIV tag found without filter.')); + + // Enable the default P fix filter. + $edit = array( + 'filters[media_filter_paragraph_fix][status]' => TRUE, + ); + $this->drupalPost('admin/config/content/formats/filtered_html', $edit, t('Save configuration')); + + // Retest the content to check nested paragraphs are removed. + $this->drupalGet('node/' . $nid); + $this->assertNoPattern($this->regexpPWrapped, t('Nested media DIV tags within paragraphs with filter defaults.')); + $this->assertNoPattern($this->regexpReplaced, t('No replacement DIV tag found with filter defaults.')); + + // Enable replacement option. + $edit = array( + 'filters[media_filter_paragraph_fix][settings][replace]' => TRUE, + ); + $this->drupalPost('admin/config/content/formats/filtered_html', $edit, t('Save configuration')); + + // Test that the replace text was found. + $this->drupalGet('node/' . $nid); + $this->assertNoPattern($this->regexpPWrapped, t('No nested media DIV tags within paragraphs with filter P replacement.')); + $this->assertPattern($this->regexpReplaced, t('No replacement DIV tag found with filter P replacement.')); + } + +} diff --git a/sites/all/modules/contrib/media/modules/media_wysiwyg/wysiwyg_plugins/media.inc b/sites/all/modules/contrib/media/modules/media_wysiwyg/wysiwyg_plugins/media.inc index 7e1f709be..d8acc0a3b 100644 --- a/sites/all/modules/contrib/media/modules/media_wysiwyg/wysiwyg_plugins/media.inc +++ b/sites/all/modules/contrib/media/modules/media_wysiwyg/wysiwyg_plugins/media.inc @@ -22,9 +22,6 @@ function media_wysiwyg_media_plugin() { 'css file' => 'media_wysiwyg.css', 'settings' => array( 'global' => array( - 'enabledPlugins' => variable_get('media_wysiwyg_wysiwyg_browser_plugins', array()), - 'file_directory' => variable_get('media_wysiwyg_wysiwyg_upload_directory', ''), - 'types' => variable_get('media_wysiwyg_wysiwyg_allowed_types', array('audio', 'image', 'video', 'document')), 'id' => 'media_wysiwyg', ), ), diff --git a/sites/all/modules/contrib/media/modules/media_wysiwyg_view_mode/media_wysiwyg_view_mode.info b/sites/all/modules/contrib/media/modules/media_wysiwyg_view_mode/media_wysiwyg_view_mode.info index a14ee9ac2..e56373b25 100644 --- a/sites/all/modules/contrib/media/modules/media_wysiwyg_view_mode/media_wysiwyg_view_mode.info +++ b/sites/all/modules/contrib/media/modules/media_wysiwyg_view_mode/media_wysiwyg_view_mode.info @@ -3,9 +3,9 @@ description = DEPRECATED, this folder is only here so that the module can be uni package = Media core = 7.x -; Information added by Drupal.org packaging script on 2017-03-05 -version = "7.x-2.0-rc12" +; Information added by Drupal.org packaging script on 2017-05-25 +version = "7.x-2.4" core = "7.x" project = "media" -datestamp = "1488724088" +datestamp = "1495749189" diff --git a/sites/all/modules/contrib/media/modules/mediafield/mediafield.info b/sites/all/modules/contrib/media/modules/mediafield/mediafield.info index b7fdd7b96..b4f13b7fa 100644 --- a/sites/all/modules/contrib/media/modules/mediafield/mediafield.info +++ b/sites/all/modules/contrib/media/modules/mediafield/mediafield.info @@ -4,9 +4,9 @@ package = Media core = 7.x dependencies[] = media -; Information added by Drupal.org packaging script on 2017-03-05 -version = "7.x-2.0-rc12" +; Information added by Drupal.org packaging script on 2017-05-25 +version = "7.x-2.4" core = "7.x" project = "media" -datestamp = "1488724088" +datestamp = "1495749189" diff --git a/sites/all/modules/contrib/media/tests/media.test b/sites/all/modules/contrib/media/tests/media.test index 5aeea2425..a60e3adfa 100644 --- a/sites/all/modules/contrib/media/tests/media.test +++ b/sites/all/modules/contrib/media/tests/media.test @@ -685,19 +685,22 @@ class MediaBrowserSettingsTestCase extends MediaFileFieldTestCase { foreach ($settings['extension'] as $extension) { $file = $this->createFileEntity(array('scheme' => $scheme, 'uid' => $this->admin_user->uid, 'type' => $type, 'filemime' => media_get_extension_mimetype($extension))); + // Some of the settings such as the scheme and extension are unsafe to + // pass as query arguments, cache them and pass the cache ID. $options = array( - 'query' => array( - 'enabledPlugins' => array( - 'media_default--media_browser_1' => 'media_default--media_browser_1', - ), - 'schemes' => array($scheme), - 'types' => array($type), - 'file_extensions' => $extension, + 'enabledPlugins' => array( + 'media_default--media_browser_1' => 'media_default--media_browser_1', ), + 'schemes' => array($scheme), + 'types' => array($type), + 'file_extensions' => $extension, ); + $cid = drupal_get_token(drupal_random_bytes(32)); + cache_set('media_options:' . $cid, $options, 'cache_form', REQUEST_TIME + 21600); + // Verify that the file is displayed. - $this->drupalGet('media/browser', $options); + $this->drupalGet('media/browser', array('query' => array('options' => $cid))); $this->assertResponse(200); $xpath = $this->buildXPathQuery('//ul[@class="media-list-thumbnails"]/li/div[@data-fid=:fid]/@data-fid', array( ':fid' => $file->fid, @@ -714,28 +717,29 @@ class MediaBrowserSettingsTestCase extends MediaFileFieldTestCase { // Perform the tests with none and all of the restrictions. foreach (array('none', 'all') as $restrictions) { $options = array( - 'query' => array( - 'enabledPlugins' => array( - 'media_default--media_browser_1' => 'media_default--media_browser_1', - ), + 'enabledPlugins' => array( + 'media_default--media_browser_1' => 'media_default--media_browser_1', ), ); switch ($restrictions) { case 'none': - $options['query']['schemes'] = array(); - $options['query']['types'] = array(); - $options['query']['file_extensions'] = array(); + $options['schemes'] = array(); + $options['types'] = array(); + $options['file_extensions'] = array(); break; case 'all': - $options['query']['schemes'] = $settings['scheme']; - $options['query']['types'] = $settings['type']; - $options['query']['file_extensions'] = implode(' ', $settings['extension']); + $options['schemes'] = $settings['scheme']; + $options['types'] = $settings['type']; + $options['file_extensions'] = implode(' ', $settings['extension']); break; } + $cid = drupal_get_token(drupal_random_bytes(32)); + cache_set('media_options:' . $cid, $options, 'cache_form', REQUEST_TIME + 21600); + // Verify that all of the files are displayed. - $this->drupalGet('media/browser', $options); + $this->drupalGet('media/browser', array('query' => array('options' => $cid))); $this->assertResponse(200); $files = $this->xpath('//ul[@class="media-list-thumbnails"]/li/div[@data-fid]'); $this->assertEqual(count($files), 8, format_string('All of the files were displayed when %restrictions of the restrictions were enabled.', array('%restrictions' => $restrictions))); @@ -749,18 +753,19 @@ class MediaBrowserSettingsTestCase extends MediaFileFieldTestCase { $file = $this->createFileEntity(array('scheme' => $scheme, 'uid' => $this->admin_user->uid, 'type' => $type, 'filemime' => media_get_extension_mimetype($extension))); $options = array( - 'query' => array( - 'enabledPlugins' => array( - 'media_default--media_browser_1' => 'media_default--media_browser_1', - ), - 'schemes' => array($scheme, 'public'), // Include a local stream wrapper in order to trigger extension restrictions. - 'types' => array($type), - 'file_extensions' => 'fake', // Use an invalid file extension to ensure that it does not affect restrictions. + 'enabledPlugins' => array( + 'media_default--media_browser_1' => 'media_default--media_browser_1', ), + 'schemes' => array($scheme, 'public'), // Include a local stream wrapper in order to trigger extension restrictions. + 'types' => array($type), + 'file_extensions' => 'fake', // Use an invalid file extension to ensure that it does not affect restrictions. ); + $cid = drupal_get_token(drupal_random_bytes(32)); + cache_set('media_options:' . $cid, $options, 'cache_form', REQUEST_TIME + 21600); + // Verify that the file is displayed. - $this->drupalGet('media/browser', $options); + $this->drupalGet('media/browser', array('query' => array('options' => $cid))); $this->assertResponse(200); $xpath = $this->buildXPathQuery('//ul[@class="media-list-thumbnails"]/li/div[@data-fid=:fid]/@data-fid', array( ':fid' => $file->fid, @@ -896,6 +901,73 @@ class MediaElementSettingsTestCase extends MediaFileFieldTestCase { $this->assertTrue(strpos($javascript, $settings) > 0, 'Rendered media element adds the global settings.'); } + /** + * Tests that the field widget does not contain the insecure settings. + */ + function testInsecureSettings() { + // Use 'page' instead of 'article', so that the 'article' image field does + // not conflict with this test. If in the future the 'page' type gets its + // own default file or image field, this test can be made more robust by + // using a custom node type. + $type_name = 'page'; + $field_name = strtolower($this->randomName()); + $this->createFileField($field_name, $type_name); + $this->drupalGet("node/add/$type_name"); + + $insecure_settings = array( + 'file_directory', + 'file_extensions', + 'max_filesize', + 'uri_scheme', + ); + foreach ($insecure_settings as $setting) { + $this->assertNoRaw($setting, format_string('Media file field widget does not contain the insecure element-specific setting @setting.', array( + '@setting' => $setting, + ))); + } + } + + /** + * Tests that insecure settings are not processed when sent via query parameters. + */ + function testBrowserInsecureQueryParameters() { + // Test file directory override. + $path = file_unmanaged_save_data('directorytest', 'temporary://directorytest.txt'); + $data = array('files[upload]' => drupal_realpath($path)); + $this->drupalPost('media/browser', $data, t('Upload'), array('query' => array('file_directory' => 'insecure_upload'))); + // Verify that the file was placed in the normal public:// path instead of the folder we specified. + $this->assertFalse(is_file('public://insecure_upload/directorytest.txt'), 'File was not uploaded to the directory specified in the query parameters.'); + $this->assertTrue(is_file('public://directorytest.txt'), 'File was uploaded to the default public directory.'); + + // Test file_extensions override. + $path = file_unmanaged_save_data('extensiontest', 'temporary://extensiontest.exe'); + $data = array('files[upload]' => drupal_realpath($path)); + $this->drupalPost('media/browser', $data, t('Upload'), array('query' => array('file_extensions' => 'exe'))); + $this->assertFalse(is_file('public://extensiontest.exe'), 'File with extension passed via query parameter was not uploaded.'); + + // Test max_filesize override. + variable_set('file_entity_max_filesize', '8 bytes'); + $path = file_unmanaged_save_data('maxfilesize', 'temporary://maxfilesize.txt'); + $data = array('files[upload]' => drupal_realpath($path)); + $this->drupalPost('media/browser', $data, t('Upload'), array('query' => array('max_filesize' => '100 bytes'))); + $this->assertFalse(is_file('public://maxfilesize.txt'), 'File larger than max file size was not uploaded with larger query parameter.'); + variable_del('file_entity_max_filesize'); + + // Test uri_scheme override. + $path = file_unmanaged_save_data('urischeme', 'temporary://urischeme.txt'); + $data = array('files[upload]' => drupal_realpath($path)); + $this->drupalPost('media/browser', $data, t('Upload'), array('query' => array('uri_scheme' => 'private'))); + $this->assertFalse(is_file('private://urischeme.txt'), 'File was not uploaded to scheme set in URL.'); + $this->assertTrue(is_file('public://urischeme.txt'), 'File was uploaded to default scheme instead of scheme set in URL.'); + + // Test upload_validators override. + $path = file_unmanaged_save_data('uploadvalidators', 'temporary://uploadvalidators.txt'); + $data = array('files[upload]' => drupal_realpath($path)); + $this->drupalPost('media/browser', $data, t('Upload'), array('query' => array('upload_validators' => array('file_move' => array('public://exploit.php'))))); + $this->assertFalse(is_file('public://exploit.php'), 'file_move() was not triggered by upload_validators parameter.'); + $this->assertTrue(is_file('public://uploadvalidators.txt'), 'File was uploaded without triggering file_move().'); + } + /** * Tests the media file field widget settings. */ @@ -934,7 +1006,19 @@ class MediaElementSettingsTestCase extends MediaFileFieldTestCase { ), ); $settings = drupal_json_encode(drupal_array_merge_deep_array($field_widget)); - $this->assertTrue(strpos($javascript, $settings) > 0, 'Media file field widget adds element-specific settings.'); + $string_with_options = '-0-upload":{"global":{"options":"'; + $index_of_cid = strpos($javascript, $string_with_options) + strlen($string_with_options); + $index_end_of_cid = strpos($javascript, '"', $index_of_cid + 1); + $cid = substr($javascript, $index_of_cid, ($index_end_of_cid - $index_of_cid)); + + // Retrieve the security sensitive options from the cache using the cid parsed out from the $javascript variable + $retrieved_settings = cache_get('media_options:' . $cid, 'cache_form'); + $retrieved_settings = array('.js-media-element-edit-' . $field_name . '-' . LANGUAGE_NONE . '-0-upload' => array( + 'global' => $retrieved_settings->data)); + $retrieved_settings_json = drupal_json_encode($retrieved_settings); + + $this->assertTrue($retrieved_settings_json == $settings, 'Media file field widget retrieved from cache and has element-specific settings.'); + $this->assertTrue(strpos($javascript, $cid) > 0, 'Media file field widget is cached and its` cache id is found.'); } } diff --git a/sites/all/modules/contrib/media/tests/media_module_test.info b/sites/all/modules/contrib/media/tests/media_module_test.info index f43c1df31..5fde716b9 100644 --- a/sites/all/modules/contrib/media/tests/media_module_test.info +++ b/sites/all/modules/contrib/media/tests/media_module_test.info @@ -6,9 +6,9 @@ hidden = TRUE files[] = includes/MediaModuleTest.inc -; Information added by Drupal.org packaging script on 2017-03-05 -version = "7.x-2.0-rc12" +; Information added by Drupal.org packaging script on 2017-05-25 +version = "7.x-2.4" core = "7.x" project = "media" -datestamp = "1488724088" +datestamp = "1495749189" diff --git a/sites/all/modules/contrib/mimemail/CHANGELOG.txt b/sites/all/modules/contrib/mimemail/CHANGELOG.txt index a35cb6e5b..a7e42b9f9 100644 --- a/sites/all/modules/contrib/mimemail/CHANGELOG.txt +++ b/sites/all/modules/contrib/mimemail/CHANGELOG.txt @@ -1,6 +1,24 @@ Mime Mail 7.x-1.x, xxxx-xx-xx ----------------------- +Mime Mail 7.x-1.0, 2017-05-14 +----------------------- +- #2743229 by Bird-Kid, AdamPS: CSS doesn't get attached in PHP7 +- #2858390 by Munavijayalakshmi, Jigar.addweb: Fix coding standard issues +- #2594743 by skipyT: Theme function called for plain messages also +- #2374673 by smokris: Prevent processing of already embedded images +- #1532352 by sgabe, Matt B: Add permission to view user specific settings +- #2783815 by argiepiano: Add 'Reply-to' field to "Send HTML mail to all users of a role" action +- #2796993 by Cauliflower: Return send status in Rules actions +- #2796965 by Cauliflower: Allow NULL values in Rules actions +- #1568680 by jamsilver: Use $message for themeing +- #2721799 by igorik, sgabe: Minor typo +- #2146513 by torotil, Anybody: Scan theme for other *css* file types +- #2678818 by hoebekewim, das-peter: Mime Mail Compress has a deprecated constructor +- #2553815 by nitrocad, anthonys: Imported font generates empty attachment +- #2562181 by rrfegade: Remove unused varibles +- #2562177 by rrfegade: Spelling errors + Mime Mail 7.x-1.0-beta4, 2015-08-02 ----------------------- - #2413495 by sgabe, siggi_kid: Public images not embedded when file default scheme is private diff --git a/sites/all/modules/contrib/mimemail/README.txt b/sites/all/modules/contrib/mimemail/README.txt index 1bb6aa47b..804e73d43 100644 --- a/sites/all/modules/contrib/mimemail/README.txt +++ b/sites/all/modules/contrib/mimemail/README.txt @@ -2,7 +2,7 @@ -- SUMMARY -- This is a Mime Mail component module (for use by other modules). - * It permits users to recieve HTML email and can be used by other modules. The mail + * It permits users to receieve HTML email and can be used by other modules. The mail functionality accepts an HTML message body, mime-endcodes it and sends it. * If the HTML has embedded graphics, these graphics are MIME-encoded and included as a message attachment. diff --git a/sites/all/modules/contrib/mimemail/includes/mimemail.admin.inc b/sites/all/modules/contrib/mimemail/includes/mimemail.admin.inc index ca71e54e3..7155e79a5 100644 --- a/sites/all/modules/contrib/mimemail/includes/mimemail.admin.inc +++ b/sites/all/modules/contrib/mimemail/includes/mimemail.admin.inc @@ -85,7 +85,7 @@ function mimemail_admin_settings() { '#access' => count($formats) > 1, '#attributes' => array('class' => array('filter-list')), '#description' => t('The filter set that will be applied to the message body. - If you are using Mime Mail as default mail sytem, make sure to enable + If you are using Mime Mail as default mail system, make sure to enable "Convert line breaks into HTML" and "Convert URLs into links" with a long enough maximum length for e.g. password reset URLs!'), ); diff --git a/sites/all/modules/contrib/mimemail/includes/mimemail.incoming.inc b/sites/all/modules/contrib/mimemail/includes/mimemail.incoming.inc index ff1d43f7e..43f98eb4a 100644 --- a/sites/all/modules/contrib/mimemail/includes/mimemail.incoming.inc +++ b/sites/all/modules/contrib/mimemail/includes/mimemail.incoming.inc @@ -75,13 +75,13 @@ function mimemail_parse($message) { // We're dealing with a multi-part message. $mail['parts'] = mimemail_parse_boundary($mail); - foreach ($mail['parts'] as $i => $part_body) { + foreach ($mail['parts'] as $part_body) { $part = mimemail_parse_headers($part_body); $sub_parts = mimemail_parse_boundary($part); // Content is encoded in a multipart/alternative section. if (count($sub_parts) > 1) { - foreach ($sub_parts as $j => $sub_part_body) { + foreach ($sub_parts as $sub_part_body) { $sub_part = mimemail_parse_headers($sub_part_body); if ($sub_part['content-type'] == 'text/plain') { $mail['text'] = mimemail_parse_content($sub_part); diff --git a/sites/all/modules/contrib/mimemail/includes/mimemail.mail.inc b/sites/all/modules/contrib/mimemail/includes/mimemail.mail.inc index 04774faab..28bebfc80 100644 --- a/sites/all/modules/contrib/mimemail/includes/mimemail.mail.inc +++ b/sites/all/modules/contrib/mimemail/includes/mimemail.mail.inc @@ -29,7 +29,6 @@ class MimeMailSystem implements MailSystemInterface { } $engine = variable_get('mimemail_engine', 'mimemail'); - $mailengine = $engine . '_mailengine'; $engine_prepare_message = $engine . '_prepare_message'; if (function_exists($engine_prepare_message)) { diff --git a/sites/all/modules/contrib/mimemail/mimemail.inc b/sites/all/modules/contrib/mimemail/mimemail.inc index 3dbb0052d..dd7847ff8 100644 --- a/sites/all/modules/contrib/mimemail/mimemail.inc +++ b/sites/all/modules/contrib/mimemail/mimemail.inc @@ -112,7 +112,7 @@ function mimemail_headers($headers, $from = NULL) { * ) */ function mimemail_extract_files($html) { - $pattern = '/(]+href=[\'"]?|]+codebase=[\'"]?|@import |[\s]src=[\'"]?)([^\'>"]+)([\'"]?)/mis'; + $pattern = '/(]+href=[\'"]?|]+codebase=[\'"]?|@import (?:url\()?[\'"]?|[\s]src=[\'"]?)([^\'>")]+)([\'"]?)/mis'; $content = preg_replace_callback($pattern, '_mimemail_replace_files', $html); $encoding = '8Bit'; @@ -177,7 +177,7 @@ function _mimemail_file($url = NULL, $content = NULL, $name = '', $type = '', $d $url = _mimemail_url($url, 'TRUE'); // The $url is absolute, we're done here. $scheme = file_uri_scheme($url); - if ($scheme == 'http' || $scheme == 'https' || preg_match('!mailto:!', $url)) { + if ($scheme == 'http' || $scheme == 'https' || preg_match('!mailto:!', $url) || preg_match('!^data:!', $url)) { return $url; } // The $url is a non-local URI that needs to be converted to a URL. @@ -209,11 +209,11 @@ function _mimemail_file($url = NULL, $content = NULL, $name = '', $type = '', $d $type = $is_file ? file_get_mimetype($file) : file_get_mimetype($name); } - $id = md5($file) .'@'. $_SERVER['HTTP_HOST']; + $id = md5($file) . '@' . $_SERVER['HTTP_HOST']; // Prevent duplicate items. if (isset($ids[$id])) { - return 'cid:'. $ids[$id]; + return 'cid:' . $ids[$id]; } $new_file = array( @@ -449,7 +449,6 @@ function mimemail_html_body($body, $subject, $plain = FALSE, $plaintext = NULL, * A processed URL. */ function _mimemail_url($url, $to_embed = NULL) { - global $base_url; $url = urldecode($url); $to_link = variable_get('mimemail_linkonly', 0); @@ -541,7 +540,6 @@ function mimemail_address($address, $simplify = FALSE) { if (is_array($address)) { // It's an array containing 'mail' and/or 'name'. if (isset($address['mail'])) { - $output = ''; if (empty($address['name']) || $simplify) { return $address['mail']; } diff --git a/sites/all/modules/contrib/mimemail/mimemail.info b/sites/all/modules/contrib/mimemail/mimemail.info index 2d5d028f3..dc167376b 100644 --- a/sites/all/modules/contrib/mimemail/mimemail.info +++ b/sites/all/modules/contrib/mimemail/mimemail.info @@ -14,9 +14,9 @@ files[] = tests/mimemail.test files[] = tests/mimemail_rules.test files[] = tests/mimemail_compress.test -; Information added by Drupal.org packaging script on 2015-08-02 -version = "7.x-1.0-beta4" +; Information added by Drupal.org packaging script on 2017-05-14 +version = "7.x-1.0" core = "7.x" project = "mimemail" -datestamp = "1438530555" +datestamp = "1494775689" diff --git a/sites/all/modules/contrib/mimemail/mimemail.module b/sites/all/modules/contrib/mimemail/mimemail.module index 21164b8b6..0b3937454 100644 --- a/sites/all/modules/contrib/mimemail/mimemail.module +++ b/sites/all/modules/contrib/mimemail/mimemail.module @@ -35,6 +35,10 @@ function mimemail_menu() { */ function mimemail_permission() { return array( + 'view mimemail user settings' => array( + 'title' => t('View Mime Mail user settings'), + 'description' => t('View user specific settings for Mime Mail.'), + ), 'edit mimemail user settings' => array( 'title' => t('Edit Mime Mail user settings'), 'description' => t('Edit user specific settings for Mime Mail.'), @@ -85,6 +89,7 @@ function mimemail_user_view($account, $view_mode, $langcode) { $account->content['mimemail'] = array( '#type' => 'user_profile_category', '#title' => t('Email'), + '#access' => user_access('view mimemail user settings'), ); $account->content['mimemail']['textonly'] = array( @@ -359,7 +364,7 @@ function mimemail_prepare_message($message) { $hook = array( 'mimemail_message__' . $key, - 'mimemail_message__' . $module .'__'. $key, + 'mimemail_message__' . $module . '__' . $key, ); $variables = array( @@ -367,10 +372,13 @@ function mimemail_prepare_message($message) { 'key' => $key, 'recipient' => $to, 'subject' => $subject, - 'body' => $body + 'body' => $body, + 'message' => $message ); - $body = theme($hook, $variables); + if (!$plain) { + $body = theme($hook, $variables); + } foreach (module_implements('mail_post_process') as $module) { $function = $module . '_mail_post_process'; diff --git a/sites/all/modules/contrib/mimemail/mimemail.rules.inc b/sites/all/modules/contrib/mimemail/mimemail.rules.inc index 29a6dee5c..53716e31f 100644 --- a/sites/all/modules/contrib/mimemail/mimemail.rules.inc +++ b/sites/all/modules/contrib/mimemail/mimemail.rules.inc @@ -32,35 +32,40 @@ function mimemail_rules_action_info() { 'label' => t('CC Recipient'), 'description' => t("The mail's carbon copy address. You may separate multiple addresses with comma."), 'optional' => TRUE, + 'allow null' => TRUE, ), 'bcc' => array( 'type' => 'text', 'label' => t('BCC Recipient'), 'description' => t("The mail's blind carbon copy address. You may separate multiple addresses with comma."), 'optional' => TRUE, + 'allow null' => TRUE, ), 'from_name' => array( 'type' => 'text', 'label' => t('Sender name'), 'description' => t("The sender's name. Leave it empty to use the site-wide configured name."), 'optional' => TRUE, + 'allow null' => TRUE, ), 'from_mail' => array( 'type' => 'text', 'label' => t('Sender e-mail address'), 'description' => t("The sender's address. Leave it empty to use the site-wide configured address."), 'optional' => TRUE, + 'allow null' => TRUE, ), 'reply_to' => array( 'type' => 'text', 'label' => t('Reply e-mail address'), 'description' => t("The address to reply to. Leave it empty to use the sender's address."), 'optional' => TRUE, + 'allow null' => TRUE, ), 'list_unsubscribe' => array( 'type' => 'text', 'label' => t('Unsubscription e-mail and/or URL'), - 'description' => t("An e-mail address and/or a URL which can be used for unsubscription. Values must be enclosed by angel brackets and separated by a comma."), + 'description' => t("An e-mail address and/or a URL which can be used for unsubscription. Values must be enclosed by angle brackets and separated by a comma."), 'optional' => TRUE, ), 'subject' => array( @@ -100,6 +105,12 @@ function mimemail_rules_action_info() { 'default mode' => 'selector', ), ), + 'provides' => array( + 'send_status' => array( + 'type' => 'boolean', + 'label' => t('Send status'), + ), + ), 'base' => 'rules_action_mimemail', 'access callback' => 'rules_system_integration_access', ), @@ -120,7 +131,7 @@ function mimemail_rules_action_info() { ), 'active' => array( 'type' => 'boolean', - 'label' =>('Send to active users'), + 'label' => t('Send to active users'), 'description' => t('Send mail only to active users.'), ), 'from_name' => array( @@ -128,12 +139,21 @@ function mimemail_rules_action_info() { 'label' => t('Sender name'), 'description' => t("The sender's name. Leave it empty to use the site-wide configured name."), 'optional' => TRUE, + 'allow null' => TRUE, ), 'from_mail' => array( 'type' => 'text', 'label' => t('Sender e-mail address'), 'description' => t("The sender's address. Leave it empty to use the site-wide configured address."), 'optional' => TRUE, + 'allow null' => TRUE, + ), + 'reply_to' => array( + 'type' => 'text', + 'label' => t('Reply e-mail address'), + 'description' => t("The address to reply to. Leave it empty to use the sender's address."), + 'optional' => TRUE, + 'allow null' => TRUE, ), 'subject' => array( 'type' => 'text', @@ -176,6 +196,12 @@ function mimemail_rules_action_info() { 'default mode' => 'selector', ), ), + 'provides' => array( + 'send_status' => array( + 'type' => 'boolean', + 'label' => t('Send status'), + ), + ), 'base' => 'rules_action_mimemail_to_users_of_role', 'access callback' => 'rules_system_integration_access', ), @@ -279,13 +305,15 @@ function rules_action_mimemail($key, $to, $cc = NULL, $bcc = NULL, $from_name = 'attachments' => $attachments, ); - drupal_mail('mimemail', $key, $to, $language, $params, $from); + $message = drupal_mail('mimemail', $key, $to, $language, $params, $from); + + return array('send_status' => !empty($message['result'])); } /** * Action: Send HTML mail to all users of a specific role group(s). */ -function rules_action_mimemail_to_users_of_role($key, $roles, $active, $from_name = NULL, $from_mail = NULL, $subject, $body, $plaintext = NULL, $attachments = array(), $use_userlang = FALSE, $langcode= NULL, $settings, RulesState $state, RulesPlugin $element) { +function rules_action_mimemail_to_users_of_role($key, $roles, $active, $from_name = NULL, $from_mail = NULL, $reply_to = NULL, $subject, $body, $plaintext = NULL, $attachments = array(), $use_userlang = FALSE, $langcode= NULL, $settings, RulesState $state, RulesPlugin $element) { module_load_include('inc', 'mimemail'); // Set the sender name and from address. @@ -326,6 +354,7 @@ function rules_action_mimemail_to_users_of_role($key, $roles, $active, $from_nam 'action' => $element, 'state' => $state, ), + 'reply-to' => $reply_to, 'plaintext' => $plaintext, 'attachments' => $attachments, ); @@ -352,6 +381,8 @@ function rules_action_mimemail_to_users_of_role($key, $roles, $active, $from_nam $role_names = array_intersect_key(user_roles(TRUE), array_flip($roles)); watchdog('rules', 'Successfully sent HTML email to the role(s) %roles.', array('%roles' => implode(', ', $role_names))); } + + return array('send_status' => !empty($message['result'])); } /** diff --git a/sites/all/modules/contrib/mimemail/modules/mimemail_action/mimemail_action.info b/sites/all/modules/contrib/mimemail/modules/mimemail_action/mimemail_action.info index e6ede6a84..fd7c9af5a 100644 --- a/sites/all/modules/contrib/mimemail/modules/mimemail_action/mimemail_action.info +++ b/sites/all/modules/contrib/mimemail/modules/mimemail_action/mimemail_action.info @@ -6,9 +6,9 @@ dependencies[] = trigger core = 7.x -; Information added by Drupal.org packaging script on 2015-08-02 -version = "7.x-1.0-beta4" +; Information added by Drupal.org packaging script on 2017-05-14 +version = "7.x-1.0" core = "7.x" project = "mimemail" -datestamp = "1438530555" +datestamp = "1494775689" diff --git a/sites/all/modules/contrib/mimemail/modules/mimemail_action/mimemail_action.module b/sites/all/modules/contrib/mimemail/modules/mimemail_action/mimemail_action.module index 5ad51d61b..9d690d1fd 100644 --- a/sites/all/modules/contrib/mimemail/modules/mimemail_action/mimemail_action.module +++ b/sites/all/modules/contrib/mimemail/modules/mimemail_action/mimemail_action.module @@ -26,7 +26,8 @@ function mimemail_send_email_action($entity, $context) { if (empty($context['node'])) { if (get_class($entity) == 'OgMembership') { $context['user'] = user_load($entity->etid); - } else { + } + else { $context['node'] = $entity; } } diff --git a/sites/all/modules/contrib/mimemail/modules/mimemail_compress/mimemail_compress.inc b/sites/all/modules/contrib/mimemail/modules/mimemail_compress/mimemail_compress.inc index dcf3f0aa9..ad5432e89 100644 --- a/sites/all/modules/contrib/mimemail/modules/mimemail_compress/mimemail_compress.inc +++ b/sites/all/modules/contrib/mimemail/modules/mimemail_compress/mimemail_compress.inc @@ -32,7 +32,7 @@ class mimemail_compress { private $css = ''; private $unprocessable_tags = array('wbr'); - public function mimemail_compress($html = '', $css = '') { + public function __construct($html = '', $css = '') { $this->html = $html; $this->css = $css; } @@ -144,7 +144,7 @@ class mimemail_compress { $node->setAttribute('style', $style); // Convert float to align for images. - $float = preg_match ('/float:(left|right)/', $style, $matches); + $float = preg_match('/float:(left|right)/', $style, $matches); if ($node->nodeName == 'img' && $float) { $node->setAttribute('align', $matches[1]); $node->setAttribute('vspace', 5); @@ -196,6 +196,23 @@ class mimemail_compress { return $precedence; } + /** + * Replace callback function that matches ID attributes. + */ + private static function replace_id_attributes($m) { + return (strlen($m[1]) ? $m[1] : '*') . '[@id="' . $m[2] . '"]'; + } + + /** + * Replace callback function that matches class attributes. + */ + private static function replace_class_attributes($m) { + return (strlen($m[1]) ? $m[1] : '*') . + '[contains(concat(" ",normalize-space(@class)," "),concat(" ","' . + implode('"," "))][contains(concat(" ",normalize-space(@class)," "),concat(" ","', explode('.', substr($m[2], 1))) . + '"," "))]'; + } + /** * Right now we only support CSS 1 selectors, but include CSS2/3 selectors are fully possible. * @@ -206,6 +223,7 @@ class mimemail_compress { // Already an XPath expression. return $selector; } + // Returns an Xpath selector. $search = array( '/\s+>\s+/', // Matches any F element that is a child of an element E. @@ -213,8 +231,6 @@ class mimemail_compress { '/\s+/', // Matches any F element that is a descendant of an E element. '/(\w)\[(\w+)\]/', // Matches element with attribute. '/(\w)\[(\w+)\=[\'"]?(\w+)[\'"]?\]/', // Matches element with EXACT attribute. - '/(\w+)?\#([\w\-]+)/e', // Matches id attributes. - '/(\w+|\*)?((\.[\w\-]+)+)/e', // Matches class attributes. ); $replace = array( '/', @@ -222,10 +238,13 @@ class mimemail_compress { '//', '\\1[@\\2]', '\\1[@\\2="\\3"]', - "(strlen('\\1') ? '\\1' : '*').'[@id=\"\\2\"]'", - "(strlen('\\1') ? '\\1' : '*').'[contains(concat(\" \",normalize-space(@class),\" \"),concat(\" \",\"'.implode('\",\" \"))][contains(concat(\" \",normalize-space(@class),\" \"),concat(\" \",\"',explode('.',substr('\\2',1))).'\",\" \"))]'", ); - return '//' . preg_replace($search, $replace, trim($selector)); + + $result = preg_replace($search, $replace, trim($selector)); + $result = preg_replace_callback('/(\w+)?\#([\w\-]+)/', 'mimemail_compress::replace_id_attributes', $result); + $result = preg_replace_callback('/(\w+|\*)?((\.[\w\-]+)+)/', 'mimemail_compress::replace_class_attributes', $result); + + return '//' . $result; } private function css_style_to_array($style) { diff --git a/sites/all/modules/contrib/mimemail/modules/mimemail_compress/mimemail_compress.info b/sites/all/modules/contrib/mimemail/modules/mimemail_compress/mimemail_compress.info index 4733b2142..4c158295a 100644 --- a/sites/all/modules/contrib/mimemail/modules/mimemail_compress/mimemail_compress.info +++ b/sites/all/modules/contrib/mimemail/modules/mimemail_compress/mimemail_compress.info @@ -6,9 +6,9 @@ core = 7.x files[] = mimemail_compress.inc -; Information added by Drupal.org packaging script on 2015-08-02 -version = "7.x-1.0-beta4" +; Information added by Drupal.org packaging script on 2017-05-14 +version = "7.x-1.0" core = "7.x" project = "mimemail" -datestamp = "1438530555" +datestamp = "1494775689" diff --git a/sites/all/modules/contrib/mimemail/modules/mimemail_example/mimemail_example.info b/sites/all/modules/contrib/mimemail/modules/mimemail_example/mimemail_example.info index 649bc3a6a..ac3a01fda 100644 --- a/sites/all/modules/contrib/mimemail/modules/mimemail_example/mimemail_example.info +++ b/sites/all/modules/contrib/mimemail/modules/mimemail_example/mimemail_example.info @@ -4,9 +4,9 @@ dependencies[] = mimemail package = Example modules core = 7.x -; Information added by Drupal.org packaging script on 2015-08-02 -version = "7.x-1.0-beta4" +; Information added by Drupal.org packaging script on 2017-05-14 +version = "7.x-1.0" core = "7.x" project = "mimemail" -datestamp = "1438530555" +datestamp = "1494775689" diff --git a/sites/all/modules/contrib/mimemail/tests/mimemail.test b/sites/all/modules/contrib/mimemail/tests/mimemail.test index 5896a3c13..661984140 100644 --- a/sites/all/modules/contrib/mimemail/tests/mimemail.test +++ b/sites/all/modules/contrib/mimemail/tests/mimemail.test @@ -31,7 +31,7 @@ class MimeMailUnitTestCase extends DrupalUnitTestCase { $chars = array('-', '.', '+', '_'); $name = $this->randomString(); $local = $this->randomName() . $chars[array_rand($chars)] . $this->randomName(); - $domain = $this->randomName() . '-' . $this->randomName() . '.' . $this->randomName(rand(2,4)); + $domain = $this->randomName() . '-' . $this->randomName() . '.' . $this->randomName(rand(2, 4)); $headers = mimemail_headers(array(), "$name <$local@$domain>"); $result = $headers['Return-Path']; $expected = "<$local@$domain>"; @@ -47,7 +47,7 @@ class MimeMailUnitTestCase extends DrupalUnitTestCase { $expected = 'sites/default/files/styles/thumbnail/public/image.jpg'; $this->assertIdentical($result, $expected, 'Security token removed from styled image URL.'); - $expected = $url = 'public://' . $this->randomName() . ' '. $this->randomName() . '.' . $this->randomName(3); + $expected = $url = 'public://' . $this->randomName() . ' ' . $this->randomName() . '.' . $this->randomName(3); $result = _mimemail_url($url, TRUE); $this->assertIdentical($result, $expected, 'Space in the filename of the attachment left intact.'); } @@ -89,7 +89,7 @@ class MimeMailWebTestCase extends DrupalWebTestCase { array('mimemail_linkonly' => TRUE), t('Save configuration')); - $url = 'public://' . $this->randomName() . ' '. $this->randomName() . '.jpg'; + $url = 'public://' . $this->randomName() . ' ' . $this->randomName() . '.jpg'; $result = _mimemail_url($url, TRUE); $expected = str_replace(' ', '%20', file_create_url($url)); $message = 'Stream wrapper converted to web accessible URL for linked image.'; diff --git a/sites/all/modules/contrib/mimemail/theme/mimemail.theme.inc b/sites/all/modules/contrib/mimemail/theme/mimemail.theme.inc index 2a2df7cff..36dc8189c 100644 --- a/sites/all/modules/contrib/mimemail/theme/mimemail.theme.inc +++ b/sites/all/modules/contrib/mimemail/theme/mimemail.theme.inc @@ -10,7 +10,7 @@ function mimemail_theme_theme() { return array( 'mimemail_message' => array( - 'variables' => array('module' => NULL, 'key' => NULL, 'recipient' => NULL, 'subject' => NULL, 'body' => NULL), + 'variables' => array('module' => NULL, 'key' => NULL, 'recipient' => NULL, 'subject' => NULL, 'body' => NULL, 'message' => array()), 'template' => 'mimemail-message', 'pattern' => 'mimemail_message__', 'file' => 'mimemail.theme.inc', @@ -36,7 +36,7 @@ function template_preprocess_mimemail_message(&$variables) { $themepath = drupal_get_path('theme', $theme); $sitestyle = variable_get('mimemail_sitestyle', 1); - $mailstyles = file_scan_directory($themepath, '#^mail\.css*$#'); + $mailstyles = file_scan_directory($themepath, '#^mail(-.+)?\.(c|le|sc|sa)ss$#'); // Check recursively for the existence of a mail.css file in the theme folder. if (!empty($mailstyles)) { diff --git a/sites/all/modules/contrib/rules/includes/rules.core.inc b/sites/all/modules/contrib/rules/includes/rules.core.inc index 559d1cff7..04b174b9d 100644 --- a/sites/all/modules/contrib/rules/includes/rules.core.inc +++ b/sites/all/modules/contrib/rules/includes/rules.core.inc @@ -1805,16 +1805,22 @@ abstract class RulesAbstractPlugin extends RulesPlugin { } protected function getFileName($function, $includes) { - $reflector = new ReflectionFunction($function); - // On windows the path contains backslashes instead of slashes, fix that. - $file = str_replace('\\', '/', $reflector->getFileName()); - foreach ($includes as $include) { - $pos = strpos($file, $include . '.inc'); - // Test whether the file ends with the given filename.inc. - if ($pos !== FALSE && strlen($file) - $pos == strlen($include) + 4) { - return $include; + static $filenames; + if (!isset($filenames) || !array_key_exists($function, $filenames)) { + $filenames[$function] = NULL; + $reflector = new ReflectionFunction($function); + // On windows the path contains backslashes instead of slashes, fix that. + $file = str_replace('\\', '/', $reflector->getFileName()); + foreach ($includes as $include) { + $pos = strpos($file, $include . '.inc'); + // Test whether the file ends with the given filename.inc. + if ($pos !== FALSE && strlen($file) - $pos == strlen($include) + 4) { + $filenames[$function] = $include; + return $include; + } } } + return $filenames[$function]; } } diff --git a/sites/all/modules/contrib/rules/rules.info b/sites/all/modules/contrib/rules/rules.info index 0a744d5fd..4d9092b68 100644 --- a/sites/all/modules/contrib/rules/rules.info +++ b/sites/all/modules/contrib/rules/rules.info @@ -22,9 +22,9 @@ files[] = ui/ui.plugins.inc dependencies[] = entity_token dependencies[] = entity -; Information added by Drupal.org packaging script on 2015-03-16 -version = "7.x-2.9" +; Information added by Drupal.org packaging script on 2017-04-20 +version = "7.x-2.10" core = "7.x" project = "rules" -datestamp = "1426527210" +datestamp = "1492697958" diff --git a/sites/all/modules/contrib/rules/rules.install b/sites/all/modules/contrib/rules/rules.install index 8485df3b7..56bc0c53e 100644 --- a/sites/all/modules/contrib/rules/rules.install +++ b/sites/all/modules/contrib/rules/rules.install @@ -122,7 +122,7 @@ function rules_schema() { 'name' => array('name'), ), 'indexes' => array( - 'plugin' => array('plugin'), + 'plugin' => array('plugin', 'active'), ), ); $schema['rules_trigger'] = array( @@ -526,3 +526,13 @@ function rules_update_7214() { _rules_rebuild_component_cache(); RulesEventSet::rebuildEventCache(); } + +/** + * Add an index for retrieving active config of a certain plugin. + */ +function rules_update_7215() { + if (db_index_exists('rules_config', 'plugin')) { + db_drop_index('rules_config', 'plugin'); + } + db_add_index('rules_config', 'plugin', array('plugin', 'active')); +} diff --git a/sites/all/modules/contrib/rules/rules.module b/sites/all/modules/contrib/rules/rules.module index 719852c46..038288e86 100644 --- a/sites/all/modules/contrib/rules/rules.module +++ b/sites/all/modules/contrib/rules/rules.module @@ -366,9 +366,8 @@ function &rules_get_cache($cid = 'data') { // Prevent stampeding by ensuring the cache is rebuilt just once at the // same time. while (!lock_acquire(__FUNCTION__ . $cid . $cid_suffix, 60)) { - rules_log('Cache rebuild lock hit: !cid', array('!cid' => $cid), RulesLog::WARN); // Now wait until the lock is released. - lock_wait(__FUNCTION__ . $cid . $cid_suffix, 10); + lock_wait(__FUNCTION__ . $cid . $cid_suffix, 30); // If the lock is released it's likely the cache was rebuild. Thus check // again if we can fetch it from the persistent cache. if ($get = cache_get($cid . $cid_suffix, 'cache_rules')) { diff --git a/sites/all/modules/contrib/rules/rules.rules.inc b/sites/all/modules/contrib/rules/rules.rules.inc index 3beb76ebd..0595bd7a8 100644 --- a/sites/all/modules/contrib/rules/rules.rules.inc +++ b/sites/all/modules/contrib/rules/rules.rules.inc @@ -20,13 +20,22 @@ foreach (rules_core_modules() as $module) { * for providing some general stuff. */ function rules_core_modules() { - $return = array('data', 'entity', 'node', 'system', 'user', 'rules_core'); - foreach (array('comment', 'taxonomy', 'php', 'path') as $module) { - if (module_exists($module)) { - $return[] = $module; + // Make use of the fast, advanced drupal static pattern. + static $drupal_static_fast; + if (!isset($drupal_static_fast)) { + $drupal_static_fast = &drupal_static(__FUNCTION__); + } + $modules = &$drupal_static_fast; + + if (!isset($modules)) { + $modules = array('data', 'entity', 'node', 'system', 'user', 'rules_core'); + foreach (array('comment', 'taxonomy', 'php', 'path') as $module) { + if (module_exists($module)) { + $modules[] = $module; + } } } - return $return; + return $modules; } /** @@ -46,12 +55,20 @@ function _rules_rules_collect_items($hook) { * Implements hook_rules_file_info(). */ function rules_rules_file_info() { - $items = array(); - foreach (rules_core_modules() as $module) { - if (function_exists($function = "rules_{$module}_file_info")) { - $items = array_merge($items, (array)$function()); - // Automatically add "$module.rules.inc" for each module. - $items[] = 'modules/' . $module . '.rules'; + // Make use of the fast, advanced drupal static pattern. + static $drupal_static_fast; + if (!isset($drupal_static_fast)) { + $drupal_static_fast = &drupal_static(__FUNCTION__); + } + $items = &$drupal_static_fast; + if (!isset($items)) { + $items = array(); + foreach (rules_core_modules() as $module) { + if (function_exists($function = "rules_{$module}_file_info")) { + $items = array_merge($items, (array) $function()); + // Automatically add "$module.rules.inc" for each module. + $items[] = 'modules/' . $module . '.rules'; + } } } return $items; diff --git a/sites/all/modules/contrib/rules/rules_admin/rules_admin.info b/sites/all/modules/contrib/rules/rules_admin/rules_admin.info index 350c0e3aa..f30401fa9 100644 --- a/sites/all/modules/contrib/rules/rules_admin/rules_admin.info +++ b/sites/all/modules/contrib/rules/rules_admin/rules_admin.info @@ -6,9 +6,9 @@ files[] = rules_admin.module files[] = rules_admin.inc dependencies[] = rules -; Information added by Drupal.org packaging script on 2015-03-16 -version = "7.x-2.9" +; Information added by Drupal.org packaging script on 2017-04-20 +version = "7.x-2.10" core = "7.x" project = "rules" -datestamp = "1426527210" +datestamp = "1492697958" diff --git a/sites/all/modules/contrib/rules/rules_i18n/rules_i18n.info b/sites/all/modules/contrib/rules/rules_i18n/rules_i18n.info index 297650e7f..2aff39133 100644 --- a/sites/all/modules/contrib/rules/rules_i18n/rules_i18n.info +++ b/sites/all/modules/contrib/rules/rules_i18n/rules_i18n.info @@ -7,9 +7,9 @@ core = 7.x files[] = rules_i18n.i18n.inc files[] = rules_i18n.rules.inc files[] = rules_i18n.test -; Information added by Drupal.org packaging script on 2015-03-16 -version = "7.x-2.9" +; Information added by Drupal.org packaging script on 2017-04-20 +version = "7.x-2.10" core = "7.x" project = "rules" -datestamp = "1426527210" +datestamp = "1492697958" diff --git a/sites/all/modules/contrib/rules/rules_scheduler/rules_scheduler.info b/sites/all/modules/contrib/rules/rules_scheduler/rules_scheduler.info index accdf0bc7..866c6fb60 100644 --- a/sites/all/modules/contrib/rules/rules_scheduler/rules_scheduler.info +++ b/sites/all/modules/contrib/rules/rules_scheduler/rules_scheduler.info @@ -13,9 +13,9 @@ files[] = includes/rules_scheduler.views_default.inc files[] = includes/rules_scheduler.views.inc files[] = includes/rules_scheduler_views_filter.inc -; Information added by Drupal.org packaging script on 2015-03-16 -version = "7.x-2.9" +; Information added by Drupal.org packaging script on 2017-04-20 +version = "7.x-2.10" core = "7.x" project = "rules" -datestamp = "1426527210" +datestamp = "1492697958" diff --git a/sites/all/modules/contrib/rules/rules_scheduler/tests/rules_scheduler_test.info b/sites/all/modules/contrib/rules/rules_scheduler/tests/rules_scheduler_test.info index 3ce5f8368..aaca105ec 100644 --- a/sites/all/modules/contrib/rules/rules_scheduler/tests/rules_scheduler_test.info +++ b/sites/all/modules/contrib/rules/rules_scheduler/tests/rules_scheduler_test.info @@ -5,9 +5,9 @@ core = 7.x files[] = rules_scheduler_test.inc hidden = TRUE -; Information added by Drupal.org packaging script on 2015-03-16 -version = "7.x-2.9" +; Information added by Drupal.org packaging script on 2017-04-20 +version = "7.x-2.10" core = "7.x" project = "rules" -datestamp = "1426527210" +datestamp = "1492697958" diff --git a/sites/all/modules/contrib/rules/tests/rules_test.info b/sites/all/modules/contrib/rules/tests/rules_test.info index b06516c18..1fc6094e6 100644 --- a/sites/all/modules/contrib/rules/tests/rules_test.info +++ b/sites/all/modules/contrib/rules/tests/rules_test.info @@ -6,9 +6,9 @@ files[] = rules_test.rules.inc files[] = rules_test.rules_defaults.inc hidden = TRUE -; Information added by Drupal.org packaging script on 2015-03-16 -version = "7.x-2.9" +; Information added by Drupal.org packaging script on 2017-04-20 +version = "7.x-2.10" core = "7.x" project = "rules" -datestamp = "1426527210" +datestamp = "1492697958" diff --git a/sites/all/modules/contrib/rules/tests/rules_test_invocation.info b/sites/all/modules/contrib/rules/tests/rules_test_invocation.info index 51aea33e3..a9ae0cba7 100644 --- a/sites/all/modules/contrib/rules/tests/rules_test_invocation.info +++ b/sites/all/modules/contrib/rules/tests/rules_test_invocation.info @@ -4,9 +4,9 @@ package = Testing core = 7.x hidden = TRUE -; Information added by Drupal.org packaging script on 2015-03-16 -version = "7.x-2.9" +; Information added by Drupal.org packaging script on 2017-04-20 +version = "7.x-2.10" core = "7.x" project = "rules" -datestamp = "1426527210" +datestamp = "1492697958" diff --git a/sites/all/modules/contrib/socialfield/css/socialfield.css b/sites/all/modules/contrib/socialfield/css/socialfield.css index dc387e044..9d544c34a 100644 --- a/sites/all/modules/contrib/socialfield/css/socialfield.css +++ b/sites/all/modules/contrib/socialfield/css/socialfield.css @@ -3,16 +3,17 @@ overflow: auto; padding: 0 !important; } + .social-links ul li { float: left; margin: 5px !important; padding: 0 !important; } + .social-links .icon { color: #FFF; font-size: 24px; padding: 8px 4px; - background-color: #4a6ea9; } @@ -20,6 +21,27 @@ border-radius: 50%; } +#socialfield-instance-settings-services-table span.instance-settings-table-service-name { + position: relative; + top: -3px; +} + +#socialfield-instance-settings-services-table tr { + height: 55px; +} + +#socialfield-instance-settings-services-table tr .social-links-instance-settings span { + margin-right: 5px; +} + +#socialfield-instance-settings-services-table tr .social-links-instance-settings { + float: left; +} + +#socialfield-table tr .social-links { + float: left; +} + /* Service-specific style */ .social-links .service-reddit .icon { padding: 8px 7px 8px 1px; @@ -72,3 +94,6 @@ .social-links .service-stumbleupon .icon { background-color: #f74425; } +.social-links .service-instagram .icon { + background-color: #3f729b; +} diff --git a/sites/all/modules/contrib/socialfield/font_icons/font_icons.info b/sites/all/modules/contrib/socialfield/font_icons/font_icons.info index 91a389d54..3bccff0ac 100644 --- a/sites/all/modules/contrib/socialfield/font_icons/font_icons.info +++ b/sites/all/modules/contrib/socialfield/font_icons/font_icons.info @@ -4,9 +4,9 @@ core = 7.x dependencies[] = libraries -; Information added by Drupal.org packaging script on 2014-12-04 -version = "7.x-1.4" +; Information added by Drupal.org packaging script on 2017-04-25 +version = "7.x-1.5" core = "7.x" project = "socialfield" -datestamp = "1417710812" +datestamp = "1493127246" diff --git a/sites/all/modules/contrib/socialfield/socialfield.info b/sites/all/modules/contrib/socialfield/socialfield.info index 3cab719a8..0ffc55f6e 100644 --- a/sites/all/modules/contrib/socialfield/socialfield.info +++ b/sites/all/modules/contrib/socialfield/socialfield.info @@ -4,9 +4,9 @@ core = 7.x package = Fields configure = admin/config/media/socialfield -; Information added by Drupal.org packaging script on 2014-12-04 -version = "7.x-1.4" +; Information added by Drupal.org packaging script on 2017-04-25 +version = "7.x-1.5" core = "7.x" project = "socialfield" -datestamp = "1417710812" +datestamp = "1493127246" diff --git a/sites/all/modules/contrib/socialfield/socialfield.install b/sites/all/modules/contrib/socialfield/socialfield.install index 8c4cbcd87..172342565 100644 --- a/sites/all/modules/contrib/socialfield/socialfield.install +++ b/sites/all/modules/contrib/socialfield/socialfield.install @@ -107,6 +107,11 @@ function socialfield_install() { 'icon' => 'icon-stumbleupon', 'validation_pattern' => '*stumbleupon.com/*', ), + 'instagram' => array( + 'name' => 'Instagram', + 'icon' => 'icon-instagram', + 'validation_pattern' => '*instagram.com/*', + ), ); variable_set('socialfield_services', $base_services); diff --git a/sites/all/modules/contrib/socialfield/socialfield.module b/sites/all/modules/contrib/socialfield/socialfield.module index 5d13c58b5..79a8f0685 100644 --- a/sites/all/modules/contrib/socialfield/socialfield.module +++ b/sites/all/modules/contrib/socialfield/socialfield.module @@ -122,7 +122,7 @@ function socialfield_add_service($form, $form_state) { ); $form['service_validation_pattern'] = array( '#type' => 'textarea', - '#description' => t('Enter the list of allowed urls separated by new line.
Leave empty to allow user input any urls.
The "*" character is a wildcard.
Example: facebook.com/* for any page on Facebook site.'), + '#description' => t('Enter the list of allowed urls separated by new line.
Leave empty to allow user input any urls.
The "*" character is a wildcard.
Example: *facebook.com/* for any page on Facebook site.'), '#title' => t('Url validation pattern'), ); $form['submit'] = array( @@ -197,7 +197,7 @@ function socialfield_edit_service($form, $form_state, $service = NULL) { ); $form['service_validation_pattern'] = array( '#type' => 'textarea', - '#description' => t('Enter the list of allowed urls separated by new line.
Leave empty to allow user input any urls.
The "*" character is a wildcard.
Example: facebook.com/* for any page on Facebook site.'), + '#description' => t('Enter the list of allowed urls separated by new line.
Leave empty to allow user input any urls.
The "*" character is a wildcard.
Example: *facebook.com/* for any page on Facebook site.'), '#title' => t('Url validation pattern'), '#default_value' => $services[$service]['validation_pattern'], ); @@ -278,6 +278,9 @@ function socialfield_field_info() { $displayed_services = array('twitter', 'googleplus', 'facebook'); $displayed_services = array_combine($displayed_services, $displayed_services); + $weight_values = range(-10, count($used_services) - 11); + $weights = array_combine($used_services, $weight_values); + return array( 'social_links_field' => array( 'label' => t('Social links'), @@ -289,6 +292,7 @@ function socialfield_field_info() { 'instance_settings' => array( 'used_services' => $used_services, 'services' => $displayed_services, + 'weights' => $weights, ), ), ); @@ -316,6 +320,11 @@ function socialfield_field_instance_settings_form($field, $instance) { foreach ($social_services as $name => $service) { $options[$name] = $service['name']; } + + // Removing services from instance settings that were deleted from module settings. + $used_services = &$instance['settings']['used_services']; + $used_services = array_intersect($used_services, array_keys($social_services)); + $form = array( '#type' => 'container', '#process' => array( @@ -334,14 +343,35 @@ function socialfield_field_instance_settings_form($field, $instance) { function _socialfield_field_instance_settings_form($form, &$form_state) { $form_state['#used_services'] = $form['#instance']['settings']['used_services']; $form_state['#services'] = $form['#instance']['settings']['services']; + $form_state['#weights'] = $form['#instance']['settings']['weights']; + + asort($form_state['#weights']); + $services = variable_get('socialfield_services'); $rows = array(); - foreach ($form['#options'] as $service_name => $s_name) { + foreach ($form_state['#weights'] as $service_name => $weight) { + // Checking if service was deleted from module configuration. + if (!isset($services[$service_name])) { + unset($form_state['#services'][$service_name]); + unset($form_state['#weights'][$service_name]); + continue; + } $use_service = in_array($service_name, $form_state['#used_services']); $display_service = in_array($service_name, $form_state['#services']); + + $service_markup = ''; + $row = array( 'service_name' => array( - '#markup' => $service_name, + '#type' => 'hidden', + '#value' => $service_name, + ), + 'name' => array( + '#markup' => $service_markup, ), 'used_services' => array( '#type' => 'checkbox', @@ -353,6 +383,13 @@ function _socialfield_field_instance_settings_form($form, &$form_state) { '#default_value' => $display_service, '#attributes' => array('class' => array('socialfield-table-displayed-service-checkbox')), ), + 'weight' => array( + '#type' => 'weight', + '#title' => t('Weight'), + '#title_display' => 'invisible', + '#default_value' => $weight, + '#attributes' => array('class' => array('services-order-weight')), + ), ); if ($display_service) { @@ -362,18 +399,21 @@ function _socialfield_field_instance_settings_form($form, &$form_state) { $rows[] = $row; } + $module_path = drupal_get_path('module', 'socialfield'); + // Theme this part of the form as a table. $form['table'] = array( '#theme' => 'socialfield_form_table', - '#header' => array(t('Service'), t('Used services'), t('Displayed services')), + '#header' => array(t('Service'), t('Used services'), t('Displayed services'), t('Weight')), 'rows' => $rows, '#attributes' => array('id' => 'socialfield-instance-settings-services-table'), '#element_validate' => array('_socialfield_validate_services'), + '#attached' => array( + 'js' => array($module_path . '/js/socialfield.js'), + 'css' => array($module_path . '/css/socialfield.css'), + ), ); - $path = drupal_get_path('module', 'socialfield'); - $form['#attached']['js'][] = $path . '/js/socialfield.js'; - return $form; } @@ -383,6 +423,7 @@ function _socialfield_field_instance_settings_form($form, &$form_state) { function _socialfield_validate_services($form, &$form_state) { $used_services = $form_state['#used_services']; $displayed_services = $form_state['#services']; + $weights = $form_state['#weights']; // Getting new instance settings. foreach ($form['rows'] as $key => &$row) { @@ -390,7 +431,7 @@ function _socialfield_validate_services($form, &$form_state) { continue; } - $service_name = $row['service_name']['#markup']; + $service_name = $row['service_name']['#value']; // Service checked, wanted to be used. if ($row['used_services']['#value']) { @@ -408,6 +449,8 @@ function _socialfield_validate_services($form, &$form_state) { else { unset($displayed_services[$service_name]); } + + $weights[$service_name] = $row['weight']['#value']; } if (!count($displayed_services)) { @@ -417,6 +460,7 @@ function _socialfield_validate_services($form, &$form_state) { // Setting new instance settings. $form_state['values']['instance']['settings']['used_services'] = $used_services; $form_state['values']['instance']['settings']['services'] = $displayed_services; + $form_state['values']['instance']['settings']['weights'] = $weights; unset($form_state['values']['instance']['settings']['table']); } @@ -426,28 +470,37 @@ function _socialfield_validate_services($form, &$form_state) { function theme_socialfield_form_table(&$variables) { // Getting the userful values. $form = $variables['form']; - $rows = $form['rows']; + $data = $form['rows']; $header = $form['#header']; $attributes = $form['#attributes']; - // Setup the structure to be rendered and returned. - $content = array( - '#theme' => 'table', - '#header' => $header, - '#rows' => array(), - '#attributes' => $attributes, - ); - // Traverse each row and each column in each row. - foreach (element_children($rows) as $row_index) { + $rows = array(); + foreach (element_children($data) as $row_index) { $row = array(); - foreach (element_children($rows[$row_index]) as $col_index) { - $row[] = drupal_render($rows[$row_index][$col_index]); + foreach (element_children($data[$row_index]) as $col_index) { + $element = $data[$row_index][$col_index]; + if (isset($element['#type']) && $element['#type'] == 'hidden') { + continue; + } + $row[] = drupal_render($data[$row_index][$col_index]); } - $content['#rows'][] = $row; + + $rows[] = array( + 'data' => $row, + 'class' => array('draggable'), + ); } - return drupal_render($content); + $output = theme('table', array( + 'header' => $header, + 'rows' => $rows, + 'attributes' => $attributes + )); + + drupal_add_tabledrag($attributes['id'], 'order', 'sibling', 'services-order-weight'); + + return $output; } /** @@ -549,13 +602,13 @@ function theme_socialfield_drag_components($vars) { $header = array(t('Social links'), '', '', ''); $rows = array(); $index = 0; + for ($i=0; $i<$element['#num_elements']; $i++) { while (!isset($element['element_' . $index])) { // There is no element with this index. Moving on to the next possible element. $index++; } $current_element = $element['element_' . $index]; - $index++; $rows[] = array( 'data' => array( @@ -571,6 +624,8 @@ function theme_socialfield_drag_components($vars) { 'class' => array('draggable'), 'weight' => $current_element['weight']['#value'], ); + + $index++; } // Sorting elements by their weight. @@ -582,7 +637,7 @@ function theme_socialfield_drag_components($vars) { 'attributes' => array( 'id' => 'socialfield-table' ), - )) . drupal_render($element['add_one_social']); + )) . drupal_render($element['add_one_social']) . '
' . drupal_render($element['description']) . '
'; } /** @@ -591,9 +646,12 @@ function theme_socialfield_drag_components($vars) { function socialfield_field_widget_form(&$form, &$form_state, $field, $instance, $langcode, $items, $delta, $element) { $value_key = key($field['columns']); + $all_services = variable_get('socialfield_services'); + // Settings to be accessed from ajax callback. $form_state['socialfield']['field_data'] = array( 'field_name' => $field['field_name'], + 'parents' => $element['#field_parents'], 'langcode' => $langcode, ); @@ -606,7 +664,8 @@ function socialfield_field_widget_form(&$form, &$form_state, $field, $instance, // Remove button was pressed. if (isset($form_state['clicked_button']['#name']) && isset($form_state['clicked_button']['#socialfield_delete'])) { - $remove_element = (int)end(explode('_', $form_state['clicked_button']['#name'])); + $element_array = explode('_', $form_state['clicked_button']['#name']); + $remove_element = (int)end($element_array); unset($form_state['values'][$field['field_name']][$langcode][$remove_element]); unset($form_state['services'][$remove_element]); } @@ -614,7 +673,7 @@ function socialfield_field_widget_form(&$form, &$form_state, $field, $instance, // Setting number of form elements. if (!isset($form_state['services'])) { if (count($items)) { - // Edit page. Showing all saved elements, not only the default ones. + // Edit page. Showing all saved elements. $form_state['num_elements'] = count($items) - 1; $form_state['services'] = array(); foreach ($items as $item) { @@ -628,7 +687,8 @@ function socialfield_field_widget_form(&$form, &$form_state, $field, $instance, } } - $element['#num_elements'] = $form_state['num_elements'] = count($form_state['services']); + // Setting the number of services on widget. + $form_state['num_elements'] = count($form_state['services']); // Creating wrapper and setting theme (table) on it. $element['social_buttons'] = array( @@ -639,8 +699,8 @@ function socialfield_field_widget_form(&$form, &$form_state, $field, $instance, '#num_elements' => $form_state['num_elements'], ); + // Getting maximum weight from existing services. $weights = array(-11); - // If there are items saved in database, get the maximum weight. if (!empty($items)) { foreach ($items as $item) { $weights[] = $item['weight']; @@ -651,6 +711,13 @@ function socialfield_field_widget_form(&$form, &$form_state, $field, $instance, // Creating form. foreach ($form_state['services'] as $i => $service) { + // Checking if service was deleted from module settings. + if (!isset($all_services[$service])) { + $form_state['num_elements']--; + $element['social_buttons']['#num_elements']--; + continue; + } + $element['social_buttons']['element_' . $i] = array( 'url' => array( '#type' => 'textfield', @@ -684,10 +751,10 @@ function socialfield_field_widget_form(&$form, &$form_state, $field, $instance, $element['#element_validate'] = array('socialfield_service_validate'); // Getting services for the select list. - $options = array_keys(variable_get('socialfield_services')); + $options = array_keys($all_services); $options = array_combine($options, $options); if (isset($instance['settings']['used_services'])) { - $options = $instance['settings']['used_services']; + $options = array_intersect($options, $instance['settings']['used_services']); } $options = array_diff($options, $form_state['services']); $options = array_map('ucfirst', $options); @@ -707,6 +774,11 @@ function socialfield_field_widget_form(&$form, &$form_state, $field, $instance, ), ); + // Add help description. + $element['social_buttons']['description'] = array( + '#markup' => $instance['description'], + ); + // Attaching css and js files. $path = drupal_get_path('module', 'socialfield'); $element['#attached']['js'][] = $path . '/js/socialfield.js'; @@ -721,8 +793,10 @@ function socialfield_field_widget_form(&$form, &$form_state, $field, $instance, function socialfield_remove_one_element_callback($form, $form_state) { $field_name = $form_state['socialfield']['field_data']['field_name']; $langcode = $form_state['socialfield']['field_data']['langcode']; + $parents = $form_state['socialfield']['field_data']['parents']; - return $form[$field_name][$langcode]['social_buttons']; + $return_form = drupal_array_get_nested_value($form, $parents); + return $return_form[$field_name][$langcode]['social_buttons']; } /** @@ -734,8 +808,10 @@ function socialfield_add_one_element_callback($form, $form_state) { } $field_name = $form_state['socialfield']['field_data']['field_name']; $langcode = $form_state['socialfield']['field_data']['langcode']; + $parents = $form_state['socialfield']['field_data']['parents']; - return $form[$field_name][$langcode]['social_buttons']; + $return_form = drupal_array_get_nested_value($form, $parents); + return $return_form[$field_name][$langcode]['social_buttons']; } /** @@ -751,7 +827,7 @@ function socialfield_service_validate($element, &$form_state) { $items['validate_link'] = FALSE; } - for ($i=0; $i<$element['#num_elements']; $i++) { + for ($i=0; $i < $element['social_buttons']['#num_elements']; $i++) { while (!isset($element['social_buttons']['element_' . $index])) { // There is no element with this index. Moving on to the next possible element. $index++; @@ -838,8 +914,13 @@ function socialfield_field_formatter_view($entity_type, $entity, $field, $instan switch ($display['type']) { case 'socialfield_formatter': uasort($items, 'drupal_sort_weight'); - $servicess = variable_get('socialfield_services'); + $services = variable_get('socialfield_services'); foreach ($items as $item) { + // Checking if the service was deleted. + if (!isset($services[$item['service']])) { + continue; + } + // Social link. $hidden_text = ''; if (isset($display['settings']['link_text']) && $display['settings']['link_text']) { @@ -847,7 +928,7 @@ function socialfield_field_formatter_view($entity_type, $entity, $field, $instan $hidden_text = '' . check_plain($item['service']) . ''; } $link = l( - '' . $hidden_text . '', + '' . $hidden_text . '', $item['url'], array( 'html' => TRUE, @@ -863,21 +944,21 @@ function socialfield_field_formatter_view($entity_type, $entity, $field, $instan ); } - // Label. - $label = array( - 'label' => $instance['label'], - 'mode' => $display['label'], - ); + // Checking if there are links to display. + if (!$list_items) { + return; + } // Make list from items. - $element = array( + $element[] = array( '#theme' => 'socialfield_formatter', '#items' => $list_items, '#field' => $field, - '#label' => $label, + '#attached' => array( + 'css' => array(drupal_get_path('module', 'socialfield') . '/css/socialfield.css'), + ), ); - $element['#attached']['css'][] = drupal_get_path('module', 'socialfield') . '/css/socialfield.css'; break; } @@ -933,8 +1014,11 @@ function theme_socialfield_formatter($variables) { */ function socialfield_form_field_ui_field_edit_form_alter(&$form, &$form_state) { if ($form['#field']['type'] == 'social_links_field') { - // Hide the cardinality setting on the field settings for social_links_field fields. + // Hide the cardinality setting on field instance settings for social_links_field fields. $form['field']['cardinality']['#default_value'] = FIELD_CARDINALITY_UNLIMITED; $form['field']['cardinality']['#disabled'] = TRUE; + + // Hide default value on field instance settings. + $form['instance']['default_value_widget']['#access'] = FALSE; } } diff --git a/sites/all/modules/contrib/views_data_export/images/csv.png b/sites/all/modules/contrib/views_data_export/images/csv.png index 732d9376c..ce028f0fc 100644 Binary files a/sites/all/modules/contrib/views_data_export/images/csv.png and b/sites/all/modules/contrib/views_data_export/images/csv.png differ diff --git a/sites/all/modules/contrib/views_data_export/images/doc.png b/sites/all/modules/contrib/views_data_export/images/doc.png index f12c9ef60..59e49c22f 100644 Binary files a/sites/all/modules/contrib/views_data_export/images/doc.png and b/sites/all/modules/contrib/views_data_export/images/doc.png differ diff --git a/sites/all/modules/contrib/views_data_export/images/txt.png b/sites/all/modules/contrib/views_data_export/images/txt.png index 6d282e2e6..75ec06fa5 100644 Binary files a/sites/all/modules/contrib/views_data_export/images/txt.png and b/sites/all/modules/contrib/views_data_export/images/txt.png differ diff --git a/sites/all/modules/contrib/views_data_export/images/xls.png b/sites/all/modules/contrib/views_data_export/images/xls.png index 2d256ca3b..4b5e44e20 100644 Binary files a/sites/all/modules/contrib/views_data_export/images/xls.png and b/sites/all/modules/contrib/views_data_export/images/xls.png differ diff --git a/sites/all/modules/contrib/views_data_export/images/xml.png b/sites/all/modules/contrib/views_data_export/images/xml.png index e677a2585..6eb3bec7a 100644 Binary files a/sites/all/modules/contrib/views_data_export/images/xml.png and b/sites/all/modules/contrib/views_data_export/images/xml.png differ diff --git a/sites/all/modules/contrib/views_data_export/plugins/views_data_export_plugin_display_export.inc b/sites/all/modules/contrib/views_data_export/plugins/views_data_export_plugin_display_export.inc index 9ce5721e0..54209d2af 100644 --- a/sites/all/modules/contrib/views_data_export/plugins/views_data_export_plugin_display_export.inc +++ b/sites/all/modules/contrib/views_data_export/plugins/views_data_export_plugin_display_export.inc @@ -524,20 +524,30 @@ class views_data_export_plugin_display_export extends views_plugin_display_feed // 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; + $query_plugin = get_class($this->view->query); + if ($query_plugin == 'views_plugin_query_default') { + 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; + } + } + elseif ($query_plugin == 'SearchApiViewsQuery') { + foreach ($result as $row) { + $item = unserialize($row->data); + $item->{$this->batched_execution_state->sandbox['weight_field_alias']} = $row->{$this->batched_execution_state->sandbox['weight_field_alias']}; + $this->view->result[] = $item; } - // Push the restored $item in the views result array. - $this->view->result[] = $item; } $this->view->_post_execute(); break; @@ -623,25 +633,31 @@ class views_data_export_plugin_display_export extends views_plugin_display_feed // 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; + $query_plugin = get_class($view->query); + if ($query_plugin == 'views_plugin_query_default') { + // 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; + 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(); } - $query = new $query_class(); - // Copy the query over: - foreach ($view->query as $property => $value) { - $query->$property = $value; + elseif ($query_plugin == 'SearchApiViewsQuery') { + $this->store_search_api_result(clone($view)); } - // Replace the query object. - $view->query = $query; - - $view->execute(); } /** @@ -846,6 +862,55 @@ class views_data_export_plugin_display_export extends views_plugin_display_feed $conn_info = Database::getConnectionInfo($name); return $conn_info['default']['driver']; } + + /** + * Based on views_data_export_plugin_query_default_batched::execute(). + */ + function store_search_api_result($view) { + $display_handler = &$view->display_handler; + $start = microtime(TRUE); + + try { + // Get all the view results. + $view->query->set_limit(NULL); + $view->query->set_offset(0); + $view->query->execute($view); + $weight_alias = 'vde_weight'; + $display_handler->batched_execution_state->sandbox['weight_field_alias'] = $weight_alias; + + $schema = array( + 'fields' => array( + $weight_alias => array('type' => 'int'), + 'data' => array('type' => 'blob'), + )); + + db_create_table($display_handler->index_tablename(), $schema); + + if (!empty($view->result)) { + $insert_query = db_insert($display_handler->index_tablename())->fields(array($weight_alias, 'data')); + $weight = 0; + foreach ($view->result as $item) { + $insert_query->values(array( + $weight_alias => $weight, + 'data' => serialize($item), + )); + $weight++; + } + $insert_query->execute(); + } + + $view->result = array(); + // 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(), $weight_alias, array($weight_alias)); + } + catch (Exception $e) { + $view->result = array(); + debug('Exception: ' . $e->getMessage()); + } + + $view->execute_time = microtime(TRUE) - $start; + } } class views_data_export_plugin_query_default_batched extends views_plugin_query_default { diff --git a/sites/all/modules/contrib/views_data_export/views_data_export.drush.inc b/sites/all/modules/contrib/views_data_export/views_data_export.drush.inc index 4d5c3cced..cd3fa982c 100644 --- a/sites/all/modules/contrib/views_data_export/views_data_export.drush.inc +++ b/sites/all/modules/contrib/views_data_export/views_data_export.drush.inc @@ -169,7 +169,9 @@ function drush_views_data_export($view_name, $display_id, $output_file) { } $arguments = drush_get_option('arguments', ''); - $arguments = explode(',', $arguments); + if(!empty($arguments) && is_array($arguments)) { + $arguments = explode(',', $arguments); + } if ($view->display_handler->is_batched()) { // This is a batched export, and needs to be handled as such. diff --git a/sites/all/modules/contrib/views_data_export/views_data_export.info b/sites/all/modules/contrib/views_data_export/views_data_export.info index c7bf2ded5..6fca8d004 100644 --- a/sites/all/modules/contrib/views_data_export/views_data_export.info +++ b/sites/all/modules/contrib/views_data_export/views_data_export.info @@ -22,9 +22,9 @@ files[] = "tests/txt_export.test" files[] = "tests/xls_export.test" files[] = "tests/xml_export.test" -; Information added by Drupal.org packaging script on 2016-09-20 -version = "7.x-3.1" +; Information added by Drupal.org packaging script on 2017-04-05 +version = "7.x-3.2" core = "7.x" project = "views_data_export" -datestamp = "1474360174" +datestamp = "1491379387" diff --git a/sites/all/modules/contrib/views_data_export/views_data_export.install b/sites/all/modules/contrib/views_data_export/views_data_export.install index 9bd0dd107..ba44a56dc 100644 --- a/sites/all/modules/contrib/views_data_export/views_data_export.install +++ b/sites/all/modules/contrib/views_data_export/views_data_export.install @@ -185,7 +185,12 @@ function views_data_export_requirements($phase) { 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); + try { + $max_allowed_packet = db_query('SHOW VARIABLES WHERE variable_name = :name', array(':name' => 'max_allowed_packet'))->fetchField(1); + } + catch (Exception $e) { + $max_allowed_packet = NULL; + } if (is_numeric($max_allowed_packet)) { if ($max_allowed_packet < (16 * 1024 * 1024)) { $requirements['views_data_export'] = array( diff --git a/sites/all/modules/contrib/views_data_export/views_data_export.module b/sites/all/modules/contrib/views_data_export/views_data_export.module index a616545aa..0a8c90b5d 100644 --- a/sites/all/modules/contrib/views_data_export/views_data_export.module +++ b/sites/all/modules/contrib/views_data_export/views_data_export.module @@ -114,7 +114,7 @@ function views_data_export_cron() { 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 + $expires = variable_get('views_data_export_gc_expires', DRUPAL_MAXIMUM_TEMP_FILE_AGE); } if (!isset($chunk)) { $chunk = variable_get('views_data_export_gc_chunk', 30); diff --git a/sites/all/modules/contrib/webform/components/date.inc b/sites/all/modules/contrib/webform/components/date.inc index f58421fb3..8b51c306f 100644 --- a/sites/all/modules/contrib/webform/components/date.inc +++ b/sites/all/modules/contrib/webform/components/date.inc @@ -70,7 +70,7 @@ function _webform_edit_date($component) { '#type' => 'radios', '#title' => t('Default value timezone'), '#default_value' => empty($component['extra']['timezone']) ? 'user' : $component['extra']['timezone'], - '#description' => t('If using relative dates for a default value (e.g. "today") base the current day on this timezone.'), + '#description' => t('If using relative dates for a default value (for example, "today") base the current day on this timezone.'), '#options' => array('user' => t('User timezone'), 'site' => t('Website timezone')), '#weight' => 2, '#access' => variable_get('configurable_timezones', 1), @@ -131,7 +131,7 @@ function _webform_edit_date($component) { } /** - * Implements hook_form_id_validate. + * Implements hook_form_id_validate(). * * Warns user about hiding all the date fields and not using the date picker. */ @@ -140,7 +140,7 @@ function _webform_edit_date_validate($form, &$form_state) { form_set_value($form['extra']['exclude'], array_filter(array_values($form_state['values']['extra']['exclude'])), $form_state); // Note that Drupal 7 doesn't support setting errors no checkboxes due to - // browser issues. @see https://www.drupal.org/node/222380 + // browser issues. See: https://www.drupal.org/node/222380 if (count($form_state['values']['extra']['exclude']) == 3) { form_set_error('extra][exclude', 'The day, month and year can\'t all be hidden.'); } @@ -155,7 +155,7 @@ function _webform_edit_date_validate($form, &$form_state) { foreach (array('start_date', 'end_date') as $field) { if (trim($form_state['values']['extra'][$field]) && !($date[$field] = webform_strtodate('c', $form_state['values']['extra'][$field]))) { form_set_error("extra][$field", t('The @field could not be interpreted in GNU Date Input Format.', - array('@field' => $form['validation'][$field]['#title']))); + array('@field' => $form['validation'][$field]['#title']))); } } if (!empty($date['start_date']) && !empty($date['end_date']) && $date['end_date'] < $date['start_date']) { @@ -213,7 +213,7 @@ function _webform_render_date($component, $value = NULL, $filter = TRUE, $submis */ function webform_expand_date($element) { $timezone = $element['#timezone'] != 'user' ? NULL : 'user'; - + // Accept a string or array value for #default_value. if (!empty($element['#default_value']) && is_string($element['#default_value'])) { $timestring = webform_strtodate('c', $element['#default_value'], $timezone); @@ -281,7 +281,7 @@ function webform_expand_date($element) { } else { $month_options = array_intersect_key($month_options, array_flip(range($range['start']['month'], 12))) + - array_intersect_key($month_options, array_flip(range(1, $range['end']['month']))); + array_intersect_key($month_options, array_flip(range(1, $range['end']['month']))); } unset($month_options); // Drop PHP reference. if ($delta_months <= 1) { @@ -297,7 +297,7 @@ function webform_expand_date($element) { // Range spans two months and at least one day would be omitted. $days_in_month = date('t', mktime(0, 0, 0, $range['start']['month'], 1, $range['start']['year'])); $day_options = array_intersect_key($day_options, array_flip(range($range['start']['day'], $days_in_month))) + - array_intersect_key($day_options, array_flip(range(1, $range['end']['day']))); + array_intersect_key($day_options, array_flip(range(1, $range['end']['day']))); } unset($day_options); // Drop PHP reference. } @@ -423,6 +423,11 @@ function webform_validate_date($element, $form_state) { return; } + // Ensure date is made up of integers. + foreach (array('year', 'month', 'day') as $date_part) { + $element[$date_part]['#value'] = (int) $element[$date_part]['#value']; + } + // Check for a valid date. if (!checkdate($element['month']['#value'], $element['day']['#value'], $element['year']['#value'])) { form_error($element, t('Entered !name is not a valid date.', array('!name' => $element['#title']))); diff --git a/sites/all/modules/contrib/webform/components/email.inc b/sites/all/modules/contrib/webform/components/email.inc index bb8496000..e22d32891 100644 --- a/sites/all/modules/contrib/webform/components/email.inc +++ b/sites/all/modules/contrib/webform/components/email.inc @@ -176,7 +176,7 @@ function _webform_render_email($component, $value = NULL, $filter = TRUE, $submi // pattern validation. This means that long format email addresses must be // rendered as text. $element['#attributes']['type'] = 'text'; - + // html5 patterns have implied delimters and start and end patterns. // The are also case sensitive, not global, and not multi-line. // See https://developer.mozilla.org/en-US/docs/Web/Guide/HTML/HTML5/Constraint_validation @@ -255,11 +255,11 @@ function _webform_validate_email($form_element, &$form_state) { $component = $form_element['#webform_component']; $format = webform_variable_get('webform_email_address_format') == 'long' ? $component['extra']['format'] : 'short'; webform_email_validate($form_element['#value'], - implode('][', $form_element ['#parents']), - TRUE, // Required validation is done elsewhere. - $component['extra']['multiple'], - FALSE, // No tokens are allowed in user input. - $format); + implode('][', $form_element ['#parents']), + TRUE, // Required validation is done elsewhere. + $component['extra']['multiple'], + FALSE, // No tokens are allowed in user input. + $format); } /** diff --git a/sites/all/modules/contrib/webform/components/file.inc b/sites/all/modules/contrib/webform/components/file.inc index f22b78526..bb2bc4359 100644 --- a/sites/all/modules/contrib/webform/components/file.inc +++ b/sites/all/modules/contrib/webform/components/file.inc @@ -222,12 +222,12 @@ function _webform_edit_file_size_validate($element) { if (!empty($element['#value'])) { $set_filesize = parse_size($element['#value']); if ($set_filesize == FALSE) { - form_error($element, t('File size @value is not a valid filesize. Use a value such as 2 MB or 800 KB.', array('@value' => $element['#value']))); + form_error($element, t('File size @value is not a valid file size. Use a value such as 2 MB or 800 KB.', array('@value' => $element['#value']))); } else { $max_filesize = parse_size(file_upload_max_size()); if ($max_filesize < $set_filesize) { - form_error($element, t('An upload size of @value is too large, you are allow to upload files @max or less.', array('@value' => $element['#value'], '@max' => format_size($max_filesize)))); + form_error($element, t('An upload size of @value is too large. You are allowed to upload files that are @max or less.', array('@value' => $element['#value'], '@max' => format_size($max_filesize)))); } } } @@ -389,6 +389,7 @@ function _webform_render_file($component, $value = NULL, $filter = TRUE, $submis * An associative array containing: * - element: A render element representing the file. * + * @return string */ function theme_webform_managed_file($variables) { $element = $variables['element']; diff --git a/sites/all/modules/contrib/webform/components/grid.inc b/sites/all/modules/contrib/webform/components/grid.inc index 8984606b6..4e36888ea 100644 --- a/sites/all/modules/contrib/webform/components/grid.inc +++ b/sites/all/modules/contrib/webform/components/grid.inc @@ -366,7 +366,7 @@ function webform_grid_merge_options($existing, $new) { * An array of options to be used for this row. * @return array * The $row_options with any missing options replaced with empty values. - **/ + */ function webform_grid_remove_options($header, $row_options) { foreach ($header as $key => $value) { if (!isset($row_options[$key])) { @@ -498,7 +498,7 @@ function theme_webform_display_grid($variables) { $value = drupal_render($element[$question_key]); } $items[] = ' - ' . _webform_grid_question_header($question_element['#title']) . ': ' . $value; - } + } $output = implode("\n", $items); } @@ -646,7 +646,8 @@ function _webform_edit_grid_unique_validate($element) { // Grids may contain nested multiple value select components. // Create a flat array of values. $values = array(); - array_walk_recursive($element['#value'], function($a) use (&$values) { $values[] = $a; }); + $element['#value'] = (array) $element['#value']; + array_walk_recursive($element['#value'], function ($a) use (&$values) { $values[] = $a; }); $nr_unique = count(array_unique($values)); $nr_values = count($values); @@ -795,9 +796,14 @@ function webform_validate_grid($element, $form_state) { foreach ($element['#parents'] as $key) { $values = isset($values[$key]) ? $values[$key] : $values; } - $nr_values = array_reduce($values, function($count, $item) { return $count + (int)!is_null($item); }, 0); - if (count($element['#grid_questions']) != $nr_values) { - form_error($element, t('!name field is required.', array('!name' => $element['#title']))); + // Remove any values that aren't grid question (i.e. nested components) + $grid_questions = $element['#grid_questions']; + $values = array_intersect_key($values, $grid_questions); + // Remove any unanswered grid questions. + $answers = array_filter($values, function($item) { return !is_null($item); }); + // Give required errors for any questions that aren't answered. + foreach (array_diff_key($grid_questions, $answers) as $question_key => $question) { + form_error($element[$question_key], t('!question field within !name is required.', array('!question' => $question, '!name' => $element['#title']))); } } } diff --git a/sites/all/modules/contrib/webform/components/number.inc b/sites/all/modules/contrib/webform/components/number.inc index dc127c2fe..d8434d0ef 100644 --- a/sites/all/modules/contrib/webform/components/number.inc +++ b/sites/all/modules/contrib/webform/components/number.inc @@ -171,7 +171,7 @@ function _webform_edit_number($component) { '#type' => 'checkbox', '#title' => t('Integer'), '#return_value' => 1, - '#description' => t('Permit only integer values as input. e.g. 12.34 would be invalid.'), + '#description' => t('Permit only integer values as input. For example, 12.34 would be invalid.'), '#weight' => 1.5, '#default_value' => $component['extra']['integer'], '#parents' => array('extra', 'integer'), @@ -180,7 +180,7 @@ function _webform_edit_number($component) { '#type' => 'textfield', '#title' => t('Minimum'), '#default_value' => $component['extra']['min'], - '#description' => t('Minimum numeric value. e.g. 0 would ensure positive numbers.'), + '#description' => t('Minimum numeric value. For example, 0 would ensure positive numbers.'), '#size' => 5, '#maxlength' => 10, '#weight' => 2.1, @@ -202,7 +202,7 @@ function _webform_edit_number($component) { '#type' => 'textfield', '#title' => t('Step'), '#default_value' => $component['extra']['step'], - '#description' => t('Limit options to a specific increment. e.g. a step of "5" would allow values 5, 10, 15, etc.'), + '#description' => t('Limit options to a specific increment. For example, a step of "5" would allow values 5, 10, 15, etc.'), '#size' => 5, '#maxlength' => 10, '#weight' => 3, @@ -527,7 +527,7 @@ function _webform_analysis_number($component, $sids = array(), $single = FALSE, $limit[$key] = _webform_number_format($component, $value); } - // Column headings (override potential theme uppercase, e.g. Seven in D7). + // Column headings (override potential theme uppercase, for example, Seven in D7). $header = array( t('Normal Distribution'), array('data' => '-4' . $sigma, 'style' => 'text-transform: lowercase;'), @@ -602,8 +602,6 @@ function _webform_csv_data_number($component, $export_options, $value) { * The form element. May either be a select or a webform_number element. * @param $form_state * The full form state for the webform. - * @return - * None. Calls a form_set_error if the number is not valid. */ function _webform_validate_number($element, &$form_state) { // Trim spaces for basic cleanup. @@ -869,6 +867,8 @@ function webform_number_format($value, $decimals = NULL, $point = '.', $separato * The string value to be standardized into a numeric string. * @param $point * The point separator between the whole number and the decimals. + * + * @return mixed|string */ function webform_number_standardize($value, $point) { // For simplicity, strip everything that's not the decimal point. diff --git a/sites/all/modules/contrib/webform/components/select.inc b/sites/all/modules/contrib/webform/components/select.inc index debaf3317..421c192c5 100644 --- a/sites/all/modules/contrib/webform/components/select.inc +++ b/sites/all/modules/contrib/webform/components/select.inc @@ -395,7 +395,7 @@ function _webform_render_select($component, $value = NULL, $filter = TRUE, $subm $option_value = reset($value); $element['#default_value'] = $option_value === '' ? NULL : $option_value; } - + if ($component['extra']['other_option'] && module_exists('select_or_other')) { // Set display as a select_or_other element: $element['#type'] = 'select_or_other'; @@ -762,8 +762,8 @@ function _webform_action_set_select($component, &$element, &$form_state, $value) // Set the value as an array for multiple select or single value otherwise. if ($element['#type'] == 'checkboxes') { $checkbox_values = $element['#options']; - array_walk($checkbox_values, function(&$value, $key) use($value) { - $value = (int)(strval($key) === $value); + array_walk($checkbox_values, function(&$option_value, $key) use($value) { + $option_value = (int)(strval($key) === $value); }); } else { @@ -1023,7 +1023,7 @@ function _webform_select_options_from_text($text, $flat = FALSE) { $group = NULL; foreach ($rows as $option) { $option = trim($option); - /** + /* * If the Key of the option is within < >, treat as an optgroup * * diff --git a/sites/all/modules/contrib/webform/components/textfield.inc b/sites/all/modules/contrib/webform/components/textfield.inc index 036e70d99..9e2f490fd 100644 --- a/sites/all/modules/contrib/webform/components/textfield.inc +++ b/sites/all/modules/contrib/webform/components/textfield.inc @@ -19,6 +19,7 @@ function _webform_defaults_textfield() { 'extra' => array( 'width' => '', 'maxlength' => '', + 'minlength' => '', 'field_prefix' => '', 'field_suffix' => '', 'disabled' => 0, @@ -126,6 +127,16 @@ function _webform_edit_textfield($component) { '#weight' => 2, '#parents' => array('extra', 'maxlength'), ); + $form['validation']['minlength'] = array( + '#type' => 'textfield', + '#title' => t('Minlength'), + '#default_value' => $component['extra']['minlength'], + '#description' => t('Minimum length of the textfield value. The component may still be empty unless it is set as Required.'), + '#size' => 5, + '#maxlength' => 10, + '#weight' => 3, + '#parents' => array('extra', 'minlength'), + ); return $form; } @@ -179,6 +190,9 @@ function _webform_render_textfield($component, $value = NULL, $filter = TRUE, $s if ($component['extra']['maxlength'] > 0) { $element['#maxlength'] = $component['extra']['maxlength']; } + if ($component['extra']['minlength'] > 0) { + $element['#minlength'] = $component['extra']['minlength']; + } if (isset($value[0])) { $element['#default_value'] = $value[0]; diff --git a/sites/all/modules/contrib/webform/components/time.inc b/sites/all/modules/contrib/webform/components/time.inc index 59c8fd310..a4276eed5 100644 --- a/sites/all/modules/contrib/webform/components/time.inc +++ b/sites/all/modules/contrib/webform/components/time.inc @@ -86,7 +86,7 @@ function _webform_edit_time($component) { '#type' => 'radios', '#title' => t('Default value timezone'), '#default_value' => $component['extra']['timezone'], - '#description' => t('If using relative dates for a default value (e.g. "now") base the current time on this timezone.'), + '#description' => t('If using relative dates for a default value (for example, "now") base the current time on this timezone.'), '#options' => array('user' => t('User timezone'), 'site' => t('Website timezone')), '#weight' => 2, '#access' => variable_get('configurable_timezones', 1), @@ -118,7 +118,7 @@ function _webform_edit_time($component) { } /** - * Implements hook_form_id_validate. + * Implements hook_form_id_validate(). * * Validate start and end times. */ @@ -214,7 +214,7 @@ function webform_expand_time($element) { } else { $hours = array_intersect_key($hours, array_flip(range($start_hour, 23))) + - array_intersect_key($hours, array_flip(range(0, $end_hour))); + array_intersect_key($hours, array_flip(range(0, $end_hour))); } } @@ -325,7 +325,7 @@ function webform_validate_time($element, $form_state) { // Enforce the start and end times, if any. $timestamp = strtotime($element['hour']['#value'] . ':' . $element['minute']['#value'] . ' ' . - (isset($element['ampm']) ? $element['ampm']['#value'] : '')); + (isset($element['ampm']) ? $element['ampm']['#value'] : '')); $start_time = strtotime($element['#start_time']); $end_time = strtotime($element['#end_time']); $subs = array( diff --git a/sites/all/modules/contrib/webform/css/webform-admin.css b/sites/all/modules/contrib/webform/css/webform-admin.css index cca57759a..4044504ea 100644 --- a/sites/all/modules/contrib/webform/css/webform-admin.css +++ b/sites/all/modules/contrib/webform/css/webform-admin.css @@ -104,20 +104,6 @@ td.webform-pagebreak { font-weight: bold; } -/* Special theming for the options element widget (if installed) */ -.webform-options-element thead { - display: none; -} -.webform-options-element fieldset { - border: none; - background: none; - margin: 0; - padding: 0; -} -.webform-options-element fieldset legend { - display: none; -} - /* Checkboxes for allowed file extensions */ table.webform-file-extensions td { vertical-align: top; diff --git a/sites/all/modules/contrib/webform/includes/exporters/webform_exporter.inc b/sites/all/modules/contrib/webform/includes/exporters/webform_exporter.inc index de356308c..b065bdfcb 100644 --- a/sites/all/modules/contrib/webform/includes/exporters/webform_exporter.inc +++ b/sites/all/modules/contrib/webform/includes/exporters/webform_exporter.inc @@ -4,7 +4,6 @@ * @file * Base class defining the common methods available to exporters. */ - class webform_exporter { public $options = array(); public $export_wordrap; @@ -40,7 +39,7 @@ class webform_exporter { */ function wrappable($row, $column, $value) { return strpos($value, "\n") !== FALSE || - $this->export_wordwrap && ($row > 2 || $column > 0 || $this->options['header_keys'] < 0); + $this->export_wordwrap && ($row > 2 || $column > 0 || $this->options['header_keys'] < 0); } /** @@ -60,7 +59,7 @@ class webform_exporter { * Provide headers to the page when an export file is being downloaded. * * @param $filename - * The name of the file being downloaded. e.g. export.xls. + * The name of the file being downloaded, for example, export.xls. */ function set_headers($filename) { drupal_add_http_header('Content-Type', 'application/force-download'); diff --git a/sites/all/modules/contrib/webform/includes/exporters/webform_exporter_delimited.inc b/sites/all/modules/contrib/webform/includes/exporters/webform_exporter_delimited.inc index 20872b7b3..b90d17f5b 100644 --- a/sites/all/modules/contrib/webform/includes/exporters/webform_exporter_delimited.inc +++ b/sites/all/modules/contrib/webform/includes/exporters/webform_exporter_delimited.inc @@ -4,7 +4,6 @@ * @file * Webform exporter for creating CSV/TSV delimited files. */ - class webform_exporter_delimited extends webform_exporter { public $line_ending; public $delimiter; diff --git a/sites/all/modules/contrib/webform/includes/webform.components.inc b/sites/all/modules/contrib/webform/includes/webform.components.inc index 876548b34..f9412c612 100644 --- a/sites/all/modules/contrib/webform/includes/webform.components.inc +++ b/sites/all/modules/contrib/webform/includes/webform.components.inc @@ -245,7 +245,7 @@ function _webform_components_form_rows($node, $cid, $component, $level, &$form, $row_data = array( array('data' => $indents . filter_xss($component['name']), 'class' => array('webform-component-name')), array('data' => check_plain($form_key), 'class' => array('webform-component-formkey')) + - ($component['form_key'] == $form_key ? array() : array('title' => $component['form_key'])), + ((string) $component['form_key'] === (string) $form_key ? array() : array('title' => $component['form_key'])), array('data' => $form['add']['type']['#options'][$component['type']], 'class' => array('webform-component-type')), array('data' => ($value == '') ? '-' : check_plain($value), 'class' => array('webform-component-value')) + ($component['value'] == $value ? array() : array('title' => $component['value'])), @@ -302,8 +302,8 @@ function webform_components_form_validate($form, &$form_state) { $parents = array(); if (isset($form_state['values']['components'])) { foreach ($form_state['values']['components'] as $cid => $component) { - $form_key = $form['#node']->webform['components'][$cid]['form_key']; - if (isset($parents[$component['pid']]) && ($existing = array_search($form_key, $parents[$component['pid']])) && $existing !== FALSE) { + $form_key = (string) $form['#node']->webform['components'][$cid]['form_key']; + if (isset($parents[$component['pid']]) && ($existing = array_search($form_key, $parents[$component['pid']], TRUE)) && $existing !== FALSE) { if (!isset($duplicates[$form_key])) { $duplicates[$form_key] = array($existing); } @@ -721,9 +721,9 @@ function webform_component_delete_form($form, $form_state, $node, $component) { if (webform_component_feature($component_type, 'group')) { $question = t('Delete the %name fieldset?', array('%name' => $node->webform['components'][$cid]['name'])); $description = t('This will immediately delete the %name @type component and all nested components within %name from the %webform webform. This cannot be undone.', - array('%name' => $node->webform['components'][$cid]['name'], - '@type' => webform_component_property($component_type, 'label'), - '%webform' => $node->title)); + array('%name' => $node->webform['components'][$cid]['name'], + '@type' => webform_component_property($component_type, 'label'), + '%webform' => $node->title)); } else { $question = t('Delete the %name component?', array('%name' => $node->webform['components'][$cid]['name'])); @@ -764,6 +764,10 @@ function webform_component_delete_form_submit($form, &$form_state) { * * @param $component * A full component containing fields from the component form. + * + * @return false|int + * On success return identifier for the components within node. + * FALSE on failure */ function webform_component_insert(&$component) { // Allow modules to modify the component before saving. @@ -946,6 +950,10 @@ function webform_component_delete($node, $component) { * The node object containing the current webform. * @param $component * A full component containing fields from the component form. + * + * @return false|int + * On success return identifier for the components within node. + * FALSE on failure */ function webform_component_clone(&$node, &$component) { $original_cid = $component['cid']; @@ -1019,11 +1027,13 @@ function webform_component_property($type, $property) { * email, required, conditional) on which this list of components will be * restricted. * @param $prefix_group - * TRUE to indent with a hyphen, or 'path" to Prepend enclosing group (e.g. + * TRUE to indent with a hyphen, or 'path" to Prepend enclosing group (for example, * fieldset) name(s) * @param $pagebreak_groups * Determine if pagebreaks should be converted to option groups in the * returned list of options. + * + * @return array */ function webform_component_list($node, $component_filter = NULL, $prepend_group = TRUE, $pagebreak_groups = FALSE) { $options = array(); @@ -1034,7 +1044,7 @@ function webform_component_list($node, $component_filter = NULL, $prepend_group $feature = is_string($component_filter) ? $component_filter : NULL; foreach ($components as $cid => $component) { - // If this component is a group (e.g. fieldset), then remember its name, including any parents. + // If this component is a group (for example, fieldset), then remember its name, including any parents. if ($prepend_group && webform_component_feature($component['type'], 'group')) { $parent_names[$cid] = ($component['pid'] ? $parent_names[$component['pid']] : '') . ($prepend_group === 'path' ? $component['name'] . ': ' : '-'); @@ -1043,6 +1053,13 @@ function webform_component_list($node, $component_filter = NULL, $prepend_group // If this component is a pagebreak, then generate an option group, ensuring a unique name. if ($pagebreak_groups && $component['type'] == 'pagebreak') { $page_name = $component['name']; + + // When a $page_name consists only of digits, append a space to ensure it + // is never the same as a $cid. + if ((string) $page_name === (string) (int) $page_name) { + $page_name .= ' '; + } + $copy = 1; while (in_array($page_name, $page_names)) { $page_name = $component['name'] . '_' . ++$copy; @@ -1144,13 +1161,26 @@ function theme_webform_component_select($variables) { } /** - * Find a components parents within a node. + * Find a component's parents within a node. + * + * @param object $node + * The webform node. + * @param array $component + * The component to start with. + * @param string|true $what_to_return + * If TRUE, return complete component arrays. Otherwise, return the property + * of each component named in this parametre. + * + * @return array + * An array with a value for each parent and for the start component, in order + * ending with start component. What the value is is controlled by + * $what_to_return. */ -function webform_component_parent_keys($node, $component) { - $parents = array($component['form_key']); +function webform_component_parent_keys($node, array $component, $what_to_return = 'form_key') { + $parents = array(($what_to_return === TRUE) ? $component : $component[$what_to_return]); $pid = $component['pid']; while ($pid) { - $parents[] = $node->webform['components'][$pid]['form_key']; + $parents[] = ($what_to_return === TRUE) ? $node->webform['components'][$pid] : $node->webform['components'][$pid][$what_to_return]; $pid = $node->webform['components'][$pid]['pid']; } return array_reverse($parents); diff --git a/sites/all/modules/contrib/webform/includes/webform.conditionals.inc b/sites/all/modules/contrib/webform/includes/webform.conditionals.inc index 75088e5ab..560459082 100644 --- a/sites/all/modules/contrib/webform/includes/webform.conditionals.inc +++ b/sites/all/modules/contrib/webform/includes/webform.conditionals.inc @@ -90,9 +90,9 @@ function webform_conditionals_form($form, &$form_state, $node) { $cid = $action['target']; if ($action['action'] == 'require' && !$components[$cid]['required']) { drupal_set_message(t('Component %title must be configured as Required for Webform to conditionally change its required status. Configure %title.', - array('%title' => $components[$cid]['name'], - '!url' => url("node/{$node->nid}/webform/components/$cid", array('query' => array('destination' => "node/{$node->nid}/webform/conditionals")))) - ), 'error'); + array('%title' => $components[$cid]['name'], + '!url' => url("node/{$node->nid}/webform/components/$cid", array('query' => array('destination' => "node/{$node->nid}/webform/conditionals")))) + ), 'error'); } } $form['conditionals'][$rgid]['weight'] = array( @@ -209,18 +209,18 @@ function webform_conditionals_form_validate($form, &$form_state) { $target_id = $action['target']['#value']; if (isset($targets[$target_id][$operation])) { form_set_error('conditionals][' . $conditional_key . '][actions][' . $action_key . '][target', - t('A operation %op cannot be made for a component more than once. (%target).', - array('%op' => $action['action']['#options'][$operation], - '%target' => $components[$action['target']['#value']]['name']))); + t('A operation %op cannot be made for a component more than once. (%target).', + array('%op' => $action['action']['#options'][$operation], + '%target' => $components[$action['target']['#value']]['name']))); } $component_type = $node->webform['components'][$action['target']['#value']]['type']; if (!webform_conditional_action_able($component_type, $action['action']['#value'])) { form_set_error('conditionals][' . $conditional_key . '][actions][' . $action_key . '][action', - t('A component of type %type can\'t be %action. (%target)', - array( - '%action' => $action['action']['#options'][$action['action']['#value']], - '%type' => $component_options[$component_type], - '%target' => $components[$action['target']['#value']]['name']))); + t('A component of type %type can\'t be %action. (%target)', + array( + '%action' => $action['action']['#options'][$action['action']['#value']], + '%type' => $component_options[$component_type], + '%target' => $components[$action['target']['#value']]['name']))); } $targets[$target_id][$operation] = $target_id; } @@ -229,8 +229,8 @@ function webform_conditionals_form_validate($form, &$form_state) { // Validate component rules, but not conditional_start/end rules. if (is_numeric($rule_key) && $rule['source_type']['#value'] == 'component' && isset($targets[$rule['source']['#value']])) { form_set_error('conditionals][' . $conditional_key . '][rules][' . $rule_key . '][source', - t('The subject of the conditional cannot be the same as the component that is changed (%target).', - array('%target' => $components[$rule['source']['#value']]['name']))); + t('The subject of the conditional cannot be the same as the component that is changed (%target).', + array('%target' => $components[$rule['source']['#value']]['name']))); } } } @@ -458,7 +458,7 @@ function _webform_conditional_add_expand($element, $rid, $subconditional) { '#submit' => array('webform_conditional_element_add'), '#subconditional' => $subconditional, '#name' => implode('_', $element['#parents']) . '_rules_' . $rid . - ($subconditional ? '_add_subconditional' : '_add'), + ($subconditional ? '_add_subconditional' : '_add'), '#attributes' => array('class' => array('webform-conditional-rule-add')), '#ajax' => array( 'progress' => 'none', @@ -653,6 +653,7 @@ function _webform_conditional_expand_value_forms($node) { * The starting rule id for the search * @param integer $target_delta_level * The level that is sought. 0 for current left. -1 for parent. + * * @return integer * The rid of the found rule, or -1 if none. Note that NULL is not used as a * semaphore for "not found" because it casts to 0, which is a valid rule id. @@ -681,7 +682,7 @@ function _webform_conditional_find_end($rules, $origin_rid, $target_delta_level /** * Helper. Find the matching start or end of a given subconditional. * - * @see _webform_conditional_find_end(). + * @see _webform_conditional_find_end() */ function _webform_conditional_find_start($rules, $origin_rid, $target_delta_level = 0) { $rids = array_keys($rules); @@ -999,7 +1000,7 @@ function webform_conditional_operators_list() { } /** - * Internal implementation of hook_webform_conditional_operator_info(). + * Implements hook_webform_conditional_operator_info(). * * Called from webform.module's webform_webform_conditional_operator_info(). */ @@ -1682,7 +1683,7 @@ function webform_conditional_operator_datetime_after($input_values, $rule_value) */ function webform_conditional_operator_datetime_after_equal($input_values, $rule_value) { return webform_conditional_operator_datetime_after($input_values, $rule_value) || - webform_conditional_operator_datetime_equal($input_values, $rule_value); + webform_conditional_operator_datetime_equal($input_values, $rule_value); } /** @@ -1698,7 +1699,7 @@ function webform_conditional_operator_datetime_before($input_values, $rule_value */ function webform_conditional_operator_datetime_before_equal($input_values, $rule_value) { return webform_conditional_operator_datetime_before($input_values, $rule_value) || - webform_conditional_operator_datetime_equal($input_values, $rule_value); + webform_conditional_operator_datetime_equal($input_values, $rule_value); } /** diff --git a/sites/all/modules/contrib/webform/includes/webform.emails.inc b/sites/all/modules/contrib/webform/includes/webform.emails.inc index 2385425ca..cdea8fb43 100644 --- a/sites/all/modules/contrib/webform/includes/webform.emails.inc +++ b/sites/all/modules/contrib/webform/includes/webform.emails.inc @@ -94,10 +94,13 @@ function webform_emails_form($form, $form_state, $node) { /** * Theme the node components form. Use a table to organize the components. * - * @param $form - * The form array. - * @return + * @param array $variables + * Array with key "form" containing the form array. + * + * @return string * Formatted HTML form, ready for display. + * + * @throws Exception */ function theme_webform_emails_form($variables) { $form = $variables['form']; @@ -692,6 +695,9 @@ function webform_email_load($eid, $nid) { * * @param $email * An array of settings for sending an e-mail. + * + * @return int|false + * The e-mail identifier for this row's settings on success else false. */ function webform_email_insert($email) { // TODO: This is not race-condition safe. Switch to using transactions? @@ -716,6 +722,9 @@ function webform_email_insert($email) { * * @param $email * An array of settings for sending an e-mail. + * + * @return false|int + * The e-mail identifier for this row's settings on success else false. */ function webform_email_clone($email) { $email['eid'] = NULL; @@ -728,6 +737,10 @@ function webform_email_clone($email) { * @param $email * An array of settings for sending an e-mail containing a nid, eid, and all * other fields from the e-mail form. + * + * @return false|int + * On success SAVED_NEW or SAVED_UPDATED, depending on the operation performed, + * false on failure. */ function webform_email_update($email) { $email['excluded_components'] = implode(',', $email['excluded_components']); diff --git a/sites/all/modules/contrib/webform/includes/webform.export.inc b/sites/all/modules/contrib/webform/includes/webform.export.inc index 249f31264..55685c9c8 100644 --- a/sites/all/modules/contrib/webform/includes/webform.export.inc +++ b/sites/all/modules/contrib/webform/includes/webform.export.inc @@ -10,7 +10,7 @@ * * Defines the exporters this module implements. * - * @return + * @return array * An "array of arrays", keyed by content-types. The 'handler' slot * should point to the PHP class implementing this flag. */ diff --git a/sites/all/modules/contrib/webform/includes/webform.options.inc b/sites/all/modules/contrib/webform/includes/webform.options.inc index 902a74cc0..85323c13c 100644 --- a/sites/all/modules/contrib/webform/includes/webform.options.inc +++ b/sites/all/modules/contrib/webform/includes/webform.options.inc @@ -6,7 +6,7 @@ */ /** - * Private implementation of hook_webform_select_options_info(). + * Implements hook_webform_select_options_info(). * * @see webform_webform_select_options_info() */ @@ -35,6 +35,8 @@ function _webform_options_info() { } /** + * Implements callback_webform_options(). + * * Option list containing the days of the week. */ function webform_options_days($component, $flat, $arguments) { @@ -58,6 +60,8 @@ function webform_options_days($component, $flat, $arguments) { } /** + * Implements callback_webform_options(). + * * Options list containing country names. */ function webform_options_countries($component, $flat, $arguments) { @@ -66,6 +70,8 @@ function webform_options_countries($component, $flat, $arguments) { } /** + * Implements callback_webform_options(). + * * Options list containing United States states and territories. */ function webform_options_united_states($component, $flat, $arguments) { diff --git a/sites/all/modules/contrib/webform/includes/webform.pages.inc b/sites/all/modules/contrib/webform/includes/webform.pages.inc index 782dfb94b..8f5d8ed42 100644 --- a/sites/all/modules/contrib/webform/includes/webform.pages.inc +++ b/sites/all/modules/contrib/webform/includes/webform.pages.inc @@ -412,9 +412,9 @@ function webform_configure_form_validate($form, &$form_state) { } // Ensure only positive integers are entered as submission limits. - foreach (array('total_submit_limit', 'submit_limit') as $field) { + foreach (array('total_submit_limit' => 'enforce_total_limit', 'submit_limit' => 'enforce_limit') as $field => $enforce_fk) { $limit = $form['submission'][$field][$field]['#value']; - if ($limit !== '' && (int) $limit < 1 && (int) $limit !== -1) { + if ($form['submission'][$field][$enforce_fk]['#value'] !== 'no' && $limit !== '' && (int) $limit < 1 && (int) $limit !== -1) { form_error($form['submission'][$field][$field], t('The submission limit must be at least 1.')); } } @@ -427,7 +427,7 @@ function webform_configure_form_validate($form, &$form_state) { // Note that FAPI doesn't actually support error highlighting on radio or // checkbox form elements. form_error($form['advanced']['confidential'], - t('Choose a "Per user submission limit" or "Confidential submissions", but not both. Or ask the adminstrator to track anonymous users by cookie, rather than IP address only.')); + t('Choose a "Per user submission limit" or "Confidential submissions", but not both. Or ask the adminstrator to track anonymous users by cookie, rather than IP address only.')); } } @@ -521,7 +521,7 @@ function webform_configure_form_submit($form, &$form_state) { $next_serial = (int)$form_state['values']['next_serial']; if ($next_serial < $next_min) { drupal_set_message(t('The next submission number was increased to @min to make it higher than existing submissions.', - array('@min' => $next_min))); + array('@min' => $next_min))); $next_serial = $next_min; } $node->webform['next_serial'] = $next_serial; @@ -538,7 +538,7 @@ function webform_configure_form_submit_save($form, &$form_state) { drupal_set_message(t('The form settings have been updated.')); $node = &$form['#node']; - if (!$node->webform['block'] && function_exists('block_load') && + if (!$node->webform['block'] && function_exists('block_load') && ($block = block_load('webform', 'client-block-' . $node->nid)) && !empty($block->bid)) { // An existing block for this not-currently-available block was already configured. diff --git a/sites/all/modules/contrib/webform/includes/webform.report.inc b/sites/all/modules/contrib/webform/includes/webform.report.inc index 9eb7d4abc..9a0f99b12 100644 --- a/sites/all/modules/contrib/webform/includes/webform.report.inc +++ b/sites/all/modules/contrib/webform/includes/webform.report.inc @@ -130,10 +130,13 @@ function webform_get_view($node, $view_id) { /** * Theme the list of links for selecting the number of results per page. * - * @param $total_count - * The total number of results available. - * @param $pager_count - * The current number of results displayed per page. + * @param array $variables + * Array with keys: + * - "total_count": The total number of results available. + * - "pager_count": The current number of results displayed per page. + * + * @return string + * Pager. */ function theme_webform_results_per_page($variables) { $total_count = $variables['total_count']; @@ -262,6 +265,9 @@ function theme_webform_results_table_header($variables) { * The total number of submissions to this webform. * @param $pager_count * The number of results to be shown per page. + * + * @return string + * HTML string with result data. */ function theme_webform_results_table($variables) { drupal_add_library('webform', 'admin'); @@ -444,9 +450,9 @@ function webform_results_download_form($form, &$form_state, $node) { '#type' => 'container', 'warning' => array( '#markup' => '

' . - t('Warning: Opening delimited text files with spreadsheet applications may expose you to formula injection or other security vulnerabilities. When the submissions contain data from untrusted users and the downloaded file will be used with spreadsheets, use Microsoft Excel format.', - array('!link' => url('https://www.google.com/search?q=spreadsheet+formula+injection'))) . - '

', + t('Warning: Opening delimited text files with spreadsheet applications may expose you to formula injection or other security vulnerabilities. When the submissions contain data from untrusted users and the downloaded file will be used with spreadsheets, use Microsoft Excel format.', + array('!link' => url('https://www.google.com/search?q=spreadsheet+formula+injection'))) . + '

', ), 'delimiter' => array( '#type' => 'select', @@ -766,7 +772,7 @@ function theme_webform_results_download_range($variables) { $element['range_type']['range_serial']['#theme_wrappers'] = array('webform_inline_radio'); $last_serial = $download_info['serial'] ? $download_info['serial'] : drupal_placeholder(t('none')); $element['range_type']['range_serial']['#title'] = t('Submissions by number from !start and optionally to: !end   (Last downloaded: !serial)', - array('!start' => drupal_render($element['start']), '!end' => drupal_render($element['end']), '!serial' => $last_serial)); + array('!start' => drupal_render($element['start']), '!end' => drupal_render($element['end']), '!serial' => $last_serial)); // date range $element['range_type']['range_date']['#theme_wrappers'] = array('webform_inline_radio'); @@ -856,7 +862,7 @@ function webform_results_download_form_submit(&$form, &$form_state) { */ function webform_export_batch_size($node) { // Start the batch size at 50,000 per batch, but divide by number of - // components in the form. e.g. If a form had 10 components, it would + // components in the form. For example, if a form had 10 components, it would // export 5,000 submissions at a time. $batch_size = ceil(50000 / max(1, count($node->webform['components']))); @@ -1058,12 +1064,12 @@ function webform_results_download_headers($node, $options) { * @param array $options * A list of options that define the output format. These are generally passed * through from the GUI interface. - * @param $serial_start + * @param int $serial_start * The starting position for the Serial column in the output. * @param $last_sid * If set to a non-NULL value, the last sid will be returned. * - * @return $rows + * @return array $rows * An array of rows built according to the provided $serial_start and * $pager_count variables. Note that the current page number is determined * by the super-global $_GET['page'] variable. @@ -1102,7 +1108,7 @@ function webform_results_download_rows($node, $options, $serial_start = 0, &$las * @param array $submissions * An associative array of loaded submissions, indexed by sid. * - * @return $rows + * @return array $rows * An array of rows built according to the provided $serial_start and * $pager_count variables. Note that the current page number is determined * by the super-global $_GET['page'] variable. @@ -1165,6 +1171,9 @@ function webform_results_download_rows_process($node, $options, $serial_start, $ * * @param $options * Filter down the list of columns based on a provided column list. + * + * @return array + * List of generic columns plus columns added by other modules */ function webform_results_download_submission_information($node, $options = array()) { $submission_information = module_invoke_all('webform_results_download_submission_information_info'); @@ -1255,6 +1264,9 @@ function webform_webform_results_download_submission_information_data($token, $s * @param array $options * A list of options that define the output format. These are generally passed * through from the GUI interface. + * + * @return array + * Option for creating downloadable version of the webform data. */ function webform_results_download_default_options($node, $format) { $submission_information = webform_results_download_submission_information($node); @@ -1291,7 +1303,7 @@ function webform_results_download_default_options($node, $format) { function webform_results_download($node, $export_info) { // If the exporter provides a custom download method, use that. if (method_exists($export_info['exporter'], 'download')) { - $export_info['exporter']->download($node); + $export_info['exporter']->download($node, $export_info); } // Otherwise use the set_headers() method to set headers and then read in the // file directly. Delete it when complete. @@ -1301,6 +1313,7 @@ function webform_results_download($node, $export_info) { $export_name = t('Untitled'); } $export_info['exporter']->set_headers($export_name); + ob_clean(); @readfile($export_info['file_name']); // The @ makes it silent. @unlink($export_info['file_name']); // Clean up, the @ makes it silent. } @@ -1345,7 +1358,7 @@ function webform_results_export_success($node, $export_info) { * @param $node * The webform $node whose export file is being downloaded. * - * @return + * @return null|string * Either an export file is downloaded with the appropriate HTTP headers set, * or an error page is shown if now export is available to download. */ @@ -1439,6 +1452,10 @@ function webform_results_batch_rows($node, $format = 'delimited', $options = arr $query->fields('u', array('name')); $query->fields('ws'); + if (!empty($options['sids'])) { + $query->condition('ws.sid', $options['sids'], 'IN'); + } + $submissions = webform_get_submissions_load($query); $rows = webform_results_download_rows_process($node, $options, $context['sandbox']['serial'], $submissions); @@ -1563,6 +1580,9 @@ function webform_results_batch_finished($success, $results, $operations) { * A webform component. If passed in, additional information may be returned * relating specifically to that component's analysis, such as a list of * "Other" values within a select list. + * + * @return string + * Provides a simple analysis of all submissions to a webform. */ function webform_results_analysis($node, $sids = array(), $analysis_component = NULL) { if (!is_array($sids)) { @@ -1672,6 +1692,8 @@ function template_preprocess_webform_analysis_component(&$variables) { * of header labels. * - table_rows: If this component has a table that should be rendered, an * array of values + * + * @return string */ function theme_webform_analysis_component_basic($variables) { $data = $variables['data']; @@ -1870,6 +1892,8 @@ function webform_download_sids_count($nid, $range_options, $uid = NULL) { * The user id of the user whose last download information should be used, * or the current user if NULL. This is unrelated to which user submitted * the submissions. + * + * @return QueryAlterableInterface */ function webform_download_sids_query($nid, $range_options, $uid = NULL) { $query = db_select('webform_submissions', 'ws') diff --git a/sites/all/modules/contrib/webform/includes/webform.submissions.inc b/sites/all/modules/contrib/webform/includes/webform.submissions.inc index dccf12dac..94e7e6c5b 100644 --- a/sites/all/modules/contrib/webform/includes/webform.submissions.inc +++ b/sites/all/modules/contrib/webform/includes/webform.submissions.inc @@ -15,7 +15,8 @@ * The node object containing the current webform. * @param $submitted * The submitted user values from the webform. - * @return + * + * @return array * An array suitable for use in the 'data' property of a $submission object. */ function webform_submission_data($node, $submitted) { @@ -54,7 +55,7 @@ function webform_submission_data($node, $submitted) { */ function webform_submission_create($node, $account, $form_state, $is_preview = FALSE, $prototype = NULL) { $data = webform_submission_data($node, $form_state['values']['submitted']); - if ($prototype) { + if (is_object($prototype)) { $submission = clone $prototype; $submission->preview = $is_preview; $submission->data = $data; @@ -222,6 +223,9 @@ function webform_submission_delete($node, $submission) { * @param $emails * (optional) An array of specific e-mail settings to be used. If omitted, all * e-mails in $node->webform['emails'] will be sent. + * + * @return int + * Number of mail sent. */ function webform_submission_send_mail($node, $submission, $emails = NULL) { global $user; @@ -321,7 +325,7 @@ function webform_submission_send_mail($node, $submission, $emails = NULL) { foreach ($addresses_final as $address) { if (_webform_submission_spam_check($address, $email['subject'], $email['from'], $email['headers'])) { watchdog('webform', 'Possible spam attempt from @remote !message', - array('@remote' => ip_address(), '!message'=> "
\n" . nl2br(htmlentities($email['message'])))); + array('@remote' => ip_address(), '!message' => "
\n" . nl2br(htmlentities($email['message'])))); drupal_set_message(t('Illegal information. Data not submitted.'), 'error'); return FALSE; } @@ -429,8 +433,8 @@ function webform_submission_delete_form_submit($form, &$form_state) { webform_submission_delete($node, $submission); drupal_set_message(t('Submission deleted.')); - // If no destination query was supplied in the URL (e.g. Edit tab), redirect - // to the most-privledged destination. + // If no destination query was supplied in the URL (for example, Edit tab), + // redirect to the most-privledged destination. $form_state['redirect'] = 'node/' . $node->nid . (webform_results_access($node) ? '/webform-results' : '/submissions'); } @@ -588,10 +592,13 @@ function webform_submission_resend_submit($form, &$form_state) { /** * Theme the node components form. Use a table to organize the components. * - * @param $form - * The form array. - * @return + * @param array $variables + * Array with key "form" containing the form array. + * + * @return string * Formatted HTML form, ready for display. + * + * @throws Exception */ function theme_webform_submission_resend($variables) { $form = $variables['form']; @@ -683,6 +690,9 @@ function webform_submission_render($node, $submission, $email, $format, $exclude * table, pass the array header of the table. * @param $pager_count * Optional. The number of submissions to include in the results. + * + * @return array + * Array of submission data for a particular node. */ function webform_get_submissions($filters = array(), $header = NULL, $pager_count = 0) { return webform_get_submissions_load(webform_get_submissions_query($filters, $header, $pager_count)); @@ -703,6 +713,8 @@ function webform_get_submissions($filters = array(), $header = NULL, $pager_coun * table, pass the array header of the table. * @param $pager_count * Optional. The number of submissions to include in the results. + * + * @return QueryExtendableInterface|SelectQueryInterface */ function webform_get_submissions_query($filters = array(), $header = NULL, $pager_count = 0) { if (!is_array($filters)) { @@ -945,7 +957,7 @@ function _webform_submission_spam_check($to, $subject, $from, $headers = array() * Optional parameter. Specify the account you want to check the limit * against. * - * @return + * @return bool * Boolean TRUE if the user has exceeded their limit. FALSE otherwise. */ function webform_submission_user_limit_check($node, $account = NULL) { @@ -1022,7 +1034,8 @@ function webform_submission_user_limit_check($node, $account = NULL) { * * @param $node * The webform node to be checked. - * @return + * + * @return bool * Boolean TRUE if the form has exceeded it's limit. FALSE otherwise. */ function webform_submission_total_limit_check($node) { diff --git a/sites/all/modules/contrib/webform/includes/webform.webformconditionals.inc b/sites/all/modules/contrib/webform/includes/webform.webformconditionals.inc index 8aef99a23..72b8d07ae 100644 --- a/sites/all/modules/contrib/webform/includes/webform.webformconditionals.inc +++ b/sites/all/modules/contrib/webform/includes/webform.webformconditionals.inc @@ -140,18 +140,18 @@ class WebformConditionals { } if ($source_component['page_num'] > $page_num) { $errors[$page_num][] = t('A forward reference from page @from, %from to %to was found.', - array( - '%from' => $source_component['name'], - '@from' => $source_component['page_num'], - '%to' => $component['name'], - )); + array( + '%from' => $source_component['name'], + '@from' => $source_component['page_num'], + '%to' => $component['name'], + )); } elseif ($source_component['page_num'] == $page_num && $component['type'] == 'pagebreak') { $errors[$page_num][] = t('The page break %to can\'t be controlled by %from on the same page.', - array( - '%from' => $source_component['name'], - '%to' => $component['name'], - )); + array( + '%from' => $source_component['name'], + '%to' => $component['name'], + )); } } } @@ -207,7 +207,7 @@ class WebformConditionals { foreach ($nodes as $id => $n) { if ($n['in'] || $n['out']) { $errors[$page_num][] = t('A circular reference involving %name was found.', - array('%name' => $components[$id]['name'])); + array('%name' => $components[$id]['name'])); } } @@ -254,14 +254,14 @@ class WebformConditionals { if (webform_node_update_access($this->node)) { foreach ($this->errors as $page_num => $page_errors) { drupal_set_message(format_plural(count($page_errors), - 'Conditional error on page @num:', - 'Conditional errors on page @num:', - array('@num' => $page_num)) . - '
  • ' . implode('
  • ', $page_errors) . '
', 'warning'); + 'Conditional error on page @num:', + 'Conditional errors on page @num:', + array('@num' => $page_num)) . + '
  • ' . implode('
  • ', $page_errors) . '
', 'warning'); } } else { - drupal_set_message(t('This form is improperly configured. Contact the administrator.'), 'warning'); + drupal_set_message(t('This form is improperly configured. Contact the administrator.'), 'warning'); } } } @@ -366,7 +366,7 @@ class WebformConditionals { $this->requiredMap = array_fill(1, count($this->pageMap), array()); $this->setMap = $this->requiredMap; $this->markupMap = $this->requiredMap; - } + } else { array_walk($this->visibilityMap[$page_num], function (&$status) { $status = WebformConditionals::componentShown; @@ -387,7 +387,7 @@ class WebformConditionals { $last_page = $page_num ? $page_num : count($this->topologicalOrder); for ($page = $first_page; $page <= $last_page; $page++) { foreach ($this->topologicalOrder[$page] as $conditional_spec) { - + $conditional = $conditionals[$conditional_spec['rgid']]; $source_page_nums = array(); @@ -422,6 +422,21 @@ class WebformConditionals { // without building it. It is possible that the component include file // hasn't been included yet. See #2529246. webform_component_include($source_component['type']); + + // Load missing include files for conditional types. + // In the case of the 'string', 'date', and 'time' conditional types, it is + // not necessary to load their include files for conditional behavior + // because the required functions are already loaded + // in webform.conditionals.inc. + switch ($conditional_type) { + case 'numeric': + module_load_include('inc', 'webform', 'components/number'); + break; + case 'select': + module_load_include('inc', 'webform', 'components/select'); + break; + } + $this->executionStackAccumulate($comparison_callback($source_values, $rule['value'], $source_component)); // Record page number to later determine any intra-page dependency on this source. diff --git a/sites/all/modules/contrib/webform/js/webform.js b/sites/all/modules/contrib/webform/js/webform.js index da41e228a..7daacef6b 100644 --- a/sites/all/modules/contrib/webform/js/webform.js +++ b/sites/all/modules/contrib/webform/js/webform.js @@ -100,7 +100,7 @@ $currentForm.bind('change', {'settings': settings}, Drupal.webform.conditionalCheck); // Trigger all the elements that cause conditionals on this form. - Drupal.webform.doConditions($form, settings); + Drupal.webform.doConditions($currentForm, settings); }); }); }; diff --git a/sites/all/modules/contrib/webform/templates/webform-calendar.tpl.php b/sites/all/modules/contrib/webform/templates/webform-calendar.tpl.php index ca57cec80..21f1f205a 100644 --- a/sites/all/modules/contrib/webform/templates/webform-calendar.tpl.php +++ b/sites/all/modules/contrib/webform/templates/webform-calendar.tpl.php @@ -5,4 +5,4 @@ * Theme the button for the date component date popup. */ ?> - + diff --git a/sites/all/modules/contrib/webform/templates/webform-confirmation.tpl.php b/sites/all/modules/contrib/webform/templates/webform-confirmation.tpl.php index 9ac179ffb..a7d690656 100644 --- a/sites/all/modules/contrib/webform/templates/webform-confirmation.tpl.php +++ b/sites/all/modules/contrib/webform/templates/webform-confirmation.tpl.php @@ -30,5 +30,5 @@
diff --git a/sites/all/modules/contrib/webform/templates/webform-mail.tpl.php b/sites/all/modules/contrib/webform/templates/webform-mail.tpl.php index 578c82770..f7ed9837d 100644 --- a/sites/all/modules/contrib/webform/templates/webform-mail.tpl.php +++ b/sites/all/modules/contrib/webform/templates/webform-mail.tpl.php @@ -33,6 +33,6 @@ [submission:values] -' : '') . t('The results of this submission may be viewed at:') . ($email['html'] ? '

' : '') ?> +' : '') . t('The results of this submission may be viewed at:') . ($email['html'] ? '

' : ''); ?> ' : ''); ?>[submission:url]' : ''); ?> diff --git a/sites/all/modules/contrib/webform/templates/webform-progressbar.tpl.php b/sites/all/modules/contrib/webform/templates/webform-progressbar.tpl.php index fa695e767..a739a279d 100644 --- a/sites/all/modules/contrib/webform/templates/webform-progressbar.tpl.php +++ b/sites/all/modules/contrib/webform/templates/webform-progressbar.tpl.php @@ -25,7 +25,7 @@
 
- + diff --git a/sites/all/modules/contrib/webform/tests/WebformComponentsTestCase.test b/sites/all/modules/contrib/webform/tests/WebformComponentsTestCase.test new file mode 100644 index 000000000..c802fea58 --- /dev/null +++ b/sites/all/modules/contrib/webform/tests/WebformComponentsTestCase.test @@ -0,0 +1,192 @@ + t('Webform components'), + 'description' => t('Add and remove components from a webform.'), + 'group' => t('Webform'), + ); + } + + /** + * Webform module component tests. + */ + function testWebformComponents() { + // Test webform_component_list(). + // Create a form consisting of three textfields separated by pagebreaks. + $test_components = $this->webformComponents(); + $textfield = $test_components['textfield']['component']; + + // Page 1 textfield. + $textfield['page_num'] = 1; + $textfield['name'] = $this->randomName(); + $textfield['form_key'] = $this->randomName(); + $components[1] = $textfield; + // Page 2 break. + $components[2] = array( + 'type' => 'pagebreak', + 'form_key' => 'pagebreak_' . $this->randomName(), + 'pid' => 0, + 'name' => $this->randomName(), + 'page_num' => 2, + ); + // Page 2 textfield. + $textfield['name'] = $this->randomName(); + $textfield['form_key'] = $this->randomName(); + $textfield['page_num'] = 2; + $components[3] = $textfield; + // Page 3 break. + $components[4] = array( + 'type' => 'pagebreak', + 'form_key' => 'pagebreak_' . $this->randomName(), + 'pid' => 0, + // Name is the cid of another component. Should get a space added when it + // is used as a key in the value returned from webform_component_list(). + 'name' => '1', + 'page_num' => 3, + ); + // Page 3 textfield. + $textfield['name'] = $this->randomName(); + $textfield['form_key'] = $this->randomName(); + $textfield['page_num'] = 3; + $components[5] = $textfield; + // Page 4 break. + $components[6] = array( + 'type' => 'pagebreak', + 'form_key' => 'pagebreak_' . $this->randomName(), + 'pid' => 0, + // Name is the same as Page 3 break. Tests that name is made unique. + 'name' => '1', + 'page_num' => 4, + ); + // Page 4 textfield. + $textfield['name'] = $this->randomName(); + $textfield['form_key'] = $this->randomName(); + $textfield['page_num'] = 4; + $components[7] = $textfield; + + // Generate a component list. + $node = new stdClass(); + $node->webform['components'] = $components; + $webform_component_list = webform_component_list($node, NULL, TRUE, TRUE); + // Test return value. + $test_list = array( + 1 => $components[1]['name'], + $components[2]['name'] => array(2 => $components[2]['name'], 3 => $components[3]['name']), + $components[4]['name'] . ' ' => array(4 => $components[4]['name'], 5 => $components[5]['name']), + $components[6]['name'] . '_2' => array(6 => $components[6]['name'], 7 => $components[7]['name']), + ); + $this->assertIdentical($webform_component_list, $test_list, 'webform_component_list() returns expected value.'); + + // Test webform_component_parent_keys(). + $components = array( + 1 => array( + 'form_key' => $this->randomName(), + 'name' => $this->randomName(), + 'pid' => 0, + ), + 2 => array( + 'form_key' => $this->randomName(), + 'name' => $this->randomName(), + 'pid' => 1, + ), + 3 => array( + 'form_key' => $this->randomName(), + 'name' => $this->randomName(), + 'pid' => 2, + ), + ); + + $node = new stdClass(); + $node->webform['components'] = $components; + + $parents = webform_component_parent_keys($node, $components[3]); + $test_parents = array($components[1]['form_key'], $components[2]['form_key'], $components[3]['form_key']); + $this->assertIdentical($parents, $test_parents, 'webform_component_parent_keys() returns expected form_keys.'); + + $parents = webform_component_parent_keys($node, $components[3], 'name'); + $test_parents = array($components[1]['name'], $components[2]['name'], $components[3]['name']); + $this->assertIdentical($parents, $test_parents, 'webform_component_parent_keys() returns expected names.'); + + $parents = webform_component_parent_keys($node, $components[3], TRUE); + $test_parents = array($components[1], $components[2], $components[3]); + $this->assertIdentical($parents, $test_parents, 'webform_component_parent_keys() returns expected component arrays.'); + + + // Test webform_get_cid(). + $settings = array( + 'title' => 'Test webform with multiple instances of a Form Key', + 'type' => 'webform', + ); + $node = $this->drupalCreateNode($settings); + + // Add a new textarea component. + $components = $this->webformComponents(); + $textarea = $components['textarea']; + $textarea['type'] = 'textarea'; + $textarea['form_key'] = 'testing_key'; + $textarea['cid'] = 1; + $textarea['pid'] = 0; + $textarea = array_merge(webform_component_invoke('textarea', 'defaults'), $textarea); + $node->webform['components'][1] = $textarea; + + // Add a new fieldset component. + $fieldset = array( + 'cid' => 2, + 'pid' => 0, + 'form_key' => 'another_key', + 'name' => 'Fieldset', + 'type' => 'fieldset', + 'value' => '', + 'required' => '0', + 'weight' => '0', + ); + $node->webform['components'][2] = $fieldset; + + // Add a new textfield component as child of the fieldset. + $textfield = $components['textfield']; + $textfield['type'] = 'textfield'; + $textfield['form_key'] = 'testing_key'; + $textfield['cid'] = 3; + $textfield['pid'] = 2; + $textfield = array_merge(webform_component_invoke('textarea', 'defaults'), $textfield); + $node->webform['components'][3] = $textfield; + + // Add another textfield component. + $textfield = $components['textfield']; + $textfield['type'] = 'textfield'; + $textfield['form_key'] = 'dummy_key'; + $textfield['cid'] = 4; + $textfield['pid'] = 0; + $textfield = array_merge(webform_component_invoke('textarea', 'defaults'), $textfield); + $node->webform['components'][4] = $textfield; + + node_save($node); + + // Test webform_get_cid() with providing a parent cid. + $this->assertTrue(webform_get_cid($node, 'testing_key', 2) == 3, t('Returned expected Webform component id for a given form_key and parent (pid).')); + + // Test webform_get_cid() without providing a parent cid. + $this->assertTrue(webform_get_cid($node, 'testing_key') == array(1, 3), t('Returned expected Webform component ids array for a given form_key.')); + + + // Create and visit a new Webform test node. + $node = $this->webformForm(); + $this->drupalGet('node/' . $node->nid); + + // Check that each label @for points to an element. + $labels = $this->xpath('//label/@for'); + foreach ($labels as $label) { + $for = $this->xpath("//*[@id=':id']", array(':id' => $label['for'])); + $this->assertTrue($for, 'Label with @for "' . $label['for'] . '" points to an element.'); + } + } +} diff --git a/sites/all/modules/contrib/webform/tests/conditionals.test b/sites/all/modules/contrib/webform/tests/WebformConditionalsTestCase.test similarity index 91% rename from sites/all/modules/contrib/webform/tests/conditionals.test rename to sites/all/modules/contrib/webform/tests/WebformConditionalsTestCase.test index a13b77aed..2a8484c66 100644 --- a/sites/all/modules/contrib/webform/tests/conditionals.test +++ b/sites/all/modules/contrib/webform/tests/WebformConditionalsTestCase.test @@ -4,12 +4,9 @@ * @file * Webform module conditional tests. */ - -include_once(dirname(__FILE__) . '/webform.test'); - class WebformConditionalsTestCase extends WebformTestCase { /** - * Implements getInfo(). + * {@inheritdoc} */ public static function getInfo() { return array( @@ -19,20 +16,6 @@ class WebformConditionalsTestCase extends WebformTestCase { ); } - /** - * Implements setUp(). - */ - function setUp($added_modules = array()) { - parent::setUp($added_modules); - } - - /** - * Implements tearDown(). - */ - function tearDown() { - parent::tearDown(); - } - /** * Test that required fields with no default value can't be submitted as-is. */ @@ -40,7 +23,7 @@ class WebformConditionalsTestCase extends WebformTestCase { $this->drupalLogin($this->webform_users['admin']); $this->webformReset(); - $test_components = $this->testWebformComponents(); + $test_components = $this->webformComponents(); $test_specs = array( 'match conditional values' => TRUE, 'mismatch conditional values' => FALSE, @@ -78,9 +61,10 @@ class WebformConditionalsTestCase extends WebformTestCase { * operation to trigger or not. If TRUE, the submission should succeed. * If FALSE, validation errors are expected to be triggered. The input * $value will be compared against the "sample values" input provided by - * testWebformComponents(). - * @return - * None. This function executes its own assert statements to show results. + * webformComponents(). + * + * @return void + * This function executes its own assert statements to show results. */ private function webformTestConditionalComponent($component, $input_values, $operator, $conditional_values, $should_match) { // Create the Webform test node and add a same-page conditional followed @@ -97,7 +81,7 @@ class WebformConditionalsTestCase extends WebformTestCase { $components = array(); $components[] = $component; - $test_components = $this->testWebformComponents(); + $test_components = $this->webformComponents(); $textfield = $test_components['textfield']['component']; // Add a test textfield on the first page. @@ -154,7 +138,7 @@ class WebformConditionalsTestCase extends WebformTestCase { node_save($node); // Submit our test data. - $edit = $this->testWebformPost(array($component['form_key'] => $input_values)); + $edit = $this->webformPost(array($component['form_key'] => $input_values)); $this->drupalPost('node/' . $node->nid, $edit, 'Next Page >', array(), array(), 'webform-client-form-' . $node->nid); // Ensure we reached the second page for matched conditionals. diff --git a/sites/all/modules/contrib/webform/tests/WebformGeneralTestCase.test b/sites/all/modules/contrib/webform/tests/WebformGeneralTestCase.test new file mode 100644 index 000000000..2722c73ff --- /dev/null +++ b/sites/all/modules/contrib/webform/tests/WebformGeneralTestCase.test @@ -0,0 +1,88 @@ + t('Webform'), + 'description' => t('Checks global Webform settings and content types.'), + 'group' => t('Webform'), + ); + } + + /** + * Test creating a new Webform node. + */ + function testWebformCreate() { + $settings = array( + 'title' => 'Test webform, no components', + 'type' => 'webform', + ); + $node = $this->drupalCreateNode($settings); + + // Because this is a "webform" type node, it should have an entry in the + // database even though it's using the default settings. + $this->assertTrue($this->webformRecordExists($node->nid), t('Webform record made in the database for the new webform node.')); + + // Make a change to the node, ensure that the record stays intact. + $node->title .= '!'; + node_save($node); + $this->assertTrue($this->webformRecordExists($node->nid), t('Webform record still in the database after modifying webform node.')); + } + + /** + * Test webform-enabling a different node type and testing behavior. + */ + function testWebformCreateNewType() { + // Enable webforms on the page content type. + variable_set('webform_node_webform', TRUE); + variable_set('webform_node_page', TRUE); + + $settings = array( + 'title' => 'Test webform-enabled page', + 'type' => 'page', + ); + $node = $this->drupalCreateNode($settings); + + // Because this is a webform-enabled type node but does not yet have any + // components, it should not have an entry in the database because it is + // using the default settings. + $this->assertFalse($this->webformRecordExists($node->nid), t('Webform record not in the database for the new page node.')); + + // Make a change to the node, ensure that the record stays empty. + $node->title .= '!'; + node_save($node); + $this->assertFalse($this->webformRecordExists($node->nid), t('Webform record still not in the database after modifying page node.')); + + // Add a new component to the node and check that a record is made in the + // webform table. + $components = $this->webformComponents(); + $textarea = $components['textarea']; + $textarea['type'] = 'textarea'; + $textarea['form_key'] = 'textarea'; + $textarea['cid'] = 1; + $textarea['pid'] = 0; + $textarea = array_merge(webform_component_invoke('textarea', 'defaults'), $textarea); + $node->webform['components'][1] = $textarea; + node_save($node); + $this->assertTrue($this->webformRecordExists($node->nid), t('Webform record now exists after adding a new component.')); + + // Remove the new component and ensure that the record is deleted. + $node->webform['components'] = array(); + node_save($node); + $this->assertFalse($this->webformRecordExists($node->nid), t('Webform record deleted after deleting last component.')); + } + + /** + * @return bool + */ + function webformRecordExists($nid) { + return (bool) db_query("SELECT nid FROM {webform} WHERE nid = :nid", array(':nid' => $nid))->fetchField(); + } +} diff --git a/sites/all/modules/contrib/webform/tests/permissions.test b/sites/all/modules/contrib/webform/tests/WebformPermissionsTestCase.test similarity index 81% rename from sites/all/modules/contrib/webform/tests/permissions.test rename to sites/all/modules/contrib/webform/tests/WebformPermissionsTestCase.test index a238059d8..9779ab72e 100644 --- a/sites/all/modules/contrib/webform/tests/permissions.test +++ b/sites/all/modules/contrib/webform/tests/WebformPermissionsTestCase.test @@ -4,12 +4,9 @@ * @file * Webform module permission tests. */ - -include_once(dirname(__FILE__) . '/webform.test'); - class WebformPermissionsTestCase extends WebformTestCase { /** - * Implements getInfo(). + * {@inheritdoc} */ public static function getInfo() { return array( @@ -19,26 +16,12 @@ class WebformPermissionsTestCase extends WebformTestCase { ); } - /** - * Implements setUp(). - */ - function setUp($added_modules = array()) { - parent::setUp($added_modules); - } - - /** - * Implements tearDown(). - */ - function tearDown() { - parent::tearDown(); - } - /** * Create a webform node in which authenticated users have access to submit. - */ + */ function testWebformSubmitAccess() { $this->webformReset(); - $node = $this->testWebformForm(); + $node = $this->webformForm(); $node->webform['roles'] = array(2); node_save($node); @@ -59,9 +42,4 @@ class WebformPermissionsTestCase extends WebformTestCase { // Something in SimpleTest isn't handling the string correctly. $this->assertText('to view this form.', t('Anonymous user is not allowed to submit form.'), t('Webform')); } - - /** - * Create webform - */ - } diff --git a/sites/all/modules/contrib/webform/tests/submission.test b/sites/all/modules/contrib/webform/tests/WebformSubmissionTestCase.test similarity index 73% rename from sites/all/modules/contrib/webform/tests/submission.test rename to sites/all/modules/contrib/webform/tests/WebformSubmissionTestCase.test index c20413cf5..4e703db84 100644 --- a/sites/all/modules/contrib/webform/tests/submission.test +++ b/sites/all/modules/contrib/webform/tests/WebformSubmissionTestCase.test @@ -4,12 +4,9 @@ * @file * Webform module submission tests. */ - -include_once(dirname(__FILE__) . '/webform.test'); - class WebformSubmissionTestCase extends WebformTestCase { /** - * Implements getInfo(). + * {@inheritdoc} */ public static function getInfo() { return array( @@ -19,20 +16,6 @@ class WebformSubmissionTestCase extends WebformTestCase { ); } - /** - * Implements setUp(). - */ - function setUp($added_modules = array()) { - parent::setUp($added_modules); - } - - /** - * Implements tearDown(). - */ - function tearDown() { - parent::tearDown(); - } - /** * Test sending a submission and check database integrity. */ @@ -72,7 +55,7 @@ class WebformSubmissionTestCase extends WebformTestCase { // Create the Webform test node, and set all components to be required // with no default value. - $node = $this->testWebformForm(); + $node = $this->webformForm(); $node = node_load($node->nid); foreach ($node->webform['components'] as &$component) { $component['value'] = ''; @@ -95,6 +78,44 @@ class WebformSubmissionTestCase extends WebformTestCase { $this->drupalLogout(); } + /** + * Test length validation. + */ + function testWebformSubmissionComponentLength() { + $this->drupalLogin($this->webform_users['admin']); + $this->webformReset(); + + // Create the Webform test node. + $node = $this->webformForm(); + $node = node_load($node->nid); + + // Get the cid of the textfield component. + foreach ($node->webform['components'] as &$component) { + if ($component['form_key'] === 'textfield') { + $textfield_cid = $component['cid']; + break; + } + } + + // Set length validation rules. + $node->webform['components'][$textfield_cid]['extra']['maxlength'] = 5; + $node->webform['components'][$textfield_cid]['extra']['minlength'] = 4; + + node_save($node); + + // Text value that is too long. + $this->drupalPost('node/' . $node->nid, array('submitted[textfield]' => '123456'), 'Submit', array(), array(), 'webform-client-form-' . $node->nid); + $this->assertRaw(t('!name cannot be longer than %max characters but is currently %length characters long.', array('!name' => $node->webform['components'][$textfield_cid]['name'], '%max' => 5, '%length' => 6))); + + // Text value that is too short. + $this->drupalPost('node/' . $node->nid, array('submitted[textfield]' => '123'), 'Submit', array(), array(), 'webform-client-form-' . $node->nid); + $this->assertRaw(t('!name cannot be shorter than %min characters but is currently %length characters long.', array('!name' => $node->webform['components'][$textfield_cid]['name'], '%min' => 4, '%length' => 3))); + + // Test value that meets validation rules has no error message. + $this->drupalPost('node/' . $node->nid, array('submitted[textfield]' => '12345'), 'Submit', array(), array(), 'webform-client-form-' . $node->nid); + $this->assertNoPattern('/ cannot be (longer|shorter) than /'); + } + /** * Execute the submission test. * @@ -106,8 +127,8 @@ class WebformSubmissionTestCase extends WebformTestCase { module_load_include('inc', 'webform', 'includes/webform.submissions'); // Create a new Webform test node. - $node = $this->testWebformForm(); - $submission_values = $value_type == 'sample' ? $this->testWebformPost() : array(); + $node = $this->webformForm(); + $submission_values = $value_type == 'sample' ? $this->webformPost() : array(); // Visit the node page with the "foo=bar" query, to test // [current-page:query:?] default values. @@ -129,7 +150,7 @@ class WebformSubmissionTestCase extends WebformTestCase { drupal_static_reset('webform_get_submission'); $actual_submission = webform_get_submission($node->nid, $sid); - $component_info = $this->testWebformComponents(); + $component_info = $this->webformComponents(); foreach ($node->webform['components'] as $cid => $component) { $stable_value = $value_type == 'sample' ? $component_info[$component['form_key']]['database values'] : $component_info[$component['form_key']]['database default values']; $actual_value = $actual_submission->data[$cid]; @@ -151,12 +172,12 @@ class WebformSubmissionTestCase extends WebformTestCase { module_load_include('inc', 'webform', 'includes/webform.submissions'); // Create a new Webform test node. - $node = $this->testWebformForm(); + $node = $this->webformForm(); // Visit the node page. $this->drupalGet('node/' . $node->nid); - foreach ($this->testWebformComponents() as $key => $component_info) { + foreach ($this->webformComponents() as $key => $component_info) { if (isset($component_info['error values'])) { foreach ($component_info['error values'] as $value => $error_message) { $submission_values = array(); diff --git a/sites/all/modules/contrib/webform/tests/webform.test b/sites/all/modules/contrib/webform/tests/WebformTestCase.test similarity index 90% rename from sites/all/modules/contrib/webform/tests/webform.test rename to sites/all/modules/contrib/webform/tests/WebformTestCase.test index cfad13175..4d12f4250 100644 --- a/sites/all/modules/contrib/webform/tests/webform.test +++ b/sites/all/modules/contrib/webform/tests/WebformTestCase.test @@ -4,14 +4,13 @@ * @file * Webform module tests. */ - class WebformTestCase extends DrupalWebTestCase { private $_webform_node; private $_webform_components; public $webform_users; /** - * Implements setUp(). + * {@inheritdoc} */ function setUp($added_modules = array()) { // Enable Webform and Token module if available. @@ -81,7 +80,7 @@ class WebformTestCase extends DrupalWebTestCase { } /** - * Implemenation of tearDown(). + * {@inheritdoc} */ function tearDown() { // Delete the webform admin and any created nodes. @@ -100,9 +99,6 @@ class WebformTestCase extends DrupalWebTestCase { parent::tearDown(); } - /** - * - */ function webformReset() { $this->_webform_node = NULL; $this->_webform_components = NULL; @@ -120,7 +116,7 @@ class WebformTestCase extends DrupalWebTestCase { * @return array * An array of each component settings. */ - function testWebformComponents() { + function webformComponents() { if (isset($this->_webform_components)) { return $this->_webform_components; } @@ -504,7 +500,7 @@ class WebformTestCase extends DrupalWebTestCase { 'pid' => '0', 'weight' => '-10', ), - // TODO: I'd like to test a value, but SimpleTest can't set multiple values. + // @todo: I'd like to test a value, but SimpleTest can't set multiple values. 'sample values' => NULL, 'database values' => array('one', 'two'), 'database default values' => array('one', 'two'), @@ -721,7 +717,7 @@ class WebformTestCase extends DrupalWebTestCase { 'weight' => '-15', ), // Manually hard-code the input if token is not available. - // TODO: Update after http://drupal.org/node/1347790 is finished. + // @todo: Update after http://drupal.org/node/1347790 is finished. 'sample values' => module_exists('token') ? NULL : 'bar', 'database values' => array('bar'), 'database default values' => module_exists('token') ? array('bar') : array(''), @@ -742,7 +738,7 @@ class WebformTestCase extends DrupalWebTestCase { 'sample values' => 'Female', 'database values' => array('Female'), // The default value will be blank if token does not exist. - // TODO: Update after http://drupal.org/node/1347790 is finished. + // @todo: Update after http://drupal.org/node/1347790 is finished. 'database default values' => module_exists('token') ? array($this->webform_users['admin']->gender[LANGUAGE_NONE][0]['value']) : array(''), ), @@ -990,7 +986,7 @@ class WebformTestCase extends DrupalWebTestCase { return $this->_webform_components; } - function testWebformForm() { + function webformForm() { if (isset($this->_webform_node)) { return $this->_webform_node; } @@ -1014,7 +1010,7 @@ class WebformTestCase extends DrupalWebTestCase { ); $cid = 0; - foreach ($this->testWebformComponents() as $key => $component_info) { + foreach ($this->webformComponents() as $key => $component_info) { $cid++; $settings['webform']['components'][$cid] = $component_info['component']; $settings['webform']['components'][$cid]['cid'] = $cid; @@ -1031,14 +1027,14 @@ class WebformTestCase extends DrupalWebTestCase { * * @param $input_values * An array of input values keyed by the component form key. If none - * are specified, the defaults will be pulled from testWebformComponents(). + * are specified, the defaults will be pulled from webformComponents(). */ - function testWebformPost($input_values = NULL) { + function webformPost($input_values = NULL) { $edit = array(); if (empty($input_values)) { $input_values = array(); - foreach ($this->testWebformComponents() as $key => $component_info) { + foreach ($this->webformComponents() as $key => $component_info) { $input_values[$key] = $component_info['sample values']; } } @@ -1066,86 +1062,3 @@ class WebformTestCase extends DrupalWebTestCase { $this->verbose($this->drupalGetContent()); } } - -/** - * Test general functionality of Webform. - */ -class WebformGeneralTestCase extends WebformTestCase { - /** - * Implements getInfo(). - */ - public static function getInfo() { - return array( - 'name' => t('Webform'), - 'description' => t('Checks global Webform settings and content types.'), - 'group' => t('Webform'), - ); - } - - /** - * Test creating a new Webform node. - */ - function testWebformCreate() { - $settings = array( - 'title' => 'Test webform, no components', - 'type' => 'webform', - ); - $node = $this->drupalCreateNode($settings); - - // Because this is a "webform" type node, it should have an entry in the - // database even though it's using the default settings. - $this->assertTrue($this->webformRecordExists($node->nid), t('Webform record made in the database for the new webform node.')); - - // Make a change to the node, ensure that the record stays intact. - $node->title .= '!'; - node_save($node); - $this->assertTrue($this->webformRecordExists($node->nid), t('Webform record still in the database after modifying webform node.')); - } - - /** - * Test webform-enabling a different node type and testing behavior. - */ - function testWebformCreateNewType() { - // Enable webforms on the page content type. - variable_set('webform_node_webform', TRUE); - variable_set('webform_node_page', TRUE); - - $settings = array( - 'title' => 'Test webform-enabled page', - 'type' => 'page', - ); - $node = $this->drupalCreateNode($settings); - - // Because this is a webform-enabled type node but does not yet have any - // components, it should not have an entry in the database because it is - // using the default settings. - $this->assertFalse($this->webformRecordExists($node->nid), t('Webform record not in the database for the new page node.')); - - // Make a change to the node, ensure that the record stays empty. - $node->title .= '!'; - node_save($node); - $this->assertFalse($this->webformRecordExists($node->nid), t('Webform record still not in the database after modifying page node.')); - - // Add a new component to the node and check that a record is made in the - // webform table. - $components = $this->testWebformComponents(); - $textarea = $components['textarea']; - $textarea['type'] = 'textarea'; - $textarea['form_key'] = 'textarea'; - $textarea['cid'] = 1; - $textarea['pid'] = 0; - $textarea = array_merge(webform_component_invoke('textarea', 'defaults'), $textarea); - $node->webform['components'][1] = $textarea; - node_save($node); - $this->assertTrue($this->webformRecordExists($node->nid), t('Webform record now exists after adding a new component.')); - - // Remove the new component and ensure that the record is deleted. - $node->webform['components'] = array(); - node_save($node); - $this->assertFalse($this->webformRecordExists($node->nid), t('Webform record deleted after deleting last component.')); - } - - function webformRecordExists($nid) { - return (bool) db_query("SELECT nid FROM {webform} WHERE nid = :nid", array(':nid' => $nid))->fetchField(); - } -} diff --git a/sites/all/modules/contrib/webform/tests/components.test b/sites/all/modules/contrib/webform/tests/components.test deleted file mode 100644 index 143f44777..000000000 --- a/sites/all/modules/contrib/webform/tests/components.test +++ /dev/null @@ -1,37 +0,0 @@ - t('Webform components'), - 'description' => t('Add and remove components from a webform.'), - 'group' => t('Webform'), - ); - } - - /** - * Implements setUp(). - */ - function setUp($added_modules = array()) { - parent::setUp($added_modules); - } - - /** - * Implements testWebformDummy(). if it is not present, - * then the test runs fine, but when combined with other tests - * the whole block fails, since there would be no output. - */ - function testWebformDummy() { - $this->pass = t('WebformComponentsTest pass.'); - } -} diff --git a/sites/all/modules/contrib/webform/views/default_views/webform_results.inc b/sites/all/modules/contrib/webform/views/default_views/webform_results.inc index 2e381de52..737f4e09a 100644 --- a/sites/all/modules/contrib/webform/views/default_views/webform_results.inc +++ b/sites/all/modules/contrib/webform/views/default_views/webform_results.inc @@ -90,7 +90,7 @@ $handler->display->display_options['empty']['area_text_custom']['id'] = 'area_te $handler->display->display_options['empty']['area_text_custom']['table'] = 'views'; $handler->display->display_options['empty']['area_text_custom']['field'] = 'area_text_custom'; $handler->display->display_options['empty']['area_text_custom']['empty'] = TRUE; -$handler->display->display_options['empty']['area_text_custom']['content'] = 'There are no submissions for this form. View this form.'; +$handler->display->display_options['empty']['area_text_custom']['content'] = t('There are no submissions for this form. View this form.', array('!url' => url('node/'))); $handler->display->display_options['empty']['area_text_custom']['tokenize'] = TRUE; /* Relationship: Webform submissions: User */ $handler->display->display_options['relationships']['uid']['id'] = 'uid'; @@ -142,7 +142,7 @@ $handler->display->display_options['arguments']['nid']['summary_options']['items $handler->display->display_options['arguments']['nid']['specify_validation'] = TRUE; $handler->display->display_options['arguments']['nid']['validate']['type'] = 'node'; $translatables['webform_results'] = array( - t('Master'), + t('Master'), t('more'), t('Apply'), t('Reset'), diff --git a/sites/all/modules/contrib/webform/views/default_views/webform_submissions.inc b/sites/all/modules/contrib/webform/views/default_views/webform_submissions.inc index 2c93d06d2..703768666 100644 --- a/sites/all/modules/contrib/webform/views/default_views/webform_submissions.inc +++ b/sites/all/modules/contrib/webform/views/default_views/webform_submissions.inc @@ -107,7 +107,7 @@ $handler->display->display_options['empty']['area_text_custom']['id'] = 'area_te $handler->display->display_options['empty']['area_text_custom']['table'] = 'views'; $handler->display->display_options['empty']['area_text_custom']['field'] = 'area_text_custom'; $handler->display->display_options['empty']['area_text_custom']['empty'] = TRUE; -$handler->display->display_options['empty']['area_text_custom']['content'] = 'There are no submissions for this form. View this form.'; +$handler->display->display_options['empty']['area_text_custom']['content'] = t('There are no submissions for this form. View this form.', array('!url' => url('node/'))); $handler->display->display_options['empty']['area_text_custom']['tokenize'] = TRUE; /* Relationship: Webform submissions: User */ $handler->display->display_options['relationships']['uid']['id'] = 'uid'; diff --git a/sites/all/modules/contrib/webform/views/webform.views.inc b/sites/all/modules/contrib/webform/views/webform.views.inc index 79ac3968f..fb062465d 100644 --- a/sites/all/modules/contrib/webform/views/webform.views.inc +++ b/sites/all/modules/contrib/webform/views/webform.views.inc @@ -6,10 +6,7 @@ */ function webform_views_data() { - - /** - * Webform table definitions. - */ + // Webform table definitions. $data['webform']['table']['group'] = t('Webform'); $data['webform']['table']['base'] = array( 'field' => 'nid', @@ -66,9 +63,7 @@ function webform_views_data() { ), ); - /** - * Submissions table definitions. - */ + // Submissions table definitions. $data['webform_submissions']['table']['group'] = t('Webform submissions'); $data['webform_submissions']['table']['base'] = array( 'field' => 'sid', @@ -342,9 +337,7 @@ function webform_views_data() { ), ); - /** - * Submission data table definitions. - */ + // Submission data table definitions. $data['webform_submitted_data']['table']['group'] = t('Webform submission data'); // Raw access to the submitted values. This usually will only be used for diff --git a/sites/all/modules/contrib/webform/views/webform_handler_field_submission_data.inc b/sites/all/modules/contrib/webform/views/webform_handler_field_submission_data.inc index 87f66aa44..3366b6411 100644 --- a/sites/all/modules/contrib/webform/views/webform_handler_field_submission_data.inc +++ b/sites/all/modules/contrib/webform/views/webform_handler_field_submission_data.inc @@ -188,7 +188,7 @@ class webform_handler_field_submission_data extends views_handler_field { $this->view->_webform_components[$nid][$submission->nid][$cid] = $component; $submission_node = node_load($submission->nid); foreach ($submission_node->webform['components'] as $sub_cid => $sub_component) { - if ($sub_component['form_key'] == $component['form_key'] && $sub_component['type'] == $component['type']) { + if ((string) $sub_component['form_key'] === (string) $component['form_key'] && $sub_component['type'] == $component['type']) { $this->view->_webform_components[$nid][$submission->nid]['webform'] = $submission_node; $this->view->_webform_components[$nid][$submission->nid][$cid] = $sub_component; break; diff --git a/sites/all/modules/contrib/webform/views/webform_handler_numeric_data.inc b/sites/all/modules/contrib/webform/views/webform_handler_numeric_data.inc index d3b15ba76..8c678ba7f 100644 --- a/sites/all/modules/contrib/webform/views/webform_handler_numeric_data.inc +++ b/sites/all/modules/contrib/webform/views/webform_handler_numeric_data.inc @@ -21,7 +21,7 @@ class webform_handler_field_numeric_data extends views_handler_field_numeric { parent::construct(); $this->formula = TRUE; } - + /** * Get the formula for this argument. * @@ -53,7 +53,7 @@ class webform_handler_field_numeric_data extends views_handler_field_numeric { function get_field($field = NULL) { return parent::get_field($this->get_formula()); } - + } /** @@ -91,8 +91,8 @@ class webform_handler_filter_numeric_data extends views_handler_filter_numeric { static $sequence = 1; $param = ":value" . $sequence++; $this->query->add_where_expression($this->options['group'], - $field . $this->operator . $param, - array($param => $this->value['value'])); + $field . $this->operator . $param, + array($param => $this->value['value'])); } /** @@ -104,13 +104,13 @@ class webform_handler_filter_numeric_data extends views_handler_filter_numeric { $max = ":max" . $sequence++; if ($this->operator == 'between') { $this->query->add_where_expression($this->options['group'], - "($min <= $field AND $field <= $max)", - array($min => $this->value['min'], $max => $this->value['max'])); + "($min <= $field AND $field <= $max)", + array($min => $this->value['min'], $max => $this->value['max'])); } else { $this->query->add_where_expression($this->options['group'], - "($min > $field OR $field > $max)", - array($min => $this->value['min'], $max => $this->value['max'])); + "($min > $field OR $field > $max)", + array($min => $this->value['min'], $max => $this->value['max'])); } } @@ -137,8 +137,8 @@ class webform_handler_filter_numeric_data extends views_handler_filter_numeric { $param = ":expression" . $sequence++; $this->query->add_where_expression($this->options['group'], - "$field RLIKE $param", - array($param => $this->value['value'])); + "$field RLIKE $param", + array($param => $this->value['value'])); } } diff --git a/sites/all/modules/contrib/webform/webform.api.php b/sites/all/modules/contrib/webform/webform.api.php index 70968e624..8691fcacb 100644 --- a/sites/all/modules/contrib/webform/webform.api.php +++ b/sites/all/modules/contrib/webform/webform.api.php @@ -24,12 +24,13 @@ * @see webform_options_example() * @see hook_webform_select_options_info_alter() * - * @return + * @return array * An array of callbacks that can be used for select list options. This array * should be keyed by the "name" of the pre-defined list. The values should * be an array with the following additional keys: * - title: The translated title for this list. - * - options callback: The name of the function that will return the list. + * - options callback: The name of a function implementing + * callback_webform_options() that will return the list. * - options arguments: Any additional arguments to send to the callback. * - file: Optional. The file containing the options callback, relative to * the module root. @@ -49,7 +50,7 @@ function hook_webform_select_options_info() { /** * Alter the list of select list options provided by Webform and other modules. * - * @see hook_webform_select_options_info(). + * @see hook_webform_select_options_info() */ function hook_webform_select_options_info_alter(&$items) { // Remove the days of the week options. @@ -57,12 +58,9 @@ function hook_webform_select_options_info_alter(&$items) { } /** - * This is an example function to demonstrate a webform options callback. + * Define a list of options that Webform may use in a select component. * - * This function returns a list of options that Webform may use in a select - * component. In order to be called, the function name - * ("webform_options_example" in this case), needs to be specified as a callback - * in hook_webform_select_options_info(). + * Callback for hook_webform_select_options_info(). * * @param $component * The Webform component array for the select component being displayed. @@ -73,11 +71,12 @@ function hook_webform_select_options_info_alter(&$items) { * without the nesting. * @param $arguments * The "options arguments" specified in hook_webform_select_options_info(). - * @return + * + * @return array * An array of key => value pairs suitable for a select list's #options * FormAPI property. */ -function webform_options_example($component, $flat, $arguments) { +function callback_webform_options($component, $flat, $arguments) { $options = array( 'one' => t('Pre-built option one'), 'two' => t('Pre-built option two'), @@ -214,6 +213,9 @@ function hook_webform_submission_delete($node, $submission) { * The Webform node on which this submission was made. * @param $submission * The Webform submission on which the actions may be performed. + * + * @return array + * List of action. */ function hook_webform_submission_actions($node, $submission) { $actions= array(); @@ -428,7 +430,7 @@ function hook_webform_csv_data_alter(&$data, $component, $submission) { /** * Define components to Webform. * - * @return + * @return array * An array of components, keyed by machine name. Required properties are * "label" and "description". The "features" array defines which capabilities * the component has, such as being displayed in e-mails or csv downloads. @@ -613,7 +615,8 @@ function hook_webform_component_defaults_alter(&$defaults, $type) { * - "list" * @param $account * A user account object. - * @return + * + * @return bool * TRUE if the current user has access to submission, * or FALSE otherwise. */ @@ -642,13 +645,14 @@ function hook_webform_submission_access($node, $submission, $op = 'view', $accou * Access via this hook is in addition (adds permission) to the standard * webform access. * - * @see webform_results_access(). + * @see webform_results_access() * * @param $node * The Webform node to check access on. * @param $account * The user account to check access on. - * @return + * + * @return bool * TRUE or FALSE if the user can access the webform results. */ function hook_webform_results_access($node, $account) { @@ -667,7 +671,7 @@ function hook_webform_results_access($node, $account) { * Access via this hook is in addition (adds permission) to the standard * webform access (delete all webform submissions). * - * @see webform_results_clear_access(). + * @see webform_results_clear_access() * * @param object $node * The Webform node to check access on. @@ -695,7 +699,7 @@ function hook_webform_results_clear_access($node, $account) { * access as this will be the only test. For example, 'return TRUE;' would grant * annonymous access to creating webform components, which seldom be desired. * - * @see webform_node_update_access(). + * @see webform_node_update_access() * * @param object $node * The Webform node to check access on. @@ -715,7 +719,6 @@ function hook_webform_update_access($node, $account) { } } - /** * Return an array of files associated with the component. * @@ -726,7 +729,8 @@ function hook_webform_update_access($node, $account) { * @param $value * An array of information containing the submission result, directly * correlating to the webform_submitted_data database schema. - * @return + * + * @return array * An array of files, each file is an array with following keys: * - filepath: The relative path to the file. * - filename: The name of the file including the extension. @@ -746,7 +750,6 @@ function _webform_attachments_component($component, $value) { return $files; } - /** * Alter default settings for a newly created webform node. * @@ -762,7 +765,7 @@ function hook_webform_node_defaults_alter(&$defaults) { /** * Add additional fields to submission data downloads. * - * @return + * @return array * Keys and titles for default submission information. * * @see hook_webform_results_download_submission_information_data() @@ -781,7 +784,7 @@ function hook_webform_results_download_submission_information_info() { * The name of the token being replaced. * @param $submission * The data for an individual submission from webform_get_submissions(). - * @param $options + * @param array $options * A list of options that define the output format. These are generally passed * through from the GUI interface. * @param $serial_start @@ -789,7 +792,7 @@ function hook_webform_results_download_submission_information_info() { * @param $row_count * The number of the row being generated. * - * @return + * @return string * Value for requested submission information field. * * @see hook_webform_results_download_submission_information_info() @@ -819,7 +822,7 @@ function hook_webform_results_download_submission_information_data($token, $subm /** * Specify the default properties of a component. * - * @return + * @return array * An array defining the default structure of a component. */ function _webform_defaults_component() { @@ -853,7 +856,8 @@ function _webform_defaults_component() { * * @param $component * A Webform component array. - * @return + * + * @return array * An array of form items to be displayed on the edit component page */ function _webform_edit_component($component) { @@ -896,6 +900,9 @@ function _webform_edit_component($component) { * needed. Used by _webform_render_date() to validate using the submission's * completion date. * + * @return array + * $form_item + * * @see _webform_client_form_add_component() */ function _webform_render_component($component, $value = NULL, $filter = TRUE, $submission = NULL) { @@ -950,7 +957,8 @@ function hook_webform_component_render_alter(&$element, &$component) { * or other filtering functions when returning HTML. * @param $submission * The submission. Used to generate tokens. - * @return + * + * @return array * A renderable element containing at the very least these properties: * - #title * - #weight @@ -1120,7 +1128,8 @@ function _webform_theme_component() { * @param $join * An optional SelectQuery object to be used to join with the submissions * table to restrict the submissions being analyzed. - * @return + * + * @return array * An array containing one or more of the following keys: * - table_rows: If this component has numeric data that can be represented in * a grid, return the values here. This array assumes a 2-dimensional @@ -1208,7 +1217,8 @@ function _webform_analysis_component($component, $sids = array(), $single = FALS * @param $value * An array of information containing the submission result, directly * correlating to the webform_submitted_data database schema. - * @return + * + * @return string * Textual output formatted for human reading. */ function _webform_table_component($component, $value) { @@ -1238,7 +1248,8 @@ function _webform_table_component($component, $value) { * A Webform component array. * @param $export_options * An array of options that may configure export of this field. - * @return + * + * @return array * An array of data to be displayed in the first three rows of a CSV file, not * including either prefixed or trailing commas. */ @@ -1275,7 +1286,8 @@ function _webform_csv_headers_component($component, $export_options) { * @param $value * An array of information containing the submission result, directly * correlating to the webform_submitted_data database schema. - * @return + * + * @return array * An array of items to be added to the CSV file. Each value within the array * will be another column within the file. This function is called once for * every row of data. diff --git a/sites/all/modules/contrib/webform/webform.drush.inc b/sites/all/modules/contrib/webform/webform.drush.inc index 9a95901ed..b8435f741 100644 --- a/sites/all/modules/contrib/webform/webform.drush.inc +++ b/sites/all/modules/contrib/webform/webform.drush.inc @@ -2,9 +2,12 @@ /** * @file - * Implementation of hook_drush_command(). + * Functions relating to Drush integration. */ +/** + * Implements hook_drush_command(). + */ function webform_drush_command() { return array( 'webform-export' => array( @@ -46,8 +49,11 @@ function webform_drush_command() { * Exports a webform via drush, useful for large data dumps that would otherwise * time out due to memory consumption. * - * @param int $nid + * @param bool|int $nid * Node ID of the webform that we want to export. + * + * @return + * The value returned from drush_set_error(). */ function drush_webform_export($nid = FALSE) { if (!$nid) { @@ -102,16 +108,16 @@ function drush_webform_export($nid = FALSE) { array('!opt' => "range-$option_name", '!val' => $option_value))); } } - + // Determine the range type based on provided input, if not explicitly set. if (empty($options['range']['range_type'])) { $options['range']['range_type'] = isset($options['range']['start']) - ? 'range' - : (isset($options['range']['latest']) - ? 'latest' - : 'all'); + ? 'range' + : (isset($options['range']['latest']) + ? 'latest' + : 'all'); } - + // Set defaults for any missing range arguments. switch ($options['range']['range_type']) { case 'latest': @@ -134,7 +140,7 @@ function drush_webform_export($nid = FALSE) { } // Get the preferred completion type - $options['range']['completion_type'] = drush_get_option('completion-type', NULL); + $options['range']['completion_type'] = drush_get_option('completion-type', NULL); if (isset($options['range']['completion_type']) && !in_array($options['range']['completion_type'], array('finished', 'draft', 'all'))) { return drush_set_error('Unsupported completion-type. The available options are "finished", "draft", or "all".'); } @@ -189,10 +195,21 @@ function drush_webform_clear($nid = FALSE) { } /** - * Implements hook_drush_sql_sync_sanitize. + * Implements hook_drush_sql_sync_sanitize(). */ function webform_drush_sql_sync_sanitize($source) { + // Fetch list of all table. + $all_tables = drush_sql_get_class()->listTables(); + $tables_to_truncate = array('webform_submitted_data', 'webform_submissions'); + + $truncate_webform_tables_query = array(); + foreach ($tables_to_truncate as $table) { + if (in_array($table, $all_tables, TRUE)) { + $truncate_webform_tables_query[] = 'TRUNCATE ' . $table . ';'; + } + } + drush_sql_register_post_sync_op('webform_submitted_data', dt('Delete all data submitted to webforms (depending on the site config, may contain sensitive data).'), - "TRUNCATE webform_submitted_data; TRUNCATE webform_submissions;"); + implode(' ', $truncate_webform_tables_query)); } diff --git a/sites/all/modules/contrib/webform/webform.info b/sites/all/modules/contrib/webform/webform.info index 873a63a79..ad3d1fe15 100644 --- a/sites/all/modules/contrib/webform/webform.info +++ b/sites/all/modules/contrib/webform/webform.info @@ -1,4 +1,3 @@ -; $Id: $ name = Webform description = Enables the creation of forms and questionnaires. core = 7.x @@ -34,15 +33,16 @@ files[] = views/webform_handler_relationship_submission_data.inc files[] = views/webform_plugin_row_submission_view.inc files[] = views/webform.views.inc -files[] = tests/components.test -files[] = tests/conditionals.test -files[] = tests/permissions.test -files[] = tests/submission.test -files[] = tests/webform.test +files[] = tests/WebformComponentsTestCase.test +files[] = tests/WebformConditionalsTestCase.test +files[] = tests/WebformGeneralTestCase.test +files[] = tests/WebformPermissionsTestCase.test +files[] = tests/WebformSubmissionTestCase.test +files[] = tests/WebformTestCase.test -; Information added by Drupal.org packaging script on 2016-08-28 -version = "7.x-4.14" +; Information added by Drupal.org packaging script on 2017-04-13 +version = "7.x-4.15" core = "7.x" project = "webform" -datestamp = "1472386754" +datestamp = "1492107249" diff --git a/sites/all/modules/contrib/webform/webform.module b/sites/all/modules/contrib/webform/webform.module index b0971bed7..48a151651 100644 --- a/sites/all/modules/contrib/webform/webform.module +++ b/sites/all/modules/contrib/webform/webform.module @@ -36,9 +36,11 @@ function webform_help($section = 'admin/help#webform', $arg = NULL) { } $output = '

' . $output . '

'; break; + case 'admin/content/webform': $output = '

' . t('This page lists all of the content on the site that may have a webform attached to it.') . '

'; break; + case 'admin/help#webform': module_load_include('inc', 'webform', 'includes/webform.admin'); $types = webform_admin_type_list(); @@ -67,11 +69,18 @@ function webform_help($section = 'admin/help#webform', $arg = NULL) {
  • Your form is now ready for viewing. After receiving submissions, you can check the results users have submitted by visiting the Results tab on the piece of content.
  • Help on adding and configuring the components will be shown after you add your first component.

    - ", array('!webform-types-message' => $types_message, '!create-content' => url('node/add'), '!types' => $types)); + ", array( + '!webform-types-message' => $types_message, + '!create-content' => url('node/add'), + '!types' => $types, + ) + ); break; + case 'node/%/webform/conditionals': $output .= '

    ' . t('Conditionals may be used to hide or show certain components (or entire pages!) based on the value of other components.') . '

    '; break; + case 'node/%/submission/%/resend': $output .= '

    ' . t('This form may be used to resend e-mails configured for this webform. Check the e-mails that need to be sent and click Resend e-mails to send these e-mails again.') . '

    '; break; @@ -455,10 +464,11 @@ function webform_menu_load($nid) { * @param string $arg * The argument supplied by the caller. * @param array $map - * Array of path fragments (e.g. e.g. array('node','123','edit') for + * Array of path fragments (for example, array('node','123','edit') for * 'node/123/edit'). - * @param integer $index + * @param int $index * Which element of $map corresponds to $arg. + * * @return string * The $arg, modified as desired. */ @@ -484,7 +494,14 @@ function webform_menu_component_load($cid, $nid, $type) { module_load_include('inc', 'webform', 'includes/webform.components'); if ($cid == 'new') { $components = webform_components(); - $component = in_array($type, array_keys($components)) ? array('type' => $type, 'nid' => $nid, 'name' => $_GET['name'], 'required' => $_GET['required'], 'pid' => $_GET['pid'], 'weight' => $_GET['weight']) : FALSE; + $component = in_array($type, array_keys($components)) ? array( + 'type' => $type, + 'nid' => $nid, + 'name' => $_GET['name'], + 'required' => $_GET['required'], + 'pid' => $_GET['pid'], + 'weight' => $_GET['weight'], + ) : FALSE; } else { $node = node_load($nid); @@ -496,7 +513,6 @@ function webform_menu_component_load($cid, $nid, $type) { return $component; } - /** * Menu loader callback. Load a webform e-mail if the given eid is a valid. */ @@ -538,11 +554,11 @@ function webform_get_submission_access_token($submission) { /** * Access function for confirmation pages. * - * @param $node + * @param stdClass $node * The webform node object. * - * @return - * Boolean whether the user has access to the confirmation page. + * @return bool + * Boolean whether the user has access to the confirmation page. */ function webform_confirmation_page_access($node) { global $user; @@ -582,7 +598,8 @@ function webform_confirmation_page_access($node) { } else { // No submission exists (such as auto-deleted by another module, such as - // webform_clear), just ensure that the user has access to view the node page. + // webform_clear), just ensure that the user has access to view the node + // page. if (node_access('view', $node)) { return TRUE; } @@ -594,16 +611,16 @@ function webform_confirmation_page_access($node) { /** * Access function for Webform submissions. * - * @param $node + * @param stdClass $node * The webform node object. - * @param $submission + * @param stdClass $submission * The webform submission object. - * @param $op + * @param stdClass $op * The operation to perform. Must be one of view, edit, delete, list. - * @param $account + * @param array $account * Optional. A user object or NULL to use the currently logged-in user. * - * @return + * @return bool * Boolean whether the user has access to a webform submission. */ function webform_submission_access($node, $submission, $op = 'view', $account = NULL) { @@ -635,10 +652,13 @@ function webform_submission_access($node, $submission, $op = 'view', $account = switch ($op) { case 'view': return $module_access || $general_access; + case 'edit': return $module_access || ($general_access && (user_access('edit all webform submissions', $account) || (user_access('edit own webform submissions', $account) && $account->uid == $submission->uid))); + case 'delete': return $module_access || ($general_access && (user_access('delete all webform submissions', $account) || (user_access('delete own webform submissions', $account) && $account->uid == $submission->uid))); + case 'list': return $module_access || user_access('access all webform results', $account) || (user_access('access own webform submissions', $account) && ($account->uid || isset($_SESSION['webform_submission']))) || (user_access('access own webform results', $account) && $account->uid == $node->uid); } @@ -685,7 +705,7 @@ function webform_results_clear_access($node, $account = NULL) { * be granted, FALSE if it should absolutely be denied, or NULL if node_access * and 'edit webform components' permission should determine access. * - * @see hook_webform_update_access(). + * @see hook_webform_update_access() */ function webform_node_update_access($node, $account = NULL) { global $user; @@ -758,7 +778,16 @@ function webform_theme() { 'render element' => 'webform', ), 'webform_view_messages' => array( - 'variables' => array('node' => NULL, 'page' => NULL, 'submission_count' => NULL, 'user_limit_exceeded' => NULL, 'total_limit_exceeded' => NULL, 'allowed_roles' => NULL, 'closed' => NULL, 'cached' => NULL), + 'variables' => array( + 'node' => NULL, + 'page' => NULL, + 'submission_count' => NULL, + 'user_limit_exceeded' => NULL, + 'total_limit_exceeded' => NULL, + 'allowed_roles' => NULL, + 'closed' => NULL, + 'cached' => NULL, + ), ), 'webform_form' => array( 'render element' => 'form', @@ -783,16 +812,29 @@ function webform_theme() { 'render element' => 'element', ), 'webform_progressbar' => array( - 'variables' => array('node' => NULL, 'page_num' => NULL, 'page_count' => NULL, 'page_labels' => array()), + 'variables' => array( + 'node' => NULL, + 'page_num' => NULL, + 'page_count' => NULL, + 'page_labels' => array(), + ), 'template' => 'templates/webform-progressbar', ), 'webform_mail_message' => array( - 'variables' => array('node' => NULL, 'submission' => NULL, 'email' => NULL), + 'variables' => array( + 'node' => NULL, + 'submission' => NULL, + 'email' => NULL, + ), 'template' => 'templates/webform-mail', 'pattern' => 'webform_mail(_[0-9]+)?', ), 'webform_mail_headers' => array( - 'variables' => array('node' => NULL, 'submission' => NULL, 'email' => NULL), + 'variables' => array( + 'node' => NULL, + 'submission' => NULL, + 'email' => NULL, + ), 'pattern' => 'webform_mail_headers_[0-9]+', ), 'webform_token_help' => array( @@ -882,7 +924,13 @@ function webform_theme() { 'file' => 'includes/webform.report.inc', ), 'webform_results_table' => array( - 'variables' => array('node' => NULL, 'components' => NULL, 'submissions' => NULL, 'total_count' => NULL, 'pager_count' => NULL), + 'variables' => array( + 'node' => NULL, + 'components' => NULL, + 'submissions' => NULL, + 'total_count' => NULL, + 'pager_count' => NULL, + ), 'file' => 'includes/webform.report.inc', ), 'webform_results_download_range' => array( @@ -907,7 +955,7 @@ function webform_theme() { 'variables' => array('component' => NULL, 'data' => NULL), 'file' => 'includes/webform.report.inc', ), - // webform.submissions.inc + // webform.submissions.inc. 'webform_submission' => array( 'render element' => 'renderable', 'template' => 'templates/webform-submission', @@ -915,12 +963,24 @@ function webform_theme() { 'file' => 'includes/webform.submissions.inc', ), 'webform_submission_page' => array( - 'variables' => array('node' => NULL, 'submission' => NULL, 'submission_content' => NULL, 'submission_navigation' => NULL, 'submission_information' => NULL, 'submission_actions' => NULL, 'mode' => NULL), + 'variables' => array( + 'node' => NULL, + 'submission' => NULL, + 'submission_content' => NULL, + 'submission_navigation' => NULL, + 'submission_information' => NULL, + 'submission_actions' => NULL, + 'mode' => NULL, + ), 'template' => 'templates/webform-submission-page', 'file' => 'includes/webform.submissions.inc', ), 'webform_submission_information' => array( - 'variables' => array('node' => NULL, 'submission' => NULL, 'mode' => 'display'), + 'variables' => array( + 'node' => NULL, + 'submission' => NULL, + 'mode' => 'display', + ), 'template' => 'templates/webform-submission-information', 'file' => 'includes/webform.submissions.inc', ), @@ -1187,7 +1247,7 @@ function webform_webform_conditional_operator_info() { /** * Implements hook_forms(). * - * All webform_client_form forms share the same form handler + * All webform_client_form forms share the same form handler. */ function webform_forms($form_id) { $forms = array(); @@ -1285,7 +1345,8 @@ function webform_webform_submission_presave($node, &$submission) { } } - // Only rename files if this is the first time the submission is being saved as finished. + // Only rename files if this is the first time the submission is being saved + // as finished. if ($submission->is_draft || (isset($old_submission) && !$old_submission->is_draft)) { $renameable = array(); } @@ -1460,7 +1521,8 @@ function webform_node_insert($node) { // Insert the components into the database. Used with clone.module. if (isset($node->webform['components']) && !empty($node->webform['components'])) { foreach ($node->webform['components'] as $cid => $component) { - $component['nid'] = $node->nid; // Required for clone.module. + // Required for clone.module. + $component['nid'] = $node->nid; webform_component_insert($component); } } @@ -1571,7 +1633,7 @@ function webform_node_update($node) { $conditional['nid'] = $node->nid; $conditional['rgid'] = $rgid; if (!isset($original->webform['conditionals'][$rgid]) || $original->webform['conditionals'][$rgid] != $conditional) { - webform_conditional_insert($conditional); + webform_conditional_insert($conditional); } } } @@ -1696,7 +1758,6 @@ function webform_node_prepare($node) { } } - /** * Implements hook_node_load(). */ @@ -1811,14 +1872,13 @@ function webform_node_load($nodes, $types) { } } } - } /** -* Implements hook_user_role_delete(). -* -* Removes references to deleted role from existing webforms. -*/ + * Implements hook_user_role_delete(). + * + * Removes references to deleted role from existing webforms. + */ function webform_user_role_delete($role) { db_delete('webform_roles')->condition('rid', $role->rid)->execute(); } @@ -1934,7 +1994,8 @@ function webform_node_view($node, $view_mode) { $allowed_roles = array(); } else { - $allowed_roles = _webform_allowed_roles($node, $enabled); // $enabled set by reference. + // $enabled set by reference. + $allowed_roles = _webform_allowed_roles($node, $enabled); } // Get a count of previous submissions by this user. Note that the @@ -1948,13 +2009,16 @@ function webform_node_view($node, $view_mode) { // Check if this page is cached or not. $cached = drupal_page_is_cacheable(); - // Check if the user can add another submission based on the individual submission limit. - if ($node->webform['submit_limit'] != -1) { // -1: Submissions are never throttled. + // Check if the user can add another submission based on the individual + // submission limit. + // -1: Submissions are never throttled. + if ($node->webform['submit_limit'] != -1) { module_load_include('inc', 'webform', 'includes/webform.submissions'); - // Disable the form if the limit is exceeded and page cache is not active. This prevent - // One anonymous user from generated a disabled webform page for the cache, which would - // be shown to other anonymous users who have not exceeded the limit. + // Disable the form if the limit is exceeded and page cache is not active. + // This prevents one anonymous user from generated a disabled webform page + // for the cache, which would be shown to other anonymous users who have not + // exceeded the limit. if (($user_limit_exceeded = webform_submission_user_limit_check($node)) && !$cached) { $enabled = FALSE; } @@ -1962,12 +2026,13 @@ function webform_node_view($node, $view_mode) { // Check if the user can add another submission if there is a limit on total // submissions. - if ($node->webform['total_submit_limit'] != -1) { // -1: Submissions are never throttled. + // -1: Submissions are never throttled. + if ($node->webform['total_submit_limit'] != -1) { module_load_include('inc', 'webform', 'includes/webform.submissions'); - // Disable the form if the limit is exceeded. The cache is irrelevant for the total - // submission limit; when it is exceeded for one user, it is exceeded for any other - // user. + // Disable the form if the limit is exceeded. The cache is irrelevant for + // the total submission limit; when it is exceeded for one user, it is + // exceeded for any other user. if (($total_limit_exceeded = webform_submission_total_limit_check($node))) { $enabled = FALSE; } @@ -2005,7 +2070,17 @@ function webform_node_view($node, $view_mode) { // Print out messages for the webform. if (empty($node->in_preview) && !isset($node->webform_block) && !$logging_in) { - theme('webform_view_messages', array('node' => $node, 'page' => $page, 'submission_count' => $submission_count, 'user_limit_exceeded' => $user_limit_exceeded, 'total_limit_exceeded' => $total_limit_exceeded, 'allowed_roles' => $allowed_roles, 'closed' => $closed, 'cached' => $cached)); + theme('webform_view_messages', array( + 'node' => $node, + 'page' => $page, + 'submission_count' => $submission_count, + 'user_limit_exceeded' => $user_limit_exceeded, + 'total_limit_exceeded' => $total_limit_exceeded, + 'allowed_roles' => $allowed_roles, + 'closed' => $closed, + 'cached' => $cached, + ) + ); } // Add the output to the node. @@ -2023,10 +2098,11 @@ function webform_node_view($node, $view_mode) { /** * Helper. Generates an array of allowed roles. * - * @param object $node + * @param stdClass $node * The loaded node object containing a webform. - * @param boolean $user_is_allowed + * @param bool $user_is_allowed * Reference to boolean to be set to whether the current user is allowed. + * * @return array * Associative array of allowed roles indexed by the role id with a boolean * value indicating if the current user has this role. @@ -2057,14 +2133,16 @@ function _webform_allowed_roles($node, &$user_is_allowed) { /** * Output the Webform into the node content. * - * @param $node + * @param stdClass $node * The webform node object. - * @param $page + * @param stdClass $page * If this webform node is being viewed as the main content of the page. - * @param $form + * @param array $form * The rendered form. * @param $enabled * If the form allowed to be completed by the current user. + * + * @return string */ function theme_webform_view($variables) { // Only show the form if this user is allowed access. @@ -2076,7 +2154,7 @@ function theme_webform_view($variables) { /** * Display a message to a user if they are not allowed to fill out a form. * - * @param $node + * @param stdClass $node * The webform node object. * @param $page * If this webform node is being viewed as the main content of the page. @@ -2110,8 +2188,7 @@ function theme_webform_view_messages($variables) { $message = t('Submissions for this form are closed.'); } elseif ($node->webform['confidential'] && user_is_logged_in()) { - $message = t('This form is confidential. You must Log out to submit it.', - array('!url' => url('/user/logout', array('query' => array('destination' => request_uri()))))); + $message = t('This form is confidential. You must Log out to submit it.', array('!url' => url('/user/logout', array('query' => array('destination' => request_uri()))))); } // If open and not allowed to submit the form, give an explanation. elseif (array_search(TRUE, $allowed_roles) === FALSE && $user->uid != 1) { @@ -2276,7 +2353,7 @@ function webform_block_view($delta = '') { '#node' => $node, '#sid' => $_SESSION['webform_confirmation'][$nid]['sid'], ); - } + } elseif (strlen(trim(strip_tags($node->webform['confirmation'])))) { // Display confirmation link drupal status messages, but in the block. $message = webform_replace_tokens($node->webform['confirmation'], @@ -2287,14 +2364,13 @@ function webform_block_view($delta = '') { $content = array( 'confirmation_message' => array( '#markup' => "
    \n" . - '

    ' . t('Status message') . "

    \n" . - $message . - "
    \n", + '

    ' . t('Status message') . "

    \n" . + $message . + "
    \n", '#weight' => -1, ), 'webform_view' => $content, ); - } unset($_SESSION['webform_confirmation'][$nid]); if (empty($_SESSION['webform_confirmation'])) { @@ -2401,7 +2477,7 @@ function webform_block_save($delta = '', $edit = array()) { * The current form array (always empty). * @param $form_state * The current form values of a submission, used in multipage webforms. - * @param $node + * @param stdClass $node * The current webform node. * @param $submission * An object containing information about the form submission if we're @@ -2463,13 +2539,13 @@ function webform_client_form($form, &$form_state, $node, $submission = FALSE, $r $form['#submit'] = array('webform_client_form_pages', 'webform_client_form_submit'); $form['#validate'] = array('webform_client_form_validate'); - // Add includes for used component types and pre/post validation handlers + // Add includes for used component types and pre/post validation handlers. $form['#process'] = array('webform_client_form_process'); if (is_array($node->webform['components']) && !empty($node->webform['components'])) { // Prepare a new form array. $form['submitted'] = array( - '#tree' => TRUE + '#tree' => TRUE, ); $form['details'] = array( '#tree' => TRUE, @@ -2528,7 +2604,7 @@ function webform_client_form($form, &$form_state, $node, $submission = FALSE, $r $sorter = webform_get_conditional_sorter($node); $sorter->reportErrors(); - // Excecute the condtionals on the current input values + // Excecute the condtionals on the current input values. $input_values = $sorter->executeConditionals($input_values); // Allow values from other pages to be sent to browser for conditionals. @@ -2544,12 +2620,12 @@ function webform_client_form($form, &$form_state, $node, $submission = FALSE, $r // 1) previous/next non-empty page, or // 2) the preview page, or // 3) the preview page, forcing its display if the form would unexpectedly submit, or - // 4) page 1 even if empty, if no other previous page would be shown + // 4) page 1 even if empty, if no other previous page would be shown. $form_state['webform']['page_num'] = $submission->highest_valid_page; do { $form_state['webform']['page_num']++; } while (!webform_get_conditional_sorter($node)->pageVisibility($form_state['webform']['page_num'])); - if (!$form_state['webform']['preview'] && $form_state['webform']['page_num'] == $form_state['webform']['page_count'] + (int)!$form_state['webform']['preview']) { + if (!$form_state['webform']['preview'] && $form_state['webform']['page_num'] == $form_state['webform']['page_count'] + (int) !$form_state['webform']['preview']) { // Force a preview to avert an unintended submission via Next. $form_state['webform']['preview'] = TRUE; $form_state['webform']['page_count']++; @@ -2575,7 +2651,7 @@ function webform_client_form($form, &$form_state, $node, $submission = FALSE, $r '#page_num' => $page_num, '#page_count' => count($page_labels), '#page_labels' => $page_labels, - '#weight' => -100 + '#weight' => -100, ); } @@ -2631,11 +2707,11 @@ function webform_client_form($form, &$form_state, $node, $submission = FALSE, $r '#value' => isset($submission->uid) ? $submission->uid : $user->uid, ); $form['details']['page_num'] = array( - '#type' => 'hidden', + '#type' => 'hidden', '#value' => $page_num, ); $form['details']['page_count'] = array( - '#type' => 'hidden', + '#type' => 'hidden', '#value' => $page_count, ); $form['details']['finished'] = array( @@ -2660,7 +2736,8 @@ function webform_client_form($form, &$form_state, $node, $submission = FALSE, $r '#type' => 'submit', '#value' => t('Save Draft'), '#weight' => -2, - '#validate' => array('webform_client_form_prevalidate'), // Prevalidation only; no element validation for Save Draft + // Prevalidation only; no element validation for Save Draft. + '#validate' => array('webform_client_form_prevalidate'), '#attributes' => array( 'formnovalidate' => 'formnovalidate', 'class' => array('webform-draft'), @@ -2726,20 +2803,19 @@ function webform_client_form_process($form, $form_state) { // Add the post validation to end of validators. Do this first on the off // chance that an _alter function has unset form['#validate']. $form['#validate'][] = 'webform_client_form_postvalidate'; - // Add the pre-validator to the front of the list to run first + // Add the pre-validator to the front of the list to run first. array_unshift($form['#validate'], 'webform_client_form_prevalidate'); return $form; } - /** * Add a component to a renderable array. Called recursively for fieldsets. * * This function assists in the building of the client form, as well as the * display of results, and the text of e-mails. * - * @param $node + * @param stdClass $node * The current webform node. * @param $component * The component to be added to the form. @@ -2808,7 +2884,8 @@ function _webform_client_form_add_component($node, $component, $component_value, // Show the component only on its form page, or if building an unfiltered // version of the form (such as for Form Builder). elseif ($component['page_num'] == $page_num || $filter == FALSE) { - // Add this user-defined field to the form (with all the values that are always available). + // Add this user-defined field to the form (with all the values that are + // always available). if ($element = webform_component_invoke($component['type'], 'render', $component, $component_value, $filter, $form['#submission'])) { // Set access based on the private property. $element += array('#access' => TRUE); @@ -2832,7 +2909,8 @@ function _webform_client_form_add_component($node, $component, $component_value, // Add custom CSS classes to the field and wrapper. _webform_component_classes($element, $component); - // Allow modules to modify a webform component that is going to be render in a form. + // Allow modules to modify a webform component that is going to be render + // in a form. drupal_alter('webform_component_render', $element, $component); // Add the element into the proper parent in the form. @@ -2854,7 +2932,8 @@ function _webform_client_form_add_component($node, $component, $component_value, $sorter = webform_get_conditional_sorter($node); foreach ($component['children'] as $scid => $subcomponent) { $subcomponent_value = isset($input_values[$scid]) ? $input_values[$scid] : NULL; - // Include if always shown, or for forms, also if currently hidden but might be shown due to conditionals. + // Include if always shown, or for forms, also if currently hidden but + // might be shown due to conditionals. $visibility = $sorter->componentVisibility($scid, $subcomponent['page_num']); if ($visibility == WebformConditionals::componentShown || ($format == 'form' && $visibility) || !$filter) { _webform_client_form_add_component($node, $subcomponent, $subcomponent_value, $parent_fieldset[$component['form_key']], $form, $input_values, $format, $page_num, $filter); @@ -2880,7 +2959,8 @@ function webform_client_form_prevalidate($form, &$form_state) { // Check if the user is allowed to submit based on role. This check is // repeated here to ensure the user is still logged in at the time of // submission, otherwise a stale form in another window may be allowed. - $allowed_roles = _webform_allowed_roles($node, $allowed_role); // $allowed_role set by reference. + // $allowed_role set by reference. + $allowed_roles = _webform_allowed_roles($node, $allowed_role); // Check that the submissions have not exceeded the total submission limit. $total_limit_exceeded = FALSE; @@ -2902,8 +2982,7 @@ function webform_client_form_prevalidate($form, &$form_state) { // is no submission yet (hence isn't being edited) or the user isn't an admin. // Another way to consider this is that the form is open when its status is // open OR there is a submission and the user is an admin. - $closed = empty($node->webform['status']) && - (empty($form['#submission']) || !user_access('edit all webform submissions')); + $closed = empty($node->webform['status']) && (empty($form['#submission']) || !user_access('edit all webform submissions')); // Prevent submission by throwing an error. if ((!$allowed_role || $total_limit_exceeded || $user_limit_exceeded || $closed)) { @@ -2916,8 +2995,9 @@ function webform_client_form_prevalidate($form, &$form_state) { * Form API #validate handler for the webform_client_form() form. */ function webform_client_form_validate($form, &$form_state) { - if (($errors = form_get_errors()) && key_exists('', $errors)) { - // Prevalidation failed. The form cannot be submitted. Do not attemp futher validation. + if (($errors = form_get_errors()) && array_key_exists('', $errors)) { + // Prevalidation failed. The form cannot be submitted. Do not attemp futher + // validation. return; } if ($form_state['webform']['preview'] && $form_state['webform']['page_count'] === $form_state['webform']['page_num']) { @@ -2943,7 +3023,6 @@ function webform_client_form_validate($form, &$form_state) { $input_values = NULL; } - // Run all #element_validate and #required checks. These are skipped initially // by setting #validated = TRUE on all components when they are added. _webform_client_form_validate($form, $form_state, 'webform_client_form', $input_values); @@ -2982,8 +3061,10 @@ function _webform_client_form_validate(&$elements, &$form_state, $form_id = NULL $component = $elements['#webform_component']; $value = $input_values[$cid]; $value = is_array($value) ? $value[0] : $value; - // webform_component_invoke cannot be called with reference arguments. Call directly. - // webform_component_invoke($component['type'], 'action_set', $component, $elements, $form_state, $value); + // webform_component_invoke cannot be called with reference arguments. + // Call directly. + // webform_component_invoke($component['type'], 'action_set', $component, + // $elements, $form_state, $value);. $function = '_webform_action_set_' . $component['type']; $function($component, $elements, $form_state, $value); } @@ -2993,8 +3074,8 @@ function _webform_client_form_validate(&$elements, &$form_state, $form_id = NULL if (isset($required)) { $elements['#required'] = $required; - // Some components, e.g. grids, have nested sub-elements. Extend required - // to any sub-components. + // Some components, for example, grids, have nested sub-elements. Extend + // required to any sub-components. foreach (element_children($elements) as $key) { if (isset($elements[$key]) && $elements[$key] && !isset($elements[$key]['#webform_component'])) { // Child is *not* a component. @@ -3027,6 +3108,15 @@ function _webform_client_form_validate(&$elements, &$form_state, $form_id = NULL form_error($elements, t('!name cannot be longer than %max characters but is currently %length characters long.', array('!name' => empty($elements['#title']) ? $elements['#parents'][0] : $elements['#title'], '%max' => $elements['#maxlength'], '%length' => drupal_strlen($elements['#value'])))); } + // Verify that the value is not shorter than #minlength. The value may + // still be empty (required is a separate validation option). + if (isset($elements['#minlength'])) { + $length = drupal_strlen($elements['#value']); + if ($length > 0 && $length < $elements['#minlength']) { + form_error($elements, t('!name cannot be shorter than %min characters but is currently %length characters long.', array('!name' => empty($elements['#title']) ? $elements['#parents'][0] : $elements['#title'], '%min' => $elements['#minlength'], '%length' => drupal_strlen($elements['#value'])))); + } + } + if (isset($elements['#options']) && isset($elements['#value'])) { if ($elements['#type'] == 'select') { $options = form_options_flatten($elements['#options']); @@ -3058,7 +3148,7 @@ function _webform_client_form_validate(&$elements, &$form_state, $form_id = NULL // #value data. elseif (isset($elements['#element_validate'])) { foreach ($elements['#element_validate'] as $function) { - if (is_callable($function)) { + if (is_callable($function)) { $function($elements, $form_state, $form_state['complete form']); } } @@ -3113,10 +3203,10 @@ function webform_client_form_postvalidate(&$form, &$form_state) { $node->webform['auto_save'] && !$form_state['values']['details']['finished'] && !empty($form_state['values']['op'])) { - // Validation errors are present, prevalidation succeeded (e.g. submission - // limits are ok), auto-save is enabled, this form isn't finished (i.e. is - // or soon will be a draft) and a button was pushed (not ajax). - + // Validation errors are present, prevalidation succeeded (for example + // submission limits are ok), auto-save is enabled, this form isn't finished + // (this is, is or soon will be a draft) and a button was pushed (not ajax). + // // Process submission on a copy of the form and form_state to prevent the // submission handlers from making unintended changes. Use a button that // isn't Save Draft, Next Page, Submit, etc to avoid triggering any @@ -3208,8 +3298,8 @@ function webform_client_form_pages($form, &$form_state) { // 1) previous/next non-empty page, or // 2) the preview page, or // 3) the preview page, forcing its display if the form would unexpectedly submit, or - // 4) page 1 even if empty, if no other previous page would be shown - $preview_page_num = $form_state['storage']['page_count'] + (int)!$form_state['webform']['preview']; + // 4) page 1 even if empty, if no other previous page would be shown. + $preview_page_num = $form_state['storage']['page_count'] + (int) !$form_state['webform']['preview']; $page_num = $current_page; do { $page_num += $forward; @@ -3228,8 +3318,7 @@ function webform_client_form_pages($form, &$form_state) { } // Inform the submit handlers that a draft will be saved. - $form_state['save_draft'] = in_array($form_state['values']['op'], array($draft_op, '__AUTOSAVE__')) || - ($node->webform['auto_save'] && !$form_state['values']['details']['finished'] && !$form_state['webform_completed'] && user_is_logged_in()); + $form_state['save_draft'] = in_array($form_state['values']['op'], array($draft_op, '__AUTOSAVE__')) || ($node->webform['auto_save'] && !$form_state['values']['details']['finished'] && !$form_state['webform_completed'] && user_is_logged_in()); // Determine what we need to do on the next page. if (!empty($form_state['save_draft']) || !$form_state['webform_completed']) { @@ -3271,9 +3360,9 @@ function webform_client_form_submit($form, &$form_state) { // Merge with new submission data. The + operator maintains numeric keys. // This maintains existing data with just-submitted data when a user resumes - // a submission previously saved as a draft. - // Remove any existing data on this and previous pages. If components are hidden, they may - // be in the $submission->data but absent entirely from $new_data; + // a submission previously saved as a draft. Remove any existing data on + // this and previous pages. If components are hidden, they may be in the + // $submission->data but absent entirely from $new_data. $page_map = webform_get_conditional_sorter($node)->getPageMap(); for ($page_nr = 1; $page_nr <= $form_state['webform']['page_num']; $page_nr++) { $submission->data = array_diff_key($submission->data, $page_map[$page_nr]); @@ -3292,9 +3381,9 @@ function webform_client_form_submit($form, &$form_state) { $submission->is_draft = $is_draft; $submission->highest_valid_page = 0; if ($is_draft) { - $submission->highest_valid_page = end($form_state['clicked_button']['#parents']) == 'next' && $form_state['values']['op'] != '__AUTOSAVE__' - ? $form_state['webform']['page_num'] - : $form_state['webform']['page_num'] - 1; + $submission->highest_valid_page = end($form_state['clicked_button']['#parents']) == 'next' && $form_state['values']['op'] != '__AUTOSAVE__' + ? $form_state['webform']['page_num'] + : $form_state['webform']['page_num'] - 1; } // If there is no data to be saved (such as on a multipage form with no fields @@ -3409,7 +3498,7 @@ function webform_client_form_submit($form, &$form_state) { /** * Post processes the submission tree with any updates from components. * - * @param $node + * @param stdClass $node * The full webform node. * @param $form_values * The form values for the form. @@ -3481,11 +3570,10 @@ function template_preprocess_webform_form(&$vars) { module_load_include('inc', 'webform', 'includes/webform.conditionals'); $submission_data = isset($vars['form']['#conditional_values']) ? $vars['form']['#conditional_values'] : array(); $settings = webform_conditional_prepare_javascript($vars['form']['#node'], - $submission_data, - $vars['form']['details']['page_num']['#value']); + $submission_data, + $vars['form']['details']['page_num']['#value']); drupal_add_js(array('webform' => array('conditionals' => array('webform-client-form-' . $vars['nid'] => $settings))), 'setting'); } - } /** @@ -3654,6 +3742,12 @@ function theme_webform_element($variables) { TRUE => !empty($element['#description']) ? '
    ' . $element['#description'] . "
    \n" : '', ); + // If #children does not contain an element with a matching @id, do not + // include @for in the label. + if (strpos($element['#children'], ' id="' . $variables['element']['#id'] . '"') === FALSE) { + $variables['element']['#id'] = NULL; + } + switch ($element['#title_display']) { case 'inline': $output .= $description[$above]; @@ -3813,10 +3907,10 @@ function theme_webform_inline_radio_label($variables) { $attributes['class'][] = 'element-invisible'; } - $attributes['class'][] = 'webform-inline-radio'; - if (!empty($element['#id'])) { - $attributes['for'] = $element['#id']; - } + $attributes['class'][] = 'webform-inline-radio'; + if (!empty($element['#id'])) { + $attributes['for'] = $element['#id']; + } // The leading whitespace helps visually separate fields from inline labels. return ' ' . $t('!title !required', array('!title' => $title, '!required' => $required)) . "\n"; @@ -3825,7 +3919,7 @@ function theme_webform_inline_radio_label($variables) { /** * Theme the headers when sending an email from webform. * - * @param $node + * @param stdClass $node * The complete node object for the webform. * @param $submission * The webform submission of the user. @@ -3834,6 +3928,7 @@ function theme_webform_inline_radio_label($variables) { * you can check the $email['email'] property to output different content. * This will be the ID of the component that is a conditional e-mail * recipient. For the normal e-mails, it will have the value of 'default'. + * * @return * An array of headers to be used when sending a webform email. If headers * for "From", "To", or "Subject" are set, they will take precedence over @@ -3854,7 +3949,7 @@ function _webform_fetch_draft_sid($nid, $uid) { // should be returned. if (isset($_POST['form_id']) && stripos($_POST['form_id'], 'webform_client_form_') === 0 && !empty($_POST['details']['sid']) && empty($_POST['details']['finished'])) { - // A draft is already being edited + // A draft is already being edited. $sid = $_POST['details']['sid']; } else { @@ -3881,8 +3976,9 @@ function _webform_fetch_draft_sid($nid, $uid) { /** * Returns a new or cached WebformConditionals object for the specified node. * - * @param object $node + * @param stdClass $node * The loaded webform node. + * * @returns object * Object of type WebformConditionals, possibly with the conditionals already * analyzed for dependencies. @@ -3901,12 +3997,12 @@ function _webform_filter_values($string, $node = NULL, $submission = NULL, $emai return $strict ? webform_filter_xss($output) : $output; } -/* +/** * Replace tokens with Webform contexts populated. * * @param $string * The string to have its tokens replaced. - * @param $node + * @param stdClass $node * If replacing node-level tokens, the node for which tokens will be created. * @param $submission * If replacing submission-level tokens, the submission for which tokens will @@ -3957,14 +4053,18 @@ function webform_replace_tokens($string, $node = NULL, $submission = NULL, $emai * to be called with the option 'clear' => FALSE, to not remove input filters. * For security reasons webform_replace_tokens() is called before * check_markup(), where input filters get replaced. Tokens won't be replaced if - * there is no value provided. These tokens i.e. [current-page:query:*] needs to - * be removed to not show up in the output. + * there is no value provided. These tokens, that is, [current-page:query:*] + * needs to be removed to not show up in the output. * * Note: This function was previously named webform_clear_tokens, which * conflicted with the webform_clear module, being called as hook_tokens. * * @param string $text * The text to have its tokens removed. + * + * @return mixed|string + * Replace tokens with actual value. + * * @see token_replace() */ function webform_replace_tokens_clear($text) { @@ -3993,11 +4093,12 @@ function webform_replace_tokens_clear($text) { * * @param string $redirect_url * The redirect URL, with everything other than tokens already URL encoded. - * @param $node + * @param stdClass $node * If replacing node-level tokens, the node for which tokens will be created. * @param $submission * If replacing submission-level tokens, the submission for which tokens will * be created. + * * @return array * An array of path and url() options, suitable for a redirect or drupal_goto. */ @@ -4061,8 +4162,9 @@ function _webform_filter_xss($string) { /** * Utility function to ensure that a webform record exists in the database. * - * @param $node + * @param stdClass $node * The node object to check if a database entry exists. + * * @return * This function should always return TRUE if no errors were encountered, * ensuring that a webform table row has been created. Will return FALSE if @@ -4086,8 +4188,9 @@ function webform_ensure_record(&$node) { * delete rows from the webform table if the node-type is exclusively used for * webforms (per the "webform_node_types_primary" variable). * - * @param $node + * @param stdClass $node * The node object to check if a database entry is still required. + * * @return * Returns TRUE if the webform still has a record in the database. Returns * FALSE if the webform does not have a record or if the previously existing @@ -4110,19 +4213,33 @@ function webform_check_record(&$node) { } /** - * Given a form_key and a list of form_key parents, determine the cid. + * Given a component's form_key and optionally its parent's cid, get its cid(s). * - * @param $node - * A fully loaded node object. - * @param $form_key - * The form key for which we're finding a cid. - * @param $parent + * @param stdClass $node + * A fully loaded webform node object. + * @param string $form_key + * The form key for which to find the cid(s). + * @param int|null $pid * The cid of the parent component. + * + * @return int|int[] + * The cid of the component or an array of component ids. */ -function webform_get_cid(&$node, $form_key, $pid) { - foreach ($node->webform['components'] as $cid => $component) { - if ($component['form_key'] == $form_key && $component['pid'] == $pid) { - return $cid; +function webform_get_cid(&$node, $form_key, $pid = NULL) { + if ($pid === NULL) { + $cids = array(); + foreach ($node->webform['components'] as $cid => $component) { + if ((string) $component['form_key'] === (string) $form_key) { + $cids[] = $cid; + } + } + return $cids; + } + else { + foreach ($node->webform['components'] as $cid => $component) { + if ((string) $component['form_key'] === (string) $form_key && $component['pid'] == $pid) { + return $cid; + } } } } @@ -4130,10 +4247,11 @@ function webform_get_cid(&$node, $form_key, $pid) { /** * Find the label of a given page based on page breaks. * - * @param $node + * @param stdClass $node * The webform node. * @param $form_state * The form's state, if available + * * @return array * An array of all page labels, indexed by page number. */ @@ -4164,90 +4282,118 @@ function webform_variable_get($variable) { case 'webform_blocks': $result = variable_get('webform_blocks', array()); break; + case 'webform_tracking_mode': $result = variable_get('webform_tracking_mode', 'cookie'); break; + case 'webform_allowed_tags': $result = variable_get('webform_allowed_tags', array('a', 'em', 'strong', 'code', 'img')); break; + case 'webform_email_address_format': $result = variable_get('webform_email_address_format', 'long'); break; + case 'webform_email_address_individual': $result = variable_get('webform_email_address_individual', 0); break; + case 'webform_default_from_name': $result = variable_get('webform_default_from_name', variable_get('site_name', '')); break; + case 'webform_default_from_address': $result = variable_get('webform_default_from_address', variable_get('site_mail', ini_get('sendmail_from'))); break; + case 'webform_default_subject': $result = variable_get('webform_default_subject', t('Form submission from: [node:title]')); break; + case 'webform_email_replyto': $result = variable_get('webform_email_replyto', TRUE); break; + case 'webform_email_html_capable': $result = variable_get('webform_email_html_capable', FALSE); break; + case 'webform_default_format': $result = variable_get('webform_default_format', 0); break; + case 'webform_format_override': $result = variable_get('webform_format_override', 0); break; + case 'webform_email_select_max': $result = variable_get('webform_email_select_max', 50); break; + case 'webform_node_types': $result = webform_node_types(); break; + case 'webform_node_types_primary': $result = variable_get('webform_node_types_primary', array('webform')); break; + case 'webform_date_type': $result = variable_get('webform_date_type', 'medium'); break; + case 'webform_export_format': module_load_include('inc', 'webform', 'includes/webform.export'); $options = webform_export_list(); $result = variable_get('webform_export_format', 'excel'); $result = isset($options[$result]) ? $result : key($options); break; + case 'webform_csv_delimiter': $result = variable_get('webform_csv_delimiter', '\t'); break; + case 'webform_csv_line_ending': $result = variable_get('webform_csv_line_ending', "\n"); break; + case 'webform_export_wordwrap': $result = variable_get('webform_export_wordwrap', 0); break; + case 'webform_excel_legacy_exporter': $result = variable_get('webform_excel_legacy_exporter', 0); break; + case 'webform_progressbar_style': $result = variable_get('webform_progressbar_style', array('progressbar_bar', 'progressbar_pagebreak_labels', 'progressbar_include_confirmation')); break; + case 'webform_progressbar_label_first': $result = variable_get('webform_progressbar_label_first', t('Start')); break; + case 'webform_progressbar_label_confirmation': $result = variable_get('webform_progressbar_label_confirmation', t('Complete')); break; + case 'webform_table': $result = variable_get('webform_table', FALSE); break; + case 'webform_submission_access_control': $result = variable_get('webform_submission_access_control', 1); break; + case 'webform_token_access': $result = variable_get('webform_token_access', 1); break; + case 'webform_update_batch_size': $result = variable_get('webform_update_batch_size', 100); break; + case 'webform_disabled_components': $result = variable_get('webform_disabled_components', array()); break; @@ -4340,7 +4486,7 @@ function _webform_transliterate($name) { * @param $name * The name to be used in the formatted address. If the address contains a * name in 'Some Name ' format, $name is ignored. - * @param $node + * @param stdClass $node * The webform node if replacements will be done. * @param $submission * The webform submission values if replacements will be done. @@ -4355,6 +4501,7 @@ function _webform_transliterate($name) { * "long", or NULL for the system default. * @param $mapping * A mapping array to be applied to the address values. + * * @return string|array * The formatted e-mail address -- or addresses (if not $single) */ @@ -4377,7 +4524,8 @@ function webform_format_email_address($address, $name, $node = NULL, $submission foreach ($name as &$one_name) { $one_name = isset($options[$one_name]) ? $options[$one_name] : $one_name; } - unset($one_name); // Drop PHP reference. + // Drop PHP reference. + unset($one_name); } } else { @@ -4431,7 +4579,6 @@ function webform_format_email_address($address, $name, $node = NULL, $submission } return $single ? reset($address) : $address; - } /** @@ -4443,16 +4590,17 @@ function webform_format_email_address($address, $name, $node = NULL, $submission * with a comma and space. * @param string $form_name * The name of the form element to receive an error, in form_set_error format. - * @param boolean $allow_empty + * @param bool $allow_empty * TRUE if optional. FALSE if required. - * @param boolean $allow_multiple + * @param bool $allow_multiple * TRUE if a list of emails is allowed. FALSE if only one. - * @param boolean $allow_tokens + * @param bool $allow_tokens * TRUE if any token should be assumed to contain a valid e-mail address. * @param string $format * 'short', 'long', or NULL (for default) format. Long format has a name and * the address in angle brackets. - * @return integer|boolean + * + * @return int|bool * The number of valid addresses found, or FALSE for an invalid email found. */ function webform_email_validate(&$emails, $form_name, $allow_empty, $allow_multiple, $allow_tokens, $format = NULL) { @@ -4476,12 +4624,13 @@ function webform_email_validate(&$emails, $form_name, $allow_empty, $allow_multi * An email address, a list of comma-separated email addresses. If all the * addresses are valid, the list of trimmed, non-empty emails is returned by * reference. - * @param boolean $allow_tokens + * @param bool $allow_tokens * TRUE if any token should be assumed to contain a valid e-mail address. * @param string $format * 'short', 'long', or NULL (for default) format. Long format has a name and * the address in angle brackets. - * @return boolean|integer + * + * @return bool|int * Returns FALSE if an invalid e-mail address was found, 0 if no email * address(es) were found, or the number of valid e-mail addresses found. */ @@ -4514,6 +4663,7 @@ function webform_valid_email_address(&$emails, $allow_tokens = FALSE, $format = * @param string $format * 'short', 'long', or NULL (for default) format. Long format has a name and * the address in angle brackets. + * * @return array * Associative array indexed by 'name' and 'address'. */ @@ -4522,14 +4672,7 @@ function webform_parse_email_address($email, $format = NULL) { $format = webform_variable_get('webform_email_address_format'); } if ($format == 'long') { - // Match e-mails of the form 'My Name ' as follows: - // ^ = beginning of string - // "? = optional quote - // ([^<]*?) = match optional characters that aren't a < (non-greedy) - // "? = optional quote - // SPACE* = optional spaces - // (?:<(.*)>) = < matching stuff > (without the angle brakets) - // $ = end of string + // Match e-mails of the form 'My Name '. preg_match('/^"?([^<]*?)"? *(?:<(.*)>)?$/', $email, $matches); if (isset($matches[2]) && strlen($matches[2])) { return array( @@ -4582,7 +4725,7 @@ function webform_format_email_subject($subject, $node = NULL, $submission = NULL } /** - * Convert an array of components into a tree + * Convert an array of components into a tree. */ function _webform_components_tree_build($src, &$tree, $parent, &$page_count) { foreach ($src as $cid => $component) { @@ -4631,7 +4774,7 @@ function _webform_components_sort($a, $b) { } /** - * Sort each level of a component tree by weight and name + * Sort each level of a component tree by weight and name. */ function _webform_components_tree_sort($tree) { if (isset($tree['children']) && is_array($tree['children'])) { @@ -4663,7 +4806,7 @@ function webform_components($include_disabled = FALSE, $reset = FALSE) { $components += $module_components; } drupal_alter('webform_component_info', $components); - uasort($components, function($a, $b) { + uasort($components, function ($a, $b) { return strnatcasecmp($a['label'], $b['label']); }); $enabled = array_diff_key($components, $disabled); @@ -4716,6 +4859,9 @@ function webform_component_include($component_type) { * The callback to execute. * @param ... * Any additional parameters required by the $callback. + * + * @return mixed + * Return value of the callback on success and FALSE on failure. */ function webform_component_invoke($type, $callback) { $args = func_get_args(); @@ -4735,6 +4881,8 @@ function webform_component_invoke($type, $callback) { * The component type as a string. * @param $callback * The callback to check. + * + * @return bool */ function webform_component_implements($type, $callback) { $function = '_webform_' . $callback . '_' . $type; @@ -4756,11 +4904,11 @@ function webform_conditional_expand($element) { function _webform_component_classes(&$element, $component) { if (isset($component['extra']['css_classes']) && drupal_strlen($component['extra']['css_classes'])) { $element['#attributes']['class'] = isset($element['#attributes']['class']) ? $element['#attributes']['class'] : array(); - $element['#attributes']['class'] = array_merge($element['#attributes']['class'], explode(' ' , $component['extra']['css_classes'])); + $element['#attributes']['class'] = array_merge($element['#attributes']['class'], explode(' ', $component['extra']['css_classes'])); } if (isset($component['extra']['wrapper_classes']) && drupal_strlen($component['extra']['wrapper_classes'])) { $element['#wrapper_attributes']['class'] = isset($element['#wrapper_attributes']['class']) ? $element['#wrapper_attributes']['class'] : array(); - $element['#wrapper_attributes']['class'] = array_merge($element['#wrapper_attributes']['class'], explode(' ' , $component['extra']['wrapper_classes'])); + $element['#wrapper_attributes']['class'] = array_merge($element['#wrapper_attributes']['class'], explode(' ', $component['extra']['wrapper_classes'])); } } @@ -4774,13 +4922,13 @@ function webform_disable_page_cache() { /** * Set the necessary breadcrumb for the page we are on. * - * @param object $node + * @param stdClass $node * The loaded webform node. - * @param boolean|object $submission + * @param bool|object $submission * The submission if the current page is viewing or dealing with a submission, * or TRUE to just include the webform node in the breadcrumbs (used for * the submission completion confirmation page), or NULL for no extra - * processing + * processing. */ function webform_set_breadcrumb($node, $submission = NULL) { $node_path = "node/{$node->nid}"; @@ -4807,7 +4955,7 @@ function webform_set_breadcrumb($node, $submission = NULL) { // Setting the current menu href will cause the submission title and current // tab (if not the default tab) to be added to the active path when the // webform is in the default location in the menu (node/NID). The title - // is desirable, but the tab name (e.g. Edit or Delete) isn't. + // is desirable, but the tab name (for example Edit or Delete) isn't. if (preg_match('/href=".*"/', end($breadcrumb), $matches)) { foreach ($breadcrumb as $index => $link) { if (stripos($link, $matches[0]) !== FALSE) { @@ -4815,7 +4963,7 @@ function webform_set_breadcrumb($node, $submission = NULL) { break; } } - } + } // If the user is dealing with a submission, then the breadcrumb should // be fudged to allow them to return to a likely list of webforms. @@ -4858,6 +5006,9 @@ function webform_set_breadcrumb($node, $submission = NULL) { * - hour (in 24hr notation) * - minute * - second + * + * @return array + * Date in array formate. */ function webform_date_array($string, $type = NULL) { $pattern = '/((\d{4}?)-(\d{2}?)-(\d{2}?))?(T?(\d{2}?):(\d{2}?):(\d{2}?))?/'; @@ -4892,6 +5043,9 @@ function webform_date_array($string, $type = NULL) { * @param $type * If wanting a specific string format back specify either "date" or "time". * Otherwise a full ISO 8601 date and time string will be returned. + * + * @return string + * Date in string format */ function webform_date_string($array, $type = NULL) { $string = ''; @@ -4909,11 +5063,11 @@ function webform_date_string($array, $type = NULL) { } if ($type == 'time' || !isset($type)) { - $string .= empty($array['hour']) ? '00' : sprintf('%02d', $array['hour']); + $string .= empty($array['hour']) ? '00' : sprintf('%02d', $array['hour']); $string .= ':'; - $string .= empty($array['minute']) ? '00' : sprintf('%02d', $array['minute']); + $string .= empty($array['minute']) ? '00' : sprintf('%02d', $array['minute']); $string .= ':'; - $string .= empty($array['second']) ? '00' : sprintf('%02d', $array['second']); + $string .= empty($array['second']) ? '00' : sprintf('%02d', $array['second']); } return $string; @@ -4929,6 +5083,7 @@ function webform_date_string($array, $type = NULL) { * @param array $exclude * An array containing 'day', 'month', and/or 'year' if they should be * removed from the format. + * * @return string * A date/time format string. */ @@ -4953,7 +5108,7 @@ function webform_date_format($type = NULL, $exclude = array()) { // -------------------------------------------------------------------------- // Time aABgGhHisueIOPTZ // Special /.,-: - + // // Strip Time and Special characters from the beginning and end of format. $date_format = trim($format, 'aABgGhHisueIOPTZ/.,-: '); @@ -5013,11 +5168,10 @@ function webform_strtodate($format, $string, $timezone_name = NULL, $reference_t // 3) Set the time to midnight because when a non-referenced relative // date is created without a time, it is created at midnight (0:00). // 4) Adjust to the specified relative (or absolute) time. - @$datetime = new DateTime('@' . $reference_timestamp); @$datetime->setTimezone($timezone) - ->setTime(0, 0, 0) - ->modify($string); + ->setTime(0, 0, 0) + ->modify($string); } else { @$datetime = new DateTime($string, $timezone); @@ -5084,7 +5238,7 @@ function webform_views_default_views() { $path = './' . drupal_get_path('module', 'webform') . '/views/default_views/*.inc'; $views = array(); foreach (glob($path) as $views_filename) { - require_once($views_filename); + require_once $views_filename; } return $views; } @@ -5248,7 +5402,7 @@ function _webform_submission_serial_next_value($nid) { * @param int $nid * The Node ID of the Webform. * - * $return int + * @return int * The largest serial number used by a submission plus 1 for the specified * node or 1 when there are no submissions. */ @@ -5266,7 +5420,7 @@ function _webform_submission_serial_next_value_used($nid) { /** * Alter the node before saving a clone. * - * @param $node + * @param stdClass $node * Reference to the fully loaded node object being saved (the clone) that * can be altered as needed. * @param array $context @@ -5310,16 +5464,16 @@ function webform_input_vars_check(&$form, $form_state, $detect_key, $parent_key // is that the POST was truncated because PHP exceeded its max_input_vars limit. $subs = array( '@count' => webform_count_terminals($_POST), - '@limit' => (int)ini_get('max_input_vars'), + '@limit' => (int) ini_get('max_input_vars'), ); drupal_set_message(user_access('administer site configuration') ? t('This form could not be submitted because $_POST was truncated to @count input vars. PHP max_input_vars is @limit and needs to be increased.', $subs) : t('This form could not be submitted because it exceeds the server configuration. Contact the administrator.'), 'error'); watchdog('webform', - 'POST truncated to @count input vars. PHP max_input_vars is @limit. Increase max_input_vars.', - $subs, - WATCHDOG_ERROR); + 'POST truncated to @count input vars. PHP max_input_vars is @limit. Increase max_input_vars.', + $subs, + WATCHDOG_ERROR); } } @@ -5334,7 +5488,8 @@ function webform_pre_render_input_vars($element) { $limit = ini_get('max_input_vars'); if ($limit) { // Estimate the number of input vars needed to see if the PHP limit has been exceeded. - $count = 1 + webform_count_input_vars($element); // Additional input_vars: op + // Additional input_vars: op. + $count = 1 + webform_count_input_vars($element); if ($count > $limit * 0.95) { $subs = array( '@count' => $count, @@ -5342,10 +5497,10 @@ function webform_pre_render_input_vars($element) { ); $warning = array( '#markup' => '
    ' . - (user_access('administer site configuration') - ? t('This form contains @count input elements. PHP max_input_vars is @limit and should be increased.', $subs) - : t('This form may be too long to work properly. Contact the administrator.')) - . '
    ', + (user_access('administer site configuration') + ? t('This form contains @count input elements. PHP max_input_vars is @limit and should be increased.', $subs) + : t('This form may be too long to work properly. Contact the administrator.')) + . '
    ', '#weight' => -1, ); if ($element['#input_var_waring_parent']) { @@ -5355,9 +5510,9 @@ function webform_pre_render_input_vars($element) { $element['input_vars_warning'] = $warning; } watchdog('webform', - 'Page contains @count input elements but PHP max_input_vars is only @limit. Increase max_input_vars.', - $subs, - WATCHDOG_ERROR); + 'Page contains @count input elements but PHP max_input_vars is only @limit. Increase max_input_vars.', + $subs, + WATCHDOG_ERROR); } } return $element; @@ -5378,7 +5533,8 @@ function webform_pre_render_input_vars($element) { * * @param array $element * The form whose elements should be counted. - * @return integer + * + * @return int * The number of elements in the form that will result in $_POST entries. */ function webform_count_input_vars($element) { @@ -5412,7 +5568,8 @@ function webform_count_input_vars($element) { * * @param $a * Array or array element to be counted - * @return integer + * + * @return int * Number of non-array elements within $a. */ function webform_count_terminals($a) { diff --git a/sites/all/modules/contrib/webform/webform.tokens.inc b/sites/all/modules/contrib/webform/webform.tokens.inc index 9104ce83e..3c39fbfab 100644 --- a/sites/all/modules/contrib/webform/webform.tokens.inc +++ b/sites/all/modules/contrib/webform/webform.tokens.inc @@ -109,7 +109,7 @@ function webform_tokens($type, $tokens, array $data = array(), array $options = // from the rendered submission if recursion has been detected. if ($recursion_level) { $markup_components = array_keys(array_filter($node->webform['components'], - function ($component) { return $component['type'] == 'markup'; })); + function ($component) { return $component['type'] == 'markup'; })); } $recursion_level++; diff --git a/sites/all/modules/contrib/wysiwyg/editors/ckeditor.inc b/sites/all/modules/contrib/wysiwyg/editors/ckeditor.inc index b2c20f3ea..0a1ff6824 100644 --- a/sites/all/modules/contrib/wysiwyg/editors/ckeditor.inc +++ b/sites/all/modules/contrib/wysiwyg/editors/ckeditor.inc @@ -32,7 +32,7 @@ function wysiwyg_ckeditor_editor() { ), ), 'install note callback' => 'wysiwyg_ckeditor_install_note', - 'verified version range' => array('3.0', '4.6.1.580bcaf'), + 'verified version range' => array('3.0', '4.6.2.20af917'), 'migrate settings callback' => 'wysiwyg_ckeditor_migrate_settings', 'version callback' => 'wysiwyg_ckeditor_version', 'themes callback' => 'wysiwyg_ckeditor_themes', diff --git a/sites/all/modules/contrib/wysiwyg/editors/css/wymeditor.css b/sites/all/modules/contrib/wysiwyg/editors/css/wymeditor.css new file mode 100644 index 000000000..ea86604b6 --- /dev/null +++ b/sites/all/modules/contrib/wysiwyg/editors/css/wymeditor.css @@ -0,0 +1,6 @@ +.wym_skin_compact .wym_dropdown ul { + margin-top: 0; +} +.wym_skin_compact .wym_iframe iframe { + height: 400px !important; +} diff --git a/sites/all/modules/contrib/wysiwyg/editors/js/ckeditor-3.0.js b/sites/all/modules/contrib/wysiwyg/editors/js/ckeditor-3.0.js index d8c7185ac..70eebe462 100644 --- a/sites/all/modules/contrib/wysiwyg/editors/js/ckeditor-3.0.js +++ b/sites/all/modules/contrib/wysiwyg/editors/js/ckeditor-3.0.js @@ -12,7 +12,7 @@ var instanceMap; * Initialize the editor library. * * This method is called once the first time a library is needed. If new - * WYSIWYG fieldsare added later, update() will be called instead. + * WYSIWYG fields are added later, update() will be called instead. * * @param settings * An object containing editor settings for each input format. @@ -202,7 +202,7 @@ Drupal.wysiwyg.editor.detach.ckeditor = function (context, params, trigger) { Drupal.wysiwyg.editor.instance.ckeditor = { addPlugin: function (pluginName, pluginSettings) { CKEDITOR.plugins.add(pluginName, { - // Wrap Drupal plugin in a proxy pluygin. + // Wrap Drupal plugin in a proxy plugin. init: function(editor) { if (pluginSettings.css) { editor.on('mode', function(ev) { @@ -224,7 +224,18 @@ Drupal.wysiwyg.editor.instance.ckeditor = { data.node = data.node.$; } if (selection.getType() == CKEDITOR.SELECTION_TEXT) { - data.content = selection.getSelectedText(); + if (selection.getSelectedText) { + data.content = selection.getSelectedText(); + } + else { + // Pre v3.6.1. + if (CKEDITOR.env.ie) { + data.content = selection.getNative().createRange().text; + } + else { + data.content = selection.getNative().toString(); + } + } } else if (data.node) { // content is supposed to contain the "outerHTML". @@ -253,7 +264,7 @@ Drupal.wysiwyg.editor.instance.ckeditor = { insert: function(content) { content = this.prepareContent(content); - if (CKEDITOR.env.webkit || CKEDITOR.env.chrome || CKEDITOR.env.opera || CKEDITOR.env.safari) { + if (CKEDITOR.version.split('.')[0] === '3' && (CKEDITOR.env.webkit || CKEDITOR.env.chrome || CKEDITOR.env.opera || CKEDITOR.env.safari)) { // Works around a WebKit bug which removes wrapper elements. // @see https://drupal.org/node/1927968 var tmp = new CKEDITOR.dom.element('div'), children, skip = 0, item; diff --git a/sites/all/modules/contrib/wysiwyg/editors/js/epiceditor.js b/sites/all/modules/contrib/wysiwyg/editors/js/epiceditor.js index 0d983ff2e..f6e2db07a 100644 --- a/sites/all/modules/contrib/wysiwyg/editors/js/epiceditor.js +++ b/sites/all/modules/contrib/wysiwyg/editors/js/epiceditor.js @@ -27,7 +27,7 @@ Drupal.wysiwyg.editor.attach.epiceditor = function (context, params, settings) { }; /** - * Detach a single edtor instance. + * Detach a single editor instance. */ Drupal.wysiwyg.editor.detach.epiceditor = function (context, params, trigger) { var $target = $('#' + params.field, context); diff --git a/sites/all/modules/contrib/wysiwyg/editors/js/tinymce-3.js b/sites/all/modules/contrib/wysiwyg/editors/js/tinymce-3.js index 929b3f878..40a5632e0 100644 --- a/sites/all/modules/contrib/wysiwyg/editors/js/tinymce-3.js +++ b/sites/all/modules/contrib/wysiwyg/editors/js/tinymce-3.js @@ -50,7 +50,7 @@ Drupal.wysiwyg.editor.update.tinymce = function(settings, pluginInfo) { /** * Attach this editor to a target element. * - * See Drupal.wysiwyg.editor.attach.none() for a full desciption of this hook. + * See Drupal.wysiwyg.editor.attach.none() for a full description of this hook. */ Drupal.wysiwyg.editor.attach.tinymce = function(context, params, settings) { // Configure editor settings for this input format. @@ -96,7 +96,7 @@ Drupal.wysiwyg.editor.attach.tinymce = function(context, params, settings) { /** * Detach a single editor instance. * - * See Drupal.wysiwyg.editor.detach.none() for a full desciption of this hook. + * See Drupal.wysiwyg.editor.detach.none() for a full description of this hook. */ Drupal.wysiwyg.editor.detach.tinymce = function (context, params, trigger) { var instance = tinyMCE.get(params.field); diff --git a/sites/all/modules/contrib/wysiwyg/editors/js/tinymce-4.js b/sites/all/modules/contrib/wysiwyg/editors/js/tinymce-4.js index 448e1b942..ff6980ac3 100644 --- a/sites/all/modules/contrib/wysiwyg/editors/js/tinymce-4.js +++ b/sites/all/modules/contrib/wysiwyg/editors/js/tinymce-4.js @@ -47,7 +47,7 @@ Drupal.wysiwyg.editor.update.tinymce = function (settings, pluginInfo) { /** * Attach this editor to a target element. * - * See Drupal.wysiwyg.editor.attach.none() for a full desciption of this hook. + * See Drupal.wysiwyg.editor.attach.none() for a full description of this hook. */ Drupal.wysiwyg.editor.attach.tinymce = function (context, params, settings) { // Remove TinyMCE's internal mceItem class, which was incorrectly added to @@ -77,7 +77,7 @@ Drupal.wysiwyg.editor.attach.tinymce = function (context, params, settings) { /** * Detach a single or all editors. * - * See Drupal.wysiwyg.editor.detach.none() for a full desciption of this hook. + * See Drupal.wysiwyg.editor.detach.none() for a full description of this hook. */ Drupal.wysiwyg.editor.detach.tinymce = function (context, params, trigger) { var instance; diff --git a/sites/all/modules/contrib/wysiwyg/editors/js/wymeditor-1.js b/sites/all/modules/contrib/wysiwyg/editors/js/wymeditor-1.js new file mode 100644 index 000000000..369e41564 --- /dev/null +++ b/sites/all/modules/contrib/wysiwyg/editors/js/wymeditor-1.js @@ -0,0 +1,63 @@ +(function($) { + +/** + * Attach this editor to a target element. + */ +Drupal.wysiwyg.editor.attach.wymeditor = function (context, params, settings) { + // Prepend basePath to wymPath. + settings.wymPath = settings.basePath + settings.wymPath; + settings.postInit = function (instance) { + var $doc = $(instance._doc); + // Inject stylesheet for backwards compatibility. + if (settings.stylesheet) { + $doc.find('head').append(''); + } + // Update activeId on focus. + $doc.find('body').focus(function () { + Drupal.wysiwyg.activeId = params.field; + }); + }; + // Attach editor. + $('#' + params.field).wymeditor(settings); +}; + +/** + * Detach a single editor instance. + */ +Drupal.wysiwyg.editor.detach.wymeditor = function (context, params, trigger) { + var $field = $('#' + params.field, context); + var index = $field.data(WYMeditor.WYM_INDEX); + if (typeof index == 'undefined' || !WYMeditor.INSTANCES[index]) { + return; + } + var instance = WYMeditor.INSTANCES[index]; + instance.update(); + if (trigger != 'serialize') { + instance.vanish(); + } +}; + +Drupal.wysiwyg.editor.instance.wymeditor = { + insert: function (content) { + this.getInstance().insert(content); + }, + + setContent: function (content) { + this.getInstance().html(content); + }, + + getContent: function () { + return this.getInstance().html(); + }, + + getInstance: function () { + var $field = $('#' + this.field); + var index = $field.data(WYMeditor.WYM_INDEX); + if (typeof index != 'undefined') { + return WYMeditor.INSTANCES[index]; + } + return null; + } +}; + +})(jQuery); diff --git a/sites/all/modules/contrib/wysiwyg/editors/js/wymeditor.js b/sites/all/modules/contrib/wysiwyg/editors/js/wymeditor.js index db4f570e6..6c195dddc 100644 --- a/sites/all/modules/contrib/wysiwyg/editors/js/wymeditor.js +++ b/sites/all/modules/contrib/wysiwyg/editors/js/wymeditor.js @@ -8,7 +8,7 @@ Drupal.wysiwyg.editor.attach.wymeditor = function (context, params, settings) { settings.wymPath = settings.basePath + settings.wymPath; // Update activeId on focus. settings.postInit = function (instance) { - $(instance._doc).focus(function () { + $(instance._doc).find('body').focus(function () { Drupal.wysiwyg.activeId = params.field; }); }; @@ -26,11 +26,17 @@ Drupal.wysiwyg.editor.detach.wymeditor = function (context, params, trigger) { return; } var instance = WYMeditor.INSTANCES[index]; + var i; instance.update(); if (trigger != 'serialize') { $(instance._box).remove(); $(instance._element).show(); - delete WYMeditor.INSTANCES[index]; + $field.removeData(WYMeditor.WYM_INDEX); + WYMeditor.INSTANCES.splice(index, 1); + // Reindex the editors to maintain internal state.. + for (i = 0; i < WYMeditor.INSTANCES.length; i++) { + WYMeditor.INSTANCES[i]._index = i; + } $field.show(); } }; diff --git a/sites/all/modules/contrib/wysiwyg/editors/tinymce.inc b/sites/all/modules/contrib/wysiwyg/editors/tinymce.inc index 56b8aff34..2fbec94ec 100644 --- a/sites/all/modules/contrib/wysiwyg/editors/tinymce.inc +++ b/sites/all/modules/contrib/wysiwyg/editors/tinymce.inc @@ -27,7 +27,7 @@ function wysiwyg_tinymce_editor() { ), ), 'version callback' => 'wysiwyg_tinymce_version', - 'verified version range' => array('3.3.9.2', '4.5.1'), + 'verified version range' => array('3.3.9.2', '4.5.7'), 'themes callback' => 'wysiwyg_tinymce_themes', 'settings form callback' => 'wysiwyg_tinymce_settings_form', 'init callback' => 'wysiwyg_tinymce_init', @@ -887,7 +887,7 @@ function wysiwyg_tinymce_settings($editor, $config, $theme) { $settings['toolbar'][] = $settings['buttons'][$i]; } } - // TinyMCE 3 allowed the callback to be the name of a funciton. Convert it + // TinyMCE 3 allowed the callback to be the name of a function. Convert it // to a reference to keep compatibility with IMCE Wysiwyg bridge module. // Both isset() and is_string() needed, generates a notice if undefined. if (isset($settings['file_browser_callback']) && is_string($settings['file_browser_callback'])) { @@ -995,11 +995,29 @@ function _wysiwyg_tinymce_migrate_settings(&$settings, $editor, $profile_version $settings['buttons'] = $fixed_buttons; $migrated_version = '4.0.0'; } + if (version_compare($profile_version, '4.5.0', '<') && version_compare($installed_version, '4.5.0', '>=')) { + // The list buttons now require the lists plugin. + if (!empty($settings['buttons']['default']['bullist'])) { + $settings['buttons']['lists']['bullist'] = 1; + } + if (!empty($settings['buttons']['default']['numlist'])) { + $settings['buttons']['lists']['numlist'] = 1; + } + unset($settings['buttons']['default']['bullist'], $settings['buttons']['default']['numlist']); + $migrated_version = '4.5.0'; + } } else { // Downgrading, starting at the profile version going down. if (version_compare($profile_version, '4.5.0', '>=') && version_compare($installed_version, '4.5.0', '<')) { unset($settings['buttons']['toc']); + if (!empty($settings['buttons']['lists']['bullist'])) { + $settings['buttons']['default']['bullist'] = 1; + } + if (!empty($settings['buttons']['lists']['numlist'])) { + $settings['buttons']['default']['numlist'] = 1; + } + unset($settings['buttons']['lists']); $migrated_version = '4.5.0'; } if (version_compare($profile_version, '4.3.0', '>=') && version_compare($installed_version, '4.3.0', '<')) { @@ -1371,6 +1389,9 @@ function _wysiwyg_tinymce_plugins($editor) { 'buttons' => array('toc' => t('Table of Contents')), 'url' => _wysiwyg_tinymce_get_plugin_url('toc'), ); + // The list buttons now require the lists plugin. + $plugins['lists']['buttons'] = array('bullist' => t('Unordered list'), 'numlist' => t('Ordered list')); + unset($plugins['default']['buttons']['bullist'], $plugins['default']['buttons']['numlist']); } } diff --git a/sites/all/modules/contrib/wysiwyg/editors/wymeditor.inc b/sites/all/modules/contrib/wysiwyg/editors/wymeditor.inc index af1917fae..0e180cb31 100644 --- a/sites/all/modules/contrib/wysiwyg/editors/wymeditor.inc +++ b/sites/all/modules/contrib/wysiwyg/editors/wymeditor.inc @@ -11,24 +11,20 @@ function wysiwyg_wymeditor_editor() { $editor['wymeditor'] = array( 'title' => 'WYMeditor', - 'vendor url' => 'http://www.wymeditor.org/', - 'download url' => 'http://www.wymeditor.org/download/', - 'library path' => wysiwyg_get_path('wymeditor') . '/wymeditor', + 'vendor url' => 'http://wymeditor.github.io/wymeditor/', + 'download url' => 'https://github.com/wymeditor/wymeditor/releases', + 'library path' => wysiwyg_get_path('wymeditor'), 'libraries' => array( 'min' => array( 'title' => 'Minified', 'files' => array('jquery.wymeditor.min.js'), ), - 'pack' => array( - 'title' => 'Packed', - 'files' => array('jquery.wymeditor.pack.js'), - ), 'src' => array( 'title' => 'Source', 'files' => array('jquery.wymeditor.js'), ), ), - 'verified version range' => array('0.5', '1.0.0b5'), + 'verified version range' => array('0.5', '1.1.1'), 'version callback' => 'wysiwyg_wymeditor_version', 'themes callback' => 'wysiwyg_wymeditor_themes', 'settings form callback' => 'wysiwyg_wymeditor_settings_form', @@ -36,8 +32,18 @@ function wysiwyg_wymeditor_editor() { 'plugin callback' => '_wysiwyg_wymeditor_plugins', 'versions' => array( '0.5-rc1' => array( + 'library path' => wysiwyg_get_path('wymeditor') . '/wymeditor', 'js files' => array('wymeditor.js'), ), + '1.0.0a1' => array( + 'library path' => wysiwyg_get_path('wymeditor') . '/wymeditor', + 'js files' => array('wymeditor-1.js'), + 'css files' => array('wymeditor.css'), + ), + '1.0.0b5' => array( + 'js files' => array('wymeditor-1.js'), + 'css files' => array('wymeditor.css'), + ), ), ); return $editor; @@ -53,16 +59,23 @@ function wysiwyg_wymeditor_editor() { * The installed editor version. */ function wysiwyg_wymeditor_version($editor) { - $script = $editor['library path'] . '/jquery.wymeditor.js'; + $script = $editor['library path'] . '/wymeditor/jquery.wymeditor.js'; + if (!file_exists($script)) { + $script = $editor['library path'] . '/jquery.wymeditor.js'; + if (!file_exists($script)) { + return; + } + } if (!file_exists($script)) { return; } $script = fopen($script, 'r'); - fgets($script); - $line = fgets($script); - if (preg_match('@version\s+([0-9a-z\.-]+)@', $line, $version)) { - fclose($script); - return $version[1]; + for ($i = 0; $i < 189; $i++) { + $line = fgets($script); + if (preg_match('@version\s+([0-9\.]+(?:-?[a-z0-9\.]+)?[^\.])@', $line, $version)) { + fclose($script); + return $version[1]; + } } fclose($script); } @@ -127,8 +140,6 @@ function wysiwyg_wymeditor_settings($editor, $config, $theme) { $settings = array( 'basePath' => base_path() . $editor['library path'] . '/', 'wymPath' => $editor['libraries'][$library]['files'][0], - // @todo Does not work in Drupal; jQuery can live anywhere. - 'jQueryPath' => base_path() . 'misc/jquery.js', // WYMeditor's update event handler will revert the field contents if // changes were made after it was detached. Wysiwyg takes care of submit // events anyway so make sure WYMeditor does not bind it anywhere. @@ -136,6 +147,13 @@ function wysiwyg_wymeditor_settings($editor, $config, $theme) { 'updateEvent' => 'wysiwyg-no-event', 'skin' => $theme, ); + if (version_compare($editor['installed version'], '1.0.0-b5', '>=')) { + drupal_add_css(wysiwyg_get_path('wymeditor') . '/skins/' . $settings['skin'] . '/skin.css'); + } + else { + // @todo Does not work in Drupal; jQuery can live anywhere. + $settings['jQueryPath'] = base_path() . 'misc/jquery.js'; + } if (isset($config['language'])) { $settings['lang'] = $config['language']; @@ -144,7 +162,7 @@ function wysiwyg_wymeditor_settings($editor, $config, $theme) { // Add configured buttons. $settings['toolsItems'] = array(); if (!empty($config['buttons'])) { - $buttoninfo = _wysiwyg_wymeditor_button_info(); + $buttoninfo = _wysiwyg_wymeditor_button_info($editor['installed version']); $plugins = wysiwyg_get_plugins($editor['name']); foreach ($config['buttons'] as $plugin => $buttons) { foreach ($buttons as $button => $enabled) { @@ -246,8 +264,8 @@ function _wysiwyg_wymeditor_plugins($editor) { /** * Helper function to provide additional meta-data for internal default buttons. */ -function _wysiwyg_wymeditor_button_info() { - return array( +function _wysiwyg_wymeditor_button_info($version) { + $info = array( 'Bold' => array('title' => 'Strong', 'css' => 'wym_tools_strong'), 'Italic' => array('title' => 'Emphasis', 'css' => 'wym_tools_emphasis'), 'Superscript' => array('title' => 'Superscript', 'css' => 'wym_tools_superscript'), @@ -266,4 +284,10 @@ function _wysiwyg_wymeditor_button_info() { 'ToggleHtml' => array('title' => 'HTML', 'css' => 'wym_tools_html'), 'Preview' => array('title' => 'Preview', 'css' => 'wym_tools_preview'), ); + if (version_compare($version, '1.0.0-rc2', '>=')) { + foreach (array('CreateLink', 'InsertImage', 'InsertTable', 'Paste', 'Preview') as $button) { + $info[$button]['css'] .= ' wym_opens_dialog'; + } + } + return $info; } diff --git a/sites/all/modules/contrib/wysiwyg/includes/styling.inc b/sites/all/modules/contrib/wysiwyg/includes/styling.inc index 3c0100734..ce2547d13 100644 --- a/sites/all/modules/contrib/wysiwyg/includes/styling.inc +++ b/sites/all/modules/contrib/wysiwyg/includes/styling.inc @@ -58,7 +58,7 @@ function _wysiwyg_delivery_dummy($page_callback_result) { } /** - * Theme callback to simply suggest a theme based on the page arugment. + * Theme callback to simply suggest a theme based on the page argument. */ function _wysiwyg_theme_callback($theme) { return $theme; @@ -102,7 +102,7 @@ function _wysiwyg_pre_render_styles($elements) { } $cached = cache_get('wysiwyg_css'); foreach (element_children($elements) as $child) { - if ($elements['#groups'][$child]['group'] != CSS_THEME) { + if (isset($elements['#groups'][$child]['group']) && $elements['#groups'][$child]['group'] != CSS_THEME) { continue; } switch ($elements[$child]['#tag']) { diff --git a/sites/all/modules/contrib/wysiwyg/tests/wysiwyg_test.info b/sites/all/modules/contrib/wysiwyg/tests/wysiwyg_test.info index 7cdf8ba5c..5291db602 100644 --- a/sites/all/modules/contrib/wysiwyg/tests/wysiwyg_test.info +++ b/sites/all/modules/contrib/wysiwyg/tests/wysiwyg_test.info @@ -6,9 +6,9 @@ hidden = TRUE dependencies[] = wysiwyg files[] = wysiwyg_test.module -; Information added by Drupal.org packaging script on 2016-12-31 -version = "7.x-2.3" +; Information added by Drupal.org packaging script on 2017-05-01 +version = "7.x-2.4" core = "7.x" project = "wysiwyg" -datestamp = "1483223295" +datestamp = "1493674446" diff --git a/sites/all/modules/contrib/wysiwyg/wysiwyg.admin.inc b/sites/all/modules/contrib/wysiwyg/wysiwyg.admin.inc index 5ee0bca91..49e9af0c5 100644 --- a/sites/all/modules/contrib/wysiwyg/wysiwyg.admin.inc +++ b/sites/all/modules/contrib/wysiwyg/wysiwyg.admin.inc @@ -89,10 +89,10 @@ function wysiwyg_profile_form($form, &$form_state, $profile) { // If the editor integration supports migration between versions, attempt // it before widgets for settings are rendered. if (isset($editor['migrate settings callback']) && function_exists($editor['migrate settings callback'])) { - // Get the version migrated to. MUST be in the verified version range or - // FALSE if no migration was possible. + // Get the version migrated to. MUST be in the verified version range, + // FALSE if no migration was possible, or TRUE if no change needed. $migrated_version = $editor['migrate settings callback']($settings, $editor, $profile_version, $installed_version); - $profile->changed = ($migrated_version !== TRUE && $migrated_version != $installed_version); + $profile->changed = ($migrated_version !== TRUE && $migrated_version != $profile_version); if ($migrated_version === FALSE) { $migrate_message .= ' ' . t('Wysiwyg is not able to automatically adapt the profile to the installed editor version. It is recommended to start over with a new profile.'); $migrate_status = 'error'; @@ -590,7 +590,10 @@ function wysiwyg_profile_overview($form, &$form_state) { '#title' => t('Installation instructions'), '#collapsible' => TRUE, '#collapsed' => !isset($show_instructions), - '#description' => t('A complete list of supported editor versions for all Wysiwyg module versions can be found on drupal.org.', array('@url' => url('https://www.drupal.org/node/596966'))) . (!$count ? ' ' . t('There are no editor libraries installed currently. The following list contains a list of currently supported editors:') : ''), + '#description' => t('A complete list of supported editor versions for all Wysiwyg module versions can be found on drupal.org.', array('@url' => url('https://www.drupal.org/node/596966'))) + . (!$count ? ' ' . t('There are no editor libraries installed currently. The following list contains a list of currently supported editors:') : '') + . '
    ' . t('The instructions apply to the latest supported editor version. Paths for previous versions may differ.') + . '
    ' . t('In general: Unpack the downloaded library archive directly into the libraries folder. You may need to rename the top folder to exactly match the plain library name.'), '#weight' => 10, ); $form['status']['report'] = array('#markup' => theme('status_report', array('requirements' => $status))); diff --git a/sites/all/modules/contrib/wysiwyg/wysiwyg.api.php b/sites/all/modules/contrib/wysiwyg/wysiwyg.api.php index 5c156dc2a..7bc7ac4e4 100644 --- a/sites/all/modules/contrib/wysiwyg/wysiwyg.api.php +++ b/sites/all/modules/contrib/wysiwyg/wysiwyg.api.php @@ -206,7 +206,7 @@ function hook_INCLUDE_editor() { // (optional) A callback to invoke to return additional notes for installing // the editor library in the administrative list/overview. 'install note callback' => 'wysiwyg_ckeditor_install_note', - // The minimum and maximum versions the implemetation has been tested with. + // The minimum and maximum versions the implementation has been tested with. // Users will be notified if installing a version not within this range. 'verified version range' => array('1.2.3', '3.4.5'), // (optional) A callback to perform migrations of the settings stored in a @@ -320,7 +320,7 @@ function hook_wysiwyg_editor_settings_alter(&$settings, $context) { * * This hook acts like a pre-render callback to the style element normally * output in the document header. It is invoked before Core has - * sorted/grouped/aggregated stylehsheets and changes made here will only have + * sorted/grouped/aggregated stylesheets and changes made here will only have * an effect on the stylesheets used in an editor's WYSIWYG mode. * Wysiwyg will only keep items if their type is 'file' or 'inline' and only if * they are in the group CSS_THEME. diff --git a/sites/all/modules/contrib/wysiwyg/wysiwyg.features.inc b/sites/all/modules/contrib/wysiwyg/wysiwyg.features.inc index 02742a6f3..00e638e92 100644 --- a/sites/all/modules/contrib/wysiwyg/wysiwyg.features.inc +++ b/sites/all/modules/contrib/wysiwyg/wysiwyg.features.inc @@ -52,9 +52,10 @@ function wysiwyg_features_export_render($module, $data, $export = NULL) { foreach ($data as $name) { if ($profile = wysiwyg_get_profile($name)) { + $profile = (array) $profile; $profile_export = features_var_export($profile, ' '); - $profile_identifier = features_var_export($profile->format); - $code[] = " // Exported profile: {$profile->format}"; + $profile_identifier = features_var_export($profile['format']); + $code[] = " // Exported profile: {$profile['format']}."; $code[] = " \$profiles[{$profile_identifier}] = {$profile_export};"; $code[] = ""; } @@ -78,27 +79,44 @@ function wysiwyg_features_revert($module) { function wysiwyg_features_rebuild($module) { if ($defaults = features_get_default('wysiwyg', $module)) { foreach ($defaults as $profile) { + $profile = is_object($profile) ? (array) $profile : $profile; if (empty($profile['settings']['_profile_preferences'])) { - $settings = &$profile['settings']; - // Importing an older profile, move state to its own section. + if (!empty($profile['preferences'])) { + $settings = &$profile['preferences']; + } + else { + // Importing an older profile, move state to its own section. + $settings = &$profile['settings']; + } + $preferences = array( - 'add_to_summaries' => $settings['add_to_summaries'], + 'add_to_summaries' => (!empty($settings['add_to_summaries']) ? $settings['add_to_summaries'] : FALSE), 'default' => $settings['default'], 'show_toggle' => $settings['show_toggle'], 'user_choose' => $settings['user_choose'], - 'version' => NULL, + 'version' => (!empty($settings['version']) ? $settings['version'] : NULL), ); - unset($settings['add_to_summaries'], $settings['default'], $settings['show_toggle'], $settings['user_choose']); + unset($settings['add_to_summaries'], + $settings['default'], + $settings['show_toggle'], + $settings['user_choose'], + $settings['version'], + $profile['preferences'] + ); + if (!empty($settings['library'])) { - $prefereces['library'] = $settings['library']; + $preferences['library'] = $settings['library']; unset($settings['library']); } - $editor = wysiwyg_get_editor($profile->editor); - if ($editor['installed']) { + + $editor = wysiwyg_get_editor($profile['editor']); + if (empty($preferences['version']) && !empty($editor['installed'])) { $preferences['version'] = $editor['installed version']; } - $settings['_profile_preferences'] = $preferences; + + $profile['settings']['_profile_preferences'] = $preferences; } + db_merge('wysiwyg') ->key(array('format' => $profile['format'])) ->fields(array( @@ -116,4 +134,3 @@ function wysiwyg_features_rebuild($module) { wysiwyg_profile_cache_clear(); } } - diff --git a/sites/all/modules/contrib/wysiwyg/wysiwyg.info b/sites/all/modules/contrib/wysiwyg/wysiwyg.info index 1bc5c2865..2f8f9fc72 100644 --- a/sites/all/modules/contrib/wysiwyg/wysiwyg.info +++ b/sites/all/modules/contrib/wysiwyg/wysiwyg.info @@ -9,9 +9,9 @@ configure = admin/config/content/wysiwyg files[] = wysiwyg.module files[] = tests/wysiwyg.test -; Information added by Drupal.org packaging script on 2016-12-31 -version = "7.x-2.3" +; Information added by Drupal.org packaging script on 2017-05-01 +version = "7.x-2.4" core = "7.x" project = "wysiwyg" -datestamp = "1483223295" +datestamp = "1493674446" diff --git a/sites/all/modules/contrib/wysiwyg/wysiwyg.install b/sites/all/modules/contrib/wysiwyg/wysiwyg.install index 99227cee5..1874e5b1d 100644 --- a/sites/all/modules/contrib/wysiwyg/wysiwyg.install +++ b/sites/all/modules/contrib/wysiwyg/wysiwyg.install @@ -384,6 +384,10 @@ function wysiwyg_update_7204() { ->execute(); $query = db_select('wysiwyg', 'w') ->fields('w', array('format', 'editor', 'settings')); + drupal_load('module', 'wysiwyg'); + if (module_exists('ctools')) { + drupal_load('module', 'ctools'); + } foreach ($query->execute() as $profile) { // Clear the editing caches. if (module_exists('ctools')) { @@ -398,7 +402,7 @@ function wysiwyg_update_7204() { continue; } $preferences = array( - 'add_to_summaries' => $settings['add_to_summaries'], + 'add_to_summaries' => !empty($settings['add_to_summaries']), 'default' => $settings['default'], 'show_toggle' => $settings['show_toggle'], 'user_choose' => $settings['user_choose'], @@ -423,3 +427,25 @@ function wysiwyg_update_7204() { } wysiwyg_profile_cache_clear(); } + +/** + * Check for profiles without add_to_summaries settings. + */ +function wysiwyg_update_7205() { + $query = db_select('wysiwyg', 'w'); + $query->join('filter_format', 'f', 'w.format = f.format'); + $query->fields('w', array('format', 'editor', 'settings')); + $query->fields('f', array('name')); + + foreach ($query->execute() as $profile) { + $settings = unserialize($profile->settings); + + if (!isset($settings['_profile_preferences']['add_to_summaries'])) { + $values = array( + '@format' => $profile->name, + '!url' => 'https://www.drupal.org/node/2851313', + ); + drupal_set_message(t('You may need to manually resave the Wysiwyg profile configuration tied to the @format text format. See !url', $values), 'warning'); + } + } +} diff --git a/sites/all/modules/contrib/wysiwyg/wysiwyg.js b/sites/all/modules/contrib/wysiwyg/wysiwyg.js index 131a4be05..0cd842382 100644 --- a/sites/all/modules/contrib/wysiwyg/wysiwyg.js +++ b/sites/all/modules/contrib/wysiwyg/wysiwyg.js @@ -213,6 +213,12 @@ Drupal.behaviors.attachWysiwyg = { } wysiwygs.each(function () { Drupal.wysiwygDetach(context, this.id, trigger); + if (trigger === 'unload') { + // Delete the instance in case the field is removed. This is safe since + // detaching with the unload trigger is reverts to the 'none' "editor". + delete _internalInstances[this.id]; + delete Drupal.wysiwyg.instances[this.id]; + } }); } }; @@ -235,25 +241,12 @@ Drupal.behaviors.attachWysiwyg = { */ Drupal.wysiwygAttach = function(context, fieldId) { var fieldInfo = getFieldInfo(fieldId), - formatInfo = fieldInfo.getFormatInfo(), - editorSettings = formatInfo.editorSettings, - editor = formatInfo.editor, - previousStatus = false, - previousEditor = 'none', - doSummary = (fieldInfo.summary && (!fieldInfo.formats[fieldInfo.activeFormat] || !fieldInfo.formats[fieldInfo.activeFormat].skip_summary)); - if (_internalInstances[fieldId]) { - previousStatus = _internalInstances[fieldId]['status']; - previousEditor = _internalInstances[fieldId].editor; - } + doSummary = (fieldInfo.summary && (!fieldInfo.formats[fieldInfo.activeFormat] || !fieldInfo.formats[fieldInfo.activeFormat].skip_summary)); // Detach any previous editor instance if enabled, else remove the grippie. - detachFromField(context, {'editor': previousEditor, 'status': previousStatus, 'field': fieldId, 'resizable': fieldInfo.resizable}, 'unload'); - if (doSummary) { - // Summary instances may have a different status if no real editor was - // attached yet because the field was hidden. - if (_internalInstances[fieldInfo.summary]) { - previousStatus = _internalInstances[fieldInfo.summary]['status']; - } - detachFromField(context, {'editor': previousEditor, 'status': previousStatus, 'field': fieldInfo.summary, 'resizable': fieldInfo.resizable}, 'unload'); + detachFromField(fieldId, context, 'unload'); + var wasSummary = !!_internalInstances[fieldInfo.summary]; + if (doSummary || wasSummary) { + detachFromField(fieldId, context, 'unload', {summary: true}); } // Store this field id, so (external) plugins can use it. // @todo Wrong point in time. Probably can only supported by editors which @@ -262,21 +255,21 @@ Drupal.wysiwygAttach = function(context, fieldId) { // Attach or update toggle link, if enabled. Drupal.wysiwygAttachToggleLink(context, fieldId); // Attach to main field. - attachToField(context, {'status': fieldInfo.enabled, 'editor': editor, 'field': fieldId, 'format': fieldInfo.activeFormat, 'resizable': fieldInfo.resizable}, editorSettings); + attachToField(fieldId, context); // Attach to summary field. - if (doSummary) { + if (doSummary || wasSummary) { // If the summary wrapper is visible, attach immediately. if ($('#' + fieldInfo.summary).parents('.text-summary-wrapper').is(':visible')) { - attachToField(context, {'status': fieldInfo.enabled, 'editor': editor, 'field': fieldInfo.summary, 'format': fieldInfo.activeFormat, 'resizable': fieldInfo.resizable}, editorSettings); + attachToField(fieldId, context, {summary: true, forceDisabled: !doSummary}); } else { // Attach an instance of the 'none' editor to have consistency while the // summary is hidden, then switch to a real editor instance when shown. - attachToField(context, {'status': false, 'editor': editor, 'field': fieldInfo.summary, 'format': fieldInfo.activeFormat, 'resizable': fieldInfo.resizable}, editorSettings); + attachToField(fieldId, context, {summary: true, forceDisabled: true}); // Unbind any existing click handler to avoid double toggling. - $('#' + fieldId).parents('.text-format-wrapper').find('.link-edit-summary').unbind('click.wysiwyg').bind('click.wysiwyg', function () { - detachFromField(context, {'status': false, 'editor': editor, 'field': fieldInfo.summary, 'format': fieldInfo.activeFormat, 'resizable': fieldInfo.resizable}, editorSettings); - attachToField(context, {'status': fieldInfo.enabled, 'editor': editor, 'field': fieldInfo.summary, 'format': fieldInfo.activeFormat, 'resizable': fieldInfo.resizable}, editorSettings); + $('#' + fieldId).parents('.text-format-wrapper').find('.link-edit-summary').closest('.field-edit-link').unbind('click.wysiwyg').bind('click.wysiwyg', function () { + detachFromField(fieldId, context, 'unload', {summary: true}); + attachToField(fieldId, context, {summary: true, forceDisabled: !doSummary}); $(this).unbind('click.wysiwyg'); }); } @@ -355,7 +348,7 @@ function WysiwygInstance(internalInstance) { /** * Open a native editor dialog. * - * Use of this method i not recomended due to limited editor support. + * Use of this method i not recommended due to limited editor support. * * @param dialog * An object with dialog settings. Keys used: @@ -482,6 +475,10 @@ function updateInternalState(settings, context) { var fieldId = trigger.field; var baseFieldId = (fieldId.indexOf('--') === -1 ? fieldId : fieldId.substr(0, fieldId.indexOf('--'))); var fieldInfo = null; + if ($('#' + triggerId, context).length === 0) { + // Skip fields which may have been removed or are not in this context. + continue; + } if (!(fieldInfo = _fieldInfoStorage[baseFieldId])) { fieldInfo = _fieldInfoStorage[baseFieldId] = { formats: {}, @@ -495,15 +492,15 @@ function updateInternalState(settings, context) { return getFormatInfo(this.activeFormat); } // 'activeFormat' and 'enabled' added below. - } - }; + }; + } for (var format in trigger) { if (format.indexOf('format') != 0 || fieldInfo.formats[format]) { continue; } fieldInfo.formats[format] = { 'enabled': trigger[format].status - } + }; if (!_formatInfoStorage[format]) { _formatInfoStorage[format] = { editor: trigger[format].editor, @@ -544,38 +541,44 @@ function updateInternalState(settings, context) { * * Creates the 'instance' object under Drupal.wysiwyg.instances[fieldId]. * + * @param mainFieldId + * The id of the field's main element, for fetching field info. * @param context * A DOM element, supplied by Drupal.attachBehaviors(). * @param params - * An object containing state information for the editor with the following - * properties: - * - 'status': A boolean stating whether the editor is currently active. If - * false, the default textarea behaviors will be attached instead (aka the - * 'none' editor implementation). - * - 'editor': The internal name of the editor to attach when active. - * - 'field': The field id to use as an output target for the editor. - * - 'format': The name of the active text format (prefixed 'format'). - * - 'resizable': A boolean indicating whether the original textarea was - * resizable. - * Note: This parameter is passed directly to the editor implementation and - * needs to have been reconstructed or cloned before attaching. - * @param editorSettings - * An object containing all the settings the editor needs for this field. - * Settings are automatically cloned to prevent editors from modifying them. + * An optional object for overriding state information for the editor with the + * following properties: + * - 'summary': Set to true to indicate to attach to the summary instead of + * the main element. Defaults to false. + * - 'forceDisabled': Set to true to override the current state of the field + * and assume it is disabled. Useful for hidden summary instances. + * + * @see Drupal.wysiwygAttach() */ -function attachToField(context, params, editorSettings) { +function attachToField(mainFieldId, context, params) { + params = params || {}; + var fieldInfo = getFieldInfo(mainFieldId); + var fieldId = (params.summary ? fieldInfo.summary : mainFieldId); + var formatInfo = fieldInfo.getFormatInfo(); // If the editor isn't active, attach default behaviors instead. - var editor = (params.status ? params.editor : 'none'); + var enabled = (fieldInfo.enabled && !params.forceDisabled); + var editor = (enabled ? formatInfo.editor : 'none'); // Settings are deep merged (cloned) to prevent editor implementations from // permanently modifying them while attaching. - var clonedSettings = jQuery.extend(true, {}, editorSettings); + var clonedSettings = (enabled ? jQuery.extend(true, {}, formatInfo.editorSettings) : {}); // (Re-)initialize field instance. - var internalInstance = new WysiwygInternalInstance(params); - _internalInstances[params.field] = internalInstance; - Drupal.wysiwyg.instances[params.field] = internalInstance.publicInstance; - if ($.isFunction(Drupal.wysiwyg.editor.attach[editor])) { - Drupal.wysiwyg.editor.attach[editor].call(internalInstance, context, params, params.status ? clonedSettings : {}); - } + var stateParams = { + field: fieldId, + editor: formatInfo.editor, + 'status': enabled, + format: fieldInfo.activeFormat, + resizable: fieldInfo.resizable + }; + var internalInstance = new WysiwygInternalInstance(stateParams); + _internalInstances[fieldId] = internalInstance; + Drupal.wysiwyg.instances[fieldId] = internalInstance.publicInstance; + // Attach editor, if enabled by default or last state was enabled. + Drupal.wysiwyg.editor.attach[editor].call(internalInstance, context, stateParams, clonedSettings); } /** @@ -601,29 +604,22 @@ function attachToField(context, params, editorSettings) { */ Drupal.wysiwygDetach = function (context, fieldId, trigger) { var fieldInfo = getFieldInfo(fieldId), - editor = fieldInfo.getFormatInfo().editor, - trigger = trigger || 'unload', - previousStatus = (_internalInstances[fieldId] && _internalInstances[fieldId]['status']); + trigger = trigger || 'unload'; // Detach from main field. - detachFromField(context, {'editor': editor, 'status': previousStatus, 'field': fieldId, 'resizable': fieldInfo.resizable}, trigger); + detachFromField(fieldId, context, trigger); if (trigger == 'unload') { // Attach the resize behavior by forcing status to false. Other values are // intentionally kept the same to show which editor is normally attached. - attachToField(context, {'editor': editor, 'status': false, 'format': fieldInfo.activeFormat, 'field': fieldId, 'resizable': fieldInfo.resizable}); + attachToField(fieldId, context, {forceDisabled: true}); Drupal.wysiwygAttachToggleLink(context, fieldId); } // Detach from summary field. if (fieldInfo.summary && _internalInstances[fieldInfo.summary]) { // The "Edit summary" click handler could re-enable the editor by mistake. $('#' + fieldId).parents('.text-format-wrapper').find('.link-edit-summary').unbind('click.wysiwyg'); - // Summary instances may have a different status if no real editor was - // attached yet because the field was hidden. - if (_internalInstances[fieldInfo.summary]) { - previousStatus = _internalInstances[fieldInfo.summary]['status']; - } - detachFromField(context, {'editor': editor, 'status': previousStatus, 'field': fieldInfo.summary, 'resizable': fieldInfo.resizable}, trigger); + detachFromField(fieldId, context, trigger, {summary: true}); if (trigger == 'unload') { - attachToField(context, {'editor': editor, 'status': false, 'format': fieldInfo.activeFormat, 'field': fieldInfo.summary, 'resizable': fieldInfo.resizable}); + attachToField(fieldId, context, {summary: true}); } } }; @@ -633,38 +629,47 @@ Drupal.wysiwygDetach = function (context, fieldId, trigger) { * * Removes the 'instance' object under Drupal.wysiwyg.instances[fieldId]. * + * @param mainFieldId + * The id of the field's main element, for fetching field info. * @param context * A DOM element, supplied by Drupal.detachBehaviors(). - * @param params - * An object containing state information for the editor with the following - * properties: - * - 'status': A boolean stating whether the editor is currently active. If - * false, the default textarea behaviors will be attached instead (aka the - * 'none' editor implementation). - * - 'editor': The internal name of the editor to attach when active. - * - 'field': The field id to use as an output target for the editor. - * - 'format': The name of the active text format (prefixed 'format'). - * - 'resizable': A boolean indicating whether the original textarea was - * resizable. - * Note: This parameter is passed directly to the editor implementation and - * needs to have been reconstructed or cloned before detaching. * @param trigger * A string describing what is causing the editor to be detached. * - 'serialize': The editor normally just syncs its contents to the original * textarea for value serialization before an AJAX request. * - 'unload': The editor is to be removed completely and the original * textarea restored. + * @param params + * An optional object for overriding state information for the editor with the + * following properties: + * - 'summary': Set to true to indicate to detach from the summary instead of + * the main element. Defaults to false. * * @see Drupal.wysiwygDetach() */ -function detachFromField(context, params, trigger) { - var editor = (params.status ? params.editor : 'none'); +function detachFromField(mainFieldId, context, trigger, params) { + params = params || {}; + var fieldInfo = getFieldInfo(mainFieldId); + var fieldId = (params.summary ? fieldInfo.summary : mainFieldId); + var enabled = false; + var editor = 'none'; + if (_internalInstances[fieldId]) { + enabled = _internalInstances[fieldId]['status']; + editor = (enabled ? _internalInstances[fieldId].editor : 'none'); + } + var stateParams = { + field: fieldId, + 'status': enabled, + editor: fieldInfo.editor, + format: fieldInfo.activeFormat, + resizable: fieldInfo.resizable + }; if (jQuery.isFunction(Drupal.wysiwyg.editor.detach[editor])) { - Drupal.wysiwyg.editor.detach[editor].call(_internalInstances[params.field], context, params, trigger); + Drupal.wysiwyg.editor.detach[editor].call(_internalInstances[fieldId], context, stateParams, trigger); } if (trigger == 'unload') { - delete Drupal.wysiwyg.instances[params.field]; - delete _internalInstances[params.field]; + delete Drupal.wysiwyg.instances[fieldId]; + delete _internalInstances[fieldId]; } } @@ -730,16 +735,21 @@ Drupal.wysiwyg.toggleWysiwyg = function (event) { * Event handler for when the selected format is changed. */ function formatChanged(event) { - var fieldId = _selectToField[this.id.replace(/--\d+$/,'')]; + var fieldId = _selectToField[this.id.replace(/--\d+$/, '')]; var context = $(this).closest('form'); + var newFormat = 'format' + $(this).val(); // Field state is fetched by reference. var currentField = getFieldInfo(fieldId); + // Prevent double-attaching if change event is triggered manually. + if (newFormat === currentField.activeFormat) { + return; + } // Save the state of the current format. if (currentField.formats[currentField.activeFormat]) { currentField.formats[currentField.activeFormat].enabled = currentField.enabled; } // Switch format/profile. - currentField.activeFormat = 'format' + this.value; + currentField.activeFormat = newFormat; // Load the state from the new format. if (currentField.formats[currentField.activeFormat]) { currentField.enabled = currentField.formats[currentField.activeFormat].enabled; diff --git a/sites/all/modules/contrib/wysiwyg/wysiwyg.module b/sites/all/modules/contrib/wysiwyg/wysiwyg.module index c925ca5ea..c966294c6 100644 --- a/sites/all/modules/contrib/wysiwyg/wysiwyg.module +++ b/sites/all/modules/contrib/wysiwyg/wysiwyg.module @@ -148,11 +148,11 @@ function wysiwyg_admin_menu_map() { } $profiles = wysiwyg_profile_load_all(); - $map['admin/config/content/wysiwyg/profile/%wysiwyg_profile'] = array( + $map['admin/config/content/wysiwyg/profile/%wysiwyg_ui_profile_cache'] = array( 'parent' => 'admin/config/content/wysiwyg', 'hide' => 'admin/config/content/wysiwyg/list', 'arguments' => array( - array('%wysiwyg_profile' => array_keys($profiles)), + array('%wysiwyg_ui_profile_cache' => array_keys($profiles)), ), ); return $map; @@ -275,7 +275,7 @@ function wysiwyg_pre_render_text_format($element) { 'toggle' => 1, ); $loaded = TRUE; - if (isset($profile->settings['add_to_summaries']) && !$profile->settings['add_to_summaries']) { + if (isset($profile->preferences['add_to_summaries']) && !$profile->preferences['add_to_summaries']) { $settings[$format]['skip_summary'] = 1; } $settings[$format]['editor'] = $profile->editor; @@ -771,13 +771,13 @@ function wysiwyg_get_css($theme = NULL) { */ function wysiwyg_themes_enabled($theme_list) { $cached = cache_get('wysiwyg_css'); - foreach ($theme_list as $theme) { - if ($cached && !empty($cached->data)) { - $css = $cached->data; + if ($cached && !empty($cached->data)) { + $css = $cached->data; + foreach ($theme_list as $theme) { unset($css[$theme]); } + cache_set('wysiwyg_css', $css); } - cache_set('wysiwyg_css', $css); } /** @@ -1099,6 +1099,17 @@ function wysiwyg_get_all_editors() { ); // Check whether library is present. if (!($editors[$editor]['installed'] = file_exists($editors[$editor]['library path']))) { + // Find the latest supported version. + ksort($editors[$editor]['versions']); + $version = key($editors[$editor]['versions']); + foreach ($editors[$editor]['versions'] as $supported_version => $version_properties) { + if (version_compare($version, $supported_version, '<')) { + $version = $supported_version; + } + } + // Apply library version specific definitions and overrides. + // This is to show the newest installation instructions. + $editors[$editor] = array_merge($editors[$editor], $editors[$editor]['versions'][$version]); continue; } $installed_version = NULL; diff --git a/sites/all/modules/contrib/yoast_seo/includes/yoast_seo.forms.inc b/sites/all/modules/contrib/yoast_seo/includes/yoast_seo.forms.inc index 9db2faf6f..dfafdac6c 100644 --- a/sites/all/modules/contrib/yoast_seo/includes/yoast_seo.forms.inc +++ b/sites/all/modules/contrib/yoast_seo/includes/yoast_seo.forms.inc @@ -100,10 +100,10 @@ function _yoast_seo_process_node_settings_form(&$form) { * '#multiple' => TRUE, */ if (empty($body_fields)) { - $description = t('There is no filed that can be used as additional field.'); + $description = t('There are no other text fields that could be used instead of the Body field.'); } else { - $description = t('Select fields to use instead or in addition to body for Real-time SEO. Access to all fields must be allowed.'); + $description = t('Select fields to use instead, or in addition to body for Real-time SEO. Access to all fields must be allowed.'); } $form['yoast_seo']['yoast_seo_body']['yoast_seo_body_fields'] = array( diff --git a/sites/all/modules/contrib/yoast_seo/js/yoast_seo.js b/sites/all/modules/contrib/yoast_seo/js/yoast_seo.js index 7ac2341e6..f1b020fde 100644 --- a/sites/all/modules/contrib/yoast_seo/js/yoast_seo.js +++ b/sites/all/modules/contrib/yoast_seo/js/yoast_seo.js @@ -151,7 +151,9 @@ YoastSEO_DrupalSource.prototype.parseSnippetData = function(source, target) { document.getElementById(target).value = (ev.target.value || ""); this.triggerEvent(target); }.bind(this); - document.getElementById(source).addEventListener("blur", listener); + if (document.getElementById(source) !== null) { + document.getElementById(source).addEventListener('blur', listener); + } }; diff --git a/sites/all/modules/contrib/yoast_seo/yoast_seo.info b/sites/all/modules/contrib/yoast_seo/yoast_seo.info index 450178d8d..f9209e634 100644 --- a/sites/all/modules/contrib/yoast_seo/yoast_seo.info +++ b/sites/all/modules/contrib/yoast_seo/yoast_seo.info @@ -16,9 +16,9 @@ configure = admin/config/search/yoast ; Views Handlers. files[] = views/yoast_seo_handler_rating.inc -; Information added by Drupal.org packaging script on 2016-12-05 -version = "7.x-1.1" +; Information added by Drupal.org packaging script on 2017-04-05 +version = "7.x-1.2" core = "7.x" project = "yoast_seo" -datestamp = "1480952305" +datestamp = "1491409085" diff --git a/sites/all/modules/contrib/yoast_seo/yoast_seo.module b/sites/all/modules/contrib/yoast_seo/yoast_seo.module index 7ec21818b..7f7b98151 100644 --- a/sites/all/modules/contrib/yoast_seo/yoast_seo.module +++ b/sites/all/modules/contrib/yoast_seo/yoast_seo.module @@ -338,11 +338,11 @@ function yoast_seo_configuration_form_after_build($form, &$form_state) { 'fields' => array( 'focusKeyword' => $form['yoast_seo'][$langcode]['focus_keyword']['#id'], 'seoStatus' => $form['yoast_seo'][$langcode]['seo_status']['#attributes']['id'], - 'pageTitle' => $form['metatags'][$langcode]['basic']['title']['value']['#id'], + 'pageTitle' => (module_exists('seo_ui')) ? $form['seo_vtab']['metatags_title']['title']['value']['#id'] : $form['metatags'][$langcode]['basic']['title']['value']['#id'], 'nodeTitle' => $form['title']['#id'], - 'description' => $form['metatags'][$langcode]['basic']['description']['value']['#id'], + 'description' => (module_exists('seo_ui')) ? $form['seo_vtab']['metatags'][$langcode]['basic']['description']['value']['#id'] : $form['metatags'][$langcode]['basic']['description']['value']['#id'], 'bodyText' => array_keys($text_content), - 'url' => $form['path']['alias']['#id'], + 'url' => (module_exists('seo_ui')) ? $form['seo_vtab']['path']['alias']['#id'] : $form['path']['alias']['#id'], ), 'placeholderText' => array( 'title' => t('Please click here to alter your page meta title'),