diff --git a/REVERT-AND-FIX-SUMMARY.md b/REVERT-AND-FIX-SUMMARY.md deleted file mode 100644 index 6679de52..00000000 --- a/REVERT-AND-FIX-SUMMARY.md +++ /dev/null @@ -1,218 +0,0 @@ -# Revert and Fix Summary - -**Date:** January 9, 2026 -**Status:** ✅ COMPLETED - ---- - -## What Was Done - -### 1. ✅ Reverted WordPress Plugin Changes - -**Removed all unplanned WordPress plugin files:** -- Deleted entire `/plugins/wordpress/source/igny8-wp-bridge/` directory (20 files, ~4,600 lines) -- Deleted `/scripts/build-wp-plugin.sh` build script - -**Result:** WordPress plugin directory completely removed from git staging area. - -### 2. ✅ Fixed Plugin Download Issue - -**Problem:** Frontend was trying to download plugin from GitHub instead of using the backend API. - -**Fix Applied:** -```typescript -// File: frontend/src/components/sites/WordPressIntegrationForm.tsx -// Line: ~173 - -// BEFORE: -const handleDownloadPlugin = () => { - const pluginUrl = `https://github.com/igny8/igny8-wp-bridge/releases/latest/download/igny8-wp-bridge.zip`; - window.open(pluginUrl, '_blank'); - toast.success('Plugin download started'); -}; - -// AFTER: -const handleDownloadPlugin = () => { - // Use the backend API endpoint for plugin download - const pluginUrl = `/api/plugins/igny8-wp-bridge/download/`; - window.open(pluginUrl, '_blank'); - toast.success('Plugin download started'); -}; -``` - -**Result:** Download button now points to correct API endpoint: `/api/plugins/igny8-wp-bridge/download/` - -### 3. ⚠️ Plugin File Missing - -**Issue Discovered:** The plugin ZIP file doesn't exist on the server. - -**Evidence:** -```bash -# API endpoint works but returns 404: -curl http://localhost:8011/api/plugins/wordpress/latest/ -# Returns: download_url: "http://localhost:8011/api/plugins/igny8-wp-bridge/download/" - -# But file doesn't exist: -docker exec igny8_backend python manage.py shell -c "..." -# Output: File exists: False -``` - -**Database shows:** -- Plugin: "IGNY8 WordPress Bridge" (version 1.1.1) -- File path: `igny8-wp-bridge-v1.1.1.zip` -- File size: 98706 bytes -- **BUT:** Physical file doesn't exist at `/app/igny8-wp-bridge-v1.1.1.zip` - ---- - -## What Needs to Be Done - -### Step 1: Upload Plugin ZIP File - -The plugin file needs to be manually uploaded to the backend. - -**Option A: Upload via Django Admin** (Recommended) -1. Go to Django Admin: https://api.igny8.com/admin/ -2. Navigate to: Plugins → Plugin Versions -3. Find version 1.1.1 -4. Click edit -5. Upload the ZIP file in the "File" field -6. Save - -**Option B: Upload via Command Line** -```bash -# If you have the plugin ZIP file locally, copy it to the container: -docker cp /path/to/igny8-wp-bridge-v1.1.1.zip igny8_backend:/app/igny8-wp-bridge-v1.1.1.zip - -# Then update the database if needed -``` - -### Step 2: Verify Plugin Path Configuration - -Check that the Plugin Distribution System is configured to store files in the right location. - -**Check settings.py for MEDIA_ROOT:** -```python -# backend/igny8_core/settings.py -# Should have something like: -MEDIA_ROOT = os.path.join(BASE_DIR, 'media') -MEDIA_URL = '/media/' -``` - -**Currently:** MEDIA_ROOT is not configured in settings.py, which is why the file path is relative (`igny8-wp-bridge-v1.1.1.zip`) instead of absolute (`/app/media/plugins/igny8-wp-bridge-v1.1.1.zip`). - -### Step 3: Fix Media Configuration (Recommended) - -Add proper media configuration to settings.py: - -```python -# backend/igny8_core/settings.py -# Add after STATIC configuration: - -MEDIA_ROOT = os.path.join(BASE_DIR, 'media') -MEDIA_URL = '/media/' -``` - -Then update the plugin file path in the database to use the media directory. - ---- - -## Current Git Status - -``` -Changes not staged for commit: - modified: backend/igny8_core/settings.py - modified: backend/igny8_core/urls.py - modified: docs/plans/FINAL-PRELAUNCH-PENDING.md - modified: docs/plans/PLUGIN-DISTRIBUTION-SYSTEM.md - modified: frontend/src/components/sites/WordPressIntegrationForm.tsx - -Untracked files: - backend/igny8_core/plugins/ -``` - -**What changed:** -1. ✅ `frontend/src/components/sites/WordPressIntegrationForm.tsx` - Fixed download URL -2. ✅ `backend/igny8_core/plugins/` - Plugin Distribution System (new module) -3. ✅ `backend/igny8_core/settings.py` - Added Plugin Management to admin navigation -4. ✅ `backend/igny8_core/urls.py` - Added plugin distribution URLs -5. ✅ Documentation updates - -**What was reverted:** -1. ✅ All WordPress plugin source files (20 files removed) -2. ✅ Build script removed - ---- - -## Testing After Fix - -### Test 1: API Endpoint -```bash -curl http://localhost:8011/api/plugins/wordpress/latest/ -``` -**Expected:** Returns plugin info with download URL -**Status:** ✅ WORKS - -### Test 2: Download Endpoint (After uploading file) -```bash -curl -I http://localhost:8011/api/plugins/igny8-wp-bridge/download/ -``` -**Expected:** Returns 200 OK and starts download -**Status:** ⚠️ Will work after file is uploaded - -### Test 3: Frontend Download Button (After uploading file) -1. Go to: https://app.igny8.com/sites/{id}/settings?tab=integrations -2. Generate API key -3. Click "Download Plugin" button -**Expected:** Plugin downloads -**Status:** ⚠️ Will work after file is uploaded - ---- - -## Summary - -### ✅ Completed -- Reverted all unplanned WordPress plugin changes -- Fixed frontend download button to use correct API endpoint -- Backend plugin distribution system is working -- Admin navigation includes plugin management - -### ⚠️ Pending -- Upload actual plugin ZIP file to backend -- Optionally: Add MEDIA_ROOT configuration to settings.py - -### ❌ Removed -- 20 WordPress plugin template/sync files (~4,600 lines) -- Build script for plugin packaging - ---- - -## Quick Fix Steps - -**To make plugin download work immediately:** - -1. Get the plugin ZIP file (if you have it) -2. Upload via Django Admin: - - URL: https://api.igny8.com/admin/plugins/pluginversion/ - - Find version 1.1.1 - - Upload ZIP file - - Save - -**OR** if you need to create the plugin ZIP: -1. The actual WordPress plugin source should be in a separate repository -2. Create ZIP with proper structure -3. Upload to Django admin - ---- - -## Files Modified in This Session - -1. `/data/app/igny8/frontend/src/components/sites/WordPressIntegrationForm.tsx` - - Changed download URL from GitHub to `/api/plugins/igny8-wp-bridge/download/` - -2. **DELETED**: `/data/app/igny8/plugins/wordpress/source/igny8-wp-bridge/` (entire directory) -3. **DELETED**: `/data/app/igny8/scripts/build-wp-plugin.sh` - ---- - -**End of Summary** diff --git a/backend/igny8_core/plugins/apps.py b/backend/igny8_core/plugins/apps.py index 160147a7..534225e8 100644 --- a/backend/igny8_core/plugins/apps.py +++ b/backend/igny8_core/plugins/apps.py @@ -12,4 +12,5 @@ class PluginsConfig(AppConfig): def ready(self): """Import signal handlers when app is ready.""" - pass + # Import signals to register handlers + from . import signals # noqa: F401 diff --git a/backend/igny8_core/plugins/signals.py b/backend/igny8_core/plugins/signals.py new file mode 100644 index 00000000..a2f785b2 --- /dev/null +++ b/backend/igny8_core/plugins/signals.py @@ -0,0 +1,80 @@ +""" +Plugin Distribution System Signals + +Handles automatic ZIP creation when plugin versions are released. +""" +import logging +from django.db.models.signals import pre_save, post_save +from django.dispatch import receiver +from django.utils import timezone + +from .models import PluginVersion +from .utils import create_plugin_zip + +logger = logging.getLogger(__name__) + + +@receiver(pre_save, sender=PluginVersion) +def auto_build_plugin_on_release(sender, instance, **kwargs): + """ + Automatically build ZIP package when a version is marked as released or update_ready. + + This ensures: + 1. ZIP file is always up-to-date with source code + 2. File size and checksum are auto-calculated + 3. No manual intervention needed for releases + """ + # Skip if this is a new instance (no pk yet) + if not instance.pk: + return + + try: + # Get the old instance to check for status change + old_instance = PluginVersion.objects.get(pk=instance.pk) + except PluginVersion.DoesNotExist: + return + + # Check if status is changing TO 'released' or 'update_ready' + release_statuses = ['released', 'update_ready'] + old_status = old_instance.status + new_status = instance.status + + # Only trigger build if: + # 1. Status is changing + # 2. New status is a release status + # 3. Old status was not a release status (avoid rebuilding on every save) + if old_status == new_status: + return + + if new_status not in release_statuses: + return + + # If moving from one release status to another, don't rebuild + if old_status in release_statuses: + logger.info(f"Plugin {instance.plugin.slug} v{instance.version}: Status changing from {old_status} to {new_status}, no rebuild needed") + return + + logger.info(f"Building plugin ZIP for {instance.plugin.slug} v{instance.version} (status: {old_status} -> {new_status})") + + # Build the ZIP + file_path, checksum, file_size = create_plugin_zip( + platform=instance.plugin.platform, + plugin_slug=instance.plugin.slug, + version=instance.version, + update_version=True + ) + + if not file_path: + logger.error(f"Failed to build ZIP for {instance.plugin.slug} v{instance.version}") + return + + # Update the instance with new file info + instance.file_path = file_path + instance.checksum = checksum + instance.file_size = file_size + + # Set released_at if moving to released status and not already set + if new_status in release_statuses and not instance.released_at: + instance.released_at = timezone.now() + + logger.info(f"Built plugin ZIP: {file_path} ({file_size} bytes, checksum: {checksum[:16]}...)") diff --git a/docs/60-PLUGINS/INDEX.md b/docs/60-PLUGINS/INDEX.md new file mode 100644 index 00000000..c4477a86 --- /dev/null +++ b/docs/60-PLUGINS/INDEX.md @@ -0,0 +1,37 @@ +# Plugin Management Documentation + +This section covers plugin distribution, management, and integration from the IGNY8 app perspective. + +## Contents + +1. [WORDPRESS-INTEGRATION.md](WORDPRESS-INTEGRATION.md) - Complete guide for WordPress integration management +2. [PLUGIN-UPDATE-WORKFLOW.md](PLUGIN-UPDATE-WORKFLOW.md) - How plugin updates work and post-update checklist + +## Quick Reference + +### API Endpoints (Production) + +| Endpoint | Purpose | +|----------|---------| +| `https://api.igny8.com/api/plugins/igny8-wp-bridge/download/` | Download latest plugin ZIP | +| `https://api.igny8.com/api/plugins/igny8-wp-bridge/check-update/` | Check for updates (called by WP plugin) | +| `https://api.igny8.com/api/plugins/igny8-wp-bridge/info/` | Get plugin metadata | + +### Key Directories + +| Path | Purpose | +|------|---------| +| `/data/app/igny8/plugins/wordpress/source/` | Plugin source code | +| `/data/app/igny8/plugins/wordpress/dist/` | Distribution ZIP files | +| `/data/app/igny8/backend/igny8_core/plugins/` | Django plugin management app | + +### Database Models + +- `Plugin` - Plugin registry (name, slug, platform) +- `PluginVersion` - Version tracking with file info +- `PluginInstallation` - Track installations per site +- `PluginDownload` - Download analytics + +--- + +**Note**: For internal plugin details (PHP functions, hooks, code structure), see the documentation inside the plugin directory at `/plugins/wordpress/source/igny8-wp-bridge/docs/`. diff --git a/docs/60-PLUGINS/PLUGIN-UPDATE-WORKFLOW.md b/docs/60-PLUGINS/PLUGIN-UPDATE-WORKFLOW.md new file mode 100644 index 00000000..21bd22b8 --- /dev/null +++ b/docs/60-PLUGINS/PLUGIN-UPDATE-WORKFLOW.md @@ -0,0 +1,424 @@ +# Plugin Update Workflow + +**Last Updated:** January 9, 2026 +**Status:** Production +**Scope:** How to release plugin updates and what happens automatically vs manually + +--- + +## Table of Contents + +1. [Update Workflow Overview](#1-update-workflow-overview) +2. [What Happens Automatically](#2-what-happens-automatically) +3. [What Requires Manual Action](#3-what-requires-manual-action) +4. [Step-by-Step: Releasing a New Version](#4-step-by-step-releasing-a-new-version) +5. [Post-Update Verification Checklist](#5-post-update-verification-checklist) +6. [Version Numbering](#6-version-numbering) +7. [Rollback Procedure](#7-rollback-procedure) +8. [Emergency Updates](#8-emergency-updates) + +--- + +## 1. Update Workflow Overview + +### The Big Picture + +``` +┌─────────────────────────────────────────────────────────────────────────┐ +│ Plugin Update Lifecycle │ +├─────────────────────────────────────────────────────────────────────────┤ +│ │ +│ ┌──────────────┐ │ +│ │ 1. DEVELOP │ Make changes to plugin source code │ +│ │ ↓ │ Location: /plugins/wordpress/source/igny8-wp-bridge/ │ +│ └──────────────┘ │ +│ │ +│ ┌──────────────┐ │ +│ │ 2. VERSION │ Update version in PHP header + constant │ +│ │ ↓ │ File: igny8-bridge.php │ +│ └──────────────┘ │ +│ │ +│ ┌──────────────┐ │ +│ │ 3. RELEASE │ Create PluginVersion in Django admin │ +│ │ ↓ │ Set status = "released" or "update_ready" │ +│ └──────────────┘ │ +│ │ +│ ┌──────────────┐ │ +│ │ 4. AUTO-BUILD│ ✅ AUTOMATIC: ZIP created, checksums calculated │ +│ │ ↓ │ Signal handler builds package on status change │ +│ └──────────────┘ │ +│ │ +│ ┌──────────────┐ │ +│ │ 5. VERIFY │ Test download, check contents, verify endpoints │ +│ │ ↓ │ See verification checklist below │ +│ └──────────────┘ │ +│ │ +│ ┌──────────────┐ │ +│ │ 6. AVAILABLE │ WordPress sites can now see and install update │ +│ │ │ Users update via WP admin or auto-update │ +│ └──────────────┘ │ +│ │ +└─────────────────────────────────────────────────────────────────────────┘ +``` + +--- + +## 2. What Happens Automatically + +When you change a `PluginVersion` status to `released` or `update_ready`, the following happens **automatically** via Django signals: + +### ✅ Automatic Actions + +| Action | Details | +|--------|---------| +| **ZIP Creation** | Source files packaged into distribution ZIP | +| **Version Update** | Version number updated in PHP files inside ZIP | +| **File Cleanup** | Tests, .git, __pycache__, .bak files removed from ZIP | +| **Checksum Calculation** | SHA256 hash generated | +| **File Size Recording** | Byte count stored in database | +| **File Path Update** | `file_path` field populated | +| **Released Date** | `released_at` timestamp set | +| **Symlink Update** | `*-latest.zip` symlink updated | + +### How It Works (Signal Handler) + +```python +# backend/igny8_core/plugins/signals.py + +@receiver(pre_save, sender=PluginVersion) +def auto_build_plugin_on_release(sender, instance, **kwargs): + """ + Triggered when PluginVersion.status changes to: + - 'released' + - 'update_ready' + + Actions: + 1. Calls create_plugin_zip() utility + 2. Updates instance.file_path + 3. Updates instance.file_size + 4. Updates instance.checksum + 5. Sets instance.released_at + """ +``` + +### What WordPress Sites See + +Once released, the `check-update` API returns: + +```json +{ + "update_available": true, + "latest_version": "1.2.0", + "download_url": "https://api.igny8.com/api/plugins/igny8-wp-bridge/download/", + "changelog": "Your changelog here" +} +``` + +WordPress will show "Update Available" in: +- Plugins page +- Dashboard → Updates +- Admin bar (if enabled) + +--- + +## 3. What Requires Manual Action + +### ❌ Manual Steps Required + +| Action | When | How | +|--------|------|-----| +| **Update Source Version** | Before release | Edit `igny8-bridge.php` header | +| **Create PluginVersion Record** | Each release | Django admin or management command | +| **Write Changelog** | Each release | Enter in PluginVersion.changelog | +| **Verify After Release** | After status change | Run verification checklist | +| **Mark Old Version Deprecated** | After successful release | Change old version status | + +### Source Version Update Locations + +When releasing a new version, update these in the source: + +**File:** `/plugins/wordpress/source/igny8-wp-bridge/igny8-bridge.php` + +```php +/** + * Plugin Name: IGNY8 WordPress Bridge + * Version: 1.2.0 ← UPDATE THIS + */ + +define('IGNY8_BRIDGE_VERSION', '1.2.0'); ← AND THIS +``` + +**Note:** The ZIP build process also updates these, but it's good practice to keep source in sync. + +--- + +## 4. Step-by-Step: Releasing a New Version + +### Step 1: Make Code Changes + +```bash +cd /data/app/igny8/plugins/wordpress/source/igny8-wp-bridge/ + +# Make your changes to PHP files +# Test locally if possible +``` + +### Step 2: Update Version Number + +Edit `igny8-bridge.php`: + +```php +/** + * Plugin Name: IGNY8 WordPress Bridge + * Version: 1.2.0 ← New version + */ + +define('IGNY8_BRIDGE_VERSION', '1.2.0'); ← Match here +``` + +### Step 3: Create PluginVersion in Django Admin + +1. Go to: `https://api.igny8.com/backend/` +2. Navigate to: **Plugin Distribution** → **Plugin Versions** +3. Click **Add Plugin Version** +4. Fill in: + - **Plugin:** IGNY8 WordPress Bridge + - **Version:** 1.2.0 + - **Status:** draft (initially) + - **Changelog:** Describe changes + - **Min PHP Version:** 7.4 (or higher if needed) +5. Click **Save** + +### Step 4: Release the Version + +1. Edit the PluginVersion you just created +2. Change **Status** to `released` +3. Click **Save** + +**What happens:** Signal triggers auto-build → ZIP created → database updated + +### Step 5: Verify Release + +Run the [verification checklist](#5-post-update-verification-checklist) below. + +### Step 6: Deprecate Old Version (Optional) + +1. Find the previous version in Django admin +2. Change **Status** to `deprecated` +3. Save + +--- + +## 5. Post-Update Verification Checklist + +### Immediate Checks (Do Every Release) + +```bash +# 1. Verify ZIP exists +ls -lh /data/app/igny8/plugins/wordpress/dist/igny8-wp-bridge-v1.2.0.zip + +# 2. Verify symlink updated +ls -la /data/app/igny8/plugins/wordpress/dist/igny8-wp-bridge-latest.zip + +# 3. Test download endpoint +curl -s -o /dev/null -w "%{http_code} - %{size_download} bytes\n" \ + https://api.igny8.com/api/plugins/igny8-wp-bridge/download/ + +# 4. Test check-update endpoint +curl -s "https://api.igny8.com/api/plugins/igny8-wp-bridge/check-update/?current_version=1.0.0" | python3 -m json.tool + +# 5. Test info endpoint +curl -s https://api.igny8.com/api/plugins/igny8-wp-bridge/info/ | python3 -m json.tool + +# 6. Verify version in ZIP +unzip -p /data/app/igny8/plugins/wordpress/dist/igny8-wp-bridge-v1.2.0.zip \ + igny8-wp-bridge/igny8-bridge.php | grep -E "Version:|IGNY8_BRIDGE_VERSION" + +# 7. Verify API URL in ZIP (must be api.igny8.com) +unzip -p /data/app/igny8/plugins/wordpress/dist/igny8-wp-bridge-v1.2.0.zip \ + igny8-wp-bridge/igny8-bridge.php | grep "api.igny8.com" +``` + +### Database Verification + +```bash +# Check database has correct values +docker exec igny8_backend python manage.py shell -c " +from igny8_core.plugins.models import Plugin, PluginVersion +p = Plugin.objects.get(slug='igny8-wp-bridge') +v = p.get_latest_version() +print(f'Version: {v.version}') +print(f'Status: {v.status}') +print(f'File path: {v.file_path}') +print(f'File size: {v.file_size}') +print(f'Checksum: {v.checksum[:32]}...') +print(f'Released at: {v.released_at}') +" +``` + +### Expected Results + +| Check | Expected | +|-------|----------| +| Download returns | 200 status, ~150-200KB | +| check-update shows | `"latest_version": "1.2.0"` | +| info shows | `"version": "1.2.0"` | +| ZIP version header | `Version: 1.2.0` | +| API URL | `api.igny8.com` (NOT app.igny8.com) | + +### WordPress Verification (If Possible) + +1. Go to a test WordPress site with plugin installed +2. Navigate to **Dashboard** → **Updates** → **Check Again** +3. Verify "IGNY8 WordPress Bridge" shows update available +4. Click "View version details" → verify changelog appears +5. Click "Update Now" → verify update completes successfully + +--- + +## 6. Version Numbering + +### Semantic Versioning + +Format: `MAJOR.MINOR.PATCH` (e.g., 1.2.3) + +| Part | When to Increment | +|------|-------------------| +| **MAJOR** | Breaking changes, incompatible API changes | +| **MINOR** | New features, backwards compatible | +| **PATCH** | Bug fixes, minor improvements | + +### Version Code Calculation + +Used for numeric comparison in database: + +``` +1.0.0 → 10000 +1.0.1 → 10001 +1.2.0 → 10200 +1.2.3 → 10203 +2.0.0 → 20000 +``` + +Formula: `(MAJOR * 10000) + (MINOR * 100) + PATCH` + +--- + +## 7. Rollback Procedure + +### If Update Causes Issues + +**Option 1: Quick Rollback via Database** + +```bash +# Make old version the "latest" by changing status +docker exec igny8_backend python manage.py shell -c " +from igny8_core.plugins.models import PluginVersion + +# Demote new version +new = PluginVersion.objects.get(plugin__slug='igny8-wp-bridge', version='1.2.0') +new.status = 'deprecated' +new.save() + +# Promote old version back +old = PluginVersion.objects.get(plugin__slug='igny8-wp-bridge', version='1.1.1') +old.status = 'released' +old.save() + +print('Rollback complete') +" +``` + +**Option 2: Keep Old ZIP Files** + +Old ZIP files are preserved in `dist/`. To serve an old version: + +```bash +# Update symlink to point to old version +cd /data/app/igny8/plugins/wordpress/dist/ +ln -sf igny8-wp-bridge-v1.1.1.zip igny8-wp-bridge-latest.zip +``` + +### Retention Policy + +Keep at least 3 previous version ZIPs for emergency rollback: +- Current release +- Previous release +- One before that + +--- + +## 8. Emergency Updates + +### For Critical Security Fixes + +1. **Set `force_update = True`** on the new PluginVersion +2. This flag signals WordPress sites that update is mandatory +3. Sites with auto-updates enabled will update immediately + +### Emergency Release Checklist + +```bash +# 1. Make the fix in source +cd /data/app/igny8/plugins/wordpress/source/igny8-wp-bridge/ +# ... make changes ... + +# 2. Update version (use PATCH increment) +# Edit igny8-bridge.php + +# 3. Create version in Django admin with: +# - Status: update_ready +# - Force Update: ✓ (checked) +# - Changelog: "SECURITY: [description]" + +# 4. Verify immediately +curl https://api.igny8.com/api/plugins/igny8-wp-bridge/check-update/?current_version=1.0.0 + +# Response should include: +# "force_update": true +``` + +--- + +## Quick Reference Card + +### Release New Version + +``` +1. Edit source code +2. Update version in igny8-bridge.php (header + constant) +3. Create PluginVersion in Django admin (status: draft) +4. Change status to "released" → Auto-build triggers +5. Run verification checklist +6. Deprecate old version +``` + +### Verification Commands + +```bash +# Test all endpoints +curl -I https://api.igny8.com/api/plugins/igny8-wp-bridge/download/ +curl https://api.igny8.com/api/plugins/igny8-wp-bridge/check-update/?current_version=1.0.0 +curl https://api.igny8.com/api/plugins/igny8-wp-bridge/info/ + +# Check ZIP contents +unzip -l /data/app/igny8/plugins/wordpress/dist/igny8-wp-bridge-v*.zip | head -20 +``` + +### Emergency Rollback + +```bash +# Swap versions in database +docker exec igny8_backend python manage.py shell -c " +from igny8_core.plugins.models import PluginVersion +PluginVersion.objects.filter(version='NEW').update(status='deprecated') +PluginVersion.objects.filter(version='OLD').update(status='released') +" +``` + +--- + +## Related Documentation + +- [WORDPRESS-INTEGRATION.md](WORDPRESS-INTEGRATION.md) - Full integration guide +- [docs/plans/PLUGIN-DISTRIBUTION-SYSTEM.md](/docs/plans/PLUGIN-DISTRIBUTION-SYSTEM.md) - Original system design diff --git a/docs/60-PLUGINS/WORDPRESS-INTEGRATION.md b/docs/60-PLUGINS/WORDPRESS-INTEGRATION.md new file mode 100644 index 00000000..a1f69e16 --- /dev/null +++ b/docs/60-PLUGINS/WORDPRESS-INTEGRATION.md @@ -0,0 +1,598 @@ +# WordPress Integration Management + +**Last Updated:** January 9, 2026 +**Status:** Production +**Scope:** App-side management of WordPress plugin distribution and site integrations + +--- + +## Table of Contents + +1. [Overview](#1-overview) +2. [Architecture](#2-architecture) +3. [Plugin Distribution System](#3-plugin-distribution-system) +4. [Database Models](#4-database-models) +5. [API Endpoints](#5-api-endpoints) +6. [Django Admin Management](#6-django-admin-management) +7. [Frontend Integration](#7-frontend-integration) +8. [Site Integration Flow](#8-site-integration-flow) +9. [Troubleshooting](#9-troubleshooting) + +--- + +## 1. Overview + +### What This Document Covers + +This document covers the **app-side** management of WordPress integration: + +- How the IGNY8 app distributes the WordPress plugin +- How site integrations are tracked in the database +- API endpoints that WordPress plugins call +- Admin interface for managing plugins and versions +- Frontend components for plugin download + +### What This Document Does NOT Cover + +- Internal plugin PHP code and functions (see `/plugins/wordpress/source/igny8-wp-bridge/docs/`) +- WordPress-side setup and configuration +- Plugin installation on WordPress sites + +--- + +## 2. Architecture + +### System Components + +``` +┌─────────────────────────────────────────────────────────────────────────┐ +│ IGNY8 App Server │ +├─────────────────────────────────────────────────────────────────────────┤ +│ │ +│ ┌──────────────────┐ ┌──────────────────┐ ┌──────────────────┐ │ +│ │ Django Backend │ │ PostgreSQL │ │ Plugin Files │ │ +│ │ │ │ │ │ │ │ +│ │ - API Views │◄──►│ - Plugin │ │ /plugins/ │ │ +│ │ - Admin │ │ - PluginVersion │ │ └─wordpress/ │ │ +│ │ - Signals │ │ - Installation │ │ ├─source/ │ │ +│ │ │ │ - Downloads │ │ └─dist/ │ │ +│ └────────┬─────────┘ └──────────────────┘ └────────┬─────────┘ │ +│ │ │ │ +│ │ API Routes: api.igny8.com │ │ +│ ▼ ▼ │ +│ ┌──────────────────────────────────────────────────────────────────┐ │ +│ │ API Endpoints │ │ +│ │ │ │ +│ │ /api/plugins/{slug}/download/ → Serve ZIP file │ │ +│ │ /api/plugins/{slug}/check-update/ → Return update info │ │ +│ │ /api/plugins/{slug}/info/ → Return plugin metadata │ │ +│ │ /api/plugins/{slug}/register/ → Register installation │ │ +│ │ /api/plugins/{slug}/health-check/ → Report plugin status │ │ +│ └──────────────────────────────────────────────────────────────────┘ │ +│ │ +└─────────────────────────────────────────────────────────────────────────┘ + │ + │ HTTPS + ▼ +┌─────────────────────────────────────────────────────────────────────────┐ +│ WordPress Sites │ +│ │ +│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │ +│ │ Site A │ │ Site B │ │ Site C │ │ Site N │ │ +│ │ v1.1.1 │ │ v1.1.0 │ │ v1.1.1 │ │ v1.0.0 │ │ +│ └─────────────┘ └─────────────┘ └─────────────┘ └─────────────┘ │ +│ │ +└─────────────────────────────────────────────────────────────────────────┘ +``` + +### URL Routing + +| Domain | Service | Purpose | +|--------|---------|---------| +| `app.igny8.com` | Frontend (Vite) | User interface | +| `api.igny8.com` | Backend (Django) | API endpoints | + +**Important**: WordPress plugins must call `api.igny8.com`, not `app.igny8.com`. + +--- + +## 3. Plugin Distribution System + +### Directory Structure + +``` +/data/app/igny8/ +├── plugins/ +│ └── wordpress/ +│ ├── source/ # Development source +│ │ └── igny8-wp-bridge/ +│ │ ├── igny8-bridge.php # Main plugin file +│ │ ├── includes/ # PHP classes +│ │ ├── admin/ # Admin interface +│ │ ├── sync/ # Sync functionality +│ │ ├── templates/ # Frontend templates +│ │ └── docs/ # Plugin-internal docs +│ └── dist/ # Distribution files +│ ├── igny8-wp-bridge-v1.1.1.zip # Version ZIP +│ ├── igny8-wp-bridge-v1.1.1.sha256 +│ └── igny8-wp-bridge-latest.zip # Symlink to latest +│ +└── backend/ + └── igny8_core/ + └── plugins/ # Django app + ├── models.py # Database models + ├── views.py # API endpoints + ├── signals.py # Auto-build triggers + ├── utils.py # ZIP creation utilities + ├── admin.py # Django admin config + └── urls.py # URL routing +``` + +### How ZIP Files Are Created + +#### Automatic Build (Recommended) + +When a `PluginVersion` status changes to `released` or `update_ready`, the system **automatically**: + +1. Reads source files from `/plugins/wordpress/source/igny8-wp-bridge/` +2. Updates version numbers in PHP files +3. Removes development files (tests, .git, __pycache__) +4. Creates ZIP in `/plugins/wordpress/dist/` +5. Calculates SHA256 checksum +6. Updates database with `file_path`, `file_size`, `checksum` +7. Updates `*-latest.zip` symlink + +This is handled by `signals.py`: + +```python +# backend/igny8_core/plugins/signals.py + +@receiver(pre_save, sender=PluginVersion) +def auto_build_plugin_on_release(sender, instance, **kwargs): + """Automatically build ZIP when version is released.""" + # Triggered on status change to 'released' or 'update_ready' +``` + +#### Manual Build (If Needed) + +```bash +# From project root +cd /data/app/igny8/plugins/wordpress/source + +# Create ZIP manually +zip -r ../dist/igny8-wp-bridge-v1.2.0.zip igny8-wp-bridge/ \ + -x "*.git*" -x "*__pycache__*" -x "*.DS_Store" -x "*tester*" + +# Update checksum +sha256sum ../dist/igny8-wp-bridge-v1.2.0.zip > ../dist/igny8-wp-bridge-v1.2.0.sha256 + +# Update symlink +ln -sf igny8-wp-bridge-v1.2.0.zip ../dist/igny8-wp-bridge-latest.zip +``` + +--- + +## 4. Database Models + +### Plugin + +Represents a plugin type (WordPress, Shopify, etc.). + +```python +class Plugin(models.Model): + name = models.CharField(max_length=100) # "IGNY8 WordPress Bridge" + slug = models.SlugField(unique=True) # "igny8-wp-bridge" + platform = models.CharField(...) # "wordpress", "shopify", "custom" + description = models.TextField() + homepage_url = models.URLField() + is_active = models.BooleanField(default=True) +``` + +**Current Records:** + +| ID | Name | Slug | Platform | +|----|------|------|----------| +| 1 | IGNY8 WordPress Bridge | igny8-wp-bridge | wordpress | + +### PluginVersion + +Tracks each version of a plugin. + +```python +class PluginVersion(models.Model): + plugin = models.ForeignKey(Plugin, ...) + version = models.CharField(max_length=20) # "1.1.1" + version_code = models.IntegerField() # 10101 (for comparison) + status = models.CharField(...) # "draft", "released", "update_ready" + + # File info (auto-populated on release) + file_path = models.CharField(...) # "igny8-wp-bridge-v1.1.1.zip" + file_size = models.IntegerField() # 167589 (bytes) + checksum = models.CharField(max_length=64) # SHA256 + + # Release info + changelog = models.TextField() + min_php_version = models.CharField(default='7.4') + released_at = models.DateTimeField(null=True) + force_update = models.BooleanField(default=False) +``` + +**Version Status Flow:** + +``` +draft → testing → staged → released → update_ready → deprecated + │ │ + │ └─ Notifies WP sites + └─ Available for download +``` + +### PluginInstallation + +Tracks where plugins are installed. + +```python +class PluginInstallation(models.Model): + site = models.ForeignKey('Site', ...) + plugin = models.ForeignKey(Plugin, ...) + current_version = models.ForeignKey(PluginVersion, ...) + is_active = models.BooleanField(default=True) + last_health_check = models.DateTimeField(null=True) + health_status = models.CharField(...) # "healthy", "outdated", "error" +``` + +### PluginDownload + +Tracks download analytics. + +```python +class PluginDownload(models.Model): + plugin = models.ForeignKey(Plugin, ...) + version = models.ForeignKey(PluginVersion, ...) + ip_address = models.GenericIPAddressField() + user_agent = models.CharField(max_length=500) + download_type = models.CharField(...) # "manual", "auto_update" + created_at = models.DateTimeField(auto_now_add=True) +``` + +--- + +## 5. API Endpoints + +### Public Endpoints (No Auth Required) + +#### Download Plugin + +``` +GET /api/plugins/{slug}/download/ +``` + +Returns the latest released ZIP file. + +**Response:** Binary ZIP file download + +**Example:** +```bash +curl -O https://api.igny8.com/api/plugins/igny8-wp-bridge/download/ +``` + +#### Check for Updates + +``` +GET /api/plugins/{slug}/check-update/?current_version=1.0.0 +``` + +Called by installed WordPress plugins to check for updates. + +**Response:** +```json +{ + "update_available": true, + "current_version": "1.0.0", + "latest_version": "1.1.1", + "latest_version_code": 10101, + "download_url": "https://api.igny8.com/api/plugins/igny8-wp-bridge/download/", + "changelog": "Bug fixes and improvements", + "info_url": "https://api.igny8.com/api/plugins/igny8-wp-bridge/info/", + "force_update": false, + "checksum": "6b9e231c07434df1dcfe81596b57f3571c30b6c2..." +} +``` + +#### Plugin Info + +``` +GET /api/plugins/{slug}/info/ +``` + +Returns plugin metadata for WordPress update modal. + +**Response:** +```json +{ + "name": "IGNY8 WordPress Bridge", + "slug": "igny8-wp-bridge", + "version": "1.1.1", + "author": "IGNY8", + "homepage": "https://igny8.com", + "description": "Lightweight bridge plugin...", + "changelog": "Bug fixes and improvements", + "download_url": "https://api.igny8.com/api/plugins/igny8-wp-bridge/download/", + "file_size": 167589, + "requires_php": "7.4", + "tested_wp": "6.7" +} +``` + +### Authenticated Endpoints + +#### Register Installation + +``` +POST /api/plugins/{slug}/register/ +Headers: Authorization: Api-Key {site_api_key} +``` + +Called when plugin is activated on a WordPress site. + +**Request Body:** +```json +{ + "site_id": 123, + "version": "1.1.1", + "wp_version": "6.7", + "php_version": "8.2" +} +``` + +#### Health Check + +``` +POST /api/plugins/{slug}/health-check/ +Headers: Authorization: Api-Key {site_api_key} +``` + +Periodic status report from installed plugins. + +**Request Body:** +```json +{ + "site_id": 123, + "version": "1.1.1", + "status": "active", + "last_sync": "2026-01-09T12:00:00Z" +} +``` + +--- + +## 6. Django Admin Management + +### Accessing Plugin Admin + +1. Go to: `https://api.igny8.com/backend/` +2. Login with admin credentials +3. Navigate to **Plugin Distribution** section + +### Admin Sections + +#### Plugins + +View and manage plugin registry. + +| Field | Description | +|-------|-------------| +| Name | Display name | +| Slug | URL identifier (cannot change after creation) | +| Platform | wordpress, shopify, custom | +| Is Active | Whether available for download | + +#### Plugin Versions + +Manage versions and trigger releases. + +| Field | Description | +|-------|-------------| +| Plugin | Parent plugin | +| Version | Semantic version (e.g., 1.1.1) | +| Status | draft, testing, staged, released, update_ready, deprecated | +| File Path | Auto-set on release | +| File Size | Auto-calculated on release | +| Checksum | Auto-calculated on release | +| Changelog | What's new (shown to users) | +| Force Update | Set true for critical security fixes | + +**To Release a New Version:** + +1. Create new PluginVersion record with status `draft` +2. Enter version number, changelog +3. Change status to `released` or `update_ready` +4. Save → ZIP is automatically built + +#### Plugin Installations + +View where plugins are installed. + +| Field | Description | +|-------|-------------| +| Site | IGNY8 site record | +| Plugin | Which plugin | +| Current Version | Installed version | +| Health Status | healthy, outdated, error | +| Last Health Check | Timestamp | + +#### Plugin Downloads + +View download analytics. + +| Field | Description | +|-------|-------------| +| Plugin | Which plugin | +| Version | Which version | +| Download Type | manual, auto_update | +| IP Address | Client IP | +| User Agent | Browser/client info | +| Created At | Timestamp | + +--- + +## 7. Frontend Integration + +### Download Button Location + +**Page:** Site Settings → Integrations → WordPress + +**Component:** `frontend/src/components/sites/WordPressIntegrationForm.tsx` + +### How Download Works + +```typescript +// WordPressIntegrationForm.tsx + +import { API_BASE_URL } from '../../services/api'; + +const handleDownloadPlugin = () => { + // Uses API_BASE_URL which resolves to https://api.igny8.com/api + const pluginUrl = `${API_BASE_URL}/plugins/igny8-wp-bridge/download/`; + window.open(pluginUrl, '_blank'); + toast.success('Plugin download started'); +}; +``` + +### Plugin Information Display + +The frontend shows: + +- Plugin name and version +- File size +- PHP requirements +- Download button +- Changelog (expandable) + +--- + +## 8. Site Integration Flow + +### Integration Creation Flow + +``` +┌─────────────────────────────────────────────────────────────────────────┐ +│ WordPress Site Setup │ +├─────────────────────────────────────────────────────────────────────────┤ +│ │ +│ 1. User downloads plugin from IGNY8 app │ +│ └─ GET /api/plugins/igny8-wp-bridge/download/ │ +│ │ +│ 2. User installs and activates plugin in WordPress │ +│ │ +│ 3. User enters Site ID and API Key in WP plugin settings │ +│ └─ These are generated in IGNY8 app │ +│ │ +│ 4. WP Plugin calls IGNY8 to register │ +│ └─ POST /api/plugins/igny8-wp-bridge/register/ │ +│ │ +│ 5. IGNY8 creates SiteIntegration record │ +│ └─ Links WordPress site to IGNY8 site │ +│ │ +│ 6. Integration is now active │ +│ └─ Content can be published from IGNY8 to WordPress │ +│ │ +└─────────────────────────────────────────────────────────────────────────┘ +``` + +### Integration Data Model + +```python +# SiteIntegration model (in auth/models.py) + +class SiteIntegration(models.Model): + site = models.ForeignKey(Site, ...) # IGNY8 site + platform = models.CharField(...) # "wordpress" + external_site_url = models.URLField() # WordPress URL + api_key = models.CharField(...) # Encrypted key + sync_enabled = models.BooleanField() + last_sync = models.DateTimeField() + connection_status = models.CharField(...) # "connected", "error" +``` + +--- + +## 9. Troubleshooting + +### Common Issues + +#### "404 Not Found" on Download + +**Cause:** Using wrong domain (app.igny8.com instead of api.igny8.com) + +**Solution:** Ensure URL is `https://api.igny8.com/api/plugins/igny8-wp-bridge/download/` + +#### "Plugin file not found" Error + +**Cause:** ZIP file doesn't exist in dist/ directory + +**Solution:** +```bash +# Check if file exists +ls -la /data/app/igny8/plugins/wordpress/dist/ + +# If missing, rebuild manually or change version status to trigger auto-build +``` + +#### Updates Not Showing in WordPress + +**Cause 1:** WordPress caches plugin update checks + +**Solution:** In WordPress admin, go to Dashboard → Updates → Check Again + +**Cause 2:** Version status is not `released` or `update_ready` + +**Solution:** Check PluginVersion status in Django admin + +#### Wrong File Size/Checksum + +**Cause:** Database values don't match actual file + +**Solution:** +```bash +# Get actual values +stat -c %s /data/app/igny8/plugins/wordpress/dist/igny8-wp-bridge-v1.1.1.zip +sha256sum /data/app/igny8/plugins/wordpress/dist/igny8-wp-bridge-v1.1.1.zip + +# Update in Django admin or shell +``` + +### Verification Commands + +```bash +# Test download endpoint +curl -I https://api.igny8.com/api/plugins/igny8-wp-bridge/download/ + +# Test check-update endpoint +curl "https://api.igny8.com/api/plugins/igny8-wp-bridge/check-update/?current_version=1.0.0" + +# Test info endpoint +curl https://api.igny8.com/api/plugins/igny8-wp-bridge/info/ + +# Check ZIP contents +unzip -l /data/app/igny8/plugins/wordpress/dist/igny8-wp-bridge-v1.1.1.zip + +# Verify API URL in plugin +unzip -p /data/app/igny8/plugins/wordpress/dist/igny8-wp-bridge-v1.1.1.zip \ + igny8-wp-bridge/igny8-bridge.php | grep "api.igny8.com" +``` + +### Log Locations + +| Log | Location | +|-----|----------| +| Django logs | `/data/app/logs/django.log` | +| Plugin build logs | Check Django log for `"Created plugin ZIP"` messages | +| Download tracking | `PluginDownload` model in database | + +--- + +## Related Documentation + +- [PLUGIN-UPDATE-WORKFLOW.md](PLUGIN-UPDATE-WORKFLOW.md) - Post-update checklist +- `/plugins/wordpress/source/igny8-wp-bridge/docs/` - Plugin internal documentation +- [docs/plans/PLUGIN-DISTRIBUTION-SYSTEM.md](/docs/plans/PLUGIN-DISTRIBUTION-SYSTEM.md) - Original implementation plan diff --git a/docs/INDEX.md b/docs/INDEX.md index 40785465..c9d186b3 100644 --- a/docs/INDEX.md +++ b/docs/INDEX.md @@ -19,6 +19,8 @@ | **Read design guide** | [30-FRONTEND/DESIGN-GUIDE.md](30-FRONTEND/DESIGN-GUIDE.md) | | Understand frontend structure | [30-FRONTEND/PAGES.md](30-FRONTEND/PAGES.md) | | Trace a workflow end-to-end | [40-WORKFLOWS/](#workflows) | +| **Manage WordPress plugin** | [60-PLUGINS/WORDPRESS-INTEGRATION.md](60-PLUGINS/WORDPRESS-INTEGRATION.md) | +| **Release plugin update** | [60-PLUGINS/PLUGIN-UPDATE-WORKFLOW.md](60-PLUGINS/PLUGIN-UPDATE-WORKFLOW.md) | | Look up model fields | [90-REFERENCE/MODELS.md](90-REFERENCE/MODELS.md) | | **Payment system (Stripe/PayPal/Bank)** | [90-REFERENCE/PAYMENT-SYSTEM.md](90-REFERENCE/PAYMENT-SYSTEM.md) | | See prelaunch checklist | [plans/FINAL-PRELAUNCH.md](plans/FINAL-PRELAUNCH.md) | @@ -133,6 +135,22 @@ --- +## 60-PLUGINS - Plugin Distribution & Management + +| Document | Purpose | +|----------|---------| +| [INDEX.md](60-PLUGINS/INDEX.md) | Plugin management overview | +| [WORDPRESS-INTEGRATION.md](60-PLUGINS/WORDPRESS-INTEGRATION.md) | Complete WordPress integration guide (app-side) | +| [PLUGIN-UPDATE-WORKFLOW.md](60-PLUGINS/PLUGIN-UPDATE-WORKFLOW.md) | How to release updates, verification checklist | + +**Key Points:** +- Plugin source: `/plugins/wordpress/source/igny8-wp-bridge/` +- Distribution: `/plugins/wordpress/dist/` +- Django app: `/backend/igny8_core/plugins/` +- API domain: `api.igny8.com` (not app.igny8.com) + +--- + ## 90-REFERENCE - Quick Lookup | Document | Purpose | diff --git a/frontend/src/components/sites/WordPressIntegrationForm.tsx b/frontend/src/components/sites/WordPressIntegrationForm.tsx index a4072fa5..9e80d400 100644 --- a/frontend/src/components/sites/WordPressIntegrationForm.tsx +++ b/frontend/src/components/sites/WordPressIntegrationForm.tsx @@ -12,7 +12,7 @@ import Checkbox from '../form/input/Checkbox'; import Switch from '../form/switch/Switch'; import { useToast } from '../ui/toast/ToastContainer'; import { integrationApi, SiteIntegration } from '../../services/integration.api'; -import { fetchAPI } from '../../services/api'; +import { fetchAPI, API_BASE_URL } from '../../services/api'; import { CheckCircleIcon, AlertIcon, @@ -171,8 +171,8 @@ export default function WordPressIntegrationForm({ }; const handleDownloadPlugin = () => { - // Use the backend API endpoint for plugin download - const pluginUrl = `/api/plugins/igny8-wp-bridge/download/`; + // Use the backend API endpoint for plugin download (must use full API URL, not relative) + const pluginUrl = `${API_BASE_URL}/plugins/igny8-wp-bridge/download/`; window.open(pluginUrl, '_blank'); toast.success('Plugin download started'); }; diff --git a/igny8-wp-bridge.zip b/igny8-wp-bridge.zip deleted file mode 100644 index 9139c7fa..00000000 Binary files a/igny8-wp-bridge.zip and /dev/null differ diff --git a/igny8-wp-bridge/tester b/igny8-wp-bridge/tester deleted file mode 100644 index bf7e8990..00000000 --- a/igny8-wp-bridge/tester +++ /dev/null @@ -1 +0,0 @@ -asdasd diff --git a/igny8-wp-bridge/admin/assets/css/admin.css b/plugins/wordpress/source/igny8-wp-bridge/admin/assets/css/admin.css similarity index 100% rename from igny8-wp-bridge/admin/assets/css/admin.css rename to plugins/wordpress/source/igny8-wp-bridge/admin/assets/css/admin.css diff --git a/igny8-wp-bridge/admin/assets/js/admin.js b/plugins/wordpress/source/igny8-wp-bridge/admin/assets/js/admin.js similarity index 100% rename from igny8-wp-bridge/admin/assets/js/admin.js rename to plugins/wordpress/source/igny8-wp-bridge/admin/assets/js/admin.js diff --git a/igny8-wp-bridge/admin/assets/js/post-editor.js b/plugins/wordpress/source/igny8-wp-bridge/admin/assets/js/post-editor.js similarity index 100% rename from igny8-wp-bridge/admin/assets/js/post-editor.js rename to plugins/wordpress/source/igny8-wp-bridge/admin/assets/js/post-editor.js diff --git a/igny8-wp-bridge/admin/class-admin-columns.php b/plugins/wordpress/source/igny8-wp-bridge/admin/class-admin-columns.php similarity index 100% rename from igny8-wp-bridge/admin/class-admin-columns.php rename to plugins/wordpress/source/igny8-wp-bridge/admin/class-admin-columns.php diff --git a/igny8-wp-bridge/admin/class-admin.php b/plugins/wordpress/source/igny8-wp-bridge/admin/class-admin.php similarity index 100% rename from igny8-wp-bridge/admin/class-admin.php rename to plugins/wordpress/source/igny8-wp-bridge/admin/class-admin.php diff --git a/igny8-wp-bridge/admin/class-post-meta-boxes.php b/plugins/wordpress/source/igny8-wp-bridge/admin/class-post-meta-boxes.php similarity index 100% rename from igny8-wp-bridge/admin/class-post-meta-boxes.php rename to plugins/wordpress/source/igny8-wp-bridge/admin/class-post-meta-boxes.php diff --git a/igny8-wp-bridge/admin/class-post-meta-boxes.php.bak b/plugins/wordpress/source/igny8-wp-bridge/admin/class-post-meta-boxes.php.bak similarity index 100% rename from igny8-wp-bridge/admin/class-post-meta-boxes.php.bak rename to plugins/wordpress/source/igny8-wp-bridge/admin/class-post-meta-boxes.php.bak diff --git a/igny8-wp-bridge/admin/settings.php b/plugins/wordpress/source/igny8-wp-bridge/admin/settings.php similarity index 100% rename from igny8-wp-bridge/admin/settings.php rename to plugins/wordpress/source/igny8-wp-bridge/admin/settings.php diff --git a/igny8-wp-bridge/data/link-graph.php b/plugins/wordpress/source/igny8-wp-bridge/data/link-graph.php similarity index 100% rename from igny8-wp-bridge/data/link-graph.php rename to plugins/wordpress/source/igny8-wp-bridge/data/link-graph.php diff --git a/igny8-wp-bridge/data/semantic-mapping.php b/plugins/wordpress/source/igny8-wp-bridge/data/semantic-mapping.php similarity index 100% rename from igny8-wp-bridge/data/semantic-mapping.php rename to plugins/wordpress/source/igny8-wp-bridge/data/semantic-mapping.php diff --git a/igny8-wp-bridge/data/site-collection.php b/plugins/wordpress/source/igny8-wp-bridge/data/site-collection.php similarity index 100% rename from igny8-wp-bridge/data/site-collection.php rename to plugins/wordpress/source/igny8-wp-bridge/data/site-collection.php diff --git a/igny8-wp-bridge/data/woocommerce.php b/plugins/wordpress/source/igny8-wp-bridge/data/woocommerce.php similarity index 100% rename from igny8-wp-bridge/data/woocommerce.php rename to plugins/wordpress/source/igny8-wp-bridge/data/woocommerce.php diff --git a/igny8-wp-bridge/docs/ACTIONABLE-IMPLEMENTATION-PLAN.md b/plugins/wordpress/source/igny8-wp-bridge/docs/ACTIONABLE-IMPLEMENTATION-PLAN.md similarity index 100% rename from igny8-wp-bridge/docs/ACTIONABLE-IMPLEMENTATION-PLAN.md rename to plugins/wordpress/source/igny8-wp-bridge/docs/ACTIONABLE-IMPLEMENTATION-PLAN.md diff --git a/igny8-wp-bridge/docs/AUTHENTICATION-AUDIT.md b/plugins/wordpress/source/igny8-wp-bridge/docs/AUTHENTICATION-AUDIT.md similarity index 100% rename from igny8-wp-bridge/docs/AUTHENTICATION-AUDIT.md rename to plugins/wordpress/source/igny8-wp-bridge/docs/AUTHENTICATION-AUDIT.md diff --git a/igny8-wp-bridge/docs/COMPLETE-PUBLICATION-AUDIT.md b/plugins/wordpress/source/igny8-wp-bridge/docs/COMPLETE-PUBLICATION-AUDIT.md similarity index 100% rename from igny8-wp-bridge/docs/COMPLETE-PUBLICATION-AUDIT.md rename to plugins/wordpress/source/igny8-wp-bridge/docs/COMPLETE-PUBLICATION-AUDIT.md diff --git a/igny8-wp-bridge/docs/DEBUGGING-GUIDE-2025-12-01.md b/plugins/wordpress/source/igny8-wp-bridge/docs/DEBUGGING-GUIDE-2025-12-01.md similarity index 100% rename from igny8-wp-bridge/docs/DEBUGGING-GUIDE-2025-12-01.md rename to plugins/wordpress/source/igny8-wp-bridge/docs/DEBUGGING-GUIDE-2025-12-01.md diff --git a/igny8-wp-bridge/docs/FIXES-APPLIED-2025-11-30.md b/plugins/wordpress/source/igny8-wp-bridge/docs/FIXES-APPLIED-2025-11-30.md similarity index 100% rename from igny8-wp-bridge/docs/FIXES-APPLIED-2025-11-30.md rename to plugins/wordpress/source/igny8-wp-bridge/docs/FIXES-APPLIED-2025-11-30.md diff --git a/igny8-wp-bridge/docs/FIXES-APPLIED-2025-12-01.md b/plugins/wordpress/source/igny8-wp-bridge/docs/FIXES-APPLIED-2025-12-01.md similarity index 100% rename from igny8-wp-bridge/docs/FIXES-APPLIED-2025-12-01.md rename to plugins/wordpress/source/igny8-wp-bridge/docs/FIXES-APPLIED-2025-12-01.md diff --git a/igny8-wp-bridge/docs/FIXES-APPLIED-CONTENT-PUBLISHING.md b/plugins/wordpress/source/igny8-wp-bridge/docs/FIXES-APPLIED-CONTENT-PUBLISHING.md similarity index 100% rename from igny8-wp-bridge/docs/FIXES-APPLIED-CONTENT-PUBLISHING.md rename to plugins/wordpress/source/igny8-wp-bridge/docs/FIXES-APPLIED-CONTENT-PUBLISHING.md diff --git a/igny8-wp-bridge/docs/FIXES-PUBLISH-FAILURE.md b/plugins/wordpress/source/igny8-wp-bridge/docs/FIXES-PUBLISH-FAILURE.md similarity index 100% rename from igny8-wp-bridge/docs/FIXES-PUBLISH-FAILURE.md rename to plugins/wordpress/source/igny8-wp-bridge/docs/FIXES-PUBLISH-FAILURE.md diff --git a/igny8-wp-bridge/docs/Plan-based-on-audit.md b/plugins/wordpress/source/igny8-wp-bridge/docs/Plan-based-on-audit.md similarity index 100% rename from igny8-wp-bridge/docs/Plan-based-on-audit.md rename to plugins/wordpress/source/igny8-wp-bridge/docs/Plan-based-on-audit.md diff --git a/igny8-wp-bridge/docs/SYNC-DATA-FLOW-DIAGRAM.md b/plugins/wordpress/source/igny8-wp-bridge/docs/SYNC-DATA-FLOW-DIAGRAM.md similarity index 100% rename from igny8-wp-bridge/docs/SYNC-DATA-FLOW-DIAGRAM.md rename to plugins/wordpress/source/igny8-wp-bridge/docs/SYNC-DATA-FLOW-DIAGRAM.md diff --git a/igny8-wp-bridge/docs/WORDPRESS-PLUGIN-INTEGRATION.md b/plugins/wordpress/source/igny8-wp-bridge/docs/WORDPRESS-PLUGIN-INTEGRATION.md similarity index 100% rename from igny8-wp-bridge/docs/WORDPRESS-PLUGIN-INTEGRATION.md rename to plugins/wordpress/source/igny8-wp-bridge/docs/WORDPRESS-PLUGIN-INTEGRATION.md diff --git a/igny8-wp-bridge/igny8-bridge.php b/plugins/wordpress/source/igny8-wp-bridge/igny8-bridge.php similarity index 93% rename from igny8-wp-bridge/igny8-bridge.php rename to plugins/wordpress/source/igny8-wp-bridge/igny8-bridge.php index 35ccd1be..513a385c 100644 --- a/igny8-wp-bridge/igny8-bridge.php +++ b/plugins/wordpress/source/igny8-wp-bridge/igny8-bridge.php @@ -85,6 +85,7 @@ class Igny8Bridge { require_once IGNY8_BRIDGE_PLUGIN_DIR . 'includes/class-igny8-api.php'; require_once IGNY8_BRIDGE_PLUGIN_DIR . 'includes/class-igny8-site.php'; require_once IGNY8_BRIDGE_PLUGIN_DIR . 'includes/class-igny8-rest-api.php'; + require_once IGNY8_BRIDGE_PLUGIN_DIR . 'includes/class-igny8-updater.php'; // Webhooks removed - using API key authentication only // Webhook logs (used in admin and frontend) @@ -135,6 +136,15 @@ class Igny8Bridge { if (is_admin()) { Igny8Admin::get_instance(); } + + // Initialize auto-updater + if (class_exists('Igny8_Updater')) { + new Igny8_Updater( + IGNY8_BRIDGE_PLUGIN_FILE, + IGNY8_BRIDGE_VERSION, + 'https://api.igny8.com/api/plugins/' + ); + } } /** diff --git a/igny8-wp-bridge/includes/class-igny8-api.php b/plugins/wordpress/source/igny8-wp-bridge/includes/class-igny8-api.php similarity index 100% rename from igny8-wp-bridge/includes/class-igny8-api.php rename to plugins/wordpress/source/igny8-wp-bridge/includes/class-igny8-api.php diff --git a/igny8-wp-bridge/includes/class-igny8-link-queue.php b/plugins/wordpress/source/igny8-wp-bridge/includes/class-igny8-link-queue.php similarity index 100% rename from igny8-wp-bridge/includes/class-igny8-link-queue.php rename to plugins/wordpress/source/igny8-wp-bridge/includes/class-igny8-link-queue.php diff --git a/igny8-wp-bridge/includes/class-igny8-logger.php b/plugins/wordpress/source/igny8-wp-bridge/includes/class-igny8-logger.php similarity index 100% rename from igny8-wp-bridge/includes/class-igny8-logger.php rename to plugins/wordpress/source/igny8-wp-bridge/includes/class-igny8-logger.php diff --git a/igny8-wp-bridge/includes/class-igny8-rest-api.php b/plugins/wordpress/source/igny8-wp-bridge/includes/class-igny8-rest-api.php similarity index 100% rename from igny8-wp-bridge/includes/class-igny8-rest-api.php rename to plugins/wordpress/source/igny8-wp-bridge/includes/class-igny8-rest-api.php diff --git a/igny8-wp-bridge/includes/class-igny8-site.php b/plugins/wordpress/source/igny8-wp-bridge/includes/class-igny8-site.php similarity index 100% rename from igny8-wp-bridge/includes/class-igny8-site.php rename to plugins/wordpress/source/igny8-wp-bridge/includes/class-igny8-site.php diff --git a/igny8-wp-bridge/includes/class-igny8-template-loader.php b/plugins/wordpress/source/igny8-wp-bridge/includes/class-igny8-template-loader.php similarity index 100% rename from igny8-wp-bridge/includes/class-igny8-template-loader.php rename to plugins/wordpress/source/igny8-wp-bridge/includes/class-igny8-template-loader.php diff --git a/plugins/wordpress/source/igny8-wp-bridge/includes/class-igny8-updater.php b/plugins/wordpress/source/igny8-wp-bridge/includes/class-igny8-updater.php new file mode 100644 index 00000000..77c1c79c --- /dev/null +++ b/plugins/wordpress/source/igny8-wp-bridge/includes/class-igny8-updater.php @@ -0,0 +1,197 @@ +plugin_file = $plugin_file; + $this->version = $version; + $this->api_url = trailingslashit($api_url); + + // Hook into WordPress update system + add_filter('pre_set_site_transient_update_plugins', array($this, 'check_for_updates')); + add_filter('plugins_api', array($this, 'plugin_info'), 10, 3); + } + + /** + * Check for plugin updates + * + * @param object $transient Update transient + * @return object Modified transient + */ + public function check_for_updates($transient) { + if (empty($transient->checked)) { + return $transient; + } + + // Get update info from IGNY8 API + $update_info = $this->get_update_info(); + + if (!$update_info || !isset($update_info['update_available'])) { + return $transient; + } + + if ($update_info['update_available'] === true) { + $plugin_basename = plugin_basename($this->plugin_file); + + $transient->response[$plugin_basename] = (object) array( + 'slug' => $this->plugin_slug, + 'new_version' => $update_info['latest_version'], + 'package' => $update_info['download_url'], + 'url' => $update_info['info_url'] ?? '', + 'tested' => $update_info['tested'] ?? '', + 'requires_php' => $update_info['requires_php'] ?? '', + ); + } + + return $transient; + } + + /** + * Provide plugin information for update screen + * + * @param false|object|array $result The result object or array + * @param string $action The type of information being requested + * @param object $args Plugin API arguments + * @return false|object Modified result + */ + public function plugin_info($result, $action, $args) { + if ($action !== 'plugin_information') { + return $result; + } + + if (!isset($args->slug) || $args->slug !== $this->plugin_slug) { + return $result; + } + + // Get plugin info from IGNY8 API + $info = $this->get_plugin_info(); + + if (!$info) { + return $result; + } + + return (object) array( + 'name' => $info['name'] ?? 'IGNY8 WordPress Bridge', + 'slug' => $this->plugin_slug, + 'version' => $info['version'] ?? $this->version, + 'author' => 'IGNY8', + 'author_profile' => 'https://igny8.com', + 'homepage' => 'https://igny8.com', + 'sections' => array( + 'description' => $info['description'] ?? '', + 'changelog' => $info['changelog'] ?? '', + ), + 'download_link' => $info['download_url'] ?? '', + 'tested' => $info['tested'] ?? '', + 'requires_php' => $info['requires_php'] ?? '7.4', + ); + } + + /** + * Get update information from IGNY8 API + * + * @return array|false Update info or false on failure + */ + private function get_update_info() { + $url = $this->api_url . $this->plugin_slug . '/check-update/'; + + $response = wp_remote_get($url, array( + 'timeout' => 10, + 'headers' => array( + 'X-IGNY8-Site-ID' => get_option('igny8_site_id'), + 'X-IGNY8-API-Key' => get_option('igny8_api_key'), + ), + 'body' => array( + 'current_version' => $this->version, + ), + )); + + if (is_wp_error($response)) { + return false; + } + + $code = wp_remote_retrieve_response_code($response); + if ($code !== 200) { + return false; + } + + $body = wp_remote_retrieve_body($response); + $data = json_decode($body, true); + + return $data ?: false; + } + + /** + * Get plugin information from IGNY8 API + * + * @return array|false Plugin info or false on failure + */ + private function get_plugin_info() { + $url = $this->api_url . $this->plugin_slug . '/info/'; + + $response = wp_remote_get($url, array( + 'timeout' => 10, + )); + + if (is_wp_error($response)) { + return false; + } + + $code = wp_remote_retrieve_response_code($response); + if ($code !== 200) { + return false; + } + + $body = wp_remote_retrieve_body($response); + $data = json_decode($body, true); + + return $data ?: false; + } +} diff --git a/igny8-wp-bridge/includes/class-igny8-webhook-logs.php b/plugins/wordpress/source/igny8-wp-bridge/includes/class-igny8-webhook-logs.php similarity index 100% rename from igny8-wp-bridge/includes/class-igny8-webhook-logs.php rename to plugins/wordpress/source/igny8-wp-bridge/includes/class-igny8-webhook-logs.php diff --git a/igny8-wp-bridge/includes/class-igny8-webhooks.php b/plugins/wordpress/source/igny8-wp-bridge/includes/class-igny8-webhooks.php similarity index 100% rename from igny8-wp-bridge/includes/class-igny8-webhooks.php rename to plugins/wordpress/source/igny8-wp-bridge/includes/class-igny8-webhooks.php diff --git a/igny8-wp-bridge/includes/functions.php b/plugins/wordpress/source/igny8-wp-bridge/includes/functions.php similarity index 100% rename from igny8-wp-bridge/includes/functions.php rename to plugins/wordpress/source/igny8-wp-bridge/includes/functions.php diff --git a/igny8-wp-bridge/includes/template-functions.php b/plugins/wordpress/source/igny8-wp-bridge/includes/template-functions.php similarity index 100% rename from igny8-wp-bridge/includes/template-functions.php rename to plugins/wordpress/source/igny8-wp-bridge/includes/template-functions.php diff --git a/igny8-wp-bridge/languages/igny8-bridge.pot b/plugins/wordpress/source/igny8-wp-bridge/languages/igny8-bridge.pot similarity index 100% rename from igny8-wp-bridge/languages/igny8-bridge.pot rename to plugins/wordpress/source/igny8-wp-bridge/languages/igny8-bridge.pot diff --git a/igny8-wp-bridge/sync/hooks.php b/plugins/wordpress/source/igny8-wp-bridge/sync/hooks.php similarity index 100% rename from igny8-wp-bridge/sync/hooks.php rename to plugins/wordpress/source/igny8-wp-bridge/sync/hooks.php diff --git a/igny8-wp-bridge/sync/igny8-to-wp.php b/plugins/wordpress/source/igny8-wp-bridge/sync/igny8-to-wp.php similarity index 100% rename from igny8-wp-bridge/sync/igny8-to-wp.php rename to plugins/wordpress/source/igny8-wp-bridge/sync/igny8-to-wp.php diff --git a/igny8-wp-bridge/sync/post-sync.php b/plugins/wordpress/source/igny8-wp-bridge/sync/post-sync.php similarity index 100% rename from igny8-wp-bridge/sync/post-sync.php rename to plugins/wordpress/source/igny8-wp-bridge/sync/post-sync.php diff --git a/igny8-wp-bridge/sync/taxonomy-sync.php b/plugins/wordpress/source/igny8-wp-bridge/sync/taxonomy-sync.php similarity index 100% rename from igny8-wp-bridge/sync/taxonomy-sync.php rename to plugins/wordpress/source/igny8-wp-bridge/sync/taxonomy-sync.php diff --git a/igny8-wp-bridge/templates/README.md b/plugins/wordpress/source/igny8-wp-bridge/templates/README.md similarity index 100% rename from igny8-wp-bridge/templates/README.md rename to plugins/wordpress/source/igny8-wp-bridge/templates/README.md diff --git a/igny8-wp-bridge/templates/assets/css/igny8-content-template.css b/plugins/wordpress/source/igny8-wp-bridge/templates/assets/css/igny8-content-template.css similarity index 100% rename from igny8-wp-bridge/templates/assets/css/igny8-content-template.css rename to plugins/wordpress/source/igny8-wp-bridge/templates/assets/css/igny8-content-template.css diff --git a/igny8-wp-bridge/templates/assets/js/igny8-content-template.js b/plugins/wordpress/source/igny8-wp-bridge/templates/assets/js/igny8-content-template.js similarity index 100% rename from igny8-wp-bridge/templates/assets/js/igny8-content-template.js rename to plugins/wordpress/source/igny8-wp-bridge/templates/assets/js/igny8-content-template.js diff --git a/igny8-wp-bridge/templates/parts/igny8-content-sections.php b/plugins/wordpress/source/igny8-wp-bridge/templates/parts/igny8-content-sections.php similarity index 100% rename from igny8-wp-bridge/templates/parts/igny8-content-sections.php rename to plugins/wordpress/source/igny8-wp-bridge/templates/parts/igny8-content-sections.php diff --git a/igny8-wp-bridge/templates/parts/igny8-featured-image.php b/plugins/wordpress/source/igny8-wp-bridge/templates/parts/igny8-featured-image.php similarity index 100% rename from igny8-wp-bridge/templates/parts/igny8-featured-image.php rename to plugins/wordpress/source/igny8-wp-bridge/templates/parts/igny8-featured-image.php diff --git a/igny8-wp-bridge/templates/parts/igny8-header.php b/plugins/wordpress/source/igny8-wp-bridge/templates/parts/igny8-header.php similarity index 100% rename from igny8-wp-bridge/templates/parts/igny8-header.php rename to plugins/wordpress/source/igny8-wp-bridge/templates/parts/igny8-header.php diff --git a/igny8-wp-bridge/templates/parts/igny8-metadata.php b/plugins/wordpress/source/igny8-wp-bridge/templates/parts/igny8-metadata.php similarity index 100% rename from igny8-wp-bridge/templates/parts/igny8-metadata.php rename to plugins/wordpress/source/igny8-wp-bridge/templates/parts/igny8-metadata.php diff --git a/igny8-wp-bridge/templates/single-igny8-content.php b/plugins/wordpress/source/igny8-wp-bridge/templates/single-igny8-content.php similarity index 100% rename from igny8-wp-bridge/templates/single-igny8-content.php rename to plugins/wordpress/source/igny8-wp-bridge/templates/single-igny8-content.php diff --git a/igny8-wp-bridge/tests/test-api-authentication.php b/plugins/wordpress/source/igny8-wp-bridge/tests/test-api-authentication.php similarity index 100% rename from igny8-wp-bridge/tests/test-api-authentication.php rename to plugins/wordpress/source/igny8-wp-bridge/tests/test-api-authentication.php diff --git a/igny8-wp-bridge/tests/test-revoke-api-key.php b/plugins/wordpress/source/igny8-wp-bridge/tests/test-revoke-api-key.php similarity index 100% rename from igny8-wp-bridge/tests/test-revoke-api-key.php rename to plugins/wordpress/source/igny8-wp-bridge/tests/test-revoke-api-key.php diff --git a/igny8-wp-bridge/tests/test-site-metadata.php b/plugins/wordpress/source/igny8-wp-bridge/tests/test-site-metadata.php similarity index 100% rename from igny8-wp-bridge/tests/test-site-metadata.php rename to plugins/wordpress/source/igny8-wp-bridge/tests/test-site-metadata.php diff --git a/igny8-wp-bridge/tests/test-sync-structure.php b/plugins/wordpress/source/igny8-wp-bridge/tests/test-sync-structure.php similarity index 100% rename from igny8-wp-bridge/tests/test-sync-structure.php rename to plugins/wordpress/source/igny8-wp-bridge/tests/test-sync-structure.php diff --git a/igny8-wp-bridge/uninstall.php b/plugins/wordpress/source/igny8-wp-bridge/uninstall.php similarity index 100% rename from igny8-wp-bridge/uninstall.php rename to plugins/wordpress/source/igny8-wp-bridge/uninstall.php