Browse Source

updated webform & faq

Bachir Soussi Chiadmi 4 years ago
parent
commit
be549a75f6
35 changed files with 569 additions and 239 deletions
  1. 9 12
      sites/all/modules/contrib/form/webform/README.md
  2. 13 16
      sites/all/modules/contrib/form/webform/THEMING.md
  3. 7 7
      sites/all/modules/contrib/form/webform/components/date.inc
  4. 3 3
      sites/all/modules/contrib/form/webform/components/email.inc
  5. 7 6
      sites/all/modules/contrib/form/webform/components/grid.inc
  6. 2 2
      sites/all/modules/contrib/form/webform/components/number.inc
  7. 2 2
      sites/all/modules/contrib/form/webform/components/textarea.inc
  8. 2 1
      sites/all/modules/contrib/form/webform/components/textfield.inc
  9. 32 0
      sites/all/modules/contrib/form/webform/css/webform.css
  10. 8 1
      sites/all/modules/contrib/form/webform/includes/webform.admin.inc
  11. 9 7
      sites/all/modules/contrib/form/webform/includes/webform.components.inc
  12. 12 2
      sites/all/modules/contrib/form/webform/includes/webform.conditionals.inc
  13. 14 12
      sites/all/modules/contrib/form/webform/includes/webform.report.inc
  14. 32 11
      sites/all/modules/contrib/form/webform/includes/webform.submissions.inc
  15. 23 3
      sites/all/modules/contrib/form/webform/js/webform.js
  16. 1 1
      sites/all/modules/contrib/form/webform/templates/webform-mail.tpl.php
  17. 8 0
      sites/all/modules/contrib/form/webform/tests/WebformComponentsTestCase.test
  18. 36 1
      sites/all/modules/contrib/form/webform/tests/WebformSubmissionTestCase.test
  19. 7 15
      sites/all/modules/contrib/form/webform/tests/WebformTestCase.test
  20. 80 0
      sites/all/modules/contrib/form/webform/tests/WebformUnitTestCase.test
  21. 1 1
      sites/all/modules/contrib/form/webform/views/webform_handler_relationship_submission_data.inc
  22. 23 3
      sites/all/modules/contrib/form/webform/webform.api.php
  23. 4 3
      sites/all/modules/contrib/form/webform/webform.info
  24. 36 19
      sites/all/modules/contrib/form/webform/webform.install
  25. 97 41
      sites/all/modules/contrib/form/webform/webform.module
  26. 0 4
      sites/all/modules/contrib/users/faq/.gitignore
  27. 7 5
      sites/all/modules/contrib/users/faq/faq.admin.inc
  28. 3 4
      sites/all/modules/contrib/users/faq/faq.info
  29. 18 1
      sites/all/modules/contrib/users/faq/faq.install
  30. 44 27
      sites/all/modules/contrib/users/faq/faq.module
  31. 15 15
      sites/all/modules/contrib/users/faq/faq.test
  32. 3 3
      sites/all/modules/contrib/users/faq/includes/faq.hide_answer.inc
  33. 3 3
      sites/all/modules/contrib/users/faq/includes/faq.new_page.inc
  34. 3 3
      sites/all/modules/contrib/users/faq/includes/faq.questions_inline.inc
  35. 5 5
      sites/all/modules/contrib/users/faq/includes/faq.questions_top.inc

+ 9 - 12
sites/all/modules/contrib/form/webform/README.txt → sites/all/modules/contrib/form/webform/README.md

@@ -1,18 +1,15 @@
-Description
------------
+# Webform
+
+## Description
+
 This module adds a webform content type to your Drupal site.
 A webform can be a questionnaire, contact or request form. These can be used
 by visitor to make contact or to enable a more complex survey than polls
 provide. Submissions from a webform are saved in a database table and
 can optionally be mailed to e-mail addresses upon submission.
 
-Requirements
-------------
-Drupal 7.x
-See https://www.drupal.org/project/webform for additional requirements.
+## Installation
 
-Installation
-------------
 1. Copy the entire webform directory the Drupal sites/all/modules directory.
 
 2. Login as an administrator. Enable the module in the "Administer" -> "Modules"
@@ -22,8 +19,8 @@ Installation
 
 4. Create a webform node at node/add/webform.
 
-Upgrading from previous versions
---------------------------------
+## Upgrading from previous versions
+
 Note that you must be running the latest 3.x version of Webform (for either
 Drupal 6 or Drupal 7) before upgrading to Webform 4.x.
 
@@ -46,7 +43,7 @@ https://drupal.org/node/1609324.
 
 4. Run update.php (at http://www.example.com/update.php).
 
-Support
--------
+## Support
+
 Please use the issue queue for filing bugs with this module at
 http://drupal.org/project/issues/webform

+ 13 - 16
sites/all/modules/contrib/form/webform/THEMING.txt → sites/all/modules/contrib/form/webform/THEMING.md

@@ -1,12 +1,14 @@
-Overview
---------
+# Theming Webform
+
+## Overview
+
 Webform supports theming similar to the CCK or Views modules. Any webform may be
 themed on the server side, though doing so may require a reasonable amount of
 knowledge about the Drupal Form API. More information about the Form API may be
 found at: http://api.drupal.org/api/file/developer/topics/forms_api.html
 
-Theme submission e-mails
------------------------
+## Theme submission e-mails
+
 The default e-mails sent by webform are fairly basic. If you like, you may
 customize the display of e-mails sent by each individual webform.
 
@@ -29,9 +31,9 @@ customize the display of e-mails sent by each individual webform.
 
 - To get a better idea of what variables are available to you, you can include
   the print_r function in your email. Simply include the line:
-
+  ```
   <?php print_r($submission) ?>
-
+  ```
   to get a listing of all the available fields you can use in your mail.
 
 - Advanced Webform e-mail Theming: Theming the e-mail headers may also be done
@@ -39,8 +41,7 @@ customize the display of e-mails sent by each individual webform.
   Just copy the code out of webform.module and change as necessary in your
   template.php file. This allows you to customize the e-mail headers.
 
-Theme the confirmation page
----------------------------
+## Theme the confirmation page
 
 After a user submits a webform, they are directed to a page that contains the
 confirmation message set in the webform node settings (assuming the form doesn't
@@ -55,19 +56,17 @@ the confirmation page of a single node or all webforms on your site.
 - Open the new file and change it's contents to the your liking. Here's an
   example that inserts some additional HTML around the confirmation message and
   gives links to edit the submission.
-
+  ```
   <?php /* Begin sample webform confirmation page */ ?>
-
   <div class="confirmation-message">
     <?php print $confirmation_message ?>
   </div>
-
   <ul>
     <li><a href="<?php print url('node/' . $node->nid . '/submission/' . $sid)?>">View your submission</a></li>
     <li><a href="<?php print url('node/' . $node->nid . '/submission/' . $sid . '/edit')?>">Edit your submission</a></li>
   </ul>
-
   <?php /* End sample webform confirmation page */ ?>
+  ```
 
 - You may edit the webform-confirmation.tpl.php file in your theme directory,
   this will affect all the webform mails sent by your entire site. Or, if you
@@ -77,8 +76,7 @@ the confirmation page of a single node or all webforms on your site.
 
 - Visit admin/settings/performance and click the "Clear cached data" button.
 
-Theme display of an entire webform
-----------------------------------
+## Theme display of an entire webform
 
 Theming a webform can be useful for rearranging elements or customizing the
 appearance of multiple components at once. This tutorial assumes usage
@@ -100,8 +98,7 @@ of the phptemplate engine.
   you have good reason to do so (like you're forwarding your webform to a custom
   PHP or PERL script).
 
-Theme display of a webform submission display
----------------------------------------------
+## Theme display of a webform submission display
 
 Theming the display of a webform submission works the same way as theming a
 webform form. Webform uses Drupal "renderable" style arrays for the display of

+ 7 - 7
sites/all/modules/contrib/form/webform/components/date.inc

@@ -407,21 +407,21 @@ function theme_webform_date($variables) {
  * Element validation for Webform date fields.
  */
 function webform_validate_date($element, $form_state) {
-  $field_types = array('day', 'month', 'year');
+  $date_parts = array('day', 'month', 'year');
 
   // Determine if the user has specified a date. Hidden parts of the date will
   // be submitted automatically.
-  foreach ($field_types as $field_type) {
-    if (!in_array($field_type, $element['#exclude']) && $element[$field_type]['#value'] !== '') {
+  foreach ($date_parts as $date_part) {
+    if (!in_array($date_part, $element['#exclude']) && $element[$date_part]['#value'] !== '') {
       $field_found = TRUE;
     }
   }
 
   if (isset($field_found)) {
     // Check that each part of the date has been filled in.
-    foreach ($field_types as $field_type) {
-      if (empty($element[$field_type]['#value'])) {
-        form_error($element[$field_type], t('!part in !name is missing.', array('!name' => $element['#title'], '!part' => $element[$field_type]['#title'])));
+    foreach ($date_parts as $date_part) {
+      if (empty($element[$date_part]['#value'])) {
+        form_error($element[$date_part], t('!part in !name is missing.', array('!name' => $element['#title'], '!part' => $element[$date_part]['#title'])));
         $missing_fields = TRUE;
       }
     }
@@ -430,7 +430,7 @@ function webform_validate_date($element, $form_state) {
     }
 
     // Ensure date is made up of integers.
-    foreach (array('year', 'month', 'day') as $date_part) {
+    foreach ($date_parts as $date_part) {
       $element[$date_part]['#value'] = (int) $element[$date_part]['#value'];
     }
 

+ 3 - 3
sites/all/modules/contrib/form/webform/components/email.inc

@@ -61,7 +61,7 @@ function _webform_edit_email($component) {
     '#size' => 60,
     '#maxlength' => 127,
     '#weight' => 0,
-    '#attributes' => ($component['value'] == '[current-user:mail]' && count(form_get_errors()) == 0) ? array('disabled' => TRUE) : array(),
+    '#attributes' => ($component['value'] == '[current-user:mail]' && !form_get_errors()) ? array('disabled' => TRUE) : array(),
     '#id' => 'email-value',
   );
   $form['user_email'] = array(
@@ -155,7 +155,7 @@ function _webform_render_email($component, $value = NULL, $filter = TRUE, $submi
     '#attributes' => $component['extra']['attributes'],
     '#element_validate'  => array('_webform_validate_email'),
     '#theme_wrappers' => array('webform_element'),
-    '#translatable' => array('title', 'description'),
+    '#translatable' => array('title', 'description', 'placeholder'),
   );
 
   if ($component['required']) {
@@ -275,7 +275,7 @@ function _webform_display_email($component, $value, $format = 'html', $submissio
     '#theme_wrappers' => $format == 'html' ? array('webform_element') : array('webform_element_text'),
     '#format' => $format,
     '#value' => isset($value[0]) ? $value[0] : '',
-    '#translatable' => array('title'),
+    '#translatable' => array('title', 'placeholder'),
   );
 }
 

+ 7 - 6
sites/all/modules/contrib/form/webform/components/grid.inc

@@ -764,10 +764,12 @@ function theme_webform_grid($variables) {
  */
 function _webform_grid_header(array $element, $right_titles) {
   $titles = explode('|', $element['#title'], 2);
+  $title_left = $titles[0];
   $header = array(
     array(
-      'data' => _webform_grid_header_title($element, $titles[0]),
+      'data' => _webform_grid_header_title($element, $title_left),
       'class' => array('webform-grid-question'),
+      'scope' => 'col',
     ),
   );
   foreach ($element['#grid_options'] as $option) {
@@ -778,9 +780,11 @@ function _webform_grid_header(array $element, $right_titles) {
     );
   }
   if ($right_titles) {
+    $title_right = isset($titles[1]) ? $titles[1] : $title_left;
     $header[] = array(
-      'data' => _webform_grid_header_title($element, isset($titles[1]) ? $titles[1] : ''),
+      'data' => _webform_grid_header_title($element, $title_right),
       'class' => array('webform-grid-question'),
+      'scope' => 'col',
     );
   }
   return $header;
@@ -792,10 +796,7 @@ function _webform_grid_header(array $element, $right_titles) {
 function _webform_grid_header_title($element, $title) {
   $header_title = '';
   if ($element['#title_display'] == 'internal') {
-    $variables = array('element' => $element);
-    $variables['element']['#title_display'] = 'before';
-    $variables['element']['#title'] = $title;
-    $header_title = theme('form_element_label', $variables);
+    $header_title = $title;
   }
   return $header_title;
 }

+ 2 - 2
sites/all/modules/contrib/form/webform/components/number.inc

@@ -305,7 +305,7 @@ function _webform_render_number($component, $value = NULL, $filter = TRUE, $subm
     '#point' => $component['extra']['point'],
     '#separator' => $component['extra']['separator'],
     '#decimals' => $component['extra']['decimals'],
-    '#translatable' => array('title', 'description', 'field_prefix', 'field_suffix'),
+    '#translatable' => array('title', 'description', 'field_prefix', 'field_suffix', 'placeholder'),
   );
 
   if ($component['required']) {
@@ -404,7 +404,7 @@ function _webform_display_number($component, $value, $format = 'html', $submissi
     '#field_suffix' => $empty ? '' : $component['extra']['field_suffix'],
     '#format' => $format,
     '#value' => $empty ? '' : _webform_number_format($component, $value[0]),
-    '#translatable' => array('title'),
+    '#translatable' => array('title', 'placeholder'),
   );
 }
 

+ 2 - 2
sites/all/modules/contrib/form/webform/components/textarea.inc

@@ -123,7 +123,7 @@ function _webform_render_textarea($component, $value = NULL, $filter = TRUE, $su
     // MUST be FALSE to disable.
     '#resizable' => (bool) $component['extra']['resizable'],
     '#theme_wrappers' => array('webform_element'),
-    '#translatable' => array('title', 'description'),
+    '#translatable' => array('title', 'description', 'placeholder'),
   );
 
   if ($component['required']) {
@@ -162,7 +162,7 @@ function _webform_display_textarea($component, $value, $format = 'html', $submis
     '#theme_wrappers' => $format == 'html' ? array('webform_element') : array('webform_element_text'),
     '#format' => $format,
     '#value' => isset($value[0]) ? $value[0] : '',
-    '#translatable' => array('title'),
+    '#translatable' => array('title', 'placeholder'),
   );
 }
 

+ 2 - 1
sites/all/modules/contrib/form/webform/components/textfield.inc

@@ -163,6 +163,7 @@ function _webform_render_textfield($component, $value = NULL, $filter = TRUE, $s
       'description',
       'field_prefix',
       'field_suffix',
+      'placeholder',
     ),
   );
 
@@ -220,7 +221,7 @@ function _webform_display_textfield($component, $value, $format = 'html', $submi
     '#field_suffix' => $component['extra']['field_suffix'],
     '#format' => $format,
     '#value' => isset($value[0]) ? $value[0] : '',
-    '#translatable' => array('title', 'field_prefix', 'field_suffix'),
+    '#translatable' => array('title', 'field_prefix', 'field_suffix', 'placeholder'),
   );
 }
 

+ 32 - 0
sites/all/modules/contrib/form/webform/css/webform.css

@@ -34,6 +34,38 @@ html.js input.webform-calendar {
 .webform-container-inline.webform-component-textarea .form-textarea-wrapper {
   display: inline-block;
 }
+
+/* Reset so that these appear the same as the label elements they replace. */
+fieldset.fieldset-invisible,
+fieldset.fieldset-invisible > legend {
+  margin: 0;
+  padding: 0;
+  border: none;
+  border-radius: 0;
+  background: inherit;
+  position: static;
+  color: inherit;
+  height: auto;
+  width: auto;
+  font-family: inherit;
+  text-indent: 0;
+  line-height: inherit;
+  text-shadow: unset;
+  top: 0;
+  right: 0;
+  bottom: 0;
+  left: 0;
+}
+fieldset.fieldset-invisible > legend {
+  font-weight: bold;
+  font-size: 0.929em;
+}
+/* This margin causes the fieldset to be too big. */
+fieldset.fieldset-invisible > div > div.form-item:last-child,
+fieldset.fieldset-invisible > table {
+  margin-bottom: 0;
+}
+
 .webform-component-textarea .grippie {
   display: block;
 }

+ 8 - 1
sites/all/modules/contrib/form/webform/includes/webform.admin.inc

@@ -20,7 +20,7 @@ function webform_admin_settings() {
   );
 
   // Add each component to the form:
-  $form['components'] = array('#tree' => TRUE);
+  $form['components'] += array('#tree' => TRUE);
   $component_types = webform_components(TRUE);
   foreach ($component_types as $key => $component) {
     $form['components'][$key] = array(
@@ -255,6 +255,13 @@ function webform_admin_settings() {
     '#description' => t('When mapping emails addresses to a select component, limit the choice to components with less than the amount of options indicated. This is to avoid flooding the email settings form.'),
   );
 
+  $form['advanced']['webform_fieldset_wrap'] = array(
+    '#type' => 'checkbox',
+    '#title' => t('Use fieldsets for grouped components'),
+    '#default_value' => webform_variable_get('webform_fieldset_wrap'),
+    '#description' => t('Components containing multiple fields, such as checkboxes and radio buttons, will be wrapped in fieldsets. This improves the accessibility of webforms and helps them conform to web standards, but may require updates to the CSS of the active theme.'),
+  );
+
   $form = system_settings_form($form);
   $form['#theme'] = 'webform_admin_settings';
   array_unshift($form['#submit'], 'webform_admin_settings_submit');

+ 9 - 7
sites/all/modules/contrib/form/webform/includes/webform.components.inc

@@ -453,16 +453,17 @@ function webform_component_edit_form($form, $form_state, $node, $component, $clo
     '#weight' => 8,
   );
   if (webform_component_feature($component['type'], 'title_display')) {
-    if (webform_component_feature($component['type'], 'title_inline')) {
+    $inline_option = webform_component_feature($component['type'], 'title_inline') ? array('inline' => t('Inline')) : array();
+    $internal_option = webform_component_feature($component['type'], 'title_internal') ? array('internal' => t('Inside the component')) : array();
+    if ($inline_option || $internal_option) {
       $form['display']['title_display'] = array(
         '#type' => 'select',
         '#title' => t('Label display'),
         '#default_value' => !empty($component['extra']['title_display']) ? $component['extra']['title_display'] : 'before',
-        '#options' => array(
-          'before' => t('Above'),
-          'inline' => t('Inline'),
-          'none' => t('None'),
-        ),
+        '#options' => array('before' => t('Above')) +
+                      $inline_option +
+                      $internal_option +
+                      array('none' => t('None')),
         '#description' => t("Determines the placement of the component's label."),
       );
     }
@@ -634,7 +635,7 @@ function webform_component_edit_form($form, $form_state, $node, $component, $clo
  * Field name validation for the webform unique key. Must be alphanumeric.
  */
 function webform_component_edit_form_validate($form, &$form_state) {
-  $node = $form['#node'];;
+  $node = $form['#node'];
 
   if (!preg_match('/^[a-z0-9_]+$/i', $form_state['values']['form_key'])) {
     form_set_error('form_key', t('The form key %form_key is invalid. Please include only lowercase alphanumeric characters and underscores.', array('%form_key' => $form_state['values']['form_key'])));
@@ -998,6 +999,7 @@ function webform_component_feature($type, $feature) {
     'title' => TRUE,
     'title_display' => TRUE,
     'title_inline' => TRUE,
+    'title_internal' => FALSE,
     'conditional' => TRUE,
     'conditional_action_set' => FALSE,
     'spam_analysis' => FALSE,

+ 12 - 2
sites/all/modules/contrib/form/webform/includes/webform.conditionals.inc

@@ -233,11 +233,18 @@ function webform_conditionals_form_validate($form, &$form_state) {
         }
       }
       foreach ($conditional['rules'] as $rule_key => $rule) {
+        if (!is_numeric($rule_key)) {
+          continue;
+        }
+        $source_cid = isset($rule['source']['#value']) ? $rule['source']['#value'] : NULL;
         // Validate component rules, but not conditional_start/end rules.
-        if (is_numeric($rule_key) && $rule['source_type']['#value'] == 'component' && isset($targets[$rule['source']['#value']])) {
+        if ($source_cid && $rule['source_type']['#value'] == 'component' && isset($targets[$source_cid])) {
           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'])));
+            array('%target' => $components[$source_cid]['name'])));
+        }
+        if ($source_cid && $components[$source_cid]['type'] === 'date' && strtotime($rule['value']['#value']) === FALSE) {
+          form_set_error('conditionals][' . $conditional_key . '][rules][' . $rule_key . '][value', t('The conditional comparison value must be a valid date.'));
         }
       }
     }
@@ -1341,6 +1348,7 @@ function webform_conditional_load($rgid, $nid) {
  * Insert a conditional rule group into the database.
  */
 function webform_conditional_insert($conditional) {
+  $transaction = db_transaction();
   drupal_write_record('webform_conditional', $conditional);
   foreach ($conditional['rules'] as $rid => $rule) {
     $rule['nid'] = $conditional['nid'];
@@ -1360,6 +1368,7 @@ function webform_conditional_insert($conditional) {
  * Update a conditional setting in the database.
  */
 function webform_conditional_update($node, $conditional) {
+  $transaction = db_transaction();
   webform_conditional_delete($node, $conditional);
   webform_conditional_insert($conditional);
 }
@@ -1368,6 +1377,7 @@ function webform_conditional_update($node, $conditional) {
  * Delete a conditional rule group.
  */
 function webform_conditional_delete($node, $conditional) {
+  $transaction = db_transaction();
   db_delete('webform_conditional')
     ->condition('nid', $node->nid)
     ->condition('rgid', $conditional['rgid'])

+ 14 - 12
sites/all/modules/contrib/form/webform/includes/webform.report.inc

@@ -909,13 +909,9 @@ function _webform_export_tempname() {
 /**
  * Generate a Excel-readable CSV file containing all submissions for a Webform.
  *
- * Note that this function is generally no longer utilized. Instead Batch API
- * should be used to generate a file over multiple requests.
- *
- * @see webform_results_export_batch()
- *
- * @deprecated This function is schedule to be removed in webform 7.x-5.x. Use
- * the batch opertions instead.
+ * @deprecated in webform:7.x-4.8 and is removed from webform:7.x-5.0. Use
+ * webform_results_export_batch().
+ * @see https://www.drupal.org/project/webform/issues/2465291
  *
  * @return array|null
  *   The array of export info or null if the file could not be opened.
@@ -1075,6 +1071,10 @@ function webform_results_download_headers($node, array $options) {
 /**
  * Returns rows of downloadable webform data.
  *
+ * @deprecated in webform:7.x-4.8 and is removed from webform:7.x-5.0. See
+ * webform_results_download_rows_process().
+ * @see https://www.drupal.org/project/webform/issues/2465291
+ *
  * @param $node
  *   The webform node on which to generate the analysis.
  * @param array $options
@@ -1089,8 +1089,6 @@ function webform_results_download_headers($node, array $options) {
  *   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.
- *
- * @deprecated This function is scheduled to be removed in webform 7.x-5.x.
  */
 function webform_results_download_rows($node, array $options, $serial_start = 0, &$last_sid = NULL) {
   // Get all the required submissions for the download.
@@ -1882,8 +1880,9 @@ function theme_webform_results_analysis($variables) {
 /**
  * Given a set of range options, retrieve a set of SIDs for a webform node.
  *
- * @deprecated This function is scheduled to be removed int webform 7.x-5.x.
- * Use webform_download_sids_query() instead.
+ * @deprecated in webform:7.x-4.8 and is removed from webform:7.x-5.0. Use
+ * webform_download_sids_query().
+ * @see https://www.drupal.org/project/webform/issues/2465291
  */
 function webform_download_sids($nid, $range_options, $uid = NULL) {
   return webform_download_sids_query($nid, $range_options, $uid)
@@ -1922,7 +1921,7 @@ function webform_download_sids_count($nid, $range_options, $uid = NULL) {
  */
 function webform_download_sids_query($nid, array $range_options, $uid = NULL) {
   $query = db_select('webform_submissions', 'ws')
-    ->condition('nid', $nid)
+    ->condition('ws.nid', $nid)
     ->addTag('webform_download_sids');
 
   switch ($range_options['range_type']) {
@@ -1988,6 +1987,9 @@ function webform_download_sids_query($nid, array $range_options, $uid = NULL) {
   if (isset($range_options['batch_number']) && !empty($range_options['batch_size'])) {
     $query->range($range_options['batch_number'] * $range_options['batch_size'], $range_options['batch_size']);
   }
+
+  drupal_alter('webform_download_sids_query', $query);
+
   return $query;
 }
 

+ 32 - 11
sites/all/modules/contrib/form/webform/includes/webform.submissions.inc

@@ -95,6 +95,7 @@ function webform_submission_create($node, $account, array $form_state, $is_previ
  *   The existing submission SID.
  */
 function webform_submission_update($node, $submission) {
+  $transaction = db_transaction();
   // Allow other modules to modify the submission before saving.
   foreach (module_implements('webform_submission_presave') as $module) {
     $function = $module . '_webform_submission_presave';
@@ -145,6 +146,7 @@ function webform_submission_update($node, $submission) {
  *   The new submission SID.
  */
 function webform_submission_insert($node, $submission) {
+  $transaction = db_transaction();
   // The submission ID may already be set if being called as an update.
   if (!isset($submission->sid) && (!isset($submission->is_new) || $submission->is_new == FALSE)) {
     // Allow other modules to modify the submission before saving.
@@ -191,6 +193,7 @@ function webform_submission_insert($node, $submission) {
  *   The webform submission object to be deleted from the database.
  */
 function webform_submission_delete($node, $submission) {
+  $transaction = db_transaction();
   // Iterate through all components and let each do cleanup if necessary.
   foreach ($node->webform['components'] as $cid => $component) {
     if (isset($submission->data[$cid])) {
@@ -228,17 +231,22 @@ 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.
+ * @param bool $send_disabled
+ *   (optional) When TRUE, send all emails, even those that are disabled.
  *
  * @return int
  *   Number of mail sent.
  */
-function webform_submission_send_mail($node, $submission, $emails = NULL) {
+function webform_submission_send_mail($node, $submission, $emails = NULL, $send_disabled = FALSE) {
   // Get the list of e-mails we'll be sending.
   $emails = isset($emails) ? $emails : $node->webform['emails'];
 
   // Create a themed message for mailing.
   $send_count = 0;
   foreach ($emails as $eid => $email) {
+    if (!$send_disabled && !$email['status']) {
+      continue;
+    }
     $mail = _webform_submission_prepare_mail($node, $submission, $email);
     if (!$mail) {
       continue;
@@ -277,11 +285,6 @@ function webform_submission_send_mail($node, $submission, $emails = NULL) {
 function _webform_submission_prepare_mail($node, $submission, &$email) {
   global $user;
 
-  // Don't process disabled emails.
-  if (!$email['status']) {
-    return;
-  }
-
   // Set the HTML property based on availablity of MIME Mail.
   $email['html'] = ($email['html'] && webform_variable_get('webform_email_html_capable'));
 
@@ -328,6 +331,7 @@ function _webform_submission_prepare_mail($node, $submission, &$email) {
                         '!name' => strlen($email_parts['name']) ? $email_parts['name'] : $email_parts['address'],
                         '!site_name' => $default_from_name,
                       ));
+      $from_name = str_replace('"', "'", $from_name);
       $from_name = implode(' ', array_map('mime_header_encode', explode(' ', $from_name)));
       $email['from'] = '"' . $from_name . '" <' . $email['from'] . '>';
     }
@@ -507,10 +511,10 @@ function webform_submission_page($node, $submission, $format) {
 
   // Determine the mode in which we're displaying this submission.
   $mode = ($format != 'form') ? 'display' : 'form';
-  if (strpos(request_uri(), 'print/') !== FALSE) {
+  if (strpos(request_path(), 'print/') !== FALSE) {
     $mode = 'print';
   }
-  if (strpos(request_uri(), 'printpdf/') !== FALSE) {
+  if (strpos(request_path(), 'printpdf/') !== FALSE) {
     $mode = 'pdf';
   }
 
@@ -614,7 +618,7 @@ function webform_submission_resend_submit($form, &$form_state) {
       $emails[] = $form['#node']->webform['emails'][$eid];
     }
   }
-  $sent_count = webform_submission_send_mail($node, $submission, $emails);
+  $sent_count = webform_submission_send_mail($node, $submission, $emails, TRUE);
   if ($sent_count) {
     drupal_set_message(format_plural($sent_count,
       'Successfully re-sent submission #@sid to 1 recipient.',
@@ -663,7 +667,24 @@ function theme_webform_submission_resend(array $variables) {
 }
 
 /**
- * Print a Webform submission for display on a page or in an e-mail.
+ * Prepare a Webform submission for display on a page or in an e-mail.
+ *
+ * @param object $node
+ *   The node object.
+ * @param object $submission
+ *   The submission object.
+ * @param array $email
+ *   The email configuration array.
+ * @param string $format
+ *   The format the form should be displayed as. May be one of the following:
+ *   - form: Show as an editable form.
+ *   - html: Show as HTML results.
+ *   - text: Show as plain text.
+ * @param array $excluded_components
+ *   An array of components to exclude as cid.
+ *
+ * @return array
+ *   A renderable array of the submission.
  */
 function webform_submission_render($node, $submission, $email, $format, $excluded_components = NULL) {
   $component_tree = array();
@@ -948,7 +969,7 @@ function webform_get_submission_count($nid, $uid = NULL, $is_draft = 0) {
     if ($uid === 0) {
       $submissions = isset($_SESSION['webform_submission']) ? $_SESSION['webform_submission'] : NULL;
       if ($submissions) {
-        $query->condition('ws.sid', $submissions, 'IN');
+        $query->condition('ws.sid', array_keys($submissions), 'IN');
       }
       else {
         // Intentionally never match anything if the anonymous user has no

+ 23 - 3
sites/all/modules/contrib/form/webform/js/webform.js

@@ -84,8 +84,25 @@
 
       // Prevent the calendar button from submitting the form.
       $calendar.click(function (event) {
-        $(this).focus();
-        event.preventDefault();
+        // This event is triggered also when pressing enter when the focus is on
+        // previous webform components, but we only want to do something when
+        // we are on the calendar component. By checking the event client x/y
+        // position we known if it was the user clicking. For keyboard navigators
+        // simply the focus handles the date picker so we don't have to do
+        // anything special for them.
+        if (event.clientX !== 0 && event.clientY !== 0) {
+          // Focus is only necessary for Safari. But it has no impact on other
+          // browsers.
+          $(this).focus();
+          event.preventDefault();
+        }
+      });
+
+      // Clear date on backspace or delete.
+      $calendar.keyup(function (e) {
+        if (e.keyCode == 8 || e.keyCode == 46) {
+          $.datepicker._clearDate(this);
+        }
       });
     });
   };
@@ -112,6 +129,9 @@
    */
   Drupal.webform.conditionalCheck = function (e) {
     var $triggerElement = $(e.target).closest('.webform-component');
+    if (!$triggerElement.length) {
+      return;
+    }
     var $form = $triggerElement.closest('form');
     var triggerElementKey = $triggerElement.attr('class').match(/webform-component--[^ ]+/)[0];
     var settings = e.data.settings;
@@ -238,7 +258,7 @@
           case 'require':
             var $requiredSpan = $target.find('.form-required, .form-optional').first();
             if (actionResult != $requiredSpan.hasClass('form-required')) {
-              var $targetInputElements = $target.find("input:text,textarea,input[type='email'],select,input:radio,input:file");
+              var $targetInputElements = $target.find("input:text,textarea,input[type='email'],select,input:radio,input:checkbox,input:file");
               // Rather than hide the required tag, remove it so that other
               // jQuery can respond via Drupal behaviors.
               Drupal.detachBehaviors($requiredSpan);

+ 1 - 1
sites/all/modules/contrib/form/webform/templates/webform-mail.tpl.php

@@ -29,7 +29,7 @@
 <?php print ($email['html'] ? '<p>' : '') . t('Submitted by anonymous user: [submission:ip-address]') . ($email['html'] ? '</p>' : ''); ?>
 <?php endif; ?>
 
-<?php print ($email['html'] ? '<p>' : '') . t('Submitted values are') . ':' . ($email['html'] ? '</p>' : ''); ?>
+<?php print ($email['html'] ? '<p>' : '') . t('Submitted values are:') . ($email['html'] ? '</p>' : ''); ?>
 
 [submission:values]
 

+ 8 - 0
sites/all/modules/contrib/form/webform/tests/WebformComponentsTestCase.test

@@ -189,6 +189,14 @@ class WebformComponentsTestCase extends WebformTestCase {
       $for = $this->xpath("//*[@id=':id']", array(':id' => $label['for']));
       $this->assertTrue($for, 'Label with @for "' . $label['for'] . '" points to an element.');
     }
+
+    // Test grid headers.
+    $grid_headers = $this->xpath('//th[@class="webform-grid-question"]');
+    $this->assertIdentical(count($grid_headers), 3, 'There are three table headers with class "webform-grid-question".');
+    $grid_headers = $this->xpath('//th[@class="webform-grid-question"]/text()');
+    $this->assertIdentical(count($grid_headers), 2, 'There are two non-empty table headers with class "webform-grid-question".');
+    $this->assertIdentical((string) $grid_headers[0], 'Grid Keyed', 'The value of the left non-empty header is "Grid Keyed".');
+    $this->assertIdentical((string) $grid_headers[1], 'Grid Keyed', 'The value of the right non-empty header is the same as the left.');
   }
 
 }

+ 36 - 1
sites/all/modules/contrib/form/webform/tests/WebformSubmissionTestCase.test

@@ -23,7 +23,42 @@ class WebformSubmissionTestCase extends WebformTestCase {
     $this->drupalLogin($this->webform_users['admin']);
     $this->webformReset();
     $this->webformSubmissionExecute('sample');
+
+    $loggedInUser = $this->loggedInUser;
+
     $this->drupalLogout();
+
+    // Test webform_get_submission_count().
+    $this->webformSubmissionExecute('sample');
+
+    $count = webform_get_submission_count($this->webformForm()->nid);
+    $this->assertIdentical((int) $count, 2, 'webform_get_submission_count() counts 2 total submissions.');
+
+    $count = webform_get_submission_count($this->webformForm()->nid, $loggedInUser->uid);
+    $this->assertIdentical((int) $count, 1, 'webform_get_submission_count() counts 1 submission from loggedInUser.');
+
+    // Counting the anonymous submission doesn't work because
+    // $_SESSION['webform_submission'] is not populated in testing.
+
+    // Test _webform_submission_prepare_mail().
+    $node = node_load($this->webformForm()->nid);
+    $submission = webform_get_submissions($node->nid);
+    $submission = array_pop($submission);
+    $email = array(
+      'status' => TRUE,
+      'html' => FALSE,
+      'template' => 'default',
+      'from_address' => 'Test From',
+      'from_name' => 'from@example.com',
+      'subject' => 'Test Subject',
+      'email' => 'to@example.com',
+    );
+    variable_set('webform_email_replyto', TRUE);
+    variable_set('webform_email_address_format', 'long');
+    variable_set('webform_default_from_name', 'Default "From" Name');
+    variable_set('webform_default_from_address', 'default-from@example.com');
+    $prepared_email = _webform_submission_prepare_mail($node, $submission, $email);
+    $this->assertIdentical($prepared_email['mail_params']['email']['from'], '"from@example.com via Default \'From\' Name" <default-from@example.com>', 'From address is correctly set in _webform_submission_prepare_mail().');
   }
 
   /**
@@ -156,7 +191,7 @@ class WebformSubmissionTestCase extends WebformTestCase {
       $actual_value = $actual_submission->data[$cid];
       $result = $this->assertEqual($stable_value, $actual_value, t('Component @form_key data integrity check when using @type values.', array('@form_key' => $component['form_key'], '@type' => $value_type)), t('Webform'));
       if (!$result || $result === 'fail') {
-        $this->fail(t('Expected !expected', array('!expected' => print_r($stable_value, TRUE))) . "\n\n" . t('Recieved !recieved', array('!recieved' => print_r($actual_value, TRUE))), t('Webform'));
+        $this->fail(t("Expected !expected\n\nRecieved !recieved", array('!expected' => print_r($stable_value, TRUE), '!recieved' => print_r($actual_value, TRUE))), t('Webform'));
       }
     }
   }

+ 7 - 15
sites/all/modules/contrib/form/webform/tests/WebformTestCase.test

@@ -12,13 +12,8 @@ class WebformTestCase extends DrupalWebTestCase {
    * {@inheritdoc}
    */
   public function setUp($added_modules = array()) {
-    // Enable Webform and Token module if available.
-    if (module_exists('token')) {
-      $modules = array('webform', 'token');
-    }
-    else {
-      $modules = array('webform');
-    }
+    // Enable Webform and Token modules.
+    $modules = array('webform', 'token');
     parent::setUp(array_merge($modules, $added_modules));
 
     // Create a profile field to test [user:?] tokens.
@@ -190,9 +185,10 @@ class WebformTestCase extends DrupalWebTestCase {
           'value' => '',
           'extra' => array(
             // Left side.
-            'questions' => "one|What's your option?\ntwo|Agåin?\nthree|One more time!",
+            'questions' => "one|What's your option?\ntwo|Agåin?|Again, right text\nthree|One more time!",
             // Top.
             'options' => "one|Option one\ntwo|Option 2\nthree| Three is me",
+            'title_display' => 'internal',
           ),
           'required' => '0',
           'pid' => '0',
@@ -724,11 +720,9 @@ class WebformTestCase extends DrupalWebTestCase {
           'pid' => '0',
           'weight' => '-15',
         ),
-        // Manually hard-code the input if token is not available.
-        // @todo: Update after http://drupal.org/node/1347790 is finished.
-        'sample values' => module_exists('token') ? NULL : 'bar',
+        'sample values' => NULL,
         'database values' => array('bar'),
-        'database default values' => module_exists('token') ? array('bar') : array(''),
+        'database default values' => array('bar'),
       ),
       'textfield_profile' => array(
         'component' => array(
@@ -745,9 +739,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.
-        'database default values' => module_exists('token') ? array($this->webform_users['admin']->gender[LANGUAGE_NONE][0]['value']) : array(''),
+        'database default values' => array($this->webform_users['admin']->gender[LANGUAGE_NONE][0]['value']),
       ),
 
       // Test time components.

+ 80 - 0
sites/all/modules/contrib/form/webform/tests/WebformUnitTestCase.test

@@ -0,0 +1,80 @@
+<?php
+
+/**
+ * Webform module unit tests.
+ */
+class WebformUnitTestCase extends DrupalUnitTestCase {
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function getInfo() {
+    return array(
+      'name' => t('Webform unit tests'),
+      'description' => t('Unit tests for Webform functions.'),
+      'group' => t('Webform'),
+    );
+  }
+
+  /**
+   * The tests.
+   */
+  public function test() {
+    require_once __DIR__ . '/../webform.module';
+
+    $test = webform_format_email_address('test@example.com', 'John Smith');
+    $sample = '"John Smith" <test@example.com>';
+    $this->assertIdentical($test, $sample, 'webform_format_email_address() returns string for single name and email address.');
+
+    $test = webform_format_email_address('default', 'default');
+    $sample = '"' . webform_variable_get('webform_default_from_name') . '" <' . webform_variable_get('webform_default_from_address') . '>';
+    $this->assertIdentical($test, $sample, 'webform_format_email_address() handles defaults.');
+
+    $test = webform_format_email_address('test@example.com', NULL);
+    $sample = 'test@example.com';
+    $this->assertIdentical($test, $sample, 'webform_format_email_address() handles NULL name.');
+
+    $test = webform_format_email_address('test@example.com', 'John Smith', NULL, NULL, TRUE, FALSE);
+    $sample = ['"John Smith" <test@example.com>'];
+    $this->assertIdentical($test, $sample, 'webform_format_email_address() returns array for single name and email address.');
+
+    $test = webform_format_email_address(['test1@example.com', 'test2@example.com'], 'John Smith');
+    $sample = '"John Smith" <test1@example.com>';
+    $this->assertIdentical($test, $sample, 'webform_format_email_address() returns single string for multiple email addresses by default.');
+
+    $test = webform_format_email_address(['test1@example.com', 'test2@example.com'], ['John One', 'John Two'], NULL, NULL, TRUE, FALSE);
+    $sample = ['"John One" <test1@example.com>', '"John Two" <test2@example.com>'];
+    $this->assertIdentical($test, $sample, 'webform_format_email_address() returns array for multiple email addresses when $single is FALSE.');
+
+    $test = webform_format_email_address(['test1@example.com', 'test2@example.com'], 'John One', NULL, NULL, TRUE, FALSE);
+    $sample = ['"John One" <test1@example.com>', '"John One" <test2@example.com>'];
+    $this->assertIdentical($test, $sample, 'webform_format_email_address() repeats first name when more emails than names provided.');
+
+    $test = webform_format_email_address('test1@example.com, test2@example.com', 'John One', NULL, NULL, TRUE, FALSE);
+    $sample = ['"John One" <test1@example.com>', '"John One" <test2@example.com>'];
+    $this->assertIdentical($test, $sample, 'webform_format_email_address() accepts multiple emails as comma-separated string.');
+
+    $node = (object) [
+      'webform' => [
+        'components' => [
+          1 => ['name' => 'Email component', 'type' => 'textfield'],
+          2 => ['name' => 'Name component', 'type' => 'textfield'],
+        ],
+      ],
+    ];
+    $test = webform_format_email_address(1, 2, $node);
+    $sample = '"Value of Name component" <Value of "Email component">';
+    $this->assertIdentical($test, $sample, 'webform_format_email_address() takes name and email from component names.');
+
+    $submission = (object) [
+      'data' => [
+        1 => ['test@example.com'],
+        2 => ['John Smith'],
+      ],
+    ];
+    $test = webform_format_email_address(1, 2, $node, $submission);
+    $sample = '"John Smith" <test@example.com>';
+    $this->assertIdentical($test, $sample, 'webform_format_email_address() takes name and email from submission values.');
+  }
+
+}

+ 1 - 1
sites/all/modules/contrib/form/webform/views/webform_handler_relationship_submission_data.inc

@@ -59,7 +59,7 @@ class webform_handler_relationship_submission_data extends views_handler_relatio
     parent::options_submit($form, $form_state);
     _webform_views_options_submit($form, $form_state);
     $options =& $form_state['values']['options'];
-    $options['webform_form_key'] = $options['webform_join_by_form_key'] == 'form_key' && ($node = node_load($options['webform_nid']))
+    $options['webform_form_key'] = $options['webform_join_by'] == 'form_key' && ($node = node_load($options['webform_nid']))
                                         ? $node->webform['components'][$options['webform_cid']]['form_key']
                                         : NULL;
     // Drop PHP reference.

+ 23 - 3
sites/all/modules/contrib/form/webform/webform.api.php

@@ -238,7 +238,7 @@ function hook_webform_submission_actions($node, $submission) {
  * presented when the webform is displayed to that user. To allow multiple
  * drafts, implement this alter function to set the $sid to NULL, or use your
  * application's business logic to determine whether a new draft or which of
- * he pre-existing drafts should be presented.
+ * the pre-existing drafts should be presented.
  *
  * @param int $sid
  *   The id of the most recent submission to be presented for editing. Change
@@ -820,6 +820,26 @@ function hook_webform_results_download_submission_information_data($token, $subm
   }
 }
 
+/**
+ * Alter the query that will produce the list of submission IDs to be
+ * downloaded.
+ *
+ * @param object $query
+ *   The query object that is being built up to provide the list of submission
+ *   IDs.
+ *
+ * @see webform_download_sids_query()
+ */
+function hook_webform_download_sids_query_alter(&$query) {
+  global $user;
+
+  // check if component value matches a node ID and author of that node.
+  $query->join('webform_submitted_data', 'wsd', 'ws.sid = wsd.sid');
+  $query->condition('wsd.cid', 2);
+  $query->join('node', 'n', 'wsd.data = n.nid');
+  $query->condition('n.uid', $user->uid);
+}
+
 /**
  * @}
  */
@@ -876,9 +896,9 @@ function _webform_defaults_component() {
  *   The form state array.
  *
  * @return array
- *   An array of form items to be displayed on the edit component page
+ *   Return $form with whatever changes are desired.
  */
-function _webform_edit_component(array $component, array &$form, array &$form_state) {
+function _webform_edit_component(array $component, array $form, array $form_state) {
   // Disabling the description if not wanted.
   $form['description']['#access'] = FALSE;
 

+ 4 - 3
sites/all/modules/contrib/form/webform/webform.info

@@ -38,9 +38,10 @@ files[] = tests/WebformGeneralTestCase.test
 files[] = tests/WebformPermissionsTestCase.test
 files[] = tests/WebformSubmissionTestCase.test
 files[] = tests/WebformTestCase.test
+files[] = tests/WebformUnitTestCase.test
 
-; Information added by Drupal.org packaging script on 2019-01-07
-version = "7.x-4.19"
+; Information added by Drupal.org packaging script on 2020-02-14
+version = "7.x-4.22"
 core = "7.x"
 project = "webform"
-datestamp = "1546876989"
+datestamp = "1581709525"

+ 36 - 19
sites/all/modules/contrib/form/webform/webform.install

@@ -645,7 +645,8 @@ function webform_schema() {
     'indexes' => array(
       'nid' => array('nid'),
       'sid_nid' => array('sid', 'nid'),
-      // For all but MS SQL Server databases, 64-character index is created on the data column after the schema is installed.
+      // For all but MS SQL Server databases, 64-character index is created on
+      // the data column after the schema is installed.
     ),
   );
 
@@ -796,6 +797,7 @@ function webform_uninstall() {
   variable_del('webform_default_format');
   variable_del('webform_format_override');
   variable_del('webform_email_select_max');
+  variable_del('webform_fieldset_wrap');
   variable_del('webform_node_types_primary');
   variable_del('webform_date_type');
   variable_del('webform_export_format');
@@ -1972,8 +1974,8 @@ function webform_update_7416() {
   }
 
   // Begin a transaction for updating the serial numbers. The transaction will
-  // commit when $txn is unset or goesout-of-scope.
-  $txn = db_transaction();
+  // commit when $transaction is unset or goes out-of-scope.
+  $transaction = db_transaction();
 
   // Delete stray entries from the Webform tables before adding serial numbers.
   db_query("DELETE FROM {webform_submissions} WHERE nid NOT IN (SELECT nid FROM {webform})");
@@ -2009,7 +2011,7 @@ function webform_update_7416() {
       ->execute();
   }
   // Commit the transaction.
-  unset($txn);
+  unset($transaction);
 
   // Now that every submission has a serial number, make serial numbers required.
   $spec['not null'] = TRUE;
@@ -2060,12 +2062,14 @@ function webform_update_7417() {
  * causes no problem, this update removes the schema module's warning.
  */
 function webform_update_7418() {
-  // While there should never be any NULL values in the extra field, change them to '' to be safe.
+  // While there should never be any NULL values in the extra field, change them
+  // to '' to be safe.
   db_update('webform_emails')
     ->fields(array('extra' => ''))
     ->isNull('extra')
     ->execute();
-  // Pass a complete field specification to db_change_field for cross-database compatiblity.
+  // Pass a complete field specification to db_change_field for cross-database
+  // compatiblity.
   $spec = array(
     'description' => 'A serialized array of additional options for the e-mail configuration, including excluded components and value mapping for the TO and FROM addresses for select lists.',
     'type' => 'text',
@@ -2118,7 +2122,10 @@ function webform_update_7422() {
 }
 
 /**
- * Convert conditionals to be able to support multiple actions per conditional. Backup your database before proceeding. WARNING: Sites with many, many conditionals should execute this update via drush to avoid a PHP timeout.
+ * Convert conditionals to be able to support multiple actions per conditional.
+ *
+ * Backup your database before proceeding. WARNING: Sites with many, many
+ * conditionals should execute this update via drush to avoid a PHP timeout.
  */
 function webform_update_7423() {
   // Create webform_condtional_actions table.
@@ -2183,9 +2190,11 @@ function webform_update_7423() {
     db_create_table('webform_conditional_actions', $schema['webform_conditional_actions']);
   }
 
-  // In a site with many, many conditionals, the db_insert may timeout. Start a transaction to ensure atomic action.
-  $tx = db_transaction();
-  // Copy target information from existing webform_conditional table to new webfrom_condtional_actions table.
+  // In a site with many, many conditionals, the db_insert may timeout. Start a
+  // transaction to ensure atomic action.
+  $transaction = db_transaction();
+  // Copy target information from existing webform_conditional table to new
+  // webfrom_condtional_actions table.
   $select = db_select('webform_conditional', 'c')
     ->fields('c', array('nid', 'rgid', 'action', 'target_type', 'target'))
     ->orderBy('nid')->orderBy('rgid');
@@ -2195,7 +2204,7 @@ function webform_update_7423() {
     ->execute();
 
   // Commit the insert.
-  unset($tx);
+  unset($transaction);
 
   // Remove unneeded columns from webform_conditional.
   foreach (array('action', 'target_type', 'target') as $fieldname) {
@@ -2204,7 +2213,8 @@ function webform_update_7423() {
     }
   }
 
-  // Rebuild the registry because this point release contains a new class: WebformConditionals.
+  // Rebuild the registry because this point release contains a new class:
+  // WebformConditionals.
   registry_rebuild();
 
   return t('Webform database tables were successfully adjusted to allow more than one action for each conditional.');
@@ -2254,7 +2264,9 @@ function webform_update_7426() {
 }
 
 /**
- * Add database columns for submission completed and modified timestamps. Sites with many submissions may wish to use drush to execute this update.
+ * Add database columns for submission completed and modified timestamps.
+ *
+ * Sites with many submissions may wish to use drush to execute this update.
  */
 function webform_update_7427() {
   // Create new timestamp columns.
@@ -2278,23 +2290,24 @@ function webform_update_7427() {
     }
   }
 
-  // In a site with many submissions, the db_update may timeout. Start a transaction to ensure atomic action.
-  $tx = db_transaction();
+  // In a site with many submissions, the db_update may timeout. Start a
+  // transaction to ensure atomic action.
+  $transaction = db_transaction();
   // Copy submitted to completed for non-draft submissions.
   db_update('webform_submissions')
     ->expression('completed', 'submitted')
     ->condition('is_draft', 0)
     ->execute();
   // Commit the update.
-  unset($tx);
+  unset($transaction);
 
   // Start another transaction.
-  $tx = db_transaction();
+  $transaction = db_transaction();
   db_update('webform_submissions')
     ->expression('modified', 'submitted')
     ->execute();
   // Commit the update.
-  unset($tx);
+  unset($transaction);
 
   // Clear the views cache since to see the completed and modified views fields.
   cache_clear_all('*', 'cache_views', TRUE);
@@ -2322,7 +2335,11 @@ function webform_update_7428() {
 }
 
 /**
- * Add a column to the submission table to store the page on which to resume a draft. Sites with many, many submissions may wish to execute this update with 'drush updatedb'.
+ * Add column highest_valid_page to webform_submissions table.
+ *
+ * Add a column to the submission table to store the page on which to resume a
+ * draft. Sites with many, many submissions may wish to execute this update with
+ * 'drush updatedb'.
  */
 function webform_update_7429() {
   // Add highest_valid_page column to webform_submissions.

+ 97 - 41
sites/all/modules/contrib/form/webform/webform.module

@@ -1245,6 +1245,7 @@ function webform_webform_component_info() {
         'conditional' => FALSE,
         'default_value' => FALSE,
         'attachment' => TRUE,
+        'file_usage' => TRUE,
       ),
       'file' => 'components/file.inc',
     );
@@ -1331,7 +1332,7 @@ function webform_webform_submission_presave($node, &$submission) {
   $renameable = array();
 
   foreach ($node->webform['components'] as $cid => $component) {
-    if ($component['type'] == 'file') {
+    if (webform_component_feature($component['type'], 'file_usage')) {
       $has_file_components = TRUE;
       if (!empty($submission->data[$cid])) {
         foreach ($submission->data[$cid] as $key => $value) {
@@ -1354,7 +1355,7 @@ function webform_webform_submission_presave($node, &$submission) {
       $old_submission = webform_get_submission($node->nid, $submission->sid);
 
       foreach ($node->webform['components'] as $cid => $component) {
-        if ($component['type'] == 'file') {
+        if (webform_component_feature($component['type'], 'file_usage')) {
           if (!empty($old_submission->data[$cid])) {
             $old_fids = array_merge($old_fids, $old_submission->data[$cid]);
           }
@@ -2188,7 +2189,7 @@ function theme_webform_view_messages(array $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 <a href="!url">Log out</a> to submit it.', array('!url' => url('/user/logout', array('query' => array('destination' => request_uri())))));
+    $message = t('This form is confidential. You must <a href="!url">Log out</a> to submit it.', array('!url' => url('user/logout', array('query' => drupal_get_destination()))));
   }
   // If open and not allowed to submit the form, give an explanation.
   elseif (array_search(TRUE, $allowed_roles) === FALSE && $user->uid != 1) {
@@ -3271,7 +3272,8 @@ function webform_client_form_pages($form, &$form_state) {
   // Merge any stored submission data for multistep forms.
   $original_values = is_array($form_state['values']['submitted']) ? $form_state['values']['submitted'] : array();
   if (isset($form_state['storage']['submitted'])) {
-    // Array + operator keeps all elements of left operand and discards any duplicate elements in right operand.
+    // Array + operator keeps all elements of left operand and discards any
+    // duplicate elements in right operand.
     $original_values += $form_state['storage']['submitted'];
   }
 
@@ -3754,6 +3756,23 @@ function theme_webform_element($variables) {
     $variables['element']['#id'] = NULL;
   }
 
+  // Determine whether or not this element has form control children. If so and
+  // if webform_fieldset_wrap is TRUE, wrap them in a fieldset and use legend
+  // instead of label.
+  $has_element_children = FALSE;
+  if (webform_variable_get('webform_fieldset_wrap')) {
+    foreach (array_keys($element) as $key) {
+      if (substr($key, 0, 1) !== '#') {
+        $has_element_children = TRUE;
+        break;
+      }
+    }
+  }
+
+  if ($has_element_children) {
+    $output .= '<fieldset class="fieldset-invisible">';
+  }
+
   switch ($element['#title_display']) {
     case 'inline':
       $output .= $description[$above];
@@ -3761,7 +3780,18 @@ function theme_webform_element($variables) {
     case 'before':
     case 'invisible':
     case 'after':
-      $title = ' ' . theme('form_element_label', $variables);
+      if ($has_element_children) {
+        $title = '<legend>' . $element['#title'];
+
+        if ($element['#required']) {
+          $title .= ' ' . theme('form_required_marker', $variables);
+        }
+
+        $title .= '</legend>';
+      }
+      else {
+        $title = ' ' . theme('form_element_label', $variables);
+      }
       break;
   }
 
@@ -3788,6 +3818,11 @@ function theme_webform_element($variables) {
   $output .= "\n";
 
   $output .= $description[!$above];
+
+  if ($has_element_children) {
+    $output .= '</fieldset>';
+  }
+
   $output .= "</div>\n";
 
   return $output;
@@ -3954,30 +3989,37 @@ function theme_webform_mail_headers(array $variables) {
  * Check if current user has a draft of this webform, and return the sid.
  */
 function _webform_fetch_draft_sid($nid, $uid) {
+  $q = db_select('webform_submissions')
+    ->fields('webform_submissions', array('sid'))
+    ->condition('nid', $nid)
+    ->condition('uid', $uid)
+    ->condition('is_draft', 1)
+    ->orderBy('submitted', 'DESC');
+
   // Detect whether a webform draft is being edited. If so, that is the one that
   // 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.
-    $sid = $_POST['details']['sid'];
+  $is_webform = isset($_POST['form_id']) && stripos($_POST['form_id'], 'webform_client_form_') === 0;
+  $sid_provided = !empty($_POST['details']['sid']) && is_string($_POST['details']['sid']);
+  $not_finished = empty($_POST['details']['finished']);
+  if ($is_webform && $sid_provided && $not_finished) {
+    // Validate that the sid from $_POST belongs to the current user.
+    $q->condition('sid', $_POST['details']['sid']);
+    $existing_sid = TRUE;
   }
-  else {
-    $sid = db_select('webform_submissions')
-      ->fields('webform_submissions', array('sid'))
-      ->condition('nid', $nid)
-      ->condition('uid', $uid)
-      ->condition('is_draft', 1)
-      ->orderBy('submitted', 'DESC')
-      ->execute()
-      ->fetchField();
 
-    if ($sid) {
-      $context = array(
-        'nid' => $nid,
-        'uid' => $uid,
-      );
-      drupal_alter('webform_draft', $sid, $context);
-    }
+  // Retrieve exisiting draft sid.
+  $sid = $q
+    ->execute()
+    ->fetchField();
+
+  // Allow modules to alter the initial choice of sid when there might be more
+  // than one.
+  if ($sid && empty($existing_sid)) {
+    $context = array(
+      'nid' => $nid,
+      'uid' => $uid,
+    );
+    drupal_alter('webform_draft', $sid, $context);
   }
   return $sid;
 }
@@ -3997,9 +4039,11 @@ function webform_get_conditional_sorter($node) {
 }
 
 /**
- * This function is deprecated! Use webform_replace_tokens() instead.
+ * Wrapper for webform_replace_tokens().
  *
- * @deprecated
+ * @deprecated in webform:7.x-4.0 and is removed from webform:7.x-5.0. Use
+ * webform_replace_tokens().
+ * @see https://www.drupal.org/project/webform/issues/2038199
  */
 function _webform_filter_values($string, $node = NULL, $submission = NULL, $email = NULL, $strict = TRUE) {
   $output = webform_replace_tokens($string, $node, $submission, $email, $strict);
@@ -4122,8 +4166,9 @@ function webform_replace_url_tokens($redirect_url, $node = NULL, $submission = N
     }
   }
   $parsed_redirect_url['fragment'] = webform_replace_tokens($parsed_redirect_url['fragment'], $node, $submission);
-  // Determine whether the path is internal or external. Paths which contain the site's
-  // base url are still considered internal. #webform_external is private to webform.
+  // Determine whether the path is internal or external. Paths which contain the
+  // site's base url are still considered internal. #webform_external is private
+  // to webform.
   $parsed_redirect_url['#webform_external'] = url_is_external($parsed_redirect_url['path']);
   foreach (array(NULL, TRUE, FALSE) as $https) {
     if (stripos($parsed_redirect_url['path'], url('', array('absolute' => TRUE, 'https' => $https))) === 0) {
@@ -4142,9 +4187,11 @@ function webform_filter_descriptions($string, $node = NULL, $submission = NULL)
 }
 
 /**
- * Deprecated! Use webform_filter_descriptions() instead.
+ * Duplicates webform_filter_descriptions().
  *
- * @deprecated
+ * @deprecated in webform:7.x-4.0 and is removed from webform:7.x-5.0. Use
+ * webform_filter_descriptions().
+ * @see https://www.drupal.org/project/webform/issues/2038199
  */
 function _webform_filter_descriptions($string, $node = NULL, $submission = NULL) {
   return webform_filter_descriptions($string, $node, $submission);
@@ -4160,9 +4207,11 @@ function webform_filter_xss($string) {
 }
 
 /**
- * Deprecated! Use webform_filter_xss() instead!
+ * Duplicates webform_filter_xss().
  *
- * @deprecated
+ * @deprecated in webform:7.x-4.0 and is removed from webform:7.x-5.0. Use
+ * webform_filter_xss().
+ * @see https://www.drupal.org/project/webform/issues/2038199
  */
 function _webform_filter_xss($string) {
   return webform_filter_xss($string);
@@ -4414,6 +4463,10 @@ function webform_variable_get($variable) {
     case 'webform_disabled_components':
       $result = variable_get('webform_disabled_components', array());
       break;
+
+    case 'webform_fieldset_wrap':
+      $result = variable_get('webform_fieldset_wrap', FALSE);
+      break;
   }
   return $result;
 }
@@ -4584,6 +4637,7 @@ function webform_format_email_address($address, $name, $node = NULL, $submission
   }
 
   foreach ($address as $key => $individual_address) {
+    $individual_address = trim($individual_address);
     $individual_address = webform_replace_tokens($individual_address, $node, $submission);
     $email_parts = webform_parse_email_address($individual_address);
     if ($format == 'long' && !empty($name[$key]) && !strlen($email_parts['name'])) {
@@ -4967,7 +5021,8 @@ function webform_set_breadcrumb($node, $submission = NULL) {
   if ($submission) {
     $breadcrumb = menu_get_active_breadcrumb();
 
-    // Append the node title (or its menu name), in case it isn't in the path already.
+    // Append the node title (or its menu name), in case it isn't in the path
+    // already.
     $active_trail = menu_get_active_trail();
     $last_active = end($active_trail);
     $breadcrumb[] = $last_active['href'] === $node_path && !empty($last_active['in_active_trail'])
@@ -5120,14 +5175,14 @@ function webform_date_format($type = NULL, array $exclude = array()) {
 
     // Date/Time formatting characters
     // WHAT           REQUIRED (at least 1) OPTIONAL (allowed but insufficient)
-    // ---------------------------------------------------------------------------
+    // -------------------------------------------------------------------------
     // Day-of-week                          DlNw
     // Day            dj                    Stz
     // Month          FmMn
     // Year           oYy                   L
     //
     //                NOT ALLOWED
-    // --------------------------------------------------------------------------
+    // -------------------------------------------------------------------------
     // Time           aABgGhHisueIOPTZ
     // Special        /.,-: <space>
     //
@@ -5303,7 +5358,7 @@ function _webform_submission_serial_next_value($nid) {
   // a transaction. The return value of db_transaction() must be assigned or the
   // transaction will commit immediately. The transaction will commit when $txn
   // goes out-of-scope.
-  $txn = db_transaction();
+  $transaction = db_transaction();
 
   // Get the next_serial value.
   $next_serial = db_select('webform', 'w')
@@ -5392,8 +5447,9 @@ function webform_input_vars_check(array &$form, array $form_state, $detect_key,
     $form['#input_var_waring_parent'] = $parent_key;
   }
   if (!empty($form_state['input']) && array_key_exists($detect_key, $form_state['input']) && !array_key_exists('form_id', $form_state['input'])) {
-    // A form was submitted with POST, but the form_id was missing. The most likely cause of this
-    // is that the POST was truncated because PHP exceeded its max_input_vars limit.
+    // A form was submitted with POST, but the form_id was missing. The most
+    // likely cause of this 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'),
@@ -5420,8 +5476,8 @@ function webform_pre_render_input_vars($element) {
   // Determine the limit on input vars for this server configuration.
   $limit = ini_get('max_input_vars');
   if ($limit) {
-    // Estimate the number of input vars needed to see if the PHP limit has been exceeded.
-    // Additional input_vars: op.
+    // Estimate the number of input vars needed to see if the PHP limit has been
+    // exceeded. Additional input_vars: op.
     $count = 1 + webform_count_input_vars($element);
     if ($count > $limit * 0.95) {
       $subs = array(

+ 0 - 4
sites/all/modules/contrib/users/faq/.gitignore

@@ -1,4 +0,0 @@
-*.patch
-*.diff
-.idea/
-.idea/*

+ 7 - 5
sites/all/modules/contrib/users/faq/faq.admin.inc

@@ -36,13 +36,15 @@ function faq_general_settings_form($form) {
     '#default_value' => variable_get('faq_title', 'Frequently Asked Questions'),
   );
 
-  $form['body_filter']['faq_description'] = array(
-    '#type' => 'textarea',
+  $faq_description_default = array('value' => '', 'format' => filter_fallback_format());
+  $faq_description = variable_get('faq_description', $faq_description_default);
+  $form['faq_description'] = array(
+    '#type' => 'text_format',
     '#title' => t('FAQ Description'),
-    '#default_value' => variable_get('faq_description', ''),
+    '#default_value' => $faq_description['value'],
     '#description' => t('Your FAQ description.  This will be placed at the top of the page, above the questions and can serve as an introductory text.'),
     '#rows' => 5,
-    '#format' => variable_get('faq_description_format', NULL),
+    '#format' => $faq_description['format'],
   );
 
   $form['faq_custom_breadcrumbs'] = array(
@@ -61,7 +63,7 @@ function faq_general_settings_form($form) {
     '#type' => 'textfield',
     '#title' => t('FAQ Path'),
     '#description' => t('This option sets the path to the faq page. DO NOT append with a \'/\''),
-    '#default_value' => variable_get('faq_path', 'faq-page'),
+    '#default_value' => _faq_path(),
   );
 
   return system_settings_form($form);

+ 3 - 4
sites/all/modules/contrib/users/faq/faq.info

@@ -22,9 +22,8 @@ files[] = includes/faq.questions_top.inc
 files[] = views/faq.views.inc
 configure = admin/config/content/faq
 
-; Information added by Drupal.org packaging script on 2015-09-08
-version = "7.x-1.1"
+; Information added by Drupal.org packaging script on 2019-11-25
+version = "7.x-1.2"
 core = "7.x"
 project = "faq"
-datestamp = "1441726722"
-
+datestamp = "1574706185"

+ 18 - 1
sites/all/modules/contrib/users/faq/faq.install

@@ -112,7 +112,7 @@ function faq_uninstall() {
   // General settings.
   variable_del('faq_title');
   variable_del('faq_description');
-  variable_del('faq_description_format');
+  variable_del('faq_path');
   // Questions page.
   variable_del('faq_display');
   variable_del('faq_question_listing');
@@ -127,6 +127,7 @@ function faq_uninstall() {
   variable_del('faq_back_to_top');
   variable_del('faq_disable_node_links');
   variable_del('faq_default_sorting');
+  variable_del('faq_question_long_form');
   // Categories page.
   variable_del('faq_use_categories');
   variable_del('faq_category_display');
@@ -406,6 +407,22 @@ function faq_update_7002(&$sandbox) {
   return t('Custom field added, @count questions converted into fields.', array('@count' => $sandbox['max'] + 1));
 }
 
+/**
+ * Convert faq page description to array.
+ */
+function faq_update_7003(&$sandbox) {
+  $description = variable_get('faq_description', '');
+  $format = variable_get('faq_description_format', filter_fallback_format());
+  $faq_description = array(
+    'value' => $description,
+    'format' => $format,
+  );
+  variable_set('faq_description', $faq_description);
+  variable_del('faq_description_format');
+
+  return t('Converted faq page description to array.');
+}
+
 /**
  * Code examples modified.
  *

+ 44 - 27
sites/all/modules/contrib/users/faq/faq.module

@@ -74,7 +74,7 @@ function faq_node_access($node, $op, $account = NULL) {
  */
 function faq_menu() {
   $items = array();
-  $faq_path = variable_get('faq_path', 'faq-page');
+  $faq_path = _faq_path();
 
   $items[$faq_path] = array(
     'title' => 'Frequently Asked Questions',
@@ -239,13 +239,13 @@ function faq_form_faq_general_settings_form_alter(&$form, &$form_state) {
  */
 function faq_insert($node) {
   $items = field_get_items('node', $node, 'field_detailed_question');
-  $detailed_question = reset($items);
+  $detailed_question = !empty($items) ? $items[0]['value'] : '';
   db_insert('faq_questions')
     ->fields(array(
       'nid' => $node->nid,
       'vid' => $node->vid,
       'question' => $node->title,
-      'detailed_question' => $detailed_question['value'],
+      'detailed_question' => $detailed_question,
     ))
     ->execute();
 }
@@ -416,6 +416,8 @@ function faq_page($tid = 0, $faq_display = '', $category_display = '') {
     module_load_include('inc', 'pathauto');
   }
 
+  $faqpath = _faq_path();
+
   // Things to provide translations for.
   $default_values = array(
     t('Frequently Asked Questions'),
@@ -426,7 +428,7 @@ function faq_page($tid = 0, $faq_display = '', $category_display = '') {
 
   $output = $output_answers = '';
   drupal_add_css(drupal_get_path('module', 'faq') . '/faq.css');
-  if (arg(0) == 'faq-page') {
+  if (arg(0) == $faqpath) {
     drupal_set_title(t(variable_get('faq_title', 'Frequently Asked Questions')));
   }
   if (!module_exists("taxonomy")) {
@@ -441,7 +443,7 @@ function faq_page($tid = 0, $faq_display = '', $category_display = '') {
         drupal_goto($alias['alias']);
       }
     }
-    if (drupal_match_path($_GET['q'], 'faq-page/*')) {
+    if (drupal_match_path($_GET['q'], $faqpath . '/*')) {
       faq_set_breadcrumb($current_term);
     }
   }
@@ -571,7 +573,7 @@ function faq_page($tid = 0, $faq_display = '', $category_display = '') {
       }
       $valid_vocab = TRUE;
 
-      if ($category_display == "new_page") {
+      if ($category_display == 'new_page') {
         $vocab_items = _get_indented_faq_terms($vid, 0);
         $items = array_merge($items, $vocab_items);
       }
@@ -616,16 +618,22 @@ function faq_page($tid = 0, $faq_display = '', $category_display = '') {
     }
   }
 
-  $faq_description = t(variable_get('faq_description', ''));
-  $format = variable_get('faq_description_format', 0);
+  $faq_description_default = array('value' => '', 'format' => filter_fallback_format());
+  $faq_description = variable_get('faq_description', $faq_description_default);
+  $format = $faq_description['format'];
+
   if ($format) {
-    $faq_description = check_markup($faq_description, $format);
+    $description = check_markup($faq_description['value'], $format);
   }
+  else {
+    $description = check_plain($faq_description['value']);
+  }
+
   return theme('faq_page',
     array(
       'content' => $output,
       'answers' => $output_answers,
-      'description' => $faq_description,
+      'description' => $description,
     )
   );
 }
@@ -943,7 +951,7 @@ function faq_block_view($delta) {
           $block['subject'] = t('FAQ Categories');
           $items = array();
           foreach ($terms as $name => $tid) {
-            $items[] = l(faq_tt("taxonomy:term:$tid:name", $name), 'faq-page/' . $tid);
+            $items[] = l(faq_tt("taxonomy:term:$tid:name", $name), _faq_path() . '/' . $tid);
           }
           $list_style = variable_get('faq_category_listing', 'ul');
           $block['content'] = theme('item_list',
@@ -1015,7 +1023,7 @@ function _get_indented_faq_terms($vid, $tid) {
         ->fetchField();
 
       if ($term_node_count > 0) {
-        $path = "faq-page/$term->tid";
+        $path = _faq_path() . "/$term->tid";
 
         if (!drupal_lookup_path('alias', arg(0) . '/' . $term->tid) && module_exists('pathauto')) {
           $alias = pathauto_create_alias('faq', 'insert', arg(0) . '/' . $term->tid, array('term' => $term));
@@ -1134,7 +1142,7 @@ function faq_get_faq_list() {
 
   foreach ($nodes as $node) {
     if (node_access('view', $node)) {
-      $items[] = l($node->question, "node/$node->nid");
+      $items[] = l($node->title, "node/$node->nid");
     }
   }
 
@@ -1217,7 +1225,7 @@ function faq_view_question(&$data, $node, $path = NULL, $anchor = NULL) {
   }
 
   // Get the detailed question.
-  $detailed_question = '';
+  $detailed_question = array();
   if ($dq = field_get_items('node', $node, 'field_detailed_question')) {
     $detailed_question = reset($dq);
   }
@@ -1225,7 +1233,7 @@ function faq_view_question(&$data, $node, $path = NULL, $anchor = NULL) {
   if (variable_get('faq_display', 'questions_top') != 'hide_answer'
         && !empty($detailed_question['value'])
         && variable_get('faq_question_length', 'short') == 'both') {
-    $question .= '<div class="faq-detailed-question">' . $detailed_question['safe_value'] . '</div>';
+    $question .= '<div class="faq-detailed-question">' . $detailed_question['value'] . '</div>';
   }
   $data['question'] = $question;
 }
@@ -1461,7 +1469,7 @@ function faq_view_child_category_headers($term) {
         $term_image = taxonomy_image_display($child_term->tid, array('class' => 'faq-tax-image'));
       }
 
-      $term_vars['link'] = l(faq_tt("taxonomy:term:$child_term->tid:name", $child_term->name), "faq-page/$child_term->tid");
+      $term_vars['link'] = l(faq_tt("taxonomy:term:$child_term->tid:name", $child_term->name), _faq_path() . "/$child_term->tid");
       $term_vars['description'] = check_markup(faq_tt("taxonomy:term:$child_term->tid:description", $child_term->description));
       $term_vars['count'] = $term_node_count;
       $term_vars['term_image'] = $term_image;
@@ -1482,7 +1490,7 @@ function faq_pathauto($op) {
       $settings['module'] = 'faq';
       $settings['groupheader'] = t('FAQ category page settings');
       $settings['patterndescr'] = t('Default path pattern (applies to all FAQ categories with blank patterns below)');
-      $settings['patterndefault'] = t('faq-page/[term:tid]');
+      $settings['patterndefault'] = t(_faq_path() . '/[term:tid]');
       $settings['batch_update_callback'] = 'faq_pathauto_bulkupdate';
       $settings['token_type'] = 'term';
       return (object) $settings;
@@ -1496,7 +1504,7 @@ function faq_pathauto($op) {
  * Implements hook_path_alias_types().
  */
 function faq_path_alias_types() {
-  $objects['faq-page/'] = t('FAQ pages');
+  $objects[_faq_path() . '/'] = t('FAQ pages');
   return $objects;
 }
 
@@ -1511,6 +1519,7 @@ function faq_pathauto_bulkupdate() {
     $context['sandbox']['current'] = 0;
   }
 
+  $faq_path = _faq_path();
   // Get the allowed vocabs.
   $vocabularies = taxonomy_get_vocabularies('faq');
   $vocab_omit = variable_get('faq_omit_vocabulary', array());
@@ -1524,7 +1533,7 @@ function faq_pathauto_bulkupdate() {
 
   // Get tids that need aliasing.
   $query = db_select('taxonomy_term_data', 'td');
-  $query->leftJoin('url_alias', 'ua', "CONCAT('faq-page/', td.tid) = ua.source");
+  $query->leftJoin('url_alias', 'ua', "CONCAT($faq_path . '/', td.tid) = ua.source");
   $query->addField('td', 'tid');
   $query->isNull('ua.source');
   $query->condition('td.tid', $context['sandbox']['current'], '>');
@@ -1549,7 +1558,7 @@ function faq_pathauto_bulkupdate() {
 
   $terms = taxonomy_term_load_multiple($tids);
   foreach ($terms as $term) {
-    pathauto_create_alias('faq', 'bulkupdate', 'faq-page/' . $term->tid, array('term' => $term));
+    pathauto_create_alias('faq', 'bulkupdate', $faq_path . '/' . $term->tid, array('term' => $term));
   }
   $context['sandbox']['count'] += count($tids);
   $context['sandbox']['current'] = max($tids);
@@ -1573,7 +1582,7 @@ function faq_taxonomy_term_insert($term) {
       if ((isset($vocab_omit[$vid]) && $vocab_omit[$vid] != 0) || ($term->vid != $vid)) {
         continue;
       }
-      $alias = pathauto_create_alias('faq', 'insert', 'faq-page/' . $term->tid, array('term' => $term));
+      $alias = pathauto_create_alias('faq', 'insert', _faq_path() . '/' . $term->tid, array('term' => $term));
     }
   }
 }
@@ -1591,7 +1600,7 @@ function faq_taxonomy_term_update($term) {
       if ((isset($vocab_omit[$vid]) && $vocab_omit[$vid] != 0) || ($term->vid != $vid)) {
         continue;
       }
-      $alias = pathauto_create_alias('faq', 'update', 'faq-page/' . $term->tid, array('term' => $term));
+      $alias = pathauto_create_alias('faq', 'update', _faq_path() . '/' . $term->tid, array('term' => $term));
     }
   }
 }
@@ -1602,7 +1611,7 @@ function faq_taxonomy_term_update($term) {
 function faq_taxonomy_term_delete($term) {
   if (module_exists('pathauto')) {
     module_load_include('inc', 'pathauto');
-    pathauto_path_delete_all("faq-page/{$term->tid}");
+    pathauto_path_delete_all(_faq_path() . "/{$term->tid}");
   }
 }
 
@@ -1617,15 +1626,16 @@ function faq_taxonomy_term_delete($term) {
  */
 function faq_set_breadcrumb($term = NULL) {
   $breadcrumb = array();
+  $faq_path = _faq_path();
   if (variable_get('faq_custom_breadcrumbs', TRUE)) {
     if (module_exists("taxonomy") && $term) {
-      $breadcrumb[] = l(faq_tt("taxonomy:term:$term->tid:name", $term->name), 'faq-page/' . $term->tid);
+      $breadcrumb[] = l(faq_tt("taxonomy:term:$term->tid:name", $term->name), $faq_path . '/' . $term->tid);
       while ($parents = taxonomy_get_parents($term->tid)) {
         $term = array_shift($parents);
-        $breadcrumb[] = l(faq_tt("taxonomy:term:$term->tid:name", $term->name), 'faq-page/' . $term->tid);
+        $breadcrumb[] = l(faq_tt("taxonomy:term:$term->tid:name", $term->name), $faq_path . '/' . $term->tid);
       }
     }
-    $breadcrumb[] = l(t(variable_get('faq_title', 'Frequently Asked Questions')), 'faq-page');
+    $breadcrumb[] = l(t(variable_get('faq_title', 'Frequently Asked Questions')), $faq_path);
     $breadcrumb[] = l(t('Home'), NULL, array('attributes' => array('title' => variable_get('site_name', ''))));
     $breadcrumb = array_reverse($breadcrumb);
     return drupal_set_breadcrumb($breadcrumb);
@@ -1815,7 +1825,7 @@ function theme_field_detailed_question($variables) {
   }
 
   // Render the items.
-  $output .= '<div class="faq-detailed-question">' . $variables['safe_value'] . '</div>';
+  $output .= '<div class="faq-detailed-question">' . $variables['value'] . '</div>';
 
   // Render the top-level DIV.
   if (isset($variables['classes']) && isset($variables['attributes'])) {
@@ -1856,3 +1866,10 @@ function theme_faq_draggable_question_order_table($variables) {
   $output .= drupal_render_children($form);
   return $output;
 }
+
+/**
+ * Helper to get the path for the faq page.
+ */
+function _faq_path() {
+  return variable_get('faq_path', 'faq-page');
+}

+ 15 - 15
sites/all/modules/contrib/users/faq/faq.test

@@ -172,20 +172,20 @@ class FaqAccessTestClass extends FaqTestCase {
   function testFaqAccess() {
 
     // Verify that anonymous user has no access to the faq page
-    $this->faqVerifyNoAccess('faq-page');
+    $this->faqVerifyNoAccess(_faq_path());
 
     // Create and login user
     $normal_user = $this->drupalCreateUser();
     $this->drupalLogin($normal_user);
     // Verify that logged in user has no access to the faq page
-    $this->faqVerifyNoAccess('faq-page');
+    $this->faqVerifyNoAccess(_faq_path());
     $this->drupalLogout();
 
     $view_faq_user = $this->drupalCreateUser(array('view faq page'));
     $this->drupalLogin($view_faq_user);
 
     // Verify that the faq page is visible and available but empty
-    $this->drupalGet('faq-page');
+    $this->drupalGet(_faq_path());
     $this->assertText(t('Frequently Asked Questions'), t('FAQ page is available for view faq page permissions.'));
 
   }
@@ -217,14 +217,14 @@ class CreateFaqTestCase extends FaqTestCase {
     // Show the detailed question
     $this->drupalGet('admin/config/content/faq/questions');
     // Set faq to allow long question text and labeled questions and answers
-    $this->drupalPost('admin/config/content/faq/questions', array('faq_question_long_form' => '1', 'faq_qa_mark' => '1'), t('Save configuration'));
-    $this->drupalPost('admin/config/content/faq/questions', array('faq_question_length' => 'both'), t('Save configuration'));
+    $this->drupalPost('admin/config/content/faq/questions', array('faq_question_long_form' => '1', 'faq_qa_mark' => '1'), t('Save configuration'));
+    $this->drupalPost('admin/config/content/faq/questions', array('faq_question_length' => 'both'), t('Save configuration'));
 
-    $this->afaq_user = $this->drupalCreateUser(array('create faq content', 'view faq page'));
-    $this->drupalLogin($this->afaq_user);
+    $this->afaq_user = $this->drupalCreateUser(array('create faq content', 'view faq page'));
+    $this->drupalLogin($this->afaq_user);
 
     // Verify that the faq page is visible and available but empty
-    $this->drupalGet('faq-page');
+    $this->drupalGet(_faq_path());
     $this->assertText(t('Frequently Asked Questions'), t('FAQ page is available for view faq page permissions.'));
 
     // Fill in the Create FAQ node 1 form and post it
@@ -242,7 +242,7 @@ class CreateFaqTestCase extends FaqTestCase {
 
     // Check that faq is found on the correct taxonomy term page too
     $this->drupalGet('taxonomy/term/' . $this->term->tid);
-    $this->assertText(t('@title', array('@title' => $this->faq1['title'])));
+    $this->assertText(t('@title', array('@title' => $this->faq1['title'])));
 
     // Fill in the Create FAQ node 2 form and post it
     $this->faq2 = array();
@@ -261,12 +261,12 @@ class CreateFaqTestCase extends FaqTestCase {
 
 
     // Verify that logged in user has no access to the faq page
-    $this->faqVerifyNoAccess('faq-page');
+    $this->faqVerifyNoAccess(_faq_path());
 
     // Check that the FAQ page is available and that the correct term is listed as grouping for the new FAQ node
     $view_faq_user = $this->drupalCreateUser(array('view faq page'));
     $this->drupalLogin($view_faq_user);
-    $this->drupalGet('faq-page');
+    $this->drupalGet(_faq_path());
     $this->assertText(t('Frequently Asked Questions'), t('FAQ page is available for view faq page permissions.'));
     $this->assertText($this->faq1['title'], t('Created FAQ node 1 available on FAQ page.'));
     $this->assertText($this->faq1[$this->instance['field_name'] . '[' . $langcode .']'], t('Term for node 1 available on FAQ page.'));
@@ -276,7 +276,7 @@ class CreateFaqTestCase extends FaqTestCase {
     // Navigate to FAQ node created on FAQ page
     $this->clickLink(t($this->faq1['title']));
     $this->assertText(t($this->faq1["field_detailed_question[$langcode][0][value]"]), t('Detailed question visible')); // Dependant on the question setting
-    $this->assertText(t($this->faq1["body[$langcode][0][value]"]), t('Answer visible'));
+    $this->assertText(t($this->faq1["body[$langcode][0][value]"]), t('Answer visible'));
 
     // Enable categorisation of FAQ nodes
     // Create and log in user with create and administer FAQ permissions
@@ -290,7 +290,7 @@ class CreateFaqTestCase extends FaqTestCase {
     $this->drupalLogout();
 
     $this->drupalLogin($view_faq_user);
-    $this->drupalGet('faq-page');
+    $this->drupalGet(_faq_path());
     $this->assertText(t('Frequently Asked Questions'), t('FAQ page is available for view faq page permissions.'));
     $this->assertText($this->faq1['title'], t('Created FAQ node 1 available on FAQ page.'));
     $this->assertText($this->faq1[$this->instance['field_name'] . '[' . $langcode .']'], t('Term for node 1 not available on FAQ page.'));
@@ -323,7 +323,7 @@ class CRAUDFaqTestCase extends FaqTestCase {
   public function testFaqCreate() {
 
     // Verify that logged in user has no access to the faq page
-    $this->faqVerifyNoAccess('faq-page');
+    $this->faqVerifyNoAccess(_faq_path());
 
     // Log in user with create FAQ permissions
     $this->drupalLogin($this->faq_user);
@@ -353,7 +353,7 @@ class CRAUDFaqTestCase extends FaqTestCase {
     $this->drupalLogin($faq_view_user);
 
     // Verify visibility on faq page
-    $this->drupalGet('faq-page'); // Load faq page
+    $this->drupalGet(_faq_path()); // Load faq page
     $this->assertText($edit['title']); // Node should be listed here
     $this->drupalGet('node/' . $node->nid); // It should also be possible to open the node directly
 

+ 3 - 3
sites/all/modules/contrib/users/faq/includes/faq.hide_answer.inc

@@ -63,7 +63,7 @@ function template_preprocess_faq_category_hide_answer(&$variables) {
   $this_page = $_GET['q'];
   $get_child_terms = 0;
   // Check if we're on a faq page.
-  if (arg(0) == 'faq-page') {
+  if (arg(0) == _faq_path()) {
     // Check if we're on a categorized faq page.
     if (is_numeric(arg(1))) {
       $get_child_terms = arg(1);
@@ -85,7 +85,7 @@ function template_preprocess_faq_category_hide_answer(&$variables) {
 
   // Get taxonomy image.
   $variables['term_image'] = '';
-  if (module_exists('taxonomy_image')) {
+  if (module_exists('taxonomy_image') && function_exists('taxonomy_image_display')) {
     $variables['term_image'] = taxonomy_image_display($term->tid, array('class' => 'faq-tax-image'));
   }
 
@@ -120,7 +120,7 @@ function template_preprocess_faq_category_hide_answer(&$variables) {
 
   // Configure sub-category bodies (theme recursively).
   $variables['subcat_body_list'] = array();
-  if (($get_child_terms && $category_display == 'categories_inline') || ((($show_term_page_children && $this_page != 'faq-page') || $hide_child_terms) && $category_display == 'hide_qa')) {
+  if (($get_child_terms && $category_display == 'categories_inline') || ((($show_term_page_children && $this_page != _faq_path()) || $hide_child_terms) && $category_display == 'hide_qa')) {
     $variables['subcat_body_list'] = faq_get_child_categories_faqs($term, 'faq_category_hide_answer', $default_weight, $default_sorting, $category_display, $variables['class'], $parent_term);
   }
 

+ 3 - 3
sites/all/modules/contrib/users/faq/includes/faq.new_page.inc

@@ -55,7 +55,7 @@ function template_preprocess_faq_category_new_page(&$variables) {
   // Initialise some variables.
   $get_child_terms = 0;
   // Check if we're on a faq page.
-  if (arg(0) == 'faq-page') {
+  if (arg(0) == _faq_path()) {
     // Check if we're on a categorized faq page.
     if (is_numeric(arg(1))) {
       $get_child_terms = arg(1);
@@ -83,7 +83,7 @@ function template_preprocess_faq_category_new_page(&$variables) {
 
   // Get taxonomy image.
   $variables['term_image'] = '';
-  if (module_exists('taxonomy_image')) {
+  if (module_exists('taxonomy_image') && function_exists('taxonomy_image_display')) {
     $variables['term_image'] = taxonomy_image_display($term->tid, array('class' => 'faq-tax-image'));
   }
 
@@ -117,7 +117,7 @@ function template_preprocess_faq_category_new_page(&$variables) {
 
   // Configure sub-category bodies (theme recursively).
   $variables['subcat_body_list'] = array();
-  if (($get_child_terms && $category_display == 'categories_inline') || ((($show_term_page_children && $this_page != 'faq-page') || $hide_child_terms) && $category_display == 'hide_qa')) {
+  if (($get_child_terms && $category_display == 'categories_inline') || ((($show_term_page_children && $this_page != _faq_path()) || $hide_child_terms) && $category_display == 'hide_qa')) {
     $variables['subcat_body_list'] = faq_get_child_categories_faqs($term, 'faq_category_new_page', $default_weight, $default_sorting, $category_display, $variables['class'], $parent_term);
   }
 

+ 3 - 3
sites/all/modules/contrib/users/faq/includes/faq.questions_inline.inc

@@ -74,7 +74,7 @@ function template_preprocess_faq_category_questions_inline(&$variables) {
   $this_page = $_GET['q'];
   $get_child_terms = 0;
   // Check if we're on a faq page.
-  if (arg(0) == 'faq-page') {
+  if (arg(0) == _faq_path()) {
     // Check if we're on a categorized faq page.
     if (is_numeric(arg(1))) {
       $get_child_terms = arg(1);
@@ -107,7 +107,7 @@ function template_preprocess_faq_category_questions_inline(&$variables) {
 
   // Get taxonomy image.
   $variables['term_image'] = '';
-  if (module_exists('taxonomy_image')) {
+  if (module_exists('taxonomy_image') && function_exists('taxonomy_image_display')) {
     $variables['term_image'] = taxonomy_image_display($term->tid, array('class' => 'faq-tax-image'));
   }
 
@@ -141,7 +141,7 @@ function template_preprocess_faq_category_questions_inline(&$variables) {
 
   // Configure sub-category bodies (theme recursively).
   $variables['subcat_body_list'] = array();
-  if (($get_child_terms && $category_display == 'categories_inline') || ((($show_term_page_children && $this_page != 'faq-page') || $hide_child_terms) && $category_display == 'hide_qa')) {
+  if (($get_child_terms && $category_display == 'categories_inline') || ((($show_term_page_children && $this_page != _faq_path()) || $hide_child_terms) && $category_display == 'hide_qa')) {
     $variables['subcat_body_list'] = faq_get_child_categories_faqs($term, 'faq_category_questions_inline', $default_weight, $default_sorting, $category_display, $variables['class'], $parent_term);
   }
 

+ 5 - 5
sites/all/modules/contrib/users/faq/includes/faq.questions_top.inc

@@ -103,7 +103,7 @@ function template_preprocess_faq_category_questions_top(&$variables) {
   $this_page = $_GET['q'];
   $get_child_terms = 0;
   // Check if we're on a faq page.
-  if (arg(0) == 'faq-page') {
+  if (arg(0) == _faq_path()) {
     // Check if we're on a categorized faq page.
     if (is_numeric(arg(1))) {
       $get_child_terms = arg(1);
@@ -128,7 +128,7 @@ function template_preprocess_faq_category_questions_top(&$variables) {
 
   // Get taxonomy image.
   $variables['term_image'] = '';
-  if (module_exists('taxonomy_image')) {
+  if (module_exists('taxonomy_image') && function_exists('taxonomy_image_display')) {
     $variables['term_image'] = taxonomy_image_display($term->tid, array('class' => 'faq-tax-image'));
   }
 
@@ -163,7 +163,7 @@ function template_preprocess_faq_category_questions_top(&$variables) {
 
   // Configure sub-category bodies (theme recursively).
   $variables['subcat_body_list'] = array();
-  if (($get_child_terms && $category_display == 'categories_inline') || ((($show_term_page_children && $this_page != 'faq-page') || $hide_child_terms) && $category_display == 'hide_qa')) {
+  if (($get_child_terms && $category_display == 'categories_inline') || ((($show_term_page_children && $this_page != _faq_path()) || $hide_child_terms) && $category_display == 'hide_qa')) {
     $variables['subcat_body_list'] = faq_get_child_categories_faqs($term, 'faq_category_questions_top', $default_weight, $default_sorting, $category_display, $variables['class'], $parent_term);
   }
 
@@ -253,7 +253,7 @@ function template_preprocess_faq_category_questions_top_answers(&$variables) {
   $this_page = $_GET['q'];
   $get_child_terms = 0;
   // Check if we're on a faq page.
-  if (arg(0) == 'faq-page') {
+  if (arg(0) == _faq_path()) {
     // Check if we're on a categorized faq page.
     if (is_numeric(arg(1))) {
       $get_child_terms = arg(1);
@@ -277,7 +277,7 @@ function template_preprocess_faq_category_questions_top_answers(&$variables) {
 
   // Configure sub-category bodies (theme recursively).
   $variables['subcat_body_list'] = array();
-  if (($get_child_terms && $category_display == 'categories_inline') || ((($show_term_page_children && $this_page != 'faq-page') || $hide_child_terms) && $category_display == 'hide_qa')) {
+  if (($get_child_terms && $category_display == 'categories_inline') || ((($show_term_page_children && $this_page != _faq_path()) || $hide_child_terms) && $category_display == 'hide_qa')) {
     $variables['subcat_body_list'] = faq_get_child_categories_faqs($term, 'faq_category_questions_top_answers', $default_weight, $default_sorting, $category_display, $variables['class'], $parent_term);
   }