In February 2019 we introduced a Developer Widget class that permits us to easily add widgets, in April we created our first public chart widget which is documented in the wiki. This August we release another widget to mass upload documents and I thought it would be a good idea to create a tutorial using Developer Blocks and the PRESAVE event we introduced in March. Enjoy!

I was reviewing the code for the Mass Upload Developer widget that we created a few months ago and I had the idea to create a widget that would launch before a record is saved. So, as a proof of concept, I wrote it and will document the steps here as a tutorial.

The Goal

When the application user clicks on the save button of a chosen module we must launch some code that will ask the user if he wants to proceed with the save or not. We will be able to execute code both in the backend and in the browser.

The Tools

To accomplish this (without modifying any base code) we will need:

  • Business Actions (Links) to add code in the application and in the events
  • Developer Blocks/Widgets that will actually do the work. In our example, I will simply ask for consent to proceed with the save or not
  • The PRESAVE event (which is launched when the user clicks on the save button). Code can be associated to the event and will be launched in the background, permitting us to do anything we need with the information coming from the screen and then we can answer back with a function to be called or not.

The Steps

Code we want to execute

First let's create the code we need: the developer block and the javascript that we want to launch when the button is clicked.

We need a Developer block that will contain the code we hook into the PRESAVE event and that will permit us to execute code inside the application. This code can live anywhere but I am going to put it in modules/Utilities/askQuestionOnSave.php and it looks like this:


// PRESAVE ACTION: block://askQuestionOnSave:modules/Utilities/askQuestionOnSave.php:recordid=$RECORD$
// javascript action: modules/Assets/askToProceed.js

require_once 'modules/Vtiger/DeveloperWidget.php';
global $currentModule;

class askQuestionOnSave {
    // Get class name of the object that will implement the widget functionality
    public static function getWidget($name) {
        return (new askQuestionOnSave_DetailViewBlock());

class askQuestionOnSave_DetailViewBlock extends DeveloperBlock {
    // Implement widget functionality
    private $widgetName = 'askQuestionOnSave';

    // This one is called to get the contents to show on screen
    public function process($context = false) {
        global $adb;
        $this->context = $context;
        $smarty = $this->getViewer();
        // Here we have full access to the application and the information on the screen should be in the context
        // We return the function name and parameters.
        // since we have full control of both anything could be sent and processed
        return '%%%FUNCTION%%%askToProceed%%%PARAMS%%%Should we proceed?';

As you can see it is just "send a string to the frontend" but we could do anything we need. In this case, I am sending a specifically formatted string that instructs the browser to look for a function with the given name (askToProceed) and call it with the default parameters and the given ones.

As per the definition of the PRESAVE event, these functions take full control of the save process and receive the context they need to do so in the form of these parameters:

  • edit_type: mass_edit or edit
  • formName
  • action: MassEditSave or Save
  • callback a callback function if any is necessary
  • any parameters sent by the developer widget

Since the function called in the PRESAVE event takes full control of the save process it must decide to return a "true" value so that the application can continue normally with the save process or a "false", in which case the save will not be continued. Also, this function receives a callback function which it can use to finish the save process itself if it needs to. The callback can be any function, but if it is dtlViewAjaxFinishSave then the save is an inline detail view edit and you will have to retrieve all the parameters for this function, additionally, if it isn't a function then you will have to call submitFormForAction(formName, action). It is a lot easier to just return true or false and let coreBOS take care of it, but, if you need it, you have it.

In our case, we will use the javascript "confirm" function to show the message given by the backend and will proceed with the save or not depending on the answer to the dialog. As before, the code can live anywhere inside the application. I put it in modules/Assets/askToProceed.js because I am going to associate this code to the Assets module. The code looks like this:

function askToProceed(edit_type, formName, action, callback, message) {
    var goahead = confirm(message);
    if (goahead) {
        return true; // continue save process
    } else {
        return false; // stops save

Hooks to launch our code

Now that we have the code we have to add links so that coreBOS knows that it must execute it and where to find it. This is can be done with the addLink vtlib function or with the Business Actions module. I am going to do it with Business Actions. Go to the module and create two records that look like this:

Business Action PHP

Business Action Javascript

It cannot be easier. The HEADERSCRIPT will load our javascript code and the PRESAVE will launch our developer widget.

Give it a try

Ask to Proceed

It is really that easy to add functionality to coreBOS while you keep up with the constant development changes we make.

Mass Upload Documents

If you want another example have a look at the new Mass Upload Documents widget we introduced this week.

Enjoying the power of the coreBOS framework.

unsplash-logoPhoto by Markus Spiske on Unsplash

Previous Post Next Post