diff --git a/datamodels/2.x/itop-portal-base/portal/config/services.yaml b/datamodels/2.x/itop-portal-base/portal/config/services.yaml index 92d23ad250..cd876e9006 100644 --- a/datamodels/2.x/itop-portal-base/portal/config/services.yaml +++ b/datamodels/2.x/itop-portal-base/portal/config/services.yaml @@ -121,6 +121,9 @@ services: context_manipulator: alias: Combodo\iTop\Portal\Helper\ContextManipulatorHelper public: true + navigation_rule_helper: + alias: Combodo\iTop\Portal\Helper\NavigationRuleHelper + public: true lifecycle_validator: alias: Combodo\iTop\Portal\Helper\LifecycleValidatorHelper public: true diff --git a/datamodels/2.x/itop-portal-base/portal/public/js/portal_form_handler.js b/datamodels/2.x/itop-portal-base/portal/public/js/portal_form_handler.js index e34364c5b2..8dca1e9fd9 100644 --- a/datamodels/2.x/itop-portal-base/portal/public/js/portal_form_handler.js +++ b/datamodels/2.x/itop-portal-base/portal/public/js/portal_form_handler.js @@ -26,21 +26,35 @@ $(function() $.widget( 'itop.portal_form_handler', $.itop.form_handler, { options: { - submit_url: null, - cancel_url: null + submit_url: null, // Deprecated. We kept those properties to preserve compatibility with extensions + cancel_url: null, // but you should start using xxx_rule.url as soon as possible. + submit_rule: { + category: 'redirect', + url: null, + modal: false, + }, + cancel_rule: { + category: 'close', + url: null, + modal: false, + }, }, // the constructor _create: function() { - this.element - .addClass('portal_form_handler'); + this.element.addClass('portal_form_handler'); // Safe check for options - if(this.options.submit_url === "") - this.options.submit_url = null; - if(this.options.cancel_url === "") - this.options.cancel_url = null; + if(this.options.submit_rule.url === '') + this.options.submit_rule.url = null; + if(this.options.cancel_rule.url === '') + this.options.cancel_rule.url = null; + // Deprecated, see this.options.submit_url + if((this.options.submit_url !== null) && (this.options.submit_url !== '')) + this.options.submit_rule.url = this.options.submit_url; + if((this.options.cancel_url !== null) && (this.options.cancel_url !== '')) + this.options.cancel_rule.url = this.options.cancel_url; this._super(); }, @@ -160,69 +174,34 @@ $(function() // If everything is okay, we close the form and reload it. if(oValidation.valid) { - + var bRedirectInModal = me.options.submit_rule.modal; + var sRedirectUrl = me.options.submit_rule.url; + $('body').trigger('unregister_blocker.portal.itop', {'sBlockerId': me.element.attr('id')}); - - if(me.options.is_modal) - { - me.element.closest('.modal').modal('hide'); - } // Checking if we have to redirect to another page + // Typically this happens when applying a stimulus, we redirect to transition form if(oValidation.redirection !== undefined) { var oRedirection = oValidation.redirection; - var bRedirectionAjax = (oRedirection.ajax !== undefined) ? oRedirection.ajax : false; - var sUrl = null; - - // URL priority order : - // redirection.url > me.option.submit_url > redirection.alternative_url - if(oRedirection.url !== undefined) + if(oRedirection.modal !== undefined) { - sUrl = oRedirection.url; + bRedirectInModal = oRedirection.modal; } - else if(me.options.submit_url !== null) - { - sUrl = me.options.submit_url; - } - else if(oRedirection.alternative_url !== undefined) - { - sUrl = oRedirection.alternative_url; - } - - if(sUrl !== null) + if(oRedirection.url !== undefined) { - if(bRedirectionAjax) - { - // Creating a new modal - CombodoPortalToolbox.OpenModal({ - content: { - endpoint: sUrl, - data: { - // Passing form manager data to the next page, just in case it needs it (eg. when applying stimulus) - formmanager_class: me.options.formmanager_class, - formmanager_data: JSON.stringify(me.options.formmanager_data) - }, - }, - }); - } - else - { - // Showing loader while redirecting, otherwise user tend to click somewhere in the page. - // Note : We use a timeout because .always() is called right after here and will hide the loader - setTimeout(function(){ me._disableFormBeforeLoading(); }, 50); - // Redirecting after a few ms so the user can see what happend - setTimeout(function() { location.href = sUrl; }, 400); - } + sRedirectUrl = oRedirection.url; } + me._applyRedirectRule(sRedirectUrl, bRedirectInModal); } - else if(me.options.submit_url !== null) + else if(me.options.submit_rule.category === 'redirect') { - // Showing loader while redirecting, otherwise user tend to click somewhere in the page. - // Note : We use a timeout because .always() is called right after here and will hide the loader - setTimeout(function(){ me._disableFormBeforeLoading(); }, 50); - // Redirecting after a few ms so the user can see what happend - setTimeout(function() { location.href = me.options.submit_url; }, 400); + me._applyRedirectRule(sRedirectUrl, bRedirectInModal); + } + // Close rule only needs to be applied to non modal forms (modal is always closed on submit) + else if(me.options.submit_rule.category === 'close') + { + me._applyCloseRule(); } } } @@ -269,38 +248,31 @@ $(function() }, function(oData) { - if(me.options.cancel_url !== null) + if(me.options.cancel_rule.category === 'redirect') + { + me._applyRedirectRule(me.options.cancel_rule.url, me.options.cancel_rule.modal); + } + else if(me.options.cancel_rule.category === 'close') { - location.href = me.options.cancel_url; + me._applyCloseRule(); } } ) - .always(function(){ - // Close the modal only if fields had to be cancelled - if(me.options.is_modal) - { - me.element.closest('.modal').modal('hide'); - } + .always(function() + { me._enableFormAfterLoading(); }); } // Otherwise we can close the modal immediately else { - if(me.options.cancel_url !== null) + if(me.options.cancel_rule.category === 'redirect') { - location.href = me.options.cancel_url; + me._applyRedirectRule(me.options.cancel_rule.url, me.options.cancel_rule.modal); } - else + else if(me.options.cancel_rule.category === 'close') { - if(me.options.is_modal) - { - me.element.closest('.modal').modal('hide'); - } - else - { - location.reload(); - } + me._applyCloseRule(); } } }, @@ -343,6 +315,53 @@ $(function() { $('#page_overlay').fadeOut(200); }, + _applyRedirectRule: function(sRedirectUrl, bRedirectInModal) + { + var me = this; + + // Always close current modal + if(this.options.is_modal) + { + this.element.closest('.modal').modal('hide'); + } + + if(sRedirectUrl !== null) + { + if(bRedirectInModal === true) + { + // Creating a new modal + CombodoPortalToolbox.OpenModal({ + content: { + endpoint: sRedirectUrl, + data: { + // Passing form manager data to the next page, just in case it needs it (eg. when applying stimulus) + formmanager_class: this.options.formmanager_class, + formmanager_data: JSON.stringify(this.options.formmanager_data) + }, + }, + }); + } + else + { + // Showing loader while redirecting, otherwise user tend to click somewhere in the page. + // Note: We use a timeout because .always() is called right after here and will hide the loader + setTimeout(function(){ me._disableFormBeforeLoading(); }, 50); + // Redirecting after a few ms so the user can see what happend + setTimeout(function() { location.href = sRedirectUrl; }, 400); + } + } + }, + _applyCloseRule: function() + { + if(this.options.is_modal) + { + this.element.closest('.modal').modal('hide'); + } + else + { + window.close(); + } + }, submit: function(oEvent) { this._onSubmitClick(oEvent); diff --git a/datamodels/2.x/itop-portal-base/portal/src/DependencyInjection/SilexCompatBootstrap/PortalXmlConfiguration/Forms.php b/datamodels/2.x/itop-portal-base/portal/src/DependencyInjection/SilexCompatBootstrap/PortalXmlConfiguration/Forms.php index 7427d6f1be..3ee2da9359 100644 --- a/datamodels/2.x/itop-portal-base/portal/src/DependencyInjection/SilexCompatBootstrap/PortalXmlConfiguration/Forms.php +++ b/datamodels/2.x/itop-portal-base/portal/src/DependencyInjection/SilexCompatBootstrap/PortalXmlConfiguration/Forms.php @@ -15,12 +15,11 @@ * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License - * - * */ namespace Combodo\iTop\Portal\DependencyInjection\SilexCompatBootstrap\PortalXmlConfiguration; +use Combodo\iTop\Portal\Helper\NavigationRuleHelper; use Symfony\Component\DependencyInjection\Container; use DOMFormatException; use Exception; @@ -66,7 +65,19 @@ public function Process(Container $oContainer) $aFormProperties = array( 'display_mode' => ApplicationHelper::FORM_DEFAULT_DISPLAY_MODE, 'always_show_submit' => ApplicationHelper::FORM_DEFAULT_ALWAYS_SHOW_SUBMIT, + 'navigation_rules' => array( + 'submit' => array( + NavigationRuleHelper::ENUM_ORIGIN_PAGE => null, + NavigationRuleHelper::ENUM_ORIGIN_MODAL => null, + ), + 'cancel' => array( + NavigationRuleHelper::ENUM_ORIGIN_PAGE => null, + NavigationRuleHelper::ENUM_ORIGIN_MODAL => null, + ), + ), ); + + $aAllowedNavRulesButtonCodes = array_keys($aFormProperties['navigation_rules']); if ($oFormNode->GetOptionalElement('properties') !== null) { /** @var \MFElement $oPropertyNode */ @@ -77,9 +88,48 @@ public function Process(Container $oContainer) case 'display_mode': $aFormProperties['display_mode'] = $oPropertyNode->GetText(ApplicationHelper::FORM_DEFAULT_DISPLAY_MODE); break; + case 'always_show_submit': $aFormProperties['always_show_submit'] = ($oPropertyNode->GetText('false') === 'true') ? true : false; break; + + case 'navigation_rules': + /** @var \MFElement $oNavRuleButtonNode */ + foreach($oPropertyNode->childNodes as $oNavRuleButtonNode) + { + $sNavRuleButtonCode = $oNavRuleButtonNode->nodeName; + if(!in_array($sNavRuleButtonCode, $aAllowedNavRulesButtonCodes)) + { + throw new DOMFormatException('navigation_rules tag must only contain '.implode('|', $aAllowedNavRulesButtonCodes).' tags, "'.$sNavRuleButtonCode.'" given.', null, null, $oPropertyNode); + } + + /** @var \MFElement $oNavRuleOriginNode */ + foreach($oNavRuleButtonNode->childNodes as $oNavRuleOriginNode) + { + $sNavRuleOrigin = $oNavRuleOriginNode->nodeName; + if(!in_array($sNavRuleOrigin, NavigationRuleHelper::GetAllowedOrigins())) + { + throw new DOMFormatException($sNavRuleButtonCode. ' tag must only contain '.implode('|', NavigationRuleHelper::GetAllowedOrigins()).' tags, "'.$sNavRuleOrigin.'" given.', null, null, $oPropertyNode); + } + + $sNavRuleId = $oNavRuleOriginNode->GetText(); + // Note: We don't check is rule exists as it would introduce a dependency to the NavigationRuleHelper service. + // Maybe we will consider it later. + if(empty($sNavRuleId)) + { + throw new DOMFormatException($sNavRuleButtonCode.' tag cannot be empty.', null, null, $oPropertyNode); + } + + $aFormProperties['navigation_rules'][$sNavRuleButtonCode][$sNavRuleOrigin] = $sNavRuleId; + } + + // Set modal rule as the same as default is not present. + // We preset it so we don't have to make checks elsewhere in the code when using it. + if(empty($aFormProperties['navigation_rules'][$sNavRuleButtonCode][NavigationRuleHelper::ENUM_ORIGIN_MODAL])) + { + $aFormProperties['navigation_rules'][$sNavRuleButtonCode][NavigationRuleHelper::ENUM_ORIGIN_MODAL] = $aFormProperties['navigation_rules'][$sNavRuleButtonCode][NavigationRuleHelper::ENUM_ORIGIN_PAGE]; + } + } } } } @@ -98,7 +148,7 @@ public function Process(Container $oContainer) } else { - throw new DOMFormatException('Mode tag must have an id attribute', null, null, + throw new DOMFormatException('mode tag must have an id attribute', null, null, $oFormNode); } diff --git a/datamodels/2.x/itop-portal-base/portal/src/Helper/ApplicationHelper.php b/datamodels/2.x/itop-portal-base/portal/src/Helper/ApplicationHelper.php index 7d291b001f..5b6e0fba3a 100644 --- a/datamodels/2.x/itop-portal-base/portal/src/Helper/ApplicationHelper.php +++ b/datamodels/2.x/itop-portal-base/portal/src/Helper/ApplicationHelper.php @@ -16,8 +16,6 @@ * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License - * - * */ namespace Combodo\iTop\Portal\Helper; @@ -374,6 +372,10 @@ protected static function GenerateDefaultFormForClass($sClass, $bAddLinksets = f 'properties' => array( 'display_mode' => static::FORM_DEFAULT_DISPLAY_MODE, 'always_show_submit' => static::FORM_DEFAULT_ALWAYS_SHOW_SUBMIT, + 'navigation_rules' => array( + 'submit' => null, + 'cancel' => null, + ), ), 'fields' => array(), 'layout' => array( diff --git a/datamodels/2.x/itop-portal-base/portal/src/Helper/NavigationRuleHelper.php b/datamodels/2.x/itop-portal-base/portal/src/Helper/NavigationRuleHelper.php new file mode 100644 index 0000000000..4715600cd7 --- /dev/null +++ b/datamodels/2.x/itop-portal-base/portal/src/Helper/NavigationRuleHelper.php @@ -0,0 +1,638 @@ + + * @since 2.7.0 + * @package Combodo\iTop\Portal\Helper + */ +class NavigationRuleHelper +{ + // Available point of origin for the navigation + const ENUM_ORIGIN_PAGE = 'default'; + const ENUM_ORIGIN_MODAL = 'modal'; + + // Available rule categories (of rule types) + /** @var string ENUM_RULE_CAT_CLOSE (eg. close modal/window) */ + const ENUM_RULE_CAT_CLOSE = 'close'; + /** @var string ENUM_RULE_CAT_REDIRECT (eg. go-to-homepage, go-to-object, go-to-brick, ...) */ + const ENUM_RULE_CAT_REDIRECT = 'redirect'; + + // Available rule types + /** @var string ENUM_RULE_CLOSE */ + const ENUM_RULE_CLOSE = 'close'; + /** @var string ENUM_RULE_GO_TO_HOMEPAGE */ + const ENUM_RULE_GO_TO_HOMEPAGE = 'go-to-homepage'; + /** @var string ENUM_RULE_GO_TO_OBJECT */ + const ENUM_RULE_GO_TO_OBJECT = 'go-to-object'; + /** @var string ENUM_RULE_GO_TO_BRICK */ + const ENUM_RULE_GO_TO_BRICK = 'go-to-brick'; + /** @var string ENUM_RULE_GO_TO_MANAGE_BRICK */ + const ENUM_RULE_GO_TO_MANAGE_BRICK = 'go-to-manage-brick'; + /** @var string ENUM_RULE_GO_TO_BROWSE_BRICK */ + const ENUM_RULE_GO_TO_BROWSE_BRICK = 'go-to-browse-brick'; + // - Defaults + /** @var string DEFAULT_RULE_SUBMIT_PAGE */ + const DEFAULT_RULE_SUBMIT_PAGE = self::ENUM_RULE_GO_TO_OBJECT; + /** @var string DEFAULT_RULE_SUBMIT_MODAL */ + const DEFAULT_RULE_SUBMIT_MODAL = self::DEFAULT_RULE_SUBMIT_PAGE; + /** @var string DEFAULT_RULE_CANCEL_PAGE */ + const DEFAULT_RULE_CANCEL_PAGE = self::ENUM_RULE_CLOSE; + /** @var string DEFAULT_RULE_CANCEL_MODAL */ + const DEFAULT_RULE_CANCEL_MODAL = self::DEFAULT_RULE_CANCEL_PAGE; + + // Rule go-to-object properties + /** @var string DEFAULT_RULE_GO_TO_OBJECT_PROP_MODE */ + const DEFAULT_RULE_GO_TO_OBJECT_PROP_MODE = ObjectFormHandlerHelper::ENUM_MODE_VIEW; + + /** @var string ENUM_RULE_GO_TO_OBJECT_PROP_OPENING_TARGET_MODAL */ + const ENUM_RULE_GO_TO_OBJECT_PROP_OPENING_TARGET_MODAL = 'modal'; + /** @var string ENUM_RULE_GO_TO_OBJECT_PROP_OPENING_TARGET_PAGE */ + const ENUM_RULE_GO_TO_OBJECT_PROP_OPENING_TARGET_PAGE = 'page'; + /** @var string ENUM_RULE_GO_TO_OBJECT_PROP_OPENING_TARGET_CURRENT */ + const ENUM_RULE_GO_TO_OBJECT_PROP_OPENING_TARGET_CURRENT = 'current'; + /** @var string DEFAULT_RULE_GO_TO_OBJECT_PROP_OPENING_TARGET */ + const DEFAULT_RULE_GO_TO_OBJECT_PROP_OPENING_TARGET = self::ENUM_RULE_GO_TO_OBJECT_PROP_OPENING_TARGET_MODAL; + + // Rule go-to-brick properties + // TODO + + /** @var array $aRules */ + protected $aRules; + /** @var \Symfony\Component\Routing\RouterInterface */ + private $oRouter; + /** @var \Combodo\iTop\Portal\Brick\BrickCollection */ + private $oBrickCollection; + /** @var \Combodo\iTop\Portal\Helper\ScopeValidatorHelper */ + private $oScopeValidator; + + /** + * NavigationRuleHelper constructor. + * + * @param \ModuleDesign $oModuleDesign + * @param \Symfony\Component\Routing\RouterInterface $oRouter + * @param \Combodo\iTop\Portal\Brick\BrickCollection $oBrickCollection + * @param \Combodo\iTop\Portal\Helper\ScopeValidatorHelper $oScopeValidator + * + * @throws \DOMFormatException + */ + public function __construct( + ModuleDesign $oModuleDesign, RouterInterface $oRouter, BrickCollection $oBrickCollection, ScopeValidatorHelper $oScopeValidator + ) { + $this->aRules = array(); + $this->oRouter = $oRouter; + $this->oBrickCollection = $oBrickCollection; + + $this->Init($oModuleDesign->GetNodes('/module_design/navigation_rules/navigation_rule')); + $this->oScopeValidator = $oScopeValidator; + } + + /** + * Initializes the NavigationRuleHelper by caching rules in memory. + * + * @param \DOMNodeList $oNodes + * + * @throws \Exception + * @throws \DOMFormatException + */ + public function Init(DOMNodeList $oNodes) + { + $this->aRules = array(); + + // Iterating over the navigation_rule nodes + /** @var \Combodo\iTop\DesignElement $oRuleNode */ + foreach ($oNodes as $oRuleNode) + { + // Checking node name + if ($oRuleNode->nodeName !== 'navigation_rule') + { + continue; + } + + // Retrieving mandatory attributes + // - ID + $sRuleId = $oRuleNode->getAttribute('id'); + if ($sRuleId === '') + { + throw new DOMFormatException('Rule tag must have an id attribute.', null, null, $oRuleNode); + } + // - Type + $sRuleType = $oRuleNode->getAttribute('xsi:type'); + if (($sRuleType === '') || !in_array($sRuleType, static::GetAllowedTypes())) + { + throw new DOMFormatException('Navigation rule tag must have a valid xsi:type, "'.$sRuleType.'" given, expected '.implode('|', + static::GetAllowedTypes()), null, null, $oRuleNode); + } + + // Load rule from XML + $sRuleLoadingFunction = 'Load'.utils::ToCamelCase($sRuleType).'RuleFromXML'; + $this->aRules[$sRuleId] = $this->$sRuleLoadingFunction($oRuleNode); + } + } + + //-------------------- + // Enumeration helpers + //-------------------- + + /** + * Return an array of the allowed point of origin for the navigation + * + * @return array + */ + public static function GetAllowedOrigins() + { + return array( + static::ENUM_ORIGIN_PAGE, + static::ENUM_ORIGIN_MODAL, + ); + } + + /** + * Return an array of allowed navigation rule types (those in ) + * + * @return array + */ + public static function GetAllowedTypes() + { + return array( + static::ENUM_RULE_CLOSE, + static::ENUM_RULE_GO_TO_HOMEPAGE, + static::ENUM_RULE_GO_TO_OBJECT, + static::ENUM_RULE_GO_TO_BRICK, + static::ENUM_RULE_GO_TO_BROWSE_BRICK, + static::ENUM_RULE_GO_TO_MANAGE_BRICK, + ); + } + + /** + * Return the definition of the rule identified by its ID, as a hash array + * + * @param string $sId + * + * @return array + * @throws \Exception + */ + public function GetRuleDefinition($sId) + { + if (!array_key_exists($sId, $this->aRules)) + { + throw new Exception('NavigationRuleHelper: Could not find "'.$sId.'" in the rules list'); + } + + return $this->aRules[$sId]; + } + + /** + * Returns a hash array of ID => rule definition + * + * @return array + */ + public function GetRulesDefinitions() + { + return $this->aRules; + } + + //------------------------- + // Default rules definition + //------------------------- + + /** + * Return the default definition of the "Close" rule + * + * @return array + */ + public function GetDefaultCloseRuleDefinition() + { + return array( + 'category' => static::ENUM_RULE_CAT_CLOSE, + 'type' => static::ENUM_RULE_CLOSE, + ); + } + + /** + * Return the default definition of the "Go to homepage" rule + * + * @return array + */ + public function GetDefaultGoToHomepageRuleDefinition() + { + return array( + 'category' => static::ENUM_RULE_CAT_REDIRECT, + 'type' => static::ENUM_RULE_GO_TO_HOMEPAGE, + ); + } + + /** + * Return the default definition of the "Go to object" rule + * + * @return array + */ + public function GetDefaultGoToObjectRuleDefinition() + { + return array( + 'category' => static::ENUM_RULE_CAT_REDIRECT, + 'type' => static::ENUM_RULE_GO_TO_OBJECT, + 'properties' => array( + 'mode' => static::DEFAULT_RULE_GO_TO_OBJECT_PROP_MODE, + 'opening_target' => static::DEFAULT_RULE_GO_TO_OBJECT_PROP_OPENING_TARGET, + 'oql' => null, + ), + ); + } + + /** + * Return the default definition of the "Go to brick" rule + * + * @return array + */ + public function GetDefaultGoToBrickRuleDefinition() + { + return array( + 'category' => static::ENUM_RULE_CAT_REDIRECT, + 'type' => static::ENUM_RULE_GO_TO_BRICK, + 'properties' => array( + 'route' => array( + 'id' => null, + 'params' => array(), + ), + ), + ); + } + + //---------------------------- + // Rules definition XML loader + //---------------------------- + + /** + * @noinspection PhpUnused Called dynamically by static::Init() + * + * @param \Combodo\iTop\DesignElement $oRuleNode + * + * @return array + */ + protected function LoadCloseRuleFromXML(DesignElement $oRuleNode) + { + // No special configuration needed + return $this->GetDefaultCloseRuleDefinition(); + } + + /** + * @noinspection PhpUnused Called dynamically by static::Init() + * + * @param \Combodo\iTop\DesignElement $oRuleNode + * + * @return array + */ + protected function LoadGoToHomepageRuleFromXML(DesignElement $oRuleNode) + { + // No special configuration needed + return $this->GetDefaultGoToHomepageRuleDefinition(); + } + + /** + * @noinspection PhpUnused Called dynamically by static::Init() + * + * @param \Combodo\iTop\DesignElement $oRuleNode + * + * @return array + * @throws \DOMFormatException + */ + protected function LoadGoToObjectRuleFromXML(DesignElement $oRuleNode) + { + $sRuleId = $oRuleNode->getAttribute('id'); + // Default values + $aRule = $this->GetDefaultGoToObjectRuleDefinition(); + + $aAllowedOpeningTarget = array( + static::ENUM_RULE_GO_TO_OBJECT_PROP_OPENING_TARGET_CURRENT, + static::ENUM_RULE_GO_TO_OBJECT_PROP_OPENING_TARGET_MODAL, + static::ENUM_RULE_GO_TO_OBJECT_PROP_OPENING_TARGET_PAGE, + ); + + /** @var \Combodo\iTop\DesignElement $oPropNode */ + foreach($oRuleNode->childNodes as $oPropNode) + { + switch($oPropNode->nodeName) + { + case 'mode': + $sMode = $oPropNode->GetText(); + if(!in_array($sMode, ObjectFormHandlerHelper::GetAllowedModes())) + { + throw new DOMFormatException('mode tag of navigation_rule "'.$sRuleId.'" must be valid. Expected '.implode('|', ObjectFormHandlerHelper::GetAllowedModes()).', "'.$sMode.'" given.', null, null, $oRuleNode); + } + $aRule['properties']['mode'] = $sMode; + break; + + case 'opening_target': + $sOpeningTarget = $oPropNode->GetText(); + if(!in_array($sOpeningTarget, $aAllowedOpeningTarget)) + { + throw new DOMFormatException('opening_target tag of navigation_rule "'.$sRuleId.'" must be valid. Expected '.implode('|', $aAllowedOpeningTarget).', "'.$sOpeningTarget.'" given.', null, null, $oRuleNode); + } + $aRule['properties']['opening_target'] = $sOpeningTarget; + break; + + case 'oql': + $sOQL = $oPropNode->GetText(); + if(empty($sOQL)) + { + throw new DOMFormatException('oql tag of navigation_rule "'.$sRuleId.'" can not be empty.'); + } + $aRule['properties']['oql'] = $sOQL; + break; + } + } + + return $aRule; + } + + /** + * @noinspection PhpUnused Called dynamically by static::Init() + * + * @param \Combodo\iTop\DesignElement $oRuleNode + * + * @return array + * @throws \DOMFormatException + */ + protected function LoadGoToBrickRuleFromXML(DesignElement $oRuleNode) + { + $sRuleId = $oRuleNode->getAttribute('id'); + // Default values + $aRule = $this->GetDefaultGoToBrickRuleDefinition(); + + /** @var \Combodo\iTop\DesignElement $oPropNode */ + foreach($oRuleNode->childNodes as $oPropNode) + { + switch($oPropNode->nodeName) + { + case 'route': + /** @var array $aRouteProperties Route ID and parameters */ + $aRouteProperties = array(); + /** @var DesignElement $oRoutePropNode */ + foreach($oPropNode->childNodes as $oRoutePropNode) + { + switch($oRoutePropNode->nodeName) + { + case 'id': + $aRouteProperties['id'] = $oRoutePropNode->GetText(); + break; + + case 'params': + /** @var DesignElement $oRouteParamNode */ + foreach($oRoutePropNode->childNodes as $oRouteParamNode) + { + $sRouteParamId = $oRouteParamNode->getAttribute('id'); + $sRouteParamValue = $oRouteParamNode->GetText(); + if(empty($sRouteParamId) || empty($sRouteParamValue)) + { + throw new DOMFormatException('param tag of navigation_rule "'.$sRuleId.'" must have a valid ID and value.', null, null, $oRuleNode); + } + + $aRouteProperties['params'][$sRouteParamId] = $sRouteParamValue; + } + break; + } + } + + // Consistency check + if(empty($aRouteProperties['id'])) + { + throw new DOMFormatException('navigation_rule "'.$sRuleId.'" must have a valid ID', null, null, $oRuleNode); + } + + $aRule['properties']['route'] = $aRouteProperties; + break; + } + } + + return $aRule; + } + + /** + * @noinspection PhpUnused Called dynamically by static::Init() + * + * Load definition of a "go-to-manage-brick" rule from XML. + * This is a shortcut to a classic "go-to-brick" rule. + * + * @param \Combodo\iTop\DesignElement $oRuleNode + * + * @return array + */ + protected function LoadGoToManageBrickRuleFromXML(DesignElement $oRuleNode) + { + $sRuleId = $oRuleNode->getAttribute('id'); + // Default values + $aRule = $this->GetDefaultGoToBrickRuleDefinition(); + $aRule['properties']['route']['id'] = 'p_manage_brick_display_as'; + + // Rule parameters to automatically map to the route parameters + $aParamsMapping = array( + 'id' => 'sBrickId', + 'display_mode' => 'sDisplayMode', + 'grouping_tab' => 'sGroupingTab', + 'filter' => 'sSearchValue', + ); + + /** @var \Combodo\iTop\DesignElement $oPropNode */ + foreach($oRuleNode->childNodes as $oPropNode) + { + $sRouteParamId = (array_key_exists($oPropNode->nodeName, $aParamsMapping)) ? $aParamsMapping[$oPropNode->nodeName] : $oPropNode->nodeName; + $aRule['properties']['route']['params'][$sRouteParamId] = $oPropNode->GetText(); + } + + return $aRule; + } + + /** + * @noinspection PhpUnused Called dynamically by static::Init() + * + * Load definition of a "go-to-browse-brick" rule from XML. + * This is a shortcut to a classic "go-to-brick" rule. + * + * @param \Combodo\iTop\DesignElement $oRuleNode + * + * @return array + */ + protected function LoadGoToBrowseBrickRuleFromXML(DesignElement $oRuleNode) + { + $sRuleId = $oRuleNode->getAttribute('id'); + // Default values + $aRule = $this->GetDefaultGoToBrickRuleDefinition(); + $aRule['properties']['route']['id'] = 'p_browse_brick_mode'; + + // Rule parameters to automatically map to the route parameters + $aParamsMapping = array( + 'id' => 'sBrickId', + 'browse_mode' => 'sBrowseMode', + 'filter' => 'sSearchValue', + ); + + /** @var \Combodo\iTop\DesignElement $oPropNode */ + foreach($oRuleNode->childNodes as $oPropNode) + { + $sRouteParamId = (array_key_exists($oPropNode->nodeName, $aParamsMapping)) ? $aParamsMapping[$oPropNode->nodeName] : $oPropNode->nodeName; + $aRule['properties']['route']['params'][$sRouteParamId] = $oPropNode->GetText(); + } + + return $aRule; + } + + //------------------------ + // Business logic function + //------------------------ + + /** + * Returns a hash array containing the target URL and if it should be opened in a modal for each type of callbacks (submit and cancel) + * + * eg : + * array( + * 'submit' => array( + * 'type' => 'redirect', + * 'url' => 'http://localhost/', + * 'modal' => false, + * 'cancel' => array( + * 'type' => 'close', + * 'url' => null, + * 'modal' => false, + * ) + * ); + * + * @param array $aFormProperties + * @param \DBObject $oCurrentObject + * @param boolean $bIsCurrentFormInModal + * + * @return array + * @throws \Exception + */ + public function PrepareRulesForForm(array $aFormProperties, DBObject $oCurrentObject, $bIsCurrentFormInModal = false) + { + // Default values + $aResults = array( + 'submit' => array( + 'category' => static::ENUM_RULE_CAT_REDIRECT, + 'url' => null, + 'modal' => false, + ), + 'cancel' => array( + 'category' => static::ENUM_RULE_CAT_CLOSE, + 'url' => null, + 'modal' => false, + ), + ); + + // Get form's navigation rules + $aFormNavRules = (isset($aFormProperties['properties'])) ? $aFormProperties['properties']['navigation_rules'] : array('submit' => null, 'cancel' => null); + + // Check from which origin the rule will be called + $sRuleCallOrigin = ($bIsCurrentFormInModal) ? 'modal' : 'default'; + + foreach(array_keys($aResults) as $sButtonCode) + { + // Retrieve rule definition + // - Default behavior when no rule specified + if(empty($aFormNavRules[$sButtonCode][$sRuleCallOrigin])) + { + switch($sButtonCode) + { + case 'submit': + $aRuleDef = $this->GetDefaultGoToObjectRuleDefinition(); + break; + + case 'cancel': + $aRuleDef = $this->GetDefaultCloseRuleDefinition(); + break; + } + } + // - Specified rule + else + { + $sRuleId = $aFormNavRules[$sButtonCode][$sRuleCallOrigin]; + $aRuleDef = $this->GetRuleDefinition($sRuleId); + } + + // Set category + $aResults[$sButtonCode]['category'] = $aRuleDef['category']; + + // Set properties regarding the type + switch($aRuleDef['type']) + { + case static::ENUM_RULE_GO_TO_HOMEPAGE: + $aResults[$sButtonCode]['url'] = $this->oRouter->generate('p_home'); + break; + + case static::ENUM_RULE_GO_TO_OBJECT: + // Target opening mode to modal if specified or should be as current form + if( ($aRuleDef['properties']['opening_target'] === static::ENUM_RULE_GO_TO_OBJECT_PROP_OPENING_TARGET_MODAL) + || (($aRuleDef['properties']['opening_target'] === static::ENUM_RULE_GO_TO_OBJECT_PROP_OPENING_TARGET_CURRENT) && ($bIsCurrentFormInModal === true)) + ) + { + $aResults[$sButtonCode]['modal'] = true; + } + + // Target URL + // - Find object + if(empty($aRuleDef['properties']['oql'])) + { + $oTargetObject = $oCurrentObject; + } + else + { + $oSearch = DBSearch::FromOQL($aRuleDef['properties']['oql']); + $oSet = new DBObjectSet($oSearch, array(), array('this' => $oCurrentObject)); + $oSet->OptimizeColumnLoad(array()); + $oTargetObject = $oSet->Fetch(); + } + // - Build URL + $aResults[$sButtonCode]['url'] = $this->oRouter->generate('p_object_'.$aRuleDef['properties']['mode'], array('sObjectClass' => get_class($oTargetObject), 'sObjectId' => $oTargetObject->GetKey())); + break; + + case static::ENUM_RULE_GO_TO_BRICK: + // Build URL + $aRouteProperties = $aRuleDef['properties']['route']; + $aResults[$sButtonCode]['url'] = $this->oRouter->generate($aRouteProperties['id'], $aRouteProperties['params']); + break; + + case static::ENUM_RULE_CLOSE: + default: + // Don't set the URL + break; + } + } + + return $aResults; + } +} diff --git a/datamodels/2.x/itop-portal-base/portal/src/Helper/ObjectFormHandlerHelper.php b/datamodels/2.x/itop-portal-base/portal/src/Helper/ObjectFormHandlerHelper.php index 300c953643..a3244ebdd6 100644 --- a/datamodels/2.x/itop-portal-base/portal/src/Helper/ObjectFormHandlerHelper.php +++ b/datamodels/2.x/itop-portal-base/portal/src/Helper/ObjectFormHandlerHelper.php @@ -16,8 +16,6 @@ * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License - * - * */ @@ -63,6 +61,8 @@ class ObjectFormHandlerHelper private $oRequestManipulator; /** @var \Combodo\iTop\Portal\Helper\ContextManipulatorHelper */ private $oContextManipulator; + /** @var \Combodo\iTop\Portal\Helper\NavigationRuleHelper */ + private $oNavigationRuleHelper; /** @var \Combodo\iTop\Portal\Helper\ScopeValidatorHelper */ private $oScopeValidator; /** @var \Combodo\iTop\Portal\Helper\SecurityHelper */ @@ -81,20 +81,22 @@ class ObjectFormHandlerHelper /** * ObjectFormHandlerHelper constructor. * - * @param \Combodo\iTop\Portal\Helper\RequestManipulatorHelper $oRequestManipulator - * @param \Combodo\iTop\Portal\Helper\ContextManipulatorHelper $oContextManipulator - * @param \Combodo\iTop\Portal\Helper\ScopeValidatorHelper $oScopeValidator - * @param \Combodo\iTop\Portal\Helper\SecurityHelper $oSecurityHelper + * @param \Combodo\iTop\Portal\Helper\RequestManipulatorHelper $oRequestManipulator + * @param \Combodo\iTop\Portal\Helper\ContextManipulatorHelper $oContextManipulator + * @param \Combodo\iTop\Portal\Helper\NavigationRuleHelper $oNavigationRuleHelper + * @param \Combodo\iTop\Portal\Helper\ScopeValidatorHelper $oScopeValidator + * @param \Combodo\iTop\Portal\Helper\SecurityHelper $oSecurityHelper * @param \Symfony\Component\Routing\Generator\UrlGeneratorInterface $oUrlGenerator - * @param array $aCombodoPortalInstanceConf - * @param string $sPortalId - * @param \Combodo\iTop\Portal\Twig\AppExtension $oAppExtension - * @param \Symfony\Component\DependencyInjection\ContainerInterface $oContainer + * @param array $aCombodoPortalInstanceConf + * @param string $sPortalId + * @param \Combodo\iTop\Portal\Twig\AppExtension $oAppExtension + * @param \Symfony\Component\DependencyInjection\ContainerInterface $oContainer */ - public function __construct(RequestManipulatorHelper $oRequestManipulator, ContextManipulatorHelper $oContextManipulator, ScopeValidatorHelper $oScopeValidator, SecurityHelper $oSecurityHelper, UrlGeneratorInterface $oUrlGenerator, $aCombodoPortalInstanceConf, $sPortalId, AppExtension $oAppExtension, ContainerInterface $oContainer) + public function __construct(RequestManipulatorHelper $oRequestManipulator, ContextManipulatorHelper $oContextManipulator, NavigationRuleHelper $oNavigationRuleHelper, ScopeValidatorHelper $oScopeValidator, SecurityHelper $oSecurityHelper, UrlGeneratorInterface $oUrlGenerator, $aCombodoPortalInstanceConf, $sPortalId, AppExtension $oAppExtension, ContainerInterface $oContainer) { $this->oRequestManipulator = $oRequestManipulator; $this->oContextManipulator = $oContextManipulator; + $this->oNavigationRuleHelper = $oNavigationRuleHelper; $this->oScopeValidator = $oScopeValidator; $this->oSecurityHelper = $oSecurityHelper; $this->oUrlGenerator = $oUrlGenerator; @@ -222,10 +224,13 @@ public function HandleForm(Request $oRequest, $sMode, $sObjectClass, $sObjectId $oObject->PrefillForm('state_change', $aPrefillFormParam); } - // Preparing callback urls - $aCallbackUrls = $this->oContextManipulator->GetCallbackUrls($aActionRules, $oObject, $bModal); - $aFormData['submit_callback'] = $aCallbackUrls['submit']; - $aFormData['cancel_callback'] = $aCallbackUrls['cancel']; + // Preparing navigation rules + $aNavigationRules = $this->oNavigationRuleHelper->PrepareRulesForForm($aFormProperties, $oObject, $bModal); + $aFormData['submit_rule'] = $aNavigationRules['submit']; + $aFormData['cancel_rule'] = $aNavigationRules['cancel']; + /** @deprecated We keep the "xxx_callback" name to keep compatibility with extensions using the portal_form_handler.js widget but they will be removed in a future version. */ + $aFormData['submit_callback'] = $aNavigationRules['submit']['url']; + $aFormData['cancel_callback'] = $aNavigationRules['cancel']['url']; // Preparing renderer // Note : We might need to distinguish form & renderer endpoints @@ -292,23 +297,16 @@ public function HandleForm(Request $oRequest, $sMode, $sObjectClass, $sObjectId ); if ($aFormData['validation']['valid'] === true) { - // Note : We don't use $sObjectId there as it can be null if we are creating a new one. Instead we use the id from the created object once it has been seralized + // Note : We don't use $sObjectId there as it can be null if we are creating a new one. Instead we use the id from the created object once it has been serialized // Check if stimulus has to be applied $sStimulusCode = $this->oRequestManipulator->ReadParam('stimulus_code', ''); if (!empty($sStimulusCode)) { $aFormData['validation']['redirection'] = array( 'url' => $this->oUrlGenerator->generate('p_object_apply_stimulus', array('sObjectClass' => $sObjectClass, 'sObjectId' => $oFormManager->GetObject()->GetKey(), 'sStimulusCode' => $sStimulusCode)), - 'ajax' => true, + 'modal' => true, ); } - // Otherwise, we show the object if there is no default -// else -// { -// $aFormData['validation']['redirection'] = array( -// 'alternative_url' => $this->oUrlGenerator->generate('p_object_edit', array('sObjectClass' => $sObjectClass, 'sObjectId' => $oFormManager->GetObject()->GetKey())) -// ); -// } } break; @@ -410,4 +408,19 @@ public function RenderFormFromTwig($sId, $sTwigString, $aData) return $oTwig->render($sId, $aData); } + + /** + * Return an array of the available modes for a form. + * + * @since 2.7.0 + * @return array + */ + public static function GetAllowedModes() + { + return array( + static::ENUM_MODE_VIEW, + static::ENUM_MODE_EDIT, + static::ENUM_MODE_CREATE, + ); + } } \ No newline at end of file diff --git a/datamodels/2.x/itop-portal-base/portal/templates/bricks/object/mode_create.html.twig b/datamodels/2.x/itop-portal-base/portal/templates/bricks/object/mode_create.html.twig index 22f0ccfb48..7b001fabbb 100644 --- a/datamodels/2.x/itop-portal-base/portal/templates/bricks/object/mode_create.html.twig +++ b/datamodels/2.x/itop-portal-base/portal/templates/bricks/object/mode_create.html.twig @@ -76,8 +76,8 @@ field_set: oFieldSet_{{ sFormIdSanitized }}, submit_btn_selector: $('#{{ sFormId }}').parent().find('.form_btn_submit, .form_btn_transition'), cancel_btn_selector: $('#{{ sFormId }}').parent().find('.form_btn_cancel'), - submit_url: {% if form.submit_callback is not null %}"{{ form.submit_callback|raw }}"{% else %}null{% endif %}, - cancel_url: {% if form.cancel_callback is not null %}"{{ form.cancel_callback|raw }}"{% else %}null{% endif %}, + {% if form.submit_rule is not null %}submit_rule: {{ form.submit_rule|json_encode|raw }}{% endif %}, + {% if form.cancel_rule is not null %}cancel_rule: {{ form.cancel_rule|json_encode|raw }}{% endif %}, endpoint: "{{ form.renderer.GetEndpoint()|raw }}", is_modal: {% if tIsModal == true %}true{% else %}false{% endif %} }); diff --git a/datamodels/2.x/itop-portal-base/portal/vendor/composer/ClassLoader.php b/datamodels/2.x/itop-portal-base/portal/vendor/composer/ClassLoader.php index 95f7e0978b..244d5b3372 100644 --- a/datamodels/2.x/itop-portal-base/portal/vendor/composer/ClassLoader.php +++ b/datamodels/2.x/itop-portal-base/portal/vendor/composer/ClassLoader.php @@ -1,13 +1,21 @@ - * Jordi Boggiano + * iTop is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. + * You should have received a copy of the GNU Affero General Public License */ namespace Composer\Autoload; @@ -279,7 +287,7 @@ public function isClassMapAuthoritative() */ public function setApcuPrefix($apcuPrefix) { - $this->apcuPrefix = function_exists('apcu_fetch') && ini_get('apc.enabled') ? $apcuPrefix : null; + $this->apcuPrefix = function_exists('apcu_fetch') && filter_var(ini_get('apc.enabled'), FILTER_VALIDATE_BOOLEAN) ? $apcuPrefix : null; } /** diff --git a/datamodels/2.x/itop-portal-base/portal/vendor/composer/autoload_classmap.php b/datamodels/2.x/itop-portal-base/portal/vendor/composer/autoload_classmap.php index 0bca33db6b..a445fe161b 100644 --- a/datamodels/2.x/itop-portal-base/portal/vendor/composer/autoload_classmap.php +++ b/datamodels/2.x/itop-portal-base/portal/vendor/composer/autoload_classmap.php @@ -1,4 +1,21 @@ $baseDir . '/src/DependencyInjection/SilexCompatBootstrap/PortalXmlConfiguration/Basic.php', 'Combodo\\iTop\\Portal\\DependencyInjection\\SilexCompatBootstrap\\PortalXmlConfiguration\\Forms' => $baseDir . '/src/DependencyInjection/SilexCompatBootstrap/PortalXmlConfiguration/Forms.php', 'Combodo\\iTop\\Portal\\DependencyInjection\\SilexCompatBootstrap\\PortalXmlConfiguration\\Lists' => $baseDir . '/src/DependencyInjection/SilexCompatBootstrap/PortalXmlConfiguration/Lists.php', - 'Combodo\\iTop\\Portal\\EventListener\\ApplicationContextSetUrlMakerClass' => $baseDir . '/src/EventListener/ApplicationContextSetUrlMakerClass.php', 'Combodo\\iTop\\Portal\\EventListener\\ApplicationContextSetPluginPropertyClass' => $baseDir . '/src/EventListener/ApplicationContextSetPluginPropertyClass.php', + 'Combodo\\iTop\\Portal\\EventListener\\ApplicationContextSetUrlMakerClass' => $baseDir . '/src/EventListener/ApplicationContextSetUrlMakerClass.php', 'Combodo\\iTop\\Portal\\EventListener\\UserProvider' => $baseDir . '/src/EventListener/UserProvider.php', 'Combodo\\iTop\\Portal\\Form\\ObjectFormManager' => $baseDir . '/src/Form/ObjectFormManager.php', 'Combodo\\iTop\\Portal\\Form\\PasswordFormManager' => $baseDir . '/src/Form/PasswordFormManager.php', @@ -41,6 +58,7 @@ 'Combodo\\iTop\\Portal\\Helper\\BrowseBrickHelper' => $baseDir . '/src/Helper/BrowseBrickHelper.php', 'Combodo\\iTop\\Portal\\Helper\\ContextManipulatorHelper' => $baseDir . '/src/Helper/ContextManipulatorHelper.php', 'Combodo\\iTop\\Portal\\Helper\\LifecycleValidatorHelper' => $baseDir . '/src/Helper/LifecycleValidatorHelper.php', + 'Combodo\\iTop\\Portal\\Helper\\NavigationRuleHelper' => $baseDir . '/src/Helper/NavigationRuleHelper.php', 'Combodo\\iTop\\Portal\\Helper\\ObjectFormHandlerHelper' => $baseDir . '/src/Helper/ObjectFormHandlerHelper.php', 'Combodo\\iTop\\Portal\\Helper\\RequestManipulatorHelper' => $baseDir . '/src/Helper/RequestManipulatorHelper.php', 'Combodo\\iTop\\Portal\\Helper\\ScopeValidatorHelper' => $baseDir . '/src/Helper/ScopeValidatorHelper.php', diff --git a/datamodels/2.x/itop-portal-base/portal/vendor/composer/autoload_static.php b/datamodels/2.x/itop-portal-base/portal/vendor/composer/autoload_static.php index 9c2d68bc08..3acb9a8573 100644 --- a/datamodels/2.x/itop-portal-base/portal/vendor/composer/autoload_static.php +++ b/datamodels/2.x/itop-portal-base/portal/vendor/composer/autoload_static.php @@ -1,4 +1,21 @@ __DIR__ . '/../..' . '/src/DependencyInjection/SilexCompatBootstrap/PortalXmlConfiguration/Basic.php', 'Combodo\\iTop\\Portal\\DependencyInjection\\SilexCompatBootstrap\\PortalXmlConfiguration\\Forms' => __DIR__ . '/../..' . '/src/DependencyInjection/SilexCompatBootstrap/PortalXmlConfiguration/Forms.php', 'Combodo\\iTop\\Portal\\DependencyInjection\\SilexCompatBootstrap\\PortalXmlConfiguration\\Lists' => __DIR__ . '/../..' . '/src/DependencyInjection/SilexCompatBootstrap/PortalXmlConfiguration/Lists.php', - 'Combodo\\iTop\\Portal\\EventListener\\ApplicationContextSetUrlMakerClass' => __DIR__ . '/../..' . '/src/EventListener/ApplicationContextSetUrlMakerClass.php', 'Combodo\\iTop\\Portal\\EventListener\\ApplicationContextSetPluginPropertyClass' => __DIR__ . '/../..' . '/src/EventListener/ApplicationContextSetPluginPropertyClass.php', + 'Combodo\\iTop\\Portal\\EventListener\\ApplicationContextSetUrlMakerClass' => __DIR__ . '/../..' . '/src/EventListener/ApplicationContextSetUrlMakerClass.php', 'Combodo\\iTop\\Portal\\EventListener\\UserProvider' => __DIR__ . '/../..' . '/src/EventListener/UserProvider.php', 'Combodo\\iTop\\Portal\\Form\\ObjectFormManager' => __DIR__ . '/../..' . '/src/Form/ObjectFormManager.php', 'Combodo\\iTop\\Portal\\Form\\PasswordFormManager' => __DIR__ . '/../..' . '/src/Form/PasswordFormManager.php', @@ -61,6 +78,7 @@ class ComposerStaticInitdf408f3f8ea034d298269cdf7647358b 'Combodo\\iTop\\Portal\\Helper\\BrowseBrickHelper' => __DIR__ . '/../..' . '/src/Helper/BrowseBrickHelper.php', 'Combodo\\iTop\\Portal\\Helper\\ContextManipulatorHelper' => __DIR__ . '/../..' . '/src/Helper/ContextManipulatorHelper.php', 'Combodo\\iTop\\Portal\\Helper\\LifecycleValidatorHelper' => __DIR__ . '/../..' . '/src/Helper/LifecycleValidatorHelper.php', + 'Combodo\\iTop\\Portal\\Helper\\NavigationRuleHelper' => __DIR__ . '/../..' . '/src/Helper/NavigationRuleHelper.php', 'Combodo\\iTop\\Portal\\Helper\\ObjectFormHandlerHelper' => __DIR__ . '/../..' . '/src/Helper/ObjectFormHandlerHelper.php', 'Combodo\\iTop\\Portal\\Helper\\RequestManipulatorHelper' => __DIR__ . '/../..' . '/src/Helper/RequestManipulatorHelper.php', 'Combodo\\iTop\\Portal\\Helper\\ScopeValidatorHelper' => __DIR__ . '/../..' . '/src/Helper/ScopeValidatorHelper.php',