only checks write-permission (no trip scope, and * any admin passes), which violates R6. This route requires the configured site * OWNER and asserts the target is a direct child of the active trip's dailies * container — sharing one guard (EntryScopeGuard) with the save path so the two * R6 enforcement points cannot diverge (KTD5). * * Custom-in-repo (NOT GPM-managed): tracked via a `!` negation in user/.gitignore * and deployed with the content push, like cache-on-save. Never in plugins.txt. */ class EntryActionsPlugin extends Plugin { public static function getSubscribedEvents(): array { return [ 'onPluginsInitialized' => ['onPluginsInitialized', 0], 'onApiRegisterRoutes' => ['onApiRegisterRoutes', 0], ]; } /** * Register a lazy PSR-4 autoloader for this plugin's classes. The api router * dispatches from a CACHED route map and instantiates the controller directly * (ApiRouter::handleRoute → `new $controllerClass`) WITHOUT re-firing * onApiRegisterRoutes, so requiring the class only there would leave it * unloaded on cached-route requests. Lazy autoloading fires exactly when the * router constructs the controller — by which point the api plugin's own * autoloader (for AbstractApiController) is already registered. */ public function onPluginsInitialized(): void { spl_autoload_register(static function (string $class): void { $prefix = 'Grav\\Plugin\\EntryActions\\'; if (strncmp($class, $prefix, strlen($prefix)) !== 0) { return; } $rel = substr($class, strlen($prefix)); $file = __DIR__ . '/classes/' . str_replace('\\', '/', $rel) . '.php'; if (is_file($file)) { require_once $file; } }); } public function onApiRegisterRoutes(Event $event): void { $routes = $event['routes']; $routes->delete('/entry/{slug}', [EntryActions\EntryActionsApiController::class, 'deleteEntry']); } }