# 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 — all images downloaded in parallel and imported immediately (typically 5–15 seconds)
- **6+ images**: Hybrid approach
  - Featured image imported immediately
  - Remaining images downloaded in parallel and imported in the background via Action Scheduler (no page visit needed)

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.2.0

Image reliability fixes, automatic self-repair for all previously affected posts, and continuous background health monitoring.

- **Images missing after publishing** — Images were sometimes missing from the WordPress Media Library after publishing from DraftSEO. This happened silently on certain hosting environments due to differences in how the server handles external downloads. The plugin now tries an additional download method before giving up, significantly increasing import success rates across all hosting environments.
- **Automatic repair for previously affected posts** — After updating, the plugin automatically scans your entire site in the background for any posts where images were never properly imported from DraftSEO. Affected posts are silently repaired without any action needed from you. Progress is visible under **Tools → Scheduled Actions** in your WordPress admin.
- **Daily background check for image health** — A lightweight check now runs automatically every 24 hours. If any post is found with images that didn't fully import — for example because a server timeout interrupted the process overnight — the plugin queues a repair automatically. No monitoring required: healthy sites see no activity and affected posts are fixed without manual intervention.
- **Health checks stay fast as your site grows** — Once a post's images are confirmed fully imported, the daily check permanently skips it in future runs, so it stays fast regardless of how many posts are on your site. On a fully audited site the check completes in a fraction of a second. Republishing a post from DraftSEO automatically resets it so new images are always verified.
- **Built to handle high-volume publishing** — Designed and tested for sites publishing large numbers of blogs, each with many images. Image imports run in controlled parallel batches to stay within the resource limits of any hosting environment, from shared hosting to dedicated servers.

### 1.1.6

Faster republishing — only new or changed images are re-downloaded.

- **No redundant image downloads on republish** — Previously, every republish downloaded all images from the CDN to WordPress even if nothing had changed. The plugin now checks which images are already in the Media Library before downloading anything, and skips the download entirely for images it has already imported. For a typical blog republish where images haven't changed, this means zero image downloads.

### 1.1.5

Reliability improvements for publishing and republishing.

- **Republish no longer blocked** — Republishing a post could fail if images from a previous publish were still importing in the background. Republishing now always works as long as the post exists on WordPress.
- **Accurate publish completion** — DraftSEO now correctly waits for all images to finish importing before marking a publish as complete, rather than marking it done immediately. Status in DraftSEO now reflects reality.
- **Automatic retry on delivery failure** — If DraftSEO cannot be reached to confirm image import completion, the plugin will retry automatically instead of leaving the publish stuck.

### 1.1.4

Cosmetic and connectivity improvements.

- **Settings heading updated** — Page title now reads "DraftSEO.ai" with the installed plugin version displayed next to it in small grey text.
- **"Connection Issue" state** — When authentication with DraftSEO.ai fails, the connection badge now shows an amber "Connection Issue" status instead of the green "Connected" badge. A guided panel explains the four steps needed to reconnect.
- **Cleaner settings button** — The "Go to DraftSEO.ai" button on the connected settings page no longer shows an icon; text updated to match current branding.

### 1.1.3

Reliability improvements, faster image loading, and cleaner background processing.

- **Images now load reliably on quiet sites** — Images were sometimes not appearing on published posts on low-traffic websites because background download jobs weren't starting until the next page visit. They now start immediately regardless of site traffic.
- **Clean deactivation** — Switching the plugin off while a publish was in progress could leave background tasks running after deactivation. The plugin now cleanly stops all background jobs when it is turned off.
- **Proper uninstall disconnect** — Removing the plugin now correctly notifies DraftSEO.AI and closes the connection before all plugin data is deleted.
- **Translations fixed** — Plugin text was not translating correctly on WordPress sites running in a language other than English. Translation files now load properly.
- **Action Scheduler for background jobs** — Background image jobs now run via Action Scheduler instead of WP Cron. Action Scheduler does not need a page visit to start — it runs as a true background process — and retries jobs automatically if a download fails. Pending and completed jobs are visible in the WordPress admin at **Tools > Scheduled Actions**.
- **Parallel image downloads** — Images in background jobs are now downloaded simultaneously before being imported into the Media Library. For a blog with 20 images this cuts total download time from roughly 60–100 seconds (sequential) to 5–15 seconds (parallel).
- **No duplicate images on republish** — Republishing a post was creating a duplicate Media Library entry for every image that had not changed since the previous publish. Only new or replaced images are now downloaded and imported — unchanged images are reused from the existing entry, keeping the Media Library clean.

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

Automatic cleanup when republishing with a new image.

- **Image replacement** — When you republish a post with a new AI-generated image, the new image is swapped in automatically
- **Media cleanup** — The previous image is removed from your WordPress Media Library — keeps your media folder clean and saves storage space

### 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"`
- **FAQ structured data (JSON-LD)** — FAQ question-answer pairs are extracted from blog content and injected into the post `<head>` for Google FAQ rich results
- **Theme-consistent styling** — CSS is injected for citations, references, and tables so they display correctly across all WordPress themes
- **HTML element support** — `<sup>` for citations, `<ol>`/`<li>` with `id` attributes, and `<a>` with `target`/`rel` are now preserved in published post content
- **Active sites filter** — WordPress site dropdown now only shows active/connected sites

### 1.0.1

Hotfix for content rendering in published posts.

- **YouTube embeds** — Were being stripped during publishing; now render as embedded players on your site
- **Data tables** — Were displaying as raw Markdown text; now output as formatted HTML tables

### 1.0.0

Major release — security hardening, reliability improvements, and full tag management.

#### Security
- **Webhook signatures** — Disconnect and deactivation notifications are signed with HMAC-SHA256 (`X-DraftSEO-Signature`, `X-DraftSEO-Timestamp` headers); the API key is the signing secret and is never transmitted in plain text
- **Replay protection** — Signed requests include a Unix timestamp; requests older than 5 minutes are rejected
- **API keys encrypted at rest** — AES-256-CBC with a unique IV per key, derived from the WordPress site's auth salt

#### Publishing & REST API
- **Tags endpoint** — `GET /wp-json/draftseo/v1/tags` added for tag sync, matching the existing `/users` and `/categories` endpoints
- **Server-side input validation** — `/publish` and `/update` routes validate and sanitise all params before the handler runs
- **Structured error responses** — All errors return specific codes (`rest_forbidden`, `rest_missing_param`, `rest_publish_error`, etc.) for better debugging
- **Bidirectional disconnect** — Disconnecting from DraftSEO.AI calls `/remote-disconnect` to clear connection settings on the plugin side automatically

#### Reliability
- **Non-JSON response handling** — Gracefully handles HTML maintenance pages, WAF blocks, and 503 responses instead of failing silently
- **Sync timeouts** — All sync requests have a configurable timeout; no more indefinitely hung connections
- **Error isolation** — Individual site connection errors in the multi-site view do not affect other connected sites

#### Performance
- **Parallel sync** — Users, categories, and tags are fetched simultaneously instead of sequentially
- **Smart retries** — 4xx errors (401, 403, 400, 422) fail immediately without retrying; only 5xx server errors trigger retries

#### Tag Management
- Auto-create WordPress tags from AI-generated keywords at publish time (configurable, 1–10 tags)
- Select from existing WordPress tags, or create new ones on the fly during publishing

#### Image Handling
- All images downloaded directly to your WordPress Media Library
- Alt text from DraftSEO.AI preserved as WordPress image alt text
- Featured image set automatically; post content image URLs updated from DraftSEO.AI CDN to local Media Library URLs

#### Usability
- "Settings" quick-link added to the Plugins page for faster access to plugin configuration

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