Custom Plugins¶
LibreBooking plugins decorate existing application services. A plugin usually wraps the default implementation, adds custom behavior before or after it, and then returns control to the base service.
Note
This documentation was generated with assistance from an AI tool and may contain errors. If you find an error, please submit a pull request with a correction when possible. If a pull request is not practical, please open an issue with the details.
Warning
LibreBooking does not guarantee a stable plugin API. Plugin interfaces, constructor arguments, method signatures, loaded services, and domain objects may change in future releases. Test custom plugins before upgrading LibreBooking, and expect that existing plugins may need code changes after an upgrade.
Plugin Layout¶
Plugins are loaded by convention from the plugins directory:
plugins/<PluginType>/<PluginName>/<PluginName>.php
For example, a pre-reservation plugin named MyRule must be located at:
plugins/PreReservation/MyRule/MyRule.php
The class name must match the plugin name. The plugin manager instantiates the class and passes the default implementation into the constructor, allowing the plugin to decorate the normal behavior.
Supported plugin types include:
AuthenticationAuthorizationExportPermissionPostRegistrationPreReservationPostReservationStyling
Plugin Types¶
Choose the plugin type based on the part of LibreBooking you need to change.
AuthenticationControls how users prove their identity and how login/logout behaves. This is useful for integrating LDAP, SAML, CAS, Shibboleth, WordPress, Moodle, web server authentication, or another external identity provider. Authentication plugins can validate credentials, create the user session, hide or show login form fields, and control whether users can change account details that are managed by an external system.
AuthorizationControls high-level role and delegation decisions after a user is known. This is useful when administrator roles, approval rights, or “reserve for someone else” rules come from an external policy source instead of LibreBooking groups. Authorization plugins answer questions such as whether a user is an application administrator, resource administrator, group administrator, or can approve/reserve for another user.
PermissionControls resource access decisions. This is useful when resource visibility or booking permission must be calculated from external data, dynamic rules, or a custom entitlement system. Permission plugins answer whether a user can access, book, or view a specific resource.
PreReservationRuns before reservation actions are saved. This is useful for custom business rules that should block or allow reservation creation, updates, deletion, approval, check-in, or check-out. Examples include checking custom attribute values, preventing reservations outside department policy, enforcing external quotas, or restricting check-in/check-out to specific users.
PostReservationRuns after reservation actions are saved. This is useful for side effects and integrations that should happen only after LibreBooking has accepted the change. Examples include sending custom notifications, calling a webhook, syncing reservations to another system, writing an audit record, or replacing the default reservation email behavior.
PostRegistrationRuns after a user completes self-registration. This is useful for changing what happens to newly registered users, such as sending custom activation messages, integrating with an external onboarding system, assigning initial state, or changing redirect behavior after registration.
ExportControls extra data in reservation exports. This is useful when iCalendar subscriptions need custom classification or additional iCalendar lines for downstream systems. Export plugins can mark events as
PUBLIC,PRIVATE, or another supported iCalendar classification and can append custom event metadata.StylingAdds custom presentation behavior. This is useful when the schedule should visually distinguish reservations based on attributes, resource state, or organization-specific rules. Styling plugins can provide an additional CSS file and add CSS classes to reservation items.
Enabling a Plugin¶
Edit config/config.php and set the matching key in the plugins section.
The value is the plugin class name, which is also the plugin directory and file
name.
'plugins' => [
'prereservation' => 'MyRule',
'postreservation' => 'MyNotifier',
],
Only enable plugins that exist in the matching plugin type directory.
You can also enable plugins through the web admin interface at Application
Configuration (/Web/admin/manage_configuration.php). The plugin fields
accept any plugin class name; built-in plugin names appear as suggestions.
Manual Installation¶
For a manual installation:
Copy the plugin directory into the matching
plugins/<PluginType>directory.Ensure the web server can read the plugin files.
Enable the plugin in
config/config.phpor through the web admin configuration page.Clear any opcode cache if PHP OPcache is enabled.
Test the workflow that loads the plugin.
Docker Installation¶
For Docker deployments, plugin files must be present inside the running container. Common approaches are:
Build a custom image that copies the plugin directory into
/app/pluginsor the LibreBooking application directory used by your image.Mount the plugin directory as a volume into the matching
plugins/<PluginType>/<PluginName>path.Keep
config/config.phppersistent and set the plugin key there, or provide the matching configuration through your deployment’s supported configuration mechanism.
After changing mounted plugin files, restart the application container if PHP OPcache or the web server does not pick up the change.
Authentication Plugins¶
Authentication plugins control login behavior. They implement
IAuthentication and decorate the default Authentication service.
The built-in examples include:
plugins/Authentication/Apache/Apache.php
plugins/Authentication/Ldap/Ldap.php
plugins/Authentication/Shibboleth/Shibboleth.php
Important methods:
Validate($username, $password)Checks whether the supplied credentials are valid. External authentication plugins often validate against LDAP, SAML, a web server variable, or another identity provider.
Login($username, $loginContext)Loads or creates the LibreBooking user session after validation succeeds. Most plugins delegate to the base authentication service once they have determined the LibreBooking username.
Logout(UserSession $user)Runs when a user logs out. Use this to clear external sessions or delegate to the base logout behavior.
AreCredentialsKnown()Returns
truewhen credentials are already known before the login form is submitted, such as with web server authentication.HandleLoginFailure(IAuthenticationPage $loginPage)Controls what happens when login fails.
ShowUsernamePrompt()andShowPasswordPrompt()Control whether the login form asks for username and password. Single sign-on plugins often return
false.ShowPersistLoginPrompt()andShowForgotPasswordPrompt()Control whether the remember-me and forgot-password prompts are shown.
- Profile field permission methods
Control which profile fields users can edit when account data is managed by an external system. These include
AllowUsernameChange(),AllowEmailAddressChange(),AllowPasswordChange(),AllowNameChange(),AllowPhoneChange(),AllowOrganizationChange(), andAllowPositionChange().- Optional URL hook methods
GetRegistrationUrl()andGetPasswordResetUrl()are not part of theIAuthenticationinterface.WebAuthenticationchecks for them withmethod_exists()at runtime. Add them only when registration or password reset should link to an external system.
Minimal example:
<?php
require_once(ROOT_DIR . 'lib/Application/Authentication/namespace.php');
class MyAuth implements IAuthentication
{
private IAuthentication $base;
public function __construct(IAuthentication $base)
{
$this->base = $base;
}
public function Validate($username, $password)
{
// Validate against an external identity provider here.
return $this->base->Validate($username, $password);
}
public function Login($username, $loginContext)
{
return $this->base->Login($username, $loginContext);
}
public function Logout(UserSession $user)
{
$this->base->Logout($user);
}
public function AreCredentialsKnown()
{
return false;
}
public function HandleLoginFailure(IAuthenticationPage $loginPage)
{
$this->base->HandleLoginFailure($loginPage);
}
public function ShowUsernamePrompt()
{
return $this->base->ShowUsernamePrompt();
}
public function ShowPasswordPrompt()
{
return $this->base->ShowPasswordPrompt();
}
public function ShowPersistLoginPrompt()
{
return $this->base->ShowPersistLoginPrompt();
}
public function ShowForgotPasswordPrompt()
{
return $this->base->ShowForgotPasswordPrompt();
}
public function AllowUsernameChange()
{
return $this->base->AllowUsernameChange();
}
public function AllowEmailAddressChange()
{
return $this->base->AllowEmailAddressChange();
}
public function AllowPasswordChange()
{
return $this->base->AllowPasswordChange();
}
public function AllowNameChange()
{
return $this->base->AllowNameChange();
}
public function AllowPhoneChange()
{
return $this->base->AllowPhoneChange();
}
public function AllowOrganizationChange()
{
return $this->base->AllowOrganizationChange();
}
public function AllowPositionChange()
{
return $this->base->AllowPositionChange();
}
public function GetRegistrationUrl()
{
return method_exists($this->base, 'GetRegistrationUrl') ?
$this->base->GetRegistrationUrl() :
'';
}
public function GetPasswordResetUrl()
{
return method_exists($this->base, 'GetPasswordResetUrl') ?
$this->base->GetPasswordResetUrl() :
'';
}
}
Permission Plugins¶
Permission plugins control resource access. They implement
IPermissionService and decorate the default PermissionService.
Important methods:
CanAccessResource(IPermissibleResource $resource, UserSession $user)Determines whether the user has full access to a resource.
CanBookResource(IPermissibleResource $resource, UserSession $user)Determines whether the user can create reservations for a resource.
CanViewResource(IPermissibleResource $resource, UserSession $user)Determines whether the user can see a resource in view-only contexts.
Minimal example:
<?php
require_once(ROOT_DIR . 'lib/Application/Authorization/namespace.php');
class MyPermission implements IPermissionService
{
private IPermissionService $base;
public function __construct(IPermissionService $base)
{
$this->base = $base;
}
public function CanAccessResource(IPermissibleResource $resource, UserSession $user)
{
return $this->base->CanAccessResource($resource, $user);
}
public function CanBookResource(IPermissibleResource $resource, UserSession $user)
{
// Add custom resource booking checks here.
return $this->base->CanBookResource($resource, $user);
}
public function CanViewResource(IPermissibleResource $resource, UserSession $user)
{
return $this->base->CanViewResource($resource, $user);
}
}
Post-Registration Plugins¶
Post-registration plugins run after self-registration. They implement
IPostRegistration and decorate the default PostRegistration service.
Important method:
HandleSelfRegistration(User $user, IRegistrationPage $page, ILoginContext $loginContext)Handles the newly registered user. The default behavior logs active users in and redirects them to their homepage, or sends activation email for pending users. Use this plugin type to customize activation, onboarding, redirects, or external registration sync.
Minimal example:
<?php
require_once(ROOT_DIR . 'lib/Application/Authentication/namespace.php');
class MyPostRegistration implements IPostRegistration
{
private IPostRegistration $base;
public function __construct(IPostRegistration $base)
{
$this->base = $base;
}
public function HandleSelfRegistration(User $user, IRegistrationPage $page, ILoginContext $loginContext)
{
// Add custom onboarding or external sync here.
$this->base->HandleSelfRegistration($user, $page, $loginContext);
}
}
Export Plugins¶
Export plugins customize reservation export data. They implement
IExportFactory and decorate the default ExportFactory.
The built-in example is:
plugins/Export/ExportExample/ExportExample.php
Important methods:
GetIcalendarClassification(IReservedItemView $item)Returns the iCalendar classification for an exported reservation, such as
PUBLIC,PRIVATE, orCONFIDENTIAL.GetIcalendarExtraLines(IReservedItemView $item)Returns additional raw iCalendar lines to add to the event, or
nullwhen no extra lines are needed. Return valid iCalendar property lines, with one property per line and a trailing newline.
Minimal example:
<?php
require_once(ROOT_DIR . 'lib/Application/Export/namespace.php');
class MyExport implements IExportFactory
{
private IExportFactory $base;
public function __construct(IExportFactory $base)
{
$this->base = $base;
}
public function GetIcalendarClassification(IReservedItemView $item)
{
return $this->base->GetIcalendarClassification($item);
}
public function GetIcalendarExtraLines(IReservedItemView $item)
{
// Add one valid iCalendar property per line, ending with a newline.
return "X-LIBREBOOKING-RESOURCE:" . $item->GetResourceName() . "\n";
}
}
Styling Plugins¶
Styling plugins customize schedule presentation. They implement
IStylingFactory and decorate the default StylingFactory.
The built-in example is:
plugins/Styling/StylingExample/StylingExample.php
Important methods:
AdditionalCSS(UserSession $userSession)Returns a server file path to an additional CSS file, or
nullwhen no extra CSS should be loaded.GetReservationAdditonalCSSClasses(IReservedItemView $item)Returns additional CSS class names for a reservation item. Use this to style reservations based on title, resource, attributes, or other reservation data.
Minimal example:
<?php
require_once(ROOT_DIR . 'lib/Application/Styling/namespace.php');
class MyStyling implements IStylingFactory
{
private IStylingFactory $base;
public function __construct(IStylingFactory $base)
{
$this->base = $base;
}
public function AdditionalCSS(UserSession $userSession)
{
$path = realpath(__DIR__ . DIRECTORY_SEPARATOR . 'MyStyling.css');
return $path === false ? null : $path;
}
public function GetReservationAdditonalCSSClasses(IReservedItemView $item)
{
$classes = $this->base->GetReservationAdditonalCSSClasses($item) ?? [];
if (str_starts_with($item->GetTitle(), 'Important')) {
$classes[] = 'important-reservation';
}
return $classes;
}
}
Pre-Reservation Plugins¶
Pre-reservation plugins run during reservation validation. Use them to reject or allow reservation actions before data is saved.
A pre-reservation plugin implements IPreReservationFactory and decorates
PreReservationFactory. The factory methods correspond to reservation
actions:
CreatePreAddServiceRuns before a new reservation is created. Use this to block or allow new reservations based on custom business rules.
CreatePreUpdateServiceRuns before an existing reservation is updated. Use this to validate changes to time, resources, participants, accessories, custom attributes, or other reservation details.
CreatePreDeleteServiceRuns before a reservation is deleted. Use this to prevent deletion unless a custom condition is met, such as approval from an external system.
CreatePreApprovalServiceRuns before a pending reservation is approved. Use this to enforce custom approval rules before the reservation status changes.
CreatePreCheckinServiceRuns before a reservation check-in is accepted. Use this to restrict check-in by role, location, time window, resource state, or external validation.
CreatePreCheckoutServiceRuns before a reservation check-out is accepted. Use this to enforce checkout policy or verify external state before the checkout is saved.
The built-in example is:
plugins/PreReservation/PreReservationExample/PreReservationExample.php
Minimal example:
<?php
require_once(dirname(__FILE__) . '/MyRuleValidation.php');
class MyRule implements IPreReservationFactory
{
private PreReservationFactory $factoryToDecorate;
public function __construct(PreReservationFactory $factoryToDecorate)
{
$this->factoryToDecorate = $factoryToDecorate;
}
public function CreatePreAddService(UserSession $userSession)
{
// Wrap validation for new reservations.
$base = $this->factoryToDecorate->CreatePreAddService($userSession);
return new MyRuleValidation($base);
}
public function CreatePreUpdateService(UserSession $userSession)
{
// Delegate unchanged actions to LibreBooking's default validation.
return $this->factoryToDecorate->CreatePreUpdateService($userSession);
}
public function CreatePreDeleteService(UserSession $userSession)
{
return $this->factoryToDecorate->CreatePreDeleteService($userSession);
}
public function CreatePreApprovalService(UserSession $userSession)
{
return $this->factoryToDecorate->CreatePreApprovalService($userSession);
}
public function CreatePreCheckinService(UserSession $userSession)
{
return $this->factoryToDecorate->CreatePreCheckinService($userSession);
}
public function CreatePreCheckoutService(UserSession $userSession)
{
return $this->factoryToDecorate->CreatePreCheckoutService($userSession);
}
}
Validation service example:
<?php
class MyRuleValidation implements IReservationValidationService
{
private IReservationValidationService $serviceToDecorate;
public function __construct(IReservationValidationService $serviceToDecorate)
{
$this->serviceToDecorate = $serviceToDecorate;
}
public function Validate($series, $retryParameters = null)
{
$result = $this->serviceToDecorate->Validate($series, $retryParameters);
// Preserve any validation failures from the base service.
if (!$result->CanBeSaved()) {
return $result;
}
// Add custom validation here.
$isValid = true;
if ($isValid) {
return new ReservationValidationResult();
}
return new ReservationValidationResult(false, 'Reservation is not allowed.');
}
}
Post-Reservation Plugins¶
Post-reservation plugins run after reservation persistence. Use them for custom notifications, integrations, webhooks, or side effects.
A post-reservation plugin implements IPostReservationFactory and decorates
PostReservationFactory. The factory methods correspond to reservation
actions:
CreatePostAddServiceRuns after a new reservation is saved. Use this for custom notifications, webhooks, external calendar sync, audit logging, or follow-up processing.
CreatePostUpdateServiceRuns after an existing reservation is updated. Use this to sync changed reservation details to another system or send custom update notifications.
CreatePostDeleteServiceRuns after a reservation is deleted. Use this to notify external systems, clean up related records, or send custom cancellation messages.
CreatePostApproveServiceRuns after a pending reservation is approved. Use this to notify approvers, owners, or external systems that the reservation is now confirmed.
CreatePostCheckinServiceRuns after a reservation check-in is saved. Use this to update access control systems, notify staff, or record custom attendance information.
CreatePostCheckoutServiceRuns after a reservation check-out is saved. Use this to release external resources, close related work orders, or record custom usage data.
The built-in example is:
plugins/PostReservation/PostReservationExample/PostReservationExample.php
Minimal example:
<?php
class MyNotifier implements IPostReservationFactory
{
private PostReservationFactory $factoryToDecorate;
public function __construct(PostReservationFactory $factoryToDecorate)
{
$this->factoryToDecorate = $factoryToDecorate;
}
public function CreatePostAddService(UserSession $userSession)
{
// Wrap notification handling after new reservations are saved.
$base = $this->factoryToDecorate->CreatePostAddService($userSession);
return new MyNotifierService($base);
}
public function CreatePostUpdateService(UserSession $userSession)
{
// Delegate unchanged actions to LibreBooking's default notifications.
return $this->factoryToDecorate->CreatePostUpdateService($userSession);
}
public function CreatePostDeleteService(UserSession $userSession)
{
return $this->factoryToDecorate->CreatePostDeleteService($userSession);
}
public function CreatePostApproveService(UserSession $userSession)
{
return $this->factoryToDecorate->CreatePostApproveService($userSession);
}
public function CreatePostCheckinService(UserSession $userSession)
{
return $this->factoryToDecorate->CreatePostCheckinService($userSession);
}
public function CreatePostCheckoutService(UserSession $userSession)
{
return $this->factoryToDecorate->CreatePostCheckoutService($userSession);
}
}
Notification service example:
<?php
class MyNotifierService implements IReservationNotificationService
{
private IReservationNotificationService $base;
public function __construct(IReservationNotificationService $base)
{
$this->base = $base;
}
public function Notify($reservationSeries)
{
// Run custom post-reservation behavior here.
// Call the base service to keep LibreBooking's default notifications.
$this->base->Notify($reservationSeries);
}
}
Plugin Configuration Files¶
If a plugin needs its own settings, place a config file in the plugin directory
and register it in the plugin constructor. New plugin configuration should use
PluginConfigKeys definitions so values can be validated, converted, and
managed consistently.
require_once(ROOT_DIR . 'lib/Config/namespace.php');
class MyRuleConfigKeys extends PluginConfigKeys
{
public const CONFIG_ID = 'MyRule';
public const MAX_VALUE = [
'key' => 'max.value',
'type' => 'integer',
'default' => 10,
'label' => 'Maximum Value',
'description' => 'Maximum value accepted by the custom reservation rule',
'section' => 'myrule',
];
}
The plugin config file must return a settings array:
<?php
return [
'settings' => [
'myrule' => [
'max.value' => 10,
],
],
];
Register the plugin config file with the config file path, optional env file path, config ID, overwrite flag, and config key class:
require_once(dirname(__FILE__) . '/MyRuleConfigKeys.php');
Configuration::Instance()->Register(
dirname(__FILE__) . '/MyRule.config.php',
__DIR__ . '/.env',
MyRuleConfigKeys::CONFIG_ID,
false,
MyRuleConfigKeys::class
);
The plugin can later read its settings with:
$configFile = Configuration::Instance()->File(MyRuleConfigKeys::CONFIG_ID);
$maxValue = $configFile->GetKey(MyRuleConfigKeys::MAX_VALUE);
If a plugin needs to inspect arbitrary raw values that are not represented by a
PluginConfigKeys definition, read the configuration array with
GetValues():
$values = Configuration::Instance()->File(MyRuleConfigKeys::CONFIG_ID)->GetValues();
$myRuleValues = $values['myrule'] ?? [];
For current plugin configuration examples, see
plugins/Authentication/Ldap/LdapConfigKeys.php and
plugins/Authentication/Ldap/LdapOptions.php.
Troubleshooting¶
If a plugin does not load:
Confirm the path, file name, and class name all match the configured plugin name.
Confirm the plugin is in the correct plugin type directory.
Confirm the plugin is enabled with the correct key in
config/config.php.Check the LibreBooking logs for
Loading pluginorError loading pluginmessages.Clear PHP OPcache or restart the web server/container after changing plugin code.
Confirm all required files are included with
require_oncefrom the plugin entry file.