diff --git a/config/install/webform.settings.yml b/config/install/webform.settings.yml index 3bb05cd6..222b6926 100644 --- a/config/install/webform.settings.yml +++ b/config/install/webform.settings.yml @@ -80,8 +80,9 @@ elements: excluded_types: { } file: file_public: false + xss_block: true default_max_filesize: '' - default_managed_file_extensions: 'gif jpg png bmp eps tif pict psd txt rtf html odf pdf doc docx ppt pptx xls xlsx xml avi mov mp3 ogg wav bz2 dmg gz jar rar sit svg tar zip' + default_managed_file_extensions: 'gif jpg png bmp eps tif pict psd txt rtf odf pdf doc docx ppt pptx xls xlsx xml avi mov mp3 ogg wav bz2 dmg gz jar rar sit svg tar zip' default_audio_file_extensions: 'mp3 ogg wav' default_document_file_extensions: 'txt rtf pdf doc docx odt ppt pptx odp xls xlsx ods' default_image_file_extensions: 'gif jpg png svg' diff --git a/config/schema/webform.schema.yml b/config/schema/webform.schema.yml index 5b946763..e35db7f0 100644 --- a/config/schema/webform.schema.yml +++ b/config/schema/webform.schema.yml @@ -131,6 +131,9 @@ webform.settings: file_public: type: boolean label: 'Allow files to be uploaded to public file system.' + xss_block: + type: boolean + label: 'Block any files that may contain Cross-Site Scripting (XSS).' default_max_filesize: type: string label: 'Default maximum upload size' diff --git a/js/jquery.common_ancestor.js b/js/jquery.common_ancestor.js deleted file mode 100644 index 06105678..00000000 --- a/js/jquery.common_ancestor.js +++ /dev/null @@ -1,41 +0,0 @@ -/** - * @file - * Jquery plugin to find common ancestor. - * - * @see http://stackoverflow.com/questions/3217147/jquery-first-parent-containing-all-children - */ - -(function ($) { - - 'use strict'; - - jQuery.fn.commonAncestor = function() { - var parents = []; - var minlen = Infinity; - - $(this).each(function() { - var curparents = $(this).parents(); - parents.push(curparents); - minlen = Math.min(minlen, curparents.length); - }); - - for (var i in parents) { - parents[i] = parents[i].slice(parents[i].length - minlen); - } - - // Iterate until equality is found - for (var i = 0; i < parents[0].length; i++) { - var equal = true; - for (var j in parents) { - if (parents[j][i] != parents[0][i]) { - equal = false; - break; - } - } - if (equal) return $(parents[0][i]); - } - return $([]); - } - -})(jQuery); - diff --git a/js/webform.element.buttons.js b/js/webform.element.buttons.js index 8f9b7d05..bd311aee 100644 --- a/js/webform.element.buttons.js +++ b/js/webform.element.buttons.js @@ -14,7 +14,7 @@ */ Drupal.behaviors.webformButtons = { attach: function (context) { - $(context).find('.js-webform-buttons [type="radio"]').commonAncestor().once('webform-buttons').each(function () { + $(context).find('fieldset.js-webform-buttons div.fieldset-wrapper').once('webform-buttons').each(function () { var $input = $(this); // Remove all div and classes around radios and labels. $input.html($input.find('input[type="radio"], label').removeClass()); @@ -29,6 +29,7 @@ $input.buttonset('option', 'disabled', $input.find('input[type="radio"]:disabled').length); }); }); + } }; diff --git a/src/Entity/WebformOptions.php b/src/Entity/WebformOptions.php index e8bb4532..07040ecd 100644 --- a/src/Entity/WebformOptions.php +++ b/src/Entity/WebformOptions.php @@ -139,8 +139,6 @@ class WebformOptions extends ConfigEntityBase implements WebformOptionsInterface */ public static function getElementOptions(array $element, $property_name = '#options') { // If element already has #options return them. - // NOTE: Only WebformOptions can be altered. If you need to alter an - // element's options, @see hook_webform_element_alter(). if (is_array($element[$property_name])) { return $element[$property_name]; } @@ -155,16 +153,14 @@ class WebformOptions extends ConfigEntityBase implements WebformOptionsInterface $id = $element[$property_name]; if ($webform_options = WebformOptions::load($id)) { $options = $webform_options->getOptions(); - } - else { - $options = []; + if ($options) { + return $options; + } } - // Alter options using hook_webform_options_alter() - // and/or hook_webform_options_WEBFORM_OPTIONS_ID_alter() hook. - // @see webform.api.php + // Get options using alter hook. + $options = []; \Drupal::moduleHandler()->alter('webform_options_' . $id, $options, $element); - \Drupal::moduleHandler()->alter('webform_options', $options, $element, $id); // Log empty options. if (empty($options)) { diff --git a/src/Form/WebformAdminHtmlToTextForm.php b/src/Form/WebformAdminHtmlToTextForm.php new file mode 100644 index 00000000..54ff3491 --- /dev/null +++ b/src/Form/WebformAdminHtmlToTextForm.php @@ -0,0 +1,226 @@ +fileStorage = $file_storage; + $this->entityQueryFactory = $entity_query; + } + + /** + * {@inheritdoc} + */ + public static function create(ContainerInterface $container) { + return new static( + $container->get('entity.manager')->getStorage('file'), + $container->get('entity.query') + ); + } + + /** + * {@inheritdoc} + */ + public function getFormId() { + return 'webform_html_to_text'; + } + + /** + * {@inheritdoc} + */ + public function getQuestion() { + return $this->t('Are you sure you want to convert @total files(s) from HTML to text?', ['@total' => $this->getTotal()]); + } + + /** + * {@inheritdoc} + */ + public function getDescription() { + return '

' . $this->t('All *.html and *.hml file extensions will be suffixed with *.txt, this will force all HTML files to be displayed as plain text.') . '

' . + '

' . $this->t('This action cannot be undone.') . '

'; + } + + /** + * {@inheritdoc} + */ + public function getConfirmText() { + return $this->t('Convert HTML files to text'); + } + + /** + * {@inheritdoc} + */ + public function getCancelUrl() { + return new Url('system.status'); + } + + /** + * {@inheritdoc} + */ + public function buildForm(array $form, FormStateInterface $form_state) { + return parent::buildForm($form, $form_state); + } + + /** + * {@inheritdoc} + */ + public function submitForm(array &$form, FormStateInterface $form_state) { + $form_state->setRedirectUrl($this->getCancelUrl()); + $this->batchSet(); + } + + /** + * Message to displayed after HTML files have been converted. + * + * @return \Drupal\Core\StringTranslation\TranslatableMarkup + * Message to be displayed after HTML files have been converted. + */ + public function getFinishedMessage() { + return $this->t('HTML files conversion to text completed.'); + } + + /** + * Batch API; Initialize batch operations. + */ + public function batchSet() { + $parameters = []; + $batch = [ + 'title' => $this->t('Convert HTML files to text'), + 'init_message' => $this->t('Convert HTML files to text'), + 'error_message' => $this->t('The HTML files could not be converted to text because an error occurred.'), + 'operations' => [ + [[$this, 'batchProcess'], $parameters], + ], + 'finished' => [$this, 'batchFinish'], + ]; + + batch_set($batch); + } + + /** + * Batch API callback; Convert HTML files to text. + * + * @param mixed|array $context + * The batch current context. + */ + public function batchProcess(&$context) { + if (empty($context['sandbox'])) { + $context['sandbox']['progress'] = 0; + $context['sandbox']['max'] = $this->getTotal(); + } + + $entity_ids = $this->getQuery()->range(0, $this->batchLimit)->execute(); + if ($entity_ids) { + /** @var \Drupal\file\FileInterface[] $files */ + $files = $this->fileStorage->loadMultiple($entity_ids); + foreach ($files as $file) { + $file->setMimeType('text/plain'); + if (file_move($file, $file->getFileUri() .'.txt')) { + $file->setFilename($file->getFilename() .'.txt'); + $file->setFileUri($file->getFileUri() .'.txt'); + $file->setMimeType('text/plain'); + $file->save(); + } + } + } + + // Track progress. + $context['sandbox']['progress'] += count($entity_ids); + + // Display progress. + $context['message'] = $this->t('Converting @count of @total files...', ['@count' => $context['sandbox']['progress'], '@total' => $context['sandbox']['max']]); + + // Track finished. + if ($context['sandbox']['progress'] != $context['sandbox']['max']) { + $context['finished'] = $context['sandbox']['progress'] / $context['sandbox']['max']; + } + } + + /** + * Batch API callback; Completed conversion. + * + * @param bool $success + * TRUE if batch successfully completed. + * @param array $results + * Batch results. + * @param array $operations + * An array of function calls (not used in this function). + */ + public function batchFinish($success = FALSE, array $results, array $operations) { + if (!$success) { + drupal_set_message($this->t('Finished with an error.')); + } + else { + drupal_set_message($this->getFinishedMessage()); + } + } + + /** + * Get an entity query containing all HTML files. + * + * @return \Drupal\Core\Entity\Query\QueryInterface + * An entity query containing all webform HTML files. + */ + protected function getQuery() { + $query = $this->entityQueryFactory->get('file'); + $query ->condition('uri', '%://webform/%', 'LIKE') + ->condition( + $query->orConditionGroup() + ->condition('filename', '%.htm', 'LIKE') + ->condition('filename', '%.html', 'LIKE') + ); + $query->sort('fid'); + return $query; + } + + /** + * Get the total number of HTML files. + * + * @return int + * The total number of HTML files. + */ + protected function getTotal() { + return $this->getQuery()->count()->execute(); + } + +} diff --git a/src/Form/WebformAdminSettingsForm.php b/src/Form/WebformAdminSettingsForm.php index 6ab27b54..6502867a 100644 --- a/src/Form/WebformAdminSettingsForm.php +++ b/src/Form/WebformAdminSettingsForm.php @@ -460,6 +460,13 @@ class WebformAdminSettingsForm extends ConfigFormBase { '#return_value' => TRUE, '#default_value' => $config->get('file.file_public'), ]; + $form['file']['xss_block'] = [ + '#type' => 'checkbox', + '#title' => $this->t('Block any files that may contain Cross-Site Scripting (XSS).'), + '#description' => $this->t('HTML files (*.html and *.html) can contain Cross-Site Scripting (XSS), which can allow a malicious user to take over your website.', [':href' => 'https://en.wikipedia.org/wiki/Cross-site_scripting']), + '#return_value' => TRUE, + '#default_value' => $config->get('file.xss_block'), + ]; $form['file']['default_max_filesize'] = [ '#type' => 'textfield', '#title' => $this->t('Default maximum upload size'), diff --git a/src/Plugin/WebformElement/WebformManagedFileBase.php b/src/Plugin/WebformElement/WebformManagedFileBase.php index af627f8c..4fd4422e 100644 --- a/src/Plugin/WebformElement/WebformManagedFileBase.php +++ b/src/Plugin/WebformElement/WebformManagedFileBase.php @@ -458,6 +458,12 @@ abstract class WebformManagedFileBase extends WebformElementBase { if (!empty($element['#file_extensions'])) { $file_extensions = $element['#file_extensions']; } + + // Block XSS file extensions. This include *.html and *.htm. + if ($this->configFactory->get('webform.settings')->get('file.xss_block')) { + $file_extensions = trim(preg_replace('/\s*(html|htm)\s*/', ' ', $file_extensions)); + } + return $file_extensions; } @@ -598,6 +604,10 @@ abstract class WebformManagedFileBase extends WebformElementBase { '#description' => $this->t('A list of additional file extensions for this upload field, separated by spaces.'), '#maxlength' => 255, ]; + // Block XSS file extensions. This includes *.html and *.htm. + if ($this->configFactory->get('webform.settings')->get('file.xss_block')) { + $form['file']['file_extensions']['#description'] .= '
' . $this->t('HTML files (*.html and *.htm) are not permitted because they can contain malicious code.') . ''; + } $form['file']['multiple'] = [ '#title' => $this->t('Multiple'), '#type' => 'checkbox', diff --git a/src/Tests/Element/WebformElementManagedFileHtmlTest.php b/src/Tests/Element/WebformElementManagedFileHtmlTest.php new file mode 100644 index 00000000..a79b3d43 --- /dev/null +++ b/src/Tests/Element/WebformElementManagedFileHtmlTest.php @@ -0,0 +1,84 @@ +drupalGetTestFiles('html'); + $admin_user = $this->createUser(['administer site configuration', 'administer webform']); + $this->drupalLogin($admin_user ); + + // Attempt to upload an HTML file. + $edit = [ + 'files[managed_file_html]' => \Drupal::service('file_system')->realpath($files[1]->uri), + ]; + $this->postSubmission($webform, $edit); + + // Check that HTML file upload is blocked. + $this->assertFalse($this->getLastFileId()); + + // Remove XSS block. + \Drupal::configFactory() + ->getEditable('webform.settings') + ->set('file.xss_block', FALSE) + ->save(); + + // Upload HTML files + $this->postSubmission($webform, $edit); + + // Check that HTML file upload is allowed. + $this->assertTrue($this->getLastFileId()); + + // Get the 'Status report'. + $this->drupalGet('admin/reports/status'); + + // Check †hat 'Webform files: HTML file uploads' warning is displayed. + $this->assertText('HTML files that may contain Cross-Site Scripting (XSS) have been uploaded. You should convert 1 existing file(s) from HTML to plain text.'); + + // Check †hat 'Webform files: XSS block' warning is displayed. + $this->assertText('Blocking users from uploading HTML files, which may contain Cross-Site Scripting (XSS) is not set.'); + + // Execute 'Convert HTML files to text' batch process. + $this->clickLink('HTML to plain text'); + $this->drupalPostForm(NULL, [], t('Convert HTML files to text')); + + // Check †hat 'Webform files: HTML file uploads' is no longer displayed. + $this->assertUrl('admin/reports/status'); + $this->assertNoText('HTML files that may contain Cross-Site Scripting (XSS) have been uploaded. You should convert 1 existing file(s) from HTML to plain text.'); + } + + /** + * Retrieves the fid of the last inserted file. + */ + protected function getLastFileId() { + return (int) db_query('SELECT MAX(fid) FROM {file_managed}')->fetchField(); + } + +} diff --git a/src/Tests/WebformOptionsTest.php b/src/Tests/WebformOptionsTest.php index 6c141f0b..8a4256de 100644 --- a/src/Tests/WebformOptionsTest.php +++ b/src/Tests/WebformOptionsTest.php @@ -76,11 +76,9 @@ class WebformOptionsTest extends WebformTestBase { // Check invalid options. $this->assertFalse($webform_options->getOptions()); - - // Check hook_webform_options_alter() && hook_webform_options_WEBFORM_OPTIONS_ID_alter(). + // Check hook_webform_options_WEBFORM_OPTIONS_ID_alter(). $this->drupalGet('webform/test_options'); - $this->assertRaw(''); - $this->assertRaw(''); + $this->assertRaw(''); // Check hook_webform_options_WEBFORM_OPTIONS_ID_alter() is not executed // when options are altered. @@ -90,7 +88,7 @@ class WebformOptionsTest extends WebformTestBase { $this->debug($webform_test_options->getOptions()); $this->drupalGet('webform/test_options'); - $this->assertRaw(''); + $this->assertRaw(''); } } diff --git a/tests/modules/webform_test/config/install/webform.webform.test_element.yml b/tests/modules/webform_test/config/install/webform.webform.test_element.yml index 5201df81..f289d137 100644 --- a/tests/modules/webform_test/config/install/webform.webform.test_element.yml +++ b/tests/modules/webform_test/config/install/webform.webform.test_element.yml @@ -52,7 +52,9 @@ elements: | textarea: '#type': textarea '#title': textarea - '#default_value': "{textarea line 1}\n{textarea line 2}\n" + '#default_value': | + {textarea line 1} + {textarea line 2} textfield: '#type': textfield '#title': textfield diff --git a/tests/modules/webform_test/config/install/webform.webform.test_element_attributes.yml b/tests/modules/webform_test/config/install/webform.webform.test_element_attributes.yml index c4c2d1be..a981f38f 100644 --- a/tests/modules/webform_test/config/install/webform.webform.test_element_attributes.yml +++ b/tests/modules/webform_test/config/install/webform.webform.test_element_attributes.yml @@ -13,7 +13,10 @@ elements: | webform_element_attributes: '#type': webform_element_attributes '#title': webform_element_attributes - '#classes': "one\ntwo\nthree\n" + '#classes': | + one + two + three '#class__description': 'This is a custom class description.' '#style__description': 'This is a custom style description.' '#attributes__description': 'This is a custom attributes description.' diff --git a/tests/modules/webform_test/config/install/webform.webform.test_element_format_multi_comp.yml b/tests/modules/webform_test/config/install/webform.webform.test_element_format_multi_comp.yml index 8065d217..5ba6a883 100644 --- a/tests/modules/webform_test/config/install/webform.webform.test_element_format_multi_comp.yml +++ b/tests/modules/webform_test/config/install/webform.webform.test_element_format_multi_comp.yml @@ -22,22 +22,19 @@ elements: | '#title': 'Address (Ordered list)' '#multiple': true '#default_value': - - - address: '10 Main Street' + - address: '10 Main Street' address_2: '10 Main Street' city: Springfield state_province: Alabama postal_code: Loremipsum country: Afghanistan - - - address: '10 Main Street' + - address: '10 Main Street' address_2: '10 Main Street' city: Springfield state_province: Alabama postal_code: Loremipsum country: Afghanistan - - - address: '10 Main Street' + - address: '10 Main Street' address_2: '10 Main Street' city: Springfield state_province: Alabama @@ -49,22 +46,19 @@ elements: | '#title': 'Address (Unordered list)' '#multiple': true '#default_value': - - - address: '10 Main Street' + - address: '10 Main Street' address_2: '10 Main Street' city: Springfield state_province: Alabama postal_code: Loremipsum country: Afghanistan - - - address: '10 Main Street' + - address: '10 Main Street' address_2: '10 Main Street' city: Springfield state_province: Alabama postal_code: Loremipsum country: Afghanistan - - - address: '10 Main Street' + - address: '10 Main Street' address_2: '10 Main Street' city: Springfield state_province: Alabama @@ -76,22 +70,19 @@ elements: | '#title': 'Address (Horizontal rule)' '#multiple': true '#default_value': - - - address: '10 Main Street' + - address: '10 Main Street' address_2: '10 Main Street' city: Springfield state_province: Alabama postal_code: Loremipsum country: Afghanistan - - - address: '10 Main Street' + - address: '10 Main Street' address_2: '10 Main Street' city: Springfield state_province: Alabama postal_code: Loremipsum country: Afghanistan - - - address: '10 Main Street' + - address: '10 Main Street' address_2: '10 Main Street' city: Springfield state_province: Alabama @@ -106,8 +97,7 @@ elements: | '#title': 'Contact (Ordered list)' '#multiple': true '#default_value': - - - name: Loremipsum + - name: Loremipsum company: Loremipsum email: example@example.com phone: 123-456-7890 @@ -117,8 +107,7 @@ elements: | state_province: Alabama postal_code: Loremipsum country: Afghanistan - - - name: Loremipsum + - name: Loremipsum company: Loremipsum email: example@example.com phone: 123-456-7890 @@ -128,8 +117,7 @@ elements: | state_province: Alabama postal_code: Loremipsum country: Afghanistan - - - name: Loremipsum + - name: Loremipsum company: Loremipsum email: example@example.com phone: 123-456-7890 @@ -145,8 +133,7 @@ elements: | '#title': 'Contact (Unordered list)' '#multiple': true '#default_value': - - - name: Loremipsum + - name: Loremipsum company: Loremipsum email: example@example.com phone: 123-456-7890 @@ -156,8 +143,7 @@ elements: | state_province: Alabama postal_code: Loremipsum country: Afghanistan - - - name: Loremipsum + - name: Loremipsum company: Loremipsum email: example@example.com phone: 123-456-7890 @@ -167,8 +153,7 @@ elements: | state_province: Alabama postal_code: Loremipsum country: Afghanistan - - - name: Loremipsum + - name: Loremipsum company: Loremipsum email: example@example.com phone: 123-456-7890 @@ -184,8 +169,7 @@ elements: | '#title': 'Contact (Horizontal rule)' '#multiple': true '#default_value': - - - name: Loremipsum + - name: Loremipsum company: Loremipsum email: example@example.com phone: 123-456-7890 @@ -195,8 +179,7 @@ elements: | state_province: Alabama postal_code: Loremipsum country: Afghanistan - - - name: Loremipsum + - name: Loremipsum company: Loremipsum email: example@example.com phone: 123-456-7890 @@ -206,8 +189,7 @@ elements: | state_province: Alabama postal_code: Loremipsum country: Afghanistan - - - name: Loremipsum + - name: Loremipsum company: Loremipsum email: example@example.com phone: 123-456-7890 @@ -226,14 +208,11 @@ elements: | '#title': 'Link (Ordered list)' '#multiple': true '#default_value': - - - title: Loremipsum + - title: Loremipsum url: 'http://example.com' - - - title: Loremipsum + - title: Loremipsum url: 'http://example.com' - - - title: Loremipsum + - title: Loremipsum url: 'http://example.com' '#format_items': ol webform_link_ul: @@ -241,14 +220,11 @@ elements: | '#title': 'Link (Unordered list)' '#multiple': true '#default_value': - - - title: Loremipsum + - title: Loremipsum url: 'http://example.com' - - - title: Loremipsum + - title: Loremipsum url: 'http://example.com' - - - title: Loremipsum + - title: Loremipsum url: 'http://example.com' '#format_items': ul webform_link_hr: @@ -256,14 +232,11 @@ elements: | '#title': 'Link (Horizontal rule)' '#multiple': true '#default_value': - - - title: Loremipsum + - title: Loremipsum url: 'http://example.com' - - - title: Loremipsum + - title: Loremipsum url: 'http://example.com' - - - title: Loremipsum + - title: Loremipsum url: 'http://example.com' '#format_items': hr webform_location: @@ -274,36 +247,27 @@ elements: | '#title': 'Location (Ordered list)' '#multiple': true '#default_value': - - - value: 'The White House, 1600 Pennsylvania Ave NW, Washington, DC 20500, USA' - - - value: 'London SW1A 1AA, United Kingdom' - - - value: 'Moscow, Russia, 10307' + - value: 'The White House, 1600 Pennsylvania Ave NW, Washington, DC 20500, USA' + - value: 'London SW1A 1AA, United Kingdom' + - value: 'Moscow, Russia, 10307' '#format_items': ol webform_location_ul: '#type': webform_location '#title': 'Location (Unordered list)' '#multiple': true '#default_value': - - - value: 'The White House, 1600 Pennsylvania Ave NW, Washington, DC 20500, USA' - - - value: 'London SW1A 1AA, United Kingdom' - - - value: 'Moscow, Russia, 10307' + - value: 'The White House, 1600 Pennsylvania Ave NW, Washington, DC 20500, USA' + - value: 'London SW1A 1AA, United Kingdom' + - value: 'Moscow, Russia, 10307' '#format_items': ul webform_location_hr: '#type': webform_location '#title': 'Location (Horizontal rule)' '#multiple': true '#default_value': - - - value: 'The White House, 1600 Pennsylvania Ave NW, Washington, DC 20500, USA' - - - value: 'London SW1A 1AA, United Kingdom' - - - value: 'Moscow, Russia, 10307' + - value: 'The White House, 1600 Pennsylvania Ave NW, Washington, DC 20500, USA' + - value: 'London SW1A 1AA, United Kingdom' + - value: 'Moscow, Russia, 10307' '#format_items': hr webform_name: '#type': details @@ -313,22 +277,19 @@ elements: | '#title': 'Name (Ordered list)' '#multiple': true '#default_value': - - - title: Miss + - title: Miss first: Loremipsum middle: Loremipsum last: Loremipsum suffix: Loremipsum degree: Loremipsum - - - title: Miss + - title: Miss first: Loremipsum middle: Loremipsum last: Loremipsum suffix: Loremipsum degree: Loremipsum - - - title: Miss + - title: Miss first: Loremipsum middle: Loremipsum last: Loremipsum @@ -340,22 +301,19 @@ elements: | '#title': 'Name (Unordered list)' '#multiple': true '#default_value': - - - title: Miss + - title: Miss first: Loremipsum middle: Loremipsum last: Loremipsum suffix: Loremipsum degree: Loremipsum - - - title: Miss + - title: Miss first: Loremipsum middle: Loremipsum last: Loremipsum suffix: Loremipsum degree: Loremipsum - - - title: Miss + - title: Miss first: Loremipsum middle: Loremipsum last: Loremipsum @@ -367,22 +325,19 @@ elements: | '#title': 'Name (Horizontal rule)' '#multiple': true '#default_value': - - - title: Miss + - title: Miss first: Loremipsum middle: Loremipsum last: Loremipsum suffix: Loremipsum degree: Loremipsum - - - title: Miss + - title: Miss first: Loremipsum middle: Loremipsum last: Loremipsum suffix: Loremipsum degree: Loremipsum - - - title: Miss + - title: Miss first: Loremipsum middle: Loremipsum last: Loremipsum diff --git a/tests/modules/webform_test/config/install/webform.webform.test_element_managed_file_html.yml b/tests/modules/webform_test/config/install/webform.webform.test_element_managed_file_html.yml new file mode 100644 index 00000000..fbf50d0f --- /dev/null +++ b/tests/modules/webform_test/config/install/webform.webform.test_element_managed_file_html.yml @@ -0,0 +1,103 @@ +langcode: en +status: true +dependencies: + enforced: + module: + - webform_test +uid: null +template: false +id: test_element_managed_file_html +title: 'Test: Element: Managed file with HTML' +description: 'Test HTML file uploads using managed file element.' +elements: | + managed_file_html: + '#type': managed_file + '#title': 'managed_file (HTML)' + '#file_extensions': 'html' +css: '' +javascript: '' +settings: + page: true + page_submit_path: '' + page_confirm_path: '' + form_submit_label: '' + form_submit_once: false + form_submit_attributes: { } + form_exception_message: '' + form_closed_message: '' + form_previous_submissions: true + form_confidential: false + form_confidential_message: '' + form_prepopulate: false + form_prepopulate_source_entity: false + form_novalidate: false + form_unsaved: false + form_disable_back: false + form_autofocus: false + form_details_toggle: false + wizard_progress_bar: true + wizard_progress_pages: false + wizard_progress_percentage: false + wizard_next_button_label: '' + wizard_next_button_attributes: { } + wizard_prev_button_label: '' + wizard_prev_button_attributes: { } + wizard_start_label: '' + wizard_complete: true + wizard_complete_label: '' + preview: 1 + preview_next_button_label: '' + preview_next_button_attributes: { } + preview_prev_button_label: '' + preview_prev_button_attributes: { } + preview_message: '' + draft: false + draft_auto_save: false + draft_button_label: '' + draft_button_attributes: { } + draft_saved_message: '' + draft_loaded_message: '' + confirmation_type: page + 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: '' + entity_limit_total: null + entity_limit_user: null + results_disabled: false + results_disabled_ignore: false + token_update: false +access: + create: + roles: + - anonymous + - authenticated + users: { } + view_any: + roles: { } + users: { } + update_any: + roles: { } + users: { } + delete_any: + roles: { } + users: { } + purge_any: + roles: { } + users: { } + view_own: + roles: { } + users: { } + update_own: + roles: { } + users: { } + delete_own: + roles: { } + users: { } +handlers: { } diff --git a/tests/modules/webform_test/config/install/webform.webform.test_element_multiple.yml b/tests/modules/webform_test/config/install/webform.webform.test_element_multiple.yml index 44eefde9..3e90c181 100644 --- a/tests/modules/webform_test/config/install/webform.webform.test_element_multiple.yml +++ b/tests/modules/webform_test/config/install/webform.webform.test_element_multiple.yml @@ -41,11 +41,9 @@ elements: | '#element': '#type': webform_name '#default_value': - - - first: John + - first: John last: Smith - - - first: Jane + - first: Jane last: Doe webform_multiple_elements_name_item: '#type': webform_multiple @@ -58,11 +56,9 @@ elements: | '#type': textfield '#title': last_name '#default_value': - - - first_name: John + - first_name: John last_name: Smith - - - first_name: Jane + - first_name: Jane last_name: Doe webform_multiple_elements_name_table: '#type': webform_multiple @@ -80,11 +76,9 @@ elements: | '#title_display': invisible '#placeholder': 'Enter last name' '#default_value': - - - first_name: John + - first_name: John last_name: Smith - - - first_name: Jane + - first_name: Jane last_name: Doe webform_multiple_options: '#type': webform_multiple @@ -104,11 +98,9 @@ elements: | '#title_display': invisible '#placeholder': 'Enter text' '#default_value': - - - value: one + - value: one text: One - - - value: two + - value: two text: Two webform_multiple_elements_hidden_table: '#type': webform_multiple @@ -129,12 +121,10 @@ elements: | '#title_display': invisible '#placeholder': 'Enter last name' '#default_value': - - - id: john + - id: john first_name: John last_name: Smith - - - id: jane + - id: jane first_name: Jane last_name: Doe css: '' diff --git a/tests/modules/webform_test/config/install/webform.webform.test_element_states.yml b/tests/modules/webform_test/config/install/webform.webform.test_element_states.yml index f7ab5b93..ea4de52f 100644 --- a/tests/modules/webform_test/config/install/webform.webform.test_element_states.yml +++ b/tests/modules/webform_test/config/install/webform.webform.test_element_states.yml @@ -27,12 +27,10 @@ elements: | selector_02: checked: true disabled: - - - selector_01: + - selector_01: checked: true - or - - - selector_02: + - selector_02: checked: true states_custom_selector: '#type': webform_element_states @@ -57,26 +55,22 @@ elements: | '#title': 'States unsupport operator' '#default_value': required: - - - custom_selector: + - custom_selector: value: 'Yes' - xor - - - custom_selector: + - custom_selector: value: 'Yes' states_unsupported_nesting: '#type': webform_element_states '#title': 'States unsupport nested multiple selectors' '#default_value': required: - - - selector_01: + - selector_01: value: 'Yes' selector_02: value: 'Yes' - or - - - selector_03: + - selector_03: value: 'Yes' selector_04: value: 'Yes' diff --git a/tests/modules/webform_test/config/install/webform.webform.test_form_states_triggers.yml b/tests/modules/webform_test/config/install/webform.webform.test_form_states_triggers.yml index c42ac1fb..828f916b 100644 --- a/tests/modules/webform_test/config/install/webform.webform.test_form_states_triggers.yml +++ b/tests/modules/webform_test/config/install/webform.webform.test_form_states_triggers.yml @@ -38,11 +38,9 @@ elements: | '#markup': 'Checked multiple triggered using AND operator' '#states': visible: - - - ':input[name="checked_multiple_and_trigger[one]"]': + - ':input[name="checked_multiple_and_trigger[one]"]': checked: true - - - ':input[name="checked_multiple_and_trigger[two]"]': + - ':input[name="checked_multiple_and_trigger[two]"]': checked: true checked_multiple_or: '#type': details @@ -59,11 +57,9 @@ elements: | '#markup': 'Checked multiple using OR operator triggered' '#states': visible: - - - ':input[name="checked_multiple_or_trigger[one]"]': + - ':input[name="checked_multiple_or_trigger[one]"]': checked: true - - - ':input[name="checked_multiple_or_trigger[two]"]': + - ':input[name="checked_multiple_or_trigger[two]"]': checked: true checked_multiple_xor: '#type': details @@ -80,12 +76,10 @@ elements: | '#markup': 'Checked multiple using XOR operator triggered' '#states': visible: - - - ':input[name="checked_multiple_xor_trigger[one]"]': + - ':input[name="checked_multiple_xor_trigger[one]"]': checked: true - xor - - - ':input[name="checked_multiple_xor_trigger[two]"]': + - ':input[name="checked_multiple_xor_trigger[two]"]': checked: true value: '#type': details diff --git a/tests/modules/webform_test/config/install/webform.webform.test_token.yml b/tests/modules/webform_test/config/install/webform.webform.test_token.yml index da597329..418925f0 100644 --- a/tests/modules/webform_test/config/install/webform.webform.test_token.yml +++ b/tests/modules/webform_test/config/install/webform.webform.test_token.yml @@ -14,27 +14,104 @@ elements: | '#type': webform_codemirror '#mode': text '#title': 'Webform Tokens' - '#default_value': "Description: [webform:description]\nWebform ID: [webform:id]\ntitle: [webform:title]\nURL: [webform:url]\nAuthor: [webform:author]\nAuthor name: [webform:author:account-name]\nAuthor roles: [webform:author:roles]\n" + '#default_value': | + Description: [webform:description] + Webform ID: [webform:id] + title: [webform:title] + URL: [webform:url] + Author: [webform:author] + Author name: [webform:author:account-name] + Author roles: [webform:author:roles] webform_submission_tokens: '#type': webform_codemirror '#mode': text '#title': 'Webform Submission Tokens' - '#default_value': "Date changed: [webform_submission:changed]\nDate changed long: [webform_submission:changed:long]\nDate completed: [webform_submission:completed]\nDate completed long: [webform_submission:completed:long]\nDate created: [webform_submission:created]\nDate created long: [webform_submission:created:long]\nWebform: [webform_submission:webform]\nIP address: [webform_submission:ip-address]\nLangcode: [webform_submission:langcode]\nLanguage: [webform_submission:language]\nIn draft: [webform_submission:in-draft]\nCompleted page: [webform_submission:completed-page]\nSource URL: [webform_submission:source-url]\nSubmission ID: [webform_submission:sid]\nSubmission serial number: [webform_submission:serial]\nSubmitter: [webform_submission:user]\nSubmitter name: [webform_submission:user:account-name]\nSubmitter mail: [webform_submission:user:mail]\nToken: [webform_submission:token]\nUpdate URL: [webform_submission:update-url]\nURL: [webform_submission:url]\nURL Edit Webform: [webform_submission:url:edit-form]\nUUID: [webform_submission:uuid]\n" + '#default_value': | + Date changed: [webform_submission:changed] + Date changed long: [webform_submission:changed:long] + Date completed: [webform_submission:completed] + Date completed long: [webform_submission:completed:long] + Date created: [webform_submission:created] + Date created long: [webform_submission:created:long] + Webform: [webform_submission:webform] + IP address: [webform_submission:ip-address] + Langcode: [webform_submission:langcode] + Language: [webform_submission:language] + In draft: [webform_submission:in-draft] + Completed page: [webform_submission:completed-page] + Source URL: [webform_submission:source-url] + Submission ID: [webform_submission:sid] + Submission serial number: [webform_submission:serial] + Submitter: [webform_submission:user] + Submitter name: [webform_submission:user:account-name] + Submitter mail: [webform_submission:user:mail] + Token: [webform_submission:token] + Update URL: [webform_submission:update-url] + URL: [webform_submission:url] + URL Edit Webform: [webform_submission:url:edit-form] + UUID: [webform_submission:uuid] webform_submission_data_tokens: '#type': webform_codemirror '#mode': text '#title': 'Webform Submission Data Tokens' - '#default_value': "Submission values: [webform_submission:values]\n" + '#default_value': | + Submission values: [webform_submission:values] webform_submission_source_entity_tokens: '#type': webform_codemirror '#mode': text '#title': 'Webform Submission Source Entity Tokens' - '#default_value': "Author: [webform_submission:source-entity:author]\nAccount cancellation URL: [webform_submission:source-entity:author:cancel-url]\nAccount Name: [webform_submission:source-entity:author:account-name]\nCreated: [webform_submission:source-entity:author:created]\nDisplay Name: [webform_submission:source-entity:author:display-name]\nEdit URL: [webform_submission:source-entity:author:edit-url]\nEmail: [webform_submission:source-entity:author:mail]\nLast login: [webform_submission:source-entity:author:last-login]\nPicture: [webform_submission:source-entity:author:user_picture]\nRoles: [webform_submission:source-entity:author:roles]\nURL: [webform_submission:source-entity:author:url]\nUser ID: [webform_submission:source-entity:author:uid]\nBody: [webform_submission:source-entity:body]\nContent ID: [webform_submission:source-entity:nid]\nContent type: [webform_submission:source-entity:content-type]\nDate changed: [webform_submission:source-entity:changed]\nDate created: [webform_submission:source-entity:created]\nImage: [webform_submission:source-entity:field_image]\nLanguage code: [webform_submission:source-entity:langcode]\nRevision ID: [webform_submission:source-entity:vid]\nSummary: [webform_submission:source-entity:summary]\nTitle: [webform_submission:source-entity:title]\nURL: [webform_submission:source-entity:url]\n" + '#default_value': | + Author: [webform_submission:source-entity:author] + Account cancellation URL: [webform_submission:source-entity:author:cancel-url] + Account Name: [webform_submission:source-entity:author:account-name] + Created: [webform_submission:source-entity:author:created] + Display Name: [webform_submission:source-entity:author:display-name] + Edit URL: [webform_submission:source-entity:author:edit-url] + Email: [webform_submission:source-entity:author:mail] + Last login: [webform_submission:source-entity:author:last-login] + Picture: [webform_submission:source-entity:author:user_picture] + Roles: [webform_submission:source-entity:author:roles] + URL: [webform_submission:source-entity:author:url] + User ID: [webform_submission:source-entity:author:uid] + Body: [webform_submission:source-entity:body] + Content ID: [webform_submission:source-entity:nid] + Content type: [webform_submission:source-entity:content-type] + Date changed: [webform_submission:source-entity:changed] + Date created: [webform_submission:source-entity:created] + Image: [webform_submission:source-entity:field_image] + Language code: [webform_submission:source-entity:langcode] + Revision ID: [webform_submission:source-entity:vid] + Summary: [webform_submission:source-entity:summary] + Title: [webform_submission:source-entity:title] + URL: [webform_submission:source-entity:url] webform_submission_node_tokens: '#type': webform_codemirror '#mode': text '#title': 'Webform Submission Node Tokens' - '#default_value': "Author: [webform_submission:node:author]\nAccount cancellation URL: [webform_submission:node:author:cancel-url]\nAccount Name: [webform_submission:node:author:account-name]\nCreated: [webform_submission:node:author:created]\nDisplay Name: [webform_submission:node:author:display-name]\nEdit URL: [webform_submission:node:author:edit-url]\nEmail: [webform_submission:node:author:mail]\nLast login: [webform_submission:node:author:last-login]\nPicture: [webform_submission:node:author:user_picture]\nRoles: [webform_submission:node:author:roles]\nURL: [webform_submission:node:author:url]\nUser ID: [webform_submission:node:author:uid]\nBody: [webform_submission:node:body]\nContent ID: [webform_submission:node:nid]\nContent type: [webform_submission:node:content-type]\nDate changed: [webform_submission:node:changed]\nDate created: [webform_submission:node:created]\nImage: [webform_submission:node:field_image]\nLanguage code: [webform_submission:node:langcode]\nRevision ID: [webform_submission:node:vid]\nSummary: [webform_submission:node:summary]\nTitle: [webform_submission:node:title]\nURL: [webform_submission:node:url]\n" + '#default_value': | + Author: [webform_submission:node:author] + Account cancellation URL: [webform_submission:node:author:cancel-url] + Account Name: [webform_submission:node:author:account-name] + Created: [webform_submission:node:author:created] + Display Name: [webform_submission:node:author:display-name] + Edit URL: [webform_submission:node:author:edit-url] + Email: [webform_submission:node:author:mail] + Last login: [webform_submission:node:author:last-login] + Picture: [webform_submission:node:author:user_picture] + Roles: [webform_submission:node:author:roles] + URL: [webform_submission:node:author:url] + User ID: [webform_submission:node:author:uid] + Body: [webform_submission:node:body] + Content ID: [webform_submission:node:nid] + Content type: [webform_submission:node:content-type] + Date changed: [webform_submission:node:changed] + Date created: [webform_submission:node:created] + Image: [webform_submission:node:field_image] + Language code: [webform_submission:node:langcode] + Revision ID: [webform_submission:node:vid] + Summary: [webform_submission:node:summary] + Title: [webform_submission:node:title] + URL: [webform_submission:node:url] css: '' javascript: '' settings: diff --git a/tests/modules/webform_test/includes/webform_test.test_options.inc b/tests/modules/webform_test/includes/webform_test.test_options.inc index 72a10bb6..df67580b 100644 --- a/tests/modules/webform_test/includes/webform_test.test_options.inc +++ b/tests/modules/webform_test/includes/webform_test.test_options.inc @@ -16,32 +16,32 @@ use Drupal\webform\Entity\WebformOptions; */ function webform_test_test_options() { $data = [ - 'general_options' => [ + 'general' => [ '#type' => 'details', '#title' => 'General options', '#open' => TRUE, ], - 'bio_options' => [ + 'bio' => [ '#type' => 'details', '#title' => 'Biographical options', '#open' => TRUE, ], - 'location_options' => [ + 'location' => [ '#type' => 'details', '#title' => 'Location options', '#open' => TRUE, ], - 'date_options' => [ + 'date' => [ '#type' => 'details', '#title' => 'Date options', '#open' => TRUE, ], - 'likert_options' => [ + 'likert' => [ '#type' => 'details', '#title' => 'Likert options', '#open' => TRUE, ], - 'test_options' => [ + 'test' => [ '#type' => 'details', '#title' => 'Test options', '#open' => TRUE, @@ -51,7 +51,7 @@ function webform_test_test_options() { foreach ($webform_options as $id => $webform_option) { $title = $webform_option->label() . ' (' . $id . ')'; if (strpos($id, 'likert') === 0) { - $data['likert_options'][$id] = [ + $data['likert'][$id] = [ '#type' => 'webform_likert', '#title' => $title, '#questions' => [ @@ -64,19 +64,19 @@ function webform_test_test_options() { } else { if (preg_match('/(state|country|countries)/', $id)) { - $group = 'location_options'; + $group = 'location'; } elseif (preg_match('/(months|days)/', $id)) { - $group = 'date_options'; + $group = 'date'; } elseif (preg_match('/(time_zones|creditcard|yes_no|days|size)/', $id)) { - $group = 'general_options'; + $group = 'general'; } elseif ($id == 'test') { - $group = 'test_options'; + $group = 'test'; } else { - $group = 'bio_options'; + $group = 'bio'; } $data[$group][$id] = [ @@ -88,12 +88,6 @@ function webform_test_test_options() { } } - $data['test_options']['custom'] = [ - '#type' => 'select', - '#title' => 'Custom (custom)', - '#options' => 'custom', - ]; - $yaml = file_get_contents(drupal_get_path('module', 'webform_test') . '/includes/webform_test.test_options.yml'); $default_elements = Yaml::decode($yaml); diff --git a/tests/modules/webform_test_options/config/install/webform.webform.test_options.yml b/tests/modules/webform_test_options/config/install/webform.webform.test_options.yml index ccbd4ccd..113d9064 100644 --- a/tests/modules/webform_test_options/config/install/webform.webform.test_options.yml +++ b/tests/modules/webform_test_options/config/install/webform.webform.test_options.yml @@ -10,7 +10,7 @@ id: test_options title: 'Test: Options' description: 'Test webform options.' elements: | - general_options: + general: '#type': details '#title': 'General options' '#open': true @@ -30,10 +30,14 @@ elements: | '#type': select '#title': 'Yes/No (yes_no)' '#options': yes_no - bio_options: + bio: '#type': details '#title': 'Biographical options' '#open': true + titles: + '#type': select + '#title': 'Titles (titles)' + '#options': titles education: '#type': select '#title': 'Education (education)' @@ -66,22 +70,22 @@ elements: | '#type': select '#title': 'Relationship (relationship)' '#options': relationship - titles: - '#type': select - '#title': 'Titles (titles)' - '#options': titles - location_options: + location: '#type': details '#title': 'Location options' '#open': true - country_codes: - '#type': select - '#title': 'Country codes (country_codes)' - '#options': country_codes country_names: '#type': select '#title': 'Country names (country_names)' '#options': country_names + state_province_names: + '#type': select + '#title': 'State/Province names (state_province_names)' + '#options': state_province_names + country_codes: + '#type': select + '#title': 'Country codes (country_codes)' + '#options': country_codes state_codes: '#type': select '#title': 'State codes (state_codes)' @@ -94,11 +98,7 @@ elements: | '#type': select '#title': 'State/Province codes (state_province_codes)' '#options': state_province_codes - state_province_names: - '#type': select - '#title': 'State/Province names (state_province_names)' - '#options': state_province_names - date_options: + date: '#type': details '#title': 'Date options' '#open': true @@ -110,7 +110,7 @@ elements: | '#type': select '#title': 'Months (months)' '#options': months - likert_options: + likert: '#type': details '#title': 'Likert options' '#open': true @@ -170,7 +170,7 @@ elements: | q2: 'How about now answering question 2?' q3: 'Finally, here is question 3?' '#answers': likert_would_you - test_options: + test: '#type': details '#title': 'Test options' '#open': true @@ -178,10 +178,6 @@ elements: | '#type': select '#title': 'Test (test)' '#options': test - custom: - '#type': select - '#title': 'Custom (custom)' - '#options': custom range_options: '#title': 'Range Options' '#type': details diff --git a/tests/modules/webform_test_options/webform_test_options.module b/tests/modules/webform_test_options/webform_test_options.module index a871ba83..e9898fa8 100644 --- a/tests/modules/webform_test_options/webform_test_options.module +++ b/tests/modules/webform_test_options/webform_test_options.module @@ -9,23 +9,9 @@ * Implements hook_webform_options_WEBFORM_OPTIONS_ID_alter(). */ function webform_test_options_webform_options_test_alter(array &$options, array &$element) { - $options += [ - 'four' => t('Four'), - 'five' => t('Five'), - 'six' => t('Six'), + $options = [ + 'one' => t('One'), + 'two' => t('Two'), + 'three' => t('Three'), ]; } - - -/** - * Implements hook_webform_options_alter(). - */ -function webform_test_options_webform_options_alter(array &$options, array &$element, $id) { - if ($id == 'custom') { - $options = [ - 'one' => t('One'), - 'two' => t('Two'), - 'three' => t('Three'), - ]; - } -} diff --git a/webform.api.php b/webform.api.php index 502700c0..12ffe503 100644 --- a/webform.api.php +++ b/webform.api.php @@ -84,20 +84,6 @@ function hook_webform_element_ELEMENT_TYPE_form_alter(array &$element, \Drupal\C } /** - * Alter form options by id. - * - * @param array $options - * An associative array of options. - * @param array $element - * The form element that the options is for. - * @param string $options_id - * The form options id. Set to NULL if the options are custom. - */ -function hook_webform_options_alter(array &$options, array &$element, $options_id = NULL) { - -} - -/** * Alter the webform options by id. * * @param array $options diff --git a/webform.install b/webform.install index fdd77d43..64e24f17 100644 --- a/webform.install +++ b/webform.install @@ -9,6 +9,7 @@ use Drupal\Component\Render\FormattableMarkup; use Drupal\Core\Database\Database; use Drupal\Core\Serialization\Yaml; use Drupal\Core\Render\Element; +use Drupal\Core\Url; use Drupal\system\Entity\Action; use Drupal\webform\Entity\Webform; use Drupal\webform\Plugin\WebformElement\ManagedFile; @@ -70,19 +71,44 @@ function webform_requirements($phase) { $scheme_options = ManagedFile::getVisibleStreamWrappers(); if (isset($scheme_options['private'])) { $requirements['webform_file_private'] = [ - 'title' => t('Webform: Private files'), + 'title' => t('Webform files: Private files'), 'value' => t('Private file system is set.'), ]; } else { $requirements['webform_file_private'] = [ - 'title' => t('Webform: Private files'), + 'title' => t('Webform files: Private files'), 'value' => t('Private file system is not set.'), 'description' => t('This must be changed in settings.php. For more information see: DRUPAL-PSA-2016-003'), 'severity' => REQUIREMENT_WARNING, ]; } + // Check XSS file upload block. + if (!\Drupal::config('webform.settings')->get('file.xss_block')) { + $requirements['webform_file_xss_block'] = [ + 'title' => t('Webform files: XSS block'), + 'value' => t('Blocking users from uploading HTML files, which may contain Cross-Site Scripting (XSS) is not set.', [':href' => Url::fromRoute('webform.settings', [], ['#fragment' => 'edit-file'])->toString()]), + 'severity' => REQUIREMENT_WARNING, + ]; + } + + // Check existing XSS file. + $query = Drupal::entityQuery('file'); + $query ->condition('uri', '%://webform/%', 'LIKE') + ->condition( + $query->orConditionGroup() + ->condition('filename', '%.htm', 'LIKE') + ->condition('filename', '%.html', 'LIKE') + ); + if ($total = $query->count()->execute()) { + $requirements['webform_file_html'] = [ + 'title' => t('Webform files: HTML file uploads'), + 'value' => t('HTML files that may contain Cross-Site Scripting (XSS) have been uploaded. You should convert @total existing file(s) from HTML to plain text.', ['@total' => $total, ':href' => Url::fromRoute('webform.admin_settings.html_to_text')->toString()]), + 'severity' => REQUIREMENT_WARNING, + ]; + } + // Check third party libraries status. /** @var \Drupal\webform\WebformLibrariesManagerInterface $libraries_manager */ $libraries_manager = \Drupal::service('webform.libraries_manager'); @@ -497,3 +523,10 @@ function _webform_update_8014(array &$element) { } } + +/** + * Issue #2849949: Improve file validation. + */ +function webform_update_8015() { + _webform_update_admin_settings(); +} diff --git a/webform.libraries.yml b/webform.libraries.yml index a29362e1..3fe7b727 100644 --- a/webform.libraries.yml +++ b/webform.libraries.yml @@ -169,7 +169,6 @@ webform.element.buttons: js/webform.element.buttons.js: {} dependencies: - core/jquery.ui.button - - webform/jquery.common_ancestor webform.element.codemirror.text: version: 1.x @@ -464,13 +463,6 @@ webform.element.video_file: theme: css/webform.element.video_file.css: {} -# jQuery libraries. - -jquery.common_ancestor: - version: 1.x - js: - js/jquery.common_ancestor.js: {} - # External libraries. ckeditor: diff --git a/webform.module b/webform.module index 10120cd0..fd25208f 100644 --- a/webform.module +++ b/webform.module @@ -5,8 +5,9 @@ * Enables the creation of webforms and questionnaires. */ -use Drupal\Component\Utility\Unicode; use Drupal\Component\Serialization\Json; +use Drupal\Component\Utility\Unicode; +use Drupal\Component\Utility\Xss; use Drupal\Core\Access\AccessResult; use Drupal\Core\Asset\AttachedAssetsInterface; use Drupal\Core\Breadcrumb\Breadcrumb; diff --git a/webform.routing.yml b/webform.routing.yml index 79171222..b050fda4 100644 --- a/webform.routing.yml +++ b/webform.routing.yml @@ -22,6 +22,14 @@ webform.admin_settings.third_party: requirements: _permission: 'administer webform' +webform.admin_settings.html_to_text: + path: '/admin/reports/status/webform/html-to-text' + defaults: + _form: '\Drupal\webform\Form\WebformAdminHtmlToTextForm' + _title: 'Webforms: Convert uploaded HTML files to plain text.' + requirements: + _permission: 'administer webform' + webform.addons: path: '/admin/structure/webform/addons' defaults: