refactor: rename namespace Core\Content to Core\Mod\Content
Aligns content module namespace with the standard module structure
convention (Core\Mod\{Name}) for consistency across the monorepo.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
f990dc1bd3
commit
6ede1b1a20
68 changed files with 2077 additions and 187 deletions
2
Boot.php
2
Boot.php
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
declare(strict_types=1);
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace Core\Content;
|
namespace Core\Mod\Content;
|
||||||
|
|
||||||
use Core\Events\ApiRoutesRegistering;
|
use Core\Events\ApiRoutesRegistering;
|
||||||
use Core\Events\ConsoleBooting;
|
use Core\Events\ConsoleBooting;
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
declare(strict_types=1);
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace Core\Content\Concerns;
|
namespace Core\Mod\Content\Concerns;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Trait for making ContentItem searchable with Laravel Scout.
|
* Trait for making ContentItem searchable with Laravel Scout.
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
declare(strict_types=1);
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace Core\Content\Console\Commands;
|
namespace Core\Mod\Content\Console\Commands;
|
||||||
|
|
||||||
use Mod\Agentic\Services\ContentService;
|
use Mod\Agentic\Services\ContentService;
|
||||||
use Illuminate\Console\Command;
|
use Illuminate\Console\Command;
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
declare(strict_types=1);
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace Core\Content\Console\Commands;
|
namespace Core\Mod\Content\Console\Commands;
|
||||||
|
|
||||||
use Mod\Agentic\Services\ContentService;
|
use Mod\Agentic\Services\ContentService;
|
||||||
use Illuminate\Console\Command;
|
use Illuminate\Console\Command;
|
||||||
|
|
|
||||||
|
|
@ -2,13 +2,13 @@
|
||||||
|
|
||||||
declare(strict_types=1);
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace Core\Content\Console\Commands;
|
namespace Core\Mod\Content\Console\Commands;
|
||||||
|
|
||||||
use Core\Content\Enums\ContentType;
|
use Core\Mod\Content\Enums\ContentType;
|
||||||
use Core\Content\Models\ContentAuthor;
|
use Core\Mod\Content\Models\ContentAuthor;
|
||||||
use Core\Content\Models\ContentItem;
|
use Core\Mod\Content\Models\ContentItem;
|
||||||
use Core\Content\Models\ContentMedia;
|
use Core\Mod\Content\Models\ContentMedia;
|
||||||
use Core\Content\Models\ContentTaxonomy;
|
use Core\Mod\Content\Models\ContentTaxonomy;
|
||||||
use Core\Mod\Tenant\Models\Workspace;
|
use Core\Mod\Tenant\Models\Workspace;
|
||||||
use Carbon\Carbon;
|
use Carbon\Carbon;
|
||||||
use Illuminate\Console\Command;
|
use Illuminate\Console\Command;
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
declare(strict_types=1);
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace Core\Content\Console\Commands;
|
namespace Core\Mod\Content\Console\Commands;
|
||||||
|
|
||||||
use Mod\Agentic\Services\ContentService;
|
use Mod\Agentic\Services\ContentService;
|
||||||
use Illuminate\Console\Command;
|
use Illuminate\Console\Command;
|
||||||
|
|
|
||||||
|
|
@ -2,11 +2,11 @@
|
||||||
|
|
||||||
declare(strict_types=1);
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace Core\Content\Console\Commands;
|
namespace Core\Mod\Content\Console\Commands;
|
||||||
|
|
||||||
use Illuminate\Console\Command;
|
use Illuminate\Console\Command;
|
||||||
use Illuminate\Support\Facades\Log;
|
use Illuminate\Support\Facades\Log;
|
||||||
use Core\Content\Services\WebhookRetryService;
|
use Core\Mod\Content\Services\WebhookRetryService;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* ProcessPendingWebhooks
|
* ProcessPendingWebhooks
|
||||||
|
|
|
||||||
|
|
@ -2,10 +2,10 @@
|
||||||
|
|
||||||
declare(strict_types=1);
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace Core\Content\Console\Commands;
|
namespace Core\Mod\Content\Console\Commands;
|
||||||
|
|
||||||
use Illuminate\Console\Command;
|
use Illuminate\Console\Command;
|
||||||
use Core\Content\Models\ContentRevision;
|
use Core\Mod\Content\Models\ContentRevision;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Prune old content revisions based on retention policy.
|
* Prune old content revisions based on retention policy.
|
||||||
|
|
|
||||||
|
|
@ -2,11 +2,11 @@
|
||||||
|
|
||||||
declare(strict_types=1);
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace Core\Content\Console\Commands;
|
namespace Core\Mod\Content\Console\Commands;
|
||||||
|
|
||||||
use Illuminate\Console\Command;
|
use Illuminate\Console\Command;
|
||||||
use Illuminate\Support\Facades\Log;
|
use Illuminate\Support\Facades\Log;
|
||||||
use Core\Content\Models\ContentItem;
|
use Core\Mod\Content\Models\ContentItem;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* PublishScheduledContent
|
* PublishScheduledContent
|
||||||
|
|
|
||||||
|
|
@ -2,15 +2,15 @@
|
||||||
|
|
||||||
declare(strict_types=1);
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace Core\Content\Controllers\Api;
|
namespace Core\Mod\Content\Controllers\Api;
|
||||||
|
|
||||||
use Core\Front\Controller;
|
use Core\Front\Controller;
|
||||||
use Illuminate\Http\JsonResponse;
|
use Illuminate\Http\JsonResponse;
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
use Core\Mod\Api\Concerns\HasApiResponses;
|
use Core\Mod\Api\Concerns\HasApiResponses;
|
||||||
use Core\Mod\Api\Concerns\ResolvesWorkspace;
|
use Core\Mod\Api\Concerns\ResolvesWorkspace;
|
||||||
use Core\Content\Models\ContentBrief;
|
use Core\Mod\Content\Models\ContentBrief;
|
||||||
use Core\Content\Resources\ContentBriefResource;
|
use Core\Mod\Content\Resources\ContentBriefResource;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Content Brief API Controller
|
* Content Brief API Controller
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
declare(strict_types=1);
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace Core\Content\Controllers\Api;
|
namespace Core\Mod\Content\Controllers\Api;
|
||||||
|
|
||||||
use Core\Front\Controller;
|
use Core\Front\Controller;
|
||||||
use Core\Mod\Api\Concerns\HasApiResponses;
|
use Core\Mod\Api\Concerns\HasApiResponses;
|
||||||
|
|
@ -11,7 +11,7 @@ use Illuminate\Http\JsonResponse;
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
use Illuminate\Support\Facades\Storage;
|
use Illuminate\Support\Facades\Storage;
|
||||||
use Illuminate\Support\Str;
|
use Illuminate\Support\Str;
|
||||||
use Core\Content\Models\ContentMedia;
|
use Core\Mod\Content\Models\ContentMedia;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Content Media API Controller
|
* Content Media API Controller
|
||||||
|
|
|
||||||
|
|
@ -2,16 +2,16 @@
|
||||||
|
|
||||||
declare(strict_types=1);
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace Core\Content\Controllers\Api;
|
namespace Core\Mod\Content\Controllers\Api;
|
||||||
|
|
||||||
use Core\Front\Controller;
|
use Core\Front\Controller;
|
||||||
use Core\Mod\Api\Concerns\HasApiResponses;
|
use Core\Mod\Api\Concerns\HasApiResponses;
|
||||||
use Core\Mod\Api\Concerns\ResolvesWorkspace;
|
use Core\Mod\Api\Concerns\ResolvesWorkspace;
|
||||||
use Illuminate\Http\JsonResponse;
|
use Illuminate\Http\JsonResponse;
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
use Core\Content\Models\ContentItem;
|
use Core\Mod\Content\Models\ContentItem;
|
||||||
use Core\Content\Models\ContentRevision;
|
use Core\Mod\Content\Models\ContentRevision;
|
||||||
use Core\Content\Resources\ContentRevisionResource;
|
use Core\Mod\Content\Resources\ContentRevisionResource;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Content Revision API Controller
|
* Content Revision API Controller
|
||||||
|
|
|
||||||
|
|
@ -2,14 +2,14 @@
|
||||||
|
|
||||||
declare(strict_types=1);
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace Core\Content\Controllers\Api;
|
namespace Core\Mod\Content\Controllers\Api;
|
||||||
|
|
||||||
use Core\Front\Controller;
|
use Core\Front\Controller;
|
||||||
use Core\Mod\Api\Concerns\HasApiResponses;
|
use Core\Mod\Api\Concerns\HasApiResponses;
|
||||||
use Core\Mod\Api\Concerns\ResolvesWorkspace;
|
use Core\Mod\Api\Concerns\ResolvesWorkspace;
|
||||||
use Illuminate\Http\JsonResponse;
|
use Illuminate\Http\JsonResponse;
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
use Core\Content\Services\ContentSearchService;
|
use Core\Mod\Content\Services\ContentSearchService;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Content Search API Controller
|
* Content Search API Controller
|
||||||
|
|
|
||||||
|
|
@ -2,15 +2,15 @@
|
||||||
|
|
||||||
declare(strict_types=1);
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace Core\Content\Controllers\Api;
|
namespace Core\Mod\Content\Controllers\Api;
|
||||||
|
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
use Illuminate\Http\Response;
|
use Illuminate\Http\Response;
|
||||||
use Illuminate\Routing\Controller;
|
use Illuminate\Routing\Controller;
|
||||||
use Illuminate\Support\Facades\Log;
|
use Illuminate\Support\Facades\Log;
|
||||||
use Core\Content\Jobs\ProcessContentWebhook;
|
use Core\Mod\Content\Jobs\ProcessContentWebhook;
|
||||||
use Core\Content\Models\ContentWebhookEndpoint;
|
use Core\Mod\Content\Models\ContentWebhookEndpoint;
|
||||||
use Core\Content\Models\ContentWebhookLog;
|
use Core\Mod\Content\Models\ContentWebhookLog;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Controller for receiving external content webhooks.
|
* Controller for receiving external content webhooks.
|
||||||
|
|
|
||||||
|
|
@ -2,18 +2,18 @@
|
||||||
|
|
||||||
declare(strict_types=1);
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace Core\Content\Controllers\Api;
|
namespace Core\Mod\Content\Controllers\Api;
|
||||||
|
|
||||||
use Core\Front\Controller;
|
use Core\Front\Controller;
|
||||||
use Illuminate\Http\JsonResponse;
|
use Illuminate\Http\JsonResponse;
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
use Core\Mod\Api\Concerns\HasApiResponses;
|
use Core\Mod\Api\Concerns\HasApiResponses;
|
||||||
use Core\Mod\Api\Concerns\ResolvesWorkspace;
|
use Core\Mod\Api\Concerns\ResolvesWorkspace;
|
||||||
use Core\Content\Jobs\GenerateContentJob;
|
use Core\Mod\Content\Jobs\GenerateContentJob;
|
||||||
use Core\Content\Models\AIUsage;
|
use Core\Mod\Content\Models\AIUsage;
|
||||||
use Core\Content\Models\ContentBrief;
|
use Core\Mod\Content\Models\ContentBrief;
|
||||||
use Core\Content\Resources\ContentBriefResource;
|
use Core\Mod\Content\Resources\ContentBriefResource;
|
||||||
use Core\Content\Services\AIGatewayService;
|
use Core\Mod\Content\Services\AIGatewayService;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Content Generation API Controller
|
* Content Generation API Controller
|
||||||
|
|
|
||||||
|
|
@ -2,13 +2,13 @@
|
||||||
|
|
||||||
declare(strict_types=1);
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace Core\Content\Controllers;
|
namespace Core\Mod\Content\Controllers;
|
||||||
|
|
||||||
use Core\Mod\Tenant\Models\Workspace;
|
use Core\Mod\Tenant\Models\Workspace;
|
||||||
use Illuminate\Http\JsonResponse;
|
use Illuminate\Http\JsonResponse;
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
use Illuminate\Routing\Controller;
|
use Illuminate\Routing\Controller;
|
||||||
use Core\Content\Models\ContentItem;
|
use Core\Mod\Content\Models\ContentItem;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* ContentPreviewController - Preview draft content before publishing.
|
* ContentPreviewController - Preview draft content before publishing.
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
declare(strict_types=1);
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace Core\Content\Enums;
|
namespace Core\Mod\Content\Enums;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Content type for ContentBrief.
|
* Content type for ContentBrief.
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
declare(strict_types=1);
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace Core\Content\Enums;
|
namespace Core\Mod\Content\Enums;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Content source type for ContentItem.
|
* Content source type for ContentItem.
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
declare(strict_types=1);
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace Core\Content\Jobs;
|
namespace Core\Mod\Content\Jobs;
|
||||||
|
|
||||||
use Illuminate\Bus\Queueable;
|
use Illuminate\Bus\Queueable;
|
||||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||||
|
|
@ -10,8 +10,8 @@ use Illuminate\Foundation\Bus\Dispatchable;
|
||||||
use Illuminate\Queue\InteractsWithQueue;
|
use Illuminate\Queue\InteractsWithQueue;
|
||||||
use Illuminate\Queue\SerializesModels;
|
use Illuminate\Queue\SerializesModels;
|
||||||
use Illuminate\Support\Facades\Log;
|
use Illuminate\Support\Facades\Log;
|
||||||
use Core\Content\Models\ContentBrief;
|
use Core\Mod\Content\Models\ContentBrief;
|
||||||
use Core\Content\Services\AIGatewayService;
|
use Core\Mod\Content\Services\AIGatewayService;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* GenerateContentJob
|
* GenerateContentJob
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
declare(strict_types=1);
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace Core\Content\Jobs;
|
namespace Core\Mod\Content\Jobs;
|
||||||
|
|
||||||
use Illuminate\Bus\Queueable;
|
use Illuminate\Bus\Queueable;
|
||||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||||
|
|
@ -10,12 +10,12 @@ use Illuminate\Foundation\Bus\Dispatchable;
|
||||||
use Illuminate\Queue\InteractsWithQueue;
|
use Illuminate\Queue\InteractsWithQueue;
|
||||||
use Illuminate\Queue\SerializesModels;
|
use Illuminate\Queue\SerializesModels;
|
||||||
use Illuminate\Support\Facades\Log;
|
use Illuminate\Support\Facades\Log;
|
||||||
use Core\Content\Enums\ContentType;
|
use Core\Mod\Content\Enums\ContentType;
|
||||||
use Core\Content\Models\ContentItem;
|
use Core\Mod\Content\Models\ContentItem;
|
||||||
use Core\Content\Models\ContentMedia;
|
use Core\Mod\Content\Models\ContentMedia;
|
||||||
use Core\Content\Models\ContentTaxonomy;
|
use Core\Mod\Content\Models\ContentTaxonomy;
|
||||||
use Core\Content\Models\ContentWebhookEndpoint;
|
use Core\Mod\Content\Models\ContentWebhookEndpoint;
|
||||||
use Core\Content\Models\ContentWebhookLog;
|
use Core\Mod\Content\Models\ContentWebhookLog;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Process incoming content webhooks.
|
* Process incoming content webhooks.
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
declare(strict_types=1);
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace Core\Content\Mcp\Handlers;
|
namespace Core\Mod\Content\Mcp\Handlers;
|
||||||
|
|
||||||
use Carbon\Carbon;
|
use Carbon\Carbon;
|
||||||
use Core\Front\Mcp\Contracts\McpToolHandler;
|
use Core\Front\Mcp\Contracts\McpToolHandler;
|
||||||
|
|
@ -11,10 +11,10 @@ use Core\Mod\Tenant\Models\Workspace;
|
||||||
use Core\Mod\Tenant\Services\EntitlementService;
|
use Core\Mod\Tenant\Services\EntitlementService;
|
||||||
use Illuminate\Support\Facades\Auth;
|
use Illuminate\Support\Facades\Auth;
|
||||||
use Illuminate\Support\Str;
|
use Illuminate\Support\Str;
|
||||||
use Core\Content\Enums\ContentType;
|
use Core\Mod\Content\Enums\ContentType;
|
||||||
use Core\Content\Models\ContentItem;
|
use Core\Mod\Content\Models\ContentItem;
|
||||||
use Core\Content\Models\ContentRevision;
|
use Core\Mod\Content\Models\ContentRevision;
|
||||||
use Core\Content\Models\ContentTaxonomy;
|
use Core\Mod\Content\Models\ContentTaxonomy;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* MCP tool handler for creating content items.
|
* MCP tool handler for creating content items.
|
||||||
|
|
|
||||||
|
|
@ -2,14 +2,14 @@
|
||||||
|
|
||||||
declare(strict_types=1);
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace Core\Content\Mcp\Handlers;
|
namespace Core\Mod\Content\Mcp\Handlers;
|
||||||
|
|
||||||
use Core\Front\Mcp\Contracts\McpToolHandler;
|
use Core\Front\Mcp\Contracts\McpToolHandler;
|
||||||
use Core\Front\Mcp\McpContext;
|
use Core\Front\Mcp\McpContext;
|
||||||
use Core\Mod\Tenant\Models\Workspace;
|
use Core\Mod\Tenant\Models\Workspace;
|
||||||
use Illuminate\Support\Facades\Auth;
|
use Illuminate\Support\Facades\Auth;
|
||||||
use Core\Content\Models\ContentItem;
|
use Core\Mod\Content\Models\ContentItem;
|
||||||
use Core\Content\Models\ContentRevision;
|
use Core\Mod\Content\Models\ContentRevision;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* MCP tool handler for deleting content items.
|
* MCP tool handler for deleting content items.
|
||||||
|
|
|
||||||
|
|
@ -2,13 +2,13 @@
|
||||||
|
|
||||||
declare(strict_types=1);
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace Core\Content\Mcp\Handlers;
|
namespace Core\Mod\Content\Mcp\Handlers;
|
||||||
|
|
||||||
use Core\Front\Mcp\Contracts\McpToolHandler;
|
use Core\Front\Mcp\Contracts\McpToolHandler;
|
||||||
use Core\Front\Mcp\McpContext;
|
use Core\Front\Mcp\McpContext;
|
||||||
use Core\Mod\Tenant\Models\Workspace;
|
use Core\Mod\Tenant\Models\Workspace;
|
||||||
use Illuminate\Support\Str;
|
use Illuminate\Support\Str;
|
||||||
use Core\Content\Models\ContentItem;
|
use Core\Mod\Content\Models\ContentItem;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* MCP tool handler for listing content items.
|
* MCP tool handler for listing content items.
|
||||||
|
|
|
||||||
|
|
@ -2,12 +2,12 @@
|
||||||
|
|
||||||
declare(strict_types=1);
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace Core\Content\Mcp\Handlers;
|
namespace Core\Mod\Content\Mcp\Handlers;
|
||||||
|
|
||||||
use Core\Front\Mcp\Contracts\McpToolHandler;
|
use Core\Front\Mcp\Contracts\McpToolHandler;
|
||||||
use Core\Front\Mcp\McpContext;
|
use Core\Front\Mcp\McpContext;
|
||||||
use Core\Mod\Tenant\Models\Workspace;
|
use Core\Mod\Tenant\Models\Workspace;
|
||||||
use Core\Content\Models\ContentItem;
|
use Core\Mod\Content\Models\ContentItem;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* MCP tool handler for reading content items.
|
* MCP tool handler for reading content items.
|
||||||
|
|
|
||||||
|
|
@ -2,13 +2,13 @@
|
||||||
|
|
||||||
declare(strict_types=1);
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace Core\Content\Mcp\Handlers;
|
namespace Core\Mod\Content\Mcp\Handlers;
|
||||||
|
|
||||||
use Core\Front\Mcp\Contracts\McpToolHandler;
|
use Core\Front\Mcp\Contracts\McpToolHandler;
|
||||||
use Core\Front\Mcp\McpContext;
|
use Core\Front\Mcp\McpContext;
|
||||||
use Core\Mod\Tenant\Models\Workspace;
|
use Core\Mod\Tenant\Models\Workspace;
|
||||||
use Illuminate\Support\Str;
|
use Illuminate\Support\Str;
|
||||||
use Core\Content\Services\ContentSearchService;
|
use Core\Mod\Content\Services\ContentSearchService;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* MCP tool handler for searching content.
|
* MCP tool handler for searching content.
|
||||||
|
|
|
||||||
|
|
@ -2,12 +2,12 @@
|
||||||
|
|
||||||
declare(strict_types=1);
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace Core\Content\Mcp\Handlers;
|
namespace Core\Mod\Content\Mcp\Handlers;
|
||||||
|
|
||||||
use Core\Front\Mcp\Contracts\McpToolHandler;
|
use Core\Front\Mcp\Contracts\McpToolHandler;
|
||||||
use Core\Front\Mcp\McpContext;
|
use Core\Front\Mcp\McpContext;
|
||||||
use Core\Mod\Tenant\Models\Workspace;
|
use Core\Mod\Tenant\Models\Workspace;
|
||||||
use Core\Content\Models\ContentTaxonomy;
|
use Core\Mod\Content\Models\ContentTaxonomy;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* MCP tool handler for listing content taxonomies.
|
* MCP tool handler for listing content taxonomies.
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
declare(strict_types=1);
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace Core\Content\Mcp\Handlers;
|
namespace Core\Mod\Content\Mcp\Handlers;
|
||||||
|
|
||||||
use Carbon\Carbon;
|
use Carbon\Carbon;
|
||||||
use Core\Front\Mcp\Contracts\McpToolHandler;
|
use Core\Front\Mcp\Contracts\McpToolHandler;
|
||||||
|
|
@ -10,9 +10,9 @@ use Core\Front\Mcp\McpContext;
|
||||||
use Core\Mod\Tenant\Models\Workspace;
|
use Core\Mod\Tenant\Models\Workspace;
|
||||||
use Illuminate\Support\Facades\Auth;
|
use Illuminate\Support\Facades\Auth;
|
||||||
use Illuminate\Support\Str;
|
use Illuminate\Support\Str;
|
||||||
use Core\Content\Models\ContentItem;
|
use Core\Mod\Content\Models\ContentItem;
|
||||||
use Core\Content\Models\ContentRevision;
|
use Core\Mod\Content\Models\ContentRevision;
|
||||||
use Core\Content\Models\ContentTaxonomy;
|
use Core\Mod\Content\Models\ContentTaxonomy;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* MCP tool handler for updating content items.
|
* MCP tool handler for updating content items.
|
||||||
|
|
|
||||||
|
|
@ -2,9 +2,9 @@
|
||||||
|
|
||||||
declare(strict_types=1);
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace Core\Content\Middleware;
|
namespace Core\Mod\Content\Middleware;
|
||||||
|
|
||||||
use Core\Content\Services\ContentRender;
|
use Core\Mod\Content\Services\ContentRender;
|
||||||
use Core\Mod\Tenant\Models\Workspace;
|
use Core\Mod\Tenant\Models\Workspace;
|
||||||
use Closure;
|
use Closure;
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
declare(strict_types=1);
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace Core\Content\Models;
|
namespace Core\Mod\Content\Models;
|
||||||
|
|
||||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||||
use Illuminate\Database\Eloquent\Model;
|
use Illuminate\Database\Eloquent\Model;
|
||||||
|
|
@ -20,9 +20,9 @@ class AIUsage extends Model
|
||||||
{
|
{
|
||||||
use HasFactory;
|
use HasFactory;
|
||||||
|
|
||||||
protected static function newFactory(): \Core\Content\Database\Factories\AIUsageFactory
|
protected static function newFactory(): \Core\Mod\Content\Database\Factories\AIUsageFactory
|
||||||
{
|
{
|
||||||
return \Core\Content\Database\Factories\AIUsageFactory::new();
|
return \Core\Mod\Content\Database\Factories\AIUsageFactory::new();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected $table = 'ai_usage';
|
protected $table = 'ai_usage';
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
declare(strict_types=1);
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace Core\Content\Models;
|
namespace Core\Mod\Content\Models;
|
||||||
|
|
||||||
use Core\Mod\Tenant\Models\Workspace;
|
use Core\Mod\Tenant\Models\Workspace;
|
||||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||||
|
|
@ -14,9 +14,9 @@ class ContentAuthor extends Model
|
||||||
{
|
{
|
||||||
use HasFactory;
|
use HasFactory;
|
||||||
|
|
||||||
protected static function newFactory(): \Core\Content\Database\Factories\ContentAuthorFactory
|
protected static function newFactory(): \Core\Mod\Content\Database\Factories\ContentAuthorFactory
|
||||||
{
|
{
|
||||||
return \Core\Content\Database\Factories\ContentAuthorFactory::new();
|
return \Core\Mod\Content\Database\Factories\ContentAuthorFactory::new();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected $fillable = [
|
protected $fillable = [
|
||||||
|
|
|
||||||
|
|
@ -2,13 +2,13 @@
|
||||||
|
|
||||||
declare(strict_types=1);
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace Core\Content\Models;
|
namespace Core\Mod\Content\Models;
|
||||||
|
|
||||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||||
use Illuminate\Database\Eloquent\Model;
|
use Illuminate\Database\Eloquent\Model;
|
||||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||||
use Illuminate\Database\Eloquent\Relations\HasMany;
|
use Illuminate\Database\Eloquent\Relations\HasMany;
|
||||||
use Core\Content\Enums\BriefContentType;
|
use Core\Mod\Content\Enums\BriefContentType;
|
||||||
use Core\Mod\Tenant\Models\Workspace;
|
use Core\Mod\Tenant\Models\Workspace;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -23,9 +23,9 @@ class ContentBrief extends Model
|
||||||
{
|
{
|
||||||
use HasFactory;
|
use HasFactory;
|
||||||
|
|
||||||
protected static function newFactory(): \Core\Content\Database\Factories\ContentBriefFactory
|
protected static function newFactory(): \Core\Mod\Content\Database\Factories\ContentBriefFactory
|
||||||
{
|
{
|
||||||
return \Core\Content\Database\Factories\ContentBriefFactory::new();
|
return \Core\Mod\Content\Database\Factories\ContentBriefFactory::new();
|
||||||
}
|
}
|
||||||
|
|
||||||
public const STATUS_PENDING = 'pending';
|
public const STATUS_PENDING = 'pending';
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
declare(strict_types=1);
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace Core\Content\Models;
|
namespace Core\Mod\Content\Models;
|
||||||
|
|
||||||
use Core\Mod\Tenant\Models\User;
|
use Core\Mod\Tenant\Models\User;
|
||||||
use Core\Mod\Tenant\Models\Workspace;
|
use Core\Mod\Tenant\Models\Workspace;
|
||||||
|
|
@ -14,17 +14,17 @@ use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||||
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
|
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
|
||||||
use Illuminate\Database\Eloquent\Relations\HasMany;
|
use Illuminate\Database\Eloquent\Relations\HasMany;
|
||||||
use Illuminate\Database\Eloquent\SoftDeletes;
|
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||||
use Core\Content\Enums\ContentType;
|
use Core\Mod\Content\Enums\ContentType;
|
||||||
use Core\Content\Observers\ContentItemObserver;
|
use Core\Mod\Content\Observers\ContentItemObserver;
|
||||||
|
|
||||||
#[ObservedBy([ContentItemObserver::class])]
|
#[ObservedBy([ContentItemObserver::class])]
|
||||||
class ContentItem extends Model
|
class ContentItem extends Model
|
||||||
{
|
{
|
||||||
use HasFactory, HasSeoMetadata, SoftDeletes;
|
use HasFactory, HasSeoMetadata, SoftDeletes;
|
||||||
|
|
||||||
protected static function newFactory(): \Core\Content\Database\Factories\ContentItemFactory
|
protected static function newFactory(): \Core\Mod\Content\Database\Factories\ContentItemFactory
|
||||||
{
|
{
|
||||||
return \Core\Content\Database\Factories\ContentItemFactory::new();
|
return \Core\Mod\Content\Database\Factories\ContentItemFactory::new();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected $fillable = [
|
protected $fillable = [
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
declare(strict_types=1);
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace Core\Content\Models;
|
namespace Core\Mod\Content\Models;
|
||||||
|
|
||||||
use Core\Mod\Tenant\Models\Workspace;
|
use Core\Mod\Tenant\Models\Workspace;
|
||||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||||
|
|
@ -13,9 +13,9 @@ class ContentMedia extends Model
|
||||||
{
|
{
|
||||||
use HasFactory;
|
use HasFactory;
|
||||||
|
|
||||||
protected static function newFactory(): \Core\Content\Database\Factories\ContentMediaFactory
|
protected static function newFactory(): \Core\Mod\Content\Database\Factories\ContentMediaFactory
|
||||||
{
|
{
|
||||||
return \Core\Content\Database\Factories\ContentMediaFactory::new();
|
return \Core\Mod\Content\Database\Factories\ContentMediaFactory::new();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected $table = 'content_media';
|
protected $table = 'content_media';
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
declare(strict_types=1);
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace Core\Content\Models;
|
namespace Core\Mod\Content\Models;
|
||||||
|
|
||||||
use Core\Mod\Tenant\Models\User;
|
use Core\Mod\Tenant\Models\User;
|
||||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
declare(strict_types=1);
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace Core\Content\Models;
|
namespace Core\Mod\Content\Models;
|
||||||
|
|
||||||
use Mod\Agentic\Models\Prompt;
|
use Mod\Agentic\Models\Prompt;
|
||||||
use Core\Mod\Tenant\Models\Workspace;
|
use Core\Mod\Tenant\Models\Workspace;
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
declare(strict_types=1);
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace Core\Content\Models;
|
namespace Core\Mod\Content\Models;
|
||||||
|
|
||||||
use Core\Mod\Tenant\Models\Workspace;
|
use Core\Mod\Tenant\Models\Workspace;
|
||||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||||
|
|
@ -14,9 +14,9 @@ class ContentTaxonomy extends Model
|
||||||
{
|
{
|
||||||
use HasFactory;
|
use HasFactory;
|
||||||
|
|
||||||
protected static function newFactory(): \Core\Content\Database\Factories\ContentTaxonomyFactory
|
protected static function newFactory(): \Core\Mod\Content\Database\Factories\ContentTaxonomyFactory
|
||||||
{
|
{
|
||||||
return \Core\Content\Database\Factories\ContentTaxonomyFactory::new();
|
return \Core\Mod\Content\Database\Factories\ContentTaxonomyFactory::new();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected $fillable = [
|
protected $fillable = [
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
declare(strict_types=1);
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace Core\Content\Models;
|
namespace Core\Mod\Content\Models;
|
||||||
|
|
||||||
use Core\Mod\Tenant\Models\Workspace;
|
use Core\Mod\Tenant\Models\Workspace;
|
||||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||||
|
|
@ -35,9 +35,9 @@ class ContentWebhookEndpoint extends Model
|
||||||
{
|
{
|
||||||
use HasFactory;
|
use HasFactory;
|
||||||
|
|
||||||
protected static function newFactory(): \Core\Content\Database\Factories\ContentWebhookEndpointFactory
|
protected static function newFactory(): \Core\Mod\Content\Database\Factories\ContentWebhookEndpointFactory
|
||||||
{
|
{
|
||||||
return \Core\Content\Database\Factories\ContentWebhookEndpointFactory::new();
|
return \Core\Mod\Content\Database\Factories\ContentWebhookEndpointFactory::new();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected $table = 'content_webhook_endpoints';
|
protected $table = 'content_webhook_endpoints';
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
declare(strict_types=1);
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace Core\Content\Models;
|
namespace Core\Mod\Content\Models;
|
||||||
|
|
||||||
use Core\Mod\Tenant\Models\Workspace;
|
use Core\Mod\Tenant\Models\Workspace;
|
||||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||||
|
|
@ -13,9 +13,9 @@ class ContentWebhookLog extends Model
|
||||||
{
|
{
|
||||||
use HasFactory;
|
use HasFactory;
|
||||||
|
|
||||||
protected static function newFactory(): \Core\Content\Database\Factories\ContentWebhookLogFactory
|
protected static function newFactory(): \Core\Mod\Content\Database\Factories\ContentWebhookLogFactory
|
||||||
{
|
{
|
||||||
return \Core\Content\Database\Factories\ContentWebhookLogFactory::new();
|
return \Core\Mod\Content\Database\Factories\ContentWebhookLogFactory::new();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected $fillable = [
|
protected $fillable = [
|
||||||
|
|
|
||||||
|
|
@ -2,11 +2,11 @@
|
||||||
|
|
||||||
declare(strict_types=1);
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace Core\Content\Observers;
|
namespace Core\Mod\Content\Observers;
|
||||||
|
|
||||||
use Illuminate\Support\Facades\Log;
|
use Illuminate\Support\Facades\Log;
|
||||||
use Core\Content\Models\ContentItem;
|
use Core\Mod\Content\Models\ContentItem;
|
||||||
use Core\Content\Services\CdnPurgeService;
|
use Core\Mod\Content\Services\CdnPurgeService;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Content Item Observer - handles CDN cache purging on content changes.
|
* Content Item Observer - handles CDN cache purging on content changes.
|
||||||
|
|
|
||||||
|
|
@ -2,13 +2,13 @@
|
||||||
|
|
||||||
declare(strict_types=1);
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace Core\Content\Services;
|
namespace Core\Mod\Content\Services;
|
||||||
|
|
||||||
use Mod\Agentic\Services\AgenticResponse;
|
use Mod\Agentic\Services\AgenticResponse;
|
||||||
use Mod\Agentic\Services\ClaudeService;
|
use Mod\Agentic\Services\ClaudeService;
|
||||||
use Mod\Agentic\Services\GeminiService;
|
use Mod\Agentic\Services\GeminiService;
|
||||||
use Core\Content\Models\AIUsage;
|
use Core\Mod\Content\Models\AIUsage;
|
||||||
use Core\Content\Models\ContentBrief;
|
use Core\Mod\Content\Models\ContentBrief;
|
||||||
use RuntimeException;
|
use RuntimeException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
|
|
@ -2,10 +2,10 @@
|
||||||
|
|
||||||
declare(strict_types=1);
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace Core\Content\Services;
|
namespace Core\Mod\Content\Services;
|
||||||
|
|
||||||
use Illuminate\Support\Facades\Log;
|
use Illuminate\Support\Facades\Log;
|
||||||
use Core\Content\Models\ContentItem;
|
use Core\Mod\Content\Models\ContentItem;
|
||||||
use Plug\Cdn\CdnManager;
|
use Plug\Cdn\CdnManager;
|
||||||
use Plug\Response;
|
use Plug\Response;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
declare(strict_types=1);
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace Core\Content\Services;
|
namespace Core\Mod\Content\Services;
|
||||||
|
|
||||||
use DOMDocument;
|
use DOMDocument;
|
||||||
use DOMElement;
|
use DOMElement;
|
||||||
|
|
|
||||||
|
|
@ -2,14 +2,14 @@
|
||||||
|
|
||||||
declare(strict_types=1);
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace Core\Content\Services;
|
namespace Core\Mod\Content\Services;
|
||||||
|
|
||||||
use Core\Front\Controller;
|
use Core\Front\Controller;
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
use Illuminate\Support\Facades\Cache;
|
use Illuminate\Support\Facades\Cache;
|
||||||
use Illuminate\Support\Facades\Log;
|
use Illuminate\Support\Facades\Log;
|
||||||
use Illuminate\View\View;
|
use Illuminate\View\View;
|
||||||
use Core\Content\Models\ContentItem;
|
use Core\Mod\Content\Models\ContentItem;
|
||||||
use Core\Mod\Tenant\Models\Workspace;
|
use Core\Mod\Tenant\Models\Workspace;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
declare(strict_types=1);
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace Core\Content\Services;
|
namespace Core\Mod\Content\Services;
|
||||||
|
|
||||||
use Carbon\Carbon;
|
use Carbon\Carbon;
|
||||||
use Core\Mod\Tenant\Models\Workspace;
|
use Core\Mod\Tenant\Models\Workspace;
|
||||||
|
|
@ -10,7 +10,7 @@ use Illuminate\Contracts\Pagination\LengthAwarePaginator;
|
||||||
use Illuminate\Database\Eloquent\Builder;
|
use Illuminate\Database\Eloquent\Builder;
|
||||||
use Illuminate\Support\Collection;
|
use Illuminate\Support\Collection;
|
||||||
use Illuminate\Support\Str;
|
use Illuminate\Support\Str;
|
||||||
use Core\Content\Models\ContentItem;
|
use Core\Mod\Content\Models\ContentItem;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Content Search Service
|
* Content Search Service
|
||||||
|
|
|
||||||
|
|
@ -2,11 +2,11 @@
|
||||||
|
|
||||||
declare(strict_types=1);
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace Core\Content\Services;
|
namespace Core\Mod\Content\Services;
|
||||||
|
|
||||||
use Illuminate\Database\Eloquent\Collection;
|
use Illuminate\Database\Eloquent\Collection;
|
||||||
use Illuminate\Support\Facades\Log;
|
use Illuminate\Support\Facades\Log;
|
||||||
use Core\Content\Models\ContentWebhookLog;
|
use Core\Mod\Content\Models\ContentWebhookLog;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* WebhookRetryService
|
* WebhookRetryService
|
||||||
|
|
|
||||||
|
|
@ -2,16 +2,16 @@
|
||||||
|
|
||||||
declare(strict_types=1);
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace Core\Content\View\Modal\Admin;
|
namespace Core\Mod\Content\View\Modal\Admin;
|
||||||
|
|
||||||
use Livewire\Attributes\Computed;
|
use Livewire\Attributes\Computed;
|
||||||
use Livewire\Attributes\Layout;
|
use Livewire\Attributes\Layout;
|
||||||
use Livewire\Attributes\Url;
|
use Livewire\Attributes\Url;
|
||||||
use Livewire\Component;
|
use Livewire\Component;
|
||||||
use Livewire\WithPagination;
|
use Livewire\WithPagination;
|
||||||
use Core\Content\Models\ContentItem;
|
use Core\Mod\Content\Models\ContentItem;
|
||||||
use Core\Content\Models\ContentTaxonomy;
|
use Core\Mod\Content\Models\ContentTaxonomy;
|
||||||
use Core\Content\Services\ContentSearchService;
|
use Core\Mod\Content\Services\ContentSearchService;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Content Search Livewire Component
|
* Content Search Livewire Component
|
||||||
|
|
|
||||||
|
|
@ -2,15 +2,15 @@
|
||||||
|
|
||||||
declare(strict_types=1);
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace Core\Content\View\Modal\Admin;
|
namespace Core\Mod\Content\View\Modal\Admin;
|
||||||
|
|
||||||
use Livewire\Attributes\Computed;
|
use Livewire\Attributes\Computed;
|
||||||
use Livewire\Attributes\Layout;
|
use Livewire\Attributes\Layout;
|
||||||
use Livewire\Attributes\Url;
|
use Livewire\Attributes\Url;
|
||||||
use Livewire\Component;
|
use Livewire\Component;
|
||||||
use Livewire\WithPagination;
|
use Livewire\WithPagination;
|
||||||
use Core\Content\Models\ContentWebhookEndpoint;
|
use Core\Mod\Content\Models\ContentWebhookEndpoint;
|
||||||
use Core\Content\Models\ContentWebhookLog;
|
use Core\Mod\Content\Models\ContentWebhookLog;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Livewire component for managing content webhook endpoints.
|
* Livewire component for managing content webhook endpoints.
|
||||||
|
|
|
||||||
|
|
@ -2,10 +2,10 @@
|
||||||
|
|
||||||
declare(strict_types=1);
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace Core\Content\View\Modal\Web;
|
namespace Core\Mod\Content\View\Modal\Web;
|
||||||
|
|
||||||
use Livewire\Component;
|
use Livewire\Component;
|
||||||
use Core\Content\Services\ContentRender;
|
use Core\Mod\Content\Services\ContentRender;
|
||||||
use Core\Mod\Tenant\Models\Workspace;
|
use Core\Mod\Tenant\Models\Workspace;
|
||||||
use Core\Mod\Tenant\Services\WorkspaceService;
|
use Core\Mod\Tenant\Services\WorkspaceService;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,10 +2,10 @@
|
||||||
|
|
||||||
declare(strict_types=1);
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace Core\Content\View\Modal\Web;
|
namespace Core\Mod\Content\View\Modal\Web;
|
||||||
|
|
||||||
use Livewire\Component;
|
use Livewire\Component;
|
||||||
use Core\Content\Models\ContentItem;
|
use Core\Mod\Content\Models\ContentItem;
|
||||||
use Core\Mod\Tenant\Models\Workspace;
|
use Core\Mod\Tenant\Models\Workspace;
|
||||||
use Core\Mod\Tenant\Services\WorkspaceService;
|
use Core\Mod\Tenant\Services\WorkspaceService;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,10 +2,10 @@
|
||||||
|
|
||||||
declare(strict_types=1);
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace Core\Content\View\Modal\Web;
|
namespace Core\Mod\Content\View\Modal\Web;
|
||||||
|
|
||||||
use Livewire\Component;
|
use Livewire\Component;
|
||||||
use Core\Content\Services\ContentRender;
|
use Core\Mod\Content\Services\ContentRender;
|
||||||
use Core\Mod\Tenant\Models\Workspace;
|
use Core\Mod\Tenant\Models\Workspace;
|
||||||
use Core\Mod\Tenant\Services\WorkspaceService;
|
use Core\Mod\Tenant\Services\WorkspaceService;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,10 +2,10 @@
|
||||||
|
|
||||||
declare(strict_types=1);
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace Core\Content\View\Modal\Web;
|
namespace Core\Mod\Content\View\Modal\Web;
|
||||||
|
|
||||||
use Livewire\Component;
|
use Livewire\Component;
|
||||||
use Core\Content\Services\ContentRender;
|
use Core\Mod\Content\Services\ContentRender;
|
||||||
use Core\Mod\Tenant\Models\Workspace;
|
use Core\Mod\Tenant\Models\Workspace;
|
||||||
use Core\Mod\Tenant\Services\WorkspaceService;
|
use Core\Mod\Tenant\Services\WorkspaceService;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,11 +2,11 @@
|
||||||
|
|
||||||
declare(strict_types=1);
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace Core\Content\View\Modal\Web;
|
namespace Core\Mod\Content\View\Modal\Web;
|
||||||
|
|
||||||
use Core\Mod\Tenant\Models\Workspace;
|
use Core\Mod\Tenant\Models\Workspace;
|
||||||
use Livewire\Component;
|
use Livewire\Component;
|
||||||
use Core\Content\Models\ContentItem;
|
use Core\Mod\Content\Models\ContentItem;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Preview - Render draft/unpublished content with preview token.
|
* Preview - Render draft/unpublished content with preview token.
|
||||||
|
|
|
||||||
677
changelog/2026/jan/TASK-004-native-cms-wordpress-removal.md
Normal file
677
changelog/2026/jan/TASK-004-native-cms-wordpress-removal.md
Normal file
|
|
@ -0,0 +1,677 @@
|
||||||
|
# TASK-004: Native CMS and WordPress Removal
|
||||||
|
|
||||||
|
**Status:** ✅ complete (verified)
|
||||||
|
**Created:** 2026-01-02
|
||||||
|
**Last Updated:** 2026-01-02 16:30 by Claude Opus 4.5 (Implementation Agent)
|
||||||
|
**Assignee:** Claude Opus 4.5 (Implementation Agent)
|
||||||
|
**Verifier:** Claude Opus 4.5 (Verification Agent)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Critical Context (READ FIRST)
|
||||||
|
|
||||||
|
**WordPress served its purpose. Now we own the content layer.**
|
||||||
|
|
||||||
|
### The Current State
|
||||||
|
|
||||||
|
WordPress (hestia.host.uk.com) has been the content backend for satellite sites:
|
||||||
|
- Blog posts and help articles synced via REST API
|
||||||
|
- ContentItem model stores local copies with sync metadata
|
||||||
|
- SatelliteService uses "local-first" pattern (check DB, fallback to WP API)
|
||||||
|
- Webhook integration for real-time sync
|
||||||
|
|
||||||
|
This worked for bootstrapping, but creates:
|
||||||
|
- **Operational overhead** — two systems to maintain
|
||||||
|
- **Sync complexity** — race conditions, stale content, webhook failures
|
||||||
|
- **Stack mismatch** — PHP ↔ WordPress when we could be pure Laravel
|
||||||
|
- **Future friction** — MCP integration wants native content, not WP bridges
|
||||||
|
|
||||||
|
### The Vision
|
||||||
|
|
||||||
|
A fully native content management system where:
|
||||||
|
- ContentItem is the **source of truth** (not a sync cache)
|
||||||
|
- Content Editor at `/hub/content-editor/{workspace}/new/{contentType}` is the primary authoring tool
|
||||||
|
- AI assistance via MCP for content generation, SEO, translation
|
||||||
|
- Satellite sites serve directly from Laravel (no WordPress dependency)
|
||||||
|
- WordPress can re-integrate later as *one option among many* (headless CMS, not *the* CMS)
|
||||||
|
|
||||||
|
### Why Now
|
||||||
|
|
||||||
|
1. Content Editor already exists and works well
|
||||||
|
2. SatelliteService already has local-first logic
|
||||||
|
3. MCP Portal (mcp.host.uk.com) needs native content APIs
|
||||||
|
4. Phase 42 content generation uses native workflows
|
||||||
|
5. WordPress hosting is an unnecessary cost
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Objective
|
||||||
|
|
||||||
|
Remove WordPress as a required dependency for satellite site content. Make the native Content Editor the primary authoring tool, with ContentItem as the source of truth. Prepare the content layer for MCP integration (AI-assisted content creation, semantic search, agent access).
|
||||||
|
|
||||||
|
**"Done" looks like:**
|
||||||
|
- Satellite sites (blog, help) serve content entirely from Laravel database
|
||||||
|
- Content Editor is the only place to create/edit content
|
||||||
|
- No WordPress API calls in normal operation
|
||||||
|
- MCP tools can read/write content natively
|
||||||
|
- WordPress integration exists as an optional import/export feature
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Acceptance Criteria
|
||||||
|
|
||||||
|
### Phase 1: Remove WordPress Dependency ✅ VERIFIED
|
||||||
|
|
||||||
|
- [x] AC1: SatelliteService never calls WordPressService in normal operation
|
||||||
|
- [x] AC2: ContentItem has `content_type = 'native'` (new value, distinct from 'wordpress')
|
||||||
|
- [x] AC3: Blog pages (`/blog`, `/blog/{slug}`) render from native content only
|
||||||
|
- [x] AC4: Help pages (`/help`, `/help/{slug}`) render from native content only
|
||||||
|
- [x] AC5: WordPress sync code is moved to `app/Legacy/` (not deleted, for future import)
|
||||||
|
- [x] AC6: `WORDPRESS_URL` env var is optional, not required for app boot
|
||||||
|
|
||||||
|
### Phase 2: Content Editor Enhancements
|
||||||
|
|
||||||
|
- [x] AC7: Content Editor supports rich text with Flux Editor (not just textarea)
|
||||||
|
- [x] AC8: Content Editor has media upload (not WordPress media library)
|
||||||
|
- [x] AC9: Content Editor has category/tag management
|
||||||
|
- [x] AC10: Content Editor has SEO fields (meta title, description, OG image)
|
||||||
|
- [x] AC11: Content Editor has scheduling (publish_at datetime)
|
||||||
|
- [x] AC12: Content Editor has revision history
|
||||||
|
|
||||||
|
### Phase 3: MCP Integration ✅ COMPLETE
|
||||||
|
|
||||||
|
- [x] AC13: MCP tool `content_list` returns content items for a workspace
|
||||||
|
- [x] AC14: MCP tool `content_read` returns full content by ID or slug
|
||||||
|
- [x] AC15: MCP tool `content_create` creates new content (respects entitlements)
|
||||||
|
- [x] AC16: MCP tool `content_update` updates existing content
|
||||||
|
- [x] AC17: MCP resource `content://workspace/slug` provides content for context
|
||||||
|
- [x] AC18: AI content generation uses MCP tools (not direct OpenAI/Claude calls)
|
||||||
|
|
||||||
|
### Phase 4: Optional WordPress Import ✅ COMPLETE
|
||||||
|
|
||||||
|
- [x] AC19: Artisan command `content:import-wordpress` imports from WP REST API
|
||||||
|
- [x] AC20: Import preserves original WordPress IDs in `wp_id` field
|
||||||
|
- [x] AC21: Import handles media, categories, tags, authors
|
||||||
|
- [x] AC22: Import is idempotent (re-running updates, doesn't duplicate)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Implementation Checklist
|
||||||
|
|
||||||
|
### Phase 1: WordPress Removal
|
||||||
|
|
||||||
|
- [x] Create new enum value `ContentType::NATIVE` in `app/Enums/ContentType.php`
|
||||||
|
- [x] Update `ContentItem` model to default to `content_type = 'native'`
|
||||||
|
- [x] Refactor `SatelliteService` to remove WordPress fallback:
|
||||||
|
- [x] `getHomepage()` — native only
|
||||||
|
- [x] `getPosts()` — native only
|
||||||
|
- [x] `getPost()` — native only
|
||||||
|
- [x] `getPage()` — native only
|
||||||
|
- [x] Move WordPress integration files to `app/Legacy/`:
|
||||||
|
- [x] `app/Services/WordPressService.php` → `app/Legacy/WordPress/WordPressService.php`
|
||||||
|
- [x] `app/Services/ContentSyncService.php` → `app/Legacy/WordPress/ContentSyncService.php`
|
||||||
|
- [x] `app/Http/Controllers/Api/ContentWebhookController.php` → `app/Legacy/WordPress/`
|
||||||
|
- [x] `app/Jobs/ProcessContentWebhook.php` → `app/Legacy/WordPress/`
|
||||||
|
- [x] Update `config/services.php` to make WordPress optional
|
||||||
|
- [x] Make WordPress routes conditional in `routes/api.php` (enabled via `CONTENT_WORDPRESS_ENABLED`)
|
||||||
|
- [x] Update satellite views to not expect WordPress-specific fields (added native filter option)
|
||||||
|
- [x] Add feature flag `CONTENT_SOURCE=native` (default) vs `CONTENT_SOURCE=wordpress`
|
||||||
|
- [x] Write migration to update existing `content_type = 'wordpress'` → `'native'`
|
||||||
|
|
||||||
|
### Phase 2: Content Editor
|
||||||
|
|
||||||
|
- [x] Implement Flux Editor integration in `ContentEditor.php`
|
||||||
|
- [x] Create media upload in Content Editor (WithFileUploads trait, Livewire)
|
||||||
|
- [x] Add media library modal to Content Editor (featured image selection)
|
||||||
|
- [x] Add taxonomy management UI in Content Editor sidebar (categories/tags)
|
||||||
|
- [x] Add SEO fields component in Content Editor sidebar (meta title, description, keywords, preview)
|
||||||
|
- [x] Implement `publish_at` scheduling with datetime picker in sidebar
|
||||||
|
- [x] Create `ContentRevision` model for version history
|
||||||
|
- [x] Add revision history panel with restore functionality
|
||||||
|
- [x] Add autosave functionality (60-second interval)
|
||||||
|
- [x] Add Ctrl+S keyboard shortcut for save
|
||||||
|
|
||||||
|
### Phase 3: MCP Tools
|
||||||
|
|
||||||
|
- [x] Create `app/Mcp/Tools/ContentTools.php`:
|
||||||
|
- [x] `content_list` — paginated, filterable by type/status/search
|
||||||
|
- [x] `content_read` — by ID or slug, returns markdown
|
||||||
|
- [x] `content_create` — with entitlement check and taxonomy support
|
||||||
|
- [x] `content_update` — with revision creation
|
||||||
|
- [x] `content_delete` — soft delete
|
||||||
|
- [x] `taxonomies` — list categories and tags
|
||||||
|
- [x] Create `app/Mcp/Resources/ContentResource.php`:
|
||||||
|
- [x] URI format: `content://{workspace}/{slug}`
|
||||||
|
- [x] Returns markdown with YAML frontmatter for AI context
|
||||||
|
- [x] Includes categories, tags, SEO meta, publish_at
|
||||||
|
- [x] Register tools in MCP server (`app/Mcp/Servers/HostHub.php`)
|
||||||
|
- [x] Add entitlement features: `content.mcp_access`, `content.items`, `content.ai_generation`
|
||||||
|
- [x] Write MCP tool tests (48 tests passing)
|
||||||
|
|
||||||
|
### Phase 4: WordPress Import ✅ COMPLETE
|
||||||
|
|
||||||
|
- [x] Create `app/Console/Commands/ContentImportWordPress.php`
|
||||||
|
- [x] Implement batch import with progress bar
|
||||||
|
- [x] Map WordPress fields to ContentItem fields
|
||||||
|
- [x] Download and store media files locally
|
||||||
|
- [x] Create authors from WordPress users
|
||||||
|
- [x] Handle categories and tags
|
||||||
|
- [x] Add `--dry-run` flag for preview
|
||||||
|
- [x] Add `--since` flag for incremental imports
|
||||||
|
- [x] Add `--limit` flag for controlling import count
|
||||||
|
- [x] Add `--skip-media` flag to skip file downloads
|
||||||
|
- [x] Add `--types` flag to select what to import
|
||||||
|
- [x] Support JWT and Basic Auth for private content
|
||||||
|
- [x] Handle HTML entities and smart quotes
|
||||||
|
- [x] Extract SEO metadata from Yoast/RankMath
|
||||||
|
|
||||||
|
### Testing
|
||||||
|
|
||||||
|
- [x] Feature test: Satellite blog renders native content
|
||||||
|
- [x] Feature test: Content Editor creates native content
|
||||||
|
- [x] Feature test: Content Editor updates native content
|
||||||
|
- [x] Feature test: Media upload works
|
||||||
|
- [x] Feature test: MCP tools require authentication
|
||||||
|
- [x] Feature test: MCP tools respect entitlements
|
||||||
|
- [x] Unit test: ContentItem has correct relationships
|
||||||
|
- [x] Unit test: SatelliteService returns native content
|
||||||
|
- [x] Integration test: WordPress import processes all content types (21 tests)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Migration Strategy
|
||||||
|
|
||||||
|
### Zero Downtime Approach
|
||||||
|
|
||||||
|
1. **Phase 1a: Add native support alongside WordPress**
|
||||||
|
- Keep WordPress sync running
|
||||||
|
- Add `content_type = 'native'` capability
|
||||||
|
- Content Editor creates native content
|
||||||
|
- SatelliteService prefers native, falls back to WordPress
|
||||||
|
|
||||||
|
2. **Phase 1b: Migrate existing content**
|
||||||
|
- Run one-time migration to copy `wordpress` → `native`
|
||||||
|
- Verify all satellite pages render correctly
|
||||||
|
- Monitor for 1 week
|
||||||
|
|
||||||
|
3. **Phase 1c: Disable WordPress sync**
|
||||||
|
- Remove webhook registration
|
||||||
|
- Set `CONTENT_SOURCE=native`
|
||||||
|
- Keep WordPress running (read-only backup)
|
||||||
|
|
||||||
|
4. **Phase 1d: Remove WordPress**
|
||||||
|
- Move sync code to `app/Legacy/`
|
||||||
|
- Remove WordPress infrastructure
|
||||||
|
- Update documentation
|
||||||
|
|
||||||
|
### Rollback Plan
|
||||||
|
|
||||||
|
If issues arise:
|
||||||
|
1. Set `CONTENT_SOURCE=wordpress`
|
||||||
|
2. SatelliteService reverts to WordPress fallback
|
||||||
|
3. Re-enable webhook sync
|
||||||
|
4. Content continues serving from WordPress
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Data Model Changes
|
||||||
|
|
||||||
|
### ContentItem Updates
|
||||||
|
|
||||||
|
```php
|
||||||
|
// New enum value
|
||||||
|
enum ContentType: string
|
||||||
|
{
|
||||||
|
case WORDPRESS = 'wordpress'; // Legacy synced content
|
||||||
|
case NATIVE = 'native'; // Native Host UK content
|
||||||
|
case HOSTUK = 'hostuk'; // Keep for backwards compat, alias to native
|
||||||
|
case SATELLITE = 'satellite'; // Per-satellite site content
|
||||||
|
}
|
||||||
|
|
||||||
|
// New fields (if not present)
|
||||||
|
Schema::table('content_items', function (Blueprint $table) {
|
||||||
|
$table->timestamp('publish_at')->nullable()->after('status');
|
||||||
|
$table->unsignedBigInteger('revision_of')->nullable()->after('id');
|
||||||
|
$table->foreign('revision_of')->references('id')->on('content_items');
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
### New Table: content_revisions
|
||||||
|
|
||||||
|
```php
|
||||||
|
Schema::create('content_revisions', function (Blueprint $table) {
|
||||||
|
$table->id();
|
||||||
|
$table->foreignId('content_item_id')->constrained()->cascadeOnDelete();
|
||||||
|
$table->foreignId('user_id')->nullable()->constrained();
|
||||||
|
$table->string('title');
|
||||||
|
$table->longText('content_json');
|
||||||
|
$table->longText('content_html');
|
||||||
|
$table->text('change_summary')->nullable();
|
||||||
|
$table->timestamps();
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## MCP Tool Specifications
|
||||||
|
|
||||||
|
### content_list
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"name": "content_list",
|
||||||
|
"description": "List content items for a workspace. Use to find articles, blog posts, or help pages.",
|
||||||
|
"parameters": {
|
||||||
|
"workspace": "Workspace slug (main, bio, social, etc.)",
|
||||||
|
"type": "Filter by type: post, page, or all",
|
||||||
|
"status": "Filter by status: draft, published, scheduled",
|
||||||
|
"limit": "Max items to return (default 20)",
|
||||||
|
"search": "Search title and content"
|
||||||
|
},
|
||||||
|
"use_when": [
|
||||||
|
"Need to find existing content",
|
||||||
|
"Want to see what's published on a satellite site",
|
||||||
|
"Looking for content to update or reference"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### content_read
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"name": "content_read",
|
||||||
|
"description": "Read full content of an article or page. Returns markdown for easy processing.",
|
||||||
|
"parameters": {
|
||||||
|
"workspace": "Workspace slug",
|
||||||
|
"identifier": "Content slug or ID"
|
||||||
|
},
|
||||||
|
"returns": "Full content as markdown with frontmatter (title, author, date, categories)"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### content_create
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"name": "content_create",
|
||||||
|
"description": "Create new content. Requires workspace write permission and ai.credits entitlement.",
|
||||||
|
"parameters": {
|
||||||
|
"workspace": "Target workspace",
|
||||||
|
"type": "post or page",
|
||||||
|
"title": "Content title",
|
||||||
|
"content": "Markdown content",
|
||||||
|
"status": "draft (default), published, or scheduled",
|
||||||
|
"publish_at": "ISO datetime for scheduled publishing",
|
||||||
|
"categories": "Array of category slugs",
|
||||||
|
"tags": "Array of tag strings"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Dependencies
|
||||||
|
|
||||||
|
- Flux Editor component (already in Flux Pro)
|
||||||
|
- S3 or local storage for media
|
||||||
|
- MCP server infrastructure (existing)
|
||||||
|
- Entitlement system (existing)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Risks and Mitigations
|
||||||
|
|
||||||
|
| Risk | Impact | Mitigation |
|
||||||
|
|------|--------|------------|
|
||||||
|
| Content loss during migration | High | Backup before migration, keep WordPress running until verified |
|
||||||
|
| SEO impact from URL changes | Medium | Keep same URL structure, 301 redirects if needed |
|
||||||
|
| Editor bugs causing data loss | High | Implement autosave, revision history from day 1 |
|
||||||
|
| MCP tools expose sensitive content | Medium | Workspace permission checks, entitlement gating |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Verification Results
|
||||||
|
|
||||||
|
### Phase 1 Verification: 2026-01-02 by Claude Opus 4.5 (Verification Agent)
|
||||||
|
|
||||||
|
| Criterion | Status | Evidence |
|
||||||
|
|-----------|--------|----------|
|
||||||
|
| AC1: SatelliteService never calls WordPressService | ✅ PASS | `grep WordPressService app/Services/SatelliteService.php` returns no matches. File uses only `ContentItem` queries with `->native()` scope. Lines 38-44, 76-80, 104-111, 128-134 all use `ContentItem::forWorkspace()->native()`. |
|
||||||
|
| AC2: ContentItem has `content_type = 'native'` (new default) | ✅ PASS | `ContentType::NATIVE` enum exists at `app/Enums/ContentType.php:16`. Model's `booted()` method at lines 444-452 sets default via `ContentType::default()` which returns `NATIVE`. |
|
||||||
|
| AC3: Blog pages render from native content only | ✅ PASS | `app/Livewire/Satellite/Blog.php` uses `SatelliteService::getPosts()` which queries `ContentItem::native()`. No WordPressService import. |
|
||||||
|
| AC4: Help pages render from native content only | ✅ PASS | `app/Livewire/Satellite/Help.php` queries `ContentItem::forWorkspace()->native()->pages()` directly at lines 38-44. No WordPressService import. |
|
||||||
|
| AC5: WordPress sync code moved to `app/Legacy/` | ✅ PASS | Files exist at `app/Legacy/WordPress/`: `WordPressService.php`, `ContentSyncService.php`, `ContentWebhookController.php`, `ProcessContentWebhook.php`. Original locations (`app/Services/WordPressService.php`, `app/Services/ContentSyncService.php`) return "No such file or directory". |
|
||||||
|
| AC6: `WORDPRESS_URL` env var is optional | ✅ PASS | `config/services.php:48-54` shows `content.source` defaults to `native` and `content.wordpress_enabled` defaults to `false`. WordPress config at lines 67-74 only read if enabled. `routes/api.php:41` gates WordPress routes behind `config('services.content.wordpress_enabled')`. |
|
||||||
|
|
||||||
|
**Additional Verification:**
|
||||||
|
- Migration `2026_01_02_140456_update_wordpress_content_to_native.php` exists and converts `wordpress`/`hostuk` to `native`
|
||||||
|
- Tests: `./vendor/bin/pest --filter="Satellite|Content"` — 67 passed (2 unrelated SocialProof failures)
|
||||||
|
|
||||||
|
**Verdict:** ✅ PASS — Phase 1 acceptance criteria AC1-AC6 all met. Ready for Phase 2 implementation.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Phase 2 Verification: 2026-01-02 by Claude Opus 4.5 (Verification Agent)
|
||||||
|
|
||||||
|
| Criterion | Status | Evidence |
|
||||||
|
|-----------|--------|----------|
|
||||||
|
| AC7: Content Editor supports rich text with Flux Editor | ✅ PASS | `<flux:editor` at `content-editor.blade.php:150`. Uses Flux Pro's rich text editor component with `wire:model="content"`. |
|
||||||
|
| AC8: Content Editor has media upload | ✅ PASS | `ContentEditor.php:355-380` implements `uploadFeaturedImage()` method with `WithFileUploads` trait. Uses Laravel's `store()` to save to `content-media` disk. Creates `ContentMedia` record. Featured image selection also supports media library modal. |
|
||||||
|
| AC9: Content Editor has category/tag management | ✅ PASS | `ContentEditor.php:273-329` implements `addTag()`, `removeTag()`, `toggleCategory()` methods. Properties `$selectedCategories` and `$selectedTags` track selections. View has category checkboxes and tag input with add/remove UI in Settings tab. |
|
||||||
|
| AC10: Content Editor has SEO fields | ✅ PASS | `ContentEditor.php:53-56` declares `$seoTitle`, `$seoDescription`, `$seoKeywords`, `$ogImage`. Lines 447-451 save to `seo_meta` JSON field. View has SEO tab with character counters (70 for title, 160 for description) and search preview. |
|
||||||
|
| AC11: Content Editor has scheduling | ✅ PASS | `ContentEditor.php:49-50` declares `$publishAt` and `$isScheduled`. Lines 257-265 toggle scheduling. Method `schedule()` at lines 521-533 saves with status='future'. Migration adds `publish_at` column to content_items. View has scheduling toggle and datetime picker. |
|
||||||
|
| AC12: Content Editor has revision history | ✅ PASS | `ContentRevision` model at `app/Models/ContentRevision.php`. `ContentEditor.php:389-440` implements `loadRevisions()` and `restoreRevision()`. Save method creates revision via `ContentRevision::createFromContentItem()`. Migration creates `content_revisions` table. View has History tab with revision list and restore buttons. |
|
||||||
|
|
||||||
|
**Additional Verification:**
|
||||||
|
- Migrations exist: `2026_01_02_141713_add_scheduling_fields_to_content_items_table.php`, `2026_01_02_141714_create_content_revisions_table.php`
|
||||||
|
- Tests: `./vendor/bin/pest --filter="Content"` — 56 Content tests passed (2 unrelated SocialProof failures)
|
||||||
|
- Autosave: `ContentEditor.php:507` calls `save(ContentRevision::CHANGE_AUTOSAVE)` every 60 seconds
|
||||||
|
- Keyboard shortcut: Ctrl+S save supported via view JavaScript
|
||||||
|
|
||||||
|
**Verdict:** ✅ PASS — Phase 2 acceptance criteria AC7-AC12 all met. Ready for Phase 3 implementation.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Phase 3 Verification: 2026-01-02 by Claude Opus 4.5 (Verification Agent)
|
||||||
|
|
||||||
|
| Criterion | Status | Evidence |
|
||||||
|
|-----------|--------|----------|
|
||||||
|
| AC13: MCP tool `content_list` returns content items for a workspace | ✅ PASS | `ContentTools.php:98-161` implements `listContent()` with pagination, filtering by type/status/search, returns items with id, slug, title, type, status, categories, tags. Test: `ContentToolsTest.php` "lists content items for a workspace" passes. |
|
||||||
|
| AC14: MCP tool `content_read` returns full content by ID or slug | ✅ PASS | `ContentTools.php:166-238` implements `readContent()` resolving by ID or slug, returns JSON with full content or markdown format with YAML frontmatter. Test: "reads content by slug", "reads content by ID", "returns content as markdown" all pass. |
|
||||||
|
| AC15: MCP tool `content_create` creates new content (respects entitlements) | ✅ PASS | `ContentTools.php:243-346` implements `createContent()` with entitlement check at line 246 (`checkEntitlement`), calls `EntitlementService::can()` for `content.mcp_access` and `content.items`. Records usage at line 329. Test: "creates a new content item" passes with entitlement setup in beforeEach. |
|
||||||
|
| AC16: MCP tool `content_update` updates existing content | ✅ PASS | `ContentTools.php:351-454` implements `updateContent()` with revision creation at line 438 via `createRevision()`. Handles title, content, status, slug, categories, tags updates. Test: "updates existing content", "creates revision on update" pass. |
|
||||||
|
| AC17: MCP resource `content://workspace/slug` provides content for context | ✅ PASS | `ContentResource.php:19-133` implements URI format `content://{workspace}/{slug}`, returns markdown with YAML frontmatter including title, slug, type, status, author, categories, tags, publish_at, seo meta. `list()` method at line 140-169 returns all published native content as resources. Tests: 14 ContentResourceTest tests pass. |
|
||||||
|
| AC18: AI content generation uses MCP tools (not direct OpenAI/Claude calls) | ✅ PASS | MCP tools for content creation are fully functional via `ContentTools::createContent()`. AI agents access content via MCP protocol using `content_create` action. Entitlement feature `content.ai_generation` exists in FeatureSeeder.php:246. HostHub MCP server (line 86) registers ContentTools. 48 MCP content tests pass demonstrating AI agent capability. |
|
||||||
|
|
||||||
|
**Additional Verification:**
|
||||||
|
- MCP Server: `HostHub.php:86` includes `ContentTools::class` in tools array, `ContentResource::class` at line 92 in resources
|
||||||
|
- Entitlement Features: `FeatureSeeder.php:228-253` defines `content.mcp_access`, `content.items`, `content.ai_generation`
|
||||||
|
- Tests: `./vendor/bin/pest tests/Feature/Mcp/ContentToolsTest.php tests/Feature/Mcp/ContentResourceTest.php` — 48 passed (117 assertions)
|
||||||
|
- Migrations: `make_wp_id_nullable_on_content_items_table.php`, `make_wp_id_nullable_on_content_taxonomies_table.php` allow native content without WordPress IDs
|
||||||
|
|
||||||
|
**Verdict:** ✅ PASS — Phase 3 acceptance criteria AC13-AC18 all met. Ready for Phase 4 implementation.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Phase 1 Implementation Notes (2026-01-02)
|
||||||
|
|
||||||
|
**Completed by:** Claude Opus 4.5 (Implementation Agent)
|
||||||
|
|
||||||
|
Phase 1 implementation is complete. Summary of changes:
|
||||||
|
|
||||||
|
1. **ContentType enum** (`app/Enums/ContentType.php`): Added `NATIVE` value as the new default content type.
|
||||||
|
|
||||||
|
2. **ContentItem model** (`app/Models/ContentItem.php`): Now defaults to `content_type = 'native'` and has `native()` scope for filtering.
|
||||||
|
|
||||||
|
3. **SatelliteService** (`app/Services/SatelliteService.php`): Completely rewritten to use native ContentItem queries. No WordPress fallback - returns content from database directly.
|
||||||
|
|
||||||
|
4. **Legacy namespace** (`app/Legacy/WordPress/`): Moved WordPress integration files:
|
||||||
|
- `WordPressService.php`
|
||||||
|
- `ContentSyncService.php`
|
||||||
|
- `ContentWebhookController.php`
|
||||||
|
- `ProcessContentWebhook.php`
|
||||||
|
|
||||||
|
5. **Feature flags** (`config/services.php`):
|
||||||
|
- `CONTENT_SOURCE=native` (default) - controls SatelliteService behaviour
|
||||||
|
- `CONTENT_WORDPRESS_ENABLED=false` (default) - gates WordPress webhook routes
|
||||||
|
|
||||||
|
6. **Routes** (`routes/api.php`): WordPress webhook endpoints now conditional on `CONTENT_WORDPRESS_ENABLED`.
|
||||||
|
|
||||||
|
7. **Livewire components updated**:
|
||||||
|
- `ContentEditor.php` - uses ContentType enum
|
||||||
|
- `Satellite/Blog.php`, `Post.php`, `Help.php`, `HelpArticle.php` - use SatelliteService
|
||||||
|
- `Workspace/Home.php` - uses SatelliteService
|
||||||
|
- `Admin/Content.php`, `ContentManager.php`, `Databases.php` - use Legacy namespace
|
||||||
|
|
||||||
|
8. **Migration** (`2026_01_02_140456_update_wordpress_content_to_native.php`): Updates existing `wordpress` and `hostuk` content types to `native`.
|
||||||
|
|
||||||
|
9. **Tests**: All 71 content-related tests pass. Updated `SatelliteRoutesTest` to expect home view (not waitlist) when workspace has no content.
|
||||||
|
|
||||||
|
**Ready for verification:** Phase 1 acceptance criteria AC1-AC6 should be tested.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Phase 2 Implementation Notes (2026-01-02)
|
||||||
|
|
||||||
|
**Completed by:** Claude Opus 4.5 (Implementation Agent)
|
||||||
|
|
||||||
|
Phase 2 implementation is complete. Summary of changes:
|
||||||
|
|
||||||
|
1. **Migrations created:**
|
||||||
|
- `2026_01_02_141713_add_scheduling_fields_to_content_items_table.php`: Adds `publish_at`, `revision_count`, `last_edited_by` fields
|
||||||
|
- `2026_01_02_141714_create_content_revisions_table.php`: Creates revision history table
|
||||||
|
|
||||||
|
2. **ContentRevision model** (`app/Models/ContentRevision.php`): New model for version history with:
|
||||||
|
- Change type constants (edit, autosave, restore, publish, unpublish, schedule)
|
||||||
|
- `createFromContentItem()` static method for creating snapshots
|
||||||
|
- `restoreToContentItem()` method for restoring previous versions
|
||||||
|
- Word/character count tracking
|
||||||
|
- Diff summary generation
|
||||||
|
|
||||||
|
3. **ContentItem model updates** (`app/Models/ContentItem.php`):
|
||||||
|
- New `lastEditedBy()` relationship
|
||||||
|
- New `revisions()` HasMany relationship
|
||||||
|
- New `scopeScheduled()` and `scopeReadyToPublish()` scopes
|
||||||
|
- New `isScheduled()`, `createRevision()`, `latestRevision()` methods
|
||||||
|
|
||||||
|
4. **ContentEditor Livewire component** (`app/Livewire/Admin/ContentEditor.php`): Complete rewrite with:
|
||||||
|
- `WithFileUploads` trait for media upload
|
||||||
|
- Scheduling support with `isScheduled` toggle and `publishAt` datetime
|
||||||
|
- SEO fields (meta title, description, keywords) with character counts
|
||||||
|
- Category management (checkbox toggles)
|
||||||
|
- Tag management (add/remove with UI)
|
||||||
|
- Featured image upload and media library selection
|
||||||
|
- Revision history with restore capability
|
||||||
|
- Autosave functionality (60-second interval)
|
||||||
|
- Revision creation on save with change type tracking
|
||||||
|
|
||||||
|
5. **ContentEditor view** (`resources/views/admin/livewire/content-editor.blade.php`): Complete rewrite with:
|
||||||
|
- Two-column layout: main editor + sidebar
|
||||||
|
- Sidebar with 4 tabbed panels: Settings, SEO, Media, History
|
||||||
|
- Settings panel: scheduling toggle, datetime picker, categories, tags
|
||||||
|
- SEO panel: meta fields with character counters and search preview
|
||||||
|
- Media panel: featured image upload with drag-drop and library selection
|
||||||
|
- History panel: revision list with timestamps and restore buttons
|
||||||
|
- Ctrl+S keyboard shortcut for save
|
||||||
|
|
||||||
|
6. **Tests:** All 39 ContentManager tests pass including ContentItem model tests.
|
||||||
|
|
||||||
|
**Ready for verification:** Phase 2 acceptance criteria AC7-AC12 should be tested.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Phase 3 Implementation Notes (2026-01-02)
|
||||||
|
|
||||||
|
**Completed by:** Claude Opus 4.5 (Implementation Agent)
|
||||||
|
|
||||||
|
Phase 3 implementation is complete. Summary of changes:
|
||||||
|
|
||||||
|
1. **ContentTools MCP Tool** (`app/Mcp/Tools/ContentTools.php`): Complete MCP tool for content management with:
|
||||||
|
- `list` action: Paginated, filterable by workspace, type, status, and search term
|
||||||
|
- `read` action: Returns content by ID or slug as markdown with frontmatter
|
||||||
|
- `create` action: Creates new content with entitlement check, taxonomy support, and automatic slug generation
|
||||||
|
- `update` action: Updates existing content with revision history tracking
|
||||||
|
- `delete` action: Soft deletes content
|
||||||
|
- `taxonomies` action: Lists categories and tags for a workspace
|
||||||
|
- Entitlement checking via `EntitlementService::can()`
|
||||||
|
- Automatic taxonomy creation when names don't exist
|
||||||
|
- Markdown output with YAML frontmatter for AI context
|
||||||
|
|
||||||
|
2. **ContentResource MCP Resource** (`app/Mcp/Resources/ContentResource.php`): MCP resource for content:// URIs:
|
||||||
|
- URI format: `content://{workspace}/{slug}` or `content://{workspace}/{id}`
|
||||||
|
- Returns markdown with YAML frontmatter including title, slug, type, status, author, categories, tags, SEO meta
|
||||||
|
- Workspace resolution by slug or ID
|
||||||
|
- Content resolution by slug or ID
|
||||||
|
- `list()` method returns all published native content as available resources
|
||||||
|
|
||||||
|
3. **MCP Server Registration** (`app/Mcp/Servers/HostHub.php`):
|
||||||
|
- Added `ContentTools` to tools array
|
||||||
|
- Added `ContentResource` to resources array
|
||||||
|
- Updated instructions with content tool documentation
|
||||||
|
|
||||||
|
4. **Entitlement Features** (`database/seeders/FeatureSeeder.php`): Added content features:
|
||||||
|
- `content.mcp_access`: Boolean feature for MCP access
|
||||||
|
- `content.items`: Limit feature for number of content items
|
||||||
|
- `content.ai_generation`: Boolean feature for AI content generation
|
||||||
|
|
||||||
|
5. **Database Migrations**: Created migrations to support native content:
|
||||||
|
- `2026_01_02_150351_make_wp_id_nullable_on_content_items_table.php`: Makes wp_id and sync_status nullable
|
||||||
|
- `2026_01_02_150640_make_wp_id_nullable_on_content_taxonomies_table.php`: Makes taxonomy wp_id nullable
|
||||||
|
|
||||||
|
6. **Factory Updates** (`database/factories/ContentItemFactory.php`): Added `native()` state for creating native content without WordPress fields.
|
||||||
|
|
||||||
|
7. **Tests**: Created comprehensive test suites:
|
||||||
|
- `tests/Feature/Mcp/ContentToolsTest.php`: 34 tests covering all actions and error handling
|
||||||
|
- `tests/Feature/Mcp/ContentResourceTest.php`: 14 tests for resource handling and listing
|
||||||
|
- All 48 tests pass
|
||||||
|
|
||||||
|
**Files Created/Modified:**
|
||||||
|
- `app/Mcp/Tools/ContentTools.php` (new)
|
||||||
|
- `app/Mcp/Resources/ContentResource.php` (new)
|
||||||
|
- `app/Mcp/Servers/HostHub.php` (modified)
|
||||||
|
- `database/seeders/FeatureSeeder.php` (modified)
|
||||||
|
- `database/factories/ContentItemFactory.php` (modified)
|
||||||
|
- `database/migrations/2026_01_02_150351_make_wp_id_nullable_on_content_items_table.php` (new)
|
||||||
|
- `database/migrations/2026_01_02_150640_make_wp_id_nullable_on_content_taxonomies_table.php` (new)
|
||||||
|
- `tests/Feature/Mcp/ContentToolsTest.php` (new)
|
||||||
|
- `tests/Feature/Mcp/ContentResourceTest.php` (new)
|
||||||
|
|
||||||
|
**Ready for verification:** Phase 3 acceptance criteria AC13-AC18 should be tested.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Phase 4 Implementation Notes (2026-01-02)
|
||||||
|
|
||||||
|
**Completed by:** Claude Opus 4.5 (Implementation Agent)
|
||||||
|
|
||||||
|
Phase 4 implementation is complete. Summary of changes:
|
||||||
|
|
||||||
|
1. **ContentImportWordPress Command** (`app/Console/Commands/ContentImportWordPress.php`): Complete WordPress import artisan command with:
|
||||||
|
- Batch import with progress bars for each content type
|
||||||
|
- Support for posts, pages, categories, tags, authors, and media
|
||||||
|
- JWT and Basic Auth support for private/draft content
|
||||||
|
- `--dry-run` flag for preview mode (no database changes)
|
||||||
|
- `--since` flag for incremental imports (ISO 8601 date filter)
|
||||||
|
- `--limit` flag for controlling maximum items per type
|
||||||
|
- `--skip-media` flag to skip file downloads (creates records only)
|
||||||
|
- `--types` flag to select specific content types (default: posts,pages)
|
||||||
|
- `--workspace` flag to target specific workspace
|
||||||
|
- HTML entity decoding with smart quote normalization
|
||||||
|
- SEO metadata extraction from Yoast and RankMath plugins
|
||||||
|
- Idempotent operation (updates existing records, doesn't duplicate)
|
||||||
|
|
||||||
|
2. **Import Flow:**
|
||||||
|
- Validates WordPress site accessibility via REST API
|
||||||
|
- Resolves target workspace by ID or slug
|
||||||
|
- Imports in dependency order: authors → categories → tags → media → posts → pages
|
||||||
|
- Maps WordPress IDs to local IDs for relationship linking
|
||||||
|
- Creates ContentMedia records with optional file download to storage disk
|
||||||
|
- Syncs taxonomies with posts via pivot table
|
||||||
|
|
||||||
|
3. **Key Features:**
|
||||||
|
- `ContentType::WORDPRESS` used for imported content (distinct from `NATIVE`)
|
||||||
|
- `wp_id` and `wp_guid` preserved for reference
|
||||||
|
- `sync_status = 'synced'` and `synced_at` timestamp recorded
|
||||||
|
- Progress bars show import progress with created/updated/skipped counts
|
||||||
|
- Scheduled posts imported with `status = 'future'` and `publish_at` set
|
||||||
|
|
||||||
|
4. **Tests** (`tests/Feature/Console/ContentImportWordPressTest.php`): 21 comprehensive tests covering:
|
||||||
|
- Validation (site accessibility, workspace existence, date format)
|
||||||
|
- Author imports and updates
|
||||||
|
- Category and tag imports
|
||||||
|
- Post and page imports with full field mapping
|
||||||
|
- WordPress ID preservation
|
||||||
|
- HTML entity handling and smart quote normalization
|
||||||
|
- Category/tag linking on posts
|
||||||
|
- Media imports with file download
|
||||||
|
- Skip media flag (records without downloads)
|
||||||
|
- Dry run mode
|
||||||
|
- Incremental imports with --since
|
||||||
|
- Idempotency (re-running updates, doesn't duplicate)
|
||||||
|
- Limit option
|
||||||
|
- SEO metadata extraction
|
||||||
|
- Scheduled post handling
|
||||||
|
|
||||||
|
**Files Created:**
|
||||||
|
- `app/Console/Commands/ContentImportWordPress.php`
|
||||||
|
- `tests/Feature/Console/ContentImportWordPressTest.php`
|
||||||
|
|
||||||
|
**Usage Examples:**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Basic import (posts and pages)
|
||||||
|
php artisan content:import-wordpress https://example.com --workspace=main
|
||||||
|
|
||||||
|
# Full import with all content types
|
||||||
|
php artisan content:import-wordpress https://example.com --workspace=main --types=authors,categories,tags,media,posts,pages
|
||||||
|
|
||||||
|
# Dry run preview
|
||||||
|
php artisan content:import-wordpress https://example.com --workspace=main --dry-run
|
||||||
|
|
||||||
|
# Incremental import (only content modified since date)
|
||||||
|
php artisan content:import-wordpress https://example.com --workspace=main --since=2024-01-01
|
||||||
|
|
||||||
|
# Authenticated import for drafts
|
||||||
|
php artisan content:import-wordpress https://example.com --workspace=main --username=admin --password=secret
|
||||||
|
|
||||||
|
# Limited import
|
||||||
|
php artisan content:import-wordpress https://example.com --workspace=main --limit=50
|
||||||
|
|
||||||
|
# Skip media downloads
|
||||||
|
php artisan content:import-wordpress https://example.com --workspace=main --skip-media
|
||||||
|
```
|
||||||
|
|
||||||
|
**Ready for verification:** Phase 4 acceptance criteria AC19-AC22 should be tested.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Phase 4 Verification: 2026-01-02 by Claude Opus 4.5 (Verification Agent)
|
||||||
|
|
||||||
|
| Criterion | Status | Evidence |
|
||||||
|
|-----------|--------|----------|
|
||||||
|
| AC19: Artisan command `content:import-wordpress` imports from WP REST API | ✅ PASS | `ContentImportWordPress.php` is 925 lines implementing full WP REST API import. Signature at line 27-36 shows `content:import-wordpress {url}` with options. `validateWordPressSite()` at line 143 calls `/wp-json`, `importContentType()` at line 655 fetches from `/wp-json/wp/v2/{posts\|pages}`. Test: "validates WordPress site and shows site name" passes. |
|
||||||
|
| AC20: Import preserves original WordPress IDs in `wp_id` field | ✅ PASS | `importContentItem()` at line 758 sets `'wp_id' => $wpId` and `'wp_guid' => $item['guid']['rendered']`. Test: "preserves original WordPress IDs" at line 313 verifies `$post->wp_id->toBe(12345)` and `$post->wp_guid->toBe('https://example.com/?p=12345')`. |
|
||||||
|
| AC21: Import handles media, categories, tags, authors | ✅ PASS | `importAuthors()` line 231, `importCategories()` line 326, `importTags()` line 381, `importMedia()` line 480. Each has dedicated import method with progress bars. Tests verify all types: "imports WordPress users as authors", "imports WordPress categories", "imports WordPress tags", "imports WordPress media items" all pass. |
|
||||||
|
| AC22: Import is idempotent (re-running updates, doesn't duplicate) | ✅ PASS | Each import method checks for existing records first (e.g., `ContentItem::forWorkspace()->where('wp_id', $wpId)->first()` at line 729). If exists, updates instead of creates (line 791-793). Test: "idempotency → updates existing content instead of duplicating" verifies `ContentItem::count()->toBe(1)` after re-import and fields are updated. |
|
||||||
|
|
||||||
|
**Additional Verification:**
|
||||||
|
- Tests: `./vendor/bin/pest tests/Feature/Console/ContentImportWordPressTest.php` — 21 passed (90 assertions)
|
||||||
|
- Dry run mode: `--dry-run` flag prevents database changes (test at line 522)
|
||||||
|
- Incremental import: `--since` flag filters by modification date (test at line 569)
|
||||||
|
- Limit option: `--limit` flag caps items per type (test at line 647)
|
||||||
|
- SEO extraction: Yoast/RankMath metadata extracted (test at line 717)
|
||||||
|
- Scheduled posts: Future status and publish_at preserved (test at line 762)
|
||||||
|
- HTML entities: Smart quotes normalised to ASCII (test at line 348)
|
||||||
|
|
||||||
|
**Verdict:** ✅ PASS — Phase 4 acceptance criteria AC19-AC22 all met. TASK-004 is complete.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Notes
|
||||||
|
|
||||||
|
### Why Keep WordPress Import
|
||||||
|
|
||||||
|
Even though we're removing WordPress as a dependency, keeping import capability:
|
||||||
|
1. Allows migration from other WordPress sites
|
||||||
|
2. Useful for clients moving from WP to Host Hub
|
||||||
|
3. Reference for future CMS integrations (Ghost, Strapi, etc.)
|
||||||
|
|
||||||
|
### MCP as the Future
|
||||||
|
|
||||||
|
The content MCP tools are strategic:
|
||||||
|
- Agents can create content without UI
|
||||||
|
- Enables automated content pipelines
|
||||||
|
- Positions Host UK as "AI-stabilised hosting" leader
|
||||||
|
- Content becomes programmable infrastructure
|
||||||
|
|
||||||
|
### Content Type Clarification
|
||||||
|
|
||||||
|
- `wordpress` — Legacy, synced from WordPress (to be migrated)
|
||||||
|
- `native` — Created in Host Hub (new default)
|
||||||
|
- `hostuk` — Alias for native (backwards compat)
|
||||||
|
- `satellite` — Per-service content (e.g., BioHost-specific help)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
*This task transforms content from a synced dependency to owned infrastructure.*
|
||||||
1012
changelog/2026/jan/TASK-content-api-backend.md
Normal file
1012
changelog/2026/jan/TASK-content-api-backend.md
Normal file
File diff suppressed because it is too large
Load diff
130
changelog/2026/jan/code-review.md
Normal file
130
changelog/2026/jan/code-review.md
Normal file
|
|
@ -0,0 +1,130 @@
|
||||||
|
# Content Module Review
|
||||||
|
|
||||||
|
**Updated:** 2026-01-21 - All recommended improvements implemented
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
|
||||||
|
The Content module manages the content system for Host UK, providing:
|
||||||
|
|
||||||
|
1. **Native Content Management** - ContentItem, ContentAuthor, ContentMedia, ContentTaxonomy, ContentRevision models for storing and managing content natively
|
||||||
|
2. **AI Content Generation** - Two-stage pipeline using Gemini (draft) + Claude (refinement) via ContentBrief and AIGatewayService
|
||||||
|
3. **WordPress Import** - One-time import from WordPress sites via REST API (ContentImportWordPress command)
|
||||||
|
4. **Public Rendering** - Satellite pages for blog/help content via Livewire components
|
||||||
|
5. **Content Processing** - HTML cleaning and JSON block parsing for headless rendering
|
||||||
|
|
||||||
|
The module is transitioning away from WordPress sync (deprecated) to a fully native content system with AI-assisted content generation.
|
||||||
|
|
||||||
|
## Production Readiness Score: 92/100 (was 85/100 - All recommended improvements implemented 2026-01-21)
|
||||||
|
|
||||||
|
## Critical Issues (Must Fix)
|
||||||
|
|
||||||
|
- [x] **Missing database migrations for core tables** - FIXED: Created `2026_01_14_000001_create_content_core_tables.php` migration documenting all 7 content tables with `Schema::hasTable()` guards.
|
||||||
|
|
||||||
|
- [x] **ContentBrief model missing factory** - FIXED: Created `ContentBriefFactory.php` with states for status, content types, and priorities.
|
||||||
|
|
||||||
|
- [x] **AIUsage model missing factory** - FIXED: Created `AIUsageFactory.php` with states for providers, purposes, and usage amounts.
|
||||||
|
|
||||||
|
- [x] **Content rendering XSS vulnerability** - FIXED: All blade templates now use `{{ }}` for titles. Added `getSanitisedContent()` method using HTMLPurifier for body content.
|
||||||
|
|
||||||
|
- [x] **Waitlist stored in cache only** - FIXED: `ContentRender` now uses `WaitlistEntry` model from Tenant module for persistent database storage.
|
||||||
|
|
||||||
|
## Recommended Improvements
|
||||||
|
|
||||||
|
- [x] **Add rate limiting to API endpoints** - FIXED: Rate limiting added to `/api/v1/content/generate/*` endpoints.
|
||||||
|
|
||||||
|
- [x] **Add validation for content_type in ContentBrief** - FIXED: BriefContentType enum added for type safety.
|
||||||
|
|
||||||
|
- [x] **ContentProcessingService error handling** - FIXED: DOM parsing errors now logged for debugging.
|
||||||
|
|
||||||
|
- [x] **Cache key collision potential** - FIXED: Cache key sanitisation added to ContentRender to handle special characters in slugs.
|
||||||
|
|
||||||
|
- [x] **Add indexes to ContentBrief table** - FIXED: Index added for `scheduled_for` column.
|
||||||
|
|
||||||
|
- [x] **Job timeout configuration** - FIXED: `GenerateContentJob` timeout now configurable for different content types.
|
||||||
|
|
||||||
|
- [x] **AIGatewayService constructor signature** - FIXED: Refactored to read config() fresh in getGemini()/getClaude() methods. Constructor now only stores optional overrides. Added resetServices() method for runtime config changes. Verified implementation shows clean separation of override vs config-based configuration.
|
||||||
|
|
||||||
|
- [x] **Help component queries pages not help articles** - FIXED: Help.php now uses `helpArticles` scope for proper content filtering.
|
||||||
|
|
||||||
|
- [x] **ContentRevision lacks pruning mechanism** - FIXED: Revision pruning command added with configurable retention policy.
|
||||||
|
|
||||||
|
- [x] **Deprecated commands still registered** - VERIFIED: ContentSync and ContentPublish already have proper deprecation warnings via trigger_error(E_USER_DEPRECATED) and console output.
|
||||||
|
|
||||||
|
## Missing Features (Future)
|
||||||
|
|
||||||
|
- [x] **Content scheduling system** - FIXED: Created `PublishScheduledContent` command (`content:publish-scheduled`) that uses `readyToPublish()` scope. Supports `--dry-run` and `--limit` options. Publishes 'future' status items whose `publish_at` has passed. Scheduled to run every minute via routes/console.php.
|
||||||
|
|
||||||
|
- [ ] **Media upload endpoint** - No API endpoint for uploading media. Only WordPress import creates ContentMedia records.
|
||||||
|
|
||||||
|
- [ ] **Content search** - No full-text search capability. Consider adding a search endpoint with elasticsearch/meilisearch integration.
|
||||||
|
|
||||||
|
- [ ] **Revision comparison/diff view** - `ContentRevision::getDiffSummary()` exists but returns boolean changed flags, not actual diff content.
|
||||||
|
|
||||||
|
- [ ] **Webhook handler** - ContentWebhookLog model exists but no webhook endpoint or processing job to receive/handle webhooks.
|
||||||
|
|
||||||
|
- [ ] **CDN cache purge integration** - `getCdnUrlsForPurgeAttribute()` generates purge URLs but no integration to actually call Bunny CDN purge API.
|
||||||
|
|
||||||
|
- [ ] **Content versioning API** - No API endpoints for listing/restoring revisions.
|
||||||
|
|
||||||
|
- [ ] **MCP tools** - `onMcpTools()` is stubbed out with commented examples. MCP integration incomplete.
|
||||||
|
|
||||||
|
- [ ] **Bulk operations** - No bulk publish, bulk delete, or bulk status change operations.
|
||||||
|
|
||||||
|
- [ ] **Content preview** - No preview endpoint for draft content before publishing.
|
||||||
|
|
||||||
|
## Test Coverage Assessment
|
||||||
|
|
||||||
|
**Current Coverage: Good for models, sparse for services**
|
||||||
|
|
||||||
|
Existing tests:
|
||||||
|
- `ContentRenderTest.php` - Tests ContentRender service, waitlist, workspace resolution (7 tests)
|
||||||
|
- `FactoriesTest.php` - Tests all model factories and computed properties (31 tests)
|
||||||
|
- `ContentManagerTest.php` - Tests model relationships, scopes, and queries (29 tests)
|
||||||
|
|
||||||
|
Missing test coverage:
|
||||||
|
- [ ] No tests for `AIGatewayService` - critical service, needs mocked tests
|
||||||
|
- [ ] No tests for `ContentProcessingService` - HTML parsing/cleaning needs test cases
|
||||||
|
- [ ] No tests for API controllers (`ContentBriefController`, `GenerationController`)
|
||||||
|
- [ ] No tests for `GenerateContentJob` queue job
|
||||||
|
- [ ] No tests for `ContentImportWordPress` command
|
||||||
|
- [ ] No integration tests for the full AI generation pipeline
|
||||||
|
- [ ] No tests for `ContentBrief` model (has no factory)
|
||||||
|
- [ ] No tests for `ContentRevision::createFromContentItem()` or `restoreToContentItem()`
|
||||||
|
|
||||||
|
## Security Concerns
|
||||||
|
|
||||||
|
1. **XSS in blade templates** - FIXED: Raw HTML output now uses sanitised content methods.
|
||||||
|
|
||||||
|
2. **No CSRF on subscribe endpoint** - The `WorkspaceRouter` handles POST /subscribe but the route bypasses middleware that would normally apply CSRF. Verify CSRF is checked.
|
||||||
|
|
||||||
|
3. **API authentication relies on middleware** - Routes use `auth` and `api.auth` middleware. Verify these are properly configured and cannot be bypassed.
|
||||||
|
|
||||||
|
4. **WordPress import stores arbitrary content** - `ContentImportWordPress` imports HTML content as-is. Could contain malicious scripts. Should sanitise on import.
|
||||||
|
|
||||||
|
5. **No authorisation checks on ContentBrief** - Controllers check workspace access but no policy/gate for fine-grained permissions (e.g., only editors can approve, only admins can delete).
|
||||||
|
|
||||||
|
6. **AI API keys in memory** - `AIGatewayService` stores API keys as instance properties. Not a major concern but consider using Laravel's encrypted config if storing in database.
|
||||||
|
|
||||||
|
## Notes
|
||||||
|
|
||||||
|
1. **Architecture clarity** - Good separation of concerns: Models handle data, Services handle business logic, Controllers handle HTTP, Jobs handle async. Clean module structure.
|
||||||
|
|
||||||
|
2. **WordPress transition** - The module is mid-transition from WordPress to native content. Some WordPress-specific code remains (ContentType::WORDPRESS, wp_id fields, import command). Deprecation notices are good.
|
||||||
|
|
||||||
|
3. **AI pipeline design** - The two-stage Gemini->Claude pipeline is well-designed for cost optimisation. Good use of usage tracking.
|
||||||
|
|
||||||
|
4. **ContentType enum** - Well-implemented enum with utility methods (label, color, icon, isNative). Good pattern.
|
||||||
|
|
||||||
|
5. **Content processing** - `ContentProcessingService` is comprehensive with proper DOM handling. Could be extracted to a shared package.
|
||||||
|
|
||||||
|
6. **Livewire components** - Follow consistent pattern. Consider extracting common workspace resolution logic to a trait.
|
||||||
|
|
||||||
|
7. **Hardcoded paths** - `ContentValidate` and `ContentGenerate` commands reference `base_path('doc/phase42/drafts')`. Should be configurable.
|
||||||
|
|
||||||
|
8. **Dependencies on other modules** - Module depends on:
|
||||||
|
- `Mod\Tenant` (Workspace, User)
|
||||||
|
- `Mod\Agentic` (ContentService, Prompt, ClaudeService, GeminiService, AgenticResponse)
|
||||||
|
- `Mod\Api` (HasApiResponses, ResolvesWorkspace)
|
||||||
|
- `Core` (Controller, events, HasSeoMetadata)
|
||||||
|
|
||||||
|
9. **Model pricing outdated** - `AIUsage::$pricing` will need regular updates as AI providers change pricing.
|
||||||
71
changelog/2026/jan/features.md
Normal file
71
changelog/2026/jan/features.md
Normal file
|
|
@ -0,0 +1,71 @@
|
||||||
|
# Core-Content - January 2026
|
||||||
|
|
||||||
|
## Features Implemented
|
||||||
|
|
||||||
|
### Native CMS (TASK-004)
|
||||||
|
|
||||||
|
Complete content management system replacing WordPress dependency.
|
||||||
|
|
||||||
|
**Features:**
|
||||||
|
- Post/page content types
|
||||||
|
- Rich text editor
|
||||||
|
- Media management
|
||||||
|
- Categories and tags
|
||||||
|
- SEO metadata
|
||||||
|
- Scheduling
|
||||||
|
|
||||||
|
**Models:**
|
||||||
|
- `ContentItem` - posts, pages
|
||||||
|
- `ContentCategory`
|
||||||
|
- `ContentTag`
|
||||||
|
- `ContentMedia`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Bulk Operations
|
||||||
|
|
||||||
|
Multi-select content management.
|
||||||
|
|
||||||
|
**Features:**
|
||||||
|
- Bulk publish/unpublish
|
||||||
|
- Bulk delete
|
||||||
|
- ContentManager UI
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### CDN Cache Purge
|
||||||
|
|
||||||
|
Automatic CDN invalidation on publish.
|
||||||
|
|
||||||
|
**Files:**
|
||||||
|
- `Services/CdnPurgeService.php`
|
||||||
|
- `Observers/ContentItemObserver.php`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Content Preview
|
||||||
|
|
||||||
|
Preview unpublished content with time-limited tokens.
|
||||||
|
|
||||||
|
**Files:**
|
||||||
|
- Preview endpoint
|
||||||
|
- Preview UI in editor
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Versioning API
|
||||||
|
|
||||||
|
Content revision history.
|
||||||
|
|
||||||
|
**Files:**
|
||||||
|
- `Controllers/ContentRevisionController.php`
|
||||||
|
- List, show, restore, compare endpoints
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Media Upload API
|
||||||
|
|
||||||
|
Full REST API for content media.
|
||||||
|
|
||||||
|
**Files:**
|
||||||
|
- `Controllers/ContentMediaController.php`
|
||||||
|
|
@ -14,18 +14,18 @@
|
||||||
},
|
},
|
||||||
"autoload": {
|
"autoload": {
|
||||||
"psr-4": {
|
"psr-4": {
|
||||||
"Core\\Content\\": ""
|
"Core\\Mod\\Content\\": ""
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"autoload-dev": {
|
"autoload-dev": {
|
||||||
"psr-4": {
|
"psr-4": {
|
||||||
"Core\\Content\\Tests\\": "Tests/"
|
"Core\\Mod\\Content\\Tests\\": "Tests/"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"extra": {
|
"extra": {
|
||||||
"laravel": {
|
"laravel": {
|
||||||
"providers": [
|
"providers": [
|
||||||
"Core\\Content\\Boot"
|
"Core\\Mod\\Content\\Boot"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -10,13 +10,13 @@ declare(strict_types=1);
|
||||||
*/
|
*/
|
||||||
|
|
||||||
use Illuminate\Support\Facades\Route;
|
use Illuminate\Support\Facades\Route;
|
||||||
use Core\Content\Controllers\Api\ContentBriefController;
|
use Core\Mod\Content\Controllers\Api\ContentBriefController;
|
||||||
use Core\Content\Controllers\Api\ContentMediaController;
|
use Core\Mod\Content\Controllers\Api\ContentMediaController;
|
||||||
use Core\Content\Controllers\Api\ContentRevisionController;
|
use Core\Mod\Content\Controllers\Api\ContentRevisionController;
|
||||||
use Core\Content\Controllers\Api\ContentSearchController;
|
use Core\Mod\Content\Controllers\Api\ContentSearchController;
|
||||||
use Core\Content\Controllers\Api\ContentWebhookController;
|
use Core\Mod\Content\Controllers\Api\ContentWebhookController;
|
||||||
use Core\Content\Controllers\Api\GenerationController;
|
use Core\Mod\Content\Controllers\Api\GenerationController;
|
||||||
use Core\Content\Controllers\ContentPreviewController;
|
use Core\Mod\Content\Controllers\ContentPreviewController;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|--------------------------------------------------------------------------
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
|
@ -3,11 +3,11 @@
|
||||||
declare(strict_types=1);
|
declare(strict_types=1);
|
||||||
|
|
||||||
use Illuminate\Support\Facades\Route;
|
use Illuminate\Support\Facades\Route;
|
||||||
use Core\Content\View\Modal\Web\Blog;
|
use Core\Mod\Content\View\Modal\Web\Blog;
|
||||||
use Core\Content\View\Modal\Web\Help;
|
use Core\Mod\Content\View\Modal\Web\Help;
|
||||||
use Core\Content\View\Modal\Web\HelpArticle;
|
use Core\Mod\Content\View\Modal\Web\HelpArticle;
|
||||||
use Core\Content\View\Modal\Web\Post;
|
use Core\Mod\Content\View\Modal\Web\Post;
|
||||||
use Core\Content\View\Modal\Web\Preview;
|
use Core\Mod\Content\View\Modal\Web\Preview;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|--------------------------------------------------------------------------
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,10 @@
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
use Core\Content\Models\ContentAuthor;
|
use Core\Mod\Content\Models\ContentAuthor;
|
||||||
use Core\Content\Models\ContentItem;
|
use Core\Mod\Content\Models\ContentItem;
|
||||||
use Core\Content\Models\ContentMedia;
|
use Core\Mod\Content\Models\ContentMedia;
|
||||||
use Core\Content\Models\ContentTaxonomy;
|
use Core\Mod\Content\Models\ContentTaxonomy;
|
||||||
use Core\Content\Models\ContentWebhookLog;
|
use Core\Mod\Content\Models\ContentWebhookLog;
|
||||||
use Core\Mod\Tenant\Models\User;
|
use Core\Mod\Tenant\Models\User;
|
||||||
use Core\Mod\Tenant\Models\Workspace;
|
use Core\Mod\Tenant\Models\Workspace;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,12 +2,12 @@
|
||||||
|
|
||||||
declare(strict_types=1);
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace Core\Content\Tests\Feature;
|
namespace Core\Mod\Content\Tests\Feature;
|
||||||
|
|
||||||
use Core\Mod\Tenant\Models\User;
|
use Core\Mod\Tenant\Models\User;
|
||||||
use Core\Mod\Tenant\Models\Workspace;
|
use Core\Mod\Tenant\Models\Workspace;
|
||||||
use Core\Content\Enums\ContentType;
|
use Core\Mod\Content\Enums\ContentType;
|
||||||
use Core\Content\Models\ContentItem;
|
use Core\Mod\Content\Models\ContentItem;
|
||||||
use PHPUnit\Framework\Attributes\Test;
|
use PHPUnit\Framework\Attributes\Test;
|
||||||
use Tests\TestCase;
|
use Tests\TestCase;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
use Core\Content\Services\ContentRender;
|
use Core\Mod\Content\Services\ContentRender;
|
||||||
use Core\Mod\Tenant\Models\Workspace;
|
use Core\Mod\Tenant\Models\Workspace;
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,10 @@
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
use Core\Content\Models\ContentAuthor;
|
use Core\Mod\Content\Models\ContentAuthor;
|
||||||
use Core\Content\Models\ContentItem;
|
use Core\Mod\Content\Models\ContentItem;
|
||||||
use Core\Content\Models\ContentMedia;
|
use Core\Mod\Content\Models\ContentMedia;
|
||||||
use Core\Content\Models\ContentTaxonomy;
|
use Core\Mod\Content\Models\ContentTaxonomy;
|
||||||
use Core\Content\Models\ContentWebhookLog;
|
use Core\Mod\Content\Models\ContentWebhookLog;
|
||||||
use Core\Mod\Tenant\Models\Workspace;
|
use Core\Mod\Tenant\Models\Workspace;
|
||||||
|
|
||||||
uses(\Illuminate\Foundation\Testing\RefreshDatabase::class);
|
uses(\Illuminate\Foundation\Testing\RefreshDatabase::class);
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
use Core\Content\Models\ContentItem;
|
use Core\Mod\Content\Models\ContentItem;
|
||||||
use Core\Content\Models\ContentRevision;
|
use Core\Mod\Content\Models\ContentRevision;
|
||||||
|
|
||||||
uses(\Illuminate\Foundation\Testing\RefreshDatabase::class);
|
uses(\Illuminate\Foundation\Testing\RefreshDatabase::class);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,14 +2,14 @@
|
||||||
|
|
||||||
declare(strict_types=1);
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace Core\Content\Tests\Unit;
|
namespace Core\Mod\Content\Tests\Unit;
|
||||||
|
|
||||||
use Core\Mod\Tenant\Models\Workspace;
|
use Core\Mod\Tenant\Models\Workspace;
|
||||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||||
use Core\Content\Enums\ContentType;
|
use Core\Mod\Content\Enums\ContentType;
|
||||||
use Core\Content\Models\ContentItem;
|
use Core\Mod\Content\Models\ContentItem;
|
||||||
use Core\Content\Models\ContentTaxonomy;
|
use Core\Mod\Content\Models\ContentTaxonomy;
|
||||||
use Core\Content\Services\ContentSearchService;
|
use Core\Mod\Content\Services\ContentSearchService;
|
||||||
use Tests\TestCase;
|
use Tests\TestCase;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
|
|
@ -2,9 +2,9 @@
|
||||||
|
|
||||||
declare(strict_types=1);
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace Core\Content\Tests\Unit;
|
namespace Core\Mod\Content\Tests\Unit;
|
||||||
|
|
||||||
use Core\Content\Models\ContentWebhookEndpoint;
|
use Core\Mod\Content\Models\ContentWebhookEndpoint;
|
||||||
use PHPUnit\Framework\Attributes\Test;
|
use PHPUnit\Framework\Attributes\Test;
|
||||||
use Tests\TestCase;
|
use Tests\TestCase;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,16 +2,16 @@
|
||||||
|
|
||||||
declare(strict_types=1);
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace Core\Content\Tests\Unit;
|
namespace Core\Mod\Content\Tests\Unit;
|
||||||
|
|
||||||
use Core\Front\Mcp\Contracts\McpToolHandler;
|
use Core\Front\Mcp\Contracts\McpToolHandler;
|
||||||
use Core\Content\Mcp\Handlers\ContentCreateHandler;
|
use Core\Mod\Content\Mcp\Handlers\ContentCreateHandler;
|
||||||
use Core\Content\Mcp\Handlers\ContentDeleteHandler;
|
use Core\Mod\Content\Mcp\Handlers\ContentDeleteHandler;
|
||||||
use Core\Content\Mcp\Handlers\ContentListHandler;
|
use Core\Mod\Content\Mcp\Handlers\ContentListHandler;
|
||||||
use Core\Content\Mcp\Handlers\ContentReadHandler;
|
use Core\Mod\Content\Mcp\Handlers\ContentReadHandler;
|
||||||
use Core\Content\Mcp\Handlers\ContentSearchHandler;
|
use Core\Mod\Content\Mcp\Handlers\ContentSearchHandler;
|
||||||
use Core\Content\Mcp\Handlers\ContentTaxonomiesHandler;
|
use Core\Mod\Content\Mcp\Handlers\ContentTaxonomiesHandler;
|
||||||
use Core\Content\Mcp\Handlers\ContentUpdateHandler;
|
use Core\Mod\Content\Mcp\Handlers\ContentUpdateHandler;
|
||||||
use PHPUnit\Framework\TestCase;
|
use PHPUnit\Framework\TestCase;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
|
|
@ -2,15 +2,15 @@
|
||||||
|
|
||||||
declare(strict_types=1);
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace Core\Content\Tests\Unit;
|
namespace Core\Mod\Content\Tests\Unit;
|
||||||
|
|
||||||
use Core\Mod\Tenant\Models\Workspace;
|
use Core\Mod\Tenant\Models\Workspace;
|
||||||
use Illuminate\Support\Facades\Queue;
|
use Illuminate\Support\Facades\Queue;
|
||||||
use Core\Content\Enums\ContentType;
|
use Core\Mod\Content\Enums\ContentType;
|
||||||
use Core\Content\Jobs\ProcessContentWebhook;
|
use Core\Mod\Content\Jobs\ProcessContentWebhook;
|
||||||
use Core\Content\Models\ContentItem;
|
use Core\Mod\Content\Models\ContentItem;
|
||||||
use Core\Content\Models\ContentWebhookEndpoint;
|
use Core\Mod\Content\Models\ContentWebhookEndpoint;
|
||||||
use Core\Content\Models\ContentWebhookLog;
|
use Core\Mod\Content\Models\ContentWebhookLog;
|
||||||
use PHPUnit\Framework\Attributes\Test;
|
use PHPUnit\Framework\Attributes\Test;
|
||||||
use Tests\TestCase;
|
use Tests\TestCase;
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue