* Copyright (C) 2025 Frédéric France * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ // Load Dolibarr environment require '../../../../../../main.inc.php'; /** * @var DoliDB $db * @var HookManager $hookmanager * @var Translate $langs * @var User $user */ // Protection if external user if ($user->socid > 0) { accessforbidden(); } // Includes require_once DOL_DOCUMENT_ROOT . '/admin/tools/ui/class/documentation.class.php'; // Load documentation translations $langs->load('uxdocumentation'); // $documentation = new Documentation($db); $group = 'ExperimentalUx'; $experimentName = 'UxDolibarrContext'; $experimentAssetsPath = $documentation->baseUrl . '/experimental/experiments/dolibarr-context/assets/'; $js = [ '/includes/ace/src/ace.js', '/includes/ace/src/ext-statusbar.js', '/includes/ace/src/ext-language_tools.js', $experimentAssetsPath . '/dolibarr-context.umd.js', $experimentAssetsPath . '/dolibarr-tool.seteventmessage.js', ]; $css = []; // Output html head + body - Param is Title $documentation->docHeader($langs->trans($experimentName, $group), $js, $css); // Set view for menu and breadcrumb $documentation->view = [$group, $experimentName]; // Output sidebar $documentation->showSidebar(); ?>
showBreadCrumb(); ?>

trans($experimentName); ?> : trans('UxDolibarrContextHowItWork'); ?>

showSummary(); ?>

Introduction

DolibarrContext is a secure global JavaScript context for Dolibarr. It provides a single global object window.Dolibarr, which cannot be replaced. It allows defining non-replaceable tools and managing hooks/events in a modular and secure way.

This system is designed to provide long-term flexibility and maintainability. You can define reusable tools that encapsulate functionality such as standardized AJAX requests and responses, ensuring consistent data handling across Dolibarr modules. For example, tools can be created to wrap API calls and automatically process returned data in a uniform format, reducing repetitive code and preventing errors.

Beyond DOM-based events, DolibarrContext allows monitoring and reacting to business events using a hook-like mechanism. For instance, you can listen to events such as Dolibarr.on('addline:load:productPricesList', function(data) { ... }); without relying on DOM changes. This enables creating logic that reacts directly to application state changes.

Similarly, you can define tools that act as global helpers, like Dolibarr.tools.setEventMessage(). This tool can display notifications (similar to PHP's setEventMessage() in Dolibarr), initially using jNotify or any other library. In the future, the underlying library can change without affecting the way modules or external code call this tool, maintaining compatibility and reducing maintenance.

In summary, DolibarrContext provides a secure, extensible foundation for adding tools, monitoring business events, and standardizing interactions across Dolibarr's frontend modules.

Console help

Open your browser console with F12 to view the available commands.
If the help does not appear automatically, type Dolibarr.tools.showConsoleHelp(); in the console to display it.

Dolibarr.log() and debugMode()

Dolibarr.log() is a lightweight logging utility provided by the Dolibarr JS context. It does not replace console.log(), but it gives an important advantage: you can enable or disable all logs globally using Dolibarr.debugMode().

Why use Dolibarr.log() instead of console.log()?

  • You can enable or disable logging dynamically from the browser console.
  • Avoid polluting the console for end-users: logs appear only when debug mode is enabled.
  • Ideal for module development: switch between quiet mode and verbose mode instantly.
  • Useful in production debugging: you can activate logs without modifying any code.

How it works

When debugMode is disabled (default state), calls to Dolibarr.log() do nothing. When enabled, Dolibarr.log() behaves like console.log().

', ' // Log something (will only appear if debug mode is ON)', ' Dolibarr.log("My debug message");', '', ' // Enable verbose logs', ' Dolibarr.debugMode(true);', '', ' // Disable logs again', ' Dolibarr.debugMode(false);', '', ); echo $documentation->showCode($lines, 'php'); ?>

Summary

  • console.log() → always prints messages, noisy, not controllable, but useful during active development and debugging.
  • Dolibarr.log() → prints messages only when debug mode is ON; fully controllable. Ideal for Dolibarr core logs or when you want to keep logs available but silent in production.
  • Dolibarr.debugMode(true) → enable verbose logs, activating Dolibarr.log() output.
  • Dolibarr.debugMode(false) → disable all Dolibarr.log() output, silencing debug messages.

JS Dolibarr hooks

Dolibarr provides a hook system to allow modules and scripts to communicate with each other through named events. There are two ways to listen to these events in JS: Dolibarr.on() and document.addEventListener().

Event listener example

You can use Dolibarr.on() to listen to a hook. The main difference with standard document events is that the callback receives directly the data object passed when the hook is executed, without needing to access e.detail.

For backward compatibility and standard DOM integration, the same hook can also be caught using document.addEventListener(), but in this case the data is inside e.detail and the event name is prefixed by Dolibarr: so for a hook named A event name is Dolibarr:A

', ' // Add a listener to the Dolibarr theEventName event', ' Dolibarr.on(\'theEventName\', function(data) {', ' console.log(\'Dolibarr theEventName\', data);', ' });', '', ' // But this work too on document', ' document.addEventListener(\'Dolibarr:theEventName\', function(e) {', ' console.log(\'Dolibarr theEventName\', e.detail);', ' });', '', ); echo $documentation->showCode($lines, 'php'); ?>

Practical usage

When Dolibarr is ready (DOM loaded and JS context initialized), you can register your hooks or trigger them. Both Dolibarr.on() and document.addEventListener() are valid, but Dolibarr.on() is simpler and more convenient because you get the data directly.

', ' document.addEventListener(\'Dolibarr:Ready\', function(e) {', ' // the dom is ready and you are sure Dolibarr js context is loaded', ' ...', ' // Do your stuff', ' ...', '', ' // Add a listener to the yourCustomHookName event with dolibarr.on()', ' Dolibarr.on(\'yourCustomHookName\', function(data) {', ' console.log(\'With Dolibarr.on : data will contain { data01: \'stuff\', data02: \'other stuff\' }\', data);', ' });', '', ' // Or you can do : Add a listener to the yourCustomHookName document.addEventListener()', ' document.addEventListener(\'Dolibarr:yourCustomHookName\', function(e) {', ' console.log(\'With document.addEventListener : e.detail will contain { data01: \'stuff\', data02: \'other stuff\' }\', e.detail);', ' });', '', ' // you can trigger js hooks', ' document.getElementById(\'try-event-yourCustomHookName\').addEventListener(\'click\', function(e) {', ' Dolibarr.executeHook(\'yourCustomHookName\', { data01: \'stuff\', data02: \'other stuff\' })', ' });', '', ' ...', ' // Do your stuff', ' ...', ' });', '', ); echo $documentation->showCode($lines, 'php'); ?> Open your console F12 and click on

Difference between Dolibarr:Init and Dolibarr:Ready event

Dolibarr provides two main initialization events for its JavaScript context: Dolibarr:Init and Dolibarr:Ready. Understanding their difference is important when developing modules or tools.

  • Dolibarr:Init is triggered immediately when the Dolibarr context is created. This event is intended for:
    • Defining or registering new tools via Dolibarr.defineTool().
    • Setting context variables (Dolibarr.setContextVar() / Dolibarr.setContextVars()).
    • Preparing configuration that must be available before the DOM is fully loaded.
    It occurs before Dolibarr:Ready, so it is ideal for setup tasks that other tools may depend on.
  • Dolibarr:Ready is triggered once the DOM is ready, similar to $(document).ready() in jQuery. This event is intended for:
    • Running code that interacts with the DOM.
    • Attaching event listeners to elements on the page.
    • Executing functionality that requires all tools and context variables to be fully initialized.

In short, use Dolibarr:Init for setting up tools and context variables, and Dolibarr:Ready for code that needs the DOM and fully initialized context.

Examples of usage

', ' // Example: Dolibarr:Init - define a tool and set context variables early', ' document.addEventListener(\'Dolibarr:Init\', function(e) {', ' console.log("Init event fired, Dolibarr is initialised and receive context vars a tools");', ' });', '', ' // Example: Dolibarr:Ready - interact with DOM and use tools', ' document.addEventListener(\'Dolibarr:Ready\', function(e) {', ' console.log("Ready event fired, DOM is ready");', '', '', ' // Attach event listener to a DOM element', ' const btn = document.getElementById("myButton");', ' if(btn) {', ' btn.addEventListener("click", function() {', ' alert("Button clicked! Context value: " + Dolibarr.getContextVar("mySetting"));', ' });', ' }', ' });', '', ); echo $documentation->showCode($lines, 'php'); ?>

In summary:

  • Dolibarr:Init → early setup, tools, context variables, configuration.
  • Dolibarr:Ready → DOM is ready, safe to manipulate elements and use tools defined in Init.

Async Hooks (Await Hooks) - sequential execution

Dolibarr supports asynchronous hooks using Dolibarr.onAwait() and Dolibarr.executeHookAwait(). These hooks allow you to register functions that execute in sequence and can modify data before passing it to the next hook. They are useful for complex workflows where multiple modules or scripts need to process or enrich the same data asynchronously.

Each hook can optionally specify before or after to control the execution order relative to other hooks. Every hook registration returns a unique id, which can be used to reference or unregister the hook later.

Unlike standard synchronous hooks registered with Dolibarr.on(), await hooks return a Promise when executed. This means you can await their results in your code, and any asynchronous operations inside a hook (e.g., API calls, timers) will be handled correctly before moving to the next hook.

">', ' document.addEventListener(\'Dolibarr:Ready\', async function(e) {', '', ' // Register async hooks will be executed in first place', ' Dolibarr.onAwait(\'calculateDiscount\', async function(order) {', ' order.total *= 0.9; // Apply 10% discount', ' return order;', ' }, { id: \'discount10\' });', '', ' // Register async hooks will be executed in third place', ' Dolibarr.onAwait(\'calculateDiscount\', async function(order) {', ' if(order.total > 1000) order.total -= 50; // Extra discount over 1000', ' return order;', ' }, { id: \'discountOver1000\', after: \'discount10\' });', '', ' // Register async hooks will be executed in second place', ' // this hook item as no id so plus10HookItemId will receive a unique random id ', ' let plus10HookItemId = Dolibarr.onAwait(\'calculateDiscount\', async function(order) {', ' order.newObjectAttribute = \'My value\';', ' order.total += 10;', ' return order;', ' }, { before: \'discountOver1000\' });', '', ' document.getElementById(\'try-event-yourCustomAwaitHookName\').addEventListener(\'click\', async function(e) {', ' // Execute all registered await hooks sequentially', ' let order = {total: 1200};', ' order = await Dolibarr.executeHookAwait(\'calculateDiscount\', order);', ' console.log(order); // order.total : 1200 -> 1080 -> 1090 -> 1040', ' });', '', ' });', '', ); echo $documentation->showCode($lines, 'php'); ?> Open your console F12 and click on

Example of creating a new context tool

Defining Tools

You can define reusable and protected tools in the Dolibarr context using Dolibarr.defineTool.

See also dolibarr-context.mock.js for defining all standard Dolibarr tools and creating mock implementations to improve code completion and editor support.

Note : a tool can be a class not only a function

', 'document.addEventListener(\'Dolibarr:Init\', function(e) {', ' // Define a simple tool', ' let overwrite = false; // Once a tool is defined, it cannot be replaced.', ' Dolibarr.defineTool(\'alertUser\', (msg) => alert(\'[Dolibarr] \' + msg), overwrite);', '});', '', 'document.addEventListener(\'Dolibarr:Ready\', function(e) {', ' // Use the tool', ' Dolibarr.tools.alertUser(\'hello world\');', '});', '', ); echo $documentation->showCode($lines, 'php'); ?>

Protected Tools

Once a tool is defined on overwrite false, it cannot be replaced. Attempting to redefine it without overwrite will throw an error:

', ' try {', ' Dolibarr.defineTool(\'alertUser\', () => {});', ' } catch (e) {', ' console.error(e.message);', ' }', '', ); echo $documentation->showCode($lines, 'php'); ?>

Reading Tools

You can read the list of available tools using Dolibarr.tools. It returns a frozen copy:

', ' console.log(Dolibarr.tools);', ' if(Dolibarr.checkToolExist(\'Tool name to check\')){/* ... */}else{/* ... */}; ', '', ); echo $documentation->showCode($lines, 'php'); ?>

Set event message tool

Instead of calling JNotify directly in your code, use Dolibarr’s setEventMessage tool. Dolibarr provides the configuration option DISABLE_JQUERY_JNOTIFY, which disables the jQuery JNotify system, usually because another notification library will be used instead.

If you rely on Dolibarr.tools.setEventMessage(), your code remains compatible even if the underlying notification system changes. The setEventMessage tool can be replaced internally without requiring any changes in your modules or custom scripts.

This means all developers can write features without worrying about frontend compatibility or future library replacements. Enjoy!

" >', ' document.addEventListener(\'Dolibarr:Ready\', function(e) {', '', ' document.getElementById(\'setEventMessage-success\').addEventListener(\'click\', function(e) {', ' Dolibarr.tools.setEventMessage(\'Success Test\');', ' });', '', ' document.getElementById(\'setEventMessage-error\').addEventListener(\'click\', function(e) {', ' Dolibarr.tools.setEventMessage(\'Error Test\', \'errors\');', ' });', '', ' document.getElementById(\'setEventMessage-error-sticky\').addEventListener(\'click\', function(e) {', ' Dolibarr.tools.setEventMessage(\'Error Test\', \'errors\', true);', ' });', '', ' document.getElementById(\'setEventMessage-warning\').addEventListener(\'click\', function(e) {', ' Dolibarr.tools.setEventMessage(\'Warning Test\', \'warnings\');', ' });', '', ' });', '', ); echo $documentation->showCode($lines, 'php'); ?>

Set and use context vars

The Context Vars system allows you to define and manage variables that are globally accessible within the Dolibarr JavaScript context. These variables can store configuration data, URLs, tokens, user IDs, object references, or any other values needed by your frontend code and tools. By using context vars, you can:

  • Pass server-side data (from PHP) to JavaScript safely and consistently.
  • Provide reusable configuration for Dolibarr tools, widgets, or modules without hardcoding values.
  • Define overridable or non-overridable vars to protect critical values while allowing flexible overrides when necessary.
  • Use Dolibarr.setContextVar for single values or Dolibarr.setContextVars to pass multiple values at once.
  • Access these variables anywhere in your code via Dolibarr.getContextVar(key).
  • Ensure that all your modules and tools can rely on consistent and up-to-date context information, improving maintainability and interoperability.
This system is particularly useful for setting up base URLs, API endpoints, user-specific information, or runtime data that needs to be shared across multiple Dolibarr frontend tools.

Add context var (overridable or not)

" >', ' document.addEventListener(\'Dolibarr:Init\', function(e) {', ' // Add no overridable context var', ' Dolibarr.setContextVar(\'yourKey\', \'YourValue\');', '', ' // Add overridable context var', ' Dolibarr.setContextVar(\'yourKey2\', \'YourValue\', true);', ' });', '', ); echo $documentation->showCode($lines, 'php'); ?>

Add multiple context vars (overridable or not)

DOL_URL_ROOT,', ' \'token\' => newToken(),', ' \'cardObjectElement\' => $object->element,', ' \'cardObjectId\' => $object->id,', ' \'currentUserId\' => $user->id', ' // ...', ' ];', '', ' $contextVars = [', ' \'lastCardDataRefresh\' => time(),', ' // ...', ' ]', '?>', '', ); echo $documentation->showCode($lines, 'php'); ?>

Get context var

" >', ' document.addEventListener(\'Dolibarr:Ready\', function(e) {', ' let url = Dolibarr.getContextVar(\'DOL_URL_ROOT\', \'The optional fallback value\'));', ' console.log(url);', ' });', '', ); echo $documentation->showCode($lines, 'php'); ?>
docFooter(); ?>