diff --git a/modules/tide_webform_jsonapi/src/TideResource/AddWebform.php b/modules/tide_webform_jsonapi/src/TideResource/AddWebform.php new file mode 100644 index 0000000..e82bdbc --- /dev/null +++ b/modules/tide_webform_jsonapi/src/TideResource/AddWebform.php @@ -0,0 +1,153 @@ +tideWebformJsonapiHelper = $tide_webform_jsonapi_helper; + $this->resourceTypeRepository = $resource_type_repository; + $this->resource = $resource; + } + + /** + * {@inheritdoc} + */ + public static function create(ContainerInterface $container) { + return new static( + $container->get('tide_webform_jsonapi.helper'), + $container->get('jsonapi.resource_type.repository'), + $container->get('jsonapi.entity_resource') + ); + } + + /** + * Process the resource request. + * + * @param \Symfony\Component\HttpFoundation\Request $request + * The request. + * @param \Drupal\webform\Entity\Webform $webform + * The webform entity. + * + * @return \Drupal\jsonapi\ResourceResponse + * The response. + * + * @throws \Symfony\Component\HttpKernel\Exception\ConflictHttpException + * Thrown when the entity to be created already exists. + * @throws \Drupal\Component\Plugin\Exception\InvalidPluginDefinitionException + * Thrown if the storage handler couldn't be loaded. + * @throws \Drupal\Core\Entity\EntityStorageException + * Thrown if the entity could not be saved. + */ + public function process(Request $request, Webform $webform): ResourceResponse { + // Purely business logic to get the webform_submission entity. + $resource_type = $this->resourceTypeRepository->get( + 'webform_submission', + $webform->id() + ); + $reflectedMethod = new \ReflectionMethod( + $this->resource, + 'deserialize' + ); + $reflectedMethod->setAccessible(TRUE); + /** @var \Drupal\webform\Entity\WebformSubmission $entity */ + $entity = $reflectedMethod->invoke( + $this->resource, + $resource_type, + $request, + JsonApiDocumentTopLevel::class + ); + static::validate($entity); + // It implies triggering the webform handlers. + + // Massage, organise and verify the data. + $original_elements = $webform->getElementsDecodedAndFlattened(); + $supported_validations = $this->tideWebformJsonapiHelper->getSupportedValidateElements(); + $results = $this->tideWebformJsonapiHelper->webformValidateSettingsExtractor($supported_validations, $original_elements); + $new_array = []; + foreach ($results as $key => $r) { + $new_array[$key] = $this->tideWebformJsonapiHelper->attachValidateSettingsToPayload($r); + } + $errors = $this->tideWebformJsonapiHelper->validatePayload($entity->getData(), $new_array, $original_elements); + // Let webform core checks words and characters. + $internal_errors = WebformSubmissionForm::validateWebformSubmission($entity); + // Prepare error messages. + if (!empty($internal_errors)) { + foreach ($internal_errors as $key => $message) { + $errors[$key][] = $message->__toString(); + } + } + $errors_collection = []; + if (!empty($errors)) { + foreach ($errors as $field_id => $details) { + foreach ($details as $item) { + $errors_collection[] = new HttpException(422, $field_id . '|' . $item); + } + } + $errs = new ErrorCollection($errors_collection); + $document = new JsonApiDocumentTopLevel($errs, new NullIncludedData(), new LinkCollection([])); + return new ResourceResponse($document, 422); + } + // Return 201 if no errors. + $entity->save(); + $resource_object = ResourceObject::createFromEntity($resource_type, $entity); + $primary_data = new ResourceObjectData([$resource_object], 1); + return $this->createJsonapiResponse($primary_data, $request, 201); + } + + /** + * {@inheritdoc} + */ + public function getRouteResourceTypes(Route $route, string $route_name): array { + return $this->getResourceTypesByEntityTypeId('webform_submission'); + } + +} diff --git a/modules/tide_webform_jsonapi/src/TideWebformJsonapiHelper.php b/modules/tide_webform_jsonapi/src/TideWebformJsonapiHelper.php new file mode 100644 index 0000000..cc0834d --- /dev/null +++ b/modules/tide_webform_jsonapi/src/TideWebformJsonapiHelper.php @@ -0,0 +1,122 @@ + $items) { + // Iterate through supported validation keys and their values. + foreach ($supported_validation as $key => $value) { + // If the current value is an array, check for a match with the key. + if (is_array($value)) { + if (array_key_exists($key, $items)) { + $res[$w_key][$key] = $items[$key]; + } + } + else { + // If the value exists in the items and is not an array, + // store it in the result. + if (array_key_exists($value, $items)) { + $res[$w_key][$value] = $items[$value]; + } + } + } + } + return $res; + } + + /** + * Supported validators. + */ + public function getSupportedValidateElements() { + return [ + '#required', + '#required_error', + '#pattern', + '#pattern_error', + ]; + } + + /** + * Attached supported validator to Payload. + */ + public function attachValidateSettingsToPayload(array $payload) { + $output = []; + $mapping = [ + '#required' => 'required', + '#required_error' => 'required', + '#pattern' => 'pattern', + '#pattern_error' => 'pattern', + ]; + foreach ($payload as $key => $value) { + if (isset($mapping[$key])) { + $output[$mapping[$key]][] = $value; + } + } + return $output; + } + + /** + * Verifies the payload. + */ + public function validatePayload(array $payload, array $massaged_validates_array, array $original_elements) { + $results = []; + // 1st step, check if all required field are submitted. + foreach ($massaged_validates_array as $key => $item) { + if (isset($item['required'])) { + if (!isset($payload[$key])) { + $payload[$key] = ""; + } + } + } + // 2nd step, check if it passes the validation. + foreach ($payload as $id => $value) { + if (array_key_exists($id, $massaged_validates_array)) { + if (!empty($this->generateErrorString($value, $massaged_validates_array[$id], $original_elements[$id]['#title']))) { + $results[$id] = $this->generateErrorString($value, $massaged_validates_array[$id], $original_elements[$id]['#title']); + } + } + } + // 3rd step, check if the field contains correct email or phone numbers. + foreach ($original_elements as $field_id => $detail) { + if (!isset($results[$field_id]) && isset($payload[$field_id])) { + if ($detail['#type'] === 'email') { + if (v::email()->validate($payload[$field_id]) === FALSE) { + $results[$field_id] = ['Please provide a valid email address.']; + } + } + if ($detail['#type'] === 'tel') { + if (v::phone('AU')->validate($payload[$field_id]) === FALSE) { + $results[$field_id] = ['Please provide a valid Phone number.']; + } + } + } + } + return $results; + } + + /** + * Generates error messages. + */ + public function generateErrorString($value, array $arr, $title) { + $res = []; + foreach ($arr as $k => $v) { + if (call_user_func('tide_webform_jsonapi_' . $k . '_validate', $value, $v, $title) !== TRUE) { + $res[] = call_user_func('tide_webform_jsonapi_' . $k . '_validate', $value, $v, $title); + } + } + return $res; + } + +} diff --git a/modules/tide_webform_jsonapi/tests/modules/tide_webform_jsonapi_test/config/install/webform.webform.tide_webform_content_rating.yml b/modules/tide_webform_jsonapi/tests/modules/tide_webform_jsonapi_test/config/install/webform.webform.tide_webform_content_rating.yml new file mode 100644 index 0000000..851102a --- /dev/null +++ b/modules/tide_webform_jsonapi/tests/modules/tide_webform_jsonapi_test/config/install/webform.webform.tide_webform_content_rating.yml @@ -0,0 +1,186 @@ +langcode: en +status: open +dependencies: { } +weight: 0 +open: null +close: null +uid: null +template: false +archive: false +id: tide_webform_content_rating +title: 'Content Rating' +description: ''Was this page helpful' rating/survey on content pages.' +category: '' +elements: | + url: + '#type': hidden + '#title': URL + was_this_page_helpful: + '#type': radios + '#title': 'Was this page helpful?' + '#options': yes_no + '#required': true + comments: + '#type': textarea + '#title': 'Any comments? (optional)' + '#rows': 2 + '#counter_type': word + '#counter_minimum': 1 + '#counter_maximum': 2 + '#states': + visible: + ':input[name="was_this_page_helpful"]': + checked: true + '#format_items': comma + reset: + '#type': processed_text + '#wrapper_attributes': + class: + - webform-content-rating-cancel + '#states': + visible: + ':input[name="was_this_page_helpful"]': + checked: true + '#text': '

Cancel

' + '#format': admin_text + testextfield: + '#type': textfield + '#title': testextfield + '#required': true + '#required_error': helloworld + testemail: + '#type': email + '#title': testemail + '#pattern': '/^[\w.-]+@[\w.-]+\.[A-Za-z]{2,6}$/' + actions: + '#type': webform_actions + '#title': 'Submit button(s)' + '#states': + visible: + ':input[name="was_this_page_helpful"]': + checked: true +css: '' +javascript: '' +settings: + ajax: false + ajax_scroll_top: form + page: true + page_submit_path: '' + page_confirm_path: '' + form_submit_once: false + form_open_message: '' + form_close_message: '' + form_exception_message: '' + form_previous_submissions: false + 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_unsaved: false + form_disable_back: false + form_submit_back: false + form_disable_autocomplete: false + form_novalidate: false + form_required: false + form_autofocus: false + form_details_toggle: false + form_reset: false + submission_label: '' + submission_exception_message: '' + submission_locked_message: '' + submission_log: false + submission_user_columns: { } + autofill: false + autofill_message: '' + autofill_excluded_elements: { } + wizard_progress_bar: true + wizard_progress_pages: false + wizard_progress_percentage: false + wizard_start_label: '' + wizard_confirmation: true + wizard_confirmation_label: '' + wizard_track: '' + preview: 0 + 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: message + confirmation_url: '' + confirmation_title: '' + confirmation_message: 'Thank you, your feedback is valuable to us.' + confirmation_attributes: { } + confirmation_back: true + confirmation_back_label: '' + confirmation_back_attributes: { } + limit_total: null + limit_total_interval: null + limit_total_message: '' + limit_user: null + limit_user_interval: null + limit_user_message: '' + entity_limit_total: null + entity_limit_total_interval: null + entity_limit_user: null + entity_limit_user_interval: null + purge: none + purge_days: null + results_disabled: false + results_disabled_ignore: false + token_update: false +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: { } + administer: + roles: { } + users: { } + permissions: { } + configuration: + roles: + - anonymous + users: { } + permissions: { } +handlers: { } +variants: { } diff --git a/modules/tide_webform_jsonapi/tests/modules/tide_webform_jsonapi_test/config/optional/webform.settings.yml b/modules/tide_webform_jsonapi/tests/modules/tide_webform_jsonapi_test/config/optional/webform.settings.yml new file mode 100644 index 0000000..f4b5aef --- /dev/null +++ b/modules/tide_webform_jsonapi/tests/modules/tide_webform_jsonapi_test/config/optional/webform.settings.yml @@ -0,0 +1,228 @@ +settings: + default_status: open + default_page_base_path: form + default_form_open_message: 'This form has not yet been opened to submissions.' + default_form_close_message: 'Sorry...This form is closed to new submissions.' + default_form_exception_message: 'Unable to display this webform. Please contact the site administrator.' + default_submit_button_label: Submit + default_reset_button_label: Reset + default_form_submit_once: false + default_form_confidential_message: 'This form is confidential. You must Log out to submit it.' + default_form_login_message: 'Please login to access this form.' + default_form_disable_back: false + default_form_submit_back: false + default_form_unsaved: false + default_form_novalidate: false + default_form_disable_inline_errors: false + default_form_required: false + default_form_required_label: 'Indicates required field' + default_form_details_toggle: true + form_classes: "container-inline clearfix\nform--inline clearfix\nmessages messages--error\nmessages messages--warning\nmessages messages--status\n" + button_classes: '' + default_wizard_prev_button_label: '< Previous Page' + default_wizard_next_button_label: 'Next Page >' + default_wizard_start_label: Start + default_wizard_confirmation_label: Complete + default_preview_next_button_label: Preview + default_preview_prev_button_label: '< Previous' + default_preview_label: Preview + default_preview_title: '[webform:title]: Preview' + default_preview_message: 'Please review your submission. Your submission is not complete until you press the "Submit" button!' + default_draft_button_label: 'Save Draft' + default_draft_saved_message: 'Submission saved. You may return to this form later and it will restore the current values.' + default_draft_loaded_message: 'A partially-completed form was found. Please complete the remaining portions.' + default_confirmation_message: 'New submission added to [webform:title].' + default_confirmation_back_label: 'Back to form' + default_submission_label: '[webform_submission:submitted-to]: Submission #[webform_submission:serial]' + default_submission_access_denied_message: 'Please login to access this submission.' + default_submission_exception_message: 'Unable to process this submission. Please contact the site administrator.' + default_submission_locked_message: 'This submission has been locked.' + default_submission_log: false + webform_submission_bulk_form: true + webform_submission_bulk_form_actions: + - webform_submission_make_sticky_action + - webform_submission_make_unsticky_action + - webform_submission_make_lock_action + - webform_submission_make_unlock_action + - webform_submission_delete_action + - webform_submission_make_process_action + - webform_submission_make_unprocess_action + default_autofill_message: 'This submission has been autofilled with your previous submission.' + preview_classes: "messages messages--error\nmessages messages--warning\nmessages messages--status\n" + confirmation_classes: "messages messages--error\nmessages messages--warning\nmessages messages--status\n" + confirmation_back_classes: "button\n" + default_limit_total_message: 'No more submissions are permitted.' + default_limit_user_message: 'No more submissions are permitted.' + dialog: false + dialog_options: + narrow: + title: Narrow + width: 600 + normal: + title: Normal + width: 800 + wide: + title: Wide + width: 1000 +assets: + css: '' + javascript: '' +handler: + excluded_handlers: { } +export: + exporter: delimited + multiple_delimiter: ; + header_format: label + header_prefix: true + header_prefix_label_delimiter: ': ' + header_prefix_key_delimiter: __ + composite_element_item_format: label + options_single_format: compact + options_multiple_format: compact + options_item_format: label + entity_reference_items: + - id + - title + - url + likert_answers_format: label + signature_format: status + delimiter: ',' + excel: false + excluded_exporters: { } +batch: + default_batch_export_size: 500 + default_batch_update_size: 500 + default_batch_delete_size: 500 + default_batch_email_size: 500 +purge: + cron_size: 100 +element: + empty_message: '{Empty}' + allowed_tags: admin + wrapper_classes: "container-inline clearfix\nform--inline clearfix\nmessages messages--error\nmessages messages--warning\nmessages messages--status\n" + classes: "container-inline clearfix\nform--inline clearfix\nmessages messages--error\nmessages messages--warning\nmessages messages--status\n" + horizontal_rule_classes: "webform-horizontal-rule--solid\nwebform-horizontal-rule--dashed\nwebform-horizontal-rule--dotted\nwebform-horizontal-rule--gradient\nwebform-horizontal-rule--thin\nwebform-horizontal-rule--medium\nwebform-horizontal-rule--thick\nwebform-horizontal-rule--flaired\nwebform-horizontal-rule--glyph\n" + default_description_display: '' + default_more_title: More + default_section_title_tag: h2 + default_empty_option: true + default_empty_option_required: '' + default_empty_option_optional: '' + default_icheck: '' + default_google_maps_api_key: '' + excluded_elements: + password: password + password_confirm: password_confirm + checkboxes: checkboxes + color: color + container: container + datelist: datelist + datetime: datetime + details: details + entity_autocomplete: entity_autocomplete + fieldset: fieldset + item: item + language_select: language_select + managed_file: managed_file + range: range + search: search + tableselect: tableselect + text_format: text_format + value: value + view: view + webform_address: webform_address + webform_audio_file: webform_audio_file + webform_autocomplete: webform_autocomplete + webform_checkboxes_other: webform_checkboxes_other + webform_codemirror: webform_codemirror + webform_computed_token: webform_computed_token + webform_computed_twig: webform_computed_twig + webform_contact: webform_contact + webform_custom_composite: webform_custom_composite + webform_document_file: webform_document_file + webform_element: webform_element + webform_email_confirm: webform_email_confirm + webform_email_multiple: webform_email_multiple + webform_entity_checkboxes: webform_entity_checkboxes + webform_entity_radios: webform_entity_radios + webform_entity_select: webform_entity_select + webform_flexbox: webform_flexbox + webform_image_file: webform_image_file + webform_likert: webform_likert + webform_link: webform_link + webform_location_places: webform_location_places + webform_mapping: webform_mapping + webform_message: webform_message + webform_more: webform_more + webform_name: webform_name + webform_radios_other: webform_radios_other + webform_rating: webform_rating + webform_same: webform_same + webform_scale: webform_scale + webform_section: webform_section + webform_select_other: webform_select_other + webform_signature: webform_signature + webform_table: webform_table + webform_table_row: webform_table_row + webform_table_sort: webform_table_sort + webform_tableselect_sort: webform_tableselect_sort + webform_telephone: webform_telephone + webform_term_checkboxes: webform_term_checkboxes + webform_terms_of_service: webform_terms_of_service + webform_time: webform_time + webform_variant: webform_variant + webform_video_file: webform_video_file + webform_wizard_page: webform_wizard_page +html_editor: + disabled: false + format: '' + tidy: true +file: + file_public: false + file_private_redirect: true + file_private_redirect_message: 'Please login to access the uploaded file.' + 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_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' + default_video_file_extensions: 'avi mov mp4 ogg wav webm' + make_unused_managed_files_temporary: true + delete_temporary_managed_files: true +format: { } +mail: + default_to_mail: '[site:mail]' + default_from_mail: '[site:mail]' + default_from_name: '[site:name]' + default_reply_to: '' + default_return_path: '' + default_sender_mail: '' + default_sender_name: '' + default_subject: 'Webform submission from: [webform_submission:source-entity]' + default_body_text: "Submitted on [webform_submission:created]\nSubmitted by: [webform_submission:user]\n\nSubmitted values are:\n[webform_submission:values]\n" + default_body_html: "

Submitted on [webform_submission:created]

\n

Submitted by: [webform_submission:user]

\n

Submitted values are:

\n[webform_submission:values]\n" + roles: { } +test: + types: "checkbox:\n - true\ncolor:\n - '#ffffcc'\n - '#ffffcc'\n - '#ccffff'\nemail:\n - 'example@example.com'\n - 'test@test.com'\n - 'random@random.com'\nlanguage_select:\n - en\nmachine_name:\n - 'loremipsum'\n - 'oratione'\n - 'dixisset'\ntel:\n - '123-456-7890'\n - '098-765-4321'\ntextarea:\n - 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Negat esse eam, inquit, propter se expetendam. Primum Theophrasti, Strato, physicum se voluit; Id mihi magnum videtur. Itaque mihi non satis videmini considerare quod iter sit naturae quaeque progressio. Quare hoc videndum est, possitne nobis hoc ratio philosophorum dare. Est enim tanti philosophi tamque nobilis audacter sua decreta defendere.'\n - 'Huius, Lyco, oratione locuples, rebus ipsis ielunior. Duo Reges: constructio interrete. Sed haec in pueris; Sed utrum hortandus es nobis, Luci, inquit, an etiam tua sponte propensus es? Sapiens autem semper beatus est et est aliquando in dolore; Immo videri fortasse. Paulum, cum regem Persem captum adduceret, eodem flumine invectio? Et ille ridens: Video, inquit, quid agas;'\n - 'Quae cum dixisset, finem ille. Quamquam non negatis nos intellegere quid sit voluptas, sed quid ille dicat. Progredientibus autem aetatibus sensim tardeve potius quasi nosmet ipsos cognoscimus. Gloriosa ostentatio in constituendo summo bono. Qui-vere falsone, quaerere mittimus-dicitur oculis se privasse; Duarum enim vitarum nobis erunt instituta capienda. Comprehensum, quod cognitum non habet? Qui enim existimabit posse se miserum esse beatus non erit. Causa autem fuit huc veniendi ut quosdam hinc libros promerem. Nunc omni virtuti vitium contrario nomine opponitur.'\ntext_format:\n - value: '

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Negat esse eam, inquit, propter se expetendam. Primum Theophrasti, Strato, physicum se voluit; Id mihi magnum videtur. Itaque mihi non satis videmini considerare quod iter sit naturae quaeque progressio. Quare hoc videndum est, possitne nobis hoc ratio philosophorum dare. Est enim tanti philosophi tamque nobilis audacter sua decreta defendere.

'\n - value: '

Huius, Lyco, oratione locuples, rebus ipsis ielunior. Duo Reges: constructio interrete. Sed haec in pueris; Sed utrum hortandus es nobis, Luci, inquit, an etiam tua sponte propensus es? Sapiens autem semper beatus est et est aliquando in dolore; Immo videri fortasse. Paulum, cum regem Persem captum adduceret, eodem flumine invectio? Et ille ridens: Video, inquit, quid agas;

'\n - value: '

Quae cum dixisset, finem ille. Quamquam non negatis nos intellegere quid sit voluptas, sed quid ille dicat. Progredientibus autem aetatibus sensim tardeve potius quasi nosmet ipsos cognoscimus. Gloriosa ostentatio in constituendo summo bono. Qui-vere falsone, quaerere mittimus-dicitur oculis se privasse; Duarum enim vitarum nobis erunt instituta capienda. Comprehensum, quod cognitum non habet? Qui enim existimabit posse se miserum esse beatus non erit. Causa autem fuit huc veniendi ut quosdam hinc libros promerem. Nunc omni virtuti vitium contrario nomine opponitur.

'\nurl:\n - 'http://example.com'\n - 'http://test.com'\nwebform_email_confirm:\n - 'example@example.com'\n - 'test@test.com'\n - 'random@random.com'\nwebform_email_multiple:\n - 'example@example.com, test@test.com, random@random.com'\nwebform_location:\n - value: 'The White House, 1600 Pennsylvania Ave NW, Washington, DC 20500, USA'\n - value: 'London SW1A 1AA, United Kingdom'\n - value: 'Moscow, Russia, 10307'\nwebform_time:\n - '09:00'\n - '17:00'\n" + names: "first_name:\n - 'John'\n - 'Paul'\n - 'Ringo'\n - 'George'\nlast_name:\n - 'Lennon'\n - 'McCartney'\n - 'Starr'\n - 'Harrison'\naddress:\n - '10 Main Street'\n - '11 Brook Alley Road. APT 1'\nzip:\n - '11111'\n - '12345'\n - '12345-6789'\npostal_code:\n - '11111'\n - '12345'\n - '12345-6789'\nphone:\n - '123-456-7890'\n - '098-765-4321'\nfax:\n - '123-456-7890'\n - '098-765-4321'\ncity:\n - 'Springfield'\n - 'Pleasantville'\n - 'Hill Valley'\nurl:\n - 'http://example.com'\n - 'http://test.com'\ndefault:\n - 'Loremipsum'\n - 'Oratione'\n - 'Dixisset'\n" +ui: + video_display: dialog + details_save: true + dialog_disabled: false + offcanvas_disabled: false + contribute_disabled: false + promotions_disabled: false + description_help: true +libraries: + excluded_libraries: + - jquery.chosen + - jquery.icheck +requirements: + cdn: true + bootstrap: true + spam: true +contribute: + account_type: user + account_id: null +langcode: en +third_party_settings: { } diff --git a/modules/tide_webform_jsonapi/tests/modules/tide_webform_jsonapi_test/tide_webform_jsonapi_test.info.yml b/modules/tide_webform_jsonapi/tests/modules/tide_webform_jsonapi_test/tide_webform_jsonapi_test.info.yml new file mode 100644 index 0000000..374627a --- /dev/null +++ b/modules/tide_webform_jsonapi/tests/modules/tide_webform_jsonapi_test/tide_webform_jsonapi_test.info.yml @@ -0,0 +1,12 @@ +name: 'JSON:API Tide_webform test module' +type: module +package: Testing +dependencies: + - drupal:jsonapi + - jsonapi_resources:jsonapi_resources +core_version_requirement: ^9 || ^10 +config_devel: + install: + - webform.webform.tide_webform_content_rating + optional: + - webform.settings diff --git a/modules/tide_webform_jsonapi/tests/src/Functional/TideWebformJsonapiResourceTest.php b/modules/tide_webform_jsonapi/tests/src/Functional/TideWebformJsonapiResourceTest.php new file mode 100644 index 0000000..7ad4cd0 --- /dev/null +++ b/modules/tide_webform_jsonapi/tests/src/Functional/TideWebformJsonapiResourceTest.php @@ -0,0 +1,132 @@ +getPermissions() as $permission) { + $user_role->revokePermission($permission); + } + $user_role->save(); + assert([] === $user_role->getPermissions(), 'The anonymous user role has no permissions at all.'); + + // Ensure the authenticated user role has no permissions at all. + $user_role = Role::load(RoleInterface::AUTHENTICATED_ID); + foreach ($user_role->getPermissions() as $permission) { + $user_role->revokePermission($permission); + } + $user_role->save(); + assert([] === $user_role->getPermissions(), 'The authenticated user role has no permissions at all.'); + $this->account = $this->createUser(); + $this->container->get('current_user')->setAccount($this->account); + } + + /** + * Tests the custom Add Webform resource. + */ + public function testAddWebformResource() { + $this->config('jsonapi.settings')->set('read_only', FALSE)->save(TRUE); + $this->grantPermissionsToTestedRole([ + 'access content', + ]); + $url = Url::fromRoute('tide_webform_jsonapi.add_webform', + ['webform' => 'tide_webform_content_rating']); + $request_options = []; + $request_options[RequestOptions::HEADERS]['Accept'] = 'application/vnd.api+json'; + $request_options[RequestOptions::HEADERS]['Content-Type'] = 'application/vnd.api+json'; + $request_options = NestedArray::mergeDeep($request_options, $this->getAuthenticationRequestOptions()); + $normalization = [ + 'data' => [ + 'type' => 'webform_submission--tide_webform_content_rating', + 'attributes' => [ + 'remote_addr' => '1.2.3.4', + 'data' => "url: '/home'\nwas_this_page_helpful: 'Yes'\ncomments: 'TEST\n Content Rating comment1'\ntestemail: 'w'\ntestextfield: ''\ntestemail: ''", + ], + ], + ]; + $request_options[RequestOptions::BODY] = Json::encode($normalization); + $response = $this->request('POST', $url, $request_options); + $this->assertSame(422, $response->getStatusCode(), $response->getBody()); + } + + /** + * Grants permissions to the authenticated role. + * + * @param string[] $permissions + * Permissions to grant. + */ + protected function grantPermissionsToTestedRole(array $permissions) { + $this->grantPermissions(Role::load(RoleInterface::AUTHENTICATED_ID), $permissions); + } + + /** + * Returns Guzzle request options for authentication. + * + * @return array + * Guzzle request options to use for authentication. + * + * @see \GuzzleHttp\ClientInterface::request() + */ + protected function getAuthenticationRequestOptions() { + return [ + 'headers' => [ + 'Authorization' => 'Basic ' . base64_encode($this->account->name->value . ':' . $this->account->passRaw), + ], + ]; + } + +} diff --git a/modules/tide_webform_jsonapi/tests/src/Kernel/Utility/UtilityTest.php b/modules/tide_webform_jsonapi/tests/src/Kernel/Utility/UtilityTest.php new file mode 100644 index 0000000..7d63543 --- /dev/null +++ b/modules/tide_webform_jsonapi/tests/src/Kernel/Utility/UtilityTest.php @@ -0,0 +1,162 @@ +tideWebformJsonapiHelper = \Drupal::service('tide_webform_jsonapi.helper'); + } + + /** + * Tests if the functions could convert and validate payloads. + * + * @covers ::webformValidateSettingsExtractor + * @covers ::getSupportedValidateElements + * @covers ::attachValidateSettingsToPayload + * @covers ::validatePayload + * @covers ::generateErrorString + * + * @dataProvider webformDataProvider + */ + public function testWebformValidation($payload, $webform_settings, $expected) { + $supported_validations = $this->tideWebformJsonapiHelper->getSupportedValidateElements(); + $results = $this->tideWebformJsonapiHelper->webformValidateSettingsExtractor($supported_validations, $webform_settings); + $new_array = []; + foreach ($results as $key => $r) { + $new_array[$key] = $this->tideWebformJsonapiHelper->attachValidateSettingsToPayload($r); + } + $errors = $this->tideWebformJsonapiHelper->validatePayload($payload, $new_array); + $this->assertEquals($expected, $errors); + } + + /** + * Data Provider. + */ + public function webformDataProvider() { + return [ + [ + [ + "comments" => "TEST Content Rating comment1", + "test_email" => "", + "url" => "/home", + "was_this_page_helpful" => "Yes", + "test_extfield" => "", + ], + [ + 'url' => [ + "#type" => "hidden", + "#title" => "URL", + ], + 'was_this_page_helpful' => [ + "#type" => "radios", + "#title" => "Was this page helpful?", + "#options" => "yes_no", + "#required" => TRUE, + ], + 'test_email' => [ + "#type" => "email", + "#title" => "testemail", + "#required" => TRUE, + '#required_error' => 'The field is mandatory 1.', + "#pattern" => "/^[\w.-]+@[\w.-]+\.[A-Za-z]{2,6}$/", + "#pattern_error" => "The value does not match the criteria 1.", + ], + 'test_extfield' => [ + "#type" => "textfield", + "#title" => "testextfield", + "#required" => TRUE, + "#required_error" => "The field is mandatory.", + "#pattern" => "/^[\w.-]+@[\w.-]+\.[A-Za-z]{2,6}$/", + "#pattern_error" => "The value does not match the criteria 2.", + "#counter_type" => "character", + "#counter_minimum" => 1, + "#counter_maximum" => 5, + "#counter_maximum_message" => "sdfgsdfg", + ], + ], + [ + "test_email" => [ + "The field is mandatory 1.", + "The value does not match the criteria 1.", + ], + "test_extfield" => [ + "The field is mandatory.", + "The value does not match the criteria 2.", + ], + ], + ], + [ + [ + "comments" => "TEST Content Rating comment1", + "test_email" => "", + "url" => "/home", + "was_this_page_helpful" => "Yes", + "test_extfield" => "", + ], + [ + 'url' => [ + "#type" => "hidden", + "#title" => "URL", + ], + 'was_this_page_helpful' => [ + "#type" => "radios", + "#title" => "Was this page helpful?", + "#options" => "yes_no", + "#required" => TRUE, + "#required_error" => 'The field is mandatory 1.', + ], + 'test_email' => [ + "#type" => "email", + "#title" => "testemail", + "#required" => TRUE, + "#pattern" => "/^[\w.-]+@[\w.-]+\.[A-Za-z]{2,6}$/", + ], + 'test_extfield' => [ + "#type" => "textfield", + "#title" => "testextfield", + "#required" => TRUE, + "#required_error" => "The field is mandatory.", + "#pattern" => "/^[\w.-]+@[\w.-]+\.[A-Za-z]{2,6}$/", + "#counter_type" => "character", + "#counter_minimum" => 1, + "#counter_maximum" => 5, + "#counter_maximum_message" => "sdfgsdfg", + ], + ], + [ + "test_email" => [ + "The field is mandatory.", + "The value does not match the criteria.", + ], + "test_extfield" => [ + "The field is mandatory.", + "The value does not match the criteria.", + ], + ], + ], + ]; + } + +} diff --git a/modules/tide_webform_jsonapi/tide_webform_jsonapi.info.yml b/modules/tide_webform_jsonapi/tide_webform_jsonapi.info.yml new file mode 100644 index 0000000..24c013b --- /dev/null +++ b/modules/tide_webform_jsonapi/tide_webform_jsonapi.info.yml @@ -0,0 +1,6 @@ +name: 'JSON:API Tide_webform' +description: "This module let's you validate payload for webform." +core_version_requirement: ^9.5 || ^10 +type: module +dependencies: + - drupal:jsonapi_resources diff --git a/modules/tide_webform_jsonapi/tide_webform_jsonapi.module b/modules/tide_webform_jsonapi/tide_webform_jsonapi.module new file mode 100644 index 0000000..f8150ef --- /dev/null +++ b/modules/tide_webform_jsonapi/tide_webform_jsonapi.module @@ -0,0 +1,32 @@ + 1) { + return $arr[1]; + } + return "Field $title is required."; + } + return TRUE; +} + +/** + * Callback for required validate. + */ +function tide_webform_jsonapi_pattern_validate($value, $arr, $title) { + if (preg_match($arr[0], $value) != 1) { + if (count($arr) > 1) { + return $arr[1]; + } + return "$title field is not in the right format."; + } + return TRUE; +} diff --git a/modules/tide_webform_jsonapi/tide_webform_jsonapi.routing.yml b/modules/tide_webform_jsonapi/tide_webform_jsonapi.routing.yml new file mode 100644 index 0000000..71115c1 --- /dev/null +++ b/modules/tide_webform_jsonapi/tide_webform_jsonapi.routing.yml @@ -0,0 +1,11 @@ +tide_webform_jsonapi.add_webform: + path: '/%jsonapi%/webform/{webform}/add' + methods: ['POST'] + defaults: + _jsonapi_resource: Drupal\tide_webform_jsonapi\TideResource\AddWebform + requirements: + _permission: 'access content' + options: + parameters: + webform: + type: 'entity:webform' diff --git a/modules/tide_webform_jsonapi/tide_webform_jsonapi.services.yml b/modules/tide_webform_jsonapi/tide_webform_jsonapi.services.yml new file mode 100644 index 0000000..b112293 --- /dev/null +++ b/modules/tide_webform_jsonapi/tide_webform_jsonapi.services.yml @@ -0,0 +1,3 @@ +services: + tide_webform_jsonapi.helper: + class: Drupal\tide_webform_jsonapi\TideWebformJsonapiHelper diff --git a/tide_webform.module b/tide_webform.module index e1560f1..fc1ca7b 100644 --- a/tide_webform.module +++ b/tide_webform.module @@ -12,7 +12,6 @@ use Drupal\Core\Form\FormStateInterface; use Drupal\Core\Session\AccountInterface; use Drupal\webform\Entity\Webform; use Drupal\webform\Entity\WebformSubmission; -use Drupal\webform\WebformSubmissionForm; /** * Implements hook_config_ignore_settings_alter(). @@ -289,14 +288,6 @@ function tide_webform_webform_submission_presave(WebformSubmission $webform_subm } } } - $errors = WebformSubmissionForm::validateWebformSubmission($webform_submission); - if ($errors) { - foreach ($elements as $key => $element) { - if (isset($errors[$key]) && !empty($errors[$key])) { - throw new Exception($errors[$key]); - } - } - } } /**