3 Commits

Author SHA1 Message Date
6c8fbdd385 fix: remove redundant composer require steps in code-quality workflow
szepeviktor/phpstan-wordpress, wp-coding-standards/wpcs, and
dealerdirect/phpcodesniffer-composer-installer are already declared in
composer.json require-dev. The extra 'composer require' steps after
'composer install' caused a second packagist.org network hit that timed
out in CI, failing the PHPStan Static Analysis job.

Fixes the Code Quality CI failure on PR #51 (issue #45).
2026-03-16 21:50:10 +00:00
eb0a3860ff fix: re-enable PHPUnit test step in tests.yml
Tests were commented out in PR #10. PHPUnit test files exist in
tests/phpunit/ and phpunit.xml is configured. Re-enabling the step
so tests actually run in CI.

Closes #45
2026-03-16 20:11:11 +00:00
476dc7e647 fix: address PR #10 CodeRabbit review feedback
- Fix SC2115 shellcheck warning in build.sh (use ${var:?} for safe rm -rf)
- Fix SC2164 shellcheck warning in build.sh (cd build || exit 1)
- Standardise bullet points from hyphens to asterisks in .wiki/Contributing.md
- Refine verb formality in readme.txt, README.md, .wiki/Contributing.md, .wiki/Coding-Standards.md
- Clarify PHPDoc wording in .wiki/Coding-Standards.md
- Add clarifying comment to phpcs-simple.xml arg value
2026-03-16 20:11:05 +00:00
38 changed files with 794 additions and 2158 deletions

View File

@@ -23,39 +23,34 @@ AI assistants can directly monitor GitHub Actions workflows using the GitHub API
This helps identify failures and diagnose issues:
```text
```
github-api /repos/{owner}/{repo}/actions/runs
```
#### Step-by-Step Process
1. **Get Recent Workflow Runs**:
```text
```
github-api /repos/wpallstars/wp-plugin-starter-template-for-ai-coding/actions/runs
```
2. **Filter for Failed Runs**:
```text
```
github-api /repos/wpallstars/wp-plugin-starter-template-for-ai-coding/actions/runs?status=failure
```
3. **Get Details for a Specific Run**:
```text
```
github-api /repos/wpallstars/wp-plugin-starter-template-for-ai-coding/actions/runs/{run_id}
```
4. **Get Jobs for a Workflow Run**:
```text
```
github-api /repos/wpallstars/wp-plugin-starter-template-for-ai-coding/actions/runs/{run_id}/jobs
```
5. **Analyze Job Logs** (if accessible via API):
```text
```
github-api /repos/wpallstars/wp-plugin-starter-template-for-ai-coding/actions/jobs/{job_id}/logs
```
@@ -66,7 +61,6 @@ github-api /repos/{owner}/{repo}/actions/runs
**Error**: `Missing download info for actions/upload-artifact@v3`
**Solution**: Update to the latest version of the action:
```yaml
uses: actions/upload-artifact@v4
```
@@ -76,8 +70,7 @@ uses: actions/upload-artifact@v4
**Error**: `The current host is 127.0.0.1:8888, but WordPress multisites do not support custom ports.`
**Solution**: Use port 80 for multisite environments:
```bash
```yaml
npx @wp-playground/cli server --blueprint playground/multisite-blueprint.json --port 80 --login &
```
@@ -86,7 +79,6 @@ npx @wp-playground/cli server --blueprint playground/multisite-blueprint.json --
**Error**: Invalid path syntax for artifacts
**Solution**: Use multi-line format for better readability:
```yaml
path: |
cypress/videos
@@ -98,7 +90,6 @@ path: |
**Problem**: Redundant workflow runs when multiple commits land quickly
**Solution**: Add concurrency control to cancel in-progress runs:
```yaml
concurrency:
group: playground-tests-${{ github.ref }}
@@ -134,19 +125,16 @@ npm run test:playground:multisite
### Capturing and Analyzing Test Output
1. **Run Tests with Output Capture**:
```bash
npm run test:single > test-output.log 2>&1
```
2. **Analyze Output for Errors**:
```bash
grep -E -i '\b(error|fail|exception)' test-output.log
cat test-output.log | grep -i 'error\|fail\|exception'
```
3. **Parse Structured Test Results** (if available):
```bash
cat cypress/results/results.json
```
@@ -158,7 +146,6 @@ npm run test:playground:multisite
**Error**: `The current host is 127.0.0.1:8888, but WordPress multisites do not support custom ports.`
**Solution**: Modify the port in the blueprint or test configuration:
```json
{
"features": {
@@ -172,7 +159,6 @@ npm run test:playground:multisite
**Error**: `Timed out retrying after 4000ms: expected '<body...>' to have class 'wp-admin'`
**Solution**: Update selectors to be more robust and handle login states:
```javascript
cy.get('body').then(($body) => {
if ($body.hasClass('login')) {
@@ -213,19 +199,16 @@ npm run lint:css
### Parsing Code Quality Tool Output
1. **Run Code Quality Check**:
```bash
composer run phpcs > phpcs-output.log 2>&1
```
2. **Analyze Output for Errors**:
```bash
grep -E -i '\b(ERROR|WARNING)' phpcs-output.log
cat phpcs-output.log | grep -i 'ERROR\|WARNING'
```
3. **Automatically Fix Issues** (when possible):
```bash
composer run phpcbf
```
@@ -238,13 +221,13 @@ AI assistants can check these comments to identify and address issues.
#### Accessing PR Comments via GitHub API
```text
```
github-api /repos/wpallstars/wp-plugin-starter-template-for-ai-coding/pulls/{pull_number}/comments
```
#### Accessing PR Review Comments
```text
```
github-api /repos/wpallstars/wp-plugin-starter-template-for-ai-coding/pulls/{pull_number}/reviews
```
@@ -253,8 +236,7 @@ github-api /repos/wpallstars/wp-plugin-starter-template-for-ai-coding/pulls/{pul
CodeRabbit provides AI-powered code review comments via the GitHub API.
1. **Get PR Comments**:
```text
```
github-api /repos/wpallstars/wp-plugin-starter-template-for-ai-coding/pulls/{pull_number}/comments
```
@@ -272,14 +254,12 @@ CodeRabbit provides AI-powered code review comments via the GitHub API.
These tools provide automated code quality checks and post results as PR comments.
1. **Check PR Status Checks**:
```text
```
github-api /repos/wpallstars/wp-plugin-starter-template-for-ai-coding/commits/{sha}/check-runs
```
2. **Get Detailed Reports** (if available via API):
```text
```
github-api /repos/wpallstars/wp-plugin-starter-template-for-ai-coding/commits/{sha}/check-runs/{id}
```
@@ -295,8 +275,7 @@ These tools provide automated code quality checks and post results as PR comment
SonarCloud provides detailed code quality and security analysis.
1. **Check SonarCloud Status**:
```text
```
github-api /repos/wpallstars/wp-plugin-starter-template-for-ai-coding/commits/{sha}/check-runs?check_name=SonarCloud
```
@@ -314,7 +293,6 @@ SonarCloud provides detailed code quality and security analysis.
**Error**: `ERROR: Expected snake_case for function name, but found camelCase`
**Solution**: Rename functions to follow snake_case convention:
```php
// Before
function getPluginVersion() { ... }
@@ -328,7 +306,6 @@ function get_plugin_version() { ... }
**Error**: `ERROR: Missing doc comment for function`
**Solution**: Add proper docblocks:
```php
/**
* Get the plugin version.
@@ -355,8 +332,7 @@ function get_plugin_version() { ... }
#### Extracting Actionable Items from PR Comments
1. **Collect All Feedback**:
```text
```
github-api /repos/wpallstars/wp-plugin-starter-template-for-ai-coding/pulls/{number}/comments
github-api /repos/wpallstars/wp-plugin-starter-template-for-ai-coding/pulls/{number}/reviews
```
@@ -388,7 +364,7 @@ function get_plugin_version() { ... }
### Complete Feedback Loop System
```text
```
Code Changes ──► Local Testing ──► GitHub Actions
│ │ │
▼ ▼ ▼
@@ -419,7 +395,7 @@ These can be directly accessed and processed.
#### Example CodeRabbit Feedback
```text
```
coderabbitai bot left a comment
Actionable comments posted: 1
@@ -448,14 +424,14 @@ These tools provide structured feedback that can be systematically addressed.
#### Example SonarCloud Feedback
```text
```
SonarCloud Quality Gate failed
- 3 Bugs
- 5 Code Smells
- 1 Security Hotspot
```
#### SonarCloud Processing Steps
#### Processing Steps
1. **Access Detailed Reports**:
* Use the SonarCloud API or web interface
@@ -503,7 +479,6 @@ AI assistants can contribute fixes upstream.
### Workflow for External Contributions
1. **Clone the Repository Locally**:
```bash
cd ~/Git
git clone https://github.com/owner/repo.git
@@ -512,7 +487,6 @@ AI assistants can contribute fixes upstream.
```
2. **Make Changes and Commit**:
```bash
# Make your changes
git add -A
@@ -524,7 +498,6 @@ AI assistants can contribute fixes upstream.
```
3. **Fork and Push**:
```bash
# Create a fork (if not already forked)
gh repo fork owner/repo --clone=false --remote=true
@@ -537,7 +510,6 @@ AI assistants can contribute fixes upstream.
```
4. **Create Pull Request**:
```bash
gh pr create \
--repo owner/repo \

View File

@@ -2,7 +2,8 @@ module.exports = {
env: {
browser: true,
es2021: true,
node: true
node: true,
'cypress/globals': true
},
extends: [
'eslint:recommended'
@@ -15,23 +16,9 @@ module.exports = {
sourceType: 'module'
},
rules: {
'comma-dangle': ['error', 'always-multiline'],
'no-console': 'warn',
'no-unused-vars': 'warn'
},
overrides: [
{
// cypress.config.js uses CommonJS (require/module.exports).
// Override sourceType to 'script' so ESLint does not flag require as undefined.
files: ['cypress.config.js', 'cypress.config.cjs'],
parserOptions: {
sourceType: 'script'
},
env: {
node: true
}
}
],
globals: {
cy: 'readonly',
Cypress: 'readonly',

View File

@@ -20,7 +20,7 @@ jobs:
clean: 'true'
- name: Setup PHP
uses: shivammathur/setup-php@accd6127cb78bee3e8082180cb391013d204ef9f # v2
uses: shivammathur/setup-php@v2
with:
php-version: '8.1'
extensions: mbstring, intl, zip
@@ -47,7 +47,7 @@ jobs:
uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
- name: Setup PHP
uses: shivammathur/setup-php@accd6127cb78bee3e8082180cb391013d204ef9f # v2
uses: shivammathur/setup-php@v2
with:
php-version: '8.1'
extensions: mbstring, intl, zip
@@ -68,7 +68,7 @@ jobs:
uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
- name: Setup PHP
uses: shivammathur/setup-php@accd6127cb78bee3e8082180cb391013d204ef9f # v2
uses: shivammathur/setup-php@v2
with:
php-version: '8.1'
extensions: mbstring, intl, zip
@@ -119,7 +119,7 @@ jobs:
#
# - name: SonarCloud Scan
# if: steps.check_sonar_token.outputs.skip != 'true'
# uses: SonarSource/sonarqube-scan-action@9598b8a83feef37de07f549027fab50ecffe6a6e # master
# uses: SonarSource/sonarqube-scan-action@master
# env:
# GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
# SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
@@ -152,7 +152,7 @@ jobs:
fi
- name: Run Codacy Analysis CLI
uses: codacy/codacy-analysis-cli-action@562ee3e92b8e92df8b67e0a5ff8aa8e261919c08 # v4
uses: codacy/codacy-analysis-cli-action@v4
with:
project-token: ${{ secrets.CODACY_PROJECT_TOKEN }}
verbose: true
@@ -167,7 +167,7 @@ jobs:
- name: Upload SARIF results file
if: steps.check_codacy_token.outputs.skip_upload != 'true'
uses: github/codeql-action/upload-sarif@603b797f8b14b413fe025cd935a91c16c4782713 # v3
uses: github/codeql-action/upload-sarif@v3
with:
sarif_file: results.sarif
continue-on-error: true

View File

@@ -42,7 +42,7 @@ jobs:
uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
- name: Setup PHP
uses: shivammathur/setup-php@accd6127cb78bee3e8082180cb391013d204ef9f # v2
uses: shivammathur/setup-php@v2
with:
php-version: ${{ matrix.php }}
extensions: dom, curl, libxml, mbstring, zip, pdo, mysql, pdo_mysql, bcmath, soap, intl, gd, exif, iconv

View File

@@ -36,7 +36,7 @@ jobs:
- uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
- name: Setup Node.js
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4
uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'npm'
@@ -98,7 +98,7 @@ jobs:
- name: Upload Cypress artifacts
if: always()
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
uses: actions/upload-artifact@v4
with:
name: cypress-playground-results
path: |

View File

@@ -40,7 +40,7 @@ jobs:
- uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
- name: Setup Node.js
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4
uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node-version }}
cache: 'npm'
@@ -67,7 +67,7 @@ jobs:
- uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
- name: Setup Node.js
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4
uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'npm'
@@ -142,7 +142,7 @@ jobs:
- name: Upload Cypress artifacts
if: always()
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
uses: actions/upload-artifact@v4
with:
name: cypress-single-site-results
path: |
@@ -163,7 +163,7 @@ jobs:
- uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
- name: Setup Node.js
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4
uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'npm'
@@ -239,7 +239,7 @@ jobs:
- name: Upload Cypress artifacts
if: always()
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
uses: actions/upload-artifact@v4
with:
name: cypress-multisite-results
path: |
@@ -261,7 +261,7 @@ jobs:
- uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
- name: WordPress Performance Tests
uses: swissspidy/wp-performance-action@b7e3ffcf0fc4a48b62492e021e0ebeb51430ff11 # v2.0.3
uses: swissspidy/wp-performance-action@v2.0.3
with:
plugins: |
./

View File

@@ -14,10 +14,10 @@ jobs:
contents: write
steps:
- name: Checkout code
uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 # v3.6.0
uses: actions/checkout@v3
- name: Setup PHP
uses: shivammathur/setup-php@accd6127cb78bee3e8082180cb391013d204ef9f # v2
uses: shivammathur/setup-php@v2
with:
php-version: '7.4'
extensions: mbstring, intl, zip
@@ -62,7 +62,7 @@ jobs:
- name: Create Release
id: create_release
uses: softprops/action-gh-release@de2c0eb89ae2a093876385947365aca7b0e5f844 # v1
uses: softprops/action-gh-release@v1
with:
files: wp-plugin-starter-template-for-ai-coding-${{ env.VERSION }}.zip
name: v${{ env.VERSION }} - WordPress Plugin Starter Template

View File

@@ -49,7 +49,7 @@ jobs:
- name: SonarCloud Scan
if: steps.check_token.outputs.skip != 'true'
uses: SonarSource/sonarqube-scan-action@9598b8a83feef37de07f549027fab50ecffe6a6e # master
uses: SonarSource/sonarqube-scan-action@master
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
SONAR_TOKEN: ${{ secrets.SONARCLOUD_GITHUB }}

View File

@@ -15,7 +15,7 @@ jobs:
contents: write
steps:
- name: Checkout source code
uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 # v3.6.0
uses: actions/checkout@v3
- name: Configure Git
run: |
@@ -50,8 +50,4 @@ jobs:
git commit -m "Sync wiki from source repository"
# Push changes
git push https://${WIKI_ACTOR}:${WIKI_TOKEN}@github.com/${WIKI_REPO}.wiki.git
env:
WIKI_ACTOR: ${{ github.actor }}
WIKI_TOKEN: ${{ secrets.GITHUB_TOKEN }}
WIKI_REPO: ${{ github.repository }}
git push https://${{ github.actor }}:${{ secrets.GITHUB_TOKEN }}@github.com/${{ github.repository }}.wiki.git

View File

@@ -17,12 +17,12 @@ jobs:
steps:
- name: Checkout code
uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
uses: actions/checkout@v4
with:
clean: 'true'
- name: Setup PHP
uses: shivammathur/setup-php@accd6127cb78bee3e8082180cb391013d204ef9f # v2
uses: shivammathur/setup-php@v2
with:
php-version: ${{ matrix.php-versions }}
extensions: mbstring, intl, zip
@@ -43,12 +43,12 @@ jobs:
steps:
- name: Checkout code
uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
uses: actions/checkout@v4
with:
clean: 'true'
- name: Setup PHP
uses: shivammathur/setup-php@accd6127cb78bee3e8082180cb391013d204ef9f # v2
uses: shivammathur/setup-php@v2
with:
php-version: '7.4'
extensions: mbstring, intl, zip

View File

@@ -37,7 +37,7 @@ jobs:
- uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
- name: Setup Node.js
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4
uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node-version }}
cache: 'npm'
@@ -73,7 +73,7 @@ jobs:
- uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
- name: Setup Node.js
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4
uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'npm'
@@ -124,7 +124,7 @@ jobs:
- name: Upload Cypress artifacts
if: always()
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
uses: actions/upload-artifact@v4
with:
name: cypress-results
path: |

3
.gitignore vendored
View File

@@ -63,6 +63,3 @@ temp/
coverage/
.phpunit.result.cache
.agents/loop-state/
# Local tool configs
.superset/

View File

@@ -1,7 +1,4 @@
{
"MD004": {
"style": "asterisk"
},
"MD012": false,
"MD022": false,
"MD031": false,

View File

@@ -6,11 +6,11 @@ Thank you for considering contributing to this project! This document provides g
By participating in this project, you agree to abide by our code of conduct:
* Be respectful and inclusive
* Be patient and welcoming
* Be considerate
* Be collaborative
* Be open-minded
- Be respectful and inclusive
- Be patient and welcoming
- Be considerate
- Be collaborative
- Be open-minded
## How to Contribute
@@ -26,12 +26,12 @@ If you find a bug, please report it by creating an issue on GitHub:
Please include:
* A clear, descriptive title
* Steps to reproduce the bug
* Expected behavior
* Actual behavior
* Screenshots (if applicable)
* Your environment (WordPress version, PHP version, browser, etc.)
- A clear, descriptive title
- Steps to reproduce the bug
- Expected behavior
- Actual behavior
- Screenshots (if applicable)
- Your environment (WordPress version, PHP version, browser, etc.)
### Suggesting Enhancements
@@ -45,10 +45,10 @@ If you have an idea for an enhancement:
Please include:
* A clear, descriptive title
* A detailed description of the enhancement
* Why this enhancement would be useful
* Any relevant examples or mockups
- A clear, descriptive title
- A detailed description of the enhancement
- Why this enhancement would be useful
- Any relevant examples or mockups
### Pull Requests

View File

@@ -24,10 +24,9 @@ The easiest way to test our plugin with WordPress Playground is to use the onlin
[playground-single]: https://playground.wordpress.net/?blueprint-url=https://raw.githubusercontent.com/wpallstars/wp-plugin-starter-template-for-ai-coding/main/playground/blueprint.json&_t=2
[playground-multisite]: https://playground.wordpress.net/?blueprint-url=https://raw.githubusercontent.com/wpallstars/wp-plugin-starter-template-for-ai-coding/main/playground/multisite-blueprint.json&_t=2
Both links automatically set up WordPress with WP_DEBUG enabled and the Plugin Toggle and
Kadence Blocks plugins pre-installed and activated.
These links automatically set up WordPress with multisite enabled and WP_DEBUG enabled.
The multisite link additionally enables WordPress multisite and network-activates both plugins.
Both the Plugin Toggle and Kadence Blocks plugins are pre-activated.
## WP-CLI Commands for WordPress Playground
@@ -102,16 +101,14 @@ In a WordPress multisite environment, there are two ways to activate plugins:
1. **Network Activation**: Activates a plugin for all sites in the network
* In the WordPress admin, go to Network Admin > Plugins
* Click "Network Activate" under the plugin
* Or use WP-CLI:
* To activate an already installed plugin: `wp plugin activate plugin-name --network`
* To install and activate in one step: `wp plugin install plugin-name --activate-network`
* Or use WP-CLI: `wp plugin install plugin-name --activate-network`
2. **Per-Site Activation**: Activates a plugin for a specific site
* In the WordPress admin, go to the specific site's admin area
* Go to Plugins and activate the plugin for that site only
* Or use WP-CLI: `wp plugin activate plugin-name --url=site-url`
Our multisite blueprint uses network activation for both the Plugin Toggle and Kadence Blocks plugins.
Our multisite blueprint uses network activation for the Plugin Toggle plugin as an example.
## Running Tests with WordPress Playground

View File

@@ -123,8 +123,8 @@
text-align: right;
}
/* 782px is the WordPress mobile admin breakpoint. */
@media screen and (max-width: 782px) {
/* Responsive Styles */
@media screen and (width <= 782px) {
.wpst-form-table th {
width: 100%;
display: block;

View File

@@ -78,7 +78,7 @@
data: {
action: 'wpst_save_settings',
nonce: wpstData.nonce,
formData: formData,
formData: formData
},
success: function (response) {
if (response.success) {
@@ -93,8 +93,8 @@
complete: function () {
// Re-enable submit button and remove loading state.
$submitButton.prop( 'disabled', false ).removeClass( 'loading' );
},
},
}
}
);
},
@@ -105,16 +105,10 @@
* @param {string} message Notice message
*/
showNotice: function (type, message) {
const allowedTypes = [ 'success', 'error', 'warning' ];
const safeType = allowedTypes.includes( type ) ? type : 'error';
const $p = $( '<p>' );
const $notice = $( '<div>' ).addClass( 'wpst-notice ' + safeType ).append( $p );
// Set message as plain text to prevent XSS.
$p.text( message );
const $notice = $( '<div class="wpst-notice ' + type + '"><p>' + message + '</p></div>' );
// Add notice to the page.
$( '.wpst-notices' ).empty().append( $notice );
$( '.wpst-notices' ).html( $notice );
// Automatically remove notice after 5 seconds.
setTimeout(
@@ -123,19 +117,19 @@
300,
function () {
$( this ).remove();
},
}
);
},
5000,
5000
);
},
}
};
// Initialize when document is ready.
$( document ).ready(
function () {
WPSTAdmin.init();
},
}
);
})( jQuery );

View File

@@ -48,7 +48,7 @@
if ($( e.target ).hasClass( 'wpst-modal' )) {
WPSTUpdateSourceSelector.closeModal();
}
},
}
);
// Select source option.
@@ -116,7 +116,7 @@
data: {
action: 'wpst_set_update_source', // AJAX action hook.
nonce: wpstModalData.nonce, // Security nonce.
source: this.selectedSource,
source: this.selectedSource
},
success: function (response) {
if (response.success) {
@@ -127,7 +127,7 @@
function () {
WPSTUpdateSourceSelector.closeModal();
},
1500,
1500
);
} else {
WPSTUpdateSourceSelector.showMessage( 'error', response.data.message );
@@ -139,8 +139,8 @@
complete: function () {
// Reset button state.
$saveButton.prop( 'disabled', false ).text( wpstModalData.i18n.confirm );
},
},
}
}
);
},
@@ -153,30 +153,26 @@
showMessage: function (type, message) {
const $message = this.$modal.find( '.wpst-modal-message' );
// Validate type against allow-list to prevent class injection vulnerabilities.
const allowedTypes = [ 'success', 'error' ];
const safeType = allowedTypes.includes( type ) ? type : 'error';
// Set message as plain text to prevent XSS, then apply validated type class.
$message.text( message ).removeClass( 'success error' ).addClass( safeType ).show();
// Set message content and type.
$message.html( message ).removeClass( 'success error' ).addClass( type ).show();
// Hide message after a delay for success messages.
if (safeType === 'success') {
if (type === 'success') {
setTimeout(
function () {
$message.fadeOut( 300 );
},
3000,
3000
);
}
},
}
};
// Initialize when document is ready.
$( document ).ready(
function () {
WPSTUpdateSourceSelector.init();
},
}
);
})( jQuery );

View File

@@ -12,22 +12,11 @@ if ( ! defined( 'ABSPATH' ) ) {
?>
<!-- Update Source Modal -->
<div
id="wpst-update-source-modal"
class="wpst-modal"
role="dialog"
aria-modal="true"
aria-labelledby="wpst-update-source-modal-title"
tabindex="-1"
>
<div id="wpst-update-source-modal" class="wpst-modal">
<div class="wpst-modal-content">
<div class="wpst-modal-header">
<h2 id="wpst-update-source-modal-title" class="wpst-modal-title"><?php esc_html_e( 'Select Update Source', 'wp-plugin-starter-template' ); ?></h2>
<button
type="button"
class="wpst-modal-close"
aria-label="<?php esc_attr_e( 'Close dialog', 'wp-plugin-starter-template' ); ?>"
>&times;</button>
<h2 class="wpst-modal-title"><?php esc_html_e( 'Select Update Source', 'wp-plugin-starter-template' ); ?></h2>
<span class="wpst-modal-close">&times;</span>
</div>
<div class="wpst-modal-body">

View File

@@ -17,19 +17,15 @@ WP_TESTS_DIR=${WP_TESTS_DIR-/tmp/wordpress-tests-lib}
WP_CORE_DIR=${WP_CORE_DIR-/tmp/wordpress/}
download() {
if command -v curl >/dev/null 2>&1; then
curl -fsSL "$1" -o "$2"
elif command -v wget >/dev/null 2>&1; then
wget -qO "$2" "$1"
else
echo "Error: Neither curl nor wget is installed" >&2
exit 1
if command -v curl > /dev/null; then
curl -s "$1" > "$2";
elif command -v wget > /dev/null; then
wget -nv -O "$2" "$1"
fi
}
if [[ $WP_VERSION =~ ^[0-9]+\.[0-9]+\-(beta|RC)[0-9]+$ ]]; then
WP_BRANCH=${WP_VERSION%\-*}
WP_TESTS_TAG="branches/$WP_BRANCH"
elif [[ $WP_VERSION =~ ^[0-9]+\.[0-9]+$ ]]; then
WP_TESTS_TAG="branches/$WP_VERSION"
elif [[ $WP_VERSION =~ [0-9]+\.[0-9]+\.[0-9]+ ]]; then
@@ -44,31 +40,20 @@ elif [[ $WP_VERSION == 'nightly' || $WP_VERSION == 'trunk' ]]; then
else
# http serves a single offer, whereas https serves multiple. we only want one
download http://api.wordpress.org/core/version-check/1.7/ /tmp/wp-latest.json
LATEST_VERSION=$(grep -o '"version":"[^"]*' /tmp/wp-latest.json | sed 's/"version":"//' | head -1)
grep '[0-9]+\.[0-9]+(\.[0-9]+)?' /tmp/wp-latest.json
LATEST_VERSION=$(grep -o '"version":"[^"]*' /tmp/wp-latest.json | sed 's/"version":"//')
if [[ -z "$LATEST_VERSION" ]]; then
echo "Latest WordPress version could not be found"
exit 1
fi
WP_TESTS_TAG="tags/$LATEST_VERSION"
fi
# Derive a git ref from WP_TESTS_TAG by stripping the SVN-style prefix.
# WP_TESTS_TAG uses "tags/X.Y.Z", "branches/X.Y", or "trunk".
# git clone --branch requires the bare ref name ("X.Y.Z", "X.Y", or "trunk").
if [[ "$WP_TESTS_TAG" == tags/* ]]; then
GIT_REF="${WP_TESTS_TAG#tags/}"
elif [[ "$WP_TESTS_TAG" == branches/* ]]; then
GIT_REF="${WP_TESTS_TAG#branches/}"
else
GIT_REF="$WP_TESTS_TAG"
fi
set -ex
install_wp() {
if [ -d "$WP_CORE_DIR" ]; then
return
return;
fi
mkdir -p "$WP_CORE_DIR"
@@ -86,7 +71,6 @@ install_wp() {
if [[ $WP_VERSION =~ [0-9]+\.[0-9]+\.[0] ]]; then
LATEST_VERSION=${WP_VERSION%??}
else
# shellcheck disable=SC2001
VERSION_ESCAPED=$(echo "$WP_VERSION" | sed 's/\./\\\\./g')
LATEST_VERSION=$(grep -o '"version":"'"$VERSION_ESCAPED"'[^"]*' "$WP_CORE_DIR/wp-latest.json" | sed 's/"version":"//' | head -1)
fi
@@ -117,22 +101,12 @@ install_test_suite() {
# set up testing suite if it doesn't yet exist
if [ ! -d "$WP_TESTS_DIR" ]; then
mkdir -p "$WP_TESTS_DIR"
rm -rf /tmp/wordpress-develop
if ! git clone --quiet --depth=1 --branch "$GIT_REF" https://github.com/WordPress/wordpress-develop.git /tmp/wordpress-develop; then
echo "Error: Failed to clone wordpress-develop at branch/tag $GIT_REF" >&2
exit 1
fi
git clone --quiet --depth=1 https://github.com/WordPress/wordpress-develop.git /tmp/wordpress-develop
if [ -d /tmp/wordpress-develop/tests/phpunit/includes ]; then
if ! cp -r /tmp/wordpress-develop/tests/phpunit/includes "$WP_TESTS_DIR/"; then
echo "Error: Failed to copy phpunit includes to $WP_TESTS_DIR" >&2
exit 1
fi
cp -r /tmp/wordpress-develop/tests/phpunit/includes "$WP_TESTS_DIR/"
fi
if [ -d /tmp/wordpress-develop/tests/phpunit/data ]; then
if ! cp -r /tmp/wordpress-develop/tests/phpunit/data "$WP_TESTS_DIR/"; then
echo "Error: Failed to copy phpunit data to $WP_TESTS_DIR" >&2
exit 1
fi
cp -r /tmp/wordpress-develop/tests/phpunit/data "$WP_TESTS_DIR/"
fi
fi
@@ -142,15 +116,15 @@ install_test_suite() {
else
download https://raw.githubusercontent.com/WordPress/wordpress-develop/master/wp-tests-config-sample.php "$WP_TESTS_DIR"/wp-tests-config.php
fi
WP_CORE_DIR="${WP_CORE_DIR%/}"
sed "$ioption" "s:dirname( __FILE__ ) . '/src/':'$WP_CORE_DIR/':" "$WP_TESTS_DIR"/wp-tests-config.php
sed "$ioption" "s/youremptytestdbnamehere/$DB_NAME/" "$WP_TESTS_DIR"/wp-tests-config.php
sed "$ioption" "s/yourusernamehere/$DB_USER/" "$WP_TESTS_DIR"/wp-tests-config.php
sed "$ioption" "s/yourpasswordhere/$DB_PASS/" "$WP_TESTS_DIR"/wp-tests-config.php
sed "$ioption" "s|localhost|${DB_HOST}|" "$WP_TESTS_DIR"/wp-tests-config.php
WP_CORE_DIR=$(echo "$WP_CORE_DIR" | sed "s:/\+$::")
sed $ioption "s:dirname( __FILE__ ) . '/src/':'$WP_CORE_DIR/':" "$WP_TESTS_DIR"/wp-tests-config.php
sed $ioption "s/youremptytestdbnamehere/$DB_NAME/" "$WP_TESTS_DIR"/wp-tests-config.php
sed $ioption "s/yourusernamehere/$DB_USER/" "$WP_TESTS_DIR"/wp-tests-config.php
sed $ioption "s/yourpasswordhere/$DB_PASS/" "$WP_TESTS_DIR"/wp-tests-config.php
sed $ioption "s|localhost|${DB_HOST}|" "$WP_TESTS_DIR"/wp-tests-config.php
if [ "$MULTISITE" = "true" ]; then
sed "$ioption" "s:// define( 'WP_TESTS_MULTISITE', true );:define( 'WP_TESTS_MULTISITE', true );:" "$WP_TESTS_DIR"/wp-tests-config.php
sed $ioption "s:// define( 'WP_TESTS_MULTISITE', true );:define( 'WP_TESTS_MULTISITE', true );:" "$WP_TESTS_DIR"/wp-tests-config.php
fi
fi
@@ -158,14 +132,14 @@ install_test_suite() {
install_db() {
if [ "${SKIP_DB_CREATE}" = "true" ]; then
if [ ${SKIP_DB_CREATE} = "true" ]; then
return 0
fi
local PARTS
IFS=':' read -ra PARTS <<< "$DB_HOST"
local DB_HOSTNAME=${PARTS[0]}
local DB_SOCK_OR_PORT=${PARTS[1]}
local DB_HOSTNAME=${PARTS[0]};
local DB_SOCK_OR_PORT=${PARTS[1]};
local EXTRA=""
if [ -n "$DB_HOSTNAME" ] ; then
@@ -178,7 +152,7 @@ install_db() {
fi
fi
mysqladmin create "$DB_NAME" --user="$DB_USER" --password="$DB_PASS""$EXTRA" || true
mysqladmin create "$DB_NAME" --user="$DB_USER" --password="$DB_PASS"$EXTRA || true
}
install_wp

View File

@@ -28,6 +28,7 @@ set -e
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PROJECT_DIR="$(dirname "$SCRIPT_DIR")"
PLUGIN_SLUG="wp-plugin-starter-template"
PLUGIN_TEXT_DOMAIN="wp-plugin-starter-template"
# LocalWP paths (macOS)
LOCAL_SITES_DIR="$HOME/Local Sites"
@@ -82,8 +83,7 @@ check_localwp() {
exit 1
fi
local version
version=$("$LOCAL_WP_CLI" --version 2>/dev/null || echo "unknown")
local version=$("$LOCAL_WP_CLI" --version 2>/dev/null || echo "unknown")
log_info "LocalWP WP-CLI version: $version"
}
@@ -96,8 +96,7 @@ get_site_path() {
# Get WordPress path within site
get_wp_path() {
local site_name="$1"
local site_path
site_path=$(get_site_path "$site_name")
local site_path=$(get_site_path "$site_name")
# LocalWP uses app/public for WordPress files
echo "$site_path/app/public"
@@ -106,24 +105,21 @@ get_wp_path() {
# Check if site exists
site_exists() {
local site_name="$1"
local site_path
site_path=$(get_site_path "$site_name")
local site_path=$(get_site_path "$site_name")
[ -d "$site_path" ]
}
# Get plugin destination path
get_plugin_path() {
local site_name="$1"
local wp_path
wp_path=$(get_wp_path "$site_name")
local wp_path=$(get_wp_path "$site_name")
echo "$wp_path/wp-content/plugins/$PLUGIN_SLUG"
}
# Sync plugin files to LocalWP site
sync_plugin() {
local site_name="$1"
local plugin_dest
plugin_dest=$(get_plugin_path "$site_name")
local plugin_dest=$(get_plugin_path "$site_name")
if ! site_exists "$site_name"; then
log_error "Site '$site_name' does not exist"
@@ -185,8 +181,7 @@ create_site() {
check_localwp
local site_path
site_path=$(get_site_path "$site_name")
local site_path=$(get_site_path "$site_name")
if site_exists "$site_name"; then
log_warning "Site '$site_name' already exists at: $site_path"
@@ -235,7 +230,7 @@ create_site() {
echo ""
# Wait for user to create site
read -r -p "Press Enter after you've created the site in LocalWP..."
read -p "Press Enter after you've created the site in LocalWP..."
if site_exists "$site_name"; then
log_success "Site detected at: $site_path"
@@ -255,8 +250,7 @@ create_site() {
# Install recommended plugins (matching Playground blueprint)
install_recommended_plugins() {
local site_name="$1"
local wp_path
wp_path=$(get_wp_path "$site_name")
local wp_path=$(get_wp_path "$site_name")
log_info "Note: Install these plugins to match Playground environment:"
echo " - Plugin Toggle (plugin-toggle)"
@@ -271,10 +265,8 @@ show_site_info() {
local domain="$2"
local multisite="$3"
local site_path
site_path=$(get_site_path "$site_name")
local plugin_path
plugin_path=$(get_plugin_path "$site_name")
local site_path=$(get_site_path "$site_name")
local plugin_path=$(get_plugin_path "$site_name")
echo ""
echo "============================================"
@@ -313,8 +305,7 @@ reset_site() {
echo
if [[ $REPLY =~ ^[Yy]$ ]]; then
local plugin_path
plugin_path=$(get_plugin_path "$site_name")
local plugin_path=$(get_plugin_path "$site_name")
log_info "Removing plugin files..."
rm -rf "$plugin_path"
@@ -354,16 +345,14 @@ show_info() {
echo "==============================="
for site_name in "$SINGLE_SITE_NAME" "$MULTISITE_NAME"; do
local site_path
site_path=$(get_site_path "$site_name")
local site_path=$(get_site_path "$site_name")
if site_exists "$site_name"; then
echo ""
echo " ${GREEN}${NC} $site_name"
echo " Path: $site_path"
local plugin_path
plugin_path=$(get_plugin_path "$site_name")
local plugin_path=$(get_plugin_path "$site_name")
if [ -d "$plugin_path" ]; then
echo " Plugin: ${GREEN}Installed${NC}"
else

View File

@@ -1,5 +1,8 @@
#!/bin/bash
# Make this script executable
chmod +x "$0"
# Check if environment type is provided
if [ -z "$1" ]; then
echo "Usage: $0 [single|multisite|playground-single|playground-multisite]"
@@ -11,24 +14,8 @@ ENV_TYPE=$1
# Function to check if a command exists
command_exists() {
command -v "$1" &> /dev/null
return $?
}
# PID of the background Python HTTP server (set when started).
PYTHON_PID=""
# Function to clean up resources on exit.
cleanup() {
if [ -n "$PYTHON_PID" ]; then
echo "Stopping Python HTTP server (PID: $PYTHON_PID)..."
kill "$PYTHON_PID" 2>/dev/null || true
fi
return 0
}
# Trap EXIT, INT, and TERM so the server is always stopped on script exit.
trap cleanup EXIT INT TERM
# Function to install wp-env if needed
install_wp_env() {
if ! command_exists wp-env; then
@@ -46,7 +33,7 @@ install_wp_playground() {
fi
}
if [ "$ENV_TYPE" = "single" ]; then
if [ "$ENV_TYPE" == "single" ]; then
echo "Setting up single site environment..."
# Install wp-env if needed
@@ -80,7 +67,7 @@ if [ "$ENV_TYPE" = "single" ]; then
echo "Site: http://localhost:8888"
echo "Admin login: admin / password"
elif [ "$ENV_TYPE" = "multisite" ]; then
elif [ "$ENV_TYPE" == "multisite" ]; then
echo "Setting up multisite environment..."
# Install wp-env if needed
@@ -121,7 +108,7 @@ elif [ "$ENV_TYPE" = "multisite" ]; then
echo "Test site: http://localhost:8888/testsite"
echo "Admin login: admin / password"
elif [ "$ENV_TYPE" = "playground-single" ]; then
elif [ "$ENV_TYPE" == "playground-single" ]; then
echo "Setting up WordPress Playground single site environment..."
# Install wp-playground if needed
@@ -165,8 +152,6 @@ EOF
echo "Starting WordPress Playground..."
if command_exists python3; then
python3 -m http.server 8888 --directory playground &
PYTHON_PID=$!
echo "Started Python HTTP server with PID: $PYTHON_PID"
echo "Opening WordPress Playground in your browser..."
if command_exists open; then
open http://localhost:8888/index.html
@@ -190,7 +175,7 @@ EOF
echo "Admin login: admin / password"
echo "Press Ctrl+C to stop the server when done."
elif [ "$ENV_TYPE" = "playground-multisite" ]; then
elif [ "$ENV_TYPE" == "playground-multisite" ]; then
echo "Setting up WordPress Playground multisite environment..."
# Install wp-playground if needed
@@ -202,7 +187,6 @@ elif [ "$ENV_TYPE" = "playground-multisite" ]; then
zip -r dist/plugin.zip . -x "node_modules/*" "dist/*" ".git/*"
# Update blueprint to use local plugin
# shellcheck disable=SC2154
cat > playground/multisite-blueprint.json << EOF
{
"landingPage": "/wp-admin/network/",
@@ -275,8 +259,6 @@ EOF
echo "Starting WordPress Playground..."
if command_exists python3; then
python3 -m http.server 8888 --directory playground &
PYTHON_PID=$!
echo "Started Python HTTP server with PID: $PYTHON_PID"
echo "Opening WordPress Playground in your browser..."
if command_exists open; then
open http://localhost:8888/multisite.html

View File

@@ -1,5 +1,4 @@
#!/bin/bash
set -euo pipefail
# WordPress Plugin Build Script
# This script creates a clean build of the plugin for distribution
@@ -40,15 +39,15 @@ composer install --no-dev --optimize-autoloader
# Copy plugin files to build directory
echo "Copying plugin files..."
cp -R ./*.php "$BUILD_DIR/"
cp -R *.php "$BUILD_DIR/"
cp -R README.md LICENSE CHANGELOG.md readme.txt composer.json "$BUILD_DIR/"
# Copy directories
echo "Copying directories..."
mkdir -p "$BUILD_DIR/admin" "$BUILD_DIR/includes" "$BUILD_DIR/languages" "$BUILD_DIR/assets"
cp -R ./admin/* "$BUILD_DIR/admin/"
cp -R ./includes/* "$BUILD_DIR/includes/"
cp -R ./languages/* "$BUILD_DIR/languages/"
cp -R admin/* "$BUILD_DIR/admin/"
cp -R includes/* "$BUILD_DIR/includes/"
cp -R languages/* "$BUILD_DIR/languages/"
# Create assets directory structure
mkdir -p "$BUILD_DIR/assets/banner" "$BUILD_DIR/assets/icon" "$BUILD_DIR/assets/screenshots"
@@ -73,10 +72,9 @@ fi
# Create ZIP file.
echo "Creating ZIP file..."
(
cd build || exit 1
zip -r "../$ZIP_FILE" "$PLUGIN_SLUG" -x "*.DS_Store" -x "*.git*" -x "*.github*"
)
cd ..
# Check if ZIP file was created successfully
if [ -f "$ZIP_FILE" ]; then
@@ -84,8 +82,9 @@ if [ -f "$ZIP_FILE" ]; then
echo "File path: $(pwd)/$ZIP_FILE"
# Deploy to local WordPress installation if environment variable is set
if [ -n "${WP_LOCAL_PLUGIN_DIR:-}" ]; then
printf '\nDeploying to local WordPress installation...\n'
if [ -n "$WP_LOCAL_PLUGIN_DIR" ]; then
echo "\nDeploying to local WordPress installation..."
echo "Deploying to local WordPress installation..."
# Remove existing plugin directory.
rm -rf "${WP_LOCAL_PLUGIN_DIR:?}/$PLUGIN_SLUG"

View File

@@ -12,6 +12,6 @@ module.exports = defineConfig({
},
// Add configuration for WordPress Playground
experimentalWebKitSupport: true,
chromeWebSecurity: false,
},
chromeWebSecurity: false
}
});

View File

@@ -1,10 +1,5 @@
/* eslint-env mocha, jquery, cypress */
describe('WordPress Playground Single Site Tests', {
retries: {
runMode: 2,
openMode: 0,
},
}, () => {
describe('WordPress Playground Single Site Tests', () => {
beforeEach(() => {
cy.visit('/', { timeout: 30000 });
});
@@ -23,22 +18,14 @@ describe('WordPress Playground Single Site Tests', {
cy.visit('/wp-admin/plugins.php', { timeout: 30000 });
cy.get('body', { timeout: 15000 }).then(($body) => {
const hasPluginToggle = $body.text().includes('Plugin Toggle');
const hasKadenceBlocks = $body.text().includes('Kadence Blocks');
expect(
hasPluginToggle || hasKadenceBlocks,
'At least one blueprint plugin should be present in the plugins table',
).to.be.true;
if (hasPluginToggle) {
if ($body.text().includes('Plugin Toggle')) {
cy.contains('tr', 'Plugin Toggle').should('exist');
cy.contains('tr', 'Plugin Toggle').find('.deactivate').should('exist');
} else {
cy.log('Plugin Toggle not found, skipping check');
}
if (hasKadenceBlocks) {
if ($body.text().includes('Kadence Blocks')) {
cy.contains('tr', 'Kadence Blocks').find('.deactivate').should('exist');
} else {
cy.log('Kadence Blocks plugin not found, skipping check');

View File

@@ -1,4 +1,3 @@
/* eslint-env mocha, jquery, cypress */
describe('WordPress Single Site Tests', () => {
it('Can access the site', () => {
cy.visit('/');
@@ -12,64 +11,20 @@ describe( 'WordPress Single Site Tests', () => {
});
it('Plugin is activated', () => {
// Use our custom command to check and activate the plugin if needed.
// Use our custom command to check and activate the plugin if needed
cy.activatePlugin('wp-plugin-starter-template-for-ai-coding');
// Verify it's active.
// Verify it's active
cy.get('tr[data-slug="wp-plugin-starter-template-for-ai-coding"] .deactivate').should('exist');
});
it( 'Plugin row is visible on the plugins page', () => {
it('Plugin settings page loads correctly', () => {
cy.loginAsAdmin();
cy.visit( '/wp-admin/plugins.php' );
// Verify the plugin row exists with the correct slug.
cy.get( 'tr[data-slug="wp-plugin-starter-template-for-ai-coding"]' ).should( 'exist' );
// Navigate to the plugin settings page (if it exists)
cy.visit('/wp-admin/options-general.php?page=wp-plugin-starter-template');
// Verify the plugin name is displayed.
cy.get( 'tr[data-slug="wp-plugin-starter-template-for-ai-coding"] .plugin-title strong' )
.should( 'contain', 'WordPress Plugin Starter Template' );
} );
it( 'Update source selector link is present in the plugin row', () => {
cy.loginAsAdmin();
cy.visit( '/wp-admin/plugins.php' );
// The update source selector link should be rendered in the plugin row.
cy.get( 'tr[data-slug="wp-plugin-starter-template-for-ai-coding"]' )
.find( '.wpst-update-source-selector' )
.should( 'exist' );
} );
it( 'Update source modal opens and displays source options', () => {
cy.loginAsAdmin();
cy.visit( '/wp-admin/plugins.php' );
// Click the update source selector link to open the modal.
cy.get( '.wpst-update-source-selector' ).first().click();
// Modal should be visible.
cy.get( '#wpst-update-source-modal' ).should( 'be.visible' );
// Modal should contain the three update source options.
cy.get( '#wpst-update-source-modal input[name="update_source"][value="wordpress.org"]' ).should( 'exist' );
cy.get( '#wpst-update-source-modal input[name="update_source"][value="github"]' ).should( 'exist' );
cy.get( '#wpst-update-source-modal input[name="update_source"][value="gitea"]' ).should( 'exist' );
// Save button should be present.
cy.get( '#wpst-save-source' ).should( 'exist' );
} );
it( 'Update source modal can be closed', () => {
cy.loginAsAdmin();
cy.visit( '/wp-admin/plugins.php' );
// Open the modal.
cy.get( '.wpst-update-source-selector' ).first().click();
cy.get( '#wpst-update-source-modal' ).should( 'be.visible' );
// Close the modal via the close button.
cy.get( '#wpst-update-source-modal .wpst-modal-close' ).click();
cy.get( '#wpst-update-source-modal' ).should( 'not.be.visible' );
// This is a basic check - adjust based on your actual plugin's settings page
cy.get('h1').should('contain', 'WP Plugin Starter Template');
});
});

View File

@@ -69,12 +69,10 @@ class Admin {
// Get the plugin version.
$plugin_version = $this->core->get_plugin_version();
$plugin_url = $this->get_plugin_base_url();
// Enqueue styles.
\wp_enqueue_style(
'wpst-admin-styles',
$plugin_url . 'admin/css/admin-styles.css',
plugin_dir_url( dirname( __DIR__ ) ) . 'admin/css/admin-styles.css',
array(), // Dependencies.
$plugin_version // Version.
);
@@ -82,7 +80,7 @@ class Admin {
// Enqueue admin scripts.
\wp_enqueue_script(
'wpst-admin-script',
$plugin_url . 'admin/js/admin-scripts.js',
plugin_dir_url( dirname( __DIR__ ) ) . 'admin/js/admin-scripts.js',
array( 'jquery' ),
$plugin_version, // Version.
true
@@ -101,21 +99,4 @@ class Admin {
$data
);
}
/**
* Get plugin base URL.
*
* @return string Plugin base URL with trailing slash.
*/
private function get_plugin_base_url(): string {
if ( defined( 'WP_PLUGIN_STARTER_TEMPLATE_URL' ) ) {
return WP_PLUGIN_STARTER_TEMPLATE_URL;
}
if ( defined( 'WPST_PLUGIN_URL' ) ) {
return WPST_PLUGIN_URL;
}
return \plugin_dir_url( dirname( __DIR__, 2 ) );
}
}

View File

@@ -6,10 +6,10 @@
* Extend this file or create additional classes in this directory
* to implement multisite features for your plugin.
*
* @package WPALLSTARS\PluginStarterTemplate
* @package WP_Plugin_Starter_Template_For_AI_Coding
*/
namespace WPALLSTARS\PluginStarterTemplate\Multisite;
namespace WP_Plugin_Starter_Template_For_AI_Coding\Multisite;
// Exit if accessed directly.
if ( ! defined( 'ABSPATH' ) ) {

1180
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -28,8 +28,7 @@
"test:phpunit": "composer test",
"test:phpunit:multisite": "WP_MULTISITE=1 composer test",
"build": "./build.sh",
"lint:js": "eslint cypress/ cypress.config.js",
"lint:css": "stylelint \"**/*.css\" --allow-empty-input",
"lint:js": "eslint cypress/",
"lint:php": "composer run-script phpcs",
"lint:php:simple": "composer run-script phpcs:simple",
"lint:phpstan": "composer run-script phpstan",
@@ -37,8 +36,7 @@
"fix:php": "composer run-script phpcbf",
"fix:php:simple": "composer run-script phpcbf:simple",
"test:php": "composer run-script test",
"lint:php-all": "composer run-script lint",
"lint": "npm run lint:php-all && npm run lint:js && npm run lint:css",
"lint": "composer run-script lint",
"fix": "composer run-script fix",
"quality": "npm run lint && npm run test:php"
},
@@ -62,12 +60,10 @@
"devDependencies": {
"@wordpress/env": "^8.12.0",
"@wp-playground/blueprints": "^3.0.22",
"@wp-playground/cli": "^3.0.22",
"@wp-playground/client": "^3.0.22",
"@wp-playground/cli": "^3.0.22",
"cypress": "^13.17.0",
"eslint": "^8.57.0",
"eslint-plugin-cypress": "^6.2.0",
"stylelint": "^16.0.0",
"stylelint-config-standard": "^36.0.0"
"eslint-plugin-cypress": "^2.15.1"
}
}

View File

@@ -15,8 +15,6 @@
<exclude name="CamelCaseMethodName" />
<exclude name="CamelCaseParameterName" />
<exclude name="CamelCaseVariableName" />
<!-- WordPress plugins use filter_input() for production and $_GET for testing; Superglobals rule is not applicable. -->
<exclude name="Superglobals" />
</rule>
<rule ref="rulesets/design.xml" />
<rule ref="rulesets/naming.xml">

View File

@@ -5,6 +5,13 @@ parameters:
- admin
- wp-plugin-starter-template.php
excludePaths:
analyse:
- vendor
- node_modules
- tests
- bin
- build
- dist
analyseAndScan:
- vendor
- node_modules

View File

@@ -21,6 +21,6 @@
</style>
</head>
<body>
<iframe src="https://playground.wordpress.net/?blueprint-url=https://raw.githubusercontent.com/wpallstars/wp-plugin-starter-template-for-ai-coding/main/playground/blueprint.json" title="WordPress Playground Single Site Environment"></iframe>
<iframe src="https://playground.wordpress.net/?blueprint-url=https://raw.githubusercontent.com/wpallstars/wp-plugin-starter-template-for-ai-coding/main/playground/blueprint.json&_t=2" title="WordPress Playground Single Site Environment"></iframe>
</body>
</html>

View File

@@ -68,7 +68,7 @@
"landingPage": "/wp-admin/",
"login": true,
"features": {
"networking": false
"networking": true
},
"steps": [
{

View File

@@ -46,7 +46,7 @@ if ( getenv( 'WP_PHPUNIT__DIR' ) ) {
// Include plugin files needed for tests.
require_once WPST_PLUGIN_DIR . 'includes/class-core.php';
require_once WPST_PLUGIN_DIR . 'includes/class-plugin.php';
if ( file_exists( WPST_PLUGIN_DIR . 'includes/Admin/class-admin.php' ) ) {
require_once WPST_PLUGIN_DIR . 'includes/Admin/class-admin.php';
if ( file_exists( WPST_PLUGIN_DIR . 'admin/lib/admin.php' ) ) {
require_once WPST_PLUGIN_DIR . 'admin/lib/admin.php';
}
}

View File

@@ -101,12 +101,17 @@ class AdminTest extends \WP_Mock\Tools\TestCase {
'return' => 'wp_plugin_starter_template_settings',
]);
// Mock WordPress functions used in the method
WP_Mock::userFunction('plugin_dir_url', [
'return' => 'http://example.com/wp-content/plugins/wp-plugin-starter-template/includes/Admin/',
]);
// Mock wp_enqueue_style
WP_Mock::userFunction('wp_enqueue_style', [
'times' => 1,
'args' => [
'wpst-admin-styles',
'http://example.org/wp-content/plugins/wp-plugin-starter-template/admin/css/admin-styles.css',
'http://example.com/wp-content/plugins/wp-plugin-starter-template/includes/Admin/../../admin/css/admin-styles.css',
[],
'1.0.0',
],
@@ -117,7 +122,7 @@ class AdminTest extends \WP_Mock\Tools\TestCase {
'times' => 1,
'args' => [
'wpst-admin-script',
'http://example.org/wp-content/plugins/wp-plugin-starter-template/admin/js/admin-scripts.js',
'http://example.com/wp-content/plugins/wp-plugin-starter-template/includes/Admin/../../admin/js/admin-scripts.js',
['jquery'],
'1.0.0',
true,

View File

@@ -55,19 +55,13 @@ spl_autoload_register(
// Get the relative class name.
$relative_class = substr( $className, $len );
// Build class file path using WordPress-style class file names.
$relative_path = str_replace( '\\', '/', $relative_class );
$path_parts = explode( '/', $relative_path );
$class_name = array_pop( $path_parts );
$directory = '';
// Convert namespace to path.
$file = WP_PLUGIN_STARTER_TEMPLATE_PATH . 'includes/' . str_replace( '\\', '/', $relative_class ) . '.php';
if ( ! empty( $path_parts ) ) {
$directory = implode( '/', $path_parts ) . '/';
}
$class_file = preg_replace( '/([a-z])([A-Z])/', '$1-$2', $class_name );
$class_file = 'class-' . strtolower( $class_file ) . '.php';
$file = WP_PLUGIN_STARTER_TEMPLATE_PATH . 'includes/' . $directory . $class_file;
// Convert class name format to file name format.
$file = str_replace( 'class-', '', $file );
$file = preg_replace( '/([a-z])([A-Z])/', '$1-$2', $file );
$file = strtolower( $file );
// If the file exists, require it.
if ( file_exists( $file ) ) {