docs: Comprehensive documentation update with best practices
- Created WORDPRESS-BEST-PRACTICES.md with complete WP standards guide - Created TESTING-GUIDE.md with E2E testing procedures and display session setup - Updated CLAUDE.md with JavaScript simplification and recent fixes - Enhanced main docs README with new documentation links - Added guidance on MCP Playwright usage vs standard Playwright - Documented proper role checking (roles vs capabilities) - Included display session configuration for headless testing - Added production testing checklists and debug procedures Key additions: - How to use jQuery properly in WordPress (no compatibility layers needed) - Display session setup (DISPLAY=:0) for Playwright testing - Security best practices with proper examples - Common pitfalls and solutions - Test user accounts for staging/production 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
parent
b110b49c01
commit
8752905f9e
4 changed files with 950 additions and 6 deletions
46
CLAUDE.md
46
CLAUDE.md
|
|
@ -1,6 +1,6 @@
|
|||
# CLAUDE.md
|
||||
|
||||
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
|
||||
This file provides comprehensive guidance to Claude Code (claude.ai/code) when working with the HVAC Community Events WordPress plugin. Follow these instructions carefully to maintain code quality, security, and WordPress best practices.
|
||||
|
||||
## 🏗️ Architecture Overview
|
||||
|
||||
|
|
@ -54,13 +54,28 @@ wp post meta update [PAGE_ID] _wp_page_template templates/page-*.php
|
|||
scripts/clear-staging-cache.sh
|
||||
```
|
||||
|
||||
### Testing with Display Session
|
||||
```bash
|
||||
# Set display environment for headless testing
|
||||
export DISPLAY=:0
|
||||
export XAUTHORITY=/run/user/1000/.mutter-Xwaylandauth.90WDB3
|
||||
|
||||
# Run Playwright tests with display
|
||||
DISPLAY=:0 node test-trainer-features.js
|
||||
|
||||
# Use MCP Playwright for browser automation
|
||||
# The MCP tools provide better integration than standard Playwright
|
||||
```
|
||||
|
||||
## 📚 Documentation
|
||||
For comprehensive information, see:
|
||||
- **[docs/README.md](docs/README.md)** - Overview and navigation
|
||||
- **[docs/WORDPRESS-BEST-PRACTICES.md](docs/WORDPRESS-BEST-PRACTICES.md)** - WordPress coding standards and patterns
|
||||
- **[docs/CONFIGURATION.md](docs/CONFIGURATION.md)** - System configuration and architecture
|
||||
- **[docs/DEVELOPMENT-GUIDE.md](docs/DEVELOPMENT-GUIDE.md)** - Development standards and workflow
|
||||
- **[docs/TROUBLESHOOTING.md](docs/TROUBLESHOOTING.md)** - Common issues and solutions
|
||||
- **[docs/API-REFERENCE.md](docs/API-REFERENCE.md)** - Complete API documentation
|
||||
- **[docs/TESTING-GUIDE.md](docs/TESTING-GUIDE.md)** - Testing procedures and best practices
|
||||
|
||||
### 🚀 Deployment Guidelines
|
||||
- **ALWAYS** use `scripts/deploy.sh staging` for staging deployments
|
||||
|
|
@ -71,6 +86,20 @@ For comprehensive information, see:
|
|||
|
||||
### 🎯 WordPress Best Practices
|
||||
|
||||
#### JavaScript and jQuery
|
||||
```javascript
|
||||
// ✅ CORRECT - Use WordPress jQuery patterns
|
||||
jQuery(document).ready(function($) {
|
||||
'use strict';
|
||||
// $ is available within this scope
|
||||
$('.element').addClass('active');
|
||||
});
|
||||
|
||||
// ❌ WRONG - Don't create compatibility layers
|
||||
// WordPress handles jQuery in no-conflict mode
|
||||
// Trust the WordPress implementation
|
||||
```
|
||||
|
||||
#### Security First
|
||||
```php
|
||||
// Always escape output
|
||||
|
|
@ -85,10 +114,16 @@ $clean_email = sanitize_email($_POST['email']);
|
|||
// Always verify nonces
|
||||
wp_verify_nonce($_POST['nonce'], 'action_name');
|
||||
|
||||
// Always check capabilities
|
||||
if (!current_user_can('required_capability')) {
|
||||
// Always check capabilities correctly
|
||||
if (!current_user_can('edit_posts')) {
|
||||
wp_die('Insufficient permissions');
|
||||
}
|
||||
|
||||
// Check custom roles properly (roles aren't capabilities!)
|
||||
$user = wp_get_current_user();
|
||||
if (!in_array('hvac_trainer', $user->roles)) {
|
||||
wp_die('Access denied');
|
||||
}
|
||||
```
|
||||
|
||||
#### Code Standards
|
||||
|
|
@ -254,6 +289,5 @@ The following systems are commented out in `/includes/class-hvac-plugin.php` lin
|
|||
- **Registration Form Production Deployment and Verification (2025-08-08)**: Successfully deployed updated registration form to production environment using `scripts/deploy.sh production`. Comprehensive Playwright E2E tests executed against production site (https://upskillhvac.com/trainer/registration/) with 100% pass rate. Test verification confirmed: all 40+ form fields functional with proper Business Type dropdown (11 options), Training Audience multi-select checkboxes (4 options), dynamic Organization Headquarters country/state dropdowns with automatic US/Canada state loading, file upload functionality for profile images and organization logos, complete form submission workflow, user registration with redirect to /registration-pending/. Database verification completed on production server: user account created (ID: 65), all user meta fields stored correctly (business_name, business_email, training_audience serialized array, first_name, last_name, application_details, business_type), organizer post created (ID: 6020 - "Test HVAC Training Company 1754663473108"), venue post created (ID: 6022 - "Test Training Center Dallas 1754663473108"). Registration system fully operational in production with complete end-to-end functionality verified through automated testing and manual database inspection. Test email: test.registration.1754663473108@example.com used for verification.
|
||||
- **Trainer Dashboard Template Refactoring (2025-08-11)**: Fixed critical dashboard and navigation issues. Root cause: hardcoded template override in `class-hvac-community-events.php` lines 804-806 forcing old shortcode-based template. Solution: removed hardcoded override, updated to use refactored `page-trainer-dashboard.php` template with proper WordPress integration. Fixed navigation menu rendering by removing conflicting `HVAC_NAV_RENDERED` constant checks in `class-hvac-menu-system.php` and page templates. Added missing `hvac-menu-system.css` file via git pull. Dashboard now displays correctly with working navigation across all trainer pages. Deployment script updated to automatically assign correct page template during deployment.
|
||||
- **Documentation Page Double Navigation Fix (2025-08-11)**: Resolved duplicate navigation bar issue on documentation page. Root cause: HVAC_Help_System class was rendering its own navigation (lines 223-231) via `[hvac_documentation]` shortcode while page template also rendered navigation. Solution: commented out duplicate navigation in `class-hvac-help-system.php`. Documentation page now uses comprehensive template (`page-trainer-documentation.php`) with table of contents sidebar, WordPress/Gutenberg integration, and single navigation instance. Help content provided via `HVAC_Documentation_Content` class with fallback to shortcode for empty pages.
|
||||
|
||||
[... rest of the existing content remains unchanged ...]
|
||||
- When testing any pages which require hvac trainer role permissions, always include login steps via the /training-login page.
|
||||
- **Custom Event Edit Implementation (2025-08-18)**: Implemented secure custom event editing without JavaScript dependencies. Created HVAC_Custom_Event_Edit class with proper authorization checks using role verification instead of capability checks. Fixed permission bug where `current_user_can('hvac_trainer')` was incorrectly used - custom roles are not capabilities. Solution: use `in_array('hvac_trainer', $user->roles)` for proper role checking. Added professional CSS styling matching registration page design with 1200px container width, card-based layout, and proper z-index layering to prevent navigation overlap. Successfully deployed to production with full functionality verified.
|
||||
- **JavaScript Simplification (2025-08-18)**: Removed 200+ lines of unnecessary jQuery compatibility code following WordPress best practices. Eliminated hvac-jquery-compatibility-fix.js and class-hvac-jquery-compatibility.php. Updated community-login.js to use standard `jQuery(document).ready()` pattern. WordPress handles jQuery in no-conflict mode automatically - complex compatibility layers violate best practices and add unnecessary complexity. Production deployment successful with all functionality working correctly.
|
||||
|
|
@ -4,8 +4,37 @@
|
|||
|
||||
The HVAC Community Events plugin is a comprehensive event management system designed specifically for HVAC trainers. It integrates seamlessly with WordPress and The Events Calendar to provide trainer profiles, certificate generation, venue management, certification tracking, advanced reporting capabilities, comprehensive CSV import functionality with taxonomy integration, and professional trainer profile sharing with QR code generation.
|
||||
|
||||
**Latest Updates (August 2025):**
|
||||
- Custom event editing interface without JavaScript dependencies
|
||||
- Simplified jQuery implementation following WordPress best practices
|
||||
- Enhanced security with proper authorization checks
|
||||
- Professional responsive design matching registration pages
|
||||
- Production-ready with comprehensive testing
|
||||
|
||||
## Documentation Structure
|
||||
|
||||
### 🎯 [WordPress Best Practices](./WORDPRESS-BEST-PRACTICES.md)
|
||||
Comprehensive guide to WordPress development standards:
|
||||
- JavaScript and jQuery patterns
|
||||
- Security implementation
|
||||
- Plugin development standards
|
||||
- Asset management
|
||||
- Database operations
|
||||
- Template development
|
||||
- Performance optimization
|
||||
- Common pitfalls to avoid
|
||||
|
||||
### 🧪 [Testing Guide](./TESTING-GUIDE.md)
|
||||
Complete testing procedures and best practices:
|
||||
- Display session configuration
|
||||
- E2E testing with Playwright
|
||||
- MCP Playwright tools usage
|
||||
- Test user accounts
|
||||
- Manual testing procedures
|
||||
- Production verification
|
||||
- Debugging failed tests
|
||||
- Performance and security testing
|
||||
|
||||
### 📋 [Configuration Guide](./CONFIGURATION.md)
|
||||
Complete reference for plugin configuration including:
|
||||
- System architecture overview
|
||||
|
|
|
|||
414
docs/TESTING-GUIDE.md
Normal file
414
docs/TESTING-GUIDE.md
Normal file
|
|
@ -0,0 +1,414 @@
|
|||
# Testing Guide
|
||||
|
||||
## Overview
|
||||
This guide provides comprehensive testing procedures for the HVAC Community Events plugin, including E2E testing with Playwright, manual testing procedures, and production verification steps.
|
||||
|
||||
## Setting Up the Testing Environment
|
||||
|
||||
### Display Session Configuration
|
||||
When running tests on a headless server or in CI/CD environments, you need to configure the display session:
|
||||
|
||||
```bash
|
||||
# Set display environment variables
|
||||
export DISPLAY=:0
|
||||
export XAUTHORITY=/run/user/1000/.mutter-Xwaylandauth.90WDB3
|
||||
|
||||
# Verify display is working
|
||||
echo $DISPLAY
|
||||
# Should output: :0
|
||||
|
||||
# Run tests with display
|
||||
DISPLAY=:0 node test-trainer-features.js
|
||||
```
|
||||
|
||||
### Why Display Session Matters
|
||||
- Playwright needs a display to render pages for screenshots
|
||||
- Some JavaScript interactions require a visible browser
|
||||
- CSS animations and transitions need a display context
|
||||
- Debugging is easier with visual feedback
|
||||
|
||||
## E2E Testing with Playwright
|
||||
|
||||
### Test Structure
|
||||
```javascript
|
||||
// test-trainer-features.js
|
||||
const { chromium } = require('playwright');
|
||||
|
||||
(async () => {
|
||||
const browser = await chromium.launch({
|
||||
headless: true, // Set to false for debugging
|
||||
args: ['--no-sandbox']
|
||||
});
|
||||
|
||||
const context = await browser.newContext({
|
||||
viewport: { width: 1920, height: 1080 }
|
||||
});
|
||||
|
||||
const page = await context.newPage();
|
||||
|
||||
// Test implementation
|
||||
await page.goto('https://upskill-staging.measurequick.com');
|
||||
|
||||
// Always close browser
|
||||
await browser.close();
|
||||
})();
|
||||
```
|
||||
|
||||
### Using MCP Playwright Tools
|
||||
MCP Playwright provides better integration with Claude Code:
|
||||
|
||||
```javascript
|
||||
// Using MCP tools (preferred in Claude Code)
|
||||
mcp__playwright__browser_navigate({ url: 'https://upskillhvac.com/training-login/' });
|
||||
mcp__playwright__browser_type({ element: 'Username field', ref: 'e40', text: 'joe@measurequick.com' });
|
||||
mcp__playwright__browser_click({ element: 'Log In button', ref: 'e50' });
|
||||
mcp__playwright__browser_snapshot(); // Get page state
|
||||
mcp__playwright__browser_take_screenshot({ filename: 'login-success.png' });
|
||||
```
|
||||
|
||||
### Key Test Files
|
||||
```bash
|
||||
# Core test suites
|
||||
test-all-features.js # Comprehensive test suite
|
||||
test-trainer-features.js # Trainer-specific features
|
||||
test-master-dashboard.js # Master trainer dashboard
|
||||
test-custom-event-edit.js # Event editing functionality
|
||||
test-registration.js # Registration form validation
|
||||
|
||||
# Run specific test
|
||||
DISPLAY=:0 node test-trainer-features.js
|
||||
|
||||
# Run on staging
|
||||
UPSKILL_STAGING_URL="https://upskill-staging.measurequick.com" node test-all-features.js
|
||||
```
|
||||
|
||||
## Test User Accounts
|
||||
|
||||
### Staging Environment
|
||||
```
|
||||
Regular Trainer:
|
||||
- Username: test_trainer
|
||||
- Password: TestTrainer123!
|
||||
|
||||
Master Trainer:
|
||||
- Username: JoeMedosch@gmail.com
|
||||
- Password: JoeTrainer2025@
|
||||
|
||||
Dual Role (Trainer + Master):
|
||||
- Username: joe@measurequick.com
|
||||
- Password: JoeTrainer2025@
|
||||
```
|
||||
|
||||
### Production Environment
|
||||
```
|
||||
Master Trainer:
|
||||
- Username: joe@measurequick.com
|
||||
- Password: JoeTrainer2025@
|
||||
|
||||
Note: Always test with real accounts in production
|
||||
Never create test data in production
|
||||
```
|
||||
|
||||
## Manual Testing Procedures
|
||||
|
||||
### Login Flow Testing
|
||||
1. Navigate to `/training-login/`
|
||||
2. Enter credentials
|
||||
3. Verify redirect to appropriate dashboard
|
||||
4. Check navigation menu displays correctly
|
||||
5. Verify role-based features are accessible
|
||||
|
||||
### Event Management Testing
|
||||
```
|
||||
1. Create Event:
|
||||
- Navigate to /trainer/event/manage/
|
||||
- Fill all required fields
|
||||
- Submit and verify creation
|
||||
- Check event appears in dashboard
|
||||
|
||||
2. Edit Event:
|
||||
- Navigate to /trainer/event/edit/?event_id=XXX
|
||||
- Verify form populates with existing data
|
||||
- Make changes and save
|
||||
- Verify changes persist
|
||||
|
||||
3. Event Permissions:
|
||||
- Test with different user roles
|
||||
- Verify trainers can only edit own events
|
||||
- Check master trainers can view all events
|
||||
```
|
||||
|
||||
### CSS and Layout Testing
|
||||
```
|
||||
1. Responsive Design:
|
||||
- Test at 320px (mobile)
|
||||
- Test at 768px (tablet)
|
||||
- Test at 1920px (desktop)
|
||||
|
||||
2. Browser Compatibility:
|
||||
- Chrome/Edge (Chromium)
|
||||
- Firefox
|
||||
- Safari (critical for production)
|
||||
- Mobile browsers
|
||||
|
||||
3. Theme Integration:
|
||||
- Verify Astra theme compatibility
|
||||
- Check sidebar removal on plugin pages
|
||||
- Verify navigation z-index layering
|
||||
```
|
||||
|
||||
## Automated Test Writing Best Practices
|
||||
|
||||
### Structure Your Tests
|
||||
```javascript
|
||||
describe('Trainer Dashboard', () => {
|
||||
beforeEach(async () => {
|
||||
// Login before each test
|
||||
await loginAsTrainer(page);
|
||||
});
|
||||
|
||||
test('displays user statistics', async () => {
|
||||
await page.goto('/trainer/dashboard/');
|
||||
const stats = await page.locator('.hvac-stats-card');
|
||||
expect(await stats.count()).toBeGreaterThan(0);
|
||||
});
|
||||
|
||||
test('shows events table', async () => {
|
||||
await page.goto('/trainer/dashboard/');
|
||||
const table = await page.locator('.hvac-events-table');
|
||||
expect(await table.isVisible()).toBeTruthy();
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
### Common Test Patterns
|
||||
```javascript
|
||||
// Wait for navigation
|
||||
await page.waitForURL('**/dashboard/**');
|
||||
|
||||
// Wait for element
|
||||
await page.waitForSelector('.hvac-dashboard', { timeout: 10000 });
|
||||
|
||||
// Check for errors in console
|
||||
page.on('console', msg => {
|
||||
if (msg.type() === 'error') {
|
||||
console.error('Console error:', msg.text());
|
||||
}
|
||||
});
|
||||
|
||||
// Take screenshot on failure
|
||||
try {
|
||||
// Test code
|
||||
} catch (error) {
|
||||
await page.screenshot({ path: 'error-screenshot.png' });
|
||||
throw error;
|
||||
}
|
||||
```
|
||||
|
||||
## Production Testing Checklist
|
||||
|
||||
### Pre-deployment Verification
|
||||
```bash
|
||||
✅ Run pre-deployment validation
|
||||
bin/pre-deployment-check.sh
|
||||
|
||||
✅ Test all features on staging
|
||||
DISPLAY=:0 node test-all-features.js
|
||||
|
||||
✅ Verify no JavaScript errors
|
||||
Check browser console for errors
|
||||
|
||||
✅ Validate CSS rendering
|
||||
Test in Chrome, Firefox, Safari
|
||||
|
||||
✅ Check database operations
|
||||
Verify CRUD operations work
|
||||
```
|
||||
|
||||
### Post-deployment Verification
|
||||
```bash
|
||||
✅ Clear all caches
|
||||
wp cache flush
|
||||
wp rewrite flush
|
||||
|
||||
✅ Test critical paths
|
||||
- Login flow
|
||||
- Dashboard access
|
||||
- Event creation/editing
|
||||
- Navigation menus
|
||||
|
||||
✅ Monitor error logs
|
||||
tail -f wp-content/debug.log
|
||||
|
||||
✅ Verify page load times
|
||||
Check performance metrics
|
||||
|
||||
✅ Test with real user accounts
|
||||
Use production credentials
|
||||
```
|
||||
|
||||
## Debugging Failed Tests
|
||||
|
||||
### Common Issues and Solutions
|
||||
|
||||
#### jQuery Errors
|
||||
```javascript
|
||||
// Error: $ is not defined
|
||||
// Solution: Use jQuery(document).ready()
|
||||
jQuery(document).ready(function($) {
|
||||
// $ is now available
|
||||
});
|
||||
```
|
||||
|
||||
#### Permission Errors
|
||||
```php
|
||||
// Error: You do not have permission
|
||||
// Check role properly:
|
||||
$user = wp_get_current_user();
|
||||
if (!in_array('hvac_trainer', $user->roles)) {
|
||||
// No access
|
||||
}
|
||||
```
|
||||
|
||||
#### CSS Not Loading
|
||||
```php
|
||||
// Verify page detection
|
||||
private function is_plugin_page() {
|
||||
// Check multiple conditions
|
||||
return $this->check_page_template() ||
|
||||
$this->check_url_pattern() ||
|
||||
$this->check_page_id();
|
||||
}
|
||||
```
|
||||
|
||||
### Debug Output
|
||||
```javascript
|
||||
// Add debug logging
|
||||
console.log('Current URL:', window.location.href);
|
||||
console.log('User role:', hvac_ajax.user_role);
|
||||
console.log('Page ID:', hvac_ajax.page_id);
|
||||
|
||||
// Check element state
|
||||
const element = document.querySelector('.hvac-dashboard');
|
||||
console.log('Element exists:', !!element);
|
||||
console.log('Element visible:', element?.offsetParent !== null);
|
||||
```
|
||||
|
||||
## Performance Testing
|
||||
|
||||
### Load Time Benchmarks
|
||||
```javascript
|
||||
// Measure page load time
|
||||
const startTime = performance.now();
|
||||
await page.goto('/trainer/dashboard/');
|
||||
const loadTime = performance.now() - startTime;
|
||||
console.log(`Page loaded in ${loadTime}ms`);
|
||||
|
||||
// Acceptable thresholds:
|
||||
// - Homepage: < 2000ms
|
||||
// - Dashboard: < 3000ms
|
||||
// - Event forms: < 2500ms
|
||||
```
|
||||
|
||||
### Database Query Monitoring
|
||||
```php
|
||||
// Enable query monitoring (development only)
|
||||
define('SAVEQUERIES', true);
|
||||
|
||||
// Log slow queries
|
||||
add_filter('log_query_custom_data', function($data, $query) {
|
||||
if ($data['elapsed'] > 0.5) {
|
||||
error_log('Slow query: ' . $query);
|
||||
}
|
||||
return $data;
|
||||
}, 10, 2);
|
||||
```
|
||||
|
||||
## Security Testing
|
||||
|
||||
### Authorization Testing
|
||||
```javascript
|
||||
// Test unauthorized access
|
||||
await page.goto('/trainer/dashboard/');
|
||||
// Should redirect to login if not authenticated
|
||||
|
||||
// Test role-based access
|
||||
await loginAsRegularUser(page);
|
||||
await page.goto('/master-trainer/master-dashboard/');
|
||||
// Should show access denied
|
||||
```
|
||||
|
||||
### Input Validation Testing
|
||||
```javascript
|
||||
// Test XSS prevention
|
||||
await page.fill('#event_title', '<script>alert("XSS")</script>');
|
||||
await page.click('#submit');
|
||||
// Should sanitize input, no alert should appear
|
||||
|
||||
// Test SQL injection prevention
|
||||
await page.fill('#user_email', "admin' OR '1'='1");
|
||||
// Should reject invalid input
|
||||
```
|
||||
|
||||
## Continuous Integration
|
||||
|
||||
### GitHub Actions Workflow
|
||||
```yaml
|
||||
name: Test Plugin
|
||||
on: [push, pull_request]
|
||||
jobs:
|
||||
test:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Setup Node
|
||||
uses: actions/setup-node@v2
|
||||
with:
|
||||
node-version: '16'
|
||||
- name: Install dependencies
|
||||
run: npm install playwright
|
||||
- name: Run tests
|
||||
run: |
|
||||
export DISPLAY=:99
|
||||
Xvfb :99 -screen 0 1920x1080x24 &
|
||||
node test-all-features.js
|
||||
```
|
||||
|
||||
## Test Coverage Goals
|
||||
|
||||
### Minimum Coverage Requirements
|
||||
- **Authentication**: 100% - All login/logout paths
|
||||
- **Authorization**: 100% - Role-based access control
|
||||
- **CRUD Operations**: 90% - Create, Read, Update, Delete
|
||||
- **UI Components**: 80% - Forms, buttons, navigation
|
||||
- **Error Handling**: 85% - Error messages, validation
|
||||
- **Edge Cases**: 70% - Boundary conditions
|
||||
|
||||
### Coverage Reporting
|
||||
```bash
|
||||
# Generate coverage report
|
||||
npm run test:coverage
|
||||
|
||||
# View coverage
|
||||
open coverage/index.html
|
||||
```
|
||||
|
||||
## Best Practices Summary
|
||||
|
||||
1. **Always test on staging first**
|
||||
2. **Use real test accounts, not mocked data**
|
||||
3. **Verify both functionality and UI**
|
||||
4. **Check browser console for errors**
|
||||
5. **Test with different user roles**
|
||||
6. **Take screenshots for debugging**
|
||||
7. **Clean up test data after tests**
|
||||
8. **Monitor performance metrics**
|
||||
9. **Document test failures**
|
||||
10. **Keep tests maintainable and readable**
|
||||
|
||||
## Resources
|
||||
|
||||
- [Playwright Documentation](https://playwright.dev/docs/intro)
|
||||
- [WordPress Testing Best Practices](https://make.wordpress.org/core/handbook/testing/)
|
||||
- [PHPUnit for WordPress](https://make.wordpress.org/core/handbook/testing/automated-testing/phpunit/)
|
||||
- [Browser Testing Guide](https://developer.mozilla.org/en-US/docs/Learn/Tools_and_testing/Cross_browser_testing)
|
||||
467
docs/WORDPRESS-BEST-PRACTICES.md
Normal file
467
docs/WORDPRESS-BEST-PRACTICES.md
Normal file
|
|
@ -0,0 +1,467 @@
|
|||
# WordPress Best Practices Guide
|
||||
|
||||
## Overview
|
||||
This guide documents WordPress best practices as implemented in the HVAC Community Events plugin. Following these practices ensures maintainability, security, and compatibility with WordPress core and other plugins.
|
||||
|
||||
## JavaScript and jQuery
|
||||
|
||||
### ✅ DO: Use WordPress jQuery Patterns
|
||||
```javascript
|
||||
// CORRECT - WordPress standard pattern
|
||||
jQuery(document).ready(function($) {
|
||||
'use strict';
|
||||
|
||||
// $ is now available within this scope
|
||||
$('.my-element').addClass('active');
|
||||
});
|
||||
```
|
||||
|
||||
### ❌ DON'T: Create Complex Compatibility Layers
|
||||
```javascript
|
||||
// WRONG - Unnecessary complexity
|
||||
(function($) {
|
||||
if (typeof $ === 'undefined' && typeof window.jQuery !== 'undefined') {
|
||||
$ = window.jQuery;
|
||||
}
|
||||
// Complex error handling for basic jQuery operations
|
||||
})(jQuery);
|
||||
```
|
||||
|
||||
### Key Principles
|
||||
1. WordPress loads jQuery in no-conflict mode by default
|
||||
2. Use `jQuery` globally, `$` within document ready callbacks
|
||||
3. Don't create compatibility shims - they add complexity without value
|
||||
4. Trust WordPress's jQuery implementation
|
||||
|
||||
## Security Best Practices
|
||||
|
||||
### Always Escape Output
|
||||
```php
|
||||
// ✅ CORRECT
|
||||
echo esc_html($user_input);
|
||||
echo esc_url($link);
|
||||
echo esc_attr($attribute);
|
||||
echo wp_kses_post($content_with_html);
|
||||
|
||||
// ❌ WRONG
|
||||
echo $user_input;
|
||||
```
|
||||
|
||||
### Always Sanitize Input
|
||||
```php
|
||||
// ✅ CORRECT
|
||||
$clean_text = sanitize_text_field($_POST['field']);
|
||||
$clean_email = sanitize_email($_POST['email']);
|
||||
$clean_url = esc_url_raw($_POST['url']);
|
||||
|
||||
// ❌ WRONG
|
||||
$data = $_POST['field'];
|
||||
```
|
||||
|
||||
### Always Verify Nonces
|
||||
```php
|
||||
// ✅ CORRECT
|
||||
if (!isset($_POST['nonce']) || !wp_verify_nonce($_POST['nonce'], 'action_name')) {
|
||||
wp_die('Security check failed');
|
||||
}
|
||||
|
||||
// ❌ WRONG - No nonce verification
|
||||
if (isset($_POST['submit'])) {
|
||||
// Process form
|
||||
}
|
||||
```
|
||||
|
||||
### Always Check Capabilities
|
||||
```php
|
||||
// ✅ CORRECT
|
||||
if (!current_user_can('edit_posts')) {
|
||||
wp_die('Insufficient permissions');
|
||||
}
|
||||
|
||||
// Check custom roles properly
|
||||
$user = wp_get_current_user();
|
||||
if (!in_array('hvac_trainer', $user->roles)) {
|
||||
wp_die('Access denied');
|
||||
}
|
||||
|
||||
// ❌ WRONG
|
||||
if (!current_user_can('hvac_trainer')) { // Custom roles aren't capabilities!
|
||||
wp_die('Access denied');
|
||||
}
|
||||
```
|
||||
|
||||
## Plugin Development Standards
|
||||
|
||||
### File Organization
|
||||
```
|
||||
hvac-community-events/
|
||||
├── hvac-community-events.php # Main plugin file
|
||||
├── includes/ # PHP classes and functions
|
||||
│ ├── class-*.php # One class per file
|
||||
│ └── functions.php # Helper functions
|
||||
├── assets/ # Frontend resources
|
||||
│ ├── css/ # Stylesheets
|
||||
│ ├── js/ # JavaScript files
|
||||
│ └── images/ # Images
|
||||
├── templates/ # Page templates
|
||||
└── docs/ # Documentation
|
||||
```
|
||||
|
||||
### Class Structure
|
||||
```php
|
||||
/**
|
||||
* HVAC Example Class
|
||||
*
|
||||
* @package HVAC_Community_Events
|
||||
* @since 1.0.0
|
||||
*/
|
||||
class HVAC_Example {
|
||||
|
||||
/**
|
||||
* Instance
|
||||
* @var HVAC_Example
|
||||
*/
|
||||
private static $instance = null;
|
||||
|
||||
/**
|
||||
* Get instance (Singleton pattern)
|
||||
*/
|
||||
public static function instance() {
|
||||
if (null === self::$instance) {
|
||||
self::$instance = new self();
|
||||
}
|
||||
return self::$instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
private function __construct() {
|
||||
$this->init_hooks();
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize hooks
|
||||
*/
|
||||
private function init_hooks() {
|
||||
add_action('init', array($this, 'init'));
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Naming Conventions
|
||||
- **PHP Classes**: `HVAC_Class_Name` (prefix + PascalCase)
|
||||
- **Functions**: `hvac_function_name` (prefix + snake_case)
|
||||
- **Hooks**: `hvac_hook_name` (prefix + snake_case)
|
||||
- **Constants**: `HVAC_CONSTANT_NAME` (prefix + UPPER_SNAKE_CASE)
|
||||
- **Database**: `hvac_table_name` (prefix + snake_case)
|
||||
|
||||
## Asset Management
|
||||
|
||||
### Conditional Loading
|
||||
```php
|
||||
/**
|
||||
* Only load assets on plugin pages
|
||||
*/
|
||||
public function enqueue_scripts() {
|
||||
// Check if we're on a plugin page
|
||||
if (!$this->is_plugin_page()) {
|
||||
return;
|
||||
}
|
||||
|
||||
wp_enqueue_script(
|
||||
'hvac-dashboard',
|
||||
HVAC_PLUGIN_URL . 'assets/js/hvac-dashboard.js',
|
||||
array('jquery'),
|
||||
HVAC_PLUGIN_VERSION,
|
||||
true
|
||||
);
|
||||
|
||||
// Localize script for AJAX
|
||||
wp_localize_script('hvac-dashboard', 'hvac_ajax', array(
|
||||
'ajax_url' => admin_url('admin-ajax.php'),
|
||||
'nonce' => wp_create_nonce('hvac_ajax_nonce')
|
||||
));
|
||||
}
|
||||
```
|
||||
|
||||
### CSS Organization
|
||||
```css
|
||||
/* Use consistent naming and organization */
|
||||
.hvac-wrapper {
|
||||
max-width: 1200px;
|
||||
margin: 0 auto;
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
/* Component-based styling */
|
||||
.hvac-card {
|
||||
background: #fff;
|
||||
border-radius: 8px;
|
||||
padding: 20px;
|
||||
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
|
||||
}
|
||||
|
||||
/* State modifiers */
|
||||
.hvac-card--active {
|
||||
border-color: var(--hvac-primary);
|
||||
}
|
||||
```
|
||||
|
||||
## Database Operations
|
||||
|
||||
### Use WordPress Database API
|
||||
```php
|
||||
global $wpdb;
|
||||
|
||||
// ✅ CORRECT - Using prepared statements
|
||||
$results = $wpdb->get_results(
|
||||
$wpdb->prepare(
|
||||
"SELECT * FROM {$wpdb->prefix}posts WHERE post_author = %d",
|
||||
$user_id
|
||||
)
|
||||
);
|
||||
|
||||
// ❌ WRONG - Direct query without preparation
|
||||
$results = $wpdb->get_results(
|
||||
"SELECT * FROM wp_posts WHERE post_author = " . $user_id
|
||||
);
|
||||
```
|
||||
|
||||
### Cache Expensive Queries
|
||||
```php
|
||||
$cache_key = 'hvac_trainer_events_' . $user_id;
|
||||
$events = wp_cache_get($cache_key);
|
||||
|
||||
if (false === $events) {
|
||||
// Expensive query
|
||||
$events = $this->get_trainer_events($user_id);
|
||||
|
||||
// Cache for 1 hour
|
||||
wp_cache_set($cache_key, $events, '', HOUR_IN_SECONDS);
|
||||
}
|
||||
```
|
||||
|
||||
## AJAX Best Practices
|
||||
|
||||
### Proper AJAX Handler
|
||||
```php
|
||||
/**
|
||||
* AJAX handler with security checks
|
||||
*/
|
||||
public function ajax_handler() {
|
||||
// Verify nonce
|
||||
if (!check_ajax_referer('hvac_ajax_nonce', 'nonce', false)) {
|
||||
wp_send_json_error('Security check failed');
|
||||
}
|
||||
|
||||
// Check capabilities
|
||||
if (!current_user_can('edit_posts')) {
|
||||
wp_send_json_error('Insufficient permissions');
|
||||
}
|
||||
|
||||
// Sanitize input
|
||||
$data = sanitize_text_field($_POST['data']);
|
||||
|
||||
// Process request
|
||||
$result = $this->process_data($data);
|
||||
|
||||
// Return JSON response
|
||||
if ($result) {
|
||||
wp_send_json_success($result);
|
||||
} else {
|
||||
wp_send_json_error('Processing failed');
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Template Development
|
||||
|
||||
### WordPress Template Requirements
|
||||
```php
|
||||
<?php
|
||||
/**
|
||||
* Template Name: HVAC Dashboard
|
||||
*
|
||||
* @package HVAC_Community_Events
|
||||
*/
|
||||
|
||||
// Security check
|
||||
if (!defined('ABSPATH')) {
|
||||
exit;
|
||||
}
|
||||
|
||||
// ALWAYS include WordPress header
|
||||
get_header();
|
||||
|
||||
// Template content
|
||||
?>
|
||||
<div class="hvac-wrapper">
|
||||
<!-- Your content here -->
|
||||
</div>
|
||||
<?php
|
||||
|
||||
// ALWAYS include WordPress footer
|
||||
get_footer();
|
||||
```
|
||||
|
||||
### Template Hierarchy
|
||||
```
|
||||
templates/
|
||||
├── page-trainer-dashboard.php # Trainer dashboard
|
||||
├── page-master-dashboard.php # Master trainer dashboard
|
||||
├── page-edit-event-custom.php # Custom event editor
|
||||
└── page-trainer-profile.php # Profile page
|
||||
```
|
||||
|
||||
## Error Handling
|
||||
|
||||
### Graceful Error Handling
|
||||
```php
|
||||
try {
|
||||
$result = $this->risky_operation();
|
||||
} catch (Exception $e) {
|
||||
// Log error for debugging
|
||||
error_log('HVAC Plugin Error: ' . $e->getMessage());
|
||||
|
||||
// Show user-friendly message
|
||||
if (WP_DEBUG) {
|
||||
wp_die('Error: ' . esc_html($e->getMessage()));
|
||||
} else {
|
||||
wp_die('An error occurred. Please try again later.');
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Performance Optimization
|
||||
|
||||
### Optimize Queries
|
||||
```php
|
||||
// ✅ CORRECT - Single query with JOIN
|
||||
$results = $wpdb->get_results("
|
||||
SELECT p.*, pm.meta_value as venue_id
|
||||
FROM {$wpdb->posts} p
|
||||
LEFT JOIN {$wpdb->postmeta} pm ON p.ID = pm.post_id
|
||||
WHERE p.post_type = 'tribe_events'
|
||||
AND pm.meta_key = '_EventVenueID'
|
||||
");
|
||||
|
||||
// ❌ WRONG - Multiple queries in loop
|
||||
foreach ($events as $event) {
|
||||
$venue = get_post_meta($event->ID, '_EventVenueID', true);
|
||||
// Process...
|
||||
}
|
||||
```
|
||||
|
||||
### Lazy Loading
|
||||
```php
|
||||
// Only load heavy resources when needed
|
||||
public function maybe_load_map_data() {
|
||||
if (!is_page('find-trainer')) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Load map data only on find-trainer page
|
||||
$this->load_trainer_locations();
|
||||
}
|
||||
```
|
||||
|
||||
## Testing with Display Session
|
||||
|
||||
### Using Headless Testing
|
||||
```bash
|
||||
# Set display environment for headless testing
|
||||
export DISPLAY=:0
|
||||
export XAUTHORITY=/run/user/1000/.mutter-Xwaylandauth.90WDB3
|
||||
|
||||
# Run Playwright tests
|
||||
node test-trainer-features.js
|
||||
```
|
||||
|
||||
### Testing Best Practices
|
||||
1. Always test on staging before production
|
||||
2. Use actual user accounts for realistic testing
|
||||
3. Verify both UI and functionality
|
||||
4. Check browser console for errors
|
||||
5. Test with different user roles
|
||||
|
||||
## Deployment Process
|
||||
|
||||
### Pre-deployment Checklist
|
||||
```bash
|
||||
# 1. Run validation
|
||||
bin/pre-deployment-check.sh
|
||||
|
||||
# 2. Test on staging
|
||||
scripts/deploy.sh staging
|
||||
|
||||
# 3. Verify staging
|
||||
node test-all-features.js
|
||||
|
||||
# 4. Deploy to production (only when requested)
|
||||
scripts/deploy.sh production
|
||||
```
|
||||
|
||||
### Post-deployment Verification
|
||||
```bash
|
||||
# Clear caches
|
||||
wp cache flush
|
||||
wp rewrite flush
|
||||
|
||||
# Verify pages exist
|
||||
wp post list --post_type=page --name=dashboard
|
||||
|
||||
# Check plugin activation
|
||||
wp plugin list --name=hvac-community-events
|
||||
```
|
||||
|
||||
## Common Pitfalls to Avoid
|
||||
|
||||
### ❌ DON'T
|
||||
- Create standalone fixes outside plugin deployment
|
||||
- Use hardcoded database prefixes
|
||||
- Skip nonce verification
|
||||
- Echo unsanitized output
|
||||
- Assume jQuery methods exist without proper setup
|
||||
- Check capabilities using role names
|
||||
- Load assets globally
|
||||
- Use PHP namespaces (WordPress compatibility)
|
||||
|
||||
### ✅ DO
|
||||
- Deploy complete plugin updates
|
||||
- Use `$wpdb->prefix`
|
||||
- Always verify nonces
|
||||
- Escape all output
|
||||
- Use WordPress jQuery patterns
|
||||
- Check roles properly with `in_array()`
|
||||
- Load assets conditionally
|
||||
- Use prefixed class/function names
|
||||
|
||||
## Debugging Tips
|
||||
|
||||
### Enable Debug Mode (Development Only)
|
||||
```php
|
||||
// wp-config.php
|
||||
define('WP_DEBUG', true);
|
||||
define('WP_DEBUG_LOG', true);
|
||||
define('WP_DEBUG_DISPLAY', false);
|
||||
define('SCRIPT_DEBUG', true);
|
||||
```
|
||||
|
||||
### Debug Logging
|
||||
```php
|
||||
// Log to debug.log
|
||||
error_log('HVAC Debug: ' . print_r($data, true));
|
||||
|
||||
// Conditional logging
|
||||
if (defined('WP_DEBUG') && WP_DEBUG) {
|
||||
error_log('HVAC: Processing user ' . $user_id);
|
||||
}
|
||||
```
|
||||
|
||||
## Resources
|
||||
|
||||
- [WordPress Coding Standards](https://developer.wordpress.org/coding-standards/)
|
||||
- [Plugin Security Best Practices](https://developer.wordpress.org/plugins/security/)
|
||||
- [WordPress Plugin Handbook](https://developer.wordpress.org/plugins/)
|
||||
- [Theme Integration Guide](https://developer.wordpress.org/themes/basics/)
|
||||
Loading…
Reference in a new issue