.. _developer: ========= Developer ========= Architecture Overview ====================== The Kanban Workspaces extension follows modern TYPO3 v13 development practices, emphasizing clean architecture, dependency injection, and separation of concerns. **Key Principles:** * **TYPO3 v13 Compatibility** - Built using TYPO3 v13 features and APIs * **Dependency Injection** - All services use constructor injection * **Event Listeners** - Hooks into TYPO3 workspace events * **Service Container** - Automatic service registration and autowiring * **Modern PHP** - Requires PHP 8.2+ with type declarations * **PSR-4 Autoloading** - Organized class structure following PSR-4 standard Core Components =============== Controller ---------- **KanbanWorkspacesController** *Namespace:* ``WebVision\KanbanWorkspaces\Controller\KanbanWorkspacesController`` *Responsibility:* Main backend module controller handling HTTP requests and view rendering. Key Methods: .. php:method:: indexAction(): ResponseInterface Main action rendering the kanban board interface. Responsibilities: * Retrieves active workspace and page context * Fetches configured workspace stages * Prepares filter options (depth, language, stage) * Loads CSS and JavaScript assets * Renders ModuleTemplate with configured data **Returns:** ResponseInterface containing rendered module HTML **Access:** Requires workspace and page access .. php:method:: addAssets(): void Loads CSS and JavaScript modules required for the kanban board. **Loaded Assets:** * ``Resources/Public/Css/Styles.css`` - Kanban board styles * ``Resources/Public/Css/Fontawesome.min.css`` - Icon fonts * ``@web-vision/kanban-workspaces/App.js`` - Main JavaScript application * ``@typo3/workspaces/renderable/send-to-stage-form.js`` - Workspace form component .. php:method:: configureKanban(array $stageConfig): void Configures the JavaScript frontend with stage and filter data. **Parameters:** * ``$stageConfig`` - Array of stage configuration with page ID, workspace, stages, and filters **Example:** .. code-block:: php $this->configureKanban([ 'pageUid' => 123, 'workspaceId' => 1, 'stages' => $stageConfig, 'selectedLanguage' => 'en', 'selectedDepth' => 2, 'selectedStage' => -99, 'filters' => [ 'depth' => [...], 'language' => [...], 'stage' => [...], ], ]); .. php:method:: getSystemLanguages(int $pageId, string $selectedLanguage = ''): array Retrieves available system languages for the given page. **Parameters:** * ``$pageId`` - Page UID to get languages for * ``$selectedLanguage`` - Currently selected language (for marking as active) **Returns:** Array of language configuration objects with id, label, and flag **Example Return:** .. code-block:: php [ ['id' => 'all', 'label' => 'All', 'flag' => ''], ['id' => '0', 'label' => 'English', 'flag' => 'en'], ['id' => '1', 'label' => 'Deutsch', 'flag' => 'de'], ] Domain Models and DTOs ---------------------- **EmConfiguration** *Namespace:* ``WebVision\KanbanWorkspaces\Domain\Model\Dto\EmConfiguration`` *Responsibility:* Provides typed access to Extension Manager configuration. Methods: .. php:method:: getDisableDefaultStage(): bool Returns whether default stages should be disabled. .. php:method:: getDefaultStageId(): int Returns the default stage ID for new records. **Usage Example:** .. code-block:: php $emSettings = GeneralUtility::makeInstance(EmConfiguration::class); if ($emSettings->getDisableDefaultStage()) { // Only show custom stages } $defaultStage = $emSettings->getDefaultStageId(); Event Listeners =============== AfterDataGeneratedForWorkspaceEventListener -------------------------------------------- *Namespace:* ``WebVision\KanbanWorkspaces\EventListener\AfterDataGeneratedForWorkspaceEventListener`` *Event:* ``TYPO3\CMS\Workspaces\Event\AfterDataGeneratedForWorkspaceEvent`` *Responsibility:* Processes workspace data after generation, applies default stage configuration. **What it does:** 1. Listens for workspace data generation events 2. Checks if default stages should be disabled (via EmConfiguration) 3. For custom default stage ID, updates record stages accordingly 4. Persists stage changes to the database **Code Example:** .. code-block:: php #[AsEventListener( identifier: 'kanban-workspaces/after-data-generated-for-workspace', )] final class AfterDataGeneratedForWorkspaceEventListener { public function __invoke(AfterDataGeneratedForWorkspaceEvent $event): void { $emSettings = GeneralUtility::makeInstance(EmConfiguration::class); if (empty($emSettings->getDisableDefaultStage())) { return; } // Process event data... } } **Key Methods:** .. php:method:: __invoke(AfterDataGeneratedForWorkspaceEvent $event): void Entry point for event processing (implements Invokable interface). Frontend JavaScript Architecture ================================ The frontend is delivered as ES6 modules under ``Resources/Public/JavaScript/``, import-mapped to ``@web-vision/kanban-workspaces/`` (see ``Configuration/JavaScriptModules.php``). What used to be a single ``Workspace.js`` file is now a central orchestrator with focused, single-responsibility collaborators. Entry point and orchestrator ---------------------------- * ``App.js`` – entry point loaded as the module trigger. It enables horizontal drag-to-scroll (``core/HorizontalScroll.js``), instantiates ``WorkspaceBoard`` with the runtime options (API URLs, feature flags such as ``enableDragDrop`` / ``enableFilters`` / ``enableSearch``, ``mockData``), and registers the application-level event handlers — most importantly ``card:moved``, which POSTs the move to the workspace dispatch endpoint. * ``WorkspaceBoard.js`` – the ``WorkspaceBoard`` class. It owns the shared state (cards, stages, filters, selection, undo/redo history, current workspace, search query) and the lifecycle wiring, and delegates the real work to its collaborators. Collaborators are constructed with a back-reference to the board and reach shared state and each other through it (e.g. ``this.board.data``, ``this.board.renderer.renderBoard()``). Collaborators ------------- .. list-table:: :header-rows: 1 :widths: 22 28 50 * - Collaborator - File - Responsibility * - ``WorkspaceApi`` - ``data/WorkspaceApi.js`` - Thin AJAX transport over the workspace dispatch endpoint; hands raw responses to the transformer. Does not touch the DOM. * - ``DataTransformer`` - ``data/DataTransformer.js`` - Stateless conversion of raw TYPO3 workspace payloads into the card / comment / history / diff shapes consumed by the UI. * - ``BoardRenderer`` - ``ui/BoardRenderer.js`` - Generates all board markup (columns, cards, filter sidebar) and re-attaches drag-and-drop after a render. * - ``DragController`` - ``ui/DragController.js`` - Card drag-and-drop and column drop targets, drop placeholders, and starting the stage-transition workflow on drop. * - ``ModalController`` - ``ui/ModalController.js`` - Preview modal (summary / comments / history tabs) and the custom Send-to-Stage modal, including the revert / approve workflow. Holds the transient send-to-stage form state. * - ``FilterController`` - ``ui/FilterController.js`` - Search and filter interaction: keeps active filters and search query in sync, persists selections and triggers reloads / re-renders. * - ``CardActions`` - ``ui/CardActions.js`` - Per-card context-menu actions: preview, edit, open page version, assign a backend user, discard the version, and the move / revert primitives. * - ``EventEmitter`` - ``core/EventEmitter.js`` - Minimal pub/sub used for the board's custom events. * - ``initHorizontalScroll`` - ``core/HorizontalScroll.js`` - Drag-to-scroll panning of the horizontal board (ignores drags starting on a card or column). * - utilities - ``core/utils.js`` - Stateless helpers shared across components (HTML escaping, initials, date/icon formatting, toasts, loading indicators, ``debounce``). Events ------ ``WorkspaceBoard`` exposes ``on(event, handler)`` / ``off(event, handler)`` / ``emit(event, …)`` through its ``EventEmitter``. Subscribe to observe and extend behaviour. Notable events: * ``board:initialized``, ``board:rendered``, ``board:destroyed`` – board lifecycle. * ``data:loaded`` – workspace records fetched and transformed. * ``card:moved``, ``card:drop``, ``card:dragstart``, ``card:dragend``, ``card:click`` – card interactions. * ``filter:change``, ``filter:clear``, ``search:change``, ``search:clear`` – filter / search changes. * ``comment:added`` – a comment was added in the preview modal. Stage Checklist Feature (Implementation) ========================================= The Stage Checklist feature adds optional checklist items per workspace stage. When editors move a card to a stage (drag or Approve/Revert), the "Send to Stage" modal shows that stage's checklist at the top. The checklist is display-only (no checkboxes or submission). Database and TCA ---------------- * **Table:** ``tx_kanbanworkspaces_stage_checklist`` (see ``ext_tables.sql``). Columns: ``uid``, ``pid``, ``tstamp``, ``crdate``, ``deleted``, ``sorting``, ``stage`` (FK to ``sys_workspace_stage.uid``), ``title``. * **TCA:** ``Configuration/TCA/tx_kanbanworkspaces_stage_checklist.php`` defines the checklist table (label, sorting, delete, columns ``stage``, ``title``; record icon ``kanban-workspaces-stage-checklist``). * **TCA override:** ``Configuration/TCA/Overrides/sys_workspace_stage.php`` adds ``checklist_items`` inline field to ``sys_workspace_stage`` (after ``responsible_persons``). Inline: sortable, ``expandSingle``, no sync/localization links. Icons ~~~~~ * **File:** ``Configuration/Icons.php`` registers ``kanban-workspaces-stage-checklist`` with ``SvgIconProvider``, source ``EXT:kanban_workspaces/Resources/Public/Icons/checklist.svg``. Used for TCA record icons and for each checklist row in the Send to Stage modal. Controller ~~~~~~~~~~ * In ``KanbanWorkspacesController::indexAction()``, each stage in the config is enriched with a ``checklist`` array. For each stage with ``uid >= 1``, ``getChecklistForStage($stageUid)`` is called. * ``getChecklistForStage(int $stageUid): array`` – queries ``tx_kanbanworkspaces_stage_checklist`` for the given ``stage``, ``deleted = 0``, ordered by ``sorting``. Deduplicates by ``uid`` and by ``title``. Returns ``[ ['id' => uid, 'title' => title], ... ]``. The array is part of ``WorkspaceConfig`` (JSON) passed to the frontend. Frontend (Template, JavaScript, CSS) ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ * **Template:** Send to Stage modal (``#sendToStageModal``) body order: ``#stageInfoBanner``, then ``#stageChecklistSection`` / ``#stageChecklistList``, then recipients, additional recipients, comments. * **JavaScript:** ``openSendToStageModal(formData, context)`` in ``ui/ModalController.js`` reads ``context.targetStage?.checklist``, deduplicates by ``id``/``title``, renders ``