diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index f86afa25ae4bdc..461f3771fe1629 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -93,6 +93,7 @@ /packages/base-styles /packages/icons /packages/primitives +/packages/dataviews @oandregal # Utilities /packages/a11y diff --git a/.github/ISSUE_TEMPLATE/New_release.md b/.github/ISSUE_TEMPLATE/New_release.md index c07dcfa3c466c6..982b9a6dac263b 100644 --- a/.github/ISSUE_TEMPLATE/New_release.md +++ b/.github/ISSUE_TEMPLATE/New_release.md @@ -1,16 +1,17 @@ --- name: Gutenberg Release +title: "Gutenberg {VERSION_NUMBER} release checklist" about: A checklist for the Gutenberg plugin release process labels: Gutenberg Plugin, [Type] Project Management --- This issue is to provide visibility on the progress of the release process of Gutenberg VERSION_NUMBER and to centralize any conversations about it. The ultimate goal of this issue is to keep the reference of the steps, resources, work, and conversations about this release so it can be helpful for the next contributors releasing a new Gutenberg version. -- Gutenberg version to release: VERSION_NUMBER ([milestone](ADD_LINK)) +- Gutenberg version to release: {VERSION_NUMBER} ([milestone](https://github.com/WordPress/gutenberg/milestone/{MILESTONE_ID}?closed=1)) - Release Manager (a.k.a. Release Lead): -- Release Date VERSION_NUMBER RC: ADD DATE -- Release Date VERSION_NUMBER: ADD DATE -- Previous version change log (as a reference): [15.3](https://github.com/WordPress/gutenberg/releases/tag/v15.3.0) +- Release Date {VERSION_NUMBER} RC: {ADD DATE} +- Release Date {VERSION_NUMBER}: {ADD DATE} +- Previous version change log (as a reference): [{PREVIOUS_VERSION_NUMBER}](https://github.com/WordPress/gutenberg/releases/tag/{PREVIOUS_TAG_NAME}) ## Resources @@ -21,33 +22,34 @@ This issue is to provide visibility on the progress of the release process of Gu ## Checklist -### RC Day - {Weekday, Month, Date} +### RC Day - {WEEKDAY}, {MONTH}, {DATE} - [ ] Post a message in `#core-editor` channel to let folks know you are starting the RC release process -- [ ] Organize and Label PRs on the relevant milestone -- [ ] Start the release process by triggering the `rc` [workflow](https://developer.wordpress.org/block-editor/contributors/code/release/#running-workflow) -- [ ] [Update the created Draft Release accordingly](https://developer.wordpress.org/block-editor/contributors/code/release/#view-the-release-draft) -- [ ] [Curate the changelog](https://developer.wordpress.org/block-editor/contributors/code/release/#1-curating-the-changelog) before publishing -- [ ] Publish Release +- [ ] [Organize and Label PRs](https://developer.wordpress.org/block-editor/contributors/code/release/#organizing-and-labeling-milestone-prs) on the relevant milestone +- [ ] Start the release process by triggering the `rc` [workflow](https://developer.wordpress.org/block-editor/contributors/code/release/#running-the-release-workflow) +- [ ] [Update the created Draft Release accordingly](https://developer.wordpress.org/block-editor/contributors/code/release/#viewing-the-release-draft) +- [ ] [Curate the changelog](https://developer.wordpress.org/block-editor/contributors/code/release/#curating-the-release-changelog) before publishing +- [ ] [Publish Release](https://developer.wordpress.org/block-editor/contributors/code/release/#publishing-the-release) - [ ] Announce in `#core-editor` channel that RC1 has been released and is ready for testing - [ ] Ping any other relevant channels announcing that the RC is available - [ ] Create Draft of Release post on Make Core blog _(initial draft in [Google doc](https://docs.google.com/document/d/1D-MTOCmL9eMlP9TDTXqlzuKVOg_ghCPm9_whHFViqMk/edit))_ ### Between RC and Release -- [ ] Post a reminder in #core-editor for backporting PRs to RC (~Label Backport to Gutenberg RC) +- [ ] Post a reminder in `#core-editor` for backporting PRs to RC (~Label `Backport to Gutenberg RC`) - [ ] If there are any PRs marked as [Backport to RC](https://github.com/WordPress/gutenberg/pulls?q=is%3Apr+label%3A%22Backport+to+Gutenberg+RC%22+is%3Aclosed), run the [cherry-pick command to apply them](https://developer.wordpress.org/block-editor/contributors/code/release/auto-cherry-picking/#how-can-i-use-it-for-a-gutenberg-plugin-release) to the release branch. **This needs to be run locally** - [ ] [Draft Release Post Highlights and Change Log](https://docs.google.com/document/d/1D-MTOCmL9eMlP9TDTXqlzuKVOg_ghCPm9_whHFViqMk/edit) -- [ ] Get assets from [Design Team](https://make.wordpress.org/design/) for the post +- [ ] Get assets from [Design Team](https://make.wordpress.org/design/) for the post if required - [ ] Reach out to Highlight Authors to draft sections (if necessary) ### Release Day - {Weekday, Month, Date} - [ ] Post a message in `#core-editor` channel to let folks know you are starting the release process -- [ ] Start the release process by triggering the `stable` [workflow](https://developer.wordpress.org/block-editor/contributors/code/release/#running-workflow) +- [ ] Start the release process by triggering the `stable` [workflow](https://developer.wordpress.org/block-editor/contributors/code/release/#running-the-release-workflow) - [ ] Update the created Draft Release accordingly. Typically by copy/pasting the last RC release notes and add any changes/updates as needed. -- [ ] Publish Release +- [ ] [Publish Release](https://developer.wordpress.org/block-editor/contributors/code/release/#publishing-the-release) - [ ] Trigger the update to the plugin directory. _(Get approval from a member of [Gutenberg Release team](https://github.com/orgs/WordPress/teams/gutenberg-release/members) if necessary)_ - [ ] Announce in `#core-editor` channel that the plugin has been released - [ ] Reach out to other contributors to help get the post reviewed -- [ ] Publish Release post on Make Core blog +- [ ] [Publish Release post](https://developer.wordpress.org/block-editor/contributors/code/release/#publishing-the-release-post) on Make Core blog +- [ ] Post in the `#core-editor` channel [requesting a volunteer for the next Gutenberg release](https://developer.wordpress.org/block-editor/contributors/code/release/#call-for-volunteer-for-the-next-release). diff --git a/.github/actionlint.yml b/.github/actionlint.yml new file mode 100644 index 00000000000000..63f4c91333ac8d --- /dev/null +++ b/.github/actionlint.yml @@ -0,0 +1,13 @@ +# This is the configuration file for actionlint, a static checker for GitHub Actions workflow files. +# See https://github.com/rhysd/actionlint. + +# Path-specific configurations. +paths: + .github/workflows/**/*.{yml,yaml}: + ignore: + # [SC2129](https://www.shellcheck.net/wiki/SC2129) is ignored because it is a stylistic issue. + - 'shellcheck reported issue in this script: SC2129:.+' + .github/workflows/end2end-test.yml: + ignore: + # This file gets created in the step prior. + - 'file "build/index.js" does not exist.+' diff --git a/.github/setup-node/action.yml b/.github/setup-node/action.yml index 4313639e78c062..ad6552cc91e96d 100644 --- a/.github/setup-node/action.yml +++ b/.github/setup-node/action.yml @@ -10,7 +10,7 @@ runs: using: 'composite' steps: - name: Use desired version of Node.js - uses: actions/setup-node@cdca7365b2dadb8aad0a33bc7601856ffabcc48e # v4.3.0 + uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0 with: node-version-file: '.nvmrc' node-version: ${{ inputs.node-version }} @@ -20,12 +20,12 @@ runs: - name: Get Node.js and npm version id: node-version run: | - echo "NODE_VERSION=$(node -v)" >> $GITHUB_OUTPUT + echo "NODE_VERSION=$(node -v)" >> "$GITHUB_OUTPUT" shell: bash - name: Cache node_modules id: cache-node_modules - uses: actions/cache@d4323d4df104b026a6aa633fdb11d772146be0bf # v4.2.2 + uses: actions/cache@5a3ec84eff668545956fd18022155c47e93e2684 # v4.2.3 with: path: '**/node_modules' key: node_modules-${{ runner.os }}-${{ runner.arch }}-${{ steps.node-version.outputs.NODE_VERSION }}-${{ hashFiles('package-lock.json') }} @@ -36,7 +36,7 @@ runs: npm ci shell: bash - name: Upload npm logs as an artifact on failure - uses: actions/upload-artifact@4cec3d8aa04e39d1a68397de0c4cd6fb9dce8ec1 # v4.6.1 + uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 if: failure() with: name: npm-logs diff --git a/.github/workflows/build-plugin-zip.yml b/.github/workflows/build-plugin-zip.yml index 95e641accd18d7..4108e0c9b96495 100644 --- a/.github/workflows/build-plugin-zip.yml +++ b/.github/workflows/build-plugin-zip.yml @@ -20,10 +20,16 @@ concurrency: group: ${{ github.workflow }}-${{ github.event_name == 'pull_request' && github.head_ref || github.sha }} cancel-in-progress: true +# Disable permissions for all available scopes by default. +# Any needed permissions should be configured at the job level. +permissions: {} + jobs: compute-stable-branches: name: Compute current and next stable release branches runs-on: ubuntu-latest + permissions: + contents: read if: ${{ github.event_name == 'workflow_dispatch' }} outputs: current_stable_branch: ${{ steps.get_branches.outputs.current_stable_branch }} @@ -36,19 +42,22 @@ jobs: curl \ -H "Accept: application/vnd.github.v3+json" \ -o latest.json \ - "https://api.github.com/repos/${{ github.repository }}/releases/latest" - LATEST_STABLE_TAG=$(jq --raw-output '.tag_name' latest.json) - IFS='.' read LATEST_STABLE_MAJOR LATEST_STABLE_MINOR LATEST_STABLE_PATCH <<< "${LATEST_STABLE_TAG#v}" - echo "current_stable_branch=release/${LATEST_STABLE_MAJOR}.${LATEST_STABLE_MINOR}" >> $GITHUB_OUTPUT - if [[ ${LATEST_STABLE_MINOR} == "9" ]]; then - echo "next_stable_branch=release/$((LATEST_STABLE_MAJOR + 1)).0" >> $GITHUB_OUTPUT + "https://api.github.com/repos/${GITHUB_REPOSITORY}/releases/latest" + LATEST_STABLE_TAG="$(jq --raw-output '.tag_name' latest.json)" + # shellcheck disable=SC2034 + IFS='.' read -r LATEST_STABLE_MAJOR LATEST_STABLE_MINOR LATEST_STABLE_PATCH <<< "${LATEST_STABLE_TAG#v}" + echo "current_stable_branch=release/${LATEST_STABLE_MAJOR}.${LATEST_STABLE_MINOR}" >> "$GITHUB_OUTPUT" + if [[ "${LATEST_STABLE_MINOR}" == "9" ]]; then + echo "next_stable_branch=release/$((LATEST_STABLE_MAJOR + 1)).0" >> "$GITHUB_OUTPUT" else - echo "next_stable_branch=release/${LATEST_STABLE_MAJOR}.$((LATEST_STABLE_MINOR + 1))" >> $GITHUB_OUTPUT + echo "next_stable_branch=release/${LATEST_STABLE_MAJOR}.$((LATEST_STABLE_MINOR + 1))" >> "$GITHUB_OUTPUT" fi bump-version: name: Bump version runs-on: ubuntu-latest + permissions: + contents: write needs: compute-stable-branches if: | github.event_name == 'workflow_dispatch' && ( @@ -76,31 +85,34 @@ jobs: with: token: ${{ secrets.GUTENBERG_TOKEN }} show-progress: ${{ runner.debug == '1' && 'true' || 'false' }} + persist-credentials: true - name: Compute old and new version id: get_version + env: + VERSION: ${{ github.event.inputs.version }} run: | - OLD_VERSION=$(jq --raw-output '.version' package.json) - echo "old_version=${OLD_VERSION}" >> $GITHUB_OUTPUT - if [[ ${{ github.event.inputs.version }} == 'stable' ]]; then - NEW_VERSION=$(npx semver $OLD_VERSION -i patch) + OLD_VERSION="$(jq --raw-output '.version' package.json)" + echo "old_version=${OLD_VERSION}" >> "$GITHUB_OUTPUT" + if [[ "$VERSION" == 'stable' ]]; then + NEW_VERSION="$(npx semver "$OLD_VERSION" -i patch)" else - if [[ $OLD_VERSION == *"rc"* ]]; then - NEW_VERSION=$(npx semver $OLD_VERSION -i prerelease) + if [[ "$OLD_VERSION" == *"rc"* ]]; then + NEW_VERSION="$(npx semver "$OLD_VERSION" -i prerelease)" else # WordPress version guidelines: If minor is 9, bump major instead. IFS='.' read -r -a OLD_VERSION_ARRAY <<< "$OLD_VERSION" if [[ ${OLD_VERSION_ARRAY[1]} == "9" ]]; then - NEW_VERSION="$(npx semver $OLD_VERSION -i major)-rc.1" + NEW_VERSION="$(npx semver "$OLD_VERSION" -i major)-rc.1" else - NEW_VERSION="$(npx semver $OLD_VERSION -i minor)-rc.1" + NEW_VERSION="$(npx semver "$OLD_VERSION" -i minor)-rc.1" fi fi fi - echo "new_version=${NEW_VERSION}" >> $GITHUB_OUTPUT + echo "new_version=${NEW_VERSION}" >> "$GITHUB_OUTPUT" IFS='.' read -r -a NEW_VERSION_ARRAY <<< "$NEW_VERSION" RELEASE_BRANCH="release/${NEW_VERSION_ARRAY[0]}.${NEW_VERSION_ARRAY[1]}" - echo "release_branch=${RELEASE_BRANCH}" >> $GITHUB_OUTPUT + echo "release_branch=${RELEASE_BRANCH}" >> "$GITHUB_OUTPUT" - name: Configure git user name and email run: | @@ -111,31 +123,41 @@ jobs: if: | github.event.inputs.version == 'rc' && ! contains( steps.get_version.outputs.old_version, 'rc' ) - run: git checkout -b "${{ steps.get_version.outputs.release_branch }}" + env: + TARGET_BRANCH: ${{ steps.get_version.outputs.release_branch }} + run: git checkout -b "$TARGET_BRANCH" - name: Switch to release branch if: | github.event.inputs.version == 'stable' || contains( steps.get_version.outputs.old_version, 'rc' ) + env: + TARGET_BRANCH: ${{ steps.get_version.outputs.release_branch }} run: | - git fetch --depth=1 origin "${{ steps.get_version.outputs.release_branch }}" - git checkout "${{ steps.get_version.outputs.release_branch }}" + git fetch --depth=1 origin "$TARGET_BRANCH" + git checkout "$TARGET_BRANCH" - name: Update plugin version env: VERSION: ${{ steps.get_version.outputs.new_version }} + OLD_VERSION: ${{ steps.get_version.outputs.old_version }} run: | - cat <<< $(jq --tab --arg version "${VERSION}" '.version = $version' package.json) > package.json - cat <<< $(jq --tab --arg version "${VERSION}" '.version = $version | .packages[""].version = $version' package-lock.json) > package-lock.json - sed -i "s/${{ steps.get_version.outputs.old_version }}/${VERSION}/g" gutenberg.php + jq --tab --arg version "${VERSION}" '.version = $version' package.json > package.json.tmp + mv package.json.tmp package.json + jq --tab --arg version "${VERSION}" '.version = $version | .packages[""].version = $version' package-lock.json > package-lock.json.tmp + mv package-lock.json.tmp package-lock.json + sed -i "s/${OLD_VERSION}/${VERSION}/g" gutenberg.php - name: Commit the version bump to the release branch id: commit_version_bump_to_release_branch + env: + TARGET_BRANCH: ${{ steps.get_version.outputs.release_branch }} + VERSION: ${{ steps.get_version.outputs.new_version }} run: | git add gutenberg.php package.json package-lock.json - git commit -m "Bump plugin version to ${{ steps.get_version.outputs.new_version }}" - git push --set-upstream origin "${{ steps.get_version.outputs.release_branch }}" - echo "version_bump_commit=$(git rev-parse --verify --short HEAD)" >> $GITHUB_OUTPUT + git commit -m "Bump plugin version to ${VERSION}" + git push --set-upstream origin "$TARGET_BRANCH" + echo "version_bump_commit=$(git rev-parse --verify --short HEAD)" >> "$GITHUB_OUTPUT" - name: Fetch trunk if: ${{ github.ref != 'refs/heads/trunk' }} @@ -143,19 +165,24 @@ jobs: - name: Cherry-pick the version bump commit to trunk id: commit_version_bump_to_trunk + env: + TARGET_BRANCH: ${{ steps.get_version.outputs.release_branch }} + OLD_VERSION: ${{ steps.get_version.outputs.old_version }} run: | git checkout trunk git pull - TRUNK_VERSION=$(jq --raw-output '.version' package.json) - if [[ ${{ steps.get_version.outputs.old_version }} == "$TRUNK_VERSION" ]]; then - git cherry-pick "${{ steps.get_version.outputs.release_branch }}" + TRUNK_VERSION="$(jq --raw-output '.version' package.json)" + if [[ "$OLD_VERSION" == "$TRUNK_VERSION" ]]; then + git cherry-pick "$TARGET_BRANCH" git push - echo "version_bump_commit=$(git rev-parse --verify --short HEAD)" >> $GITHUB_OUTPUT + echo "version_bump_commit=$(git rev-parse --verify --short HEAD)" >> "$GITHUB_OUTPUT" fi build: name: Build Release Artifact runs-on: ubuntu-latest + permissions: + contents: read needs: bump-version if: | always() && ( @@ -172,13 +199,13 @@ jobs: with: ref: ${{ needs.bump-version.outputs.release_branch || github.ref }} show-progress: ${{ runner.debug == '1' && 'true' || 'false' }} + persist-credentials: false - name: Use desired version of Node.js - uses: actions/setup-node@cdca7365b2dadb8aad0a33bc7601856ffabcc48e # v4.3.0 + uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0 with: node-version-file: '.nvmrc' check-latest: true - cache: npm - name: Build Gutenberg plugin ZIP file run: ./bin/build-plugin-zip.sh @@ -200,7 +227,7 @@ jobs: MILESTONE="Gutenberg ${VERSION_ARRAY[0]}.${VERSION_ARRAY[1]}" npm run other:changelog -- --milestone="$MILESTONE" --unreleased > release-notes.txt sed -ie '1,6d' release-notes.txt - if [[ ${{ needs.bump-version.outputs.new_version }} != *"rc"* ]]; then + if [[ "${VERSION}" != *"rc"* ]]; then # Include previous RCs' release notes, if any CHANGELOG_REGEX="=\s[0-9]+\.[0-9]+\.[0-9]+(-rc\.[0-9]+)?\s=" RC_REGEX="=\s${VERSION}(-rc\.[0-9]+)?\s=" @@ -218,6 +245,8 @@ jobs: name: Revert version bump if build failed needs: [bump-version, build] runs-on: ubuntu-latest + permissions: + contents: write if: | always() && ( needs.build.outputs.job_status == 'failure' ) && @@ -231,6 +260,7 @@ jobs: ref: ${{ needs.bump-version.outputs.release_branch }} token: ${{ secrets.GUTENBERG_TOKEN }} show-progress: ${{ runner.debug == '1' && 'true' || 'false' }} + persist-credentials: true - name: Configure git user name and email run: | @@ -241,44 +271,53 @@ jobs: if: | github.event.inputs.version == 'stable' || contains( needs.bump-version.outputs.old_version, 'rc' ) + env: + RELEAD_BRANCH_COMMIT: ${{ needs.bump-version.outputs.release_branch_commit }} + RELEASE_BRANCH: ${{ needs.bump-version.outputs.release_branch }} run: | - git revert --no-edit ${{ needs.bump-version.outputs.release_branch_commit }} - git push --set-upstream origin "${{ needs.bump-version.outputs.release_branch }}" + git revert --no-edit "$RELEAD_BRANCH_COMMIT" + git push --set-upstream origin "$RELEASE_BRANCH" - name: Delete release branch if it was only just created for the RC if: | github.event.inputs.version == 'rc' && ! contains( needs.bump-version.outputs.old_version, 'rc' ) + env: + RELEASE_BRANCH: ${{ needs.bump-version.outputs.release_branch }} run: | - git push origin :"${{ needs.bump-version.outputs.release_branch }}" + git push origin :"$RELEASE_BRANCH" - name: Revert version bump on trunk if: ${{ needs.bump-version.outputs.trunk_commit }} + env: + TRUNK_COMMIT: ${{ needs.bump-version.outputs.trunk_commit }} run: | git fetch --depth=2 origin trunk git checkout trunk - git revert --no-edit ${{ needs.bump-version.outputs.trunk_commit }} + git revert --no-edit "$TRUNK_COMMIT" git push --set-upstream origin trunk create-release: name: Create Release Draft and Attach Asset needs: [bump-version, build] runs-on: ubuntu-latest + permissions: + contents: write steps: - name: Set Release Version id: get_release_version env: VERSION: ${{ needs.bump-version.outputs.new_version }} - run: echo "version=$(echo $VERSION | cut -d / -f 3 | sed 's/-rc./ RC/' )" >> $GITHUB_OUTPUT + run: echo "version=$(echo "$VERSION" | cut -d / -f 3 | sed 's/-rc./ RC/' )" >> "$GITHUB_OUTPUT" - name: Download Plugin Zip Artifact - uses: actions/download-artifact@cc203385981b70ca67e1cc392babf9cc229d5806 # v4.1.9 + uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0 with: name: gutenberg-plugin - name: Download Release Notes Artifact - uses: actions/download-artifact@cc203385981b70ca67e1cc392babf9cc229d5806 # v4.1.9 + uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0 with: name: release-notes @@ -309,6 +348,8 @@ jobs: npm-publish: name: Publish WordPress packages to npm runs-on: ubuntu-latest + permissions: + contents: read environment: WordPress packages needs: [bump-version, build] if: ${{ endsWith( needs.bump-version.outputs.new_version, '-rc.1' ) }} @@ -319,6 +360,7 @@ jobs: path: main ref: trunk show-progress: ${{ runner.debug == '1' && 'true' || 'false' }} + persist-credentials: false - name: Checkout (for publishing) uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 @@ -328,6 +370,7 @@ jobs: ref: trunk token: ${{ secrets.GUTENBERG_TOKEN }} show-progress: ${{ runner.debug == '1' && 'true' || 'false' }} + persist-credentials: true - name: Configure git user name and email (for publishing) run: | @@ -336,7 +379,7 @@ jobs: git config user.email gutenberg@wordpress.org - name: Setup Node.js - uses: actions/setup-node@cdca7365b2dadb8aad0a33bc7601856ffabcc48e # v4.3.0 + uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0 with: node-version-file: 'main/.nvmrc' registry-url: 'https://registry.npmjs.org' diff --git a/.github/workflows/bundle-size.yml b/.github/workflows/bundle-size.yml index 83b273b8fdca0c..063cf996529038 100644 --- a/.github/workflows/bundle-size.yml +++ b/.github/workflows/bundle-size.yml @@ -31,19 +31,27 @@ concurrency: group: ${{ github.workflow }}-${{ github.event_name == 'pull_request' && github.head_ref || github.sha }} cancel-in-progress: true +# Disable permissions for all available scopes by default. +# Any needed permissions should be configured at the job level. +permissions: {} + jobs: build: name: Check runs-on: ubuntu-latest + permissions: + contents: read + pull-requests: write steps: - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 with: fetch-depth: 1 show-progress: ${{ runner.debug == '1' && 'true' || 'false' }} + persist-credentials: false - name: Use desired version of Node.js - uses: actions/setup-node@cdca7365b2dadb8aad0a33bc7601856ffabcc48e # v4.3.0 + uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0 with: node-version-file: '.nvmrc' check-latest: true diff --git a/.github/workflows/check-backport-changelog.yml b/.github/workflows/check-backport-changelog.yml index 48fb56b425be39..85e36fb8e66713 100644 --- a/.github/workflows/check-backport-changelog.yml +++ b/.github/workflows/check-backport-changelog.yml @@ -15,17 +15,28 @@ on: - '!phpunit/blocks/**' - 'packages/**/*.php' - '!packages/block-library/**' + - '!packages/block-serialization-default-parser/**' + - '!packages/widgets/**' - '!packages/e2e-tests/**' + +# Disable permissions for all available scopes by default. +# Any needed permissions should be configured at the job level. +permissions: {} + jobs: check: name: Check for a Core backport changelog entry runs-on: ubuntu-latest + permissions: + contents: read if: ${{ !contains(github.event.pull_request.labels.*.name, 'No Core Sync Required') && !contains(github.event.pull_request.labels.*.name, 'Backport from WordPress Core') }} steps: - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 with: ref: ${{ github.event.pull_request.head.ref }} repository: ${{ github.event.pull_request.head.repo.full_name }} + persist-credentials: false + - name: Check the changelog folder env: PR_NUMBER: ${{ github.event.number }} @@ -44,7 +55,7 @@ jobs: exit 1 fi - core_pr_number=$(basename "${changelog_file}" .md) + core_pr_number="$(basename "${changelog_file}" .md)" core_pr_url="https://github\.com/WordPress/wordpress-develop/pull/${core_pr_number}" # Confirm that the entry has the correct core backport PR URL. diff --git a/.github/workflows/check-components-changelog.yml b/.github/workflows/check-components-changelog.yml index 373a782d5d6ddf..7e51425c0be5f6 100644 --- a/.github/workflows/check-components-changelog.yml +++ b/.github/workflows/check-components-changelog.yml @@ -14,13 +14,22 @@ on: - '!packages/components/src/**/*.native.js' - '!packages/components/src/**/*.native.scss' - '!packages/components/src/**/react-native-*' + +# Disable permissions for all available scopes by default. +# Any needed permissions should be configured at the job level. +permissions: {} + jobs: check: name: Check CHANGELOG diff runs-on: ubuntu-latest + permissions: + contents: read steps: - name: 'Get PR commit count' - run: echo "PR_COMMIT_COUNT=$(( ${{ github.event.pull_request.commits }} + 1 ))" >> "${GITHUB_ENV}" + env: + PR_COUNT: ${{ github.event.pull_request.commits }} + run: echo "PR_COMMIT_COUNT=$(( PR_COUNT + 1 ))" >> "${GITHUB_ENV}" - name: Checkout code uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 with: @@ -28,17 +37,19 @@ jobs: repository: ${{ github.event.pull_request.head.repo.full_name }} fetch-depth: ${{ env.PR_COMMIT_COUNT }} show-progress: ${{ runner.debug == '1' && 'true' || 'false' }} + persist-credentials: false - name: 'Fetch relevant history from origin' - run: git fetch origin ${{ github.event.pull_request.base.ref }} + run: git fetch origin "$GITHUB_BASE_REF" - name: Check CHANGELOG status env: PR_NUMBER: ${{ github.event.number }} + BASE_SHA: ${{ github.event.pull_request.base.sha }} run: | changelog_path="packages/components/CHANGELOG.md" optional_check_notice="This isn't a required check, so if you think your changes are small enough that they don't warrant a CHANGELOG entry, please go ahead and merge without one." # Fail if the PR doesn't touch the changelog - if git diff --quiet ${{ github.event.pull_request.base.sha }} HEAD -- "$changelog_path"; then + if git diff --quiet "$BASE_SHA" HEAD -- "$changelog_path"; then echo "Please add a CHANGELOG entry to $changelog_path" echo echo "${optional_check_notice}" diff --git a/.github/workflows/cherry-pick-wp-release.yml b/.github/workflows/cherry-pick-wp-release.yml index 14bee71c90c909..a703332b9edf68 100644 --- a/.github/workflows/cherry-pick-wp-release.yml +++ b/.github/workflows/cherry-pick-wp-release.yml @@ -16,9 +16,17 @@ concurrency: group: ${{ github.workflow }} cancel-in-progress: false +# Disable permissions for all available scopes by default. +# Any needed permissions should be configured at the job level. +permissions: {} + jobs: cherry-pick: runs-on: ubuntu-latest + permissions: + contents: write + issues: write + pull-requests: read # When in the context of a PR, ensure the PR is merged. if: github.event.pull_request == null || github.event.pull_request.merged == true steps: @@ -74,6 +82,7 @@ jobs: with: token: ${{ secrets.GUTENBERG_TOKEN }} fetch-depth: 0 + persist-credentials: false - name: Set up Git if: env.cherry_pick == 'true' @@ -85,20 +94,20 @@ jobs: id: cherry-pick if: env.cherry_pick == 'true' run: | - TARGET_BRANCH="wp/${{ env.version }}" - COMMIT_SHA="${{ env.commit_sha }}" + TARGET_BRANCH="wp/${version}" + COMMIT_SHA="${commit_sha}" echo "Target branch: $TARGET_BRANCH" echo "Commit SHA: $COMMIT_SHA" - git checkout $TARGET_BRANCH - git cherry-pick $COMMIT_SHA || echo "cherry-pick-failed" > result + git checkout "$TARGET_BRANCH" + git cherry-pick "$COMMIT_SHA" || echo "cherry-pick-failed" > result if [ -f result ] && grep -q "cherry-pick-failed" result; then - echo "conflict=true" >> $GITHUB_ENV + echo "conflict=true" >> "$GITHUB_ENV" git cherry-pick --abort else - CHERRY_PICK_SHA=$(git rev-parse HEAD) - echo "conflict=false" >> $GITHUB_ENV - echo "cherry_pick_sha=$CHERRY_PICK_SHA" >> $GITHUB_ENV - git push origin $TARGET_BRANCH + CHERRY_PICK_SHA="$(git rev-parse HEAD)" + echo "conflict=false" >> "$GITHUB_ENV" + echo "cherry_pick_sha=$CHERRY_PICK_SHA" >> "$GITHUB_ENV" + git push origin "$TARGET_BRANCH" fi - name: Remove cherry-pick label diff --git a/.github/workflows/create-block.yml b/.github/workflows/create-block.yml index 1cb40466abe1ef..ad63a563a93ba1 100644 --- a/.github/workflows/create-block.yml +++ b/.github/workflows/create-block.yml @@ -12,10 +12,16 @@ concurrency: group: ${{ github.workflow }}-${{ github.event_name == 'pull_request' && github.head_ref || github.sha }} cancel-in-progress: true +# Disable permissions for all available scopes by default. +# Any needed permissions should be configured at the job level. +permissions: {} + jobs: checks: name: Checks w/Node.js ${{ matrix.node }} on ${{ matrix.os }} runs-on: ${{ matrix.os }} + permissions: + contents: read if: ${{ github.repository == 'WordPress/gutenberg' || github.event_name == 'pull_request' }} strategy: fail-fast: false @@ -27,6 +33,7 @@ jobs: - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 with: show-progress: ${{ runner.debug == '1' && 'true' || 'false' }} + persist-credentials: false - name: Setup Node.js and install dependencies uses: ./.github/setup-node diff --git a/.github/workflows/end2end-test.yml b/.github/workflows/end2end-test.yml index ab6054f65ce967..9b154ef86f69f7 100644 --- a/.github/workflows/end2end-test.yml +++ b/.github/workflows/end2end-test.yml @@ -15,10 +15,16 @@ concurrency: group: ${{ github.workflow }}-${{ github.event_name == 'pull_request' && github.head_ref || github.sha }} cancel-in-progress: true +# Disable permissions for all available scopes by default. +# Any needed permissions should be configured at the job level. +permissions: {} + jobs: e2e-playwright: name: Playwright - ${{ matrix.part }} runs-on: ubuntu-latest + permissions: + contents: read if: ${{ github.repository == 'WordPress/gutenberg' || github.event_name == 'pull_request' }} strategy: fail-fast: false @@ -30,6 +36,7 @@ jobs: - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 with: show-progress: ${{ runner.debug == '1' && 'true' || 'false' }} + persist-credentials: false - name: Setup Node.js and install dependencies uses: ./.github/setup-node @@ -48,8 +55,10 @@ jobs: - name: Run the tests env: PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD: 1 + SHARD_PART: ${{ matrix.part }} + SHARD_TOTAL: ${{ matrix.totalParts }} run: | - xvfb-run --auto-servernum --server-args="-screen 0 1280x960x24" -- npm run test:e2e -- --shard=${{ matrix.part }}/${{ matrix.totalParts }} + xvfb-run --auto-servernum --server-args="-screen 0 1280x960x24" -- npm run test:e2e -- --shard="${SHARD_PART}/${SHARD_TOTAL}" - name: Archive debug artifacts (screenshots, traces) uses: actions/upload-artifact@65462800fd760344b1a7b4382951275a0abb4808 # v4.3.3 @@ -72,6 +81,7 @@ jobs: if: ${{ !cancelled() }} needs: [e2e-playwright] runs-on: ubuntu-latest + permissions: {} outputs: has-flaky-test-report: ${{ !!steps.merge-flaky-tests-reports.outputs.artifact-id }} steps: @@ -99,6 +109,10 @@ jobs: needs: [merge-artifacts] if: ${{ needs.merge-artifacts.outputs.has-flaky-test-report == 'true' }} runs-on: ubuntu-latest + permissions: + contents: read + issues: write + pull-requests: write steps: # Checkout defaults to using the branch which triggered the event, which # isn't necessarily `trunk` (e.g. in the case of a merge). @@ -106,8 +120,9 @@ jobs: with: ref: trunk show-progress: ${{ runner.debug == '1' && 'true' || 'false' }} + persist-credentials: false - - uses: actions/download-artifact@v4.1.9 + - uses: actions/download-artifact@v4.3.0 # Don't fail the job if there isn't any flaky tests report. continue-on-error: true with: diff --git a/.github/workflows/enforce-pr-labels.yml b/.github/workflows/enforce-pr-labels.yml index 7493459a6ff35c..b7f0c27a716331 100644 --- a/.github/workflows/enforce-pr-labels.yml +++ b/.github/workflows/enforce-pr-labels.yml @@ -2,6 +2,11 @@ name: Enforce labels on Pull Request on: pull_request_target: types: [labeled, unlabeled, ready_for_review, review_requested] + +# Disable permissions for all available scopes by default. +# Any needed permissions should be configured at the job level. +permissions: {} + jobs: type-related-labels: runs-on: ubuntu-latest diff --git a/.github/workflows/gradle-wrapper-validation.yml b/.github/workflows/gradle-wrapper-validation.yml index c4c5eeba9c51a7..c4a289645104ec 100644 --- a/.github/workflows/gradle-wrapper-validation.yml +++ b/.github/workflows/gradle-wrapper-validation.yml @@ -1,14 +1,22 @@ name: 'Validate Gradle Wrapper' on: [push, pull_request] +# Disable permissions for all available scopes by default. +# Any needed permissions should be configured at the job level. +permissions: {} + jobs: validation: name: 'Validation' runs-on: ubuntu-latest + permissions: + contents: read steps: - name: Checkout repository uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 with: show-progress: ${{ runner.debug == '1' && 'true' || 'false' }} + persist-credentials: false + - name: Validate checksums uses: gradle/actions/wrapper-validation@0bdd871935719febd78681f197cd39af5b6e16a6 # v4.2.2 diff --git a/.github/workflows/performance.yml b/.github/workflows/performance.yml index 4a5b576b424b53..6a7be0419130ab 100644 --- a/.github/workflows/performance.yml +++ b/.github/workflows/performance.yml @@ -23,11 +23,17 @@ concurrency: group: ${{ github.workflow }}-${{ github.event_name == 'pull_request' && github.head_ref || github.sha }} cancel-in-progress: true +# Disable permissions for all available scopes by default. +# Any needed permissions should be configured at the job level. +permissions: {} + jobs: performance: timeout-minutes: 60 name: Run performance tests runs-on: ubuntu-latest + permissions: + contents: read if: ${{ github.repository == 'WordPress/gutenberg' }} env: WP_ARTIFACTS_PATH: ${{ github.workspace }}/artifacts @@ -36,6 +42,7 @@ jobs: - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 with: show-progress: ${{ runner.debug == '1' && 'true' || 'false' }} + persist-credentials: false - name: Setup Node.js and install dependencies uses: ./.github/setup-node @@ -49,7 +56,7 @@ jobs: - name: Compare performance with base branch if: github.event_name == 'pull_request' - run: ./bin/plugin/cli.js perf $GITHUB_SHA ${{ github.base_ref }} --tests-branch $GITHUB_SHA + run: ./bin/plugin/cli.js perf "$GITHUB_SHA" "$GITHUB_BASE_REF" --tests-branch "$GITHUB_SHA" - name: Compare performance with current WordPress Core and previous Gutenberg versions if: github.event_name == 'release' @@ -61,21 +68,22 @@ jobs: CURRENT_RELEASE_BRANCH="release/${PLUGIN_VERSION_ARRAY[0]}.${PLUGIN_VERSION_ARRAY[1]}" PREVIOUS_VERSION_BASE_10=$((PLUGIN_VERSION_ARRAY[0] * 10 + PLUGIN_VERSION_ARRAY[1] - 1)) PREVIOUS_RELEASE_BRANCH="release/$((PREVIOUS_VERSION_BASE_10 / 10)).$((PREVIOUS_VERSION_BASE_10 % 10))" - WP_VERSION=$(awk -F ': ' '/^Tested up to/{print $2}' readme.txt) + WP_VERSION="$(awk -F ': ' '/^Tested up to/{print $2}' readme.txt)" IFS=. read -ra WP_VERSION_ARRAY <<< "$WP_VERSION" WP_MAJOR="${WP_VERSION_ARRAY[0]}.${WP_VERSION_ARRAY[1]}" - ./bin/plugin/cli.js perf "wp/$WP_MAJOR" "$PREVIOUS_RELEASE_BRANCH" "$CURRENT_RELEASE_BRANCH" --tests-branch $GITHUB_SHA --wp-version "$WP_MAJOR" + ./bin/plugin/cli.js perf "wp/$WP_MAJOR" "$PREVIOUS_RELEASE_BRANCH" "$CURRENT_RELEASE_BRANCH" --tests-branch "$GITHUB_SHA" --wp-version "$WP_MAJOR" - name: Compare performance with base branch if: github.event_name == 'push' # The base hash used here need to be a commit that is compatible with the current WP version - # The current one is c7722262e65a3f4d0f1a2d1ad29eccb2069509e4 and it needs to be updated every WP major release. + # The current one is 1b8efa77d39f68fb3d963ea02ce6ea6a8201d521 and it needs to be updated every WP major release. # It is used as a base comparison point to avoid fluctuation in the performance metrics. + # See: https://developer.wordpress.org/block-editor/explanations/architecture/performance/#update-the-reference-commit. run: | - WP_VERSION=$(awk -F ': ' '/^Tested up to/{print $2}' readme.txt) + WP_VERSION="$(awk -F ': ' '/^Tested up to/{print $2}' readme.txt)" IFS=. read -ra WP_VERSION_ARRAY <<< "$WP_VERSION" WP_MAJOR="${WP_VERSION_ARRAY[0]}.${WP_VERSION_ARRAY[1]}" - ./bin/plugin/cli.js perf $GITHUB_SHA c7722262e65a3f4d0f1a2d1ad29eccb2069509e4 --tests-branch $GITHUB_SHA --wp-version "$WP_MAJOR" + ./bin/plugin/cli.js perf "$GITHUB_SHA" 1b8efa77d39f68fb3d963ea02ce6ea6a8201d521 --tests-branch "$GITHUB_SHA" --wp-version "$WP_MAJOR" - name: Compare performance with custom branches if: github.event_name == 'workflow_dispatch' @@ -83,10 +91,10 @@ jobs: BRANCHES: ${{ github.event.inputs.branches }} WP_VERSION: ${{ github.event.inputs.wpversion }} run: | - ./bin/plugin/cli.js perf $(echo $BRANCHES | tr ',' ' ') --tests-branch $GITHUB_SHA --wp-version "$WP_VERSION" + ./bin/plugin/cli.js perf "$(echo "$BRANCHES" | tr ',' ' ')" --tests-branch "$GITHUB_SHA" --wp-version "$WP_VERSION" - name: Add workflow summary - run: cat ${{ env.WP_ARTIFACTS_PATH }}/summary.md >> $GITHUB_STEP_SUMMARY + run: cat "${WP_ARTIFACTS_PATH}/summary.md" >> "$GITHUB_STEP_SUMMARY" - name: Archive performance results if: success() @@ -100,8 +108,8 @@ jobs: env: CODEHEALTH_PROJECT_TOKEN: ${{ secrets.CODEHEALTH_PROJECT_TOKEN }} run: | - COMMITTED_AT=$(git show -s $GITHUB_SHA --format="%cI") - ./bin/log-performance-results.js $CODEHEALTH_PROJECT_TOKEN trunk $GITHUB_SHA c7722262e65a3f4d0f1a2d1ad29eccb2069509e4 $COMMITTED_AT + COMMITTED_AT="$(git show -s "$GITHUB_SHA" --format="%cI")" + ./bin/log-performance-results.js "$CODEHEALTH_PROJECT_TOKEN" trunk "$GITHUB_SHA" 1b8efa77d39f68fb3d963ea02ce6ea6a8201d521 "$COMMITTED_AT" - name: Archive debug artifacts (screenshots, HTML snapshots) uses: actions/upload-artifact@65462800fd760344b1a7b4382951275a0abb4808 # v4.3.3 diff --git a/.github/workflows/props-bot.yml b/.github/workflows/props-bot.yml index b2332aabb816c7..574aea9f9ae948 100644 --- a/.github/workflows/props-bot.yml +++ b/.github/workflows/props-bot.yml @@ -76,6 +76,8 @@ jobs: - name: Remove the props-bot label uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1 if: ${{ github.event.action == 'labeled' && 'props-bot' == github.event.label.name }} + env: + ISSUE_NUMBER: ${{ github.event.number }} with: retries: 2 retry-exempt-status-codes: 418 @@ -83,6 +85,6 @@ jobs: github.rest.issues.removeLabel({ owner: context.repo.owner, repo: context.repo.repo, - issue_number: '${{ github.event.number }}', + issue_number: process.env.ISSUE_NUMBER, name: 'props-bot' }); diff --git a/.github/workflows/publish-npm-packages.yml b/.github/workflows/publish-npm-packages.yml index 256c1e112a71ae..5e58eccdeee2ee 100644 --- a/.github/workflows/publish-npm-packages.yml +++ b/.github/workflows/publish-npm-packages.yml @@ -10,6 +10,7 @@ on: default: 'development' options: - development + - latest - bugfix - wp wp_version: @@ -23,10 +24,16 @@ concurrency: group: ${{ github.workflow }}-${{ github.event_name == 'pull_request' && github.head_ref || github.sha }} cancel-in-progress: true +# Disable permissions for all available scopes by default. +# Any needed permissions should be configured at the job level. +permissions: {} + jobs: release: name: Release - ${{ github.event.inputs.release_type }} runs-on: ubuntu-latest + permissions: + contents: read environment: WordPress packages steps: - name: Checkout (for CLI) @@ -36,6 +43,7 @@ jobs: path: cli ref: trunk show-progress: ${{ runner.debug == '1' && 'true' || 'false' }} + persist-credentials: false - name: Checkout (for publishing) if: ${{ github.event.inputs.release_type != 'wp' }} @@ -46,6 +54,7 @@ jobs: ref: trunk token: ${{ secrets.GUTENBERG_TOKEN }} show-progress: ${{ runner.debug == '1' && 'true' || 'false' }} + persist-credentials: true - name: Checkout (for publishing WP major version) if: ${{ github.event.inputs.release_type == 'wp' && github.event.inputs.wp_version }} @@ -58,6 +67,7 @@ jobs: fetch-depth: 999 token: ${{ secrets.GUTENBERG_TOKEN }} show-progress: ${{ runner.debug == '1' && 'true' || 'false' }} + persist-credentials: true - name: Configure git user name and email (for publishing) run: | @@ -67,7 +77,7 @@ jobs: - name: Setup Node.js if: ${{ github.event.inputs.release_type != 'wp' }} - uses: actions/setup-node@cdca7365b2dadb8aad0a33bc7601856ffabcc48e # v4.3.0 + uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0 with: node-version-file: 'cli/.nvmrc' registry-url: 'https://registry.npmjs.org' @@ -75,7 +85,7 @@ jobs: - name: Setup Node.js (for WP major version) if: ${{ github.event.inputs.release_type == 'wp' && github.event.inputs.wp_version }} - uses: actions/setup-node@cdca7365b2dadb8aad0a33bc7601856ffabcc48e # v4.3.0 + uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0 with: node-version-file: 'publish/.nvmrc' registry-url: 'https://registry.npmjs.org' @@ -90,6 +100,15 @@ jobs: env: NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} + - name: Publish packages based on the latest Gutenberg plugin to npm ("latest" dist-tag) + if: ${{ github.event.inputs.release_type == 'latest' }} + run: | + cd cli + npm ci + ./bin/plugin/cli.js npm-latest --semver minor --ci --repository-path ../publish + env: + NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} + - name: Publish packages to npm with bug fixes ("latest" dist-tag) if: ${{ github.event.inputs.release_type == 'bugfix' }} run: | @@ -104,6 +123,7 @@ jobs: run: | cd publish npm ci - npx lerna publish patch --dist-tag wp-${{ github.event.inputs.wp_version }} --no-private --yes --no-verify-access + npx lerna publish patch --dist-tag "wp-$WP_VERSION" --no-private --yes --no-verify-access env: NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} + WP_VERSION: ${{ github.event.inputs.wp_version }} diff --git a/.github/workflows/pull-request-automation.yml b/.github/workflows/pull-request-automation.yml index e1a3defc641aa3..1c6ecf90469201 100644 --- a/.github/workflows/pull-request-automation.yml +++ b/.github/workflows/pull-request-automation.yml @@ -4,9 +4,17 @@ on: push: name: Pull request automation +# Disable permissions for all available scopes by default. +# Any needed permissions should be configured at the job level. +permissions: {} + jobs: pull-request-automation: runs-on: ubuntu-latest + permissions: + contents: read + issues: write + pull-requests: write if: ${{ github.repository == 'WordPress/gutenberg' }} steps: @@ -16,6 +24,7 @@ jobs: with: ref: trunk show-progress: ${{ runner.debug == '1' && 'true' || 'false' }} + persist-credentials: false - name: Setup Node.js and install dependencies uses: ./.github/setup-node diff --git a/.github/workflows/reusable-workflow-lint.yml b/.github/workflows/reusable-workflow-lint.yml new file mode 100644 index 00000000000000..fc2e7752211c19 --- /dev/null +++ b/.github/workflows/reusable-workflow-lint.yml @@ -0,0 +1,37 @@ +name: Lint GitHub Actions workflows +on: + workflow_call: + +# Disable permissions for all available scopes by default. +# Any needed permissions should be configured at the job level. +permissions: {} + +jobs: + # Runs the actionlint GitHub Action workflow file linter. + # + # This helps guard against common mistakes including strong type checking for expressions (${{ }}), security checks, + # `run:` script checking, glob syntax validation, and more. + # + # Performs the following steps: + # - Checks out the repository. + # - Runs actionlint. + actionlint: + name: Run actionlint + runs-on: ubuntu-24.04 + permissions: + contents: read + timeout-minutes: 5 + steps: + - name: Checkout repository + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + with: + persist-credentials: false + show-progress: ${{ runner.debug == '1' && 'true' || 'false' }} + + # actionlint is static checker for GitHub Actions workflow files. + # See https://github.com/rhysd/actionlint. + # Configuration file: .github/actionlint.yml + - name: Run actionlint + uses: docker://rhysd/actionlint:1.7.7 + with: + args: '-color -verbose' diff --git a/.github/workflows/rnmobile-android-runner.yml b/.github/workflows/rnmobile-android-runner.yml index dbb17461fe8027..0637df1b7c2c63 100644 --- a/.github/workflows/rnmobile-android-runner.yml +++ b/.github/workflows/rnmobile-android-runner.yml @@ -12,9 +12,15 @@ concurrency: group: ${{ github.workflow }}-${{ github.event_name == 'pull_request' && github.head_ref || github.sha }} cancel-in-progress: true +# Disable permissions for all available scopes by default. +# Any needed permissions should be configured at the job level. +permissions: {} + jobs: test: runs-on: macos-13 + permissions: + contents: read if: false #if: ${{ github.repository == 'WordPress/gutenberg' || github.event_name == 'pull_request' }} strategy: @@ -27,6 +33,7 @@ jobs: uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 with: show-progress: ${{ runner.debug == '1' && 'true' || 'false' }} + persist-credentials: false - name: Use desired version of Java uses: actions/setup-java@7a6d8a8234af8eb26422e24e3006232cccaa061b # v4.6.0 @@ -38,7 +45,7 @@ jobs: uses: ./.github/setup-node - name: Restore tests setup cache - uses: actions/cache@d4323d4df104b026a6aa633fdb11d772146be0bf # v4.2.2 + uses: actions/cache@5a3ec84eff668545956fd18022155c47e93e2684 # v4.2.3 with: path: | ~/.appium @@ -53,7 +60,7 @@ jobs: # AVD cache disabled as it caused emulator termination to hang indefinitely. # https://github.com/ReactiveCircus/android-emulator-runner/issues/385 # - name: AVD cache - # uses: actions/cache@d4323d4df104b026a6aa633fdb11d772146be0bf # v4.2.2 + # uses: actions/cache@5a3ec84eff668545956fd18022155c47e93e2684 # v4.2.3 # id: avd-cache # with: # path: | @@ -75,6 +82,8 @@ jobs: - name: Run tests uses: reactivecircus/android-emulator-runner@62dbb605bba737720e10b196cb4220d374026a6d # v2.33.0 + env: + NATIVE_TEST_NAME: ${{ matrix.native-test-name }} with: api-level: ${{ matrix.api-level }} force-avd-creation: false @@ -82,7 +91,7 @@ jobs: disable-animations: true arch: x86_64 profile: Nexus 6 - script: npm run native test:e2e:android:local ${{ matrix.native-test-name }} + script: npm run native test:e2e:android:local "$NATIVE_TEST_NAME" - uses: actions/upload-artifact@65462800fd760344b1a7b4382951275a0abb4808 # v4.3.3 if: always() diff --git a/.github/workflows/rnmobile-ios-runner.yml b/.github/workflows/rnmobile-ios-runner.yml index ab63603afd129c..2ba35cbe2da630 100644 --- a/.github/workflows/rnmobile-ios-runner.yml +++ b/.github/workflows/rnmobile-ios-runner.yml @@ -12,9 +12,15 @@ concurrency: group: ${{ github.workflow }}-${{ github.event_name == 'pull_request' && github.head_ref || github.sha }} cancel-in-progress: true +# Disable permissions for all available scopes by default. +# Any needed permissions should be configured at the job level. +permissions: {} + jobs: test: runs-on: macos-13 + permissions: + contents: read if: false #if: ${{ github.repository == 'WordPress/gutenberg' || github.event_name == 'pull_request' }} strategy: @@ -27,6 +33,7 @@ jobs: - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 with: show-progress: ${{ runner.debug == '1' && 'true' || 'false' }} + persist-credentials: false - uses: ruby/setup-ruby@1287d2b408066abada82d5ad1c63652e758428d9 # v1.214.0 with: @@ -34,16 +41,20 @@ jobs: working-directory: packages/react-native-editor/ios - name: Switch Xcode version to ${{ matrix.xcode }} - run: sudo xcode-select --switch /Applications/Xcode_${{ matrix.xcode }}.app + env: + VERSION: ${{ matrix.xcode }} + run: sudo xcode-select --switch "/Applications/Xcode_${VERSION}.app" - name: Launch simulator - run: (open -a Simulator && xcrun simctl boot '${{ matrix.device }}') & + env: + DEVICE: ${{ matrix.device }} + run: (open -a Simulator && xcrun simctl boot "${DEVICE}") & - name: Setup Node.js and install dependencies uses: ./.github/setup-node - name: Restore tests setup cache - uses: actions/cache@d4323d4df104b026a6aa633fdb11d772146be0bf # v4.2.2 + uses: actions/cache@5a3ec84eff668545956fd18022155c47e93e2684 # v4.2.3 with: path: | ~/.appium @@ -56,7 +67,7 @@ jobs: run: find package-lock.json packages/react-native-editor/ios packages/react-native-aztec/ios packages/react-native-bridge/ios -type f -print0 | sort -z | xargs -0 shasum | tee ios-checksums.txt - name: Restore build cache - uses: actions/cache@d4323d4df104b026a6aa633fdb11d772146be0bf # v4.2.2 + uses: actions/cache@5a3ec84eff668545956fd18022155c47e93e2684 # v4.2.3 with: path: | packages/react-native-editor/ios/build/GutenbergDemo/Build/Products/Release-iphonesimulator/GutenbergDemo.app @@ -64,7 +75,7 @@ jobs: key: ${{ runner.os }}-ios-build-${{ matrix.xcode }}-${{ matrix.device }}-${{ hashFiles('ios-checksums.txt') }} - name: Restore pods cache - uses: actions/cache@d4323d4df104b026a6aa633fdb11d772146be0bf # v4.2.2 + uses: actions/cache@5a3ec84eff668545956fd18022155c47e93e2684 # v4.2.3 with: path: | packages/react-native-editor/ios/Pods @@ -83,7 +94,9 @@ jobs: run: test -d packages/react-native-editor/ios/build/WDA || npm run native test:e2e:build-wda - name: Run iOS Device Tests - run: TEST_RN_PLATFORM=ios npm run native device-tests:local ${{ matrix.native-test-name }} + env: + NATIVE_TEST_NAME: ${{ matrix.native-test-name }} + run: TEST_RN_PLATFORM=ios npm run native device-tests:local "$NATIVE_TEST_NAME" - name: Prepare build cache run: | diff --git a/.github/workflows/stale-issue-gardening.yml b/.github/workflows/stale-issue-gardening.yml index 6b8c7e82d1ca7b..5caeee49198dba 100644 --- a/.github/workflows/stale-issue-gardening.yml +++ b/.github/workflows/stale-issue-gardening.yml @@ -4,10 +4,17 @@ on: schedule: - cron: '0 0 * * *' +# Disable permissions for all available scopes by default. +# Any needed permissions should be configured at the job level. +permissions: {} + jobs: issue-gardening: name: ${{ matrix.name }} runs-on: ubuntu-latest + permissions: + issues: write + pull-requests: write if: ${{ github.repository == 'WordPress/gutenberg' }} strategy: matrix: diff --git a/.github/workflows/static-checks.yml b/.github/workflows/static-checks.yml index 46a7a22b323256..86cbacd6d960a9 100644 --- a/.github/workflows/static-checks.yml +++ b/.github/workflows/static-checks.yml @@ -15,19 +15,26 @@ concurrency: group: ${{ github.workflow }}-${{ github.event_name == 'pull_request' && github.head_ref || github.sha }} cancel-in-progress: true +# Disable permissions for all available scopes by default. +# Any needed permissions should be configured at the job level. +permissions: {} + jobs: check: name: All runs-on: ubuntu-latest + permissions: + contents: read if: ${{ github.repository == 'WordPress/gutenberg' || github.event_name == 'pull_request' }} steps: - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 with: show-progress: ${{ runner.debug == '1' && 'true' || 'false' }} + persist-credentials: false - name: Use desired version of Node.js - uses: actions/setup-node@cdca7365b2dadb8aad0a33bc7601856ffabcc48e # v4.3.0 + uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0 with: node-version-file: '.nvmrc' check-latest: true diff --git a/.github/workflows/storybook-check.yml b/.github/workflows/storybook-check.yml index dd710f96747128..97697b3f897cf9 100644 --- a/.github/workflows/storybook-check.yml +++ b/.github/workflows/storybook-check.yml @@ -1,4 +1,4 @@ -name: Check Storybook build +name: Storybook build and Smoke Tests on: pull_request @@ -9,19 +9,39 @@ concurrency: group: ${{ github.workflow }}-${{ github.event_name == 'pull_request' && github.head_ref || github.sha }} cancel-in-progress: true +# Disable permissions for all available scopes by default. +# Any needed permissions should be configured at the job level. +permissions: {} + jobs: check: runs-on: ubuntu-latest + permissions: + contents: read if: ${{ github.repository == 'WordPress/gutenberg' || github.event_name == 'pull_request' }} + timeout-minutes: 30 steps: - name: Checkout uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 with: show-progress: ${{ runner.debug == '1' && 'true' || 'false' }} + persist-credentials: false - name: Setup Node.js and install dependencies uses: ./.github/setup-node - name: Build Storybook run: npm run storybook:build + + # - name: Install Playwright dependencies + # run: npx playwright install --with-deps + + # - name: Serve Storybook and run tests + # run: | + # npx concurrently -k -s first -n "SB,TEST" -c "magenta,blue" \ + # "npx http-server ./storybook/build --port 50240 --silent" \ + # "npx wait-on tcp:127.0.0.1:50240 && \ + # NODE_PATH=./node_modules \ + # npx --package=@storybook/test-runner -- \ + # test-storybook --url http://localhost:50240 --config-dir ./storybook" diff --git a/.github/workflows/storybook-pages.yml b/.github/workflows/storybook-pages.yml index 4af4934cf0325b..9defe8d0ef89d5 100644 --- a/.github/workflows/storybook-pages.yml +++ b/.github/workflows/storybook-pages.yml @@ -5,9 +5,15 @@ on: branches: - trunk +# Disable permissions for all available scopes by default. +# Any needed permissions should be configured at the job level. +permissions: {} + jobs: deploy: runs-on: ubuntu-latest + permissions: + contents: write if: ${{ github.repository == 'WordPress/gutenberg' }} steps: @@ -16,6 +22,7 @@ jobs: with: ref: trunk show-progress: ${{ runner.debug == '1' && 'true' || 'false' }} + persist-credentials: false - name: Setup Node.js and install dependencies uses: ./.github/setup-node diff --git a/.github/workflows/sync-assets-to-plugin-repo.yml b/.github/workflows/sync-assets-to-plugin-repo.yml index c841b3ffc79579..745bf37c208bed 100644 --- a/.github/workflows/sync-assets-to-plugin-repo.yml +++ b/.github/workflows/sync-assets-to-plugin-repo.yml @@ -7,10 +7,16 @@ on: paths: - assets/** +# Disable permissions for all available scopes by default. +# Any needed permissions should be configured at the job level. +permissions: {} + jobs: sync-assets: name: Sync assets to WordPress.org plugin repo runs-on: ubuntu-latest + permissions: + contents: read environment: wp.org plugin env: PLUGIN_REPO_URL: 'https://plugins.svn.wordpress.org/gutenberg' @@ -33,6 +39,7 @@ jobs: assets show-progress: ${{ runner.debug == '1' && 'true' || 'false' }} path: git + persist-credentials: false - name: Copy files from git checkout to svn working copy run: cp -R git/assets/* assets diff --git a/.github/workflows/sync-backport-changelog.yml b/.github/workflows/sync-backport-changelog.yml index e530ca667de3d7..a42a2f1e8be7be 100644 --- a/.github/workflows/sync-backport-changelog.yml +++ b/.github/workflows/sync-backport-changelog.yml @@ -7,10 +7,17 @@ on: issues: types: [labeled] +# Disable permissions for all available scopes by default. +# Any needed permissions should be configured at the job level. +permissions: {} + jobs: sync-backport-changelog: name: Sync Core Backport Issue runs-on: ubuntu-latest + permissions: + contents: read + issues: write if: > github.event_name == 'push' || ( @@ -23,6 +30,7 @@ jobs: uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 with: fetch-depth: 2 # Fetch the last two commits to compare changes + persist-credentials: false - name: Check for changes in backport-changelog if: github.event_name == 'push' run: | diff --git a/.github/workflows/unit-test.yml b/.github/workflows/unit-test.yml index 621bba5b10b248..74344dd3cf6c21 100644 --- a/.github/workflows/unit-test.yml +++ b/.github/workflows/unit-test.yml @@ -19,10 +19,16 @@ concurrency: group: ${{ github.workflow }}-${{ github.event_name == 'pull_request' && github.head_ref || github.sha }} cancel-in-progress: true +# Disable permissions for all available scopes by default. +# Any needed permissions should be configured at the job level. +permissions: {} + jobs: unit-js: name: JavaScript (Node.js ${{ matrix.node }}) ${{ matrix.shard }} runs-on: ubuntu-latest + permissions: + contents: read if: ${{ github.repository == 'WordPress/gutenberg' || github.event_name == 'pull_request' }} strategy: fail-fast: false @@ -35,6 +41,7 @@ jobs: uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 with: show-progress: ${{ runner.debug == '1' && 'true' || 'false' }} + persist-credentials: false - name: Setup Node.js and install dependencies uses: ./.github/setup-node @@ -52,16 +59,21 @@ jobs: run: npx lerna run build - name: Running the tests + env: + MAXWORKERS: ${{ steps.cpu-cores.outputs.count }} + SHARD: ${{ matrix.shard }} run: | npm run test:unit -- \ --ci \ - --maxWorkers="${{ steps.cpu-cores.outputs.count }}" \ - --shard="${{ matrix.shard }}" \ + --maxWorkers="$MAXWORKERS" \ + --shard="$SHARD" \ --cacheDirectory="$HOME/.jest-cache" unit-js-date: name: JavaScript Date Tests (Node.js ${{ matrix.node }}) runs-on: ubuntu-latest + permissions: + contents: read if: ${{ github.repository == 'WordPress/gutenberg' || github.event_name == 'pull_request' }} strategy: fail-fast: false @@ -73,6 +85,7 @@ jobs: uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 with: show-progress: ${{ runner.debug == '1' && 'true' || 'false' }} + persist-credentials: false - name: Setup Node.js and install dependencies uses: ./.github/setup-node @@ -90,11 +103,14 @@ jobs: run: npx lerna run build - name: Run the date tests - run: npm run test:unit:date -- --ci --maxWorkers=${{ steps.cpu-cores.outputs.count }} --cacheDirectory="$HOME/.jest-cache" + env: + MAXWORKERS: ${{ steps.cpu-cores.outputs.count }} + run: npm run test:unit:date -- --ci --maxWorkers="$MAXWORKERS" --cacheDirectory="$HOME/.jest-cache" compute-previous-wordpress-version: name: Compute previous WordPress version runs-on: ubuntu-latest + permissions: {} outputs: previous-wordpress-version: ${{ steps.get-previous-wordpress-version.outputs.previous-wordpress-version }} @@ -106,24 +122,28 @@ jobs: -H "Accept: application/json" \ -o versions.json \ "http://api.wordpress.org/core/stable-check/1.0/" - LATEST_WP_VERSION=$(jq --raw-output 'with_entries(select(.value=="latest"))|keys[]' versions.json) - IFS='.' read LATEST_WP_MAJOR LATEST_WP_MINOR LATEST_WP_PATCH <<< "${LATEST_WP_VERSION}" - if [[ ${LATEST_WP_MINOR} == "0" ]]; then + LATEST_WP_VERSION="$(jq --raw-output 'with_entries(select(.value=="latest"))|keys[]' versions.json)" + # shellcheck disable=SC2034 + IFS='.' read -r LATEST_WP_MAJOR LATEST_WP_MINOR LATEST_WP_PATCH <<< "${LATEST_WP_VERSION}" + if [[ "${LATEST_WP_MINOR}" == "0" ]]; then PREVIOUS_WP_SERIES="$((LATEST_WP_MAJOR - 1)).9" else PREVIOUS_WP_SERIES="${LATEST_WP_MAJOR}.$((LATEST_WP_MINOR - 1))" fi - PREVIOUS_WP_VERSION=$(jq --raw-output --arg series "${PREVIOUS_WP_SERIES}" 'with_entries(select(.key|startswith($series)))|keys[-1]' versions.json) - echo "previous-wordpress-version=${PREVIOUS_WP_VERSION}" >> $GITHUB_OUTPUT + PREVIOUS_WP_VERSION="$(jq --raw-output --arg series "${PREVIOUS_WP_SERIES}" 'with_entries(select(.key|startswith($series)))|keys[-1]' versions.json)" + echo "previous-wordpress-version=${PREVIOUS_WP_VERSION}" >> "$GITHUB_OUTPUT" rm versions.json build-assets: name: Build JavaScript assets for PHP unit tests runs-on: ubuntu-latest + permissions: + contents: read steps: - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 with: show-progress: ${{ runner.debug == '1' && 'true' || 'false' }} + persist-credentials: false - name: Setup Node.js and install dependencies uses: ./.github/setup-node @@ -143,6 +163,8 @@ jobs: name: PHP ${{ matrix.php }}${{ matrix.multisite && ' multisite' || '' }}${{ matrix.wordpress != '' && format( ' (WP {0}) ', matrix.wordpress ) || '' }} on ubuntu-latest needs: [compute-previous-wordpress-version, build-assets] runs-on: ubuntu-latest + permissions: + contents: read timeout-minutes: 20 if: ${{ github.repository == 'WordPress/gutenberg' || github.event_name == 'pull_request' }} strategy: @@ -176,6 +198,7 @@ jobs: uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 with: show-progress: ${{ runner.debug == '1' && 'true' || 'false' }} + persist-credentials: false - name: Setup Node.js and install dependencies uses: ./.github/setup-node @@ -188,7 +211,7 @@ jobs: # dependency versions are installed and cached. ## - name: Set up PHP - uses: shivammathur/setup-php@9e72090525849c5e82e596468b86eb55e9cc5401 # v2.32.0 + uses: shivammathur/setup-php@0f7f1d08e3e32076e51cae65eb0b0c871405b16e # v2.34.1 with: php-version: '${{ matrix.php }}' ini-file: development @@ -196,17 +219,19 @@ jobs: # Ensure that Composer installs the correct versions of packages. - name: Override PHP version in composer.json - run: composer config platform.php ${{ matrix.php }} + env: + VERSION: ${{ matrix.php }} + run: composer config platform.php "$VERSION" # Since Composer dependencies are installed using `composer update` and no lock file is in version control, # passing a custom cache suffix ensures that the cache is flushed at least once per week. - name: Install Composer dependencies - uses: ramsey/composer-install@a2636af0004d1c0499ffca16ac0b4cc94df70565 # v3.1.0 + uses: ramsey/composer-install@3cf229dc2919194e9e36783941438d17239e8520 # v3.1.1 with: custom-cache-suffix: $(/bin/date -u --date='last Mon' "+%F") - name: Download built JavaScript assets - uses: actions/download-artifact@cc203385981b70ca67e1cc392babf9cc229d5806 # v4.1.9 + uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0 with: name: build-assets @@ -258,14 +283,14 @@ jobs: run: | # Note: relies on PHPUnit execution to fail on test failure. # Extract the number of executed tests from the log file. - if ! num_tests=$(grep -Eo 'OK \([0-9]+ tests' phpunit.log) ; then - if ! num_tests=$(grep -Eo 'Tests: [0-9]+, Assertions:' phpunit.log) ; then + if ! num_tests="$(grep -Eo 'OK \([0-9]+ tests' phpunit.log)" ; then + if ! num_tests="$(grep -Eo 'Tests: [0-9]+, Assertions:' phpunit.log)" ; then echo "PHPUnit failed or did not run. Check the PHPUnit output in the previous step to debug." && exit 1 fi fi # Extract just the number of tests from the string. - num_tests=$(echo "$num_tests" | grep -Eo '[0-9]+') - if [ $num_tests -lt 500 ] ; then + num_tests="$(echo "$num_tests" | grep -Eo '[0-9]+')" + if [ "$num_tests" -lt 500 ] ; then echo "Only $num_tests tests passed, which is much fewer than expected." && exit 1 fi echo "$num_tests tests passed." @@ -273,6 +298,8 @@ jobs: phpcs: name: PHP coding standards runs-on: ubuntu-latest + permissions: + contents: read timeout-minutes: 20 if: ${{ github.repository == 'WordPress/gutenberg' || github.event_name == 'pull_request' }} @@ -281,9 +308,10 @@ jobs: uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 with: show-progress: ${{ runner.debug == '1' && 'true' || 'false' }} + persist-credentials: false - name: Set up PHP - uses: shivammathur/setup-php@9e72090525849c5e82e596468b86eb55e9cc5401 # v2.32.0 + uses: shivammathur/setup-php@0f7f1d08e3e32076e51cae65eb0b0c871405b16e # v2.34.1 with: php-version: '7.4' coverage: none @@ -293,10 +321,10 @@ jobs: # http://man7.org/linux/man-pages/man1/date.1.html - name: "Get last Monday's date" id: get-date - run: echo "date=$(/bin/date -u --date='last Mon' "+%F")" >> $GITHUB_OUTPUT + run: echo "date=$(/bin/date -u --date='last Mon' "+%F")" >> "$GITHUB_OUTPUT" - name: Cache PHPCS scan cache - uses: actions/cache@d4323d4df104b026a6aa633fdb11d772146be0bf # v4.2.2 + uses: actions/cache@5a3ec84eff668545956fd18022155c47e93e2684 # v4.2.3 with: path: .cache/phpcs.json key: ${{ runner.os }}-date-${{ steps.get-date.outputs.date }}-phpcs-cache-${{ hashFiles('**/composer.json', 'phpcs.xml.dist') }} @@ -304,12 +332,12 @@ jobs: # Since Composer dependencies are installed using `composer update` and no lock file is in version control, # passing a custom cache suffix ensures that the cache is flushed at least once per week. - name: Install Composer dependencies - uses: ramsey/composer-install@a2636af0004d1c0499ffca16ac0b4cc94df70565 # v3.1.0 + uses: ramsey/composer-install@3cf229dc2919194e9e36783941438d17239e8520 # v3.1.1 with: custom-cache-suffix: ${{ steps.get-date.outputs.date }} - name: Make Composer packages available globally - run: echo "${PWD}/vendor/bin" >> $GITHUB_PATH + run: echo "${PWD}/vendor/bin" >> "$GITHUB_PATH" - name: Run PHPCS on all Gutenberg files id: phpcs-gutenberg @@ -326,6 +354,7 @@ jobs: unit-php: name: PHP runs-on: ubuntu-latest + permissions: {} needs: [test-php, phpcs] if: ${{ always() }} steps: @@ -344,6 +373,8 @@ jobs: mobile-unit-js: name: Mobile runs-on: ubuntu-latest + permissions: + contents: read if: ${{ github.repository == 'WordPress/gutenberg' || github.event_name == 'pull_request' }} steps: @@ -351,6 +382,7 @@ jobs: uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 with: show-progress: ${{ runner.debug == '1' && 'true' || 'false' }} + persist-credentials: false - name: Determine the number of CPU cores uses: SimenB/github-actions-cpu-cores@97ba232459a8e02ff6121db9362b09661c875ab8 # v2.0.0 @@ -366,4 +398,6 @@ jobs: run: npx lerna run build - name: Running the tests - run: npm run test:native -- --ci --maxWorkers=${{ steps.cpu-cores.outputs.count }} --cacheDirectory="$HOME/.jest-cache" + env: + MAXWORKERS: ${{ steps.cpu-cores.outputs.count }} + run: npm run test:native -- --ci --maxWorkers="$MAXWORKERS" --cacheDirectory="$HOME/.jest-cache" diff --git a/.github/workflows/upload-release-to-plugin-repo.yml b/.github/workflows/upload-release-to-plugin-repo.yml index 997b0cff9a44a1..1d5b607015859a 100644 --- a/.github/workflows/upload-release-to-plugin-repo.yml +++ b/.github/workflows/upload-release-to-plugin-repo.yml @@ -4,10 +4,15 @@ on: release: types: [published] +# Disable permissions for all available scopes by default. +# Any needed permissions should be configured at the job level. +permissions: {} + jobs: compute-should-update-trunk: name: Decide if trunk or tag runs-on: ubuntu-latest + permissions: {} # Skip this job if the release is a release candidate. This will in turn skip # the upload jobs, which are only relevant for non-RC releases. # We first check if the release is a prerelease, and then if the ref contains @@ -25,15 +30,15 @@ jobs: run: | latest_version_in_core_repo=$(curl -s 'https://api.wordpress.org/plugins/info/1.2/?action=plugin_information&request\[slug\]=gutenberg' | jq -r '.version') echo "Latest Core Repo version: $latest_version_in_core_repo" - echo "version=$latest_version_in_core_repo" >> $GITHUB_OUTPUT + echo "version=$latest_version_in_core_repo" >> "$GITHUB_OUTPUT" - name: Decide if it is a trunk or tag update id: compute_should_update_trunk env: GITHUB_REF: ${{ github.ref }} + LATEST_VERSION: ${{ steps.compute_latest_version_in_core_repo.outputs.version }} run: | latestPublishedVersion=$(echo "$GITHUB_REF" | sed -E 's/refs\/tags\/(v?)([0-9.]+)/\2/') - latestVersionInCoreRepo="${{ steps.compute_latest_version_in_core_repo.outputs.version }}" # Determines if the first version string is greater than the second version string. # @@ -55,16 +60,17 @@ jobs: # than the version currently published in the WP plugins repo. If not, then it # will upload it as a new tag. shouldUpdateTrunk=false - if is_first_version_greater_than_second "$latestPublishedVersion" "$latestVersionInCoreRepo"; then + if is_first_version_greater_than_second "$latestPublishedVersion" "$LATEST_VERSION"; then shouldUpdateTrunk=true fi echo "Should update trunk: $shouldUpdateTrunk" - echo "should_update_trunk=$shouldUpdateTrunk" >> $GITHUB_OUTPUT + echo "should_update_trunk=$shouldUpdateTrunk" >> "$GITHUB_OUTPUT" get-release-branch: name: Get release branch name runs-on: ubuntu-latest + permissions: {} outputs: release_branch: ${{ steps.get_release_branch.outputs.release_branch }} @@ -76,11 +82,13 @@ jobs: run: | IFS='.' read -r -a VERSION_ARRAY <<< "${TAG#v}" RELEASE_BRANCH="release/${VERSION_ARRAY[0]}.${VERSION_ARRAY[1]}" - echo "release_branch=${RELEASE_BRANCH}" >> $GITHUB_OUTPUT + echo "release_branch=${RELEASE_BRANCH}" >> "$GITHUB_OUTPUT" update-changelog: name: Update Changelog on ${{ matrix.branch }} branch runs-on: ubuntu-latest + permissions: + contents: write if: | github.event.release.assets[0] needs: get-release-branch @@ -101,8 +109,11 @@ jobs: ref: ${{ matrix.branch }} token: ${{ secrets.GUTENBERG_TOKEN }} show-progress: ${{ runner.debug == '1' && 'true' || 'false' }} + persist-credentials: true - name: Update the Changelog to include the release notes + env: + RELEASE_BODY: ${{ github.event.release.body }} run: | # First, determine where to insert the new Changelog entry. SERIES="${RELEASE_BRANCH#release/}" @@ -119,9 +130,7 @@ jobs: head -n $(( "${BEFORE}" - 1 )) changelog.txt > new_changelog.txt printf '= %s =\n\n' "${TAG#v}" >> new_changelog.txt # Need to use a heredoc in order to preserve special characters. - cat <<- "EOF" > release_notes.txt - ${{ github.event.release.body }} - EOF + echo "$RELEASE_BODY" > release_notes.txt # Normalize empty lines: Trim them from beginning and end of file... awk 'NF {p=1} p' <<< "$(< release_notes.txt)" >> new_changelog.txt # ...then add two empty lines at the end. @@ -135,6 +144,8 @@ jobs: git config user.email gutenberg@wordpress.org - name: Commit the Changelog update + env: + TARGET_BRANCH: ${{ matrix.branch }} run: | git add changelog.txt # Remove files that are not meant to be committed @@ -143,7 +154,7 @@ jobs: # Only attempt to commit changelog if it has been modified. if ! git diff-index --quiet HEAD --; then git commit -m "Update Changelog for ${TAG#v}" - git push --set-upstream origin "${{ matrix.branch }}" + git push --set-upstream origin "$TARGET_BRANCH" fi - name: Upload Changelog artifact @@ -155,6 +166,7 @@ jobs: upload: name: Publish as trunk (and tag) runs-on: ubuntu-latest + permissions: {} environment: wp.org plugin needs: [compute-should-update-trunk, update-changelog] if: | @@ -184,7 +196,7 @@ jobs: env: PLUGIN_URL: ${{ github.event.release.assets[0].browser_download_url }} run: | - curl -L -o gutenberg.zip $PLUGIN_URL + curl -L -o gutenberg.zip "$PLUGIN_URL" unzip gutenberg.zip -d trunk rm gutenberg.zip @@ -195,7 +207,7 @@ jobs: sed -i "s/$STABLE_TAG_PLACEHOLDER/Stable tag: $VERSION/g" ./trunk/readme.txt - name: Download Changelog Artifact - uses: actions/download-artifact@cc203385981b70ca67e1cc392babf9cc229d5806 # v4.1.9 + uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0 with: name: changelog trunk path: trunk @@ -214,6 +226,7 @@ jobs: upload-tag: name: Publish as tag runs-on: ubuntu-latest + permissions: {} environment: wp.org plugin needs: [compute-should-update-trunk, update-changelog] if: | @@ -235,7 +248,7 @@ jobs: PLUGIN_URL: ${{ github.event.release.assets[0].browser_download_url }} run: | # do the magic here - curl -L -o gutenberg.zip $PLUGIN_URL + curl -L -o gutenberg.zip "$PLUGIN_URL" unzip gutenberg.zip -d "$VERSION" rm gutenberg.zip @@ -246,7 +259,7 @@ jobs: sed -i "s/$STABLE_TAG_PLACEHOLDER/Stable tag: $VERSION/g" "$VERSION/readme.txt" - name: Download Changelog Artifact - uses: actions/download-artifact@cc203385981b70ca67e1cc392babf9cc229d5806 # v4.1.9 + uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0 with: name: changelog trunk path: ${{ github.event.release.name }} diff --git a/.github/workflows/workflow-lint.yml b/.github/workflows/workflow-lint.yml new file mode 100644 index 00000000000000..6789acb03785f8 --- /dev/null +++ b/.github/workflows/workflow-lint.yml @@ -0,0 +1,36 @@ +name: Lint GitHub Actions workflow files + +on: + push: + branches: + - trunk + paths: + # Only run when changes are made to workflow files. + - '.github/workflows/**' + pull_request: + branches: + - trunk + paths: + # Only run when changes are made to workflow files. + - '.github/workflows/**' + workflow_dispatch: + +# Cancels all previous workflow runs for pull requests that have not completed. +concurrency: + # The concurrency group contains the workflow name and the branch name for pull requests + # or the commit hash for any other events. + group: ${{ github.workflow }}-${{ github.event_name == 'pull_request' && github.head_ref || github.sha }} + cancel-in-progress: true + +# Disable permissions for all available scopes by default. +# Any needed permissions should be configured at the job level. +permissions: {} + +jobs: + lint: + name: Lint GitHub Action files + permissions: + security-events: write + actions: read + contents: read + uses: ./.github/workflows/reusable-workflow-lint.yml diff --git a/.gitignore b/.gitignore index 9e7e4333af8689..95a0bd7ead37d5 100644 --- a/.gitignore +++ b/.gitignore @@ -53,3 +53,7 @@ test/gutenberg-test-themes/twentytwentyone test/gutenberg-test-themes/twentytwentythree test/gutenberg-test-themes/twentytwentyfour packages/react-native-editor/src/setup-local.js + +# Files related to applying patches +*.rej +*.orig diff --git a/.stylelintrc.js b/.stylelintrc.js new file mode 100644 index 00000000000000..12d1050cfbace8 --- /dev/null +++ b/.stylelintrc.js @@ -0,0 +1,52 @@ +/** @type {import('stylelint').Config} */ +module.exports = { + extends: '@wordpress/stylelint-config/scss-stylistic', + rules: { + 'at-rule-empty-line-before': null, + 'at-rule-no-unknown': null, + 'comment-empty-line-before': null, + 'declaration-property-value-allowed-list': [ + { + 'flex-direction': '/^(?!(row|column)-reverse).*$/', + }, + { + message: ( property, value ) => + `Avoid "${ value }" value for the "${ property }" property. For accessibility reasons, visual, reading, and DOM order must match. Only use the reverse values when they do not affect reading order, meaning, and interaction.`, + }, + ], + 'declaration-property-value-disallowed-list': [ + { + '/.*/': [ '/--wp-components-color-/' ], + }, + { + message: ( property, value ) => + `Avoid using "${ value }" in "${ property }". --wp-components-color-* variables are not ready to be used outside of the components package.`, + }, + ], + 'font-weight-notation': null, + '@stylistic/max-line-length': null, + 'no-descending-specificity': null, + 'property-disallowed-list': [ + [ 'order' ], + { + message: + 'Avoid the order property. For accessibility reasons, visual, reading, and DOM order must match. Only use the order property when it does not affect reading order, meaning, and interaction.', + }, + ], + 'rule-empty-line-before': null, + 'selector-class-pattern': null, + 'value-keyword-case': null, + 'scss/operator-no-unspaced': null, + 'scss/selector-no-redundant-nesting-selector': null, + 'scss/load-partial-extension': null, + 'scss/no-global-function-names': null, + 'scss/comment-no-empty': null, + 'scss/at-extend-no-missing-placeholder': null, + 'scss/operator-no-newline-after': null, + 'scss/at-if-closing-brace-newline-after': null, + 'scss/at-else-empty-line-before': null, + 'scss/at-if-closing-brace-space-after': null, + 'no-invalid-position-at-import-rule': null, + }, + reportDescriptionlessDisables: true, +}; diff --git a/.stylelintrc.json b/.stylelintrc.json deleted file mode 100644 index 557376e02c4062..00000000000000 --- a/.stylelintrc.json +++ /dev/null @@ -1,48 +0,0 @@ -{ - "extends": "@wordpress/stylelint-config/scss-stylistic", - "rules": { - "at-rule-empty-line-before": null, - "at-rule-no-unknown": null, - "comment-empty-line-before": null, - "declaration-property-value-allowed-list": [ - { - "flex-direction": "/^(?!(row|column)-reverse).*$/" - }, - { - "message": "Avoid the flex-direction reverse values. For accessibility reasons, visual, reading, and DOM order must match. Only use the reverse values when they do not affect reading order, meaning, and interaction." - } - ], - "declaration-property-value-disallowed-list": [ - { - "/.*/": [ "/--wp-components-color-/" ] - }, - { - "message": "--wp-components-color-* variables are not ready to be used outside of the components package." - } - ], - "font-weight-notation": null, - "@stylistic/max-line-length": null, - "no-descending-specificity": null, - "property-disallowed-list": [ - [ "order" ], - { - "message": "Avoid the order property. For accessibility reasons, visual, reading, and DOM order must match. Only use the order property when it does not affect reading order, meaning, and interaction." - } - ], - "rule-empty-line-before": null, - "selector-class-pattern": null, - "value-keyword-case": null, - "scss/operator-no-unspaced": null, - "scss/selector-no-redundant-nesting-selector": null, - "scss/load-partial-extension": null, - "scss/no-global-function-names": null, - "scss/comment-no-empty": null, - "scss/at-extend-no-missing-placeholder": null, - "scss/operator-no-newline-after": null, - "scss/at-if-closing-brace-newline-after": null, - "scss/at-else-empty-line-before": null, - "scss/at-if-closing-brace-space-after": null, - "no-invalid-position-at-import-rule": null - }, - "reportDescriptionlessDisables": true -} diff --git a/README.md b/README.md index 87db481b5b6b4f..5008a39beb35e3 100644 --- a/README.md +++ b/README.md @@ -25,7 +25,7 @@ Check out the [Keeping up with Gutenberg Index](https://make.wordpress.org/core/ ## Getting Started -Get hands on: check out the [block editor live demo](https://wordpress.org/gutenberg/) to play with a test instance of the editor. +Get hands-on: check out the [block editor live demo](https://wordpress.org/gutenberg/) to play with a test instance of the editor. ### Using Gutenberg diff --git a/backport-changelog/6.9/8770.md b/backport-changelog/6.9/8770.md new file mode 100644 index 00000000000000..9346719b02ef22 --- /dev/null +++ b/backport-changelog/6.9/8770.md @@ -0,0 +1,3 @@ +https://github.com/WordPress/wordpress-develop/pull/8770 + +* https://github.com/WordPress/gutenberg/pull/69971 diff --git a/backport-changelog/6.9/9268.md b/backport-changelog/6.9/9268.md new file mode 100644 index 00000000000000..127eb50707e65a --- /dev/null +++ b/backport-changelog/6.9/9268.md @@ -0,0 +1,3 @@ +https://github.com/WordPress/wordpress-develop/pull/9268 + +* https://github.com/WordPress/gutenberg/pull/70676 \ No newline at end of file diff --git a/changelog.txt b/changelog.txt index 839fac53122066..58eae0e1c3ee88 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,7 +1,1020 @@ == Changelog == -= 20.6.0-rc.1 = += 21.3.0-rc.1 = +## Changelog + +### Features + +#### DataViews +- Add groupByField support to grid layout. ([70752](https://github.com/WordPress/gutenberg/pull/70752)) + +#### Components +- Add ValidateControls components. ([70620](https://github.com/WordPress/gutenberg/pull/70620)) + + +### Enhancements + +- DataViews: Add `date` field type. ([70657](https://github.com/WordPress/gutenberg/pull/70657)) +- wp-env: Add wp-cli configuration when creating environment. ([70661](https://github.com/WordPress/gutenberg/pull/70661)) + +#### Block Library +- Cover: Enable support for adding posters over video. ([70816](https://github.com/WordPress/gutenberg/pull/70816)) +- Navigation Submenu Link: Add `Open in new tab` toggle to navigation block sidebar. ([70687](https://github.com/WordPress/gutenberg/pull/70687)) +- Post Content Block: Add tagName selector. ([70698](https://github.com/WordPress/gutenberg/pull/70698)) +- Video: Enable support for adding multiple tracks. ([70689](https://github.com/WordPress/gutenberg/pull/70689)) +- Site tagline: update block description. ([34473](https://github.com/WordPress/gutenberg/pull/34473)) + +#### Write mode +- Allow add before/after on block toolbar menu dropdown when in write mode. ([70827](https://github.com/WordPress/gutenberg/pull/70827)) +- Remove cover block content position and full height controls from write mode. ([70826](https://github.com/WordPress/gutenberg/pull/70826)) + +#### Block API +- Add attribute role-based filtering to 'isUnmodifiedBlock'. ([70764](https://github.com/WordPress/gutenberg/pull/70764)) + +#### List View +- Package blocks: Introduce a utility for getting the block image. ([68910](https://github.com/WordPress/gutenberg/pull/68910)) + +#### Layout +- Adds max limit to row span and column span in grid. ([64895](https://github.com/WordPress/gutenberg/pull/64895)) + + +### Bug Fixes + +- Update @tannin/sprintf to resolve CommonJS error. ([70799](https://github.com/WordPress/gutenberg/pull/70799)) +- wp-env: Don't overwrite wp-cli.yml if it already exists. ([70677](https://github.com/WordPress/gutenberg/pull/70677)) +- wp-env: Fix Docker build errors with Debian Buster repositories. ([70718](https://github.com/WordPress/gutenberg/pull/70718)) + +#### Block Library +- Archives: Update padding for archives block to remain consistent with `list`. ([69008](https://github.com/WordPress/gutenberg/pull/69008)) +- Fix: Preserve hasParallax when switching from video to image in Cover block. ([70703](https://github.com/WordPress/gutenberg/pull/70703)) +- Media & Text: Fixed resetAll to return image resolution to default value. ([70683](https://github.com/WordPress/gutenberg/pull/70683)) +- Navigation Block: Fix inconsistent padding in nested submenus when "Open on click" is enabled. ([70471](https://github.com/WordPress/gutenberg/pull/70471)) +- Navigation Link: Fix URL deletion in Inspector Controls. ([70749](https://github.com/WordPress/gutenberg/pull/70749)) +- Separator: Surfacing the HTML markup control. ([70455](https://github.com/WordPress/gutenberg/pull/70455)) +- Sever Nav Link item entity link on URL change if ID already exists. ([68143](https://github.com/WordPress/gutenberg/pull/68143)) +- Video: Fix track editor state saves without explicitly applying the changes. ([70628](https://github.com/WordPress/gutenberg/pull/70628)) +- [Block] Cover: Fix resize handler gets stuck on undo. ([70820](https://github.com/WordPress/gutenberg/pull/70820)) + +#### Block Editor +- Block Editor Components: Add missing style resets for fieldset elements. ([70685](https://github.com/WordPress/gutenberg/pull/70685)) +- Inserter: Fix focus style for disabled items. ([70691](https://github.com/WordPress/gutenberg/pull/70691)) +- Inserter: Restore styling for disabled blocks. ([70663](https://github.com/WordPress/gutenberg/pull/70663)) + +#### Global Styles +- Core/File: Reduce specificity of pseudo-selector styles for better override support via theme.json. ([70358](https://github.com/WordPress/gutenberg/pull/70358)) +- Make Button element inherit all typography styles on the frontend. ([70676](https://github.com/WordPress/gutenberg/pull/70676)) + +#### Components +- Update Icon component to support bundled dataviews package. ([70756](https://github.com/WordPress/gutenberg/pull/70756)) + +#### CSS & Styling +- Block Lock Modal: Add ul browser defaults CSS resets. ([70700](https://github.com/WordPress/gutenberg/pull/70700)) + +#### Interactivity API +- iAPI Router: Preserve media attribute on intial style sheets after client-side navigation. ([70668](https://github.com/WordPress/gutenberg/pull/70668)) + +#### Font Library +- Fix: Font Library Modal: Focus outline not displaying correctly. ([70667](https://github.com/WordPress/gutenberg/pull/70667)) + +#### Post Editor +- Match front-page link style to admin styles. ([70600](https://github.com/WordPress/gutenberg/pull/70600)) + + +### Accessibility + +#### Block Editor +- Autocomplete: Prevent text cursor position loss when clicking to insert an item. ([70660](https://github.com/WordPress/gutenberg/pull/70660)) +- Components: Fix label and placeholder handling in `LinkControlSearchInput`. ([70096](https://github.com/WordPress/gutenberg/pull/70096)) + +#### Block Library +- a11y: Comments Pagination Nav Wrapper. ([70730](https://github.com/WordPress/gutenberg/pull/70730)) + + +### Performance + +- core-data: Try using cached permissions even when `_fields` is present in the query. ([70738](https://github.com/WordPress/gutenberg/pull/70738)) + +#### Block Editor +- Avoid unbound requests in inserter selectors. ([66817](https://github.com/WordPress/gutenberg/pull/66817)) +- Deprecate block hovered global state. ([70731](https://github.com/WordPress/gutenberg/pull/70731)) + +#### Block Library +- Fix: Avoid duplicate `get_the_title()` calls in `render_block_core_post_featured_image()`. ([70813](https://github.com/WordPress/gutenberg/pull/70813)) + +#### List View +- Delay block highlighting when moving the mouse over the items. ([70732](https://github.com/WordPress/gutenberg/pull/70732)) + + +### Documentation + +- Deprecated: Fix code example. ([70781](https://github.com/WordPress/gutenberg/pull/70781)) +- Docs: Update RichTextToolbarButton link in documentation. ([70804](https://github.com/WordPress/gutenberg/pull/70804)) +- Docs: Update WP Studio to WordPress Studio. ([70716](https://github.com/WordPress/gutenberg/pull/70716)) + + +### Code Quality + +- Convert packages to TypeScript: + - Migrate `@packages/url` package to TypeScript. ([70496](https://github.com/WordPress/gutenberg/pull/70496)) + - Migrate `hooks` package to TypeScript. ([70488](https://github.com/WordPress/gutenberg/pull/70488)) + - Migrate `lazy-import` package to `TypeScript`. ([70475](https://github.com/WordPress/gutenberg/pull/70475)) + - Migrate `redux-routine` package to TS. ([70686](https://github.com/WordPress/gutenberg/pull/70686)) + - Migrate plugins package to TS. ([70773](https://github.com/WordPress/gutenberg/pull/70773)) + - Migrate Icons package to `TypeScript`. ([70456](https://github.com/WordPress/gutenberg/pull/70456)) + - TypeScript: Fix types for core entity meta. ([70788](https://github.com/WordPress/gutenberg/pull/70788)) + - TypeScript: Migrate `block-serialization-default-parser` to TS. (#70507). ([70766](https://github.com/WordPress/gutenberg/pull/70766)) + - TypeScript: Migrate wordcount package to TypeScript. ([70501](https://github.com/WordPress/gutenberg/pull/70501)) + +#### Block Library +- Navigation Link: Add `opensInNewTab` to resetAll. ([70717](https://github.com/WordPress/gutenberg/pull/70717)) +- Video: Fix 'MediaUploadCheck' wrapper for Poster control. ([70830](https://github.com/WordPress/gutenberg/pull/70830)) +- Video: Reuse logic for uploaded track selection. ([70675](https://github.com/WordPress/gutenberg/pull/70675)) + +#### DataViews +- Improve data in DataViews storybook. ([70817](https://github.com/WordPress/gutenberg/pull/70817)) +- filterSortAndPaginate: Extract sortBy and groupBy logic. ([70822](https://github.com/WordPress/gutenberg/pull/70822)) + +#### Components +- Reuse existing `useControlledValue` hook in Calendar components. ([70655](https://github.com/WordPress/gutenberg/pull/70655)) +- SelectControl: Move classnames to root. ([70643](https://github.com/WordPress/gutenberg/pull/70643)) +- `DateCalendar`, `DateRangeCalendar`: Move to private APIs. ([70681](https://github.com/WordPress/gutenberg/pull/70681)) + +#### Layout +- Dimensions support: Fix duplicate output of styling rules. ([70796](https://github.com/WordPress/gutenberg/pull/70796)) + +#### Block API +- Blocks: Set default 'apiVersion' for client-side registration. ([70750](https://github.com/WordPress/gutenberg/pull/70750)) + +#### List View +- Remove public APIs for getting images. ([70705](https://github.com/WordPress/gutenberg/pull/70705)) + +### Tools + +- Improve plugin release checklist and documentation. ([70808](https://github.com/WordPress/gutenberg/pull/70808)) +- Improving Gitignore for files related to applying patches. ([70745](https://github.com/WordPress/gutenberg/pull/70745)) +- Update codeowners for `packages/dataviews`. ([70753](https://github.com/WordPress/gutenberg/pull/70753)) +- Update gutenberg release documentation. ([70772](https://github.com/WordPress/gutenberg/pull/70772)) +- Sync package-lock.json on latest NPM. ([70839](https://github.com/WordPress/gutenberg/pull/70839)) + +## Contributors + +The following contributors merged PRs in this release: + +@Adi-ty @aduth @andrewserong @carolinan @chihsuan @ciampo @DAreRodz @dhruvikpatel18 @dmsnell @EliezerSPP @ellatrix @getdave @gigitux @im3dabasia @Infinite-Null @jeryj @jsnajdr @kellychoffman @Mamaduka @manzoorwanijk @mukeshpanchal27 @n2erjo00 @ndiego @oandregal @prasadkarmalkar @priethor @shimotmk @shrivastavanolo @SirLouen @t-hamano @talldan @vipul0425 @yogeshbhutkar @youknowriad + + += 21.2.0 = + +## Changelog + +### Enhancements + +- DataViews: Introduce `perPageSizes` to control the available sizes of the items per page. ([70604](https://github.com/WordPress/gutenberg/pull/70604)) +- ESLint: Enforce stricter translator comment placeholder matching. ([70458](https://github.com/WordPress/gutenberg/pull/70458)) +- Format Library: Improve inline image replacement workflow. ([70598](https://github.com/WordPress/gutenberg/pull/70598)) +- I18N: Use `@tannin/sprintf`. ([70434](https://github.com/WordPress/gutenberg/pull/70434)) + +#### Block Library +- Gallery: Refactor "Settings" panel of Gallery block to use `ToolsPanel` instead of `PanelBody`. ([67904](https://github.com/WordPress/gutenberg/pull/67904)) +- Navigation Link: Add "Open in new tab" toggle to navigation block sidebar. ([67262](https://github.com/WordPress/gutenberg/pull/67262)) +- Site Logo: Refactor the media panel to use ToolsPanel. ([70599](https://github.com/WordPress/gutenberg/pull/70599)) + +#### Icons +- Update circle icons to have a unified footprint. ([70581](https://github.com/WordPress/gutenberg/pull/70581)) +- Update icons: Help, PlusCircle, Typography. ([70299](https://github.com/WordPress/gutenberg/pull/70299)) + +#### Block Editor +- Use consistent sizing for the block locking modal. ([70593](https://github.com/WordPress/gutenberg/pull/70593)) + +#### Post Editor +- Editor: Allow replacing the 'PostLockedModal' component. ([70586](https://github.com/WordPress/gutenberg/pull/70586)) + +#### Components +- Add `DateCalendar` and `DateRangeCalendar` components. ([70578](https://github.com/WordPress/gutenberg/pull/70578)) + +#### DataViews +- More fields, controls and operators. ([70567](https://github.com/WordPress/gutenberg/pull/70567)) + +#### Interactivity API +- iAPI Router: Prioritize custom click event handlers when full-page navigation is enabled. ([70566](https://github.com/WordPress/gutenberg/pull/70566)) + +#### Templates API +- Site Editor: Sort post types alphabetically within the `Add Template` modal. ([70562](https://github.com/WordPress/gutenberg/pull/70562)) + + +### Bug Fixes + +- iAPI Router: Preserve media attribute on intial style sheets after client-side navigation (#70668) +- wp-env: Fix Docker build errors with Debian Buster repositories (#70718) +- Block Editor: Avoid unbound requests in inserter selectors (#66817) +#### Block Library +- Cover: Fixed resetAll to return image resolution to default value. ([70608](https://github.com/WordPress/gutenberg/pull/70608)) +- Featured Image: Fixed resetAll to return image resolution to default value. ([70514](https://github.com/WordPress/gutenberg/pull/70514)) +- Fix submenu border visibility in Navigation block. ([70520](https://github.com/WordPress/gutenberg/pull/70520)) +- Image Block: Preserve line breaks in media caption. ([70476](https://github.com/WordPress/gutenberg/pull/70476)) +- RSS Block: Decode HTML entities in feed titles before display. ([70491](https://github.com/WordPress/gutenberg/pull/70491)) + +#### Icons +- Fix plusCircleFilled size. ([70650](https://github.com/WordPress/gutenberg/pull/70650)) + +#### DataViews +- Correct padding for empty-state elements when is in a . ([70638](https://github.com/WordPress/gutenberg/pull/70638)) + +#### Components +- Remove the extra font-size override for the select control on medium layouts. ([70619](https://github.com/WordPress/gutenberg/pull/70619)) + +#### Style Book +- Enable support for showing individual block variations. ([70448](https://github.com/WordPress/gutenberg/pull/70448)) + + +### Accessibility + +#### Components +- `FormTokenField`: Fix focus lost on tab when `__experimentalExpandOnFocus` is set. ([70591](https://github.com/WordPress/gutenberg/pull/70591)) + + +### Performance + +- usePostActions: Avoid fetching all templates (unbound request). ([70535](https://github.com/WordPress/gutenberg/pull/70535)) + +#### Block Library +- Post Author: Optimize and improve user control queries. ([70510](https://github.com/WordPress/gutenberg/pull/70510)) + + +### Documentation + +- Add `isPostAutosavingLocked` example to doc block. ([70640](https://github.com/WordPress/gutenberg/pull/70640)) +- Docs: Update Locking interface example image. ([70590](https://github.com/WordPress/gutenberg/pull/70590)) +- Media Upload: Correct wrapper reference in docstring. ([70651](https://github.com/WordPress/gutenberg/pull/70651)) +- RawHTML: Clarify the wrapper 'div' element behavior. ([70527](https://github.com/WordPress/gutenberg/pull/70527)) +- ServerSideRender: Remove the 'POST' method shim example. ([70577](https://github.com/WordPress/gutenberg/pull/70577)) +- Update nested-blocks-inner-blocks.md. ([70587](https://github.com/WordPress/gutenberg/pull/70587)) +- Updated support for MS Word Images. ([70564](https://github.com/WordPress/gutenberg/pull/70564)) + + +### Code Quality + +- I18n: Use more descriptive type arguments in i18n package. ([70606](https://github.com/WordPress/gutenberg/pull/70606)) +- ServerSideRender: Remove unnecessary props for default loading placeholder. ([70576](https://github.com/WordPress/gutenberg/pull/70576)) +- ServerSideRender: Use data hooks instead of HoC. ([70529](https://github.com/WordPress/gutenberg/pull/70529)) + +#### Block Editor +- Background panel: Remove unused props. ([70573](https://github.com/WordPress/gutenberg/pull/70573)) +- Background panel: Remove unused style. ([70556](https://github.com/WordPress/gutenberg/pull/70556)) + +#### Block Library +- List Block: Remove unused __unstableMultilineWrapperTags field from block.json. ([70209](https://github.com/WordPress/gutenberg/pull/70209)) + +#### Components +- Fix Floating UI packages dependencies. ([70553](https://github.com/WordPress/gutenberg/pull/70553)) + + +### Tools + +#### Testing +- CodeVitals: Use a different SHA for the base branch. ([70565](https://github.com/WordPress/gutenberg/pull/70565)) +- Fix unit test for flaky filterSortAndPaginate function. ([70582](https://github.com/WordPress/gutenberg/pull/70582)) +- e2e: Re-enable skipped end-to-end tests related to the settings REST API. ([70551](https://github.com/WordPress/gutenberg/pull/70551)) + + +## First-time contributors + +The following PRs were merged by first-time contributors: + +- @Adi-ty: e2e: Re-enable skipped end-to-end tests related to the settings REST API. ([70551](https://github.com/WordPress/gutenberg/pull/70551)) +- @chihsuan: Components: Add `DateCalendar` and `DateRangeCalendar` components. ([70578](https://github.com/WordPress/gutenberg/pull/70578)) +- @coderGtm: ([70482](https://github.com/WordPress/gutenberg/pull/70482)) +- @mecskyverse: Updated support for MS Word Images. ([70564](https://github.com/WordPress/gutenberg/pull/70564)) +- @R1shabh-Gupta: Fix submenu border visibility in Navigation block. ([70520](https://github.com/WordPress/gutenberg/pull/70520)) +- @USERSATOSHI: ESLint: Enforce stricter translator comment placeholder matching. ([70458](https://github.com/WordPress/gutenberg/pull/70458)) + + +## Contributors + +The following contributors merged PRs in this release: + +@Adi-ty @arthur791004 @BugReportOnWeb @chihsuan @coderGtm @DAreRodz @ellatrix @himanshupathak95 @Infinite-Null @jameskoster @jasmussen @Lovor01 @Mamaduka @mecskyverse @p-jackson @R1shabh-Gupta @sejas @shimotmk @sirreal @Sukhendu2002 @swissspidy @t-hamano @USERSATOSHI @yogeshbhutkar @youknowriad + + + + += 21.1.1 = + + +## Changelog + +### Bug Fixes + +#### Media +- Media library modal: Fix plugin incompatibilities with MediaUpload filter. ([70648](https://github.com/WordPress/gutenberg/pull/70648)) + + + + +## Contributors + +The following contributors merged PRs in this release: + +@talldan + + += 21.1.0 = + +## Changelog + +### Enhancements + +#### Block Library +- Author Block: Refactor Settings panel to use Toolspanel. ([67965](https://github.com/WordPress/gutenberg/pull/67965)) +- Avatar: Refactor settings panel to use ToolsPanel. ([67952](https://github.com/WordPress/gutenberg/pull/67952)) +- Comments Pagination: Remove unwanted bottom margin from links. ([70360](https://github.com/WordPress/gutenberg/pull/70360)) +- Navigation Block: Flip submenu indicator icon on submenu expansion. ([70307](https://github.com/WordPress/gutenberg/pull/70307)) +- Navigation Block: Rotate submenu indicator icons on submenu expansion. ([70442](https://github.com/WordPress/gutenberg/pull/70442)) +- Navigation Link: Add `dropdownMenuProps` and a `resetAll` function. ([70505](https://github.com/WordPress/gutenberg/pull/70505)) +- Post Navigation Link: Refactor settings panel to use ToolsPanel. ([70276](https://github.com/WordPress/gutenberg/pull/70276)) +- Refactor Site Logo "Settings" Panel to Use ToolsPanel. ([67972](https://github.com/WordPress/gutenberg/pull/67972)) +- [ Experimental Form ]: Add example block previews. ([70436](https://github.com/WordPress/gutenberg/pull/70436)) + +#### Interactivity API +- iAPI Router: Add support for new router regions with `attachTo`. ([70421](https://github.com/WordPress/gutenberg/pull/70421)) +- iAPI Router: Support new styles and script modules on client-side navigation. ([70353](https://github.com/WordPress/gutenberg/pull/70353)) +- iAPI: Export `NavigateOptions` and `PrefetchOptions` types. ([70315](https://github.com/WordPress/gutenberg/pull/70315)) +- iAPI: Introduce `AsyncAction` and `TypeYield` type helpers. ([70422](https://github.com/WordPress/gutenberg/pull/70422)) + +#### Extensibility +- Social: Allow custom link icons using block variations. ([70261](https://github.com/WordPress/gutenberg/pull/70261)) + + +### Bug Fixes + +- Core data: getHomePage: Do not return object until resolved. ([70345](https://github.com/WordPress/gutenberg/pull/70345)) + +#### Block Library +- Fix : Calendar block: Colors do not change between global styles and `theme.json`. ([70184](https://github.com/WordPress/gutenberg/pull/70184)) +- Form Block: Apply class names correctly in the block editor. ([70394](https://github.com/WordPress/gutenberg/pull/70394)) +- FormFileUpload: Extend audio accept MIME types for iOS compatibility. ([70354](https://github.com/WordPress/gutenberg/pull/70354)) +- Image: Fix outdated accessibility hint on native component. ([70346](https://github.com/WordPress/gutenberg/pull/70346)) +- Image: Fixed resetAll to return image resolution to default value. ([70398](https://github.com/WordPress/gutenberg/pull/70398)) +- Revert "Flip submenu indicator icon on submenu expansion (#70307)". ([70427](https://github.com/WordPress/gutenberg/pull/70427)) +- Social Links: Allow icon size to be reset and honor theme.json styles. ([70380](https://github.com/WordPress/gutenberg/pull/70380)) +- Video Block: Guard against duplicate tracks. ([70295](https://github.com/WordPress/gutenberg/pull/70295)) + +#### Interactivity API +- Fix image lightbox issues in new full client-side navigation logic. ([70416](https://github.com/WordPress/gutenberg/pull/70416)) +- iAPI Router: Fix dynamic imports on new visited pages. ([70489](https://github.com/WordPress/gutenberg/pull/70489)) +- iAPI: Fix captured errors in `withScope` generators. ([70303](https://github.com/WordPress/gutenberg/pull/70303)) +- iAPI: Fix duplicated nested regions. ([70302](https://github.com/WordPress/gutenberg/pull/70302)) +- iAPI: Fix parsing of comments without siblings. ([70304](https://github.com/WordPress/gutenberg/pull/70304)) + +#### Global Styles +- Fix: Global styles affect all form elements ( Form Block ). ([70392](https://github.com/WordPress/gutenberg/pull/70392)) +- Implement uniform header layout. ([70464](https://github.com/WordPress/gutenberg/pull/70464)) + +#### DataViews +- Data forms: Achieve vertical spacing with vertical spacing rather than cell padding. ([70435](https://github.com/WordPress/gutenberg/pull/70435)) + +#### Post Editor +- Fix: Update post-publish panel to use taxonomy label instead of hardcoded "Tags". ([70410](https://github.com/WordPress/gutenberg/pull/70410)) + +#### Media +- Invalidate entities when new media is uploaded. ([70405](https://github.com/WordPress/gutenberg/pull/70405)) + +#### Plugin +- Fix: Incorrect style handle in RTL style registration for wp-list-reusable-blocks. ([70402](https://github.com/WordPress/gutenberg/pull/70402)) + +#### Data Layer +- Add private selector support to resolveSelect and suspendSelect. ([52036](https://github.com/WordPress/gutenberg/pull/52036)) + + +### Accessibility + +#### Templates API +- Templates: Add back button & fix focus loss when navigating through template creation flow. ([70091](https://github.com/WordPress/gutenberg/pull/70091)) + + +### Performance + +#### Block Library +- Avatar: Optimize user control query. ([70511](https://github.com/WordPress/gutenberg/pull/70511)) + + +### Documentation + +- Add `isPostSavingLocked ` example to doc block. ([70370](https://github.com/WordPress/gutenberg/pull/70370)) +- Commands: Add stylesheet requirements to README. ([70323](https://github.com/WordPress/gutenberg/pull/70323)) +- Create Block: Add documentation for custom block namespace. ([70215](https://github.com/WordPress/gutenberg/pull/70215)) +- Docs: Fix broken links on Developer.WordPress.org. ([70473](https://github.com/WordPress/gutenberg/pull/70473)) +- Fix some typos. ([70419](https://github.com/WordPress/gutenberg/pull/70419)) +- Fix typo folder-structure.md. ([70375](https://github.com/WordPress/gutenberg/pull/70375)) +- block-attributes: Add `role` attribute explanation. ([70361](https://github.com/WordPress/gutenberg/pull/70361)) + + +### Code Quality + +- Patterns: Delete unused property. ([70321](https://github.com/WordPress/gutenberg/pull/70321)) +- TypeScript: Converts HtmlEntities Package to TS. ([69326](https://github.com/WordPress/gutenberg/pull/69326)) +- TypeScript: Migrate deprecated package to TS. ([70362](https://github.com/WordPress/gutenberg/pull/70362)) +- TypeScript: Migrate is-shallow-equal package to TS. ([70407](https://github.com/WordPress/gutenberg/pull/70407)) +- iAPI: Fix the changelog to include PR 70296 in 6.25. ([70309](https://github.com/WordPress/gutenberg/pull/70309)) + +#### Block Editor +- Media List: Use 'placement' prop for popover positioning. ([70350](https://github.com/WordPress/gutenberg/pull/70350)) +- Refactor: `URLInputButton` component to functional react component. ([70384](https://github.com/WordPress/gutenberg/pull/70384)) +- Warning: Replace popoverProps.position to popoverProps.placement. ([70347](https://github.com/WordPress/gutenberg/pull/70347)) + +#### Components +- Circular Option Picker: Use 'placement' prop for popover positioning. ([70359](https://github.com/WordPress/gutenberg/pull/70359)) +- FormFileUpload: Remove temporary fix for selecting .heic file in Chromium browsers. ([70383](https://github.com/WordPress/gutenberg/pull/70383)) + +#### Post Editor +- PostAuthorCombobox: Simplify 'onFilterValueChange' debounced callback. ([70512](https://github.com/WordPress/gutenberg/pull/70512)) + +#### Interactivity API +- iAPI: Minor fixes to the Interactivity and Interactivity Router comments. ([70420](https://github.com/WordPress/gutenberg/pull/70420)) + +#### Block Library +- Social Link: Use `placement` prop for popover positioning. ([70348](https://github.com/WordPress/gutenberg/pull/70348)) + + +### Tools + +#### Testing +- Test: Improve document settings sidebar locator. ([70331](https://github.com/WordPress/gutenberg/pull/70331)) +- Ensure the actual tests environment is used for end-to-end tests. ([70280](https://github.com/WordPress/gutenberg/pull/70280)) +- Skip falling end-to-end tests. ([70460](https://github.com/WordPress/gutenberg/pull/70460)) +- end-to-end Tests: Add test case for `Link` color support. ([69141](https://github.com/WordPress/gutenberg/pull/69141)) +- end-to-end Tests: Minor code quality improvement in some tests. ([70382](https://github.com/WordPress/gutenberg/pull/70382)) + + +### Various + +#### Block Library +- Remove screencast.com embed block variation. ([70480](https://github.com/WordPress/gutenberg/pull/70480)) + + +## First-time contributors + +The following PRs were merged by first-time contributors: + +- @alejandrogonzalvo: test: Improve document settings sidebar locator. ([70331](https://github.com/WordPress/gutenberg/pull/70331)) +- @BugReportOnWeb: FormFileUpload: Extend audio accept MIME types for iOS compatibility. ([70354](https://github.com/WordPress/gutenberg/pull/70354)) +- @iamsandeepdahiya: Fix: Incorrect style handle in RTL style registration for wp-list-reusable-blocks. ([70402](https://github.com/WordPress/gutenberg/pull/70402)) +- @kushagra-goyal-14: TypeScript: Migrate is-shallow-equal package to TS. ([70407](https://github.com/WordPress/gutenberg/pull/70407)) +- @NidhiDhandhukiya74: Refactor Site Logo "Settings" Panel to Use ToolsPanel. ([67972](https://github.com/WordPress/gutenberg/pull/67972)) +- @shrivastavanolo: Navigation Block: Flip submenu indicator icon on submenu expansion. ([70307](https://github.com/WordPress/gutenberg/pull/70307)) +- @USERSATOSHI: TypeScript: Migrate deprecated package to TS. ([70362](https://github.com/WordPress/gutenberg/pull/70362)) +- @yashjawale: Image: Fix outdated accessibility hint on native component. ([70346](https://github.com/WordPress/gutenberg/pull/70346)) + + +## Contributors + +The following contributors merged PRs in this release: + +@alejandrogonzalvo @BugReportOnWeb @DAreRodz @dd32 @ellatrix @himanshupathak95 @iamsandeepdahiya @im3dabasia @Infinite-Null @jsnajdr @karthikeya-io @kushagra-goyal-14 @Maikuolan @Mamaduka @Mayank-Tripathi32 @NidhiDhandhukiya74 @peterwilsoncc @ramonjd @Rishit30G @SainathPoojary @SantosGuillamot @shimotmk @shrivastavanolo @t-hamano @talldan @USERSATOSHI @vk17-starlord @Vrishabhsk @westonruter @yashjawale @yogeshbhutkar + + + + += 21.0.0 = + +## Changelog + +### Enhancements + +#### Block Library +- Button Block: Add HTML Element selection in Advanced settings. ([70139](https://github.com/WordPress/gutenberg/pull/70139)) +- Comment Date: Convert Comment Date block settings to ToolsPanel. ([70248](https://github.com/WordPress/gutenberg/pull/70248)) +- Comment Author Name Block: Refactor setting panel to use ToolsPanel. ([69407](https://github.com/WordPress/gutenberg/pull/69407)) +- Comment Edit Link: Refactor settings panel to use ToolsPanel. ([70247](https://github.com/WordPress/gutenberg/pull/70247)) +- Comment Title: Refactor settings panel to use ToolsPanel. ([70246](https://github.com/WordPress/gutenberg/pull/70246)) +- Comments Pagination Block: Refactor settings panel to use ToolsPanel. ([70245](https://github.com/WordPress/gutenberg/pull/70245)) +- Embed Block: Refactor setting panel to use ToolsPanel. ([69636](https://github.com/WordPress/gutenberg/pull/69636)) +- File Block: Refactor setting panel to use ToolsPanel. ([70189](https://github.com/WordPress/gutenberg/pull/70189)) +- Form Input: Convert Form Input block settings to ToolsPanel. ([70249](https://github.com/WordPress/gutenberg/pull/70249)) +- Form: Convert Form block settings to ToolsPanel. ([70253](https://github.com/WordPress/gutenberg/pull/70253)) +- Latest posts: Change panel body to tools panel. ([70200](https://github.com/WordPress/gutenberg/pull/70200)) +- List Block: Refactor setting panel to use ToolsPanel. ([69387](https://github.com/WordPress/gutenberg/pull/69387)) +- Navigation Link, Navigation Submenu: Hide title UI controls while keeping attribute support. ([70234](https://github.com/WordPress/gutenberg/pull/70234)) +- Navigation: Refactor display panel to use ToolsPanel. ([68011](https://github.com/WordPress/gutenberg/pull/68011)) +- Post Title: Refactor settings panel to use ToolsPanel. ([70229](https://github.com/WordPress/gutenberg/pull/70229)) +- RSS: Refactor setting panel to use `ToolsPanel`. ([70213](https://github.com/WordPress/gutenberg/pull/70213)) +- Separator: Change html element option visibility. ([70185](https://github.com/WordPress/gutenberg/pull/70185)) +- Shortcode: Add role attribute to content in `block.json`. ([70164](https://github.com/WordPress/gutenberg/pull/70164)) +- Video: Add option to set a track as default. ([70227](https://github.com/WordPress/gutenberg/pull/70227)) + +#### Components +- ComboboxControl: Handle Unicode characters when matching values. ([70180](https://github.com/WordPress/gutenberg/pull/70180)) +- DropZone: Allow overriding the default icon. ([70236](https://github.com/WordPress/gutenberg/pull/70236)) + +#### Block Transforms +- Adds transforms functionality to Post comments link and post comments number. ([70287](https://github.com/WordPress/gutenberg/pull/70287)) + + +### Bug Fixes + +#### Block Library +- Button: Avoid empty block control slot. ([70191](https://github.com/WordPress/gutenberg/pull/70191)) +- Fix: Click-through blur effect when gallery images have a caption. ([69067](https://github.com/WordPress/gutenberg/pull/69067)) +- Pullquote block: Cannot override cite element style via theme.json. ([70260](https://github.com/WordPress/gutenberg/pull/70260)) +- Social Link: Localize social service names for translations. ([70199](https://github.com/WordPress/gutenberg/pull/70199)) +- Social: Moves size settings to inspector controls. ([70186](https://github.com/WordPress/gutenberg/pull/70186)) + +#### Components +- Ensure SVG Images Without a Width Attribute Are Displayed Correctly in FocalPointPicker. ([70061](https://github.com/WordPress/gutenberg/pull/70061)) +- FormTokenField: Fix filtering with full-width string. ([70232](https://github.com/WordPress/gutenberg/pull/70232)) + +#### Interactivity API +- iAPI: Make storePart argument optional in overloads. ([70296](https://github.com/WordPress/gutenberg/pull/70296)) + +#### Post Editor +- Editor: Better normalize strings for hierarchical term filtering. ([70178](https://github.com/WordPress/gutenberg/pull/70178)) + +#### Block Editor +- Flex Layout: Fix incorrect default alignment values for `Vertical Alignment Control` component. ([68866](https://github.com/WordPress/gutenberg/pull/68866)) + +### Accessibility + +#### Block Library +- Button: Avoid focus loss when unlinking using keyboard. ([70192](https://github.com/WordPress/gutenberg/pull/70192)) +- Columns block: Don't use ToolsPanelItem for Columns setting. ([70210](https://github.com/WordPress/gutenberg/pull/70210)) +- Details Block: Fix keyboard accessibility issues and allow list view selection to show up inner blocks. ([70056](https://github.com/WordPress/gutenberg/pull/70056)) + +#### Components +- Toolbar: Adjust colors for dark mode support. ([69278](https://github.com/WordPress/gutenberg/pull/69278)) + +### Experiments + +#### Interactivity API +- iAPI: Remove the full-page client-side navigation experiment. ([70228](https://github.com/WordPress/gutenberg/pull/70228)) + +### Documentation + +- Docs: Added the missing ALT attribute to all images. ([70225](https://github.com/WordPress/gutenberg/pull/70225)) +- Docs: Remove misleading section from curating the editor experience. ([70262](https://github.com/WordPress/gutenberg/pull/70262)) +- Fix parameter hint for `canInsertBlocks`. ([70305](https://github.com/WordPress/gutenberg/pull/70305)) +- Fix: Render_block_core_site_title function doc has missing return type. ([70269](https://github.com/WordPress/gutenberg/pull/70269)) +- Improve default ALT attribute value for images in documentation. ([70226](https://github.com/WordPress/gutenberg/pull/70226)) +- JSON schema: Clean reference URLs. ([70274](https://github.com/WordPress/gutenberg/pull/70274)) +- Removed Empty line and corrected typo in inline document. ([70203](https://github.com/WordPress/gutenberg/pull/70203)) +- Replace “sidebar” with “panel” in tutorial.md. ([70257](https://github.com/WordPress/gutenberg/pull/70257)) +- Small grammar fix in glossary.md. ([70292](https://github.com/WordPress/gutenberg/pull/70292)) +- The wp_admin_notice() function should be called instead of custom HTML for admin notice in docs/how-to-guides/notices/README.md. ([70231](https://github.com/WordPress/gutenberg/pull/70231)) +- Updated small typo in inline documentation. ([70187](https://github.com/WordPress/gutenberg/pull/70187)) +- `useDropZone`: Refactor documentation to use the correct function syntax. ([70286](https://github.com/WordPress/gutenberg/pull/70286)) +- `block.json` schema: Add role field. ([70272](https://github.com/WordPress/gutenberg/pull/70272)) + + +### Code Quality + +#### Block Editor +- Clarify 'clientId' prop use for 'HTMLElementControl'. ([70258](https://github.com/WordPress/gutenberg/pull/70258)) +- ImageSizeControl: Use margin-free style. ([70202](https://github.com/WordPress/gutenberg/pull/70202)) + +#### Components +- Remove forced focus of `InputControl` on mousedown. ([41118](https://github.com/WordPress/gutenberg/pull/41118)) +- SuggestionsList: Remove unused code. ([70195](https://github.com/WordPress/gutenberg/pull/70195)) + +#### Block Library +- Video: Use a unique 'key' in the 'TrackList' component. ([70263](https://github.com/WordPress/gutenberg/pull/70263)) + +### Tools + +#### Testing +- Cover Block: Add `E2E` Test for `FocalPointPicker`. ([69077](https://github.com/WordPress/gutenberg/pull/69077)) +- E2E: Add general end-to-end tests for `templateLock` functionality. ([70160](https://github.com/WordPress/gutenberg/pull/70160)) +- Fix end-to-end test for block API / filtered blocks. ([51882](https://github.com/WordPress/gutenberg/pull/51882)) +- Format `.github` workflows `.yml` file. ([70219](https://github.com/WordPress/gutenberg/pull/70219)) +- Account for tests env port being overridden from 8889 in PHP unit tests. ([70267](https://github.com/WordPress/gutenberg/pull/70267)) + + +## First-time contributors + +The following PRs were merged by first-time contributors: + +- @catgofire: Small grammar fix in glossary.md. ([70292](https://github.com/WordPress/gutenberg/pull/70292)) +- @Mukulsingh27: Pullquote block: Cannot override cite element style via theme.json. ([70260](https://github.com/WordPress/gutenberg/pull/70260)) +- @nani-samireddy: Comments Pagination Block: Refactor settings panel to use ToolsPanel. ([70245](https://github.com/WordPress/gutenberg/pull/70245)) +- @OpuRockey: Fix: Render_block_core_site_title function doc has missing return type. ([70269](https://github.com/WordPress/gutenberg/pull/70269)) +- @ravigadhiya007: Docs: Added the missing ALT attribute to all images. ([70225](https://github.com/WordPress/gutenberg/pull/70225)) + + +## Contributors + +The following contributors merged PRs in this release: + +@aurooba @catgofire @DAreRodz @dhruvikpatel18 @himanshupathak95 @im3dabasia @Infinite-Null @Mamaduka @Mayank-Tripathi32 @Mukulsingh27 @nani-samireddy @OpuRockey @pmbaldha @ravigadhiya007 @Rishit30G @SainathPoojary @shail-mehta @shimotmk @stokesman @t-hamano @troychaplin @vipul0425 @vk17-starlord @westonruter @yogeshbhutkar + + += 20.9.0 = + + +## Changelog + +### Enhancements + +- Base Styles: Update to modern Sass module system. ([70135](https://github.com/WordPress/gutenberg/pull/70135)) +- Post Publish Panel: Open view post links in a new tab. ([70127](https://github.com/WordPress/gutenberg/pull/70127)) +- Revert Admin bar: Update the Edit Site link. ([69974](https://github.com/WordPress/gutenberg/pull/69974)) + +#### Block Library +- Custom HTML: Add role attribute to content in `block.json`. ([70154](https://github.com/WordPress/gutenberg/pull/70154)) +- HTML Block: Remove space below textarea. ([70055](https://github.com/WordPress/gutenberg/pull/70055)) +- Post Navigation Link: Reverse order of variations. ([70149](https://github.com/WordPress/gutenberg/pull/70149)) +- RSS: Add role attribute to content in `block.json`. ([70166](https://github.com/WordPress/gutenberg/pull/70166)) +- Search Block: Move search setting to inspector. ([70144](https://github.com/WordPress/gutenberg/pull/70144)) + +#### Write mode +- Details block: Add role attribute to summary. ([70124](https://github.com/WordPress/gutenberg/pull/70124)) +- Enable Code block content editing in contentOnly. ([70111](https://github.com/WordPress/gutenberg/pull/70111)) +- Enable More block's "Read more" text editing in contentOnly mode. ([70100](https://github.com/WordPress/gutenberg/pull/70100)) +- Post blocks: Enhance contentOnly editing experience. ([70148](https://github.com/WordPress/gutenberg/pull/70148)) +- Table Block: Enhance contentOnly Editing Experience. ([70114](https://github.com/WordPress/gutenberg/pull/70114)) + +#### Components +- ColorPicker: Add a visual cue when the value is copied. ([70083](https://github.com/WordPress/gutenberg/pull/70083)) +- Snackbar: Add support to open links in new tab. ([69905](https://github.com/WordPress/gutenberg/pull/69905)) + +#### Block Transforms +- Block Library: Add missing transforms between Verse and Preformatted blocks. ([70101](https://github.com/WordPress/gutenberg/pull/70101)) + +#### Global Styles +- Remove "global" from styles variation in schema descriptions. ([70089](https://github.com/WordPress/gutenberg/pull/70089)) + +#### Site Editor +- Move `Styles` to top of sidebar navigation. ([68582](https://github.com/WordPress/gutenberg/pull/68582)) + + +### Bug Fixes + +- DOM: Add summary element to focusable elements. ([70051](https://github.com/WordPress/gutenberg/pull/70051)) +- Site Editor: Fix PHP warnings in `gutenberg_get_site_editor_redirection`. ([70062](https://github.com/WordPress/gutenberg/pull/70062)) +- URL: Handle HTML entities and ampersand in 'cleanForSlug'. ([70078](https://github.com/WordPress/gutenberg/pull/70078)) +- Workflows: Persist git credentials when publishing packages via Lerna. ([70058](https://github.com/WordPress/gutenberg/pull/70058)) + +#### Block Library +- Button: Limit scope of width style for link. ([70054](https://github.com/WordPress/gutenberg/pull/70054)) +- Image: Provide 'defaultValue' for 'ResolutionTool' component. ([70122](https://github.com/WordPress/gutenberg/pull/70122)) +- Post Template: Don't rely on the default 'ignore_sticky' REST API value. ([70020](https://github.com/WordPress/gutenberg/pull/70020)) + +#### Components +- Fix Styling for `TextControl`and `TextAreaControl` in dark themes. ([69640](https://github.com/WordPress/gutenberg/pull/69640)) +- Fix `URLPopover` input handling to properly update value. ([70156](https://github.com/WordPress/gutenberg/pull/70156)) + +#### Post Editor +- Editor: Honor rich/code editor settings when deriving the editor mode. ([70080](https://github.com/WordPress/gutenberg/pull/70080)) +- Hide "Create template part" menu from block settings dropdown for classic themes. ([65568](https://github.com/WordPress/gutenberg/pull/65568)) + +#### Block Editor +- MediaPlaceholder: Fix potential error in 'handleBlocksDrop'. ([70090](https://github.com/WordPress/gutenberg/pull/70090)) + + +### Accessibility + +- Editor: Add label in`TextareaControl` in CollabSidebar. ([70133](https://github.com/WordPress/gutenberg/pull/70133)) + +#### Global Styles +- Background Image Panel: Fix focus loss. ([69813](https://github.com/WordPress/gutenberg/pull/69813)) +- Move `Randomize colors` button to Edit Palette panel. ([70128](https://github.com/WordPress/gutenberg/pull/70128)) + + +### Documentation + +- Add example doc comment. ([70116](https://github.com/WordPress/gutenberg/pull/70116)) +- Docs: Clarify versions in WordPress page title. ([70112](https://github.com/WordPress/gutenberg/pull/70112)) +- Docs: Fix PEG.js link. ([70082](https://github.com/WordPress/gutenberg/pull/70082)) +- Docs: Interactivity API Block removed. ([69513](https://github.com/WordPress/gutenberg/pull/69513)) +- Modify the spinner example code. ([70084](https://github.com/WordPress/gutenberg/pull/70084)) +- Popover: Update Popover component documentation. ([70106](https://github.com/WordPress/gutenberg/pull/70106)) +- RichText Readme - Fix 404 URL To Mozilla documentation. ([70171](https://github.com/WordPress/gutenberg/pull/70171)) +- Updated backticks to quotes. ([70070](https://github.com/WordPress/gutenberg/pull/70070)) + + +### Code Quality + +- ServerSideRender: Colocate delayed spinner logic. ([70147](https://github.com/WordPress/gutenberg/pull/70147)) +- Stylelint: Customise warning for `CSS` variables used outside `components` package. ([70108](https://github.com/WordPress/gutenberg/pull/70108)) +- Stylelint: Customise warning message for `flex-direction` warning. ([70057](https://github.com/WordPress/gutenberg/pull/70057)) + +#### Block Library +- Blocks: Remove TODO for post comment block conversion. ([70110](https://github.com/WordPress/gutenberg/pull/70110)) + +#### Components +- `withIgnoreIMEEvents`: Update documentation for clarity. ([70098](https://github.com/WordPress/gutenberg/pull/70098)) + +#### Font Library +- Fix missing trailing period in user message. ([70132](https://github.com/WordPress/gutenberg/pull/70132)) + + +### Tools + +#### Build Tooling +- Workflows: Add option for publishing the latest npm packages. ([70079](https://github.com/WordPress/gutenberg/pull/70079)) + +#### Testing +- end-to-end Tests: Add test suite for columns block template lock. ([69089](https://github.com/WordPress/gutenberg/pull/69089)) + + +## First-time contributors + +The following PRs were merged by first-time contributors: + +- @jasalt: Docs: Fix PEG.js link. ([70082](https://github.com/WordPress/gutenberg/pull/70082)) + + +## Contributors + +The following contributors merged PRs in this release: + +@carolinan @himanshupathak95 @igorradovanov @im3dabasia @Infinite-Null @jasalt @karthikeya-io @Mamaduka @Mayank-Tripathi32 @peterwilsoncc @SainathPoojary @shimotmk @SirLouen @StevenDufresne @Sukhendu2002 @t-hamano @unscripted @yogeshbhutkar + + + + += 20.8.0 = + +## Changelog + +### Enhancements + +- Create Block: Add `--textdomain` flag to create block tool. ([69802](https://github.com/WordPress/gutenberg/pull/69802)) + +#### Components +- Guide: Make `next` and `previous` button text customizable. ([69907](https://github.com/WordPress/gutenberg/pull/69907)) +- Popover: Add virtual padding to prevent it from hitting the viewport edge. ([69555](https://github.com/WordPress/gutenberg/pull/69555)) + +#### Document Settings +- Editor: Add search functionality to template swapping modal. ([69667](https://github.com/WordPress/gutenberg/pull/69667)) + +#### Block Editor +- Widget Editor: Don't close the inserter when focusing outside it. ([67838](https://github.com/WordPress/gutenberg/pull/67838)) + +#### Editor +- Use split view for meta boxes only when canvas is iframed and “Desktop” view. ([69958](https://github.com/WordPress/gutenberg/pull/69958)) + + +### New APIs + +#### Widgets Editor +- Edit Widgets: Save lock control via actions. ([69984](https://github.com/WordPress/gutenberg/pull/69984)) + + +### Bug Fixes + +- GitHub Actions: Fix components changelog CI check. ([70034](https://github.com/WordPress/gutenberg/pull/70034)) +- Persist git credentials when publishing via lerna. ([70007](https://github.com/WordPress/gutenberg/pull/70007)) +- Plugin: Fix deprecation for 'apply_block_hooks_to_content_from_post_object'. ([69909](https://github.com/WordPress/gutenberg/pull/69909)) +- ServerSideRender: Don't render the previous error response in loading state. ([69988](https://github.com/WordPress/gutenberg/pull/69988)) +- ServerSideRender: Refactor fetchData to use `useCallback` and `refs`. ([69237](https://github.com/WordPress/gutenberg/pull/69237)) + +#### Block Library +- Author, Author Name block: Fix PHP warning error when there is no context. ([69949](https://github.com/WordPress/gutenberg/pull/69949)) +- Block Editor: Allow editing invalid blocks as HTML. ([69902](https://github.com/WordPress/gutenberg/pull/69902)) +- Block Editor: Prevent negative width values in Spacer block when used inside a row block. ([68845](https://github.com/WordPress/gutenberg/pull/68845)) +- Calendar block: Disable edit as HTML support. ([69961](https://github.com/WordPress/gutenberg/pull/69961)) +- Image: Avoid stale URL when reselecting the same image from the library. ([69985](https://github.com/WordPress/gutenberg/pull/69985)) +- Latest Posts: Update content display labels to `Display post content` and `Content length`. ([69839](https://github.com/WordPress/gutenberg/pull/69839)) +- Navigation Link, Navigation Submenu: Fix undefined key warning. ([69951](https://github.com/WordPress/gutenberg/pull/69951)) +- fix: Update the logic of fetching current URL in loginout block. ([70031](https://github.com/WordPress/gutenberg/pull/70031)) + +#### Block Editor +- Block HTML: Add `box-sizing` property to prevent overflow. ([70014](https://github.com/WordPress/gutenberg/pull/70014)) +- Fix: Improve warning message and add spacing in HTMLElementControl. ([70002](https://github.com/WordPress/gutenberg/pull/70002)) +- MediaPlaceholder: Fix Regression with media URL input type to allow a local URL path. ([70043](https://github.com/WordPress/gutenberg/pull/70043)) +- Prevent block merging on blocks with the 'disabled' editing mode. ([69918](https://github.com/WordPress/gutenberg/pull/69918)) + +#### Components +- Don't mutate 'ALL_CSS_UNITS' default value in 'useCustomUnits'. ([70037](https://github.com/WordPress/gutenberg/pull/70037)) +- Fix double border in `ItemGroup` when last item is focused. ([70021](https://github.com/WordPress/gutenberg/pull/70021)) +- Fix: Duotone unset button. ([69981](https://github.com/WordPress/gutenberg/pull/69981)) +- ToggleGroupControl: Fix active background for empty string value. ([69969](https://github.com/WordPress/gutenberg/pull/69969)) + +#### Site Editor +- Check user permissions before rendering export menu item. ([69971](https://github.com/WordPress/gutenberg/pull/69971)) + +#### Widgets Editor +- Fix: Browser Warnings when closing keyboard shortcut modal. ([69929](https://github.com/WordPress/gutenberg/pull/69929)) + +#### Block API +- Fix: Missing conversion from json styled variable to CSS variable. ([69551](https://github.com/WordPress/gutenberg/pull/69551)) + +#### Typography +- Fix: Prevent `FontSizePicker` crash when no font size exists. ([68733](https://github.com/WordPress/gutenberg/pull/68733)) + +#### Icons +- Remove width / height attributes from svg element. ([43747](https://github.com/WordPress/gutenberg/pull/43747)) + + +### Accessibility + +- Keyboard Shortcuts: Revert delete shortcut to `access + z`. ([69931](https://github.com/WordPress/gutenberg/pull/69931)) + +#### Block Library +- Add new HTMLElementControl component. ([69904](https://github.com/WordPress/gutenberg/pull/69904)) + + +### Performance + +#### Block Library +- Latest Posts: Delay inspector control queries until the block is selected. ([69898](https://github.com/WordPress/gutenberg/pull/69898)) +- Latest Posts: Embed author in response. ([69889](https://github.com/WordPress/gutenberg/pull/69889)) + +#### Document Settings +- Lazy load post authors' data. ([69950](https://github.com/WordPress/gutenberg/pull/69950)) + + +### Documentation + +- Add WP version note for 'setAttributes' updater function. ([69972](https://github.com/WordPress/gutenberg/pull/69972)) +- Create Block: Add `--textdomain` in create-block README. ([69927](https://github.com/WordPress/gutenberg/pull/69927)) +- Docs: Add post meta limitations to Block Bindings API documentation. ([68583](https://github.com/WordPress/gutenberg/pull/68583)) +- Docs: Added copy and code for style_data. ([69920](https://github.com/WordPress/gutenberg/pull/69920)) +- Fix missing `CSS` import in data basics tutorial code. ([69914](https://github.com/WordPress/gutenberg/pull/69914)) +- Fix typos. ([69957](https://github.com/WordPress/gutenberg/pull/69957)) +- Fix: Remove an unnecessary `@return` tag from this filter docblock. ([69960](https://github.com/WordPress/gutenberg/pull/69960)) + + +### Code Quality + +- ServerSideRender: Use `useLayoutEffect` to update `latestPropsRef`. ([69999](https://github.com/WordPress/gutenberg/pull/69999)) +- Stylelint: Migrate configuration from `JSON` to `JavaScript` format. ([69590](https://github.com/WordPress/gutenberg/pull/69590)) +- theme.json: Unify property order. ([70033](https://github.com/WordPress/gutenberg/pull/70033)) + +#### Themes +- Simplify color specifications from `rgba` to `rgb`. ([70008](https://github.com/WordPress/gutenberg/pull/70008)) + +#### Block Editor +- Simplify condition selectors for 'useHasBlockToolbar'. ([69930](https://github.com/WordPress/gutenberg/pull/69930)) + +#### Block Library +- Latest Posts: Only fetch necessary fields for categories. ([69888](https://github.com/WordPress/gutenberg/pull/69888)) + +#### Components +- ColorPicker: Add tests for `ColorPicker` Alpha slider. ([69203](https://github.com/WordPress/gutenberg/pull/69203)) + + +### Tools + +#### Testing +- Automated Testing: Skip storybook smoke test. ([69943](https://github.com/WordPress/gutenberg/pull/69943)) +- List View: Add end-to-end test to verify pasting block styles via keyboard. ([69979](https://github.com/WordPress/gutenberg/pull/69979)) +- Try: Fix 'wp-polyfill' script dependents unit test. ([69912](https://github.com/WordPress/gutenberg/pull/69912)) +- Workflows: Try different SHA. ([70048](https://github.com/WordPress/gutenberg/pull/70048)) + + +## First-time contributors + +The following PRs were merged by first-time contributors: + +- @jrmd: Block Editor: Prevent block merging on blocks with the 'disabled' editing mode. ([69918](https://github.com/WordPress/gutenberg/pull/69918)) +- @Maikuolan: Navigation Link, Navigation Submenu: Fix undefined key warning. ([69951](https://github.com/WordPress/gutenberg/pull/69951)) + + +## Contributors + +The following contributors merged PRs in this release: + +@bph @Debarghya-Banerjee @georgestephanis @himanshupathak95 @im3dabasia @Infinite-Null @johnbillion @jrmd @karthikeya-io @Maikuolan @Mamaduka @Mayank-Tripathi32 @Rishit30G @SH4LIN @shimotmk @stokesman @Sukhendu2002 @t-hamano @yogeshbhutkar + + + + += 20.7.0 = + +## Changelog + +### Enhancements + +- Format Library: Add a feature to set the alpha value to Highlight. ([67525](https://github.com/WordPress/gutenberg/pull/67525)) +- Playwright end-to-end utils: Add new `emulateNetworkConditions` helper. ([69865](https://github.com/WordPress/gutenberg/pull/69865)) + +#### Block Library +- Details: Enable all non-interactive formats. ([68741](https://github.com/WordPress/gutenberg/pull/68741)) +- Heading: Add/correct typography examples. ([69812](https://github.com/WordPress/gutenberg/pull/69812)) +- Navigation in Site View: Automatically expand all options. ([69343](https://github.com/WordPress/gutenberg/pull/69343)) +- Remove experimental status from comments count and comment link blocks. ([69832](https://github.com/WordPress/gutenberg/pull/69832)) +- Table Block: Preserve Column Alignment when Pasting Markdown Tables. ([69322](https://github.com/WordPress/gutenberg/pull/69322)) +- Tag Cloud: Remove unnecessary full-width padding. ([69725](https://github.com/WordPress/gutenberg/pull/69725)) + +#### Block Editor +- Remove truncation from media tab preview tooltips. ([69741](https://github.com/WordPress/gutenberg/pull/69741)) +- Support passing updater function to 'setAttributes'. ([69709](https://github.com/WordPress/gutenberg/pull/69709)) + +#### Media +- Remove `@shopify/web-worker` Dependency from `packages/upload-media`. ([69816](https://github.com/WordPress/gutenberg/pull/69816)) + + +### Bug Fixes + +- DataViews: Ensure consistent display of primary ellipsis in list layout. ([69846](https://github.com/WordPress/gutenberg/pull/69846)) +- Scripts: Fix blocks manifest generation when directory name has space. ([69766](https://github.com/WordPress/gutenberg/pull/69766)) + +#### Block Library +- Enhance Featured Image Block: Don't include interactive elements within an element. ([69730](https://github.com/WordPress/gutenberg/pull/69730)) +- Featured Image: Don't render empty Resolution tools panel if media is not set. ([69805](https://github.com/WordPress/gutenberg/pull/69805)) +- Fix: Background wrapper for 'Button Inside' option. ([69624](https://github.com/WordPress/gutenberg/pull/69624)) +- Fix: Update the static text and make them translatable in the Blocks example. ([69859](https://github.com/WordPress/gutenberg/pull/69859)) +- Fixed Media & Text Block - Image not rendered properly on frontend when inside stack. ([68610](https://github.com/WordPress/gutenberg/pull/68610)) +- Query Loop: Don't overwrite the 'query.inherit' attribute value. ([69698](https://github.com/WordPress/gutenberg/pull/69698)) +- Query Loop: Fix 'undo trap' and improve debouncing for 'Keyword' control. ([69845](https://github.com/WordPress/gutenberg/pull/69845)) +- ToC: Fix condition for 'Limit heading levels'. ([69811](https://github.com/WordPress/gutenberg/pull/69811)) +- ToC: Fix related block attribute persistence. ([69734](https://github.com/WordPress/gutenberg/pull/69734)) + +#### Block Editor +- Block Tools: Prevent showing block toolbar when block interface is hidden. ([69795](https://github.com/WordPress/gutenberg/pull/69795)) +- Block drag & drop: Enable mouse click on input/textarea elements in Firefox. ([69781](https://github.com/WordPress/gutenberg/pull/69781)) +- Fix: Restore static properties on deprecated __experimentalLinkControl. ([69860](https://github.com/WordPress/gutenberg/pull/69860)) +- Fix: Space visualizer showing previous value. ([69747](https://github.com/WordPress/gutenberg/pull/69747)) + +#### Post Editor +- Fix: Save custom template with non-latin slug. ([69732](https://github.com/WordPress/gutenberg/pull/69732)) +- Iframe: Don't enable for block themes in core if v2 blocks exist. ([69778](https://github.com/WordPress/gutenberg/pull/69778)) + +#### Document Settings +- PostPublishPanel: Fix post title overflow. ([69804](https://github.com/WordPress/gutenberg/pull/69804)) + +#### Patterns +- Editor: Enable starter pattern modal for all post types. ([69753](https://github.com/WordPress/gutenberg/pull/69753)) + +#### Components +- UI Components: Add theme support for `FormTokenField` and `ComboboxControl`. ([69638](https://github.com/WordPress/gutenberg/pull/69638)) + + +### Accessibility + +- DataViews: Always show primary action for list layout if hover isn't supported. ([69876](https://github.com/WordPress/gutenberg/pull/69876)) + +#### Block Library +- Navigation Link, Navigation Submenu: Remove the title attribute controls. ([69689](https://github.com/WordPress/gutenberg/pull/69689)) +- Navigation block: Fix submenu Escape key behavior. ([69837](https://github.com/WordPress/gutenberg/pull/69837)) +- Social Icons: Remove custom placeholder state. ([69821](https://github.com/WordPress/gutenberg/pull/69821)) + +#### Components +- Button: Update hover styles to account for pressed state for `tertiary button`. ([68542](https://github.com/WordPress/gutenberg/pull/68542)) +- Global Styles: Fix incorrect usage of ItemGroup in the Background image panel. ([68631](https://github.com/WordPress/gutenberg/pull/68631)) + +#### DataViews +- ActionModal: Add support for customisable `focusOnMount`. ([69609](https://github.com/WordPress/gutenberg/pull/69609)) + + +### Documentation + +- Add WithCloseHandlers story. ([69688](https://github.com/WordPress/gutenberg/pull/69688)) +- Create Block: Improve support for blocks manifest and relevant Core APIs. ([69767](https://github.com/WordPress/gutenberg/pull/69767)) +- Docs: Improve document and parameters in render_block_core_* function. ([69856](https://github.com/WordPress/gutenberg/pull/69856)) +- Fix: Incorrect Type of `wp_widget_factory` in `render_block_core_legacy_widget` Doc Block. ([69775](https://github.com/WordPress/gutenberg/pull/69775)) +- Fix: Expose TypeScript types for subcomponents. ([69619](https://github.com/WordPress/gutenberg/pull/69619)) +- Latest Posts block: Add missing @global PHP documentation. ([69763](https://github.com/WordPress/gutenberg/pull/69763)) + + +### Code Quality + +- Block Parser: Remove description for singular since tag. ([69799](https://github.com/WordPress/gutenberg/pull/69799)) +- Fix Documentation inconsistencies in `Block Parser` classes. ([69785](https://github.com/WordPress/gutenberg/pull/69785)) +- Stylelint-config: Add stylelint-scss as a peer dependency. ([69685](https://github.com/WordPress/gutenberg/pull/69685)) + +#### Block Library +- Query Loop: Remove unused 'Columns' control. ([69843](https://github.com/WordPress/gutenberg/pull/69843)) +- Search: `wp-block-search__inside-wrapper` classname trailing spaces. ([69405](https://github.com/WordPress/gutenberg/pull/69405)) +- Social Icons: Fix effect dependencies. ([69824](https://github.com/WordPress/gutenberg/pull/69824)) +- Social Icons: Remove unused editor style. ([69836](https://github.com/WordPress/gutenberg/pull/69836)) + +#### Block Directory +- Update metadata fields requested from server. ([69801](https://github.com/WordPress/gutenberg/pull/69801)) + +#### Components +- Update gradient-parser dependency from 0.1.5 to 1.0.2. ([69783](https://github.com/WordPress/gutenberg/pull/69783)) + + +### Tools + +#### Testing +- Automated Testing: Add GitHub Actions workflow for Storybook smoke testing. ([69679](https://github.com/WordPress/gutenberg/pull/69679)) +- E2E: Split list view keyboard shortcuts into multiple tests. ([69231](https://github.com/WordPress/gutenberg/pull/69231)) +- Workflows: Ignore 'widgets' for manual backports. ([69792](https://github.com/WordPress/gutenberg/pull/69792)) + +#### Build Tooling +- Workflows: Ignore 'block-serialization-default-parser' for manual backports. ([69791](https://github.com/WordPress/gutenberg/pull/69791)) + + +## First-time contributors + +The following PRs were merged by first-time contributors: + +- @Debarghya-Banerjee: Fix: Incorrect Type of `wp_widget_factory` in `render_block_core_legacy_widget` Doc Block. ([69775](https://github.com/WordPress/gutenberg/pull/69775)) +- @karthikeya-io: Fix: Space visualizer showing previous value. ([69747](https://github.com/WordPress/gutenberg/pull/69747)) + + +## Contributors + +The following contributors merged PRs in this release: + +@carolinan @Debarghya-Banerjee @dhruvikpatel18 @dilipbheda @himanshupathak95 @im3dabasia @Infinite-Null @jonathanbossenger @karthikeya-io @Mamaduka @Mayank-Tripathi32 @Rishit30G @SainathPoojary @shimotmk @snehapatil2001 @stokesman @Sukhendu2002 @swissspidy @t-hamano @timse201 @yogeshbhutkar + + + + += 20.6.0 = ## Changelog @@ -142,6 +1155,8 @@ The following contributors merged PRs in this release: @bph @chrisbellboy @dhruvikpatel18 @felixarntz @gmovr @himanshupathak95 @im3dabasia @Infinite-Null @karthick-murugan @Mamaduka @Mayank-Tripathi32 @Rishit30G @SainathPoojary @shimotmk @Sourav61 @StevenDufresne @stokesman @t-hamano @westonruter @yogeshbhutkar + + = 20.5.0 = ## Changelog diff --git a/docs/assets/Locking interface.png b/docs/assets/Locking interface.png index afbc602400c047..8ada25b0279484 100644 Binary files a/docs/assets/Locking interface.png and b/docs/assets/Locking interface.png differ diff --git a/docs/contributors/code/coding-guidelines.md b/docs/contributors/code/coding-guidelines.md index e8e55921f56fae..3c3977a6a599db 100644 --- a/docs/contributors/code/coding-guidelines.md +++ b/docs/contributors/code/coding-guidelines.md @@ -354,8 +354,8 @@ privateValidateBlocks( blocks, true ); #### Private React component properties -To add an private argument to a stable component you'll need -to prepare a stable and an private version of that component. +To add a private argument to a stable component you'll need +to prepare a stable and a private version of that component. Then, export the stable function and `lock()` the unstable function inside it: diff --git a/docs/contributors/code/release.md b/docs/contributors/code/release.md index 1a3af716f5848a..b5ec50e4b68acc 100644 --- a/docs/contributors/code/release.md +++ b/docs/contributors/code/release.md @@ -4,7 +4,7 @@ The [Gutenberg repository](https://github.com/WordPress/gutenberg) on GitHub is Before you begin, there are some requirements that must be met in order to successfully release a stable version of the Gutenberg plugin. You will need to: -- Be a member of the [Gutenberg development team](https://developer.wordpress.org/block-editor/block-editor/contributors/repository-management/#teams). This gives you the ability to launch the GitHub actions that are related to the release process and to backport pull requests (PRs) to the release branch. +- Be a member of the [Gutenberg development team](https://developer.wordpress.org/block-editor/contributors/repository-management/#teams). This gives you the ability to launch the GitHub actions that are related to the release process and to backport pull requests (PRs) to the release branch. - Have write permissions on the [Make WordPress Core](https://make.wordpress.org/core) blog. This allows you to draft the release post. - Obtain approval from a member of the Gutenberg Core team in order to upload the new version Gutenberg to the WordPress.org plugin directory. @@ -12,7 +12,7 @@ Similar requirements apply to releasing WordPress's [npm packages](https://devel ## Gutenberg plugin releases -The first step in releasing a stable version of the Gutenberg plugin is to [create an issue](https://github.com/WordPress/gutenberg/issues/new?assignees=&labels=&projects=&template=New_release.md) in the Gutenberg repository. The issue template is called "Gutenberg Release," and it contains a checklist for the complete release process, from release candidate to changelog curation to cherry-picking, stable release, and release post. The issue for [Gutenberg 15.7](https://github.com/WordPress/gutenberg/issues/50092) is a good example. +The first step in releasing a stable version of the Gutenberg plugin is to [create an issue](https://github.com/WordPress/gutenberg/issues/new?template=New_release.md) in the Gutenberg repository. The issue template is called "Gutenberg Release," and it contains a checklist for the complete release process, from release candidate to changelog curation to cherry-picking, stable release, and release post. The issue for [Gutenberg 21.2](https://github.com/WordPress/gutenberg/issues/70662) is a good example. The checklist helps you coordinate with developers and other teams involved in the release process. It ensures that all of the necessary steps are completed and that everyone is aware of the schedule and important milestones. @@ -30,12 +30,12 @@ If critical bugs are discovered in stable versions of the plugin, patch versions ### Release management -Each major Gutenberg release is run by a release manager, also known as a release lead. This individual, or small team of individuals, is responsible for the release of Gutenberg with support from the broader [Gutenberg development team](https://developer.wordpress.org/block-editor/block-editor/contributors/repository-management/#teams). +Each major Gutenberg release is run by a release manager, also known as a release lead. This individual, or small team of individuals, is responsible for the release of Gutenberg with support from the broader [Gutenberg development team](https://developer.wordpress.org/block-editor/contributors/repository-management/#teams). The release manager is responsible for initiating all release activities, and their approval is required for any changes to the release plan. In the event of an emergency or if the release manager is unavailable, other team members may take appropriate action, but they should keep the release manager informed.
-If you are a member of the Gutenberg development team and are interested in leading a Gutenberg release, reach out in the #core-editor Slack channel.
+If you are a member of the Gutenberg development team and are interested in leading a Gutenberg release, reach out in the #core-editor Slack channel. ### Preparing a release @@ -126,7 +126,7 @@ Here are some additional tips for preparing clear and concise changelogs: - Move all entries under the `Various` section to a more appropriate section. - Fix spelling errors or clarify wording. Phrasing should be easy to understand where the intended audience is those who use the plugin or are keeping up with ongoing development. - Create new groupings as applicable, and move pull requests between. -- When multiple PRs relate to the same task (such as a follow-up pull request), try to combine them into a single entry. Good examples for this are PRs around removing Lodash for performance purposes, replacement of Puppeteer E2D tests with Playwright or efforts to convert public components to TypeScript. +- When multiple PRs relate to the same task (such as a follow-up pull request), try to combine them into a single entry. Good examples for this are PRs around removing Lodash for performance purposes, replacement of Puppeteer E2E tests with Playwright or efforts to convert public components to TypeScript. - If subtasks of a related set of PRs are substantial, consider organizing as entries in a nested list. - Remove PRs that revert other PRs in the same release if the net change in code is zero. - Remove all PRs that only update the mobile app. The only exception to this rule is if the mobile app pull request also updates functionality for the web. @@ -139,7 +139,7 @@ Here are some additional tips for preparing clear and concise changelogs: @@ -155,7 +155,7 @@ There are a couple of ways you might be made aware of these bugs as a release ma The cherry-picking process can be automated with the `npm run other:cherry-pick "[Insert Label]"` script, which is included in Gutenberg. You will need to use the label `Backport to Gutenberg RC` when running the command and ensure all PRs that need cherry-picking have the label assigned.
-To cherry-pick PRs, you must clone (not fork) the Gutenberg repository and have write access. Only members of the Gutenberg development team have the necessary permissions to perform this action.
+To cherry-pick PRs, you must clone (not fork) the Gutenberg repository and have write access. Only members of the Gutenberg development team have the necessary permissions to perform this action. Once you have cloned the Gutenberg repository to your local development environment, begin by switching to the release branch: @@ -217,7 +217,7 @@ Publishing the release will create a `git` tag for the version, publish the rele 1. Use the release notes that you just edited to update `changelog.txt`, and 2. Upload the new plugin version to the WordPress.org plugin repository (SVN) (only if you’re releasing a stable version). -The last step needs approval by a member of either the [Gutenberg Release](https://github.com/orgs/WordPress/teams/gutenberg-release), [Gutenberg Core](https://github.com/orgs/WordPress/teams/gutenberg-core), or the [WordPress Core](https://github.com/orgs/WordPress/teams/wordpress-core) teams. These teams get a notification email when the release is ready to be approved, but if time is of the essence, you can ask in the #core-editor Slack channel or ping the [Gutenberg Release team](https://github.com/orgs/WordPress/teams/gutenberg-release)) to accelerate the process. Reaching out before launching the release process so that somebody is ready to approve is recommended. Locate the [“Upload Gutenberg plugin to WordPress.org plugin repo” workflow](https://github.com/WordPress/gutenberg/actions/workflows/upload-release-to-plugin-repo.yml) for the new version, and have it [approved](https://docs.github.com/en/actions/managing-workflow-runs/reviewing-deployments#approving-or-rejecting-a-job). +The last step needs approval by a member of either the [Gutenberg Release](https://github.com/orgs/WordPress/teams/gutenberg-release), [Gutenberg Core](https://github.com/orgs/WordPress/teams/gutenberg-core), or the [WordPress Core](https://github.com/orgs/WordPress/teams/wordpress-core) teams. These teams get a notification email when the release is ready to be approved, but if time is of the essence, you can ask in the `#core-editor` Slack channel or ping the [Gutenberg Release team](https://github.com/orgs/WordPress/teams/gutenberg-release) to accelerate the process. Reaching out before launching the release process so that somebody is ready to approve is recommended. Locate the [“Upload Gutenberg plugin to WordPress.org plugin repo” workflow](https://github.com/WordPress/gutenberg/actions/workflows/upload-release-to-plugin-repo.yml) for the new version, and have it [approved](https://docs.github.com/en/actions/how-tos/managing-workflow-runs-and-deployments/managing-deployments/reviewing-deployments#approving-or-rejecting-a-job). Once approved, the new Gutenberg version will be available to WordPress users all over the globe. Once uploaded, confirm that the latest version can be downloaded and updated from the WordPress plugin dashboard. @@ -227,7 +227,7 @@ The final step is to write a release post on [make.wordpress.org/core](https://m > The plugin was published to the WordPress.org plugin directory but the workflow failed. -This has happened occasionally, see [this one](https://github.com/WordPress/gutenberg/actions/runs/6955409957/job/18924124118) for example. +This has happened occasionally, see [this one](https://github.com/WordPress/gutenberg/actions/runs/16325916698/job/46115920213) for example. It's important to check that: @@ -237,9 +237,9 @@ It's important to check that: - the `trunk` folder should have "Committing version X.Y.Z" - there is a new `tags/X.Y.Z` folder with the same contents as `trunk` whose latest commit is "Tagging version X.Y.Z" -Most likely, the tag folder couldn't be created. This is a [known issue](https://plugins.trac.wordpress.org/browser/gutenberg/) that [can be fixed manually](https://github.com/WordPress/gutenberg/issues/55295#issuecomment-1759292978). +Most likely, the tag folder couldn't be created. This is a [known issue](https://github.com/WordPress/gutenberg/issues/55295) that [can be fixed manually](https://github.com/WordPress/gutenberg/issues/55295#issuecomment-1759292978). -Either substitute SVN_USERNAME, SVN_PASSWORD, and VERSION for the proper values or set them as global environment variables first: +Either substitute `SVN_USERNAME`, `SVN_PASSWORD`, and `VERSION` for the proper values or set them as global environment variables first: ```sh # CHECKOUT THE REPOSITORY @@ -260,7 +260,7 @@ Ask around if you need help with any of this. ### Documenting the release -Documenting the release is led by the release manager with the help of [Gutenberg development team](https://developer.wordpress.org/block-editor/block-editor/contributors/repository-management/#teams) members. This process is comprised of a series of sequential steps that, because of the number of people involved, and the coordination required, need to adhere to a timeline between the RC and stable releases. Stable Gutenberg releases happen on Wednesdays, one week after the initial RC. +Documenting the release is led by the release manager with the help of [Gutenberg development team](https://developer.wordpress.org/block-editor/contributors/repository-management/#teams) members. This process is comprised of a series of sequential steps that, because of the number of people involved, and the coordination required, need to adhere to a timeline between the RC and stable releases. Stable Gutenberg releases happen on Wednesdays, one week after the initial RC.
Timeline @@ -279,9 +279,13 @@ Once the changelog is cleaned up, the next step is to choose a few changes to hi Given the big scope of Gutenberg and the high number of PRs merged in each milestone, it is not uncommon to overlook impactful changes worth highlighting; because of this, this step is a collaborative effort between the release manager and other Gutenberg development team members. If you don’t know what to pick, reach out to others on the team for assistance. -#### Requesting release assets +#### Release assets -After identifying the highlights of a new WordPress release, the release manager requests visual assets from the Design team. The request is made in the [#design](https://wordpress.slack.com/archives/C02S78ZAL) Slack channel, and an example post for 15.8 can be found [here](https://wordpress.slack.com/archives/C02S78ZAL/p1684161811926279). The assets will be provided in a [Google Drive folder](https://drive.google.com/drive/folders/1U8bVbjOc0MekWjpMjNaVFVhHFEzQkYLB) assigned to the specific release. +The release post has a few visual assets that need to be organized. For the post's featured image, use the same image as the previous release used. It should already be in the media library called 'gb-featured'. + +There's also a banner in the post body, which can be added via a synced pattern called 'Gutenberg What's New Banner'. Insert this pattern and update the text to the correct version number. + +The highlighted features also require visual assets. For a high profile feature you can request visual assets from the Design team. For other features you can create the assets yourself if you're comfortable. To request assets from design, reach out in the [#design](https://wordpress.slack.com/archives/C02S78ZAL) Slack channel, and an example post for 15.8 can be found [here](https://wordpress.slack.com/archives/C02S78ZAL/p1684161811926279). The assets will be provided in a [Google Drive folder](https://drive.google.com/drive/folders/1U8bVbjOc0MekWjpMjNaVFVhHFEzQkYLB) assigned to the specific release. When creating visual assets for a WordPress release, use animations (video or GIF) or static images to showcase the highlights. Use [previous release posts](https://make.wordpress.org/core/tag/gutenberg-new/) as a guide, and keep in mind that animations are better for demonstrating workflows, while more direct highlights can be shown with an image. When creating assets, avoid using copyrighted material and disable browser plugins that can be seen in the browser canvas. @@ -302,6 +306,12 @@ The author should then enable public preview on the post and ask for a final pee Finally, the post should be published after the stable version is released and is available on WordPress.org. This will help external media to echo and amplify the release. +### Call for volunteer for the next release + +After you've completed the release, post in #core-editor slack channel asking for volunteers to handle the next Gutenberg release. + +See an example of that [here](https://wordpress.slack.com/archives/C02QB2JS7/p1751595983193709). + ### Creating minor releases Occasionally it's necessary to create a minor release (i.e. X.Y.**Z**) of the Plugin. This is usually done to expedite fixes for bad regressions or bugs. The `Backport to Gutenberg Minor Release` is usually used to identify PRs that need to be included in a minor release, but as release coordinator you may also be notified more informally through slack. Even so, it's good to ensure all relevant PRs have the correct label. @@ -315,7 +325,7 @@ The method for minor releases is nearly identical to the main Plugin release pro The minor release should only contain the _specific commits_ required. To do this you should checkout the previous _major_ stable (i.e. non-RC) release branch (e.g. `release/12.5`) locally and then cherry pick any commits that you require into that branch.
-If an RC already exists for a new version, you _need_ to cherry-pick the same commits in the respective release branch, as they will not be included automatically. E.g.: If you're about to release a new minor release for 12.5 and just cherry-picked into `release/12.5`, but 12.6.0-rc.1 is already out, then you need to cherry-pick the same commits into the `release/12.6` branch, or they won't be included in subsequent releases for 12.6! Usually it's best to coordinate this process with the release coordinator for the next release. +If an RC already exists for a new version, you need to cherry-pick the same commits in the respective release branch, as they will not be included automatically. E.g.: If you're about to release a new minor release for 12.5 and just cherry-picked into release/12.5, but 12.6.0-rc.1 is already out, then you need to cherry-pick the same commits into the release/12.6 branch, or they won't be included in subsequent releases for 12.6! Usually it's best to coordinate this process with the release coordinator for the next release.
The cherry-picking process can be automated with the [`npm run cherry-pick`](/docs/contributors/code/auto-cherry-picking.md) script, but be sure to use the `Backport to Gutenberg Minor Release` label when running the script. @@ -344,7 +354,7 @@ To do this, when running the Workflow, select the appropriate `release/` branch It is possible to create a minor release for any release branch even after a more recent stable release has been published. This can be done for _any_ previous release branches, allowing more flexibility in delivering updates to users. In the past, users had to wait for the next stable release, potentially taking days. Now, fixes can be swiftly shipped to any previous release branches as required. -The process is identical to the one documented above when an RC is already out: choose a previous release branch, type `stable`, and click "Run workflow". The release will be published on the GitHub releases page for Gutenberg and to the WordPress core repository SVN as a `tag` under https://plugins.svn.wordpress.org/gutenberg/tags/. The SVN `trunk` directory will not be touched. +The process is identical to the one documented above when an RC is already out: choose a previous release branch, type `stable`, and click "Run workflow". The release will be published on the GitHub releases page for Gutenberg and to the WordPress core repository SVN as a `tag` under [https://plugins.svn.wordpress.org/gutenberg/tags/](https://plugins.svn.wordpress.org/gutenberg/tags/). The SVN `trunk` directory will not be touched. **IMPORTANT:** When publishing the draft created by the ["Build Plugin Zip" workflow](https://github.com/WordPress/gutenberg/actions/workflows/build-plugin-zip.yml), make sure to leave the "Set as last release" checkbox unchecked. If it is left checked by accident, the ["Upload Gutenberg plugin to WordPress.org plugin" workflow](https://github.com/WordPress/gutenberg/actions/workflows/upload-release-to-plugin-repo.yml) will still correctly upload it **as a tag (and will _not_ replace the `trunk` version)** to the WordPress plugin repository SVN - the workflow will perform some version arithmetic to determine how the plugin should be shipped - but you'll still need to fix the state on GitHub by setting the right release as `latest` on the [releases](https://github.com/WordPress/gutenberg/releases/) page! @@ -382,7 +392,7 @@ The Gutenberg repository follows the [WordPress SVN repository's](https://make.w Release types and their schedule: -- [Synchronizing Gutenberg Plugin](#synchronizing-gutenberg-plugin) (`latest` dist tag) – publishing happens automatically every two weeks based on the newly created `release/X.Y` (example `release/12.8`) branch with the RC1 version of the Gutenberg plugin. +- [Synchronizing Gutenberg Plugin](#synchronizing-the-gutenberg-plugin) (`latest` dist tag) – publishing happens automatically every two weeks based on the newly created `release/X.Y` (example `release/12.8`) branch with the RC1 version of the Gutenberg plugin. - [WordPress Releases](#wordpress-releases) (`wp-X.Y` dist tag, example `wp-6.2`) – publishing gets triggered on demand from the `wp/X.Y` (example `wp/6.2`) branch. Once we reach the point in the WordPress major release cycle (shortly before Beta 1) where we only cherry-pick commits from the Gutenberg repository to the WordPress core, we use `wp/X.Y` branch (created from `release/X.Y` branch, example `release/15.1`) for npm publishing with the `wp-X.Y` dist-tag. It's also possible to use older branches to backport bug or security fixes to the corresponding older versions of WordPress Core. - [Development Releases](#development-releases) (`next` dist tag) – it is also possible to perform development releases at any time when there is a need to test the upcoming changes. @@ -390,7 +400,7 @@ There is also an option to perform [Standalone Bugfix Package Releases](#standal ### Synchronizing the Gutenberg plugin -For each Gutenberg plugin release, we also publish to npm an updated version of WordPress packages. This is automated with the [Release Tool](#release-tool) that handles releases for the Gutenberg plugin. A successful RC1 release triggers the npm publishing job, and this needs to be approved by a Gutenberg Core team member. Locate the ["Build Gutenberg Plugin Zip" workflow](https://github.com/WordPress/gutenberg/actions/workflows/build-plugin-zip.yml) for the new version, and have it [approved](https://docs.github.com/en/actions/managing-workflow-runs/reviewing-deployments#approving-or-rejecting-a-job). +For each Gutenberg plugin release, we also publish to npm an updated version of WordPress packages. This is automated with the [Release Tool](https://github.com/WordPress/gutenberg/blob/trunk/.github/workflows/build-plugin-zip.yml) that handles releases for the Gutenberg plugin. A successful RC1 release triggers the npm publishing job, and this needs to be approved by a Gutenberg Core team member. Locate the ["Build Gutenberg Plugin Zip" workflow](https://github.com/WordPress/gutenberg/actions/workflows/build-plugin-zip.yml) for the new version, and have it [approved](https://docs.github.com/en/actions/how-tos/managing-workflow-runs-and-deployments/managing-deployments/reviewing-deployments#approving-or-rejecting-a-job). We deliberately update the `wp/latest` branch within the Gutenberg repo with the content from the Gutenberg release `release/X.Y` (example `release/12.7`) branch at the time of the Gutenberg RC1 release. This is done to ensure that the `wp/latest` branch is as close as possible to the latest version of the Gutenberg plugin. It also practically removes the chances of conflicts while backporting to `trunk` commits with updates applied during publishing to `package.json` and `CHANGELOG.md` files. In the past, we had many issues in that aspect when doing npm publishing after the regular Gutenberg release a week later. When publishing the new package versions to npm, we pick at least the `minor` version bump to account for future bugfix or security releases. @@ -408,7 +418,7 @@ Behind the scenes, all steps are automated via `./bin/plugin/cli.js npm-latest` 10. Run the script `npx lerna publish --no-private`. - When asked for the version numbers to choose for each package pick the values of the updated CHANGELOG files. - You'll be asked for your One-Time Password (OTP) a couple of times. This is the code from the 2FA authenticator app you use. Depending on how many packages are to be released you may be asked for more than one OTP, as they tend to expire before all packages are released. - - If the publishing process ends up incomplete (perhaps because it timed-out or an bad OTP was introduce) you can resume it via [`npx lerna publish from-package`](https://lerna.js.org/docs/features/version-and-publish#from-package). + - If the publishing process ends up incomplete (perhaps because it timed-out or a bad OTP was introduced) you can resume it via [`npx lerna publish from-package`](https://lerna.js.org/docs/features/version-and-publish#from-package). 11. Finally, now that the npm packages are published, cherry-pick the commits created by lerna ("Publish" and the CHANGELOG update) into the `trunk` branch of Gutenberg. ### WordPress releases @@ -418,8 +428,8 @@ The following workflow is needed when bug or security fixes need to be backporte - During the `beta` and `RC` periods of the WordPress release cycle when `wp/X.Y` (example `wp/5.7`) branch for the release is already present. - For WordPress minor releases and WordPress security releases (example `5.1.1`). -1. Check out the relevant WordPress major branch (If the minor release is 5.2.1, check out `wp/5.2`). -2. Create a feature branch from that branch, and cherry-pick the merge commits for the needed bug fixes onto it. The cherry-picking process can be automated with the [`npm run other:cherry-pick` script](/docs/contributors/code/auto-cherry-picking.md). +1. Check out the relevant WordPress major branch (If the minor release is `5.2.1`, check out `wp/5.2`). +2. Create a feature branch from that branch, and cherry-pick the merge commits for the needed bug fixes onto it. The cherry-picking process can be automated with the [`npm run other:cherry-pick`](/docs/contributors/code/auto-cherry-picking.md) script. 3. Create a Pull Request from this branch targeting the WordPress major branch used above. 4. Merge the Pull Request using the "Rebase and Merge" button to keep the history of the commits. @@ -427,7 +437,7 @@ Now, the `wp/X.Y` branch is ready for publishing npm packages. In order to start ![Run workflow dropdown for npm publishing](https://developer.wordpress.org/files/2023/07/image-2.png) -To publish packages to npm for the WordPress major release, select `trunk` as the branch to run the workflow from (this means that the script used to run the workflow comes from the trunk branch, though the packages themselves will published from the release branch as long as the correct "Release type" is selected below), then select `wp` from the "Release type" dropdown and enter `X.Y` (example `5.2`) in the "WordPress major release" input field. Finally, press the green "Run workflow" button. It triggers the npm publishing job, and this needs to be approved by a Gutenberg Core team member. Locate the ["Publish npm packages" action](https://github.com/WordPress/gutenberg/actions/workflows/publish-npm-packages.yml) for the current publishing, and have it [approved](https://docs.github.com/en/actions/managing-workflow-runs/reviewing-deployments#approving-or-rejecting-a-job). +To publish packages to npm for the WordPress major release, select `trunk` as the branch to run the workflow from (this means that the script used to run the workflow comes from the trunk branch, though the packages themselves will published from the release branch as long as the correct "Release type" is selected below), then select `wp` from the "Release type" dropdown and enter `X.Y` (example `5.2`) in the "WordPress major release" input field. Finally, press the green "Run workflow" button. It triggers the npm publishing job, and this needs to be approved by a Gutenberg Core team member. Locate the ["Publish npm packages" action](https://github.com/WordPress/gutenberg/actions/workflows/publish-npm-packages.yml) for the current publishing, and have it [approved](https://docs.github.com/en/actions/how-tos/managing-workflow-runs-and-deployments/managing-deployments/reviewing-deployments#approving-or-rejecting-a-job). For the record, the manual process would look like the following: @@ -473,15 +483,14 @@ Whilst waiting for the GitHub actions build for `wp/latest`[branch to pass](http 1. `git checkout wp/latest` 2. `npx lerna updated` - > Example - > - > ```shell - > npx lerna updated - > @wordpress/e2e-tests - > @wordpress/jest-preset-default - > @wordpress/scripts - > lerna success found 3 packages ready to publish - > ``` + Example: + ```shell + npx lerna updated + @wordpress/e2e-tests + @wordpress/jest-preset-default + @wordpress/scripts + lerna success found 3 packages ready to publish + ``` Check the versions listed in the current `CHANGELOG.md` file, looking through the commit history of a package e.g [@wordpress/scripts](https://github.com/WordPress/gutenberg/commits/HEAD/packages/scripts) and look out for _"chore(release): publish"_ and _"Update changelogs"_ commits to determine recent version bumps, then looking at the commits since the most recent release should aid with discovering what changes have occurred since the last release. @@ -491,7 +500,7 @@ Now, the `wp/latest` branch is ready for publishing npm packages. In order to st ![Run workflow dropdown for npm publishing](https://developer.wordpress.org/files/2023/07/image-6.png) -To publish packages to npm with bugfixes, select `bugfix` from the "Release type" dropdown and leave empty "WordPress major release" input field. Finally, press the green "Run workflow" button. It triggers the npm publishing job, and this needs to be approved by a Gutenberg Core team member. Locate the ["Publish npm packages" action](https://github.com/WordPress/gutenberg/actions/workflows/publish-npm-packages.yml) for the current publishing, and have it [approved](https://docs.github.com/en/actions/managing-workflow-runs/reviewing-deployments#approving-or-rejecting-a-job). +To publish packages to npm with bugfixes, select `bugfix` from the "Release type" dropdown and leave empty "WordPress major release" input field. Finally, press the green "Run workflow" button. It triggers the npm publishing job, and this needs to be approved by a Gutenberg Core team member. Locate the ["Publish npm packages" action](https://github.com/WordPress/gutenberg/actions/workflows/publish-npm-packages.yml) for the current publishing, and have it [approved](https://docs.github.com/en/actions/how-tos/managing-workflow-runs-and-deployments/managing-deployments/reviewing-deployments#approving-or-rejecting-a-job). Behind the scenes, the rest of the process is automated with `./bin/plugin/cli.js npm-bugfix` command. For the record, the manual process would look very close to the following steps: @@ -502,12 +511,12 @@ Behind the scenes, the rest of the process is automated with `./bin/plugin/cli.j 5. Run the script `npx lerna publish --no-private`. - When asked for the version numbers to choose for each package pick the values of the updated CHANGELOG files. - You'll be asked for your One-Time Password (OTP) a couple of times. This is the code from the 2FA authenticator app you use. Depending on how many packages are to be released you may be asked for more than one OTP, as they tend to expire before all packages are released. - - If the publishing process ends up incomplete (perhaps because it timed-out or an bad OTP was introduce) you can resume it via [`npx lerna publish from-package`](https://lerna.js.org/docs/features/version-and-publish#from-package). + - If the publishing process ends up incomplete (perhaps because it timed-out or a bad OTP was introduced) you can resume it via [`npx lerna publish from-package`](https://lerna.js.org/docs/features/version-and-publish#from-package). 6. Finally, now that the npm packages are published, cherry-pick the commits created by lerna ("Publish" and the CHANGELOG update) into the `trunk` branch of Gutenberg. ### Development releases -As noted in the [Synchronizing Gutenberg Plugin](#synchronizing-gutenberg-plugin) section, packages publishing happens every two weeks from the `wp/latest` branch. It's also possible to use the development release to test the upcoming changes present in the `trunk` branch at any time. We are taking advantage of [package distribution tags](https://docs.npmjs.com/cli/v7/commands/npm-dist-tag) that make it possible to consume the future version of the codebase according to npm guidelines: +As noted in the [Synchronizing Gutenberg Plugin](#synchronizing-the-gutenberg-plugin) section, packages publishing happens every two weeks from the `wp/latest` branch. It's also possible to use the development release to test the upcoming changes present in the `trunk` branch at any time. We are taking advantage of [package distribution tags](https://docs.npmjs.com/cli/v7/commands/npm-dist-tag) that make it possible to consume the future version of the codebase according to npm guidelines: > By default, the `latest` tag is used by npm to identify the current version of a package, and `npm install ` (without any `@` or `@` specifier) installs the `latest` tag. Typically, projects only use the `latest` tag for stable release versions, and use other tags for unstable versions such as prereleases. @@ -521,7 +530,7 @@ In order to start the publishing process for development version of npm packages ![Run workflow dropdown for npm publishing](https://developer.wordpress.org/files/2023/07/image-4.png) -To publish development packages to npm, select `development` from the "Release type" dropdown and leave empty "WordPress major release" input field. Finally, press the green "Run workflow" button. It triggers the npm publishing job, and this needs to be approved by a Gutenberg Core team member. Locate the ["Publish npm packages" action](https://github.com/WordPress/gutenberg/actions/workflows/publish-npm-packages.yml) for the current publishing, and have it [approved](https://docs.github.com/en/actions/managing-workflow-runs/reviewing-deployments#approving-or-rejecting-a-job). +To publish development packages to npm, select `development` from the "Release type" dropdown and leave empty "WordPress major release" input field. Finally, press the green "Run workflow" button. It triggers the npm publishing job, and this needs to be approved by a Gutenberg Core team member. Locate the ["Publish npm packages" action](https://github.com/WordPress/gutenberg/actions/workflows/publish-npm-packages.yml) for the current publishing, and have it [approved](https://docs.github.com/en/actions/how-tos/managing-workflow-runs-and-deployments/managing-deployments/reviewing-deployments#approving-or-rejecting-a-job). Behind the scenes, the release process is fully automated via `./bin/plugin/cli.js npm-next` command. It ensures the `wp/next` branch is synchronized with the latest release branch (`release/X.Y`) created for the Gutenberg plugin. To avoid collisions in the versioning of packages, we always include the newest commit's `sha`, for example, `@wordpress/block-editor@5.2.10-next.645224df70.0`. diff --git a/docs/contributors/folder-structure.md b/docs/contributors/folder-structure.md index e20ba81708a064..6014f28614f518 100644 --- a/docs/contributors/folder-structure.md +++ b/docs/contributors/folder-structure.md @@ -15,7 +15,7 @@ The following snippet explains how the Gutenberg repository is structured omitti ├── .eslintignore ├── .prettierrc.js ├── .stylelintignore - ├── .stylelintrc.json + ├── .stylelintrc.js ├── .markdownlintignore ├── .npmpackagejsonlintrc.json ├── phpcs.xml.dist @@ -76,8 +76,8 @@ The following snippet explains how the Gutenberg repository is structured omitti │ Set of documentation pages composing the [Block editor handbook](https://developer.wordpress.org/block-editor/). │ ├── platform-docs - │ Documentation website targeted to non WordPress developers - │ using Gutenberg in their own applications. + │ Documentation website targeted to non WordPress developers + │ using Gutenberg in their own applications. │ Deployed on [https://wordpress.org/gutenberg-framework/](https://wordpress.org/gutenberg-framework/). │ │ @@ -85,7 +85,7 @@ The following snippet explains how the Gutenberg repository is structured omitti │ PHP Source code of the Gutenberg plugin. │ ├── lib/compact/wordpress-x.x - │ PHP code that was include in WordPress ont the WordPrexx X.X version. + │ PHP code that was include in WordPress ont the WordPress X.X version. │ It is kept to ensure plugin compatibility with older WordPress versions. │ ├── packages diff --git a/docs/contributors/versions-in-wordpress.md b/docs/contributors/versions-in-wordpress.md index 751a82c5f319ff..27e8ced4a45f1c 100644 --- a/docs/contributors/versions-in-wordpress.md +++ b/docs/contributors/versions-in-wordpress.md @@ -1,4 +1,4 @@ -# Versions in WordPress +# Gutenberg versions in WordPress With each major release of WordPress a new version of Gutenberg is included. This has caused confusion over time as people have tried to figure out how to best debug problems and report bugs appropriately. To make this easier we have made this document to serve as a canonical list of the Gutenberg versions integrated into each major WordPress release. Of note, during the beta period of a WordPress release, additional bug fixes from later Gutenberg releases than those noted are added into the WordPress release where it is needed. If you want details about what's in each Gutenberg release outside of the high level items shared as part of major WordPress releases, please review the [release notes shared on Make Core](https://make.wordpress.org/core/tag/gutenberg-new/). diff --git a/docs/getting-started/devenv/README.md b/docs/getting-started/devenv/README.md index 4539dacbdf5047..adf55b11e75194 100644 --- a/docs/getting-started/devenv/README.md +++ b/docs/getting-started/devenv/README.md @@ -25,7 +25,7 @@ Node.js (`node`) is an open-source runtime environment that allows you to execut Node.js and its accompanying development tools allow you to: - Install and run WordPress packages needed for Block Editor development, such as `wp-scripts` -- Setup local WordPress environments with `wp-env` and `wp-now` +- Set up local WordPress environments with `wp-env` and `wp-now` - Use the latest ECMAScript features and write code in ESNext - Lint, format, and test JavaScript code - Scaffold custom blocks with the `create-block` package @@ -53,8 +53,8 @@ Refer to the [Get started with `wp-env`](/docs/getting-started/devenv/get-starte This list is not exhaustive, but here are several additional options to choose from if you prefer not to use `wp-env`: +- [WordPress Studio](https://developer.wordpress.com/studio/) - [Local](https://localwp.com/) -- [WP Studio](https://developer.wordpress.com/studio/) - [XAMPP](https://www.apachefriends.org/) - [MAMP](https://www.mamp.info/en/mamp/mac/) - [Varying Vagrant Vagrants](https://varyingvagrantvagrants.org/) (VVV) diff --git a/docs/getting-started/glossary.md b/docs/getting-started/glossary.md index 3c7ba7f722c445..068df51a77e59a 100644 --- a/docs/getting-started/glossary.md +++ b/docs/getting-started/glossary.md @@ -60,7 +60,7 @@ A block which embeds the TinyMCE editor as a block, TinyMCE was the base of the ## Dynamic block -A type of block where the content of which may change and cannot be determined at the time of saving a post, instead calculated any time the post is shown on the front of a site. These blocks may save fallback content or no content at all in their JavaScript implementation, instead deferring to a PHP block implementation for runtime rendering. +A type of block in which the content may change and cannot be determined at the time of saving a post, instead calculated any time the post is shown on the front of a site. These blocks may save fallback content or no content at all in their JavaScript implementation, instead deferring to a PHP block implementation for runtime rendering. ## Full Site Editing diff --git a/docs/getting-started/tutorial.md b/docs/getting-started/tutorial.md index 2a5dd979d3a569..e21ffded4df42b 100644 --- a/docs/getting-started/tutorial.md +++ b/docs/getting-started/tutorial.md @@ -150,7 +150,7 @@ Update the [`supports`](https://developer.wordpress.org/block-editor/getting-sta Note that when you enable text color support with `"text": true`, the background color is also enabled by default. You are welcome to keep it enabled, but it's not required for this tutorial, so you can manually set `"background": false`. -Save the file and select the block in the Editor. You will now see both Color and Typography panels in the Settings Sidebar. Try modifying the settings and see what happens. +Save the file and select the block in the Editor. You will now see both Color and Typography panels in the Settings Panel. Try modifying the settings and see what happens. ![The block in the Editor with block supports](https://developer.wordpress.org/files/2023/12/block-tutorial-5.png) diff --git a/docs/how-to-guides/block-tutorial/nested-blocks-inner-blocks.md b/docs/how-to-guides/block-tutorial/nested-blocks-inner-blocks.md index 3c75e1e82668f2..f935e51747fe6d 100644 --- a/docs/how-to-guides/block-tutorial/nested-blocks-inner-blocks.md +++ b/docs/how-to-guides/block-tutorial/nested-blocks-inner-blocks.md @@ -176,6 +176,8 @@ You can use a react hook called `useInnerBlocksProps` instead of the `InnerBlock The `useInnerBlocksProps` is exported from the `@wordpress/block-editor` package same as the `InnerBlocks` component itself and supports everything the component does. It also works like the `useBlockProps` hook. +It is important to note that `useBlockProps` hook must be called *before* `useInnerBlocksProps`, otherwise `useBlockProps` will return empty object. + Here is the basic `useInnerBlocksProps` hook usage. diff --git a/docs/how-to-guides/curating-the-editor-experience/disable-editor-functionality.md b/docs/how-to-guides/curating-the-editor-experience/disable-editor-functionality.md index a20d2cf9d8b8a7..81d61921873eef 100644 --- a/docs/how-to-guides/curating-the-editor-experience/disable-editor-functionality.md +++ b/docs/how-to-guides/curating-the-editor-experience/disable-editor-functionality.md @@ -24,14 +24,14 @@ You can also use [block filters](/docs/reference-guides/filters/block-filters.md ```php function example_modify_heading_levels_globally( $args, $block_type ) { - + if ( 'core/heading' !== $block_type ) { return $args; } // Remove H1, H2, and H6. $args['attributes']['levelOptions']['default'] = [ 3, 4, 5 ]; - + return $args; } add_filter( 'register_block_type_args', 'example_modify_heading_levels_globally', 10, 2 ); @@ -91,19 +91,6 @@ wp.domReady( () => { This JavaScript should be enqueued much like the block variation example above. Refer to the [block styles](https://developer.wordpress.org/block-editor/reference-guides/block-api/block-styles/) documentation for how to register and unregister styles using PHP. -## Disable access to the Template Editor - -Whether you’re using theme.json in a Classic or Block theme, you can add the following to your `functions.php` file to remove access to the Template Editor that is available when editing posts or pages: - -```php -function example_theme_support() { - remove_theme_support( 'block-templates'); -} -add_action( 'after_setup_theme', 'example_theme_support' ); -``` - -This prevents both the ability to create new block templates or edit them from within the Post Editor. - ## Disable access to the Code Editor The Code Editor allows you to view the underlying block markup for a page or post. While this view is handy for experienced users, you can inadvertently break block markup by editing content. Add the following to your `functions.php` file to restrict access. @@ -135,4 +122,4 @@ wp.domReady( () => { }); ``` -This JavaScript should be enqueued much like the block variation example above. \ No newline at end of file +This JavaScript should be enqueued much like the block variation example above. diff --git a/docs/how-to-guides/data-basics/1-data-basics-setup.md b/docs/how-to-guides/data-basics/1-data-basics-setup.md index d3208ceb352b4b..d17c3966e0ea12 100644 --- a/docs/how-to-guides/data-basics/1-data-basics-setup.md +++ b/docs/how-to-guides/data-basics/1-data-basics-setup.md @@ -19,6 +19,7 @@ Go ahead and create these files using the following snippets: ```js import { createRoot } from 'react-dom'; +import './style.css'; function MyFirstApp() { return Hello from JavaScript!; diff --git a/docs/how-to-guides/data-basics/2-building-a-list-of-pages.md b/docs/how-to-guides/data-basics/2-building-a-list-of-pages.md index 955bc1f869ad85..dbd73e06c894c0 100644 --- a/docs/how-to-guides/data-basics/2-building-a-list-of-pages.md +++ b/docs/how-to-guides/data-basics/2-building-a-list-of-pages.md @@ -2,7 +2,7 @@ In this part, we will build a filterable list of all WordPress pages. This is what the app will look like at the end of this section: -![](https://raw.githubusercontent.com/WordPress/gutenberg/HEAD/docs/how-to-guides/data-basics/media/list-of-pages/part1-finished.jpg) +![Searchable WordPress pages list](https://raw.githubusercontent.com/WordPress/gutenberg/HEAD/docs/how-to-guides/data-basics/media/list-of-pages/part1-finished.jpg) Let’s see how we can get there step by step. @@ -31,7 +31,7 @@ function PagesList( { pages } ) { Note that this component does not fetch any data yet, only presents the hardcoded list of pages. When you refresh the page, you should see the following: -![](https://raw.githubusercontent.com/WordPress/gutenberg/HEAD/docs/how-to-guides/data-basics/media/list-of-pages/simple-list.jpg) +![WordPress pages list showing Sample page](https://raw.githubusercontent.com/WordPress/gutenberg/HEAD/docs/how-to-guides/data-basics/media/list-of-pages/simple-list.jpg) ## Step 2: Fetch the data @@ -39,7 +39,7 @@ The hard-coded sample page isn’t very useful. We want to display your actual W Before we start, let’s confirm we actually have some pages to fetch. Within WPAdmin, Navigate to Pages using the sidebar menu and ensure it shows at least four or five Pages: -![](https://raw.githubusercontent.com/WordPress/gutenberg/HEAD/docs/how-to-guides/data-basics/media/list-of-pages/pages-list.jpg) +![WordPress admin Pages list](https://raw.githubusercontent.com/WordPress/gutenberg/HEAD/docs/how-to-guides/data-basics/media/list-of-pages/pages-list.jpg) If it doesn’t, go ahead and create a few pages – you can use the same titles as on the screenshot above. Be sure to _publish_ and not just _save_ them. @@ -116,7 +116,7 @@ Note that post title may contain HTML entities like `á`, so we need to us Refreshing the page should display a list similar to this one: -![](https://raw.githubusercontent.com/WordPress/gutenberg/HEAD/docs/how-to-guides/data-basics/media/list-of-pages/fetch-the-data.jpg) +![List of website pages](https://raw.githubusercontent.com/WordPress/gutenberg/HEAD/docs/how-to-guides/data-basics/media/list-of-pages/fetch-the-data.jpg) ## Step 3: Turn it into a table @@ -141,7 +141,7 @@ function PagesList( { pages } ) { } ``` -![](https://raw.githubusercontent.com/WordPress/gutenberg/HEAD/docs/how-to-guides/data-basics/media/list-of-pages/make-a-table.jpg) +![Table listing website page titles](https://raw.githubusercontent.com/WordPress/gutenberg/HEAD/docs/how-to-guides/data-basics/media/list-of-pages/make-a-table.jpg) ## Step 4: Add a search box @@ -170,7 +170,7 @@ function MyFirstApp() { Note that instead of using an `input` tag, we took advantage of the [SearchControl](https://developer.wordpress.org/block-editor/reference-guides/components/search-control/) component. This is what it looks like: -![](https://raw.githubusercontent.com/WordPress/gutenberg/HEAD/docs/how-to-guides/data-basics/media/list-of-pages/filter-field.jpg) +![Searchable list of WordPress pages](https://raw.githubusercontent.com/WordPress/gutenberg/HEAD/docs/how-to-guides/data-basics/media/list-of-pages/filter-field.jpg) The field starts empty, and the contents are stored in the `searchTerm` state value. If you aren’t familiar with the [useState](https://react.dev/reference/react/useState) hook, you can learn more in [React’s documentation](https://react.dev/reference/react/useState). @@ -241,7 +241,7 @@ function MyFirstApp() { Voila! We can now filter the results: -![](https://raw.githubusercontent.com/WordPress/gutenberg/HEAD/docs/how-to-guides/data-basics/media/list-of-pages/filter.jpg) +![Filtered WordPress pages list showing About us](https://raw.githubusercontent.com/WordPress/gutenberg/HEAD/docs/how-to-guides/data-basics/media/list-of-pages/filter.jpg) ### Using core-data instead vs calling the API directly @@ -277,7 +277,7 @@ All in all, the utilities built into core-data are designed to solve the typical There is one problem with our search feature. We can’t be quite sure whether it’s still searching or showing no results: -![](https://raw.githubusercontent.com/WordPress/gutenberg/HEAD/docs/how-to-guides/data-basics/media/list-of-pages/unclear-status.jpg) +![No matching WordPress pages found for search query](https://raw.githubusercontent.com/WordPress/gutenberg/HEAD/docs/how-to-guides/data-basics/media/list-of-pages/unclear-status.jpg) A few messages like _Loading…_ or _No results_ would clear it up. Let’s implement them! First, `PagesList` has to be aware of the current status: @@ -366,6 +366,7 @@ import { SearchControl, Spinner } from '@wordpress/components'; import { useSelect } from '@wordpress/data'; import { store as coreDataStore } from '@wordpress/core-data'; import { decodeEntities } from '@wordpress/html-entities'; +import './style.css'; function MyFirstApp() { const [ searchTerm, setSearchTerm ] = useState( '' ); @@ -439,8 +440,8 @@ window.addEventListener( All that’s left is to refresh the page and enjoy the brand new status indicator: -![](https://raw.githubusercontent.com/WordPress/gutenberg/HEAD/docs/how-to-guides/data-basics/media/list-of-pages/indicator.jpg) -![](https://raw.githubusercontent.com/WordPress/gutenberg/HEAD/docs/how-to-guides/data-basics/media/list-of-pages/no-results.jpg) +![Loading indicator shown while searching WordPress pages](https://raw.githubusercontent.com/WordPress/gutenberg/HEAD/docs/how-to-guides/data-basics/media/list-of-pages/indicator.jpg) +![No results found for page search query in WordPress](https://raw.githubusercontent.com/WordPress/gutenberg/HEAD/docs/how-to-guides/data-basics/media/list-of-pages/no-results.jpg) ## What's next? diff --git a/docs/how-to-guides/format-api.md b/docs/how-to-guides/format-api.md index 188bc1725c3633..c383222133d481 100644 --- a/docs/how-to-guides/format-api.md +++ b/docs/how-to-guides/format-api.md @@ -225,7 +225,7 @@ If you run into errors: Reference documentation used in this guide: - RichText: [`registerFormatType`](/packages/rich-text/README.md#registerformattype) -- Components: [`RichTextToolbarButton`](/packages/block-editor/src/components/rich-text#richtexttoolbarbutton) +- Components: [`RichTextToolbarButton`](/packages/block-editor/README.md#richtexttoolbarbutton) - RichText: [`applyFormat`](/packages/rich-text/README.md#applyformat) - RichText: [`removeFormat`](/packages/rich-text/README.md#removeformat) - RichText: [`toggleFormat`](/packages/rich-text/README.md#toggleformat) diff --git a/docs/how-to-guides/notices/README.md b/docs/how-to-guides/notices/README.md index 15895e2bd32131..479d44889dd2d3 100644 --- a/docs/how-to-guides/notices/README.md +++ b/docs/how-to-guides/notices/README.md @@ -24,11 +24,13 @@ function myguten_admin_notice() { return; } // Render the notice's HTML. - // Each notice should be wrapped in a
- // with a 'notice' class. - echo '

'; - echo sprintf( __( 'Post draft updated. Preview post' ), get_preview_post_link() ); - echo '

'; + wp_admin_notice( + sprintf( __( 'Post draft updated. Preview post' ), get_preview_post_link() ), + array( + 'type' => 'success', + 'dismissible' => true, + ) + ); }; add_action( 'admin_notices', 'myguten_admin_notice' ); ``` diff --git a/docs/how-to-guides/platform/custom-block-editor.md b/docs/how-to-guides/platform/custom-block-editor.md index e100820ea0e8b6..b376cc3a9569a4 100644 --- a/docs/how-to-guides/platform/custom-block-editor.md +++ b/docs/how-to-guides/platform/custom-block-editor.md @@ -4,7 +4,7 @@ The WordPress block editor is a powerful tool that allows you to create and form This package can also be used to create custom block editors for virtually any other web application. This means that you can use the same blocks and block editing experience outside of WordPress. -![alt text](https://developer.wordpress.org/files/2023/07/custom-block-editor.png 'The Standalone Editor instance populated with example Blocks within a custom WordPress admin page.') +![WordPress block editor showing content blocks and editing options](https://developer.wordpress.org/files/2023/07/custom-block-editor.png 'The Standalone Editor instance populated with example Blocks within a custom WordPress admin page.') This flexibility and interoperability makes blocks a powerful tool for building and managing content across multiple applications. It also makes it simpler for developers to create content editors that work best for their users. @@ -46,7 +46,7 @@ The custom editor is going to be built as a WordPress plugin. To keep things sim The plugin file structure will look like this: -![alt text](https://wordpress.org/gutenberg/files/2020/03/repo-files.png 'Screenshot showing file structure of the Plugin at https://github.com/getdave/standalone-block-editor.') +![Project directory listing with configuration and source files](https://wordpress.org/gutenberg/files/2020/03/repo-files.png 'Screenshot showing file structure of the Plugin at https://github.com/getdave/standalone-block-editor.') Here is a brief summary of what's going on: @@ -461,7 +461,7 @@ it display within the editor's sidebar. Exploring this component in more detail You have come a long way on your journey to create a custom block editor. But there is one major area left to touch upon - block persistence. In other words, having your blocks saved and available _between_ page refreshes. -![alt text](https://developer.wordpress.org/files/2023/07/custom-block-editor-persistance.gif 'Screencapture showing blocks being restored between page refreshes.') +![WordPress custom block editor interface with various content blocks and editing options](https://developer.wordpress.org/files/2023/07/custom-block-editor-persistance.gif 'Screencapture showing blocks being restored between page refreshes.') As this is only an _experiment_, this guide has opted to utilize the browser's `localStorage` API to handle saving block data. In a real-world scenario, you would likely choose a more reliable and robust system (e.g. a database). diff --git a/docs/manifest.json b/docs/manifest.json index 94610061e430b5..830c08c0108469 100644 --- a/docs/manifest.json +++ b/docs/manifest.json @@ -725,6 +725,18 @@ "markdown_source": "../packages/components/src/button/README.md", "parent": "components" }, + { + "title": "DateCalendar", + "slug": "date-calendar", + "markdown_source": "../packages/components/src/calendar/date-calendar/README.md", + "parent": "components" + }, + { + "title": "DateRangeCalendar", + "slug": "date-range-calendar", + "markdown_source": "../packages/components/src/calendar/date-range-calendar/README.md", + "parent": "components" + }, { "title": "CardBody", "slug": "card-body", @@ -2388,7 +2400,7 @@ "parent": "contributors" }, { - "title": "Versions in WordPress", + "title": "Gutenberg versions in WordPress", "slug": "versions-in-wordpress", "markdown_source": "../docs/contributors/versions-in-wordpress.md", "parent": "contributors" diff --git a/docs/reference-guides/block-api/block-attributes.md b/docs/reference-guides/block-api/block-attributes.md index 544f35593106f1..52f4a8709f1f05 100644 --- a/docs/reference-guides/block-api/block-attributes.md +++ b/docs/reference-guides/block-api/block-attributes.md @@ -461,3 +461,36 @@ _Example_: Example `default` values. } } ``` + +## Role + +The `role` property designates an attribute as being of a particular conceptual type. This property can be applied to any attribute to provide semantic meaning about how the attribute should be handled. + +Use `content` to designate the attribute as user-editable content. Blocks with attributes marked as `content` may be enabled for privileged editing in special circumstances such as content only locking. +Use `local` to mark the attribute as temporary and non-persistable. Attributes marked as `local` are ignored by the Block Serializer and never saved to post content. + +_Example_: `content` role used by the paragraph block + +```js +{ + content: { + type: 'string', + source: 'html', + selector: 'p', + role: 'content', + } +} +``` + +_Example_: `local` role used for temporary data. + +```js +{ + blob: { + type: 'string', + role: 'local', + } +} +``` + +Learn more in the [WordPress 6.7 dev note](https://make.wordpress.org/core/2024/10/20/miscellaneous-block-editor-changes-in-wordpress-6-7/#stabilized-role-property-for-block-attributes). \ No newline at end of file diff --git a/docs/reference-guides/block-api/block-bindings.md b/docs/reference-guides/block-api/block-bindings.md index c26ade45e8b5e3..dc4e397f1a9d42 100644 --- a/docs/reference-guides/block-api/block-bindings.md +++ b/docs/reference-guides/block-api/block-bindings.md @@ -99,6 +99,10 @@ add_action( ); ``` +
+Note: Post meta keys that begin with an underscore (e.g. `_example_key`) are protected and cannot be used with Block Bindings. Additionally, post meta must be registered with `show_in_rest = true` to be available through the Block Bindings API. +
+ #### Block bindings source value filter _**Note:** Since WordPress 6.7._ diff --git a/docs/reference-guides/block-api/block-edit-save.md b/docs/reference-guides/block-api/block-edit-save.md index a50a17b75cb54d..a00d89afdf31d7 100644 --- a/docs/reference-guides/block-api/block-edit-save.md +++ b/docs/reference-guides/block-api/block-edit-save.md @@ -135,6 +135,24 @@ const addListItem = ( newListItem ) => { Why do this? In JavaScript, arrays and objects are passed by reference, so this practice ensures changes won't affect other code that might hold references to the same data. Furthermore, the Gutenberg project follows the philosophy of the Redux library that [state should be immutable](https://redux.js.org/faq/immutable-data#what-are-the-benefits-of-immutability)—data should not be changed directly, but instead a new version of the data created containing the changes. +The `setAttribute` also supports an updater function as an argument. It must be a pure function, which takes current attributes as its only argument and returns updated attributes. This method is helpful when you want to update an value based on a previous state or when working with objects and arrays. + +_**Note:** Since WordPress 6.9._ + +```js +// Toggle a setting when the user clicks the button. +const toggleSetting = () => + setAttributes( ( currentAttr ) => ( { + mySetting: ! currentAttr.mySetting, + } ) ); + +// Add item to the list. +const addListItem = ( newListItem ) => + setAttributes( ( currentAttr ) => ( { + list: [ ...currentAttr.list, newListItem ], + } ) ); +``` + ## Save The `save` function defines the way in which the different attributes should be combined into the final markup, which is then serialized into `post_content`. diff --git a/docs/reference-guides/block-api/block-styles.md b/docs/reference-guides/block-api/block-styles.md index b47b1a76a71f68..b2e9bdc2749f36 100644 --- a/docs/reference-guides/block-api/block-styles.md +++ b/docs/reference-guides/block-api/block-styles.md @@ -64,12 +64,13 @@ The properties of the style array must include `name` and `label`: - `name`: The identifier of the style used to compute a CSS class. - `label`: A human-readable label for the style. -Besides the two mandatory properties, the styles properties array should also include an `inline_style` or a `style_handle` property: +Besides the two mandatory properties, the styles properties array should also include an `inline_style` or a `style_handle` or a `style_data`property: - `inline_style`: Contains inline CSS code that registers the CSS class required for the style. - `style_handle`: Contains the handle to an already registered style that should be enqueued in places where block styles are needed. +- `style_data`: Contains a theme.json-like notation in an array of style properties. -It is also possible to set the `is_default` property to `true` to mark one of the block styles as the default one. +It is also possible to set the `is_default` property to `true` to mark one of the block styles as the default one, should one be missing. The following code sample registers a style for the quote block named "Blue Quote", and provides an inline style that makes quote blocks with the "Blue Quote" style have blue color: @@ -102,6 +103,29 @@ register_block_style( ) ); ``` +Another way is using the `style_data`property, as in below code example. It adds a block style to the image block with an orange border and slightly rounded corners. + +```php +register_block_style( + array( 'core/image' ), + array( + 'name' => 'orange-border', + 'label' => __( 'Orange Border', 'pauli' ), + 'style_data'=> array( + 'border' => array( + 'color' => '#f5bc42', + 'style' => 'solid', + 'width' => '4px', + 'radius' => '15px' + ) + ) + ) +); +``` +Using the `style_data` property empowers the user to change it with the Global Styles UI via the **Editor > Styles**. The `style_data`property was added in WordPress 6.6. + +More information via WordPress 6.6 Dev Note: [Section Styles](https://make.wordpress.org/core/2024/06/24/section-styles/). +Also on the WordPress Developer Blog: [Styling sections, nested elements, and more with Block Style Variations in WordPress 6.6](https://developer.wordpress.org/news/2024/06/styling-sections-nested-elements-and-more-with-block-style-variations-in-wordpress-6-6/) ### unregister_block_style diff --git a/docs/reference-guides/block-api/block-templates.md b/docs/reference-guides/block-api/block-templates.md index b664867eb5b67f..60af351bdea110 100644 --- a/docs/reference-guides/block-api/block-templates.md +++ b/docs/reference-guides/block-api/block-templates.md @@ -176,7 +176,7 @@ $template = array( ) ), array( 'core/column', array(), array( array( 'core/paragraph', array( - 'placeholder' => 'Add a inner paragraph' + 'placeholder' => 'Add an inner paragraph' ) ), ) ), ) ) diff --git a/docs/reference-guides/block-api/block-transforms.md b/docs/reference-guides/block-api/block-transforms.md index 9055ed0a3b45b3..c78842f2b43c62 100644 --- a/docs/reference-guides/block-api/block-transforms.md +++ b/docs/reference-guides/block-api/block-transforms.md @@ -271,7 +271,7 @@ A transformation of type `shortcode` is an object that takes the following param - **type** _(string)_: the value `shortcode`. - **tag** _(string|array)_: the shortcode tag or list of shortcode aliases this transform can work with. - **transform** _(function, optional)_: a callback that receives the shortcode attributes as the first argument and the [WPShortcodeMatch](/packages/shortcode/README.md#next) as the second. It should return a block object or an array of block objects. When this parameter is defined, it will take precedence over the `attributes` parameter. -- **attributes** _(object, optional)_: object representing where the block attributes should be sourced from, according to the attributes shape defined by the [block configuration object](./block-registration.md). If a particular attribute contains a `shortcode` key, it should be a function that receives the shortcode attributes as the first arguments and the [WPShortcodeMatch](/packages/shortcode/README.md#next) as second, and returns a value for the attribute that will be sourced in the block's comment. +- **attributes** _(object, optional)_: object representing where the block attributes should be sourced from, according to the attributes shape defined by the [block configuration object](/docs/reference-guides/block-api/block-registration.md). If a particular attribute contains a `shortcode` key, it should be a function that receives the shortcode attributes as the first arguments and the [WPShortcodeMatch](/packages/shortcode/README.md#next) as second, and returns a value for the attribute that will be sourced in the block's comment. - **isMatch** _(function, optional)_: a callback that receives the shortcode attributes per the [Shortcode API](https://codex.wordpress.org/Shortcode_API) and should return a boolean. Returning `false` from this function will prevent the shortcode to be transformed into this block. - **priority** _(number, optional)_: controls the priority with which a transform is applied, where a lower value will take precedence over higher values. This behaves much like a [WordPress hook](https://developer.wordpress.org/reference/#Hook_to_WordPress). Like hooks, the default priority is `10` when not otherwise set. diff --git a/docs/reference-guides/core-blocks.md b/docs/reference-guides/core-blocks.md index a548e5df8f28a8..259e40e859aaf3 100644 --- a/docs/reference-guides/core-blocks.md +++ b/docs/reference-guides/core-blocks.md @@ -69,7 +69,7 @@ A calendar of your site’s posts. ([Source](https://github.com/WordPress/gutenb - **Name:** core/calendar - **Category:** widgets -- **Supports:** align, color (background, link, text), interactivity (clientNavigation), typography (fontSize, lineHeight) +- **Supports:** align, color (background, link, text), interactivity (clientNavigation), typography (fontSize, lineHeight), ~~html~~ - **Attributes:** month, year ## Terms List @@ -246,7 +246,7 @@ Add an image or video with a text overlay. ([Source](https://github.com/WordPres - **Name:** core/cover - **Category:** media - **Supports:** align, anchor, color (heading, text, ~~background~~, ~~enableContrastChecker~~), dimensions (aspectRatio), filter (duotone), interactivity (clientNavigation), layout (~~allowJustification~~), shadow, spacing (blockGap, margin, padding), typography (fontSize, lineHeight), ~~html~~ -- **Attributes:** allowedBlocks, alt, backgroundType, contentPosition, customGradient, customOverlayColor, dimRatio, focalPoint, gradient, hasParallax, id, isDark, isRepeated, isUserOverlayColor, minHeight, minHeightUnit, overlayColor, sizeSlug, tagName, templateLock, url, useFeaturedImage +- **Attributes:** allowedBlocks, alt, backgroundType, contentPosition, customGradient, customOverlayColor, dimRatio, focalPoint, gradient, hasParallax, id, isDark, isRepeated, isUserOverlayColor, minHeight, minHeightUnit, overlayColor, poster, sizeSlug, tagName, templateLock, url, useFeaturedImage ## Details @@ -586,7 +586,6 @@ This block is deprecated. Please use the Comments block instead. ([Source](https Display a post's comments count. ([Source](https://github.com/WordPress/gutenberg/tree/trunk/packages/block-library/src/post-comments-count)) - **Name:** core/post-comments-count -- **Experimental:** fse - **Category:** theme - **Supports:** color (background, gradients, text), interactivity (clientNavigation), spacing (margin, padding), typography (fontSize, lineHeight), ~~html~~ - **Attributes:** textAlign @@ -605,7 +604,6 @@ Display a post's comments form. ([Source](https://github.com/WordPress/gutenberg Displays the link to the current post comments. ([Source](https://github.com/WordPress/gutenberg/tree/trunk/packages/block-library/src/post-comments-link)) - **Name:** core/post-comments-link -- **Experimental:** fse - **Category:** theme - **Supports:** color (background, link, ~~text~~), interactivity (clientNavigation), spacing (margin, padding), typography (fontSize, lineHeight), ~~html~~ - **Attributes:** textAlign @@ -617,6 +615,7 @@ Displays the contents of a post or page. ([Source](https://github.com/WordPress/ - **Name:** core/post-content - **Category:** theme - **Supports:** align (full, wide), background (backgroundImage, backgroundSize), color (background, gradients, heading, link, text), dimensions (minHeight), layout, spacing (blockGap, margin, padding), typography (fontSize, lineHeight), ~~html~~ +- **Attributes:** tagName ## Date @@ -852,7 +851,7 @@ Display an image to represent this site. Update this block and the changes apply ## Site Tagline -Describe in a few words what the site is about. The tagline can be used in search results or when sharing on social networks even if it’s not displayed in the theme design. ([Source](https://github.com/WordPress/gutenberg/tree/trunk/packages/block-library/src/site-tagline)) +Describe in a few words what this site is about. This is important for search results, sharing on social media, and gives overall clarity to visitors. ([Source](https://github.com/WordPress/gutenberg/tree/trunk/packages/block-library/src/site-tagline)) - **Name:** core/site-tagline - **Category:** theme diff --git a/docs/reference-guides/data/data-core-block-editor.md b/docs/reference-guides/data/data-core-block-editor.md index c7ea40d3a6ff11..c6b498bf160fa1 100644 --- a/docs/reference-guides/data/data-core-block-editor.md +++ b/docs/reference-guides/data/data-core-block-editor.md @@ -39,7 +39,7 @@ Determines if the given blocks are allowed to be inserted into the block list. _Parameters_ - _state_ `Object`: Editor state. -- _clientIds_ `string`: The block client IDs to be inserted. +- _clientIds_ `string[]`: The block client IDs to be inserted. - _rootClientId_ `?string`: Optional root client ID of block list. _Returns_ @@ -564,15 +564,9 @@ _Returns_ ### getHoveredBlockClientId -Returns the currently hovered block. - -_Parameters_ - -- _state_ `Object`: Global application state. - -_Returns_ +> **Deprecated** -- `Object`: Client Id of the hovered block. +Returns the currently hovered block. ### getInserterItems @@ -1298,15 +1292,9 @@ Action that hides the insertion point. ### hoverBlock -Returns an action object used in signalling that the block with the specified client ID has been hovered. - -_Parameters_ - -- _clientId_ `string`: Block client ID. - -_Returns_ +> **Deprecated** -- `Object`: Action object. +Returns an action object used in signalling that the block with the specified client ID has been hovered. ### insertAfterBlock diff --git a/docs/reference-guides/data/data-core-edit-widgets.md b/docs/reference-guides/data/data-core-edit-widgets.md index 2dde89bf69ad73..94b4af5a9f176d 100644 --- a/docs/reference-guides/data/data-core-edit-widgets.md +++ b/docs/reference-guides/data/data-core-edit-widgets.md @@ -136,6 +136,39 @@ _Returns_ - `boolean`: True if any widget area is currently being saved. False otherwise. +### isWidgetSavingLocked + +Returns whether widget saving is locked. + +_Usage_ + +```jsx +import { __ } from '@wordpress/i18n'; +import { store as widgetStore } from '@wordpress/edit-widgets'; +import { useSelect } from '@wordpress/data'; + +const ExampleComponent = () => { + const isSavingLocked = useSelect( + ( select ) => select( widgetStore ).isWidgetSavingLocked(), + [] + ); + + return isSavingLocked ? ( +

{ __( 'Widget saving is locked' ) }

+ ) : ( +

{ __( 'Widget saving is not locked' ) }

+ ); +}; +``` + +_Parameters_ + +- _state_ `Object`: Global application state. + +_Returns_ + +- `boolean`: Is locked. + ## Actions @@ -150,6 +183,34 @@ _Returns_ - `Object`: Action creator. +### lockWidgetSaving + +Returns an action object used to signal that widget saving is locked. + +_Usage_ + +```js +import { store as widgetStore } from '@wordpress/edit-widgets'; +import { useDispatch } from '@wordpress/data'; + +const ExampleComponent = () => { + const { lockWidgetSaving } = useDispatch( widgetStore ); + return ( + + ); +}; +``` + +_Parameters_ + +- _lockName_ `string`: The lock name. + +_Returns_ + +- `Object`: Action object + ### moveBlockToWidgetArea Action that handles moving a block between widget areas @@ -270,4 +331,32 @@ _Returns_ - `Object`: Action. +### unlockWidgetSaving + +Returns an action object used to signal that widget saving is unlocked. + +_Usage_ + +```js +import { store as widgetStore } from '@wordpress/edit-widgets'; +import { useDispatch } from '@wordpress/data'; + +const ExampleComponent = () => { + const { unlockWidgetSaving } = useDispatch( widgetStore ); + return ( + + ); +}; +``` + +_Parameters_ + +- _lockName_ `string`: The lock name. + +_Returns_ + +- `Object`: Action object + diff --git a/docs/reference-guides/data/data-core-editor.md b/docs/reference-guides/data/data-core-editor.md index a9f07104bd3b6e..714aabaaab6e14 100644 --- a/docs/reference-guides/data/data-core-editor.md +++ b/docs/reference-guides/data/data-core-editor.md @@ -1006,6 +1006,27 @@ _Returns_ Returns whether post autosaving is locked. +_Usage_ + +```jsx +import { __ } from '@wordpress/i18n'; +import { store as editorStore } from '@wordpress/editor'; +import { useSelect } from '@wordpress/data'; + +const ExampleComponent = () => { + const isAutoSavingLocked = useSelect( + ( select ) => select( editorStore ).isPostAutosavingLocked(), + [] + ); + + return isAutoSavingLocked ? ( +

{ __( 'Post auto saving is locked' ) }

+ ) : ( +

{ __( 'Post auto saving is not locked' ) }

+ ); +}; +``` + _Parameters_ - _state_ `Object`: Global application state. @@ -1042,6 +1063,27 @@ _Returns_ Returns whether post saving is locked. +_Usage_ + +```jsx +import { __ } from '@wordpress/i18n'; +import { store as editorStore } from '@wordpress/editor'; +import { useSelect } from '@wordpress/data'; + +const ExampleComponent = () => { + const isSavingLocked = useSelect( + ( select ) => select( editorStore ).isPostSavingLocked(), + [] + ); + + return isSavingLocked ? ( +

{ __( 'Post saving is locked' ) }

+ ) : ( +

{ __( 'Post saving is not locked' ) }

+ ); +}; +``` + _Parameters_ - _state_ `Object`: Global application state. diff --git a/docs/reference-guides/data/data-core-notices.md b/docs/reference-guides/data/data-core-notices.md index d36098429811dd..ca71b8430285d5 100644 --- a/docs/reference-guides/data/data-core-notices.md +++ b/docs/reference-guides/data/data-core-notices.md @@ -73,7 +73,7 @@ const ExampleComponent = () => { } > { __( - 'Generate an snackbar error notice with explicit dismiss button.' + 'Generate a snackbar error notice with explicit dismiss button.' ) } ); diff --git a/docs/reference-guides/interactivity-api/README.md b/docs/reference-guides/interactivity-api/README.md index 5504f8fd19fafe..87070d6314fa76 100644 --- a/docs/reference-guides/interactivity-api/README.md +++ b/docs/reference-guides/interactivity-api/README.md @@ -115,7 +115,7 @@ Here you have some more resources to learn/read more about the Interactivity API - [Interactivity API Discussions](https://github.com/WordPress/gutenberg/discussions/52882), especially the [showcase](https://github.com/WordPress/gutenberg/discussions/55642#discussioncomment-9667164) discussions. - [wpmovies.dev](https://wpmovies.dev/) demo and its [wp-movies-demo](https://github.com/WordPress/wp-movies-demo) repo - Examples using the Interactivity API at [block-development-examples](https://github.com/WordPress/block-development-examples): - - [`interactivity-api-block-833d15`](https://github.com/WordPress/block-development-examples/tree/trunk/plugins/interactivity-api-block-833d15) + - [`my-first-interactive-block`](https://github.com/WordPress/block-development-examples/tree/trunk/plugins/my-first-interactive-block) - [`interactivity-api-countdown-3cd73e`](https://github.com/WordPress/block-development-examples/tree/trunk/plugins/interactivity-api-countdown-3cd73e) - [`interactivity-api-quiz-1835fa`](https://github.com/WordPress/block-development-examples/tree/trunk/plugins/interactivity-api-quiz-1835fa) diff --git a/docs/reference-guides/interactivity-api/core-concepts/server-side-rendering.md b/docs/reference-guides/interactivity-api/core-concepts/server-side-rendering.md index 075c2cb977bdfd..d28e8d67007ae5 100644 --- a/docs/reference-guides/interactivity-api/core-concepts/server-side-rendering.md +++ b/docs/reference-guides/interactivity-api/core-concepts/server-side-rendering.md @@ -175,7 +175,7 @@ store( 'myFruitPlugin', { The derived state, regardless of whether it derives from the global state, local context, or both, can also be processed on the server by the Server Directive Processing. -_Please, visit the [Understanding global state, local context and derived state](./undestanding-global-state-local-context-and-derived-state.md) guide to learn more about how derived state works in the Interactivity API._ +_Please, visit the [Understanding global state, local context and derived state](/docs/reference-guides/interactivity-api/core-concepts/undestanding-global-state-local-context-and-derived-state.md) guide to learn more about how derived state works in the Interactivity API._ ### Derived state that can be defined statically diff --git a/docs/reference-guides/interactivity-api/core-concepts/using-typescript.md b/docs/reference-guides/interactivity-api/core-concepts/using-typescript.md index 4e4ab7cb1038ed..256f00020f9b9d 100644 --- a/docs/reference-guides/interactivity-api/core-concepts/using-typescript.md +++ b/docs/reference-guides/interactivity-api/core-concepts/using-typescript.md @@ -269,7 +269,7 @@ That's it! Now you can access the context properties with the correct types. The derived state is data that is calculated based on the global state or local context. In the client store definition, it is defined using a getter in the `state` object. -_Please, visit the [Understanding global state, local context and derived state](./undestanding-global-state-local-context-and-derived-state.md) guide to learn more about how derived state works in the Interactivity API._ +_Please, visit the [Understanding global state, local context and derived state](/docs/reference-guides/interactivity-api/core-concepts/undestanding-global-state-local-context-and-derived-state.md) guide to learn more about how derived state works in the Interactivity API._ Following our previous example, let's create a derived state that is the double of our counter. @@ -471,7 +471,7 @@ type Store = { }; ``` -There's something to keep in mind when when using asynchronous actions. Just like with the derived state, if the asynchronous action needs to return a value and this value directly depends on some part of the global state, TypeScript will not be able to infer the type due to a circular reference. +There's something to keep in mind when using asynchronous actions. Just like with the derived state, if an asynchronous action uses `state` within a `yield` expression (for example, by passing `state` to an async function that is then yielded) or if its return value depends on `state`, TypeScript might not be able to infer the types correctly due to a potential circular reference. ```ts const { state, actions } = store( 'myCounterPlugin', { @@ -479,31 +479,76 @@ There's something to keep in mind when when using asynchronous actions. Just lik counter: 0, }, actions: { - *delayedReturn() { - yield new Promise( ( r ) => setTimeout( r, 1000 ) ); - return state.counter; // TypeScript can't infer this return type. + *delayedOperation() { + // Example: state.counter is used as part of the yielded logic. + yield fetchCounterData( state.counter ); + + // And/or the final return value depends on state. + return state.counter + 1; }, }, } ); ``` - In this case, just as we did with the derived state, we must manually type the return value of the generator. +In such cases, TypeScript might issue a warning about a circular reference or default to `any`. To solve this, you need to manually type the generator function. The Interactivity API provides a helper type, `AsyncAction`, for this purpose. ```ts + import { store, type AsyncAction } from '@wordpress/interactivity'; + const { state, actions } = store( 'myCounterPlugin', { state: { counter: 0, }, actions: { - *delayedReturn(): Generator< unknown, number, unknown > { - yield new Promise( ( r ) => setTimeout( r, 1000 ) ); - return state.counter; // Now this is correctly inferred. + *delayedOperation(): AsyncAction< number > { + // Now, this doesn't cause a circular reference. + yield fetchCounterData( state.counter ); + + // Now, this is correctly typed. + return state.counter + 1; + }, + }, + } ); + ``` + +That's it! The `AsyncAction` helper is defined as `Generator`. By using `any` for the type of values yielded by the generator, it helps break the circular reference, allowing TypeScript to correctly infer the types when `state` is involved in `yield` expressions or in the final return value. You only need to specify the final `ReturnType` of your asynchronous action. + +### Typing yielded values in asynchronous actions + +While `AsyncAction` types the overall generator and its final return value, the value resolved by an individual `yield` expression within that generator might still be typed as `any`. + +If you need to ensure the correct type for a value that a `yield` expression resolves to (e.g., the result of a `fetch` call or another async operation), you can use the `TypeYield` helper. This helper takes the type of the asynchronous function/operation being yielded (`T`) and resolves to the type of the value that the promise fulfills with. + +Suppose `fetchCounterData` returns a promise that resolves to an object: + + ```ts + import { store, type AsyncAction, type TypeYield } from '@wordpress/interactivity'; + + // Assume this function is defined elsewhere and fetches specific data. + const fetchCounterData = async ( counterValue: number ): Promise< { current: number, next: number } > => { + // internal logic... + }; + + const { state, actions } = store( 'myCounterPlugin', { + state: { + counter: 0, + }, + actions: { + *loadCounterData(): AsyncAction< void > { + // Use TypeYield to correctly type the resolved value of the yield. + const data = ( yield fetchCounterData( state.counter ) ) as TypeYield< typeof fetchCounterData >; + + // Now, `data` is correctly typed as { current: number, next: number }. + console.log( data.current, data.next ); + + // Update state based on the fetched data. + state.counter = data.next; }, }, } ); ``` - That's it! Remember that the return type of a Generator is the second generic argument: `Generator< unknown, ReturnType, unknown >`. +In this example, `( yield fetchCounterData( state.counter ) ) as TypeYield< typeof fetchCounterData >` ensures that the `data` constant is correctly typed as `{ current: number, next: number }`, matching the return type of `fetchCounterData`. This allows you to confidently access properties like `data.current` and `data.next` with type safety. ## Typing stores that are divided into multiple parts diff --git a/gutenberg.php b/gutenberg.php index f4c03c7e60bb73..6b74180c979e63 100644 --- a/gutenberg.php +++ b/gutenberg.php @@ -3,9 +3,9 @@ * Plugin Name: Gutenberg * Plugin URI: https://github.com/WordPress/gutenberg * Description: Printing since 1440. This is the development plugin for the block editor, site editor, and other future WordPress core functionality. - * Requires at least: 6.6 + * Requires at least: 6.7 * Requires PHP: 7.2 - * Version: 20.6.0-rc.1 + * Version: 21.3.0-rc.1 * Author: Gutenberg Team * Text Domain: gutenberg * @@ -15,7 +15,7 @@ ### BEGIN AUTO-GENERATED DEFINES defined( 'GUTENBERG_DEVELOPMENT_MODE' ) or define( 'GUTENBERG_DEVELOPMENT_MODE', true ); ### END AUTO-GENERATED DEFINES -defined( 'GUTENBERG_MINIMUM_WP_VERSION' ) or define( 'GUTENBERG_MINIMUM_WP_VERSION', '6.6' ); +defined( 'GUTENBERG_MINIMUM_WP_VERSION' ) or define( 'GUTENBERG_MINIMUM_WP_VERSION', '6.7' ); gutenberg_pre_init(); diff --git a/lib/block-supports/dimensions.php b/lib/block-supports/dimensions.php index 1980faba278175..008f67adeccdde 100644 --- a/lib/block-supports/dimensions.php +++ b/lib/block-supports/dimensions.php @@ -149,6 +149,7 @@ function gutenberg_render_dimensions_support( $block_content, $block ) { return $block_content; } +remove_filter( 'render_block', 'wp_render_dimensions_support', 10 ); add_filter( 'render_block', 'gutenberg_render_dimensions_support', 10, 2 ); // Register the block support. diff --git a/lib/blocks.php b/lib/blocks.php index 342cd25191e689..75ffd0a6c34a42 100644 --- a/lib/blocks.php +++ b/lib/blocks.php @@ -41,7 +41,6 @@ function gutenberg_reregister_core_block_types() { 'table-of-contents', 'text-columns', 'verse', - 'video', 'embed', ), 'block_names' => array( @@ -120,6 +119,7 @@ function gutenberg_reregister_core_block_types() { 'tag-cloud.php' => 'core/tag-cloud', 'template-part.php' => 'core/template-part', 'term-description.php' => 'core/term-description', + 'video.php' => 'core/video', ), ), __DIR__ . '/../build/edit-widgets/blocks/' => array( diff --git a/lib/client-assets.php b/lib/client-assets.php index 4a94ca212c3422..e6f64106cbb323 100644 --- a/lib/client-assets.php +++ b/lib/client-assets.php @@ -404,7 +404,7 @@ function gutenberg_register_packages_styles( $styles ) { array( 'wp-components' ), $version ); - $styles->add_data( 'wp-list-reusable-block', 'rtl', 'replace' ); + $styles->add_data( 'wp-list-reusable-blocks', 'rtl', 'replace' ); gutenberg_override_style( $styles, diff --git a/lib/compat/wordpress-6.7/block-bindings.php b/lib/compat/wordpress-6.7/block-bindings.php deleted file mode 100644 index 08608a8d394e72..00000000000000 --- a/lib/compat/wordpress-6.7/block-bindings.php +++ /dev/null @@ -1,82 +0,0 @@ - $source->name, - 'label' => $source->label, - 'usesContext' => $source->uses_context, - ); - } - $script = sprintf( 'for ( const source of %s ) { ! wp.blocks.getBlockBindingsSource( source.name ) && wp.blocks.registerBlockBindingsSource( source ); }', wp_json_encode( $filtered_sources ) ); - wp_add_inline_script( - 'wp-blocks', - $script - ); - } -} - -add_action( 'enqueue_block_editor_assets', 'gutenberg_bootstrap_server_block_bindings_sources', 5 ); - -/** - * Initialize `canUpdateBlockBindings` editor setting if it doesn't exist. By default, it is `true` only for admin users. - * - * @param array $editor_settings The block editor settings from the `block_editor_settings_all` filter. - * @return array The editor settings including `canUpdateBlockBindings`. - */ -function gutenberg_add_can_update_block_bindings_editor_setting( $editor_settings ) { - if ( empty( $editor_settings['canUpdateBlockBindings'] ) ) { - $editor_settings['canUpdateBlockBindings'] = current_user_can( 'manage_options' ); - } - return $editor_settings; -} - -add_filter( 'block_editor_settings_all', 'gutenberg_add_can_update_block_bindings_editor_setting', 10 ); - -/** - * Add `label` to `register_meta`. - * - * @param array $args Array of arguments for registering meta. - * @return array Modified arguments array including `label`. - */ -function gutenberg_update_meta_args_with_label( $args ) { - // Don't update schema when label isn't provided. - if ( ! isset( $args['label'] ) ) { - return $args; - } - - // Don't update schema if not exposed to REST - if ( ! isset( $args['show_in_rest'] ) ) { - return $args; - } - - $schema = array( 'title' => $args['label'] ); - if ( ! is_array( $args['show_in_rest'] ) ) { - $args['show_in_rest'] = array( - 'schema' => $schema, - ); - return $args; - } - - if ( ! empty( $args['show_in_rest']['schema'] ) ) { - $args['show_in_rest']['schema'] = array_merge( $args['show_in_rest']['schema'], $schema ); - } else { - $args['show_in_rest']['schema'] = $schema; - } - - return $args; -} - -// Priority must be lower than 10 to ensure the label is not removed. -add_filter( 'register_meta_args', 'gutenberg_update_meta_args_with_label', 5, 1 ); diff --git a/lib/compat/wordpress-6.7/block-templates.php b/lib/compat/wordpress-6.7/block-templates.php deleted file mode 100644 index acbd0b4981fe29..00000000000000 --- a/lib/compat/wordpress-6.7/block-templates.php +++ /dev/null @@ -1,84 +0,0 @@ -register( $template_name, $args ); - } -} - -if ( ! function_exists( 'unregister_block_template' ) ) { - /** - * Unregister a template. - * - * @param string $template_name Template name in the form of `plugin_uri//template_name`. - * @return WP_Block_Template|WP_Error The unregistered template object on success, WP_Error object on failure or if - * the template doesn't exist. - */ - function unregister_block_template( $template_name ) { - return WP_Block_Templates_Registry::get_instance()->unregister( $template_name ); - } -} - -if ( ! function_exists( 'wp_register_block_template' ) ) { - /** - * Register a template. - * - * @deprecated 19.4.0 wp_register_block_template is deprecated. Please use register_block_template instead. - * - * @param string $template_name Template name in the form of `plugin_uri//template_name`. - * @param array|string $args { - * Optional. Array or string of arguments for registering a block template. - * - * @type string $title Optional. Title of the template as it will be shown in the Site Editor - * and other UI elements. - * @type string $description Optional. Description of the template as it will be shown in the Site - * Editor. - * @type string $content Optional. Default content of the template that will be used when the - * template is rendered or edited in the editor. - * @type string[] $post_types Optional. Array of post types to which the template should be available. - * @type string $plugin Uri of the plugin that registers the template. - * } - * @return WP_Block_Template|WP_Error The registered template object on success, WP_Error object on failure. - */ - function wp_register_block_template( $template_name, $args = array() ) { - _deprecated_function( __FUNCTION__, 'Gutenberg 19.4.0', 'register_block_template' ); - return register_block_template( $template_name, $args ); - } -} - -if ( ! function_exists( 'wp_unregister_block_template' ) ) { - /** - * Unregister a template. - * - * @deprecated 19.4.0 wp_unregister_block_template is deprecated. Please use unregister_block_template instead. - * - * @param string $template_name Template name in the form of `plugin_uri//template_name`. - * @return WP_Block_Template|WP_Error The unregistered template object on success, WP_Error object on failure or if - * the template doesn't exist. - */ - function wp_unregister_block_template( $template_name ) { - _deprecated_function( __FUNCTION__, 'Gutenberg 19.4.0', 'unregister_block_template' ); - return unregister_block_template( $template_name ); - } -} diff --git a/lib/compat/wordpress-6.7/blocks.php b/lib/compat/wordpress-6.7/blocks.php deleted file mode 100644 index e739808000c5f3..00000000000000 --- a/lib/compat/wordpress-6.7/blocks.php +++ /dev/null @@ -1,139 +0,0 @@ -context['query']['format'] ) || ! is_array( $block->context['query']['format'] ) ) { - return $query; - } - - // Return early if the query already contains a post format. This is to avoid duplication if the - // WordPress core filter is already applied. - if ( ! empty( $query['tax_query'] ) ) { - foreach ( $query['tax_query'] as $taxquery ) { - if ( isset( $taxquery['taxonomy'] ) && 'post_format' === $taxquery['taxonomy'] ) { - return $query; - } - } - } - - $formats = $block->context['query']['format']; - /* - * Validate that the format is either `standard` or a supported post format. - * - First, add `standard` to the array of valid formats. - * - Then, remove any invalid formats. - */ - $valid_formats = array_merge( array( 'standard' ), get_post_format_slugs() ); - $formats = array_intersect( $formats, $valid_formats ); - - /* - * The relation needs to be set to `OR` since the request can contain - * two separate conditions. The user may be querying for items that have - * either the `standard` format or a specific format. - */ - $formats_query = array( 'relation' => 'OR' ); - - /* - * The default post format, `standard`, is not stored in the database. - * If `standard` is part of the request, the query needs to exclude all post items that - * have a format assigned. - */ - if ( in_array( 'standard', $formats, true ) ) { - $formats_query[] = array( - 'taxonomy' => 'post_format', - 'field' => 'slug', - 'operator' => 'NOT EXISTS', - ); - // Remove the `standard` format, since it cannot be queried. - unset( $formats[ array_search( 'standard', $formats, true ) ] ); - } - // Add any remaining formats to the formats query. - if ( ! empty( $formats ) ) { - // Add the `post-format-` prefix. - $terms = array_map( - static function ( $format ) { - return "post-format-$format"; - }, - $formats - ); - $formats_query[] = array( - 'taxonomy' => 'post_format', - 'field' => 'slug', - 'terms' => $terms, - 'operator' => 'IN', - ); - } - - /* - * Add `$formats_query` to `$query`, as long as it contains more than one key: - * If `$formats_query` only contains the initial `relation` key, there are no valid formats to query, - * and the query should not be modified. - */ - if ( count( $formats_query ) > 1 ) { - // Enable filtering by both post formats and other taxonomies by combining them with `AND`. - if ( empty( $query['tax_query'] ) ) { - $query['tax_query'] = $formats_query; - } else { - $query['tax_query'] = array( - 'relation' => 'AND', - $query['tax_query'], - $formats_query, - ); - } - } - - return $query; -} -add_filter( 'query_loop_block_query_vars', 'gutenberg_add_format_query_vars_to_query_loop_block', 10, 2 ); diff --git a/lib/compat/wordpress-6.7/class-gutenberg-rest-posts-controller-6-7.php b/lib/compat/wordpress-6.7/class-gutenberg-rest-posts-controller-6-7.php deleted file mode 100644 index 5d3795cf68db76..00000000000000 --- a/lib/compat/wordpress-6.7/class-gutenberg-rest-posts-controller-6-7.php +++ /dev/null @@ -1,704 +0,0 @@ - 400 ) - ); - } - - // Ensure an include parameter is set in case the orderby is set to 'include'. - if ( ! empty( $request['orderby'] ) && 'include' === $request['orderby'] && empty( $request['include'] ) ) { - return new WP_Error( - 'rest_orderby_include_missing_include', - __( 'You need to define an include parameter to order by include.' ), - array( 'status' => 400 ) - ); - } - - // Retrieve the list of registered collection query parameters. - $registered = $this->get_collection_params(); - $args = array(); - - /* - * This array defines mappings between public API query parameters whose - * values are accepted as-passed, and their internal WP_Query parameter - * name equivalents (some are the same). Only values which are also - * present in $registered will be set. - */ - $parameter_mappings = array( - 'author' => 'author__in', - 'author_exclude' => 'author__not_in', - 'exclude' => 'post__not_in', - 'include' => 'post__in', - 'menu_order' => 'menu_order', - 'offset' => 'offset', - 'order' => 'order', - 'orderby' => 'orderby', - 'page' => 'paged', - 'parent' => 'post_parent__in', - 'parent_exclude' => 'post_parent__not_in', - 'search' => 's', - 'search_columns' => 'search_columns', - 'slug' => 'post_name__in', - 'status' => 'post_status', - ); - - /* - * For each known parameter which is both registered and present in the request, - * set the parameter's value on the query $args. - */ - foreach ( $parameter_mappings as $api_param => $wp_param ) { - if ( isset( $registered[ $api_param ], $request[ $api_param ] ) ) { - $args[ $wp_param ] = $request[ $api_param ]; - } - } - - // Check for & assign any parameters which require special handling or setting. - $args['date_query'] = array(); - - if ( isset( $registered['before'], $request['before'] ) ) { - $args['date_query'][] = array( - 'before' => $request['before'], - 'column' => 'post_date', - ); - } - - if ( isset( $registered['modified_before'], $request['modified_before'] ) ) { - $args['date_query'][] = array( - 'before' => $request['modified_before'], - 'column' => 'post_modified', - ); - } - - if ( isset( $registered['after'], $request['after'] ) ) { - $args['date_query'][] = array( - 'after' => $request['after'], - 'column' => 'post_date', - ); - } - - if ( isset( $registered['modified_after'], $request['modified_after'] ) ) { - $args['date_query'][] = array( - 'after' => $request['modified_after'], - 'column' => 'post_modified', - ); - } - - // Ensure our per_page parameter overrides any provided posts_per_page filter. - if ( isset( $registered['per_page'] ) ) { - $args['posts_per_page'] = $request['per_page']; - } - - if ( isset( $registered['sticky'], $request['sticky'] ) ) { - $sticky_posts = get_option( 'sticky_posts', array() ); - if ( ! is_array( $sticky_posts ) ) { - $sticky_posts = array(); - } - if ( $request['sticky'] ) { - /* - * As post__in will be used to only get sticky posts, - * we have to support the case where post__in was already - * specified. - */ - $args['post__in'] = $args['post__in'] ? array_intersect( $sticky_posts, $args['post__in'] ) : $sticky_posts; - - /* - * If we intersected, but there are no post IDs in common, - * WP_Query won't return "no posts" for post__in = array() - * so we have to fake it a bit. - */ - if ( ! $args['post__in'] ) { - $args['post__in'] = array( 0 ); - } - } elseif ( $sticky_posts ) { - /* - * As post___not_in will be used to only get posts that - * are not sticky, we have to support the case where post__not_in - * was already specified. - */ - $args['post__not_in'] = array_merge( $args['post__not_in'], $sticky_posts ); - } - } - - $args = $this->prepare_tax_query( $args, $request ); - - if ( ! empty( $request['format'] ) ) { - $formats = $request['format']; - /* - * The relation needs to be set to `OR` since the request can contain - * two separate conditions. The user may be querying for items that have - * either the `standard` format or a specific format. - */ - $formats_query = array( 'relation' => 'OR' ); - - /* - * The default post format, `standard`, is not stored in the database. - * If `standard` is part of the request, the query needs to exclude all post items that - * have a format assigned. - */ - if ( in_array( 'standard', $formats, true ) ) { - $formats_query[] = array( - 'taxonomy' => 'post_format', - 'field' => 'slug', - 'operator' => 'NOT EXISTS', - ); - // Remove the `standard` format, since it cannot be queried. - unset( $formats[ array_search( 'standard', $formats, true ) ] ); - } - - // Add any remaining formats to the formats query. - if ( ! empty( $formats ) ) { - // Add the `post-format-` prefix. - $terms = array_map( - static function ( $format ) { - return "post-format-$format"; - }, - $formats - ); - - $formats_query[] = array( - 'taxonomy' => 'post_format', - 'field' => 'slug', - 'terms' => $terms, - 'operator' => 'IN', - ); - } - - // Enable filtering by both post formats and other taxonomies by combining them with `AND`. - if ( isset( $args['tax_query'] ) ) { - $args['tax_query'][] = array( - 'relation' => 'AND', - $formats_query, - ); - } else { - $args['tax_query'] = $formats_query; - } - } - - // Force the post_type argument, since it's not a user input variable. - $args['post_type'] = $this->post_type; - - /** - * Filters WP_Query arguments when querying posts via the REST API. - * - * The dynamic portion of the hook name, `$this->post_type`, refers to the post type slug. - * - * Possible hook names include: - * - * - `rest_post_query` - * - `rest_page_query` - * - `rest_attachment_query` - * - * Enables adding extra arguments or setting defaults for a post collection request. - * - * @since 4.7.0 - * @since 5.7.0 Moved after the `tax_query` query arg is generated. - * - * @link https://developer.wordpress.org/reference/classes/wp_query/ - * - * @param array $args Array of arguments for WP_Query. - * @param WP_REST_Request $request The REST API request. - */ - $args = apply_filters( "rest_{$this->post_type}_query", $args, $request ); - $query_args = $this->prepare_items_query( $args, $request ); - - $posts_query = new WP_Query(); - $query_result = $posts_query->query( $query_args ); - - // Allow access to all password protected posts if the context is edit. - if ( 'edit' === $request['context'] ) { - add_filter( 'post_password_required', array( $this, 'check_password_required' ), 10, 2 ); - } - - $posts = array(); - - update_post_author_caches( $query_result ); - update_post_parent_caches( $query_result ); - - if ( post_type_supports( $this->post_type, 'thumbnail' ) ) { - update_post_thumbnail_cache( $posts_query ); - } - - foreach ( $query_result as $post ) { - if ( ! $this->check_read_permission( $post ) ) { - continue; - } - - $data = $this->prepare_item_for_response( $post, $request ); - $posts[] = $this->prepare_response_for_collection( $data ); - } - - // Reset filter. - if ( 'edit' === $request['context'] ) { - remove_filter( 'post_password_required', array( $this, 'check_password_required' ) ); - } - - $page = (int) $query_args['paged']; - $total_posts = $posts_query->found_posts; - - if ( $total_posts < 1 && $page > 1 ) { - // Out-of-bounds, run the query again without LIMIT for total count. - unset( $query_args['paged'] ); - - $count_query = new WP_Query(); - $count_query->query( $query_args ); - $total_posts = $count_query->found_posts; - } - - $max_pages = (int) ceil( $total_posts / (int) $posts_query->query_vars['posts_per_page'] ); - - if ( $page > $max_pages && $total_posts > 0 ) { - return new WP_Error( - 'rest_post_invalid_page_number', - __( 'The page number requested is larger than the number of pages available.' ), - array( 'status' => 400 ) - ); - } - - $response = rest_ensure_response( $posts ); - - $response->header( 'X-WP-Total', (int) $total_posts ); - $response->header( 'X-WP-TotalPages', (int) $max_pages ); - - $request_params = $request->get_query_params(); - $collection_url = rest_url( rest_get_route_for_post_type_items( $this->post_type ) ); - $base = add_query_arg( urlencode_deep( $request_params ), $collection_url ); - - if ( $page > 1 ) { - $prev_page = $page - 1; - - if ( $prev_page > $max_pages ) { - $prev_page = $max_pages; - } - - $prev_link = add_query_arg( 'page', $prev_page, $base ); - $response->link_header( 'prev', $prev_link ); - } - if ( $max_pages > $page ) { - $next_page = $page + 1; - $next_link = add_query_arg( 'page', $next_page, $base ); - - $response->link_header( 'next', $next_link ); - } - - return $response; - } - - /** - * Retrieves the query params for the posts collection. - * - * @since 4.7.0 - * @since 5.4.0 The `tax_relation` query parameter was added. - * @since 5.7.0 The `modified_after` and `modified_before` query parameters were added. - * @since 6.7.0 The `format` query parameter was added. - * - * @return array Collection parameters. - */ - public function get_collection_params() { - $query_params = parent::get_collection_params(); - - $query_params['context']['default'] = 'view'; - - $query_params['after'] = array( - 'description' => __( 'Limit response to posts published after a given ISO8601 compliant date.' ), - 'type' => 'string', - 'format' => 'date-time', - ); - - $query_params['modified_after'] = array( - 'description' => __( 'Limit response to posts modified after a given ISO8601 compliant date.' ), - 'type' => 'string', - 'format' => 'date-time', - ); - - if ( post_type_supports( $this->post_type, 'author' ) ) { - $query_params['author'] = array( - 'description' => __( 'Limit result set to posts assigned to specific authors.' ), - 'type' => 'array', - 'items' => array( - 'type' => 'integer', - ), - 'default' => array(), - ); - $query_params['author_exclude'] = array( - 'description' => __( 'Ensure result set excludes posts assigned to specific authors.' ), - 'type' => 'array', - 'items' => array( - 'type' => 'integer', - ), - 'default' => array(), - ); - } - - $query_params['before'] = array( - 'description' => __( 'Limit response to posts published before a given ISO8601 compliant date.' ), - 'type' => 'string', - 'format' => 'date-time', - ); - - $query_params['modified_before'] = array( - 'description' => __( 'Limit response to posts modified before a given ISO8601 compliant date.' ), - 'type' => 'string', - 'format' => 'date-time', - ); - - $query_params['exclude'] = array( - 'description' => __( 'Ensure result set excludes specific IDs.' ), - 'type' => 'array', - 'items' => array( - 'type' => 'integer', - ), - 'default' => array(), - ); - - $query_params['include'] = array( - 'description' => __( 'Limit result set to specific IDs.' ), - 'type' => 'array', - 'items' => array( - 'type' => 'integer', - ), - 'default' => array(), - ); - - if ( 'page' === $this->post_type || post_type_supports( $this->post_type, 'page-attributes' ) ) { - $query_params['menu_order'] = array( - 'description' => __( 'Limit result set to posts with a specific menu_order value.' ), - 'type' => 'integer', - ); - } - - $query_params['offset'] = array( - 'description' => __( 'Offset the result set by a specific number of items.' ), - 'type' => 'integer', - ); - - $query_params['order'] = array( - 'description' => __( 'Order sort attribute ascending or descending.' ), - 'type' => 'string', - 'default' => 'desc', - 'enum' => array( 'asc', 'desc' ), - ); - - $query_params['orderby'] = array( - 'description' => __( 'Sort collection by post attribute.' ), - 'type' => 'string', - 'default' => 'date', - 'enum' => array( - 'author', - 'date', - 'id', - 'include', - 'modified', - 'parent', - 'relevance', - 'slug', - 'include_slugs', - 'title', - ), - ); - - if ( 'page' === $this->post_type || post_type_supports( $this->post_type, 'page-attributes' ) ) { - $query_params['orderby']['enum'][] = 'menu_order'; - } - - $post_type = get_post_type_object( $this->post_type ); - - if ( $post_type->hierarchical || 'attachment' === $this->post_type ) { - $query_params['parent'] = array( - 'description' => __( 'Limit result set to items with particular parent IDs.' ), - 'type' => 'array', - 'items' => array( - 'type' => 'integer', - ), - 'default' => array(), - ); - $query_params['parent_exclude'] = array( - 'description' => __( 'Limit result set to all items except those of a particular parent ID.' ), - 'type' => 'array', - 'items' => array( - 'type' => 'integer', - ), - 'default' => array(), - ); - } - - $query_params['search_columns'] = array( - 'default' => array(), - 'description' => __( 'Array of column names to be searched.' ), - 'type' => 'array', - 'items' => array( - 'enum' => array( 'post_title', 'post_content', 'post_excerpt' ), - 'type' => 'string', - ), - ); - - $query_params['slug'] = array( - 'description' => __( 'Limit result set to posts with one or more specific slugs.' ), - 'type' => 'array', - 'items' => array( - 'type' => 'string', - ), - ); - - $query_params['status'] = array( - 'default' => 'publish', - 'description' => __( 'Limit result set to posts assigned one or more statuses.' ), - 'type' => 'array', - 'items' => array( - 'enum' => array_merge( array_keys( get_post_stati() ), array( 'any' ) ), - 'type' => 'string', - ), - 'sanitize_callback' => array( $this, 'sanitize_post_statuses' ), - ); - - $query_params = $this->prepare_taxonomy_limit_schema( $query_params ); - - if ( 'post' === $this->post_type ) { - $query_params['sticky'] = array( - 'description' => __( 'Limit result set to items that are sticky.' ), - 'type' => 'boolean', - ); - } - - if ( post_type_supports( $this->post_type, 'post-formats' ) ) { - $query_params['format'] = array( - 'description' => __( 'Limit result set to items assigned one or more given formats.' ), - 'type' => 'array', - 'uniqueItems' => true, - 'items' => array( - 'enum' => array_values( get_post_format_slugs() ), - 'type' => 'string', - ), - ); - } - - /** - * Filters collection parameters for the posts controller. - * - * The dynamic part of the filter `$this->post_type` refers to the post - * type slug for the controller. - * - * This filter registers the collection parameter, but does not map the - * collection parameter to an internal WP_Query parameter. Use the - * `rest_{$this->post_type}_query` filter to set WP_Query parameters. - * - * @since 4.7.0 - * - * @param array $query_params JSON Schema-formatted collection parameters. - * @param WP_Post_Type $post_type Post type object. - */ - return apply_filters( "rest_{$this->post_type}_collection_params", $query_params, $post_type ); - } - - /** - * Prepares the 'tax_query' for a collection of posts. - * - * @since 5.7.0 - * - * @param array $args WP_Query arguments. - * @param WP_REST_Request $request Full details about the request. - * @return array Updated query arguments. - */ - private function prepare_tax_query( array $args, WP_REST_Request $request ) { - $relation = $request['tax_relation']; - - if ( $relation ) { - $args['tax_query'] = array( 'relation' => $relation ); - } - - $taxonomies = wp_list_filter( - get_object_taxonomies( $this->post_type, 'objects' ), - array( 'show_in_rest' => true ) - ); - - foreach ( $taxonomies as $taxonomy ) { - $base = ! empty( $taxonomy->rest_base ) ? $taxonomy->rest_base : $taxonomy->name; - - $tax_include = $request[ $base ]; - $tax_exclude = $request[ $base . '_exclude' ]; - - if ( $tax_include ) { - $terms = array(); - $include_children = false; - $operator = 'IN'; - - if ( rest_is_array( $tax_include ) ) { - $terms = $tax_include; - } elseif ( rest_is_object( $tax_include ) ) { - $terms = empty( $tax_include['terms'] ) ? array() : $tax_include['terms']; - $include_children = ! empty( $tax_include['include_children'] ); - - if ( isset( $tax_include['operator'] ) && 'AND' === $tax_include['operator'] ) { - $operator = 'AND'; - } - } - - if ( $terms ) { - $args['tax_query'][] = array( - 'taxonomy' => $taxonomy->name, - 'field' => 'term_id', - 'terms' => $terms, - 'include_children' => $include_children, - 'operator' => $operator, - ); - } - } - - if ( $tax_exclude ) { - $terms = array(); - $include_children = false; - - if ( rest_is_array( $tax_exclude ) ) { - $terms = $tax_exclude; - } elseif ( rest_is_object( $tax_exclude ) ) { - $terms = empty( $tax_exclude['terms'] ) ? array() : $tax_exclude['terms']; - $include_children = ! empty( $tax_exclude['include_children'] ); - } - - if ( $terms ) { - $args['tax_query'][] = array( - 'taxonomy' => $taxonomy->name, - 'field' => 'term_id', - 'terms' => $terms, - 'include_children' => $include_children, - 'operator' => 'NOT IN', - ); - } - } - } - - return $args; - } - - /** - * Prepares the collection schema for including and excluding items by terms. - * - * @since 5.7.0 - * - * @param array $query_params Collection schema. - * @return array Updated schema. - */ - private function prepare_taxonomy_limit_schema( array $query_params ) { - $taxonomies = wp_list_filter( get_object_taxonomies( $this->post_type, 'objects' ), array( 'show_in_rest' => true ) ); - - if ( ! $taxonomies ) { - return $query_params; - } - - $query_params['tax_relation'] = array( - 'description' => __( 'Limit result set based on relationship between multiple taxonomies.' ), - 'type' => 'string', - 'enum' => array( 'AND', 'OR' ), - ); - - $limit_schema = array( - 'type' => array( 'object', 'array' ), - 'oneOf' => array( - array( - 'title' => __( 'Term ID List' ), - 'description' => __( 'Match terms with the listed IDs.' ), - 'type' => 'array', - 'items' => array( - 'type' => 'integer', - ), - ), - array( - 'title' => __( 'Term ID Taxonomy Query' ), - 'description' => __( 'Perform an advanced term query.' ), - 'type' => 'object', - 'properties' => array( - 'terms' => array( - 'description' => __( 'Term IDs.' ), - 'type' => 'array', - 'items' => array( - 'type' => 'integer', - ), - 'default' => array(), - ), - 'include_children' => array( - 'description' => __( 'Whether to include child terms in the terms limiting the result set.' ), - 'type' => 'boolean', - 'default' => false, - ), - ), - 'additionalProperties' => false, - ), - ), - ); - - $include_schema = array_merge( - array( - /* translators: %s: Taxonomy name. */ - 'description' => __( 'Limit result set to items with specific terms assigned in the %s taxonomy.' ), - ), - $limit_schema - ); - // 'operator' is supported only for 'include' queries. - $include_schema['oneOf'][1]['properties']['operator'] = array( - 'description' => __( 'Whether items must be assigned all or any of the specified terms.' ), - 'type' => 'string', - 'enum' => array( 'AND', 'OR' ), - 'default' => 'OR', - ); - - $exclude_schema = array_merge( - array( - /* translators: %s: Taxonomy name. */ - 'description' => __( 'Limit result set to items except those with specific terms assigned in the %s taxonomy.' ), - ), - $limit_schema - ); - - foreach ( $taxonomies as $taxonomy ) { - $base = ! empty( $taxonomy->rest_base ) ? $taxonomy->rest_base : $taxonomy->name; - $base_exclude = $base . '_exclude'; - - $query_params[ $base ] = $include_schema; - $query_params[ $base ]['description'] = sprintf( $query_params[ $base ]['description'], $base ); - - $query_params[ $base_exclude ] = $exclude_schema; - $query_params[ $base_exclude ]['description'] = sprintf( $query_params[ $base_exclude ]['description'], $base ); - - if ( ! $taxonomy->hierarchical ) { - unset( $query_params[ $base ]['oneOf'][1]['properties']['include_children'] ); - unset( $query_params[ $base_exclude ]['oneOf'][1]['properties']['include_children'] ); - } - } - - return $query_params; - } -} diff --git a/lib/compat/wordpress-6.7/class-gutenberg-rest-server.php b/lib/compat/wordpress-6.7/class-gutenberg-rest-server.php deleted file mode 100644 index efdc41289dfac2..00000000000000 --- a/lib/compat/wordpress-6.7/class-gutenberg-rest-server.php +++ /dev/null @@ -1,185 +0,0 @@ -get_data(); - $links = static::get_compact_response_links( $response ); - - if ( ! empty( $links ) ) { - // Convert links to part of the data. - $data['_links'] = $links; - } - - if ( $embed ) { - $this->embed_cache = array(); - // Determine if this is a numeric array. - if ( wp_is_numeric_array( $data ) ) { - foreach ( $data as $key => $item ) { - $data[ $key ] = $this->embed_links( $item, $embed ); - } - } else { - $data = $this->embed_links( $data, $embed ); - } - $this->embed_cache = array(); - } - - return $data; - } - - /** - * Retrieves links from a response. - * - * Extracts the links from a response into a structured hash, suitable for - * direct output. - * - * @since 4.4.0 - * @since 6.7.0 The `targetHints` property to the `self` link object was added. - * - * @param WP_REST_Response $response Response to extract links from. - * @return array Map of link relation to list of link hashes. - */ - public static function get_response_links( $response ) { - $links = $response->get_links(); - - if ( empty( $links ) ) { - return array(); - } - - $server = rest_get_server(); - - // Convert links to part of the data. - $data = array(); - foreach ( $links as $rel => $items ) { - $data[ $rel ] = array(); - - foreach ( $items as $item ) { - $attributes = $item['attributes']; - $attributes['href'] = $item['href']; - - if ( 'self' !== $rel ) { - $data[ $rel ][] = $attributes; - continue; - } - - // Prefer targetHints that were specifically designated by the developer. - if ( isset( $attributes['targetHints']['allow'] ) ) { - $data[ $rel ][] = $attributes; - continue; - } - - $request = WP_REST_Request::from_url( $item['href'] ); - if ( ! $request ) { - $data[ $rel ][] = $attributes; - continue; - } - - $matched = $server->match_request_to_handler( $request ); - - if ( is_wp_error( $matched ) ) { - $data[ $rel ][] = $attributes; - continue; - } - - if ( is_wp_error( $request->has_valid_params() ) ) { - $data[ $rel ][] = $attributes; - continue; - } - - if ( is_wp_error( $request->sanitize_params() ) ) { - $data[ $rel ][] = $attributes; - continue; - } - - list( $route, $handler ) = $matched; - - $response = new WP_REST_Response(); - $response->set_matched_route( $route ); - $response->set_matched_handler( $handler ); - $headers = rest_send_allow_header( $response, $server, $request )->get_headers(); - - foreach ( $headers as $name => $value ) { - $name = WP_REST_Request::canonicalize_header_name( $name ); - $attributes['targetHints'][ $name ] = array_map( 'trim', explode( ',', $value ) ); - } - - $data[ $rel ][] = $attributes; - } - } - - return $data; - } - - /** - * Retrieves the CURIEs (compact URIs) used for relations. - * - * Extracts the links from a response into a structured hash, suitable for - * direct output. - * - * @since 4.5.0 - * - * @param WP_REST_Response $response Response to extract links from. - * @return array Map of link relation to list of link hashes. - */ - // @core-merge: Do not merge. The method is copied here to fix the inheritance issue. - public static function get_compact_response_links( $response ) { - $links = static::get_response_links( $response ); - - if ( empty( $links ) ) { - return array(); - } - - $curies = $response->get_curies(); - $used_curies = array(); - - foreach ( $links as $rel => $items ) { - - // Convert $rel URIs to their compact versions if they exist. - foreach ( $curies as $curie ) { - $href_prefix = substr( $curie['href'], 0, strpos( $curie['href'], '{rel}' ) ); - if ( ! str_starts_with( $rel, $href_prefix ) ) { - continue; - } - - // Relation now changes from '$uri' to '$curie:$relation'. - $rel_regex = str_replace( '\{rel\}', '(.+)', preg_quote( $curie['href'], '!' ) ); - preg_match( '!' . $rel_regex . '!', $rel, $matches ); - if ( $matches ) { - $new_rel = $curie['name'] . ':' . $matches[1]; - $used_curies[ $curie['name'] ] = $curie; - $links[ $new_rel ] = $items; - unset( $links[ $rel ] ); - break; - } - } - } - - // Push the curies onto the start of the links array. - if ( $used_curies ) { - $links['curies'] = array_values( $used_curies ); - } - - return $links; - } -} diff --git a/lib/compat/wordpress-6.7/class-gutenberg-rest-templates-controller-6-7.php b/lib/compat/wordpress-6.7/class-gutenberg-rest-templates-controller-6-7.php deleted file mode 100644 index e5f6eb126f2a6a..00000000000000 --- a/lib/compat/wordpress-6.7/class-gutenberg-rest-templates-controller-6-7.php +++ /dev/null @@ -1,203 +0,0 @@ -post_type ); - } else { - $template = get_block_template( $request['id'], $this->post_type ); - } - - if ( ! $template ) { - return new WP_Error( 'rest_template_not_found', __( 'No templates exist with that id.' ), array( 'status' => 404 ) ); - } - - return $this->prepare_item_for_response( $template, $request ); - } - - /** - * Prepare a single template output for response - * - * @param WP_Block_Template $item Template instance. - * @param WP_REST_Request $request Request object. - * @return WP_REST_Response Response object. - */ - // @core-merge: Fix wrong author in plugin templates. - public function prepare_item_for_response( $item, $request ) { - $template = $item; - - $fields = $this->get_fields_for_response( $request ); - - if ( 'plugin' !== $item->origin ) { - return parent::prepare_item_for_response( $item, $request ); - } - $cloned_item = clone $item; - // Set the origin as theme when calling the previous `prepare_item_for_response()` to prevent warnings when generating the author text. - $cloned_item->origin = 'theme'; - $response = parent::prepare_item_for_response( $cloned_item, $request ); - $data = $response->data; - - if ( rest_is_field_included( 'origin', $fields ) ) { - $data['origin'] = 'plugin'; - } - - if ( rest_is_field_included( 'plugin', $fields ) ) { - $registered_template = WP_Block_Templates_Registry::get_instance()->get_by_slug( $cloned_item->slug ); - if ( $registered_template ) { - $data['plugin'] = $registered_template->plugin; - } - } - - if ( rest_is_field_included( 'author_text', $fields ) ) { - $data['author_text'] = $this->get_wp_templates_author_text_field( $template ); - } - - if ( rest_is_field_included( 'original_source', $fields ) ) { - $data['original_source'] = $this->get_wp_templates_original_source_field( $template ); - } - - $response = rest_ensure_response( $data ); - - if ( rest_is_field_included( '_links', $fields ) || rest_is_field_included( '_embedded', $fields ) ) { - $links = $this->prepare_links( $template->id ); - $response->add_links( $links ); - if ( ! empty( $links['self']['href'] ) ) { - $actions = $this->get_available_actions(); - $self = $links['self']['href']; - foreach ( $actions as $rel ) { - $response->add_link( $rel, $self ); - } - } - } - - return $response; - } - - /** - * Returns the source from where the template originally comes from. - * - * @param WP_Block_Template $template_object Template instance. - * @return string Original source of the template one of theme, plugin, site, or user. - */ - // @core-merge: Changed the comments format (from inline to multi-line) in the entire function. - private static function get_wp_templates_original_source_field( $template_object ) { - if ( 'wp_template' === $template_object->type || 'wp_template_part' === $template_object->type ) { - /* - * Added by theme. - * Template originally provided by a theme, but customized by a user. - * Templates originally didn't have the 'origin' field so identify - * older customized templates by checking for no origin and a 'theme' - * or 'custom' source. - */ - if ( $template_object->has_theme_file && - ( 'theme' === $template_object->origin || ( - empty( $template_object->origin ) && in_array( - $template_object->source, - array( - 'theme', - 'custom', - ), - true - ) ) - ) - ) { - return 'theme'; - } - - // Added by plugin. - // @core-merge: Removed `$template_object->has_theme_file` check from this if clause. - if ( 'plugin' === $template_object->origin ) { - return 'plugin'; - } - - /* - * Added by site. - * Template was created from scratch, but has no author. Author support - * was only added to templates in WordPress 5.9. Fallback to showing the - * site logo and title. - */ - if ( empty( $template_object->has_theme_file ) && 'custom' === $template_object->source && empty( $template_object->author ) ) { - return 'site'; - } - } - - // Added by user. - return 'user'; - } - - /** - * Returns a human readable text for the author of the template. - * - * @param WP_Block_Template $template_object Template instance. - * @return string Human readable text for the author. - */ - private static function get_wp_templates_author_text_field( $template_object ) { - $original_source = self::get_wp_templates_original_source_field( $template_object ); - switch ( $original_source ) { - case 'theme': - $theme_name = wp_get_theme( $template_object->theme )->get( 'Name' ); - return empty( $theme_name ) ? $template_object->theme : $theme_name; - case 'plugin': - // @core-merge: Prioritize plugin name instead of theme name for plugin-registered templates. - if ( ! function_exists( 'get_plugins' ) || ! function_exists( 'get_plugin_data' ) ) { - require_once ABSPATH . 'wp-admin/includes/plugin.php'; - } - if ( isset( $template_object->plugin ) ) { - $plugins = wp_get_active_and_valid_plugins(); - - foreach ( $plugins as $plugin_file ) { - $plugin_basename = plugin_basename( $plugin_file ); - // Split basename by '/' to get the plugin slug. - list( $plugin_slug, ) = explode( '/', $plugin_basename ); - - if ( $plugin_slug === $template_object->plugin ) { - $plugin_data = get_plugin_data( $plugin_file ); - - if ( ! empty( $plugin_data['Name'] ) ) { - return $plugin_data['Name']; - } - - break; - } - } - } - - /* - * Fall back to the theme name if the plugin is not defined. That's needed to keep backwards - * compatibility with templates that were registered before the plugin attribute was added. - */ - $plugins = get_plugins(); - $plugin_basename = plugin_basename( sanitize_text_field( $template_object->theme . '.php' ) ); - if ( isset( $plugins[ $plugin_basename ] ) && isset( $plugins[ $plugin_basename ]['Name'] ) ) { - return $plugins[ $plugin_basename ]['Name']; - } - return isset( $template_object->plugin ) ? - $template_object->plugin : - $template_object->theme; - // @core-merge: End of changes to merge in core. - case 'site': - return get_bloginfo( 'name' ); - case 'user': - $author = get_user_by( 'id', $template_object->author ); - if ( ! $author ) { - return __( 'Unknown author' ); - } - return $author->get( 'display_name' ); - } - } -} diff --git a/lib/compat/wordpress-6.7/class-gutenberg-token-map-6-7.php b/lib/compat/wordpress-6.7/class-gutenberg-token-map-6-7.php deleted file mode 100644 index a2142171ddc14f..00000000000000 --- a/lib/compat/wordpress-6.7/class-gutenberg-token-map-6-7.php +++ /dev/null @@ -1,820 +0,0 @@ - '😯', - * ':(' => '🙁', - * ':)' => '🙂', - * ':?' => '😕', - * ) ); - * - * true === $smilies->contains( ':)' ); - * false === $smilies->contains( 'simile' ); - * - * '😕' === $smilies->read_token( 'Not sure :?.', 9, $length_of_smily_syntax ); - * 2 === $length_of_smily_syntax; - * - * ## Precomputing the Token Map. - * - * Creating the class involves some work sorting and organizing the tokens and their - * replacement values. In order to skip this, it's possible for the class to export - * its state and be used as actual PHP source code. - * - * Example: - * - * // Export with four spaces as the indent, only for the sake of this docblock. - * // The default indent is a tab character. - * $indent = ' '; - * echo $smilies->precomputed_php_source_table( $indent ); - * - * // Output, to be pasted into a PHP source file: - * WP_Token_Map::from_precomputed_table( - * array( - * "storage_version" => "6.6.0", - * "key_length" => 2, - * "groups" => "", - * "long_words" => array(), - * "small_words" => "8O\x00:)\x00:(\x00:?\x00", - * "small_mappings" => array( "😯", "🙂", "🙁", "😕" ) - * ) - * ); - * - * ## Large vs. small words. - * - * This class uses a short prefix called the "key" to optimize lookup of its tokens. - * This means that some tokens may be shorter than or equal in length to that key. - * Those words that are longer than the key are called "large" while those shorter - * than or equal to the key length are called "small." - * - * This separation of large and small words is incidental to the way this class - * optimizes lookup, and should be considered an internal implementation detail - * of the class. It may still be important to be aware of it, however. - * - * ## Determining Key Length. - * - * The choice of the size of the key length should be based on the data being stored in - * the token map. It should divide the data as evenly as possible, but should not create - * so many groups that a large fraction of the groups only contain a single token. - * - * For the HTML5 named character references, a key length of 2 was found to provide a - * sufficient spread and should be a good default for relatively large sets of tokens. - * - * However, for some data sets this might be too long. For example, a list of smilies - * may be too small for a key length of 2. Perhaps 1 would be more appropriate. It's - * best to experiment and determine empirically which values are appropriate. - * - * ## Generate Pre-Computed Source Code. - * - * Since the `WP_Token_Map` is designed for relatively static lookups, it can be - * advantageous to precompute the values and instantiate a table that has already - * sorted and grouped the tokens and built the lookup strings. - * - * This can be done with `WP_Token_Map::precomputed_php_source_table()`. - * - * Note that if there is a leading character that all tokens need, such as `&` for - * HTML named character references, it can be beneficial to exclude this from the - * token map. Instead, find occurrences of the leading character and then use the - * token map to see if the following characters complete the token. - * - * Example: - * - * $map = WP_Token_Map::from_array( array( 'simple_smile:' => '🙂', 'sob:' => '😭', 'soba:' => '🍜' ) ); - * echo $map->precomputed_php_source_table(); - * // Output - * WP_Token_Map::from_precomputed_table( - * array( - * "storage_version" => "6.6.0", - * "key_length" => 2, - * "groups" => "si\x00so\x00", - * "long_words" => array( - * // simple_smile:[🙂]. - * "\x0bmple_smile:\x04🙂", - * // soba:[🍜] sob:[😭]. - * "\x03ba:\x04🍜\x02b:\x04😭", - * ), - * "short_words" => "", - * "short_mappings" => array() - * } - * ); - * - * This precomputed value can be stored directly in source code and will skip the - * startup cost of generating the lookup strings. See `$html5_named_character_entities`. - * - * Note that any updates to the precomputed format should update the storage version - * constant. It would also be best to provide an update function to take older known - * versions and upgrade them in place when loading into `from_precomputed_table()`. - * - * ## Future Direction. - * - * It may be viable to dynamically increase the length limits such that there's no need to impose them. - * The limit appears because of the packing structure, which indicates how many bytes each segment of - * text in the lookup tables spans. If, however, care were taken to track the longest word length, then - * the packing structure could change its representation to allow for that. Each additional byte storing - * length, however, increases the memory overhead and lookup runtime. - * - * An alternative approach could be to borrow the UTF-8 variable-length encoding and store lengths of less - * than 127 as a single byte with the high bit unset, storing longer lengths as the combination of - * continuation bytes. - * - * Since it has not been shown during the development of this class that longer strings are required, this - * update is deferred until such a need is clear. - * - * @since 6.6.0 - */ -class Gutenberg_Token_Map_6_7 { - /** - * Denotes the version of the code which produces pre-computed source tables. - * - * This version will be used not only to verify pre-computed data, but also - * to upgrade pre-computed data from older versions. Choosing a name that - * corresponds to the WordPress release will help people identify where an - * old copy of data came from. - */ - const STORAGE_VERSION = '6.6.0-trunk'; - - /** - * Maximum length for each key and each transformed value in the table (in bytes). - * - * @since 6.6.0 - */ - const MAX_LENGTH = 256; - - /** - * How many bytes of each key are used to form a group key for lookup. - * This also determines whether a word is considered short or long. - * - * @since 6.6.0 - * - * @var int - */ - private $key_length = 2; - - /** - * Stores an optimized form of the word set, where words are grouped - * by a prefix of the `$key_length` and then collapsed into a string. - * - * In each group, the keys and lookups form a packed data structure. - * The keys in the string are stripped of their "group key," which is - * the prefix of length `$this->key_length` shared by all of the items - * in the group. Each word in the string is prefixed by a single byte - * whose raw unsigned integer value represents how many bytes follow. - * - * ┌────────────────┬───────────────┬─────────────────┬────────┐ - * │ Length of rest │ Rest of key │ Length of value │ Value │ - * │ of key (bytes) │ │ (bytes) │ │ - * ├────────────────┼───────────────┼─────────────────┼────────┤ - * │ 0x08 │ nterDot; │ 0x02 │ · │ - * └────────────────┴───────────────┴─────────────────┴────────┘ - * - * In this example, the key `CenterDot;` has a group key `Ce`, leaving - * eight bytes for the rest of the key, `nterDot;`, and two bytes for - * the transformed value `·` (or U+B7 or "\xC2\xB7"). - * - * Example: - * - * // Stores array( 'CenterDot;' => '·', 'Cedilla;' => '¸' ). - * $groups = "Ce\x00"; - * $large_words = array( "\x08nterDot;\x02·\x06dilla;\x02¸" ) - * - * The prefixes appear in the `$groups` string, each followed by a null - * byte. This makes for quick lookup of where in the group string the key - * is found, and then a simple division converts that offset into the index - * in the `$large_words` array where the group string is to be found. - * - * This lookup data structure is designed to optimize cache locality and - * minimize indirect memory reads when matching strings in the set. - * - * @since 6.6.0 - * - * @var array - */ - private $large_words = array(); - - /** - * Stores the group keys for sequential string lookup. - * - * The offset into this string where the group key appears corresponds with the index - * into the group array where the rest of the group string appears. This is an optimization - * to improve cache locality while searching and minimize indirect memory accesses. - * - * @since 6.6.0 - * - * @var string - */ - private $groups = ''; - - /** - * Stores an optimized row of small words, where every entry is - * `$this->key_size + 1` bytes long and zero-extended. - * - * This packing allows for direct lookup of a short word followed - * by the null byte, if extended to `$this->key_size + 1`. - * - * Example: - * - * // Stores array( 'GT', 'LT', 'gt', 'lt' ). - * "GT\x00LT\x00gt\x00lt\x00" - * - * @since 6.6.0 - * - * @var string - */ - private $small_words = ''; - - /** - * Replacements for the small words, in the same order they appear. - * - * With the position of a small word it's possible to index the translation - * directly, as its position in the `$small_words` string corresponds to - * the index of the replacement in the `$small_mapping` array. - * - * Example: - * - * array( '>', '<', '>', '<' ) - * - * @since 6.6.0 - * - * @var string[] - */ - private $small_mappings = array(); - - /** - * Create a token map using an associative array of key/value pairs as the input. - * - * Example: - * - * $smilies = WP_Token_Map::from_array( array( - * '8O' => '😯', - * ':(' => '🙁', - * ':)' => '🙂', - * ':?' => '😕', - * ) ); - * - * @since 6.6.0 - * - * @param array $mappings The keys transform into the values, both are strings. - * @param int $key_length Determines the group key length. Leave at the default value - * of 2 unless there's an empirical reason to change it. - * - * @return WP_Token_Map|null Token map, unless unable to create it. - */ - public static function from_array( array $mappings, int $key_length = 2 ): ?WP_Token_Map { - $map = new WP_Token_Map(); - $map->key_length = $key_length; - - // Start by grouping words. - - $groups = array(); - $shorts = array(); - foreach ( $mappings as $word => $mapping ) { - if ( - self::MAX_LENGTH <= strlen( $word ) || - self::MAX_LENGTH <= strlen( $mapping ) - ) { - _doing_it_wrong( - __METHOD__, - sprintf( - /* translators: 1: maximum byte length (a count) */ - __( 'Token Map tokens and substitutions must all be shorter than %1$d bytes.' ), - self::MAX_LENGTH - ), - '6.6.0' - ); - return null; - } - - $length = strlen( $word ); - - if ( $key_length >= $length ) { - $shorts[] = $word; - } else { - $group = substr( $word, 0, $key_length ); - - if ( ! isset( $groups[ $group ] ) ) { - $groups[ $group ] = array(); - } - - $groups[ $group ][] = array( substr( $word, $key_length ), $mapping ); - } - } - - /* - * Sort the words to ensure that no smaller substring of a match masks the full match. - * For example, `Cap` should not match before `CapitalDifferentialD`. - */ - usort( $shorts, 'WP_Token_Map::longest_first_then_alphabetical' ); - foreach ( $groups as $group_key => $group ) { - usort( - $groups[ $group_key ], - static function ( array $a, array $b ): int { - return self::longest_first_then_alphabetical( $a[0], $b[0] ); - } - ); - } - - // Finally construct the optimized lookups. - - foreach ( $shorts as $word ) { - $map->small_words .= str_pad( $word, $key_length + 1, "\x00", STR_PAD_RIGHT ); - $map->small_mappings[] = $mappings[ $word ]; - } - - $group_keys = array_keys( $groups ); - sort( $group_keys ); - - foreach ( $group_keys as $group ) { - $map->groups .= "{$group}\x00"; - - $group_string = ''; - - foreach ( $groups[ $group ] as $group_word ) { - list( $word, $mapping ) = $group_word; - - $word_length = pack( 'C', strlen( $word ) ); - $mapping_length = pack( 'C', strlen( $mapping ) ); - $group_string .= "{$word_length}{$word}{$mapping_length}{$mapping}"; - } - - $map->large_words[] = $group_string; - } - - return $map; - } - - /** - * Creates a token map from a pre-computed table. - * This skips the initialization cost of generating the table. - * - * This function should only be used to load data created with - * WP_Token_Map::precomputed_php_source_tag(). - * - * @since 6.6.0 - * - * @param array $state { - * Stores pre-computed state for directly loading into a Token Map. - * - * @type string $storage_version Which version of the code produced this state. - * @type int $key_length Group key length. - * @type string $groups Group lookup index. - * @type array $large_words Large word groups and packed strings. - * @type string $small_words Small words packed string. - * @type array $small_mappings Small word mappings. - * } - * - * @return WP_Token_Map Map with precomputed data loaded. - */ - public static function from_precomputed_table( $state ): ?WP_Token_Map { - $has_necessary_state = isset( - $state['storage_version'], - $state['key_length'], - $state['groups'], - $state['large_words'], - $state['small_words'], - $state['small_mappings'] - ); - - if ( ! $has_necessary_state ) { - _doing_it_wrong( - __METHOD__, - __( 'Missing required inputs to pre-computed WP_Token_Map.' ), - '6.6.0' - ); - return null; - } - - if ( self::STORAGE_VERSION !== $state['storage_version'] ) { - _doing_it_wrong( - __METHOD__, - /* translators: 1: version string, 2: version string. */ - sprintf( __( 'Loaded version \'%1$s\' incompatible with expected version \'%2$s\'.' ), $state['storage_version'], self::STORAGE_VERSION ), - '6.6.0' - ); - return null; - } - - $map = new WP_Token_Map(); - - $map->key_length = $state['key_length']; - $map->groups = $state['groups']; - $map->large_words = $state['large_words']; - $map->small_words = $state['small_words']; - $map->small_mappings = $state['small_mappings']; - - return $map; - } - - /** - * Indicates if a given word is a lookup key in the map. - * - * Example: - * - * true === $smilies->contains( ':)' ); - * false === $smilies->contains( 'simile' ); - * - * @since 6.6.0 - * - * @param string $word Determine if this word is a lookup key in the map. - * @param string $case_sensitivity Optional. Pass 'ascii-case-insensitive' to ignore ASCII case when matching. Default 'case-sensitive'. - * @return bool Whether there's an entry for the given word in the map. - */ - public function contains( string $word, string $case_sensitivity = 'case-sensitive' ): bool { - $ignore_case = 'ascii-case-insensitive' === $case_sensitivity; - - if ( $this->key_length >= strlen( $word ) ) { - if ( 0 === strlen( $this->small_words ) ) { - return false; - } - - $term = str_pad( $word, $this->key_length + 1, "\x00", STR_PAD_RIGHT ); - $word_at = $ignore_case ? stripos( $this->small_words, $term ) : strpos( $this->small_words, $term ); - if ( false === $word_at ) { - return false; - } - - return true; - } - - $group_key = substr( $word, 0, $this->key_length ); - $group_at = $ignore_case ? stripos( $this->groups, $group_key ) : strpos( $this->groups, $group_key ); - if ( false === $group_at ) { - return false; - } - $group = $this->large_words[ $group_at / ( $this->key_length + 1 ) ]; - $group_length = strlen( $group ); - $slug = substr( $word, $this->key_length ); - $length = strlen( $slug ); - $at = 0; - - while ( $at < $group_length ) { - $token_length = unpack( 'C', $group[ $at++ ] )[1]; - $token_at = $at; - $at += $token_length; - $mapping_length = unpack( 'C', $group[ $at++ ] )[1]; - $mapping_at = $at; - - if ( $token_length === $length && 0 === substr_compare( $group, $slug, $token_at, $token_length, $ignore_case ) ) { - return true; - } - - $at = $mapping_at + $mapping_length; - } - - return false; - } - - /** - * If the text starting at a given offset is a lookup key in the map, - * return the corresponding transformation from the map, else `false`. - * - * This function returns the translated string, but accepts an optional - * parameter `$matched_token_byte_length`, which communicates how many - * bytes long the lookup key was, if it found one. This can be used to - * advance a cursor in calling code if a lookup key was found. - * - * Example: - * - * false === $smilies->read_token( 'Not sure :?.', 0, $token_byte_length ); - * '😕' === $smilies->read_token( 'Not sure :?.', 9, $token_byte_length ); - * 2 === $token_byte_length; - * - * Example: - * - * while ( $at < strlen( $input ) ) { - * $next_at = strpos( $input, ':', $at ); - * if ( false === $next_at ) { - * break; - * } - * - * $smily = $smilies->read_token( $input, $next_at, $token_byte_length ); - * if ( false === $next_at ) { - * ++$at; - * continue; - * } - * - * $prefix = substr( $input, $at, $next_at - $at ); - * $at += $token_byte_length; - * $output .= "{$prefix}{$smily}"; - * } - * - * @since 6.6.0 - * - * @param string $text String in which to search for a lookup key. - * @param int $offset Optional. How many bytes into the string where the lookup key ought to start. Default 0. - * @param int|null &$matched_token_byte_length Optional. Holds byte-length of found token matched, otherwise not set. Default null. - * @param string $case_sensitivity Optional. Pass 'ascii-case-insensitive' to ignore ASCII case when matching. Default 'case-sensitive'. - * - * @return string|null Mapped value of lookup key if found, otherwise `null`. - */ - public function read_token( string $text, int $offset = 0, &$matched_token_byte_length = null, $case_sensitivity = 'case-sensitive' ): ?string { - $ignore_case = 'ascii-case-insensitive' === $case_sensitivity; - $text_length = strlen( $text ); - - // Search for a long word first, if the text is long enough, and if that fails, a short one. - if ( $text_length > $this->key_length ) { - $group_key = substr( $text, $offset, $this->key_length ); - - $group_at = $ignore_case ? stripos( $this->groups, $group_key ) : strpos( $this->groups, $group_key ); - if ( false === $group_at ) { - // Perhaps a short word then. - return strlen( $this->small_words ) > 0 - ? $this->read_small_token( $text, $offset, $matched_token_byte_length, $case_sensitivity ) - : null; - } - - $group = $this->large_words[ $group_at / ( $this->key_length + 1 ) ]; - $group_length = strlen( $group ); - $at = 0; - while ( $at < $group_length ) { - $token_length = unpack( 'C', $group[ $at++ ] )[1]; - $token = substr( $group, $at, $token_length ); - $at += $token_length; - $mapping_length = unpack( 'C', $group[ $at++ ] )[1]; - $mapping_at = $at; - - if ( 0 === substr_compare( $text, $token, $offset + $this->key_length, $token_length, $ignore_case ) ) { - $matched_token_byte_length = $this->key_length + $token_length; - return substr( $group, $mapping_at, $mapping_length ); - } - - $at = $mapping_at + $mapping_length; - } - } - - // Perhaps a short word then. - return strlen( $this->small_words ) > 0 - ? $this->read_small_token( $text, $offset, $matched_token_byte_length, $case_sensitivity ) - : null; - } - - /** - * Finds a match for a short word at the index. - * - * @since 6.6.0 - * - * @param string $text String in which to search for a lookup key. - * @param int $offset Optional. How many bytes into the string where the lookup key ought to start. Default 0. - * @param int|null &$matched_token_byte_length Optional. Holds byte-length of found lookup key if matched, otherwise not set. Default null. - * @param string $case_sensitivity Optional. Pass 'ascii-case-insensitive' to ignore ASCII case when matching. Default 'case-sensitive'. - * - * @return string|null Mapped value of lookup key if found, otherwise `null`. - */ - private function read_small_token( string $text, int $offset = 0, &$matched_token_byte_length = null, $case_sensitivity = 'case-sensitive' ): ?string { - $ignore_case = 'ascii-case-insensitive' === $case_sensitivity; - $small_length = strlen( $this->small_words ); - $search_text = substr( $text, $offset, $this->key_length ); - if ( $ignore_case ) { - $search_text = strtoupper( $search_text ); - } - $starting_char = $search_text[0]; - - $at = 0; - while ( $at < $small_length ) { - if ( - $starting_char !== $this->small_words[ $at ] && - ( ! $ignore_case || strtoupper( $this->small_words[ $at ] ) !== $starting_char ) - ) { - $at += $this->key_length + 1; - continue; - } - - for ( $adjust = 1; $adjust < $this->key_length; $adjust++ ) { - if ( "\x00" === $this->small_words[ $at + $adjust ] ) { - $matched_token_byte_length = $adjust; - return $this->small_mappings[ $at / ( $this->key_length + 1 ) ]; - } - - if ( - $search_text[ $adjust ] !== $this->small_words[ $at + $adjust ] && - ( ! $ignore_case || strtoupper( $this->small_words[ $at + $adjust ] !== $search_text[ $adjust ] ) ) - ) { - $at += $this->key_length + 1; - continue 2; - } - } - - $matched_token_byte_length = $adjust; - return $this->small_mappings[ $at / ( $this->key_length + 1 ) ]; - } - - return null; - } - - /** - * Exports the token map into an associate array of key/value pairs. - * - * Example: - * - * $smilies->to_array() === array( - * '8O' => '😯', - * ':(' => '🙁', - * ':)' => '🙂', - * ':?' => '😕', - * ); - * - * @return array The lookup key/substitution values as an associate array. - */ - public function to_array(): array { - $tokens = array(); - - $at = 0; - $small_mapping = 0; - $small_length = strlen( $this->small_words ); - while ( $at < $small_length ) { - $key = rtrim( substr( $this->small_words, $at, $this->key_length + 1 ), "\x00" ); - $value = $this->small_mappings[ $small_mapping++ ]; - $tokens[ $key ] = $value; - - $at += $this->key_length + 1; - } - - foreach ( $this->large_words as $index => $group ) { - $prefix = substr( $this->groups, $index * ( $this->key_length + 1 ), 2 ); - $group_length = strlen( $group ); - $at = 0; - while ( $at < $group_length ) { - $length = unpack( 'C', $group[ $at++ ] )[1]; - $key = $prefix . substr( $group, $at, $length ); - - $at += $length; - $length = unpack( 'C', $group[ $at++ ] )[1]; - $value = substr( $group, $at, $length ); - - $tokens[ $key ] = $value; - $at += $length; - } - } - - return $tokens; - } - - /** - * Export the token map for quick loading in PHP source code. - * - * This function has a specific purpose, to make loading of static token maps fast. - * It's used to ensure that the HTML character reference lookups add a minimal cost - * to initializing the PHP process. - * - * Example: - * - * echo $smilies->precomputed_php_source_table(); - * - * // Output. - * WP_Token_Map::from_precomputed_table( - * array( - * "storage_version" => "6.6.0", - * "key_length" => 2, - * "groups" => "", - * "long_words" => array(), - * "small_words" => "8O\x00:)\x00:(\x00:?\x00", - * "small_mappings" => array( "😯", "🙂", "🙁", "😕" ) - * ) - * ); - * - * @since 6.6.0 - * - * @param string $indent Optional. Use this string for indentation, or rely on the default horizontal tab character. Default "\t". - * @return string Value which can be pasted into a PHP source file for quick loading of table. - */ - public function precomputed_php_source_table( string $indent = "\t" ): string { - $i1 = $indent; - $i2 = $i1 . $indent; - $i3 = $i2 . $indent; - - $class_version = self::STORAGE_VERSION; - - $output = self::class . "::from_precomputed_table(\n"; - $output .= "{$i1}array(\n"; - $output .= "{$i2}\"storage_version\" => \"{$class_version}\",\n"; - $output .= "{$i2}\"key_length\" => {$this->key_length},\n"; - - $group_line = str_replace( "\x00", "\\x00", $this->groups ); - $output .= "{$i2}\"groups\" => \"{$group_line}\",\n"; - - $output .= "{$i2}\"large_words\" => array(\n"; - - $prefixes = explode( "\x00", $this->groups ); - foreach ( $prefixes as $index => $prefix ) { - if ( '' === $prefix ) { - break; - } - $group = $this->large_words[ $index ]; - $group_length = strlen( $group ); - $comment_line = "{$i3}//"; - $data_line = "{$i3}\""; - $at = 0; - while ( $at < $group_length ) { - $token_length = unpack( 'C', $group[ $at++ ] )[1]; - $token = substr( $group, $at, $token_length ); - $at += $token_length; - $mapping_length = unpack( 'C', $group[ $at++ ] )[1]; - $mapping = substr( $group, $at, $mapping_length ); - $at += $mapping_length; - - $token_digits = str_pad( dechex( $token_length ), 2, '0', STR_PAD_LEFT ); - $mapping_digits = str_pad( dechex( $mapping_length ), 2, '0', STR_PAD_LEFT ); - - $mapping = preg_replace_callback( - "~[\\x00-\\x1f\\x22\\x5c]~", - static function ( $match_result ) { - switch ( $match_result[0] ) { - case '"': - return '\\"'; - - case '\\': - return '\\\\'; - - default: - $hex = dechex( ord( $match_result[0] ) ); - return "\\x{$hex}"; - } - }, - $mapping - ); - - $comment_line .= " {$prefix}{$token}[{$mapping}]"; - $data_line .= "\\x{$token_digits}{$token}\\x{$mapping_digits}{$mapping}"; - } - $comment_line .= ".\n"; - $data_line .= "\",\n"; - - $output .= $comment_line; - $output .= $data_line; - } - - $output .= "{$i2}),\n"; - - $small_words = array(); - $small_length = strlen( $this->small_words ); - $at = 0; - while ( $at < $small_length ) { - $small_words[] = substr( $this->small_words, $at, $this->key_length + 1 ); - $at += $this->key_length + 1; - } - - $small_text = str_replace( "\x00", '\x00', implode( '', $small_words ) ); - $output .= "{$i2}\"small_words\" => \"{$small_text}\",\n"; - - $output .= "{$i2}\"small_mappings\" => array(\n"; - foreach ( $this->small_mappings as $mapping ) { - $output .= "{$i3}\"{$mapping}\",\n"; - } - $output .= "{$i2})\n"; - $output .= "{$i1})\n"; - $output .= ')'; - - return $output; - } - - /** - * Compares two strings, returning the longest, or whichever - * is first alphabetically if they are the same length. - * - * This is an important sort when building the token map because - * it should not form a match on a substring of a longer potential - * match. For example, it should not detect `Cap` when matching - * against the string `CapitalDifferentialD`. - * - * @since 6.6.0 - * - * @param string $a First string to compare. - * @param string $b Second string to compare. - * @return int -1 or lower if `$a` is less than `$b`; 1 or greater if `$a` is greater than `$b`, and 0 if they are equal. - */ - private static function longest_first_then_alphabetical( string $a, string $b ): int { - if ( $a === $b ) { - return 0; - } - - $length_a = strlen( $a ); - $length_b = strlen( $b ); - - // Longer strings are less-than for comparison's sake. - if ( $length_a !== $length_b ) { - return $length_b - $length_a; - } - - return strcmp( $a, $b ); - } -} diff --git a/lib/compat/wordpress-6.7/class-wp-block-templates-registry.php b/lib/compat/wordpress-6.7/class-wp-block-templates-registry.php deleted file mode 100644 index 92673c0bf50f90..00000000000000 --- a/lib/compat/wordpress-6.7/class-wp-block-templates-registry.php +++ /dev/null @@ -1,256 +0,0 @@ - $instance` pairs. - * - * @since 6.7.0 - * @var WP_Block_Template[] $registered_block_templates Registered templates. - */ - private $registered_templates = array(); - - /** - * Container for the main instance of the class. - * - * @since 6.7.0 - * @var WP_Block_Templates_Registry|null - */ - private static $instance = null; - - /** - * Registers a template. - * - * @since 6.7.0 - * - * @param string $template_name Template name including namespace. - * @param array $args Optional. Array of template arguments. - * @return WP_Block_Template|WP_Error The registered template on success, or WP_Error on failure. - */ - public function register( $template_name, $args = array() ) { - - $template = null; - - $error_message = ''; - $error_code = ''; - if ( ! is_string( $template_name ) ) { - $error_message = __( 'Template names must be strings.', 'gutenberg' ); - $error_code = 'template_name_no_string'; - } elseif ( preg_match( '/[A-Z]+/', $template_name ) ) { - $error_message = __( 'Template names must not contain uppercase characters.', 'gutenberg' ); - $error_code = 'template_name_no_uppercase'; - } elseif ( ! preg_match( '/^[a-z0-9-]+\/\/[a-z0-9-]+$/', $template_name ) ) { - $error_message = __( 'Template names must contain a namespace prefix. Example: my-plugin//my-custom-template', 'gutenberg' ); - $error_code = 'template_no_prefix'; - } elseif ( $this->is_registered( $template_name ) ) { - /* translators: %s: Template name. */ - $error_message = sprintf( __( 'Template "%s" is already registered.', 'gutenberg' ), $template_name ); - $error_code = 'template_already_registered'; - } - - if ( $error_message ) { - _doing_it_wrong( - __METHOD__, - $error_message, - '6.7.0' - ); - return new WP_Error( $error_code, $error_message ); - } - - if ( ! $template ) { - $theme_name = get_stylesheet(); - list( $plugin, $slug ) = explode( '//', $template_name ); - $default_template_types = get_default_block_template_types(); - - $template = new WP_Block_Template(); - $template->id = $theme_name . '//' . $slug; - $template->theme = $theme_name; - $template->plugin = $plugin; - $template->author = null; - $template->content = isset( $args['content'] ) ? $args['content'] : ''; - $template->source = 'plugin'; - $template->slug = $slug; - $template->type = 'wp_template'; - $template->title = isset( $args['title'] ) ? $args['title'] : $template_name; - $template->description = isset( $args['description'] ) ? $args['description'] : ''; - $template->status = 'publish'; - $template->origin = 'plugin'; - $template->is_custom = ! isset( $default_template_types[ $template_name ] ); - $template->post_types = isset( $args['post_types'] ) ? $args['post_types'] : array(); - } - - $this->registered_templates[ $template_name ] = $template; - - return $template; - } - - /** - * Retrieves all registered templates. - * - * @since 6.7.0 - * - * @return WP_Block_Template[] Associative array of `$template_name => $template` pairs. - */ - public function get_all_registered() { - return $this->registered_templates; - } - - /** - * Retrieves a registered template by its name. - * - * @since 6.7.0 - * - * @param string $template_name Template name including namespace. - * @return WP_Block_Template|null The registered template, or null if it is not registered. - */ - public function get_registered( $template_name ) { - if ( ! $this->is_registered( $template_name ) ) { - return null; - } - - return $this->registered_templates[ $template_name ]; - } - - /** - * Retrieves a registered template by its slug. - * - * @since 6.7.0 - * - * @param string $template_slug Slug of the template. - * @return WP_Block_Template|null The registered template, or null if it is not registered. - */ - public function get_by_slug( $template_slug ) { - $all_templates = $this->get_all_registered(); - - if ( ! $all_templates ) { - return null; - } - - foreach ( $all_templates as $template ) { - if ( $template->slug === $template_slug ) { - return $template; - } - } - - return null; - } - - /** - * Retrieves registered templates matching a query. - * - * @since 6.7.0 - * - * @param array $query { - * Arguments to retrieve templates. Optional, empty by default. - * - * @type string[] $slug__in List of slugs to include. - * @type string[] $slug__not_in List of slugs to skip. - * @type string $post_type Post type to get the templates for. - * } - */ - public function get_by_query( $query = array() ) { - $all_templates = $this->get_all_registered(); - - if ( ! $all_templates ) { - return array(); - } - - $query = wp_parse_args( - $query, - array( - 'slug__in' => array(), - 'slug__not_in' => array(), - 'post_type' => '', - ) - ); - $slugs_to_include = $query['slug__in']; - $slugs_to_skip = $query['slug__not_in']; - $post_type = $query['post_type']; - - $matching_templates = array(); - foreach ( $all_templates as $template_name => $template ) { - if ( $slugs_to_include && ! in_array( $template->slug, $slugs_to_include, true ) ) { - continue; - } - - if ( $slugs_to_skip && in_array( $template->slug, $slugs_to_skip, true ) ) { - continue; - } - - if ( $post_type && ! in_array( $post_type, $template->post_types, true ) ) { - continue; - } - - $matching_templates[ $template_name ] = $template; - } - - return $matching_templates; - } - - /** - * Checks if a template is registered. - * - * @since 6.7.0 - * - * @param string $template_name Template name. - * @return bool True if the template is registered, false otherwise. - */ - public function is_registered( $template_name ) { - return isset( $this->registered_templates[ $template_name ] ); - } - - /** - * Unregisters a template. - * - * @since 6.7.0 - * - * @param string $template_name Template name including namespace. - * @return WP_Block_Template|WP_Error The unregistered template on success, or WP_Error on failure. - */ - public function unregister( $template_name ) { - if ( ! $this->is_registered( $template_name ) ) { - _doing_it_wrong( - __METHOD__, - /* translators: %s: Template name. */ - sprintf( __( 'Template "%s" is not registered.', 'gutenberg' ), $template_name ), - '6.7.0' - ); - /* translators: %s: Template name. */ - return new WP_Error( 'template_not_registered', __( 'Template "%s" is not registered.', 'gutenberg' ) ); - } - - $unregistered_template = $this->registered_templates[ $template_name ]; - unset( $this->registered_templates[ $template_name ] ); - - return $unregistered_template; - } - - /** - * Utility method to retrieve the main instance of the class. - * - * The instance will be created if it does not exist yet. - * - * @since 6.7.0 - * - * @return WP_Block_Templates_Registry The main instance. - */ - public static function get_instance() { - if ( null === self::$instance ) { - self::$instance = new self(); - } - - return self::$instance; - } - } -} diff --git a/lib/compat/wordpress-6.7/compat.php b/lib/compat/wordpress-6.7/compat.php deleted file mode 100644 index 98ea34c813ae43..00000000000000 --- a/lib/compat/wordpress-6.7/compat.php +++ /dev/null @@ -1,126 +0,0 @@ - $value ) { - $registered_template = WP_Block_Templates_Registry::get_instance()->get_by_slug( $query_result[ $key ]->slug ); - if ( $registered_template ) { - $query_result[ $key ]->plugin = $registered_template->plugin; - $query_result[ $key ]->origin = - 'theme' !== $query_result[ $key ]->origin && 'theme' !== $query_result[ $key ]->source ? - 'plugin' : - $query_result[ $key ]->origin; - $query_result[ $key ]->title = - empty( $query_result[ $key ]->title ) || $query_result[ $key ]->title === $query_result[ $key ]->slug ? - $registered_template->title : $query_result[ $key ]->title; - $query_result[ $key ]->description = empty( $query_result[ $key ]->description ) ? $registered_template->description : $query_result[ $key ]->description; - } - } - - if ( ! isset( $query['wp_id'] ) ) { - // We need to unset the post_type query param because some templates - // would be excluded otherwise, like `page.html` when looking for - // `page` templates. - // See: https://github.com/WordPress/gutenberg/issues/65584 - $template_files_query = $query; - unset( $template_files_query['post_type'] ); - $template_files = _get_block_templates_files( $template_type, $template_files_query ); - - /* - * Add templates registered in the template registry. Filtering out the ones which have a theme file. - */ - $registered_templates = WP_Block_Templates_Registry::get_instance()->get_by_query( $query ); - $matching_registered_templates = array_filter( - $registered_templates, - function ( $registered_template ) use ( $template_files ) { - foreach ( $template_files as $template_file ) { - if ( $template_file['slug'] === $registered_template->slug ) { - return false; - } - } - return true; - } - ); - $query_result = array_merge( $query_result, $matching_registered_templates ); - } - - return $query_result; -} -add_filter( 'get_block_templates', '_gutenberg_add_block_templates_from_registry', 10, 3 ); - -/** - * Hooks into `get_block_template` to add the `plugin` property when necessary. - * - * @param WP_Block_Template|null $block_template The found block template, or null if there isn’t one. - * @return WP_Block_Template|null The block template that was already found with the plugin property defined if it was registered by a plugin. - */ -function _gutenberg_add_block_template_plugin_attribute( $block_template ) { - if ( $block_template ) { - $registered_template = WP_Block_Templates_Registry::get_instance()->get_by_slug( $block_template->slug ); - if ( $registered_template ) { - $block_template->plugin = $registered_template->plugin; - $block_template->origin = - 'theme' !== $block_template->origin && 'theme' !== $block_template->source ? - 'plugin' : - $block_template->origin; - $block_template->title = empty( $block_template->title ) || $block_template->title === $block_template->slug ? $registered_template->title : $block_template->title; - $block_template->description = empty( $block_template->description ) ? $registered_template->description : $block_template->description; - } - } - - return $block_template; -} -add_filter( 'get_block_template', '_gutenberg_add_block_template_plugin_attribute', 10, 1 ); - -/** - * Hooks into `get_block_file_template` so templates from the registry are also returned. - * - * @param WP_Block_Template|null $block_template The found block template, or null if there is none. - * @param string $id Template unique identifier (example: 'theme_slug//template_slug'). - * @return WP_Block_Template|null The block template that was already found or from the registry. In case the template was already found, add the necessary details from the registry. - */ -function _gutenberg_add_block_file_templates_from_registry( $block_template, $id ) { - if ( $block_template ) { - $registered_template = WP_Block_Templates_Registry::get_instance()->get_by_slug( $block_template->slug ); - if ( $registered_template ) { - $block_template->plugin = $registered_template->plugin; - $block_template->origin = - 'theme' !== $block_template->origin && 'theme' !== $block_template->source ? - 'plugin' : - $block_template->origin; - } - return $block_template; - } - - $parts = explode( '//', $id, 2 ); - - if ( count( $parts ) < 2 ) { - return $block_template; - } - - list( , $slug ) = $parts; - return WP_Block_Templates_Registry::get_instance()->get_by_slug( $slug ); -} -add_filter( 'get_block_file_template', '_gutenberg_add_block_file_templates_from_registry', 10, 2 ); diff --git a/lib/compat/wordpress-6.7/html-api/class-gutenberg-html-active-formatting-elements-6-7.php b/lib/compat/wordpress-6.7/html-api/class-gutenberg-html-active-formatting-elements-6-7.php deleted file mode 100644 index 10f53fe82ce4e0..00000000000000 --- a/lib/compat/wordpress-6.7/html-api/class-gutenberg-html-active-formatting-elements-6-7.php +++ /dev/null @@ -1,187 +0,0 @@ - Initially, the list of active formatting elements is empty. - * > It is used to handle mis-nested formatting element tags. - * > - * > The list contains elements in the formatting category, and markers. - * > The markers are inserted when entering applet, object, marquee, - * > template, td, th, and caption elements, and are used to prevent - * > formatting from "leaking" into applet, object, marquee, template, - * > td, th, and caption elements. - * > - * > In addition, each element in the list of active formatting elements - * > is associated with the token for which it was created, so that - * > further elements can be created for that token if necessary. - * - * @since 6.4.0 - * - * @access private - * - * @see https://html.spec.whatwg.org/#list-of-active-formatting-elements - * @see WP_HTML_Processor - */ -class Gutenberg_HTML_Active_Formatting_Elements_6_7 { - /** - * Holds the stack of active formatting element references. - * - * @since 6.4.0 - * - * @var WP_HTML_Token[] - */ - private $stack = array(); - - /** - * Reports if a specific node is in the stack of active formatting elements. - * - * @since 6.4.0 - * - * @param WP_HTML_Token $token Look for this node in the stack. - * @return bool Whether the referenced node is in the stack of active formatting elements. - */ - public function contains_node( Gutenberg_HTML_Token_6_7 $token ) { - foreach ( $this->walk_up() as $item ) { - if ( $token->bookmark_name === $item->bookmark_name ) { - return true; - } - } - - return false; - } - - /** - * Returns how many nodes are currently in the stack of active formatting elements. - * - * @since 6.4.0 - * - * @return int How many node are in the stack of active formatting elements. - */ - public function count() { - return count( $this->stack ); - } - - /** - * Returns the node at the end of the stack of active formatting elements, - * if one exists. If the stack is empty, returns null. - * - * @since 6.4.0 - * - * @return WP_HTML_Token|null Last node in the stack of active formatting elements, if one exists, otherwise null. - */ - public function current_node() { - $current_node = end( $this->stack ); - - return $current_node ? $current_node : null; - } - - /** - * Pushes a node onto the stack of active formatting elements. - * - * @since 6.4.0 - * - * @see https://html.spec.whatwg.org/#push-onto-the-list-of-active-formatting-elements - * - * @param WP_HTML_Token $token Push this node onto the stack. - */ - public function push( Gutenberg_HTML_Token_6_7 $token ) { - /* - * > If there are already three elements in the list of active formatting elements after the last marker, - * > if any, or anywhere in the list if there are no markers, that have the same tag name, namespace, and - * > attributes as element, then remove the earliest such element from the list of active formatting - * > elements. For these purposes, the attributes must be compared as they were when the elements were - * > created by the parser; two elements have the same attributes if all their parsed attributes can be - * > paired such that the two attributes in each pair have identical names, namespaces, and values - * > (the order of the attributes does not matter). - * - * @todo Implement the "Noah's Ark clause" to only add up to three of any given kind of formatting elements to the stack. - */ - // > Add element to the list of active formatting elements. - $this->stack[] = $token; - } - - /** - * Removes a node from the stack of active formatting elements. - * - * @since 6.4.0 - * - * @param WP_HTML_Token $token Remove this node from the stack, if it's there already. - * @return bool Whether the node was found and removed from the stack of active formatting elements. - */ - public function remove_node( Gutenberg_HTML_Token_6_7 $token ) { - foreach ( $this->walk_up() as $position_from_end => $item ) { - if ( $token->bookmark_name !== $item->bookmark_name ) { - continue; - } - - $position_from_start = $this->count() - $position_from_end - 1; - array_splice( $this->stack, $position_from_start, 1 ); - return true; - } - - return false; - } - - /** - * Steps through the stack of active formatting elements, starting with the - * top element (added first) and walking downwards to the one added last. - * - * This generator function is designed to be used inside a "foreach" loop. - * - * Example: - * - * $html = 'We are here'; - * foreach ( $stack->walk_down() as $node ) { - * echo "{$node->node_name} -> "; - * } - * > EM -> STRONG -> A -> - * - * To start with the most-recently added element and walk towards the top, - * see WP_HTML_Active_Formatting_Elements::walk_up(). - * - * @since 6.4.0 - */ - public function walk_down() { - $count = count( $this->stack ); - - for ( $i = 0; $i < $count; $i++ ) { - yield $this->stack[ $i ]; - } - } - - /** - * Steps through the stack of active formatting elements, starting with the - * bottom element (added last) and walking upwards to the one added first. - * - * This generator function is designed to be used inside a "foreach" loop. - * - * Example: - * - * $html = 'We are here'; - * foreach ( $stack->walk_up() as $node ) { - * echo "{$node->node_name} -> "; - * } - * > A -> STRONG -> EM -> - * - * To start with the first added element and walk towards the bottom, - * see WP_HTML_Active_Formatting_Elements::walk_down(). - * - * @since 6.4.0 - */ - public function walk_up() { - for ( $i = count( $this->stack ) - 1; $i >= 0; $i-- ) { - yield $this->stack[ $i ]; - } - } -} diff --git a/lib/compat/wordpress-6.7/html-api/class-gutenberg-html-attribute-token-6-7.php b/lib/compat/wordpress-6.7/html-api/class-gutenberg-html-attribute-token-6-7.php deleted file mode 100644 index 4ee369b795c84c..00000000000000 --- a/lib/compat/wordpress-6.7/html-api/class-gutenberg-html-attribute-token-6-7.php +++ /dev/null @@ -1,116 +0,0 @@ - - * ------------ length is 12, including quotes - * - * - * ------- length is 6 - * - * - * ------------ length is 11 - * - * @since 6.5.0 Replaced `end` with `length` to more closely match `substr()`. - * - * @var int - */ - public $length; - - /** - * Whether the attribute is a boolean attribute with value `true`. - * - * @since 6.2.0 - * - * @var bool - */ - public $is_true; - - /** - * Constructor. - * - * @since 6.2.0 - * @since 6.5.0 Replaced `end` with `length` to more closely match `substr()`. - * - * @param string $name Attribute name. - * @param int $value_start Attribute value. - * @param int $value_length Number of bytes attribute value spans. - * @param int $start The string offset where the attribute name starts. - * @param int $length Byte length of the entire attribute name or name and value pair expression. - * @param bool $is_true Whether the attribute is a boolean attribute with true value. - */ - public function __construct( $name, $value_start, $value_length, $start, $length, $is_true ) { - $this->name = $name; - $this->value_starts_at = $value_start; - $this->value_length = $value_length; - $this->start = $start; - $this->length = $length; - $this->is_true = $is_true; - } -} diff --git a/lib/compat/wordpress-6.7/html-api/class-gutenberg-html-decoder-6-7.php b/lib/compat/wordpress-6.7/html-api/class-gutenberg-html-decoder-6-7.php deleted file mode 100644 index c745904865b1f8..00000000000000 --- a/lib/compat/wordpress-6.7/html-api/class-gutenberg-html-decoder-6-7.php +++ /dev/null @@ -1,463 +0,0 @@ -= $length ) { - return null; - } - - if ( '&' !== $text[ $at ] ) { - return null; - } - - /* - * Numeric character references. - * - * When truncated, these will encode the code point found by parsing the - * digits that are available. For example, when `🅰` is truncated - * to `DZ` it will encode `DZ`. It does not: - * - know how to parse the original `🅰`. - * - fail to parse and return plaintext `DZ`. - * - fail to parse and return the replacement character `�` - */ - if ( '#' === $text[ $at + 1 ] ) { - if ( $at + 2 >= $length ) { - return null; - } - - /** Tracks inner parsing within the numeric character reference. */ - $digits_at = $at + 2; - - if ( 'x' === $text[ $digits_at ] || 'X' === $text[ $digits_at ] ) { - $numeric_base = 16; - $numeric_digits = '0123456789abcdefABCDEF'; - $max_digits = 6; // 􏿿 - ++$digits_at; - } else { - $numeric_base = 10; - $numeric_digits = '0123456789'; - $max_digits = 7; // 􏿿 - } - - // Cannot encode invalid Unicode code points. Max is to U+10FFFF. - $zero_count = strspn( $text, '0', $digits_at ); - $digit_count = strspn( $text, $numeric_digits, $digits_at + $zero_count ); - $after_digits = $digits_at + $zero_count + $digit_count; - $has_semicolon = $after_digits < $length && ';' === $text[ $after_digits ]; - $end_of_span = $has_semicolon ? $after_digits + 1 : $after_digits; - - // `&#` or `&#x` without digits returns into plaintext. - if ( 0 === $digit_count && 0 === $zero_count ) { - return null; - } - - // Whereas `&#` and only zeros is invalid. - if ( 0 === $digit_count ) { - $match_byte_length = $end_of_span - $at; - return '�'; - } - - // If there are too many digits then it's not worth parsing. It's invalid. - if ( $digit_count > $max_digits ) { - $match_byte_length = $end_of_span - $at; - return '�'; - } - - $digits = substr( $text, $digits_at + $zero_count, $digit_count ); - $code_point = intval( $digits, $numeric_base ); - - /* - * Noncharacters, 0x0D, and non-ASCII-whitespace control characters. - * - * > A noncharacter is a code point that is in the range U+FDD0 to U+FDEF, - * > inclusive, or U+FFFE, U+FFFF, U+1FFFE, U+1FFFF, U+2FFFE, U+2FFFF, - * > U+3FFFE, U+3FFFF, U+4FFFE, U+4FFFF, U+5FFFE, U+5FFFF, U+6FFFE, - * > U+6FFFF, U+7FFFE, U+7FFFF, U+8FFFE, U+8FFFF, U+9FFFE, U+9FFFF, - * > U+AFFFE, U+AFFFF, U+BFFFE, U+BFFFF, U+CFFFE, U+CFFFF, U+DFFFE, - * > U+DFFFF, U+EFFFE, U+EFFFF, U+FFFFE, U+FFFFF, U+10FFFE, or U+10FFFF. - * - * A C0 control is a code point that is in the range of U+00 to U+1F, - * but ASCII whitespace includes U+09, U+0A, U+0C, and U+0D. - * - * These characters are invalid but still decode as any valid character. - * This comment is here to note and explain why there's no check to - * remove these characters or replace them. - * - * @see https://infra.spec.whatwg.org/#noncharacter - */ - - /* - * Code points in the C1 controls area need to be remapped as if they - * were stored in Windows-1252. Note! This transformation only happens - * for numeric character references. The raw code points in the byte - * stream are not translated. - * - * > If the number is one of the numbers in the first column of - * > the following table, then find the row with that number in - * > the first column, and set the character reference code to - * > the number in the second column of that row. - */ - if ( $code_point >= 0x80 && $code_point <= 0x9F ) { - $windows_1252_mapping = array( - 0x20AC, // 0x80 -> EURO SIGN (€). - 0x81, // 0x81 -> (no change). - 0x201A, // 0x82 -> SINGLE LOW-9 QUOTATION MARK (‚). - 0x0192, // 0x83 -> LATIN SMALL LETTER F WITH HOOK (ƒ). - 0x201E, // 0x84 -> DOUBLE LOW-9 QUOTATION MARK („). - 0x2026, // 0x85 -> HORIZONTAL ELLIPSIS (…). - 0x2020, // 0x86 -> DAGGER (†). - 0x2021, // 0x87 -> DOUBLE DAGGER (‡). - 0x02C6, // 0x88 -> MODIFIER LETTER CIRCUMFLEX ACCENT (ˆ). - 0x2030, // 0x89 -> PER MILLE SIGN (‰). - 0x0160, // 0x8A -> LATIN CAPITAL LETTER S WITH CARON (Š). - 0x2039, // 0x8B -> SINGLE LEFT-POINTING ANGLE QUOTATION MARK (‹). - 0x0152, // 0x8C -> LATIN CAPITAL LIGATURE OE (Œ). - 0x8D, // 0x8D -> (no change). - 0x017D, // 0x8E -> LATIN CAPITAL LETTER Z WITH CARON (Ž). - 0x8F, // 0x8F -> (no change). - 0x90, // 0x90 -> (no change). - 0x2018, // 0x91 -> LEFT SINGLE QUOTATION MARK (‘). - 0x2019, // 0x92 -> RIGHT SINGLE QUOTATION MARK (’). - 0x201C, // 0x93 -> LEFT DOUBLE QUOTATION MARK (“). - 0x201D, // 0x94 -> RIGHT DOUBLE QUOTATION MARK (”). - 0x2022, // 0x95 -> BULLET (•). - 0x2013, // 0x96 -> EN DASH (–). - 0x2014, // 0x97 -> EM DASH (—). - 0x02DC, // 0x98 -> SMALL TILDE (˜). - 0x2122, // 0x99 -> TRADE MARK SIGN (™). - 0x0161, // 0x9A -> LATIN SMALL LETTER S WITH CARON (š). - 0x203A, // 0x9B -> SINGLE RIGHT-POINTING ANGLE QUOTATION MARK (›). - 0x0153, // 0x9C -> LATIN SMALL LIGATURE OE (œ). - 0x9D, // 0x9D -> (no change). - 0x017E, // 0x9E -> LATIN SMALL LETTER Z WITH CARON (ž). - 0x0178, // 0x9F -> LATIN CAPITAL LETTER Y WITH DIAERESIS (Ÿ). - ); - - $code_point = $windows_1252_mapping[ $code_point - 0x80 ]; - } - - $match_byte_length = $end_of_span - $at; - return self::code_point_to_utf8_bytes( $code_point ); - } - - /** Tracks inner parsing within the named character reference. */ - $name_at = $at + 1; - // Minimum named character reference is two characters. E.g. `GT`. - if ( $name_at + 2 > $length ) { - return null; - } - - $name_length = 0; - $replacement = $html5_named_character_references->read_token( $text, $name_at, $name_length ); - if ( false === $replacement ) { - return null; - } - - $after_name = $name_at + $name_length; - - // If the match ended with a semicolon then it should always be decoded. - if ( ';' === $text[ $name_at + $name_length - 1 ] ) { - $match_byte_length = $after_name - $at; - return $replacement; - } - - /* - * At this point though there's a match for an entry in the named - * character reference table but the match doesn't end in `;`. - * It may be allowed if it's followed by something unambiguous. - */ - $ambiguous_follower = ( - $after_name < $length && - $name_at < $length && - ( - ctype_alnum( $text[ $after_name ] ) || - '=' === $text[ $after_name ] - ) - ); - - // It's non-ambiguous, safe to leave it in. - if ( ! $ambiguous_follower ) { - $match_byte_length = $after_name - $at; - return $replacement; - } - - // It's ambiguous, which isn't allowed inside attributes. - if ( 'attribute' === $context ) { - return null; - } - - $match_byte_length = $after_name - $at; - return $replacement; - } - - /** - * Encode a code point number into the UTF-8 encoding. - * - * This encoder implements the UTF-8 encoding algorithm for converting - * a code point into a byte sequence. If it receives an invalid code - * point it will return the Unicode Replacement Character U+FFFD `�`. - * - * Example: - * - * '🅰' === WP_HTML_Decoder::code_point_to_utf8_bytes( 0x1f170 ); - * - * // Half of a surrogate pair is an invalid code point. - * '�' === WP_HTML_Decoder::code_point_to_utf8_bytes( 0xd83c ); - * - * @since 6.6.0 - * - * @see https://www.rfc-editor.org/rfc/rfc3629 For the UTF-8 standard. - * - * @param int $code_point Which code point to convert. - * @return string Converted code point, or `�` if invalid. - */ - public static function code_point_to_utf8_bytes( $code_point ): string { - // Pre-check to ensure a valid code point. - if ( - $code_point <= 0 || - ( $code_point >= 0xD800 && $code_point <= 0xDFFF ) || - $code_point > 0x10FFFF - ) { - return '�'; - } - - if ( $code_point <= 0x7F ) { - return chr( $code_point ); - } - - if ( $code_point <= 0x7FF ) { - $byte1 = chr( ( $code_point >> 6 ) | 0xC0 ); - $byte2 = chr( $code_point & 0x3F | 0x80 ); - - return "{$byte1}{$byte2}"; - } - - if ( $code_point <= 0xFFFF ) { - $byte1 = chr( ( $code_point >> 12 ) | 0xE0 ); - $byte2 = chr( ( $code_point >> 6 ) & 0x3F | 0x80 ); - $byte3 = chr( $code_point & 0x3F | 0x80 ); - - return "{$byte1}{$byte2}{$byte3}"; - } - - // Any values above U+10FFFF are eliminated above in the pre-check. - $byte1 = chr( ( $code_point >> 18 ) | 0xF0 ); - $byte2 = chr( ( $code_point >> 12 ) & 0x3F | 0x80 ); - $byte3 = chr( ( $code_point >> 6 ) & 0x3F | 0x80 ); - $byte4 = chr( $code_point & 0x3F | 0x80 ); - - return "{$byte1}{$byte2}{$byte3}{$byte4}"; - } -} diff --git a/lib/compat/wordpress-6.7/html-api/class-gutenberg-html-open-elements-6-7.php b/lib/compat/wordpress-6.7/html-api/class-gutenberg-html-open-elements-6-7.php deleted file mode 100644 index fd2b252432455e..00000000000000 --- a/lib/compat/wordpress-6.7/html-api/class-gutenberg-html-open-elements-6-7.php +++ /dev/null @@ -1,598 +0,0 @@ - Initially, the stack of open elements is empty. The stack grows - * > downwards; the topmost node on the stack is the first one added - * > to the stack, and the bottommost node of the stack is the most - * > recently added node in the stack (notwithstanding when the stack - * > is manipulated in a random access fashion as part of the handling - * > for misnested tags). - * - * @since 6.4.0 - * - * @access private - * - * @see https://html.spec.whatwg.org/#stack-of-open-elements - * @see WP_HTML_Processor - */ -class Gutenberg_HTML_Open_Elements_6_7 { - /** - * Holds the stack of open element references. - * - * @since 6.4.0 - * - * @var WP_HTML_Token[] - */ - public $stack = array(); - - /** - * Whether a P element is in button scope currently. - * - * This class optimizes scope lookup by pre-calculating - * this value when elements are added and removed to the - * stack of open elements which might change its value. - * This avoids frequent iteration over the stack. - * - * @since 6.4.0 - * - * @var bool - */ - private $has_p_in_button_scope = false; - - /** - * A function that will be called when an item is popped off the stack of open elements. - * - * The function will be called with the popped item as its argument. - * - * @since 6.6.0 - * - * @var Closure|null - */ - private $pop_handler = null; - - /** - * A function that will be called when an item is pushed onto the stack of open elements. - * - * The function will be called with the pushed item as its argument. - * - * @since 6.6.0 - * - * @var Closure|null - */ - private $push_handler = null; - - /** - * Sets a pop handler that will be called when an item is popped off the stack of - * open elements. - * - * The function will be called with the pushed item as its argument. - * - * @since 6.6.0 - * - * @param Closure $handler The handler function. - */ - public function set_pop_handler( Closure $handler ): void { - $this->pop_handler = $handler; - } - - /** - * Sets a push handler that will be called when an item is pushed onto the stack of - * open elements. - * - * The function will be called with the pushed item as its argument. - * - * @since 6.6.0 - * - * @param Closure $handler The handler function. - */ - public function set_push_handler( Closure $handler ): void { - $this->push_handler = $handler; - } - - /** - * Reports if a specific node is in the stack of open elements. - * - * @since 6.4.0 - * - * @param WP_HTML_Token $token Look for this node in the stack. - * @return bool Whether the referenced node is in the stack of open elements. - */ - public function contains_node( Gutenberg_HTML_Token_6_7 $token ): bool { - foreach ( $this->walk_up() as $item ) { - if ( $token->bookmark_name === $item->bookmark_name ) { - return true; - } - } - - return false; - } - - /** - * Returns how many nodes are currently in the stack of open elements. - * - * @since 6.4.0 - * - * @return int How many node are in the stack of open elements. - */ - public function count(): int { - return count( $this->stack ); - } - - /** - * Returns the node at the end of the stack of open elements, - * if one exists. If the stack is empty, returns null. - * - * @since 6.4.0 - * - * @return WP_HTML_Token|null Last node in the stack of open elements, if one exists, otherwise null. - */ - public function current_node(): ?Gutenberg_HTML_Token_6_7 { - $current_node = end( $this->stack ); - - return $current_node ? $current_node : null; - } - - /** - * Indicates if the current node is of a given type or name. - * - * It's possible to pass either a node type or a node name to this function. - * In the case there is no current element it will always return `false`. - * - * Example: - * - * // Is the current node a text node? - * $stack->current_node_is( '#text' ); - * - * // Is the current node a DIV element? - * $stack->current_node_is( 'DIV' ); - * - * // Is the current node any element/tag? - * $stack->current_node_is( '#tag' ); - * - * @see WP_HTML_Tag_Processor::get_token_type - * @see WP_HTML_Tag_Processor::get_token_name - * - * @since 6.7.0 - * - * @access private - * - * @param string $identity Check if the current node has this name or type (depending on what is provided). - * @return bool Whether there is a current element that matches the given identity, whether a token name or type. - */ - public function current_node_is( string $identity ): bool { - $current_node = end( $this->stack ); - if ( false === $current_node ) { - return false; - } - - $current_node_name = $current_node->node_name; - - return ( - $current_node_name === $identity || - ( '#doctype' === $identity && 'html' === $current_node_name ) || - ( '#tag' === $identity && ctype_upper( $current_node_name ) ) - ); - } - - /** - * Returns whether an element is in a specific scope. - * - * @since 6.4.0 - * - * @see https://html.spec.whatwg.org/#has-an-element-in-the-specific-scope - * - * @param string $tag_name Name of tag check. - * @param string[] $termination_list List of elements that terminate the search. - * @return bool Whether the element was found in a specific scope. - */ - public function has_element_in_specific_scope( string $tag_name, $termination_list ): bool { - foreach ( $this->walk_up() as $node ) { - if ( $node->node_name === $tag_name ) { - return true; - } - - if ( - '(internal: H1 through H6 - do not use)' === $tag_name && - in_array( $node->node_name, array( 'H1', 'H2', 'H3', 'H4', 'H5', 'H6' ), true ) - ) { - return true; - } - - switch ( $node->node_name ) { - case 'HTML': - return false; - } - - if ( in_array( $node->node_name, $termination_list, true ) ) { - return false; - } - } - - return false; - } - - /** - * Returns whether a particular element is in scope. - * - * @since 6.4.0 - * - * @see https://html.spec.whatwg.org/#has-an-element-in-scope - * - * @param string $tag_name Name of tag to check. - * @return bool Whether given element is in scope. - */ - public function has_element_in_scope( string $tag_name ): bool { - return $this->has_element_in_specific_scope( - $tag_name, - array( - - /* - * Because it's not currently possible to encounter - * one of the termination elements, they don't need - * to be listed here. If they were, they would be - * unreachable and only waste CPU cycles while - * scanning through HTML. - */ - ) - ); - } - - /** - * Returns whether a particular element is in list item scope. - * - * @since 6.4.0 - * @since 6.5.0 Implemented: no longer throws on every invocation. - * - * @see https://html.spec.whatwg.org/#has-an-element-in-list-item-scope - * - * @param string $tag_name Name of tag to check. - * @return bool Whether given element is in scope. - */ - public function has_element_in_list_item_scope( string $tag_name ): bool { - return $this->has_element_in_specific_scope( - $tag_name, - array( - // There are more elements that belong here which aren't currently supported. - 'OL', - 'UL', - ) - ); - } - - /** - * Returns whether a particular element is in button scope. - * - * @since 6.4.0 - * - * @see https://html.spec.whatwg.org/#has-an-element-in-button-scope - * - * @param string $tag_name Name of tag to check. - * @return bool Whether given element is in scope. - */ - public function has_element_in_button_scope( string $tag_name ): bool { - return $this->has_element_in_specific_scope( $tag_name, array( 'BUTTON' ) ); - } - - /** - * Returns whether a particular element is in table scope. - * - * @since 6.4.0 - * - * @see https://html.spec.whatwg.org/#has-an-element-in-table-scope - * - * @throws WP_HTML_Unsupported_Exception Always until this function is implemented. - * - * @param string $tag_name Name of tag to check. - * @return bool Whether given element is in scope. - */ - public function has_element_in_table_scope( string $tag_name ): bool { - throw new Gutenberg_HTML_Unsupported_Exception_6_7( 'Cannot process elements depending on table scope.' ); - - return false; // The linter requires this unreachable code until the function is implemented and can return. - } - - /** - * Returns whether a particular element is in select scope. - * - * This test differs from the others like it, in that its rules are inverted. - * Instead of arriving at a match when one of any tag in a termination group - * is reached, this one terminates if any other tag is reached. - * - * > The stack of open elements is said to have a particular element in select scope when it has - * > that element in the specific scope consisting of all element types except the following: - * > - optgroup in the HTML namespace - * > - option in the HTML namespace - * - * @since 6.4.0 Stub implementation (throws). - * @since 6.7.0 Full implementation. - * - * @see https://html.spec.whatwg.org/#has-an-element-in-select-scope - * - * @param string $tag_name Name of tag to check. - * @return bool Whether the given element is in SELECT scope. - */ - public function has_element_in_select_scope( string $tag_name ): bool { - foreach ( $this->walk_up() as $node ) { - if ( $node->node_name === $tag_name ) { - return true; - } - - if ( - 'OPTION' !== $node->node_name && - 'OPTGROUP' !== $node->node_name - ) { - return false; - } - } - - return false; - } - - /** - * Returns whether a P is in BUTTON scope. - * - * @since 6.4.0 - * - * @see https://html.spec.whatwg.org/#has-an-element-in-button-scope - * - * @return bool Whether a P is in BUTTON scope. - */ - public function has_p_in_button_scope(): bool { - return $this->has_p_in_button_scope; - } - - /** - * Pops a node off of the stack of open elements. - * - * @since 6.4.0 - * - * @see https://html.spec.whatwg.org/#stack-of-open-elements - * - * @return bool Whether a node was popped off of the stack. - */ - public function pop(): bool { - $item = array_pop( $this->stack ); - if ( null === $item ) { - return false; - } - - if ( 'context-node' === $item->bookmark_name ) { - $this->stack[] = $item; - return false; - } - - $this->after_element_pop( $item ); - return true; - } - - /** - * Pops nodes off of the stack of open elements until one with the given tag name has been popped. - * - * @since 6.4.0 - * - * @see WP_HTML_Open_Elements::pop - * - * @param string $tag_name Name of tag that needs to be popped off of the stack of open elements. - * @return bool Whether a tag of the given name was found and popped off of the stack of open elements. - */ - public function pop_until( string $tag_name ): bool { - foreach ( $this->walk_up() as $item ) { - if ( 'context-node' === $item->bookmark_name ) { - return true; - } - - $this->pop(); - - if ( - '(internal: H1 through H6 - do not use)' === $tag_name && - in_array( $item->node_name, array( 'H1', 'H2', 'H3', 'H4', 'H5', 'H6' ), true ) - ) { - return true; - } - - if ( $tag_name === $item->node_name ) { - return true; - } - } - - return false; - } - - /** - * Pushes a node onto the stack of open elements. - * - * @since 6.4.0 - * - * @see https://html.spec.whatwg.org/#stack-of-open-elements - * - * @param WP_HTML_Token $stack_item Item to add onto stack. - */ - public function push( Gutenberg_HTML_Token_6_7 $stack_item ): void { - $this->stack[] = $stack_item; - $this->after_element_push( $stack_item ); - } - - /** - * Removes a specific node from the stack of open elements. - * - * @since 6.4.0 - * - * @param WP_HTML_Token $token The node to remove from the stack of open elements. - * @return bool Whether the node was found and removed from the stack of open elements. - */ - public function remove_node( Gutenberg_HTML_Token_6_7 $token ): bool { - if ( 'context-node' === $token->bookmark_name ) { - return false; - } - - foreach ( $this->walk_up() as $position_from_end => $item ) { - if ( $token->bookmark_name !== $item->bookmark_name ) { - continue; - } - - $position_from_start = $this->count() - $position_from_end - 1; - array_splice( $this->stack, $position_from_start, 1 ); - $this->after_element_pop( $item ); - return true; - } - - return false; - } - - - /** - * Steps through the stack of open elements, starting with the top element - * (added first) and walking downwards to the one added last. - * - * This generator function is designed to be used inside a "foreach" loop. - * - * Example: - * - * $html = 'We are here'; - * foreach ( $stack->walk_down() as $node ) { - * echo "{$node->node_name} -> "; - * } - * > EM -> STRONG -> A -> - * - * To start with the most-recently added element and walk towards the top, - * see WP_HTML_Open_Elements::walk_up(). - * - * @since 6.4.0 - */ - public function walk_down() { - $count = count( $this->stack ); - - for ( $i = 0; $i < $count; $i++ ) { - yield $this->stack[ $i ]; - } - } - - /** - * Steps through the stack of open elements, starting with the bottom element - * (added last) and walking upwards to the one added first. - * - * This generator function is designed to be used inside a "foreach" loop. - * - * Example: - * - * $html = 'We are here'; - * foreach ( $stack->walk_up() as $node ) { - * echo "{$node->node_name} -> "; - * } - * > A -> STRONG -> EM -> - * - * To start with the first added element and walk towards the bottom, - * see WP_HTML_Open_Elements::walk_down(). - * - * @since 6.4.0 - * @since 6.5.0 Accepts $above_this_node to start traversal above a given node, if it exists. - * - * @param WP_HTML_Token|null $above_this_node Optional. Start traversing above this node, - * if provided and if the node exists. - */ - public function walk_up( ?Gutenberg_HTML_Token_6_7 $above_this_node = null ) { - $has_found_node = null === $above_this_node; - - for ( $i = count( $this->stack ) - 1; $i >= 0; $i-- ) { - $node = $this->stack[ $i ]; - - if ( ! $has_found_node ) { - $has_found_node = $node === $above_this_node; - continue; - } - - yield $node; - } - } - - /* - * Internal helpers. - */ - - /** - * Updates internal flags after adding an element. - * - * Certain conditions (such as "has_p_in_button_scope") are maintained here as - * flags that are only modified when adding and removing elements. This allows - * the HTML Processor to quickly check for these conditions instead of iterating - * over the open stack elements upon each new tag it encounters. These flags, - * however, need to be maintained as items are added and removed from the stack. - * - * @since 6.4.0 - * - * @param WP_HTML_Token $item Element that was added to the stack of open elements. - */ - public function after_element_push( Gutenberg_HTML_Token_6_7 $item ): void { - /* - * When adding support for new elements, expand this switch to trap - * cases where the precalculated value needs to change. - */ - switch ( $item->node_name ) { - case 'BUTTON': - $this->has_p_in_button_scope = false; - break; - - case 'P': - $this->has_p_in_button_scope = true; - break; - } - - if ( null !== $this->push_handler ) { - ( $this->push_handler )( $item ); - } - } - - /** - * Updates internal flags after removing an element. - * - * Certain conditions (such as "has_p_in_button_scope") are maintained here as - * flags that are only modified when adding and removing elements. This allows - * the HTML Processor to quickly check for these conditions instead of iterating - * over the open stack elements upon each new tag it encounters. These flags, - * however, need to be maintained as items are added and removed from the stack. - * - * @since 6.4.0 - * - * @param WP_HTML_Token $item Element that was removed from the stack of open elements. - */ - public function after_element_pop( Gutenberg_HTML_Token_6_7 $item ): void { - /* - * When adding support for new elements, expand this switch to trap - * cases where the precalculated value needs to change. - */ - switch ( $item->node_name ) { - case 'BUTTON': - $this->has_p_in_button_scope = $this->has_element_in_button_scope( 'P' ); - break; - - case 'P': - $this->has_p_in_button_scope = $this->has_element_in_button_scope( 'P' ); - break; - } - - if ( null !== $this->pop_handler ) { - ( $this->pop_handler )( $item ); - } - } - - /** - * Wakeup magic method. - * - * @since 6.6.0 - */ - public function __wakeup() { - throw new \LogicException( __CLASS__ . ' should never be unserialized' ); - } -} diff --git a/lib/compat/wordpress-6.7/html-api/class-gutenberg-html-processor-6-7.php b/lib/compat/wordpress-6.7/html-api/class-gutenberg-html-processor-6-7.php deleted file mode 100644 index 15cbbdc6adda8c..00000000000000 --- a/lib/compat/wordpress-6.7/html-api/class-gutenberg-html-processor-6-7.php +++ /dev/null @@ -1,3402 +0,0 @@ -next_tag( array( 'breadcrumbs' => array( 'DIV', 'FIGURE', 'IMG' ) ) ) ) { - * $processor->add_class( 'responsive-image' ); - * } - * - * #### Breadcrumbs - * - * Breadcrumbs represent the stack of open elements from the root - * of the document or fragment down to the currently-matched node, - * if one is currently selected. Call WP_HTML_Processor::get_breadcrumbs() - * to inspect the breadcrumbs for a matched tag. - * - * Breadcrumbs can specify nested HTML structure and are equivalent - * to a CSS selector comprising tag names separated by the child - * combinator, such as "DIV > FIGURE > IMG". - * - * Since all elements find themselves inside a full HTML document - * when parsed, the return value from `get_breadcrumbs()` will always - * contain any implicit outermost elements. For example, when parsing - * with `create_fragment()` in the `BODY` context (the default), any - * tag in the given HTML document will contain `array( 'HTML', 'BODY', … )` - * in its breadcrumbs. - * - * Despite containing the implied outermost elements in their breadcrumbs, - * tags may be found with the shortest-matching breadcrumb query. That is, - * `array( 'IMG' )` matches all IMG elements and `array( 'P', 'IMG' )` - * matches all IMG elements directly inside a P element. To ensure that no - * partial matches erroneously match it's possible to specify in a query - * the full breadcrumb match all the way down from the root HTML element. - * - * Example: - * - * $html = '
A lovely day outside
'; - * // ----- Matches here. - * $processor->next_tag( array( 'breadcrumbs' => array( 'FIGURE', 'IMG' ) ) ); - * - * $html = '
A lovely day outside
'; - * // ---- Matches here. - * $processor->next_tag( array( 'breadcrumbs' => array( 'FIGURE', 'FIGCAPTION', 'EM' ) ) ); - * - * $html = '
'; - * // ----- Matches here, because IMG must be a direct child of the implicit BODY. - * $processor->next_tag( array( 'breadcrumbs' => array( 'BODY', 'IMG' ) ) ); - * - * ## HTML Support - * - * This class implements a small part of the HTML5 specification. - * It's designed to operate within its support and abort early whenever - * encountering circumstances it can't properly handle. This is - * the principle way in which this class remains as simple as possible - * without cutting corners and breaking compliance. - * - * ### Supported elements - * - * If any unsupported element appears in the HTML input the HTML Processor - * will abort early and stop all processing. This draconian measure ensures - * that the HTML Processor won't break any HTML it doesn't fully understand. - * - * The following list specifies the HTML tags that _are_ supported: - * - * - Containers: ADDRESS, BLOCKQUOTE, DETAILS, DIALOG, DIV, FOOTER, HEADER, MAIN, MENU, SPAN, SUMMARY. - * - Custom elements: All custom elements are supported. :) - * - Form elements: BUTTON, DATALIST, FIELDSET, INPUT, LABEL, LEGEND, METER, OPTGROUP, OPTION, PROGRESS, SEARCH, SELECT. - * - Formatting elements: B, BIG, CODE, EM, FONT, I, PRE, SMALL, STRIKE, STRONG, TT, U, WBR. - * - Heading elements: H1, H2, H3, H4, H5, H6, HGROUP. - * - Links: A. - * - Lists: DD, DL, DT, LI, OL, UL. - * - Media elements: AUDIO, CANVAS, EMBED, FIGCAPTION, FIGURE, IMG, MAP, PICTURE, SOURCE, TRACK, VIDEO. - * - Paragraph: BR, P. - * - Phrasing elements: ABBR, AREA, BDI, BDO, CITE, DATA, DEL, DFN, INS, MARK, OUTPUT, Q, SAMP, SUB, SUP, TIME, VAR. - * - Sectioning elements: ARTICLE, ASIDE, HR, NAV, SECTION. - * - Templating elements: SLOT. - * - Text decoration: RUBY. - * - Deprecated elements: ACRONYM, BLINK, CENTER, DIR, ISINDEX, KEYGEN, LISTING, MULTICOL, NEXTID, PARAM, SPACER. - * - * ### Supported markup - * - * Some kinds of non-normative HTML involve reconstruction of formatting elements and - * re-parenting of mis-nested elements. For example, a DIV tag found inside a TABLE - * may in fact belong _before_ the table in the DOM. If the HTML Processor encounters - * such a case it will stop processing. - * - * The following list specifies HTML markup that _is_ supported: - * - * - Markup involving only those tags listed above. - * - Fully-balanced and non-overlapping tags. - * - HTML with unexpected tag closers. - * - Some unbalanced or overlapping tags. - * - P tags after unclosed P tags. - * - BUTTON tags after unclosed BUTTON tags. - * - A tags after unclosed A tags that don't involve any active formatting elements. - * - * @since 6.4.0 - * - * @see WP_HTML_Tag_Processor - * @see https://html.spec.whatwg.org/ - */ -class Gutenberg_HTML_Processor_6_7 extends Gutenberg_HTML_Tag_Processor_6_7 { - /** - * The maximum number of bookmarks allowed to exist at any given time. - * - * HTML processing requires more bookmarks than basic tag processing, - * so this class constant from the Tag Processor is overwritten. - * - * @since 6.4.0 - * - * @var int - */ - const MAX_BOOKMARKS = 100; - - /** - * Holds the working state of the parser, including the stack of - * open elements and the stack of active formatting elements. - * - * Initialized in the constructor. - * - * @since 6.4.0 - * - * @var WP_HTML_Processor_State - */ - private $state; - - /** - * Used to create unique bookmark names. - * - * This class sets a bookmark for every tag in the HTML document that it encounters. - * The bookmark name is auto-generated and increments, starting with `1`. These are - * internal bookmarks and are automatically released when the referring WP_HTML_Token - * goes out of scope and is garbage-collected. - * - * @since 6.4.0 - * - * @see WP_HTML_Processor::$release_internal_bookmark_on_destruct - * - * @var int - */ - private $bookmark_counter = 0; - - /** - * Stores an explanation for why something failed, if it did. - * - * @see self::get_last_error - * - * @since 6.4.0 - * - * @var string|null - */ - private $last_error = null; - - /** - * Stores context for why the parser bailed on unsupported HTML, if it did. - * - * @see self::get_unsupported_exception - * - * @since 6.7.0 - * - * @var WP_HTML_Unsupported_Exception|null - */ - private $unsupported_exception = null; - - /** - * Releases a bookmark when PHP garbage-collects its wrapping WP_HTML_Token instance. - * - * This function is created inside the class constructor so that it can be passed to - * the stack of open elements and the stack of active formatting elements without - * exposing it as a public method on the class. - * - * @since 6.4.0 - * - * @var Closure|null - */ - private $release_internal_bookmark_on_destruct = null; - - /** - * Stores stack events which arise during parsing of the - * HTML document, which will then supply the "match" events. - * - * @since 6.6.0 - * - * @var WP_HTML_Stack_Event[] - */ - private $element_queue = array(); - - /** - * Stores the current breadcrumbs. - * - * @since 6.7.0 - * - * @var string[] - */ - private $breadcrumbs = array(); - - /** - * Current stack event, if set, representing a matched token. - * - * Because the parser may internally point to a place further along in a document - * than the nodes which have already been processed (some "virtual" nodes may have - * appeared while scanning the HTML document), this will point at the "current" node - * being processed. It comes from the front of the element queue. - * - * @since 6.6.0 - * - * @var WP_HTML_Stack_Event|null - */ - private $current_element = null; - - /** - * Context node if created as a fragment parser. - * - * @var WP_HTML_Token|null - */ - private $context_node = null; - - /** - * Whether the parser has yet processed the context node, - * if created as a fragment parser. - * - * The context node will be initially pushed onto the stack of open elements, - * but when created as a fragment parser, this context element (and the implicit - * HTML document node above it) should not be exposed as a matched token or node. - * - * This boolean indicates whether the processor should skip over the current - * node in its initial search for the first node created from the input HTML. - * - * @var bool - */ - private $has_seen_context_node = false; - - /* - * Public Interface Functions - */ - - /** - * Creates an HTML processor in the fragment parsing mode. - * - * Use this for cases where you are processing chunks of HTML that - * will be found within a bigger HTML document, such as rendered - * block output that exists within a post, `the_content` inside a - * rendered site layout. - * - * Fragment parsing occurs within a context, which is an HTML element - * that the document will eventually be placed in. It becomes important - * when special elements have different rules than others, such as inside - * a TEXTAREA or a TITLE tag where things that look like tags are text, - * or inside a SCRIPT tag where things that look like HTML syntax are JS. - * - * The context value should be a representation of the tag into which the - * HTML is found. For most cases this will be the body element. The HTML - * form is provided because a context element may have attributes that - * impact the parse, such as with a SCRIPT tag and its `type` attribute. - * - * ## Current HTML Support - * - * - The only supported context is ``, which is the default value. - * - The only supported document encoding is `UTF-8`, which is the default value. - * - * @since 6.4.0 - * @since 6.6.0 Returns `static` instead of `self` so it can create subclass instances. - * - * @param string $html Input HTML fragment to process. - * @param string $context Context element for the fragment, must be default of ``. - * @param string $encoding Text encoding of the document; must be default of 'UTF-8'. - * @return static|null The created processor if successful, otherwise null. - */ - public static function create_fragment( $html, $context = '', $encoding = 'UTF-8' ) { - if ( '' !== $context || 'UTF-8' !== $encoding ) { - return null; - } - - $processor = new static( $html, self::CONSTRUCTOR_UNLOCK_CODE ); - $processor->state->context_node = array( 'BODY', array() ); - $processor->state->insertion_mode = Gutenberg_HTML_Processor_State_6_7::INSERTION_MODE_IN_BODY; - - // @todo Create "fake" bookmarks for non-existent but implied nodes. - $processor->bookmarks['root-node'] = new Gutenberg_HTML_Span_6_7( 0, 0 ); - $processor->bookmarks['context-node'] = new Gutenberg_HTML_Span_6_7( 0, 0 ); - - $processor->state->stack_of_open_elements->push( - new Gutenberg_HTML_Token_6_7( - 'root-node', - 'HTML', - false - ) - ); - - $context_node = new Gutenberg_HTML_Token_6_7( - 'context-node', - $processor->state->context_node[0], - false - ); - - $processor->context_node = $context_node; - $processor->breadcrumbs = array( 'HTML', $context_node->node_name ); - - return $processor; - } - - /** - * Constructor. - * - * Do not use this method. Use the static creator methods instead. - * - * @access private - * - * @since 6.4.0 - * - * @see WP_HTML_Processor::create_fragment() - * - * @param string $html HTML to process. - * @param string|null $use_the_static_create_methods_instead This constructor should not be called manually. - */ - public function __construct( $html, $use_the_static_create_methods_instead = null ) { - parent::__construct( $html ); - - if ( self::CONSTRUCTOR_UNLOCK_CODE !== $use_the_static_create_methods_instead ) { - _doing_it_wrong( - __METHOD__, - sprintf( - /* translators: %s: WP_HTML_Processor::create_fragment(). */ - __( 'Call %s to create an HTML Processor instead of calling the constructor directly.' ), - 'WP_HTML_Processor::create_fragment()' - ), - '6.4.0' - ); - } - - $this->state = new Gutenberg_HTML_Processor_State_6_7(); - - $this->state->stack_of_open_elements->set_push_handler( - function ( Gutenberg_HTML_Token_6_7 $token ): void { - $is_virtual = ! isset( $this->state->current_token ) || $this->is_tag_closer(); - $same_node = isset( $this->state->current_token ) && $token->node_name === $this->state->current_token->node_name; - $provenance = ( ! $same_node || $is_virtual ) ? 'virtual' : 'real'; - $this->element_queue[] = new Gutenberg_HTML_Stack_Event_6_7( $token, Gutenberg_HTML_Stack_Event_6_7::PUSH, $provenance ); - } - ); - - $this->state->stack_of_open_elements->set_pop_handler( - function ( Gutenberg_HTML_Token_6_7 $token ): void { - $is_virtual = ! isset( $this->state->current_token ) || ! $this->is_tag_closer(); - $same_node = isset( $this->state->current_token ) && $token->node_name === $this->state->current_token->node_name; - $provenance = ( ! $same_node || $is_virtual ) ? 'virtual' : 'real'; - $this->element_queue[] = new Gutenberg_HTML_Stack_Event_6_7( $token, Gutenberg_HTML_Stack_Event_6_7::POP, $provenance ); - } - ); - - /* - * Create this wrapper so that it's possible to pass - * a private method into WP_HTML_Token classes without - * exposing it to any public API. - */ - $this->release_internal_bookmark_on_destruct = function ( string $name ): void { - parent::release_bookmark( $name ); - }; - } - - /** - * Stops the parser and terminates its execution when encountering unsupported markup. - * - * @throws WP_HTML_Unsupported_Exception Halts execution of the parser. - * - * @since 6.7.0 - * - * @param string $message Explains support is missing in order to parse the current node. - */ - private function bail( string $message ) { - $here = $this->bookmarks[ $this->state->current_token->bookmark_name ]; - $token = substr( $this->html, $here->start, $here->length ); - - $open_elements = array(); - foreach ( $this->state->stack_of_open_elements->stack as $item ) { - $open_elements[] = $item->node_name; - } - - $active_formats = array(); - foreach ( $this->state->active_formatting_elements->walk_down() as $item ) { - $active_formats[] = $item->node_name; - } - - $this->last_error = self::ERROR_UNSUPPORTED; - - $this->unsupported_exception = new Gutenberg_HTML_Unsupported_Exception_6_7( - $message, - $this->state->current_token->node_name, - $here->start, - $token, - $open_elements, - $active_formats - ); - - throw $this->unsupported_exception; - } - - /** - * Returns the last error, if any. - * - * Various situations lead to parsing failure but this class will - * return `false` in all those cases. To determine why something - * failed it's possible to request the last error. This can be - * helpful to know to distinguish whether a given tag couldn't - * be found or if content in the document caused the processor - * to give up and abort processing. - * - * Example - * - * $processor = WP_HTML_Processor::create_fragment( '