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 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 diff --git a/web/profiles/custom/yalesites_profile/composer.json b/web/profiles/custom/yalesites_profile/composer.json index 6ace4eda62..62e6e05d3b 100644 --- a/web/profiles/custom/yalesites_profile/composer.json +++ b/web/profiles/custom/yalesites_profile/composer.json @@ -128,7 +128,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" @@ -143,6 +144,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" @@ -162,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' 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/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 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/js/block-form.js b/web/profiles/custom/yalesites_profile/modules/custom/ys_core/js/block-form.js new file mode 100644 index 0000000000..3c3a43d278 --- /dev/null +++ b/web/profiles/custom/yalesites_profile/modules/custom/ys_core/js/block-form.js @@ -0,0 +1,110 @@ +((Drupal) => { + Drupal.behaviors.ysCoreBlockForm = { + attach: () => { // eslint-disable-line + + /* + * 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); + + // 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}.`; + } + + // An empty string signifies no errors and resets validation for the input. + return ''; + } + + /* + * 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 (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); + }); + } + } + + // Define block type objects with input to check plus min and max. + const blockTypes = [ + { + // 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: '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: '.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: '.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', + } + ]; + + // Apply the function to each block type. + blockTypes.forEach((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/ys_core.libraries.yml b/web/profiles/custom/yalesites_profile/modules/custom/ys_core/ys_core.libraries.yml index 149c9fb7a2..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 @@ -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: {} 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..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 @@ -118,6 +118,41 @@ function ys_core_form_alter(&$form, $form_state, $form_id) { '#markup' => "

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

", ]; } + + $block_content_forms = [ + 'block_content_tabs_form', + '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)) { + $form['#attached']['library'][] = 'ys_core/block_form'; + } + + $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'; + } + + } } /**