.. _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'],
]
Extension Configuration
=======================
Extension Manager settings are read through the
``WebVision\KanbanWorkspaces\Configuration\EmConfiguration`` accessor. It is
instantiated by the static ``create()`` factory, wired via the
``#[Autoconfigure(constructor: 'create')]`` attribute. The factory receives the
``ExtensionConfiguration`` service, reads the raw values and type-casts them, so
the immutable value object only exposes properly typed settings:
.. code-block:: php
#[Autoconfigure(constructor: 'create')]
final readonly class EmConfiguration
{
public function __construct(
private bool $disableResetToEditingStage = false,
) {}
public static function create(ExtensionConfiguration $extensionConfiguration): self
{
// read + cast the raw extension configuration values here
}
}
.. php:method:: getDisableResetToEditingStage(): bool
Returns whether resetting a workspace record to the editing stage (stage 0)
after a field update should be prevented.
.. php:method:: getCustomStageEditTitle(): string
getCustomStageReadyToPublishTitle(): string
getCustomStagePublishTitle(): string
Return the configured custom titles for the default "Editing", "Ready to
publish" and "Publish" stages. Each value is either a plain string, a
``LLL:EXT:...`` reference, or empty (use the TYPO3 default).
Custom Stage Titles (StagesService override)
============================================
``WebVision\KanbanWorkspaces\Service\StagesService`` extends the core
``TYPO3\CMS\Workspaces\Service\StagesService`` and overrides ``getStageTitle()``
to apply the ``customStage*Title`` options for the three default stages
(``STAGE_EDIT_ID``, ``STAGE_PUBLISH_ID``, ``STAGE_PUBLISH_EXECUTE_ID``). When the
matching option is empty, the parent implementation is called; a ``LLL:EXT:...``
value is resolved through the language service, any other non-empty value is used
verbatim.
The core service is replaced through Symfony DI using the ``#[AsAlias]``
attribute, so all consumers (and ``GeneralUtility::makeInstance()``) receive the
extended service. The constructor is intentionally not overridden, so the parent
dependencies keep being autowired through the inherited constructor. The
``EmConfiguration`` value object (a public service) is fetched on demand via
``GeneralUtility::makeInstance()`` inside ``getStageTitle()``:
.. code-block:: php
#[AsAlias(id: \TYPO3\CMS\Workspaces\Service\StagesService::class, public: true)]
final readonly class StagesService extends \TYPO3\CMS\Workspaces\Service\StagesService
{
public function getStageTitle(int $stageId): string
{
$emConfiguration = GeneralUtility::makeInstance(EmConfiguration::class);
// return the configured title (resolving LLL:) or fall back to parent
}
}
DataHandler Hook
================
``WebVision\KanbanWorkspaces\Hooks\PreventResetToEditingStageDataHandlerHook`` is
registered on the ``processDatamapClass`` hook in ``ext_localconf.php`` and
implements ``processDatamap_postProcessFieldArray``. It only acts when the
operation is an ``update``, the affected table has a workspace-aware TCA schema
(checked via ``TcaSchemaFactory``), and ``disableResetToEditingStage`` is enabled.
It then removes ``t3ver_stage`` from the field array so TYPO3 does not reset the
record to the editing stage when the record is persisted.
Event Listeners
===============
The extension registers its event listeners via PHP attributes
(``#[AsEventListener]``) in ``Classes/EventListener/``:
* **AssigneeEnrichmentListener** – listens to
``TYPO3\CMS\Workspaces\Event\AfterDataGeneratedForWorkspaceEvent`` and enriches
the generated workspace records with their assignment information.
* **AssigneeCleanupAfterPublishListener** – removes assignment rows for a record
and workspace after the record is published.
* **WorkspaceLanguageFallbackListener** – applies language fallback handling for
workspace records.
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 ``
`` of ``- `` with ```` and title; injects icon via ``Icons.getIcon('kanban-workspaces-stage-checklist', Icons.sizes.small)`` into each icon span. When modal is opened via Approve/Revert, ``handleNextStage`` / ``handleRevertStage`` (also in ``ui/ModalController.js``) compute target stage and pass ``targetStage`` in context.
* **CSS:** ``Resources/Public/Css/Styles.css`` – ``.stage-checklist-section``, ``.stage-checklist-list`` (max-height, overflow, scrollable), ``.stage-checklist-ul``, ``.stage-checklist-item``, ``.stage-checklist-item-icon``.
Assign Feature (Implementation)
================================
The Assign feature allows editors to assign a backend user to a workspace record (card). It consists of an Ajax controller, persistence service, notification service, two event listeners, a dedicated database table, and TCA enrichment.
AssignAjaxController
--------------------
*Namespace:* ``WebVision\KanbanWorkspaces\Controller\AssignAjaxController``
*Responsibility:* Handles the Ajax request for saving an assignment (assignee, title, description) and triggers persistence and email notification.
*Route:* ``kanban_workspace_assign``, path ``/kanban-workspace/assign`` (see ``Configuration/Backend/AjaxRoutes.php``). Access is inherited from module ``web_kanbanworkspaces``.
*Key method:* ``assignAction(ServerRequestInterface $request): ResponseInterface`` – parses request body (table, record_uid, workspace_id, stage_id, be_user, title, description), calls ``AssigneeMappingService::persistAssignmentWithMeta()`` and ``AssignmentNotificationService::notifyAssignee()``, returns JSON success/error.
*Note:* The controller is instantiated by the backend dispatcher via ``GeneralUtility::makeInstance()`` (no constructor args). It resolves ``AssigneeMappingService`` and ``AssignmentNotificationService`` from the container inside the action; both services are registered as **public** in ``Configuration/Services.yaml``.
AssigneeMappingService
----------------------
*Namespace:* ``WebVision\KanbanWorkspaces\Service\AssigneeMappingService``
*Responsibility:* Persists assignment data to ``sys_workspaces_assignee`` (insert or update per record/workspace), sets ``t3ver_assignee`` on the versioned record, and cleans up assignee rows when a record is published.
*Key methods:*
* ``persistAssignmentWithMeta(beUserId, tableName, recordUid, workspaceId, stageId, title, description)`` – upserts one row in ``sys_workspaces_assignee`` and updates the record’s ``t3ver_assignee`` column.
* ``cleanupForPublished(table, recordId, workspaceId)`` – deletes rows in ``sys_workspaces_assignee`` for the given table/record/workspace (called by ``AssigneeCleanupAfterPublishListener``).
AssignmentNotificationService
-----------------------------
*Namespace:* ``WebVision\KanbanWorkspaces\Notification\AssignmentNotificationService``
*Responsibility:* Sends an email to the assignee when they are assigned to a record (unless the assignee is the current user or has no valid email). Uses TYPO3’s ``MailerInterface`` and ``FluidEmail`` with the extension’s templates and the core **SystemEmail** layout.
*Dependencies:* ``MailerInterface``, ``LoggerInterface``, ``StagesService``, ``PreviewUriBuilder``.
*Templates:* ``Resources/Private/Templates/Email/AssignmentNotification.html`` and ``AssignmentNotification.txt``. Fallback MAIL template/layout/partial paths (EXT:core, EXT:backend) are used when global MAIL config does not define them.
*Configuration:* Sender/format can be overridden via Page TSconfig ``tx_workspaces.emails.*``; otherwise ``MAIL.defaultMailFromAddress`` / ``defaultMailFromName`` and transport apply.
AssigneeEnrichmentListener
--------------------------
*Namespace:* ``WebVision\KanbanWorkspaces\EventListener\AssigneeEnrichmentListener``
*Event:* ``TYPO3\CMS\Workspaces\Event\AfterDataGeneratedForWorkspaceEvent``
*Identifier:* ``kanban-workspaces/assignee-enrichment`` (runs after ``kanban-workspaces/after-data-generated-for-workspace``).
*Responsibility:* Enriches each workspace item in the event data with assignee info from ``sys_workspaces_assignee`` and ``be_users``: ``assignee_uid``, ``assignee_username``, ``assignee_avatar_url`` (from FAL avatar when available). The frontend uses these to display the assignee on the card.
AssigneeCleanupAfterPublishListener
------------------------------------
*Namespace:* ``WebVision\KanbanWorkspaces\EventListener\AssigneeCleanupAfterPublishListener``
*Event:* ``TYPO3\CMS\Workspaces\Event\AfterRecordPublishedEvent``
*Identifier:* ``kanban-workspaces/assignee-cleanup-after-publish``
*Responsibility:* After a record is published, calls ``AssigneeMappingService::cleanupForPublished()`` to remove assignee rows for that table/record/workspace.
Database and TCA
----------------
*Table:* ``sys_workspaces_assignee`` (defined in ``ext_tables.sql``). Columns include ``uid``, ``pid``, ``tstamp``, ``crdate``, ``title``, ``description``, ``be_user``, ``table_name``, ``record_uid``, ``workspace_id``, ``stage_id``. One row per record/workspace (last assignee wins on update).
*TCA:* ``Configuration/TCA/Overrides/t3ver_assignee.php`` adds a read-only ``t3ver_assignee`` column (select on ``sys_workspaces_assignee``) to all tables with ``versioningWS``, so the versioned record can reference the assignee mapping row.
Services and Ajax Route
-----------------------
*Services.yaml:* ``AssigneeMappingService`` and ``AssignmentNotificationService`` are explicitly **public** so they can be resolved from the container when ``AssignAjaxController`` is created by ``makeInstance``. ``KanbanWorkspacesController`` and ``EmConfiguration`` remain public for module and config access.
*Ajax route:* ``Configuration/Backend/AjaxRoutes.php`` registers route ``kanban_workspace_assign`` with path ``/kanban-workspace/assign`` and target ``AssignAjaxController::assignAction``.
API Reference
=============
Controller Interface
--------------------
The KanbanWorkspacesController extends ``ActionController`` and provides the backend module interface:
**Injected Dependencies:**
.. code-block:: php
public function __construct(
protected readonly ModuleTemplateFactory $moduleTemplateFactory,
protected readonly IconFactory $iconFactory,
protected readonly PageRenderer $pageRenderer,
protected readonly WorkspaceStageRepository $workspaceStageRepository,
protected readonly WorkspaceRepository $workspaceRepository,
protected readonly EmConfiguration $emSettings,
protected readonly TranslationConfigurationProvider $translationConfigurationProvider,
) {}
**Available TYPO3 Services:**
* **ModuleTemplateFactory** - Creates module template for rendering
* **IconFactory** - Generates icons for UI elements
* **PageRenderer** - Adds CSS/JS and inline configuration
* **WorkspaceStageRepository** - Retrieves workspace stages
* **WorkspaceRepository** - Retrieves workspace records
* **TranslationConfigurationProvider** - Gets site language configuration
* **EmConfiguration** - Extension Manager settings
Configuration Files
===================
Backend Module Configuration
-----------------------------
**File:** ``Configuration/Backend/Modules.php``
Defines the backend module registration:
.. code-block:: php
return [
'web_kanbanworkspaces' => [
'parent' => 'web',
'position' => ['after' => 'web_info'],
'inheritNavigationComponentFromMainModule' => true,
'access' => 'user',
'workspaces' => '*',
'icon' => 'EXT:kanban_workspaces/Resources/Public/Icons/Extension.png',
'path' => '/module/web/kanbanworkspaces',
'labels' => 'LLL:EXT:kanban_workspaces/Resources/Private/Language/locallang_mod.xlf',
'extensionName' => 'KanbanWorkspaces',
'controllerActions' => [
KanbanWorkspacesController::class => [
'index',
],
],
]
];
**Configuration Properties:**
* **parent** - Parent module in the backend menu tree
* **position** - Position relative to other modules
* **inheritNavigationComponentFromMainModule** - Inherit parent module's navigation
* **access** - Access level required (user, admin, systemMaintainer)
* **workspaces** - Which workspaces the module is available in
* **icon** - Icon file path
* **path** - URL path for the module
* **labels** - Language file for localization
* **extensionName** - Extension identifier
* **controllerActions** - Maps controller classes to available actions
Service Configuration
---------------------
**File:** ``Configuration/Services.yaml``
.. code-block:: yaml
services:
_defaults:
autowire: true
autoconfigure: true
WebVision\KanbanWorkspaces\:
resource: '../Classes/'
Enables automatic service registration and dependency injection for all classes in the ``Classes/`` directory.
JavaScript Modules
------------------
**File:** ``Configuration/JavaScriptModules.php``
Defines and configures JavaScript modules needed for the module:
.. code-block:: php
return [
'dependencies' => [
'core',
'workspaces',
],
'triggers' => [
'@web-vision/kanban-workspaces/App.js',
],
];
**Available Modules:**
* ``@web-vision/kanban-workspaces/App.js`` - Main kanban board application
* ``@typo3/workspaces/renderable/send-to-stage-form.js`` - Stage transition form
Extending the Extension
=======================
Creating Custom Event Listeners
--------------------------------
To hook into workspace events:
.. code-block:: php
[
ExtendedKanbanWorkspacesController::class => [
'index',
'custom',
],
],
Customizing Views
-----------------
Override the default template in your extension:
1. Create ``Resources/Private/Templates/KanbanWorkspaces/Index.html``
2. Copy content from the original extension
3. Modify as needed
4. Register in your extension's module configuration
Customizing Assets
------------------
To customize styles or JavaScript:
1. Create custom CSS in ``Resources/Public/Css/Custom.css``
2. Create custom JavaScript in ``Resources/Public/JavaScript/Custom.js``
3. Load in your extension's controller or module configuration
Testing
=======
Unit Testing
------------
To test your custom extensions:
.. code-block:: php
assertTrue(true); // Your assertions here
}
}
Functional Testing
------------------
Test the kanban board integration:
1. Create a test workspace with stages
2. Add test pages and content items
3. Navigate to the kanban module
4. Verify stage display and item appearance
5. Test drag-and-drop functionality
Debugging
=========
Enable Debug Output
-------------------
In ``LocalConfiguration.php``:
.. code-block:: php
'BE' => [
'debug' => true,
],
Browser Console
---------------
Open browser developer tools (F12) to see JavaScript console for:
* Module initialization errors
* Drag-and-drop logging
* Filter update notifications
* API communication logs
Backend Logging
---------------
TYPO3 logs extension errors to ``var/log/typo3_*.log``
Check logs for:
* Service initialization errors
* Database query failures
* Event listener exceptions
* Permission check failures
Performance Optimization
========================
Database Query Optimization
---------------------------
The extension uses efficient queries through TYPO3's repository pattern:
* Respects workspace context automatically
* Filters by language configuration
* Uses depth parameters for limited results
* Implements lazy loading where possible
For large workspaces:
1. Filter by depth in the UI
2. Filter by language for multi-language sites
3. Regularly publish items to keep workspace size manageable
4. Use workspace archiving features
Caching Strategy
----------------
Workspace data is typically not cached to ensure current status. For custom implementations, consider:
.. code-block:: php
// Cache workspace configuration (stable)
$cacheIdentifier = 'workspace_' . $workspaceId;
// Don't cache item/stage data (changes frequently)
// Always query fresh from database
Asset Optimization
------------------
The extension loads assets efficiently:
* CSS files are concatenated by TYPO3
* JavaScript modules are loaded asynchronously
* Icons use icon factory for optimization
* Images should be optimized before deployment
Code Quality
============
PHP Standards
-------------
The extension follows:
* **PSR-12** - Extended coding style guide
* **TYPO3 Coding Guidelines** - TYPO3-specific standards
* **Strict Types** - All files use ``declare(strict_types=1)``
* **Type Hints** - Full type declarations for methods and properties
JavaScript Standards
--------------------
* **ES6+ Syntax** - Modern JavaScript features
* **TYPO3 JS Module Pattern** - Uses TYPO3's module system
* **Linting** - TYPO3 ESLint configuration
Documentation Standards
-----------------------
* **PHPDoc Comments** - All classes and methods documented
* **RST Documentation** - User and developer documentation
* **Inline Comments** - Complex logic is documented
Version Compatibility
=====================
TYPO3 Versions
--------------
* **Minimum:** TYPO3 13.4.0 LTS
* **Maximum:** TYPO3 13.4.99 (latest v13 patch)
**Breaking Changes:** None planned for v13 support period
PHP Versions
------------
* **Minimum:** PHP 8.2.0
* **Maximum:** PHP 8.3.99
**Features Used:**
* Constructor property promotion (PHP 8.0+)
* Named arguments (PHP 8.0+)
* Match expressions (PHP 8.0+)
* Attributes (PHP 8.0+)
Upgrading and Migration
=======================
Backward Compatibility
----------------------
The extension maintains backward compatibility within v1.x versions. Changes between minor versions should not break existing code.
Breaking Changes
----------------
Any breaking changes in major versions (2.0+) will be documented in:
* ``CHANGELOG.md`` - Version history
* ``Documentation/ChangeLog/Index.rst`` - Detailed migration guide
Migration Guide
---------------
For upgrades from v0.x to v1.x:
1. Update via Composer
2. Run database migrations (if any)
3. Clear caches
4. Test functionality in development
5. Deploy to production
Troubleshooting Guide
=====================
Common Issues and Solutions
---------------------------
**Module not showing up**
1. Verify extension is activated
2. Check user has Web module access
3. Clear backend caches
4. Check browser console for errors
**Stages not displaying**
1. Verify workspace is active (not Live)
2. Check workspace configuration has stages
3. Verify page access permissions
4. Check EmConfiguration settings
**Drag-and-drop not working**
1. Check JavaScript console for errors
2. Verify user has stage edit permissions
3. Clear browser cache
4. Try different browser to isolate issue
**Performance issues with large workspaces**
1. Use depth filter to limit displayed items
2. Filter by language for multi-language sites
3. Consider splitting large workspaces
4. Monitor database query performance
Resources
=========
* **TYPO3 Documentation** - https://docs.typo3.org/
* **TYPO3 API Documentation** - https://typo3api.docs.typo3.org/
* **Extension Repository** - https://extensions.typo3.org/
* **Community Forum** - https://typo3.org/community/