Upgrade Plugin to Module

1. Implement Module Interfaces

Add IModule to your plugin class, plus any scope interface you need (ISystemSpaceModule, IWorkSpaceModule, IUserSpaceModule).

2. Migrate Initialization Logic

Replace onInit() scope-specific logic with the matching lifecycle method. Keep onInit() only for logic that must always run (e.g. facade setup).

Before:

public function onInit()
{
    parent::onInit();
    $this->_booksFacade = $this->_getBooksFacadeInstance();
}

After:

public function onInit()
{
    parent::onInit();
    $this->_booksFacade = $this->_getBooksFacadeInstance();
}

public function onLoadSystemSpaceModule(): void
{
    // module-specific initialization here
}

parent::onInit() is not needed inside the scope lifecycle method — only module-specific work belongs there.

3. SQL Migration

Register the module and clean up the old plugin navigation:

-- Register module
INSERT INTO modules (caption, ident, plugin, required, description, hooks, is_active, is_workspace)
VALUES ('Test Module', 'test_module', 'TestModule', NULL, 'Test description.', NULL, true, true);

-- Remove old navigation
DELETE FROM festi_menus WHERE url = '/test_plugin_menu/';
DELETE FROM festi_url_rules WHERE plugin = 'TestModule';

-- Activate in all target workspaces
INSERT INTO modules2workspaces (id_module, id_workspace, is_active)
SELECT (SELECT id FROM modules WHERE ident = 'test_module'), id, TRUE
FROM schools
WHERE type = 'general';

Add module options to module_options if needed — see docs/ModuleOptions.md.

4. Replace Menu Registration

Remove the old static menu entry and implement IModuleMenuFetcher instead — see docs/CreatingModule.md for the full example.

5. Access the Module

After the migration the module is accessible both ways:

// As a module
/** @var TestModulePlugin $plugin */
$plugin = ModulesManager::getInstance()->getModule('TestModule');
assert($plugin instanceof TestModulePlugin);

// As a plugin (works as long as onInit() is still present)
$this->plugin->testModule->method();
Core::getInstance()->getPluginInstance(TestModulePlugin::class)->method();