From 97d99dd3e0b447f47248ca24b42a37025ebac9fd Mon Sep 17 00:00:00 2001 From: CI Bot Date: Fri, 15 Dec 2023 18:51:42 -0500 Subject: [PATCH 01/10] feat: yalb-959 add min/max help text and constraints on specified custom block types --- ...ck_content.gallery.field_gallery_items.yml | 2 +- ...tent.media_grid.field_media_grid_items.yml | 2 +- ....block_content.quick_links.field_links.yml | 2 +- ...ld.field.block_content.tabs.field_tabs.yml | 2 +- .../config/sync/user.role.site_admin.yml | 1 - .../Constraint/MinMaxConstraint.php | 24 +++++++++ .../Constraint/MinMaxConstraintValidator.php | 54 +++++++++++++++++++ .../modules/custom/ys_core/ys_core.module | 41 ++++++++++++++ 8 files changed, 123 insertions(+), 5 deletions(-) create mode 100644 web/profiles/custom/yalesites_profile/modules/custom/ys_core/src/Plugin/Validation/Constraint/MinMaxConstraint.php create mode 100644 web/profiles/custom/yalesites_profile/modules/custom/ys_core/src/Plugin/Validation/Constraint/MinMaxConstraintValidator.php diff --git a/web/profiles/custom/yalesites_profile/config/sync/field.field.block_content.gallery.field_gallery_items.yml b/web/profiles/custom/yalesites_profile/config/sync/field.field.block_content.gallery.field_gallery_items.yml index 1762af358d..0e03fadc6f 100644 --- a/web/profiles/custom/yalesites_profile/config/sync/field.field.block_content.gallery.field_gallery_items.yml +++ b/web/profiles/custom/yalesites_profile/config/sync/field.field.block_content.gallery.field_gallery_items.yml @@ -13,7 +13,7 @@ field_name: field_gallery_items entity_type: block_content bundle: gallery label: Images -description: '' +description: 'This block requires a minimum of 2 gallery items.' required: true translatable: false default_value: { } diff --git a/web/profiles/custom/yalesites_profile/config/sync/field.field.block_content.media_grid.field_media_grid_items.yml b/web/profiles/custom/yalesites_profile/config/sync/field.field.block_content.media_grid.field_media_grid_items.yml index e46b65e7db..7973980b7c 100644 --- a/web/profiles/custom/yalesites_profile/config/sync/field.field.block_content.media_grid.field_media_grid_items.yml +++ b/web/profiles/custom/yalesites_profile/config/sync/field.field.block_content.media_grid.field_media_grid_items.yml @@ -13,7 +13,7 @@ field_name: field_media_grid_items entity_type: block_content bundle: media_grid label: Media -description: '' +description: 'This block requires a minimum of 2 media grid items.' required: false translatable: false default_value: { } diff --git a/web/profiles/custom/yalesites_profile/config/sync/field.field.block_content.quick_links.field_links.yml b/web/profiles/custom/yalesites_profile/config/sync/field.field.block_content.quick_links.field_links.yml index 97cae4fb32..dc02479c11 100644 --- a/web/profiles/custom/yalesites_profile/config/sync/field.field.block_content.quick_links.field_links.yml +++ b/web/profiles/custom/yalesites_profile/config/sync/field.field.block_content.quick_links.field_links.yml @@ -12,7 +12,7 @@ field_name: field_links entity_type: block_content bundle: quick_links label: Links -description: '' +description: 'This block requires a minimum of 3 and a maximum of 9 links.' required: true translatable: false default_value: { } diff --git a/web/profiles/custom/yalesites_profile/config/sync/field.field.block_content.tabs.field_tabs.yml b/web/profiles/custom/yalesites_profile/config/sync/field.field.block_content.tabs.field_tabs.yml index 8079df4aa5..22fbcfaa85 100644 --- a/web/profiles/custom/yalesites_profile/config/sync/field.field.block_content.tabs.field_tabs.yml +++ b/web/profiles/custom/yalesites_profile/config/sync/field.field.block_content.tabs.field_tabs.yml @@ -13,7 +13,7 @@ field_name: field_tabs entity_type: block_content bundle: tabs label: Tabs -description: '' +description: 'This block requires a minimum of 2 and a maximum of 5 tabs.' required: true translatable: false default_value: { } diff --git a/web/profiles/custom/yalesites_profile/config/sync/user.role.site_admin.yml b/web/profiles/custom/yalesites_profile/config/sync/user.role.site_admin.yml index 89cb244eb9..51bfcc76fb 100644 --- a/web/profiles/custom/yalesites_profile/config/sync/user.role.site_admin.yml +++ b/web/profiles/custom/yalesites_profile/config/sync/user.role.site_admin.yml @@ -70,7 +70,6 @@ label: 'Site administrator' weight: 3 is_admin: null permissions: - - 'access block library' - 'access content overview' - 'access contextual links' - 'access files overview' diff --git a/web/profiles/custom/yalesites_profile/modules/custom/ys_core/src/Plugin/Validation/Constraint/MinMaxConstraint.php b/web/profiles/custom/yalesites_profile/modules/custom/ys_core/src/Plugin/Validation/Constraint/MinMaxConstraint.php new file mode 100644 index 0000000000..e0d5fc9b34 --- /dev/null +++ b/web/profiles/custom/yalesites_profile/modules/custom/ys_core/src/Plugin/Validation/Constraint/MinMaxConstraint.php @@ -0,0 +1,24 @@ +type == 'links') { + $items = $value->getValue(); + } + else { + $items = $value->referencedEntities(); + } + + // Get item count for the field. + if (!empty($items)) { + $item_count = count($items); + + // Constraints with minimum but no maximum. + if (isset($constraint->min) && isset($constraint->max)) { + // Constraints with minimum and maximum. + if ($item_count < $constraint->min || $item_count > $constraint->max) { + $this->context->addViolation($constraint->outsideMinMax, [ + '@min' => $constraint->min, + '@max' => $constraint->max, + '@type' => $constraint->type, + '@count' => $item_count, + ]); + } + } + elseif (isset($constraint->min) && !isset($constraint->max)) { + if ($item_count < $constraint->min) { + $this->context->addViolation($constraint->belowMin, [ + '@min' => $constraint->min, + '@type' => $constraint->type, + '@count' => $item_count, + ]); + } + } + } + } + +} \ No newline at end of file diff --git a/web/profiles/custom/yalesites_profile/modules/custom/ys_core/ys_core.module b/web/profiles/custom/yalesites_profile/modules/custom/ys_core/ys_core.module index 6bc108e820..fe09679462 100644 --- a/web/profiles/custom/yalesites_profile/modules/custom/ys_core/ys_core.module +++ b/web/profiles/custom/yalesites_profile/modules/custom/ys_core/ys_core.module @@ -7,6 +7,7 @@ use Drupal\Core\Cache\Cache; use Drupal\Core\Entity\Entity\EntityFormDisplay; +use Drupal\Core\Entity\EntityTypeInterface; use Drupal\Core\Form\FormStateInterface; use Drupal\Core\Render\Element; use Drupal\Core\Routing\RouteMatchInterface; @@ -464,3 +465,43 @@ function ys_core_preprocess_page(&$variables) { $config = \Drupal::config('ys_core.header_settings'); \Drupal::service('renderer')->addCacheableDependency($variables, $config); } + +/** + * Implements hook_form_FORM_ID_alter(). + */ +function ys_core_form_block_content_quick_links_form_alter(&$form, FormStateInterface $form_state, $form_id) { + // Prevent validation on 'add more' ajax callback for links. + // Use custom constraints instead (see ys_core_entity_bundle_field_info_alter) + $form['field_links']['widget']['add_more']['#limit_validation_errors'] = []; +} + +/** + * Implements hook_entity_bundle_field_info_alter(). + */ +function ys_core_entity_bundle_field_info_alter(&$fields, EntityTypeInterface $entity_type, $bundle) { + if ($entity_type->id() == 'block_content') { + // Bundle in this case refers to the block type. + switch ($bundle) { + case 'tabs': + if (isset($fields['field_tabs'])) { + $fields['field_tabs']->addConstraint('MinMax', ['min' => 2, 'max' => 5, 'type' => 'tabs']); + } + break; + case 'quick_links': + if (isset($fields['field_links'])) { + $fields['field_links']->addConstraint('MinMax', ['min' => 3, 'max' => 9, 'type' => 'links']); + } + break; + case 'media_grid': + if (isset($fields['field_media_grid_items'])) { + $fields['field_media_grid_items']->addConstraint('MinMax', ['min' => 2, 'type' => 'media grid items']); + } + break; + case 'gallery': + if (isset($fields['field_gallery_items'])) { + $fields['field_gallery_items']->addConstraint('MinMax', ['min' => 2, 'type' => 'gallery items']); + } + break; + } + } +} \ No newline at end of file From 9fc692121f4e9cddaa0b4027d431adb5ab1c0d58 Mon Sep 17 00:00:00 2001 From: CI Bot Date: Mon, 18 Dec 2023 17:54:28 -0500 Subject: [PATCH 02/10] feat: yalb-959 add custom js to layout builder block form --- .../modules/custom/ys_core/js/block-form.js | 96 +++++++++++++++++++ .../custom/ys_core/ys_core.libraries.yml | 3 + .../modules/custom/ys_core/ys_core.module | 25 ++++- 3 files changed, 123 insertions(+), 1 deletion(-) create mode 100644 web/profiles/custom/yalesites_profile/modules/custom/ys_core/js/block-form.js diff --git a/web/profiles/custom/yalesites_profile/modules/custom/ys_core/js/block-form.js b/web/profiles/custom/yalesites_profile/modules/custom/ys_core/js/block-form.js new file mode 100644 index 0000000000..7accbf6e99 --- /dev/null +++ b/web/profiles/custom/yalesites_profile/modules/custom/ys_core/js/block-form.js @@ -0,0 +1,96 @@ +((Drupal) => { + Drupal.behaviors.ysCoreBlockForm = { + attach: () => { // eslint-disable-line + + function errorMessage(blockType, numberOfItems) { + let messageText = ''; + if (blockType.max > 0) { + messageText = `Number of ${blockType.type} must be between ${blockType.min} and ${blockType.max}. `; + } + else { + messageText = `Number of ${blockType.type} must be above ${blockType.min}. `; + } + return messageText + `Number of ${blockType.type} added: ${numberOfItems}.`; + } + + // Function to handle block types and check for maximum/minimum number of inputs. + function handleBlockTypes(blockType) { + // Get forms that start with layout-builder (the add and update forms) + const blockForm = document.querySelector('form[id^="layout-builder"]'); + if (blockForm) { + + // Get all of the input elements of the specified type. + const items = document.querySelectorAll(blockType.itemSelector); + const input = document.querySelector(blockType.inputSelector); + // Count the number of items present. + const numberOfItems = items.length; + // If we are below the min or above the max, trigger an error. + if (numberOfItems < blockType.min || (blockType.max > 0 && numberOfItems > blockType.max)) { + + let itemsContainMedia = true; + items.forEach((item) => { + console.log(item); + console.log(item.querySelector('.summary-content')); + console.log(item.querySelector('img')); + if (!item.querySelector('.summary-content') && !item.querySelector('img')) { + itemsContainMedia = false; + } + }); + + const errorString = !itemsContainMedia ? 'All items must contain media.' : errorMessage(blockType, numberOfItems); + + if (input) { + input.setCustomValidity(errorString); + blockForm.addEventListener("submit", (event) => { + input.reportValidity(); + event.preventDefault(); + }); + } + } + } + } + + // Define block type objects with input to check plus min and max. + const blockTypes = [ + { + // Tabs + itemSelector: '[data-drupal-selector^="edit-settings-block-form-field-tabs"].paragraph-top', + inputSelector: 'input[data-drupal-selector^="edit-settings-block-form-field-tabs"]', + min: 2, + max: 5, + type: 'tabs', + }, + { + // Quick links + itemSelector: '[data-drupal-selector^="edit-settings-block-form-field-links"].ui-autocomplete-input', + inputSelector: 'input[data-drupal-selector^="edit-settings-block-form-field-links"]', + min: 3, + max: 9, + type: 'links', + }, + { + // Media Grid + itemSelector: '[data-drupal-selector^="edit-settings-block-form-field-media-grid-items"].paragraph-top', + inputSelector: 'input[data-drupal-selector^="edit-settings-block-form-field-heading"]', + min: 2, + max: 0, + type: 'media grid items', + }, + { + // Gallery + itemSelector: '[data-drupal-selector^="edit-settings-block-form-field-links"].ui-autocomplete-input', + inputSelector: 'input[data-drupal-selector^="edit-settings-block-form-field-links"]', + min: 2, + max: 0, + type: 'gallery items', + } + ]; + + // Apply the function to each block type. + blockTypes.forEach((blockType) => { + handleBlockTypes(blockType); + }); + + }, + }; +})(Drupal); diff --git a/web/profiles/custom/yalesites_profile/modules/custom/ys_core/ys_core.libraries.yml b/web/profiles/custom/yalesites_profile/modules/custom/ys_core/ys_core.libraries.yml index 149c9fb7a2..c554cb65b8 100644 --- a/web/profiles/custom/yalesites_profile/modules/custom/ys_core/ys_core.libraries.yml +++ b/web/profiles/custom/yalesites_profile/modules/custom/ys_core/ys_core.libraries.yml @@ -16,3 +16,6 @@ header_footer_settings: css/header-footer-settings.css: {} js: js/header-footer-settings.js: {} +block_form: + js: + js/block-form.js: {} \ No newline at end of file diff --git a/web/profiles/custom/yalesites_profile/modules/custom/ys_core/ys_core.module b/web/profiles/custom/yalesites_profile/modules/custom/ys_core/ys_core.module index fe09679462..264644018e 100644 --- a/web/profiles/custom/yalesites_profile/modules/custom/ys_core/ys_core.module +++ b/web/profiles/custom/yalesites_profile/modules/custom/ys_core/ys_core.module @@ -119,8 +119,30 @@ function ys_core_form_alter(&$form, $form_state, $form_id) { '#markup' => "

" . t('@description', $replacements) . "

", ]; } + + $layout_builder_block_forms = [ + 'layout_builder_update_block', + 'layout_builder_add_block' + ]; + + if (in_array($form_id, $layout_builder_block_forms)) { + // Get the block type from the form. + $block = $form['settings']['block_form']['#block']; + $limited_block_types = [ + 'tabs', + 'quick_links', + 'media_grid', + 'gallery' + ]; + if (in_array($block->bundle(), $limited_block_types)) { + $form['#attached']['library'][] = 'ys_core/block_form'; + } + + + } } + /** * Implements hook_preprocess_block(). */ @@ -475,6 +497,7 @@ function ys_core_form_block_content_quick_links_form_alter(&$form, FormStateInte $form['field_links']['widget']['add_more']['#limit_validation_errors'] = []; } + /** * Implements hook_entity_bundle_field_info_alter(). */ @@ -504,4 +527,4 @@ function ys_core_entity_bundle_field_info_alter(&$fields, EntityTypeInterface $e break; } } -} \ No newline at end of file +} From 996a2317d5b6249da3c7c82d4f831e6de2f43e66 Mon Sep 17 00:00:00 2001 From: CI Bot Date: Tue, 2 Jan 2024 12:51:46 -0500 Subject: [PATCH 03/10] feat: yalb-959 remove constraints and add client-side validation for both forms --- .../custom/yalesites_profile/composer.json | 6 +- .../modules/custom/ys_core/js/block-form.js | 116 ++++++++++-------- .../Constraint/MinMaxConstraint.php | 24 ---- .../Constraint/MinMaxConstraintValidator.php | 54 -------- .../modules/custom/ys_core/ys_core.module | 55 ++------- 5 files changed, 82 insertions(+), 173 deletions(-) delete mode 100644 web/profiles/custom/yalesites_profile/modules/custom/ys_core/src/Plugin/Validation/Constraint/MinMaxConstraint.php delete mode 100644 web/profiles/custom/yalesites_profile/modules/custom/ys_core/src/Plugin/Validation/Constraint/MinMaxConstraintValidator.php diff --git a/web/profiles/custom/yalesites_profile/composer.json b/web/profiles/custom/yalesites_profile/composer.json index 74196fb190..b9e931a43e 100644 --- a/web/profiles/custom/yalesites_profile/composer.json +++ b/web/profiles/custom/yalesites_profile/composer.json @@ -114,7 +114,8 @@ "hide remove button": "https://www.drupal.org/files/issues/2020-05-13/hide_field_required_paragraphs_remove_button_1.patch" }, "drupal/core": { - "plural results summary https://www.drupal.org/project/drupal/issues/2888320": "https://www.drupal.org/files/issues/2021-12-15/2888320-78.patch" + "plural results summary https://www.drupal.org/project/drupal/issues/2888320": "https://www.drupal.org/files/issues/2021-12-15/2888320-78.patch", + "Prevent empty block_content info fields from causing php deprecation notices": "https://www.drupal.org/files/issues/2023-07-21/3340159-empty-block-label_0.patch" }, "drupal/entity_redirect": { "fix layout route https://www.drupal.org/project/entity_redirect/issues/3352265": "https://git.drupalcode.org/project/entity_redirect/-/merge_requests/6.patch" @@ -129,6 +130,9 @@ "fix description toggle for CKEditor fields https://www.drupal.org/project/gin/issues/3316265": "https://git.drupalcode.org/project/gin/-/merge_requests/227.patch", "update dark mode localstorage https://www.drupal.org/project/gin/issues/3387653": "https://git.drupalcode.org/project/gin/-/merge_requests/304.diff" }, + "drupal/gin_lb": { + "fix multivalued fields throw an error": "https://www.drupal.org/files/issues/2024-01-02/gin_lb-fix-attribute-3387157-5.patch" + }, "drupal/media_library_form_element": { "Order Media items https://www.drupal.org/project/media_library_form_element/issues/3168027":"https://www.drupal.org/files/issues/2022-01-22/order_media_items-3168027-8.patch", "Deprecated function: explode(): Passing null to parameter #2 ($string) of type string is deprecated https://www.drupal.org/project/media_library_form_element/issues/3277273": "https://www.drupal.org/files/issues/2023-01-25/deprecated-explode-3277273-11.patch" diff --git a/web/profiles/custom/yalesites_profile/modules/custom/ys_core/js/block-form.js b/web/profiles/custom/yalesites_profile/modules/custom/ys_core/js/block-form.js index 7accbf6e99..3c3a43d278 100644 --- a/web/profiles/custom/yalesites_profile/modules/custom/ys_core/js/block-form.js +++ b/web/profiles/custom/yalesites_profile/modules/custom/ys_core/js/block-form.js @@ -2,51 +2,59 @@ Drupal.behaviors.ysCoreBlockForm = { attach: () => { // eslint-disable-line - function errorMessage(blockType, numberOfItems) { - let messageText = ''; - if (blockType.max > 0) { - messageText = `Number of ${blockType.type} must be between ${blockType.min} and ${blockType.max}. `; - } - else { - messageText = `Number of ${blockType.type} must be above ${blockType.min}. `; - } - return messageText + `Number of ${blockType.type} added: ${numberOfItems}.`; - } + /* + * Function to validate that all media items contain media. + * + * @param object blockType + * blockType object. + * + * @return string + * The error message or empty string. + * */ + function getErrors(blockType) { + // Get all items of the specified type. + const items = document.querySelectorAll(blockType.itemSelector); - // Function to handle block types and check for maximum/minimum number of inputs. - function handleBlockTypes(blockType) { - // Get forms that start with layout-builder (the add and update forms) - const blockForm = document.querySelector('form[id^="layout-builder"]'); - if (blockForm) { - - // Get all of the input elements of the specified type. - const items = document.querySelectorAll(blockType.itemSelector); - const input = document.querySelector(blockType.inputSelector); - // Count the number of items present. - const numberOfItems = items.length; - // If we are below the min or above the max, trigger an error. - if (numberOfItems < blockType.min || (blockType.max > 0 && numberOfItems > blockType.max)) { + // Count the number of items present. + const numberOfItems = items.length; + if (numberOfItems < blockType.min || (blockType.max > 0 && numberOfItems > blockType.max)) { + let messageText = ''; + if (blockType.max > 0) { + messageText = `Number of ${blockType.type} must be between ${blockType.min} and ${blockType.max}. `; + } else { + messageText = `Number of ${blockType.type} must be ${blockType.min} or more. `; + } + return messageText + `Number of ${blockType.type} added: ${numberOfItems}.`; + } - let itemsContainMedia = true; - items.forEach((item) => { - console.log(item); - console.log(item.querySelector('.summary-content')); - console.log(item.querySelector('img')); - if (!item.querySelector('.summary-content') && !item.querySelector('img')) { - itemsContainMedia = false; - } - }); + // An empty string signifies no errors and resets validation for the input. + return ''; + } - const errorString = !itemsContainMedia ? 'All items must contain media.' : errorMessage(blockType, numberOfItems); + /* + * Function to validate a block type. + * + * @param object blocktype + * Object containing block type information for validation. + * + * @return void + * */ + function validateBlockType(blockType) { + // Get the layout builder add and update forms. + const blockContentForm = document.querySelector('form[id^="block-content"]'); + // Inputs and submit selectors are different on block content and layout builder forms. + const inputSelector = blockContentForm ? blockType.inputSelector : blockType.lbInputSelector; + const submitSelector = blockContentForm ? 'edit-submit' : 'edit-actions-submit'; + const submitButton = document.querySelector(`input[data-drupal-selector=${submitSelector}]`); - if (input) { - input.setCustomValidity(errorString); - blockForm.addEventListener("submit", (event) => { - input.reportValidity(); - event.preventDefault(); - }); - } - } + if (submitButton) { + // On click, check for custom errors. + submitButton.addEventListener("click", () => { + const input = document.querySelector(inputSelector); + const errorMsg = getErrors(blockType); + // If there are any errors, set custom validity on the chosen input field. + input.setCustomValidity(errorMsg); + }); } } @@ -54,32 +62,36 @@ const blockTypes = [ { // Tabs - itemSelector: '[data-drupal-selector^="edit-settings-block-form-field-tabs"].paragraph-top', - inputSelector: 'input[data-drupal-selector^="edit-settings-block-form-field-tabs"]', + itemSelector: 'tr.paragraph-type--tab', + inputSelector: 'input[data-drupal-selector^="edit-field-tabs"]', + lbInputSelector: 'input[data-drupal-selector^="edit-settings-block-form-field-tabs"]', min: 2, max: 5, type: 'tabs', }, { // Quick links - itemSelector: '[data-drupal-selector^="edit-settings-block-form-field-links"].ui-autocomplete-input', - inputSelector: 'input[data-drupal-selector^="edit-settings-block-form-field-links"]', + itemSelector: 'input.ui-autocomplete-input', + inputSelector: 'input[data-drupal-selector^="edit-field-heading"]', + lbInputSelector: 'input[data-drupal-selector^="edit-settings-block-form-field-links"]', min: 3, max: 9, type: 'links', }, { // Media Grid - itemSelector: '[data-drupal-selector^="edit-settings-block-form-field-media-grid-items"].paragraph-top', - inputSelector: 'input[data-drupal-selector^="edit-settings-block-form-field-heading"]', + itemSelector: '.paragraph-type--media-grid-item', + inputSelector: 'input[data-drupal-selector^="edit-field-heading"]', + lbInputSelector: 'input[data-drupal-selector^="edit-settings-block-form-field-heading"]', min: 2, max: 0, type: 'media grid items', }, { // Gallery - itemSelector: '[data-drupal-selector^="edit-settings-block-form-field-links"].ui-autocomplete-input', - inputSelector: 'input[data-drupal-selector^="edit-settings-block-form-field-links"]', + itemSelector: '.paragraph-type--gallery-item', + inputSelector: 'input[data-drupal-selector^="edit-field-heading"]', + lbInputSelector: 'input[data-drupal-selector^="edit-settings-block-form-field-heading"]', min: 2, max: 0, type: 'gallery items', @@ -88,9 +100,11 @@ // Apply the function to each block type. blockTypes.forEach((blockType) => { - handleBlockTypes(blockType); + // Check the form for this block type. + if (document.querySelector(blockType.itemSelector)) { + validateBlockType(blockType); + } }); - }, }; })(Drupal); diff --git a/web/profiles/custom/yalesites_profile/modules/custom/ys_core/src/Plugin/Validation/Constraint/MinMaxConstraint.php b/web/profiles/custom/yalesites_profile/modules/custom/ys_core/src/Plugin/Validation/Constraint/MinMaxConstraint.php deleted file mode 100644 index e0d5fc9b34..0000000000 --- a/web/profiles/custom/yalesites_profile/modules/custom/ys_core/src/Plugin/Validation/Constraint/MinMaxConstraint.php +++ /dev/null @@ -1,24 +0,0 @@ -type == 'links') { - $items = $value->getValue(); - } - else { - $items = $value->referencedEntities(); - } - - // Get item count for the field. - if (!empty($items)) { - $item_count = count($items); - - // Constraints with minimum but no maximum. - if (isset($constraint->min) && isset($constraint->max)) { - // Constraints with minimum and maximum. - if ($item_count < $constraint->min || $item_count > $constraint->max) { - $this->context->addViolation($constraint->outsideMinMax, [ - '@min' => $constraint->min, - '@max' => $constraint->max, - '@type' => $constraint->type, - '@count' => $item_count, - ]); - } - } - elseif (isset($constraint->min) && !isset($constraint->max)) { - if ($item_count < $constraint->min) { - $this->context->addViolation($constraint->belowMin, [ - '@min' => $constraint->min, - '@type' => $constraint->type, - '@count' => $item_count, - ]); - } - } - } - } - -} \ No newline at end of file diff --git a/web/profiles/custom/yalesites_profile/modules/custom/ys_core/ys_core.module b/web/profiles/custom/yalesites_profile/modules/custom/ys_core/ys_core.module index 264644018e..4a04e85b7b 100644 --- a/web/profiles/custom/yalesites_profile/modules/custom/ys_core/ys_core.module +++ b/web/profiles/custom/yalesites_profile/modules/custom/ys_core/ys_core.module @@ -120,9 +120,20 @@ function ys_core_form_alter(&$form, $form_state, $form_id) { ]; } + $block_content_forms = [ + 'block_content_tabs_form', + 'block_content_quick_links_form', + 'block_content_media_grid_form', + 'block_content_gallery_form', + ]; + + if (in_array($form_id, $block_content_forms)) { + $form['#attached']['library'][] = 'ys_core/block_form'; + } + $layout_builder_block_forms = [ 'layout_builder_update_block', - 'layout_builder_add_block' + 'layout_builder_add_block', ]; if (in_array($form_id, $layout_builder_block_forms)) { @@ -138,7 +149,6 @@ function ys_core_form_alter(&$form, $form_state, $form_id) { $form['#attached']['library'][] = 'ys_core/block_form'; } - } } @@ -487,44 +497,3 @@ function ys_core_preprocess_page(&$variables) { $config = \Drupal::config('ys_core.header_settings'); \Drupal::service('renderer')->addCacheableDependency($variables, $config); } - -/** - * Implements hook_form_FORM_ID_alter(). - */ -function ys_core_form_block_content_quick_links_form_alter(&$form, FormStateInterface $form_state, $form_id) { - // Prevent validation on 'add more' ajax callback for links. - // Use custom constraints instead (see ys_core_entity_bundle_field_info_alter) - $form['field_links']['widget']['add_more']['#limit_validation_errors'] = []; -} - - -/** - * Implements hook_entity_bundle_field_info_alter(). - */ -function ys_core_entity_bundle_field_info_alter(&$fields, EntityTypeInterface $entity_type, $bundle) { - if ($entity_type->id() == 'block_content') { - // Bundle in this case refers to the block type. - switch ($bundle) { - case 'tabs': - if (isset($fields['field_tabs'])) { - $fields['field_tabs']->addConstraint('MinMax', ['min' => 2, 'max' => 5, 'type' => 'tabs']); - } - break; - case 'quick_links': - if (isset($fields['field_links'])) { - $fields['field_links']->addConstraint('MinMax', ['min' => 3, 'max' => 9, 'type' => 'links']); - } - break; - case 'media_grid': - if (isset($fields['field_media_grid_items'])) { - $fields['field_media_grid_items']->addConstraint('MinMax', ['min' => 2, 'type' => 'media grid items']); - } - break; - case 'gallery': - if (isset($fields['field_gallery_items'])) { - $fields['field_gallery_items']->addConstraint('MinMax', ['min' => 2, 'type' => 'gallery items']); - } - break; - } - } -} From fc774b41048de04c522ab4189afbfba57b38112c Mon Sep 17 00:00:00 2001 From: CI Bot Date: Tue, 2 Jan 2024 12:58:09 -0500 Subject: [PATCH 04/10] feat: yalb-959 cleanup --- .../modules/custom/ys_core/ys_core.libraries.yml | 2 +- .../yalesites_profile/modules/custom/ys_core/ys_core.module | 2 -- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/web/profiles/custom/yalesites_profile/modules/custom/ys_core/ys_core.libraries.yml b/web/profiles/custom/yalesites_profile/modules/custom/ys_core/ys_core.libraries.yml index c554cb65b8..9b25dc607a 100644 --- a/web/profiles/custom/yalesites_profile/modules/custom/ys_core/ys_core.libraries.yml +++ b/web/profiles/custom/yalesites_profile/modules/custom/ys_core/ys_core.libraries.yml @@ -18,4 +18,4 @@ header_footer_settings: js/header-footer-settings.js: {} block_form: js: - js/block-form.js: {} \ No newline at end of file + js/block-form.js: {} diff --git a/web/profiles/custom/yalesites_profile/modules/custom/ys_core/ys_core.module b/web/profiles/custom/yalesites_profile/modules/custom/ys_core/ys_core.module index 4a04e85b7b..50a8c49374 100644 --- a/web/profiles/custom/yalesites_profile/modules/custom/ys_core/ys_core.module +++ b/web/profiles/custom/yalesites_profile/modules/custom/ys_core/ys_core.module @@ -7,7 +7,6 @@ use Drupal\Core\Cache\Cache; use Drupal\Core\Entity\Entity\EntityFormDisplay; -use Drupal\Core\Entity\EntityTypeInterface; use Drupal\Core\Form\FormStateInterface; use Drupal\Core\Render\Element; use Drupal\Core\Routing\RouteMatchInterface; @@ -152,7 +151,6 @@ function ys_core_form_alter(&$form, $form_state, $form_id) { } } - /** * Implements hook_preprocess_block(). */ From 9a00b32a0485e61a661eb396b38f398b4fc512c5 Mon Sep 17 00:00:00 2001 From: CI Bot Date: Tue, 2 Jan 2024 13:17:45 -0500 Subject: [PATCH 05/10] feat: yalb-959 add edit block content forms --- .../yalesites_profile/modules/custom/ys_core/ys_core.module | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/web/profiles/custom/yalesites_profile/modules/custom/ys_core/ys_core.module b/web/profiles/custom/yalesites_profile/modules/custom/ys_core/ys_core.module index 50a8c49374..48790aeb1c 100644 --- a/web/profiles/custom/yalesites_profile/modules/custom/ys_core/ys_core.module +++ b/web/profiles/custom/yalesites_profile/modules/custom/ys_core/ys_core.module @@ -124,6 +124,10 @@ function ys_core_form_alter(&$form, $form_state, $form_id) { 'block_content_quick_links_form', 'block_content_media_grid_form', 'block_content_gallery_form', + 'block_content_tabs_edit_form', + 'block_content_quick_links_edit_form', + 'block_content_media_grid_edit_form', + 'block_content_gallery_edit_form', ]; if (in_array($form_id, $block_content_forms)) { From 14a4a691b2f3b67510b576a65007d2ba4652fec8 Mon Sep 17 00:00:00 2001 From: CI Bot Date: Tue, 2 Jan 2024 13:59:56 -0500 Subject: [PATCH 06/10] feat: yalb-959 fix code-sniff violation --- .../yalesites_profile/modules/custom/ys_core/ys_core.module | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/profiles/custom/yalesites_profile/modules/custom/ys_core/ys_core.module b/web/profiles/custom/yalesites_profile/modules/custom/ys_core/ys_core.module index 48790aeb1c..78604ff0f1 100644 --- a/web/profiles/custom/yalesites_profile/modules/custom/ys_core/ys_core.module +++ b/web/profiles/custom/yalesites_profile/modules/custom/ys_core/ys_core.module @@ -146,7 +146,7 @@ function ys_core_form_alter(&$form, $form_state, $form_id) { 'tabs', 'quick_links', 'media_grid', - 'gallery' + 'gallery', ]; if (in_array($block->bundle(), $limited_block_types)) { $form['#attached']['library'][] = 'ys_core/block_form'; From d7406cc230a61a72db969551395f704d5b24598f Mon Sep 17 00:00:00 2001 From: nJim Date: Wed, 10 Jan 2024 10:24:36 -0500 Subject: [PATCH 07/10] fix(YALB-1686): allow rtf file uploads --- .../config/sync/field.field.media.document.field_media_file.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/profiles/custom/yalesites_profile/config/sync/field.field.media.document.field_media_file.yml b/web/profiles/custom/yalesites_profile/config/sync/field.field.media.document.field_media_file.yml index 8e3aa5a538..719f3a861e 100644 --- a/web/profiles/custom/yalesites_profile/config/sync/field.field.media.document.field_media_file.yml +++ b/web/profiles/custom/yalesites_profile/config/sync/field.field.media.document.field_media_file.yml @@ -21,7 +21,7 @@ settings: handler: 'default:file' handler_settings: { } file_directory: '[date:custom:Y]-[date:custom:m]' - file_extensions: 'txt doc docx pdf xls xlsx' + file_extensions: 'txt rtf doc docx pdf xls xlsx' max_filesize: '' description_field: false field_type: file From bdcbcd4e2d58739edb96469fff68de5671d5fd82 Mon Sep 17 00:00:00 2001 From: Vincent Massaro Date: Wed, 10 Jan 2024 12:12:14 -0500 Subject: [PATCH 08/10] feat: support updating existing release PRs --- .ci/github/create_release_pull_request | 42 ++++++++++++++------------ .github/workflows/release_pr.yml | 7 ++++- 2 files changed, 28 insertions(+), 21 deletions(-) diff --git a/.ci/github/create_release_pull_request b/.ci/github/create_release_pull_request index fff58ddec0..c99940dc0f 100755 --- a/.ci/github/create_release_pull_request +++ b/.ci/github/create_release_pull_request @@ -2,24 +2,26 @@ set -eo pipefail -# Create a pull request to merge develop into master. -pull_request_response=$(curl -s -H "Accept: application/vnd.github+json" \ - -H "Authorization: token $ACCESS_TOKEN" \ - -X POST -d '{"title": "Release", "head": "develop", "base": "master"}' \ - "https://api.github.com/repos/$REPO/pulls") - -# Check if the pull request creation was successful. -if [ -z "$(echo "$pull_request_response" | jq -r '.html_url')" ]; then - message=$(echo "$pull_request_response" | jq -r '.errors[].message') - echo "Failed to create pull request." - echo "Error: $message" - exit 1 -else - opened_pr_url=$(echo "$pull_request_response" | jq -r '.html_url') -fi +# If we aren't updating an existing pull request, create one to merge develop into master. +if [ -z "$PR_NUMBER" ]; then + pull_request_response=$(curl -s -H "Accept: application/vnd.github+json" \ + -H "Authorization: token $ACCESS_TOKEN" \ + -X POST -d '{"title": "Release", "head": "develop", "base": "master"}' \ + "https://api.github.com/repos/$REPO/pulls") + + # Check if the pull request creation was successful. + if [ -z "$(echo "$pull_request_response" | jq -r '.html_url')" ]; then + message=$(echo "$pull_request_response" | jq -r '.errors[].message') + echo "Failed to create pull request." + echo "Error: $message" + exit 1 + else + opened_pr_url=$(echo "$pull_request_response" | jq -r '.html_url') + fi -# Extract the pull request number from the response. -pr_number=$(echo "$pull_request_response" | jq -r '.number') + # Extract the pull request number from the response. + PR_NUMBER=$(echo "$pull_request_response" | jq -r '.number') +fi # Page through the /pulls/#/commits endpoint. page=1 @@ -28,7 +30,7 @@ per_page=100 while true; do # Get the list of commits from the pull request and extract commit SHAs. commits_response=$(curl --silent -H "Authorization: token $ACCESS_TOKEN" \ - "https://api.github.com/repos/$REPO/pulls/$pr_number/commits?per_page=$per_page&page=$page") + "https://api.github.com/repos/$REPO/pulls/$PR_NUMBER/commits?per_page=$per_page&page=$page") current_commit_shas=$(echo "$commits_response" | jq -r '.[].sha') @@ -76,7 +78,7 @@ fi # Output changes that are not in a pull request. if [[ -n "${changes_without_pr[*]}" ]]; then - description+="\n## Changes without a pull rqeuest:\n" + description+="\n## Changes without a pull request:\n" for sha in "${changes_without_pr[@]}"; do description+="- $sha\n" @@ -88,7 +90,7 @@ update_pull_request=$(curl -s -L \ -H "Accept: application/vnd.github+json" \ -H "Authorization: token $ACCESS_TOKEN" \ -X PATCH -d "{\"body\": \"$description\"}" \ - "https://api.github.com/repos/$REPO/pulls/$pr_number") + "https://api.github.com/repos/$REPO/pulls/$PR_NUMBER") if [[ -n "$pull_request_response" || -n "$update_pull_request" ]]; then echo "Pull request created: $opened_pr_url" diff --git a/.github/workflows/release_pr.yml b/.github/workflows/release_pr.yml index 1826849c47..4b7dd713c5 100644 --- a/.github/workflows/release_pr.yml +++ b/.github/workflows/release_pr.yml @@ -1,11 +1,16 @@ name: Create release pull request -on: workflow_dispatch +on: + pull_request: + branches: + - master + workflow_dispatch: jobs: create_pull_request: runs-on: ubuntu-latest env: ACCESS_TOKEN: ${{ secrets.YALESITES_BUILD_TOKEN }} REPO: ${{ github.repository }} + PR_NUMBER: ${{ github.event.number }} steps: - name: Checkout uses: actions/checkout@v4 From 65bd72278195f7d351e01660c6435419c1bc24af Mon Sep 17 00:00:00 2001 From: David Blankenship <128765777+dblanken-yale@users.noreply.github.com> Date: Thu, 11 Jan 2024 16:06:21 -0500 Subject: [PATCH 09/10] YALB-1663: User getting error page when clicking "edit layout and content" in top menu (#530) * fix(YALB-1663): deleting default section does not error There was a problem where a user deleted the default content section. Upon saving this, they were unable to go back in to modify the page in layout builder. This was due to an access check issue in section_library. This patch fixes this so that the user could edit the page in layout builder again. * feat(YALB-1663): allow "Add Section" after Title and Metadata layout_builder_lock was disallowing sections to be added after the Title and Metadata section of a Page, or the bottom content section of a Profile. Since we are now allowing them to remove the default content section, they will need a way to add a new section, which this provides. "Add Section" is now displayed below in these cases. --- web/profiles/custom/yalesites_profile/composer.json | 3 +++ .../sync/core.entity_view_display.node.page.default.yml | 1 - .../sync/core.entity_view_display.node.profile.default.yml | 5 +++-- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/web/profiles/custom/yalesites_profile/composer.json b/web/profiles/custom/yalesites_profile/composer.json index 627ad3c368..62e6e05d3b 100644 --- a/web/profiles/custom/yalesites_profile/composer.json +++ b/web/profiles/custom/yalesites_profile/composer.json @@ -166,6 +166,9 @@ }, "drupal/quick_node_clone": { "Fix cloning of inline blocks and paragraphs: https://www.drupal.org/project/quick_node_clone/issues/3100117": "https://www.drupal.org/files/issues/2023-04-25/quick-node-clone--inline-blocks--3100117-32.patch" + }, + "drupal/section_library": { + "Fix access check issues on add_to_template link: https://www.drupal.org/project/section_library/issues/3241715": "https://www.drupal.org/files/issues/2022-09-21/3241715-6.patch" } } } diff --git a/web/profiles/custom/yalesites_profile/config/sync/core.entity_view_display.node.page.default.yml b/web/profiles/custom/yalesites_profile/config/sync/core.entity_view_display.node.page.default.yml index e605ec6a92..7437b9422a 100644 --- a/web/profiles/custom/yalesites_profile/config/sync/core.entity_view_display.node.page.default.yml +++ b/web/profiles/custom/yalesites_profile/config/sync/core.entity_view_display.node.page.default.yml @@ -61,7 +61,6 @@ third_party_settings: 4: 4 5: 5 6: 6 - 7: 7 8: 8 - layout_id: layout_onecol diff --git a/web/profiles/custom/yalesites_profile/config/sync/core.entity_view_display.node.profile.default.yml b/web/profiles/custom/yalesites_profile/config/sync/core.entity_view_display.node.profile.default.yml index 4a170b0b0b..225f23f7e3 100644 --- a/web/profiles/custom/yalesites_profile/config/sync/core.entity_view_display.node.profile.default.yml +++ b/web/profiles/custom/yalesites_profile/config/sync/core.entity_view_display.node.profile.default.yml @@ -60,7 +60,6 @@ third_party_settings: 4: 4 5: 5 6: 6 - 7: 7 8: 8 - layout_id: ys_layout_two_column @@ -79,7 +78,9 @@ third_party_settings: context_mapping: { } weight: 0 additional: { } - third_party_settings: { } + third_party_settings: + layout_builder_lock: + lock: { } layout_builder_restrictions: allowed_block_categories: - 'Chaos Tools' From bbf67b19182e2f866a8cfd8da3e625cc32a7ae4f Mon Sep 17 00:00:00 2001 From: Vincent Massaro Date: Fri, 12 Jan 2024 11:29:13 -0500 Subject: [PATCH 10/10] feat: content on install to environments other than dev We need the ability to set the environment dynamically when calling this script, so change to using a variable and fall back to dev if it isn't set. --- scripts/shared/content-import.sh | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/scripts/shared/content-import.sh b/scripts/shared/content-import.sh index 7405485e03..68ceb15aee 100755 --- a/scripts/shared/content-import.sh +++ b/scripts/shared/content-import.sh @@ -22,10 +22,11 @@ if [ "$YALESITES_IS_LOCAL" == "1" ]; then homepage_nid=$(lando drush ev "$nid_command") lando drush cset system.site page.front "$homepage_nid" -y else - COMMAND=$(terminus connection:info "$SITE_MACHINE_NAME".dev --field=sftp_command) + [ -z "$env" ] && env="dev" + COMMAND=$(terminus connection:info "$SITE_MACHINE_NAME"."$env" --field=sftp_command) eval "$COMMAND:/files/ <<< 'put $STARTERKIT_FILE'" - terminus drush "$SITE_MACHINE_NAME".dev -- content:import ../../files/"$STARTERKIT_FILE" + terminus drush "$SITE_MACHINE_NAME"."$env" -- content:import ../../files/"$STARTERKIT_FILE" eval "$COMMAND:/files/ <<< 'rm $STARTERKIT_FILE'" - homepage_nid=$(echo "$nid_command" | terminus drush "$SITE_MACHINE_NAME".dev -- php-script - 2>/dev/null) - terminus drush "$SITE_MACHINE_NAME".dev -- cset system.site page.front "$homepage_nid" -y + homepage_nid=$(echo "$nid_command" | terminus drush "$SITE_MACHINE_NAME"."$env" -- php-script - 2>/dev/null) + terminus drush "$SITE_MACHINE_NAME"."$env" -- cset system.site page.front "$homepage_nid" -y fi