- 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>
467 lines
No EOL
10 KiB
Markdown
467 lines
No EOL
10 KiB
Markdown
# 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/) |