After careful consideration and thorough evaluation of the advantages and disadvantages, I have implemented hooking functionality in Scriptor. This feature simplifies the process for developers to adapt existing Editor core methods according to their specific requirements.

Scriptor provides a wide range of methods, with new ones being added continuously, that can be easily hooked into to modify their behavior or even introduce new methods to existing classes.

In this tutorial, we will delve into the usage of Scriptor's hooking functionality. By the end of this tutorial, you will have a solid understanding of how to utilize hooks to customize and extend Scriptor's functionality.

Types of Hooks

  • Before Hooks: These hooks execute before the hooked method.
  • After Hooks: These hooks run after the hooked method.
  • Method Hooks: These hooks add new methods to existing modules.
  • Replace Hooks: These hooks completely replace the hooked method.

Defining Hooks in Scriptor

To make hooks available in Scriptor, they must be defined in the scriptor-config.php or custom.scriptor-config.php file. It's important to note that while this is the typical approach, it is not mandatory. Hooks can be defined dynamically from anywhere, as long as the initialization occurs prior to the invocation of the function intended to be hooked.

Here's an improved example snippet from the custom.scriptor-config.php file, demonstrating how to define hooks:

// ...
'hooks' => [
    'Pages::afterRenderEditorTemplateField' => [
        [
            'module' => 'YourModule',
            'method' => 'renderYourPageField'
        ]
    ],
    'Pages::afterSavePage' => [
        [
            'module' => 'YourModule',
            'method' => 'yourSaveMethod'
        ]
    ],
    // Or you can use closures
    'Pages::afterRenderPageList' => [
            [
                'method' => function ($event) {
                    // ...
                }
            ]
        ]
    // Add more hooks as needed
],
// ...

In the above code, you can define hooks by specifying the hook name (hookable method) and the corresponding module method to be called. Here are the key elements:

  • 'Pages::afterRenderEditorTemplateField' represents the hook name for the afterRenderEditorTemplateField method in the Pages class.
  • 'module' => 'YourModule' specifies the name of the module containing the custom method.
  • 'method' => 'renderYourPageField' indicates the method renderYourPageField within the specified module that should be called when the hook is triggered.

You can add more hooks to the 'hooks' array as necessary, following the same structure.

Ensure that the code snippet is placed in the appropriate location within the custom.scriptor-config.php file for the hooks to be properly defined.

By defining hooks in this way, you can modify the behavior of existing methods, or even add new methods to existing classes. This provides a powerful way to customize and extend the functionality of Scriptor to meet your specific needs.

Using Hooks

There are no restrictions on how you can use your hooks, but it is common practice to create a separate module for using the hooks and place the functionality there. Note, however, that you need to make your module known to the Scriptor before you use it. You can read about how to make the module known to the Scriptor here.

I understand that hooks in Scriptor can be confusing if you haven't used them before. To make it easier to grasp, let's explore a practical application of hooks before diving into further explanations. I've developed a fantastic additional feature for Scriptor that enables us to utilize the Meta data on our website:

› Continue with Metadata tutorial ...

Reading or Modifying Arguments

To access and modify the arguments passed to the hooked method, you can utilize the event object provided within each hook. You can leverage this object to access and manipulate the values within it.

The following code snippet demonstrates how to access and modify arguments within the Pages module while in edit mode:

'Pages::beforeRenderEditorTitleField' => [
    [
        'method' => function ($event) {
            $page = $event->args[0];
            $page->set('name', 'This is my new page name');
        }
    ]
]

This code will execute each time you open any page within the Pages module in edit mode, specifically before the rendering of the page title field. The provided closure is invoked, and $event->args[0] retrieves the first argument, which represents the current page being edited. In this example, the page's name is then modified by setting it to "This is my new page name" using $page->set('name', 'This is my new page name').

Important: The objects or properties you can access within the function you hook into depend on the specific functionality you are hooking into. Therefore, it is recommended to review the methods in the modules you intend to hook into.

Accessing Objects within the Hooked Function

Let's now examine another example. Suppose you want to prevent the editing of a specific page, specifically a page with the slug "scriptors-demo-page", by the user in the editor. Instead, you want to display a notification to the user stating that they are not authorized to edit the page.

To achieve this, we will hook into the Pages::beforeRenderEditorPage method. This hook is triggered before the page is rendered in the editor, allowing us to modify the rendering process.

Here is the code snippet:

'Pages::beforeRenderEditorPage' => [
    [
        'method' => function ($event) {
            $page = $event->object->page;
            if ($page->slug === 'scriptors-demo-page') {
                $event->return = 
                    '<div id="screen-content">' .
                        '<h3>Notice</h3>' .
                        '<p>You don\'t have permission to edit this page!</p>' .
                    '</div>';
            }
        }
    ]
],

In this example, the provided closure is executed when the user attempts to edit a page in the editor. We access the current page being edited using $event->object->page. If the page's slug matches "scriptors-demo-page", we replace the $event->return value with a custom HTML notification that informs the user they don't have permission to edit the page.

Make sure to modify the code according to your specific page slug and customize the HTML notification as per your requirements.

Modify return value of the hooked method

The return value of a hooked method can be accessed and modified in a similar way as the arguments. Let's illustrate this with a code example. Suppose we want to add an additional table row at the end of the page table in the page list view of Scriptor. We can achieve this by hooking into Pages::afterRenderPageList and implementing a hook method like the following:

'Pages::afterRenderPageList' => [
    [
        'method' => function ($event) {
            $event->return .= 
                '<div style="margin-top: 20px; padding: 5px 10px; 
                    background: red; color: white;">
                    <p>An additional line after the page listing.</p>
                </div>';
        }
    ]
]

In this code, we hook into the Pages::afterRenderPageList method, which is triggered after rendering the page list. Inside the hook method, we directly modify the return value by appending an additional table row to it using the .= operator.

Executing functions across the entire editor area

There may be situations where you want to execute a function throughout the entire editor area using hooks, for example, on every call to https://your-site.com/editor/*. One common use case is to add your own CSS resource in the <head> section of the editor. In such cases, it is recommended to directly hook into the editor's execute() method, as shown in the following code:

'Editor::beforeExecute' => [
    [
        'method' => function ($event) {
            $event->object->addResource('link', [
                'rel' => "stylesheet",
                'href' => dirname($event->object->siteUrl).
                    '/site/modules/yourModule/css/styles.css'
            ]);
        }
    ]
],
...

In this code snippet, we hook into the Editor::beforeExecute method, which is called before executing any editor request. Inside the hook method, we use the $event->object to access the editor instance and call the addResource() method. This allows us to add a custom CSS resource by specifying its rel and href attributes. In this example, we construct the CSS file URL based on the location of your module's CSS file.

By using this approach, you can perform custom actions across the entire editor area, giving you the flexibility to add your own resources, modify the editor's behavior, or implement other functionality to suit your specific needs.