# 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.1.2

Security update: fixes API token authentication on WordPress sites where the Application Passwords feature or a security plugin was blocking server-to-server requests from DraftSEO.AI before they could be validated.

### 1.1.0

- **Image Replacement** — When a new image is generated and republished from DraftSEO.ai, replace the image inside the blog automatically.
- **Assets Cleanup** — The old now-unused image is auto-deleted from the Media folder in Wordpress. Saves storage space, cleans up unused media assets.

### 1.0.5

YouTube video embeds now work on WordPress.

- **YouTube videos fixed** — YouTube videos were not appearing on published WordPress posts. Videos are now properly converted to native WordPress embed blocks before publishing, so they show up as responsive YouTube players on your site. Previously published posts need to be republished to pick up this fix.

### 1.0.4

Content formatting and image fixes.

- **Headings after images fixed** — Headings that appeared immediately after an image were showing as plain text instead of proper headings. They now display correctly.
- **Unwanted image captions removed** — Image descriptions were showing as visible text below every image on the page. They are now used for accessibility only (screen readers) and no longer display as captions.

### 1.0.3

Content formatting fixes.

- **Heading formatting fixed** — Some headings were appearing as plain text instead of proper headings. They now render correctly on WordPress.
- **Citation links fixed** — Citation references in blog posts were being malformed during publishing. They now display as clean numbered references.

### 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
