Customer Portal 2.7: Migration guide

In order to ensure better security, support and sustainability; we migrated the portal's framework from Silex 2.x to Symfony 3.4. Even though we managed to keep a backward compatibility for most of the code, if you made a custom extension for the portal, you most likely will need to rework some parts of it.

Here are the particular cases which require some code rework:

  • Templates
  • UrlMaker classes
  • Bricks
  • Others
Themes and XML structure didn't change, so if your extension sticks to those 2 points, it is already 2.7 compatible!

Templates

Some of the TWIG app properties have changed, check the table below to know what to search & replace:

Search… … replace with
{# itop-portal-base/portal/src/views/…/xxx.html.twig #} {# itop-portal-base/portal/templates/…/xxx.html.twig #}
{% extends 'itop-portal-base/portal/src/views/…/xxx.html.twig' %} {% extends 'itop-portal-base/portal/templates/…/xxx.html.twig' %}
{% include 'itop-portal-base/portal/src/views/…/xxx.html.twig' %} {% include 'itop-portal-base/portal/templates/…/xxx.html.twig' %}
app['combodo.portal.instance.conf'].bricks_ordering.home app['brick_collection'].home_ordering
app['combodo.portal.instance.conf'].bricks_ordering.navigation_menu app['brick_collection'].navigation_menu_ordering
app['combodo.portal.instance.conf'].ui_extensions app['ui_extensions_helper']
app['combodo.portal.instance.conf'].portals app['combodo.current_user.allowed_portals']
app['debug'] app['kernel'].debug

UrlMaker classes

UrlMaker classes are necessary for portals made from scratch (ID different than the standard itop-portal), it allows iTop to build URLs pointing to the right portal (eg. in notifications).

If you have such a portal, you should also have at least one class implementing the iDBObjectURLMaker interface with a lot of PHP code in it (either in a PHP file or a XML snippet). It should contain two parts.

In the following my-portal has to be replaced with your portal ID. Same goes with the MyPortalEditUrlMaker & MyPortalViewUrlMaker classes that should be replaced with your classes name.

The first one are the classes themselves, with a lot of code duplicated from the classes of standard portal. (Not great!)

<?php
 
/**
 * Hyperlinks to the "edition" of the object
 */
class MyPortalEditUrlMaker implements iDBObjectURLMaker
{
    public static function PrepareObjectURL($sClass, $iId, $sMode)
    {
        require_once APPROOT . '/lib/silex/vendor/autoload.php';
        require_once APPROOT . '/env-' . utils::GetCurrentEnvironment() . '/itop-portal-base/portal/src/providers/urlgeneratorserviceprovider.class.inc.php';
        require_once APPROOT . '/env-' . utils::GetCurrentEnvironment() . '/itop-portal-base/portal/src/helpers/urlgeneratorhelper.class.inc.php';
        require_once APPROOT . '/env-' . utils::GetCurrentEnvironment() . '/itop-portal-base/portal/src/providers/scopevalidatorserviceprovider.class.inc.php';
        require_once APPROOT . '/env-' . utils::GetCurrentEnvironment() . '/itop-portal-base/portal/src/helpers/scopevalidatorhelper.class.inc.php';
        require_once APPROOT . '/env-' . utils::GetCurrentEnvironment() . '/itop-portal-base/portal/src/helpers/securityhelper.class.inc.php';
        require_once APPROOT . '/env-' . utils::GetCurrentEnvironment() . '/itop-portal-base/portal/src/helpers/applicationhelper.class.inc.php';
 
        ... 100 lines later 🙈 ...
 
        return $sUrl;
 
    }
 
    public static function MakeObjectURL($sClass, $iId)
    {
        return static::PrepareObjectURL($sClass, $iId, 'edit');
    }
}
 
/**
 * Hyperlinks to the "view" of the object (vs edition)
 *
 */
class MyPortalViewUrlMaker extends MyPortalEditUrlMaker
{
    public static function MakeObjectURL($sClass, $iId)
    {
        return static::PrepareObjectURL($sClass, $iId, 'view');
    }
}

The second part is the registration of thoses classes so iTop can use them when building URLs.

// Default my-portal hyperlink (for notifications) is the edit hyperlink
DBObject::RegisterURLMakerClass('my-portal', 'MyPortalEditUrlMaker');

Only the first part needs to be refactored and good news it has been simplified a lot!

At the beginning of the file, add the new autoloader

<?php
use Combodo\iTop\Portal\UrlMaker\AbstractPortalUrlMaker;
 
require_once APPROOT.'/lib/autoload.php';

Then, change the class implementing iDBObjectURLMaker so it now extends AbstractPortalUrlMaker instead and replace the whole class content with the following. (Yes, you are replacing more than 100 lines with only 1 😁)

class MyPortalEditUrlMaker extends AbstractPortalUrlMaker
{
        const PORTAL_ID = 'my-portal';
}

And that's it! Your file should be way more simple and future-proof.

Here is the complete new file so you can copy / paste:

<?php
 
use Combodo\iTop\Portal\UrlMaker\AbstractPortalUrlMaker;
 
require_once APPROOT.'/lib/autoload.php';
 
/**
 * Hyperlinks to the "edition" of the object (vs view)
 */
class MyPortalEditUrlMaker extends AbstractPortalUrlMaker
{
        const PORTAL_ID = 'my-portal';
}
 
/**
 * Hyperlinks to the "view" of the object (vs edition)
 */
class MyPortalViewUrlMaker extends MyPortalEditUrlMaker
{
        /**
         * @inheritDoc
         */
        public static function MakeObjectURL($sClass, $iId)
        {
                return static::PrepareObjectURL($sClass, $iId, 'view');
        }
 
}
 
// Default portal hyperlink (for notifications) is the edit hyperlink
DBObject::RegisterURLMakerClass('my-portal', 'MyPortalEditUrlMaker');

Bricks

Brick router

Declaring routes for your brick no longer goes through extending the AbstractRouter class. With Symfony, routes should be declared in a YAML file, but to simplify the migration (on both your side and ours) we made a bridge to easily register them.

Mind to replace MyCompany, MyExtension, MyBrickController, my-brick, p_my_brick with your namings.

Currently your router should look like this, a class with a static $aRoutes variable containing an array of routes:

iTop 2.6 and earlier
<?php
 
namespace MyCompany\iTop\MyExtension\Portal\Router;
 
use Combodo\iTop\Portal\Router\AbstractRouter;
 
class ApprovalBrickRouter extends AbstractRouter
{
  static $aRoutes = array(
    array('pattern' => '/my-brick/{sBrickId}',
      'callback' => 'MyCompany\\iTop\\MyExtension\\Portal\\Controller\\MyBrickController::DisplayAction',
      'bind' => 'p_my_brick'
    ),
  );
}

To migrate, simply:

  • Include the bridge class with the use Combodo\iTop\Portal\Routing\ItopExtensionsExtraRoutes;
  • Call it with your routes array
  • Remove the former class

You should have something like this:

iTop 2.7 and later
<?php
 
use Combodo\iTop\Portal\Routing\ItopExtensionsExtraRoutes;
 
ItopExtensionsExtraRoutes::AddRoutes(
  array(
    array('pattern' => '/my-brick/{sBrickId}',
      'callback' => 'MyCompany\\iTop\\MyExtension\\Portal\\Controller\\MyBrickController::DisplayAction',
      'bind' => 'p_my_brick'
    ),
  )
);

Notes:

  • You must empty the cache for the changes to take effect!</note>
  • You can enable the web profiler in order to ease the debug of your routes

Brick controller

One of the most important change is the removal of the Silex\Application $oApp variable which was encapsulating main services and parameters such as:

  • The URL Generator
  • The Security Helper
  • The Scope Validator
  • The portal instance configuration
  • The object forms

With Symfony the Silex application is replaced by the service container which gives you access to most of them. It is accessible directly from the controller if you extended the BrickController class so that the first thing you should check.

Mind to replace MyCompany, MyExtension, MyBrickController, MyAction with your namings.
Brick controller example
<?php
 
namespace MyCompany\iTop\MyExtension\Portal\Controller;
 
use Combodo\iTop\Portal\Controller\BrickController;
 
class MyBrickController extends BrickController
{
    ...
}

Now let's see what has to be rework.

Actions prototype

Remove the $oApp from your actions and internal methods prototypes as it is no longer available.

iTop 2.6 and earlier
...
class MyBrickController extends BrickController
{
    public function MyAction(Request $oRequest, Application $oApp, $sBrickId, $sFoo = null)
    {
        ...

Should become

iTop 2.7 and later
...
class MyBrickController extends BrickController
{
    public function MyAction(Request $oRequest, $sBrickId, $sFoo = null)
    {
        ...

Parameters

Search… … replace with
$oApp['combodo.portal.instance.conf'] $this→getParameter('combodo.portal.instance.conf')
$oApp['combodo.current_environment'] $this→getParameter('combodo.current_environment');
$oApp['combodo.absolute_url'] $this→getParameter('combodo.absolute_url');
$oApp['combodo.modules.absolute_url'] $this→getParameter('combodo.modules.absolute_url');
$oApp['combodo.modules.absolute_path'] $this→getParameter('combodo.modules.absolute_path');
$oApp['combodo.portal.base.absolute_url'] $this→getParameter('combodo.portal.base.absolute_url');
$oApp['combodo.portal.base.absolute_path'] $this→getParameter('combodo.portal.base.absolute_path');
$oApp['combodo.portal.instance.absolute_url'] $this→getParameter('combodo.portal.instance.absolute_url');
$oApp['combodo.portal.instance.id'] $this→getParameter('combodo.portal.instance.id');

Services

Change calls to the services provided by the portal, check the table below to know what to search & replace:

Search… … replace with
$oApp['request_manipulator'] $this→get('request_manipulator')
$oApp['scope_validator'] $this→get('scope_validator')
$oApp['security_helper'] $this→get('security_helper')
$oApp['context_manipulator'] $this→get('context_manipulator')
$oApp['lifecycle_validator'] $this→get('lifecycle_validator')
$oApp['url_generator'] $this→get('url_generator')
$oApp['twig']→render(…) $this→render(…)
ApplicationHelper::GetLoadedBrickFromId($oApp, $sBrickId) $this→get('brick_collection')→getBrickById($sBrickId)
SecurityHelper::IsActionAllowed($oApp, …) $this→get('security_helper')→IsActionAllowed(…)
SecurityHelper::IsStimulusAllowed($oApp, … $this→get('security_helper')→IsStimulusAllowed(…)
ApplicationHelper::GetLoadedFormFromClass(Application $oApp, …) ApplicationHelper::GetLoadedFormFromClass($this→getParameter('combodo.portal.instance.conf')['forms'], …)

Misc.

Some other calls must be changed as well, check the table below to know what to search & replace:

Search… … replace with
$oApp→json(…) new JsonResponse(…)
$oApp→abort(…) throw new \Symfony\Component\HttpKernel\Exception\HttpException(…)
Make sure the HttpException is not catched by your code as it must be propagated to the Symfony framework in order to work properly.

Brick templates

Templates used in custom bricks need the same rework as the global templates. See the Templates section above for all the details.

Depreciations

ObjectFormHandlerHelper::HandleForm()

This method returns an array with many information about the form being manipulated, among them the submit_callback and the cancel_callback URLs. They have been deprecated in iTop 2.7 and will be removed in iTop 3.0. Use submit_rule and cancel_rule instead, which provide more structured information.

iTop 2.6 and earlier
<?php
    $aFormData = $oObjectFormHandlerHelper->HandleForm(...);
    ...
    $sSubmitUrl = $aFormData['submit_callback'];
    $sCancelUrl = $aFormData['cancel_callback'];
iTop 2.7 and later
<?php
    $aFormData = $oObjectFormHandlerHelper->HandleForm(...);
    ...
    $sSubmitUrl = $aFormData['submit_rule']['url'];
    $sCancelUrl = $aFormData['cancel_rule']['url'];

portal_form_handler.js

The Javascript widget has evolved, the submit_url and cancel_url options have been deprecated in iTop 2.7 and will be removed in iTop 3.0. Use the submit_rule and cancel_rule options instead hich provide more structured information.

iTop 2.6 and earlier
$('.foo').portal_form_handler({
    ...
    submit_url: "https://someurl",
    cancel_url: "https://anotherurl",
    ...
});
iTop 2.7 and earlier
$('.foo').portal_form_handler({
    ...
    submit_rule: {
        category: "redirect",
        url: "https://someurl",
        modal: true,
    },
    cancel_rule: {
        category: "redirect",
        url: "https://anotherurl",
        modal: true,
    }
    ...
});

API removals

ObjectController::HandleForm()

This method has been extracted in a dedicated service, use ObjectFormHandlerHelper::HandleForm() instead.
In the following example, we assume that you are in a portal controller, which gives direct access to the services container.

iTop 2.6 and earlier
<?php
 
namespace MyCompany\iTop\MyExtension\Portal\Controller;
 
use Combodo\iTop\Portal\Controller\BrickController;
 
class MyBrickController extends BrickController
{
    ...
 
    $aData['form'] = ObjectController::HandleForm(...);
iTop 2.7 and later
<?php
 
namespace MyCompany\iTop\MyExtension\Portal\Controller;
 
use Combodo\iTop\Portal\Controller\BrickController;
 
class MyBrickController extends BrickController
{
    ...
 
    /** @var \Combodo\iTop\Portal\Helper\ObjectFormHandlerHelper $oObjectFormHandler */
    $oObjectFormHandler = $this->get('object_form_handler');
    ...
    $aData['form'] = $oObjectFormHandler->HandleForm(...);
latest/install/portal_270_migration.txt · Last modified: 2024/06/20 17:28 (external edit)
Back to top
Contact us