Adding block types to your extension

In order to add a block type to your extension and be able to use it you need at least a block definition file and a controller. Depending on what your block is supposed to do you might also need one or more Twig templates as well as CSS and JS files.

The first thing you need to do is declare you block in the module.config.php file of your extension like in the following example:

'blocksDefinition' => array(
        'form' => array(
            'controller' => 'Survey\\Blocks\\Controller\\Forms',
            'definitionFile' => realpath(__DIR__ . "/blocks/") . '/form.json'
        )
 )

 

 

In this example we define a form block type in the « blockDefinitions » array. « form » is the technical name of the block type and will be used in the definition file as bType in order to ensure controller resolution.

 

Block definition file

 

This file is required in order to make your block type usable in the Back Office. It contains the title and description of your block, the category it belongs to and most importantly the initial configuration options as well as the definitions of all the configuration fields used to edit those options. It is this definition file that will allow users to add blocks of this type to pages or page masks and configure them. It will also determine the controller that those blocks resolve to during page rendering.

Let us examine the following example:

{
       "type":"#ext-gen1179#",
       "description":"#ext-gen1180#",
       "configBasique":{
             "title":"#ext-gen1179#",
             "bType":"form",
             "flex":1,
             "champsConfig":{
                    "simple":[
                           {
                                  "categorie":"#ext-gen1063#",
                                  "champs":[
                                        {
                                               "type":"Rubedo.view.FormPickerField",
                                               "config":{
                                                      "fieldLabel":"#ext-gen1179#",
                                                      "name":"formId"
                                               }
                                        },
                                        {
                                               "type":"Ext.form.field.ComboBox",
                                               "isAutoStored":true,
                                               "autoStoreData":[
                                                      {
                                                            "value":0,
                                                            "label":"#ext-gen1185#"
                                                      },
                                                      {
                                                            "value":1,
                                                            "label":"#ext-gen1186#"
                                                      }
                                               ],
                                               "config":{
                                                      "fieldLabel":"#ext-gen1184#",
                                                      "name":"progression",
                                                      "editable":false,
                                                      "forceSelection":true,
                                                      "queryMode":"local",
                                                      "value":0
                                               }
                                        }
                                  ]
                           }
                    ],
                    "avance":[
 
                    ]
             },
             "configBloc":{
                    "progression":0
             }
       },
       "category":"#ext-gen1150#",
       "bType":"form",
       "id":"514ae0c5c0e051ac0d000002"
}

 

The above example is the block definition file for the from block type. As you can see, block definitions are written in JSON.

One of the first thing you might notice is a lot of properties have values like "#ext-gen1179#". This is because they are labels and are thus localized. These codes are replaced by localized labels during display in the Back Office. Although doing this is necessary if you want your block to have labels for different interface languages it is not vital to the block definition: you can just assign readable labels directly to the properties if you do not want address localization at this time. This is recommended when you first write your block definition as you might want to just get it to work before localizing it later on. For more information on how to localize your block labels using codes and localization files please refer to the localization subsection of the Back Office extension section.

At the root of this object we have several properties:

  • “type”, “description”, and “category” are used to display your block in the block insertion grid. They represent the tile of the block, the detail description and the category it will be grouped into. You can use the name of an existing category or one that has not yet been used in order to create a new one.
  • “id” is the unique MongoId of that block type
  • “bType” is the technical name of the block. It must be same as the one used to declare the block in the blockDefiniitions of module.config.php in order for controller resolution to function properly.
  • “configBasique” contains all that is necessary to create a block of this type and configure it. Let us detail the contents of this object:
    • “title” is the tile given to a new block of this type upon creation. In this case it is the same as “type”, the title used for the insertion grid, but this is not mandatory. You can change this to any text you want such as “New form block”.
    • “bType” is the same as the one in the root object and must be identical. This is duplicated in order to increase block -> block type resolution in the Back Office during page and page mask structure rendering.
    • “flex” is a proportion modifier for Back Office rendering. Change this to any strictly positive float value in order to make the block look bigger or smaller than the others in the page and page mask structure rendering.
    • “configBloc” defines the default configuration of your block. You do not need to define all of the configuration options here but if you want any of them to have a default value upon block creation this is the place to do that.
    • “champsConfig” defines two things at once : configuration options and the fields used to edit them in the Back Office. Indeed, defining a configuration field with the name of a configuration option as the “name” property of that field means a value can be assigned to that option using that field. Setting the “name” property of any field to any value thus creates a new possible configuration option. This does not mean that option will be defined when you read a blocks config in the controller! Only options that have been set using the fields or have a default value in “configBloc” will be defined so if any of your configuration options are mandatory don’t forget to define them in “configBloc” with the appropriate default values. These default values must be of the correct type regarding the associated field.

Let us take a closer look at the field definitions. They are organized in two sections (“simple” and “avance”) wich map to the “Simple” and “Advanced” tabs in the block configurator in the properties panel of the page or page mask editor. These sections contain field sets who have a title property called “categorie” and the field definitions themselves are in the “champs” property of each field set. This structure is mandatory in order to ensure standardized rendering of the block configurator.

When you take a look at what the block configurator looks like for this block in the Back office you might notice many more fields in the “Simple” and “Advanced” tabs than the ones you defined in “champsConfig”. Those are generic fields common to all blocks (title, responsive design options, language filters, etc.) and automatically rendered without any definition required in the block definition file.

A field definition requires a “type” and a “config” property. “type” is the class name of the Ext JS 4.2.1 field class you want to use and “config” contains configuration options that apply to that class. For example, if you use a number field, the “type” would be “Ext.form.field.Number” and “config” might contain something like “maxValue”:40 alongside the mandatory “name” property. A field definition without a valid “name” property in “config” will not only be useless as it configures no value but may also cause errors. Some field definitions may have other properties due to specific requirements. The ComboBox field in the above example has two new properties. They are used to tell the Back Office block configurator builder to create a store for this ComboBox using the values defined in the second property. This is useful for defining the possible values for this field directly in the field definitions. For more information on the field classes provided in Ext JS 4.2.1 and their config options please visit http://docs.sencha.com/extjs/4.2.1/ .

You may also notice that the first field is not from the default Ext JS release. It is a field specific to Rubedo and defined in the Back Office itself. This is the case of all fields whose class name begins with “Rubedo.view.”. This particular field called “Rubedo.view.FormPickerField” renders as a ComboBox which allows a user to choose a form and the stores the id of that form in the “formId” property of the block. There are several custom fields built into the Back Office of Rubedo and we will list the most important ones and their specific config options in the following subsection. These fields are all used in one or more of the blocks natively built into Rubedo so feel free to take a look at those block definitions and their rendering to see them in action. Please note that you can create your own custom fields and integrate them into Rubedo by extending the Back Office. You can then use the by their class name in field definitions just as you would use any other field.

 

Field types specific to Rubedo

 

Rubedo.view.CTCField

Renders as a ComboBox allowing the user to choose one or more content types. Stores an array of content type ids.

Rubedo.view.ImagePickerField

Despite its name this field can be used to choose any digital asset from Rubedos digital asset manager. The “smallMode” property must always be set to true in order for the rendered interface to fit in the block configurator (this field is not only used in blocks and has other rendering possibilities). Use the “allowedFileType” property to define what digital asset can be selected depending on its main file type (possible options are “Image”, “Audio”, “Video” or “Document”). You can also use the “allowedDAMTypes” property to directly assign an array of media type ids in order to restrict the selection to those types. Do not combine these two properties.

This field renders as a media selector wityh a prewiew and buttons that allow the user to select a digital asset via a digital asset picker window. It stores the id of the selected digital asset.

Ext.ux.TreePicker

This field belonging to the Ext user extension has been modified to adopt a specific behavior when used in blocks. It allows auser to select a page from the site this block is on via the page or page mask and stores the id of that page.

Rubedo.view.queryBuilderField

This field is used to create and edit database queries. It is most useful for blocks that use lists of contents as a data source. It renders as a ComboBox which allows selection of existing advanced queries as well as creation of manual, simple or advanced queries. It can also edit any type of query. This field comes with a complete query builder wizard for each type of query. It stores the id of the selected query.

Rubedo.view.MQField

Same as the previous one except its queries concern digital assets instead of contents and can only be simple queries.

Rubedo.view.DCEField

This field allows users to select, create or edit a content. It comes with a content picker as well as a built-in complete editor. Its behavior can be restricted by setting the “addOnly” or “chooseOnly” options to true thus making the field unable to choose existing contents or to create new ones. When “addOnly” is set to true, you can select the content type to be reated using “allowedCT” set to the value of the “CTType” property of that content type. For example use “richText” as value for “allowedCT” to obtain the same behavior as in the rich text block. It stores the id of the selected content.

Rubedo.view.ESQfield

This field allows the creation and editing of queries based on Elastic Search. It is useful for predefined facet input on all blocks using this search engine. It has its own query builder window but unlike the Rubedo.view.queryBuilderField it does not use a distinct collection and thus stores the query directly as a JSON string instead of an id. It has several interesting options such as “queryMode”, “geoQueryMode” and “damQueryMode”. Only one of these can be set as true and will determine the query type: query on all contents, geolocalized contents only or digital assets only.

Rubedo.view.MailingListPickerField

Allows the selection of a mailing list using a ComboBox. Stores the id of the selected mailing list.

Controller

 

Now that the new block type is defined and usable in the Back Office, it requires a controller in order to be resolved and processed during page rendering.

Declaration and file structure

 

We recommend placing such a controller in “YourExtensionFolder/src/YourExtensionName/Blocks/Controller/YourBlockController” and declaring it in module.config.php like in the following example:

'controllers' => array(
        'invokables' => array(
            'Survey\\Blocks\\Controller\\Forms' => 'Survey\\Blocks\\Controller\\FormsController'
        )
    )

Controller code

 

First of all, you need to properly namespace your controller to “YourExtensionName\Blocks\Controller” and declare required classes with “use” statements. Your controller will extend “Rubedo\Blocks\Controller\AbstractController” (unless you are extending one of your own block controllers for some reason) so you need to “use” that class. You might also want to use “Rubedo\Services\Manager” if you will be using any Rubedo service whether to read or manipulate collection data or rendering any templates.

The rest of the controller code is entirely up to you to write depending on what you want your block to do. We will however provide guidance for some frequently needed code segments through the following example:

class TextController extends AbstractController
{


    public function indexAction ()
    {
        $blockConfig = $this->params()->fromQuery('block-config', array());
        $content = array();
        if ($blockConfig["contentId"]) {
            $content = Manager::getService('Contents')->findById($blockConfig["contentId"], true, false);
        }
        $output = $this->params()->fromQuery();
        $output['contentId'] = $blockConfig["contentId"];
        $output['text'] = $content["fields"]["body"];
        $template = Manager::getService('FrontOfficeTemplates')->getFileThemePath("blocks/text.html.twig");
       
        $css = array();
        $js = array();
        return $this->_sendResponse($output, $template, $css, $js);
    }
}

 

The above example is the TextController class which is the associated controller of the simple text block, a simple block that just displays some text using a content of the simpleText content type. It covers the basics of block controllers in its index action. First of all, we begin by recovering the ‘block-config’ parameter which is an array containing all the defined configuration options for this block and their values. These are the same options as those declared in the block definition file for this block type. Once the right content is retrieved via the “Contents” service, an output array is created for template rendering and initialized using all received parameters in order to ensure parameter propagation. After adding several properties to the output array that are significant to this particular template, the template path is retrieved and a response is sent. This will automatically render the template using the properties in the output array and attach any JS or CSS if defined.