versioning and wp plugin updates

This commit is contained in:
IGNY8 VPS (Salman)
2026-01-10 00:26:00 +00:00
parent 0ea3a30909
commit a86524a6b1
24 changed files with 2011 additions and 101 deletions

View File

@@ -178,17 +178,12 @@ class PluginVersionAdmin(ModelAdmin):
def release_versions(self, request, queryset):
from django.utils import timezone
count = 0
for version in queryset.filter(status__in=['draft', 'testing', 'staged']):
for version in queryset.filter(status='draft'):
version.status = 'released'
version.save() # Triggers signal to build ZIP
count += 1
self.message_user(request, f"Released {count} version(s). ZIP files are being built automatically.")
@admin.action(description="📢 Mark as update ready (notify WordPress sites)")
def mark_as_update_ready(self, request, queryset):
count = queryset.filter(status='released').update(status='update_ready')
self.message_user(request, f"Marked {count} version(s) as update ready. WordPress sites will be notified.")
@admin.action(description="🗑️ Mark as deprecated")
def mark_as_deprecated(self, request, queryset):
count = queryset.update(status='deprecated')

View File

@@ -0,0 +1,18 @@
# Generated by Django 5.2.10 on 2026-01-09 23:50
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('plugins', '0002_allow_blank_autogenerated_fields'),
]
operations = [
migrations.AlterField(
model_name='pluginversion',
name='status',
field=models.CharField(choices=[('draft', 'Draft'), ('released', 'Released'), ('deprecated', 'Deprecated')], db_index=True, default='draft', help_text='Release status of this version', max_length=20),
),
]

View File

@@ -64,7 +64,7 @@ class Plugin(models.Model):
def get_latest_version(self):
"""Get the latest released version of this plugin."""
return self.versions.filter(
status__in=['released', 'update_ready']
status='released'
).first()
def get_download_count(self):
@@ -80,11 +80,8 @@ class PluginVersion(models.Model):
"""
STATUS_CHOICES = [
('draft', 'Draft'), # In development
('testing', 'Testing'), # Internal testing
('staged', 'Staged'), # Ready, not yet pushed
('released', 'Released'), # Available for download
('update_ready', 'Update Ready'), # Push to installed sites
('draft', 'Draft'), # In development - NOT available for download
('released', 'Released'), # Available for download and updates
('deprecated', 'Deprecated'), # Old version, not recommended
]

View File

@@ -17,7 +17,7 @@ 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.
Automatically build ZIP package when a version is marked as released.
This ensures:
1. ZIP file is always up-to-date with source code
@@ -25,36 +25,32 @@ def auto_build_plugin_on_release(sender, instance, **kwargs):
3. No manual intervention needed for releases
Triggers on:
- New version created with status 'released' or 'update_ready'
- Existing version status changed to 'released' or 'update_ready'
"""
release_statuses = ['released', 'update_ready']
- New version created with status 'released'
- Existing version status changed to 'released'
# Check if this version should have a ZIP built
Note: Only 'released' status makes the version available for download.
"""
# Only build ZIP when status is 'released'
should_build = False
if not instance.pk:
# New instance - build if status is a release status
if instance.status in release_statuses:
# New instance - build if status is released
if instance.status == 'released':
should_build = True
logger.info(f"New plugin version {instance.plugin.slug} v{instance.version} created with status '{instance.status}' - building ZIP")
logger.info(f"New plugin version {instance.plugin.slug} v{instance.version} created with status 'released' - building ZIP")
else:
# Existing instance - check if status changed to a release status
# Existing instance - check if status changed to released
try:
old_instance = PluginVersion.objects.get(pk=instance.pk)
old_status = old_instance.status
new_status = instance.status
# Build if moving to a release status from a non-release status
if new_status in release_statuses and old_status not in release_statuses:
# Build if moving to released from any other status
if new_status == 'released' and old_status != 'released':
should_build = True
logger.info(f"Building plugin ZIP for {instance.plugin.slug} v{instance.version} (status: {old_status} -> {new_status})")
elif old_status == new_status and new_status in release_statuses:
# No status change, but already released - no rebuild
return
elif old_status in release_statuses and new_status in release_statuses:
# Moving between release statuses - no rebuild
logger.info(f"Plugin {instance.plugin.slug} v{instance.version}: Status changing from {old_status} to {new_status}, no rebuild needed")
logger.info(f"Building plugin ZIP for {instance.plugin.slug} v{instance.version} (status: {old_status} -> released)")
elif old_status == 'released' and new_status == 'released':
# No status change, already released - no rebuild
return
except PluginVersion.DoesNotExist:
return

View File

@@ -190,6 +190,9 @@ def create_plugin_zip(
'**/tests',
'**/tester',
'**/.DS_Store',
'**/*.bak',
'**/*.tmp',
'**/*.log',
]
for pattern in patterns_to_remove:

View File

@@ -1,53 +0,0 @@
<svg width="231" height="48" viewBox="0 0 231 48" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M0.425781 12.6316C0.425781 5.65535 6.08113 0 13.0574 0H35.7942C42.7704 0 48.4258 5.65535 48.4258 12.6316V35.3684C48.4258 42.3446 42.7704 48 35.7942 48H13.0574C6.08113 48 0.425781 42.3446 0.425781 35.3684V12.6316Z" fill="#465FFF"/>
<g filter="url(#filter0_d_3903_56743)">
<path d="M13.0615 12.6323C13.0615 11.237 14.1926 10.106 15.5878 10.106C16.9831 10.106 18.1142 11.237 18.1142 12.6323V35.3691C18.1142 36.7644 16.9831 37.8954 15.5878 37.8954C14.1926 37.8954 13.0615 36.7644 13.0615 35.3691V12.6323Z" fill="white"/>
</g>
<g filter="url(#filter1_d_3903_56743)">
<path d="M22.5391 22.7353C22.5391 21.3401 23.6701 20.209 25.0654 20.209C26.4606 20.209 27.5917 21.3401 27.5917 22.7353V35.3669C27.5917 36.7621 26.4606 37.8932 25.0654 37.8932C23.6701 37.8932 22.5391 36.7621 22.5391 35.3669V22.7353Z" fill="white" fill-opacity="0.9" shape-rendering="crispEdges"/>
</g>
<g filter="url(#filter2_d_3903_56743)">
<path d="M32.0078 16.4189C32.0078 15.0236 33.1389 13.8926 34.5341 13.8926C35.9294 13.8926 37.0604 15.0236 37.0604 16.4189V35.3663C37.0604 36.7615 35.9294 37.8926 34.5341 37.8926C33.1389 37.8926 32.0078 36.7615 32.0078 35.3663V16.4189Z" fill="white" fill-opacity="0.7" shape-rendering="crispEdges"/>
</g>
<path d="M66.4258 15.1724H74.0585V37.0363H78.6239V15.1724H86.2567V10.9637H66.4258V15.1724Z" fill="white"/>
<path d="M91.3521 37.5C94.0984 37.5 96.4881 36.2516 97.2371 34.4326L97.5581 37.0363H101.375V26.3362C101.375 21.4498 98.4498 18.8818 93.7061 18.8818C88.9267 18.8818 85.788 21.3785 85.788 25.1948H89.4974C89.4974 23.3402 90.9241 22.2701 93.4921 22.2701C95.7035 22.2701 97.1301 23.2332 97.1301 25.6229V26.0152L91.8514 26.4075C87.6784 26.7285 85.3243 28.7616 85.3243 32.0073C85.3243 35.3243 87.607 37.5 91.3521 37.5ZM92.7788 34.2186C90.8171 34.2186 89.747 33.4339 89.747 31.8289C89.747 30.4022 90.7814 29.5106 93.4921 29.2609L97.1658 28.9756V29.9029C97.1658 32.6136 95.4538 34.2186 92.7788 34.2186Z" fill="white"/>
<path d="M107.825 15.8857C109.252 15.8857 110.429 14.7087 110.429 13.2464C110.429 11.784 109.252 10.6427 107.825 10.6427C106.327 10.6427 105.15 11.784 105.15 13.2464C105.15 14.7087 106.327 15.8857 107.825 15.8857ZM105.649 37.0363H110.001V19.4168H105.649V37.0363Z" fill="white"/>
<path d="M118.883 37.0363V10.5H114.568V37.0363H118.883Z" fill="white"/>
<path d="M126.337 37.0363L128.441 31.0086H138.179L140.283 37.0363H145.098L135.682 10.9637H131.009L121.593 37.0363H126.337ZM132.757 18.7391C133.007 18.0258 133.221 17.2411 133.328 16.7417C133.399 17.2768 133.649 18.0614 133.863 18.7391L136.859 27.1565H129.797L132.757 18.7391Z" fill="white"/>
<path d="M154.165 37.5C156.84 37.5 159.122 36.323 160.192 34.29L160.478 37.0363H164.472V10.5H160.157V21.6638C159.051 19.9161 156.875 18.8818 154.414 18.8818C149.1 18.8818 145.89 22.8052 145.89 28.2979C145.89 33.755 149.064 37.5 154.165 37.5ZM155.128 33.5053C152.096 33.5053 150.241 31.2939 150.241 28.1552C150.241 25.0165 152.096 22.7695 155.128 22.7695C158.159 22.7695 160.121 24.9808 160.121 28.1552C160.121 31.3296 158.159 33.5053 155.128 33.5053Z" fill="white"/>
<path d="M173.359 37.0363V27.0495C173.359 24.1962 175.035 22.8408 177.104 22.8408C179.172 22.8408 180.492 24.1605 180.492 26.6215V37.0363H184.843V27.0495C184.843 24.1605 186.448 22.8052 188.553 22.8052C190.621 22.8052 191.977 24.1248 191.977 26.6572V37.0363H196.292V25.5159C196.292 21.4498 193.938 18.8818 189.658 18.8818C186.983 18.8818 184.915 20.2015 184.023 22.2345C183.096 20.2015 181.241 18.8818 178.566 18.8818C176.034 18.8818 174.25 20.0231 173.359 21.4855L173.002 19.4168H169.007V37.0363H173.359Z" fill="white"/>
<path d="M202.74 15.8857C204.167 15.8857 205.344 14.7087 205.344 13.2464C205.344 11.784 204.167 10.6427 202.74 10.6427C201.242 10.6427 200.065 11.784 200.065 13.2464C200.065 14.7087 201.242 15.8857 202.74 15.8857ZM200.564 37.0363H204.916V19.4168H200.564V37.0363Z" fill="white"/>
<path d="M213.763 37.0363V27.5489C213.763 24.6955 215.403 22.8408 218.078 22.8408C220.325 22.8408 221.788 24.2675 221.788 27.2279V37.0363H226.139V26.1935C226.139 21.6281 223.856 18.8818 219.434 18.8818C217.044 18.8818 214.904 19.9161 213.798 21.6995L213.442 19.4168H209.411V37.0363H213.763Z" fill="white"/>
<defs>
<filter id="filter0_d_3903_56743" x="12.0615" y="9.60596" width="7.05273" height="29.7896" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
<feOffset dy="0.5"/>
<feGaussianBlur stdDeviation="0.5"/>
<feComposite in2="hardAlpha" operator="out"/>
<feColorMatrix type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.12 0"/>
<feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow_3903_56743"/>
<feBlend mode="normal" in="SourceGraphic" in2="effect1_dropShadow_3903_56743" result="shape"/>
</filter>
<filter id="filter1_d_3903_56743" x="21.5391" y="19.709" width="7.05273" height="19.6843" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
<feOffset dy="0.5"/>
<feGaussianBlur stdDeviation="0.5"/>
<feComposite in2="hardAlpha" operator="out"/>
<feColorMatrix type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.12 0"/>
<feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow_3903_56743"/>
<feBlend mode="normal" in="SourceGraphic" in2="effect1_dropShadow_3903_56743" result="shape"/>
</filter>
<filter id="filter2_d_3903_56743" x="31.0078" y="13.3926" width="7.05273" height="26" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
<feOffset dy="0.5"/>
<feGaussianBlur stdDeviation="0.5"/>
<feComposite in2="hardAlpha" operator="out"/>
<feColorMatrix type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.12 0"/>
<feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow_3903_56743"/>
<feBlend mode="normal" in="SourceGraphic" in2="effect1_dropShadow_3903_56743" result="shape"/>
</filter>
</defs>
</svg>

Before

Width:  |  Height:  |  Size: 6.3 KiB

View File

@@ -0,0 +1,168 @@
# WordPress Plugin UI Redesign - Version 1.2.0
## Overview
Complete redesign of the WordPress plugin admin interface to match the main IGNY8 app design system. The plugin now features a modern, professional interface with sidebar navigation and separate pages for each function.
## What Changed
### 1. Menu Structure
**Before:**
- Plugin menu was in Settings → IGNY8 API (submenu)
- Single settings page with all options
**After:**
- Plugin menu in main WordPress sidebar (position 58, after Comments)
- Custom Igny8 logo icon in sidebar
- Separate submenu items for each section
### 2. Design System
Created `admin/assets/css/igny8-modern.css` with:
- CSS design tokens matching main app
- Color variables: `--igny8-primary`, `--igny8-success`, `--igny8-warning`, etc.
- Modern card-based layout
- Responsive grid system
- Consistent shadows, borders, and radius
### 3. Navigation Structure
New sidebar navigation with 6 main pages:
1. **Dashboard** - Overview and quick actions
2. **Connection** - API key management
3. **Controls** - Post types, taxonomies, modules
4. **Sync** - Sync status and history
5. **Data** - Link queue and statistics
6. **Logs** - Webhook activity logs
### 4. Layout Components
Created reusable layout files:
- `admin/layout-header.php` - Sidebar navigation + main content wrapper
- `admin/layout-footer.php` - Closing tags
### 5. New Page Templates
Created in `admin/pages/`:
- `dashboard.php` - Connection status, sync info, quick actions
- `connection.php` - API key form and connection details
- `controls.php` - Content type controls and settings
- `sync.php` - Sync enable/disable and history
- `data.php` - Link queue and data statistics
- `logs.php` - Webhook logs table
## Technical Implementation
### Modified Files
#### `admin/class-admin.php`
- Changed `add_menu_pages()` to use `add_menu_page()` instead of `add_options_page()`
- Added custom menu icon: `plugins_url('admin/assets/images/logo-icon.png')`
- Updated `enqueue_scripts()` to load modern CSS
- Added `render_page()` method for page routing
#### `igny8-bridge.php`
- Updated version from 1.1.4 to 1.2.0
### New Files Created
1. `admin/assets/css/igny8-modern.css` (480 lines) - Complete design system
2. `admin/assets/images/` - 5 logo files copied from frontend
3. `admin/layout-header.php` - Shared layout wrapper
4. `admin/layout-footer.php` - Closing wrapper
5. `admin/pages/dashboard.php` - Dashboard page
6. `admin/pages/connection.php` - Connection management
7. `admin/pages/controls.php` - Content controls
8. `admin/pages/sync.php` - Sync settings
9. `admin/pages/data.php` - Data overview
10. `admin/pages/logs.php` - Activity logs
## Design Tokens Applied
### Colors
```css
--igny8-primary: #3B82F6 (Blue)
--igny8-success: #10B981 (Green)
--igny8-warning: #F59E0B (Orange)
--igny8-danger: #DC2626 (Red)
--igny8-purple: #F63B82 (Purple accent)
--igny8-gray-base: #031D48 (Navy base)
```
### Components
- Cards with subtle shadows
- Modern form inputs with focus states
- Status indicators (connected/disconnected)
- Responsive grid system (2 and 3 columns)
- Modern buttons with icons
- Alert boxes (success, warning, danger)
## Visual Changes
### Header
- Removed "IGNY8 API Settings" page title
- Minimal page headers with icon and description
- Connection status in sidebar footer
### Sidebar
- Igny8 logo at top
- Icon-based navigation
- Active state highlighting
- Connection status indicator at bottom
### Content Area
- Clean white background
- Card-based layout
- Generous spacing and padding
- SVG icons throughout
- Modern typography
## User Experience Improvements
1. **Better Navigation**: Clear menu structure with dedicated pages
2. **Visual Hierarchy**: Cards and sections clearly separated
3. **Status Indicators**: Quick visual feedback on connection and sync status
4. **Responsive Design**: Works on all screen sizes
5. **Consistent Design**: Matches main IGNY8 app perfectly
6. **Quick Actions**: Dashboard provides shortcuts to common tasks
## File Size
- Version 1.1.5: 155KB
- Version 1.2.0: 567KB (includes CSS, images, and new page templates)
## Distribution
- Built: `/data/app/igny8/plugins/wordpress/dist/igny8-wp-bridge-v1.2.0.zip`
- SHA256: Generated and saved alongside ZIP
- Ready for deployment
## Backwards Compatibility
- All existing functionality preserved
- Old `render_settings_page()` method redirects to new `render_page()`
- Form handlers remain unchanged
- Database options unchanged
## Testing Checklist
- [ ] Plugin appears in main WordPress sidebar
- [ ] Logo icon displays correctly
- [ ] All 6 pages load without errors
- [ ] Sidebar navigation works
- [ ] Active page highlighting works
- [ ] Connection form functions
- [ ] Settings save correctly
- [ ] Responsive on mobile
- [ ] Colors match main app
- [ ] Status indicators work
## Deployment Notes
1. Users will see new sidebar menu location
2. All settings preserved during update
3. No database migrations needed
4. Assets load from new paths
5. Old `igny8-settings` page slug redirects to `igny8-dashboard`
## Version Info
- **Version**: 1.2.0
- **Release Date**: January 10, 2024
- **Type**: Major UI Update
- **Breaking Changes**: None (backwards compatible)
- **Min WordPress**: 5.0
- **Min PHP**: 7.4
## Credits
- Design tokens from: `frontend/src/styles/design-system.css`
- Logo assets from: `frontend/public/images/logo/`
- Icons: Heroicons (via SVG)

View File

@@ -845,3 +845,9 @@
flex-direction: column;
}
}
/* ============================================
Hide 3rd party clutter (Sellvia banner)
============================================ */
.sellvia-banner.notice.notice-info {
display: none;
}

View File

@@ -0,0 +1,454 @@
/**
* IGNY8 WordPress Bridge - Modern Design System
* Based on main app design tokens
*/
/* ===================================================================
DESIGN TOKENS - Matching Main App
=================================================================== */
:root {
/* Primary Colors */
--igny8-primary: #3B82F6;
--igny8-success: #10B981;
--igny8-warning: #F59E0B;
--igny8-danger: #DC2626;
--igny8-purple: #F63B82;
--igny8-gray-base: #031D48;
/* Derived Colors */
--igny8-primary-dark: #2563EB;
--igny8-primary-light: #60A5FA;
--igny8-primary-subtle: #DBEAFE;
/* Background Colors */
--igny8-navy: #020617;
--igny8-navy-light: #0F172A;
--igny8-surface: #F8FAFC;
--igny8-panel: #FFFFFF;
--igny8-panel-alt: #F1F5F9;
/* Text Colors */
--igny8-text: #1E293B;
--igny8-text-dim: #475569;
--igny8-text-light: #94A3B8;
--igny8-stroke: #E2E8F0;
/* Border Radius */
--igny8-radius-sm: 4px;
--igny8-radius-base: 6px;
--igny8-radius-md: 8px;
--igny8-radius-lg: 12px;
/* Shadows */
--igny8-shadow-sm: 0 1px 2px 0 rgba(0, 0, 0, 0.05);
--igny8-shadow-base: 0 1px 3px 0 rgba(0, 0, 0, 0.1), 0 1px 2px 0 rgba(0, 0, 0, 0.06);
--igny8-shadow-md: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06);
--igny8-shadow-lg: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05);
/* Gradients */
--igny8-gradient-primary: linear-gradient(135deg, var(--igny8-primary) 0%, var(--igny8-primary-dark) 100%);
}
/* ===================================================================
LAYOUT - New Sidebar Structure
=================================================================== */
.igny8-admin-wrapper {
display: flex;
min-height: 100vh;
background: var(--igny8-surface);
margin-left: -20px;
margin-top: -10px;
}
.igny8-sidebar {
width: 260px;
background: var(--igny8-panel);
border-right: 1px solid var(--igny8-stroke);
padding: 24px 0;
position: fixed;
height: 100vh;
overflow-y: auto;
}
.igny8-sidebar-logo {
padding: 0 24px 24px;
border-bottom: 1px solid var(--igny8-stroke);
margin-bottom: 24px;
}
.igny8-sidebar-logo img {
height: 32px;
width: auto;
}
.igny8-sidebar-nav {
list-style: none;
margin: 0;
padding: 0 12px;
}
.igny8-sidebar-nav li {
margin-bottom: 4px;
}
.igny8-sidebar-nav a {
display: flex;
align-items: center;
padding: 10px 12px;
color: var(--igny8-text-dim);
text-decoration: none;
border-radius: var(--igny8-radius-base);
font-size: 14px;
font-weight: 500;
transition: all 0.2s ease;
}
.igny8-sidebar-nav a:hover {
background: var(--igny8-surface);
color: var(--igny8-text);
}
.igny8-sidebar-nav a.active {
background: var(--igny8-primary-subtle);
color: var(--igny8-primary);
}
.igny8-sidebar-nav svg {
width: 18px;
height: 18px;
margin-right: 12px;
flex-shrink: 0;
}
.igny8-main-content {
margin-left: 260px;
flex: 1;
padding: 32px 40px;
max-width: 1400px;
}
/* ===================================================================
HEADER - Minimal Design
=================================================================== */
.igny8-page-header {
margin-bottom: 32px;
}
.igny8-page-header h1 {
font-size: 28px;
font-weight: 600;
color: var(--igny8-text);
margin: 0 0 8px 0;
}
.igny8-page-header p {
color: var(--igny8-text-dim);
font-size: 14px;
margin: 0;
}
/* ===================================================================
CARDS - Modern Panel Design
=================================================================== */
.igny8-card {
background: var(--igny8-panel);
border-radius: var(--igny8-radius-md);
padding: 24px;
box-shadow: var(--igny8-shadow-base);
margin-bottom: 24px;
border: 1px solid var(--igny8-stroke);
}
.igny8-card-header {
display: flex;
align-items: center;
margin-bottom: 20px;
padding-bottom: 16px;
border-bottom: 1px solid var(--igny8-stroke);
}
.igny8-card-header h2 {
font-size: 18px;
font-weight: 600;
color: var(--igny8-text);
margin: 0;
display: flex;
align-items: center;
}
.igny8-card-header svg {
width: 20px;
height: 20px;
margin-right: 10px;
color: var(--igny8-primary);
}
/* ===================================================================
FORM ELEMENTS - Modern Inputs
=================================================================== */
.igny8-form-group {
margin-bottom: 20px;
}
.igny8-form-group label {
display: block;
font-size: 14px;
font-weight: 500;
color: var(--igny8-text);
margin-bottom: 8px;
}
.igny8-form-group input[type="text"],
.igny8-form-group input[type="password"],
.igny8-form-group input[type="number"],
.igny8-form-group textarea,
.igny8-form-group select {
width: 100%;
padding: 10px 12px;
font-size: 14px;
border: 1px solid var(--igny8-stroke);
border-radius: var(--igny8-radius-base);
background: var(--igny8-panel);
color: var(--igny8-text);
transition: all 0.2s ease;
}
.igny8-form-group input:focus,
.igny8-form-group textarea:focus,
.igny8-form-group select:focus {
outline: none;
border-color: var(--igny8-primary);
box-shadow: 0 0 0 3px var(--igny8-primary-subtle);
}
.igny8-form-help {
font-size: 13px;
color: var(--igny8-text-light);
margin-top: 6px;
}
/* ===================================================================
BUTTONS - Modern Button System
=================================================================== */
.igny8-btn {
display: inline-flex;
align-items: center;
justify-content: center;
padding: 10px 16px;
font-size: 14px;
font-weight: 500;
border-radius: var(--igny8-radius-base);
border: none;
cursor: pointer;
transition: all 0.2s ease;
text-decoration: none;
}
.igny8-btn-primary {
background: var(--igny8-primary);
color: white;
}
.igny8-btn-primary:hover {
background: var(--igny8-primary-dark);
transform: translateY(-1px);
box-shadow: var(--igny8-shadow-md);
}
.igny8-btn-secondary {
background: var(--igny8-surface);
color: var(--igny8-text);
border: 1px solid var(--igny8-stroke);
}
.igny8-btn-secondary:hover {
background: var(--igny8-panel-alt);
}
.igny8-btn-danger {
background: var(--igny8-danger);
color: white;
}
.igny8-btn-danger:hover {
background: #B91C1C;
}
.igny8-btn svg {
width: 16px;
height: 16px;
margin-right: 8px;
}
/* ===================================================================
STATUS INDICATORS
=================================================================== */
.igny8-status {
display: inline-flex;
align-items: center;
padding: 4px 12px;
border-radius: 12px;
font-size: 13px;
font-weight: 500;
}
.igny8-status-connected {
background: rgba(16, 185, 129, 0.1);
color: var(--igny8-success);
}
.igny8-status-disconnected {
background: rgba(220, 38, 38, 0.1);
color: var(--igny8-danger);
}
.igny8-status-indicator {
width: 8px;
height: 8px;
border-radius: 50%;
margin-right: 8px;
}
.igny8-status-connected .igny8-status-indicator {
background: var(--igny8-success);
box-shadow: 0 0 0 3px rgba(16, 185, 129, 0.2);
}
.igny8-status-disconnected .igny8-status-indicator {
background: var(--igny8-danger);
box-shadow: 0 0 0 3px rgba(220, 38, 38, 0.2);
}
/* ===================================================================
GRID SYSTEM
=================================================================== */
.igny8-grid {
display: grid;
gap: 24px;
}
.igny8-grid-2 {
grid-template-columns: repeat(2, 1fr);
}
.igny8-grid-3 {
grid-template-columns: repeat(3, 1fr);
}
@media (max-width: 1024px) {
.igny8-grid-2,
.igny8-grid-3 {
grid-template-columns: 1fr;
}
.igny8-sidebar {
width: 220px;
}
.igny8-main-content {
margin-left: 220px;
}
}
/* ===================================================================
TABLES
=================================================================== */
.igny8-table {
width: 100%;
border-collapse: collapse;
}
.igny8-table th {
text-align: left;
padding: 12px;
font-size: 13px;
font-weight: 600;
color: var(--igny8-text-dim);
text-transform: uppercase;
letter-spacing: 0.5px;
border-bottom: 2px solid var(--igny8-stroke);
}
.igny8-table td {
padding: 12px;
border-bottom: 1px solid var(--igny8-stroke);
color: var(--igny8-text);
}
.igny8-table tr:last-child td {
border-bottom: none;
}
/* ===================================================================
ALERTS & MESSAGES
=================================================================== */
.igny8-alert {
padding: 16px;
border-radius: var(--igny8-radius-base);
margin-bottom: 20px;
display: flex;
align-items: flex-start;
}
.igny8-alert svg {
width: 20px;
height: 20px;
margin-right: 12px;
flex-shrink: 0;
margin-top: 2px;
}
.igny8-alert-success {
background: rgba(16, 185, 129, 0.1);
border: 1px solid rgba(16, 185, 129, 0.3);
color: #065F46;
}
.igny8-alert-warning {
background: rgba(245, 158, 11, 0.1);
border: 1px solid rgba(245, 158, 11, 0.3);
color: #92400E;
}
.igny8-alert-danger {
background: rgba(220, 38, 38, 0.1);
border: 1px solid rgba(220, 38, 38, 0.3);
color: #991B1B;
}
/* ===================================================================
LOADING & SPINNERS
=================================================================== */
.igny8-spinner {
display: inline-block;
width: 20px;
height: 20px;
border: 2px solid var(--igny8-stroke);
border-top-color: var(--igny8-primary);
border-radius: 50%;
animation: igny8-spin 0.6s linear infinite;
}
@keyframes igny8-spin {
to { transform: rotate(360deg); }
}
/* ===================================================================
OVERRIDE WORDPRESS ADMIN STYLES
=================================================================== */
.igny8-admin-page .wrap {
margin: 0;
padding: 0;
}
.igny8-admin-page .wrap > h1 {
display: none;
}
/* Hide WordPress notices on our pages */
.igny8-admin-page .notice,
.igny8-admin-page .updated,
.igny8-admin-page .error {
margin-left: 260px;
margin-top: 20px;
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 40 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 320 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

View File

@@ -49,12 +49,70 @@ class Igny8Admin {
* Add admin menu pages
*/
public function add_menu_pages() {
add_options_page(
__('IGNY8 API Settings', 'igny8-bridge'),
__('IGNY8 API', 'igny8-bridge'),
// Add main menu page
add_menu_page(
__('IGNY8', 'igny8-bridge'),
__('IGNY8', 'igny8-bridge'),
'manage_options',
'igny8-settings',
array($this, 'render_settings_page')
'igny8-dashboard',
array($this, 'render_page'),
plugins_url('admin/assets/images/logo-icon.png', IGNY8_BRIDGE_PLUGIN_FILE),
58
);
// Add submenu pages
add_submenu_page(
'igny8-dashboard',
__('Dashboard', 'igny8-bridge'),
__('Dashboard', 'igny8-bridge'),
'manage_options',
'igny8-dashboard',
array($this, 'render_page')
);
add_submenu_page(
'igny8-dashboard',
__('Connection', 'igny8-bridge'),
__('Connection', 'igny8-bridge'),
'manage_options',
'igny8-connection',
array($this, 'render_page')
);
add_submenu_page(
'igny8-dashboard',
__('Controls', 'igny8-bridge'),
__('Controls', 'igny8-bridge'),
'manage_options',
'igny8-controls',
array($this, 'render_page')
);
add_submenu_page(
'igny8-dashboard',
__('Sync', 'igny8-bridge'),
__('Sync', 'igny8-bridge'),
'manage_options',
'igny8-sync',
array($this, 'render_page')
);
add_submenu_page(
'igny8-dashboard',
__('Data', 'igny8-bridge'),
__('Data', 'igny8-bridge'),
'manage_options',
'igny8-data',
array($this, 'render_page')
);
add_submenu_page(
'igny8-dashboard',
__('Logs', 'igny8-bridge'),
__('Logs', 'igny8-bridge'),
'manage_options',
'igny8-logs',
array($this, 'render_page')
);
}
@@ -120,8 +178,17 @@ class Igny8Admin {
* @param string $hook Current admin page hook
*/
public function enqueue_scripts($hook) {
// Enqueue on settings page
if ($hook === 'settings_page_igny8-settings') {
// Enqueue on IGNY8 pages
if (strpos($hook, 'igny8-') !== false || strpos($hook, 'toplevel_page_igny8') !== false) {
// Load modern design system CSS
wp_enqueue_style(
'igny8-modern-style',
IGNY8_BRIDGE_PLUGIN_URL . 'admin/assets/css/igny8-modern.css',
array(),
IGNY8_BRIDGE_VERSION
);
// Load legacy admin CSS for backwards compatibility
wp_enqueue_style(
'igny8-admin-style',
IGNY8_BRIDGE_PLUGIN_URL . 'admin/assets/css/admin.css',
@@ -171,10 +238,10 @@ class Igny8Admin {
}
/**
* Render settings page
* Render page based on current menu slug
*/
public function render_settings_page() {
// Handle form submission (use wp_verify_nonce to avoid wp_die on failure)
public function render_page() {
// Handle form submissions for connection page
if (isset($_POST['igny8_connect'])) {
if (empty($_POST['_wpnonce']) || !wp_verify_nonce($_POST['_wpnonce'], 'igny8_settings_nonce')) {
add_settings_error(
@@ -188,7 +255,7 @@ class Igny8Admin {
}
}
// Handle revoke API key (use wp_verify_nonce)
// Handle revoke API key
if (isset($_POST['igny8_revoke_api_key'])) {
if (empty($_POST['_wpnonce']) || !wp_verify_nonce($_POST['_wpnonce'], 'igny8_revoke_api_key')) {
add_settings_error(
@@ -207,11 +274,55 @@ class Igny8Admin {
);
}
}
// Webhook secret regeneration removed - using API key only
// Include settings template
include IGNY8_BRIDGE_PLUGIN_DIR . 'admin/settings.php';
// Determine which page to render
$page = isset($_GET['page']) ? sanitize_text_field($_GET['page']) : 'igny8-dashboard';
// Add wrapper class for modern design
echo '<div class="igny8-admin-page">';
// Include the appropriate page template
$template_file = '';
switch ($page) {
case 'igny8-dashboard':
$template_file = IGNY8_BRIDGE_PLUGIN_DIR . 'admin/pages/dashboard.php';
break;
case 'igny8-connection':
$template_file = IGNY8_BRIDGE_PLUGIN_DIR . 'admin/pages/connection.php';
break;
case 'igny8-controls':
$template_file = IGNY8_BRIDGE_PLUGIN_DIR . 'admin/pages/controls.php';
break;
case 'igny8-sync':
$template_file = IGNY8_BRIDGE_PLUGIN_DIR . 'admin/pages/sync.php';
break;
case 'igny8-data':
$template_file = IGNY8_BRIDGE_PLUGIN_DIR . 'admin/pages/data.php';
break;
case 'igny8-logs':
$template_file = IGNY8_BRIDGE_PLUGIN_DIR . 'admin/pages/logs.php';
break;
default:
$template_file = IGNY8_BRIDGE_PLUGIN_DIR . 'admin/pages/dashboard.php';
}
// If the template file doesn't exist, fall back to the old settings page
if (file_exists($template_file)) {
include $template_file;
} else {
// Fallback to old settings page during transition
include IGNY8_BRIDGE_PLUGIN_DIR . 'admin/settings.php';
}
echo '</div>';
}
/**
* Render settings page - DEPRECATED, keeping for backwards compatibility
*/
public function render_settings_page() {
// Redirect to new render_page method
$this->render_page();
}
/**

View File

@@ -0,0 +1,2 @@
</main>
</div>

View File

@@ -0,0 +1,111 @@
<?php
/**
* Modern Layout Wrapper
*
* This template provides the sidebar navigation and layout structure for all IGNY8 admin pages.
* Each page template should start by including this file.
*
* @package Igny8Bridge
*/
// Prevent direct access
if (!defined('ABSPATH')) {
exit;
}
// Get current page
$current_page = isset($_GET['page']) ? sanitize_text_field($_GET['page']) : 'igny8-dashboard';
$api_key = get_option('igny8_api_key');
$is_connected = !empty($api_key);
?>
<div class="igny8-admin-wrapper">
<!-- Sidebar Navigation -->
<aside class="igny8-sidebar">
<div class="igny8-sidebar-logo">
<img src="<?php echo esc_url(plugins_url('assets/images/IGNY8_DARK_LOGO.png', __FILE__)); ?>" alt="IGNY8" />
</div>
<nav>
<ul class="igny8-sidebar-nav">
<li>
<a href="<?php echo esc_url(admin_url('admin.php?page=igny8-dashboard')); ?>"
class="<?php echo ($current_page === 'igny8-dashboard') ? 'active' : ''; ?>">
<svg fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 12l2-2m0 0l7-7 7 7M5 10v10a1 1 0 001 1h3m10-11l2 2m-2-2v10a1 1 0 01-1 1h-3m-6 0a1 1 0 001-1v-4a1 1 0 011-1h2a1 1 0 011 1v4a1 1 0 001 1m-6 0h6"/>
</svg>
<?php _e('Dashboard', 'igny8-bridge'); ?>
</a>
</li>
<li>
<a href="<?php echo esc_url(admin_url('admin.php?page=igny8-connection')); ?>"
class="<?php echo ($current_page === 'igny8-connection') ? 'active' : ''; ?>">
<svg fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13.828 10.172a4 4 0 00-5.656 0l-4 4a4 4 0 105.656 5.656l1.102-1.101m-.758-4.899a4 4 0 005.656 0l4-4a4 4 0 00-5.656-5.656l-1.1 1.1"/>
</svg>
<?php _e('Connection', 'igny8-bridge'); ?>
</a>
</li>
<li>
<a href="<?php echo esc_url(admin_url('admin.php?page=igny8-controls')); ?>"
class="<?php echo ($current_page === 'igny8-controls') ? 'active' : ''; ?>">
<svg fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 6V4m0 2a2 2 0 100 4m0-4a2 2 0 110 4m-6 8a2 2 0 100-4m0 4a2 2 0 110-4m0 4v2m0-6V4m6 6v10m6-2a2 2 0 100-4m0 4a2 2 0 110-4m0 4v2m0-6V4"/>
</svg>
<?php _e('Controls', 'igny8-bridge'); ?>
</a>
</li>
<li>
<a href="<?php echo esc_url(admin_url('admin.php?page=igny8-sync')); ?>"
class="<?php echo ($current_page === 'igny8-sync') ? 'active' : ''; ?>">
<svg fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15"/>
</svg>
<?php _e('Sync', 'igny8-bridge'); ?>
</a>
</li>
<li>
<a href="<?php echo esc_url(admin_url('admin.php?page=igny8-data')); ?>"
class="<?php echo ($current_page === 'igny8-data') ? 'active' : ''; ?>">
<svg fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 7v10c0 2.21 3.582 4 8 4s8-1.79 8-4V7M4 7c0 2.21 3.582 4 8 4s8-1.79 8-4M4 7c0-2.21 3.582-4 8-4s8 1.79 8 4m0 5c0 2.21-3.582 4-8 4s-8-1.79-8-4"/>
</svg>
<?php _e('Data', 'igny8-bridge'); ?>
</a>
</li>
<li>
<a href="<?php echo esc_url(admin_url('admin.php?page=igny8-logs')); ?>"
class="<?php echo ($current_page === 'igny8-logs') ? 'active' : ''; ?>">
<svg fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z"/>
</svg>
<?php _e('Logs', 'igny8-bridge'); ?>
</a>
</li>
</ul>
</nav>
<!-- Connection Status in Sidebar -->
<div style="padding: 16px 24px; margin-top: auto; border-top: 1px solid var(--igny8-stroke);">
<?php if ($is_connected): ?>
<div class="igny8-status igny8-status-connected">
<span class="igny8-status-indicator"></span>
<?php _e('Connected', 'igny8-bridge'); ?>
</div>
<?php else: ?>
<div class="igny8-status igny8-status-disconnected">
<span class="igny8-status-indicator"></span>
<?php _e('Not Connected', 'igny8-bridge'); ?>
</div>
<?php endif; ?>
</div>
</aside>
<!-- Main Content Area -->
<main class="igny8-main-content">
<?php settings_errors('igny8_settings'); ?>

View File

@@ -0,0 +1,227 @@
<?php
/**
* Connection Page
*
* @package Igny8Bridge
*/
// Prevent direct access
if (!defined('ABSPATH')) {
exit;
}
// Include layout header
include IGNY8_BRIDGE_PLUGIN_DIR . 'admin/layout-header.php';
// Get connection settings
$api_key = function_exists('igny8_get_secure_option') ? igny8_get_secure_option('igny8_api_key') : get_option('igny8_api_key');
$site_id = get_option('igny8_site_id', '');
$integration_id = get_option('igny8_integration_id', '');
$access_token = function_exists('igny8_get_secure_option') ? igny8_get_secure_option('igny8_access_token') : get_option('igny8_access_token');
$last_structure_sync = get_option('igny8_last_structure_sync', 0);
$is_connected = !empty($access_token) && !empty($integration_id) && !empty($last_structure_sync);
$date_format = get_option('date_format');
$time_format = get_option('time_format');
$now = current_time('timestamp');
$token_issued = intval(get_option('igny8_access_token_issued', 0));
$token_age_text = $token_issued ? sprintf(__('%s ago', 'igny8-bridge'), human_time_diff($token_issued, $now)) : __('Not generated yet', 'igny8-bridge');
$token_issued_formatted = $token_issued ? date_i18n($date_format . ' ' . $time_format, $token_issued) : __('—', 'igny8-bridge');
$last_health_check = intval(get_option('igny8_last_api_health_check', 0));
$last_health_check_formatted = $last_health_check ? date_i18n($date_format . ' ' . $time_format, $last_health_check) : __('Never', 'igny8-bridge');
?>
<div class="igny8-page-header">
<h1><?php _e('Connection', 'igny8-bridge'); ?></h1>
<p><?php _e('Manage your IGNY8 API connection and authentication', 'igny8-bridge'); ?></p>
</div>
<?php if (!$is_connected): ?>
<!-- Not Connected - Show Connection Form -->
<div class="igny8-card">
<div class="igny8-card-header">
<h2>
<svg fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13.828 10.172a4 4 0 00-5.656 0l-4 4a4 4 0 105.656 5.656l1.102-1.101m-.758-4.899a4 4 0 005.656 0l4-4a4 4 0 00-5.656-5.656l-1.1 1.1"/>
</svg>
<?php _e('Connect to IGNY8', 'igny8-bridge'); ?>
</h2>
</div>
<div class="igny8-status igny8-status-disconnected" style="margin-bottom: 24px;">
<span class="igny8-status-indicator"></span>
<?php _e('Not Connected', 'igny8-bridge'); ?>
</div>
<form method="post" action="">
<?php wp_nonce_field('igny8_settings_nonce'); ?>
<div class="igny8-form-group">
<label for="igny8_api_key">
<?php _e('API Key', 'igny8-bridge'); ?>
<span style="color: var(--igny8-danger);">*</span>
</label>
<input
type="text"
id="igny8_api_key"
name="igny8_api_key"
value=""
placeholder="<?php _e('igny8_site_5_1764575388582_u671q2e2mv', 'igny8-bridge'); ?>"
required
/>
<p class="igny8-form-help">
<?php printf(
__('Get your API key from the <a href="%s" target="_blank">IGNY8 app integrations page</a>. The API key format includes your site ID.', 'igny8-bridge'),
'https://app.igny8.com'
); ?>
</p>
</div>
<button type="submit" name="igny8_connect" class="igny8-btn igny8-btn-primary">
<svg fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13.828 10.172a4 4 0 00-5.656 0l-4 4a4 4 0 105.656 5.656l1.102-1.101m-.758-4.899a4 4 0 005.656 0l4-4a4 4 0 00-5.656-5.656l-1.1 1.1"/>
</svg>
<?php _e('Connect to IGNY8', 'igny8-bridge'); ?>
</button>
</form>
</div>
<!-- Connection Instructions -->
<div class="igny8-card">
<div class="igny8-card-header">
<h2>
<svg fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"/>
</svg>
<?php _e('How to Connect', 'igny8-bridge'); ?>
</h2>
</div>
<ol style="padding-left: 20px; line-height: 1.8;">
<li><?php _e('Log in to your IGNY8 account', 'igny8-bridge'); ?></li>
<li><?php _e('Navigate to Settings → Integrations', 'igny8-bridge'); ?></li>
<li><?php _e('Find WordPress in the integrations list', 'igny8-bridge'); ?></li>
<li><?php _e('Click "Add Site" to generate a new API key', 'igny8-bridge'); ?></li>
<li><?php _e('Copy the API key and paste it in the field above', 'igny8-bridge'); ?></li>
<li><?php _e('Click "Connect to IGNY8" to establish the connection', 'igny8-bridge'); ?></li>
</ol>
</div>
<?php else: ?>
<!-- Connected - Show Connection Details -->
<div class="igny8-alert igny8-alert-success">
<svg fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z"/>
</svg>
<div>
<strong><?php _e('Connected Successfully', 'igny8-bridge'); ?></strong>
<p><?php _e('Your WordPress site is connected to IGNY8 and ready to sync content.', 'igny8-bridge'); ?></p>
</div>
</div>
<div class="igny8-grid igny8-grid-2">
<!-- Connection Status -->
<div class="igny8-card">
<div class="igny8-card-header">
<h2>
<svg fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z"/>
</svg>
<?php _e('Connection Status', 'igny8-bridge'); ?>
</h2>
</div>
<div class="igny8-status igny8-status-connected" style="margin-bottom: 20px;">
<span class="igny8-status-indicator"></span>
<?php _e('Connected & Active', 'igny8-bridge'); ?>
</div>
<div class="igny8-form-group">
<label><?php _e('Site ID', 'igny8-bridge'); ?></label>
<code style="display: block; padding: 10px; background: var(--igny8-surface); border-radius: var(--igny8-radius-base);">
<?php echo esc_html($site_id ?: __('Not set', 'igny8-bridge')); ?>
</code>
</div>
<div class="igny8-form-group">
<label><?php _e('Integration ID', 'igny8-bridge'); ?></label>
<code style="display: block; padding: 10px; background: var(--igny8-surface); border-radius: var(--igny8-radius-base);">
<?php echo esc_html($integration_id ?: __('Not set', 'igny8-bridge')); ?>
</code>
</div>
</div>
<!-- API Key Details -->
<div class="igny8-card">
<div class="igny8-card-header">
<h2>
<svg fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 7a2 2 0 012 2m4 0a6 6 0 01-7.743 5.743L11 17H9v2H7v2H4a1 1 0 01-1-1v-2.586a1 1 0 01.293-.707l5.964-5.964A6 6 0 1121 9z"/>
</svg>
<?php _e('API Key', 'igny8-bridge'); ?>
</h2>
</div>
<div class="igny8-form-group">
<label><?php _e('Active API Key', 'igny8-bridge'); ?></label>
<div style="padding: 10px 12px; background: var(--igny8-surface); border: 1px solid var(--igny8-stroke); border-radius: var(--igny8-radius-base); font-family: monospace;">
<?php echo esc_html(substr($api_key, 0, 7) . '••••••••••••' . substr($api_key, -7)); ?>
<span style="color: var(--igny8-success); font-weight: 600; margin-left: 12px;">✓ Verified</span>
</div>
</div>
<?php if ($token_issued): ?>
<div class="igny8-form-group">
<label><?php _e('Token Issued', 'igny8-bridge'); ?></label>
<p style="margin: 0; font-size: 14px;">
<?php echo esc_html($token_age_text); ?>
<span style="color: var(--igny8-text-dim); display: block; font-size: 13px; margin-top: 4px;">
<?php echo esc_html($token_issued_formatted); ?>
</span>
</p>
</div>
<?php endif; ?>
<?php if ($last_health_check): ?>
<div class="igny8-form-group">
<label><?php _e('Last Health Check', 'igny8-bridge'); ?></label>
<p style="margin: 0; font-size: 14px;">
<?php echo esc_html($last_health_check_formatted); ?>
</p>
</div>
<?php endif; ?>
</div>
</div>
<!-- Disconnect Section -->
<div class="igny8-card">
<div class="igny8-card-header">
<h2>
<svg fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z"/>
</svg>
<?php _e('Danger Zone', 'igny8-bridge'); ?>
</h2>
</div>
<p style="margin-bottom: 16px; color: var(--igny8-text-dim);">
<?php _e('Disconnecting will stop all syncing with IGNY8. Your content in WordPress will remain unchanged, but no new updates will be received from IGNY8.', 'igny8-bridge'); ?>
</p>
<form method="post" action="">
<?php wp_nonce_field('igny8_revoke_api_key'); ?>
<button type="submit" name="igny8_revoke_api_key" class="igny8-btn igny8-btn-danger"
onclick="return confirm('<?php _e('Are you sure you want to disconnect? This will stop all syncing with IGNY8.', 'igny8-bridge'); ?>');">
<svg fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M18.364 18.364A9 9 0 005.636 5.636m12.728 12.728A9 9 0 015.636 5.636m12.728 12.728L5.636 5.636"/>
</svg>
<?php _e('Disconnect from IGNY8', 'igny8-bridge'); ?>
</button>
</form>
</div>
<?php endif; ?>
<?php
// Include layout footer
include IGNY8_BRIDGE_PLUGIN_DIR . 'admin/layout-footer.php';
?>

View File

@@ -0,0 +1,276 @@
<?php
/**
* Controls Page
*
* @package Igny8Bridge
*/
// Prevent direct access
if (!defined('ABSPATH')) {
exit;
}
// Include layout header
include IGNY8_BRIDGE_PLUGIN_DIR . 'admin/layout-header.php';
// Get settings
$available_post_types = igny8_get_supported_post_types();
$enabled_post_types = igny8_get_enabled_post_types();
$available_taxonomies = igny8_get_supported_taxonomies();
$enabled_taxonomies = igny8_get_enabled_taxonomies();
$control_mode = igny8_get_control_mode();
$woocommerce_enabled = (int) get_option('igny8_enable_woocommerce', class_exists('WooCommerce') ? 1 : 0);
$woocommerce_detected = class_exists('WooCommerce');
$available_modules = igny8_get_available_modules();
$enabled_modules = igny8_get_enabled_modules();
$default_post_status = get_option('igny8_default_post_status', 'draft');
?>
<div class="igny8-page-header">
<h1><?php _e('Controls', 'igny8-bridge'); ?></h1>
<p><?php _e('Configure which content types to sync and how IGNY8 manages your content', 'igny8-bridge'); ?></p>
</div>
<form method="post" action="options.php">
<?php settings_fields('igny8_bridge_controls'); ?>
<div class="igny8-grid igny8-grid-2">
<!-- Post Types -->
<div class="igny8-card">
<div class="igny8-card-header">
<h2>
<svg fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z"/>
</svg>
<?php _e('Post Types to Sync', 'igny8-bridge'); ?>
</h2>
</div>
<?php foreach ($available_post_types as $slug => $label) : ?>
<div class="igny8-form-group" style="margin-bottom: 12px;">
<label style="display: flex; align-items: center; cursor: pointer;">
<input
type="checkbox"
name="igny8_enabled_post_types[]"
value="<?php echo esc_attr($slug); ?>"
<?php checked(in_array($slug, $enabled_post_types, true)); ?>
style="margin-right: 8px;"
/>
<?php echo esc_html($label); ?>
</label>
</div>
<?php endforeach; ?>
<p class="igny8-form-help">
<?php _e('Select the content types IGNY8 should manage automatically.', 'igny8-bridge'); ?>
</p>
</div>
<!-- Taxonomies -->
<div class="igny8-card">
<div class="igny8-card-header">
<h2>
<svg fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M7 7h.01M7 3h5c.512 0 1.024.195 1.414.586l7 7a2 2 0 010 2.828l-7 7a2 2 0 01-2.828 0l-7-7A1.994 1.994 0 013 12V7a4 4 0 014-4z"/>
</svg>
<?php _e('Taxonomies to Sync', 'igny8-bridge'); ?>
</h2>
</div>
<?php foreach ($available_taxonomies as $slug => $label) : ?>
<div class="igny8-form-group" style="margin-bottom: 12px;">
<label style="display: flex; align-items: center; cursor: pointer;">
<input
type="checkbox"
name="igny8_enabled_taxonomies[]"
value="<?php echo esc_attr($slug); ?>"
<?php checked(in_array($slug, $enabled_taxonomies, true)); ?>
style="margin-right: 8px;"
/>
<?php echo esc_html($label); ?>
</label>
</div>
<?php endforeach; ?>
<p class="igny8-form-help">
<?php _e('Select which taxonomies to sync with IGNY8.', 'igny8-bridge'); ?>
</p>
</div>
</div>
<!-- Control Mode -->
<div class="igny8-card">
<div class="igny8-card-header">
<h2>
<svg fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 6V4m0 2a2 2 0 100 4m0-4a2 2 0 110 4m-6 8a2 2 0 100-4m0 4a2 2 0 110-4m0 4v2m0-6V4m6 6v10m6-2a2 2 0 100-4m0 4a2 2 0 110-4m0 4v2m0-6V4"/>
</svg>
<?php _e('Control Mode', 'igny8-bridge'); ?>
</h2>
</div>
<div style="display: grid; gap: 16px;">
<label style="display: flex; align-items: flex-start; cursor: pointer; padding: 16px; border: 2px solid var(--igny8-stroke); border-radius: var(--igny8-radius-base); transition: all 0.2s;"
class="<?php echo $control_mode === 'mirror' ? 'igny8-control-mode-selected' : ''; ?>">
<input
type="radio"
name="igny8_control_mode"
value="mirror"
<?php checked($control_mode, 'mirror'); ?>
style="margin-right: 12px; margin-top: 4px;"
/>
<div>
<strong style="font-size: 16px; display: block; margin-bottom: 4px;">
<?php _e('Mirror Mode', 'igny8-bridge'); ?>
</strong>
<span style="color: var(--igny8-text-dim); font-size: 14px;">
<?php _e('IGNY8 is the source of truth; WordPress reflects changes only. Content is read-only in WordPress.', 'igny8-bridge'); ?>
</span>
</div>
</label>
<label style="display: flex; align-items: flex-start; cursor: pointer; padding: 16px; border: 2px solid var(--igny8-stroke); border-radius: var(--igny8-radius-base); transition: all 0.2s;"
class="<?php echo $control_mode === 'hybrid' ? 'igny8-control-mode-selected' : ''; ?>">
<input
type="radio"
name="igny8_control_mode"
value="hybrid"
<?php checked($control_mode, 'hybrid'); ?>
style="margin-right: 12px; margin-top: 4px;"
/>
<div>
<strong style="font-size: 16px; display: block; margin-bottom: 4px;">
<?php _e('Hybrid Mode', 'igny8-bridge'); ?>
</strong>
<span style="color: var(--igny8-text-dim); font-size: 14px;">
<?php _e('Allow editors to update content in WordPress and sync changes back to IGNY8. Two-way synchronization.', 'igny8-bridge'); ?>
</span>
</div>
</label>
</div>
</div>
<div class="igny8-grid igny8-grid-2">
<!-- Modules -->
<div class="igny8-card">
<div class="igny8-card-header">
<h2>
<svg fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 11H5m14 0a2 2 0 012 2v6a2 2 0 01-2 2H5a2 2 0 01-2-2v-6a2 2 0 012-2m14 0V9a2 2 0 00-2-2M5 11V9a2 2 0 012-2m0 0V5a2 2 0 012-2h6a2 2 0 012 2v2M7 7h10"/>
</svg>
<?php _e('IGNY8 Modules', 'igny8-bridge'); ?>
</h2>
</div>
<?php foreach ($available_modules as $module_key => $module_label) : ?>
<div class="igny8-form-group" style="margin-bottom: 12px;">
<label style="display: flex; align-items: center; cursor: pointer;">
<input
type="checkbox"
name="igny8_enabled_modules[]"
value="<?php echo esc_attr($module_key); ?>"
<?php checked(in_array($module_key, $enabled_modules, true)); ?>
style="margin-right: 8px;"
/>
<?php echo esc_html($module_label); ?>
</label>
</div>
<?php endforeach; ?>
<p class="igny8-form-help">
<?php _e('Disable modules temporarily if a feature is not ready in the SaaS app.', 'igny8-bridge'); ?>
</p>
</div>
<!-- Default Post Status -->
<div class="igny8-card">
<div class="igny8-card-header">
<h2>
<svg fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z"/>
</svg>
<?php _e('Default Post Status', 'igny8-bridge'); ?>
</h2>
</div>
<div style="display: grid; gap: 12px; margin-bottom: 16px;">
<label style="display: flex; align-items: flex-start; cursor: pointer;">
<input
type="radio"
name="igny8_default_post_status"
value="draft"
<?php checked($default_post_status, 'draft'); ?>
style="margin-right: 8px; margin-top: 4px;"
/>
<div>
<strong><?php _e('Draft', 'igny8-bridge'); ?></strong>
<span style="display: block; color: var(--igny8-text-dim); font-size: 13px;">
<?php _e('Save content as draft for review before publishing.', 'igny8-bridge'); ?>
</span>
</div>
</label>
<label style="display: flex; align-items: flex-start; cursor: pointer;">
<input
type="radio"
name="igny8_default_post_status"
value="publish"
<?php checked($default_post_status, 'publish'); ?>
style="margin-right: 8px; margin-top: 4px;"
/>
<div>
<strong><?php _e('Publish', 'igny8-bridge'); ?></strong>
<span style="display: block; color: var(--igny8-text-dim); font-size: 13px;">
<?php _e('Publish content immediately when received from IGNY8.', 'igny8-bridge'); ?>
</span>
</div>
</label>
</div>
<p class="igny8-form-help">
<?php _e('Choose whether content published from IGNY8 should be saved as draft or published immediately in WordPress.', 'igny8-bridge'); ?>
</p>
<?php if ($woocommerce_detected): ?>
<div style="margin-top: 20px; padding-top: 20px; border-top: 1px solid var(--igny8-stroke);">
<label style="display: flex; align-items: center; cursor: pointer;">
<input
type="checkbox"
name="igny8_enable_woocommerce"
value="1"
<?php checked($woocommerce_enabled, 1); ?>
style="margin-right: 8px;"
/>
<strong><?php _e('Sync WooCommerce products and categories', 'igny8-bridge'); ?></strong>
</label>
</div>
<?php else: ?>
<div class="igny8-alert igny8-alert-warning" style="margin-top: 20px;">
<svg fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"/>
</svg>
<span><?php _e('WooCommerce is not installed. Install WooCommerce to sync products.', 'igny8-bridge'); ?></span>
</div>
<?php endif; ?>
</div>
</div>
<button type="submit" class="igny8-btn igny8-btn-primary">
<svg fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 13l4 4L19 7"/>
</svg>
<?php _e('Save Controls', 'igny8-bridge'); ?>
</button>
</form>
<style>
.igny8-control-mode-selected {
border-color: var(--igny8-primary) !important;
background: var(--igny8-primary-subtle) !important;
}
</style>
<?php
// Include layout footer
include IGNY8_BRIDGE_PLUGIN_DIR . 'admin/layout-footer.php';
?>

View File

@@ -0,0 +1,182 @@
<?php
/**
* Dashboard Page
*
* @package Igny8Bridge
*/
// Prevent direct access
if (!defined('ABSPATH')) {
exit;
}
// Include layout header
include IGNY8_BRIDGE_PLUGIN_DIR . 'admin/layout-header.php';
// Get connection status
$api_key = get_option('igny8_api_key');
$is_connected = !empty($api_key);
$site_id = get_option('igny8_site_id');
$last_sync = get_option('igny8_last_sync_time');
?>
<div class="igny8-page-header">
<h1><?php _e('Dashboard', 'igny8-bridge'); ?></h1>
<p><?php _e('Overview of your IGNY8 integration', 'igny8-bridge'); ?></p>
</div>
<?php if (!$is_connected): ?>
<div class="igny8-alert igny8-alert-warning">
<svg fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z"/>
</svg>
<div>
<strong><?php _e('Not Connected', 'igny8-bridge'); ?></strong>
<p><?php _e('You need to connect to IGNY8 to start syncing content.', 'igny8-bridge'); ?>
<a href="<?php echo esc_url(admin_url('admin.php?page=igny8-connection')); ?>"><?php _e('Connect now', 'igny8-bridge'); ?></a>
</p>
</div>
</div>
<?php endif; ?>
<div class="igny8-grid igny8-grid-3">
<!-- Connection Status Card -->
<div class="igny8-card">
<div class="igny8-card-header">
<h2>
<svg fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13.828 10.172a4 4 0 00-5.656 0l-4 4a4 4 0 105.656 5.656l1.102-1.101m-.758-4.899a4 4 0 005.656 0l4-4a4 4 0 00-5.656-5.656l-1.1 1.1"/>
</svg>
<?php _e('Connection', 'igny8-bridge'); ?>
</h2>
</div>
<div style="padding: 16px 0;">
<?php if ($is_connected): ?>
<div class="igny8-status igny8-status-connected" style="font-size: 16px; padding: 8px 16px;">
<span class="igny8-status-indicator"></span>
<?php _e('Connected', 'igny8-bridge'); ?>
</div>
<?php if ($site_id): ?>
<p style="margin-top: 12px; font-size: 13px; color: var(--igny8-text-dim);">
<?php _e('Site ID:', 'igny8-bridge'); ?> <code><?php echo esc_html($site_id); ?></code>
</p>
<?php endif; ?>
<?php else: ?>
<div class="igny8-status igny8-status-disconnected" style="font-size: 16px; padding: 8px 16px;">
<span class="igny8-status-indicator"></span>
<?php _e('Not Connected', 'igny8-bridge'); ?>
</div>
<p style="margin-top: 12px;">
<a href="<?php echo esc_url(admin_url('admin.php?page=igny8-connection')); ?>" class="igny8-btn igny8-btn-primary">
<?php _e('Connect Now', 'igny8-bridge'); ?>
</a>
</p>
<?php endif; ?>
</div>
</div>
<!-- Sync Status Card -->
<div class="igny8-card">
<div class="igny8-card-header">
<h2>
<svg fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15"/>
</svg>
<?php _e('Last Sync', 'igny8-bridge'); ?>
</h2>
</div>
<div style="padding: 16px 0;">
<?php if ($last_sync): ?>
<p style="font-size: 14px; margin: 0;">
<?php echo esc_html(human_time_diff($last_sync, current_time('timestamp'))) . ' ' . __('ago', 'igny8-bridge'); ?>
</p>
<p style="font-size: 13px; color: var(--igny8-text-dim); margin: 8px 0 0 0;">
<?php echo esc_html(date_i18n(get_option('date_format') . ' ' . get_option('time_format'), $last_sync)); ?>
</p>
<?php else: ?>
<p style="font-size: 14px; color: var(--igny8-text-dim); margin: 0;">
<?php _e('No sync performed yet', 'igny8-bridge'); ?>
</p>
<?php endif; ?>
</div>
</div>
<!-- Plugin Info Card -->
<div class="igny8-card">
<div class="igny8-card-header">
<h2>
<svg fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"/>
</svg>
<?php _e('Plugin Info', 'igny8-bridge'); ?>
</h2>
</div>
<div style="padding: 16px 0;">
<p style="font-size: 14px; margin: 0 0 8px 0;">
<strong><?php _e('Version:', 'igny8-bridge'); ?></strong> <?php echo esc_html(IGNY8_BRIDGE_VERSION); ?>
</p>
<p style="font-size: 14px; margin: 0;">
<strong><?php _e('PHP:', 'igny8-bridge'); ?></strong> <?php echo esc_html(PHP_VERSION); ?>
</p>
</div>
</div>
</div>
<!-- Quick Actions -->
<div class="igny8-card">
<div class="igny8-card-header">
<h2>
<svg fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 10V3L4 14h7v7l9-11h-7z"/>
</svg>
<?php _e('Quick Actions', 'igny8-bridge'); ?>
</h2>
</div>
<div class="igny8-grid igny8-grid-2" style="padding: 16px 0;">
<div>
<h3 style="font-size: 16px; margin: 0 0 12px 0;"><?php _e('Connection', 'igny8-bridge'); ?></h3>
<p style="font-size: 14px; color: var(--igny8-text-dim); margin-bottom: 16px;">
<?php _e('Manage your API connection and authentication', 'igny8-bridge'); ?>
</p>
<a href="<?php echo esc_url(admin_url('admin.php?page=igny8-connection')); ?>" class="igny8-btn igny8-btn-secondary">
<?php _e('Manage Connection', 'igny8-bridge'); ?>
</a>
</div>
<div>
<h3 style="font-size: 16px; margin: 0 0 12px 0;"><?php _e('Controls', 'igny8-bridge'); ?></h3>
<p style="font-size: 14px; color: var(--igny8-text-dim); margin-bottom: 16px;">
<?php _e('Configure which content types to sync', 'igny8-bridge'); ?>
</p>
<a href="<?php echo esc_url(admin_url('admin.php?page=igny8-controls')); ?>" class="igny8-btn igny8-btn-secondary">
<?php _e('Configure Controls', 'igny8-bridge'); ?>
</a>
</div>
<div>
<h3 style="font-size: 16px; margin: 0 0 12px 0;"><?php _e('Sync Settings', 'igny8-bridge'); ?></h3>
<p style="font-size: 14px; color: var(--igny8-text-dim); margin-bottom: 16px;">
<?php _e('Configure automatic sync and content updates', 'igny8-bridge'); ?>
</p>
<a href="<?php echo esc_url(admin_url('admin.php?page=igny8-sync')); ?>" class="igny8-btn igny8-btn-secondary">
<?php _e('Sync Settings', 'igny8-bridge'); ?>
</a>
</div>
<div>
<h3 style="font-size: 16px; margin: 0 0 12px 0;"><?php _e('View Logs', 'igny8-bridge'); ?></h3>
<p style="font-size: 14px; color: var(--igny8-text-dim); margin-bottom: 16px;">
<?php _e('Review sync history and troubleshoot issues', 'igny8-bridge'); ?>
</p>
<a href="<?php echo esc_url(admin_url('admin.php?page=igny8-logs')); ?>" class="igny8-btn igny8-btn-secondary">
<?php _e('View Logs', 'igny8-bridge'); ?>
</a>
</div>
</div>
</div>
<?php
// Include layout footer
include IGNY8_BRIDGE_PLUGIN_DIR . 'admin/layout-footer.php';
?>

View File

@@ -0,0 +1,148 @@
<?php
/**
* Data Page
*
* @package Igny8Bridge
*/
// Prevent direct access
if (!defined('ABSPATH')) {
exit;
}
// Include layout header
include IGNY8_BRIDGE_PLUGIN_DIR . 'admin/layout-header.php';
// Get data statistics
$link_queue = get_option('igny8_link_queue', array());
$pending_links = array_filter($link_queue, function($item) {
return isset($item['status']) && $item['status'] === 'pending';
});
$total_links = count($link_queue);
$pending_count = count($pending_links);
$processed_count = $total_links - $pending_count;
?>
<div class="igny8-page-header">
<h1><?php _e('Data', 'igny8-bridge'); ?></h1>
<p><?php _e('View synced data, queues, and content status', 'igny8-bridge'); ?></p>
</div>
<!-- Statistics -->
<div class="igny8-grid igny8-grid-3">
<div class="igny8-card">
<div class="igny8-card-header">
<h2>
<svg fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13.828 10.172a4 4 0 00-5.656 0l-4 4a4 4 0 105.656 5.656l1.102-1.101m-.758-4.899a4 4 0 005.656 0l4-4a4 4 0 00-5.656-5.656l-1.1 1.1"/>
</svg>
<?php _e('Total Links', 'igny8-bridge'); ?>
</h2>
</div>
<div style="padding: 16px 0;">
<p style="font-size: 32px; font-weight: 700; margin: 0; color: var(--igny8-primary);">
<?php echo esc_html($total_links); ?>
</p>
</div>
</div>
<div class="igny8-card">
<div class="igny8-card-header">
<h2>
<svg fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z"/>
</svg>
<?php _e('Pending', 'igny8-bridge'); ?>
</h2>
</div>
<div style="padding: 16px 0;">
<p style="font-size: 32px; font-weight: 700; margin: 0; color: var(--igny8-warning);">
<?php echo esc_html($pending_count); ?>
</p>
</div>
</div>
<div class="igny8-card">
<div class="igny8-card-header">
<h2>
<svg fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z"/>
</svg>
<?php _e('Processed', 'igny8-bridge'); ?>
</h2>
</div>
<div style="padding: 16px 0;">
<p style="font-size: 32px; font-weight: 700; margin: 0; color: var(--igny8-success);">
<?php echo esc_html($processed_count); ?>
</p>
</div>
</div>
</div>
<!-- Link Queue -->
<?php if (!empty($link_queue)): ?>
<div class="igny8-card">
<div class="igny8-card-header">
<h2>
<svg fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 6h16M4 10h16M4 14h16M4 18h16"/>
</svg>
<?php _e('Link Queue', 'igny8-bridge'); ?>
</h2>
</div>
<table class="igny8-table">
<thead>
<tr>
<th><?php _e('Post Title', 'igny8-bridge'); ?></th>
<th><?php _e('Target URL', 'igny8-bridge'); ?></th>
<th><?php _e('Anchor', 'igny8-bridge'); ?></th>
<th><?php _e('Status', 'igny8-bridge'); ?></th>
</tr>
</thead>
<tbody>
<?php foreach (array_slice($link_queue, 0, 10) as $item): ?>
<tr>
<td>
<?php
$post = get_post($item['post_id'] ?? 0);
echo $post ? esc_html($post->post_title) : __('Unknown Post', 'igny8-bridge');
?>
</td>
<td>
<a href="<?php echo esc_url($item['target_url'] ?? '#'); ?>" target="_blank" style="color: var(--igny8-primary);">
<?php echo esc_html(isset($item['target_url']) ? parse_url($item['target_url'], PHP_URL_HOST) : '—'); ?>
</a>
</td>
<td><?php echo esc_html($item['anchor'] ?? '—'); ?></td>
<td>
<?php if (($item['status'] ?? '') === 'pending'): ?>
<span class="igny8-status igny8-status-disconnected"><?php _e('Pending', 'igny8-bridge'); ?></span>
<?php else: ?>
<span class="igny8-status igny8-status-connected"><?php _e('Processed', 'igny8-bridge'); ?></span>
<?php endif; ?>
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
<?php if (count($link_queue) > 10): ?>
<p style="margin-top: 16px; color: var(--igny8-text-dim); font-size: 14px;">
<?php printf(__('Showing 10 of %d total items', 'igny8-bridge'), count($link_queue)); ?>
</p>
<?php endif; ?>
</div>
<?php else: ?>
<div class="igny8-alert igny8-alert-success">
<svg fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z"/>
</svg>
<span><?php _e('No items in link queue. All links have been processed.', 'igny8-bridge'); ?></span>
</div>
<?php endif; ?>
<?php
// Include layout footer
include IGNY8_BRIDGE_PLUGIN_DIR . 'admin/layout-footer.php';
?>

View File

@@ -0,0 +1,100 @@
<?php
/**
* Logs Page
*
* @package Igny8Bridge
*/
// Prevent direct access
if (!defined('ABSPATH')) {
exit;
}
// Include layout header
include IGNY8_BRIDGE_PLUGIN_DIR . 'admin/layout-header.php';
// Get logs
$webhook_logs = igny8_get_webhook_logs(array('limit' => 20));
?>
<div class="igny8-page-header">
<h1><?php _e('Logs', 'igny8-bridge'); ?></h1>
<p><?php _e('Review webhook activity and troubleshoot sync issues', 'igny8-bridge'); ?></p>
</div>
<?php if (!empty($webhook_logs)): ?>
<div class="igny8-card">
<div class="igny8-card-header">
<h2>
<svg fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z"/>
</svg>
<?php _e('Webhook Logs', 'igny8-bridge'); ?>
</h2>
</div>
<table class="igny8-table">
<thead>
<tr>
<th><?php _e('Timestamp', 'igny8-bridge'); ?></th>
<th><?php _e('Event', 'igny8-bridge'); ?></th>
<th><?php _e('Status', 'igny8-bridge'); ?></th>
<th><?php _e('Response Time', 'igny8-bridge'); ?></th>
</tr>
</thead>
<tbody>
<?php foreach ($webhook_logs as $log): ?>
<tr>
<td>
<?php
$timestamp = isset($log['timestamp']) ? $log['timestamp'] : 0;
echo esc_html(date_i18n(get_option('date_format') . ' ' . get_option('time_format'), $timestamp));
?>
</td>
<td>
<strong><?php echo esc_html($log['event'] ?? __('Unknown', 'igny8-bridge')); ?></strong>
</td>
<td>
<?php if (($log['status'] ?? '') === 'success'): ?>
<span class="igny8-status igny8-status-connected">✓ <?php _e('Success', 'igny8-bridge'); ?></span>
<?php else: ?>
<span class="igny8-status igny8-status-disconnected">✗ <?php _e('Failed', 'igny8-bridge'); ?></span>
<?php endif; ?>
</td>
<td>
<?php
$response_time = isset($log['response_time']) ? $log['response_time'] : 0;
echo esc_html(number_format($response_time, 2)) . 'ms';
?>
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</div>
<div class="igny8-alert igny8-alert-success" style="margin-top: 24px;">
<svg fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"/>
</svg>
<span>
<?php _e('For detailed API request/response logs, check', 'igny8-bridge'); ?>
<code>wp-content/debug.log</code>
</span>
</div>
<?php else: ?>
<div class="igny8-alert igny8-alert-warning">
<svg fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"/>
</svg>
<div>
<strong><?php _e('No Logs Available', 'igny8-bridge'); ?></strong>
<p><?php _e('No webhook activity has been recorded yet. Logs will appear here once IGNY8 starts sending content to your site.', 'igny8-bridge'); ?></p>
</div>
</div>
<?php endif; ?>
<?php
// Include layout footer
include IGNY8_BRIDGE_PLUGIN_DIR . 'admin/layout-footer.php';
?>

View File

@@ -0,0 +1,169 @@
<?php
/**
* Sync Page
*
* @package Igny8Bridge
*/
// Prevent direct access
if (!defined('ABSPATH')) {
exit;
}
// Include layout header
include IGNY8_BRIDGE_PLUGIN_DIR . 'admin/layout-header.php';
// Get sync settings
$connection_enabled = igny8_is_connection_enabled();
$last_site_sync = intval(get_option('igny8_last_site_sync', 0));
$last_taxonomy_sync = intval(get_option('igny8_last_taxonomy_sync', 0));
$last_keyword_sync = intval(get_option('igny8_last_keyword_sync', 0));
$last_writer_sync = intval(get_option('igny8_last_writer_sync', 0));
$next_site_sync = wp_next_scheduled('igny8_sync_site_data');
$date_format = get_option('date_format');
$time_format = get_option('time_format');
$last_site_sync_formatted = $last_site_sync ? date_i18n($date_format . ' ' . $time_format, $last_site_sync) : __('Never', 'igny8-bridge');
$last_taxonomy_sync_formatted = $last_taxonomy_sync ? date_i18n($date_format . ' ' . $time_format, $last_taxonomy_sync) : __('Never', 'igny8-bridge');
$last_keyword_sync_formatted = $last_keyword_sync ? date_i18n($date_format . ' ' . $time_format, $last_keyword_sync) : __('Never', 'igny8-bridge');
$last_writer_sync_formatted = $last_writer_sync ? date_i18n($date_format . ' ' . $time_format, $last_writer_sync) : __('Never', 'igny8-bridge');
$next_site_sync_formatted = $next_site_sync ? date_i18n($date_format . ' ' . $time_format, $next_site_sync) : __('Not scheduled', 'igny8-bridge');
?>
<div class="igny8-page-header">
<h1><?php _e('Sync', 'igny8-bridge'); ?></h1>
<p><?php _e('Configure automatic sync and monitor synchronization status', 'igny8-bridge'); ?></p>
</div>
<form method="post" action="options.php">
<?php settings_fields('igny8_bridge_connection'); ?>
<!-- Connection Enable/Disable -->
<div class="igny8-card">
<div class="igny8-card-header">
<h2>
<svg fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 7h12m0 0l-4-4m4 4l-4 4m0 6H4m0 0l4 4m-4-4l4-4"/>
</svg>
<?php _e('Sync Status', 'igny8-bridge'); ?>
</h2>
</div>
<label style="display: flex; align-items: center; cursor: pointer; padding: 16px; background: var(--igny8-surface); border-radius: var(--igny8-radius-base);">
<input
type="checkbox"
name="igny8_connection_enabled"
value="1"
<?php checked($connection_enabled, 1); ?>
style="margin-right: 12px;"
/>
<div>
<strong style="font-size: 16px; display: block; margin-bottom: 4px;">
<?php _e('Enable IGNY8 Sync', 'igny8-bridge'); ?>
</strong>
<span style="color: var(--igny8-text-dim); font-size: 14px;">
<?php _e('Allow IGNY8 to sync content to this WordPress site. Disable this to pause syncing temporarily.', 'igny8-bridge'); ?>
</span>
</div>
</label>
</div>
<button type="submit" class="igny8-btn igny8-btn-primary">
<svg fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 13l4 4L19 7"/>
</svg>
<?php _e('Save Sync Settings', 'igny8-bridge'); ?>
</button>
</form>
<!-- Sync History -->
<div class="igny8-card">
<div class="igny8-card-header">
<h2>
<svg fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z"/>
</svg>
<?php _e('Sync History', 'igny8-bridge'); ?>
</h2>
</div>
<table class="igny8-table">
<thead>
<tr>
<th><?php _e('Sync Type', 'igny8-bridge'); ?></th>
<th><?php _e('Last Sync', 'igny8-bridge'); ?></th>
<th><?php _e('Status', 'igny8-bridge'); ?></th>
</tr>
</thead>
<tbody>
<tr>
<td><strong><?php _e('Site Data', 'igny8-bridge'); ?></strong></td>
<td><?php echo esc_html($last_site_sync_formatted); ?></td>
<td>
<?php if ($last_site_sync): ?>
<span class="igny8-status igny8-status-connected">✓ <?php _e('Synced', 'igny8-bridge'); ?></span>
<?php else: ?>
<span class="igny8-status igny8-status-disconnected">— <?php _e('Never', 'igny8-bridge'); ?></span>
<?php endif; ?>
</td>
</tr>
<tr>
<td><strong><?php _e('Taxonomies', 'igny8-bridge'); ?></strong></td>
<td><?php echo esc_html($last_taxonomy_sync_formatted); ?></td>
<td>
<?php if ($last_taxonomy_sync): ?>
<span class="igny8-status igny8-status-connected">✓ <?php _e('Synced', 'igny8-bridge'); ?></span>
<?php else: ?>
<span class="igny8-status igny8-status-disconnected">— <?php _e('Never', 'igny8-bridge'); ?></span>
<?php endif; ?>
</td>
</tr>
<tr>
<td><strong><?php _e('Keywords', 'igny8-bridge'); ?></strong></td>
<td><?php echo esc_html($last_keyword_sync_formatted); ?></td>
<td>
<?php if ($last_keyword_sync): ?>
<span class="igny8-status igny8-status-connected">✓ <?php _e('Synced', 'igny8-bridge'); ?></span>
<?php else: ?>
<span class="igny8-status igny8-status-disconnected">— <?php _e('Never', 'igny8-bridge'); ?></span>
<?php endif; ?>
</td>
</tr>
<tr>
<td><strong><?php _e('Writers', 'igny8-bridge'); ?></strong></td>
<td><?php echo esc_html($last_writer_sync_formatted); ?></td>
<td>
<?php if ($last_writer_sync): ?>
<span class="igny8-status igny8-status-connected">✓ <?php _e('Synced', 'igny8-bridge'); ?></span>
<?php else: ?>
<span class="igny8-status igny8-status-disconnected">— <?php _e('Never', 'igny8-bridge'); ?></span>
<?php endif; ?>
</td>
</tr>
</tbody>
</table>
</div>
<!-- Next Scheduled Sync -->
<div class="igny8-card">
<div class="igny8-card-header">
<h2>
<svg fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 7V3m8 4V3m-9 8h10M5 21h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v12a2 2 0 002 2z"/>
</svg>
<?php _e('Scheduled Syncs', 'igny8-bridge'); ?>
</h2>
</div>
<div class="igny8-form-group">
<label><?php _e('Next Site Data Sync', 'igny8-bridge'); ?></label>
<p style="margin: 0; font-size: 15px; font-weight: 600;">
<?php echo esc_html($next_site_sync_formatted); ?>
</p>
</div>
</div>
<?php
// Include layout footer
include IGNY8_BRIDGE_PLUGIN_DIR . 'admin/layout-footer.php';
?>

View File

@@ -3,7 +3,7 @@
* Plugin Name: IGNY8 WordPress Bridge
* Plugin URI: https://igny8.com/igny8-wp-bridge
* Description: Lightweight bridge plugin that connects WordPress to IGNY8 API for one-way content publishing.
* Version: 1.1.2
* Version: 1.2.0
* Author: IGNY8
* Author URI: https://igny8.com/
* License: GPL v2 or later
@@ -22,7 +22,7 @@ if (!defined('ABSPATH')) {
}
// Define plugin constants
define('IGNY8_BRIDGE_VERSION', '1.1.0');
define('IGNY8_BRIDGE_VERSION', '1.2.0');
define('IGNY8_BRIDGE_PLUGIN_DIR', plugin_dir_path(__FILE__));
define('IGNY8_BRIDGE_PLUGIN_URL', plugin_dir_url(__FILE__));
define('IGNY8_BRIDGE_PLUGIN_FILE', __FILE__);