Workflow Plugin
Content workflow management with approval chains, scheduled publishing, state transitions, and comprehensive audit trails.
Overview
The Workflow plugin provides enterprise-grade content lifecycle management for SonicJS. Control how content moves from draft to publication with configurable approval workflows, scheduled publishing, and detailed audit trails.
State Management
Track content through draft, review, scheduled, and published states
Scheduled Publishing
Schedule content to publish, unpublish, or archive automatically
Approval Chains
Configure multi-level approval workflows for content review
Audit Trail
Complete history of all workflow transitions and actions
Features
Content Lifecycle
- Six distinct content states (draft, review, scheduled, published, archived, deleted)
- Configurable state transitions
- Automatic workflow initialization for new content
Scheduled Publishing
- Schedule content to publish at specific times
- Schedule unpublishing for time-sensitive content
- Automatic archival scheduling
- Timezone-aware scheduling
Approval Workflows
- Multi-level approval chains
- Role-based transition permissions
- Review assignment with due dates
- Approval/rejection with comments
Audit & History
- Complete state transition history
- User action tracking
- Comment logging on transitions
- Metadata preservation
Content States
Content moves through these states during its lifecycle:
Content States
enum ContentStatus {
DRAFT = 'draft', // Work in progress
REVIEW = 'review', // Awaiting approval
SCHEDULED = 'scheduled', // Approved, waiting to publish
PUBLISHED = 'published', // Live and visible
ARCHIVED = 'archived', // Hidden but preserved
DELETED = 'deleted' // Soft-deleted
}
State Diagram
ββββββββββββββββββββββββ
β β
βΌ β
βββββββββββ Submit βββββββββββ Approve βββββββββββββ
β DRAFT ββββββββββββΆβ REVIEW ββββββββββββΆβ SCHEDULED β
βββββββββββ βββββββββββ βββββββββββββ
β β β
β Publish β Reject β Publish
β (direct) β β (auto)
βΌ βΌ βΌ
βββββββββββββ βββββββββββ βββββββββββββ
β PUBLISHED ββββββββββββ DRAFT β β PUBLISHED β
βββββββββββββ restore βββββββββββ βββββββββββββ
β β
β Archive β
βΌ β
ββββββββββββ β
β ARCHIVED ββββββββββββββββββββββββββββββββββββββββ
ββββββββββββ
β
β Delete
βΌ
βββββββββββ
β DELETED β βββΆ Restore βββΆ DRAFT
βββββββββββ
State Descriptions
| State | Description | Visible To |
|---|---|---|
draft | Work in progress, can be edited | Author, Editor, Admin |
review | Submitted for approval | Editor, Admin |
scheduled | Approved, waiting for publish time | Editor, Admin |
published | Live and publicly visible | Everyone |
archived | Hidden but preserved | Editor, Admin |
deleted | Soft-deleted, can be restored | Admin only |
Workflow Actions
Actions that can be performed on content:
Workflow Actions
enum WorkflowAction {
SAVE_DRAFT = 'save_draft', // Save changes to draft
SUBMIT_FOR_REVIEW = 'submit_for_review', // Send to reviewers
APPROVE = 'approve', // Approve for publishing
REJECT = 'reject', // Reject and return to draft
PUBLISH = 'publish', // Publish immediately
UNPUBLISH = 'unpublish', // Return to draft
SCHEDULE = 'schedule', // Schedule for future publishing
ARCHIVE = 'archive', // Archive content
DELETE = 'delete', // Soft delete
RESTORE = 'restore' // Restore from deleted/archived
}
Available Actions by State
| Current State | Available Actions |
|---|---|
draft | Save Draft, Submit for Review, Publish, Schedule, Delete |
review | Approve, Reject, Publish, Schedule |
scheduled | Publish, Save Draft, Delete |
published | Unpublish, Archive, Delete |
archived | Publish, Save Draft, Delete |
deleted | Restore |
Performing Actions
Perform Workflow Action
import { WorkflowManager, WorkflowAction, ContentStatus } from '@sonicjs-cms/core/plugins'
// Initialize manager
const workflowManager = new WorkflowManager(db)
// Check if action is allowed
const canPublish = await workflowManager.canPerformAction(
contentId,
WorkflowAction.PUBLISH,
userId,
userRole // 'admin', 'editor', 'author'
)
// Perform the action
const result = await workflowManager.performAction(
contentId,
WorkflowAction.PUBLISH,
userId,
userRole,
{ comment: 'Approved after review' }
)
if (result.success) {
console.log('New status:', result.newStatus) // 'published'
} else {
console.error('Error:', result.error)
}
Get Available Actions
Get Available Actions
// Get actions the current user can perform
const actions = await workflowManager.getAvailableActions(
contentId,
userId,
userRole
)
// Returns: ['publish', 'unpublish', 'archive', 'delete']
console.log('Available actions:', actions)
// Use static method for known status
const actionsForDraft = ContentWorkflow.getAvailableActions(
ContentStatus.DRAFT,
'editor',
false // isAuthor
)
Scheduled Publishing
Schedule content to publish, unpublish, or archive at specific times.
Schedule Content
Schedule Publishing
import { SchedulerService } from '@sonicjs-cms/core/plugins'
const scheduler = new SchedulerService(db)
// Schedule content to publish
const scheduleId = await scheduler.scheduleContent(
contentId,
'publish', // action: 'publish' | 'unpublish' | 'archive'
new Date('2024-12-25T00:00:00Z'), // scheduled time
'America/New_York', // timezone
userId
)
console.log('Scheduled:', scheduleId)
Update Schedule
Update Schedule
// Reschedule to a new time
await scheduler.updateScheduledContent(
scheduleId,
new Date('2024-12-26T00:00:00Z'),
'Europe/London' // optional new timezone
)
// Cancel scheduled action
await scheduler.cancelScheduledContent(scheduleId)
View Scheduled Content
Query Scheduled Content
// Get all scheduled content for a user
const myScheduled = await scheduler.getScheduledContentForUser(userId)
// Get scheduled actions for specific content
const contentSchedule = await scheduler.getScheduledContentForContent(contentId)
// Get statistics
const stats = await scheduler.getScheduledContentStats()
console.log(stats)
// { pending: 5, completed: 120, failed: 2, cancelled: 3 }
Process Scheduled Content
Scheduled content is processed automatically or via a cron trigger:
Process Scheduled Content
// Process all due scheduled content (call from cron)
const result = await scheduler.processScheduledContent()
console.log(`Processed: ${result.processed}, Errors: ${result.errors}`)
// Or process a specific scheduled action
const success = await scheduler.executeScheduledAction(scheduleId)
Scheduled Content Schema
Scheduled Content Structure
interface ScheduledContent {
id: string // Unique schedule ID
content_id: string // Content being scheduled
action: 'publish' | 'unpublish' | 'archive'
scheduled_at: string // ISO timestamp
timezone: string // Timezone for scheduling
user_id: string // Who scheduled it
status: 'pending' | 'completed' | 'failed' | 'cancelled'
executed_at?: string // When it was processed
error_message?: string // Error if failed
}
Permissions
Role-based permissions control who can perform which actions.
Default Permissions
Default Workflow Permissions
const defaultWorkflowPermissions = {
draft: ['admin', 'editor', 'author'], // Who can work on drafts
review: ['admin', 'editor'], // Who can review
scheduled: ['admin', 'editor'], // Who can manage scheduled
published: ['admin', 'editor'], // Who can unpublish
archived: ['admin', 'editor'], // Who can manage archives
deleted: ['admin'] // Only admins can restore
}
Author Permissions
Authors have special permissions for their own content:
- Can edit their own drafts
- Can submit their drafts for review
- Cannot publish directly (unless also an editor)
- Cannot see other users' drafts
Custom Permissions
Custom Permissions
import { WorkflowManager, WorkflowPermissions } from '@sonicjs-cms/core/plugins'
// Define custom permissions
const customPermissions: WorkflowPermissions = {
draft: ['admin', 'editor', 'author', 'contributor'],
review: ['admin', 'editor', 'senior-editor'],
scheduled: ['admin', 'editor'],
published: ['admin'], // Only admins can unpublish
archived: ['admin'],
deleted: ['admin']
}
// Use with workflow manager
const manager = new WorkflowManager(db, customPermissions)
Check Permissions
Check Permissions
import { ContentWorkflow, WorkflowAction, ContentStatus } from '@sonicjs-cms/core/plugins'
// Check if user can perform action
const canPublish = ContentWorkflow.canPerformAction(
WorkflowAction.PUBLISH,
ContentStatus.REVIEW, // current status
'editor', // user role
false // isAuthor
)
// Get all statuses visible to user
const visibleStatuses = ContentWorkflow.getContentVisibility('author')
// ['draft', 'review', 'published', 'scheduled']
// Filter content by permissions
const filteredContent = ContentWorkflow.filterContentByPermissions(
allContent,
userRole,
userId
)
Services
The plugin provides several services for workflow management.
WorkflowEngine
Core workflow state management:
WorkflowEngine
import { WorkflowEngine } from '@sonicjs-cms/core/plugins'
const engine = new WorkflowEngine(db)
// Get all workflow states
const states = await engine.getWorkflowStates()
// Get workflow for a collection
const workflow = await engine.getWorkflowByCollection(collectionId)
// Get available transitions
const transitions = await engine.getAvailableTransitions(
workflowId,
currentStateId,
userId
)
// Transition content to new state
await engine.transitionContent(
contentId,
'published', // target state
userId,
'Approved for publication' // comment
)
// Get workflow history
const history = await engine.getWorkflowHistory(contentId)
// Assign content to user
await engine.assignContentToUser(contentId, assigneeId, dueDate)
// Get content assigned to user
const assigned = await engine.getAssignedContent(userId)
// Get content by state
const inReview = await engine.getContentByState('review')
SchedulerService
Content scheduling:
SchedulerService
import { SchedulerService } from '@sonicjs-cms/core/plugins'
const scheduler = new SchedulerService(db)
// Schedule actions
await scheduler.scheduleContent(contentId, 'publish', date, timezone, userId)
// Manage schedules
await scheduler.updateScheduledContent(scheduleId, newDate)
await scheduler.cancelScheduledContent(scheduleId)
// Query schedules
const pending = await scheduler.getPendingScheduledContent()
const userSchedules = await scheduler.getScheduledContentForUser(userId)
// Process scheduled content
await scheduler.processScheduledContent()
WorkflowManager
High-level workflow operations with permission checking:
WorkflowManager
import { WorkflowManager } from '@sonicjs-cms/core/plugins'
const manager = new WorkflowManager(db)
// Check and perform actions
const canDo = await manager.canPerformAction(contentId, action, userId, role)
const result = await manager.performAction(contentId, action, userId, role, metadata)
// Get available actions
const actions = await manager.getAvailableActions(contentId, userId, role)
// Get history
const history = await manager.getWorkflowHistory(contentId)
// Bulk operations
await manager.bulkUpdateStatus(contentIds, 'published', userId, role)
Admin Interface
The plugin adds admin pages for workflow management.
Workflow Dashboard
Path: /admin/workflow/dashboard
View and manage all content workflows:
- Content grouped by state
- Quick actions on content items
- Filter by collection, author, date
- Assignment overview
Content Workflow Detail
Path: /admin/workflow/content/:contentId
Manage workflow for specific content:
- Current state and available actions
- Action buttons with confirmation
- Workflow history timeline
- Assignment and due date management
Scheduled Content
Path: /admin/workflow/scheduled
Manage scheduled publishing:
- View all pending schedules
- Edit or cancel schedules
- View completed and failed schedules
- Schedule statistics
Menu Items
The plugin adds these menu items:
- Workflow - Dashboard (order: 30)
- Scheduled - Scheduled content (order: 31, under Workflow)
Integration
Automatic Workflow Initialization
When content is created, the plugin automatically initializes its workflow status:
Auto-initialization
// This happens automatically via the content:create hook
builder.addHook('content:create', async (data, context) => {
if (context?.db) {
const workflowEngine = new WorkflowEngine(context.db)
await workflowEngine.initializeContentWorkflow(
data.id,
data.collectionId
)
}
return data
})
Using in Custom Routes
Custom Route Integration
import { Hono } from 'hono'
import { WorkflowManager, WorkflowAction } from '@sonicjs-cms/core/plugins'
const app = new Hono()
app.post('/api/content/:id/publish', async (c) => {
const contentId = c.req.param('id')
const user = c.get('user')
const manager = new WorkflowManager(c.env.DB)
// Check permission
const canPublish = await manager.canPerformAction(
contentId,
WorkflowAction.PUBLISH,
user.id,
user.role
)
if (!canPublish) {
return c.json({ error: 'Not authorized to publish' }, 403)
}
// Perform action
const result = await manager.performAction(
contentId,
WorkflowAction.PUBLISH,
user.id,
user.role,
{ comment: 'Published via API' }
)
return c.json(result)
})
Generating UI Components
Generate Status Badge
import { ContentWorkflow, ContentStatus } from '@sonicjs-cms/core/plugins'
// Generate status badge HTML
const badge = ContentWorkflow.generateStatusBadge(ContentStatus.REVIEW)
// Returns: <span class="inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium bg-yellow-100 text-yellow-800">Under Review</span>
// Generate action buttons
const buttons = ContentWorkflow.generateActionButtons(
contentId,
ContentStatus.DRAFT,
[WorkflowAction.SUBMIT_FOR_REVIEW, WorkflowAction.PUBLISH]
)
Database Schema
The plugin creates these tables:
| Table | Description |
|---|---|
workflows | Workflow definitions per collection |
workflow_states | Available states (draft, review, etc.) |
workflow_transitions | Allowed state transitions |
content_workflow_status | Current status per content item |
workflow_history | Transition audit trail |
scheduled_content | Scheduled publishing queue |