upskill-event-manager/docs/WORDPRESS-BEST-PRACTICES.md
Ben 8752905f9e 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>
2025-08-18 22:44:43 -03:00

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/)