├── .distignore ├── .editorconfig ├── .eslintignore ├── .eslintrc ├── .github ├── CODEOWNERS └── workflows │ ├── build-release-zip.yml │ ├── close-stale-issues.yml │ ├── cypress.yml │ ├── dependency-review.yml │ ├── dotorg-asset-readme-update.yml │ ├── dotorg-push-deploy.yml │ ├── lint.yml │ ├── test.yml │ └── wordpress-version-checker.yml ├── .gitignore ├── .npmrc ├── .nvmrc ├── .wordpress-org ├── banner-1544x500.png ├── banner-772x250.png ├── blueprints │ └── blueprint.json ├── icon-128x128.png ├── icon-256x256.png ├── icon.svg └── screenshot-1.gif ├── .wordpress-version-checker.json ├── .wp-env.json ├── CHANGELOG.md ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── CREDITS.md ├── LICENSE.md ├── README.md ├── assets └── js │ └── editor │ ├── editor.js │ └── transform │ ├── ClassicBlockTransformer.js │ └── MigrationClient.js ├── babel.config.js ├── bin └── install-wp-tests.sh ├── composer.json ├── composer.lock ├── config.php ├── convert-to-blocks.php ├── includes └── ConvertToBlocks │ ├── Assets.php │ ├── ClassicEditorSupport.php │ ├── MigrationAgent.php │ ├── MigrationCommand.php │ ├── Plugin.php │ ├── PostTypeColumnSupport.php │ ├── RESTSupport.php │ ├── ReverseMigrationSupport.php │ ├── RevisionSupport.php │ └── Settings.php ├── package-lock.json ├── package.json ├── phpcs.xml ├── phpunit.xml ├── portkey.json ├── readme.txt ├── tests ├── ConvertToBlocks │ ├── AssetsTest.php │ ├── ClassicEditorSupportTest.php │ └── PluginTest.php ├── bin │ ├── initialize.sh │ ├── set-core-version.js │ └── wp-cli.yml ├── bootstrap.php └── cypress │ ├── config.js │ ├── e2e │ └── admin.test.js │ ├── fixtures │ ├── example.json │ └── sample-post.txt │ ├── support │ ├── commands.js │ └── e2e.js │ └── tsconfig.json └── webpack.config.js /.distignore: -------------------------------------------------------------------------------- 1 | # Directories 2 | /.git 3 | /.github 4 | /.wordpress-org 5 | /assets 6 | /bin 7 | /config 8 | /node_modules 9 | /tests 10 | 11 | # Files 12 | .* 13 | babel.config.js 14 | CHANGELOG.md 15 | CODE_OF_CONDUCT.md 16 | composer.json 17 | composer.lock 18 | CONTRIBUTING.md 19 | CREDITS.md 20 | LICENSE.md 21 | package-lock.json 22 | package.json 23 | phpcs.xml 24 | phpunit.xml 25 | portkey.json 26 | README.md 27 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | charset = utf-8 5 | end_of_line = lf 6 | insert_final_newline = true 7 | trim_trailing_whitespace = true 8 | indent_style = tab 9 | 10 | [{*.json,*.yml,.babelrc,.bowerrc,.browserslistrc,.postcssrc}] 11 | indent_style = space 12 | indent_size = 2 13 | 14 | [*.txt,wp-config-sample.php] 15 | end_of_line = crlf 16 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | assets/js/vendor 2 | assets/js/admin/vendor 3 | assets/js/frontend/vendor 4 | assets/js/shared/vendor 5 | gulp-tasks/ 6 | webpack.config.babel.js 7 | gulpfile.babel.js 8 | tests/cypress/* 9 | tests/bin/set-core-version.js 10 | -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@10up/eslint-config", 3 | "globals": { 4 | "module": true, 5 | "process": true, 6 | "wp": true, 7 | "lodash": true 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /.github/CODEOWNERS: -------------------------------------------------------------------------------- 1 | # These owners will be the default owners for everything in the repo. Unless a later match takes precedence, @jeffpaul, @dkotter, or @dsawardekar, as primary maintainers will be requested for review when someone opens a Pull Request. 2 | * @jeffpaul @dkotter @dsawardekar 3 | 4 | # GitHub and WordPress.org specifics 5 | /.github/ @jeffpaul 6 | /.wordpress-org/ @jeffpaul 7 | CODE_OF_CONDUCT.md @jeffpaul 8 | LICENSE.md @jeffpaul 9 | -------------------------------------------------------------------------------- /.github/workflows/build-release-zip.yml: -------------------------------------------------------------------------------- 1 | name: Build release zip 2 | 3 | on: 4 | workflow_dispatch: 5 | push: 6 | branches: 7 | - trunk 8 | 9 | jobs: 10 | build: 11 | name: Build release zip 12 | runs-on: ubuntu-latest 13 | steps: 14 | - name: Checkout 15 | uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 16 | 17 | - name: Cache node_modules 18 | id: cache-node-modules 19 | uses: actions/cache@d4323d4df104b026a6aa633fdb11d772146be0bf # v4.2.2 20 | env: 21 | cache-name: cache-node-modules 22 | with: 23 | path: node_modules 24 | key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('**/package-lock.json') }} 25 | 26 | - name: Setup node version and npm cache 27 | uses: actions/setup-node@cdca7365b2dadb8aad0a33bc7601856ffabcc48e # v4.3.0 28 | with: 29 | node-version-file: '.nvmrc' 30 | cache: 'npm' 31 | 32 | - name: Install Node dependencies 33 | if: steps.cache-node-modules.outputs.cache-hit != 'true' 34 | run: npm ci --no-optional 35 | 36 | - name: Build plugin 37 | run: npm run build 38 | 39 | - name: Generate ZIP file 40 | uses: 10up/action-wordpress-plugin-build-zip@b9e621e1261ccf51592b6f3943e4dc4518fca0d1 # v1.0.2 41 | -------------------------------------------------------------------------------- /.github/workflows/close-stale-issues.yml: -------------------------------------------------------------------------------- 1 | name: 'Close stale issues' 2 | 3 | # **What it does**: Closes issues where the original author doesn't respond to a request for information. 4 | # **Why we have it**: To remove the need for maintainers to remember to check back on issues periodically to see if contributors have responded. 5 | 6 | on: 7 | schedule: 8 | # Schedule for every day at 1:30am UTC 9 | - cron: '30 1 * * *' 10 | 11 | permissions: 12 | issues: write 13 | 14 | jobs: 15 | stale: 16 | runs-on: ubuntu-latest 17 | steps: 18 | - uses: actions/stale@5bef64f19d7facfb25b37b414482c7164d639639 # v9.1.0 19 | with: 20 | days-before-stale: 7 21 | days-before-close: 7 22 | stale-issue-message: > 23 | It has been 7 days since more information was requested from you in this issue and we have not heard back. This issue is now marked as stale and will be closed in 7 days, but if you have more information to add then please comment and the issue will stay open. 24 | close-issue-message: > 25 | This issue has been automatically closed because there has been no response 26 | to our request for more information. With only the 27 | information that is currently in the issue, we don't have enough information 28 | to take action. Please reach out if you have or find the answers we need so 29 | that we can investigate further. See [this blog post on bug reports and the 30 | importance of repro steps](https://www.lee-dohm.com/2015/01/04/writing-good-bug-reports/) 31 | for more information about the kind of information that may be helpful. 32 | stale-issue-label: 'stale' 33 | close-issue-reason: 'not_planned' 34 | any-of-labels: 'needs:feedback' 35 | remove-stale-when-updated: true 36 | 37 | -------------------------------------------------------------------------------- /.github/workflows/cypress.yml: -------------------------------------------------------------------------------- 1 | name: E2E test 2 | 3 | on: 4 | push: 5 | branches: 6 | - develop 7 | - trunk 8 | pull_request: 9 | branches: 10 | - develop 11 | jobs: 12 | cypress: 13 | name: ${{ matrix.core.name }} 14 | runs-on: ubuntu-latest 15 | strategy: 16 | matrix: 17 | core: 18 | - {name: 'WP latest', version: 'latest', continue: false} 19 | - {name: 'WP minimum', version: 'WordPress/WordPress#6.6', continue: false} 20 | - {name: 'WP trunk', version: 'WordPress/WordPress#master', continue: true} 21 | 22 | steps: 23 | - name: Checkout 24 | uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 25 | 26 | - name: Install dependencies 27 | run: npm ci 28 | 29 | - name: Composer (optional) 30 | run: composer install 31 | continue-on-error: true 32 | 33 | - uses: actions/setup-node@cdca7365b2dadb8aad0a33bc7601856ffabcc48e # v4.3.0 34 | with: 35 | node-version-file: '.nvmrc' 36 | 37 | - name: Build (optional) 38 | run: npm ci && npm run build 39 | continue-on-error: true 40 | 41 | - name: Set the core version 42 | run: ./tests/bin/set-core-version.js ${{ matrix.core.version }} 43 | 44 | - name: Set up WP environment 45 | run: npm run env:start 46 | 47 | - name: Test 48 | run: npm run cypress:run 49 | continue-on-error: ${{ matrix.core.continue }} 50 | 51 | - name: Make artifacts available 52 | uses: actions/upload-artifact@4cec3d8aa04e39d1a68397de0c4cd6fb9dce8ec1 # v4.6.1 53 | if: failure() 54 | with: 55 | name: cypress-artifact 56 | retention-days: 2 57 | path: | 58 | ${{ github.workspace }}/tests/cypress/screenshots/ 59 | ${{ github.workspace }}/tests/cypress/videos/ 60 | ${{ github.workspace }}/tests/cypress/logs/ 61 | ${{ github.workspace }}/tests/cypress/reports/ 62 | -------------------------------------------------------------------------------- /.github/workflows/dependency-review.yml: -------------------------------------------------------------------------------- 1 | # Dependency Review Action 2 | # 3 | # This Action will scan dependency manifest files that change as part of a Pull Reqest, surfacing known-vulnerable versions of the packages declared or updated in the PR. Once installed, if the workflow run is marked as required, PRs introducing known-vulnerable packages will be blocked from merging. 4 | # 5 | # Source repository: https://github.com/actions/dependency-review-action 6 | # Public documentation: https://docs.github.com/en/code-security/supply-chain-security/understanding-your-software-supply-chain/about-dependency-review#dependency-review-enforcement 7 | name: 'Dependency Review' 8 | on: [pull_request] 9 | 10 | permissions: 11 | contents: read 12 | 13 | jobs: 14 | dependency-review: 15 | runs-on: ubuntu-latest 16 | steps: 17 | - name: 'Checkout Repository' 18 | uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 19 | 20 | - name: Dependency Review 21 | uses: actions/dependency-review-action@72eb03d02c7872a771aacd928f3123ac62ad6d3a # v4.3.3 22 | with: 23 | license-check: true 24 | vulnerability-check: false 25 | config-file: 10up/.github/.github/dependency-review-config.yml@trunk 26 | -------------------------------------------------------------------------------- /.github/workflows/dotorg-asset-readme-update.yml: -------------------------------------------------------------------------------- 1 | name: Plugin asset/readme update 2 | 3 | on: 4 | push: 5 | branches: 6 | - trunk 7 | 8 | jobs: 9 | trunk: 10 | name: Push to trunk 11 | runs-on: ubuntu-latest 12 | 13 | steps: 14 | - name: Checkout code 15 | uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 16 | 17 | - name: Setup node version 18 | uses: actions/setup-node@cdca7365b2dadb8aad0a33bc7601856ffabcc48e # v4.3.0 19 | with: 20 | node-version-file: .nvmrc 21 | cache: npm 22 | 23 | - name: Build 24 | run: | 25 | npm install 26 | npm run build 27 | 28 | - name: WordPress.org plugin asset/readme update 29 | uses: 10up/action-wordpress-plugin-asset-update@2480306f6f693672726d08b5917ea114cb2825f7 # v2.2.0 30 | env: 31 | SVN_PASSWORD: ${{ secrets.SVN_PASSWORD }} 32 | SVN_USERNAME: ${{ secrets.SVN_USERNAME }} 33 | -------------------------------------------------------------------------------- /.github/workflows/dotorg-push-deploy.yml: -------------------------------------------------------------------------------- 1 | name: Deploy to WordPress.org 2 | 3 | on: 4 | release: 5 | types: [published] 6 | 7 | jobs: 8 | tag: 9 | name: New release 10 | runs-on: ubuntu-latest 11 | 12 | steps: 13 | - name: Checkout code 14 | uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 15 | 16 | - name: Setup node version 17 | uses: actions/setup-node@cdca7365b2dadb8aad0a33bc7601856ffabcc48e # v4.3.0 18 | with: 19 | node-version-file: '.nvmrc' 20 | 21 | - name: Build 22 | run: | 23 | npm install 24 | npm run build 25 | 26 | - name: WordPress Plugin Deploy 27 | id: deploy 28 | uses: 10up/action-wordpress-plugin-deploy@54bd289b8525fd23a5c365ec369185f2966529c2 # v2.3.0 29 | with: 30 | generate-zip: true 31 | env: 32 | SVN_USERNAME: ${{ secrets.SVN_USERNAME }} 33 | SVN_PASSWORD: ${{ secrets.SVN_PASSWORD }} 34 | 35 | - name: Upload release asset 36 | uses: softprops/action-gh-release@c95fe1489396fe8a9eb87c0abf8aa5b2ef267fda # v2.2.1 37 | env: 38 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 39 | with: 40 | files: ${{github.workspace}}/${{ github.event.repository.name }}.zip 41 | -------------------------------------------------------------------------------- /.github/workflows/lint.yml: -------------------------------------------------------------------------------- 1 | name: Linting 2 | 3 | on: 4 | push: 5 | branches: 6 | - develop 7 | - trunk 8 | pull_request: 9 | branches: 10 | - develop 11 | 12 | jobs: 13 | eslint: 14 | name: eslint 15 | runs-on: ubuntu-latest 16 | 17 | steps: 18 | - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 19 | 20 | - name: Install node 21 | uses: actions/setup-node@cdca7365b2dadb8aad0a33bc7601856ffabcc48e # v4.3.0 22 | with: 23 | node-version-file: '.nvmrc' 24 | 25 | - name: npm install 26 | run: npm install 27 | 28 | - name: eslint 29 | uses: icrawl/action-eslint@ff2a58dcbc1aaec51815d58e8bf70962c4f2b557 # v1 30 | with: 31 | custom-glob: assets 32 | env: 33 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 34 | 35 | phpcs: 36 | name: phpcs 37 | runs-on: ubuntu-latest 38 | 39 | steps: 40 | - name: Checkout 41 | uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 42 | 43 | - name: Set PHP version 44 | uses: shivammathur/setup-php@9e72090525849c5e82e596468b86eb55e9cc5401 # v2.32.0 45 | with: 46 | php-version: '8.0' 47 | coverage: none 48 | tools: phpcs 49 | 50 | - name: composer install 51 | run: composer install 52 | 53 | - name: PHPCS check 54 | run: './vendor/bin/phpcs .' 55 | -------------------------------------------------------------------------------- /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | name: Test 2 | 3 | on: 4 | push: 5 | branches: 6 | - develop 7 | - trunk 8 | pull_request: 9 | branches: 10 | - develop 11 | 12 | jobs: 13 | phpunit: 14 | runs-on: ubuntu-latest 15 | 16 | steps: 17 | - name: Checkout 18 | uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 19 | 20 | - uses: getong/mariadb-action@d6d2ec41fd5588f369be4c9398ce77ee725ca9ea # v1.11 21 | 22 | - name: Set PHP version 23 | uses: shivammathur/setup-php@9e72090525849c5e82e596468b86eb55e9cc5401 # v2.32.0 24 | with: 25 | php-version: '8.0' 26 | coverage: none 27 | 28 | - name: Install dependencies 29 | run: composer install 30 | 31 | - name: Setup WP Tests 32 | run: bash bin/install-wp-tests.sh wordpress_test root '' 127.0.0.1 33 | 34 | - name: PHPUnit 35 | run: './vendor/bin/phpunit' 36 | -------------------------------------------------------------------------------- /.github/workflows/wordpress-version-checker.yml: -------------------------------------------------------------------------------- 1 | name: "WordPress version checker" 2 | on: 3 | push: 4 | branches: 5 | - develop 6 | - trunk 7 | pull_request: 8 | branches: 9 | - develop 10 | schedule: 11 | - cron: '0 0 * * 1' 12 | 13 | permissions: 14 | issues: write 15 | 16 | jobs: 17 | wordpress-version-checker: 18 | runs-on: ubuntu-latest 19 | steps: 20 | - name: WordPress version checker 21 | uses: skaut/wordpress-version-checker@9d247334f5b30202cb9c1f4aee74c52f37399f69 # v2.2.3 22 | with: 23 | repo-token: ${{ secrets.GITHUB_TOKEN }} 24 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | bower_components 3 | release 4 | vendor 5 | .idea 6 | *.log 7 | 8 | dist 9 | build 10 | 11 | # Editors 12 | *.esproj 13 | *.tmproj 14 | *.tmproject 15 | tmtags 16 | .*.sw[a-z] 17 | *.un~ 18 | Session.vim 19 | *.swp 20 | 21 | # Mac OSX 22 | .DS_Store 23 | ._* 24 | .Spotlight-V100 25 | .Trashes 26 | 27 | # Windows 28 | Thumbs.db 29 | Desktop.ini 30 | 31 | config.local.php 32 | 33 | tests/cypress/videos 34 | tests/cypress/screenshots 35 | tests/cypress/reports 36 | -------------------------------------------------------------------------------- /.npmrc: -------------------------------------------------------------------------------- 1 | engine-strict=true 2 | -------------------------------------------------------------------------------- /.nvmrc: -------------------------------------------------------------------------------- 1 | v20 -------------------------------------------------------------------------------- /.wordpress-org/banner-1544x500.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/10up/convert-to-blocks/b181b0f900cb4618140d39bc8d867a7941106531/.wordpress-org/banner-1544x500.png -------------------------------------------------------------------------------- /.wordpress-org/banner-772x250.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/10up/convert-to-blocks/b181b0f900cb4618140d39bc8d867a7941106531/.wordpress-org/banner-772x250.png -------------------------------------------------------------------------------- /.wordpress-org/blueprints/blueprint.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://playground.wordpress.net/blueprint-schema.json", 3 | "landingPage": "\/wp-admin\/edit.php?post_type=post", 4 | "preferredVersions": { 5 | "php": "8.0", 6 | "wp": "latest" 7 | }, 8 | "phpExtensionBundles": ["kitchen-sink"], 9 | "steps": [ 10 | { 11 | "step": "login", 12 | "username": "admin", 13 | "password": "password" 14 | }, 15 | { 16 | "step": "importFile", 17 | "file": { 18 | "resource": "url", 19 | "url": "https:\/\/raw.githubusercontent.com\/WordPress\/theme-test-data\/42c0fdc29d0055c276fc9fdd335a672a8133c605\/theme-preview.xml" 20 | } 21 | }, 22 | { 23 | "step": "installPlugin", 24 | "pluginZipFile": { 25 | "resource": "wordpress.org\/plugins", 26 | "slug": "convert-to-blocks" 27 | }, 28 | "options": { 29 | "activate": true 30 | } 31 | } 32 | ] 33 | } 34 | -------------------------------------------------------------------------------- /.wordpress-org/icon-128x128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/10up/convert-to-blocks/b181b0f900cb4618140d39bc8d867a7941106531/.wordpress-org/icon-128x128.png -------------------------------------------------------------------------------- /.wordpress-org/icon-256x256.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/10up/convert-to-blocks/b181b0f900cb4618140d39bc8d867a7941106531/.wordpress-org/icon-256x256.png -------------------------------------------------------------------------------- /.wordpress-org/icon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /.wordpress-org/screenshot-1.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/10up/convert-to-blocks/b181b0f900cb4618140d39bc8d867a7941106531/.wordpress-org/screenshot-1.gif -------------------------------------------------------------------------------- /.wordpress-version-checker.json: -------------------------------------------------------------------------------- 1 | { 2 | "readme": "readme.txt", 3 | "channel": "rc" 4 | } 5 | -------------------------------------------------------------------------------- /.wp-env.json: -------------------------------------------------------------------------------- 1 | { 2 | "plugins": ["."], 3 | "env": { 4 | "tests": { 5 | "mappings": { 6 | "sample-post.txt": "./tests/cypress/fixtures/sample-post.txt", 7 | "wp-cli.yml": "./tests/bin/wp-cli.yml" 8 | } 9 | } 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | All notable changes to this project will be documented in this file, per [the Keep a Changelog standard](http://keepachangelog.com/), and will adhere to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). 4 | 5 | ## [Unreleased] - TBD 6 | - Add plugin page link to settings page (props [@badasswp](https://github.com/badasswp) via [#195](https://github.com/10up/convert-to-blocks/pull/195)). 7 | 8 | ## [1.3.2] - 2025-02-03 9 | ### Changed 10 | - Bump WordPress "tested up to" version 6.7 (props [@colinswinney](https://github.com/colinswinney), [@jeffpaul](https://github.com/jeffpaul) via [#188](https://github.com/10up/convert-to-blocks/pull/188), [#190](https://github.com/10up/convert-to-blocks/pull/190)). 11 | 12 | ### Security 13 | - Bump `axios` from 1.6.8 to 1.7.4 (props [@dependabot](https://github.com/apps/dependabot), [@dkotter](https://github.com/dkotter) via [#180](https://github.com/10up/convert-to-blocks/pull/180)). 14 | - Bump `webpack` from 5.91.0 to 5.94.0 (props [@dependabot](https://github.com/apps/dependabot), [@faisal-alvi](https://github.com/faisal-alvi) via [#181](https://github.com/10up/convert-to-blocks/pull/181)). 15 | - Bump `ws` from 7.5.10 to 8.18.0 and `@wordpress/scripts` from 27.8.0 to 30.4.0 (props [@dependabot](https://github.com/apps/dependabot), [@faisal-alvi](https://github.com/faisal-alvi) via [#182](https://github.com/10up/convert-to-blocks/pull/182), [#189](https://github.com/10up/convert-to-blocks/pull/189)). 16 | - Bump `express` from 4.19.2 to 4.21.0 (props [@dependabot](https://github.com/apps/dependabot), [@iamdharmesh](https://github.com/iamdharmesh) via [#185](https://github.com/10up/convert-to-blocks/pull/185)). 17 | 18 | ### Developer 19 | - Fix broken link in the README (props [@GaryJones](https://github.com/GaryJones), [@dkotter](https://github.com/dkotter) via [#184](https://github.com/10up/convert-to-blocks/pull/184)). 20 | - Add banner image to the README and update existing badges (props [@jeffpaul](https://github.com/jeffpaul), [@dkotter](https://github.com/dkotter) via [#183](https://github.com/10up/convert-to-blocks/pull/183)). 21 | 22 | ## [1.3.1] - 2024-08-20 23 | **Note that this release bumps the WordPress minimum version from 6.3 to 6.4.** 24 | 25 | ### Changed 26 | - Bump WordPress "tested up to" version 6.6 (props [@sudip-md](https://github.com/sudip-md), [@jeffpaul](https://github.com/jeffpaul), [@Sidsector9](https://github.com/Sidsector9), [@ankitguptaindia](https://github.com/ankitguptaindia) via [#174](https://github.com/10up/convert-to-blocks/pull/174)). 27 | - Bump WordPress minimum from 6.3 to 6.4 (props [@sudip-md](https://github.com/sudip-md), [@jeffpaul](https://github.com/jeffpaul), [@Sidsector9](https://github.com/Sidsector9), [@ankitguptaindia](https://github.com/ankitguptaindia) via [#174](https://github.com/10up/convert-to-blocks/pull/174)). 28 | 29 | ### Fixed 30 | - Issue with saving a post before the convert to blocks transform was completed (props [@mdesplenter](https://github.com/mdesplenter), [@Sidsector9](https://github.com/Sidsector9), [@dsawardekar](https://github.com/dsawardekar) via [#173](https://github.com/10up/convert-to-blocks/pull/173)). 31 | 32 | ### Security 33 | - Bump `braces` from 3.0.2 to 3.0.3 (props [@dependabot](https://github.com/apps/dependabot), [@Sidsector9](https://github.com/Sidsector9) via [#168](https://github.com/10up/convert-to-blocks/pull/168)). 34 | - Bump `ws` from 7.5.9 to 7.5.10 (props [@dependabot](https://github.com/apps/dependabot), [@Sidsector9](https://github.com/Sidsector9) via [#169](https://github.com/10up/convert-to-blocks/pull/169)). 35 | 36 | ### Developer 37 | - Update code comments (props [@barryceelen](https://github.com/barryceelen), [@dkotter](https://github.com/dkotter) via [#175](https://github.com/10up/convert-to-blocks/pull/175)). 38 | - Move from `actions/upload-release-asset` to `softprops/action-gh-release` Github action (props [@Sidsector9](https://github.com/Sidsector9), [@jeffpaul](https://github.com/jeffpaul) via [#176](https://github.com/10up/convert-to-blocks/pull/176)). 39 | - Update repo badges, add WordPress Playground badge (props [@jeffpaul](https://github.com/jeffpaul), [@dkotter](https://github.com/dkotter) via [#177](https://github.com/10up/convert-to-blocks/pull/177)). 40 | 41 | ## [1.3.0] - 2024-05-14 42 | ### Added 43 | - Block Catalog integration, and pagination support (props [@dsawardekar](https://github.com/dsawardekar), [@iamdharmesh](https://github.com/iamdharmesh) via [#164](https://github.com/10up/convert-to-blocks/pull/164)). 44 | 45 | ### Changed 46 | - Adjust `enable_block_editor` method only to alter posts that support the gutenbridge (props [@stormrockwell](https://github.com/stormrockwell), [@Sidsector9](https://github.com/Sidsector9), [@jeffpaul](https://github.com/jeffpaul), [@dsawardekar](https://github.com/dsawardekar) via [#136](https://github.com/10up/convert-to-blocks/pull/136)). 47 | - Bump WordPress "tested up to" version 6.5 (props [@QAharshalkadu](https://github.com/QAharshalkadu), [@jeffpaul](https://github.com/jeffpaul), [@Sidsector9](https://github.com/Sidsector9), [@sudip-md](https://github.com/sudip-md), [@dkotter](https://github.com/dkotter) via [#146](https://github.com/10up/convert-to-blocks/pull/146), [#161](https://github.com/10up/convert-to-blocks/pull/161)). 48 | - Replaced [lee-dohm/no-response](https://github.com/lee-dohm/no-response) with [actions/stale](https://github.com/actions/stale) to help with closing no-response/stale issues (props [@jeffpaul](https://github.com/jeffpaul) via [#159](https://github.com/10up/convert-to-blocks/pull/159)). 49 | - Bump Node version from 16 to 20 (props [@aaronjorbin](https://github.com/aaronjorbin), [@Sidsector9](https://github.com/Sidsector9), [@dkotter](https://github.com/dkotter) via [#160](https://github.com/10up/convert-to-blocks/pull/160)). 50 | - Bump `actions/upload-artifact` from v3 to v4 (props [@iamdharmesh](https://github.com/iamdharmesh) via [#162](https://github.com/10up/convert-to-blocks/pull/162)). 51 | 52 | ### Security 53 | - Bump `follow-redirects` from 1.15.2 to 1.15.4 (props [@dependabot](https://github.com/apps/dependabot), [@dkotter](https://github.com/dkotter) via [#153](https://github.com/10up/convert-to-blocks/pull/153)). 54 | - Bump `browserify-sign` from 4.0.4 to 4.2.2 (props [@dependabot](https://github.com/apps/dependabot), [@dkotter](https://github.com/dkotter) via [#145](https://github.com/10up/convert-to-blocks/pull/145)). 55 | - Bump `@babel/traverse` from 7.11.5 to 7.23.7 (props [@dependabot](https://github.com/apps/dependabot), [@dkotter](https://github.com/dkotter) via [#155](https://github.com/10up/convert-to-blocks/pull/155)). 56 | - Remove old dependencies in favor of using `@wordpress/scripts` in our build workflow (props [@Sidsector9](https://github.com/Sidsector9), [@dkotter](https://github.com/dkotter) via [#167](https://github.com/10up/convert-to-blocks/pull/167)). 57 | 58 | ## [1.2.2] - 2023-10-16 59 | ### Changed 60 | - Bump WordPress "tested up to" version to 6.3 (props [@kmgalanakis](https://github.com/kmgalanakis), [@faisal-alvi](https://github.com/faisal-alvi), [@jeffpaul](https://github.com/jeffpaul), [@peterwilsoncc](https://github.com/peterwilsoncc) via [#132](https://github.com/10up/convert-to-blocks/pull/132), [#134](https://github.com/10up/convert-to-blocks/pull/134)). 61 | 62 | ### Security 63 | - Bump `@cypress/request` from 2.88.11 to 3.0.1 and `cypress` from 10.11.0 to 13.3.0 (props [@dependabot](https://github.com/apps/dependabot), [@iamdharmesh](https://github.com/apps/iamdharmesh), [@ravinderk](https://github.com/apps/ravinderk) via [#138](https://github.com/10up/convert-to-blocks/pull/138)). 64 | - Bump `postcss` from 8.4.20 to 8.4.31 (props [@dependabot](https://github.com/apps/dependabot), [@dkotter](https://github.com/apps/dkotter) via [#139](https://github.com/10up/convert-to-blocks/pull/139)). 65 | - Bump `fsevents` from 1.2.9 to 1.2.13 (props [@dependabot](https://github.com/apps/dependabot), [@ravinderk](https://github.com/apps/ravinderk) via [#140](https://github.com/10up/convert-to-blocks/pull/140)). 66 | 67 | ## [1.2.1] - 2023-07-26 68 | ### Added 69 | - More robust minimum PHP version check (props [@dkotter](https://github.com/dkotter), [@ravinderk](https://github.com/ravinderk) via [#129](https://github.com/10up/convert-to-blocks/pull/129)). 70 | 71 | ### Changed 72 | - Bump minimum required PHP version from 7.4 to 8.0 in our `composer.json` config (props [@c0ntax](https://github.com/c0ntax), [@Sidsector9](https://github.com/Sidsector9) via [#122](https://github.com/10up/convert-to-blocks/pull/122)). 73 | 74 | ### Fixed 75 | - Parse error caused by a comma (props [@Sidsector9](https://github.com/Sidsector9), [@iamdharmesh](https://github.com/iamdharmesh), [@ravinderk](https://github.com/ravinderk), [@felipeelia](https://github.com/felipeelia) via [#123](https://github.com/10up/convert-to-blocks/pull/123)). 76 | 77 | ### Security 78 | - Bump `minimist` from 1.2.0 to 1.2.7 and `mkdirp` from 0.5.1 to 0.5.6 (props [@dependabot](https://github.com/apps/dependabot) via [#117](https://github.com/10up/convert-to-blocks/pull/117)). 79 | - Bump `ini` from 1.3.5 to 1.3.8 (props [@dependabot](https://github.com/apps/dependabot) via [#119](https://github.com/10up/convert-to-blocks/pull/119)). 80 | - Bump `browser-sync` from 2.27.10 to 2.29.3 and removes `qs` (props [@dependabot](https://github.com/apps/dependabot) via [#120](https://github.com/10up/convert-to-blocks/pull/120)). 81 | - Bump `word-wrap` from 1.2.3 to 1.2.5 (props [@dependabot](https://github.com/apps/dependabot) via [#127](https://github.com/10up/convert-to-blocks/pull/127)). 82 | 83 | ## [1.2.0] - 2023-06-27 84 | **Note that this release bumps the WordPress minimum version from 5.7 to 6.1 and the PHP minimum version from 7.4 to 8.0.** 85 | 86 | ### Added 87 | - Settings UI for managing supported post types (props [@akshitsethi](https://github.com/akshitsethi), [@dinhtungdu](https://github.com/dinhtungdu), [@Sidsector9](https://github.com/Sidsector9), [@jayedul](https://github.com/jayedul), [@dsawardekar](https://github.com/dsawardekar), [@terrance-orletsky-d7](https://github.com/terrance-orletsky-d7), [@ouun](https://github.com/ouun) via [#66](https://github.com/10up/convert-to-blocks/pull/66), [#104](https://github.com/10up/convert-to-blocks/pull/104), [#112](https://github.com/10up/convert-to-blocks/pull/112), [#114](https://github.com/10up/convert-to-blocks/pull/114)). 88 | - Filter hook `convert_to_blocks_update_posts_query_params` to modify `WP_Query` parameters to query posts that need to be migrated (props [@kmgalanakis](https://github.com/kmgalanakis), [@Sidsector9](https://github.com/Sidsector9), [@sanketio](https://github.com/sanketio) via [#113](https://github.com/10up/convert-to-blocks/pull/113)). 89 | - Cypress end-to-end tests (props [@barneyjeffries](https://github.com/barneyjeffries), [@jeffpaul](https://github.com/jeffpaul), [@iamdharmesh](https://github.com/iamdharmesh), [@Sidsector9](https://github.com/Sidsector9), [@vikrampm1](https://github.com/vikrampm1) via [#106](https://github.com/10up/convert-to-blocks/pull/106)). 90 | 91 | ### Changed 92 | - Bump PHP minimum supported version from 7.4 to 8.0 (props [@barneyjeffries](https://github.com/barneyjeffries), [@jeffpaul](https://github.com/jeffpaul), [@iamdharmesh](https://github.com/iamdharmesh), [@Sidsector9](https://github.com/Sidsector9), [@vikrampm1](https://github.com/vikrampm1) via [#106](https://github.com/10up/convert-to-blocks/pull/106)). 93 | - Bump WordPress minimum supported version from 5.7 to 6.1 (props [@barneyjeffries](https://github.com/barneyjeffries), [@jeffpaul](https://github.com/jeffpaul), [@iamdharmesh](https://github.com/iamdharmesh), [@Sidsector9](https://github.com/Sidsector9), [@vikrampm1](https://github.com/vikrampm1) via [#106](https://github.com/10up/convert-to-blocks/pull/106)). 94 | - Bump WordPress "tested up to" version 6.2 (props [@Sidsector9](https://github.com/Sidsector9) via [#115](https://github.com/10up/convert-to-blocks/pull/115)). 95 | - Updated the Dependency Review GitHub Action (props [@jeffpaul](https://github.com/jeffpaul) via [#109](https://github.com/10up/convert-to-blocks/pull/109)). 96 | - WordPress.org Deploy action updated to use Node 16 (props [@dkotter](https://github.com/dkotter) via [#116](https://github.com/10up/convert-to-blocks/pull/116)). 97 | 98 | ## [1.1.1] - 2023-01-05 99 | **Note that this release bumps the WordPress minimum version from 5.4 to 5.7 and the PHP minimum version from 7.0 to 7.4.** 100 | 101 | ### Added 102 | - Bulk migration demo to readme (props [@jeffpaul](https://github.com/jeffpaul), [@dsawardekar](https://github.com/dsawardekar) via [#79](https://github.com/10up/convert-to-blocks/pull/79)). 103 | - Release build GitHub Action to build a release zip used for testing (props [@dkotter](https://github.com/dkotter) via [#98](https://github.com/10up/convert-to-blocks/pull/98)). 104 | 105 | ### Changed 106 | - Bump WordPress minimum version from 5.4 to 5.7 and PHP minimum version from 7.0 to 7.4 (props [@zamanq](https://github.com/zamanq), [@jeffpaul](https://github.com/jeffpaul), [@faisal-alvi](https://github.com/faisal-alvi), [@mehul0810](https://github.com/mehul0810) via [#80](https://github.com/10up/convert-to-blocks/pull/80)). 107 | - Bump WordPress "tested up to" version to 6.1 props [@peterwilsoncc](https://github.com/peterwilsoncc), [@faisal-alvi](https://github.com/faisal-alvi), [@cadic](https://github.com/cadic) via [#88](https://github.com/10up/convert-to-blocks/pull/88), [#91](https://github.com/10up/convert-to-blocks/pull/91)). 108 | 109 | ### Removed 110 | - `is-svg` as it is no longer used after updating ancestor dependency `postcss-svgo` (props [@dependabot](https://github.com/apps/dependabot) via [#85](https://github.com/10up/convert-to-blocks/pull/85)). 111 | 112 | ### Fixed 113 | - WP-CLI helptext that is causing an unknown parameter error (props [@dsawardekar](https://github.com/dsawardekar), [@jeffpaul](https://github.com/jeffpaul), [@norcross](https://github.com/norcross) via [#78](https://github.com/10up/convert-to-blocks/pull/78)). 114 | 115 | ### Security 116 | - Bump `socket.io-parser` from 3.2.0 to 4.2.1 and `browser-sync` from 2.26.7 to 2.27.10 (props [@dependabot](https://github.com/apps/dependabot) via [#81](https://github.com/10up/convert-to-blocks/pull/81)). 117 | - Bump `minimatch` from 3.0.4 to 3.1.2 (props [@dependabot](https://github.com/apps/dependabot) via [#82](https://github.com/10up/convert-to-blocks/pull/82)). 118 | - Bump `nth-check` from 1.0.2 to 2.1.1 and `cssnano` from 4.1.10 to 5.1.14 (props [@dependabot](https://github.com/apps/dependabot) via [#84](https://github.com/10up/convert-to-blocks/pull/84)). 119 | - Bump `postcss-svgo` from 4.0.2 to 4.0.3 (props [@dependabot](https://github.com/apps/dependabot) via [#85](https://github.com/10up/convert-to-blocks/pull/85)). 120 | - Bump `minimist` from 1.2.0 to 1.2.7 and `mkdirp` from 0.5.1 to 0.5.6 (props [@dependabot](https://github.com/apps/dependabot) via [#86](https://github.com/10up/convert-to-blocks/pull/86)). 121 | - Bump `loader-utils` from 1.2.3 to 1.4.2 and `webpack-cli` from 3.3.10 to 3.3.12 (props [@dependabot](https://github.com/apps/dependabot) via [#89](https://github.com/10up/convert-to-blocks/pull/89)). 122 | - Bump `glob-parent` from 5.1.0 to 5.1.2 and `watchpack` from 1.6.0 to 1.7.5 (props [@dependabot](https://github.com/apps/dependabot) via [#90](https://github.com/10up/convert-to-blocks/pull/90)). 123 | - Bump `kind-of` from 6.0.2 to 6.0.3 (props [@dependabot](https://github.com/apps/dependabot) via [#93](https://github.com/10up/convert-to-blocks/pull/93)). 124 | - Bump `serialize-javascript` from 2.1.2 to 4.0.0 and `terser-webpack-plugin` from 1.4.3 to 1.4.5 (props [@dependabot](https://github.com/apps/dependabot) via [#94](https://github.com/10up/convert-to-blocks/pull/94)). 125 | - Bump `engine.io` from 6.2.0 to 6.2.1 (props [@dependabot](https://github.com/apps/dependabot) via [#95](https://github.com/10up/convert-to-blocks/pull/95)). 126 | - Bump `decode-uri-component` from 0.2.0 to 0.2.2 (props [@dependabot](https://github.com/apps/dependabot) via [#97](https://github.com/10up/convert-to-blocks/pull/97)). 127 | 128 | ## [1.1.0] - 2022-07-28 129 | ### Added 130 | - Support for bulk migrating Classic Editor items to the Block Editor, utilizing WP-CLI (props [@dsawardekar](https://github.com/dsawardekar), [@jeffpaul](https://github.com/jeffpaul), [@gthayer](https://github.com/gthayer), [@faisal-alvi](https://github.com/faisal-alvi) via [#70](https://github.com/10up/convert-to-blocks/pull/70)). 131 | - Dependency security scanning (props [@jeffpaul](https://github.com/jeffpaul), [@Sidsector9](https://github.com/Sidsector9) via [#64](https://github.com/10up/convert-to-blocks/pull/64)). 132 | 133 | ### Fixed 134 | - Added polyfill to fix PHPUnit tests (props [@cadic](https://github.com/cadic), [@iamdharmesh](https://github.com/iamdharmesh) via [#69](https://github.com/10up/convert-to-blocks/pull/69)). 135 | 136 | ### Changed 137 | - Bump WordPress version "tested up to" 6.0 (props [@mohitwp](https://github.com/mohitwp), [@jeffpaul](https://github.com/jeffpaul), [@cadic](https://github.com/cadic), [@iamdharmesh](https://github.com/iamdharmesh) via [#59](https://github.com/10up/convert-to-blocks/pull/59), [#67](https://github.com/10up/convert-to-blocks/pull/67)). 138 | 139 | ### Security 140 | - Bump `path-parse` from 1.0.6 to 1.0.7 (props [@dependabot](https://github.com/apps/dependabot) via [#55](https://github.com/10up/convert-to-blocks/pull/55)). 141 | - Bump `ajv` from 6.10.0 to 6.12.6 (props [@dependabot](https://github.com/apps/dependabot) via [#60](https://github.com/10up/convert-to-blocks/pull/60)). 142 | - Bump `tar` from 4.4.8 to 4.4.19 (props [@dependabot](https://github.com/apps/dependabot) via [#61](https://github.com/10up/convert-to-blocks/pull/61)). 143 | - Bump `terser` from 4.6.0 to 4.8.1 (props [@dependabot](https://github.com/apps/dependabot) via [#74](https://github.com/10up/convert-to-blocks/pull/74)). 144 | 145 | ## [1.0.2] - 2021-07-12 146 | ### Changed 147 | - Bump WordPress version "tested up to" 5.8 (props [@psorensen](https://github.com/psorensen), [@BBerg10up](https://github.com/BBerg10up), [@jeffpaul](https://github.com/jeffpaul) via [#47](https://github.com/10up/convert-to-blocks/pull/47), [#50](https://github.com/10up/convert-to-blocks/pull/50)). 148 | - Documentation updates (props [@hashimwarren](https://github.com/hashimwarren) via [#34](https://github.com/10up/convert-to-blocks/pull/34)). 149 | 150 | ### Fixed 151 | - Transform Classic Editor blocks nested inside other blocks recursively (props [@dsawardekar](https://github.com/dsawardekar), [@MadtownLems](https://github.com/MadtownLems), [@dinhtungdu](https://github.com/dinhtungdu), [@jeffpaul](https://github.com/jeffpaul) via [#46](https://github.com/10up/convert-to-blocks/pull/46)). 152 | - Add Editor column to all supported post types (props [@dinhtungdu](https://github.com/dinhtungdu), [Simon Carne](https://profiles.wordpress.org/scarne/) via [#36](https://github.com/10up/convert-to-blocks/pull/36)). 153 | - Display current editor for hierarchical posts (props [@rodrigo-arias](https://github.com/rodrigo-arias) via [#43](https://github.com/10up/convert-to-blocks/pull/43)). 154 | 155 | ### Security 156 | - Bump `elliptic` from 6.5.3 to 6.5.4 (props [@dependabot](https://github.com/apps/dependabot) via [#33](https://github.com/10up/convert-to-blocks/pull/33)). 157 | - Bump `y18n` from 3.2.1 to 3.2.2 (@dependabot](https://github.com/apps/dependabot) via [#35](https://github.com/10up/convert-to-blocks/pull/35)). 158 | - Bump `ssri` from 6.0.1 to 6.0.2 (props [@dependabot](https://github.com/apps/dependabot) via [#37](https://github.com/10up/convert-to-blocks/pull/37)). 159 | - Bump `lodash` from 4.17.20 to 4.17.21 (props [@dependabot](https://github.com/apps/dependabot) via [#39](https://github.com/10up/convert-to-blocks/pull/39)). 160 | - Bump `hosted-git-info` from 2.7.1 to 2.8.9 (props [@dependabot](https://github.com/apps/dependabot) via [#40](https://github.com/10up/convert-to-blocks/pull/40)). 161 | - Bump `browserslist` from 4.8.3 to 4.16.5 (props [@dependabot](https://github.com/apps/dependabot) via [#41](https://github.com/10up/convert-to-blocks/pull/41)). 162 | - Bump `postcss` from 7.0.14 to 7.0.36 (props [@dependabot](https://github.com/apps/dependabot) via [#44](https://github.com/10up/convert-to-blocks/pull/44)). 163 | - Bump `color-string` from 1.5.3 to 1.5.5 (props [@dependabot](https://github.com/apps/dependabot) via [#48](https://github.com/10up/convert-to-blocks/pull/48)). 164 | 165 | ## [1.0.1] - 2021-02-08 166 | ### Added 167 | - Label and updated icons for Classic and Block Editor in Editor column of post table list view (props [@dinhtungdu](https://github.com/dinhtungdu) via [#12](https://github.com/10up/convert-to-blocks/pull/12)). 168 | - Plugin banner and icon images (props [@dianapadron](https://profiles.wordpress.org/dianapadron), [@jeffpaul](https://github.com/jeffpaul) via [#14](https://github.com/10up/convert-to-blocks/pull/14)). 169 | - Documentation updates, unit tests, PHPCS fixes, GitHub Actions and continuous integration testing (props [@barryceelen](https://github.com/barryceelen), [@dsawardekar](https://github.com/dsawardekar), [@dinhtungdu](https://github.com/dinhtungdu), [@jeffpaul](https://github.com/jeffpaul) via [#5](https://github.com/10up/convert-to-blocks/pull/5), [#6](https://github.com/10up/convert-to-blocks/pull/6), [#8](https://github.com/10up/convert-to-blocks/pull/8), [#11](https://github.com/10up/convert-to-blocks/pull/11), [#13](https://github.com/10up/convert-to-blocks/pull/13), [#18](https://github.com/10up/convert-to-blocks/pull/18)). 170 | 171 | ### Fixed 172 | - Issue where edit links force Classic Editor regardless of editor chosen (props [@dkotter](https://github.com/dkotter), [@tlovett1](https://github.com/tlovett1), [@dinhtungdu](https://github.com/dinhtungdu) via [#17](https://github.com/10up/convert-to-blocks/pull/17)). 173 | 174 | ### Security 175 | - Bump `lodash` from 4.17.15 to 4.17.20 (props [@dependabot](https://github.com/apps/dependabot) via [#2](https://github.com/10up/convert-to-blocks/pull/2)). 176 | - Bump `elliptic` from 6.5.2 to 6.5.3 (props [@dependabot](https://github.com/apps/dependabot) via [#4](https://github.com/10up/convert-to-blocks/pull/4)). 177 | - Bump `acorn` from 6.4.0 to 6.4.2 (props [@dependabot](https://github.com/apps/dependabot) via [#7](https://github.com/10up/convert-to-blocks/pull/7)). 178 | - Bump `dot-prop` from 4.2.0 to 4.2.1 (props [@dependabot](https://github.com/apps/dependabot) via [#15](https://github.com/10up/convert-to-blocks/pull/15)). 179 | 180 | ## [1.0.0] - 2020-09-23 181 | ### Added 182 | - Initial release of Convert to Blocks. 183 | 184 | [Unreleased]: https://github.com/10up/convert-to-blocks/compare/trunk...develop 185 | [1.3.2]: https://github.com/10up/convert-to-blocks/compare/1.3.1...1.3.2 186 | [1.3.1]: https://github.com/10up/convert-to-blocks/compare/1.3.0...1.3.1 187 | [1.3.0]: https://github.com/10up/convert-to-blocks/compare/1.2.1...1.3.0 188 | [1.2.2]: https://github.com/10up/convert-to-blocks/compare/1.2.1...1.2.2 189 | [1.2.1]: https://github.com/10up/convert-to-blocks/compare/1.2.0...1.2.1 190 | [1.2.0]: https://github.com/10up/convert-to-blocks/compare/1.1.1...1.2.0 191 | [1.1.1]: https://github.com/10up/convert-to-blocks/compare/1.1.0...1.1.1 192 | [1.1.0]: https://github.com/10up/convert-to-blocks/compare/1.0.2...1.1.0 193 | [1.0.2]: https://github.com/10up/convert-to-blocks/compare/1.0.1...1.0.2 194 | [1.0.1]: https://github.com/10up/convert-to-blocks/compare/cf8c873...1.0.1 195 | [1.0.0]: https://github.com/10up/convert-to-blocks/tree/cf8c873ae4f88286723f6e996fbab4c1f8cf2940 196 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as 6 | contributors and maintainers pledge to making participation in our project and 7 | our community a harassment-free experience for everyone, regardless of age, body 8 | size, disability, ethnicity, sex characteristics, gender identity and expression, 9 | level of experience, education, socio-economic status, nationality, personal 10 | appearance, race, religion, or sexual identity and orientation. 11 | 12 | ## Our Standards 13 | 14 | Examples of behavior that contributes to creating a positive environment 15 | include: 16 | 17 | * Using welcoming and inclusive language 18 | * Being respectful of differing viewpoints and experiences 19 | * Gracefully accepting constructive criticism 20 | * Focusing on what is best for the community 21 | * Showing empathy towards other community members 22 | 23 | Examples of unacceptable behavior by participants include: 24 | 25 | * The use of sexualized language or imagery and unwelcome sexual attention or 26 | advances 27 | * Trolling, insulting/derogatory comments, and personal or political attacks 28 | * Public or private harassment 29 | * Publishing others' private information, such as a physical or electronic 30 | address, without explicit permission 31 | * Other conduct which could reasonably be considered inappropriate in a 32 | professional setting 33 | 34 | ## Our Responsibilities 35 | 36 | Project maintainers are responsible for clarifying the standards of acceptable 37 | behavior and are expected to take appropriate and fair corrective action in 38 | response to any instances of unacceptable behavior. 39 | 40 | Project maintainers have the right and responsibility to remove, edit, or 41 | reject comments, commits, code, wiki edits, issues, and other contributions 42 | that are not aligned to this Code of Conduct, or to ban temporarily or 43 | permanently any contributor for other behaviors that they deem inappropriate, 44 | threatening, offensive, or harmful. 45 | 46 | ## Scope 47 | 48 | This Code of Conduct applies both within project spaces and in public spaces 49 | when an individual is representing the project or its community. Examples of 50 | representing a project or community include using an official project e-mail 51 | address, posting via an official social media account, or acting as an appointed 52 | representative at an online or offline event. Representation of a project may be 53 | further defined and clarified by project maintainers. 54 | 55 | ## Enforcement 56 | 57 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 58 | reported by contacting the project team at opensource@10up.com. All 59 | complaints will be reviewed and investigated and will result in a response that 60 | is deemed necessary and appropriate to the circumstances. The project team is 61 | obligated to maintain confidentiality with regard to the reporter of an incident. 62 | Further details of specific enforcement policies may be posted separately. 63 | 64 | Project maintainers who do not follow or enforce the Code of Conduct in good 65 | faith may face temporary or permanent repercussions as determined by other 66 | members of the project's leadership. 67 | 68 | ## Attribution 69 | 70 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, 71 | available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html 72 | 73 | [homepage]: https://www.contributor-covenant.org 74 | 75 | For answers to common questions about this code of conduct, see 76 | https://www.contributor-covenant.org/faq 77 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing and Maintaining 2 | 3 | First, thank you for taking the time to contribute! 4 | 5 | The following is a set of guidelines for contributors as well as information and instructions around our maintenance process. The two are closely tied together in terms of how we all work together and set expectations, so while you may not need to know everything in here to submit an issue or pull request, it's best to keep them in the same document. 6 | 7 | ## Ways to contribute 8 | 9 | Contributing isn't just writing code - it's anything that improves the project. All contributions for Convert to Blocks are managed right here on GitHub. Here are some ways you can help: 10 | 11 | ### Reporting bugs 12 | 13 | If you're running into an issue with the plugin, please take a look through [existing issues](https://github.com/10up/convert-to-blocks/issues) and [open a new one](https://github.com/10up/convert-to-blocks/issues/new) if needed. If you're able, include steps to reproduce, environment information, and screenshots/screencasts as relevant. 14 | 15 | ### Suggesting enhancements 16 | 17 | New features and enhancements are also managed via [issues](https://github.com/10up/convert-to-blocks/issues). 18 | 19 | ### Pull requests 20 | 21 | Pull requests represent a proposed solution to a specified problem. They should always reference an issue that describes the problem and contains discussion about the problem itself. Discussion on pull requests should be limited to the pull request itself, i.e. code review. 22 | 23 | For more on how 10up writes and manages code, check out our [10up Engineering Best Practices](https://10up.github.io/Engineering-Best-Practices/). 24 | 25 | ## Workflow 26 | 27 | The `develop` branch is the development branch which means it contains the next version to be released. `trunk` contains the latest released version as reflected in the WordPress.org plugin repository. Always work on the `develop` branch and open up PRs against `develop`. 28 | 29 | ## Release instructions 30 | 31 | 1. Branch: Starting from `develop`, cut a release branch named `release/X.Y.Z` for your changes. 32 | 1. Version bump: Bump the version number in `config.php`, `convert-to-blocks.php`, `readme.txt`, `package.json` and `package-lock.json` if it does not already reflect the version being released. Update both the plugin "Version:" property in `convert-to-blocks.php` and the plugin `$plugin_version` constant in `config.php`. 33 | 1. Changelog: Add/update the changelog in `CHANGELOG.md` and `readme.txt`. 34 | 1. Props: update `CREDITS.md` with any new contributors, confirm maintainers are accurate. 35 | 1. New files: Check to be sure any new files/paths that are unnecessary in the production version are included in `.distignore`. 36 | 1. Readme updates: Make any other readme changes as necessary. `README.md` is geared toward GitHub and `readme.txt` contains WordPress.org-specific content. The two are slightly different. 37 | 1. Merge: Make a non-fast-forward merge from your release branch to `develop` (or merge the pull request), then do the same for `develop` into `trunk`, ensuring you pull the most recent changes into `develop` first (`git checkout develop && git pull origin develop && git checkout trunk && git merge --no-ff develop`). `trunk` contains the stable development version. 38 | 1. Push: Push your `trunk` branch to GitHub (e.g. `git push origin trunk`). 39 | 1. [Compare](https://github.com/10up/convert-to-blocks/compare/trunk...develop) trunk to develop to ensure no additional changes were missed. 40 | 1. Test the pre-release ZIP locally by [downloading](https://github.com/10up/convert-to-blocks/actions/workflows/build-release-zip.yml) it from the Build release zip action artifact and installing it locally. Ensure this zip has all the files we expect, that it installs and activates correctly and that all basic functionality is working 41 | 1. Release: Create a [new release](https://github.com/10up/convert-to-blocks/releases/new), naming the tag and the release with the new version number, and targeting the `trunk` branch. Paste the changelog from `CHANGELOG.md` into the body of the release and include a link to the [closed issues on the milestone](https://github.com/10up/convert-to-blocks/milestone/#?closed=1). The release should now appear under [releases](https://github.com/10up/convert-to-blocks/releases). 42 | 1. SVN: Wait for the [GitHub Action](https://github.com/10up/convert-to-blocks/actions) to finish deploying to the WordPress.org repository. If all goes well, users with SVN commit access for that plugin will receive an emailed diff of changes. 43 | 1. Check WordPress.org: Ensure that the changes are live on https://wordpress.org/plugins/convert-to-blocks/. This may take a few minutes. 44 | 1. Close the milestone: Edit the [milestone](https://github.com/10up/convert-to-blocks/milestone/#) with release date (in the `Due date (optional)` field) and link to GitHub release (in the `Description` field), then close the milestone. 45 | 1. Punt incomplete items: If any open issues or PRs which were milestoned for `X.Y.Z` do not make it into the release, update their milestone to `X+1.0.0`, `X.Y+1.0`, `X.Y.Z+1`, or `Future Release` 46 | -------------------------------------------------------------------------------- /CREDITS.md: -------------------------------------------------------------------------------- 1 | The following acknowledges the Maintainers for this repository, those who have Contributed to this repository (via bug reports, code, design, ideas, project management, translation, testing, etc.), and any Libraries utilized. 2 | 3 | ## Maintainers 4 | 5 | The following individuals are responsible for curating the list of issues, responding to pull requests, and ensuring regular releases happen. 6 | 7 | [Darshan Sawardekar (@dsawardekar)](https://github.com/dsawardekar), [Taylor Lovett (@tlovett1)](https://github.com/tlovett1), [Jeffrey Paul (@jeffpaul)](https://github.com/jeffpaul). 8 | 9 | ## Contributors 10 | 11 | Thank you to all the people who have already contributed to this repository via bug reports, code, design, ideas, project management, translation, testing, etc. 12 | 13 | [Darshan Sawardekar (@dsawardekar)](https://github.com/dsawardekar), [Taylor Lovett (@tlovett1)](https://github.com/tlovett1), [Tyler Bailey (@TylerB24890)](https://github.com/TylerB24890), [Jeffrey Paul (@jeffpaul)](https://github.com/jeffpaul), [Tung Du (@dinhtungdu)](https://github.com/dinhtungdu), [Diana Padron](https://profiles.wordpress.org/dianapadron), [Barry Ceelen (@barryceelen)](https://github.com/barryceelen), [Darin Kotter (@dkotter)](https://github.com/dkotter), [Peter Sorensen (@psorensen)](https://github.com/psorensen), [Brandon Berg (@BBerg10up)](https://github.com/BBerg10up), [Hashim Warren (@hashimwarren)](https://github.com/hashimwarren), [Jason LeMahieu (@MadtownLems)](https://github.com/MadtownLems), [Simon Carne](https://profiles.wordpress.org/scarne/), [Rodrigo Arias (@rodrigo-arias)](https://github.com/rodrigo-arias), [Gary Thayer (@gthayer)](https://github.com/gthayer), [Faisal Alvi (@faisal-alvi)](https://github.com/faisal-alvi), [Siddharth Thevaril (@Sidsector9)](https://github.com/Sidsector9), [Max Lyuchin (@cadic)](https://github.com/cadic), [Dharmesh Patel (@iamdharmesh)](https://github.com/iamdharmesh), [Mohit Dadhich (@mohitwp)](https://github.com/mohitwp), [GitHub Dependabot (@dependabot)](https://github.com/apps/dependabot), [Peter Wilson (@peterwilsoncc)](https://github.com/peterwilsoncc), [Andrew Norcross (@norcross)](https://github.com/norcross), [Quamruz Zaman (@zamanq)](https://github.com/zamanq), [Mehul Gohil (@mehul0810)](https://github.com/mehul0810), [Akshit Sethi (@akshitsethi)](https://github.com/akshitsethi), [Terrance Orletsky (@terrance-orletsky-d7)](https://github.com/terrance-orletsky-d7), [Philipp (@ouun)](https://github.com/ouun), [Sanket Parmar (@sanketio)](https://github.com/sanketio), [Konstantinos Galanakis (@kmgalanakis)](https://github.com/kmgalanakis), [Vikram Moparthy (@vikrampm1)](https://github.com/vikrampm1), [Barney Jeffries (@barneyjeffries)](https://github.com/barneyjeffries), [Ravinder Kumar (@ravinderk)](https://github.com/ravinderk), [Felipe Elia (@felipeelia)](https://github.com/felipeelia), [Dan Burzynski (@c0ntax)](https://github.com/c0ntax), [Storm Rockwell (@stormrockwell)](https://github.com/stormrockwell), [Harshal Kadu (@QAharshalkadu)](https://github.com/QAharshalkadu), [Sudip Dadhaniya (@sudip-md)](https://github.com/sudip-md), [Aaron Jorbin (@aaronjorbin)](https://github.com/aaronjorbin), [Ankit K Gupta (@ankitguptaindia)](https://github.com/ankitguptaindia), [Maarten de Splenter (@mdesplenter)](https://github.com/mdesplenter), [Colin Swinney (@colinswinney)](https://github.com/colinswinney), [Gary Jones (@GaryJones)](https://github.com/GaryJones). 14 | 15 | ## Libraries 16 | 17 | The following software libraries are utilized in this repository. 18 | 19 | n/a. 20 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 2, June 1991 3 | 4 | Copyright (C) 1989, 1991 Free Software Foundation, Inc., 5 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 6 | Everyone is permitted to copy and distribute verbatim copies 7 | of this license document, but changing it is not allowed. 8 | 9 | Preamble 10 | 11 | The licenses for most software are designed to take away your 12 | freedom to share and change it. By contrast, the GNU General Public 13 | License is intended to guarantee your freedom to share and change free 14 | software--to make sure the software is free for all its users. This 15 | General Public License applies to most of the Free Software 16 | Foundation's software and to any other program whose authors commit to 17 | using it. (Some other Free Software Foundation software is covered by 18 | the GNU Lesser General Public License instead.) You can apply it to 19 | your programs, too. 20 | 21 | When we speak of free software, we are referring to freedom, not 22 | price. Our General Public Licenses are designed to make sure that you 23 | have the freedom to distribute copies of free software (and charge for 24 | this service if you wish), that you receive source code or can get it 25 | if you want it, that you can change the software or use pieces of it 26 | in new free programs; and that you know you can do these things. 27 | 28 | To protect your rights, we need to make restrictions that forbid 29 | anyone to deny you these rights or to ask you to surrender the rights. 30 | These restrictions translate to certain responsibilities for you if you 31 | distribute copies of the software, or if you modify it. 32 | 33 | For example, if you distribute copies of such a program, whether 34 | gratis or for a fee, you must give the recipients all the rights that 35 | you have. You must make sure that they, too, receive or can get the 36 | source code. And you must show them these terms so they know their 37 | rights. 38 | 39 | We protect your rights with two steps: (1) copyright the software, and 40 | (2) offer you this license which gives you legal permission to copy, 41 | distribute and/or modify the software. 42 | 43 | Also, for each author's protection and ours, we want to make certain 44 | that everyone understands that there is no warranty for this free 45 | software. If the software is modified by someone else and passed on, we 46 | want its recipients to know that what they have is not the original, so 47 | that any problems introduced by others will not reflect on the original 48 | authors' reputations. 49 | 50 | Finally, any free program is threatened constantly by software 51 | patents. We wish to avoid the danger that redistributors of a free 52 | program will individually obtain patent licenses, in effect making the 53 | program proprietary. To prevent this, we have made it clear that any 54 | patent must be licensed for everyone's free use or not licensed at all. 55 | 56 | The precise terms and conditions for copying, distribution and 57 | modification follow. 58 | 59 | GNU GENERAL PUBLIC LICENSE 60 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 61 | 62 | 0. This License applies to any program or other work which contains 63 | a notice placed by the copyright holder saying it may be distributed 64 | under the terms of this General Public License. The "Program", below, 65 | refers to any such program or work, and a "work based on the Program" 66 | means either the Program or any derivative work under copyright law: 67 | that is to say, a work containing the Program or a portion of it, 68 | either verbatim or with modifications and/or translated into another 69 | language. (Hereinafter, translation is included without limitation in 70 | the term "modification".) Each licensee is addressed as "you". 71 | 72 | Activities other than copying, distribution and modification are not 73 | covered by this License; they are outside its scope. The act of 74 | running the Program is not restricted, and the output from the Program 75 | is covered only if its contents constitute a work based on the 76 | Program (independent of having been made by running the Program). 77 | Whether that is true depends on what the Program does. 78 | 79 | 1. You may copy and distribute verbatim copies of the Program's 80 | source code as you receive it, in any medium, provided that you 81 | conspicuously and appropriately publish on each copy an appropriate 82 | copyright notice and disclaimer of warranty; keep intact all the 83 | notices that refer to this License and to the absence of any warranty; 84 | and give any other recipients of the Program a copy of this License 85 | along with the Program. 86 | 87 | You may charge a fee for the physical act of transferring a copy, and 88 | you may at your option offer warranty protection in exchange for a fee. 89 | 90 | 2. You may modify your copy or copies of the Program or any portion 91 | of it, thus forming a work based on the Program, and copy and 92 | distribute such modifications or work under the terms of Section 1 93 | above, provided that you also meet all of these conditions: 94 | 95 | a) You must cause the modified files to carry prominent notices 96 | stating that you changed the files and the date of any change. 97 | 98 | b) You must cause any work that you distribute or publish, that in 99 | whole or in part contains or is derived from the Program or any 100 | part thereof, to be licensed as a whole at no charge to all third 101 | parties under the terms of this License. 102 | 103 | c) If the modified program normally reads commands interactively 104 | when run, you must cause it, when started running for such 105 | interactive use in the most ordinary way, to print or display an 106 | announcement including an appropriate copyright notice and a 107 | notice that there is no warranty (or else, saying that you provide 108 | a warranty) and that users may redistribute the program under 109 | these conditions, and telling the user how to view a copy of this 110 | License. (Exception: if the Program itself is interactive but 111 | does not normally print such an announcement, your work based on 112 | the Program is not required to print an announcement.) 113 | 114 | These requirements apply to the modified work as a whole. If 115 | identifiable sections of that work are not derived from the Program, 116 | and can be reasonably considered independent and separate works in 117 | themselves, then this License, and its terms, do not apply to those 118 | sections when you distribute them as separate works. But when you 119 | distribute the same sections as part of a whole which is a work based 120 | on the Program, the distribution of the whole must be on the terms of 121 | this License, whose permissions for other licensees extend to the 122 | entire whole, and thus to each and every part regardless of who wrote it. 123 | 124 | Thus, it is not the intent of this section to claim rights or contest 125 | your rights to work written entirely by you; rather, the intent is to 126 | exercise the right to control the distribution of derivative or 127 | collective works based on the Program. 128 | 129 | In addition, mere aggregation of another work not based on the Program 130 | with the Program (or with a work based on the Program) on a volume of 131 | a storage or distribution medium does not bring the other work under 132 | the scope of this License. 133 | 134 | 3. You may copy and distribute the Program (or a work based on it, 135 | under Section 2) in object code or executable form under the terms of 136 | Sections 1 and 2 above provided that you also do one of the following: 137 | 138 | a) Accompany it with the complete corresponding machine-readable 139 | source code, which must be distributed under the terms of Sections 140 | 1 and 2 above on a medium customarily used for software interchange; or, 141 | 142 | b) Accompany it with a written offer, valid for at least three 143 | years, to give any third party, for a charge no more than your 144 | cost of physically performing source distribution, a complete 145 | machine-readable copy of the corresponding source code, to be 146 | distributed under the terms of Sections 1 and 2 above on a medium 147 | customarily used for software interchange; or, 148 | 149 | c) Accompany it with the information you received as to the offer 150 | to distribute corresponding source code. (This alternative is 151 | allowed only for noncommercial distribution and only if you 152 | received the program in object code or executable form with such 153 | an offer, in accord with Subsection b above.) 154 | 155 | The source code for a work means the preferred form of the work for 156 | making modifications to it. For an executable work, complete source 157 | code means all the source code for all modules it contains, plus any 158 | associated interface definition files, plus the scripts used to 159 | control compilation and installation of the executable. However, as a 160 | special exception, the source code distributed need not include 161 | anything that is normally distributed (in either source or binary 162 | form) with the major components (compiler, kernel, and so on) of the 163 | operating system on which the executable runs, unless that component 164 | itself accompanies the executable. 165 | 166 | If distribution of executable or object code is made by offering 167 | access to copy from a designated place, then offering equivalent 168 | access to copy the source code from the same place counts as 169 | distribution of the source code, even though third parties are not 170 | compelled to copy the source along with the object code. 171 | 172 | 4. You may not copy, modify, sublicense, or distribute the Program 173 | except as expressly provided under this License. Any attempt 174 | otherwise to copy, modify, sublicense or distribute the Program is 175 | void, and will automatically terminate your rights under this License. 176 | However, parties who have received copies, or rights, from you under 177 | this License will not have their licenses terminated so long as such 178 | parties remain in full compliance. 179 | 180 | 5. You are not required to accept this License, since you have not 181 | signed it. However, nothing else grants you permission to modify or 182 | distribute the Program or its derivative works. These actions are 183 | prohibited by law if you do not accept this License. Therefore, by 184 | modifying or distributing the Program (or any work based on the 185 | Program), you indicate your acceptance of this License to do so, and 186 | all its terms and conditions for copying, distributing or modifying 187 | the Program or works based on it. 188 | 189 | 6. Each time you redistribute the Program (or any work based on the 190 | Program), the recipient automatically receives a license from the 191 | original licensor to copy, distribute or modify the Program subject to 192 | these terms and conditions. You may not impose any further 193 | restrictions on the recipients' exercise of the rights granted herein. 194 | You are not responsible for enforcing compliance by third parties to 195 | this License. 196 | 197 | 7. If, as a consequence of a court judgment or allegation of patent 198 | infringement or for any other reason (not limited to patent issues), 199 | conditions are imposed on you (whether by court order, agreement or 200 | otherwise) that contradict the conditions of this License, they do not 201 | excuse you from the conditions of this License. If you cannot 202 | distribute so as to satisfy simultaneously your obligations under this 203 | License and any other pertinent obligations, then as a consequence you 204 | may not distribute the Program at all. For example, if a patent 205 | license would not permit royalty-free redistribution of the Program by 206 | all those who receive copies directly or indirectly through you, then 207 | the only way you could satisfy both it and this License would be to 208 | refrain entirely from distribution of the Program. 209 | 210 | If any portion of this section is held invalid or unenforceable under 211 | any particular circumstance, the balance of the section is intended to 212 | apply and the section as a whole is intended to apply in other 213 | circumstances. 214 | 215 | It is not the purpose of this section to induce you to infringe any 216 | patents or other property right claims or to contest validity of any 217 | such claims; this section has the sole purpose of protecting the 218 | integrity of the free software distribution system, which is 219 | implemented by public license practices. Many people have made 220 | generous contributions to the wide range of software distributed 221 | through that system in reliance on consistent application of that 222 | system; it is up to the author/donor to decide if he or she is willing 223 | to distribute software through any other system and a licensee cannot 224 | impose that choice. 225 | 226 | This section is intended to make thoroughly clear what is believed to 227 | be a consequence of the rest of this License. 228 | 229 | 8. If the distribution and/or use of the Program is restricted in 230 | certain countries either by patents or by copyrighted interfaces, the 231 | original copyright holder who places the Program under this License 232 | may add an explicit geographical distribution limitation excluding 233 | those countries, so that distribution is permitted only in or among 234 | countries not thus excluded. In such case, this License incorporates 235 | the limitation as if written in the body of this License. 236 | 237 | 9. The Free Software Foundation may publish revised and/or new versions 238 | of the General Public License from time to time. Such new versions will 239 | be similar in spirit to the present version, but may differ in detail to 240 | address new problems or concerns. 241 | 242 | Each version is given a distinguishing version number. If the Program 243 | specifies a version number of this License which applies to it and "any 244 | later version", you have the option of following the terms and conditions 245 | either of that version or of any later version published by the Free 246 | Software Foundation. If the Program does not specify a version number of 247 | this License, you may choose any version ever published by the Free Software 248 | Foundation. 249 | 250 | 10. If you wish to incorporate parts of the Program into other free 251 | programs whose distribution conditions are different, write to the author 252 | to ask for permission. For software which is copyrighted by the Free 253 | Software Foundation, write to the Free Software Foundation; we sometimes 254 | make exceptions for this. Our decision will be guided by the two goals 255 | of preserving the free status of all derivatives of our free software and 256 | of promoting the sharing and reuse of software generally. 257 | 258 | NO WARRANTY 259 | 260 | 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY 261 | FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN 262 | OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES 263 | PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED 264 | OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 265 | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS 266 | TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE 267 | PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, 268 | REPAIR OR CORRECTION. 269 | 270 | 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 271 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR 272 | REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, 273 | INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING 274 | OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED 275 | TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY 276 | YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER 277 | PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE 278 | POSSIBILITY OF SUCH DAMAGES. 279 | 280 | END OF TERMS AND CONDITIONS 281 | 282 | How to Apply These Terms to Your New Programs 283 | 284 | If you develop a new program, and you want it to be of the greatest 285 | possible use to the public, the best way to achieve this is to make it 286 | free software which everyone can redistribute and change under these terms. 287 | 288 | To do so, attach the following notices to the program. It is safest 289 | to attach them to the start of each source file to most effectively 290 | convey the exclusion of warranty; and each file should have at least 291 | the "copyright" line and a pointer to where the full notice is found. 292 | 293 | 294 | Copyright (C) 295 | 296 | This program is free software; you can redistribute it and/or modify 297 | it under the terms of the GNU General Public License as published by 298 | the Free Software Foundation; either version 2 of the License, or 299 | (at your option) any later version. 300 | 301 | This program is distributed in the hope that it will be useful, 302 | but WITHOUT ANY WARRANTY; without even the implied warranty of 303 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 304 | GNU General Public License for more details. 305 | 306 | You should have received a copy of the GNU General Public License along 307 | with this program; if not, write to the Free Software Foundation, Inc., 308 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 309 | 310 | Also add information on how to contact you by electronic and paper mail. 311 | 312 | If the program is interactive, make it output a short notice like this 313 | when it starts in an interactive mode: 314 | 315 | Gnomovision version 69, Copyright (C) year name of author 316 | Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 317 | This is free software, and you are welcome to redistribute it 318 | under certain conditions; type `show c' for details. 319 | 320 | The hypothetical commands `show w' and `show c' should show the appropriate 321 | parts of the General Public License. Of course, the commands you use may 322 | be called something other than `show w' and `show c'; they could even be 323 | mouse-clicks or menu items--whatever suits your program. 324 | 325 | You should also get your employer (if you work as a programmer) or your 326 | school, if any, to sign a "copyright disclaimer" for the program, if 327 | necessary. Here is a sample; alter the names: 328 | 329 | Yoyodyne, Inc., hereby disclaims all copyright interest in the program 330 | `Gnomovision' (which makes passes at compilers) written by James Hacker. 331 | 332 | , 1 April 1989 333 | Ty Coon, President of Vice 334 | 335 | This General Public License does not permit incorporating your program into 336 | proprietary programs. If your program is a subroutine library, you may 337 | consider it more useful to permit linking proprietary applications with the 338 | library. If this is what you want to do, use the GNU Lesser General 339 | Public License instead of this License. 340 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Convert to Blocks 2 | 3 | ![Convert to Blocks](https://github.com/10up/convert-to-blocks/blob/develop/.wordpress-org/banner-1544x500.png) 4 | 5 | [![Support Level](https://img.shields.io/badge/support-stable-blue.svg)](#support-level) ![WordPress Plugin Required PHP Version](https://img.shields.io/wordpress/plugin/required-php/convert-to-blocks?label=Requires%20PHP) ![WordPress Plugin: Required WP Version](https://img.shields.io/wordpress/plugin/wp-version/convert-to-blocks?label=Requires%20WordPress) ![WordPress Plugin: Tested WP Version](https://img.shields.io/wordpress/plugin/tested/convert-to-blocks?label=WordPress%20tested%20up%20to) [![GPL-2.0-or-later License](https://img.shields.io/github/license/10up/convert-to-blocks.svg)](https://github.com/10up/convert-to-blocks/blob/develop/LICENSE.md) [![Dependency Review](https://github.com/10up/convert-to-blocks/actions/workflows/dependency-review.yml/badge.svg)](https://github.com/10up/convert-to-blocks/actions/workflows/dependency-review.yml) [![E2E test](https://github.com/10up/convert-to-blocks/actions/workflows/cypress.yml/badge.svg)](https://github.com/10up/convert-to-blocks/actions/workflows/cypress.yml) [![Linting](https://github.com/10up/convert-to-blocks/actions/workflows/lint.yml/badge.svg)](https://github.com/10up/convert-to-blocks/actions/workflows/lint.yml) [![CodeQL](https://github.com/10up/convert-to-blocks/actions/workflows/github-code-scanning/codeql/badge.svg)](https://github.com/10up/convert-to-blocks/actions/workflows/github-code-scanning/codeql) [![WordPress Plugin Version](https://img.shields.io/wordpress/plugin/v/convert-to-blocks?logo=wordpress&logoColor=FFFFFF&label=Playground%20Demo&labelColor=3858E9&color=3858E9)](https://playground.wordpress.net/?blueprint-url=https://raw.githubusercontent.com/10up/convert-to-blocks/develop/.wordpress-org/blueprints/blueprint.json) 6 | 7 | > Transforms classic editor content to blocks on-the-fly. 8 | 9 | ## Overview 10 | 11 | Convert to Blocks is a WordPress plugin that transforms classic editor content to blocks on-the-fly. After installing Gutenberg or upgrading to WordPress 5.0+, your content will be displayed in "Classic Editor Blocks". While these blocks are completely functional and will display fine on the frontend of your website, they do not empower editors to fully make use of the block editing experience. In order to do so, your classic editor posts need to be converted to blocks. This plugin does that for you "on the fly". When an editor goes to edit a classic post, the content will be parsed into blocks. When the editor saves the post, the new structure will be saved into the database. This strategy reduces risk as you are only altering database values for content that needs to be changed. 12 | 13 | When combined with the [Block Catalog Plugin](https://github.com/10up/block-catalog), Convert to Blocks can be used to bulk convert only classic editor content to blocks. This is especially useful for sites with a combination of classic and block editor content. 14 | 15 | ### Bulk migration of Classic Editor items to the Block Editor 16 | 17 | The `wp convert-to-blocks start` [WP-CLI command](https://github.com/10up/convert-to-blocks/blob/4df0e970c51eee8d84e3edf3c6210dc10011d574/includes/ConvertToBlocks/MigrationCommand.php) that converts posts iteratively in the browser without requiring any manual input. One caveat worth mentioning is that Gutenberg is a CPU intensive application. You will want to keep your computer plugged in before doing this! 18 | 19 | ![Demo of WP-CLI command](.wordpress-org/screenshot-1.gif "Example of a convert-to-blocks WP-CLI command bulk migration") 20 | 21 | ## Requirements 22 | 23 | - Inner Blocks Transforms is only supported with the Gutenberg Plugin 10.9.0+ 24 | 25 | ## Installation 26 | 27 | 1. Clone the repository into your `/plugins` directory. 28 | 2. Inside the repository directory, run `npm install` and then `npm run build`. 29 | 3. Inside the repository directory, run `composer install`. 30 | 31 | ## Command Line Options 32 | 33 | ```bash 34 | NAME 35 | 36 | wp convert-to-blocks start 37 | 38 | DESCRIPTION 39 | 40 | Starts a new Migration. The command prints the URL that must be opened in a browser to connect it to the WP CLI. 41 | 42 | SYNOPSIS 43 | 44 | wp convert-to-blocks start [--post_type=] [--per_page=] [--page=] [--only=] [--catalog] [--reset] 45 | 46 | OPTIONS 47 | 48 | [--post_type=] 49 | Optional comma delimited list of post types to migrate. Defaults to post,page 50 | 51 | [--per_page=] 52 | Optional number of posts to migrate per batch. Defaults to no limit. Combine with --page to paginate. 53 | 54 | [--page=] 55 | Optional page number to start migration from. Defaults to 1. 56 | 57 | [--only=] 58 | Optional comma delimited list of post ids to migrate. 59 | 60 | [--catalog] 61 | Optional flag to only migrate classic editor tagged posts. Requires that Block 62 | Catalog plugin is present and has been indexed. 63 | 64 | [--reset] 65 | Stops any currently running migration and resets the migration state. 66 | 67 | NAME 68 | 69 | wp convert-to-blocks stop 70 | 71 | DESCRIPTION 72 | 73 | Stops the currently running migration if active. 74 | 75 | NAME 76 | 77 | wp convert-to-blocks status 78 | 79 | DESCRIPTION 80 | 81 | Prints the status of the currently running migration. 82 | 83 | ``` 84 | 85 | ## Frequently Asked Questions 86 | 87 | ### How Do I Know It's Working? 88 | 89 | Find a classic editor in the post, try to navigate away from the page. You will get an error saying your changes will be discarded. This is because Convert to Blocks converted your content to blocks on the fly and those changes will be saved when you update the post. 90 | 91 | ### Will Convert to Blocks Handle My Custom Blocks? 92 | 93 | By default it will not. 94 | 95 | ### Will Convert to Blocks Handle Nested Blocks? 96 | 97 | Nested / Inner Block support does not work with Gutenberg bundled with WordPress Core <=5.7.2. This feature needs the Gutenberg Plugin >=10.9.0. 98 | 99 | ## Support Level 100 | 101 | **Stable:** 10up is not planning to develop any new features for this, but will still respond to bug reports and security concerns. We welcome PRs, but any that include new features should be small and easy to integrate and should not include breaking changes. We otherwise intend to keep this tested up to the most recent version of WordPress. 102 | 103 | ## Changelog 104 | 105 | A complete listing of all notable changes to Convert to Blocks are documented in [CHANGELOG.md](https://github.com/10up/convert-to-blocks/blob/develop/CHANGELOG.md). 106 | 107 | ## Contributing 108 | 109 | Please read [CODE_OF_CONDUCT.md](https://github.com/10up/convert-to-blocks/blob/develop/CODE_OF_CONDUCT.md) for details on our code of conduct, [CONTRIBUTING.md](https://github.com/10up/convert-to-blocks/blob/develop/CONTRIBUTING.md) for details on the process for submitting pull requests to us, and [CREDITS.md](https://github.com/10up/convert-to-blocks/blob/develop/CREDITS.md) for a listing of maintainers of, contributors to, and libraries used by Convert to Blocks. 110 | 111 | ## Like what you see? 112 | 113 |

114 | 115 |

116 | -------------------------------------------------------------------------------- /assets/js/editor/editor.js: -------------------------------------------------------------------------------- 1 | import ClassicBlockTransformer from './transform/ClassicBlockTransformer'; 2 | import MigrationClient from './transform/MigrationClient'; 3 | 4 | let loaded = false; 5 | 6 | /** 7 | * ConvertToBlocksSupport connects the JS implementation of 8 | * Convert to Blocks to Gutenberg JS. 9 | */ 10 | class ConvertToBlocksEditorSupport { 11 | /** 12 | * Returns the singleton instance of ConvertToBlocksEditorSupport. 13 | * 14 | * @returns {ConvertToBlocksEditorSupport} 15 | */ 16 | static getInstance() { 17 | if (!this.instance) { 18 | this.instance = new ConvertToBlocksEditorSupport(); 19 | } 20 | 21 | return this.instance; 22 | } 23 | 24 | /** 25 | * Activates the ConvertToBlocksEditorSupport 26 | */ 27 | enable() { 28 | document.addEventListener('DOMContentLoaded', this.didBlockEditorLoad.bind(this)); 29 | } 30 | 31 | /** 32 | * Executes the classic to block transform. 33 | */ 34 | didBlockEditorLoad() { 35 | const { registerPlugin } = window.wp.plugins; 36 | const transformer = new ClassicBlockTransformer(); 37 | 38 | if (!registerPlugin) { 39 | return; 40 | } 41 | 42 | registerPlugin('convert-to-blocks', { 43 | render: () => { 44 | // Don't render more than once, to avoid triggering multiple migrations 45 | if (loaded) { 46 | return null; 47 | } 48 | 49 | loaded = true; 50 | 51 | // This delay allows Gutenberg to initialize legacy content into freeform blocks 52 | setTimeout(() => { 53 | const result = transformer.execute(); 54 | const config = window.convert_to_blocks_agent || false; 55 | 56 | // if no migration config, then ignore this request 57 | if (!config) { 58 | return null; 59 | } 60 | 61 | const client = new MigrationClient(config); 62 | 63 | // if no blocks transformed, then we can jump to the next post 64 | if (!result) { 65 | client.next(); 66 | return null; 67 | } 68 | 69 | const saveDelay = config?.agent?.save_delay || 0; 70 | 71 | if (saveDelay > 0) { 72 | setTimeout(() => { 73 | client.save(); 74 | }, saveDelay); 75 | } else { 76 | client.save(); 77 | } 78 | 79 | return null; 80 | }, 500); 81 | 82 | return null; 83 | }, 84 | }); 85 | } 86 | } 87 | 88 | const support = ConvertToBlocksEditorSupport.getInstance(); 89 | support.enable(); 90 | 91 | export default ConvertToBlocksEditorSupport; 92 | -------------------------------------------------------------------------------- /assets/js/editor/transform/ClassicBlockTransformer.js: -------------------------------------------------------------------------------- 1 | /** 2 | * ClassicBlockTransformer upgrades classic content on the current document into 3 | * Gutenberg Blocks. 4 | * 5 | * Props: Ty Bailey & Gutenberg Core 6 | */ 7 | class ClassicBlockTransformer { 8 | /** 9 | * Saves a local wp object for later lookup. 10 | */ 11 | constructor() { 12 | this.wp = window.wp; 13 | this.didTransform = false; 14 | } 15 | 16 | /** 17 | * Runs the Classic to Gutenberg Block transform on the current document. 18 | * 19 | * @returns {boolean} The result of the transformation. 20 | */ 21 | execute() { 22 | const coreEditor = this.wp.data.select('core/block-editor'); 23 | const blocks = coreEditor.getBlocks(); 24 | 25 | if (this.validBlocks(blocks)) { 26 | /* Currently set to do 3 levels of recursion */ 27 | this.convertBlocks(blocks, 1, 3); 28 | } 29 | 30 | return this.didTransform; 31 | } 32 | 33 | /** 34 | * Converts the specified blocks and it's nested blocks if within 35 | * the depth constraints. 36 | * 37 | * Note: This function is called recursively. Specifying a very high 38 | * maxDepth can crash the browser. 39 | * 40 | * @param {Array} blocks The list of blocks to convert 41 | * @param {number} depth The current call stack depth 42 | * @param {number} maxDepth The maximum allowed depth 43 | */ 44 | convertBlocks(blocks, depth = 1, maxDepth = 3) { 45 | const n = blocks.length; 46 | let i; 47 | let block; 48 | let innerBlocks; 49 | 50 | for (i = 0; i < n; i++) { 51 | block = blocks[i]; 52 | innerBlocks = { block }; 53 | 54 | this.transform(block); 55 | 56 | if (depth <= maxDepth && this.validBlocks(innerBlocks)) { 57 | this.convertBlocks(innerBlocks, depth + 1, maxDepth); 58 | } 59 | } 60 | } 61 | 62 | /** 63 | * If the specified block is a freeform / classic block, replaces it 64 | * with corresponding Gutenberg blocks 65 | * 66 | * @param {object} block The current block object 67 | */ 68 | transform(block) { 69 | if (this.isFreeformBlock(block)) { 70 | this.wp.data 71 | .dispatch('core/block-editor') 72 | .replaceBlocks(block.clientId, this.blockHandler(block)); 73 | 74 | this.didTransform = true; 75 | } else if (block.innerBlocks && block.innerBlocks.length > 0) { 76 | this.convertBlocks(block.innerBlocks); 77 | } 78 | } 79 | 80 | /** 81 | * Uses the Core Raw HTML Block Handler to convert classic block to 82 | * corresponding blocks 83 | * 84 | * @param {object} block The block object 85 | * @returns {object} 86 | */ 87 | blockHandler(block) { 88 | const { blocks } = this.wp; 89 | 90 | return blocks.rawHandler({ 91 | HTML: blocks.getBlockContent(block), 92 | }); 93 | } 94 | 95 | /* helpers */ 96 | 97 | /** 98 | * Checks if the blocks specified are valid 99 | * 100 | * @param {Array} blocks The array of blocks 101 | * @returns {boolean} 102 | */ 103 | validBlocks(blocks) { 104 | return blocks && blocks.length > 0; 105 | } 106 | 107 | /** 108 | * Checks if the specified block is a freeform/classic block 109 | * 110 | * @param {object} block The block object 111 | * @returns {boolean} 112 | */ 113 | isFreeformBlock(block) { 114 | return block.name === 'core/freeform'; 115 | } 116 | } 117 | 118 | export default ClassicBlockTransformer; 119 | -------------------------------------------------------------------------------- /assets/js/editor/transform/MigrationClient.js: -------------------------------------------------------------------------------- 1 | const { wp, location } = window; 2 | 3 | /** 4 | * MigrationClient provides the client-side support for the BE MigrationAgent. 5 | */ 6 | class MigrationClient { 7 | /** 8 | * Initializes the client with the specified config settings. 9 | * 10 | * @param {object} config The convert to blocks config 11 | */ 12 | constructor(config) { 13 | this.config = config; 14 | this.saved = false; 15 | this.didNext = false; 16 | } 17 | 18 | /** 19 | * Saves the curent post by manually dispatching savePost. 20 | */ 21 | save() { 22 | // don't rerun after save 23 | if (this.saved) { 24 | return; 25 | } 26 | 27 | this.saved = true; 28 | 29 | const { dispatch, subscribe } = wp.data; 30 | const editor = dispatch('core/editor'); 31 | 32 | subscribe(this.didSave.bind(this)); 33 | editor.savePost(); 34 | } 35 | 36 | /** 37 | * On Post save, runs the next post migration. 38 | */ 39 | didSave() { 40 | const { select } = wp.data; 41 | const isSavingPost = select('core/editor').isSavingPost(); 42 | const isAutosavingPost = select('core/editor').isAutosavingPost(); 43 | 44 | if (isAutosavingPost && !isSavingPost) { 45 | return; 46 | } 47 | 48 | if (this.hasNext()) { 49 | this.next(); 50 | } 51 | } 52 | 53 | /** 54 | * Checks if there is a post in the queue. 55 | * 56 | * @returns {boolean} True or false if next is present. 57 | */ 58 | hasNext() { 59 | if (this.didNext) { 60 | return false; 61 | } 62 | 63 | if (!this.hasNextConfig()) { 64 | return false; 65 | } 66 | 67 | return this.config.agent.next; 68 | } 69 | 70 | /** 71 | * Navigates to the next post to migrate. 72 | */ 73 | next() { 74 | if (!this.hasNextConfig()) { 75 | return; 76 | } 77 | 78 | this.didNext = true; 79 | location.href = this.config.agent.next; 80 | } 81 | 82 | /** 83 | * Checks if the next migration post data is present in config. 84 | * 85 | * @returns {boolean} True or false if next config is present 86 | */ 87 | hasNextConfig() { 88 | if (!this.config) { 89 | return false; 90 | } 91 | 92 | if (!this.config.agent) { 93 | return false; 94 | } 95 | 96 | if (!this.config.agent.next) { 97 | return false; 98 | } 99 | 100 | return true; 101 | } 102 | } 103 | 104 | export default MigrationClient; 105 | -------------------------------------------------------------------------------- /babel.config.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Babel Config. 3 | * 4 | * @param {Object} api 5 | * @returns {{presets: {Object}}} 6 | */ 7 | module.exports = api => { 8 | 9 | /** 10 | * @link https://babeljs.io/docs/en/config-files#apicache 11 | */ 12 | api.cache.using( () => 'development' === process.env.NODE_ENV ); 13 | 14 | /** 15 | * Presets 16 | * 17 | * @link https://babeljs.io/docs/en/presets 18 | * @type {Array} 19 | */ 20 | const presets = [ 21 | [ 22 | /** 23 | * @link https://babeljs.io/docs/en/babel-preset-env#corejs 24 | */ 25 | '@babel/preset-env', 26 | { 27 | useBuiltIns: 'usage', 28 | corejs: { 29 | version: 3, 30 | proposals: true 31 | }, 32 | } 33 | ], 34 | ]; 35 | 36 | /** 37 | * Plugins 38 | * 39 | * @link https://babeljs.io/docs/en/plugins 40 | * @type {Array} 41 | */ 42 | const plugins = []; 43 | 44 | return { 45 | presets, 46 | plugins 47 | }; 48 | }; 49 | -------------------------------------------------------------------------------- /bin/install-wp-tests.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | if [ $# -lt 3 ]; then 4 | echo "usage: $0 [db-host] [wp-version] [skip-database-creation]" 5 | exit 1 6 | fi 7 | 8 | DB_NAME=$1 9 | DB_USER=$2 10 | DB_PASS=$3 11 | DB_HOST=${4-localhost} 12 | WP_VERSION=${5-latest} 13 | SKIP_DB_CREATE=${6-false} 14 | 15 | TMPDIR=${TMPDIR-/tmp} 16 | TMPDIR=$(echo $TMPDIR | sed -e "s/\/$//") 17 | WP_TESTS_DIR=${WP_TESTS_DIR-$TMPDIR/wordpress-tests-lib} 18 | WP_CORE_DIR=${WP_CORE_DIR-$TMPDIR/wordpress/} 19 | 20 | download() { 21 | if [ `which curl` ]; then 22 | curl -s "$1" > "$2"; 23 | elif [ `which wget` ]; then 24 | wget -nv -O "$2" "$1" 25 | fi 26 | } 27 | 28 | # Check if git is installed 29 | check_git_installed() { 30 | if ! command -v git > /dev/null; then 31 | echo "Error: git is not installed. Please install git and try again." 32 | exit 1 33 | fi 34 | } 35 | 36 | if [[ $WP_VERSION =~ ^[0-9]+\.[0-9]+$ ]]; then 37 | WP_TESTS_TAG="branches/$WP_VERSION" 38 | elif [[ $WP_VERSION =~ [0-9]+\.[0-9]+\.[0-9]+ ]]; then 39 | if [[ $WP_VERSION =~ [0-9]+\.[0-9]+\.[0] ]]; then 40 | # version x.x.0 means the first release of the major version, so strip off the .0 and download version x.x 41 | WP_TESTS_TAG="tags/${WP_VERSION%??}" 42 | else 43 | WP_TESTS_TAG="tags/$WP_VERSION" 44 | fi 45 | elif [[ $WP_VERSION == 'nightly' || $WP_VERSION == 'trunk' ]]; then 46 | WP_BRANCH="trunk" 47 | WP_TESTS_TAG="trunk" 48 | else 49 | # http serves a single offer, whereas https serves multiple. we only want one 50 | download http://api.wordpress.org/core/version-check/1.7/ /tmp/wp-latest.json 51 | grep '[0-9]+\.[0-9]+(\.[0-9]+)?' /tmp/wp-latest.json 52 | LATEST_VERSION=$(grep -o '"version":"[^"]*' /tmp/wp-latest.json | sed 's/"version":"//') 53 | if [[ -z "$LATEST_VERSION" ]]; then 54 | echo "Latest WordPress version could not be found" 55 | exit 1 56 | fi 57 | WP_BRANCH=$LATEST_VERSION 58 | WP_TESTS_TAG="tags/$LATEST_VERSION" 59 | fi 60 | 61 | set -ex 62 | 63 | install_wp() { 64 | 65 | if [ -d $WP_CORE_DIR ]; then 66 | return; 67 | fi 68 | 69 | mkdir -p $WP_CORE_DIR 70 | 71 | if [[ $WP_VERSION == 'nightly' || $WP_VERSION == 'trunk' ]]; then 72 | mkdir -p $TMPDIR/wordpress-nightly 73 | download https://wordpress.org/nightly-builds/wordpress-latest.zip $TMPDIR/wordpress-nightly/wordpress-nightly.zip 74 | unzip -q $TMPDIR/wordpress-nightly/wordpress-nightly.zip -d $TMPDIR/wordpress-nightly/ 75 | mv $TMPDIR/wordpress-nightly/wordpress/* $WP_CORE_DIR 76 | else 77 | if [ $WP_VERSION == 'latest' ]; then 78 | local ARCHIVE_NAME='latest' 79 | elif [[ $WP_VERSION =~ [0-9]+\.[0-9]+ ]]; then 80 | # https serves multiple offers, whereas http serves single. 81 | download https://api.wordpress.org/core/version-check/1.7/ $TMPDIR/wp-latest.json 82 | if [[ $WP_VERSION =~ [0-9]+\.[0-9]+\.[0] ]]; then 83 | # version x.x.0 means the first release of the major version, so strip off the .0 and download version x.x 84 | LATEST_VERSION=${WP_VERSION%??} 85 | else 86 | # otherwise, scan the releases and get the most up to date minor version of the major release 87 | local VERSION_ESCAPED=`echo $WP_VERSION | sed 's/\./\\\\./g'` 88 | LATEST_VERSION=$(grep -o '"version":"'$VERSION_ESCAPED'[^"]*' $TMPDIR/wp-latest.json | sed 's/"version":"//' | head -1) 89 | fi 90 | if [[ -z "$LATEST_VERSION" ]]; then 91 | local ARCHIVE_NAME="wordpress-$WP_VERSION" 92 | else 93 | local ARCHIVE_NAME="wordpress-$LATEST_VERSION" 94 | fi 95 | else 96 | local ARCHIVE_NAME="wordpress-$WP_VERSION" 97 | fi 98 | download https://wordpress.org/${ARCHIVE_NAME}.tar.gz $TMPDIR/wordpress.tar.gz 99 | tar --strip-components=1 -zxmf $TMPDIR/wordpress.tar.gz -C $WP_CORE_DIR 100 | fi 101 | 102 | download https://raw.github.com/markoheijnen/wp-mysqli/master/db.php $WP_CORE_DIR/wp-content/db.php 103 | } 104 | 105 | install_test_suite() { 106 | # portable in-place argument for both GNU sed and Mac OSX sed 107 | if [[ $(uname -s) == 'Darwin' ]]; then 108 | local ioption='-i .bak' 109 | else 110 | local ioption='-i' 111 | fi 112 | 113 | # set up testing suite if it doesn't yet exist 114 | if [ ! -d $WP_TESTS_DIR ]; then 115 | # set up testing suite 116 | mkdir -p $WP_TESTS_DIR 117 | check_git_installed 118 | git clone --depth 1 --branch $WP_BRANCH https://github.com/wordpress/wordpress-develop $TMPDIR/wordpress-develop 119 | rm -r $TMPDIR/wordpress-develop/.git 120 | mv $TMPDIR/wordpress-develop/tests/phpunit/includes/ $WP_TESTS_DIR/includes 121 | mv $TMPDIR/wordpress-develop/tests/phpunit/data/ $WP_TESTS_DIR/data 122 | rm -r $TMPDIR/wordpress-develop 123 | fi 124 | 125 | if [ ! -f wp-tests-config.php ]; then 126 | download https://raw.githubusercontent.com/wordpress/wordpress-develop/${WP_BRANCH}/wp-tests-config-sample.php "$WP_TESTS_DIR"/wp-tests-config.php 127 | # remove all forward slashes in the end 128 | WP_CORE_DIR=$(echo $WP_CORE_DIR | sed "s:/\+$::") 129 | sed $ioption "s:dirname( __FILE__ ) . '/src/':'$WP_CORE_DIR/':" "$WP_TESTS_DIR"/wp-tests-config.php 130 | sed $ioption "s/youremptytestdbnamehere/$DB_NAME/" "$WP_TESTS_DIR"/wp-tests-config.php 131 | sed $ioption "s/yourusernamehere/$DB_USER/" "$WP_TESTS_DIR"/wp-tests-config.php 132 | sed $ioption "s/yourpasswordhere/$DB_PASS/" "$WP_TESTS_DIR"/wp-tests-config.php 133 | sed $ioption "s|localhost|${DB_HOST}|" "$WP_TESTS_DIR"/wp-tests-config.php 134 | fi 135 | 136 | } 137 | 138 | install_db() { 139 | 140 | if [ ${SKIP_DB_CREATE} = "true" ]; then 141 | return 0 142 | fi 143 | 144 | # parse DB_HOST for port or socket references 145 | local PARTS=(${DB_HOST//\:/ }) 146 | local DB_HOSTNAME=${PARTS[0]}; 147 | local DB_SOCK_OR_PORT=${PARTS[1]}; 148 | local EXTRA="" 149 | 150 | if ! [ -z $DB_HOSTNAME ] ; then 151 | if [ $(echo $DB_SOCK_OR_PORT | grep -e '^[0-9]\{1,\}$') ]; then 152 | EXTRA=" --host=$DB_HOSTNAME --port=$DB_SOCK_OR_PORT --protocol=tcp" 153 | elif ! [ -z $DB_SOCK_OR_PORT ] ; then 154 | EXTRA=" --socket=$DB_SOCK_OR_PORT" 155 | elif ! [ -z $DB_HOSTNAME ] ; then 156 | EXTRA=" --host=$DB_HOSTNAME --protocol=tcp" 157 | fi 158 | fi 159 | 160 | # create database 161 | if [ -z $DB_PASS ] ; then 162 | echo 'No DB password' 163 | mysqladmin create $DB_NAME --user="$DB_USER" $EXTRA 164 | else 165 | mysqladmin create $DB_NAME --user="$DB_USER" --password="$DB_PASS"$EXTRA 166 | fi 167 | } 168 | 169 | install_wp 170 | install_test_suite 171 | install_db 172 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "10up/convert-to-blocks", 3 | "description": "Convert classic editor posts to blocks on the fly.", 4 | "type": "wordpress-plugin", 5 | "license": "GPL-2.0-or-later", 6 | "authors": [ 7 | { 8 | "name": "10up", 9 | "email": "opensource@10up.com", 10 | "homepage": "https://10up.com", 11 | "role": "Developer" 12 | } 13 | ], 14 | "require": { 15 | "php": ">=8.0" 16 | }, 17 | "autoload": { 18 | "psr-4": { 19 | "ConvertToBlocks\\": "includes/ConvertToBlocks/" 20 | } 21 | }, 22 | "require-dev": { 23 | "phpunit/phpunit": "~9.5.0", 24 | "10up/phpcs-composer": "dev-master", 25 | "yoast/phpunit-polyfills": "^1.1.0" 26 | }, 27 | "scripts": { 28 | "lint": "phpcs .", 29 | "lint-fix": "phpcbf .", 30 | "test": "phpunit", 31 | "setup-local-tests": "bash bin/install-wp-tests.sh convert_to_blocks_test root password mysql trunk" 32 | }, 33 | "config": { 34 | "allow-plugins": { 35 | "dealerdirect/phpcodesniffer-composer-installer": true 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /config.php: -------------------------------------------------------------------------------- 1 | =' ); 68 | } 69 | 70 | if ( ! convert_to_blocks_site_meets_php_requirements() ) { 71 | add_action( 72 | 'admin_notices', 73 | function() { 74 | ?> 75 |
76 |

77 | 86 |

87 |
88 | enable(); 140 | -------------------------------------------------------------------------------- /includes/ConvertToBlocks/Assets.php: -------------------------------------------------------------------------------- 1 | is_block_editor(); 80 | } 81 | 82 | } 83 | -------------------------------------------------------------------------------- /includes/ConvertToBlocks/ClassicEditorSupport.php: -------------------------------------------------------------------------------- 1 | container->has_classic_param() ) { 49 | return false; 50 | } 51 | 52 | if ( $this->container->post_supports_convert_to_blocks( $post ) ) { 53 | return true; 54 | } 55 | 56 | return $enabled; 57 | } 58 | 59 | } 60 | -------------------------------------------------------------------------------- /includes/ConvertToBlocks/MigrationAgent.php: -------------------------------------------------------------------------------- 1 | has_ctb_client_param() ) { 20 | return; 21 | } 22 | 23 | if ( ! $this->is_running() ) { 24 | return; 25 | } 26 | 27 | $posts_to_update = get_option( 'ctb_posts_to_update' ); 28 | $cursor = get_option( 'ctb_cursor' ); 29 | $posts_to_update = is_array( $posts_to_update ) ? $posts_to_update : []; 30 | $post_id = $posts_to_update[ $cursor ] ?? 0; 31 | 32 | wp_localize_script( 33 | 'convert_to_blocks_editor', 34 | 'convert_to_blocks_agent', 35 | [ 36 | 'agent' => [ 37 | 'next' => $this->next(), 38 | 'save_delay' => apply_filters( 'convert_to_blocks_save_delay', 0, $post_id ), 39 | ], 40 | ] 41 | ); 42 | } 43 | 44 | /** 45 | * Always register since this check needs to be happen later in lifecycle. 46 | * 47 | * @return bool 48 | */ 49 | public function can_register() { 50 | return true; 51 | } 52 | 53 | /** 54 | * Starts the batch conversion 55 | * 56 | * @param array $opts Optional opts. 57 | * @return bool 58 | */ 59 | public function start( $opts = [] ) { 60 | $posts_to_update = $this->get_posts_to_update( $opts ); 61 | 62 | if ( empty( $posts_to_update ) ) { 63 | return false; 64 | } 65 | 66 | update_option( 'ctb_running', 1 ); 67 | update_option( 'ctb_posts_to_update', $posts_to_update ); 68 | update_option( 'ctb_cursor', -1 ); 69 | 70 | return $this->next(); 71 | } 72 | 73 | /** 74 | * Stops the batch conversion if running and resets the previous session. 75 | */ 76 | public function stop() { 77 | update_option( 'ctb_running', 0 ); 78 | update_option( 'ctb_posts_to_update', [] ); 79 | update_option( 'ctb_cursor', -1 ); 80 | } 81 | 82 | /** 83 | * Returns the current status of the batch conversion. 84 | * 85 | * @return array 86 | */ 87 | public function get_status() { 88 | $running = get_option( 'ctb_running' ); 89 | $posts_to_update = get_option( 'ctb_posts_to_update' ); 90 | 91 | if ( empty( $posts_to_update ) ) { 92 | $posts_to_update = []; 93 | } 94 | 95 | $total = count( $posts_to_update ); 96 | $cursor = get_option( 'ctb_cursor' ); 97 | 98 | if ( $total > 0 ) { 99 | $progress = round( ( $cursor + 1 ) / $total * 100 ); 100 | } else { 101 | $progress = 0; 102 | } 103 | 104 | return [ 105 | 'running' => $running, 106 | 'cursor' => $cursor, 107 | 'total' => $total, 108 | 'progress' => $progress, 109 | 'active' => $this->get_client_link( $posts_to_update[ $cursor ] ?? 0 ), 110 | ]; 111 | } 112 | 113 | /** 114 | * Returns a boolean based on whether a migration is currently running. 115 | * 116 | * @return bool 117 | */ 118 | public function is_running() { 119 | $running = get_option( 'ctb_running' ); 120 | return ! empty( $running ); 121 | } 122 | 123 | /** 124 | * Updates the progress cursor to jump to the next post in the queue. 125 | */ 126 | public function next() { 127 | $posts_to_update = get_option( 'ctb_posts_to_update' ); 128 | $total = count( $posts_to_update ); 129 | $cursor = get_option( 'ctb_cursor' ); 130 | 131 | if ( $cursor + 1 < $total ) { 132 | $next_cursor = ++$cursor; 133 | update_option( 'ctb_cursor', $next_cursor ); 134 | 135 | return $this->get_client_link( $posts_to_update[ $next_cursor ] ); 136 | } elseif ( $cursor + 1 === $total ) { 137 | update_option( 'ctb_running', 0 ); 138 | return false; 139 | } else { 140 | return false; 141 | } 142 | } 143 | 144 | /** 145 | * Returns the next post URL to migrate. 146 | * 147 | * @param int $post_id The next post id. 148 | * @return string 149 | */ 150 | public function get_client_link( $post_id ) { 151 | if ( empty( $post_id ) ) { 152 | return ''; 153 | } 154 | 155 | $edit_post_link = admin_url( 'post.php' ); 156 | 157 | $args = [ 158 | 'post' => $post_id, 159 | 'action' => 'edit', 160 | 'ctb_client' => $post_id, 161 | ]; 162 | 163 | return add_query_arg( $args, $edit_post_link ); 164 | } 165 | 166 | /** 167 | * Returns the list of post ids that need to be migrated. 168 | * 169 | * @param array $opts Optional opts 170 | * @return array 171 | */ 172 | public function get_posts_to_update( $opts = [] ) { 173 | if ( ! empty( $opts['post_type'] ) ) { 174 | $post_type = explode( ',', $opts['post_type'] ); 175 | $post_type = array_filter( $post_type ); 176 | 177 | if ( empty( $post_type ) ) { 178 | $post_type = [ 'post', 'page' ]; 179 | } 180 | } else { 181 | $post_type = [ 'post', 'page' ]; 182 | } 183 | 184 | $posts_per_page = $opts['per_page'] ?? -1; 185 | $page = $opts['page'] ?? 1; 186 | 187 | $query_params = [ 188 | 'post_type' => $post_type, 189 | 'post_status' => 'publish', 190 | 'fields' => 'ids', 191 | 'posts_per_page' => $posts_per_page, 192 | 'paged' => $page, 193 | 'ignore_sticky_posts' => true, 194 | ]; 195 | 196 | if ( ! empty( $opts['catalog'] ) ) { 197 | $query_params['tax_query'] = [ 198 | [ 199 | 'taxonomy' => BLOCK_CATALOG_TAXONOMY, 200 | 'field' => 'slug', 201 | 'terms' => [ 'core-classic' ], 202 | ], 203 | ]; 204 | } 205 | 206 | /** 207 | * Filter query parameters for the query to get the posts to be updated. 208 | * 209 | * @since 1.1.2 210 | * 211 | * @hook convert_to_blocks_update_posts_query_params 212 | * 213 | * @param {array} $query_params Array of query parameters. 214 | * @param {array} $post_type Array with the post types. 215 | * @param {array} $opts Optional opts. 216 | * 217 | * @return {array} Array of request arguments. 218 | */ 219 | $query_params = apply_filters( 'convert_to_blocks_update_posts_query_params', $query_params, $post_type, $opts ); 220 | 221 | if ( ! empty( $opts['only'] ) ) { 222 | $post_in = explode( ',', $opts['only'] ); 223 | $post_in = array_map( 'intval', $post_in ); 224 | $post_in = array_filter( $post_in ); 225 | 226 | $query_params['post__in'] = $post_in; 227 | } 228 | 229 | $query = new \WP_Query( $query_params ); 230 | $posts = $query->posts; 231 | 232 | if ( ! empty( $posts ) && defined( '\WP_CLI' ) && \WP_CLI ) { 233 | $posts_per_page = $posts_per_page > 0 ? $posts_per_page : $query->found_posts; 234 | $pages = ceil( $query->found_posts / $posts_per_page ); 235 | \WP_CLI::line( sprintf( 'Pagination: %d/%d of %d', $page, $pages, $query->found_posts ) ); 236 | } 237 | 238 | return $posts; 239 | } 240 | 241 | /** 242 | * Returns a boolean based on whether the current url has the ctb_client 243 | * editor parameter 244 | * 245 | * @return bool 246 | */ 247 | public function has_ctb_client_param() { 248 | // phpcs:disable 249 | $ctb_client = sanitize_text_field( $_GET['ctb_client'] ?? '' ); 250 | // phpcs:enable 251 | $ctb_client = intval( $ctb_client ); 252 | 253 | return ! empty( $ctb_client ); 254 | } 255 | 256 | } 257 | -------------------------------------------------------------------------------- /includes/ConvertToBlocks/MigrationCommand.php: -------------------------------------------------------------------------------- 1 | ] 21 | * : Optional comma delimited list of post types to migrate. Defaults to post,page 22 | * 23 | * [--per_page=] 24 | * : Optional number of posts to migrate per batch. Defaults to no limit. Combine with --page to paginate. 25 | * 26 | * [--page=] 27 | * : Optional page number to start migration from. Defaults to 1. 28 | * 29 | * [--only=] 30 | * : Optional comma delimited list of post ids to migrate. 31 | * 32 | * [--catalog] 33 | * : Optional flag to only migrate classic editor tagged posts. Requires that Block 34 | * Catalog plugin is present and has been indexed. 35 | * 36 | * [--reset] 37 | * : Stops any currently running migration and resets the migration state. 38 | * 39 | * @param array $args The command args 40 | * @param array $opts The command opts 41 | */ 42 | public function start( $args = [], $opts = [] ) { 43 | $agent = new MigrationAgent(); 44 | $delay = 5; // 5 second delay between each tick 45 | 46 | $opts['catalog'] = ! empty( $opts['catalog'] ) ? $opts['catalog'] : false; 47 | $opts['catalog'] = filter_var( $opts['catalog'], FILTER_VALIDATE_BOOLEAN ); 48 | 49 | $opts['reset'] = ! empty( $opts['reset'] ) ? $opts['reset'] : false; 50 | $opts['reset'] = filter_var( $opts['reset'], FILTER_VALIDATE_BOOLEAN ); 51 | 52 | $opts['per_page'] = ! empty( $opts['per_page'] ) ? intval( $opts['per_page'] ) : -1; 53 | $opts['paged'] = ! empty( $opts['page'] ) ? intval( $opts['page'] ) : 1; 54 | 55 | if ( $opts['reset'] ) { 56 | $this->stop( $args, $opts ); 57 | } 58 | 59 | // check if block-catalog plugin is installed 60 | if ( $opts['catalog'] && ! defined( 'BLOCK_CATALOG_PLUGIN_VERSION' ) ) { 61 | \WP_CLI::error( __( 'The Block Catalog plugin must be active and indexed to run in --catalog mode.', 'convert-to-blocks' ) ); 62 | } 63 | 64 | if ( $agent->is_running() ) { 65 | \WP_CLI::error( 'Please stop the currently running migration first.' ); 66 | } 67 | 68 | $result = $agent->start( $opts ); 69 | 70 | if ( empty( $result ) ) { 71 | \WP_CLI::error( 'No posts to migrate.' ); 72 | } 73 | 74 | $status = $agent->get_status( $opts ); 75 | 76 | if ( ! $status['running'] ) { 77 | \WP_CLI::error( 'Failed to start migration.' ); 78 | } 79 | 80 | \WP_CLI::log( 'Migration started...' ); 81 | 82 | $options = []; 83 | $options[] = 'Posts Per Page: ' . $opts['per_page']; 84 | $options[] = 'Page: ' . ( ! empty( $opts['page'] ) ? $opts['page'] : 1 ); 85 | $options[] = 'Catalog: ' . ( $opts['catalog'] ? 'true' : 'false' ); 86 | 87 | \WP_CLI::log( 'Options: ' . implode( ', ', $options ) ); 88 | 89 | \WP_CLI::log( 'Please open the following URL in a browser to start the migration agent.' ); 90 | \WP_CLI::line( '' ); 91 | \WP_CLI::log( $result ); 92 | \WP_CLI::line( '' ); 93 | 94 | $total = $status['total']; 95 | 96 | $message = "Converting $total Posts ..."; 97 | $progress_bar = \WP_CLI\Utils\make_progress_bar( $message, $total ); 98 | $progress_bar->tick(); 99 | 100 | $prev_progress = 0; 101 | $ticks = 0; 102 | 103 | while ( true ) { 104 | $status = $agent->get_status(); 105 | 106 | if ( ! $status['running'] ) { 107 | break; 108 | } 109 | 110 | $progress = $status['progress']; 111 | 112 | // since the WP CLI progress bar can't tick upto a progress % we need to 113 | // tick in steps upto the progress % of total 114 | if ( $progress !== $prev_progress ) { 115 | $required_ticks = floor( $progress / 100 * $total ); 116 | 117 | while ( $ticks < $required_ticks ) { 118 | $progress_bar->tick(); 119 | $ticks++; 120 | } 121 | 122 | $prev_progress = $progress; 123 | } 124 | 125 | if ( $ticks < $total ) { 126 | // sleeping helps reduce load on server 127 | sleep( $delay ); 128 | } else { 129 | // don't need the full sleep delay on last tick 130 | sleep( 1 ); 131 | } 132 | 133 | // required as we need to reload options that the browser client is updating 134 | wp_cache_delete( 'alloptions', 'options' ); 135 | } 136 | 137 | $progress_bar->finish(); 138 | 139 | \WP_CLI::success( 'Migration finished successfully.' ); 140 | 141 | // cleanup the options used during migration 142 | $agent->stop(); 143 | } 144 | 145 | /** 146 | * Stops the currently running migration if active. 147 | * 148 | * @param array $args The command args 149 | * @param array $opts The command opts 150 | */ 151 | public function stop( $args = [], $opts = [] ) { 152 | $agent = new MigrationAgent(); 153 | 154 | if ( ! $agent->is_running() ) { 155 | \WP_CLI::warning( 'No migrations are currently running' ); 156 | return; 157 | } 158 | 159 | $agent->stop( $opts ); 160 | 161 | \WP_CLI::success( 'Migration stopped successfully' ); 162 | } 163 | 164 | /** 165 | * Prints the status of the currently running migration. 166 | * 167 | * @param array $args The command args 168 | * @param array $opts The command opts 169 | */ 170 | public function status( $args = [], $opts = [] ) { 171 | $agent = new MigrationAgent(); 172 | $status = $agent->get_status( $opts ); 173 | 174 | if ( ! $status['running'] ) { 175 | \WP_CLI::log( 'No migrations are currently running.' ); 176 | return; 177 | } 178 | 179 | \WP_CLI::log( 'Migration is currently running ...' ); 180 | \WP_CLI::log( $status['progress'] . ' [' . ( $status['cursor'] + 1 ) . '/' . $status['total'] . ']' ); 181 | \WP_CLI::log( 'Active: ' . $status['active'] ); 182 | } 183 | 184 | } 185 | -------------------------------------------------------------------------------- /includes/ConvertToBlocks/Plugin.php: -------------------------------------------------------------------------------- 1 | rest_support = new RESTSupport(); 86 | $this->register( $this->rest_support ); 87 | 88 | if ( $this->is_wp_cli() ) { 89 | $this->init_commands(); 90 | } 91 | } 92 | 93 | /* WordPress Lifecyle Hooks */ 94 | 95 | /** 96 | * Initializes the Plugin modules 97 | */ 98 | public function init() { 99 | $this->init_locale(); 100 | 101 | $this->register_objects( 102 | [ 103 | new RESTSupport(), 104 | new Settings(), 105 | ] 106 | ); 107 | } 108 | 109 | /** 110 | * Initializes the Plugin admin modules 111 | */ 112 | public function init_admin() { 113 | $this->register_objects( 114 | [ 115 | new PostTypeColumnSupport(), 116 | new ReverseMigrationSupport(), 117 | new RevisionSupport(), 118 | new ClassicEditorSupport(), 119 | new Assets(), 120 | new MigrationAgent(), 121 | ] 122 | ); 123 | } 124 | 125 | /** 126 | * Initializes the Plugin WP CLI commands 127 | */ 128 | public function init_commands() { 129 | \WP_CLI::add_command( 'convert-to-blocks', '\ConvertToBlocks\MigrationCommand' ); 130 | } 131 | 132 | /* Helpers */ 133 | 134 | /** 135 | * Helper method to register array of objects 136 | * 137 | * @param array $objects Plugin module objects 138 | */ 139 | public function register_objects( $objects ) { 140 | foreach ( $objects as $object ) { 141 | $this->register( $object ); 142 | } 143 | } 144 | 145 | /** 146 | * Helper method to register single object 147 | * 148 | * @param mixed $object Plugin module object 149 | */ 150 | public function register( $object ) { 151 | if ( $object->can_register() ) { 152 | 153 | if ( property_exists( $object, 'container' ) ) { 154 | $object->container = $this; 155 | } 156 | 157 | $object->register(); 158 | } 159 | } 160 | 161 | /** 162 | * Checks if running in WP CLI mode. 163 | * 164 | * @return bool True if in CLI mode else false. 165 | */ 166 | public function is_wp_cli() { 167 | return defined( 'WP_CLI' ) && WP_CLI; 168 | } 169 | 170 | /** 171 | * Initializes Plugin Local 172 | */ 173 | public function init_locale() { 174 | $locale = apply_filters( 'plugin_locale', get_locale(), 'convert-to-blocks' ); 175 | load_textdomain( 'convert-to-blocks', WP_LANG_DIR . '/convert-to-blocks/convert-to-blocks-' . $locale . '.mo' ); 176 | load_plugin_textdomain( 'convert-to-blocks', false, plugin_basename( CONVERT_TO_BLOCKS_PLUGIN ) . '/languages/' ); 177 | } 178 | 179 | /* Editor Context Helpers */ 180 | 181 | /** 182 | * Checks if the specified post type supports Convert to Blocks. 183 | * 184 | * @param string $post_type The post type name. 185 | * @return bool 186 | */ 187 | public function post_type_supports_convert_to_blocks( $post_type ) { 188 | $supports = post_type_supports( $post_type, 'convert-to-blocks' ); 189 | $use_defaults = apply_filters( 'convert_to_blocks_defaults', true ); 190 | $default_post_types = $this->get_default_post_types(); 191 | $user_selected_post_types = get_option( sprintf( '%s_post_types', CONVERT_TO_BLOCKS_SLUG ), $default_post_types ); 192 | 193 | if ( ! $supports && $use_defaults && in_array( $post_type, $default_post_types, true ) ) { 194 | $supports = true; 195 | } 196 | 197 | // For user-selected option via the Settings UI. 198 | if ( false !== $user_selected_post_types ) { 199 | $supports = false; 200 | 201 | // If no post_type is selected. 202 | if ( empty( $user_selected_post_types ) ) { 203 | $supports = false; 204 | } 205 | 206 | // Check if post_type is selected by the user. 207 | if ( in_array( $post_type, $user_selected_post_types, true ) ) { 208 | $supports = true; 209 | } 210 | } 211 | 212 | $supports = apply_filters( 'post_type_supports_convert_to_blocks', $supports, $post_type ); 213 | 214 | return $supports; 215 | } 216 | 217 | /** 218 | * Checks if the specified post supports Convert to Blocks. 219 | * 220 | * @param int|WP_Post $post The post object or id. 221 | * @return bool 222 | */ 223 | public function post_supports_convert_to_blocks( $post ) { 224 | if ( $post instanceof \WP_Post ) { 225 | $post_id = $post->post_id; 226 | } else { 227 | $post_id = $post; 228 | } 229 | 230 | $post_type = get_post_type( $post_id ); 231 | $supports = $this->post_type_supports_convert_to_blocks( $post_type ); 232 | $supports = apply_filters( 'post_supports_convert_to_blocks', $supports, $post_id ); 233 | 234 | return $supports; 235 | } 236 | 237 | /** 238 | * Checks if the specified post was created or updated in Gutenberg. 239 | * 240 | * @param int $post_id The post id. 241 | * @return bool 242 | */ 243 | public function is_block_editor_post( $post_id ) { 244 | if ( ! $this->post_supports_convert_to_blocks( $post_id ) ) { 245 | return false; 246 | } 247 | 248 | $block_editor = get_post_meta( $post_id, 'block_editor', true ); 249 | 250 | if ( ! $block_editor ) { 251 | $block_editor = has_blocks( $post_id ); 252 | } 253 | 254 | $block_editor = filter_var( $block_editor, FILTER_VALIDATE_BOOLEAN ); 255 | $block_editor = apply_filters( 'convert_to_blocks_is_block_editor_post', $block_editor, $post_id ); 256 | 257 | return $block_editor; 258 | } 259 | 260 | /** 261 | * Checks if the specified post was created or updated in the Classic 262 | * Editor. CE is assumed if not created or updated in Block Editor. 263 | * 264 | * @param int $post_id The post id. 265 | * @return bool 266 | */ 267 | public function is_classic_editor_post( $post_id ) { 268 | return ! $this->is_block_editor_post( $post_id ); 269 | } 270 | 271 | /** 272 | * Convert to Blocks is enabled on the following post types by default. 273 | * 274 | * @return array 275 | */ 276 | public function get_default_post_types() { 277 | return apply_filters( 'convert_to_blocks_default_post_types', CONVERT_TO_BLOCKS_DEFAULT_POST_TYPES ); 278 | } 279 | 280 | /** 281 | * Checks if the user is on the classic editor context. 282 | * 283 | * @return bool 284 | */ 285 | public function is_classic_editor_context() { 286 | $screen = $this->get_current_screen(); 287 | 288 | if ( empty( $screen ) ) { 289 | return false; 290 | } 291 | 292 | if ( empty( $screen->post_type ) ) { 293 | return false; 294 | } 295 | 296 | $screen_post_type = $screen->post_type; 297 | 298 | if ( ! $this->post_type_supports_convert_to_blocks( $screen_post_type ) ) { 299 | return false; 300 | } 301 | 302 | if ( ! $this->has_classic_param() ) { 303 | return false; 304 | } 305 | 306 | return true; 307 | } 308 | 309 | /** 310 | * Checks if the user is on the post list screen. 311 | * 312 | * @return bool 313 | */ 314 | public function is_post_list_context() { 315 | $screen = $this->get_current_screen(); 316 | 317 | if ( empty( $screen ) ) { 318 | return false; 319 | } 320 | 321 | if ( empty( $screen->post_type ) ) { 322 | return false; 323 | } 324 | 325 | $screen_post_type = $screen->post_type; 326 | 327 | if ( ! $this->post_type_supports_convert_to_blocks( $screen_post_type ) ) { 328 | return false; 329 | } 330 | 331 | return true; 332 | } 333 | 334 | /** 335 | * Returns a boolean based on whether the current url has the classic 336 | * editor parameter 337 | * 338 | * @return bool 339 | */ 340 | public function has_classic_param() { 341 | // phpcs:disable 342 | $classic = sanitize_text_field( isset( $_GET['classic'] ) ? $_GET['classic'] : '' ) ; 343 | // phpcs:enable 344 | $classic = filter_var( $classic, FILTER_VALIDATE_BOOLEAN ); 345 | 346 | return $classic; 347 | } 348 | 349 | /** 350 | * Returns the current screen object or false if too early to check. 351 | * 352 | * @return bool 353 | */ 354 | public function get_current_screen() { 355 | if ( function_exists( 'get_current_screen' ) ) { 356 | return get_current_screen(); 357 | } 358 | 359 | return false; 360 | } 361 | 362 | /** 363 | * Returns the post id of the current admin post 364 | * 365 | * @return int 366 | */ 367 | public function get_current_post() { 368 | if ( is_admin() ) { 369 | // phpcs:disable 370 | return intval( isset( $_GET['post'] ) ? $_GET['post'] : '' ); 371 | // phpcs:enable 372 | } elseif ( is_singular() ) { 373 | return get_queried_object(); 374 | } else { 375 | return false; 376 | } 377 | } 378 | 379 | /** 380 | * Checks if the current page referer was a classic editor page 381 | * 382 | * @return bool 383 | */ 384 | public function is_classic_referer() { 385 | $referer = isset( $_SERVER['HTTP_REFERER'] ) ? $_SERVER['HTTP_REFERER'] : ''; 386 | $referer_params = wp_parse_args( wp_parse_url( $referer, PHP_URL_QUERY ) ); 387 | $classic = ! empty( $referer_params['classic'] ) ? $referer_params['classic'] : ''; 388 | $classic = filter_var( $classic, FILTER_VALIDATE_BOOLEAN ); 389 | 390 | return $classic; 391 | } 392 | 393 | /** 394 | * Checks if the current page referer was a classic editor page 395 | * 396 | * @return bool 397 | */ 398 | public function is_classic_post_referer() { 399 | if ( ! isset( $_SERVER['HTTP_REFERER'] ) ) { 400 | return false; 401 | } 402 | 403 | $referer = $_SERVER['HTTP_REFERER']; 404 | $referer_params = wp_parse_args( wp_parse_url( $referer, PHP_URL_QUERY ) ); 405 | $classic = ! empty( $referer_params['classic'] ) ? $referer_params['classic'] : ''; 406 | $classic = filter_var( $classic, FILTER_VALIDATE_BOOLEAN ); 407 | 408 | if ( $classic && ! empty( $referer_params['post'] ) ) { 409 | return true; 410 | } 411 | 412 | return false; 413 | } 414 | 415 | /** 416 | * Returns the required capability to be able to edit specified post 417 | * type. 418 | * 419 | * @param string $post_type The post type name. 420 | * @return string 421 | */ 422 | public function get_post_type_capability( $post_type ) { 423 | $object = get_post_type_object( $post_type ); 424 | 425 | if ( empty( $object->cap->edit_posts ) ) { 426 | return 'unknown_capability'; 427 | } 428 | 429 | $cap = $object->cap->edit_posts; 430 | $cap = apply_filters( 'convert_to_blocks_post_type_capability', $cap, $post_type ); 431 | 432 | return $cap; 433 | } 434 | 435 | } 436 | -------------------------------------------------------------------------------- /includes/ConvertToBlocks/PostTypeColumnSupport.php: -------------------------------------------------------------------------------- 1 | container->post_type_supports_convert_to_blocks( $post_type ) ) { 33 | continue; 34 | } 35 | add_filter( "manage_{$post_type}_posts_columns", [ $this, 'update_post_columns' ], 10000 ); 36 | add_action( "manage_{$post_type}_pages_custom_column", [ $this, 'update_post_column_value' ], 10, 2 ); 37 | add_action( "manage_{$post_type}_posts_custom_column", [ $this, 'update_post_column_value' ], 10, 2 ); 38 | } 39 | } 40 | 41 | /** 42 | * Only register if in an admin context. 43 | */ 44 | public function can_register() { 45 | return is_admin(); 46 | } 47 | 48 | /** 49 | * Adds Editor column and updates display order 50 | * 51 | * @param array $columns List of post columns 52 | * @return array 53 | */ 54 | public function update_post_columns( $columns ) { 55 | $column_label = $this->get_editor_column_label(); 56 | 57 | $columns['editor'] = $column_label; 58 | 59 | $ranks = [ 60 | 'cb' => 1, 61 | 'title' => 2, 62 | 'editor' => 3, 63 | 'coauthors' => 4, 64 | ]; 65 | 66 | $sorter = function( $a, $b ) use ( $ranks ) { 67 | $a_rank = ! empty( $ranks[ $a ] ) ? $ranks[ $a ] : 1000; 68 | $b_rank = ! empty( $ranks[ $b ] ) ? $ranks[ $b ] : 1000; 69 | 70 | if ( $a_rank < $b_rank ) { 71 | return -1; 72 | } elseif ( $a_rank > $b_rank ) { 73 | return 1; 74 | } else { 75 | return 0; 76 | } 77 | }; 78 | 79 | uksort( $columns, $sorter ); 80 | 81 | $columns = apply_filters( 'convert_to_blocks_post_list_columns', $columns ); 82 | 83 | return $columns; 84 | } 85 | 86 | /** 87 | * Outputs Post Column based on whether the specified post was created 88 | * or updated in CE or block editor. 89 | * 90 | * @param string $column The column name 91 | * @param int $post_id The current post id 92 | * @return void 93 | */ 94 | public function update_post_column_value( $column, $post_id ) { 95 | if ( 'editor' !== $column ) { 96 | return; 97 | } 98 | 99 | $column_label = $this->get_editor_column_label(); 100 | 101 | // phpcs:disable 102 | if ( $this->is_block_editor_post( $post_id ) ) { 103 | $icon = $this->get_block_editor_column_icon(); 104 | $title = __( BLOCK_EDITOR_LABEL, 'convert-to-blocks' ); 105 | } else { 106 | $icon = $this->get_classic_editor_column_icon(); 107 | $title = __( CLASSIC_EDITOR_LABEL, 'convert-to-blocks' ); 108 | } 109 | // phpcs:enable 110 | 111 | ?> 112 | 116 | 117 | %s', esc_attr( $title ) ); 120 | } 121 | ?> 122 | container->is_block_editor_post( $post_id ); 162 | } 163 | 164 | } 165 | -------------------------------------------------------------------------------- /includes/ConvertToBlocks/RESTSupport.php: -------------------------------------------------------------------------------- 1 | container->post_type_supports_convert_to_blocks( $post_type ) ) { 73 | add_action( 'rest_prepare_' . $post_type, [ $this, 'update_response' ], 10, 3 ); 74 | } 75 | } 76 | } 77 | 78 | /** 79 | * If Edit Context then transforms Shortcodes in post content and 80 | * returns in raw content. 81 | * 82 | * @param WP_Response $response REST Response 83 | * @param WP_Post $post Post to return 84 | * @param WP_Request $request REST Request 85 | */ 86 | public function update_response( $response, $post, $request ) { 87 | $context = $request->get_param( 'context' ); 88 | 89 | if ( 'edit' === $context ) { 90 | $data = $response->get_data(); 91 | $raw = $data['content']['raw'] ?? ''; 92 | 93 | $updated = apply_filters( 'convert_to_blocks_raw_transform', $raw, $post, $request ); 94 | $data['content']['raw'] = $updated; 95 | 96 | $data = apply_filters( 'convert_to_blocks_data_transform', $data, $post, $request ); 97 | 98 | $response->set_data( $data ); 99 | } 100 | 101 | return $response; 102 | } 103 | 104 | /** 105 | * Gets the post types that are allowed to use Gutenberg. 106 | * 107 | * @return array supported post types. 108 | */ 109 | public function get_allowed_post_types() { 110 | return [ 'post' ]; 111 | } 112 | 113 | /** 114 | * Marks a Post as a Block Editor post if post save originated in 115 | * Gutenberg. 116 | * 117 | * @param int $post_id The post to save 118 | */ 119 | public function update_post_editor( $post_id ) { 120 | if ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE ) { 121 | return; 122 | } 123 | 124 | if ( wp_is_post_autosave( $post_id ) ) { 125 | return; 126 | } 127 | 128 | if ( wp_is_post_revision( $post_id ) ) { 129 | return; 130 | } 131 | 132 | remove_action( 'save_post', [ $this, 'update_post_editor' ] ); 133 | 134 | if ( $this->is_rest() ) { 135 | update_post_meta( $post_id, 'block_editor', true ); 136 | } elseif ( $this->container->is_classic_post_referer() ) { 137 | delete_post_meta( $post_id, 'block_editor' ); 138 | } 139 | } 140 | 141 | /** 142 | * Enables REST endpoints if a User can edit posts. This allows 143 | * Gutenberg blocks that pull data for that post type to work 144 | * correctly. 145 | * 146 | * @param array $args The post type args 147 | * @param string $post_type The post type name 148 | * @return array 149 | */ 150 | public function update_rest_support( $args, $post_type ) { 151 | if ( empty( $args['show_in_rest'] ) ) { 152 | $cap = $this->container->get_post_type_capability( $post_type ); 153 | $args['show_in_rest'] = current_user_can( $cap ); 154 | } 155 | 156 | return $args; 157 | } 158 | 159 | /* helpers */ 160 | /** 161 | * Checks if the current request is a WP REST API request. 162 | * 163 | * @return bool 164 | */ 165 | public function is_rest() { 166 | if ( is_null( $this->rest ) ) { 167 | $this->rest = defined( 'REST_REQUEST' ) && REST_REQUEST; 168 | } 169 | 170 | return $this->rest; 171 | } 172 | 173 | /** 174 | * Sets up the rest support after all plugins loaded 175 | */ 176 | public function on_plugins_loaded() { 177 | add_filter( 178 | 'register_post_type_args', 179 | [ $this, 'update_rest_support' ], 180 | 10, 181 | 2 182 | ); 183 | } 184 | 185 | /** 186 | * Disables EP integration if in the Gutenberg REST search context 187 | * 188 | * @param bool $skip Default skip value 189 | * @param WP_Query $query The query object 190 | * @return bool 191 | */ 192 | public function skip_ep_integration_if( $skip, $query ) { 193 | if ( $this->is_search_context() ) { 194 | $query->set( 'ep_integrate', false ); 195 | return true; 196 | } 197 | 198 | return $skip; 199 | } 200 | 201 | /** 202 | * Check if we are in a REST API search context. 203 | * 204 | * @return bool 205 | */ 206 | public function is_search_context() { 207 | return $this->is_rest() && 208 | isset( $_GET['search'] ); // phpcs:disable 209 | } 210 | 211 | } 212 | -------------------------------------------------------------------------------- /includes/ConvertToBlocks/ReverseMigrationSupport.php: -------------------------------------------------------------------------------- 1 | container->get_current_post(); 52 | 53 | if ( empty( $post_id ) ) { 54 | return; 55 | } 56 | 57 | if ( ! $this->container->is_classic_editor_context() ) { 58 | return; 59 | } 60 | 61 | if ( ! $this->container->is_block_editor_post( $post_id ) ) { 62 | return; 63 | } 64 | 65 | $this->render_reverse_migration_notice(); 66 | } 67 | 68 | /** 69 | * Renders the reverse migration notice 70 | * 71 | * @return void 72 | */ 73 | public function render_reverse_migration_notice() { 74 | $block_editor_url = remove_query_arg( 'classic' ); 75 | 76 | $line1 = __( 77 | 'This post was created or updated in the Gutenberg Block Editor.', 78 | 'convert-to-blocks' 79 | ); 80 | 81 | $line2 = sprintf( 82 | /* translators: Paragraph has link to switch to the Block Editor */ 83 | __( 84 | 'Please Switch to the Block Editor to avoid errors.', 85 | 'convert-to-blocks' 86 | ), 87 | $block_editor_url 88 | ); 89 | 90 | ?> 91 |
92 |

93 | 94 |
95 | [ 'href' => true ] ] ); ?> 96 |

97 |
98 | container->is_classic_referer() || $this->container->has_classic_param() ) { 49 | $location = add_query_arg( [ 'classic' => 1 ], $location ); 50 | } 51 | 52 | return $location; 53 | } 54 | 55 | /** 56 | * Ensures that restoring revision from Classic Editor takes user back to CE. 57 | * 58 | * @param array $revision_data The revision data array 59 | * @param array $revision The revision meta 60 | * @param WP_Post $post The revision post object 61 | */ 62 | public function update_revision_js( $revision_data, $revision, $post ) { 63 | if ( $this->container->is_classic_referer() || $this->container->has_classic_param() ) { 64 | $revision_data['restoreUrl'] = add_query_arg( [ 'classic' => 1 ], $revision_data['restoreUrl'] ); 65 | } 66 | 67 | return $revision_data; 68 | } 69 | 70 | } 71 | -------------------------------------------------------------------------------- /includes/ConvertToBlocks/Settings.php: -------------------------------------------------------------------------------- 1 | init(); 56 | 57 | add_action( 'admin_menu', [ $this, 'add_menu' ] ); 58 | add_action( 'admin_init', [ $this, 'register_section' ], 10 ); 59 | add_action( 'admin_init', [ $this, 'register_fields' ], 20 ); 60 | add_action( 'admin_notices', [ $this, 'filter_notice' ], 10 ); 61 | add_filter( 'plugin_action_links_' . CONVERT_TO_BLOCKS_PLUGIN_BASENAME, [ $this, 'add_settings_link' ] ); 62 | } 63 | 64 | /** 65 | * Only registers on admin context. 66 | */ 67 | public function can_register() { 68 | return is_admin(); 69 | } 70 | 71 | /** 72 | * Configures variables and fetches post types. 73 | * 74 | * @return void 75 | */ 76 | public function init() { 77 | // Configure variables. 78 | $this->settings_page = sprintf( '%s-%s', CONVERT_TO_BLOCKS_SLUG, $this->settings_page ); 79 | $this->settings_section = sprintf( '%s-%s', CONVERT_TO_BLOCKS_SLUG, $this->settings_section ); 80 | $this->settings_group = sprintf( '%s_%s', CONVERT_TO_BLOCKS_SLUG, $this->settings_group ); 81 | 82 | // Get post types. 83 | $this->post_types = $this->get_post_types(); 84 | } 85 | 86 | /** 87 | * Retrieves all public post types. 88 | * 89 | * @return array 90 | */ 91 | public function get_post_types() { 92 | $post_types = get_post_types( 93 | [ 'public' => true ] 94 | ); 95 | 96 | if ( ! empty( $post_types['attachment'] ) ) { 97 | unset( $post_types['attachment'] ); 98 | } 99 | 100 | return $post_types; 101 | } 102 | 103 | /** 104 | * Adds a submenu item for the `Settings` menu. 105 | * 106 | * @return void 107 | */ 108 | public function add_menu() { 109 | add_options_page( 110 | esc_html__( 'Convert to Blocks', 'convert-to-blocks' ), 111 | esc_html__( 'Convert to Blocks', 'convert-to-blocks' ), 112 | $this->capability, 113 | CONVERT_TO_BLOCKS_SLUG, 114 | [ $this, 'settings_page' ] 115 | ); 116 | } 117 | 118 | /** 119 | * Registers the settings page. 120 | * 121 | * @return void 122 | */ 123 | public function settings_page() { 124 | ?> 125 |
126 |

127 | 128 |

129 |
130 | 131 |

132 | 133 |

134 | 135 |
136 | settings_group ); 138 | do_settings_sections( CONVERT_TO_BLOCKS_SLUG ); 139 | submit_button(); 140 | ?> 141 |
142 |
143 | settings_section, 154 | false, 155 | false, 156 | CONVERT_TO_BLOCKS_SLUG 157 | ); 158 | } 159 | 160 | /** 161 | * Registers setting fields. 162 | * 163 | * @return void 164 | */ 165 | public function register_fields() { 166 | // Supported post types. 167 | add_settings_field( 168 | sprintf( '%s_post_types', CONVERT_TO_BLOCKS_SLUG ), 169 | esc_html__( 'Supported Post Types', 'convert-to-blocks' ), 170 | [ $this, 'field_post_types' ], 171 | CONVERT_TO_BLOCKS_SLUG, 172 | $this->settings_section, 173 | [ 174 | 'label_for' => sprintf( '%s_post_types', CONVERT_TO_BLOCKS_SLUG ), 175 | ] 176 | ); 177 | 178 | register_setting( 179 | $this->settings_group, 180 | sprintf( '%s_post_types', CONVERT_TO_BLOCKS_SLUG ), 181 | [ 182 | 'sanitize_callback' => [ $this, 'sanitize_post_types' ], 183 | ] 184 | ); 185 | } 186 | 187 | /** 188 | * Renders the `post_types` field. 189 | * 190 | * @return void 191 | */ 192 | public function field_post_types() { 193 | $post_types = get_option( 194 | sprintf( '%s_post_types', CONVERT_TO_BLOCKS_SLUG ), 195 | Plugin::get_instance()->get_default_post_types() 196 | ); 197 | $output_html = ''; 198 | 199 | foreach ( $this->post_types as $post_type ) { 200 | $output_html .= sprintf( 201 | '
', 202 | sprintf( '%s_post_types_%s', esc_attr( CONVERT_TO_BLOCKS_SLUG ), esc_attr( $post_type ) ), 203 | sprintf( '%s_post_types', esc_attr( CONVERT_TO_BLOCKS_SLUG ) ), 204 | checked( in_array( $post_type, $post_types, true ), 1, false ), 205 | esc_attr( $post_type ), 206 | esc_attr( ucfirst( $post_type ) ) 207 | ); 208 | } 209 | ?> 210 |
211 | 212 |
213 | post_type ) || in_array( $screen->id, $show_on_pages, true ) ) ) { 252 | return; 253 | } 254 | 255 | ?> 256 |
257 |

258 | 259 |

260 |
261 | %s', 277 | esc_url( 278 | add_query_arg( 279 | [ 280 | 'page' => CONVERT_TO_BLOCKS_SLUG, 281 | ], 282 | admin_url( 'options-general.php' ) 283 | ) 284 | ), 285 | esc_html__( 'Settings', 'convert-to-blocks' ) 286 | ); 287 | 288 | return [ $settings_link, ...$links ]; 289 | } 290 | 291 | } 292 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "convert-to-blocks", 3 | "version": "1.3.2", 4 | "description": "Convert to Blocks is a WordPress plugin that transforms classic editor content to blocks on the fly.", 5 | "author": { 6 | "name": "10up", 7 | "email": "opensource@10up.com", 8 | "url": "https://10up.com", 9 | "role": "developer" 10 | }, 11 | "license": "GPL-2.0-or-later", 12 | "scripts": { 13 | "test": "phpunit", 14 | "start": "composer install && npm install && npm run build", 15 | "build": "NODE_ENV=production wp-scripts build", 16 | "watch": "NODE_ENV=development wp-scripts start", 17 | "build-release": "npm install && composer install --no-dev -o && npm run build", 18 | "lint-release": "npm install && composer install && npm run lint", 19 | "lint-js": "wp-scripts lint-js ./assets/js", 20 | "lint-php": "composer run lint", 21 | "format-js": "wp-scripts format ./assets/js", 22 | "lint": "npm run lint-js && npm run lint-php", 23 | "format": "npm run format-js", 24 | "wp-env": "wp-env", 25 | "env:start": "wp-env start", 26 | "env:stop": "wp-env stop", 27 | "env:reset": "wp-env clean all", 28 | "env:destroy": "wp-env destroy", 29 | "cypress:open": "cypress open --config-file tests/cypress/config.js --e2e --browser chrome", 30 | "cypress:run": "cypress run --config-file tests/cypress/config.js", 31 | "env": "wp-env", 32 | "postenv:start": "./tests/bin/initialize.sh" 33 | }, 34 | "husky": { 35 | "hooks": { 36 | "pre-commit": "lint-staged" 37 | } 38 | }, 39 | "lint-staged": { 40 | "*.js": [ 41 | "eslint" 42 | ] 43 | }, 44 | "repository": { 45 | "type": "git", 46 | "url": "https://github.com/10up/convert-to-blocks" 47 | }, 48 | "devDependencies": { 49 | "@10up/cypress-wp-utils": "^0.2.0", 50 | "@10up/eslint-config": "^4.0.0", 51 | "@wordpress/env": "^10.2.0", 52 | "@wordpress/scripts": "^30.4.0", 53 | "cypress": "^13.3.0", 54 | "cypress-mochawesome-reporter": "^3.6.0", 55 | "husky": "^3.1.0", 56 | "lint-staged": "^9.5.0", 57 | "prettier": "^3.2.5" 58 | }, 59 | "engines": { 60 | "node": ">=8.11" 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /phpcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 10up PHPCS extended. 4 | 8 | 9 | 10 | 11 | /build/ 12 | 13 | -------------------------------------------------------------------------------- /phpunit.xml: -------------------------------------------------------------------------------- 1 | 6 | 7 | 8 | ./tests/ 9 | 10 | 11 | 12 | 13 | ./includes 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /portkey.json: -------------------------------------------------------------------------------- 1 | { 2 | "includes/ConvertToBlocks/*.php": { 3 | "type": "plugin", 4 | "test": "tests/ConvertToBlocks/%sTest.php" 5 | }, 6 | "tests/ConvertToBlocks/*Test.php": { 7 | "type": "test", 8 | "alternate": "includes/ConvertToBlocks/%s.php" 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /readme.txt: -------------------------------------------------------------------------------- 1 | === Convert to Blocks === 2 | Contributors: 10up, dsawardekar, tlovett1, jeffpaul 3 | Tags: block, block migration, gutenberg migration, gutenberg conversion, convert to blocks 4 | Tested up to: 6.8 5 | Stable tag: 1.3.2 6 | License: GPL-2.0-or-later 7 | License URI: https://spdx.org/licenses/GPL-2.0-or-later.html 8 | 9 | Convert to Blocks transforms classic editor content to blocks on-the-fly. 10 | 11 | == Description == 12 | 13 | Convert to Blocks is a WordPress plugin that transforms classic editor content to blocks on-the-fly. After installing Gutenberg or upgrading to WordPress 5.0+, your content will be displayed in "Classic Editor Blocks". While these blocks are completely functional and will display fine on the frontend of your website, they do not empower editors to fully make use of the block editing experience. In order to do so, your classic editor posts need to be converted to blocks. This plugin does that for you "on the fly". When an editor goes to edit a classic post, the content will be parsed into blocks. When the editor saves the post, the new structure will be saved into the database. This strategy reduces risk as you are only altering database values for content that needs to be changed. 14 | 15 | **Note that Inner Blocks Transforms is only supported with the Gutenberg Plugin 10.9.0+.** 16 | 17 | == Installation == 18 | 19 | = Manual Installation = 20 | 21 | 1. Upload the entire `/convert-to-blocks` directory to the `/wp-content/plugins/` directory. 22 | 2. Activate Convert to Blocks through the 'Plugins' menu in WordPress. 23 | 24 | == Frequently Asked Questions == 25 | 26 | = How Do I Know It's Working? = 27 | 28 | Find a classic editor in the post, try to navigate away from the page. You will get an error saying your changes will be discarded. This is because Convert to Blocks converted your content to blocks on the fly and those changes will be saved when you update the post. 29 | 30 | = Will Convert to Blocks Handle My Custom Blocks? = 31 | 32 | By default it will not. 33 | 34 | = Will Convert to Blocks Handle Nested Blocks? = 35 | 36 | Nested / Inner Block support does not work with Gutenberg bundled with WordPress Core <=5.7.2. This feature needs the Gutenberg Plugin >=10.9.0. 37 | 38 | == Screenshots == 39 | 40 | 1. Bulk migration using the `wp convert-to-blocks start` WP-CLI command that converts posts iteratively in the browser without requireing any manual input. 41 | 42 | == Changelog == 43 | 44 | = 1.3.2 - 2025-02-03 = 45 | * **Changed:** Bump WordPress "tested up to" version 6.7 (props [@colinswinney](https://github.com/colinswinney), [@jeffpaul](https://github.com/jeffpaul) via [#188](https://github.com/10up/convert-to-blocks/pull/188), [#190](https://github.com/10up/convert-to-blocks/pull/190)). 46 | * **Security:** Bump `axios` from 1.6.8 to 1.7.4 (props [@dependabot](https://github.com/apps/dependabot), [@dkotter](https://github.com/dkotter) via [#180](https://github.com/10up/convert-to-blocks/pull/180)). 47 | * **Security:** Bump `webpack` from 5.91.0 to 5.94.0 (props [@dependabot](https://github.com/apps/dependabot), [@faisal-alvi](https://github.com/faisal-alvi) via [#181](https://github.com/10up/convert-to-blocks/pull/181)). 48 | * **Security:** Bump `ws` from 7.5.10 to 8.18.0 and `@wordpress/scripts` from 27.8.0 to 30.4.0 (props [@dependabot](https://github.com/apps/dependabot), [@faisal-alvi](https://github.com/faisal-alvi) via [#182](https://github.com/10up/convert-to-blocks/pull/182), [#189](https://github.com/10up/convert-to-blocks/pull/189)). 49 | * **Security:** Bump `express` from 4.19.2 to 4.21.0 (props [@dependabot](https://github.com/apps/dependabot), [@iamdharmesh](https://github.com/iamdharmesh) via [#185](https://github.com/10up/convert-to-blocks/pull/185)). 50 | 51 | = 1.3.1 - 2024-08-20 = 52 | * **Changed:** Bump WordPress "tested up to" version 6.6 (props [@sudip-md](https://github.com/sudip-md), [@jeffpaul](https://github.com/jeffpaul), [@Sidsector9](https://github.com/Sidsector9), [@ankitguptaindia](https://github.com/ankitguptaindia) via [#174](https://github.com/10up/convert-to-blocks/pull/174)). 53 | * **Changed:** Bump WordPress minimum from 6.3 to 6.4 (props [@sudip-md](https://github.com/sudip-md), [@jeffpaul](https://github.com/jeffpaul), [@Sidsector9](https://github.com/Sidsector9), [@ankitguptaindia](https://github.com/ankitguptaindia) via [#174](https://github.com/10up/convert-to-blocks/pull/174)). 54 | * **Fixed:** Issue with saving a post before the convert to blocks transform was completed (props [@mdesplenter](https://github.com/mdesplenter), [@Sidsector9](https://github.com/Sidsector9), [@dsawardekar](https://github.com/dsawardekar) via [#173](https://github.com/10up/convert-to-blocks/pull/173)). 55 | * **Security:** Bump `braces` from 3.0.2 to 3.0.3 (props [@dependabot](https://github.com/apps/dependabot), [@Sidsector9](https://github.com/Sidsector9) via [#168](https://github.com/10up/convert-to-blocks/pull/168)). 56 | * **Security:** Bump `ws` from 7.5.9 to 7.5.10 (props [@dependabot](https://github.com/apps/dependabot), [@Sidsector9](https://github.com/Sidsector9) via [#169](https://github.com/10up/convert-to-blocks/pull/169)). 57 | 58 | = 1.3.0 - 2024-05-14 = 59 | * **Added:** Block Catalog integration, and pagination support (props [@dsawardekar](https://github.com/dsawardekar), [@iamdharmesh](https://github.com/iamdharmesh) via [#164](https://github.com/10up/convert-to-blocks/pull/164)). 60 | * **Changed:** Adjust `enable_block_editor` method only to alter posts that support the gutenbridge (props [@stormrockwell](https://github.com/stormrockwell), [@Sidsector9](https://github.com/Sidsector9), [@jeffpaul](https://github.com/jeffpaul), [@dsawardekar](https://github.com/dsawardekar) via [#136](https://github.com/10up/convert-to-blocks/pull/136)). 61 | * **Changed:** Bump WordPress "tested up to" version 6.5 (props [@QAharshalkadu](https://github.com/QAharshalkadu), [@jeffpaul](https://github.com/jeffpaul), [@Sidsector9](https://github.com/Sidsector9), [@sudip-md](https://github.com/sudip-md), [@dkotter](https://github.com/dkotter) via [#146](https://github.com/10up/convert-to-blocks/pull/146), [#161](https://github.com/10up/convert-to-blocks/pull/161)). 62 | * **Changed:** Replaced [lee-dohm/no-response](https://github.com/lee-dohm/no-response) with [actions/stale](https://github.com/actions/stale) to help with closing no-response/stale issues (props [@jeffpaul](https://github.com/jeffpaul) via [#159](https://github.com/10up/convert-to-blocks/pull/159)). 63 | * **Changed:** Bump Node version from 16 to 20 (props [@aaronjorbin](https://github.com/aaronjorbin), [@Sidsector9](https://github.com/Sidsector9), [@dkotter](https://github.com/dkotter) via [#160](https://github.com/10up/convert-to-blocks/pull/160)). 64 | * **Changed:** Bump `actions/upload-artifact` from v3 to v4 (props [@iamdharmesh](https://github.com/iamdharmesh) via [#162](https://github.com/10up/convert-to-blocks/pull/162)). 65 | * **Security:** Bump `follow-redirects` from 1.15.2 to 1.15.4 (props [@dependabot](https://github.com/apps/dependabot), [@dkotter](https://github.com/dkotter) via [#153](https://github.com/10up/convert-to-blocks/pull/153)). 66 | * **Security:** Bump `browserify-sign` from 4.0.4 to 4.2.2 (props [@dependabot](https://github.com/apps/dependabot), [@dkotter](https://github.com/dkotter) via [#145](https://github.com/10up/convert-to-blocks/pull/145)). 67 | * **Security:** Bump `@babel/traverse` from 7.11.5 to 7.23.7 (props [@dependabot](https://github.com/apps/dependabot), [@dkotter](https://github.com/dkotter) via [#155](https://github.com/10up/convert-to-blocks/pull/155)). 68 | * **Security:** Remove old dependencies in favor of using `@wordpress/scripts` in our build workflow (props [@Sidsector9](https://github.com/Sidsector9), [@dkotter](https://github.com/dkotter) via [#167](https://github.com/10up/convert-to-blocks/pull/167)). 69 | 70 | = 1.2.2 - 2023-10-16 = 71 | * **Changed:** Bump WordPress "tested up to" version to 6.3 (props [@kmgalanakis](https://github.com/kmgalanakis), [@faisal-alvi](https://github.com/faisal-alvi), [@jeffpaul](https://github.com/jeffpaul), [@peterwilsoncc](https://github.com/peterwilsoncc) via [#132](https://github.com/10up/convert-to-blocks/pull/132), [#134](https://github.com/10up/convert-to-blocks/pull/134) 72 | * **Security:** Bump `@cypress/request` from 2.88.11 to 3.0.1 and `cypress` from 10.11.0 to 13.3.0 (props [@dependabot](https://github.com/apps/dependabot), [@iamdharmesh](https://github.com/apps/iamdharmesh), [@ravinderk](https://github.com/apps/ravinderk) via [#138](https://github.com/10up/convert-to-blocks/pull/138)). 73 | * **Security:** Bump `postcss` from 8.4.20 to 8.4.31 (props [@dependabot](https://github.com/apps/dependabot), [@dkotter](https://github.com/apps/dkotter) via [#139](https://github.com/10up/convert-to-blocks/pull/139)). 74 | * **Security:** Bump `fsevents` from 1.2.9 to 1.2.13 (props [@dependabot](https://github.com/apps/dependabot), [@ravinderk](https://github.com/apps/ravinderk) via [#140](https://github.com/10up/convert-to-blocks/pull/140)). 75 | 76 | = 1.2.1 - 2023-07-26 = 77 | * **Added:** More robust minimum PHP version check (props [@dkotter](https://github.com/dkotter), [@ravinderk](https://github.com/ravinderk) via [#129](https://github.com/10up/convert-to-blocks/pull/129)). 78 | * **Changed:** Bump minimum required PHP version from 7.4 to 8.0 in our `composer.json` config (props [@c0ntax](https://github.com/c0ntax), [@Sidsector9](https://github.com/Sidsector9) via [#122](https://github.com/10up/convert-to-blocks/pull/122)). 79 | * **Fixed:** Parse error caused by a comma (props [@Sidsector9](https://github.com/Sidsector9), [@iamdharmesh](https://github.com/iamdharmesh), [@ravinderk](https://github.com/ravinderk), [@felipeelia](https://github.com/felipeelia) via [#123](https://github.com/10up/convert-to-blocks/pull/123)). 80 | * **Security:** Bump `minimist` from 1.2.0 to 1.2.7 and `mkdirp` from 0.5.1 to 0.5.6 (props [@dependabot](https://github.com/apps/dependabot) via [#117](https://github.com/10up/convert-to-blocks/pull/117)). 81 | * **Security:** Bump `ini` from 1.3.5 to 1.3.8 (props [@dependabot](https://github.com/apps/dependabot) via [#119](https://github.com/10up/convert-to-blocks/pull/119)). 82 | * **Security:** Bump `browser-sync` from 2.27.10 to 2.29.3 and removes `qs` (props [@dependabot](https://github.com/apps/dependabot) via [#120](https://github.com/10up/convert-to-blocks/pull/120)). 83 | * **Security:** Bump `word-wrap` from 1.2.3 to 1.2.5 (props [@dependabot](https://github.com/apps/dependabot) via [#127](https://github.com/10up/convert-to-blocks/pull/127)). 84 | 85 | = 1.2.0 - 2023-06-27 = 86 | **Note that this release bumps the WordPress minimum version from 5.7 to 6.1 and the PHP minimum version from 7.4 to 8.0.** 87 | 88 | * **Added:** Settings UI for managing supported post types (props [@akshitsethi](https://github.com/akshitsethi), [@dinhtungdu](https://github.com/dinhtungdu), [@Sidsector9](https://github.com/Sidsector9), [@jayedul](https://github.com/jayedul), [@dsawardekar](https://github.com/dsawardekar), [@terrance-orletsky-d7](https://github.com/terrance-orletsky-d7), [@ouun](https://github.com/ouun) via [#66](https://github.com/10up/convert-to-blocks/pull/66), [#104](https://github.com/10up/convert-to-blocks/pull/104), [#112](https://github.com/10up/convert-to-blocks/pull/112), [#114](https://github.com/10up/convert-to-blocks/pull/114)). 89 | * **Added:** Filter hook `convert_to_blocks_update_posts_query_params` to modify `WP_Query` parameters to query posts that need to be migrated (props [@kmgalanakis](https://github.com/kmgalanakis), [@Sidsector9](https://github.com/Sidsector9), [@sanketio](https://github.com/sanketio) via [#113](https://github.com/10up/convert-to-blocks/pull/113)). 90 | * **Added:** Cypress end-to-end tests (props [@barneyjeffries](https://github.com/barneyjeffries), [@jeffpaul](https://github.com/jeffpaul), [@iamdharmesh](https://github.com/iamdharmesh), [@Sidsector9](https://github.com/Sidsector9), [@vikrampm1](https://github.com/vikrampm1) via [#106](https://github.com/10up/convert-to-blocks/pull/106)). 91 | * **Changed:** Bump PHP minimum supported version from 7.4 to 8.0 (props [@barneyjeffries](https://github.com/barneyjeffries), [@jeffpaul](https://github.com/jeffpaul), [@iamdharmesh](https://github.com/iamdharmesh), [@Sidsector9](https://github.com/Sidsector9), [@vikrampm1](https://github.com/vikrampm1) via [#106](https://github.com/10up/convert-to-blocks/pull/106)). 92 | * **Changed:** Bump WordPress minimum supported version from 5.7 to 6.1 (props [@barneyjeffries](https://github.com/barneyjeffries), [@jeffpaul](https://github.com/jeffpaul), [@iamdharmesh](https://github.com/iamdharmesh), [@Sidsector9](https://github.com/Sidsector9), [@vikrampm1](https://github.com/vikrampm1) via [#106](https://github.com/10up/convert-to-blocks/pull/106)). 93 | * **Changed:** Bump WordPress "tested up to" version 6.2 (props [@Sidsector9](https://github.com/Sidsector9) via [#115](https://github.com/10up/convert-to-blocks/pull/115)). 94 | * **Changed:** Updated the Dependency Review GitHub Action (props [@jeffpaul](https://github.com/jeffpaul) via [#109](https://github.com/10up/convert-to-blocks/pull/109)). 95 | * **Changed:** WordPress.org Deploy action updated to use Node 16 (props [@dkotter](https://github.com/dkotter) via [#116](https://github.com/10up/convert-to-blocks/pull/116)). 96 | 97 | = 1.1.1 - 2023-01-05 = 98 | **Note that this version bumps the minimum PHP version from 7.0 to 7.4 and the minimum WordPress version from 5.4 to 5.7.** 99 | 100 | * **Added:** Bulk migration demo to readme (props [@jeffpaul](https://github.com/jeffpaul), [@dsawardekar](https://github.com/dsawardekar) via [#79](https://github.com/10up/convert-to-blocks/pull/79)). 101 | * **Added:** Release build GitHub Action to build a release zip used for testing (props [@dkotter](https://github.com/dkotter) via [#98](https://github.com/10up/convert-to-blocks/pull/98)). 102 | * **Changed:** Bump WordPress minimum version from 5.4 to 5.7 and PHP minimum version from 7.0 to 7.4 (props [@zamanq](https://github.com/zamanq), [@jeffpaul](https://github.com/jeffpaul), [@faisal-alvi](https://github.com/faisal-alvi), [@mehul0810](https://github.com/mehul0810) via [#80](https://github.com/10up/convert-to-blocks/pull/80)). 103 | * **Changed:** Bump WordPress "tested up to" version to 6.1 props [@peterwilsoncc](https://github.com/peterwilsoncc), [@faisal-alvi](https://github.com/faisal-alvi), [@cadic](https://github.com/cadic) via [#88](https://github.com/10up/convert-to-blocks/pull/88), [#91](https://github.com/10up/convert-to-blocks/pull/91)). 104 | * **Removed:** `is-svg` as it is no longer used after updating ancestor dependency `postcss-svgo` (props [@dependabot](https://github.com/apps/dependabot) via [#85](https://github.com/10up/convert-to-blocks/pull/85)). 105 | * **Fixed:** WP-CLI helptext that is causing an unknown parameter error (props [@dsawardekar](https://github.com/dsawardekar), [@jeffpaul](https://github.com/jeffpaul), [@norcross](https://github.com/norcross) via [#78](https://github.com/10up/convert-to-blocks/pull/78)). 106 | * **Security:** Bump `socket.io-parser` from 3.2.0 to 4.2.1 and `browser-sync` from 2.26.7 to 2.27.10 (props [@dependabot](https://github.com/apps/dependabot) via [#81](https://github.com/10up/convert-to-blocks/pull/81)). 107 | * **Security:** Bump `minimatch` from 3.0.4 to 3.1.2 (props [@dependabot](https://github.com/apps/dependabot) via [#82](https://github.com/10up/convert-to-blocks/pull/82)). 108 | * **Security:** Bump `nth-check` from 1.0.2 to 2.1.1 and `cssnano` from 4.1.10 to 5.1.14 (props [@dependabot](https://github.com/apps/dependabot) via [#84](https://github.com/10up/convert-to-blocks/pull/84)). 109 | * **Security:** Bump `postcss-svgo` from 4.0.2 to 4.0.3 (props [@dependabot](https://github.com/apps/dependabot) via [#85](https://github.com/10up/convert-to-blocks/pull/85)). 110 | * **Security:** Bump `minimist` from 1.2.0 to 1.2.7 and `mkdirp` from 0.5.1 to 0.5.6 (props [@dependabot](https://github.com/apps/dependabot) via [#86](https://github.com/10up/convert-to-blocks/pull/86)). 111 | * **Security:** Bump `loader-utils` from 1.2.3 to 1.4.2 and `webpack-cli` from 3.3.10 to 3.3.12 (props [@dependabot](https://github.com/apps/dependabot) via [#89](https://github.com/10up/convert-to-blocks/pull/89)). 112 | * **Security:** Bump `glob-parent` from 5.1.0 to 5.1.2 and `watchpack` from 1.6.0 to 1.7.5 (props [@dependabot](https://github.com/apps/dependabot) via [#90](https://github.com/10up/convert-to-blocks/pull/90)). 113 | * **Security:** Bump `kind-of` from 6.0.2 to 6.0.3 (props [@dependabot](https://github.com/apps/dependabot) via [#93](https://github.com/10up/convert-to-blocks/pull/93)). 114 | * **Security:** Bump `serialize-javascript` from 2.1.2 to 4.0.0 and `terser-webpack-plugin` from 1.4.3 to 1.4.5 (props [@dependabot](https://github.com/apps/dependabot) via [#94](https://github.com/10up/convert-to-blocks/pull/94)). 115 | * **Security:** Bump `engine.io` from 6.2.0 to 6.2.1 (props [@dependabot](https://github.com/apps/dependabot) via [#95](https://github.com/10up/convert-to-blocks/pull/95)). 116 | * **Security:** Bump `decode-uri-component` from 0.2.0 to 0.2.2 (props [@dependabot](https://github.com/apps/dependabot) via [#97](https://github.com/10up/convert-to-blocks/pull/97)). 117 | 118 | [View historical changelog details here](https://github.com/10up/convert-to-blocks/blob/develop/CHANGELOG.md). 119 | 120 | == Upgrade Notice == 121 | 122 | = 1.3.1 = 123 | Note that this release bumps the WordPress minimum version from 6.3 to 6.4. 124 | 125 | = 1.2.0 = 126 | Note that this release bumps the WordPress minimum version from 5.7 to 6.1 and the PHP minimum version from 7.4 to 8.0. 127 | 128 | = 1.1.1 = 129 | Note that this version bumps the minimum PHP version from 7.0 to 7.4 and the minimum WordPress version from 5.4 to 5.7. 130 | -------------------------------------------------------------------------------- /tests/ConvertToBlocks/AssetsTest.php: -------------------------------------------------------------------------------- 1 | assets = new Assets(); 13 | } 14 | 15 | function test_it_knows_if_not_on_block_editor() { 16 | $actual = $this->assets->is_block_editor(); 17 | $this->assertFalse( $actual ); 18 | } 19 | 20 | function test_it_knows_if_on_block_editor_screen() { 21 | $GLOBALS['current_screen'] = 22 | $mock = $this->getMockBuilder( stdObject::class ) 23 | ->setMethods( ['is_block_editor'] ) 24 | ->getMock(); 25 | 26 | $mock->expects( $this->once() ) 27 | ->method( 'is_block_editor' ) 28 | ->will( $this->returnValue( true ) ); 29 | 30 | $actual = $this->assets->is_block_editor(); 31 | $this->assertTrue( $actual ); 32 | 33 | $GLOBALS['current_screen'] = null; 34 | } 35 | 36 | function test_it_will_not_be_registered_on_frontend() { 37 | $actual = $this->assets->can_register(); 38 | $this->assertFalse( $actual ); 39 | } 40 | 41 | function test_it_will_be_registered_on_admin_screens() { 42 | $GLOBALS['current_screen'] = 43 | $mock = $this->getMockBuilder( stdObject::class ) 44 | ->setMethods( ['in_admin'] ) 45 | ->getMock(); 46 | 47 | $mock->expects( $this->once() ) 48 | ->method( 'in_admin' ) 49 | ->will( $this->returnValue( true ) ); 50 | 51 | $actual = $this->assets->can_register(); 52 | $this->assertTrue( $actual ); 53 | 54 | $GLOBALS['current_screen'] = null; 55 | } 56 | 57 | function test_it_registers_gutenbridge_script_on_registration() { 58 | $this->assets->register(); 59 | $actual = wp_script_is( 'convert_to_blocks_editor', 'registered' ); 60 | $this->assertTrue( $actual ); 61 | } 62 | 63 | function test_it_enqueues_script_on_assets_hook() { 64 | $this->assets->register(); 65 | $actual = wp_script_is( 'convert_to_blocks_editor', 'queue' ); 66 | 67 | $this->assertFalse( $actual ); 68 | 69 | $this->assets->do_assets(); 70 | 71 | $actual = wp_script_is( 'convert_to_blocks_editor', 'queue' ); 72 | 73 | $this->assertTrue( $actual ); 74 | } 75 | 76 | } 77 | -------------------------------------------------------------------------------- /tests/ConvertToBlocks/ClassicEditorSupportTest.php: -------------------------------------------------------------------------------- 1 | support = new ClassicEditorSupport(); 15 | $this->support->container = $this; 16 | } 17 | 18 | function test_it_has_a_container() { 19 | $this->assertSame( $this, $this->support->container ); 20 | } 21 | 22 | function test_it_will_not_be_registered_by_default() { 23 | $actual = $this->support->can_register(); 24 | $this->assertFalse( $actual ); 25 | } 26 | 27 | function test_it_can_be_registered_on_admin_pages() { 28 | $GLOBALS['current_screen'] = 29 | $mock = $this->getMockBuilder( stdObject::class ) 30 | ->setMethods( ['in_admin'] ) 31 | ->getMock(); 32 | 33 | $mock->expects( $this->once() ) 34 | ->method( 'in_admin' ) 35 | ->will( $this->returnValue( true ) ); 36 | 37 | $actual = $this->support->can_register(); 38 | $this->assertTrue( $actual ); 39 | 40 | $GLOBALS['current_screen'] = null; 41 | } 42 | 43 | function test_it_will_enable_block_editor_if_post_supports_gutenbridge() { 44 | $this->post_supports = true; 45 | $supports_editor = $this->support->enable_block_editor( true, 1 ); 46 | $not_supports_editor = $this->support->enable_block_editor( false, 1 ); 47 | 48 | $this->assertTrue( $supports_editor && $not_supports_editor ); 49 | } 50 | 51 | function test_wont_alter_editor_support_if_post_does_not_support_gutenbridge() { 52 | $this->post_supports = false; 53 | $supports_editor = $this->support->enable_block_editor( true, 1 ); 54 | $not_supports_editor = $this->support->enable_block_editor( false, 1 ); 55 | 56 | $this->assertTrue( $supports_editor && ! $not_supports_editor ); 57 | } 58 | 59 | function test_it_will_disable_block_editor_if_classic_query_param_is_present() { 60 | $this->post_supports = true; 61 | $this->classic_param = true; 62 | 63 | $actual = $this->support->enable_block_editor( true, 1 ); 64 | 65 | $this->assertFalse( $actual ); 66 | } 67 | 68 | /* helpers */ 69 | 70 | function post_supports_convert_to_blocks() { 71 | return $this->post_supports; 72 | } 73 | 74 | function has_classic_param() { 75 | return $this->classic_param; 76 | } 77 | 78 | } 79 | -------------------------------------------------------------------------------- /tests/ConvertToBlocks/PluginTest.php: -------------------------------------------------------------------------------- 1 | plugin = Plugin::get_instance(); 11 | } 12 | 13 | function test_it_is_a_singleton() { 14 | $a = Plugin::get_instance(); 15 | $b = Plugin::get_instance(); 16 | 17 | $this->assertSame( $a, $b ); 18 | } 19 | 20 | } 21 | -------------------------------------------------------------------------------- /tests/bin/initialize.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | wp-env run tests-wordpress chmod -c ugo+w /var/www/html 3 | wp-env run tests-cli wp rewrite structure '/%postname%/' --hard 4 | wp-env run tests-cli wp post create ./sample-post.txt --post_title='Classic Post' --post_status=publish 5 | 6 | # Disable gutenberg editor welcome guide modal. 7 | wp-env run tests-cli wp user meta add admin wp_persisted_preferences '{"core\/edit-post":{"isComplementaryAreaVisible":true,"welcomeGuide":false},"_modified":"2023-10-05T11:26:20.517Z"}' --format=json 8 | -------------------------------------------------------------------------------- /tests/bin/set-core-version.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | const fs = require("fs"); 4 | const { exit } = require("process"); 5 | 6 | const path = `${process.cwd()}/.wp-env.override.json`; 7 | 8 | let config = fs.existsSync(path) ? require(path) : {}; 9 | 10 | const args = process.argv.slice(2); 11 | 12 | if (args.length == 0) exit(0); 13 | 14 | if (args[0] == "latest") { 15 | if (fs.existsSync(path)) { 16 | fs.unlinkSync(path); 17 | } 18 | exit(0); 19 | } 20 | 21 | config.core = args[0]; 22 | 23 | if (!config.core.match(/^WordPress\/WordPress\#/)) { 24 | config.core = "WordPress/WordPress#" + config.core; 25 | } 26 | 27 | try { 28 | fs.writeFileSync(path, JSON.stringify(config)); 29 | } catch (err) { 30 | console.error(err); 31 | } 32 | -------------------------------------------------------------------------------- /tests/bin/wp-cli.yml: -------------------------------------------------------------------------------- 1 | apache_modules: 2 | - mod_rewrite 3 | -------------------------------------------------------------------------------- /tests/bootstrap.php: -------------------------------------------------------------------------------- 1 | { 36 | const wpEnvConfig = await loadConfig('../../'); 37 | 38 | if (wpEnvConfig) { 39 | const port = wpEnvConfig.env.tests.port || null; 40 | 41 | if (port) { 42 | config.baseUrl = wpEnvConfig.env.tests.config.WP_SITEURL; 43 | } 44 | } 45 | 46 | return config; 47 | }; 48 | -------------------------------------------------------------------------------- /tests/cypress/e2e/admin.test.js: -------------------------------------------------------------------------------- 1 | describe("Admin can login and open dashboard", () => { 2 | beforeEach(() => { 3 | cy.login(); 4 | }); 5 | 6 | it("Open dashboard", () => { 7 | cy.visit(`/wp-admin`); 8 | cy.get("h1").should("contain", "Dashboard"); 9 | }); 10 | 11 | it("Activate Convert to Blocks and deactivate it back", () => { 12 | cy.deactivatePlugin("convert-to-blocks"); 13 | cy.activatePlugin("convert-to-blocks"); 14 | }); 15 | }); 16 | -------------------------------------------------------------------------------- /tests/cypress/fixtures/example.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Using fixtures to represent data", 3 | "email": "hello@cypress.io", 4 | "body": "Fixtures are a great way to mock data for responses to routes" 5 | } 6 | -------------------------------------------------------------------------------- /tests/cypress/fixtures/sample-post.txt: -------------------------------------------------------------------------------- 1 |

Heading 2

2 |

Heading 3

3 |

Heading 4

4 |
Heading 5
5 |
Heading 6
6 | 7 |

Paragraph duis fringilla maximus ullamcorper. Sed id nulla semper, varius augue eu, cursus sapien. In hac habitasse platea dictumst.

8 | 9 |

Link to Google

10 | 11 |
    12 |
  • Curabitur in lacus lorem.
  • 13 |
  • Sed purus ipsum, luctus eu augue sed, aliquam luctus lacus.
  • 14 |
15 | 16 |
    17 |
  1. Curabitur in lacus lorem.
  2. 18 |
  3. Sed purus ipsum, luctus eu augue sed, aliquam luctus lacus.
  4. 19 |
20 | 21 | 22 | 23 |
24 |

Quote commodo molestie dui, ut convallis sapien sodales vel. Mauris at commodo ante, eu tristique justo. Duis elementum egestas nulla, bibendum eleifend massa hendrerit sit amet. Sed id ipsum et velit lacinia mattis. Mauris facilisis non ante id elementum.

25 | J. Smith 26 |
27 | -------------------------------------------------------------------------------- /tests/cypress/support/commands.js: -------------------------------------------------------------------------------- 1 | // *********************************************** 2 | // This example commands.js shows you how to 3 | // create various custom commands and overwrite 4 | // existing commands. 5 | // 6 | // For more comprehensive examples of custom 7 | // commands please read more here: 8 | // https://on.cypress.io/custom-commands 9 | // *********************************************** 10 | // 11 | // 12 | // -- This is a parent command -- 13 | // Cypress.Commands.add('login', (email, password) => { ... }) 14 | // 15 | // 16 | // -- This is a child command -- 17 | // Cypress.Commands.add('drag', { prevSubject: 'element'}, (subject, options) => { ... }) 18 | // 19 | // 20 | // -- This is a dual command -- 21 | // Cypress.Commands.add('dismiss', { prevSubject: 'optional'}, (subject, options) => { ... }) 22 | // 23 | // 24 | // -- This will overwrite an existing command -- 25 | // Cypress.Commands.overwrite('visit', (originalFn, url, options) => { ... }) 26 | -------------------------------------------------------------------------------- /tests/cypress/support/e2e.js: -------------------------------------------------------------------------------- 1 | // *********************************************************** 2 | // This example support/e2e.js is processed and 3 | // loaded automatically before your test files. 4 | // 5 | // This is a great place to put global configuration and 6 | // behavior that modifies Cypress. 7 | // 8 | // You can change the location of this file or turn off 9 | // automatically serving support files with the 10 | // 'supportFile' configuration option. 11 | // 12 | // You can read more here: 13 | // https://on.cypress.io/configuration 14 | // *********************************************************** 15 | 16 | import "@10up/cypress-wp-utils"; 17 | 18 | // Import commands.js using ES2015 syntax: 19 | import "./commands"; 20 | -------------------------------------------------------------------------------- /tests/cypress/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "allowJs": true, 4 | "types": ["cypress"] 5 | }, 6 | "include": ["**/*.*"] 7 | } 8 | -------------------------------------------------------------------------------- /webpack.config.js: -------------------------------------------------------------------------------- 1 | const defaultConfig = require('@wordpress/scripts/config/webpack.config'); 2 | 3 | module.exports = { 4 | ...defaultConfig, 5 | entry: { 6 | ...defaultConfig.entry, 7 | editor: './assets/js/editor/editor.js', 8 | }, 9 | module: { 10 | ...defaultConfig.module, 11 | }, 12 | }; 13 | --------------------------------------------------------------------------------