# DraftSEO.AI - WordPress Plugin

Publish AI-generated blogs from DraftSEO.AI platform directly to WordPress with automatic image import and SEO optimization.

## Installation

### From WordPress.org (Recommended)

1. Go to WordPress Admin → Plugins → Add New
2. Search for "DraftSEO.AI"
3. Click "Install Now" and then "Activate"

### Manual Installation

1. Download the plugin ZIP file
2. Go to WordPress Admin → Plugins → Add New → Upload Plugin
3. Choose the ZIP file and click "Install Now"
4. Activate the plugin

### From this Repository

1. Clone or download this repository
2. Copy the `draftseo-ai` directory to `wp-content/plugins/`
3. Go to WordPress Admin → Plugins
4. Find "DraftSEO.AI" and click "Activate"

## Configuration

1. Navigate to WordPress Admin → DraftSEO → Settings
2. Click "Connect with DraftSEO.AI" button
   - You'll be automatically redirected to DraftSEO.AI
   - Sign in if you're not already logged in
   - Connection completes automatically (OAuth-based, no API key needed)

## Features

### Core Features
- ✅ One-click publishing from DraftSEO.AI to WordPress
- ✅ Automatic image import from DraftSEO.AI to WordPress Media Library
- ✅ SEO metadata preservation (keywords, meta descriptions)
- ✅ WordPress category sync
- ✅ Automatic tag creation from blog keywords
- ✅ Multiple post status options (draft, publish, schedule)
- ✅ Secure API key encryption and HMAC-SHA256 webhook signatures

### Image Handling
The plugin intelligently handles images based on blog size:

- **1-5 images**: Direct import (10-20 seconds)
- **6+ images**: Hybrid approach
  - Featured image imported immediately
  - Remaining images processed in background via WordPress Cron

All images are downloaded directly from DraftSEO.AI to your WordPress Media Library.


## Usage

### Publishing a Blog

1. **Generate Blog on DraftSEO.AI**
   - Create your blog using DraftSEO.AI platform
   - Configure SEO settings, categories, and tags

2. **Publish to WordPress**
   - Click "Publish to WordPress" button
   - Select your connected WordPress site
   - Choose publishing options (author, category, status, date)
   - Click "Publish"


### API Endpoints

The plugin provides REST API endpoints for DraftSEO.AI integration:

- `GET /wp-json/draftseo/v1/users` - Get WordPress users
- `GET /wp-json/draftseo/v1/categories` - Get WordPress categories
- `GET /wp-json/draftseo/v1/tags` - Get WordPress tags
- `POST /wp-json/draftseo/v1/publish` - Publish blog to WordPress
- `POST /wp-json/draftseo/v1/update` - Update/republish existing post
- `GET|POST /wp-json/draftseo/v1/test-connection` - Test API connection
- `POST /wp-json/draftseo/v1/remote-disconnect` - Clear connection (called by DraftSEO.AI)

All endpoints require Bearer token authentication using your API key.

## Requirements

- **WordPress**: 6.2 or higher
- **PHP**: 7.4 or higher
- **MySQL**: 5.6 or higher
- **WordPress Cron**: Enabled (for background image processing)
- **DraftSEO.AI Account**: Active subscription

## File Structure

```
draftseo-ai/
├── admin/                          # Admin interface files
│   ├── css/
│   │   └── admin-styles.css       # Admin page styles
│   ├── js/
│   │   └── admin-scripts.js       # Admin page JavaScript
│   └── settings-page.php          # Settings page template
├── includes/                       # Core plugin classes
│   ├── class-api-client.php       # DraftSEO.AI API communication
│   ├── class-content-processor.php # HTML cleanup and formatting
│   ├── class-image-handler.php    # Image import strategies
│   ├── class-rest-api.php         # WordPress REST API endpoints
│   ├── class-seo-handler.php      # SEO metadata management
│   └── class-settings.php         # Settings and API key management
├── languages/                      # Translation files
├── draftseo-ai.php         # Main plugin file
├── LICENSE.txt                     # GPL v2 license
├── readme.txt                      # WordPress.org readme
├── uninstall.php                  # Uninstall handler
└── README.md                       # This file
```

## Development

### Coding Standards
- Follows WordPress Coding Standards (WPCS)
- PHPCS compliant
- Nonces for security
- Capability checks for permissions
- Input validation and sanitization

### Hooks and Filters

#### Actions
- `draftseo_process_images_background` - Background image processing
- `draftseo_before_publish` - Before publishing a blog
- `draftseo_after_publish` - After publishing a blog

#### Filters
- `draftseo_content_cleanup` - Modify content cleanup settings
- `draftseo_seo_metadata` - Modify SEO metadata before saving
- `draftseo_image_import_strategy` - Override automatic strategy selection

## Troubleshooting

### Connection Test Fails
- Verify API key is correct
- Check WordPress site URL is accessible from internet
- Ensure REST API is enabled on WordPress
- Check firewall/security plugins aren't blocking API requests

### Images Not Importing
- Check WordPress has write permissions to uploads directory
- Verify WordPress Cron is running (test with WP Crontrol plugin)
- Check PHP `max_execution_time` setting (should be 60+ seconds)
- Review WordPress error logs for image download errors

### Tags Not Created
- Ensure "Auto-create tags" is enabled in settings
- Check keywords are provided in the blog data
- Verify user has permission to create tags
- Check maximum tags limit in settings

## Support

- **Support**: [Contact Support](https://draftseo.ai/contact)
- **WordPress.org**: [Plugin Support Forum](https://wordpress.org/support/plugin/draftseo-ai)

## License

This plugin is licensed under the GNU General Public License v2 or later.

## Credits

Developed by [DraftSEO.AI](https://draftseo.ai)

## Changelog

### 1.0.2

Content formatting and SEO structured data hotfix release.

- **In-text citations** — `[1]`, `[2]` markers now render as clickable superscript links that scroll to the matching reference in the References section
- **References section** — Converted to a styled numbered list with anchor IDs (`#ref-1`, `#ref-2`) for citation linking; supports all reference styles (url_title, harvard_apa6, mla9, etc.)
- **External links** — All external links now open in a new tab with `target="_blank"` and `rel="noopener noreferrer"` for security and better UX
- **FAQ Schema (JSON-LD)** — FAQ question-answer pairs are extracted from blog content and injected as structured data in the WordPress post `<head>` for Google FAQ rich results
- **Front-end CSS** — Plugin now injects lightweight CSS for consistent styling of citations, references, and tables across all WordPress themes
- **Content sanitization** — Updated `wp_kses` allowlist to support `<sup>`, `<ol>`/`<li>` with `id`/`value`/`class`, and `<a>` with `target`/`rel` attributes
- **Active sites filter** — WordPress site dropdown now only shows active/connected sites

### 1.0.1

Hotfix for content rendering in published posts.

- **YouTube video embeds** — Now render correctly using WordPress oEmbed (previously stripped by sanitization)
- **Data tables** — Now display as formatted HTML tables instead of raw markdown text
- **Content processor** — Updated to use custom sanitization allowlist with full table tag support
- **Removed legacy markdown-to-HTML converter** — All content conversion now handled on the platform side before sending

### 1.0.0

Major release with 30+ improvements across security, stability, performance, and API architecture.

#### Security (6 improvements)
- **HMAC-SHA256 webhook authentication** — Deactivation and disconnect webhooks now sign payloads with HMAC-SHA256 using the API key as the secret; the API key is never transmitted over the wire. Headers: `X-DraftSEO-Signature`, `X-DraftSEO-Timestamp`
- **Replay protection** — Webhook requests include a Unix timestamp; requests older than 5 minutes are rejected to prevent replay attacks
- **Timing-safe comparisons** — All API key and signature comparisons use `hash_equals()` (PHP) and `crypto.timingSafeEqual` (Node.js) to prevent timing-based side-channel attacks
- **AES-256-CBC encryption** — API keys stored at rest using AES-256-CBC with a random IV per encryption, derived from WordPress auth salt (site-specific, not hardcoded)
- **Improved deactivation hook** — Now reads the API key via `DraftSEO_Settings::get_api_key()` (properly decrypted) for more reliable key handling
- **Enhanced key validation** — `verify_api_key()` now explicitly validates both stored and provided keys with specific, actionable error codes

#### API & REST Endpoint Improvements (7 improvements)
- **New `/tags` endpoint** — Added `GET /wp-json/draftseo/v1/tags` for tag synchronization, matching the existing `/users` and `/categories` endpoints
- **Unified endpoint architecture** — All three sync resources (users, categories, tags) now use the same plugin-first-then-fallback pattern via `fetchWithPluginFallback()`
- **Structured error responses** — All error responses now use proper `WP_Error` objects with specific error codes (`rest_forbidden`, `rest_missing_param`, `rest_publish_error`, `rest_update_error`, `rest_post_not_found`, `rest_tags_error`) for better debugging and integration
- **`rest_ensure_response()`** — All success responses now use `rest_ensure_response()` per WordPress REST API Handbook, allowing WordPress filters to process responses through the standard pipeline
- **Input validation arguments** — `/publish` and `/update` routes now define `args` with `validate_callback` and `sanitize_callback` for server-side input validation before the handler runs
- **Remote disconnect endpoint** — `/remote-disconnect` properly clears stored API key and connection settings when triggered from DraftSEO.AI platform
- **Bidirectional disconnect sync** — When a user disconnects from DraftSEO.AI, the platform now calls the plugin's `/remote-disconnect` endpoint before local deletion, keeping both sides in sync

#### Stability & Error Handling (6 improvements)
- **Non-JSON response resilience** — Gracefully handles HTML maintenance pages, WAF blocks, and 503 errors from WordPress instead of failing silently
- **Sync endpoint timeout & abort** — Added configurable timeout with AbortController to prevent hanging sync requests
- **Error isolation** — Per-card Error Boundaries in the WordPress site list ensure individual site issues don't affect other connected sites
- **Guarded data access** — All connection data property accesses use optional chaining with fallbacks for maximum reliability
- **Response validation** — API responses are validated as proper arrays/objects before processing for robust data handling
- **Health check hardening** — Health check response parsing improved with dedicated error paths for edge cases

#### Performance & Optimization (4 improvements)
- **Parallel sync** — Users, categories, and tags are fetched simultaneously via `Promise.all()` instead of sequentially
- **Smart retry logic** — 4xx client errors (401, 403, 400, 422) skip retry entirely; only 5xx server errors are retried, reducing wasted API calls
- **Optimized cache invalidation** — Streamlined cache invalidation strategy; added health check invalidation after sync for immediate UI updates
- **Image import strategy** — Intelligent strategy selection: 1-5 images use direct import (fast), 6+ images use hybrid approach (featured image immediate, rest via WordPress Cron background processing)

#### Usability
- **Settings link on Plugins page** — Added "Settings" quick-access link on the Plugins page (next to Deactivate) for one-click access to plugin configuration

#### WordPress Best Practices
- Requires WordPress 6.2+ and PHP 7.4+
- Follows WordPress Coding Standards (WPCS)
- Uses `wp_kses_post()` for content sanitization
- Nonces for admin AJAX security
- Capability checks (`manage_options`) for settings access
- Content cleanup: responsive table wrapping, blockquote formatting
- Publication logging to custom database table
- Image duplicate detection via URL hash with WordPress object cache

#### Tag Management
- Auto-create tags from AI-generated keywords (configurable 1-10 count)
- Manual tag selection from existing WordPress tags
- Custom tags: create new tags on-the-fly during publishing

#### Image Handling
- Direct download from DraftSEO.AI to WordPress Media Library
- Alt text and heading text metadata preserved
- Featured image setting with URL replacement in post content (Nebius URLs → local WordPress URLs)
- Background processing via WordPress Cron for large image sets (6+ images)

### 0.2.0 (Initial Beta)
- One-click blog publishing from DraftSEO.AI
- Automatic image import from DraftSEO.AI
- SEO metadata transfer
- WordPress category sync
- Auto-create tags from keywords
- Multiple post status options
- Content cleanup and formatting
- Secure API key encryption
- Background image processing for large blogs
- Remote disconnect synchronization
- OAuth-based connection flow
