After a long time of consideration and many trains of thought about pros and cons, I decided to implement a hooking functionality in Scriptor to easily adapt the existing Editor core methods to the needs of the developers.

Scriptor provides many methods (and there is a growing number) that you can easily hook in to modify their behavior or even add new methods to existing classes.

Let's take a closer look to get familiar with the hooking functionality.

Hook types

before - A hook that runs before the hooked method.

after - A hook that runs after the hooked method.

method hooks - A hook which adds a new method to an existing module.

replace hooks - A hook that completely replaces the hooked method.

Where to define the hooks

Like many other components of Scriptor, the hooks must first be made available. This is usually done in the scriptor-config.php resp. custom.scriptor-config.php file. This is not a requirement though, and the hooks can also be defined dynamically from anywhere. However, that initialization must be performed before the function to be hooked is called. Below is an snippet from the custom.scriptor-config.php file, as an example:

...
'hooks' => [
    /** 
     * @var string - Hook name (hookable method)
     */
    'Pages::afterRenderEditorTemplateField' => [
        [
           /**
            * @var string|null - Name of the module 
            */
            'module' => 'YourModule', 
           /**
            * @var string|closure - Module method that should be called
            */
            'method' => 'renderYourPageField'
        ]
    ],
    /** 
     * Next hook name ...
     */
    'Pages::afterSavePage' => [
        [
            'module' => 'YourModule',
            'method' => 'yourSaveMethod'
        ]
    ],
]

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.

Yeah, I know, if you haven't used the hooks in Scriptor yet, it already gets very confusing at this point. Let's see a practical application of the hooks first before we continue with that explanation. For this I have come up with something great, an extra feature for Scriptor that allows us to use the Meta data on our website:

› Continue with Metadata tutorial ...

Read or modify arguments

How can your hook read and modify the arguments sent to the hooked method? Every hook is passed an event object argument, you can access its values:

public function beforeRenderPageEditor($event) 
{
    // retrieve first argument by index
    $page = $event->args[0];
    $page->set('name', 'This is my new page name');
}

How can my hook know if it is a right object to edit? Let's say you want no edit option to be available when opening a certain page in the editor, you want a message to be displayed instead:

public function beforeRenderSpecialPage($event)
{
    // Get page to be rendered
    $page = $event->object->page;
    // If it is a page with the slug 'scriptors-demo-page', a message should be displayed
    if($page && $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>';
    }
}

Modify return value of the hooked method

The return value of a hooked method may be read or modified in a similar manner to the arguments. This is best demonstrated by a code example. Let's say that we want to add an additional table row to the end of the pages table in Scriptor's page list view. We would hook after Pages::renderRows with a hook method implementation like this:

public function afterRenderPageRows($event)
{
    $value  = $event->return;
    $value .= '<tr><td colspan="5">An additional line of the pages table</td></tr>';
    $event->return = $value;
}

Your hook definition in custom.scriptor-config.php could look like this:

'hooks' => [
    'Pages::afterRenderRows' => [
        [
            'module' => 'YourModule',
            'method' => 'afterRenderPageRows'
        ]
    ],
]

If you open the pages list view, it should look like this:

Additional line