From f082fa276394fcb061c18b5784cf1c0f7a857835 Mon Sep 17 00:00:00 2001 From: Arthur Baghdasaryan <62405127+arthurbaghdas@users.noreply.github.com> Date: Fri, 6 Dec 2024 22:20:19 +0400 Subject: [PATCH] DP-34883: Modify form content type to accomodate other form types (#2678) * DP-34883: Modify form content type to accomodate other form types * Add validation * Front-end https://www.mass.gov/themes/custom/mass_theme/overrides/js/iframe_resizer_iframe.js * Changelog * Changelog * Changelog * Add help text * Help text * Fix * Fix * Fix * Fix * Bundle class changes * Form embed code changes * Form embed code changes * Form embed code changes * Form embed code changes * Update mass_validation.module with new domains * Update mass_validation.module - added live domain * update help text for form platform field --------- Co-authored-by: joeg8612 Co-authored-by: Joe Galluccio Co-authored-by: Tom Fleming --- changelogs/DP-34883.yml | 41 ++++++ ...ty_form_display.node.form_page.default.yml | 128 +++++++++++++++--- ...ty_view_display.node.form_page.default.yml | 22 +++ ...ay.node.form_page.link_and_description.yml | 4 + ..._view_display.node.form_page.link_only.yml | 4 + ...ty_view_display.node.form_page.listing.yml | 4 + ...ity_view_display.node.form_page.teaser.yml | 4 + ...isplay.node.form_page.title_short_desc.yml | 4 + ....field.node.form_page.field_form_embed.yml | 2 +- ...eld.node.form_page.field_form_platform.yml | 23 ++++ ...ld.field.node.form_page.field_form_url.yml | 23 ++++ ...field.storage.node.field_form_platform.yml | 27 ++++ .../field.storage.node.field_form_url.yml | 19 +++ .../mass_content/mass_content.deploy.php | 72 ++++++++++ .../custom/mass_content/mass_content.module | 4 - .../src/Entity/Bundle/node/FormPageBundle.php | 45 ++++++ .../Bundle/node/ServiceDetailsBundle.php | 10 -- .../custom/mass_utility/mass_utility.module | 3 + .../mass_validation/mass_validation.module | 59 +++++--- .../Constraint/GravityFormConstraint.php | 56 ++++++++ .../GravityFormConstraintValidator.php | 49 +++++++ .../themes/custom/mass_theme/mass_theme.theme | 50 ++++--- .../content/node--form-page.html.twig | 40 ++++-- 23 files changed, 610 insertions(+), 83 deletions(-) create mode 100644 changelogs/DP-34883.yml create mode 100644 conf/drupal/config/field.field.node.form_page.field_form_platform.yml create mode 100644 conf/drupal/config/field.field.node.form_page.field_form_url.yml create mode 100644 conf/drupal/config/field.storage.node.field_form_platform.yml create mode 100644 conf/drupal/config/field.storage.node.field_form_url.yml delete mode 100644 docroot/modules/custom/mass_content/src/Entity/Bundle/node/ServiceDetailsBundle.php create mode 100644 docroot/modules/custom/mass_validation/src/Plugin/Validation/Constraint/GravityFormConstraint.php create mode 100644 docroot/modules/custom/mass_validation/src/Plugin/Validation/Constraint/GravityFormConstraintValidator.php diff --git a/changelogs/DP-34883.yml b/changelogs/DP-34883.yml new file mode 100644 index 0000000000..e6d2da82c7 --- /dev/null +++ b/changelogs/DP-34883.yml @@ -0,0 +1,41 @@ +# +# Write your changelog entry here. Every pull request must have a changelog yml file. +# +# Change types: +# ############################################################################# +# You can use one of the following types: +# - Added: For new features. +# - Changed: For changes to existing functionality. +# - Deprecated: For soon-to-be removed features. +# - Removed: For removed features. +# - Fixed: For any bug fixes. +# - Security: In case of vulnerabilities. +# +# Format +# ############################################################################# +# The format is crucial. Please follow the examples below. For reference, the requirements are: +# - All 3 parts are required and you must include "Type", "description" and "issue". +# - "Type" must be left aligned and followed by a colon. +# - "description" must be indented with 2 spaces followed by a colon +# - "issue" must be indented with 4 spaces followed by a colon. +# - "issue" is for the Jira ticket number only e.g. DP-1234 +# - No extra spaces, indents, or blank lines are allowed. +# +# Example: +# ############################################################################# +# Fixed: +# - description: Fixes scrolling on edit pages in Safari. +# issue: DP-13314 +# +# You may add more than 1 description & issue for each type using the following format: +# Changed: +# - description: Automating the release branch. +# issue: DP-10166 +# - description: Second change item that needs a description. +# issue: DP-19875 +# - description: Third change item that needs a description along with an issue. +# issue: DP-19843 +# +Added: + - description: Modify form content type to accomodate other form types. + issue: DP-34883 diff --git a/conf/drupal/config/core.entity_form_display.node.form_page.default.yml b/conf/drupal/config/core.entity_form_display.node.form_page.default.yml index 8084700925..5b7b62eeba 100644 --- a/conf/drupal/config/core.entity_form_display.node.form_page.default.yml +++ b/conf/drupal/config/core.entity_form_display.node.form_page.default.yml @@ -8,7 +8,9 @@ dependencies: - field.field.node.form_page.field_form_embed - field.field.node.form_page.field_form_lede - field.field.node.form_page.field_form_listing_desc + - field.field.node.form_page.field_form_platform - field.field.node.form_page.field_form_ref_contacts_3 + - field.field.node.form_page.field_form_url - field.field.node.form_page.field_form_you_will - field.field.node.form_page.field_hide_feedback_component - field.field.node.form_page.field_intended_audience @@ -20,10 +22,12 @@ dependencies: - node.type.form_page - workflows.workflow.editorial module: + - conditional_fields - content_moderation - entity_hierarchy - entity_reference_tree - field_group + - link - mass_fields - maxlength - metatag @@ -36,7 +40,7 @@ third_party_settings: label: 'Form page edit form' region: content parent_name: '' - weight: 0 + weight: 1 format_type: tabs format_settings: classes: '' @@ -50,6 +54,8 @@ third_party_settings: - field_form_listing_desc - field_form_you_will - field_form_ref_contacts_3 + - field_form_platform + - field_form_url - field_form_embed - field_hide_feedback_component - field_primary_parent @@ -78,7 +84,7 @@ mode: default content: field_collections: type: entity_reference_tree - weight: 21 + weight: 23 region: content settings: theme: default @@ -96,7 +102,7 @@ content: third_party_settings: { } field_english_version: type: entity_reference_autocomplete - weight: 19 + weight: 21 region: content settings: match_operator: CONTAINS @@ -124,10 +130,47 @@ content: selector: '' field_form_embed: type: form_embed - weight: 13 + weight: 15 region: content settings: { } - third_party_settings: { } + third_party_settings: + conditional_fields: + 7f133b58-2762-4fab-aea2-7cf02fc8ef77: + entity_type: node + bundle: form_page + dependee: field_form_platform + settings: + state: visible + reset: false + condition: value + grouping: AND + values_set: 1 + value: '' + values: { } + value_form: + - + value: formstack + effect: show + effect_options: { } + selector: '' + 8703e2e2-aceb-432c-b7a5-2a39e92ce045: + entity_type: node + bundle: form_page + dependee: field_form_platform + settings: + state: required + reset: false + condition: value + grouping: AND + values_set: 1 + value: '' + values: { } + value_form: + - + value: formstack + effect: show + effect_options: { } + selector: '' field_form_lede: type: string_textfield weight: 9 @@ -144,6 +187,12 @@ content: size: 60 placeholder: '' third_party_settings: { } + field_form_platform: + type: options_select + weight: 13 + region: content + settings: { } + third_party_settings: { } field_form_ref_contacts_3: type: entity_reference_autocomplete weight: 12 @@ -154,6 +203,51 @@ content: size: 60 placeholder: '' third_party_settings: { } + field_form_url: + type: link_default + weight: 14 + region: content + settings: + placeholder_url: '' + placeholder_title: '' + third_party_settings: + conditional_fields: + 45983f26-adea-428d-86e9-e9edbcaf562a: + entity_type: node + bundle: form_page + dependee: field_form_platform + settings: + state: visible + reset: false + condition: value + grouping: AND + values_set: 1 + value: '' + values: { } + value_form: + - + value: gravity_forms + effect: show + effect_options: { } + selector: '' + db5a152d-4798-4b27-82b9-aaa01ec0def3: + entity_type: node + bundle: form_page + dependee: field_form_platform + settings: + state: required + reset: false + condition: value + grouping: AND + values_set: 1 + value: '' + values: { } + value_form: + - + value: gravity_forms + effect: show + effect_options: { } + selector: '' field_form_you_will: type: text_textarea weight: 11 @@ -164,20 +258,20 @@ content: third_party_settings: { } field_hide_feedback_component: type: boolean_checkbox - weight: 14 + weight: 16 region: content settings: display_label: true third_party_settings: { } field_intended_audience: type: options_buttons - weight: 17 + weight: 19 region: content settings: { } third_party_settings: { } field_metatags: type: metatag_firehose - weight: 8 + weight: 2 region: content settings: sidebar: true @@ -185,7 +279,7 @@ content: third_party_settings: { } field_organizations: type: entity_reference_autocomplete - weight: 16 + weight: 18 region: content settings: match_operator: CONTAINS @@ -195,7 +289,7 @@ content: third_party_settings: { } field_primary_parent: type: entity_reference_hierarchy_autocomplete - weight: 15 + weight: 17 region: content settings: match_operator: CONTAINS @@ -206,7 +300,7 @@ content: third_party_settings: { } field_reusable_label: type: entity_reference_autocomplete - weight: 20 + weight: 22 region: content settings: match_operator: CONTAINS @@ -224,14 +318,14 @@ content: third_party_settings: { } langcode: type: language_select - weight: 18 + weight: 20 region: content settings: include_locked: false third_party_settings: { } moderation_state: type: moderation_state_default - weight: 100 + weight: 8 region: content settings: { } third_party_settings: { } @@ -254,20 +348,20 @@ content: third_party_settings: { } search: type: boolean_checkbox - weight: 99 + weight: 6 region: content settings: display_label: true third_party_settings: { } search_nosnippet: type: boolean_checkbox - weight: 99 + weight: 7 region: content settings: display_label: true third_party_settings: { } simple_sitemap: - weight: 10 + weight: 3 region: content settings: { } third_party_settings: { } @@ -306,7 +400,7 @@ content: settings: { } third_party_settings: { } url_redirects: - weight: 50 + weight: 5 region: content settings: { } third_party_settings: { } diff --git a/conf/drupal/config/core.entity_view_display.node.form_page.default.yml b/conf/drupal/config/core.entity_view_display.node.form_page.default.yml index d7f4b329d1..8925728767 100644 --- a/conf/drupal/config/core.entity_view_display.node.form_page.default.yml +++ b/conf/drupal/config/core.entity_view_display.node.form_page.default.yml @@ -8,7 +8,9 @@ dependencies: - field.field.node.form_page.field_form_embed - field.field.node.form_page.field_form_lede - field.field.node.form_page.field_form_listing_desc + - field.field.node.form_page.field_form_platform - field.field.node.form_page.field_form_ref_contacts_3 + - field.field.node.form_page.field_form_url - field.field.node.form_page.field_form_you_will - field.field.node.form_page.field_hide_feedback_component - field.field.node.form_page.field_intended_audience @@ -19,6 +21,7 @@ dependencies: - field.field.node.form_page.field_short_title - node.type.form_page module: + - link - mass_fields - metatag - options @@ -83,6 +86,13 @@ content: third_party_settings: { } weight: 7 region: content + field_form_platform: + type: list_default + label: above + settings: { } + third_party_settings: { } + weight: 16 + region: content field_form_ref_contacts_3: type: entity_reference_label label: above @@ -91,6 +101,18 @@ content: third_party_settings: { } weight: 4 region: content + field_form_url: + type: link + label: above + settings: + trim_length: 80 + url_only: false + url_plain: false + rel: '' + target: '' + third_party_settings: { } + weight: 17 + region: content field_form_you_will: type: text_default label: above diff --git a/conf/drupal/config/core.entity_view_display.node.form_page.link_and_description.yml b/conf/drupal/config/core.entity_view_display.node.form_page.link_and_description.yml index efa0d3c8db..9b876af7d7 100644 --- a/conf/drupal/config/core.entity_view_display.node.form_page.link_and_description.yml +++ b/conf/drupal/config/core.entity_view_display.node.form_page.link_and_description.yml @@ -9,7 +9,9 @@ dependencies: - field.field.node.form_page.field_form_embed - field.field.node.form_page.field_form_lede - field.field.node.form_page.field_form_listing_desc + - field.field.node.form_page.field_form_platform - field.field.node.form_page.field_form_ref_contacts_3 + - field.field.node.form_page.field_form_url - field.field.node.form_page.field_form_you_will - field.field.node.form_page.field_hide_feedback_component - field.field.node.form_page.field_intended_audience @@ -48,7 +50,9 @@ hidden: field_english_version: true field_form_embed: true field_form_lede: true + field_form_platform: true field_form_ref_contacts_3: true + field_form_url: true field_form_you_will: true field_hide_feedback_component: true field_intended_audience: true diff --git a/conf/drupal/config/core.entity_view_display.node.form_page.link_only.yml b/conf/drupal/config/core.entity_view_display.node.form_page.link_only.yml index 0f40f0bd4f..9f5fd930bd 100644 --- a/conf/drupal/config/core.entity_view_display.node.form_page.link_only.yml +++ b/conf/drupal/config/core.entity_view_display.node.form_page.link_only.yml @@ -9,7 +9,9 @@ dependencies: - field.field.node.form_page.field_form_embed - field.field.node.form_page.field_form_lede - field.field.node.form_page.field_form_listing_desc + - field.field.node.form_page.field_form_platform - field.field.node.form_page.field_form_ref_contacts_3 + - field.field.node.form_page.field_form_url - field.field.node.form_page.field_form_you_will - field.field.node.form_page.field_hide_feedback_component - field.field.node.form_page.field_intended_audience @@ -41,7 +43,9 @@ hidden: field_form_embed: true field_form_lede: true field_form_listing_desc: true + field_form_platform: true field_form_ref_contacts_3: true + field_form_url: true field_form_you_will: true field_hide_feedback_component: true field_intended_audience: true diff --git a/conf/drupal/config/core.entity_view_display.node.form_page.listing.yml b/conf/drupal/config/core.entity_view_display.node.form_page.listing.yml index b9ec300da2..7e2f7bdfab 100644 --- a/conf/drupal/config/core.entity_view_display.node.form_page.listing.yml +++ b/conf/drupal/config/core.entity_view_display.node.form_page.listing.yml @@ -9,7 +9,9 @@ dependencies: - field.field.node.form_page.field_form_embed - field.field.node.form_page.field_form_lede - field.field.node.form_page.field_form_listing_desc + - field.field.node.form_page.field_form_platform - field.field.node.form_page.field_form_ref_contacts_3 + - field.field.node.form_page.field_form_url - field.field.node.form_page.field_form_you_will - field.field.node.form_page.field_hide_feedback_component - field.field.node.form_page.field_intended_audience @@ -49,7 +51,9 @@ hidden: field_english_version: true field_form_embed: true field_form_listing_desc: true + field_form_platform: true field_form_ref_contacts_3: true + field_form_url: true field_form_you_will: true field_hide_feedback_component: true field_intended_audience: true diff --git a/conf/drupal/config/core.entity_view_display.node.form_page.teaser.yml b/conf/drupal/config/core.entity_view_display.node.form_page.teaser.yml index a91fa35dfd..15e19dbae1 100644 --- a/conf/drupal/config/core.entity_view_display.node.form_page.teaser.yml +++ b/conf/drupal/config/core.entity_view_display.node.form_page.teaser.yml @@ -9,7 +9,9 @@ dependencies: - field.field.node.form_page.field_form_embed - field.field.node.form_page.field_form_lede - field.field.node.form_page.field_form_listing_desc + - field.field.node.form_page.field_form_platform - field.field.node.form_page.field_form_ref_contacts_3 + - field.field.node.form_page.field_form_url - field.field.node.form_page.field_form_you_will - field.field.node.form_page.field_hide_feedback_component - field.field.node.form_page.field_intended_audience @@ -51,7 +53,9 @@ hidden: field_form_embed: true field_form_lede: true field_form_listing_desc: true + field_form_platform: true field_form_ref_contacts_3: true + field_form_url: true field_form_you_will: true field_hide_feedback_component: true field_intended_audience: true diff --git a/conf/drupal/config/core.entity_view_display.node.form_page.title_short_desc.yml b/conf/drupal/config/core.entity_view_display.node.form_page.title_short_desc.yml index 3b93c6b39c..5c98e4a943 100644 --- a/conf/drupal/config/core.entity_view_display.node.form_page.title_short_desc.yml +++ b/conf/drupal/config/core.entity_view_display.node.form_page.title_short_desc.yml @@ -9,7 +9,9 @@ dependencies: - field.field.node.form_page.field_form_embed - field.field.node.form_page.field_form_lede - field.field.node.form_page.field_form_listing_desc + - field.field.node.form_page.field_form_platform - field.field.node.form_page.field_form_ref_contacts_3 + - field.field.node.form_page.field_form_url - field.field.node.form_page.field_form_you_will - field.field.node.form_page.field_hide_feedback_component - field.field.node.form_page.field_intended_audience @@ -48,7 +50,9 @@ hidden: field_english_version: true field_form_embed: true field_form_listing_desc: true + field_form_platform: true field_form_ref_contacts_3: true + field_form_url: true field_form_you_will: true field_hide_feedback_component: true field_intended_audience: true diff --git a/conf/drupal/config/field.field.node.form_page.field_form_embed.yml b/conf/drupal/config/field.field.node.form_page.field_form_embed.yml index d87c38c5ef..af9f85671b 100644 --- a/conf/drupal/config/field.field.node.form_page.field_form_embed.yml +++ b/conf/drupal/config/field.field.node.form_page.field_form_embed.yml @@ -13,7 +13,7 @@ entity_type: node bundle: form_page label: 'Embedded form' description: 'Form pages on Mass.gov embed forms created separately in Formstack. In Formstack, click the “publish” link for your form to find the embed code and paste it here. IMPORTANT: In addition, open the form in Build mode, choose the Theme tab from the lower left, and confirm your form uses the ‘Mayflower’ theme.' -required: true +required: false translatable: false default_value: { } default_value_callback: '' diff --git a/conf/drupal/config/field.field.node.form_page.field_form_platform.yml b/conf/drupal/config/field.field.node.form_page.field_form_platform.yml new file mode 100644 index 0000000000..a07e6dceff --- /dev/null +++ b/conf/drupal/config/field.field.node.form_page.field_form_platform.yml @@ -0,0 +1,23 @@ +uuid: aacfaae0-36e7-4c3b-b2c6-7609382aebf1 +langcode: en +status: true +dependencies: + config: + - field.storage.node.field_form_platform + - node.type.form_page + module: + - options +id: node.form_page.field_form_platform +field_name: field_form_platform +entity_type: node +bundle: form_page +label: 'Form platform' +description: 'Keep the default option which is "Formstack". The new "Gravity forms" option is being used for internal testing.' +required: true +translatable: false +default_value: + - + value: formstack +default_value_callback: '' +settings: { } +field_type: list_string diff --git a/conf/drupal/config/field.field.node.form_page.field_form_url.yml b/conf/drupal/config/field.field.node.form_page.field_form_url.yml new file mode 100644 index 0000000000..fc33b47f58 --- /dev/null +++ b/conf/drupal/config/field.field.node.form_page.field_form_url.yml @@ -0,0 +1,23 @@ +uuid: 6f20fbfa-6938-4610-82d8-5966c49f7671 +langcode: en +status: true +dependencies: + config: + - field.storage.node.field_form_url + - node.type.form_page + module: + - link +id: node.form_page.field_form_url +field_name: field_form_url +entity_type: node +bundle: form_page +label: 'Form URL' +description: 'FOR INTERNAL TESTING ONLY. Paste the embed URL from the Gravity Forms product provided by Mass Digital. It is not possible to embed forms from other sources.' +required: false +translatable: false +default_value: { } +default_value_callback: '' +settings: + title: 0 + link_type: 16 +field_type: link diff --git a/conf/drupal/config/field.storage.node.field_form_platform.yml b/conf/drupal/config/field.storage.node.field_form_platform.yml new file mode 100644 index 0000000000..44126859bf --- /dev/null +++ b/conf/drupal/config/field.storage.node.field_form_platform.yml @@ -0,0 +1,27 @@ +uuid: 2207f6ea-2541-4234-980b-cf0b9cdf422c +langcode: en +status: true +dependencies: + module: + - node + - options +id: node.field_form_platform +field_name: field_form_platform +entity_type: node +type: list_string +settings: + allowed_values: + - + value: formstack + label: Formstack + - + value: gravity_forms + label: 'Gravity forms' + allowed_values_function: '' +module: options +locked: false +cardinality: 1 +translatable: true +indexes: { } +persist_with_no_fields: false +custom_storage: false diff --git a/conf/drupal/config/field.storage.node.field_form_url.yml b/conf/drupal/config/field.storage.node.field_form_url.yml new file mode 100644 index 0000000000..67911827df --- /dev/null +++ b/conf/drupal/config/field.storage.node.field_form_url.yml @@ -0,0 +1,19 @@ +uuid: e7d29c73-91aa-403e-9935-fefda11d14b8 +langcode: en +status: true +dependencies: + module: + - link + - node +id: node.field_form_url +field_name: field_form_url +entity_type: node +type: link +settings: { } +module: link +locked: false +cardinality: 1 +translatable: true +indexes: { } +persist_with_no_fields: false +custom_storage: false diff --git a/docroot/modules/custom/mass_content/mass_content.deploy.php b/docroot/modules/custom/mass_content/mass_content.deploy.php index 33c9785625..0841ec4987 100644 --- a/docroot/modules/custom/mass_content/mass_content.deploy.php +++ b/docroot/modules/custom/mass_content/mass_content.deploy.php @@ -257,3 +257,75 @@ function mass_content_deploy_card_label_migration(&$sandbox) { return t('Card paragraph link field label into the Card header text field migration has been completed.'); } } + +/** + * Deploy hook to update field_form_platform to 'Formstack' for all form_page content type nodes. + */ +function mass_content_deploy_form_platform_update(&$sandbox) { + $_ENV['MASS_FLAGGING_BYPASS'] = TRUE; + $query = \Drupal::entityQuery('node')->accessCheck(FALSE); + $query->condition('type', 'form_page'); + + // Initialize sandbox if this is the first run. + if (empty($sandbox)) { + $sandbox['progress'] = 0; + $sandbox['current'] = 0; + $count_query = clone $query; + $sandbox['max'] = $count_query->count()->execute(); + } + + // Set batch size to avoid memory exhaustion. + $batch_size = 50; + + // Fetch node IDs in batches. + $nids = $query->condition('nid', $sandbox['current'], '>') + ->sort('nid') + ->range(0, $batch_size) + ->execute(); + + // Load the nodes from the fetched node IDs. + $node_storage = \Drupal::entityTypeManager()->getStorage('node'); + $nodes = $node_storage->loadMultiple($nids); + + // Temporarily disable entity hierarchy writes to avoid issues during batch processing. + \Drupal::state()->set('entity_hierarchy_disable_writes', TRUE); + + foreach ($nodes as $node) { + // Update the sandbox current nid. + $sandbox['current'] = $node->id(); + try { + // Check if the field exists and is not already set to 'formstack'. + if ($node->hasField('field_form_platform')) { + // Set the field value to 'formstack'. + $node->set('field_form_platform', 'formstack'); + // Save the node. + $node->save(); + } + } + catch (\Exception $e) { + // Catch any exception and continue processing. + \Drupal::logger('mass_validation')->error('Failed to update node @nid: @message', [ + '@nid' => $node->id(), + '@message' => $e->getMessage(), + ]); + // Log the error but do not re-enable writes here, continue processing. + } + + // Track progress. + $sandbox['progress']++; + } + + // Log progress for each batch. + \Drupal::logger('mass_validation')->notice('Processed @progress out of @max nodes.', [ + '@progress' => $sandbox['progress'], + '@max' => $sandbox['max'], + ]); + + // Update finished state. + $sandbox['#finished'] = empty($sandbox['max']) ? 1 : ($sandbox['progress'] / $sandbox['max']); + if ($sandbox['#finished'] >= 1) { + // Re-enable entity hierarchy writes after all nodes are processed. + \Drupal::state()->set('entity_hierarchy_disable_writes', FALSE); + return t('All form_page nodes have been updated with the "Formstack" value in field_form_platform.'); + } +} diff --git a/docroot/modules/custom/mass_content/mass_content.module b/docroot/modules/custom/mass_content/mass_content.module index e64ada5425..444fa2a551 100644 --- a/docroot/modules/custom/mass_content/mass_content.module +++ b/docroot/modules/custom/mass_content/mass_content.module @@ -39,7 +39,6 @@ use Drupal\mass_content\Entity\Bundle\node\PageBundle; use Drupal\mass_content\Entity\Bundle\node\PersonBundle; use Drupal\mass_content\Entity\Bundle\node\RegulationBundle; use Drupal\mass_content\Entity\Bundle\node\RulesBundle; -use Drupal\mass_content\Entity\Bundle\node\ServiceDetailsBundle; use Drupal\mass_content\Entity\Bundle\node\ServicePageBundle; use Drupal\mass_content\Entity\Bundle\node\SitewideAlertBundle; use Drupal\mass_content\Entity\Bundle\node\StackedLayoutBundle; @@ -1091,9 +1090,6 @@ function mass_content_entity_bundle_info_alter(array &$bundles): void { if (isset($bundles['node']['rules'])) { $bundles['node']['rules']['class'] = RulesBundle::class; } - if (isset($bundles['node']['service_details'])) { - $bundles['node']['service_details']['class'] = ServiceDetailsBundle::class; - } if (isset($bundles['node']['service_page'])) { $bundles['node']['service_page']['class'] = ServicePageBundle::class; } diff --git a/docroot/modules/custom/mass_content/src/Entity/Bundle/node/FormPageBundle.php b/docroot/modules/custom/mass_content/src/Entity/Bundle/node/FormPageBundle.php index df90fe3df9..0613376cb7 100644 --- a/docroot/modules/custom/mass_content/src/Entity/Bundle/node/FormPageBundle.php +++ b/docroot/modules/custom/mass_content/src/Entity/Bundle/node/FormPageBundle.php @@ -2,9 +2,54 @@ namespace Drupal\mass_content\Entity\Bundle\node; +use Drupal\Core\Field\FieldItemListInterface; + /** * A bundle class for node entities. */ class FormPageBundle extends NodeBundle { + // Define constants for platform values. + public const PLATFORM_FORMSTACK = 'formstack'; + public const PLATFORM_GRAVITY_FORMS = 'gravity_forms'; + + /** + * Get platform. + */ + public function getPlatform(): FieldItemListInterface { + return $this->get('field_form_platform'); + } + + /** + * Check if platform is FormStack. + * + * @return bool + * TRUE if the platform is FormStack and field is set, FALSE otherwise. + */ + public function isFormStack(): bool { + $platform = $this->getPlatform(); + return !empty($platform->getString()) && $platform->getString() === self::PLATFORM_FORMSTACK; + } + + /** + * Check if platform is Gravity Forms. + * + * @return bool + * TRUE if the platform is Gravity Forms and field is set, FALSE otherwise. + */ + public function isGravityForms(): bool { + $platform = $this->getPlatform(); + return !empty($platform->getString()) && $platform->getString() === self::PLATFORM_GRAVITY_FORMS; + } + + /** + * Get form embed field. + * + * @return \Drupal\Core\Field\FieldItemListInterface|null + * The form embed field, or NULL if it's not set. + */ + public function getFormEmbed(): ?FieldItemListInterface { + return $this->get('field_form_embed'); + } + } diff --git a/docroot/modules/custom/mass_content/src/Entity/Bundle/node/ServiceDetailsBundle.php b/docroot/modules/custom/mass_content/src/Entity/Bundle/node/ServiceDetailsBundle.php deleted file mode 100644 index bcb5c44615..0000000000 --- a/docroot/modules/custom/mass_content/src/Entity/Bundle/node/ServiceDetailsBundle.php +++ /dev/null @@ -1,10 +0,0 @@ -id() == 'node' && array_key_exists('field_social_links', $fields) && !empty($fields['field_social_links'])) { - $field_social_links = &$fields['field_social_links']; - $field_social_links->addConstraint('SocialLink', [ - 'allowedValues' => [ - 'twitter.com', - 'x.com', - 'flickr.com', - 'facebook.com', - 'threads.net', - 'instagram.com', - 'linkedin.com', - 'youtube.com', - 'vimeo.com', - 'medium.com', - 'blog.mass.gov', - 'www.mass.gov', - ], - ]); + + if ($entity_type->id() == 'node') { + if (array_key_exists('field_social_links', $fields) && !empty($fields['field_social_links'])) { + /** @var Drupal\field\Entity\FieldConfig $field_social_links */ + $field_social_links = &$fields['field_social_links']; + $field_social_links->addConstraint('SocialLink', [ + 'allowedValues' => [ + 'twitter.com', + 'x.com', + 'flickr.com', + 'facebook.com', + 'threads.net', + 'instagram.com', + 'linkedin.com', + 'youtube.com', + 'vimeo.com', + 'medium.com', + 'blog.mass.gov', + 'www.mass.gov', + ], + ]); + } + if (array_key_exists('field_form_url', $fields) && !empty($fields['field_form_url'])) { + $field_form_url = &$fields['field_form_url']; + $field_form_url->addConstraint('GravityForm', [ + 'allowedValues' => [ + 'dev.forms.mass.gov', + 'live.forms.mass.gov', + 'test.forms.mass.gov', + 'forms.mass.gov', + 'live-mass-forms.pantheonsite.io', + 'dev-mass-forms.pantheonsite.io', + 'test-mass-forms.pantheonsite.io', + 'embed-test2-mass-forms.pantheonsite.io', + 'embed-test3-mass-forms.pantheonsite.io', + 'embed-test-mass-forms.pantheonsite.io', + ] + ]); + } + } if ($entity_type->id() === 'paragraph') { diff --git a/docroot/modules/custom/mass_validation/src/Plugin/Validation/Constraint/GravityFormConstraint.php b/docroot/modules/custom/mass_validation/src/Plugin/Validation/Constraint/GravityFormConstraint.php new file mode 100644 index 0000000000..e93e0744b2 --- /dev/null +++ b/docroot/modules/custom/mass_validation/src/Plugin/Validation/Constraint/GravityFormConstraint.php @@ -0,0 +1,56 @@ + $options, + ]; + } + + parent::__construct($options); + if (NULL === $this->allowedValues) { + throw new MissingOptionsException(sprintf('Gravity Form Link must be given for constraint %s', __CLASS__), ['gravity_form_link']); + } + } + +} diff --git a/docroot/modules/custom/mass_validation/src/Plugin/Validation/Constraint/GravityFormConstraintValidator.php b/docroot/modules/custom/mass_validation/src/Plugin/Validation/Constraint/GravityFormConstraintValidator.php new file mode 100644 index 0000000000..09466f32f9 --- /dev/null +++ b/docroot/modules/custom/mass_validation/src/Plugin/Validation/Constraint/GravityFormConstraintValidator.php @@ -0,0 +1,49 @@ +isEmpty()) { + $allowed_values = $constraint->allowedValues; + $invalid_link = ''; + foreach ($field->getValue() as $item) { + $match_flag = FALSE; + foreach ($allowed_values as $allowed_value) { + // We take for valid url the one that have 'allowed_value' followed by + // '/' character with the allowed values. We accept path-less entries. + if (preg_match('/^https?:\/\/' . preg_quote($allowed_value, '/') . '(\/|$)/', $item['uri']) === 1) { + $match_flag = TRUE; + continue; + } + } + if (!$match_flag) { + $invalid_link = $item; + break; + } + } + + // If we have an invalid link then add violation. + if (is_array($invalid_link)) { + $this->context->addViolation($constraint->message, ['%gravity_form_link' => $invalid_link['uri']]); + } + } + } + +} diff --git a/docroot/themes/custom/mass_theme/mass_theme.theme b/docroot/themes/custom/mass_theme/mass_theme.theme index e611a2a214..3fe74629e3 100644 --- a/docroot/themes/custom/mass_theme/mass_theme.theme +++ b/docroot/themes/custom/mass_theme/mass_theme.theme @@ -612,25 +612,41 @@ function mass_theme_preprocess_node_form_page(&$variables) { $variables['mainContent'] = $mainContent; $variables['sideContent'] = $sideContent; - if (!empty($node->field_form_embed->value)) { - try { - $crawler = new Crawler($node->field_form_embed->value); - $script_url = $crawler->filterXPath('//script[@src]')->attr('src'); - $noscript_url = $crawler->filterXPath('//noscript/a[@href]')->attr('href'); - $validate_script_url = parse_url($script_url, PHP_URL_HOST); - $validate_noscript_url = parse_url($noscript_url, PHP_URL_HOST); - - if (str_ends_with($validate_script_url, "formstack.com")) { - $variables['form_url'] = Url::fromUri($script_url); - } - if (str_ends_with($validate_noscript_url, "formstack.com")) { - $variables['form_url_noscript'] = $noscript_url; + if (!$node->getPlatform()->isEmpty()) { + // Check if the platform is FormStack. + if ($node->isFormStack()) { + if (!$node->getFormEmbed()->isEmpty()) { + try { + if ($form_embed = $node->getFormEmbed()->getString()) { + $crawler = new Crawler($form_embed); + $script_url = $crawler->filterXPath('//script[@src]') + ->attr('src'); + $noscript_url = $crawler->filterXPath('//noscript/a[@href]') + ->attr('href'); + + $validate_script_url = parse_url($script_url, PHP_URL_HOST); + $validate_noscript_url = parse_url($noscript_url, PHP_URL_HOST); + + if (str_ends_with($validate_script_url, 'formstack.com')) { + $variables['form_url'] = Url::fromUri($script_url); + } + if (str_ends_with($validate_noscript_url, 'formstack.com')) { + $variables['form_url_noscript'] = $noscript_url; + } + } + } + catch (\Exception $e) { + \Drupal::logger('mass_fields') + ->warning('Parsing of formstack URL failed: %message', [ + '%message' => $e->getMessage(), + ]); + } } } - catch (\Exception $e) { - \Drupal::logger('mass_fields')->warning('Parsing of formstack URL failed: %message', [ - '%message' => $e->getMessage(), - ]); + + // Check if the platform is Gravity Forms. + elseif ($node->isGravityForms()) { + $variables['#attached']['library'][] = 'mass_theme/iframe-resizer'; } } diff --git a/docroot/themes/custom/mass_theme/templates/content/node--form-page.html.twig b/docroot/themes/custom/mass_theme/templates/content/node--form-page.html.twig index d99f9fd6a8..c623e281ef 100755 --- a/docroot/themes/custom/mass_theme/templates/content/node--form-page.html.twig +++ b/docroot/themes/custom/mass_theme/templates/content/node--form-page.html.twig @@ -69,21 +69,31 @@ {% set formRequirements = formRequirements|merge({'level': level}) %} {% include "@organisms/by-author/form-requirements.twig" %} {% endif %} - - + {% if node.getPlatform().getString() == constant('Drupal\\mass_content\\Entity\\Bundle\\node\\FormPageBundle::PLATFORM_FORMSTACK') %} + + + {% elseif node.getPlatform().getString() == constant('Drupal\\mass_content\\Entity\\Bundle\\node\\FormPageBundle::PLATFORM_GRAVITY_FORMS') %} + {% include "@atoms/09-media/iframe.twig" with { + "iframe": { + "customClass": 'js-iframe-resizer', + "src": node.field_form_url.uri, + "height": 'auto', + } + } %} + {% endif %} {% endblock %}