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
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.
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.
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.
- 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(…) |
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::HandleFor🤦) 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(...);