diff --git a/includes/webform.theme.template.inc b/includes/webform.theme.template.inc index 1ff227b4..6fc762d6 100644 --- a/includes/webform.theme.template.inc +++ b/includes/webform.theme.template.inc @@ -566,6 +566,9 @@ function template_preprocess_webform_element_base_html(array &$variables) { $variables['item']['#markup'] = $variables['value']; } } + else { + $variables['title'] = ['#markup' => $variables['title']]; + } } /** diff --git a/js/webform.ajax.js b/js/webform.ajax.js index 2e39419c..f70f0d6e 100644 --- a/js/webform.ajax.js +++ b/js/webform.ajax.js @@ -16,7 +16,7 @@ /** * Provide Webform Ajax link behavior. * - * Display fullscreen progress indicator instead of throber. + * Display fullscreen progress indicator instead of throbber. * Copied from: Drupal.behaviors.AJAX * * @type {Drupal~behavior} diff --git a/modules/webform_node/src/Access/WebformNodeAccess.php b/modules/webform_node/src/Access/WebformNodeAccess.php index a252095a..405bc242 100644 --- a/modules/webform_node/src/Access/WebformNodeAccess.php +++ b/modules/webform_node/src/Access/WebformNodeAccess.php @@ -120,7 +120,7 @@ class WebformNodeAccess { return WebformSubmissionAccess::checkWizardPagesAccess($webform_submission); case 'webform_submission_resend': - return WebformSubmissionAccess::checkEmailAccess($webform_submission, $account); + return WebformSubmissionAccess::checkResendAccess($webform_submission, $account); } return $access_result; diff --git a/src/Access/WebformSubmissionAccess.php b/src/Access/WebformSubmissionAccess.php index 647e74bc..e83a5cbc 100644 --- a/src/Access/WebformSubmissionAccess.php +++ b/src/Access/WebformSubmissionAccess.php @@ -28,7 +28,7 @@ class WebformSubmissionAccess { } /** - * Check that webform submission has email and the user can update any webform submission. + * Check that webform submission has (email) messages and the user can update any webform submission. * * @param \Drupal\webform\WebformSubmissionInterface $webform_submission * A webform submission. @@ -38,7 +38,7 @@ class WebformSubmissionAccess { * @return \Drupal\Core\Access\AccessResultInterface * The access result. */ - public static function checkEmailAccess(WebformSubmissionInterface $webform_submission, AccountInterface $account) { + public static function checkResendAccess(WebformSubmissionInterface $webform_submission, AccountInterface $account) { $webform = $webform_submission->getWebform(); if ($webform->access('submission_update_any', $account)) { $handlers = $webform->getHandlers(); diff --git a/src/Element/WebformComputedToken.php b/src/Element/WebformComputedToken.php index f577b88c..ab54bbb5 100644 --- a/src/Element/WebformComputedToken.php +++ b/src/Element/WebformComputedToken.php @@ -24,12 +24,7 @@ class WebformComputedToken extends WebformComputedBase { $token_manager = \Drupal::service('webform.token_manager'); // Replace tokens in value. - $value = $token_manager->replace($element['#value'], $webform_submission, [], ['html' => ($mode == static::MODE_HTML)]); - - // Must decode HTML entities so that they are not double escaped. - $value = Html::decodeEntities($value); - - return $value; + return $token_manager->replace($element['#value'], $webform_submission, [], ['html' => ($mode == static::MODE_HTML)]); } } diff --git a/src/Entity/WebformSubmission.php b/src/Entity/WebformSubmission.php index 4592b100..193c2fcf 100644 --- a/src/Entity/WebformSubmission.php +++ b/src/Entity/WebformSubmission.php @@ -2,6 +2,7 @@ namespace Drupal\webform\Entity; +use Drupal\Component\Render\PlainTextOutput; use Drupal\Core\Serialization\Yaml; use Drupal\Component\Utility\Crypt; use Drupal\Core\Entity\EntityStorageInterface; @@ -217,7 +218,7 @@ class WebformSubmission extends ContentEntityBase implements WebformSubmissionIn public function label() { $submission_label = $this->getWebform()->getSetting('submission_label') ?: \Drupal::config('webform.settings')->get('settings.default_submission_label'); - return \Drupal::service('webform.token_manager')->replace($submission_label, $this); + return PlainTextOutput::renderFromHtml(\Drupal::service('webform.token_manager')->replace($submission_label, $this)); } /** diff --git a/src/Form/WebformSubmissionResendForm.php b/src/Form/WebformSubmissionResendForm.php index 77eeaf10..4eb599cf 100644 --- a/src/Form/WebformSubmissionResendForm.php +++ b/src/Form/WebformSubmissionResendForm.php @@ -2,9 +2,10 @@ namespace Drupal\webform\Form; +use Drupal\Component\Utility\NestedArray; use Drupal\Core\Form\FormBase; use Drupal\Core\Form\FormStateInterface; -use Drupal\webform\Plugin\WebformHandler\EmailWebformHandler; +use Drupal\webform\Plugin\WebformHandlerMessageInterface; use Drupal\webform\WebformRequestInterface; use Drupal\webform\WebformSubmissionInterface; use Symfony\Component\DependencyInjection\ContainerInterface; @@ -67,16 +68,6 @@ class WebformSubmissionResendForm extends FormBase { public function buildForm(array $form, FormStateInterface $form_state, WebformSubmissionInterface $webform_submission = NULL) { $this->webformSubmission = $webform_submission; - $handlers = $webform_submission->getWebform()->getHandlers(); - - /** @var \Drupal\webform\Plugin\WebformHandlerMessageInterface[] $message_handlers */ - $message_handlers = []; - foreach ($handlers as $handler_id => $handler) { - if ($handler instanceof EmailWebformHandler) { - $message_handlers[$handler_id] = $handler; - } - } - // Get header. $header = []; $header['title'] = [ @@ -96,40 +87,17 @@ class WebformSubmissionResendForm extends FormBase { ]; // Get options. - $options = []; - foreach ($message_handlers as $index => $message_handler) { - $message = $message_handler->getMessage($this->webformSubmission); - - $options[$index]['title'] = [ - 'data' => [ - 'label' => [ - '#type' => 'label', - '#title' => $message_handler->label() . ': ' . $message_handler->description(), - '#title_display' => NULL, - '#for' => 'edit-message-handler-id-' . str_replace('_', '-', $message_handler->getHandlerId()), - ], - ], - ]; - $options[$index]['id'] = [ - 'data' => $message_handler->getHandlerId(), - ]; - $options[$index]['summary'] = [ - 'data' => $message_handler->getMessageSummary($message), - ]; - $options[$index]['status'] = ($message_handler->isEnabled()) ? $this->t('Enabled') : $this->t('Disabled'); - } + $options = $this->getMessageHandlerOptions($webform_submission); - // Get message handler id. - if (empty($form_state->getValue('message_handler_id'))) { - reset($options); - $message_handler_id = key($options); - $form_state->setValue('message_handler_id', $message_handler_id); + // Get message handler id from form state or use the first message handler. + if (!empty($form_state->getValue('message_handler_id'))) { + $message_handler_id = $form_state->getValue('message_handler_id'); } else { - $message_handler_id = $form_state->getValue('message_handler_id'); + $message_handler_id = key($options); } - $message_handler = $this->getMessageHandler($form_state); + // Display message handler with change message Ajax submit button. $form['message_handler_id'] = [ '#type' => 'tableselect', '#header' => $header, @@ -138,13 +106,29 @@ class WebformSubmissionResendForm extends FormBase { '#empty' => $this->t('No messages are available.'), '#multiple' => FALSE, '#default_value' => $message_handler_id, + '#attributes' => ['data-webform-trigger-submit' => '.js-webform-message-change-submit'], + ]; + $form['message_change'] = [ + '#type' => 'submit', + '#value' => $this->t('Change message'), + '#submit' => [[get_called_class(), 'changeMessageSubmit']], + '#attributes' => [ + 'class' => [ + 'js-hide', + 'js-webform-message-change-submit', + ], + ], '#ajax' => [ - 'callback' => '::updateMessage', + 'callback' => '::ajaxMessageCallback', 'wrapper' => 'edit-webform-message-wrapper', + 'progress' => ['type' => 'fullscreen'], ], ]; // Message. + $message_handler = $this->webformSubmission->getWebform()->getHandler($message_handler_id); + $message = $message_handler->getMessage($webform_submission); + $resend_form = $message_handler->resendMessageForm($message); $form['message'] = [ '#type' => 'details', '#title' => $this->t('Message'), @@ -152,9 +136,7 @@ class WebformSubmissionResendForm extends FormBase { '#tree' => TRUE, '#prefix' => '
', '#suffix' => '
', - ]; - $message = $message_handler->getMessage($webform_submission); - $form['message'] += $message_handler->resendMessageForm($message); + ] + $resend_form; // Add resend button. $form['submit'] = [ @@ -180,32 +162,15 @@ class WebformSubmissionResendForm extends FormBase { return $form; } - /** - * Handles switching between messages. - * - * @param array $form - * An associative array containing the structure of the form. - * @param \Drupal\Core\Form\FormStateInterface $form_state - * The current state of the form. - * - * @return array - * An associative array containing an email message. - */ - public function updateMessage(array $form, FormStateInterface $form_state) { - $message_handler = $this->getMessageHandler($form_state); - $message = $message_handler->getMessage($this->webformSubmission); - foreach ($message as $key => $value) { - $form['message'][$key]['#value'] = $value; - } - return $form['message']; - } - /** * {@inheritdoc} */ public function submitForm(array &$form, FormStateInterface $form_state) { $params = $form_state->getValue('message'); - $message_handler = $this->getMessageHandler($form_state); + + $message_handler_id = $form_state->getValue('message_handler_id'); + $message_handler = $this->webformSubmission->getWebform()->getHandler($message_handler_id); + $message_handler->sendMessage($this->webformSubmission, $params); $t_args = [ @@ -228,4 +193,86 @@ class WebformSubmissionResendForm extends FormBase { return $this->webformSubmission->getWebform()->getHandler($message_handler_id); } + /****************************************************************************/ + // Helper methods. + /****************************************************************************/ + + /** + * Get a webform submission's message handlers as options. + * + * @param \Drupal\webform\WebformSubmissionInterface $webform_submission + * A webform submission. + * + * @return array + * An associative array containing a webform submission's message handlers + * as table select options. + */ + protected function getMessageHandlerOptions(WebformSubmissionInterface $webform_submission) { + $handlers = $webform_submission->getWebform()->getHandlers(); + + // Get options. + $options = []; + foreach ($handlers as $handler_id => $message_handler) { + if (!($message_handler instanceof WebformHandlerMessageInterface)) { + continue; + } + + $message = $message_handler->getMessage($webform_submission); + + $options[$handler_id]['title'] = [ + 'data' => [ + 'label' => [ + '#type' => 'label', + '#title' => $message_handler->label() . ': ' . $message_handler->description(), + '#title_display' => NULL, + '#for' => 'edit-message-handler-id-' . str_replace('_', '-', $message_handler->getHandlerId()), + ], + ], + ]; + $options[$handler_id]['id'] = [ + 'data' => $message_handler->getHandlerId(), + ]; + $options[$handler_id]['summary'] = [ + 'data' => $message_handler->getMessageSummary($message), + ]; + $options[$handler_id]['status'] = ($message_handler->isEnabled()) ? $this->t('Enabled') : $this->t('Disabled'); + } + return $options; + } + + /****************************************************************************/ + // Change message handling. + /****************************************************************************/ + + /** + * Change message handler. + * + * @param array $form + * An associative array containing the structure of the form. + * @param \Drupal\Core\Form\FormStateInterface $form_state + * The current state of the form. + */ + public static function changeMessageSubmit(array &$form, FormStateInterface $form_state) { + // Unset the message so that it can be completely rebuilt. + NestedArray::unsetValue($form_state->getUserInput(), ['message']); + $form_state->unsetValue('message'); + + // Rebuild the form. + $form_state->setRebuild(); + } + + /** + * Handles switching between messages. + * + * @param array $form + * An associative array containing the structure of the form. + * @param \Drupal\Core\Form\FormStateInterface $form_state + * The current state of the form. + * + * @return array + * An associative array containing an email message. + */ + public function ajaxMessageCallback(array $form, FormStateInterface $form_state) { + return $form['message']; + } } diff --git a/src/Plugin/WebformElement/BooleanBase.php b/src/Plugin/WebformElement/BooleanBase.php index 5e311dad..64debad9 100644 --- a/src/Plugin/WebformElement/BooleanBase.php +++ b/src/Plugin/WebformElement/BooleanBase.php @@ -24,7 +24,7 @@ abstract class BooleanBase extends WebformElementBase { /** * {@inheritdoc} */ - public function formatTextItem(array $element, WebformSubmissionInterface $webform_submission, array $options = []) { + protected function formatTextItem(array $element, WebformSubmissionInterface $webform_submission, array $options = []) { $value = $this->getValue($element, $webform_submission, $options); $format = $this->getItemFormat($element); diff --git a/src/Plugin/WebformElement/Color.php b/src/Plugin/WebformElement/Color.php index b0c0cd00..cb0fef92 100644 --- a/src/Plugin/WebformElement/Color.php +++ b/src/Plugin/WebformElement/Color.php @@ -53,7 +53,7 @@ class Color extends WebformElementBase { /** * {@inheritdoc} */ - public function formatHtmlItem(array $element, WebformSubmissionInterface $webform_submission, array $options = []) { + protected function formatHtmlItem(array $element, WebformSubmissionInterface $webform_submission, array $options = []) { $value = $this->getValue($element, $webform_submission, $options); if (empty($value)) { diff --git a/src/Plugin/WebformElement/DateBase.php b/src/Plugin/WebformElement/DateBase.php index 4ea4c9ff..1f56acf9 100644 --- a/src/Plugin/WebformElement/DateBase.php +++ b/src/Plugin/WebformElement/DateBase.php @@ -92,7 +92,7 @@ abstract class DateBase extends WebformElementBase { /** * {@inheritdoc} */ - public function formatTextItem(array $element, WebformSubmissionInterface $webform_submission, array $options = []) { + protected function formatTextItem(array $element, WebformSubmissionInterface $webform_submission, array $options = []) { $value = $this->getValue($element, $webform_submission, $options); $timestamp = strtotime($value); @@ -101,7 +101,7 @@ abstract class DateBase extends WebformElementBase { } $format = $this->getItemFormat($element) ?: 'html_' . $this->getDateType($element); - if ($format == 'raw') { + if ($format === 'raw') { return $value; } elseif (DateFormat::load($format)) { diff --git a/src/Plugin/WebformElement/Email.php b/src/Plugin/WebformElement/Email.php index 38ecc79e..6947fec8 100644 --- a/src/Plugin/WebformElement/Email.php +++ b/src/Plugin/WebformElement/Email.php @@ -27,7 +27,7 @@ class Email extends TextBase { /** * {@inheritdoc} */ - public function formatHtmlItem(array $element, WebformSubmissionInterface $webform_submission, array $options = []) { + protected function formatHtmlItem(array $element, WebformSubmissionInterface $webform_submission, array $options = []) { $value = $this->getValue($element, $webform_submission, $options); if (empty($value)) { diff --git a/src/Plugin/WebformElement/LanguageSelect.php b/src/Plugin/WebformElement/LanguageSelect.php index e7e8375f..f8b53fdb 100644 --- a/src/Plugin/WebformElement/LanguageSelect.php +++ b/src/Plugin/WebformElement/LanguageSelect.php @@ -21,7 +21,7 @@ class LanguageSelect extends WebformElementBase { /** * {@inheritdoc} */ - public function formatTextItem(array $element, WebformSubmissionInterface $webform_submission, array $options = []) { + protected function formatTextItem(array $element, WebformSubmissionInterface $webform_submission, array $options = []) { $value = $this->getValue($element, $webform_submission, $options); $language = \Drupal::languageManager()->getLanguage($value); diff --git a/src/Plugin/WebformElement/Password.php b/src/Plugin/WebformElement/Password.php index 85905ef9..c97b692f 100644 --- a/src/Plugin/WebformElement/Password.php +++ b/src/Plugin/WebformElement/Password.php @@ -21,7 +21,7 @@ class Password extends WebformElementBase { /** * {@inheritdoc} */ - public function formatTextItem(array $element, WebformSubmissionInterface $webform_submission, array $options = []) { + protected function formatTextItem(array $element, WebformSubmissionInterface $webform_submission, array $options = []) { $value = $this->getValue($element, $webform_submission, $options); // Return empty value. diff --git a/src/Plugin/WebformElement/Table.php b/src/Plugin/WebformElement/Table.php index 7612866a..60fdcb77 100644 --- a/src/Plugin/WebformElement/Table.php +++ b/src/Plugin/WebformElement/Table.php @@ -102,7 +102,7 @@ class Table extends WebformElementBase { /** * {@inheritdoc} */ - public function formatHtmlItem(array $element, WebformSubmissionInterface $webform_submission, array $options = []) { + protected function formatHtmlItem(array $element, WebformSubmissionInterface $webform_submission, array $options = []) { $rows = []; foreach ($element as $row_key => $row_element) { if (Element::property($row_key)) { @@ -138,7 +138,7 @@ class Table extends WebformElementBase { /** * {@inheritdoc} */ - public function formatTextItem(array $element, WebformSubmissionInterface $webform_submission, array $options = []) { + protected function formatTextItem(array $element, WebformSubmissionInterface $webform_submission, array $options = []) { // Render the HTML table. $build = $this->formatHtml($element, $webform_submission, $options); $html = \Drupal::service('renderer')->renderPlain($build); diff --git a/src/Plugin/WebformElement/Telephone.php b/src/Plugin/WebformElement/Telephone.php index 9483c0eb..d142144d 100644 --- a/src/Plugin/WebformElement/Telephone.php +++ b/src/Plugin/WebformElement/Telephone.php @@ -85,7 +85,7 @@ class Telephone extends TextBase { /** * {@inheritdoc} */ - public function formatHtmlItem(array $element, WebformSubmissionInterface $webform_submission, array $options = []) { + protected function formatHtmlItem(array $element, WebformSubmissionInterface $webform_submission, array $options = []) { $value = $this->getValue($element, $webform_submission, $options); if (empty($value)) { diff --git a/src/Plugin/WebformElement/TextFormat.php b/src/Plugin/WebformElement/TextFormat.php index f3db0b22..7cad20d2 100644 --- a/src/Plugin/WebformElement/TextFormat.php +++ b/src/Plugin/WebformElement/TextFormat.php @@ -121,7 +121,7 @@ class TextFormat extends WebformElementBase { /** * {@inheritdoc} */ - public function formatHtmlItem(array $element, WebformSubmissionInterface $webform_submission, array $options = []) { + protected function formatHtmlItem(array $element, WebformSubmissionInterface $webform_submission, array $options = []) { $value = $this->getValue($element, $webform_submission, $options); $format = (isset($value['format'])) ? $value['format'] : $this->getItemFormat($element); @@ -139,7 +139,7 @@ class TextFormat extends WebformElementBase { /** * {@inheritdoc} */ - public function formatTextItem(array $element, WebformSubmissionInterface $webform_submission, array $options = []) { + protected function formatTextItem(array $element, WebformSubmissionInterface $webform_submission, array $options = []) { $value = $this->getValue($element, $webform_submission, $options); $format = (isset($value['format'])) ? $value['format'] : $this->getItemFormat($element); diff --git a/src/Plugin/WebformElement/Textarea.php b/src/Plugin/WebformElement/Textarea.php index 49938bc6..f3503304 100644 --- a/src/Plugin/WebformElement/Textarea.php +++ b/src/Plugin/WebformElement/Textarea.php @@ -88,7 +88,7 @@ class Textarea extends TextBase { /** * {@inheritdoc} */ - public function formatHtmlItem(array $element, WebformSubmissionInterface $webform_submission, array $options = []) { + protected function formatHtmlItem(array $element, WebformSubmissionInterface $webform_submission, array $options = []) { $value = $this->getValue($element, $webform_submission, $options); return [ diff --git a/src/Plugin/WebformElement/Url.php b/src/Plugin/WebformElement/Url.php index 7b7c4c77..97f955ba 100644 --- a/src/Plugin/WebformElement/Url.php +++ b/src/Plugin/WebformElement/Url.php @@ -27,7 +27,7 @@ class Url extends TextBase { /** * {@inheritdoc} */ - public function formatHtmlItem(array $element, WebformSubmissionInterface $webform_submission, array $options = []) { + protected function formatHtmlItem(array $element, WebformSubmissionInterface $webform_submission, array $options = []) { $value = $this->getValue($element, $webform_submission, $options); if (empty($value)) { diff --git a/src/Plugin/WebformElement/WebformCodeMirror.php b/src/Plugin/WebformElement/WebformCodeMirror.php index d5dbbb55..5767c7aa 100644 --- a/src/Plugin/WebformElement/WebformCodeMirror.php +++ b/src/Plugin/WebformElement/WebformCodeMirror.php @@ -34,7 +34,7 @@ class WebformCodeMirror extends WebformElementBase { /** * {@inheritdoc} */ - public function formatHtmlItem(array $element, WebformSubmissionInterface $webform_submission, array $options = []) { + protected function formatHtmlItem(array $element, WebformSubmissionInterface $webform_submission, array $options = []) { $value = $this->getValue($element, $webform_submission, $options); if (empty($value)) { diff --git a/src/Plugin/WebformElement/WebformCompositeBase.php b/src/Plugin/WebformElement/WebformCompositeBase.php index 017b205a..04688512 100644 --- a/src/Plugin/WebformElement/WebformCompositeBase.php +++ b/src/Plugin/WebformElement/WebformCompositeBase.php @@ -330,7 +330,7 @@ abstract class WebformCompositeBase extends WebformElementBase { /** * {@inheritdoc} */ - public function formatHtmlItem(array $element, WebformSubmissionInterface $webform_submission, array $options = []) { + protected function formatHtmlItem(array $element, WebformSubmissionInterface $webform_submission, array $options = []) { if (!$this->hasValue($element, $webform_submission, $options)) { return ''; } @@ -424,7 +424,7 @@ abstract class WebformCompositeBase extends WebformElementBase { /** * {@inheritdoc} */ - public function formatTextItem(array $element, WebformSubmissionInterface $webform_submission, array $options = []) { + protected function formatTextItem(array $element, WebformSubmissionInterface $webform_submission, array $options = []) { if (!$this->hasValue($element, $webform_submission, $options)) { return ''; } diff --git a/src/Plugin/WebformElement/WebformEmailMultiple.php b/src/Plugin/WebformElement/WebformEmailMultiple.php index f38b43e6..8622a5d6 100644 --- a/src/Plugin/WebformElement/WebformEmailMultiple.php +++ b/src/Plugin/WebformElement/WebformEmailMultiple.php @@ -28,7 +28,7 @@ class WebformEmailMultiple extends Email { /** * {@inheritdoc} */ - public function formatHtmlItem(array $element, WebformSubmissionInterface $webform_submission, array $options = []) { + protected function formatHtmlItem(array $element, WebformSubmissionInterface $webform_submission, array $options = []) { $value = $this->getValue($element, $webform_submission, $options); if (empty($value)) { diff --git a/src/Plugin/WebformElement/WebformEntityReferenceTrait.php b/src/Plugin/WebformElement/WebformEntityReferenceTrait.php index ff1702e5..c2302365 100644 --- a/src/Plugin/WebformElement/WebformEntityReferenceTrait.php +++ b/src/Plugin/WebformElement/WebformEntityReferenceTrait.php @@ -38,7 +38,7 @@ trait WebformEntityReferenceTrait { /** * {@inheritdoc} */ - public function formatHtmlItem(array $element, WebformSubmissionInterface $webform_submission, array $options = []) { + protected function formatHtmlItem(array $element, WebformSubmissionInterface $webform_submission, array $options = []) { $entity = $this->getTargetEntity($element, $webform_submission, $options); if (!$entity) { return ''; @@ -69,7 +69,7 @@ trait WebformEntityReferenceTrait { /** * {@inheritdoc} */ - public function formatTextItem(array $element, WebformSubmissionInterface $webform_submission, array $options = []) { + protected function formatTextItem(array $element, WebformSubmissionInterface $webform_submission, array $options = []) { $entity = $this->getTargetEntity($element, $webform_submission, $options); if (!$entity) { return ''; diff --git a/src/Plugin/WebformElement/WebformLikert.php b/src/Plugin/WebformElement/WebformLikert.php index 1d32a698..b3ea3618 100644 --- a/src/Plugin/WebformElement/WebformLikert.php +++ b/src/Plugin/WebformElement/WebformLikert.php @@ -87,7 +87,7 @@ class WebformLikert extends WebformElementBase { /** * {@inheritdoc} */ - public function formatHtmlItem(array $element, WebformSubmissionInterface $webform_submission, array $options = []) { + protected function formatHtmlItem(array $element, WebformSubmissionInterface $webform_submission, array $options = []) { $value = $this->getValue($element, $webform_submission, $options); $format = $this->getItemFormat($element); @@ -170,7 +170,7 @@ class WebformLikert extends WebformElementBase { /** * {@inheritdoc} */ - public function formatTextItem(array $element, WebformSubmissionInterface $webform_submission, array $options = []) { + protected function formatTextItem(array $element, WebformSubmissionInterface $webform_submission, array $options = []) { $value = $this->getValue($element, $webform_submission, $options); $format = $this->getItemFormat($element); diff --git a/src/Plugin/WebformElement/WebformLocation.php b/src/Plugin/WebformElement/WebformLocation.php index 78e40a10..c5485086 100644 --- a/src/Plugin/WebformElement/WebformLocation.php +++ b/src/Plugin/WebformElement/WebformLocation.php @@ -99,7 +99,7 @@ class WebformLocation extends WebformCompositeBase { /** * {@inheritdoc} */ - public function formatHtmlItem(array $element, WebformSubmissionInterface $webform_submission, array $options = []) { + protected function formatHtmlItem(array $element, WebformSubmissionInterface $webform_submission, array $options = []) { $value = $this->getValue($element, $webform_submission, $options); // Return empty value. diff --git a/src/Plugin/WebformElement/WebformMapping.php b/src/Plugin/WebformElement/WebformMapping.php index e358c753..a4d02319 100644 --- a/src/Plugin/WebformElement/WebformMapping.php +++ b/src/Plugin/WebformElement/WebformMapping.php @@ -99,7 +99,7 @@ class WebformMapping extends WebformElementBase { /** * {@inheritdoc} */ - public function formatHtmlItem(array $element, WebformSubmissionInterface $webform_submission, array $options = []) { + protected function formatHtmlItem(array $element, WebformSubmissionInterface $webform_submission, array $options = []) { $value = $this->getValue($element, $webform_submission, $options); $element += [ @@ -172,7 +172,7 @@ class WebformMapping extends WebformElementBase { /** * {@inheritdoc} */ - public function formatTextItem(array $element, WebformSubmissionInterface $webform_submission, array $options = []) { + protected function formatTextItem(array $element, WebformSubmissionInterface $webform_submission, array $options = []) { if ($this->hasValue($element, $webform_submission, $options)) { return ''; } diff --git a/src/Plugin/WebformElement/WebformRating.php b/src/Plugin/WebformElement/WebformRating.php index 8f041b08..a365f1a2 100644 --- a/src/Plugin/WebformElement/WebformRating.php +++ b/src/Plugin/WebformElement/WebformRating.php @@ -60,7 +60,7 @@ class WebformRating extends Range { /** * {@inheritdoc} */ - public function formatHtmlItem(array $element, WebformSubmissionInterface $webform_submission, array $options = []) { + protected function formatHtmlItem(array $element, WebformSubmissionInterface $webform_submission, array $options = []) { $value = $this->getValue($element, $webform_submission, $options); $format = $this->getItemFormat($element); diff --git a/src/Plugin/WebformElement/WebformSignature.php b/src/Plugin/WebformElement/WebformSignature.php index aa96ace8..e5525eb4 100644 --- a/src/Plugin/WebformElement/WebformSignature.php +++ b/src/Plugin/WebformElement/WebformSignature.php @@ -47,7 +47,7 @@ class WebformSignature extends WebformElementBase { /** * {@inheritdoc} */ - public function formatHtmlItem(array $element, WebformSubmissionInterface $webform_submission, array $options = []) { + protected function formatHtmlItem(array $element, WebformSubmissionInterface $webform_submission, array $options = []) { $value = $this->getValue($element, $webform_submission, $options); $format = $this->getItemFormat($element); @@ -77,7 +77,7 @@ class WebformSignature extends WebformElementBase { /** * {@inheritdoc} */ - public function formatTextItem(array $element, WebformSubmissionInterface $webform_submission, array $options = []) { + protected function formatTextItem(array $element, WebformSubmissionInterface $webform_submission, array $options = []) { $format = $this->getItemFormat($element); switch ($format) { case 'image': diff --git a/src/Plugin/WebformElement/WebformTime.php b/src/Plugin/WebformElement/WebformTime.php index 80bbc82b..6739342e 100644 --- a/src/Plugin/WebformElement/WebformTime.php +++ b/src/Plugin/WebformElement/WebformTime.php @@ -49,7 +49,7 @@ class WebformTime extends WebformElementBase { /** * {@inheritdoc} */ - public function formatTextItem(array $element, WebformSubmissionInterface $webform_submission, array $options = []) { + protected function formatTextItem(array $element, WebformSubmissionInterface $webform_submission, array $options = []) { $value = $this->getValue($element, $webform_submission, $options); if (empty($value)) { diff --git a/src/Plugin/WebformElement/WebformToggle.php b/src/Plugin/WebformElement/WebformToggle.php index 84dee25e..88f06fbc 100644 --- a/src/Plugin/WebformElement/WebformToggle.php +++ b/src/Plugin/WebformElement/WebformToggle.php @@ -36,7 +36,7 @@ class WebformToggle extends Checkbox { /** * {@inheritdoc} */ - public function formatTextItem(array $element, WebformSubmissionInterface $webform_submission, array $options = []) { + protected function formatTextItem(array $element, WebformSubmissionInterface $webform_submission, array $options = []) { $value = $this->getValue($element, $webform_submission, $options); $format = $this->getItemFormat($element); diff --git a/src/Plugin/WebformElementBase.php b/src/Plugin/WebformElementBase.php index 02597f3f..88e3f6f3 100644 --- a/src/Plugin/WebformElementBase.php +++ b/src/Plugin/WebformElementBase.php @@ -14,6 +14,7 @@ use Drupal\Core\Form\OptGroup; use Drupal\Core\Link; use Drupal\Core\Render\Element; use Drupal\Core\Render\ElementInfoManagerInterface; +use Drupal\Core\Render\Markup; use Drupal\Core\Session\AccountInterface; use Drupal\Core\StringTranslation\StringTranslationTrait; use Drupal\Core\Url; @@ -1366,31 +1367,42 @@ class WebformElementBase extends PluginBase implements WebformElementInterface { * @param array $options * An array of options. * - * @return string + * @return \Drupal\Component\Render\MarkupInterface|string * The element's value formatted as text. + * Use Markup::create() to make sure the element's value is not double + * escaped when used as a token. + * + * @see _webform_token_get_submission_value() */ protected function formatTextItem(array $element, WebformSubmissionInterface $webform_submission, array $options = []) { $value = $this->getValue($element, $webform_submission, $options); $format = $this->getItemFormat($element); - if ($format != 'raw') { - // Escape all HTML entities. - if (is_string($value)) { - $value = Html::escape($value); - } + if ($format === 'raw') { + return Markup::create($value); + } - // Apply #field prefix and #field_suffix to value. - if (isset($element['#type'])) { - if (isset($element['#field_prefix'])) { - $value = $element['#field_prefix'] . $value; - } - if (isset($element['#field_suffix'])) { - $value .= $element['#field_suffix']; - } - } + // Build a render that used #plain_text so that HTML characters are escaped. + // @see \Drupal\Core\Render\Renderer::ensureMarkupIsSafe + if ($value === '0') { + // Issue #2765609: #plain_text doesn't render empty-like values + // (e.g. 0 and "0"). + // Workaround: Use #markup until this issue is fixed. + $build = ['#markup' => $value]; + } + else { + $build = ['#plain_text' => $value]; } - return $value; + // Apply #field prefix and #field_suffix to the render array. + if (isset($element['#field_prefix'])) { + $build['#prefix'] = $element['#field_prefix']; + } + if (isset($element['#field_suffix'])) { + $build['#suffix'] = $element['#field_suffix']; + } + + return \Drupal::service('renderer')->renderPlain($build); } /** diff --git a/src/Plugin/WebformHandler/EmailWebformHandler.php b/src/Plugin/WebformHandler/EmailWebformHandler.php index 13134e8b..650690ce 100644 --- a/src/Plugin/WebformHandler/EmailWebformHandler.php +++ b/src/Plugin/WebformHandler/EmailWebformHandler.php @@ -676,10 +676,6 @@ class EmailWebformHandler extends WebformHandlerBase implements WebformHandlerMe if ($this->configuration['html'] && $this->supportsHtml()) { $message['body'] = WebformHtmlEditor::checkMarkup($message['body'], TRUE); } - else { - // Decode HTML entities in plain text body. - $message['body'] = Html::decodeEntities($message['body']); - } // Add attachments. $message['attachments'] = $this->getMessageAttachments($webform_submission); diff --git a/src/Tests/Handler/WebformHandlerEmailBasicTest.php b/src/Tests/Handler/WebformHandlerEmailBasicTest.php index b7ee9163..e7360ec9 100644 --- a/src/Tests/Handler/WebformHandlerEmailBasicTest.php +++ b/src/Tests/Handler/WebformHandlerEmailBasicTest.php @@ -111,14 +111,14 @@ class WebformHandlerEmailBasicTest extends WebformTestBase { // Drupal strip_tags() from mail subject. // @see \Drupal\Core\Mail\MailManager::doMail // @see http://cgit.drupalcode.org/drupal/tree/core/lib/Drupal/Core/Mail/MailManager.php#n285 - 'subject' => 'This has "special" \'chararacters\'', - 'message' => 'This has "special" \'chararacters\'', + 'subject' => 'This has "special" \'characters\'', + 'message' => 'This has "special" \'characters\'', ]; $this->postSubmission($webform, $edit); $sent_email = $this->getLastEmail(); $this->assertEqual($sent_email['reply-to'], '"first_name" "last_name" '); - $this->assertEqual($sent_email['subject'], 'This has "special" \'chararacters\''); - $this->assertEqual($sent_email['body'], 'This has "special" \'chararacters\'' . PHP_EOL); + $this->assertEqual($sent_email['subject'], 'This has "special" \'characters\''); + $this->assertEqual($sent_email['body'], 'This has "special" \'characters\'' . PHP_EOL); } } diff --git a/src/Twig/TwigExtension.php b/src/Twig/TwigExtension.php index 52de4bbc..d132d793 100644 --- a/src/Twig/TwigExtension.php +++ b/src/Twig/TwigExtension.php @@ -60,9 +60,6 @@ class TwigExtension extends \Twig_Extension { /** @var \Drupal\webform\WebformTokenManagerInterface $value */ $value = \Drupal::service('webform.token_manager')->replace($token, $entity, $data, $options); - // Must decode HTML entities which are going to re-encoded. - $value = Html::decodeEntities($value); - return (WebformHtmlHelper::containsHtml($value)) ? ['#markup' => $value] : $value; } diff --git a/tests/modules/webform_test/config/install/webform.webform.test_rendering.yml b/tests/modules/webform_test/config/install/webform.webform.test_rendering.yml new file mode 100644 index 00000000..16fc2b1c --- /dev/null +++ b/tests/modules/webform_test/config/install/webform.webform.test_rendering.yml @@ -0,0 +1,220 @@ +langcode: en +status: open +dependencies: + enforced: + module: + - webform_test +open: null +close: null +uid: null +template: false +id: test_rendering +title: 'Test: Rendering' +description: 'Test Webform and Webform Submission rendering' +category: 'Test: Rendering' +elements: | + submission_label: + '#type': textfield + '#title': submission_label + '#description': 'Check HTML markup and special characters in submission title' + '#default_value': 'submission label (&><#)' + textfield_plain_text: + '#type': textfield + '#title': textfield_plain_text + '#description': 'This is a plain text description.' + '#field_suffix': '{suffix}' + '#field_prefix': '{prefix}' + '#default_value': '{default_value}' + textfield_markup: + '#type': textfield + '#title': 'textfield_markup' + '#description': 'This is a description with HTML markup.' + '#field_suffix': '{suffix}' + '#field_prefix': '{prefix}' + '#default_value': '{default_value}' + textfield_special_characters: + '#type': textfield + '#title': 'textfield_special_characters (&><#)' + '#description': 'This is a description with special characters (&><#).' + '#field_suffix': '(&><#)' + '#field_prefix': '(&><#)' + '#default_value': '{default_value}' + text_format_basic_html: + '#type': text_format + '#title': text_format_basic_html + '#allowed_formats': + basic_html: basic_html + '#default_value': + value: '

{default_value}

' + format: basic_html +css: '' +javascript: '' +settings: + ajax: false + ajax_scroll_top: form + page: true + page_submit_path: '' + page_confirm_path: '' + form_submit_once: false + form_exception_message: '' + form_open_message: '' + form_close_message: '' + form_previous_submissions: true + form_confidential: false + form_confidential_message: '' + form_convert_anonymous: false + form_prepopulate: false + form_prepopulate_source_entity: false + form_prepopulate_source_entity_required: false + form_prepopulate_source_entity_type: '' + form_reset: false + form_disable_autocomplete: false + form_novalidate: false + form_unsaved: false + form_disable_back: false + form_submit_back: false + form_autofocus: false + form_details_toggle: false + form_login: false + form_login_message: '' + submission_label: '[webform_submission:values:submission_label]' + submission_log: false + submission_user_columns: { } + submission_login: false + submission_login_message: '' + wizard_progress_bar: true + wizard_progress_pages: false + wizard_progress_percentage: false + wizard_start_label: '' + wizard_confirmation: true + wizard_confirmation_label: '' + wizard_track: '' + preview: 1 + preview_label: '' + preview_title: '' + preview_message: '' + preview_attributes: { } + preview_excluded_elements: { } + preview_exclude_empty: true + draft: none + draft_multiple: false + draft_auto_save: false + draft_saved_message: '' + draft_loaded_message: '' + confirmation_type: page + confirmation_title: '' + confirmation_message: '' + confirmation_url: '' + confirmation_attributes: { } + confirmation_back: true + confirmation_back_label: '' + confirmation_back_attributes: { } + limit_total: null + limit_total_message: '' + limit_user: null + limit_user_message: '' + purge: none + purge_days: null + entity_limit_total: null + entity_limit_user: null + results_disabled: false + results_disabled_ignore: false + token_update: true +access: + create: + roles: + - anonymous + - authenticated + users: { } + permissions: { } + view_any: + roles: { } + users: { } + permissions: { } + update_any: + roles: { } + users: { } + permissions: { } + delete_any: + roles: { } + users: { } + permissions: { } + purge_any: + roles: { } + users: { } + permissions: { } + view_own: + roles: { } + users: { } + permissions: { } + update_own: + roles: { } + users: { } + permissions: { } + delete_own: + roles: { } + users: { } + permissions: { } +handlers: + email_html: + id: email + label: 'Email html' + handler_id: email_html + status: true + conditions: { } + weight: 0 + settings: + states: + - completed + to_mail: default + to_options: { } + cc_mail: '' + cc_options: { } + bcc_mail: '' + bcc_options: { } + from_mail: default + from_options: { } + from_name: default + subject: '[webform_submission:values:submission_label:raw]' + body: default + excluded_elements: { } + ignore_access: false + exclude_empty: true + html: true + attachments: false + debug: true + reply_to: '' + return_path: '' + sender_mail: '' + sender_name: '' + email_text: + id: email + label: 'Email text' + handler_id: email_text + status: true + conditions: { } + weight: 0 + settings: + states: + - completed + to_mail: default + to_options: { } + cc_mail: '' + cc_options: { } + bcc_mail: '' + bcc_options: { } + from_mail: default + from_options: { } + from_name: default + subject: '[webform_submission:values:submission_label:raw]' + body: default + excluded_elements: { } + ignore_access: false + exclude_empty: true + html: false + attachments: false + debug: true + reply_to: '' + return_path: '' + sender_mail: '' + sender_name: '' diff --git a/tests/src/Unit/Access/WebformAccessCheckTest.php b/tests/src/Unit/Access/WebformAccessCheckTest.php index bfb9b152..5aff512b 100644 --- a/tests/src/Unit/Access/WebformAccessCheckTest.php +++ b/tests/src/Unit/Access/WebformAccessCheckTest.php @@ -98,9 +98,9 @@ class WebformAccessCheckTest extends UnitTestCase { $this->assertEquals(AccessResult::neutral(), WebformAccountAccess::checkOverviewAccess($account)); $this->assertEquals(AccessResult::allowed(), WebformAccountAccess::checkOverviewAccess($submission_manager_account)); - // Check email access. - $this->assertEquals(AccessResult::forbidden(), WebformSubmissionAccess::checkEmailAccess($webform_submission, $account)); - $this->assertEquals(AccessResult::allowed(), WebformSubmissionAccess::checkEmailAccess($email_webform_submission, $submission_manager_account)); + // Check resend (email) message access. + $this->assertEquals(AccessResult::forbidden(), WebformSubmissionAccess::checkResendAccess($webform_submission, $account)); + $this->assertEquals(AccessResult::allowed(), WebformSubmissionAccess::checkResendAccess($email_webform_submission, $submission_manager_account)); // @todo Fix below access check which is looping through the node's fields. // Check entity results access. diff --git a/webform.routing.yml b/webform.routing.yml index 9562e8f9..c84afdc6 100644 --- a/webform.routing.yml +++ b/webform.routing.yml @@ -528,7 +528,7 @@ entity.webform_submission.resend_form: _form: 'Drupal\webform\Form\WebformSubmissionResendForm' _title_callback: 'Drupal\webform\Controller\WebformSubmissionController::title' requirements: - _custom_access: '\Drupal\webform\Access\WebformSubmissionAccess::checkEmailAccess' + _custom_access: '\Drupal\webform\Access\WebformSubmissionAccess::checkResendAccess' entity.webform_submission.duplicate_form: path: '/admin/structure/webform/manage/{webform}/submission/{webform_submission}/duplicate' diff --git a/webform.tokens.inc b/webform.tokens.inc index 0299c20b..565dc08b 100644 --- a/webform.tokens.inc +++ b/webform.tokens.inc @@ -714,12 +714,12 @@ function _webform_token_get_submission_value($value_token, array $options, Webfo ]; $entity_token_name = (isset($entity_token_names[$entity_type])) ? $entity_token_names[$entity_type] : $entity_type; $entity_token = implode(':', $keys); - return \Drupal::token()->replace( + return Markup::create(\Drupal::token()->replace( "[$entity_token_name:$entity_token]", [$entity_token_name => $entity], $options, $bubbleable_metadata - ); + )); } else { return ''; @@ -747,19 +747,19 @@ function _webform_token_get_submission_value($value_token, array $options, Webfo if (is_array($token_value)) { // Note, tokens can't include CSS and JS libraries since they will // can be included in an email. - $markup = \Drupal::service('renderer')->renderPlain($token_value); - return $markup; + return \Drupal::service('renderer')->renderPlain($token_value); + } + elseif ($token_value instanceof MarkupInterface) { + return $token_value; } elseif (isset($element['#format']) && $element['#format'] === 'raw') { - // Raw needs return Markup. - if ($token_value instanceof MarkupInterface) { - return $token_value; - } - else { - return Markup::create((string) $token_value); - } + // Make sure raw tokens are always rendered AS-IS. + return Markup::create((string) $token_value); } else { + // All strings will be escaped as HtmlEscapedText. + // @see \Drupal\Core\Utility\Token::replace + // @see \Drupal\Component\Render\HtmlEscapedText return (string) $token_value; } }