commit 8d02775be79dc90f8b05ac548a54b0c4a6e3fd51
Author: Assos Assos <assos+forge@centrale-marseille.fr>
Date:   Wed Feb 19 23:19:18 2020 +0100

    Weekly update of contrib modules

diff --git a/drupal7/sites/all/modules/webform/components/email.inc b/drupal7/sites/all/modules/webform/components/email.inc
index 4a124ccc..6430843d 100644
--- a/drupal7/sites/all/modules/webform/components/email.inc
+++ b/drupal7/sites/all/modules/webform/components/email.inc
@@ -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'),
   );
 }
 
diff --git a/drupal7/sites/all/modules/webform/components/grid.inc b/drupal7/sites/all/modules/webform/components/grid.inc
index bffa2366..eb806113 100644
--- a/drupal7/sites/all/modules/webform/components/grid.inc
+++ b/drupal7/sites/all/modules/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;
 }
diff --git a/drupal7/sites/all/modules/webform/components/number.inc b/drupal7/sites/all/modules/webform/components/number.inc
index c2bf76e7..8c24b356 100644
--- a/drupal7/sites/all/modules/webform/components/number.inc
+++ b/drupal7/sites/all/modules/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'),
   );
 }
 
diff --git a/drupal7/sites/all/modules/webform/components/textarea.inc b/drupal7/sites/all/modules/webform/components/textarea.inc
index c4556b78..7a173d41 100644
--- a/drupal7/sites/all/modules/webform/components/textarea.inc
+++ b/drupal7/sites/all/modules/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'),
   );
 }
 
diff --git a/drupal7/sites/all/modules/webform/components/textfield.inc b/drupal7/sites/all/modules/webform/components/textfield.inc
index 1bddc466..d7bb1a3d 100644
--- a/drupal7/sites/all/modules/webform/components/textfield.inc
+++ b/drupal7/sites/all/modules/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'),
   );
 }
 
diff --git a/drupal7/sites/all/modules/webform/includes/webform.components.inc b/drupal7/sites/all/modules/webform/includes/webform.components.inc
index 00ca5eb8..af7d48b6 100644
--- a/drupal7/sites/all/modules/webform/includes/webform.components.inc
+++ b/drupal7/sites/all/modules/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."),
       );
     }
@@ -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,
diff --git a/drupal7/sites/all/modules/webform/includes/webform.conditionals.inc b/drupal7/sites/all/modules/webform/includes/webform.conditionals.inc
index 4e4b2456..2be2e122 100644
--- a/drupal7/sites/all/modules/webform/includes/webform.conditionals.inc
+++ b/drupal7/sites/all/modules/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.'));
         }
       }
     }
diff --git a/drupal7/sites/all/modules/webform/includes/webform.report.inc b/drupal7/sites/all/modules/webform/includes/webform.report.inc
index 98257aee..ca7c3878 100644
--- a/drupal7/sites/all/modules/webform/includes/webform.report.inc
+++ b/drupal7/sites/all/modules/webform/includes/webform.report.inc
@@ -1921,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']) {
@@ -1987,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;
 }
 
diff --git a/drupal7/sites/all/modules/webform/includes/webform.submissions.inc b/drupal7/sites/all/modules/webform/includes/webform.submissions.inc
index b1607a9c..f27a81e5 100644
--- a/drupal7/sites/all/modules/webform/includes/webform.submissions.inc
+++ b/drupal7/sites/all/modules/webform/includes/webform.submissions.inc
@@ -231,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;
@@ -280,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'));
 
@@ -331,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'] . '>';
     }
@@ -617,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.',
diff --git a/drupal7/sites/all/modules/webform/tests/WebformComponentsTestCase.test b/drupal7/sites/all/modules/webform/tests/WebformComponentsTestCase.test
index 1fb85547..ccdbd3e9 100644
--- a/drupal7/sites/all/modules/webform/tests/WebformComponentsTestCase.test
+++ b/drupal7/sites/all/modules/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.');
   }
 
 }
diff --git a/drupal7/sites/all/modules/webform/tests/WebformSubmissionTestCase.test b/drupal7/sites/all/modules/webform/tests/WebformSubmissionTestCase.test
index 63cc9d1d..1b3d696c 100644
--- a/drupal7/sites/all/modules/webform/tests/WebformSubmissionTestCase.test
+++ b/drupal7/sites/all/modules/webform/tests/WebformSubmissionTestCase.test
@@ -39,6 +39,26 @@ class WebformSubmissionTestCase extends WebformTestCase {
 
     // 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().');
   }
 
   /**
diff --git a/drupal7/sites/all/modules/webform/tests/WebformTestCase.test b/drupal7/sites/all/modules/webform/tests/WebformTestCase.test
index 7f1efc66..0551279e 100644
--- a/drupal7/sites/all/modules/webform/tests/WebformTestCase.test
+++ b/drupal7/sites/all/modules/webform/tests/WebformTestCase.test
@@ -185,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',
diff --git a/drupal7/sites/all/modules/webform/webform.api.php b/drupal7/sites/all/modules/webform/webform.api.php
index f6997766..4f5a9fe9 100644
--- a/drupal7/sites/all/modules/webform/webform.api.php
+++ b/drupal7/sites/all/modules/webform/webform.api.php
@@ -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;
 
diff --git a/drupal7/sites/all/modules/webform/webform.info b/drupal7/sites/all/modules/webform/webform.info
index 903417b5..8b4b3c95 100644
--- a/drupal7/sites/all/modules/webform/webform.info
+++ b/drupal7/sites/all/modules/webform/webform.info
@@ -40,8 +40,8 @@ files[] = tests/WebformSubmissionTestCase.test
 files[] = tests/WebformTestCase.test
 files[] = tests/WebformUnitTestCase.test
 
-; Information added by Drupal.org packaging script on 2019-12-11
-version = "7.x-4.21"
+; Information added by Drupal.org packaging script on 2020-02-14
+version = "7.x-4.22"
 core = "7.x"
 project = "webform"
-datestamp = "1576092840"
+datestamp = "1581709525"
diff --git a/drupal7/sites/all/modules/webform/webform.module b/drupal7/sites/all/modules/webform/webform.module
index 72385b12..73305616 100644
--- a/drupal7/sites/all/modules/webform/webform.module
+++ b/drupal7/sites/all/modules/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]);
           }
