Plugin Architecture
Plugin Location
Plugins are stored in public/plugin/. Each plugin has its own directory:
public/plugin/
├── Bbb/ # BigBlueButton integration
├── Zoom/ # Zoom integration
├── Onlyoffice/ # OnlyOffice document editing
├── XApi/ # xAPI/Tin Can
├── ... # bundled plugins ship under public/plugin/Plugin Structure
A typical plugin directory contains:
public/plugin/MyPlugin/
├── plugin.php # REQUIRED — assigns $plugin_info
├── install.php # Installation script
├── uninstall.php # Uninstallation script
├── index.php # Region rendering entry point (if applicable)
├── admin.php # Admin interface (optional)
├── lang/ # Translation files (locale codes: en_US.php, fr_FR.php, …)
├── src/
│ ├── MyPluginPlugin.php # Main plugin class (extends Plugin)
│ ├── Entity/ # Doctrine entities (auto-discovered)
│ ├── Repository/ # Doctrine repositories
│ └── EventSubscriber/ # Symfony event subscribers (auto-registered)
├── templates/ # Twig templates
└── resources/ # CSS/JS assetsPlugin Class
Each plugin extends the Plugin base class (public/main/inc/lib/plugin.class.php) and follows the singleton pattern:
Key Class Properties
$isCoursePlugin
bool
Registers the plugin as a course tool
$isAdminPlugin
bool
Adds an admin interface page
$isMailPlugin
bool
Integrates with the mail system
$addCourseTool
bool
Adds an icon to the course homepage
$course_settings
array
Defines per-course configuration fields
Plugin Lifecycle
Installation — The admin activates the plugin, which runs
install.phpConfiguration — Settings are defined and managed through the admin panel; stored in
access_url_rel_plugin(supports multi-tenant)Execution — The plugin injects content into display regions or reacts to platform events
Deactivation — The plugin is disabled but its data is preserved
Uninstallation — Runs
uninstall.phpto clean up data and tables
Display Regions
Plugins inject HTML into 18 predefined regions of the Vue frontend by overriding renderRegion():
Available regions: content_bottom, content_top, course_tool_plugin, footer_center, footer_left, footer_right, header_center, header_left, header_main, header_right, login_bottom, login_top, main_bottom, main_top, menu_administrator, menu_bottom, menu_top, pre_footer.
Symfony Integration
Event Subscribers
Files ending in EventSubscriber.php placed inside src/EventSubscriber/ are auto-registered via PluginEventSubscriberPass. They implement EventSubscriberInterface and react to events defined in src/CoreBundle/Event/Events.php.
Because the plugin class (MyPluginPlugin) is not a Symfony service, it cannot be autowired into the subscriber constructor. Use the create() singleton instead:
Doctrine Entities
Doctrine entities placed in src/Entity/ are auto-discovered by PluginEntityPass. Use PHP 8 attributes for mapping. The namespace must follow Chamilo\PluginBundle\{PluginName}. Use unique table name prefixes (e.g., my_plugin_*) to avoid collisions.
PluginHelper Service
For accessing plugin state from core Symfony services, inject PluginHelper rather than instantiating the plugin class directly:
Available methods:
isPluginEnabled(string $name): bool
Check if a plugin is installed and active for the current access URL
loadLegacyPlugin(string $name): ?object
Instantiate and return the plugin singleton
getPluginSetting(string $name, string $key): mixed
Read a single plugin setting value
getPluginOverrides(string $name): array
Get plugin.yaml overrides (defaults + access-URL-specific) for a plugin
Core File References
public/main/inc/lib/plugin.class.php
Plugin base class
public/main/inc/lib/plugin.lib.php
Plugin manager
src/CoreBundle/Entity/Plugin.php
Plugin Doctrine entity
src/CoreBundle/Helpers/PluginHelper.php
PluginHelper service
src/CoreBundle/Event/Events.php
Event constants
public/plugin/HelloWorld/
Minimal example plugin
public/plugin/TopLinks/
Simple example plugin
Last updated
Was this helpful?