Compare commits

..

13 Commits

Author SHA1 Message Date
dwindown
7394d2f213 feat: Add comprehensive email flow diagnostics and logging
🔍 Email Flow Diagnostic Tool:
Created test-email-flow.php - comprehensive diagnostic dashboard:
- System status (notification mode, email channel, Action Scheduler)
- Event configuration checker
- Email queue status (wp_options)
- Action Scheduler queue status
- Recent email logs viewer
- Test actions (queue test email, process queue, test order email)
- Troubleshooting guide

📝 Enhanced Debug Logging:
Added detailed logging to EmailManager:
- Hook trigger logging
- Order validation logging
- Event enabled/disabled logging
- Email rendering status
- wp_mail() call result
- Full email flow traceability

🎯 Usage:
1. Visit: /wp-content/plugins/woonoow/test-email-flow.php
2. Check all system status indicators
3. Use test buttons to trigger emails
4. Monitor debug logs for detailed flow

📋 Logs to Watch:
[EmailManager] send_order_processing_email triggered
[EmailManager] order_processing email is disabled/enabled
[EmailManager] Sending order_processing email
[EmailManager] Email rendered successfully
[EmailManager] wp_mail called - Result: success/failed
[WooNooW MailQueue] Queued email ID
[WooNooW MailQueue] Processing email_id
[WooNooW MailQueue] Sent and deleted email ID

🚀 Troubleshooting Steps:
1. Check notification system mode (woonoow vs woocommerce)
2. Check email channel enabled
3. Check event-specific email enabled
4. Check Action Scheduler for failures
5. Check debug logs for flow
6. Test with diagnostic tool
2025-11-18 15:44:08 +07:00
dwindown
f77c9b828e fix: Auto-detect dev server URL for Local by Flywheel
🐛 Problem:
- Dev server hardcoded to http://localhost:5173
- Local by Flywheel uses *.local domains with HTTPS
- Dev mode was blank page (couldn't connect to Vite)

 Solution:
Auto-detect dev server URL based on current host:
- Reads $_SERVER['HTTP_HOST']
- Detects *.local domains (Local by Flywheel)
- Uses HTTPS for *.local domains
- Falls back to HTTP for localhost

📝 Examples:
- woonoow.local → https://woonoow.local:5173
- localhost → http://localhost:5173
- example.test → http://example.test:5173

🎯 Result:
- Dev mode works on Local by Flywheel
- Dev mode works on standard localhost
- No hardcoded URLs
- Still filterable via 'woonoow/admin_dev_server'

💡 Usage:
1. Set WOONOOW_ADMIN_DEV=true in wp-config.php
2. Run: npm run dev
3. Visit wp-admin - Vite HMR works!
2025-11-16 13:48:23 +07:00
dwindown
61825c9ade fix: WordPress media modal styling and WP-Admin icon rendering
🐛 Two Visual Issues Fixed:

1. Standalone Media Modal Styling:
    Missing close button
    Messy layout
    Incomplete WordPress styles

    Added complete WordPress admin styles:
   - wp-admin (base admin styles)
   - common (common admin elements)
   - media-views (modal structure)
   - imgareaselect (image selection)
   - buttons (button styles)
   - dashicons (icon font)

   Result: Media modal now properly styled with close button

2. WP-Admin Icon Rendering:
    Icons showing filled instead of outlined
    WordPress admin CSS overriding Lucide icons

    Added inline CSS fix:
   - Force fill: none
   - Force stroke: currentColor
   - Force stroke-width: 2
   - Force stroke-linecap: round
   - Force stroke-linejoin: round

   Result: Icons now display as outlined (Lucide style)

📝 Root Causes:
1. Standalone needed full WP admin styles for modal
2. WP-Admin has global SVG styles that override icon libraries
3. !important needed to override WordPress specificity

🎯 Result:
- Standalone media modal fully functional and styled
- WP-Admin icons render correctly (outlined)
- Both modes visually consistent
2025-11-16 13:24:43 +07:00
dwindown
7e87e18a43 fix: Correct asset paths for WP-Admin and standalone media styles
🐛 CRITICAL FIX - Asset Path Issue:

1. WP-Admin Assets Not Loading:
    Old path: includes/Admin/../admin-spa/dist/
    New path: /plugin-root/admin-spa/dist/

   Problem: Relative path from includes/Admin/ was wrong
   Solution: Use dirname(__DIR__) to get plugin root, then build absolute path

   Before:
   - CSS exists: no
   - JS exists: no

   After:
   - CSS exists: yes (2.4MB)
   - JS exists: yes (70KB)

2. Standalone Media Library Styling:
    Missing WordPress core styles (buttons, dashicons)
    Added wp_print_styles for buttons and dashicons

   Problem: Media modal had unstyled text/buttons
   Solution: Enqueue all required WordPress media styles

   Styles now loaded:
   - media-views (modal structure)
   - imgareaselect (image selection)
   - buttons (WordPress buttons)
   - dashicons (icons)

📝 Debug logs now show:
[WooNooW Assets] Dist dir: /home/.../woonoow/admin-spa/dist/
[WooNooW Assets] CSS exists: yes
[WooNooW Assets] JS exists: yes

🎯 Result:
- WP-Admin SPA now loads correctly
- Standalone media library properly styled
- Both modes fully functional
2025-11-16 13:17:47 +07:00
dwindown
3b3e3bd0ad fix: Add debug logging and WordPress media templates
🐛 Two Issues Fixed:

1. WP-Admin Assets Not Loading:
   - Added debug logging to Assets.php
   - Logs hook name, dev mode status, file existence, URLs
   - Helps diagnose why assets aren't enqueued
   - Enable WP_DEBUG to see logs

2. Standalone WordPress Media Templates Missing:
   - Added wp_print_media_templates() call
   - Outputs required #tmpl-media-modal templates
   - Fixes 'Template not found' error
   - Media library modal now works correctly

📝 Debug Logs (when WP_DEBUG enabled):
- [WooNooW Assets] Hook: toplevel_page_woonoow
- [WooNooW Assets] Dev mode: false
- [WooNooW Assets] Dist dir: /path/to/admin-spa/dist/
- [WooNooW Assets] CSS exists: yes
- [WooNooW Assets] JS exists: yes
- [WooNooW Assets] CSS URL: http://...
- [WooNooW Assets] JS URL: http://...

🎯 Result:
- Better diagnostics for asset loading issues
- WordPress media library fully functional in standalone mode
- Easier troubleshooting with debug logs
2025-11-16 13:09:22 +07:00
dwindown
c1db133ffa fix: Remove hardcoded dev mode filters from woonoow.php
🐛 CRITICAL FIX - Root Cause Found!
The plugin had hardcoded dev mode filters that forced EVERYONE into dev mode:
- add_filter('woonoow/admin_is_dev', '__return_true');
- add_filter('woonoow/admin_dev_server', fn() => 'https://woonoow.local:5173');

This caused:
- ✗ SPA trying to load from localhost:5173
- ✗ Loading @react-refresh and main.tsx (dev files)
- ✗ Not loading app.js and app.css (production files)

 Solution:
- Removed hardcoded filters from woonoow.php
- Commented them out with instructions
- Added debug logging to is_dev_mode()
- Updated installation checker to detect this issue

📝 For Developers:
If you need dev mode, add to wp-config.php:
define('WOONOOW_ADMIN_DEV', true);

Or use filter in your development plugin:
add_filter('woonoow/admin_is_dev', '__return_true');

🎯 Result:
- Production mode by default (no config needed)
- Dev mode only when explicitly enabled
- Better UX - plugin works out of the box
2025-11-16 13:02:04 +07:00
dwindown
7a967c3399 docs: Add comprehensive troubleshooting guide
📝 Created TROUBLESHOOTING.md:
- Quick diagnosis steps
- Common issues with solutions
- Verification procedures
- Emergency rollback guide
- Help gathering checklist
- Prevention tips

🎯 Covers All Issues:
1. Blank page in WP-Admin
   - Files not found (404)
   - Wrong extraction path
   - Dev mode enabled
2. API 500 errors (namespace)
3. WordPress media not loading
4. Changes not reflecting (caches)
5. File permissions

 Step-by-Step Solutions:
- Clear instructions
- Copy-paste commands
- Expected outputs
- Verification steps

🚀 Quick Reference:
- Installation checker usage
- Cache clearing commands
- File structure verification
- API/asset testing
- Error log locations
2025-11-16 12:47:08 +07:00
dwindown
28593b8a1b feat: Add installation checker script
 Created check-installation.php:
- Comprehensive installation diagnostics
- File structure verification
- PHP configuration check
- WordPress detection
- Constants check (dev mode detection)
- Namespace verification
- Asset URL accessibility test
- Quick actions (clear OPcache, phpinfo)
- Actionable recommendations

🎯 Usage:
1. Upload to: wp-content/plugins/woonoow/check-installation.php
2. Visit: https://yoursite.com/wp-content/plugins/woonoow/check-installation.php
3. Review all checks
4. Follow recommendations

📋 Checks:
- ✓ Required files exist
- ✓ File sizes correct
- ✓ PHP configuration
- ✓ WordPress loaded
- ✓ Plugin active
- ✓ Dev mode disabled
- ✓ Correct namespaces
- ✓ Assets accessible

🚀 Helps diagnose:
- Missing dist files
- Wrong extraction path
- Dev mode issues
- Namespace problems
- File permission issues
- URL accessibility
2025-11-16 12:04:48 +07:00
dwindown
547cb6c4c5 docs: Add comprehensive deployment guide
📝 Created DEPLOYMENT_GUIDE.md:
- Step-by-step server deployment process
- Cache clearing procedures (OPcache, WordPress, Browser)
- File verification commands
- Endpoint testing procedures
- Common issues & solutions
- Verification checklist
- Rollback procedure
- Production checklist

🎯 Covers All Common Issues:
1. SPA not loading (dev mode detection)
2. API 500 errors (namespace case)
3. WordPress media not loading (standalone)
4. Changes not reflecting (cache layers)

 Ready for Server Deployment!
2025-11-16 11:04:20 +07:00
dwindown
b61d74fb8e fix: Add WordPress media library support in standalone mode
🐛 Problem:
- WordPress Media library not loaded in standalone mode
- Error: 'wp.media is not available'
- Image upload functionality broken

 Solution:
- Added wp_enqueue_media() in render_standalone_admin()
- Added wpApiSettings global for REST API compatibility
- Print media-editor and media-audiovideo scripts
- Print media-views and imgareaselect styles

📝 Changes:
- StandaloneAdmin.php:
  - Enqueue media library
  - Output media styles in <head>
  - Output media scripts before app.js
  - Add wpApiSettings global

🎯 Result:
- WordPress media library now available in standalone mode
- Image upload works correctly
- 'Choose from Media Library' button functional
2025-11-16 10:42:40 +07:00
dwindown
e1768a075a fix: Correct namespace case in Routes.php (API → Api)
🐛 Problem:
- 500 errors on all API endpoints
- Class "WooNooWAPIPaymentsController" not found
- Namespace inconsistency: API vs Api

 Solution:
- Fixed use statements in Routes.php
- Changed WooNooW\API\ to WooNooW\Api\
- Affects: PaymentsController, StoreController, DeveloperController, SystemController

📝 PSR-4 Autoloading:
- Namespace must match directory structure exactly
- Directory: includes/Api/ (lowercase 'i')
- Namespace: WooNooW\Api\ (lowercase 'i')

🎯 Result:
- All API endpoints now work correctly
- No more class not found errors
2025-11-16 10:29:36 +07:00
dwindown
57ce0a4e50 fix: Force production mode for admin SPA assets
🐛 Problem:
- SPA not loading in wp-admin
- Trying to load from dev server (localhost:5173)
- Happening in Local by Flywheel (wp_get_environment_type() = 'development')

 Solution:
- Changed is_dev_mode() to ONLY enable dev mode if WOONOOW_ADMIN_DEV constant is explicitly set
- Removed wp_get_environment_type() check
- Now defaults to production mode (loads from admin-spa/dist/)

📝 To Enable Dev Mode:
Add to wp-config.php:
define('WOONOOW_ADMIN_DEV', true);

🎯 Result:
- Production mode by default
- Dev mode only when explicitly enabled
- Works correctly in Local by Flywheel and other local environments
2025-11-16 10:21:37 +07:00
dwindown
056cad44f9 docs: Add plugin zip guide and create distribution package
 Plugin Zip Created:
- File: woonoow.zip
- Size: 1.4MB (compressed)
- Location: /wp-content/plugins/woonoow.zip

📝 Created PLUGIN_ZIP_GUIDE.md:
- Step-by-step zipping process
- What to include/exclude
- Size optimization tips
- Testing checklist
- Automated script template
- Version management guide

 Zip Contents:
- All PHP files (includes/, *.php)
- Admin SPA build (admin-spa/dist/)
- Assets (icons, templates)
- Main plugin file (woonoow.php)
- README.md only (no other docs)

 Excluded from Zip:
- node_modules/
- admin-spa/src/ (source files)
- .git/
- All .md docs (except README.md)
- package.json, composer.json
- Dev config files

🎯 Ready for Distribution!
2025-11-15 22:11:24 +07:00
12 changed files with 1621 additions and 19 deletions

234
DEPLOYMENT_GUIDE.md Normal file
View File

@@ -0,0 +1,234 @@
# Deployment Guide
## Server Deployment Steps
### 1. Pull Latest Code
```bash
cd /home/dewepw/woonoow.dewe.pw/wp-content/plugins/woonoow
git pull origin main
```
### 2. Clear All Caches
#### WordPress Object Cache
```bash
wp cache flush
```
#### OPcache (PHP)
Create a file `clear-opcache.php` in plugin root:
```php
<?php
if (function_exists('opcache_reset')) {
opcache_reset();
echo "OPcache cleared!";
} else {
echo "OPcache not available";
}
```
Then visit: `https://woonoow.dewe.pw/wp-content/plugins/woonoow/clear-opcache.php`
Or via command line:
```bash
php -r "opcache_reset();"
```
#### Browser Cache
- Hard refresh: `Ctrl+Shift+R` (Windows/Linux) or `Cmd+Shift+R` (Mac)
- Or clear browser cache completely
### 3. Verify Files
```bash
# Check if Routes.php has correct namespace
grep "use WooNooW" includes/Api/Routes.php
# Should show:
# use WooNooW\Api\PaymentsController;
# use WooNooW\Api\StoreController;
# use WooNooW\Api\DeveloperController;
# use WooNooW\Api\SystemController;
# Check if Assets.php has correct is_dev_mode()
grep -A 5 "is_dev_mode" includes/Admin/Assets.php
# Should show:
# defined('WOONOOW_ADMIN_DEV') && WOONOOW_ADMIN_DEV === true
```
### 4. Check File Permissions
```bash
# Plugin files should be readable
find . -type f -exec chmod 644 {} \;
find . -type d -exec chmod 755 {} \;
```
### 5. Test Endpoints
#### Test API
```bash
curl -I https://woonoow.dewe.pw/wp-json/woonoow/v1/store/settings
```
Should return `200 OK`, not `500 Internal Server Error`.
#### Test Admin SPA
Visit: `https://woonoow.dewe.pw/wp-admin/admin.php?page=woonoow`
Should load the SPA, not show blank page or errors.
#### Test Standalone
Visit: `https://woonoow.dewe.pw/admin`
Should load standalone admin interface.
---
## Common Issues & Solutions
### Issue 1: SPA Not Loading (Blank Page)
**Symptoms:**
- Blank page in wp-admin
- Console errors about `@react-refresh` or `localhost:5173`
**Cause:**
- Server is in dev mode
- Trying to load from Vite dev server
**Solution:**
```bash
# Check wp-config.php - remove or set to false:
define('WOONOOW_ADMIN_DEV', false);
# Or remove the line completely
```
### Issue 2: API 500 Errors
**Symptoms:**
- All API endpoints return 500
- Error: `Class "WooNooWAPIPaymentsController" not found`
**Cause:**
- Namespace case mismatch
- Old code cached
**Solution:**
```bash
# 1. Pull latest code
git pull origin main
# 2. Clear OPcache
php -r "opcache_reset();"
# 3. Clear WordPress cache
wp cache flush
# 4. Verify namespace fix
grep "use WooNooW\\\\Api" includes/Api/Routes.php
```
### Issue 3: WordPress Media Not Loading (Standalone)
**Symptoms:**
- "WordPress Media library is not loaded" error
- Image upload doesn't work
**Cause:**
- Missing wp.media scripts
**Solution:**
- Already fixed in latest code
- Pull latest: `git pull origin main`
- Clear cache
### Issue 4: Changes Not Reflecting
**Symptoms:**
- Code changes don't appear
- Still seeing old errors
**Cause:**
- Multiple cache layers
**Solution:**
```bash
# 1. Clear PHP OPcache
php -r "opcache_reset();"
# 2. Clear WordPress object cache
wp cache flush
# 3. Clear browser cache
# Hard refresh: Ctrl+Shift+R
# 4. Restart PHP-FPM (if needed)
sudo systemctl restart php8.1-fpm
# or
sudo systemctl restart php-fpm
```
---
## Verification Checklist
After deployment, verify:
- [ ] Git pull completed successfully
- [ ] OPcache cleared
- [ ] WordPress cache cleared
- [ ] Browser cache cleared
- [ ] API endpoints return 200 OK
- [ ] WP-Admin SPA loads correctly
- [ ] Standalone admin loads correctly
- [ ] No console errors
- [ ] Dashboard displays data
- [ ] Settings pages work
- [ ] Image upload works
---
## Rollback Procedure
If deployment causes issues:
```bash
# 1. Check recent commits
git log --oneline -5
# 2. Rollback to previous commit
git reset --hard <commit-hash>
# 3. Clear caches
php -r "opcache_reset();"
wp cache flush
# 4. Verify
curl -I https://woonoow.dewe.pw/wp-json/woonoow/v1/store/settings
```
---
## Production Checklist
Before going live:
- [ ] All features tested
- [ ] No console errors
- [ ] No PHP errors in logs
- [ ] Performance tested
- [ ] Security reviewed
- [ ] Backup created
- [ ] Rollback plan ready
- [ ] Monitoring in place
---
## Support
If issues persist:
1. Check error logs: `/home/dewepw/woonoow.dewe.pw/wp-content/debug.log`
2. Check PHP error logs: `/var/log/php-fpm/error.log`
3. Enable WP_DEBUG temporarily to see detailed errors
4. Contact development team with error details

View File

@@ -0,0 +1,170 @@
# Markdown Syntax & Variables - Analysis & Recommendations
## Current Issues
### 1. Card & Button Syntax
**Current:**
```markdown
[card type="hero"]
Content here
[/card]
[button url="https://example.com" style="solid"]Click me[/button]
```
**Problem:** Not standard Markdown - uses WordPress-style shortcodes
### 2. Variable Naming Mismatch
**Template uses:** `{order_item_table}` (singular)
**Preview defines:** `order_items_table` (plural)
**Result:** Variable not replaced, shows as `{orderitemtable}` (underscores removed by some HTML sanitizer)
---
## All Variables Used in Templates
### Order Variables
- `{order_number}` - Order ID
- `{order_date}` - Order date
- `{order_total}` - Total amount
- `{order_status}` - Current status
- `{order_url}` - Link to view order
- `{order_item_table}` ⚠️ **MISMATCH** - Should be `order_items_table`
### Customer Variables
- `{customer_name}` - Customer full name
- `{customer_email}` - Customer email
- `{customer_username}` - Username (for new accounts)
- `{customer_password}` - Temporary password (for new accounts)
### Store Variables
- `{store_name}` - Store name
- `{store_url}` - Store URL
- `{store_email}` - Store contact email
### Payment Variables
- `{payment_method}` - Payment method used
- `{payment_status}` - Payment status
- `{transaction_id}` - Transaction ID
### Shipping Variables
- `{shipping_address}` - Full shipping address
- `{tracking_number}` - Shipment tracking number
- `{carrier}` - Shipping carrier
### Date Variables
- `{completion_date}` - Order completion date
- `{cancellation_date}` - Order cancellation date
---
## Recommendations
### Option 1: Keep Current Syntax (Easiest)
**Pros:**
- No changes needed
- Users already familiar
- Clear boundaries for cards
**Cons:**
- Not standard Markdown
- Verbose
**Action:** Just fix the variable mismatch
### Option 2: Simplified Shortcode
```markdown
[card:hero]
Content here
[/card]
[button:solid](https://example.com)Click me[/button]
```
**Pros:**
- Shorter, cleaner
- Still clear
**Cons:**
- Still not standard Markdown
- Requires converter changes
### Option 3: HTML + Markdown (Hybrid)
```html
<div class="card card-hero">
**Content** with markdown
</div>
<a href="url" class="button">Click me</a>
```
**Pros:**
- Standard Markdown allows inline HTML
- No custom parsing needed
**Cons:**
- Verbose
- Less user-friendly
### Option 4: Attributes Syntax (Most Markdown-like)
```markdown
> **Order Number:** #{order_number}
> **Order Date:** {order_date}
{: .card .card-hero}
[Click me](https://example.com){: .button .button-solid}
```
**Pros:**
- More Markdown-like
- Compact
**Cons:**
- Complex to parse
- Not widely supported
- Users may not understand
---
## Recommended Action Plan
### Immediate Fixes (Priority 1)
1.**Fix `<br>` rendering** - DONE!
2. ⚠️ **Fix variable mismatch:**
- Change `order_item_table``order_items_table` in DefaultTemplates.php
- OR change `order_items_table``order_item_table` in EditTemplate.tsx preview
3. **Add all missing variables to preview sample data**
### Short-term (Priority 2)
1. **Document all variables** - Create user-facing documentation
2. **Add variable autocomplete** in markdown editor
3. **Add variable validation** - warn if variable doesn't exist
### Long-term (Priority 3)
1. **Consider syntax improvements** - Get user feedback first
2. **Add visual card/button inserter** - UI buttons to insert syntax
3. **Add syntax highlighting** in markdown editor
---
## Variable Replacement Issue
The underscore removal (`{order_item_table}``{orderitemtable}`) suggests HTML sanitization is happening somewhere. Need to check:
1. **Frontend:** DOMPurify or similar sanitizer?
2. **Backend:** WordPress `wp_kses()` or similar?
3. **Email client:** Some email clients strip underscores?
**Solution:** Use consistent naming without underscores OR fix sanitizer to preserve variable syntax.
---
## Next Steps
1. Fix variable naming mismatch
2. Test all variables in preview
3. Document syntax for users
4. Get feedback on syntax preferences
5. Consider improvements based on feedback

26
PHASE_COMPLETE.md Normal file
View File

@@ -0,0 +1,26 @@
# Phase Complete ✅
**Date:** November 15, 2025
## Completed
### 1. Email Queue ✅
- Already implemented via MailQueue + WooEmailOverride
- Prevents 30s timeout
### 2. Documentation ✅
- Reduced 56 → 27 files (52% reduction)
- Created NOTIFICATION_SYSTEM.md (consolidated)
- Deleted 30 obsolete docs
### 3. Git Push ✅
- 3 commits pushed to main
- Remote: git.backoffice.biz.id
### 4. Plugin Zip ✅
- File: woonoow.zip
- Size: 1.4MB
- Location: /wp-content/plugins/woonoow.zip
- Guide: PLUGIN_ZIP_GUIDE.md
## Ready for Distribution! 🚀

222
PLUGIN_ZIP_GUIDE.md Normal file
View File

@@ -0,0 +1,222 @@
# Plugin Zip Guide
## Overview
This guide explains how to properly zip the WooNooW plugin for distribution.
---
## What to Include
### ✅ Include
- All PHP files (`includes/`, `*.php`)
- Admin SPA build (`admin-spa/dist/`)
- Assets (`assets/`)
- Languages (`languages/`)
- README.md
- LICENSE (if exists)
- woonoow.php (main plugin file)
### ❌ Exclude
- `node_modules/`
- `admin-spa/src/` (source files, only include dist)
- `.git/`
- `.gitignore`
- All `.md` documentation files (except README.md)
- `composer.json`, `composer.lock`
- `package.json`, `package-lock.json`
- `.DS_Store`, `Thumbs.db`
- Development/testing files
---
## Step-by-Step Process
### 1. Build Admin SPA
```bash
cd admin-spa
npm run build
```
This creates optimized production files in `admin-spa/dist/`.
### 2. Create Zip (Automated)
```bash
# From plugin root directory
zip -r woonoow.zip . \
-x "*.git*" \
-x "*node_modules*" \
-x "admin-spa/src/*" \
-x "*.md" \
-x "!README.md" \
-x "composer.json" \
-x "composer.lock" \
-x "package.json" \
-x "package-lock.json" \
-x "*.DS_Store" \
-x "Thumbs.db"
```
### 3. Verify Zip Contents
```bash
unzip -l woonoow.zip | head -50
```
---
## Required Structure
```
woonoow/
├── admin-spa/
│ └── dist/ # ✅ Built files only
├── assets/
│ ├── css/
│ ├── js/
│ └── images/
├── includes/
│ ├── Admin/
│ ├── Api/
│ ├── Core/
│ └── ...
├── languages/
├── README.md # ✅ Only this MD file
├── woonoow.php # ✅ Main plugin file
└── LICENSE (optional)
```
---
## Size Optimization
### Before Zipping
1. ✅ Build admin SPA (`npm run build`)
2. ✅ Remove source maps if not needed
3. ✅ Ensure no dev dependencies
### Expected Size
- **Uncompressed:** ~5-10 MB
- **Compressed (zip):** ~2-4 MB
---
## Testing the Zip
### 1. Extract to Test Environment
```bash
unzip woonoow.zip -d /path/to/test/wp-content/plugins/
```
### 2. Verify
- [ ] Plugin activates without errors
- [ ] Admin SPA loads correctly
- [ ] All features work
- [ ] No console errors
- [ ] No missing files
### 3. Check File Permissions
```bash
find woonoow -type f -exec chmod 644 {} \;
find woonoow -type d -exec chmod 755 {} \;
```
---
## Distribution Checklist
- [ ] Admin SPA built (`admin-spa/dist/` exists)
- [ ] No `node_modules/` in zip
- [ ] No `.git/` in zip
- [ ] No source files (`admin-spa/src/`) in zip
- [ ] No documentation (except README.md) in zip
- [ ] Plugin version updated in `woonoow.php`
- [ ] README.md updated with latest info
- [ ] Tested in clean WordPress install
- [ ] All features working
- [ ] No errors in console/logs
---
## Version Management
### Before Creating Zip
1. Update version in `woonoow.php`:
```php
* Version: 1.0.0
```
2. Update version in `admin-spa/package.json`:
```json
"version": "1.0.0"
```
3. Tag in Git:
```bash
git tag -a v1.0.0 -m "Release v1.0.0"
git push origin v1.0.0
```
---
## Automated Zip Script
Save as `create-zip.sh`:
```bash
#!/bin/bash
# Build admin SPA
echo "Building admin SPA..."
cd admin-spa
npm run build
cd ..
# Create zip
echo "Creating zip..."
zip -r woonoow.zip . \
-x "*.git*" \
-x "*node_modules*" \
-x "admin-spa/src/*" \
-x "*.md" \
-x "!README.md" \
-x "composer.json" \
-x "composer.lock" \
-x "package.json" \
-x "package-lock.json" \
-x "*.DS_Store" \
-x "Thumbs.db" \
-x "create-zip.sh"
echo "✅ Zip created: woonoow.zip"
echo "📦 Size: $(du -h woonoow.zip | cut -f1)"
```
Make executable:
```bash
chmod +x create-zip.sh
./create-zip.sh
```
---
## Troubleshooting
### Issue: Zip too large
**Solution:** Ensure `node_modules/` is excluded
### Issue: Admin SPA not loading
**Solution:** Verify `admin-spa/dist/` is included and built
### Issue: Missing files error
**Solution:** Check all required files are included (use `unzip -l`)
### Issue: Permission errors
**Solution:** Set correct permissions (644 for files, 755 for dirs)
---
## Final Notes
- Always test the zip in a clean WordPress environment
- Keep source code in Git, distribute only production-ready zip
- Document any special installation requirements in README.md
- Include changelog in README.md for version tracking

340
TROUBLESHOOTING.md Normal file
View File

@@ -0,0 +1,340 @@
# Troubleshooting Guide
## Quick Diagnosis
### Step 1: Run Installation Checker
Upload `check-installation.php` to your server and visit:
```
https://yoursite.com/wp-content/plugins/woonoow/check-installation.php
```
This will show you exactly what's wrong.
---
## Common Issues
### Issue 1: Blank Page in WP-Admin
**Symptoms:**
- Blank white page when visiting `/wp-admin/admin.php?page=woonoow`
- Or shows "Please Configure Marketing Setup first"
- No SPA loads
**Diagnosis:**
1. Open browser console (F12)
2. Check Network tab
3. Look for `app.js` and `app.css`
**Possible Causes & Solutions:**
#### A. Files Not Found (404)
```
❌ admin-spa/dist/app.js → 404 Not Found
❌ admin-spa/dist/app.css → 404 Not Found
```
**Solution:**
```bash
# The dist files are missing!
# Re-extract the zip file:
cd /path/to/wp-content/plugins
rm -rf woonoow
unzip woonoow.zip
# Verify files exist:
ls -la woonoow/admin-spa/dist/
# Should show: app.js (2.4MB) and app.css (70KB)
```
#### B. Wrong Extraction Path
If you extracted into `plugins/woonoow/woonoow/`, the paths will be wrong.
**Solution:**
```bash
# Correct structure:
wp-content/plugins/woonoow/woonoow.php ✓
wp-content/plugins/woonoow/admin-spa/dist/app.js ✓
# Wrong structure:
wp-content/plugins/woonoow/woonoow/woonoow.php ✗
wp-content/plugins/woonoow/woonoow/admin-spa/dist/app.js ✗
# Fix:
cd /path/to/wp-content/plugins
rm -rf woonoow
unzip woonoow.zip
# This creates: plugins/woonoow/ (correct!)
```
#### C. Dev Mode Enabled
```
❌ Trying to load from localhost:5173
```
**Solution:**
Edit `wp-config.php` and remove or set to false:
```php
// Remove this line:
define('WOONOOW_ADMIN_DEV', true);
// Or set to false:
define('WOONOOW_ADMIN_DEV', false);
```
Then clear caches:
```bash
php -r "opcache_reset();"
wp cache flush
```
---
### Issue 2: API 500 Errors
**Symptoms:**
- All API endpoints return 500
- Console shows: "Internal Server Error"
- Error log: `Class "WooNooWAPIPaymentsController" not found`
**Cause:**
Namespace case mismatch (old code)
**Solution:**
```bash
# Check if you have the fix:
grep "use WooNooW" includes/Api/Routes.php
# Should show (lowercase 'i'):
# use WooNooW\Api\PaymentsController;
# If it shows (uppercase 'I'):
# use WooNooW\API\PaymentsController;
# Then you need to update!
# Update:
git pull origin main
# Or re-upload the latest zip
# Clear caches:
php -r "opcache_reset();"
wp cache flush
```
---
### Issue 3: WordPress Media Not Loading (Standalone)
**Symptoms:**
- Error: "WordPress Media library is not loaded"
- "Choose from Media Library" button doesn't work
- Only in standalone mode (`/admin`)
**Cause:**
Missing wp.media scripts
**Solution:**
Already fixed in latest code. Update:
```bash
git pull origin main
# Or re-upload the latest zip
php -r "opcache_reset();"
```
---
### Issue 4: Changes Not Reflecting
**Symptoms:**
- Uploaded new code but still seeing old errors
- Fixed files but issues persist
**Cause:**
Multiple cache layers
**Solution:**
```bash
# 1. Clear PHP OPcache (MOST IMPORTANT!)
php -r "opcache_reset();"
# Or visit:
# https://yoursite.com/wp-content/plugins/woonoow/check-installation.php?action=clear_opcache
# 2. Clear WordPress object cache
wp cache flush
# 3. Restart PHP-FPM (if above doesn't work)
sudo systemctl restart php8.1-fpm
# or
sudo systemctl restart php-fpm
# 4. Clear browser cache
# Hard refresh: Ctrl+Shift+R (Windows/Linux)
# Hard refresh: Cmd+Shift+R (Mac)
```
---
### Issue 5: File Permissions
**Symptoms:**
- 403 Forbidden errors
- Can't access files
**Solution:**
```bash
cd /path/to/wp-content/plugins/woonoow
# Set correct permissions:
find . -type f -exec chmod 644 {} \;
find . -type d -exec chmod 755 {} \;
# Verify:
ls -la admin-spa/dist/
# Should show: -rw-r--r-- (644) for files
```
---
## Verification Steps
After fixing, verify everything works:
### 1. Check Files
```bash
cd /path/to/wp-content/plugins/woonoow
# These files MUST exist:
ls -lh woonoow.php # Main plugin file
ls -lh includes/Admin/Assets.php # Assets handler
ls -lh includes/Api/Routes.php # API routes
ls -lh admin-spa/dist/app.js # SPA JS (2.4MB)
ls -lh admin-spa/dist/app.css # SPA CSS (70KB)
```
### 2. Test API
```bash
curl -I https://yoursite.com/wp-json/woonoow/v1/store/settings
# Should return:
# HTTP/2 200 OK
```
### 3. Test Assets
```bash
curl -I https://yoursite.com/wp-content/plugins/woonoow/admin-spa/dist/app.js
# Should return:
# HTTP/2 200 OK
# Content-Length: 2489867
```
### 4. Test WP-Admin
Visit: `https://yoursite.com/wp-admin/admin.php?page=woonoow`
**Should see:**
- ✓ WooNooW dashboard loads
- ✓ No console errors
- ✓ Navigation works
**Should NOT see:**
- ✗ Blank page
- ✗ "Please Configure Marketing Setup"
- ✗ Errors about localhost:5173
### 5. Test Standalone
Visit: `https://yoursite.com/admin`
**Should see:**
- ✓ Standalone admin loads
- ✓ Login page (if not logged in)
- ✓ Dashboard (if logged in)
---
## Emergency Rollback
If everything breaks:
```bash
# 1. Deactivate plugin
wp plugin deactivate woonoow
# 2. Remove plugin
rm -rf /path/to/wp-content/plugins/woonoow
# 3. Re-upload fresh zip
unzip woonoow.zip -d /path/to/wp-content/plugins/
# 4. Reactivate
wp plugin activate woonoow
# 5. Clear all caches
php -r "opcache_reset();"
wp cache flush
```
---
## Getting Help
If issues persist, gather this info:
1. **Run installation checker:**
```
https://yoursite.com/wp-content/plugins/woonoow/check-installation.php
```
Take screenshot of results
2. **Check error logs:**
```bash
tail -50 /path/to/wp-content/debug.log
tail -50 /var/log/php-fpm/error.log
```
3. **Browser console:**
- Open DevTools (F12)
- Go to Console tab
- Take screenshot of errors
4. **Network tab:**
- Open DevTools (F12)
- Go to Network tab
- Reload page
- Take screenshot showing failed requests
5. **File structure:**
```bash
ls -la /path/to/wp-content/plugins/woonoow/
ls -la /path/to/wp-content/plugins/woonoow/admin-spa/dist/
```
Send all this info when requesting help.
---
## Prevention
To avoid issues in the future:
1. **Always clear caches after updates:**
```bash
php -r "opcache_reset();"
wp cache flush
```
2. **Verify files after extraction:**
```bash
ls -lh admin-spa/dist/app.js
# Should be ~2.4MB
```
3. **Use installation checker:**
Run it after every deployment
4. **Keep backups:**
Before updating, backup the working version
5. **Test in staging first:**
Don't deploy directly to production

244
check-installation.php Normal file
View File

@@ -0,0 +1,244 @@
<?php
/**
* WooNooW Installation Checker
*
* Upload this file to: wp-content/plugins/woonoow/check-installation.php
* Then visit: https://yoursite.com/wp-content/plugins/woonoow/check-installation.php
*/
header('Content-Type: text/html; charset=utf-8');
?>
<!DOCTYPE html>
<html>
<head>
<title>WooNooW Installation Check</title>
<style>
body { font-family: monospace; padding: 20px; background: #1e1e1e; color: #d4d4d4; }
.pass { color: #4ec9b0; }
.fail { color: #f48771; }
.warn { color: #dcdcaa; }
h1 { color: #569cd6; }
h2 { color: #4ec9b0; margin-top: 30px; }
pre { background: #2d2d2d; padding: 10px; border-radius: 4px; overflow-x: auto; }
.status { display: inline-block; width: 60px; font-weight: bold; }
</style>
</head>
<body>
<h1>WooNooW Installation Checker</h1>
<h2>1. File Structure</h2>
<?php
$plugin_dir = __DIR__;
$required_files = [
'woonoow.php' => 'Main plugin file',
'includes/Admin/Assets.php' => 'Assets handler',
'includes/Api/Routes.php' => 'API routes',
'admin-spa/dist/app.js' => 'SPA JavaScript',
'admin-spa/dist/app.css' => 'SPA Stylesheet',
];
foreach ($required_files as $file => $desc) {
$path = $plugin_dir . '/' . $file;
$exists = file_exists($path);
$size = $exists ? filesize($path) : 0;
$status = $exists ? '<span class="pass">✓ PASS</span>' : '<span class="fail">✗ FAIL</span>';
echo "<div>$status - $desc</div>";
echo "<pre> Path: $file\n";
if ($exists) {
echo " Size: " . number_format($size) . " bytes\n";
echo " Modified: " . date('Y-m-d H:i:s', filemtime($path)) . "</pre>";
} else {
echo " <span class='fail'>FILE NOT FOUND!</span></pre>";
}
}
?>
<h2>2. PHP Configuration</h2>
<?php
$php_checks = [
'PHP Version' => phpversion(),
'OPcache Enabled' => function_exists('opcache_get_status') && opcache_get_status() ? 'Yes' : 'No',
'Memory Limit' => ini_get('memory_limit'),
'Max Execution Time' => ini_get('max_execution_time') . 's',
'Upload Max Filesize' => ini_get('upload_max_filesize'),
];
foreach ($php_checks as $check => $value) {
echo "<div><span class='status'>INFO</span> $check: <strong>$value</strong></div>";
}
?>
<h2>3. WordPress Detection</h2>
<?php
$wp_load = dirname(dirname(dirname(dirname(__DIR__)))) . '/wp-load.php';
if (file_exists($wp_load)) {
require_once $wp_load;
echo "<div><span class='pass'>✓ PASS</span> WordPress loaded</div>";
echo "<pre> WP Version: " . get_bloginfo('version') . "\n";
echo " Site URL: " . home_url() . "\n";
echo " Admin URL: " . admin_url() . "</pre>";
// Check if plugin is active
if (function_exists('is_plugin_active')) {
$is_active = is_plugin_active('woonoow/woonoow.php');
$status = $is_active ? '<span class="pass">✓ ACTIVE</span>' : '<span class="warn">⚠ INACTIVE</span>';
echo "<div>$status - Plugin Status</div>";
}
} else {
echo "<div><span class='fail'>✗ FAIL</span> WordPress not found</div>";
}
?>
<h2>4. Constants Check</h2>
<?php
if (defined('WOONOOW_ADMIN_DEV')) {
$dev_mode = WOONOOW_ADMIN_DEV;
$status = $dev_mode ? '<span class="warn">⚠ WARN</span>' : '<span class="pass">✓ PASS</span>';
echo "<div>$status - WOONOOW_ADMIN_DEV = " . ($dev_mode ? 'true (DEV MODE!)' : 'false') . "</div>";
} else {
echo "<div><span class='pass'>✓ PASS</span> - WOONOOW_ADMIN_DEV not defined (production mode)</div>";
}
if (defined('WP_DEBUG')) {
$debug = WP_DEBUG;
$status = $debug ? '<span class="warn">⚠ WARN</span>' : '<span class="pass">✓ PASS</span>';
echo "<div>$status - WP_DEBUG = " . ($debug ? 'true' : 'false') . "</div>";
}
?>
<h2>5. Namespace Check (Routes.php)</h2>
<?php
$routes_file = $plugin_dir . '/includes/Api/Routes.php';
if (file_exists($routes_file)) {
$content = file_get_contents($routes_file);
$has_correct_namespace = strpos($content, 'use WooNooW\\Api\\PaymentsController') !== false;
$has_wrong_namespace = strpos($content, 'use WooNooW\\API\\PaymentsController') !== false;
if ($has_correct_namespace) {
echo "<div><span class='pass'>✓ PASS</span> - Correct namespace (WooNooW\\Api\\)</div>";
} elseif ($has_wrong_namespace) {
echo "<div><span class='fail'>✗ FAIL</span> - Wrong namespace (WooNooW\\API\\) - needs update!</div>";
} else {
echo "<div><span class='warn'>⚠ WARN</span> - Could not detect namespace</div>";
}
}
?>
<h2>5b. Dev Mode Filter Check (woonoow.php)</h2>
<?php
$main_file = $plugin_dir . '/woonoow.php';
if (file_exists($main_file)) {
$content = file_get_contents($main_file);
$has_dev_filter = strpos($content, "add_filter('woonoow/admin_is_dev', '__return_true')") !== false;
if ($has_dev_filter) {
echo "<div><span class='fail'>✗ FAIL</span> - Hardcoded dev mode filter found in woonoow.php!</div>";
echo "<pre> This forces dev mode ON for everyone.\n";
echo " Remove this line from woonoow.php:\n";
echo " add_filter('woonoow/admin_is_dev', '__return_true');</pre>";
} else {
echo "<div><span class='pass'>✓ PASS</span> - No hardcoded dev mode filters</div>";
}
}
?>
<h2>6. Asset URLs</h2>
<?php
if (function_exists('plugins_url')) {
$base_url = plugins_url('', $plugin_dir . '/woonoow.php');
$css_url = $base_url . '/admin-spa/dist/app.css';
$js_url = $base_url . '/admin-spa/dist/app.js';
echo "<pre>CSS URL: $css_url\n";
echo "JS URL: $js_url</pre>";
// Test if URLs are accessible
echo "<div>Testing URL accessibility...</div>";
$ch = curl_init($css_url);
curl_setopt($ch, CURLOPT_NOBODY, true);
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
curl_exec($ch);
$css_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
$status = ($css_code == 200) ? '<span class="pass">✓ PASS</span>' : '<span class="fail">✗ FAIL</span>';
echo "<div>$status - app.css (HTTP $css_code)</div>";
$ch = curl_init($js_url);
curl_setopt($ch, CURLOPT_NOBODY, true);
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
curl_exec($ch);
$js_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
$status = ($js_code == 200) ? '<span class="pass">✓ PASS</span>' : '<span class="fail">✗ FAIL</span>';
echo "<div>$status - app.js (HTTP $js_code)</div>";
}
?>
<h2>7. Recommendations</h2>
<?php
$issues = [];
if (!file_exists($plugin_dir . '/admin-spa/dist/app.js')) {
$issues[] = "❌ Missing admin-spa/dist/app.js - Re-extract the zip file";
}
if (defined('WOONOOW_ADMIN_DEV') && WOONOOW_ADMIN_DEV) {
$issues[] = "⚠️ Dev mode is enabled - Remove WOONOOW_ADMIN_DEV from wp-config.php";
}
if (isset($has_wrong_namespace) && $has_wrong_namespace) {
$issues[] = "❌ Wrong namespace in Routes.php - Update to latest code";
}
if (isset($css_code) && $css_code != 200) {
$issues[] = "❌ CSS file not accessible - Check file permissions";
}
if (isset($js_code) && $js_code != 200) {
$issues[] = "❌ JS file not accessible - Check file permissions";
}
if (empty($issues)) {
echo "<div><span class='pass'>✓ All checks passed!</span></div>";
echo "<div>If SPA still not loading, try:</div>";
echo "<pre>1. Clear browser cache (Ctrl+Shift+R)\n";
echo "2. Clear OPcache: php -r \"opcache_reset();\"\n";
echo "3. Clear WordPress cache: wp cache flush</pre>";
} else {
echo "<div><span class='fail'>Issues found:</span></div>";
echo "<ul>";
foreach ($issues as $issue) {
echo "<li>$issue</li>";
}
echo "</ul>";
}
?>
<h2>8. Quick Actions</h2>
<div>
<a href="?action=clear_opcache" style="color: #4ec9b0;">Clear OPcache</a> |
<a href="?action=phpinfo" style="color: #4ec9b0;">View PHP Info</a>
</div>
<?php
if (isset($_GET['action'])) {
if ($_GET['action'] == 'clear_opcache' && function_exists('opcache_reset')) {
opcache_reset();
echo "<div style='margin-top: 20px;'><span class='pass'>✓ OPcache cleared!</span></div>";
} elseif ($_GET['action'] == 'phpinfo') {
echo "<div style='margin-top: 20px;'>";
phpinfo();
echo "</div>";
}
}
?>
<div style="margin-top: 40px; padding-top: 20px; border-top: 1px solid #444; color: #888;">
Generated: <?php echo date('Y-m-d H:i:s'); ?> |
Server: <?php echo $_SERVER['SERVER_SOFTWARE'] ?? 'Unknown'; ?>
</div>
</body>
</html>

View File

@@ -12,6 +12,11 @@ class Assets {
} }
public static function enqueue($hook) { public static function enqueue($hook) {
// Debug logging
if (defined('WP_DEBUG') && WP_DEBUG) {
error_log('[WooNooW Assets] Hook: ' . $hook);
}
if ($hook !== 'toplevel_page_woonoow') { if ($hook !== 'toplevel_page_woonoow') {
return; return;
} }
@@ -21,6 +26,12 @@ class Assets {
// Decide dev vs prod // Decide dev vs prod
$is_dev = self::is_dev_mode(); $is_dev = self::is_dev_mode();
// Debug logging
if (defined('WP_DEBUG') && WP_DEBUG) {
error_log('[WooNooW Assets] Dev mode: ' . ($is_dev ? 'true' : 'false'));
}
if ($is_dev) { if ($is_dev) {
self::enqueue_dev(); self::enqueue_dev();
} else { } else {
@@ -126,8 +137,10 @@ class Assets {
* PROD MODE (built assets in admin-spa/dist) * PROD MODE (built assets in admin-spa/dist)
* -------------------------------------- */ * -------------------------------------- */
private static function enqueue_prod(): void { private static function enqueue_prod(): void {
$dist_dir = plugin_dir_path(__FILE__) . '../admin-spa/dist/'; // Get plugin root directory (2 levels up from includes/Admin/)
$base_url = plugins_url('../admin-spa/dist/', __FILE__); $plugin_dir = dirname(dirname(__DIR__));
$dist_dir = $plugin_dir . '/admin-spa/dist/';
$base_url = plugins_url('admin-spa/dist/', $plugin_dir . '/woonoow.php');
$css = 'app.css'; $css = 'app.css';
$js = 'app.js'; $js = 'app.js';
@@ -135,8 +148,30 @@ class Assets {
$ver_css = file_exists($dist_dir . $css) ? (string) filemtime($dist_dir . $css) : self::asset_version(); $ver_css = file_exists($dist_dir . $css) ? (string) filemtime($dist_dir . $css) : self::asset_version();
$ver_js = file_exists($dist_dir . $js) ? (string) filemtime($dist_dir . $js) : self::asset_version(); $ver_js = file_exists($dist_dir . $js) ? (string) filemtime($dist_dir . $js) : self::asset_version();
// Debug logging
if (defined('WP_DEBUG') && WP_DEBUG) {
error_log('[WooNooW Assets] Dist dir: ' . $dist_dir);
error_log('[WooNooW Assets] CSS exists: ' . (file_exists($dist_dir . $css) ? 'yes' : 'no'));
error_log('[WooNooW Assets] JS exists: ' . (file_exists($dist_dir . $js) ? 'yes' : 'no'));
error_log('[WooNooW Assets] CSS URL: ' . $base_url . $css);
error_log('[WooNooW Assets] JS URL: ' . $base_url . $js);
}
if (file_exists($dist_dir . $css)) { if (file_exists($dist_dir . $css)) {
wp_enqueue_style('wnw-admin', $base_url . $css, [], $ver_css); wp_enqueue_style('wnw-admin', $base_url . $css, [], $ver_css);
// Fix icon rendering in WP-Admin (prevent WordPress admin styles from overriding)
$icon_fix_css = '
/* Fix Lucide icons in WP-Admin - force outlined style */
#woonoow-admin-app svg {
fill: none !important;
stroke: currentColor !important;
stroke-width: 2 !important;
stroke-linecap: round !important;
stroke-linejoin: round !important;
}
';
wp_add_inline_style('wnw-admin', $icon_fix_css);
} }
if (file_exists($dist_dir . $js)) { if (file_exists($dist_dir . $js)) {
@@ -225,25 +260,46 @@ class Assets {
} }
/** Determine dev mode: /** Determine dev mode:
* - WP environment 'development' * - ONLY enabled if constant WOONOOW_ADMIN_DEV=true is explicitly set
* - or constant WOONOOW_ADMIN_DEV=true
* - or filter override (woonoow/admin_is_dev) * - or filter override (woonoow/admin_is_dev)
*
* Note: We don't check WP_ENV to avoid accidentally enabling dev mode
* in Local by Flywheel or other local dev environments.
*/ */
private static function is_dev_mode(): bool { private static function is_dev_mode(): bool {
$env_is_dev = function_exists('wp_get_environment_type') && wp_get_environment_type() === 'development'; // Only enable dev mode if explicitly set via constant
$const_dev = defined('WOONOOW_ADMIN_DEV') && WOONOOW_ADMIN_DEV === true; $const_dev = defined('WOONOOW_ADMIN_DEV') && WOONOOW_ADMIN_DEV === true;
$is_dev = $env_is_dev || $const_dev;
/** /**
* Filter: force dev/prod mode for WooNooW admin assets. * Filter: force dev/prod mode for WooNooW admin assets.
* Return true to use Vite dev server, false to use built assets. * Return true to use Vite dev server, false to use built assets.
*
* IMPORTANT: This filter should NOT be used in production!
* Only use it during development.
*/ */
return (bool) apply_filters('woonoow/admin_is_dev', $is_dev); $filtered = apply_filters('woonoow/admin_is_dev', $const_dev);
// Debug logging (only if WP_DEBUG is enabled)
if (defined('WP_DEBUG') && WP_DEBUG && $filtered !== $const_dev) {
error_log('[WooNooW Assets] Dev mode changed by filter: ' . ($filtered ? 'true' : 'false'));
}
return (bool) $filtered;
} }
/** Dev server URL (filterable) */ /** Dev server URL (filterable) */
private static function dev_server_url(): string { private static function dev_server_url(): string {
$default = 'http://localhost:5173'; // Auto-detect based on current host (for Local by Flywheel compatibility)
$host = $_SERVER['HTTP_HOST'] ?? 'localhost';
$protocol = is_ssl() ? 'https' : 'http';
// If using *.local domain (Local by Flywheel), use HTTPS
if (strpos($host, '.local') !== false) {
$protocol = 'https';
}
$default = $protocol . '://' . $host . ':5173';
/** Filter: change dev server URL if needed */ /** Filter: change dev server URL if needed */
return (string) apply_filters('woonoow/admin_dev_server', $default); return (string) apply_filters('woonoow/admin_dev_server', $default);
} }

View File

@@ -43,6 +43,9 @@ class StandaloneAdmin {
* Render standalone admin interface * Render standalone admin interface
*/ */
private static function render_standalone_admin() { private static function render_standalone_admin() {
// Enqueue WordPress media library (needed for image uploads)
wp_enqueue_media();
// Check if user is logged in and has permissions // Check if user is logged in and has permissions
$is_logged_in = is_user_logged_in(); $is_logged_in = is_user_logged_in();
$has_permission = $is_logged_in && current_user_can( 'manage_woocommerce' ); $has_permission = $is_logged_in && current_user_can( 'manage_woocommerce' );
@@ -105,7 +108,17 @@ class StandaloneAdmin {
} }
?> ?>
<!-- WooNooW Assets Only - NO wp_head() --> <?php
// Print WordPress media library styles (complete set for proper modal)
wp_print_styles( 'media-views' );
wp_print_styles( 'imgareaselect' );
wp_print_styles( 'buttons' );
wp_print_styles( 'dashicons' );
wp_print_styles( 'wp-admin' );
wp_print_styles( 'common' );
?>
<!-- WooNooW Assets -->
<link rel="stylesheet" href="<?php echo esc_url( $css_url ); ?>"> <link rel="stylesheet" href="<?php echo esc_url( $css_url ); ?>">
</head> </head>
<body class="woonoow-standalone"> <body class="woonoow-standalone">
@@ -137,13 +150,25 @@ class StandaloneAdmin {
// Navigation tree (single source of truth from PHP) // Navigation tree (single source of truth from PHP)
window.WNW_NAV_TREE = <?php echo wp_json_encode( \WooNooW\Compat\NavigationRegistry::get_frontend_nav_tree() ); ?>; window.WNW_NAV_TREE = <?php echo wp_json_encode( \WooNooW\Compat\NavigationRegistry::get_frontend_nav_tree() ); ?>;
// WordPress REST API settings (for media upload compatibility)
window.wpApiSettings = {
root: <?php echo wp_json_encode( untrailingslashit( rest_url() ) ); ?>,
nonce: <?php echo wp_json_encode( $nonce ); ?>,
versionString: 'wp/v2/'
};
</script> </script>
<script type="module" src="<?php echo esc_url( $js_url ); ?>"></script>
<?php <?php
// NO wp_footer() - we don't want theme/plugin scripts // Print WordPress media library scripts (needed for wp.media)
wp_print_scripts( 'media-editor' );
wp_print_scripts( 'media-audiovideo' );
// Print media templates (required for media modal to work)
wp_print_media_templates();
?> ?>
<script type="module" src="<?php echo esc_url( $js_url ); ?>"></script>
</body> </body>
</html> </html>
<?php <?php

View File

@@ -7,14 +7,14 @@ use WooNooW\Api\CheckoutController;
use WooNooW\Api\OrdersController; use WooNooW\Api\OrdersController;
use WooNooW\Api\AnalyticsController; use WooNooW\Api\AnalyticsController;
use WooNooW\Api\AuthController; use WooNooW\Api\AuthController;
use WooNooW\API\PaymentsController; use WooNooW\Api\PaymentsController;
use WooNooW\API\StoreController; use WooNooW\Api\StoreController;
use WooNooW\Api\ShippingController; use WooNooW\Api\ShippingController;
use WooNooW\Api\TaxController; use WooNooW\Api\TaxController;
use WooNooW\Api\PickupLocationsController; use WooNooW\Api\PickupLocationsController;
use WooNooW\Api\EmailController; use WooNooW\Api\EmailController;
use WooNooW\API\DeveloperController; use WooNooW\Api\DeveloperController;
use WooNooW\API\SystemController; use WooNooW\Api\SystemController;
use WooNooW\Api\NotificationsController; use WooNooW\Api\NotificationsController;
use WooNooW\Api\ActivityLogController; use WooNooW\Api\ActivityLogController;

View File

@@ -115,19 +115,33 @@ class EmailManager {
* @param WC_Order $order * @param WC_Order $order
*/ */
public function send_order_processing_email($order_id, $order = null) { public function send_order_processing_email($order_id, $order = null) {
if (defined('WP_DEBUG') && WP_DEBUG) {
error_log('[EmailManager] send_order_processing_email triggered for order #' . $order_id);
}
if (!$order) { if (!$order) {
$order = wc_get_order($order_id); $order = wc_get_order($order_id);
} }
if (!$order) { if (!$order) {
if (defined('WP_DEBUG') && WP_DEBUG) {
error_log('[EmailManager] Order not found for ID: ' . $order_id);
}
return; return;
} }
// Check if event is enabled // Check if event is enabled
if (!$this->is_event_enabled('order_processing', 'email', 'customer')) { if (!$this->is_event_enabled('order_processing', 'email', 'customer')) {
if (defined('WP_DEBUG') && WP_DEBUG) {
error_log('[EmailManager] order_processing email is disabled in settings');
}
return; return;
} }
if (defined('WP_DEBUG') && WP_DEBUG) {
error_log('[EmailManager] Sending order_processing email for order #' . $order_id);
}
// Send email // Send email
$this->send_email('order_processing', 'customer', $order); $this->send_email('order_processing', 'customer', $order);
} }
@@ -364,6 +378,10 @@ class EmailManager {
* @param array $extra_data * @param array $extra_data
*/ */
private function send_email($event_id, $recipient_type, $data, $extra_data = []) { private function send_email($event_id, $recipient_type, $data, $extra_data = []) {
if (defined('WP_DEBUG') && WP_DEBUG) {
error_log('[EmailManager] send_email called - Event: ' . $event_id . ', Recipient: ' . $recipient_type);
}
// Get email renderer // Get email renderer
$renderer = EmailRenderer::instance(); $renderer = EmailRenderer::instance();
@@ -371,16 +389,27 @@ class EmailManager {
$email = $renderer->render($event_id, $recipient_type, $data, $extra_data); $email = $renderer->render($event_id, $recipient_type, $data, $extra_data);
if (!$email) { if (!$email) {
if (defined('WP_DEBUG') && WP_DEBUG) {
error_log('[EmailManager] Email rendering failed for event: ' . $event_id);
}
return; return;
} }
if (defined('WP_DEBUG') && WP_DEBUG) {
error_log('[EmailManager] Email rendered successfully - To: ' . $email['to'] . ', Subject: ' . $email['subject']);
}
// Send email via wp_mail // Send email via wp_mail
$headers = [ $headers = [
'Content-Type: text/html; charset=UTF-8', 'Content-Type: text/html; charset=UTF-8',
'From: ' . get_bloginfo('name') . ' <' . get_option('admin_email') . '>', 'From: ' . get_bloginfo('name') . ' <' . get_option('admin_email') . '>',
]; ];
wp_mail($email['to'], $email['subject'], $email['body'], $headers); $sent = wp_mail($email['to'], $email['subject'], $email['body'], $headers);
if (defined('WP_DEBUG') && WP_DEBUG) {
error_log('[EmailManager] wp_mail called - Result: ' . ($sent ? 'success' : 'failed'));
}
// Log email sent // Log email sent
do_action('woonoow_email_sent', $event_id, $recipient_type, $email); do_action('woonoow_email_sent', $event_id, $recipient_type, $email);

255
test-email-flow.php Normal file
View File

@@ -0,0 +1,255 @@
<?php
/**
* Email Flow Diagnostic Tool
*
* Usage: Visit https://yoursite.com/wp-content/plugins/woonoow/test-email-flow.php
*/
// Load WordPress
require_once dirname(dirname(dirname(dirname(__FILE__)))) . '/wp-load.php';
if (!current_user_can('manage_options')) {
die('Access denied');
}
header('Content-Type: text/html; charset=utf-8');
?>
<!DOCTYPE html>
<html>
<head>
<title>Email Flow Diagnostic</title>
<style>
body { font-family: monospace; padding: 20px; background: #1e1e1e; color: #d4d4d4; }
.pass { color: #4ec9b0; }
.fail { color: #f48771; }
.warn { color: #dcdcaa; }
h1 { color: #569cd6; }
h2 { color: #4ec9b0; margin-top: 30px; }
pre { background: #2d2d2d; padding: 10px; border-radius: 4px; overflow-x: auto; }
.status { display: inline-block; width: 60px; font-weight: bold; }
button { background: #0e639c; color: white; border: none; padding: 10px 20px; cursor: pointer; border-radius: 4px; margin: 10px 5px; }
button:hover { background: #1177bb; }
</style>
</head>
<body>
<h1>Email Flow Diagnostic Tool</h1>
<h2>1. System Status</h2>
<?php
// Check notification system mode
$system_mode = get_option('woonoow_notification_system_mode', 'woonoow');
$status = ($system_mode === 'woonoow') ? '<span class="pass">✓ ENABLED</span>' : '<span class="fail">✗ DISABLED</span>';
echo "<div>$status - Notification System Mode: <strong>$system_mode</strong></div>";
// Check email channel
$email_enabled = get_option('woonoow_email_notifications_enabled', false);
$status = $email_enabled ? '<span class="pass">✓ ENABLED</span>' : '<span class="fail">✗ DISABLED</span>';
echo "<div>$status - Email Channel: <strong>" . ($email_enabled ? 'enabled' : 'disabled') . "</strong></div>";
// Check Action Scheduler
$as_exists = function_exists('as_enqueue_async_action');
$status = $as_exists ? '<span class="pass">✓ AVAILABLE</span>' : '<span class="warn">⚠ NOT AVAILABLE</span>';
echo "<div>$status - Action Scheduler: <strong>" . ($as_exists ? 'available' : 'not available (using wp-cron)') . "</strong></div>";
?>
<h2>2. Event Configuration</h2>
<?php
$settings = get_option('woonoow_notification_settings', []);
$events = $settings['events'] ?? [];
if (empty($events)) {
echo "<div><span class='fail'>✗ FAIL</span> - No events configured!</div>";
} else {
echo "<div><span class='pass'>✓ PASS</span> - " . count($events) . " events configured</div>";
echo "<pre>";
foreach ($events as $event_id => $event_data) {
$email_enabled = $event_data['channels']['email']['enabled'] ?? false;
$status_icon = $email_enabled ? '✓' : '✗';
echo "$status_icon $event_id: email " . ($email_enabled ? 'enabled' : 'disabled') . "\n";
}
echo "</pre>";
}
?>
<h2>3. Email Queue Status</h2>
<?php
// Check for queued emails in wp_options
global $wpdb;
$queued_emails = $wpdb->get_results(
"SELECT option_name, option_value FROM {$wpdb->options} WHERE option_name LIKE 'woonoow_mail_%' LIMIT 10"
);
if (empty($queued_emails)) {
echo "<div><span class='warn'>⚠ INFO</span> - No emails currently queued</div>";
} else {
echo "<div><span class='pass'>✓ INFO</span> - " . count($queued_emails) . " emails in queue</div>";
echo "<pre>";
foreach ($queued_emails as $email) {
$payload = maybe_unserialize($email->option_value);
echo "ID: {$email->option_name}\n";
echo "To: " . ($payload['to'] ?? 'unknown') . "\n";
echo "Subject: " . ($payload['subject'] ?? 'unknown') . "\n";
echo "---\n";
}
echo "</pre>";
}
?>
<h2>4. Action Scheduler Queue</h2>
<?php
if ($as_exists) {
$pending = $wpdb->get_var(
"SELECT COUNT(*) FROM {$wpdb->prefix}actionscheduler_actions
WHERE hook = 'woonoow/mail/send' AND status = 'pending'"
);
$failed = $wpdb->get_var(
"SELECT COUNT(*) FROM {$wpdb->prefix}actionscheduler_actions
WHERE hook = 'woonoow/mail/send' AND status = 'failed'"
);
$complete = $wpdb->get_var(
"SELECT COUNT(*) FROM {$wpdb->prefix}actionscheduler_actions
WHERE hook = 'woonoow/mail/send' AND status = 'complete'
ORDER BY action_id DESC LIMIT 10"
);
echo "<div><span class='pass'>✓ INFO</span> - Pending: <strong>$pending</strong></div>";
echo "<div><span class='pass'>✓ INFO</span> - Completed (last 10): <strong>$complete</strong></div>";
if ($failed > 0) {
echo "<div><span class='fail'>✗ WARN</span> - Failed: <strong>$failed</strong></div>";
// Show failed actions
$failed_actions = $wpdb->get_results(
"SELECT * FROM {$wpdb->prefix}actionscheduler_actions
WHERE hook = 'woonoow/mail/send' AND status = 'failed'
ORDER BY action_id DESC LIMIT 5"
);
if ($failed_actions) {
echo "<pre>Recent failures:\n";
foreach ($failed_actions as $action) {
echo "ID: {$action->action_id}\n";
echo "Args: {$action->args}\n";
echo "Scheduled: {$action->scheduled_date_gmt}\n";
echo "---\n";
}
echo "</pre>";
}
} else {
echo "<div><span class='pass'>✓ PASS</span> - Failed: <strong>0</strong></div>";
}
} else {
echo "<div><span class='warn'>⚠ WARN</span> - Action Scheduler not available, using wp-cron</div>";
}
?>
<h2>5. Recent Email Logs</h2>
<?php
// Check debug log for email activity
$debug_log = WP_CONTENT_DIR . '/debug.log';
if (file_exists($debug_log)) {
$log_lines = file($debug_log);
$email_logs = array_filter($log_lines, function($line) {
return strpos($line, '[WooNooW MailQueue]') !== false ||
strpos($line, 'woonoow_email_sent') !== false;
});
if (empty($email_logs)) {
echo "<div><span class='warn'>⚠ INFO</span> - No email logs found (enable WP_DEBUG to see logs)</div>";
} else {
echo "<div><span class='pass'>✓ INFO</span> - " . count($email_logs) . " email log entries found</div>";
echo "<pre>";
echo implode('', array_slice($email_logs, -10)); // Last 10 entries
echo "</pre>";
}
} else {
echo "<div><span class='warn'>⚠ INFO</span> - Debug log not found (enable WP_DEBUG_LOG)</div>";
}
?>
<h2>6. Test Actions</h2>
<?php
if (isset($_GET['action'])) {
echo "<div style='margin: 20px 0; padding: 15px; background: #2d2d2d; border-left: 4px solid #4ec9b0;'>";
if ($_GET['action'] === 'test_email') {
echo "<h3>Testing Email Queue...</h3>";
// Queue a test email
$test_payload = [
'to' => get_option('admin_email'),
'subject' => 'WooNooW Test Email - ' . date('Y-m-d H:i:s'),
'html' => '<h1>Test Email</h1><p>This is a test email from WooNooW email queue system.</p><p>Time: ' . date('Y-m-d H:i:s') . '</p>',
'headers' => ['Content-Type: text/html; charset=UTF-8'],
'attachments' => [],
];
\WooNooW\Core\Mail\MailQueue::enqueue($test_payload);
echo "<p><span class='pass'>✓ SUCCESS</span> - Test email queued to: " . get_option('admin_email') . "</p>";
echo "<p>Check your inbox in a few moments. Also check Action Scheduler queue above.</p>";
}
if ($_GET['action'] === 'process_queue') {
echo "<h3>Processing Email Queue...</h3>";
if (function_exists('as_run_queue')) {
as_run_queue();
echo "<p><span class='pass'>✓ SUCCESS</span> - Action Scheduler queue processed</p>";
} else {
echo "<p><span class='warn'>⚠ WARN</span> - Action Scheduler not available</p>";
}
}
if ($_GET['action'] === 'test_order_email') {
echo "<h3>Testing Order Email...</h3>";
// Get a recent order
$orders = wc_get_orders(['limit' => 1, 'orderby' => 'date', 'order' => 'DESC']);
if (empty($orders)) {
echo "<p><span class='fail'>✗ FAIL</span> - No orders found. Create a test order first.</p>";
} else {
$order = $orders[0];
echo "<p>Using order #" . $order->get_id() . "</p>";
// Trigger order processing email
$email_manager = \WooNooW\Core\Notifications\EmailManager::instance();
$email_manager->send_order_processing_email($order->get_id(), $order);
echo "<p><span class='pass'>✓ SUCCESS</span> - Order processing email triggered</p>";
echo "<p>Check Action Scheduler queue and email logs above.</p>";
}
}
echo "</div>";
}
?>
<div style="margin-top: 20px;">
<button onclick="window.location.href='?action=test_email'">Queue Test Email</button>
<button onclick="window.location.href='?action=process_queue'">Process Queue Now</button>
<button onclick="window.location.href='?action=test_order_email'">Test Order Email</button>
<button onclick="window.location.reload()">Refresh</button>
</div>
<h2>7. Troubleshooting</h2>
<div>
<h3>If emails are not sending:</h3>
<ol>
<li>Check that "Notification System Mode" is set to "woonoow" (not "woocommerce")</li>
<li>Check that "Email Channel" is enabled</li>
<li>Check that specific events have email enabled in configuration</li>
<li>Check Action Scheduler for failed actions</li>
<li>Enable WP_DEBUG and WP_DEBUG_LOG to see detailed logs</li>
<li>Check your server's mail configuration (SMTP, sendmail, etc.)</li>
<li>Test with "Queue Test Email" button above</li>
</ol>
</div>
<div style="margin-top: 40px; padding-top: 20px; border-top: 1px solid #444; color: #888;">
Generated: <?php echo date('Y-m-d H:i:s'); ?>
</div>
</body>
</html>

View File

@@ -43,5 +43,6 @@ register_activation_hook(__FILE__, function () {
update_option('woocommerce_custom_orders_table_migration_enabled', 'yes'); update_option('woocommerce_custom_orders_table_migration_enabled', 'yes');
}); });
add_filter('woonoow/admin_is_dev', '__return_true'); // Dev mode filters removed - use wp-config.php if needed:
add_filter('woonoow/admin_dev_server', fn() => 'https://woonoow.local:5173'); // add_filter('woonoow/admin_is_dev', '__return_true');
// add_filter('woonoow/admin_dev_server', fn() => 'https://woonoow.local:5173');