{"record":{"id":"d6b48d3bdf303950","repo":"Leantime/leantime","slug":"plugin-s-is-already-enabled","errorCode":null,"errorMessage":"Plugin %s is already enabled","messagePattern":"Plugin (.+?) is already enabled","errorType":"console","errorClass":"RuntimeException","httpStatus":null,"severity":"warning","filePath":"app/Command/EnablePluginCommand.php","lineNumber":42,"sourceCode":"    protected function configure(): void\n    {\n        $this->addArgument('plugin', InputArgument::REQUIRED, 'The plugin name');\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    protected function executeCommand(): int\n    {\n        $name = $this->input->getArgument('plugin');\n        $plugin = $this->getPlugin($name);\n\n        if (! isset($plugin->id)) {\n            throw new RuntimeException(sprintf('Plugin %s is not installed', $plugin->name));\n        }\n\n        if ($plugin->enabled) {\n            throw new RuntimeException(sprintf('Plugin %s is already enabled', $plugin->name));\n        }\n\n        if (! $this->confirm(sprintf('Enable plugin %s', $plugin->name))) {\n            return Command::SUCCESS;\n        }\n\n        return $this->plugins->enablePlugin($plugin->id) ? Command::SUCCESS : Command::FAILURE;\n    }\n}\n","sourceCodeStart":24,"sourceCodeEnd":52,"githubUrl":"https://github.com/Leantime/leantime/blob/9a9f49f1008f4782b30f6723c54228f4f992e636/app/Command/EnablePluginCommand.php#L24-L52","documentation":"This browser console.warn fires on the program-board variant of the ticket kanban (app/Domain/Tickets/Templates/showKanban.blade.php:393). When $programBoard is true, the template builds ticketStatusList from $allKanbanColumns and hands control to `leantime.pgmProBoard.initProgramKanban(...)`, which lives in the commercial PgmPro plugin's JS bundle. The guard `if (leantime.pgmProBoard && typeof leantime.pgmProBoard.initProgramKanban === 'function')` fails when that bundle was never loaded — app/Plugins is a private git submodule that is essentially empty in the OSS repo — so drag-and-drop and per-project status persistence are disabled while the board still renders read-only-ish.","triggerScenarios":"Opening a program kanban board (/tickets/showKanban with the program board context) when: (1) the PgmPro plugin is not installed, not enabled (`plugin:enable pgmpro`), or its license check disabled it (Leantime disables plugins when the user count exceeds the license); (2) the plugin's compiled JS failed to load (404 on its assets because the submodule was not initialized after clone: `git submodule update --init`); (3) an earlier JS error aborted the bundle before it attached leantime.pgmProBoard to the global namespace; (4) a CSP or script-blocking extension prevented the plugin script from executing.","commonSituations":"OSS checkout where app/Plugins is the empty submodule and someone deep-links or restores a program-board URL; after a `system:update` or plugin license expiry that silently disabled PgmPro; staging copies missing the private submodule credentials; ad-blockers stripping the plugin's script tag on the kanban page.","solutions":["Verify the plugin is installed and enabled: `php bin/leantime plugin:list` then `php bin/leantime plugin:enable <pgmpro-plugin-name>` (install from marketplace with `plugin:install` and a valid license key if missing).","If you have the private plugin repo access, initialize/update the submodule so plugin JS actually exists: `git submodule update --init --recursive` and rebuild plugin assets (`npx mix` inside the plugin).","Open the browser Network tab on the kanban page and confirm the PgmPro script returns 200 and not 403/404; fix the plugin's asset path or rebuild public/dist if it 404s.","Check the browser console and Leantime storage/logs for an earlier JS/PHP error that prevented the plugin bootstrapping, and for license-validation failures that auto-disabled the plugin.","If the program board is intentionally unavailable in your edition, stop routing users to it (hide the program-board menu entry) so the degraded state is never presented."],"exampleFix":"// before — hard dependency, silent degradation when plugin absent\nif (leantime.pgmProBoard && typeof leantime.pgmProBoard.initProgramKanban === 'function') {\n    leantime.pgmProBoard.initProgramKanban(ticketStatusList);\n} else {\n    console.warn('PgmPro board JS is not loaded; program kanban drag-and-drop is disabled.');\n}\n\n// after — make the degraded state visible to the user, not just the console\nif (leantime.pgmProBoard && typeof leantime.pgmProBoard.initProgramKanban === 'function') {\n    leantime.pgmProBoard.initProgramKanban(ticketStatusList);\n} else {\n    console.warn('[Tickets] PgmPro board JS is not loaded; program kanban drag-and-drop is disabled.');\n    jQuery.growl({ message: 'Drag & drop requires the PgmPro plugin, which is not active.' , style: 'warning' });\n    jQuery('.tknCard').addClass('no-drag'); // make read-only affordance explicit\n}","handlingStrategy":"type-guard","validationCode":"// Server-side: before rendering the program board, verify the plugin that provides pgmProBoard is actually enabled\n$pluginEnabled = app(\\Leantime\\Domain\\Plugins\\Services\\Plugins::class)\n    ->getEnabledPlugins()\n    ->contains(fn ($p) => str_contains($p->name, 'PgmPro'));\n// Only pass programBoard=true to the template when $pluginEnabled — otherwise render the regular board","typeGuard":"// Feature-detect the plugin controller before wiring program-board drag-and-drop\nfunction programBoardAvailable() {\n    return typeof window.leantime === 'object'\n        && window.leantime !== null\n        && typeof window.leantime.pgmProBoard === 'object'\n        && typeof window.leantime.pgmProBoard.initProgramKanban === 'function';\n}\n\nif (programBoardAvailable()) {\n    leantime.pgmProBoard.initProgramKanban(ticketStatusList);\n} else {\n    console.warn('[Tickets] PgmPro board JS is not loaded; program kanban drag-and-drop is disabled.');\n    notifyUser('Drag & drop requires the PgmPro plugin.');\n}","tryCatchPattern":null,"preventionTips":["Gate the program-board route/menu on plugin status server-side so users never reach the degraded view.","After `plugin:enable` or a submodule update, hard-reload and check the Network tab for a 200 on the plugin script before QA-signing the board.","Monitor for license-triggered plugin auto-disable (user count over limit) — it removes the JS without touching your templates.","Keep the feature detection (typeof checks) in any code path that depends on optional-plugin controllers."],"tags":["kanban","javascript","plugin-system","conditional-loading","program-board"],"backgroundTag":"optional-plugin-javascript-missing","analyzedSha":"9a9f49f1008f4782b30f6723c54228f4f992e636","analyzedAt":"2026-08-21T02:37:38.966Z","schemaVersion":2},"datasetVersion":"2026-08-21T03:17:12.404Z"}