From a68ef51705d38da0216ac9ce7d72ae7723aa76b4 Mon Sep 17 00:00:00 2001 From: Joost de Valk Date: Wed, 6 Mar 2024 13:49:48 +0100 Subject: [PATCH] initial commit --- .gitattributes | 33 ++ .github/workflows/cs.yml | 68 +++ .github/workflows/lint.yml | 66 +++ .github/workflows/playground.yml | 16 + .github/workflows/security.yml | 34 ++ .github/workflows/wp-version-checker.yml | 20 + .gitignore | 3 + .phpcs.xml.dist | 153 ++++++ README.md | 7 + blueprint-builder.php | 26 + composer.json | 23 + composer.lock | 640 +++++++++++++++++++++++ readme.txt | 34 ++ src/autoload.php | 24 + src/class-admin.php | 63 +++ src/class-builder.php | 288 ++++++++++ src/class-endpoint.php | 100 ++++ src/class-plugin.php | 38 ++ 18 files changed, 1636 insertions(+) create mode 100644 .gitattributes create mode 100644 .github/workflows/cs.yml create mode 100644 .github/workflows/lint.yml create mode 100644 .github/workflows/playground.yml create mode 100644 .github/workflows/security.yml create mode 100644 .github/workflows/wp-version-checker.yml create mode 100644 .gitignore create mode 100644 .phpcs.xml.dist create mode 100644 README.md create mode 100644 blueprint-builder.php create mode 100644 composer.json create mode 100644 composer.lock create mode 100644 readme.txt create mode 100644 src/autoload.php create mode 100644 src/class-admin.php create mode 100644 src/class-builder.php create mode 100644 src/class-endpoint.php create mode 100644 src/class-plugin.php diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..ba46b78 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,33 @@ +# +# Exclude these files from release archives. +# This will also make them unavailable when using Composer with `--prefer-dist`. +# If you develop for Clicky using Composer, use `--prefer-source`. +# https://www.reddit.com/r/PHP/comments/2jzp6k/i_dont_need_your_tests_in_my_production +# https://blog.madewithlove.be/post/gitattributes/ +# +.distignore export-ignore +/.editorconfig export-ignore +/.eslintrc export-ignore +/.github export-ignore +/.gitattributes export-ignore +/.gitignore export-ignore +/.phpcs.xml export-ignore +/.phpcs.xml.dist export-ignore +/.cache export-ignore +composer.json export-ignore +composer.lock export-ignore +phpunit.xml.dist export-ignore +/README.md export-ignore +/tests export-ignore + +# +# Auto detect text files and perform LF normalization +# http://davidlaing.com/2012/09/19/customise-your-gitattributes-to-become-a-git-ninja/ +# +* text=auto + +# +# The above will handle all files NOT found below +# +*.md text +*.php text diff --git a/.github/workflows/cs.yml b/.github/workflows/cs.yml new file mode 100644 index 0000000..f7ea8e3 --- /dev/null +++ b/.github/workflows/cs.yml @@ -0,0 +1,68 @@ +name: CS + +on: + # Run on all relevant pushes (except to main) and on all relevant pull requests. + push: + paths: + - '**.php' + - 'composer.json' + - 'composer.lock' + - '.phpcs.xml.dist' + - 'phpcs.xml.dist' + - '.github/workflows/cs.yml' + pull_request: + paths: + - '**.php' + - 'composer.json' + - 'composer.lock' + - '.phpcs.xml.dist' + - 'phpcs.xml.dist' + - '.github/workflows/cs.yml' + # Allow manually triggering the workflow. + workflow_dispatch: + +# Cancels all previous workflow runs for the same branch that have not yet completed. +concurrency: + # The concurrency group contains the workflow name and the branch name. + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +jobs: + checkcs: + name: 'Check code style' + runs-on: ubuntu-latest + + steps: + - name: Checkout code + uses: actions/checkout@v3 + + - name: Install PHP + uses: shivammathur/setup-php@v2 + with: + php-version: '7.4' + coverage: none + tools: cs2pr + + # Validate the composer.json file. + # @link https://getcomposer.org/doc/03-cli.md#validate + - name: Validate Composer installation + run: composer validate --no-check-all + + # Install dependencies and handle caching in one go. + # @link https://github.com/marketplace/actions/install-composer-dependencies + - name: Install Composer dependencies + uses: ramsey/composer-install@v2 + with: + # Bust the cache at least once a month - output format: YYYY-MM. + custom-cache-suffix: $(date -u "+%Y-%m") + + # Check the codestyle of the files. + # The results of the CS check will be shown inline in the PR via the CS2PR tool. + # @link https://github.com/staabm/annotate-pull-request-from-checkstyle/ + - name: Check PHP code style + id: phpcs + run: composer check-cs -- --no-cache --report-full --report-checkstyle=./phpcs-report.xml + + - name: Show PHPCS results in PR + if: ${{ always() && steps.phpcs.outcome == 'failure' }} + run: cs2pr ./phpcs-report.xml diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml new file mode 100644 index 0000000..a2763b1 --- /dev/null +++ b/.github/workflows/lint.yml @@ -0,0 +1,66 @@ +name: Lint + +on: + # Run on pushes to select branches and on all pull requests. + push: + branches: + - main + - develop + - 'release/[0-9]+.[0-9]+*' + - 'hotfix/[0-9]+.[0-9]+*' + pull_request: + # Allow manually triggering the workflow. + workflow_dispatch: + +# Cancels all previous workflow runs for the same branch that have not yet completed. +concurrency: + # The concurrency group contains the workflow name and the branch name. + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +jobs: + lint: + runs-on: ubuntu-latest + + strategy: + matrix: + # Lint against the highest/lowest supported versions of each PHP major. + # And also do a run against "nightly" (the current dev version of PHP). + php_version: ['7.4', '8.0', '8.1', '8.2'] + + name: "Lint: PHP ${{ matrix.php_version }}" + + steps: + - name: Checkout code + uses: actions/checkout@v3 + + - name: Install PHP for the composer install + uses: shivammathur/setup-php@v2 + with: + php-version: 7.4 + coverage: none + + # The lint stage doesn't use code style, so no need for WPCS or phpcompatibility. + - name: 'Composer: adjust dependencies - remove PHPCompatibility' + run: composer remove --no-update --dev phpcompatibility/phpcompatibility-wp --no-scripts --no-interaction + - name: 'Composer: adjust dependencies - remove WPCS' + run: composer remove --no-update --dev wp-coding-standards/wpcs --no-scripts --no-interaction + + # Install dependencies and handle caching in one go. + # @link https://github.com/marketplace/actions/install-composer-dependencies + - name: Install Composer dependencies + uses: ramsey/composer-install@v2 + with: + # Bust the cache at least once a month - output format: YYYY-MM-DD. + custom-cache-suffix: $(date -u -d "-0 month -$(($(date +%d)-1)) days" "+%F") + + - name: Install PHP for the actual test + uses: shivammathur/setup-php@v2 + with: + php-version: ${{ matrix.php_version }} + ini-values: zend.assertions=1, error_reporting=-1, display_errors=On + coverage: none + tools: cs2pr + + - name: Lint against parse errors + run: composer lint -- --checkstyle | cs2pr diff --git a/.github/workflows/playground.yml b/.github/workflows/playground.yml new file mode 100644 index 0000000..f384d0b --- /dev/null +++ b/.github/workflows/playground.yml @@ -0,0 +1,16 @@ +name: Playground Comment + +on: + pull_request: + +jobs: + test: + runs-on: ubuntu-latest + permissions: + pull-requests: write + steps: + - uses: mshick/add-pr-comment@v2 + with: + message: | + **Test on Playground** + [Test this pull request on the Playground](https://playground.wordpress.net/#{"landingPage":"/wp-admin/admin.php?page=blueprint-builder","features":{"networking":true},"steps":[{"step":"login","username":"admin","password":"password"},{"step":"installPlugin","pluginZipFile":{"resource":"url","url":"https://bypass-cors.altha.workers.dev/${{ github.server_url }}/${{ github.repository }}/archive/${{ github.sha }}.zip"},"options":{"activate":true}}]}) diff --git a/.github/workflows/security.yml b/.github/workflows/security.yml new file mode 100644 index 0000000..a57a11f --- /dev/null +++ b/.github/workflows/security.yml @@ -0,0 +1,34 @@ +name: Security + +on: + # Run on all pushes and on all pull requests. + push: + pull_request: + # Also run this workflow every Monday at 6:00. + schedule: + - cron: '0 6 * * 1' + # Allow manually triggering the workflow. + workflow_dispatch: + +# Cancels all previous workflow runs for the same branch that have not yet completed. +concurrency: + # The concurrency group contains the workflow name and the branch name. + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +jobs: + security: + name: 'Security check' + runs-on: ubuntu-latest + + # Don't run the cronjob in this workflow on forks. + if: github.event_name != 'schedule' || (github.event_name == 'schedule' && github.repository_owner == 'Emilia-Capital') + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + # This action checks the `composer.lock` file against known security vulnerabilities in the dependencies. + # https://github.com/marketplace/actions/the-php-security-checker + - name: Run Security Check + uses: symfonycorp/security-checker-action@v4 diff --git a/.github/workflows/wp-version-checker.yml b/.github/workflows/wp-version-checker.yml new file mode 100644 index 0000000..8cad49a --- /dev/null +++ b/.github/workflows/wp-version-checker.yml @@ -0,0 +1,20 @@ +name: "WordPress version checker" +on: + push: + branches: + - develop + - main + schedule: + - cron: '0 0 * * *' + +permissions: + issues: write + +jobs: + wordpress-version-checker: + runs-on: ubuntu-latest + steps: + - name: WordPress version checker + uses: skaut/wordpress-version-checker@v2.0.0 + with: + repo-token: ${{ secrets.GITHUB_TOKEN }} diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..c519f08 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +/vendor/ +/coverage/ +.phpunit.result.cache diff --git a/.phpcs.xml.dist b/.phpcs.xml.dist new file mode 100644 index 0000000..8043436 --- /dev/null +++ b/.phpcs.xml.dist @@ -0,0 +1,153 @@ + + + + A custom set of rules to check for the Blueprint Builder project + + + + . + + + /vendor/* + + + /node_modules/* + + + /coverage/* + + + *.min.js + + + + + + + + + + + + + + + + + + + + + + + + + + + + *\.php + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + blueprint-builder.php + + + + /tests/bootstrap\.php$ + + diff --git a/README.md b/README.md new file mode 100644 index 0000000..220de75 --- /dev/null +++ b/README.md @@ -0,0 +1,7 @@ +[![CS](https://github.com/emilia-capital/blueprint-builder/actions/workflows/cs.yml/badge.svg)](https://github.com/emilia-capital/blueprint-builder/actions/workflows/cs.yml) +[![Lint](https://github.com/emilia-capital/blueprint-builder/actions/workflows/lint.yml/badge.svg)](https://github.com/emilia-capital/blueprint-builder/actions/workflows/lint.yml) +[![Security](https://github.com/emilia-capital/blueprint-builder/actions/workflows/security.yml/badge.svg)](https://github.com/emilia-capital/blueprint-builder/actions/workflows/security.yml) + +# Blueprint Builder +This plugin helps you create a blueprint from your current site. + diff --git a/blueprint-builder.php b/blueprint-builder.php new file mode 100644 index 0000000..69216b5 --- /dev/null +++ b/blueprint-builder.php @@ -0,0 +1,26 @@ +=5.4", + "squizlabs/php_codesniffer": "^2.0 || ^3.1.0 || ^4.0" + }, + "require-dev": { + "composer/composer": "*", + "ext-json": "*", + "ext-zip": "*", + "php-parallel-lint/php-parallel-lint": "^1.3.1", + "phpcompatibility/php-compatibility": "^9.0", + "yoast/phpunit-polyfills": "^1.0" + }, + "type": "composer-plugin", + "extra": { + "class": "PHPCSStandards\\Composer\\Plugin\\Installers\\PHPCodeSniffer\\Plugin" + }, + "autoload": { + "psr-4": { + "PHPCSStandards\\Composer\\Plugin\\Installers\\PHPCodeSniffer\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Franck Nijhof", + "email": "franck.nijhof@dealerdirect.com", + "homepage": "http://www.frenck.nl", + "role": "Developer / IT Manager" + }, + { + "name": "Contributors", + "homepage": "https://github.com/PHPCSStandards/composer-installer/graphs/contributors" + } + ], + "description": "PHP_CodeSniffer Standards Composer Installer Plugin", + "homepage": "http://www.dealerdirect.com", + "keywords": [ + "PHPCodeSniffer", + "PHP_CodeSniffer", + "code quality", + "codesniffer", + "composer", + "installer", + "phpcbf", + "phpcs", + "plugin", + "qa", + "quality", + "standard", + "standards", + "style guide", + "stylecheck", + "tests" + ], + "support": { + "issues": "https://github.com/PHPCSStandards/composer-installer/issues", + "source": "https://github.com/PHPCSStandards/composer-installer" + }, + "time": "2023-01-05T11:28:13+00:00" + }, + { + "name": "php-parallel-lint/php-parallel-lint", + "version": "v1.3.2", + "source": { + "type": "git", + "url": "https://github.com/php-parallel-lint/PHP-Parallel-Lint.git", + "reference": "6483c9832e71973ed29cf71bd6b3f4fde438a9de" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-parallel-lint/PHP-Parallel-Lint/zipball/6483c9832e71973ed29cf71bd6b3f4fde438a9de", + "reference": "6483c9832e71973ed29cf71bd6b3f4fde438a9de", + "shasum": "" + }, + "require": { + "ext-json": "*", + "php": ">=5.3.0" + }, + "replace": { + "grogy/php-parallel-lint": "*", + "jakub-onderka/php-parallel-lint": "*" + }, + "require-dev": { + "nette/tester": "^1.3 || ^2.0", + "php-parallel-lint/php-console-highlighter": "0.* || ^1.0", + "squizlabs/php_codesniffer": "^3.6" + }, + "suggest": { + "php-parallel-lint/php-console-highlighter": "Highlight syntax in code snippet" + }, + "bin": [ + "parallel-lint" + ], + "type": "library", + "autoload": { + "classmap": [ + "./src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-2-Clause" + ], + "authors": [ + { + "name": "Jakub Onderka", + "email": "ahoj@jakubonderka.cz" + } + ], + "description": "This tool check syntax of PHP files about 20x faster than serial check.", + "homepage": "https://github.com/php-parallel-lint/PHP-Parallel-Lint", + "support": { + "issues": "https://github.com/php-parallel-lint/PHP-Parallel-Lint/issues", + "source": "https://github.com/php-parallel-lint/PHP-Parallel-Lint/tree/v1.3.2" + }, + "time": "2022-02-21T12:50:22+00:00" + }, + { + "name": "phpcompatibility/php-compatibility", + "version": "9.3.5", + "source": { + "type": "git", + "url": "https://github.com/PHPCompatibility/PHPCompatibility.git", + "reference": "9fb324479acf6f39452e0655d2429cc0d3914243" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/PHPCompatibility/PHPCompatibility/zipball/9fb324479acf6f39452e0655d2429cc0d3914243", + "reference": "9fb324479acf6f39452e0655d2429cc0d3914243", + "shasum": "" + }, + "require": { + "php": ">=5.3", + "squizlabs/php_codesniffer": "^2.3 || ^3.0.2" + }, + "conflict": { + "squizlabs/php_codesniffer": "2.6.2" + }, + "require-dev": { + "phpunit/phpunit": "~4.5 || ^5.0 || ^6.0 || ^7.0" + }, + "suggest": { + "dealerdirect/phpcodesniffer-composer-installer": "^0.5 || This Composer plugin will sort out the PHPCS 'installed_paths' automatically.", + "roave/security-advisories": "dev-master || Helps prevent installing dependencies with known security issues." + }, + "type": "phpcodesniffer-standard", + "notification-url": "https://packagist.org/downloads/", + "license": [ + "LGPL-3.0-or-later" + ], + "authors": [ + { + "name": "Wim Godden", + "homepage": "https://github.com/wimg", + "role": "lead" + }, + { + "name": "Juliette Reinders Folmer", + "homepage": "https://github.com/jrfnl", + "role": "lead" + }, + { + "name": "Contributors", + "homepage": "https://github.com/PHPCompatibility/PHPCompatibility/graphs/contributors" + } + ], + "description": "A set of sniffs for PHP_CodeSniffer that checks for PHP cross-version compatibility.", + "homepage": "http://techblog.wimgodden.be/tag/codesniffer/", + "keywords": [ + "compatibility", + "phpcs", + "standards" + ], + "support": { + "issues": "https://github.com/PHPCompatibility/PHPCompatibility/issues", + "source": "https://github.com/PHPCompatibility/PHPCompatibility" + }, + "time": "2019-12-27T09:44:58+00:00" + }, + { + "name": "phpcompatibility/phpcompatibility-paragonie", + "version": "1.3.2", + "source": { + "type": "git", + "url": "https://github.com/PHPCompatibility/PHPCompatibilityParagonie.git", + "reference": "bba5a9dfec7fcfbd679cfaf611d86b4d3759da26" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/PHPCompatibility/PHPCompatibilityParagonie/zipball/bba5a9dfec7fcfbd679cfaf611d86b4d3759da26", + "reference": "bba5a9dfec7fcfbd679cfaf611d86b4d3759da26", + "shasum": "" + }, + "require": { + "phpcompatibility/php-compatibility": "^9.0" + }, + "require-dev": { + "dealerdirect/phpcodesniffer-composer-installer": "^0.7", + "paragonie/random_compat": "dev-master", + "paragonie/sodium_compat": "dev-master" + }, + "suggest": { + "dealerdirect/phpcodesniffer-composer-installer": "^0.7 || This Composer plugin will sort out the PHP_CodeSniffer 'installed_paths' automatically.", + "roave/security-advisories": "dev-master || Helps prevent installing dependencies with known security issues." + }, + "type": "phpcodesniffer-standard", + "notification-url": "https://packagist.org/downloads/", + "license": [ + "LGPL-3.0-or-later" + ], + "authors": [ + { + "name": "Wim Godden", + "role": "lead" + }, + { + "name": "Juliette Reinders Folmer", + "role": "lead" + } + ], + "description": "A set of rulesets for PHP_CodeSniffer to check for PHP cross-version compatibility issues in projects, while accounting for polyfills provided by the Paragonie polyfill libraries.", + "homepage": "http://phpcompatibility.com/", + "keywords": [ + "compatibility", + "paragonie", + "phpcs", + "polyfill", + "standards", + "static analysis" + ], + "support": { + "issues": "https://github.com/PHPCompatibility/PHPCompatibilityParagonie/issues", + "source": "https://github.com/PHPCompatibility/PHPCompatibilityParagonie" + }, + "time": "2022-10-25T01:46:02+00:00" + }, + { + "name": "phpcompatibility/phpcompatibility-wp", + "version": "2.1.4", + "source": { + "type": "git", + "url": "https://github.com/PHPCompatibility/PHPCompatibilityWP.git", + "reference": "b6c1e3ee1c35de6c41a511d5eb9bd03e447480a5" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/PHPCompatibility/PHPCompatibilityWP/zipball/b6c1e3ee1c35de6c41a511d5eb9bd03e447480a5", + "reference": "b6c1e3ee1c35de6c41a511d5eb9bd03e447480a5", + "shasum": "" + }, + "require": { + "phpcompatibility/php-compatibility": "^9.0", + "phpcompatibility/phpcompatibility-paragonie": "^1.0" + }, + "require-dev": { + "dealerdirect/phpcodesniffer-composer-installer": "^0.7" + }, + "suggest": { + "dealerdirect/phpcodesniffer-composer-installer": "^0.7 || This Composer plugin will sort out the PHP_CodeSniffer 'installed_paths' automatically.", + "roave/security-advisories": "dev-master || Helps prevent installing dependencies with known security issues." + }, + "type": "phpcodesniffer-standard", + "notification-url": "https://packagist.org/downloads/", + "license": [ + "LGPL-3.0-or-later" + ], + "authors": [ + { + "name": "Wim Godden", + "role": "lead" + }, + { + "name": "Juliette Reinders Folmer", + "role": "lead" + } + ], + "description": "A ruleset for PHP_CodeSniffer to check for PHP cross-version compatibility issues in projects, while accounting for polyfills provided by WordPress.", + "homepage": "http://phpcompatibility.com/", + "keywords": [ + "compatibility", + "phpcs", + "standards", + "static analysis", + "wordpress" + ], + "support": { + "issues": "https://github.com/PHPCompatibility/PHPCompatibilityWP/issues", + "source": "https://github.com/PHPCompatibility/PHPCompatibilityWP" + }, + "time": "2022-10-24T09:00:36+00:00" + }, + { + "name": "phpcsstandards/phpcsextra", + "version": "1.2.1", + "source": { + "type": "git", + "url": "https://github.com/PHPCSStandards/PHPCSExtra.git", + "reference": "11d387c6642b6e4acaf0bd9bf5203b8cca1ec489" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/PHPCSStandards/PHPCSExtra/zipball/11d387c6642b6e4acaf0bd9bf5203b8cca1ec489", + "reference": "11d387c6642b6e4acaf0bd9bf5203b8cca1ec489", + "shasum": "" + }, + "require": { + "php": ">=5.4", + "phpcsstandards/phpcsutils": "^1.0.9", + "squizlabs/php_codesniffer": "^3.8.0" + }, + "require-dev": { + "php-parallel-lint/php-console-highlighter": "^1.0", + "php-parallel-lint/php-parallel-lint": "^1.3.2", + "phpcsstandards/phpcsdevcs": "^1.1.6", + "phpcsstandards/phpcsdevtools": "^1.2.1", + "phpunit/phpunit": "^4.5 || ^5.0 || ^6.0 || ^7.0 || ^8.0 || ^9.0" + }, + "type": "phpcodesniffer-standard", + "extra": { + "branch-alias": { + "dev-stable": "1.x-dev", + "dev-develop": "1.x-dev" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "LGPL-3.0-or-later" + ], + "authors": [ + { + "name": "Juliette Reinders Folmer", + "homepage": "https://github.com/jrfnl", + "role": "lead" + }, + { + "name": "Contributors", + "homepage": "https://github.com/PHPCSStandards/PHPCSExtra/graphs/contributors" + } + ], + "description": "A collection of sniffs and standards for use with PHP_CodeSniffer.", + "keywords": [ + "PHP_CodeSniffer", + "phpcbf", + "phpcodesniffer-standard", + "phpcs", + "standards", + "static analysis" + ], + "support": { + "issues": "https://github.com/PHPCSStandards/PHPCSExtra/issues", + "security": "https://github.com/PHPCSStandards/PHPCSExtra/security/policy", + "source": "https://github.com/PHPCSStandards/PHPCSExtra" + }, + "funding": [ + { + "url": "https://github.com/PHPCSStandards", + "type": "github" + }, + { + "url": "https://github.com/jrfnl", + "type": "github" + }, + { + "url": "https://opencollective.com/php_codesniffer", + "type": "open_collective" + } + ], + "time": "2023-12-08T16:49:07+00:00" + }, + { + "name": "phpcsstandards/phpcsutils", + "version": "1.0.9", + "source": { + "type": "git", + "url": "https://github.com/PHPCSStandards/PHPCSUtils.git", + "reference": "908247bc65010c7b7541a9551e002db12e9dae70" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/PHPCSStandards/PHPCSUtils/zipball/908247bc65010c7b7541a9551e002db12e9dae70", + "reference": "908247bc65010c7b7541a9551e002db12e9dae70", + "shasum": "" + }, + "require": { + "dealerdirect/phpcodesniffer-composer-installer": "^0.4.1 || ^0.5 || ^0.6.2 || ^0.7 || ^1.0", + "php": ">=5.4", + "squizlabs/php_codesniffer": "^3.8.0 || 4.0.x-dev@dev" + }, + "require-dev": { + "ext-filter": "*", + "php-parallel-lint/php-console-highlighter": "^1.0", + "php-parallel-lint/php-parallel-lint": "^1.3.2", + "phpcsstandards/phpcsdevcs": "^1.1.6", + "yoast/phpunit-polyfills": "^1.1.0 || ^2.0.0" + }, + "type": "phpcodesniffer-standard", + "extra": { + "branch-alias": { + "dev-stable": "1.x-dev", + "dev-develop": "1.x-dev" + } + }, + "autoload": { + "classmap": [ + "PHPCSUtils/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "LGPL-3.0-or-later" + ], + "authors": [ + { + "name": "Juliette Reinders Folmer", + "homepage": "https://github.com/jrfnl", + "role": "lead" + }, + { + "name": "Contributors", + "homepage": "https://github.com/PHPCSStandards/PHPCSUtils/graphs/contributors" + } + ], + "description": "A suite of utility functions for use with PHP_CodeSniffer", + "homepage": "https://phpcsutils.com/", + "keywords": [ + "PHP_CodeSniffer", + "phpcbf", + "phpcodesniffer-standard", + "phpcs", + "phpcs3", + "standards", + "static analysis", + "tokens", + "utility" + ], + "support": { + "docs": "https://phpcsutils.com/", + "issues": "https://github.com/PHPCSStandards/PHPCSUtils/issues", + "security": "https://github.com/PHPCSStandards/PHPCSUtils/security/policy", + "source": "https://github.com/PHPCSStandards/PHPCSUtils" + }, + "funding": [ + { + "url": "https://github.com/PHPCSStandards", + "type": "github" + }, + { + "url": "https://github.com/jrfnl", + "type": "github" + }, + { + "url": "https://opencollective.com/php_codesniffer", + "type": "open_collective" + } + ], + "time": "2023-12-08T14:50:00+00:00" + }, + { + "name": "squizlabs/php_codesniffer", + "version": "3.8.1", + "source": { + "type": "git", + "url": "https://github.com/PHPCSStandards/PHP_CodeSniffer.git", + "reference": "14f5fff1e64118595db5408e946f3a22c75807f7" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/PHPCSStandards/PHP_CodeSniffer/zipball/14f5fff1e64118595db5408e946f3a22c75807f7", + "reference": "14f5fff1e64118595db5408e946f3a22c75807f7", + "shasum": "" + }, + "require": { + "ext-simplexml": "*", + "ext-tokenizer": "*", + "ext-xmlwriter": "*", + "php": ">=5.4.0" + }, + "require-dev": { + "phpunit/phpunit": "^4.0 || ^5.0 || ^6.0 || ^7.0 || ^8.0 || ^9.3.4" + }, + "bin": [ + "bin/phpcbf", + "bin/phpcs" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.x-dev" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Greg Sherwood", + "role": "Former lead" + }, + { + "name": "Juliette Reinders Folmer", + "role": "Current lead" + }, + { + "name": "Contributors", + "homepage": "https://github.com/PHPCSStandards/PHP_CodeSniffer/graphs/contributors" + } + ], + "description": "PHP_CodeSniffer tokenizes PHP, JavaScript and CSS files and detects violations of a defined set of coding standards.", + "homepage": "https://github.com/PHPCSStandards/PHP_CodeSniffer", + "keywords": [ + "phpcs", + "standards", + "static analysis" + ], + "support": { + "issues": "https://github.com/PHPCSStandards/PHP_CodeSniffer/issues", + "security": "https://github.com/PHPCSStandards/PHP_CodeSniffer/security/policy", + "source": "https://github.com/PHPCSStandards/PHP_CodeSniffer", + "wiki": "https://github.com/PHPCSStandards/PHP_CodeSniffer/wiki" + }, + "funding": [ + { + "url": "https://github.com/PHPCSStandards", + "type": "github" + }, + { + "url": "https://github.com/jrfnl", + "type": "github" + }, + { + "url": "https://opencollective.com/php_codesniffer", + "type": "open_collective" + } + ], + "time": "2024-01-11T20:47:48+00:00" + }, + { + "name": "wp-coding-standards/wpcs", + "version": "3.0.1", + "source": { + "type": "git", + "url": "https://github.com/WordPress/WordPress-Coding-Standards.git", + "reference": "b4caf9689f1a0e4a4c632679a44e638c1c67aff1" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/WordPress/WordPress-Coding-Standards/zipball/b4caf9689f1a0e4a4c632679a44e638c1c67aff1", + "reference": "b4caf9689f1a0e4a4c632679a44e638c1c67aff1", + "shasum": "" + }, + "require": { + "ext-filter": "*", + "ext-libxml": "*", + "ext-tokenizer": "*", + "ext-xmlreader": "*", + "php": ">=5.4", + "phpcsstandards/phpcsextra": "^1.1.0", + "phpcsstandards/phpcsutils": "^1.0.8", + "squizlabs/php_codesniffer": "^3.7.2" + }, + "require-dev": { + "php-parallel-lint/php-console-highlighter": "^1.0.0", + "php-parallel-lint/php-parallel-lint": "^1.3.2", + "phpcompatibility/php-compatibility": "^9.0", + "phpcsstandards/phpcsdevtools": "^1.2.0", + "phpunit/phpunit": "^4.0 || ^5.0 || ^6.0 || ^7.0" + }, + "suggest": { + "ext-iconv": "For improved results", + "ext-mbstring": "For improved results" + }, + "type": "phpcodesniffer-standard", + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Contributors", + "homepage": "https://github.com/WordPress/WordPress-Coding-Standards/graphs/contributors" + } + ], + "description": "PHP_CodeSniffer rules (sniffs) to enforce WordPress coding conventions", + "keywords": [ + "phpcs", + "standards", + "static analysis", + "wordpress" + ], + "support": { + "issues": "https://github.com/WordPress/WordPress-Coding-Standards/issues", + "source": "https://github.com/WordPress/WordPress-Coding-Standards", + "wiki": "https://github.com/WordPress/WordPress-Coding-Standards/wiki" + }, + "funding": [ + { + "url": "https://opencollective.com/thewpcc/contribute/wp-php-63406", + "type": "custom" + } + ], + "time": "2023-09-14T07:06:09+00:00" + } + ], + "aliases": [], + "minimum-stability": "stable", + "stability-flags": [], + "prefer-stable": false, + "prefer-lowest": false, + "platform": [], + "platform-dev": [], + "plugin-api-version": "2.6.0" +} diff --git a/readme.txt b/readme.txt new file mode 100644 index 0000000..49ff1bd --- /dev/null +++ b/readme.txt @@ -0,0 +1,34 @@ +=== Blueprint Builder === +Contributors: joostdevalk +Tags: blueprint, playground +Requires at least: 6.2 +Tested up to: 6.4 +Requires PHP: 7.4 +Stable tag: 1.0 +License: GPL3+ +License URI: https://www.gnu.org/licenses/gpl-3.0.en.html + +This plugin helps you create a blueprint from your current site. + +== Description == + +This plugin helps you create a blueprint from your current site. + +== Frequently Asked Questions == + += None so far = +It's weird, I know. + +== Installation == +1. Download the zip. +2. Install the plugin. +3. Go the Blueprint Builder settings. + +== Screenshots == +1. The Blueprint Builder settings. + +== Changelog == + += 1.0 = + +Initial release on GitHub. diff --git a/src/autoload.php b/src/autoload.php new file mode 100644 index 0000000..cac9f23 --- /dev/null +++ b/src/autoload.php @@ -0,0 +1,24 @@ +builder = $builder; + } + + /** + * Register hooks. + * + * @return void + */ + public function register_hooks() { + add_action( 'admin_menu', [ $this, 'add_menu' ] ); + } + + /** + * Add the menu. + * + * @return void + */ + public function add_menu() { + add_menu_page( + 'Blueprint Builder', + 'Blueprint Builder', + 'manage_options', + 'blueprint-builder', + [ $this, 'render_page' ], + 'dashicons-welcome-widgets-menus' + ); + } + + /** + * Render the page. + * + * @return void + */ + public function render_page() { + $blueprint = $this->builder->generate(); + echo '

Blueprint Builder

'; + echo '

Here you can create a blueprint of your current environment.

'; + echo '
'; + + $blueprint_url = site_url( 'wp-json/blueprint-builder/v1/json-' . get_option( 'blueprint_builder_key' ) ); + $playground_url = 'https://playground.wordpress.net/?blueprint-url=' . $blueprint_url; + + echo '

If you website is live, you can open the Playground with this blueprint

'; + } +} diff --git a/src/class-builder.php b/src/class-builder.php new file mode 100644 index 0000000..4576733 --- /dev/null +++ b/src/class-builder.php @@ -0,0 +1,288 @@ + 'https://playground.wordpress.net/blueprint-schema.json', + 'preferredVersions' => [ + 'php' => '', + 'wp' => '', + ], + 'features' => [ + 'networking' => true, + ], + 'phpExtensionBundles' => [ 'kitchen-sink' ], + 'landingPage' => '/wp-admin/', + 'steps' => [], + ]; + + /** + * WordPress.org API URL for plugin information. + */ + const WP_ORG_PLUGIN_API_URL = 'https://api.wordpress.org/plugins/info/1.0/'; + + /** + * Generates a blueprint file. + * + * @return string The blueprint, JSON encoded. + */ + public function generate() { + $php_version = explode( '.', phpversion() ); + $this->blueprint['preferredVersions']['php'] = $php_version[0] . '.' . $php_version[1]; + + $wp_version = explode( '.', get_bloginfo( 'version' ) ); + $this->blueprint['preferredVersions']['wp'] = $wp_version[0] . '.' . $wp_version[1]; + if ( ! is_numeric( $wp_version[1] ) ) { + $this->blueprint['preferredVersions']['wp'] = 'nightly'; + } + + $this->add_login_step(); + $this->add_theme_installations_steps(); + $this->add_plugins_installations_steps(); + $this->add_wxr_step(); + $this->add_option_steps(); + + $this->write(); + + return wp_json_encode( $this->blueprint ); + } + + /** + * Writes the blueprint to a file. + * + * @return void + */ + public function write() { + $filename = 'blueprint-' . \get_option( 'blueprint_builder_key' ) . '.json'; + + global $wp_filesystem; + + require_once ( ABSPATH . '/wp-admin/includes/file.php' ); + WP_Filesystem(); + $wp_filesystem->put_contents( trailingslashit( WP_CONTENT_DIR ) . $filename, wp_json_encode( $this->blueprint, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES ) ); + } + + /** + * Adds the login step to the blueprint. + * + * @return void + */ + protected function add_login_step() { + $this->blueprint['steps'][] = [ + 'step' => 'login', + 'username' => 'admin', + 'password' => 'password', + ]; + } + + /** + * Adds the WXR import step to the blueprint. + * + * @return void + */ + protected function add_wxr_step() { + $wxr_url = site_url( 'wp-json/blueprint-builder/v1/wxr-' . get_option( 'blueprint_builder_key' ) . '.xml' ); + + $this->blueprint['steps'][] = [ + 'step' => 'importFile', + 'file' => [ + 'resource' => 'url', + 'url' => $wxr_url, + ], + ]; + } + + /** + * Adds the theme installation steps to the blueprint. + * + * @return void + */ + protected function add_theme_installations_steps() { + $active_theme = $this->get_active_theme(); + + // Workaround for bug in Playground. + // @link https://github.com/WordPress/wordpress-playground/issues/999 + if ( $active_theme === 'twentytwentyfour' ) { + return; + } + + // phpcs:ignore Generic.Commenting.Todo.TaskFound + // @todo add support for child & parent themes. + $this->blueprint['steps'][] = [ + 'step' => 'installTheme', + 'themeZipFile' => [ + 'resource' => 'wordpress.org/themes', + 'slug' => $this->get_active_theme(), + ], + 'options' => [ + 'activate' => true, + ], + ]; + } + + /** + * Fetches the active theme. + * + * @return string The active theme. + */ + private function get_active_theme() { + // phpcs:ignore Generic.Commenting.Todo.TaskFound + // @todo Add support for child themes. + // Currently returns the parent theme on purpose until we have a way to download the child theme from the site itself. + return get_template(); + } + + /** + * Adds the plugin installation steps to the blueprint. + * + * @return void + */ + protected function add_plugins_installations_steps() { + foreach ( $this->get_active_plugins() as $plugin ) { + if ( $plugin['wordpress_org'] ) { + $this->blueprint['steps'][] = [ + 'step' => 'installPlugin', + 'pluginZipFile' => [ + 'resource' => 'wordpress.org/plugins', + 'slug' => $plugin['slug'], + ], + 'options' => [ + 'activate' => true, + ], + ]; + } + } + } + + /** + * Fetches the active plugins that are available on WordPress.org. + * + * @return array List of active plugins. + */ + protected function get_active_plugins() { + $plugins = get_option( 'active_plugins' ); + $return_plugins = []; + + foreach ( $plugins as $plugin ) { + $plugin_data = get_plugin_data( WP_PLUGIN_DIR . '/' . $plugin ); + $slug = pathinfo( basename( $plugin ), PATHINFO_FILENAME ); + + /** + * Prevent some special cases: + * - akismet breaks, + * - blueprint-builder is this plugin and not needed, + * - Plausible breaks - https://github.com/plausible/wordpress/issues/174 + */ + if ( in_array( $slug, [ 'akismet', 'blueprint-builder', 'plausible-analytics' ] ) ) { + continue; + } + $wp_org_data = wp_remote_post( + self::WP_ORG_PLUGIN_API_URL, + [ + 'body' => [ + 'action' => 'plugin_information', + 'request' => serialize( + (object) [ + 'slug' => $slug, + 'fields' => [ 'sections' => false ], + ] + ), + 'per_page' => 1, + ], + ] + ); + $wordpress_org = false; + if ( wp_remote_retrieve_response_code( $wp_org_data ) === 200 ) { + $wordpress_org = true; + } + $return_plugins[] = [ + 'slug' => $slug, + 'version' => $plugin_data['Version'], + 'wordpress_org' => $wordpress_org, + ]; + } + + return $return_plugins; + } + + /** + * Adds the option steps to the blueprint. + * + * @return void + */ + protected function add_option_steps() { + $options = wp_load_alloptions(); + + // Prevent some special cases. + foreach ( [ + 'active_plugins', + 'auth_key', + 'auth_salt', + 'cron', + 'home', + 'https_detection_errors', + 'initial_db_version', + 'logged_in_key', + 'logged_in_salt', + 'mailserver_url', + 'mailserver_login', + 'mailserver_pass', + 'mailserver_port', + 'new_admin_email', + 'recently_activated', + 'recovery_keys', + 'rewrite_rules', + 'siteurl', + 'site_icon', + 'site_logo', + 'theme_switched', + ] as $key + ) { + unset( $options[ $key ] ); + } + + foreach ( $options as $key => $option ) { + if ( strpos( $key, '_transient' ) === 0 || strpos( $key, '_site_transient' ) === 0 ) { + unset( $options[ $key ] ); + continue; + } + + if ( $option === '' || $option === [] ) { + unset( $options[ $key ] ); + } + } + + $i = 1; + $j = 1; + foreach ( $options as $key => $option ) { + $options_chunks[ $j ][ $key ] = $option; + ++$i; + if ( $i > 10 ) { + ++$j; + $i = 1; + } + } + + foreach ( $options_chunks as $chunk ) { + $this->blueprint['steps'][] = [ + 'step' => 'setSiteOptions', + 'options' => $chunk, + ]; + } + } +} diff --git a/src/class-endpoint.php b/src/class-endpoint.php new file mode 100644 index 0000000..cfe6c6e --- /dev/null +++ b/src/class-endpoint.php @@ -0,0 +1,100 @@ +key = get_option( 'blueprint_builder_key' ); + } + + /** + * Register hooks. + */ + public function register_hooks() { + add_action( 'rest_api_init', [ $this, 'register_routes' ] ); + } + + /** + * Register the /json-blueprint/ endpoint. + */ + public function register_routes() { + register_rest_route( + 'blueprint-builder/v1', + '/json-' . $this->key . '/', + [ + 'methods' => 'GET', + 'callback' => [ $this, 'get_blueprint_json' ], + '_pretty_json' => true, // This is a custom parameter, see 'pretty_json' in the 'register_rest_route' function in the 'wp-includes/rest-api.php' file. + 'permission_callback' => '__return_true', // Allows public access. + ] + ); + + register_rest_route( + 'blueprint-builder/v1', + '/wxr-' . $this->key . '.xml', + [ + 'methods' => 'GET', + 'callback' => [ $this, 'get_wxr' ], + 'permission_callback' => '__return_true', // Allows public access. + ] + ); + } + + /** + * Callback to output the blueprint.json content. + * + * @return \WP_REST_Response + */ + public function get_blueprint_json() { + $file_path = WP_CONTENT_DIR . '/blueprint-' . $this->key . '.json'; + + if ( ! file_exists( $file_path ) ) { + $builder = new Builder(); + $builder->generate(); + } + + if ( file_exists( $file_path ) ) { + $response = new \WP_REST_Response( + json_decode( file_get_contents( $file_path ) ), + 200, + [ + 'Content-Type' => 'application/json', + 'Access-Control-Allow-Origin' => '*', + ] + ); + return $response; + } + + return new \WP_Error( 'json_blueprint_not_found', 'Blueprint JSON file not found.', [ 'status' => 404 ] ); + } + + public function get_wxr() { + require_once ABSPATH . 'wp-admin/includes/export.php'; + + $args['content'] = 'all'; + header( 'Access-Control-Allow-Origin: *' ); + export_wp( $args ); + header_remove( 'Content-Description' ); + header_remove( 'Content-Disposition' ); + header( 'Content-Type: application/xml', true ); + } +} diff --git a/src/class-plugin.php b/src/class-plugin.php new file mode 100644 index 0000000..a57ba1a --- /dev/null +++ b/src/class-plugin.php @@ -0,0 +1,38 @@ +register_hooks(); + + if ( is_admin() ) { + $blueprint_builder = new Builder(); + $blueprint_builder_admin = new Admin( $blueprint_builder ); + $blueprint_builder_admin->register_hooks(); + } + } +}