menu_book Navigation menu

Hooks

Pragma CMS follows a "Pluggable Architecture" philosophy. Instead of modifying core files, you use Hooks to inject custom logic or transform data. This ensures your site remains compatible with future core updates.

PHP Hooks (Actions & Filters)

The PHP hook system is the backbone of server-side extensibility. It is managed by the Hooks class and uses the Hook Enum for type-safe event names.

1. Actions (addAction / doAction)

Concept: "When this event happens, execute this code."

Actions are triggers. They allow you to run additional code at specific moments (e.g., sending a Slack notification after a form is submitted) without expecting anything in return.

  • Analogy: An Announcement. The core shouts: "I just saved an entry!", and your code listens and reacts.
  • Example:
PHP
// Listen to a core event using the Enum
Hooks::addAction(Hook::ON_ENTRY_SAVE, function($entryId, $entryData) {
    // Custom logic: e.g., clearing a specific cache
    MyCacheManager::clear($entryId);
});

2. Filters (addFilter / applyFilters)

Concept: "I’m about to use this data. Does anyone want to modify it first?"

Filters are used to transform data. They are sequential; each function in the chain receives the value from the previous one.

  • Analogy: An Assembly Line. Data enters the line, and each hooked function (worker) can inspect it, modify it, and pass it to the next one.
  • Crucial Rule: A filter must return the value, even if it wasn't modified.
  • Example:
PHP
// Modify a CSS class list before rendering
Hooks::addFilter(Hook::ENQUEUE_CSS, function(array $cssFiles) {
    $cssFiles[] = getAssetsPath('css/custom-extension.css');
    return $cssFiles; // Always return the value
});

Type Safety & Custom Hooks

While core hooks are accessed via Hook::ENUM_NAME, you can create your own hooks for your extensions by passing a string:

PHP
// Triggering a custom hook in your extension
Hooks::doAction('my_extension_custom_event', $data);

JS Hooks (UI & Editor)

Pragma CMS exposes a global window.CMS namespace. This allows your scripts to register components or logic before the core UI initializes, ensuring a seamless integration.

1. Global Namespace

All core APIs and tools are exposed through window.CMS.

  • window.CMS.tools: Registry for Editor.js block classes.
  • window.CMS.media / window.CMS.icons: APIs to programmatically open system modals and handle selections.
  • window.CMS.fieldRenderers: A registry used to define how custom fields should render their configuration UI in the Content Type builder.

2. Extending the Visual Builder (Editor.js)

To keep the UI fast and prevent crashes, Pragma CMS handles Editor.js tools through two distinct registries:

Root Tools (editorExtraTools)

Used to add a new block type to the main + menu of the editor.

JAVASCRIPT
// Registering a custom "Alert" block
window.CMS.editorExtraTools = window.CMS.editorExtraTools || {};

window.CMS.editorExtraTools.alert = {
    class: AlertTool, // Your Editor.js class
    tunes: ['containerTune'], // Enable responsive width settings
    inlineToolbar: true,
    config: {
        placeholder: 'Enter alert text...'
    }
};

Sub-Blocks (subBlockRegistry)

To make a component available specifically inside the Columns tool, you must register the class in the window.CMS.subBlockRegistry. This registry allows the Columns component to instantiate sub-tools without duplicating core logic.

JAVASCRIPT
// Making the "Alert" block available inside Columns
window.CMS.subBlockRegistry = window.CMS.subBlockRegistry || {};

window.CMS.subBlockRegistry.alert = AlertTool;
info
Design Note
By separating the registry from the main configuration, we ensure O(1) lookups during recursive block rendering. This prevents the browser from attempting to instantiate non-tool objects, guaranteeing a crash-free experience even with deeply nested layouts.

3. Text Handling & Paste Rules

To maintain data integrity, all custom tools using contentEditable should use the core utility handleCmsPaste(e). This forces plain-text input and prevents "block escaping" (where Editor.js creates a new root paragraph when you paste text into a sub-field).

JAVASCRIPT
// Example in a custom render() method
myInputElement.addEventListener('paste', handleCmsPaste);

4. Custom Field Renderers

f you create a new field type for Content Types, you can hook into the Builder UI to define its settings panel:

JAVASCRIPT
window.CMS.fieldRenderers.my_custom_field = function(fieldId, data) {
    return `
        <div class="form-container">
            <label>Custom Option</label>
            <input type="text" name="fields[${fieldId}][options][my_key]" value="${data.options?.my_key || ''}">
        </div>
    `;
};

Best Practices

  1. Priority: Both PHP and JS hooks support priority (default is 10). Lower numbers execute first.
  2. Naming: Prefix your custom string hooks with your extension handle (e.g., shop:after_checkout) to avoid collisions.
  3. Performance: Keep hook callbacks lightweight. For heavy PHP tasks (like image processing), use the action to trigger a background job or a deferred task.
menu_book
Deep Dive
For a full list of every available core hook, their parameters, and timing, visit the Hooks Reference Guide.