# Publisher Module **Last Verified:** January 20, 2026 **Status:** ✅ Active **Version:** 1.8.4 **Backend Path:** `backend/igny8_core/modules/publisher/` + `backend/igny8_core/business/publishing/` **Frontend Path:** `frontend/src/pages/Publisher/` --- ## Quick Reference | What | File | Key Items | |------|------|-----------| | Views | `modules/publisher/views.py` | `PublishingRecordViewSet`, `DeploymentViewSet`, `PublishContentViewSet` | | Models | `business/publishing/models.py` | `PublishingRecord`, `DeploymentRecord` | | Integration Models | `modules/integration/models.py` | `PublishingSettings` | | **Unified Settings** | `modules/integration/views/unified_settings.py` | **v1.8.0** Consolidated settings API | | Services | `business/publishing/services/*.py` | Publishing orchestration | | Scheduler Tasks | `igny8_core/tasks/publishing_scheduler.py` | Celery beat tasks | | URLs | `modules/publisher/urls.py` | Publisher endpoints | | Frontend | `pages/Publisher/ContentCalendar.tsx` | Content calendar view | | **Site Settings** | `pages/Sites/AIAutomationSettings.tsx` | **v1.8.0** Unified settings UI | --- ## Purpose The Publisher module manages: - Content publishing pipeline - **Publishing scheduler (automated publishing)** - Publishing record tracking - Deployment management - Multi-destination publishing - **Content calendar visualization** **Settings Location (v1.8.0):** Site Settings → Automation tab > ⚠️ **v1.8.0 Change:** The standalone `/publisher/settings` page has been removed. Publishing settings are now configured in Site Settings → Automation tab under "Capacity" and "Schedule" sections. --- ## Data Models ### PublishingRecord | Field | Type | Purpose | |-------|------|---------| | account | FK | Owner account | | site | FK | Parent site | | content | FK | Source content | | destination | CharField | wordpress/ghost/webflow | | external_id | CharField | ID on destination platform | | external_url | URLField | Published URL | | status | CharField | pending/published/failed/retracted | | published_at | DateTime | Publication time | | metadata | JSON | Additional data | | created_at | DateTime | Record creation | ### DeploymentRecord | Field | Type | Purpose | |-------|------|---------| | account | FK | Owner account | | site | FK | Target site | | deployment_type | CharField | full/incremental | | status | CharField | pending/deploying/completed/failed | | items_deployed | Integer | Number of items | | started_at | DateTime | Start time | | completed_at | DateTime | Completion time | | error_log | TextField | Errors encountered | | metadata | JSON | Deployment details | ### PublishingSettings (v1.3.2, updated v1.8.0) Site-level publishing configuration: | Field | Type | Purpose | |-------|------|---------| | site | OneToOne | Parent site (unique) | | auto_approval_enabled | Boolean | Auto-approve content | | auto_publish_enabled | Boolean | Auto-publish approved content | | daily_publish_limit | Integer | Max publications per day | | weekly_publish_limit | Integer | Max publications per week | | monthly_publish_limit | Integer | Max publications per month | | publish_days | JSON | Days of week for publishing ["mon","wed","fri"] | | publish_time_slots | JSON | Time slots for publishing [{"start":"09:00","end":"17:00"}] | | **total_items_per_run** | Integer | **v1.8.0** Computed capacity display | ### Content Site Status Fields (v1.3.2) Added to Content model for scheduling: | Field | Type | Values | |-------|------|--------| | site_status | CharField | not_published, scheduled, publishing, published, failed | | scheduled_publish_at | DateTime | Future publication time (nullable) | | site_status_updated_at | DateTime | Last status change timestamp | --- ## API Endpoints ### Unified Settings API (v1.8.0) | Method | Path | Purpose | |--------|------|---------| | **GET** | `/api/v1/integration/sites/{site_id}/unified-settings/` | Get all automation + publishing settings | | **PATCH** | `/api/v1/integration/sites/{site_id}/unified-settings/` | Update settings | ### Publishing & Records | Method | Path | Handler | Purpose | |--------|------|---------|---------| | GET | `/api/v1/publisher/records/` | `PublishingRecordViewSet.list` | List publishing records | | POST | `/api/v1/publisher/records/` | `PublishingRecordViewSet.create` | Create record | | GET | `/api/v1/publisher/deployments/` | `DeploymentViewSet.list` | List deployments | | POST | `/api/v1/publisher/publish/` | `PublishContentViewSet.publish` | Publish content immediately | | GET | `/api/v1/publisher/publish/status/` | `PublishContentViewSet.status` | Get publishing status | | GET | `/api/v1/publisher/site-definition/` | `SiteDefinitionViewSet.list` | Public site definitions | ### Scheduling Endpoints (v1.3.2+) | Method | Path | Purpose | Request Body | Response | |--------|------|---------|--------------|----------| | **POST** | `/api/v1/writer/content/{id}/schedule/` | Schedule content for future publishing | `{ "scheduled_publish_at": "2025-01-20T09:00:00Z" }` | `{ "success": true, "scheduled_publish_at": "2025-01-20T09:00:00Z" }` | | **POST** | `/api/v1/writer/content/{id}/reschedule/` | Reschedule existing scheduled content | `{ "scheduled_at": "2025-01-21T10:00:00Z" }` | `{ "success": true, "scheduled_publish_at": "2025-01-21T10:00:00Z" }` | | **POST** | `/api/v1/writer/content/{id}/unschedule/` | Cancel scheduled publishing | `{}` | `{ "success": true, "message": "Content unscheduled" }` | | **POST** | `/api/v1/writer/content/bulk_schedule/` | Bulk schedule with site defaults | `{ "content_ids": [1,2,3], "use_site_defaults": true, "site_id": 5 }` | `{ "success": true, "scheduled_count": 3, "schedule_preview": [...] }` | | **POST** | `/api/v1/writer/content/bulk_schedule_preview/` | Preview bulk schedule times | `{ "content_ids": [1,2,3], "site_id": 5 }` | `{ "schedule_preview": [...], "site_settings": {...} }` | ### Legacy Publishing Settings (deprecated) > ⚠️ **Deprecated in v1.8.0:** Use unified-settings API instead | Method | Path | Purpose | |--------|------|---------| | ~~GET~~ | ~~`/api/v1/sites/{site_id}/settings?tab=publishing`~~ | ~~Get site publishing settings~~ | | ~~PUT~~ | ~~`/api/v1/sites/{site_id}/publishing-settings/`~~ | ~~Update publishing settings~~ | --- ## API Usage Examples ### Publish Content Immediately **Request:** ```bash POST /api/v1/publisher/publish/ Content-Type: application/json { "content_id": 123, "destinations": ["wordpress"] # or ["shopify"], ["custom"] } ``` **Success Response:** ```json { "success": true, "data": { "success": true, "results": [ { "destination": "wordpress", "success": true, "external_id": "456", "url": "https://mysite.com/article-title/", "publishing_record_id": 789, "platform_type": "wordpress" } ] } } ``` **Error Response:** ```json { "success": false, "error": "Publishing API error: Invalid credentials" } ``` ### Schedule Content for Future Publishing **Request:** ```bash POST /api/v1/writer/content/123/schedule/ Content-Type: application/json { "scheduled_publish_at": "2025-01-20T09:00:00Z" } ``` **Response:** ```json { "success": true, "scheduled_publish_at": "2025-01-20T09:00:00Z", "site_status": "scheduled" } ``` **Notes:** - Content `site_status` changes from `not_published` → `scheduled` - Celery task `process_scheduled_publications` will publish at scheduled time - Runs every 5 minutes, so publishing happens within 5 min of scheduled time ### Reschedule Content **Request:** ```bash POST /api/v1/writer/content/123/reschedule/ Content-Type: application/json { "scheduled_at": "2025-01-21T10:00:00Z" } ``` **Response:** ```json { "success": true, "scheduled_publish_at": "2025-01-21T10:00:00Z", "site_status": "scheduled" } ``` **Use Cases:** - Reschedule from `site_status='scheduled'` (change time) - Reschedule from `site_status='failed'` (retry at new time) ### Unschedule Content **Request:** ```bash POST /api/v1/writer/content/123/unschedule/ Content-Type: application/json {} ``` **Response:** ```json { "success": true, "message": "Content unscheduled successfully", "site_status": "not_published" } ``` **Notes:** - Removes content from publishing queue - Content returns to `site_status='not_published'` - Can be rescheduled or published immediately later ### Bulk Schedule with Site Defaults **Request:** ```bash POST /api/v1/writer/content/bulk_schedule/ Content-Type: application/json { "content_ids": [123, 124, 125, 126], "use_site_defaults": true, "site_id": 45 } ``` **Response:** ```json { "success": true, "scheduled_count": 4, "schedule_preview": [ { "content_id": 123, "scheduled_at": "2025-01-17T09:00:00Z", "title": "First Article" }, { "content_id": 124, "scheduled_at": "2025-01-17T09:15:00Z", "title": "Second Article" }, { "content_id": 125, "scheduled_at": "2025-01-17T09:30:00Z", "title": "Third Article" }, { "content_id": 126, "scheduled_at": "2025-01-17T09:45:00Z", "title": "Fourth Article" } ], "site_settings": { "base_time": "09:00 AM", "stagger_interval": 15, "timezone": "America/New_York" } } ``` **Notes:** - Uses site's default publishing schedule from Site Settings - Automatically staggers publications (e.g., 15 min intervals) - No limit on number of items (unlike direct publish which is limited to 5) - All items set to `site_status='scheduled'` ### Bulk Schedule Preview (Before Confirming) **Request:** ```bash POST /api/v1/writer/content/bulk_schedule_preview/ Content-Type: application/json { "content_ids": [123, 124, 125], "site_id": 45 } ``` **Response:** ```json { "schedule_preview": [ {"content_id": 123, "scheduled_at": "2025-01-17T09:00:00Z", "title": "Article 1"}, {"content_id": 124, "scheduled_at": "2025-01-17T09:15:00Z", "title": "Article 2"}, {"content_id": 125, "scheduled_at": "2025-01-17T09:30:00Z", "title": "Article 3"} ], "site_settings": { "base_time": "09:00 AM", "stagger_interval": 15, "timezone": "America/New_York", "publish_days": ["mon", "tue", "wed", "thu", "fri"] } } ``` **Use Case:** - Show user what times items will be scheduled before confirming - Allow user to adjust site settings if needed - User clicks "Confirm" to execute actual bulk_schedule --- ## Publishing Scheduler (v1.3.2) ### Celery Beat Tasks Located in `igny8_core/tasks/publishing_scheduler.py`: | Task | Schedule | Purpose | |------|----------|---------| | `schedule_approved_content` | Every 15 minutes | Assigns publish times to approved content | | `process_scheduled_publications` | Every 5 minutes | Publishes content when scheduled time arrives | | `cleanup_failed_publications` | Daily at midnight | Retries or cleans up failed publications | ### Scheduling Flow ``` Approved Content → schedule_approved_content (every 15 min) ↓ Check PublishingSettings ↓ Assign scheduled_publish_at based on: - publish_days configuration - publish_time_slots configuration - daily/weekly/monthly limits ↓ Set site_status = "scheduled" ↓ process_scheduled_publications (every 5 min) ↓ If scheduled_publish_at <= now: - Set site_status = "publishing" - Execute publication - Set site_status = "published" or "failed" ``` ### Site Status State Machine ``` not_published → scheduled → publishing → published ↓ ↓ ↓ → failed ↓ not_published (if unscheduled) ``` --- ## Frontend Pages (v1.3.2) ### Content Calendar (`/publisher/content-calendar`) **Purpose:** Visualize and manage scheduled content **Features:** - Calendar view of scheduled publications - List view alternative - Filter by site - Schedule/unschedule actions - Drag-and-drop rescheduling (planned) **Components:** - `ContentCalendar.tsx` - Main page component - `CalendarItemTooltip` - Hover details for calendar items - `SiteInfoBar` - Site context header ### Publishing Queue (`/publisher/publishing-queue`) **Purpose:** Review upcoming publications **Features:** - List of content pending publication - Status indicators (scheduled, publishing, failed) - Publish now / unschedule actions --- ## Publishing Flow ### Single Content Publish **Trigger:** User clicks "Publish" or API call **Flow:** 1. Validate content is in publishable state 2. Get site integration credentials 3. Transform content for destination: - Format HTML for platform - Process images - Map taxonomies 4. Push to destination API 5. Create `PublishingRecord` with external ID 6. Update Content status to `published` ### Batch Deployment **Trigger:** Deployment action **Flow:** 1. Create `DeploymentRecord` 2. Gather all pending content 3. Process each item: - Publish to destination - Create publishing record - Update content status 4. Update deployment status 5. Log any errors --- ## Destination Adapters ### WordPress Adapter - Uses WordPress REST API - Supports posts, pages, custom post types - Handles media upload - Maps categories/tags ### Ghost Adapter (Planned) - Ghost Admin API integration - Post publishing - Image handling ### Webflow Adapter (Planned) - Webflow CMS API - Collection item publishing - Asset management --- ## Publishing States | Status | Description | |--------|-------------| | pending | Ready to publish | | published | Successfully published | | failed | Publishing failed | | retracted | Unpublished/removed | --- ## Site Definition Endpoint **Purpose:** Public endpoint for headless CMS use cases Returns site structure for external consumption: - Site metadata - Available taxonomies - Content types - URL structure --- ## Integration Points | From | To | Trigger | |------|----|---------| | Writer | Publisher | Publish action | | Automation Stage 7 | Publisher | Auto-publish (future) | | Publisher | Integrations | Destination APIs | | Publisher | Content | Status updates | --- ## Common Issues | Issue | Cause | Fix | |-------|-------|-----| | Publish failed | Invalid credentials | Check integration settings | | Duplicate posts | Published twice | Check existing publishing record | | Images missing | Upload failed | Check media library access | | Wrong category | Mapping issue | Verify taxonomy sync | --- ## Planned Changes | Feature | Status | Description | |---------|--------|-------------| | Ghost integration | 🔜 Planned | Ghost CMS publishing | | Webflow integration | 🔜 Planned | Webflow publishing | | ~~Scheduled publishing~~ | ✅ Implemented (v1.3.2) | Future-date publishing | | Republish detection | 🔜 Planned | Detect and handle updates | | ~~Publishing queue~~ | ✅ Implemented (v1.3.2) | Batch publishing with queue | | Drag-and-drop calendar | 🔜 Planned | Reschedule via drag-and-drop |