diff --git a/datamodels/2.x/itop-portal-base/portal-silex/src/providers/urlgeneratorserviceprovider.class.inc.php b/datamodels/2.x/itop-portal-base/portal-silex/src/providers/urlgeneratorserviceprovider.class.inc.php deleted file mode 100644 index 05c69e485..000000000 --- a/datamodels/2.x/itop-portal-base/portal-silex/src/providers/urlgeneratorserviceprovider.class.inc.php +++ /dev/null @@ -1,49 +0,0 @@ - - -namespace Combodo\iTop\Portal\Provider; - -use Pimple\Container; -use Pimple\ServiceProviderInterface; -use Combodo\iTop\Portal\Helper\UrlGenerator; - -/** - * Based on Symfony Routing component Provider for URL generation. - * - * @author Guillaume Lajarige - */ -class UrlGeneratorServiceProvider implements ServiceProviderInterface -{ - - public function register(Container $oApp) - { - $oApp['url_generator'] = function ($oApp) - { - $oApp->flush(); - - return new UrlGenerator($oApp['routes'], $oApp['request_context']); - }; - } - - public function boot(Container $oApp) - { - - } - -} diff --git a/datamodels/2.x/itop-portal-base/portal/config/legacy_silex_compat_layer.php b/datamodels/2.x/itop-portal-base/portal/config/legacy_silex_compat_layer.php index f0f1aaf3b..6668c9a1c 100644 --- a/datamodels/2.x/itop-portal-base/portal/config/legacy_silex_compat_layer.php +++ b/datamodels/2.x/itop-portal-base/portal/config/legacy_silex_compat_layer.php @@ -27,21 +27,21 @@ use Combodo\iTop\Portal\DependencyInjection\SilexCompatBootstrap\PortalXmlConfig // Note: ModuleDesign service is not available yet as this script is processed before service generation, // that's why we have to instantiate it manually. -$moduleDesign = new \ModuleDesign(PORTAL_ID); +$oModuleDesign = new ModuleDesign(PORTAL_ID); // TODO: The following code needs to be refactored to more independent and atomic services. -//append into %combodo.portal.instance.conf% -$basicCompat = new Basic($moduleDesign); -$basicCompat->process($container); +// Append into %combodo.portal.instance.conf% +$oBasicCompat = new Basic($oModuleDesign); +$oBasicCompat->Process($container); -//create %combodo.portal.instance.conf% -$formsCompat = new Forms($moduleDesign); -$formsCompat->process($container); +// Append into %combodo.portal.instance.conf% +$oFormsCompat = new Forms($oModuleDesign); +$oFormsCompat->Process($container); -//append into %combodo.portal.instance.conf% -$listesCompat = new Lists($moduleDesign); -$listesCompat->process($container); +// Append into %combodo.portal.instance.conf% +$oListsCompat = new Lists($oModuleDesign); +$oListsCompat->Process($container); // - Generating CSS files $aImportPaths = array(COMBODO_PORTAL_BASE_ABSOLUTE_PATH.'css/'); @@ -55,9 +55,9 @@ foreach ($aPortalConf['properties']['themes'] as $key => $value) else { $aValues = array(); - foreach ($value as $sSubvalue) + foreach ($value as $sSubValue) { - $aValues[] = COMBODO_ABSOLUTE_URL.utils::GetCSSFromSASS('env-'.utils::GetCurrentEnvironment().'/'.$sSubvalue, $aImportPaths); + $aValues[] = COMBODO_ABSOLUTE_URL.utils::GetCSSFromSASS('env-'.utils::GetCurrentEnvironment().'/'.$sSubValue, $aImportPaths); } $aPortalConf['properties']['themes'][$key] = $aValues; } diff --git a/datamodels/2.x/itop-portal-base/portal/config/routes/default.yaml b/datamodels/2.x/itop-portal-base/portal/config/routes/default.yaml index 7b9463d2f..855a351f8 100644 --- a/datamodels/2.x/itop-portal-base/portal/config/routes/default.yaml +++ b/datamodels/2.x/itop-portal-base/portal/config/routes/default.yaml @@ -17,11 +17,11 @@ p_home: path: '/' - controller: 'Combodo\iTop\Portal\Controller\DefaultController::homeAction' + controller: 'Combodo\iTop\Portal\Controller\DefaultController::HomeAction' # Example route #p_example: # path: /url-pattern -# controller: 'Combodo\iTop\Portal\Controller\DefaultController::exampleAction' +# controller: 'Combodo\iTop\Portal\Controller\DefaultController::ExampleAction' # _defaults: # _fragment: 'string-to-be-append-to-the-pattern-after-a-#' \ No newline at end of file diff --git a/datamodels/2.x/itop-portal-base/portal/config/routes/object_brick.yaml b/datamodels/2.x/itop-portal-base/portal/config/routes/object_brick.yaml index 87c5c87c0..ed1350ed5 100644 --- a/datamodels/2.x/itop-portal-base/portal/config/routes/object_brick.yaml +++ b/datamodels/2.x/itop-portal-base/portal/config/routes/object_brick.yaml @@ -59,13 +59,6 @@ p_object_search_autocomplete: sHostObjectClass: ~ sHostObjectId: ~ -p_object_search_hierarchy: - path: '/object/search/hierarchy/{sTargetAttCode}/{sHostObjectClass}/{sHostObjectId}' - defaults: - _controller: 'Combodo\\iTop\\Portal\\Controller\\ObjectController::SearchHierarchyAction' - sHostObjectClass: ~ - sHostObjectId: ~ - p_object_search_generic: path: '/object/search/{sMode}/{sTargetAttCode}/{sHostObjectClass}/{sHostObjectId}' 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 f957c99e6..929b50afe 100644 --- a/datamodels/2.x/itop-portal-base/portal/config/services.yaml +++ b/datamodels/2.x/itop-portal-base/portal/config/services.yaml @@ -126,6 +126,9 @@ services: url_generator: alias: router public: true + object_form_handler: + alias: Combodo\iTop\Portal\Helper\ObjectFormHandlerHelper + public: true browse_brick: alias: Combodo\iTop\Portal\Helper\BrowseBrickHelper public: true \ No newline at end of file diff --git a/datamodels/2.x/itop-portal-base/portal/src/Brick/BrickCollection.php b/datamodels/2.x/itop-portal-base/portal/src/Brick/BrickCollection.php index 827264ae8..0ec05ecf0 100644 --- a/datamodels/2.x/itop-portal-base/portal/src/Brick/BrickCollection.php +++ b/datamodels/2.x/itop-portal-base/portal/src/Brick/BrickCollection.php @@ -19,14 +19,10 @@ * */ -/** - * Created by Bruno DA SILVA, working for Combodo - * Date: 24/01/19 - * Time: 17:28 - */ - namespace Combodo\iTop\Portal\Brick; +use DOMFormatException; +use Exception; use UserRights; use ModuleDesign; use Combodo\iTop\Portal\Helper\ApplicationHelper; @@ -35,111 +31,155 @@ use Combodo\iTop\Portal\Helper\ApplicationHelper; * Class BrickCollection * * @package Combodo\iTop\Portal\Brick + * @author Bruno Da Silva + * @author Guillaume Lajarige * @since 2.7.0 */ class BrickCollection { - /** @var array|null Lazily computed */ - private $aAllowedBricksData; - /** @var \ModuleDesign */ - private $oModuleDesign; + /** @var \ModuleDesign */ + private $oModuleDesign; + /** @var array|null $aAllowedBricks Lazily computed */ + private $aAllowedBricks; + /** @var int $iDisplayedInHome Lazily computed */ + private $iDisplayedInHome; + /** @var int $iDisplayedInNavigationMenu Lazily computed */ + private $iDisplayedInNavigationMenu; + /** @var array $aHomeOrdering */ + private $aHomeOrdering; + /** @var array $aNavigationMenuOrdering */ + private $aNavigationMenuOrdering; - public function __construct(ModuleDesign $oModuleDesign) + /** + * BrickCollection constructor. + * + * @param \ModuleDesign $oModuleDesign + * + * @throws \Exception + */ + public function __construct(ModuleDesign $oModuleDesign) { $this->oModuleDesign = $oModuleDesign; + $this->aAllowedBricks = null; + $this->iDisplayedInHome = 0; + $this->iDisplayedInNavigationMenu = 0; + $this->aHomeOrdering = array(); + $this->aNavigationMenuOrdering = array(); + + $this->Load(); } - public function __call($method, $arguments) + /** + * @param $method + * @param $arguments + * + * @return array|\Combodo\iTop\Portal\Brick\PortalBrick[]|null + * @throws \Combodo\iTop\Portal\Brick\PropertyNotFoundException + * @throws \Exception + */ + public function __call($method, $arguments) { - return $this->GetBrickProperty($method); + // Made for cleaner/easier access from twig (eg. app['brick_collection'].bricks) + switch($method) + { + case 'bricks': + return $this->GetBricks(); + break; + case 'home_ordering': + return $this->GetHomeOrdering(); + break; + case 'navigation_menu_ordering': + return $this->GetNavigationMenuOrdering(); + break; + default: + throw new PropertyNotFoundException("The property '$method' do not exists in BricksCollection"); + } } - /** - * @param string $sId - * - * @return mixed - * @throws PropertyNotFoundException - */ - private function GetBrickProperty($sId) + /** + * @return \Combodo\iTop\Portal\Brick\PortalBrick[]|null + * @throws \Exception + */ + public function GetBricks() { - if (array_key_exists($sId, $this->getBricks())) { - return $this->getBricks()[$sId]; - } - - throw new PropertyNotFoundException( - "The property '$sId' do not exists in BricksCollection with keys: ".array_keys($this->getBricks()) - ); + return $this->aAllowedBricks; } - public function getBricks() - { - if (! isset($this->aAllowedBricksData)) { - $this->LazyLoad(); - } + public function GetHomeOrdering() + { + return $this->aHomeOrdering; + } - return $this->aAllowedBricksData; - } + public function GetNavigationMenuOrdering() + { + return $this->aNavigationMenuOrdering; + } - public function getBrickById($id) + /** + * @param string $sId + * + * @return \Combodo\iTop\Portal\Brick\PortalBrick + * @throws \Combodo\iTop\Portal\Brick\BrickNotFoundException + * @throws \Exception + */ + public function GetBrickById($sId) { - foreach ($this->getBricks()['bricks'] as $brick) { - if ($brick->GetId() === $id) + foreach ($this->GetBricks() as $oBrick) { + if ($oBrick->GetId() === $sId) { - return $brick; + return $oBrick; } } - throw new BrickNotFoundException('Brick with id = "'.$id.'" was not found among loaded bricks.'); + throw new BrickNotFoundException('Brick with id = "'.$sId.'" was not found among loaded bricks.'); } - private function LazyLoad() + /** + * @throws \Exception + */ + private function Load() { $aRawBrickList = $this->GetRawBrickList(); - // TODO: Are bricks_* still used? Didn't find any references in files. Maybe they could be deprecated. - $this->aAllowedBricksData = array( - 'bricks' => array(), - 'bricks_total_width' => 0, - 'bricks_home_count' => 0, - 'bricks_navigation_menu_count' => 0 - ); - foreach ($aRawBrickList as $oBrick) { ApplicationHelper::LoadBrickSecurity($oBrick); if ($oBrick->GetActive() && $oBrick->IsGrantedForProfiles(UserRights::ListProfiles())) { - $this->aAllowedBricksData['bricks'][] = $oBrick; - $this->aAllowedBricksData['bricks_total_width'] += $oBrick->GetWidth(); + $this->aAllowedBricks[] = $oBrick; if ($oBrick->GetVisibleHome()) { - $this->aAllowedBricksData['bricks_home_count']++; + $this->iDisplayedInHome++; } if ($oBrick->GetVisibleNavigationMenu()) { - $this->aAllowedBricksData['bricks_navigation_menu_count']++; + $this->iDisplayedInNavigationMenu++; } } } // - Sorting bricks by rank - $this->aAllowedBricksData['bricks_ordering'] = array(); // - Home - $this->aAllowedBricksData['bricks_ordering']['home'] = $this->aAllowedBricksData['bricks']; - usort($this->aAllowedBricksData['bricks_ordering']['home'], function (PortalBrick $a, PortalBrick $b) { + $this->aHomeOrdering = $this->aAllowedBricks; + usort($this->aHomeOrdering, function (PortalBrick $a, PortalBrick $b) { return $a->GetRankHome() > $b->GetRankHome(); }); // - Navigation menu - $this->aAllowedBricksData['bricks_ordering']['navigation_menu'] = $this->aAllowedBricksData['bricks']; - usort($this->aAllowedBricksData['bricks_ordering']['navigation_menu'], function (PortalBrick $a, PortalBrick $b) { + $this->aNavigationMenuOrdering = $this->aAllowedBricks; + usort($this->aNavigationMenuOrdering, function (PortalBrick $a, PortalBrick $b) { return $a->GetRankNavigationMenu() > $b->GetRankNavigationMenu(); }); } - private function GetRawBrickList() + /** + * @return array + * @throws \Exception + */ + private function GetRawBrickList() { $aBricks = array(); - foreach ($this->oModuleDesign->GetNodes('/module_design/bricks/brick') as $oBrickNode) + /** @var \Combodo\iTop\DesignElement $oBrickNode */ + foreach ($this->oModuleDesign->GetNodes('/module_design/bricks/brick') as $oBrickNode) { $sBrickClass = $oBrickNode->getAttribute('xsi:type'); try diff --git a/datamodels/2.x/itop-portal-base/portal/src/Controller/BrickController.php b/datamodels/2.x/itop-portal-base/portal/src/Controller/BrickController.php index 031fde208..94ca20789 100644 --- a/datamodels/2.x/itop-portal-base/portal/src/Controller/BrickController.php +++ b/datamodels/2.x/itop-portal-base/portal/src/Controller/BrickController.php @@ -1,21 +1,24 @@ +/** + * Copyright (C) 2013-2019 Combodo SARL + * + * This file is part of iTop. + * + * iTop is free software; you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * 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. + * + * You should have received a copy of the GNU Affero General Public License + * + * + */ namespace Combodo\iTop\Portal\Controller; diff --git a/datamodels/2.x/itop-portal-base/portal/src/Controller/DefaultController.php b/datamodels/2.x/itop-portal-base/portal/src/Controller/DefaultController.php index 5e58725a4..a4e02df09 100644 --- a/datamodels/2.x/itop-portal-base/portal/src/Controller/DefaultController.php +++ b/datamodels/2.x/itop-portal-base/portal/src/Controller/DefaultController.php @@ -37,19 +37,19 @@ use Symfony\Component\HttpFoundation\Response; class DefaultController extends AbstractController { /** - * @param \Symfony\Component\HttpFoundation\Request $oRequest - * @param \Combodo\iTop\Portal\Brick\BrickCollection $bricksCollection + * @param \Symfony\Component\HttpFoundation\Request $oRequest + * @param \Combodo\iTop\Portal\Brick\BrickCollection $oBricksCollection * * @return \Symfony\Component\HttpFoundation\Response + * @throws \Exception */ - public function homeAction(Request $oRequest, BrickCollection $bricksCollection) + public function HomeAction(Request $oRequest, BrickCollection $oBricksCollection) { $aData = array(); // Rendering tiles $aData['aTilesRendering'] = array(); - /** @var \Combodo\iTop\Portal\Brick\PortalBrick $oBrick */ - foreach($bricksCollection->getBricks()['bricks'] as $oBrick) + foreach($oBricksCollection->GetBricks() as $oBrick) { // Doing it only for tile visible on home page to avoid unnecessary rendering if (($oBrick->GetVisibleHome() === true) && ($oBrick->GetTileControllerAction() !== null)) @@ -76,9 +76,9 @@ class DefaultController extends AbstractController } // Home page template - $template = $this->getParameter('combodo.portal.instance.conf')['properties']['templates']['home']; + $sTemplatePath = $this->getParameter('combodo.portal.instance.conf')['properties']['templates']['home']; - return $this->render($template, $aData); + return $this->render($sTemplatePath, $aData); } } diff --git a/datamodels/2.x/itop-portal-base/portal-silex/src/controllers/objectcontroller.class.inc.php b/datamodels/2.x/itop-portal-base/portal/src/Controller/ObjectController.php similarity index 50% rename from datamodels/2.x/itop-portal-base/portal-silex/src/controllers/objectcontroller.class.inc.php rename to datamodels/2.x/itop-portal-base/portal/src/Controller/ObjectController.php index 6fda14dd9..a7c5d1aa4 100644 --- a/datamodels/2.x/itop-portal-base/portal-silex/src/controllers/objectcontroller.class.inc.php +++ b/datamodels/2.x/itop-portal-base/portal/src/Controller/ObjectController.php @@ -1,27 +1,31 @@ +/** + * Copyright (C) 2013-2019 Combodo SARL + * + * This file is part of iTop. + * + * iTop is free software; you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * 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. + * + * You should have received a copy of the GNU Affero General Public License + * + * + */ namespace Combodo\iTop\Portal\Controller; -use Silex\Application; +use Symfony\Component\HttpFoundation\JsonResponse; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\HttpKernel\Exception\HttpException; use Symfony\Component\HttpKernel\HttpKernelInterface; use Exception; use FileUploadException; @@ -44,14 +48,9 @@ use AttributeImage; use AttributeFinalClass; use AttributeFriendlyName; use UserRights; -use iPopupMenuExtension; use URLButtonItem; -use JSButtonItem; use Combodo\iTop\Portal\Helper\ApplicationHelper; -use Combodo\iTop\Portal\Helper\SecurityHelper; use Combodo\iTop\Portal\Helper\ContextManipulatorHelper; -use Combodo\iTop\Portal\Form\ObjectFormManager; -use Combodo\iTop\Renderer\Bootstrap\BsFormRenderer; /** * Class ObjectController @@ -64,67 +63,76 @@ use Combodo\iTop\Renderer\Bootstrap\BsFormRenderer; */ class ObjectController extends AbstractController { - - const ENUM_MODE_VIEW = 'view'; - const ENUM_MODE_EDIT = 'edit'; - const ENUM_MODE_CREATE = 'create'; - const DEFAULT_PAGE_NUMBER = 1; const DEFAULT_LIST_LENGTH = 10; - /** - * Displays an cmdbAbstractObject if the connected user is allowed to. - * - * @param \Symfony\Component\HttpFoundation\Request $oRequest - * @param \Silex\Application $oApp - * @param string $sObjectClass (Class must be instance of cmdbAbstractObject) - * @param string $sObjectId - * - * @return \Symfony\Component\HttpFoundation\Response - * - * @throws \Exception - * @throws \ArchivedObjectException - * @throws \CoreException - * @throws \DictExceptionMissingString - */ - public function ViewAction(Request $oRequest, Application $oApp, $sObjectClass, $sObjectId) + /** + * Displays an cmdbAbstractObject if the connected user is allowed to. + * + * @param \Symfony\Component\HttpFoundation\Request $oRequest + * @param string $sObjectClass (Class must be instance of cmdbAbstractObject) + * @param string $sObjectId + * + * @return \Symfony\Component\HttpFoundation\Response + * + * @throws \ArchivedObjectException + * @throws \Combodo\iTop\Portal\Brick\BrickNotFoundException + * @throws \CoreException + * @throws \DictExceptionMissingString + * @throws \OQLException + * @throws \Exception + */ + public function ViewAction(Request $oRequest, $sObjectClass, $sObjectId) { + /** @var \Combodo\iTop\Portal\Helper\RequestManipulatorHelper $oRequestManipulator */ + $oRequestManipulator = $this->get('request_manipulator'); + /** @var \Combodo\iTop\Portal\Routing\UrlGenerator $oUrlGenerator */ + $oUrlGenerator = $this->get('url_generator'); + /** @var \Combodo\iTop\Portal\Helper\ObjectFormHandlerHelper $oObjectFormHandler */ + $oObjectFormHandler = $this->get('object_form_handler'); + /** @var \Combodo\iTop\Portal\Helper\SecurityHelper $oSecurityHelper */ + $oSecurityHelper = $this->get('security_helper'); + /** @var \Combodo\iTop\Portal\Helper\ScopeValidatorHelper $oScopeValidator */ + $oScopeValidator = $this->get('scope_validator'); + /** @var \Combodo\iTop\Portal\Brick\BrickCollection $oBrickCollection */ + $oBrickCollection = $this->get('brick_collection'); + // Checking parameters if ($sObjectClass === '' || $sObjectId === '') { IssueLog::Info(__METHOD__ . ' at line ' . __LINE__ . ' : sObjectClass and sObjectId expected, "' . $sObjectClass . '" and "' . $sObjectId . '" given.'); - $oApp->abort(500, Dict::Format('UI:Error:2ParametersMissing', 'class', 'id')); + throw new HttpException(Response::HTTP_INTERNAL_SERVER_ERROR, Dict::Format('UI:Error:2ParametersMissing', 'class', 'id')); } // Checking security layers - if (!SecurityHelper::IsActionAllowed($oApp, UR_ACTION_READ, $sObjectClass, $sObjectId)) + if (!$oSecurityHelper->IsActionAllowed(UR_ACTION_READ, $sObjectClass, $sObjectId)) { IssueLog::Warning(__METHOD__ . ' at line ' . __LINE__ . ' : User #' . UserRights::GetUserId() . ' not allowed to read ' . $sObjectClass . '::' . $sObjectId . ' object.'); - $oApp->abort(404, Dict::S('UI:ObjectDoesNotExist')); + throw new HttpException(Response::HTTP_NOT_FOUND, Dict::S('UI:ObjectDoesNotExist')); } // Retrieving object - $oObject = MetaModel::GetObject($sObjectClass, $sObjectId, false /* MustBeFound */, $oApp['scope_validator']->IsAllDataAllowedForScope(UserRights::ListProfiles(), $sObjectClass)); + $oObject = MetaModel::GetObject($sObjectClass, $sObjectId, false /* MustBeFound */, $oScopeValidator->IsAllDataAllowedForScope(UserRights::ListProfiles(), $sObjectClass)); if ($oObject === null) { // We should never be there as the secuirty helper makes sure that the object exists, but just in case. IssueLog::Info(__METHOD__ . ' at line ' . __LINE__ . ' : Could not load object ' . $sObjectClass . '::' . $sObjectId . '.'); - $oApp->abort(404, Dict::S('UI:ObjectDoesNotExist')); + throw new HttpException(Response::HTTP_NOT_FOUND, Dict::S('UI:ObjectDoesNotExist')); } - $sOperation = $oApp['request_manipulator']->ReadParam('operation', ''); + $sOperation = $oRequestManipulator->ReadParam('operation', ''); $aData = array('sMode' => 'view'); - $aData['form'] = $this->HandleForm($oRequest, $oApp, $aData['sMode'], $sObjectClass, $sObjectId); + $aData['form'] = $oObjectFormHandler->HandleForm($oRequest, $aData['sMode'], $sObjectClass, $sObjectId); $aData['form']['title'] = Dict::Format('Brick:Portal:Object:Form:View:Title', MetaModel::GetName($sObjectClass), $oObject->GetName()); // Add an edit button if user is allowed - if (SecurityHelper::IsActionAllowed($oApp, UR_ACTION_MODIFY, $sObjectClass, $sObjectId)) + if ($oSecurityHelper->IsActionAllowed(UR_ACTION_MODIFY, $sObjectClass, $sObjectId)) { $oModifyButton = new URLButtonItem( 'modify_object', Dict::S('UI:Menu:Modify'), - $oApp['url_generator']->generate('p_object_edit', array('sObjectClass' => $sObjectClass, 'sObjectId' => $sObjectId)) + $oUrlGenerator->generate('p_object_edit', array('sObjectClass' => $sObjectClass, 'sObjectId' => $sObjectId)) ); // Putting this one first $aData['form']['buttons']['links'][] = $oModifyButton->GetMenuItem(); @@ -136,76 +144,88 @@ class ObjectController extends AbstractController // We have to check whether the 'operation' parameter is defined or not in order to know if the form is required via ajax (to be displayed as a modal dialog) or if it's a lifecycle call from a existing form. if (empty($sOperation)) { - $oResponse = $oApp['twig']->render('itop-portal-base/portal/src/views/bricks/object/modal.html.twig', $aData); + $oResponse = $this->render('itop-portal-base/portal/src/views/bricks/object/modal.html.twig', $aData); } else { - $oResponse = $oApp->json($aData); + $oResponse = new JsonResponse($aData); } } else { // Adding brick if it was passed - $sBrickId = $oApp['request_manipulator']->ReadParam('sBrickId', ''); + $sBrickId = $oRequestManipulator->ReadParam('sBrickId', ''); if (!empty($sBrickId)) { - $oBrick = ApplicationHelper::GetLoadedBrickFromId($oApp, $sBrickId); + $oBrick = $oBrickCollection->GetBrickById($sBrickId); if ($oBrick !== null) { $aData['oBrick'] = $oBrick; } } $aData['sPageTitle'] = $aData['form']['title']; - $oResponse = $oApp['twig']->render('itop-portal-base/portal/src/views/bricks/object/layout.html.twig', $aData); + $oResponse = $this->render('itop-portal-base/portal/src/views/bricks/object/layout.html.twig', $aData); } return $oResponse; } - /** - * @param \Symfony\Component\HttpFoundation\Request $oRequest - * @param \Silex\Application $oApp - * @param $sObjectClass - * @param $sObjectId - * - * @return \Symfony\Component\HttpFoundation\Response - * - * @throws \Exception - * @throws \ArchivedObjectException - * @throws \CoreException - * @throws \DictExceptionMissingString - */ - public function EditAction(Request $oRequest, Application $oApp, $sObjectClass, $sObjectId) + /** + * @param \Symfony\Component\HttpFoundation\Request $oRequest + * @param $sObjectClass + * @param $sObjectId + * + * @return \Symfony\Component\HttpFoundation\Response + * + * @throws \ArchivedObjectException + * @throws \Combodo\iTop\Portal\Brick\BrickNotFoundException + * @throws \CoreException + * @throws \DictExceptionMissingString + * @throws \OQLException + * @throws \Exception + */ + public function EditAction(Request $oRequest, $sObjectClass, $sObjectId) { + /** @var \Combodo\iTop\Portal\Helper\RequestManipulatorHelper $oRequestManipulator */ + $oRequestManipulator = $this->get('request_manipulator'); + /** @var \Combodo\iTop\Portal\Helper\ObjectFormHandlerHelper $oObjectFormHandler */ + $oObjectFormHandler = $this->get('object_form_handler'); + /** @var \Combodo\iTop\Portal\Helper\SecurityHelper $oSecurityHelper */ + $oSecurityHelper = $this->get('security_helper'); + /** @var \Combodo\iTop\Portal\Helper\ScopeValidatorHelper $oScopeValidator */ + $oScopeValidator = $this->get('scope_validator'); + /** @var \Combodo\iTop\Portal\Brick\BrickCollection $oBrickCollection */ + $oBrickCollection = $this->get('brick_collection'); + // Checking parameters if ($sObjectClass === '' || $sObjectId === '') { IssueLog::Info(__METHOD__ . ' at line ' . __LINE__ . ' : sObjectClass and sObjectId expected, "' . $sObjectClass . '" and "' . $sObjectId . '" given.'); - $oApp->abort(500, Dict::Format('UI:Error:2ParametersMissing', 'class', 'id')); + throw new HttpException(Response::HTTP_INTERNAL_SERVER_ERROR, Dict::Format('UI:Error:2ParametersMissing', 'class', 'id')); } // Checking security layers // Warning : This is a dirty quick fix to allow editing its own contact information $bAllowWrite = ($sObjectClass === 'Person' && $sObjectId == UserRights::GetContactId()); - if (!SecurityHelper::IsActionAllowed($oApp, UR_ACTION_MODIFY, $sObjectClass, $sObjectId) && !$bAllowWrite) + if (!$oSecurityHelper->IsActionAllowed(UR_ACTION_MODIFY, $sObjectClass, $sObjectId) && !$bAllowWrite) { IssueLog::Warning(__METHOD__ . ' at line ' . __LINE__ . ' : User #' . UserRights::GetUserId() . ' not allowed to modify ' . $sObjectClass . '::' . $sObjectId . ' object.'); - $oApp->abort(404, Dict::S('UI:ObjectDoesNotExist')); + throw new HttpException(Response::HTTP_NOT_FOUND, Dict::S('UI:ObjectDoesNotExist')); } // Retrieving object - $oObject = MetaModel::GetObject($sObjectClass, $sObjectId, false /* MustBeFound */, $oApp['scope_validator']->IsAllDataAllowedForScope(UserRights::ListProfiles(), $sObjectClass)); + $oObject = MetaModel::GetObject($sObjectClass, $sObjectId, false /* MustBeFound */, $oScopeValidator->IsAllDataAllowedForScope(UserRights::ListProfiles(), $sObjectClass)); if ($oObject === null) { // We should never be there as the secuirty helper makes sure that the object exists, but just in case. IssueLog::Info(__METHOD__ . ' at line ' . __LINE__ . ' : Could not load object ' . $sObjectClass . '::' . $sObjectId . '.'); - $oApp->abort(404, Dict::S('UI:ObjectDoesNotExist')); + throw new HttpException(Response::HTTP_NOT_FOUND, Dict::S('UI:ObjectDoesNotExist')); } - $sOperation = $oApp['request_manipulator']->ReadParam('operation', ''); + $sOperation = $oRequestManipulator->ReadParam('operation', ''); $aData = array('sMode' => 'edit'); - $aData['form'] = $this->HandleForm($oRequest, $oApp, $aData['sMode'], $sObjectClass, $sObjectId); + $aData['form'] = $oObjectFormHandler->HandleForm($oRequest, $aData['sMode'], $sObjectClass, $sObjectId); $aData['form']['title'] = Dict::Format('Brick:Portal:Object:Form:Edit:Title', MetaModel::GetName($sObjectClass), $aData['form']['object_name']); // Preparing response @@ -214,58 +234,68 @@ class ObjectController extends AbstractController // We have to check whether the 'operation' parameter is defined or not in order to know if the form is required via ajax (to be displayed as a modal dialog) or if it's a lifecycle call from a existing form. if (empty($sOperation)) { - $oResponse = $oApp['twig']->render('itop-portal-base/portal/src/views/bricks/object/modal.html.twig', $aData); + $oResponse = $this->render('itop-portal-base/portal/src/views/bricks/object/modal.html.twig', $aData); } else { - $oResponse = $oApp->json($aData); + $oResponse = new JsonResponse($aData); } } else { // Adding brick if it was passed - $sBrickId = $oApp['request_manipulator']->ReadParam('sBrickId', ''); + $sBrickId = $oRequestManipulator->ReadParam('sBrickId', ''); if (!empty($sBrickId)) { - $oBrick = ApplicationHelper::GetLoadedBrickFromId($oApp, $sBrickId); + $oBrick = $oBrickCollection->GetBrickById($sBrickId); if ($oBrick !== null) { $aData['oBrick'] = $oBrick; } } $aData['sPageTitle'] = $aData['form']['title']; - $oResponse = $oApp['twig']->render('itop-portal-base/portal/src/views/bricks/object/layout.html.twig', $aData); + $oResponse = $this->render('itop-portal-base/portal/src/views/bricks/object/layout.html.twig', $aData); } return $oResponse; } - /** - * Creates an cmdbAbstractObject of the $sObjectClass - * - * @param \Symfony\Component\HttpFoundation\Request $oRequest - * @param \Silex\Application $oApp - * @param string $sObjectClass - * - * @return \Symfony\Component\HttpFoundation\Response - * - * @throws \Exception - * @throws \CoreException - * @throws \DictExceptionMissingString - */ - public function CreateAction(Request $oRequest, Application $oApp, $sObjectClass) + /** + * Creates an cmdbAbstractObject of the $sObjectClass + * + * @param \Symfony\Component\HttpFoundation\Request $oRequest + * @param string $sObjectClass + * + * @return \Symfony\Component\HttpFoundation\Response + * + * @throws \ArchivedObjectException + * @throws \Combodo\iTop\Portal\Brick\BrickNotFoundException + * @throws \CoreException + * @throws \DictExceptionMissingString + * @throws \OQLException + */ + public function CreateAction(Request $oRequest, $sObjectClass) { + /** @var \Combodo\iTop\Portal\Helper\RequestManipulatorHelper $oRequestManipulator */ + $oRequestManipulator = $this->get('request_manipulator'); + /** @var \Combodo\iTop\Portal\Helper\ObjectFormHandlerHelper $oObjectFormHandler */ + $oObjectFormHandler = $this->get('object_form_handler'); + /** @var \Combodo\iTop\Portal\Helper\SecurityHelper $oSecurityHelper */ + $oSecurityHelper = $this->get('security_helper'); + /** @var \Combodo\iTop\Portal\Brick\BrickCollection $oBrickCollection */ + $oBrickCollection = $this->get('brick_collection'); + // Checking security layers - if (!SecurityHelper::IsActionAllowed($oApp, UR_ACTION_CREATE, $sObjectClass)) + if (!$oSecurityHelper->IsActionAllowed(UR_ACTION_CREATE, $sObjectClass)) { IssueLog::Warning(__METHOD__ . ' at line ' . __LINE__ . ' : User #' . UserRights::GetUserId() . ' not allowed to create ' . $sObjectClass . ' object.'); - $oApp->abort(404, Dict::S('UI:ObjectDoesNotExist')); + throw new HttpException(Response::HTTP_NOT_FOUND, Dict::S('UI:ObjectDoesNotExist')); } - $sOperation = $oApp['request_manipulator']->ReadParam('operation', ''); + $sOperation = $oRequestManipulator->ReadParam('operation', ''); $aData = array('sMode' => 'create'); - $aData['form'] = $this->HandleForm($oRequest, $oApp, $aData['sMode'], $sObjectClass); + $aData['form'] = $oObjectFormHandler->HandleForm($oRequest, $aData['sMode'], $sObjectClass); $aData['form']['title'] = Dict::Format('Brick:Portal:Object:Form:Create:Title', MetaModel::GetName($sObjectClass)); // Preparing response @@ -274,57 +304,58 @@ class ObjectController extends AbstractController // We have to check whether the 'operation' parameter is defined or not in order to know if the form is required via ajax (to be displayed as a modal dialog) or if it's a lifecycle call from a existing form. if (empty($sOperation)) { - $oResponse = $oApp['twig']->render('itop-portal-base/portal/src/views/bricks/object/modal.html.twig', $aData); + $oResponse = $this->render('itop-portal-base/portal/src/views/bricks/object/modal.html.twig', $aData); } else { - $oResponse = $oApp->json($aData); + $oResponse = new JsonResponse($aData); } } else { // Adding brick if it was passed - $sBrickId = $oApp['request_manipulator']->ReadParam('sBrickId', ''); + $sBrickId = $oRequestManipulator->ReadParam('sBrickId', ''); if (!empty($sBrickId)) { - $oBrick = ApplicationHelper::GetLoadedBrickFromId($oApp, $sBrickId); + $oBrick = $oBrickCollection->GetBrickById($sBrickId); if ($oBrick !== null) { $aData['oBrick'] = $oBrick; } } $aData['sPageTitle'] = $aData['form']['title']; - $oResponse = $oApp['twig']->render('itop-portal-base/portal/src/views/bricks/object/layout.html.twig', $aData); + $oResponse = $this->render('itop-portal-base/portal/src/views/bricks/object/layout.html.twig', $aData); } return $oResponse; } - /** - * Creates an cmdbAbstractObject of a class determined by the method encoded in $sEncodedMethodName. - * This method use an origin DBObject in order to determine the created cmdbAbstractObject. - * - * @param \Symfony\Component\HttpFoundation\Request $oRequest - * @param \Silex\Application $oApp - * @param string $sObjectClass Class of the origin object - * @param string $sObjectId ID of the origin object - * @param string $sEncodedMethodName Base64 encoded factory method name - * - * @return \Symfony\Component\HttpFoundation\Response - * - * @throws \Exception - * @throws \ArchivedObjectException - * @throws \CoreException - */ - public function CreateFromFactoryAction(Request $oRequest, Application $oApp, $sObjectClass, $sObjectId, $sEncodedMethodName) + /** + * Creates an cmdbAbstractObject of a class determined by the method encoded in $sEncodedMethodName. + * This method use an origin DBObject in order to determine the created cmdbAbstractObject. + * + * @param \Symfony\Component\HttpFoundation\Request $oRequest + * @param string $sObjectClass Class of the origin object + * @param string $sObjectId ID of the origin object + * @param string $sEncodedMethodName Base64 encoded factory method name + * + * @return \Symfony\Component\HttpFoundation\Response + * + * @throws \ArchivedObjectException + * @throws \CoreException + */ + public function CreateFromFactoryAction(Request $oRequest, $sObjectClass, $sObjectId, $sEncodedMethodName) { + /** @var \Combodo\iTop\Portal\Routing\UrlGenerator $oUrlGenerator */ + $oUrlGenerator = $this->get('url_generator'); + $sMethodName = base64_decode($sEncodedMethodName); // Checking that the factory method is valid if (!is_callable($sMethodName)) { IssueLog::Error(__METHOD__ . ' at line ' . __LINE__ . ' : Invalid factory method "' . $sMethodName . '" used when creating an object.'); - $oApp->abort(500, 'Invalid factory method "' . $sMethodName . '" used when creating an object'); + throw new HttpException(Response::HTTP_INTERNAL_SERVER_ERROR, 'Invalid factory method "' . $sMethodName . '" used when creating an object'); } // Retrieving origin object @@ -349,57 +380,69 @@ class ObjectController extends AbstractController $aRouteParams = array( 'sObjectClass' => get_class($oTargetObject) ); - $sRedirectRoute = $oApp['url_generator']->generate('p_object_create', $aRouteParams); + $sRedirectRoute = $oUrlGenerator->generate('p_object_create', $aRouteParams); // - Request $oSubRequest = Request::create($sRedirectRoute, 'GET', $oRequest->query->all(), $oRequest->cookies->all(), array(), $oRequest->server->all()); + // TODO: How do we do that in Symfony? return $oApp->handle($oSubRequest, HttpKernelInterface::SUB_REQUEST, true); } - /** - * Applies a stimulus $sStimulus on an cmdbAbstractObject - * - * @param \Symfony\Component\HttpFoundation\Request $oRequest - * @param \Silex\Application $oApp - * @param string $sObjectClass - * @param string $sObjectId - * @param string $sStimulusCode - * - * @return \Symfony\Component\HttpFoundation\Response - * - * @throws \Exception - * @throws \ArchivedObjectException - * @throws \CoreException - */ - public function ApplyStimulusAction(Request $oRequest, Application $oApp, $sObjectClass, $sObjectId, $sStimulusCode) + /** + * Applies a stimulus $sStimulus on an cmdbAbstractObject + * + * @param \Symfony\Component\HttpFoundation\Request $oRequest + * @param string $sObjectClass + * @param string $sObjectId + * @param string $sStimulusCode + * + * @return \Symfony\Component\HttpFoundation\Response + * + * @throws \ArchivedObjectException + * @throws \CoreException + * @throws \OQLException + * @throws \Exception + */ + public function ApplyStimulusAction(Request $oRequest, $sObjectClass, $sObjectId, $sStimulusCode) { + /** @var \Combodo\iTop\Portal\Helper\RequestManipulatorHelper $oRequestManipulator */ + $oRequestManipulator = $this->get('request_manipulator'); + /** @var \Combodo\iTop\Portal\Routing\UrlGenerator $oUrlGenerator */ + $oUrlGenerator = $this->get('url_generator'); + /** @var \Combodo\iTop\Portal\Helper\ObjectFormHandlerHelper $oObjectFormHandler */ + $oObjectFormHandler = $this->get('object_form_handler'); + /** @var \Combodo\iTop\Portal\Helper\SecurityHelper $oSecurityHelper */ + $oSecurityHelper = $this->get('security_helper'); + /** @var \Combodo\iTop\Portal\Helper\ScopeValidatorHelper $oScopeValidator */ + $oScopeValidator = $this->get('scope_validator'); + // Checking parameters if ($sObjectClass === '' || $sObjectId === '' || $sStimulusCode === '') { IssueLog::Info(__METHOD__ . ' at line ' . __LINE__ . ' : sObjectClass, sObjectId and $sStimulusCode expected, "' . $sObjectClass . '", "' . $sObjectId . '" and "' . $sStimulusCode . '" given.'); - $oApp->abort(500, Dict::Format('UI:Error:3ParametersMissing', 'class', 'id', 'stimulus')); + throw new HttpException(Response::HTTP_INTERNAL_SERVER_ERROR, Dict::Format('UI:Error:3ParametersMissing', 'class', 'id', 'stimulus')); } // Checking security layers - if(!SecurityHelper::IsStimulusAllowed($oApp, $sStimulusCode, $sObjectClass)) + if(!$oSecurityHelper->IsStimulusAllowed($sStimulusCode, $sObjectClass)) { - $oApp->abort(404, Dict::S('UI:ObjectDoesNotExist')); + throw new HttpException(Response::HTTP_NOT_FOUND, Dict::S('UI:ObjectDoesNotExist')); } // Retrieving object - $oObject = MetaModel::GetObject($sObjectClass, $sObjectId, false /* MustBeFound */, $oApp['scope_validator']->IsAllDataAllowedForScope(UserRights::ListProfiles(), $sObjectClass)); + $oObject = MetaModel::GetObject($sObjectClass, $sObjectId, false /* MustBeFound */, $oScopeValidator->IsAllDataAllowedForScope(UserRights::ListProfiles(), $sObjectClass)); if ($oObject === null) { // We should never be there as the secuirty helper makes sure that the object exists, but just in case. IssueLog::Info(__METHOD__ . ' at line ' . __LINE__ . ' : Could not load object ' . $sObjectClass . '::' . $sObjectId . '.'); - $oApp->abort(404, Dict::S('UI:ObjectDoesNotExist')); + throw new HttpException(Response::HTTP_NOT_FOUND, Dict::S('UI:ObjectDoesNotExist')); } // Retrieving request parameters - $sOperation = $oApp['request_manipulator']->ReadParam('operation', ''); + $sOperation = $oRequestManipulator->ReadParam('operation', ''); // Retrieving form properties - $aStimuliForms = ApplicationHelper::GetLoadedFormFromClass($oApp, $sObjectClass, 'apply_stimulus'); + $aStimuliForms = ApplicationHelper::GetLoadedFormFromClass($this->getParameter('combodo.portal.instance.conf')['forms'], $sObjectClass, 'apply_stimulus'); if(array_key_exists($sStimulusCode, $aStimuliForms)) { $aFormProperties = $aStimuliForms[$sStimulusCode]; @@ -423,10 +466,10 @@ class ObjectController extends AbstractController $oRequest->request->set('apply_stimulus', array('code' => $sStimulusCode)); $aData = array('sMode' => 'apply_stimulus'); - $aData['form'] = $this->HandleForm($oRequest, $oApp, $aData['sMode'], $sObjectClass, $sObjectId, $aFormProperties); + $aData['form'] = $oObjectFormHandler->HandleForm($oRequest, $aData['sMode'], $sObjectClass, $sObjectId, $aFormProperties); $aData['form']['title'] = Dict::Format('Brick:Portal:Object:Form:Stimulus:Title'); $aData['form']['validation']['redirection'] = array( - 'url' => $oApp['url_generator']->generate('p_object_edit', array('sObjectClass' => $sObjectClass, 'sObjectId' => $sObjectId)) + 'url' => $oUrlGenerator->generate('p_object_edit', array('sObjectClass' => $sObjectClass, 'sObjectId' => $sObjectId)) ); // TODO : This is a ugly patch to avoid showing a modal with a readonly form to the user as it would prevent user from finishing the transition. @@ -442,10 +485,10 @@ class ObjectController extends AbstractController $oSubRequest->request->set('stimulus_code', ''); $aData = array('sMode' => 'apply_stimulus'); - $aData['form'] = $this->HandleForm($oSubRequest, $oApp, $aData['sMode'], $sObjectClass, $sObjectId, $aFormProperties); + $aData['form'] = $oObjectFormHandler->HandleForm($oSubRequest, $aData['sMode'], $sObjectClass, $sObjectId, $aFormProperties); // Redefining the array to be as simple as possible : $aData = array('redirection' => - array('url' => $oApp['url_generator']->generate('p_object_edit', array('sObjectClass' => $sObjectClass, 'sObjectId' => $sObjectId))) + array('url' => $oUrlGenerator->generate('p_object_edit', array('sObjectClass' => $sObjectClass, 'sObjectId' => $sObjectId))) ); } } @@ -456,300 +499,54 @@ class ObjectController extends AbstractController // We have to check whether the 'operation' parameter is defined or not in order to know if the form is required via ajax (to be displayed as a modal dialog) or if it's a lifecycle call from a existing form. if (empty($sOperation)) { - $oResponse = $oApp['twig']->render('itop-portal-base/portal/src/views/bricks/object/modal.html.twig', $aData); + $oResponse = $this->render('itop-portal-base/portal/src/views/bricks/object/modal.html.twig', $aData); } elseif ($sOperation === 'redirect') { - $oResponse = $oApp['twig']->render('itop-portal-base/portal/src/views/modal/mode_loader.html.twig', $aData); + $oResponse = $this->render('itop-portal-base/portal/src/views/modal/mode_loader.html.twig', $aData); } else { - $oResponse = $oApp->json($aData); + $oResponse = new JsonResponse($aData); } } else { - $oResponse = $oApp['twig']->render('itop-portal-base/portal/src/views/bricks/object/layout.html.twig', $aData); + $oResponse = $this->render('itop-portal-base/portal/src/views/bricks/object/layout.html.twig', $aData); } return $oResponse; } - /** - * @param \Symfony\Component\HttpFoundation\Request $oRequest - * @param \Silex\Application $oApp - * @param string $sMode - * @param string $sObjectClass - * @param string $sObjectId - * @param string $aFormProperties - * - * @return array - * - * @throws \Exception - * @throws \ArchivedObjectException - * @throws \CoreException - * @throws \OQLException - * @throws \Twig_Error_Loader - * @throws \Twig_Error_Runtime - * @throws \Twig_Error_Syntax - */ - public static function HandleForm(Request $oRequest, Application $oApp, $sMode, $sObjectClass, $sObjectId = null, $aFormProperties = null) + /** + * Handles the autocomplete search + * + * @param \Symfony\Component\HttpFoundation\Request $oRequest + * @param string $sTargetAttCode Attribute code of the host object pointing to the Object class to + * search + * @param string $sHostObjectClass Class name of the host object + * @param string $sHostObjectId Id of the host object + * + * @return \Symfony\Component\HttpFoundation\JsonResponse + * + * @throws \ArchivedObjectException + * @throws \CoreException + * @throws \CoreUnexpectedValue + * @throws \MySQLException + * @throws \OQLException + * @throws \Exception + */ + public function SearchAutocompleteAction(Request $oRequest, $sTargetAttCode, $sHostObjectClass, $sHostObjectId = null) { - $aFormData = array(); - $sOperation = $oApp['request_manipulator']->ReadParam('operation', ''); - $bModal = ($oRequest->isXmlHttpRequest() && empty($sOperation)); + /** @var \Combodo\iTop\Portal\Helper\RequestManipulatorHelper $oRequestManipulator */ + $oRequestManipulator = $this->get('request_manipulator'); + /** @var \Combodo\iTop\Portal\Helper\ContextManipulatorHelper $oContextManipulator */ + $oContextManipulator = $this->get('context_manipulator'); + /** @var \Combodo\iTop\Portal\Helper\SecurityHelper $oSecurityHelper */ + $oSecurityHelper = $this->get('security_helper'); + /** @var \Combodo\iTop\Portal\Helper\ScopeValidatorHelper $oScopeValidator */ + $oScopeValidator = $this->get('scope_validator'); - // - Retrieve form properties - if ($aFormProperties === null) - { - $aFormProperties = ApplicationHelper::GetLoadedFormFromClass($oApp, $sObjectClass, $sMode); - } - - // - Create and - if (empty($sOperation)) - { - // Retrieving action rules - // - // Note : The action rules must be a base64-encoded JSON object, this is just so users are tempted to changes values. - // But it would not be a security issue as it only presets values in the form. - $sActionRulesToken = $oApp['request_manipulator']->ReadParam('ar_token', ''); - $aActionRules = (!empty($sActionRulesToken)) ? ContextManipulatorHelper::DecodeRulesToken($sActionRulesToken) : array(); - - // Preparing object - if ($sObjectId === null) - { - // Create new UserRequest - $oObject = MetaModel::NewObject($sObjectClass); - - // Retrieve action rules information to auto-fill the form if available - // Preparing object - $oApp['context_manipulator']->PrepareObject($aActionRules, $oObject); - $aPrefillFormParam = array( 'user' => $_SESSION["auth_user"], - 'origin' => 'portal'); - $oObject->PrefillForm('creation_from_0', $aPrefillFormParam); - } - else - { - $oObject = MetaModel::GetObject($sObjectClass, $sObjectId, true, $oApp['scope_validator']->IsAllDataAllowedForScope(UserRights::ListProfiles(), $sObjectClass)); - } - - // Preparing buttons - $aFormData['buttons'] = array( - 'transitions' => array(), - 'actions' => array(), - 'links' => array(), - 'submit' => array( - 'label' => Dict::S('Portal:Button:Submit'), - ), - ); - if ($sMode !== 'apply_stimulus') - { - // Add transition buttons - $oSetToCheckRights = DBObjectSet::FromObject($oObject); - $aStimuli = Metamodel::EnumStimuli($sObjectClass); - foreach ($oObject->EnumTransitions() as $sStimulusCode => $aTransitionDef) - { - if(SecurityHelper::IsStimulusAllowed($oApp, $sStimulusCode, $sObjectClass, $oSetToCheckRights)) - { - $aFormData['buttons']['transitions'][$sStimulusCode] = $aStimuli[$sStimulusCode]->GetLabel(); - } - } - - // Add plugin buttons - foreach (MetaModel::EnumPlugins('iPopupMenuExtension') as $oExtensionInstance) - { - foreach($oExtensionInstance->EnumItems(iPopupMenuExtension::PORTAL_OBJDETAILS_ACTIONS, array('portal_id' => $oApp['combodo.portal.instance.id'], 'object' => $oObject, 'mode' => $sMode)) as $oMenuItem) - { - if (is_object($oMenuItem)) - { - if($oMenuItem instanceof JSButtonItem) - { - $aFormData['buttons']['actions'][] = $oMenuItem->GetMenuItem() + array('js_files' => $oMenuItem->GetLinkedScripts()); - } - elseif($oMenuItem instanceof URLButtonItem) - { - $aFormData['buttons']['links'][] = $oMenuItem->GetMenuItem(); - } - } - } - } - - // Hiding submit button or changing its label if necessary - if(!empty($aFormData['buttons']['transitions']) && isset($aFormProperties['properties']) &&$aFormProperties['properties']['always_show_submit'] === false) - { - unset($aFormData['buttons']['submit']); - } - elseif($sMode === static::ENUM_MODE_EDIT) - { - $aFormData['buttons']['submit']['label'] = Dict::S('Portal:Button:Apply'); - } - } - else - { - $aPrefillFormParam = array( - 'user' => $_SESSION["auth_user"], - 'origin' => 'portal', - 'stimulus' => $oApp['request_manipulator']->ReadParam('apply_stimulus', null)['code'], - ); - $oObject->PrefillForm('state_change', $aPrefillFormParam); - } - - // Preparing callback urls - $aCallbackUrls = $oApp['context_manipulator']->GetCallbackUrls($oApp, $aActionRules, $oObject, $bModal); - $aFormData['submit_callback'] = $aCallbackUrls['submit']; - $aFormData['cancel_callback'] = $aCallbackUrls['cancel']; - - // Preparing renderer - // Note : We might need to distinguish form & renderer endpoints - if (in_array($sMode, array('create', 'edit', 'view'))) - { - $sFormEndpoint = $oApp['url_generator']->generate('p_object_' . $sMode, array('sObjectClass' => $sObjectClass, 'sObjectId' => $sObjectId)); - } - else - { - $sFormEndpoint = $_SERVER['REQUEST_URI']; - } - $oFormRenderer = new BsFormRenderer(); - $oFormRenderer->SetEndpoint($sFormEndpoint); - - $oFormManager = new ObjectFormManager(); - $oFormManager->SetApplication($oApp) - ->SetObject($oObject) - ->SetMode($sMode) - ->SetActionRulesToken($sActionRulesToken) - ->SetRenderer($oFormRenderer) - ->SetFormProperties($aFormProperties); - - $oFormManager->Build(); - - // Check the number of editable fields - $aFormData['editable_fields_count'] = $oFormManager->GetForm()->GetEditableFieldCount(); - } - else - { - // Update / Submit / Cancel - $sFormManagerClass = $oApp['request_manipulator']->ReadParam('formmanager_class', '', FILTER_UNSAFE_RAW); - $sFormManagerData = $oApp['request_manipulator']->ReadParam('formmanager_data', '', FILTER_UNSAFE_RAW); - if ( empty($sFormManagerClass) || empty($sFormManagerData) ) - { - IssueLog::Error(__METHOD__ . ' at line ' . __LINE__ . ' : Parameters formmanager_class and formamanager_data must be defined.'); - $oApp->abort(500, 'Parameters formmanager_class and formmanager_data must be defined.'); - } - - $oFormManager = $sFormManagerClass::FromJSON($sFormManagerData); - $oFormManager->SetApplication($oApp); - - // Applying action rules if present - if (($oFormManager->GetActionRulesToken() !== null) && ($oFormManager->GetActionRulesToken() !== '')) - { - $aActionRules = ContextManipulatorHelper::DecodeRulesToken($oFormManager->GetActionRulesToken()); - $oObj = $oFormManager->GetObject(); - $oApp['context_manipulator']->PrepareObject($aActionRules, $oObj); - $oFormManager->SetObject($oObj); - } - - switch ($sOperation) - { - case 'submit': - // Applying modification to object - $aFormData['validation'] = $oFormManager->OnSubmit(array('currentValues' => $oApp['request_manipulator']->ReadParam('current_values', array(), FILTER_UNSAFE_RAW), 'attachmentIds' => $oApp['request_manipulator']->ReadParam('attachment_ids', array(), FILTER_UNSAFE_RAW), 'formProperties' => $aFormProperties, 'applyStimulus' => $oApp['request_manipulator']->ReadParam('apply_stimulus', null))); - 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 - // Check if stimulus has to be applied - $sStimulusCode = $oApp['request_manipulator']->ReadParam('stimulus_code', ''); - if (!empty($sStimulusCode)) - { - $aFormData['validation']['redirection'] = array( - 'url' => $oApp['url_generator']->generate('p_object_apply_stimulus', array('sObjectClass' => $sObjectClass, 'sObjectId' => $oFormManager->GetObject()->GetKey(), 'sStimulusCode' => $sStimulusCode)), - 'ajax' => true - ); - } - // Otherwise, we show the object if there is no default -// else -// { -// $aFormData['validation']['redirection'] = array( -// 'alternative_url' => $oApp['url_generator']->generate('p_object_edit', array('sObjectClass' => $sObjectClass, 'sObjectId' => $oFormManager->GetObject()->GetKey())) -// ); -// } - } - break; - - case 'update': - $oFormManager->OnUpdate(array('currentValues' => $oApp['request_manipulator']->ReadParam('current_values', array(), FILTER_UNSAFE_RAW), 'formProperties' => $aFormProperties)); - break; - - case 'cancel': - $oFormManager->OnCancel(); - break; - } - } - - // Preparing field_set data - $aFieldSetData = array( - //'fields_list' => $oFormManager->GetRenderer()->Render(), // GLA : This should be done just after in the if statement. - 'fields_impacts' => $oFormManager->GetForm()->GetFieldsImpacts(), - 'form_path' => $oFormManager->GetForm()->GetId() - ); - - // Preparing fields list regarding the operation - if ($sOperation === 'update') - { - $aRequestedFields = $oApp['request_manipulator']->ReadParam('requested_fields', array(), FILTER_UNSAFE_RAW); - $sFormPath = $oApp['request_manipulator']->ReadParam('form_path', ''); - - // Checking if the update was on a subform, if so we need to make the rendering for that part only - if ( !empty($sFormPath) && $sFormPath !== $oFormManager->GetForm()->GetId() ) - { - $oSubForm = $oFormManager->GetForm()->FindSubForm($sFormPath); - $oSubFormRenderer = new BsFormRenderer($oSubForm); - $oSubFormRenderer->SetEndpoint($oFormManager->GetRenderer()->GetEndpoint()); - $aFormData['updated_fields'] = $oSubFormRenderer->Render($aRequestedFields); - } - else - { - $aFormData['updated_fields'] = $oFormManager->GetRenderer()->Render($aRequestedFields); - } - } - else - { - $aFieldSetData['fields_list'] = $oFormManager->GetRenderer()->Render(); - } - - // Preparing form data - $aFormData['id'] = $oFormManager->GetForm()->GetId(); - $aFormData['transaction_id'] = $oFormManager->GetForm()->GetTransactionId(); - $aFormData['formmanager_class'] = $oFormManager->GetClass(); - $aFormData['formmanager_data'] = $oFormManager->ToJSON(); - $aFormData['renderer'] = $oFormManager->GetRenderer(); - $aFormData['object_name'] = $oFormManager->GetObject()->GetName(); - $aFormData['object_class'] = get_class($oFormManager->GetObject()); - $aFormData['object_id'] = $oFormManager->GetObject()->GetKey(); - $aFormData['object_state'] = $oFormManager->GetObject()->GetState(); - $aFormData['fieldset'] = $aFieldSetData; - $aFormData['display_mode'] = (isset($aFormProperties['properties'])) ? $aFormProperties['properties']['display_mode'] : ApplicationHelper::FORM_DEFAULT_DISPLAY_MODE; - - return $aFormData; - } - - /** - * Handles the autocomplete search - * - * @param \Symfony\Component\HttpFoundation\Request $oRequest - * @param \Silex\Application $oApp - * @param string $sTargetAttCode Attribute code of the host object pointing to the Object class to search - * @param string $sHostObjectClass Class name of the host object - * @param string $sHostObjectId Id of the host object - * - * @return \Symfony\Component\HttpFoundation\Response - * - * @throws \Exception - * @throws \ArchivedObjectException - * @throws \CoreException - * @throws \OQLException - */ - public function SearchAutocompleteAction(Request $oRequest, Application $oApp, $sTargetAttCode, $sHostObjectClass, $sHostObjectId = null) - { $aData = array( 'results' => array( 'count' => 0, @@ -764,7 +561,7 @@ class ObjectController extends AbstractController if (!isset($aRequestContent['sQuery'])) { IssueLog::Error(__METHOD__ . ' at line ' . __LINE__ . ' : Parameter sQuery missing.'); - $oApp->abort(500, Dict::Format('UI:Error:ParameterMissing', 'sQuery')); + throw new HttpException(Response::HTTP_INTERNAL_SERVER_ERROR, Dict::Format('UI:Error:ParameterMissing', 'sQuery')); } // Retrieving parameters @@ -772,10 +569,10 @@ class ObjectController extends AbstractController $sFieldId = $aRequestContent['sFieldId']; // Checking security layers - if (!SecurityHelper::IsActionAllowed($oApp, UR_ACTION_READ, $sHostObjectClass, $sHostObjectId)) + if (!$oSecurityHelper->IsActionAllowed(UR_ACTION_READ, $sHostObjectClass, $sHostObjectId)) { IssueLog::Warning(__METHOD__ . ' at line ' . __LINE__ . ' : Could not load object ' . $sHostObjectClass . '::' . $sHostObjectId . '.'); - $oApp->abort(404, Dict::S('UI:ObjectDoesNotExist')); + throw new HttpException(Response::HTTP_NOT_FOUND, Dict::S('UI:ObjectDoesNotExist')); } // Retrieving host object for future DBSearch parameters @@ -791,10 +588,10 @@ class ObjectController extends AbstractController // // Note : The action rules must be a base64-encoded JSON object, this is just so users are tempted to changes values. // But it would not be a security issue as it only presets values in the form. - $sActionRulesToken = $oApp['request_manipulator']->ReadParam('ar_token', ''); - $aActionRules = (!empty($sActionRulesToken)) ? ContextManipulatorHelper::DecodeRulesToken($sActionRulesToken) : array(); + $sActionRulesToken = $oRequestManipulator->ReadParam('ar_token', ''); + $aActionRules = (!empty($sActionRulesToken)) ? $oContextManipulator->DecodeRulesToken($sActionRulesToken) : array(); // Preparing object - $oApp['context_manipulator']->PrepareObject($aActionRules, $oHostObject); + $oContextManipulator->PrepareObject($aActionRules, $oHostObject); } // Updating host object with form data / values @@ -802,8 +599,9 @@ class ObjectController extends AbstractController $sFormManagerData = $aRequestContent['formmanager_data']; if (!empty($sFormManagerClass) && !empty($sFormManagerData)) { + /** @var \Combodo\iTop\Portal\Form\ObjectFormManager $oFormManager */ $oFormManager = $sFormManagerClass::FromJSON($sFormManagerData); - $oFormManager->SetApplication($oApp); + $oFormManager->SetApplication($oApp); // TODO: How do we do that? $oFormManager->SetObject($oHostObject); // Applying action rules if present @@ -811,7 +609,7 @@ class ObjectController extends AbstractController { $aActionRules = ContextManipulatorHelper::DecodeRulesToken($oFormManager->GetActionRulesToken()); $oObj = $oFormManager->GetObject(); - $oApp['context_manipulator']->PrepareObject($aActionRules, $oObj); + $oContextManipulator->PrepareObject($aActionRules, $oObj); $oFormManager->SetObject($oObj); } @@ -826,6 +624,7 @@ class ObjectController extends AbstractController if ($oTargetAttDef->GetEditClass() === 'CustomFields') { $oRequestTemplate = $oHostObject->Get($sTargetAttCode); + /** @var \DBSearch $oTemplateFieldSearch */ $oTemplateFieldSearch = $oRequestTemplate->GetForm()->GetField('user_data')->GetForm()->GetField($sFieldId)->GetSearch(); $sTargetObjectClass = $oTemplateFieldSearch->GetClass(); } @@ -850,10 +649,10 @@ class ObjectController extends AbstractController $oSearch->AddConditionExpression(new BinaryExpression(new FieldExpression('friendlyname', $oSearch->GetClassAlias()), 'LIKE', new VariableExpression('ac_query'))); // - Intersecting with scope constraints // Note : This do NOT apply to custom fields as the portal administrator is not supposed to know which objects will be put in the templates. - // It is the responsability of the template designer to write the right query so the user see only what he should. + // It is the responsibility of the template designer to write the right query so the user see only what he should. if ($oTargetAttDef->GetEditClass() !== 'CustomFields') { - $oScopeSearch = $oApp['scope_validator']->GetScopeFilterForProfiles(UserRights::ListProfiles(), $sTargetObjectClass, UR_ACTION_READ); + $oScopeSearch = $oScopeValidator->GetScopeFilterForProfiles(UserRights::ListProfiles(), $sTargetObjectClass, UR_ACTION_READ); $oSearch = $oSearch->Intersect($oScopeSearch); // - Allowing all data if necessary if ($oScopeSearch->IsAllDataAllowed()) @@ -885,48 +684,62 @@ class ObjectController extends AbstractController // Preparing response if ($oRequest->isXmlHttpRequest()) { - $oResponse = $oApp->json($aData); + $oResponse = new JsonResponse($aData); } else { - $oResponse = $oApp->abort(404, Dict::S('UI:ObjectDoesNotExist')); + throw new HttpException(Response::HTTP_NOT_FOUND, Dict::S('UI:ObjectDoesNotExist')); } return $oResponse; } - /** - * Handles the regular (table) search from an attribute - * - * @param \Symfony\Component\HttpFoundation\Request $oRequest - * @param \Silex\Application $oApp - * @param string $sTargetAttCode Attribute code of the host object pointing to the Object class to search - * @param string $sHostObjectClass Class name of the host object - * @param string $sHostObjectId Id of the host object - * - * @return \Symfony\Component\HttpFoundation\Response - * - * @throws \Exception - * @throws \ArchivedObjectException - * @throws \CoreException - * @throws \DictExceptionMissingString - * @throws \OQLException - */ - public function SearchFromAttributeAction(Request $oRequest, Application $oApp, $sTargetAttCode, $sHostObjectClass, $sHostObjectId = null) + /** + * Handles the regular (table) search from an attribute + * + * @param \Symfony\Component\HttpFoundation\Request $oRequest + * @param string $sTargetAttCode Attribute code of the host object pointing to the Object class to + * search + * @param string $sHostObjectClass Class name of the host object + * @param string $sHostObjectId Id of the host object + * + * @return \Symfony\Component\HttpFoundation\Response + * + * @throws \ArchivedObjectException + * @throws \CoreException + * @throws \CoreUnexpectedValue + * @throws \DictExceptionMissingString + * @throws \MissingQueryArgument + * @throws \MySQLException + * @throws \MySQLHasGoneAwayException + * @throws \OQLException + * @throws \Exception + */ + public function SearchFromAttributeAction(Request $oRequest, $sTargetAttCode, $sHostObjectClass, $sHostObjectId = null) { + /** @var \Combodo\iTop\Portal\Helper\RequestManipulatorHelper $oRequestManipulator */ + $oRequestManipulator = $this->get('request_manipulator'); + /** @var \Combodo\iTop\Portal\Helper\ContextManipulatorHelper $oContextManipulator */ + $oContextManipulator = $this->get('context_manipulator'); + /** @var \Combodo\iTop\Portal\Helper\SecurityHelper $oSecurityHelper */ + $oSecurityHelper = $this->get('security_helper'); + /** @var \Combodo\iTop\Portal\Helper\ScopeValidatorHelper $oScopeValidator */ + $oScopeValidator = $this->get('scope_validator'); + + $aData = array( 'sMode' => 'search_regular', 'sTargetAttCode' => $sTargetAttCode, 'sHostObjectClass' => $sHostObjectClass, 'sHostObjectId' => $sHostObjectId, - 'sActionRulesToken' => $oApp['request_manipulator']->ReadParam('ar_token', ''), + 'sActionRulesToken' => $oRequestManipulator->ReadParam('ar_token', ''), ); // Checking security layers - if (!SecurityHelper::IsActionAllowed($oApp, UR_ACTION_READ, $sHostObjectClass, $sHostObjectId)) + if (!$oSecurityHelper->IsActionAllowed(UR_ACTION_READ, $sHostObjectClass, $sHostObjectId)) { IssueLog::Warning(__METHOD__ . ' at line ' . __LINE__ . ' : User #' . UserRights::GetUserId() . ' not allowed to read ' . $sHostObjectClass . '::' . $sHostObjectId . ' object.'); - $oApp->abort(404, Dict::S('UI:ObjectDoesNotExist')); + throw new HttpException(Response::HTTP_NOT_FOUND, Dict::S('UI:ObjectDoesNotExist')); } // Retrieving host object for future DBSearch parameters @@ -944,16 +757,17 @@ class ObjectController extends AbstractController // But it would not be a security issue as it only presets values in the form. $aActionRules = !empty($aData['sActionRulesToken']) ? ContextManipulatorHelper::DecodeRulesToken($aData['sActionRulesToken']) : array(); // Preparing object - $oApp['context_manipulator']->PrepareObject($aActionRules, $oHostObject); + $oContextManipulator->PrepareObject($aActionRules, $oHostObject); } // Updating host object with form data / values - $sFormManagerClass = $oApp['request_manipulator']->ReadParam('formmanager_class', '', FILTER_UNSAFE_RAW); - $sFormManagerData = $oApp['request_manipulator']->ReadParam('formmanager_data', '', FILTER_UNSAFE_RAW); + $sFormManagerClass = $oRequestManipulator->ReadParam('formmanager_class', '', FILTER_UNSAFE_RAW); + $sFormManagerData = $oRequestManipulator->ReadParam('formmanager_data', '', FILTER_UNSAFE_RAW); if ( !empty($sFormManagerClass) && !empty($sFormManagerData) ) { + /** @var \Combodo\iTop\Portal\Form\ObjectFormManager $oFormManager */ $oFormManager = $sFormManagerClass::FromJSON($sFormManagerData); - $oFormManager->SetApplication($oApp); + $oFormManager->SetApplication($oApp); // TODO: How do we do that? $oFormManager->SetObject($oHostObject); // Applying action rules if present @@ -961,39 +775,43 @@ class ObjectController extends AbstractController { $aActionRules = ContextManipulatorHelper::DecodeRulesToken($oFormManager->GetActionRulesToken()); $oObj = $oFormManager->GetObject(); - $oApp['context_manipulator']->PrepareObject($aActionRules, $oObj); + $oContextManipulator->PrepareObject($aActionRules, $oObj); $oFormManager->SetObject($oObj); } // Updating host object - $oFormManager->OnUpdate(array('currentValues' => $oApp['request_manipulator']->ReadParam('current_values', array(), FILTER_UNSAFE_RAW))); + $oFormManager->OnUpdate(array('currentValues' => $oRequestManipulator->ReadParam('current_values', array(), FILTER_UNSAFE_RAW))); $oHostObject = $oFormManager->GetObject(); } // Retrieving request parameters - $iPageNumber = $oApp['request_manipulator']->ReadParam('iPageNumber', static::DEFAULT_PAGE_NUMBER, FILTER_SANITIZE_NUMBER_INT); - $iListLength = $oApp['request_manipulator']->ReadParam('iListLength', static::DEFAULT_LIST_LENGTH, FILTER_SANITIZE_NUMBER_INT); - $bInitalPass = $oApp['request_manipulator']->HasParam('draw') ? false : true; - $sQuery = $oApp['request_manipulator']->ReadParam('sSearchValue', ''); - $sFormPath = $oApp['request_manipulator']->ReadParam('sFormPath', ''); - $sFieldId = $oApp['request_manipulator']->ReadParam('sFieldId', ''); - $aObjectIdsToIgnore = $oApp['request_manipulator']->ReadParam('aObjectIdsToIgnore', null, FILTER_UNSAFE_RAW); + $iPageNumber = $oRequestManipulator->ReadParam('iPageNumber', static::DEFAULT_PAGE_NUMBER, FILTER_SANITIZE_NUMBER_INT); + $iListLength = $oRequestManipulator->ReadParam('iListLength', static::DEFAULT_LIST_LENGTH, FILTER_SANITIZE_NUMBER_INT); + $bInitialPass = $oRequestManipulator->HasParam('draw') ? false : true; + $sQuery = $oRequestManipulator->ReadParam('sSearchValue', ''); + $sFormPath = $oRequestManipulator->ReadParam('sFormPath', ''); + $sFieldId = $oRequestManipulator->ReadParam('sFieldId', ''); + $aObjectIdsToIgnore = $oRequestManipulator->ReadParam('aObjectIdsToIgnore', null, FILTER_UNSAFE_RAW); // Building search query // - Retrieving target object class from attcode $oTargetAttDef = MetaModel::GetAttributeDef($sHostObjectClass, $sTargetAttCode); if ($oTargetAttDef->IsExternalKey()) { + /** @var \AttributeExternalKey $oTargetAttDef */ $sTargetObjectClass = $oTargetAttDef->GetTargetClass(); } elseif ($oTargetAttDef->IsLinkSet()) { + /** @var \AttributeLinkedSet $oTargetAttDef */ if (!$oTargetAttDef->IsIndirect()) { $sTargetObjectClass = $oTargetAttDef->GetLinkedClass(); } else { + /** @var \AttributeLinkedSetIndirect $oTargetAttDef */ + /** @var \AttributeExternalKey $oRemoteAttDef */ $oRemoteAttDef = MetaModel::GetAttributeDef($oTargetAttDef->GetLinkedClass(), $oTargetAttDef->GetExtKeyToRemote()); $sTargetObjectClass = $oRemoteAttDef->GetTargetClass(); } @@ -1001,6 +819,7 @@ class ObjectController extends AbstractController elseif ($oTargetAttDef->GetEditClass() === 'CustomFields') { $oRequestTemplate = $oHostObject->Get($sTargetAttCode); + /** @var \DBSearch $oTemplateFieldSearch */ $oTemplateFieldSearch = $oRequestTemplate->GetForm()->GetField('user_data')->GetForm()->GetField($sFieldId)->GetSearch(); $sTargetObjectClass = $oTemplateFieldSearch->GetClass(); } @@ -1010,7 +829,7 @@ class ObjectController extends AbstractController } // - Retrieving class attribute list - $aAttCodes = ApplicationHelper::GetLoadedListFromClass($oApp, $sTargetObjectClass, 'list'); + $aAttCodes = ApplicationHelper::GetLoadedListFromClass($sTargetObjectClass, 'list'); // - Adding friendlyname attribute to the list is not already in it $sTitleAttCode = 'friendlyname'; if (($sTitleAttCode !== null) && !in_array($sTitleAttCode, $aAttCodes)) @@ -1020,16 +839,17 @@ class ObjectController extends AbstractController // - Retrieving scope search // Note : This do NOT apply to custom fields as the portal administrator is not supposed to know which objects will be put in the templates. - // It is the responsability of the template designer to write the right query so the user see only what he should. - $oScopeSearch = $oApp['scope_validator']->GetScopeFilterForProfiles(UserRights::ListProfiles(), $sTargetObjectClass, UR_ACTION_READ); + // It is the responsibility of the template designer to write the right query so the user see only what he should. + $oScopeSearch = $oScopeValidator->GetScopeFilterForProfiles(UserRights::ListProfiles(), $sTargetObjectClass, UR_ACTION_READ); $aInternalParams = array(); if (($oScopeSearch === null) && ($oTargetAttDef->GetEditClass() !== 'CustomFields')) { IssueLog::Info(__METHOD__ . ' at line ' . __LINE__ . ' : User #' . UserRights::GetUserId() . ' has no scope query for ' . $sTargetObjectClass . ' class.'); - $oApp->abort(404, Dict::S('UI:ObjectDoesNotExist')); + throw new HttpException(Response::HTTP_NOT_FOUND, Dict::S('UI:ObjectDoesNotExist')); } // - Base query from meta model + /** @var \DBSearch $oSearch */ if ($oTargetAttDef->IsExternalKey()) { $oSearch = DBSearch::FromOQL($oTargetAttDef->GetValuesDef()->GetFilterExpression()); @@ -1091,7 +911,7 @@ class ObjectController extends AbstractController $oBinExpr = new FalseExpression(); } } - // - For regular attributs + // - For regular attributes else { $oBinExpr = new BinaryExpression(new FieldExpression($sAttCode, $oSearch->GetClassAlias()), 'LIKE', new VariableExpression('re_query')); @@ -1113,7 +933,7 @@ class ObjectController extends AbstractController // - Intersecting with scope constraints // Note : This do NOT apply to custom fields as the portal administrator is not supposed to know which objects will be put in the templates. - // It is the responsability of the template designer to write the right query so the user see only what he should. + // It is the responsibility of the template designer to write the right query so the user see only what he should. if (($oScopeSearch !== null) && ($oTargetAttDef->GetEditClass() !== 'CustomFields')) { $oSearch = $oSearch->Intersect($oScopeSearch); @@ -1142,11 +962,11 @@ class ObjectController extends AbstractController $aItems = array(); while ($oItem = $oSet->Fetch()) { - $aItems[] = $this->PrepareObjectInformations($oApp, $oItem, $aAttCodes); + $aItems[] = $this->PrepareObjectInformation($oItem, $aAttCodes); } // Preparing response - if ($bInitalPass) + if ($bInitialPass) { $aData = $aData + array( 'form' => array( @@ -1170,12 +990,12 @@ class ObjectController extends AbstractController if ($oRequest->isXmlHttpRequest()) { - $oResponse = $oApp['twig']->render('itop-portal-base/portal/src/views/bricks/object/modal.html.twig', $aData); + $oResponse = $this->render('itop-portal-base/portal/src/views/bricks/object/modal.html.twig', $aData); } else { - //$oResponse = $oApp->abort(404, Dict::S('UI:ObjectDoesNotExist')); - $oResponse = $oApp['twig']->render('itop-portal-base/portal/src/views/bricks/object/layout.html.twig', $aData); + //throw new HttpException(Response::HTTP_NOT_FOUND, Dict::S('UI:ObjectDoesNotExist')); + $oResponse = $this->render('itop-portal-base/portal/src/views/bricks/object/layout.html.twig', $aData); } } else @@ -1187,45 +1007,34 @@ class ObjectController extends AbstractController 'recordsFiltered' => $oSet->Count() ); - $oResponse = $oApp->json($aData); + $oResponse = new JsonResponse($aData); } return $oResponse; } - /** - * Handles the hierarchical search from an attribute - * - * @param \Symfony\Component\HttpFoundation\Request $oRequest - * @param \Silex\Application $oApp - * @param string $sTargetAttCode Attribute code of the host object pointing to the Object class to search - * @param string $sHostObjectClass Class name of the host object - * @param string $sHostObjectId Id of the host object - * - * @return void - * - */ - public function SearchHierarchyAction(Request $oRequest, Application $oApp, $sTargetAttCode, $sHostObjectClass, $sHostObjectId = null) - { - // TODO - } - - /** - * Handles ormDocument display / download from an object - * - * Note: This is inspired from pages/ajax.document.php, but duplicated as there is no secret mecanism for ormDocument yet. - * - * @param \Symfony\Component\HttpFoundation\Request $oRequest - * @param \Silex\Application $oApp - * @param string $sOperation - * - * @return \Symfony\Component\HttpFoundation\Response - * - * @throws \ArchivedObjectException - * @throws \CoreException - */ - public function DocumentAction(Request $oRequest, Application $oApp, $sOperation = null) + /** + * Handles ormDocument display / download from an object + * + * Note: This is inspired from pages/ajax.document.php, but duplicated as there is no secret mecanism for ormDocument yet. + * + * @param \Symfony\Component\HttpFoundation\Request $oRequest + * @param string $sOperation + * + * @return \Symfony\Component\HttpFoundation\Response + * + * @throws \ArchivedObjectException + * @throws \CoreException + */ + public function DocumentAction(Request $oRequest, $sOperation = null) { + /** @var \Combodo\iTop\Portal\Helper\RequestManipulatorHelper $oRequestManipulator */ + $oRequestManipulator = $this->get('request_manipulator'); + /** @var \Combodo\iTop\Portal\Helper\SecurityHelper $oSecurityHelper */ + $oSecurityHelper = $this->get('security_helper'); + /** @var \Combodo\iTop\Portal\Helper\ScopeValidatorHelper $oScopeValidator */ + $oScopeValidator = $this->get('scope_validator'); + // Setting default operation if($sOperation === null) { @@ -1233,9 +1042,9 @@ class ObjectController extends AbstractController } // Retrieving ormDocument's host object - $sObjectClass = $oApp['request_manipulator']->ReadParam('sObjectClass', ''); - $sObjectId = $oApp['request_manipulator']->ReadParam('sObjectId', ''); - $sObjectField = $oApp['request_manipulator']->ReadParam('sObjectField', ''); + $sObjectClass = $oRequestManipulator->ReadParam('sObjectClass', ''); + $sObjectId = $oRequestManipulator->ReadParam('sObjectId', ''); + $sObjectField = $oRequestManipulator->ReadParam('sObjectField', ''); // When reaching to an Attachment, we have to check security on its host object instead of the Attachment itself if($sObjectClass === 'Attachment') @@ -1252,31 +1061,31 @@ class ObjectController extends AbstractController // Checking security layers // Note: Checking if host object already exists as we can try to download document from an object that is being created - if (($sHostId > 0) && !SecurityHelper::IsActionAllowed($oApp, UR_ACTION_READ, $sHostClass, $sHostId)) + if (($sHostId > 0) && !$oSecurityHelper->IsActionAllowed(UR_ACTION_READ, $sHostClass, $sHostId)) { IssueLog::Warning(__METHOD__ . ' at line ' . __LINE__ . ' : User #' . UserRights::GetUserId() . ' not allowed to retrieve document from attribute ' . $sObjectField . ' as it not allowed to read ' . $sHostClass . '::' . $sHostId . ' object.'); - $oApp->abort(404, Dict::S('UI:ObjectDoesNotExist')); + throw new HttpException(Response::HTTP_NOT_FOUND, Dict::S('UI:ObjectDoesNotExist')); } // Retrieving object - $oObject = MetaModel::GetObject($sObjectClass, $sObjectId, false /* Must not be found */, $oApp['scope_validator']->IsAllDataAllowedForScope(UserRights::ListProfiles(), $sHostClass)); + $oObject = MetaModel::GetObject($sObjectClass, $sObjectId, false /* Must not be found */, $oScopeValidator->IsAllDataAllowedForScope(UserRights::ListProfiles(), $sHostClass)); if ($oObject === null) { - // We should never be there as the secuirty helper makes sure that the object exists, but just in case. + // We should never be there as the security helper makes sure that the object exists, but just in case. IssueLog::Info(__METHOD__ . ' at line ' . __LINE__ . ' : Could not load object ' . $sObjectClass . '::' . $sObjectId . '.'); - $oApp->abort(404, Dict::S('UI:ObjectDoesNotExist')); + throw new HttpException(Response::HTTP_NOT_FOUND, Dict::S('UI:ObjectDoesNotExist')); } // Setting cache timeout // Note: Attachment download should be handle through AttachmentAction() if($sObjectClass === 'Attachment') { - // One year ahead: an attachement cannot change + // One year ahead: an attachment cannot change $iCacheSec = 31556926; } else { - $iCacheSec = $oApp['request_manipulator']->ReadParam('cache', 0, FILTER_SANITIZE_NUMBER_INT); + $iCacheSec = $oRequestManipulator->ReadParam('cache', 0, FILTER_SANITIZE_NUMBER_INT); } $aHeaders = array(); @@ -1298,23 +1107,32 @@ class ObjectController extends AbstractController return new Response($oDocument->GetData(), Response::HTTP_OK, $aHeaders); } - /** - * Handles attachment add/remove on an object - * - * Note: This is inspired from itop-attachment/ajax.attachment.php - * - * @param \Symfony\Component\HttpFoundation\Request $oRequest - * @param \Silex\Application $oApp - * @param string $sOperation - * - * @return \Symfony\Component\HttpFoundation\Response - * - * @throws \Exception - * @throws \CoreException - * @throws \CoreUnexpectedValue - */ - public function AttachmentAction(Request $oRequest, Application $oApp, $sOperation = null) + /** + * Handles attachment add/remove on an object + * + * Note: This is inspired from itop-attachment/ajax.attachment.php + * + * @param \Symfony\Component\HttpFoundation\Request $oRequest + * @param string $sOperation + * + * @return \Symfony\Component\HttpFoundation\JsonResponse + * + * @throws \ArchivedObjectException + * @throws \CoreCannotSaveObjectException + * @throws \CoreException + * @throws \CoreUnexpectedValue + * @throws \CoreWarning + * @throws \MySQLException + * @throws \OQLException + * @throws \Exception + */ + public function AttachmentAction(Request $oRequest, $sOperation = null) { + /** @var \Combodo\iTop\Portal\Helper\RequestManipulatorHelper $oRequestManipulator */ + $oRequestManipulator = $this->get('request_manipulator'); + /** @var \Combodo\iTop\Portal\Routing\UrlGenerator $oUrlGenerator */ + $oUrlGenerator = $this->get('url_generator'); + $aData = array( 'att_id' => 0, 'preview' => false, @@ -1324,14 +1142,14 @@ class ObjectController extends AbstractController // Retrieving sOperation from request only if it wasn't forced (determined by the route) if ($sOperation === null) { - $sOperation = $oApp['request_manipulator']->ReadParam('operation', null); + $sOperation = $oRequestManipulator->ReadParam('operation', null); } switch ($sOperation) { case 'add': - $sFieldName = $oApp['request_manipulator']->ReadParam('field_name', ''); - $sObjectClass = $oApp['request_manipulator']->ReadParam('object_class', ''); - $sTempId = $oApp['request_manipulator']->ReadParam('temp_id', ''); + $sFieldName = $oRequestManipulator->ReadParam('field_name', ''); + $sObjectClass = $oRequestManipulator->ReadParam('object_class', ''); + $sTempId = $oRequestManipulator->ReadParam('temp_id', ''); if (empty($sObjectClass) || empty($sTempId)) { @@ -1364,7 +1182,7 @@ class ObjectController extends AbstractController } // Note : The Content-Type header is set to 'text/plain' in order to be IE9 compatible. Otherwise ('application/json') IE9 will download the response as a JSON file to the user computer... - $oResponse = $oApp->json($aData, 200, array('Content-Type' => 'text/plain')); + $oResponse = new JsonResponse($aData, Response::HTTP_OK, array('Content-Type' => 'text/plain')); break; case 'download': @@ -1372,52 +1190,60 @@ class ObjectController extends AbstractController // - Route $aRouteParams = array( 'sObjectClass' => 'Attachment', - 'sObjectId' => $oApp['request_manipulator']->ReadParam('sAttachmentId', null), + 'sObjectId' => $oRequestManipulator->ReadParam('sAttachmentId', null), 'sObjectField' => 'contents', ); - $sRedirectRoute = $oApp['url_generator']->generate('p_object_document_download', $aRouteParams); + $sRedirectRoute = $oUrlGenerator->generate('p_object_document_download', $aRouteParams); // - Request $oSubRequest = Request::create($sRedirectRoute, 'GET', $oRequest->query->all(), $oRequest->cookies->all(), array(), $oRequest->server->all()); + // TODO: How do we do that? $oResponse = $oApp->handle($oSubRequest, HttpKernelInterface::SUB_REQUEST, true); break; default: - $oApp->abort(403, Dict::S('Error:HTTP:400')); + throw new HttpException(Response::HTTP_FORBIDDEN, Dict::S('Error:HTTP:400')); break; } return $oResponse; } - /** - * Returns a json response containing an array of objects informations. - * - * The service must be given 3 parameters : - * - sObjectClass : The class of objects to retrieve information from - * - aObjectIds : An array of object ids - * - aObjectAttCodes : An array of attribute codes to retrieve - * - * @param \Symfony\Component\HttpFoundation\Request $oRequest - * @param \Silex\Application $oApp - * - * @return \Symfony\Component\HttpFoundation\Response - * - * @throws \OQLException - * @throws \CoreException - */ - public function GetInformationsAsJsonAction(Request $oRequest, Application $oApp) + /** + * Returns a json response containing an array of objects informations. + * + * The service must be given 3 parameters : + * - sObjectClass : The class of objects to retrieve information from + * - aObjectIds : An array of object ids + * - aObjectAttCodes : An array of attribute codes to retrieve + * + * @param \Symfony\Component\HttpFoundation\Request $oRequest + * + * @return \Symfony\Component\HttpFoundation\JsonResponse + * + * @throws \CoreException + * @throws \CoreUnexpectedValue + * @throws \MySQLException + * @throws \OQLException + * @throws \Exception + */ + public function GetInformationsAsJsonAction(Request $oRequest) { + /** @var \Combodo\iTop\Portal\Helper\RequestManipulatorHelper $oRequestManipulator */ + $oRequestManipulator = $this->get('request_manipulator'); + /** @var \Combodo\iTop\Portal\Helper\ScopeValidatorHelper $oScopeValidator */ + $oScopeValidator = $this->get('scope_validator'); + $aData = array(); // Retrieving parameters - $sObjectClass = $oApp['request_manipulator']->ReadParam('sObjectClass', ''); - $aObjectIds = $oApp['request_manipulator']->ReadParam('aObjectIds', array(), FILTER_UNSAFE_RAW); - $aObjectAttCodes = $oApp['request_manipulator']->ReadParam('aObjectAttCodes', array(), FILTER_UNSAFE_RAW); + $sObjectClass = $oRequestManipulator->ReadParam('sObjectClass', ''); + $aObjectIds = $oRequestManipulator->ReadParam('aObjectIds', array(), FILTER_UNSAFE_RAW); + $aObjectAttCodes = $oRequestManipulator->ReadParam('aObjectAttCodes', array(), FILTER_UNSAFE_RAW); if ( empty($sObjectClass) || empty($aObjectIds) || empty($aObjectAttCodes) ) { IssueLog::Info(__METHOD__ . ' at line ' . __LINE__ . ' : sObjectClass, aObjectIds and aObjectAttCodes expected, "' . $sObjectClass . '", "' . implode('/', $aObjectIds) . '" given.'); - $oApp->abort(500, 'Invalid request data, some informations are missing'); + throw new HttpException(Response::HTTP_INTERNAL_SERVER_ERROR, 'Invalid request data, some information are missing'); } // Checking that id is in the AttCodes @@ -1427,7 +1253,7 @@ class ObjectController extends AbstractController } // Building the search - $bIgnoreSilos = $oApp['scope_validator']->IsAllDataAllowedForScope(UserRights::ListProfiles(), $sObjectClass); + $bIgnoreSilos = $oScopeValidator->IsAllDataAllowedForScope(UserRights::ListProfiles(), $sObjectClass); $aParams = array('objects_id' => $aObjectIds); $oSearch = DBObjectSearch::FromOQL("SELECT $sObjectClass WHERE id IN (:objects_id)"); if ($bIgnoreSilos === true) @@ -1440,26 +1266,30 @@ class ObjectController extends AbstractController // Retrieving objects while ($oObject = $oSet->Fetch()) { - $aData['items'][] = $this->PrepareObjectInformations($oApp, $oObject, $aObjectAttCodes); + $aData['items'][] = $this->PrepareObjectInformation($oObject, $aObjectAttCodes); } - return $oApp->json($aData); + return new JsonResponse($aData); } - /** - * Prepare a DBObject informations as an array for a client side usage (typically, add a row in a table) - * - * @param \Silex\Application $oApp - * @param \DBObject $oObject - * @param array $aAttCodes - * - * @return array - * - * @throws \Exception - * @throws \CoreException - */ - protected function PrepareObjectInformations(Application $oApp, DBObject $oObject, $aAttCodes = array()) + /** + * Prepare a DBObject information as an array for a client side usage (typically, add a row in a table) + * + * @param \DBObject $oObject + * @param array $aAttCodes + * + * @return array + * + * @throws \CoreException + * @throws \Exception + */ + protected function PrepareObjectInformation(DBObject $oObject, $aAttCodes = array()) { + /** @var \Combodo\iTop\Portal\Routing\UrlGenerator $oUrlGenerator */ + $oUrlGenerator = $this->get('url_generator'); + /** @var \Combodo\iTop\Portal\Helper\SecurityHelper $oSecurityHelper */ + $oSecurityHelper = $this->get('security_helper'); + $sObjectClass = get_class($oObject); $aObjectData = array( 'id' => $oObject->GetKey(), @@ -1489,9 +1319,9 @@ class ObjectController extends AbstractController $aAttData['value'] = $oObject->GetAsHTML($oAttDef->GetCode() . '_friendlyname'); // Checking if user can access object's external key - if (SecurityHelper::IsActionAllowed($oApp, UR_ACTION_READ, $oAttDef->GetTargetClass())) + if ($oSecurityHelper->IsActionAllowed(UR_ACTION_READ, $oAttDef->GetTargetClass())) { - $aAttData['url'] = $oApp['url_generator']->generate('p_object_view', array('sObjectClass' => $oAttDef->GetTargetClass(), 'sObjectId' => $oObject->Get($oAttDef->GetCode()))); + $aAttData['url'] = $oUrlGenerator->generate('p_object_view', array('sObjectClass' => $oAttDef->GetTargetClass(), 'sObjectId' => $oObject->Get($oAttDef->GetCode()))); } } elseif ($oAttDef->IsLinkSet()) @@ -1504,7 +1334,7 @@ class ObjectController extends AbstractController $oOrmDoc = $oObject->Get($oAttDef->GetCode()); if (is_object($oOrmDoc) && !$oOrmDoc->IsEmpty()) { - $sUrl = $oApp['url_generator']->generate('p_object_document_display', array('sObjectClass' => get_class($oObject), 'sObjectId' => $oObject->GetKey(), 'sObjectField' => $oAttDef->GetCode(), 'cache' => 86400)); + $sUrl = $oUrlGenerator->generate('p_object_document_display', array('sObjectClass' => get_class($oObject), 'sObjectId' => $oObject->GetKey(), 'sObjectField' => $oAttDef->GetCode(), 'cache' => 86400)); } else { @@ -1519,9 +1349,9 @@ class ObjectController extends AbstractController if ($oAttDef instanceof AttributeFriendlyName) { // Checking if user can access object - if(SecurityHelper::IsActionAllowed($oApp, UR_ACTION_READ, $sObjectClass)) + if($oSecurityHelper->IsActionAllowed(UR_ACTION_READ, $sObjectClass)) { - $aAttData['url'] = $oApp['url_generator']->generate('p_object_view', array('sObjectClass' => $sObjectClass, 'sObjectId' => $oObject->GetKey())); + $aAttData['url'] = $oUrlGenerator->generate('p_object_view', array('sObjectClass' => $sObjectClass, 'sObjectId' => $oObject->GetKey())); } } } diff --git a/datamodels/2.x/itop-portal-base/portal-silex/src/controllers/userprofilebrickcontroller.class.inc.php b/datamodels/2.x/itop-portal-base/portal/src/Controller/UserProfileBrickController.php similarity index 53% rename from datamodels/2.x/itop-portal-base/portal-silex/src/controllers/userprofilebrickcontroller.class.inc.php rename to datamodels/2.x/itop-portal-base/portal/src/Controller/UserProfileBrickController.php index 04bc54b13..972f3351d 100644 --- a/datamodels/2.x/itop-portal-base/portal-silex/src/controllers/userprofilebrickcontroller.class.inc.php +++ b/datamodels/2.x/itop-portal-base/portal/src/Controller/UserProfileBrickController.php @@ -1,33 +1,38 @@ +/** + * Copyright (C) 2013-2019 Combodo SARL + * + * This file is part of iTop. + * + * iTop is free software; you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * 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. + * + * You should have received a copy of the GNU Affero General Public License + * + * + */ namespace Combodo\iTop\Portal\Controller; +use Combodo\iTop\Portal\Helper\ObjectFormHandlerHelper; use Exception; use FileUploadException; use IssueLog; +use Symfony\Component\HttpFoundation\JsonResponse; +use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\HttpKernel\Exception\HttpException; use utils; use MetaModel; use UserRights; -use Silex\Application; use Symfony\Component\HttpFoundation\Request; -use Combodo\iTop\Portal\Helper\ApplicationHelper; use Combodo\iTop\Portal\Brick\UserProfileBrick; use Combodo\iTop\Portal\Form\PreferencesFormManager; use Combodo\iTop\Portal\Form\PasswordFormManager; @@ -44,27 +49,32 @@ class UserProfileBrickController extends BrickController { const ENUM_FORM_TYPE_PICTURE = 'picture'; - /** - * @param \Symfony\Component\HttpFoundation\Request $oRequest - * @param \Silex\Application $oApp - * @param $sBrickId - * - * @return \Symfony\Component\HttpFoundation\Response - * - * @throws \Exception - * @throws \ArchivedObjectException - * @throws \CoreException - * @throws \OQLException - * @throws \Twig_Error_Loader - * @throws \Twig_Error_Runtime - * @throws \Twig_Error_Syntax - */ - public function DisplayAction(Request $oRequest, Application $oApp, $sBrickId) + /** + * @param \Symfony\Component\HttpFoundation\Request $oRequest + * @param $sBrickId + * + * @return \Symfony\Component\HttpFoundation\Response + * + * @throws \ArchivedObjectException + * @throws \Combodo\iTop\Portal\Brick\BrickNotFoundException + * @throws \CoreException + * @throws \OQLException + * @throws \Exception + */ + public function DisplayAction(Request $oRequest, $sBrickId) { + /** @var \Combodo\iTop\Portal\Helper\RequestManipulatorHelper $oRequestManipulator */ + $oRequestManipulator = $this->get('request_manipulator'); + /** @var \Combodo\iTop\Portal\Helper\ObjectFormHandlerHelper $ObjectFormHandler */ + $ObjectFormHandler = $this->get('object_form_handler'); + /** @var \Combodo\iTop\Portal\Brick\BrickCollection $oBrickCollection */ + $oBrickCollection = $this->get('brick_collection'); + // If the brick id was not specified, we get the first one registered that is an instance of UserProfileBrick as default if ($sBrickId === null) { - foreach ($oApp['combodo.portal.instance.conf']['bricks'] as $oTmpBrick) + /** @var \Combodo\iTop\Portal\Brick\PortalBrick $oTmpBrick */ + foreach ($oBrickCollection->GetBricks() as $oTmpBrick) { if ($oTmpBrick instanceof UserProfileBrick) { @@ -76,84 +86,89 @@ class UserProfileBrickController extends BrickController if (!isset($oBrick) || $oBrick === null) { $oBrick = new UserProfileBrick(); - //$oApp->abort(500, 'UserProfileBrick : Brick could not be loaded as there was no UserProfileBrick loaded in the application.'); + //throw new HttpException(Response::HTTP_INTERNAL_SERVER_ERROR, 'UserProfileBrick : Brick could not be loaded as there was no UserProfileBrick loaded in the application.'); } } else { - $oBrick = ApplicationHelper::GetLoadedBrickFromId($oApp, $sBrickId); + $oBrick = $oBrickCollection->GetBrickById($sBrickId); } $aData = array(); - + // Setting form mode regarding the demo mode parameter $bDemoMode = MetaModel::GetConfig()->Get('demo_mode'); - $sFormMode = ($bDemoMode) ? ObjectController::ENUM_MODE_VIEW : ObjectController::ENUM_MODE_EDIT; + $sFormMode = ($bDemoMode) ? ObjectFormHandlerHelper::ENUM_MODE_VIEW : ObjectFormHandlerHelper::ENUM_MODE_EDIT; - // If this is ajax call, we are just submiting preferences or password forms + // If this is ajax call, we are just submitting preferences or password forms if ($oRequest->isXmlHttpRequest()) { - $aCurrentValues = $oApp['request_manipulator']->ReadParam('current_values', array(), FILTER_UNSAFE_RAW); + $aCurrentValues = $oRequestManipulator->ReadParam('current_values', array(), FILTER_UNSAFE_RAW); $sFormType = $aCurrentValues['form_type']; if ($sFormType === PreferencesFormManager::FORM_TYPE) { - $aData['form'] = $this->HandlePreferencesForm($oRequest, $oApp, $sFormMode); + $aData['form'] = $this->HandlePreferencesForm($oRequest, $sFormMode); } elseif ($sFormType === PasswordFormManager::FORM_TYPE) { - $aData['form'] = $this->HandlePasswordForm($oRequest, $oApp, $sFormMode); + $aData['form'] = $this->HandlePasswordForm($oRequest, $sFormMode); } elseif ($sFormType === static::ENUM_FORM_TYPE_PICTURE) { - $aData['form'] = $this->HandlePictureForm($oRequest, $oApp); + $aData['form'] = $this->HandlePictureForm($oRequest); } else { throw new Exception('Unknown form type.'); } - $oResponse = $oApp->json($aData); + $oResponse = new JsonResponse($aData); } // Else, we are displaying page for first time else { // Retrieving current contact + /** @var \DBObject $oCurContact */ $oCurContact = UserRights::GetContactObject(); $sCurContactClass = get_class($oCurContact); $sCurContactId = $oCurContact->GetKey(); // Preparing forms - $aData['forms']['contact'] = ObjectController::HandleForm($oRequest, $oApp, $sFormMode, $sCurContactClass, $sCurContactId, $oBrick->GetForm()); - $aData['forms']['preferences'] = $this->HandlePreferencesForm($oRequest, $oApp, $sFormMode); + $aData['forms']['contact'] = $ObjectFormHandler->HandleForm($oRequest, $sFormMode, $sCurContactClass, $sCurContactId, $oBrick->GetForm()); + $aData['forms']['preferences'] = $this->HandlePreferencesForm($oRequest, $sFormMode); // - If user can change password, we display the form - $aData['forms']['password'] = (UserRights::CanChangePassword()) ? $this->HandlePasswordForm($oRequest, $oApp, $sFormMode) : null; + $aData['forms']['password'] = (UserRights::CanChangePassword()) ? $this->HandlePasswordForm($oRequest, $sFormMode) : null; $aData = $aData + array( 'oBrick' => $oBrick, 'sFormMode' => $sFormMode, - 'bDemoMode' => $bDemoMode + 'bDemoMode' => $bDemoMode, ); - $oResponse = $oApp['twig']->render($oBrick->GetPageTemplatePath(), $aData); + $oResponse = $this->render($oBrick->GetPageTemplatePath(), $aData); } return $oResponse; } - /** - * @param \Symfony\Component\HttpFoundation\Request $oRequest - * @param \Silex\Application $oApp - * @param string $sFormMode - * - * @return array - * - * @throws \Exception - */ - public function HandlePreferencesForm(Request $oRequest, Application $oApp, $sFormMode) + /** + * @param \Symfony\Component\HttpFoundation\Request $oRequest + * @param string $sFormMode + * + * @return array + * + * @throws \Exception + */ + public function HandlePreferencesForm(Request $oRequest, $sFormMode) { + /** @var \Combodo\iTop\Portal\Helper\RequestManipulatorHelper $oRequestManipulator */ + $oRequestManipulator = $this->get('request_manipulator'); + /** @var \Combodo\iTop\Portal\Routing\UrlGenerator $oUrlGenerator */ + $oUrlGenerator = $this->get('url_generator'); + $aFormData = array(); // Handling form - $sOperation = $oApp['request_manipulator']->ReadParam('operation', null); + $sOperation = $oRequestManipulator->ReadParam('operation', null); // - Create if ($sOperation === null) { @@ -165,7 +180,7 @@ class UserProfileBrickController extends BrickController $oFormManager->SetRenderer($oFormRenderer) ->Build(); // - Checking if we have to make the form read only - if ($sFormMode === ObjectController::ENUM_MODE_VIEW) + if ($sFormMode === ObjectFormHandlerHelper::ENUM_MODE_VIEW) { $oFormManager->GetForm()->MakeReadOnly(); } @@ -173,23 +188,23 @@ class UserProfileBrickController extends BrickController // - Submit else if ($sOperation === 'submit') { - $sFormManagerClass = $oApp['request_manipulator']->ReadParam('formmanager_class', null, FILTER_UNSAFE_RAW); - $sFormManagerData = $oApp['request_manipulator']->ReadParam('formmanager_data', null, FILTER_UNSAFE_RAW); + $sFormManagerClass = $oRequestManipulator->ReadParam('formmanager_class', null, FILTER_UNSAFE_RAW); + $sFormManagerData = $oRequestManipulator->ReadParam('formmanager_data', null, FILTER_UNSAFE_RAW); if ($sFormManagerClass === null || $sFormManagerData === null) { IssueLog::Error(__METHOD__ . ' at line ' . __LINE__ . ' : Parameters formmanager_class and formamanager_data must be defined.'); - $oApp->abort(500, 'Parameters formmanager_class and formmanager_data must be defined.'); + throw new HttpException(Response::HTTP_INTERNAL_SERVER_ERROR, 'Parameters formmanager_class and formmanager_data must be defined.'); } // Rebuilding manager from json $oFormManager = $sFormManagerClass::FromJSON($sFormManagerData); // Applying modification to object - $aFormData['validation'] = $oFormManager->OnSubmit(array('currentValues' => $oApp['request_manipulator']->ReadParam('current_values', array(), FILTER_UNSAFE_RAW))); + $aFormData['validation'] = $oFormManager->OnSubmit(array('currentValues' => $oRequestManipulator->ReadParam('current_values', array(), FILTER_UNSAFE_RAW))); // Reloading page only if preferences were changed if (($aFormData['validation']['valid'] === true) && !empty($aFormData['validation']['messages']['success'])) { $aFormData['validation']['redirection'] = array( - 'url' => $oApp['url_generator']->generate('p_user_profile_brick'), + 'url' => $oUrlGenerator->generate('p_user_profile_brick'), ); } } @@ -199,7 +214,7 @@ class UserProfileBrickController extends BrickController $aFieldSetData = array( 'fields_list' => $oFormManager->GetRenderer()->Render(), 'fields_impacts' => $oFormManager->GetForm()->GetFieldsImpacts(), - 'form_path' => $oFormManager->GetForm()->GetId() + 'form_path' => $oFormManager->GetForm()->GetId(), ); // Preparing form data @@ -212,21 +227,24 @@ class UserProfileBrickController extends BrickController return $aFormData; } - /** - * @param \Symfony\Component\HttpFoundation\Request $oRequest - * @param \Silex\Application $oApp - * @param string $sFormMode - * - * @return array - * - * @throws \Exception - */ - public function HandlePasswordForm(Request $oRequest, Application $oApp, $sFormMode) + /** + * @param \Symfony\Component\HttpFoundation\Request $oRequest + * @param string $sFormMode + * + * @return array + * + * @throws \Exception + */ + public function HandlePasswordForm(Request $oRequest, $sFormMode) { + /** @var \Combodo\iTop\Portal\Helper\RequestManipulatorHelper $oRequestManipulator */ + $oRequestManipulator = $this->get('request_manipulator'); + $aFormData = array(); // Handling form - $sOperation = $oApp['request_manipulator']->ReadParam('operation', null); + $sOperation = /** @var \Combodo\iTop\Portal\Helper\RequestManipulatorHelper $oRequestManipulator */ + $oRequestManipulator->ReadParam('operation', null); // - Create if ($sOperation === null) { @@ -238,7 +256,7 @@ class UserProfileBrickController extends BrickController $oFormManager->SetRenderer($oFormRenderer) ->Build(); // - Checking if we have to make the form read only - if ($sFormMode === ObjectController::ENUM_MODE_VIEW) + if ($sFormMode === ObjectFormHandlerHelper::ENUM_MODE_VIEW) { $oFormManager->GetForm()->MakeReadOnly(); } @@ -246,18 +264,18 @@ class UserProfileBrickController extends BrickController // - Submit else if ($sOperation === 'submit') { - $sFormManagerClass = $oApp['request_manipulator']->ReadParam('formmanager_class', null, FILTER_UNSAFE_RAW); - $sFormManagerData = $oApp['request_manipulator']->ReadParam('formmanager_data', null, FILTER_UNSAFE_RAW); + $sFormManagerClass = $oRequestManipulator->ReadParam('formmanager_class', null, FILTER_UNSAFE_RAW); + $sFormManagerData = $oRequestManipulator->ReadParam('formmanager_data', null, FILTER_UNSAFE_RAW); if ($sFormManagerClass === null || $sFormManagerData === null) { IssueLog::Error(__METHOD__ . ' at line ' . __LINE__ . ' : Parameters formmanager_class and formamanager_data must be defined.'); - $oApp->abort(500, 'Parameters formmanager_class and formmanager_data must be defined.'); + throw new HttpException(Response::HTTP_INTERNAL_SERVER_ERROR, 'Parameters formmanager_class and formmanager_data must be defined.'); } // Rebuilding manager from json $oFormManager = $sFormManagerClass::FromJSON($sFormManagerData); // Applying modification to object - $aFormData['validation'] = $oFormManager->OnSubmit(array('currentValues' => $oApp['request_manipulator']->ReadParam('current_values', array(), FILTER_UNSAFE_RAW))); + $aFormData['validation'] = $oFormManager->OnSubmit(array('currentValues' => $oRequestManipulator->ReadParam('current_values', array(), FILTER_UNSAFE_RAW))); } // Else, submit from another form @@ -265,7 +283,7 @@ class UserProfileBrickController extends BrickController $aFieldSetData = array( 'fields_list' => $oFormManager->GetRenderer()->Render(), 'fields_impacts' => $oFormManager->GetForm()->GetFieldsImpacts(), - 'form_path' => $oFormManager->GetForm()->GetId() + 'form_path' => $oFormManager->GetForm()->GetId(), ); // Preparing form data @@ -278,26 +296,28 @@ class UserProfileBrickController extends BrickController return $aFormData; } - /** - * @param \Symfony\Component\HttpFoundation\Request $oRequest - * @param \Silex\Application $oApp - * - * @return array - * - * @throws \Exception - */ - public function HandlePictureForm(Request $oRequest, Application $oApp) + /** + * @param \Symfony\Component\HttpFoundation\Request $oRequest + * + * @return array + * + * @throws \Exception + */ + public function HandlePictureForm(Request $oRequest) { + /** @var \Combodo\iTop\Portal\Helper\RequestManipulatorHelper $oRequestManipulator */ + $oRequestManipulator = $this->get('request_manipulator'); + $aFormData = array(); $sPictureAttCode = 'picture'; // Handling form - $sOperation = $oApp['request_manipulator']->ReadParam('operation', null); + $sOperation = $oRequestManipulator->ReadParam('operation', null); // - No operation specified if ($sOperation === null) { IssueLog::Error(__METHOD__ . ' at line ' . __LINE__ . ' : Operation parameter must be specified.'); - $oApp->abort(500, 'Operation parameter must be specified.'); + throw new HttpException(Response::HTTP_INTERNAL_SERVER_ERROR, 'Operation parameter must be specified.'); } // - Submit else if ($sOperation === 'submit') @@ -307,14 +327,15 @@ class UserProfileBrickController extends BrickController if ($oPictureFile === null) { IssueLog::Error(__METHOD__ . ' at line ' . __LINE__ . ' : Parameter picture must be defined.'); - $oApp->abort(500, 'Parameter picture must be defined.'); + throw new HttpException(Response::HTTP_INTERNAL_SERVER_ERROR, 'Parameter picture must be defined.'); } - + try { // Retrieving image as an ORMDocument $oImage = utils::ReadPostedDocument($sPictureAttCode); // Retrieving current contact + /** @var \DBObject $oCurContact */ $oCurContact = UserRights::GetContactObject(); // Resizing image $oAttDef = MetaModel::GetAttributeDef(get_class($oCurContact), $sPictureAttCode); @@ -334,7 +355,7 @@ class UserProfileBrickController extends BrickController $aFormData['picture_url'] = $oImage->GetDownloadURL(get_class($oCurContact), $oCurContact->GetKey(), $sPictureAttCode); $aFormData['validation'] = array( 'valid' => true, - 'messages' => array() + 'messages' => array(), ); } // Else, submit from another form diff --git a/datamodels/2.x/itop-portal-base/portal/src/DependencyInjection/SilexCompatBootstrap/PortalXmlConfiguration/AbstractConfiguration.php b/datamodels/2.x/itop-portal-base/portal/src/DependencyInjection/SilexCompatBootstrap/PortalXmlConfiguration/AbstractConfiguration.php index 3e74eb11b..34fc6a746 100644 --- a/datamodels/2.x/itop-portal-base/portal/src/DependencyInjection/SilexCompatBootstrap/PortalXmlConfiguration/AbstractConfiguration.php +++ b/datamodels/2.x/itop-portal-base/portal/src/DependencyInjection/SilexCompatBootstrap/PortalXmlConfiguration/AbstractConfiguration.php @@ -1,31 +1,57 @@ + * @since 2.7.0 + */ class AbstractConfiguration { - /** - * @var \ModuleDesign - */ - private $moduleDesign; + /** @var \ModuleDesign $oModuleDesign */ + private $oModuleDesign; - public function __construct(\ModuleDesign $moduleDesign) + /** + * AbstractConfiguration constructor. + * + * @param \ModuleDesign $oModuleDesign + */ + public function __construct(ModuleDesign $oModuleDesign) { - $this->moduleDesign = $moduleDesign; + $this->oModuleDesign = $oModuleDesign; } /** * @return \ModuleDesign */ - public function getModuleDesign() + public function GetModuleDesign() { - return $this->moduleDesign; + return $this->oModuleDesign; } } \ No newline at end of file diff --git a/datamodels/2.x/itop-portal-base/portal/src/DependencyInjection/SilexCompatBootstrap/PortalXmlConfiguration/Basic.php b/datamodels/2.x/itop-portal-base/portal/src/DependencyInjection/SilexCompatBootstrap/PortalXmlConfiguration/Basic.php index 3cafecb4f..9f13dd6cf 100644 --- a/datamodels/2.x/itop-portal-base/portal/src/DependencyInjection/SilexCompatBootstrap/PortalXmlConfiguration/Basic.php +++ b/datamodels/2.x/itop-portal-base/portal/src/DependencyInjection/SilexCompatBootstrap/PortalXmlConfiguration/Basic.php @@ -19,15 +19,10 @@ * */ -/** - * Created by Bruno DA SILVA, working for Combodo - * Date: 24/01/19 - * Time: 15:55 - */ - namespace Combodo\iTop\Portal\DependencyInjection\SilexCompatBootstrap\PortalXmlConfiguration; - +use Combodo\iTop\DesignElement; +use iPortalUIExtension; use Symfony\Component\DependencyInjection\Container; use Exception; use utils; @@ -35,39 +30,51 @@ use UserRights; use MetaModel; use DOMFormatException; +/** + * Class Basic + * + * @package Combodo\iTop\Portal\DependencyInjection\SilexCompatBootstrap\PortalXmlConfiguration + * @author Guillaume Lajarige + * @since 2.7.0 + */ class Basic extends AbstractConfiguration { - public function process(Container $container) + /** + * @param \Symfony\Component\DependencyInjection\Container $oContainer + * + * @throws \Exception + */ + public function Process(Container $oContainer) { try { // Parsing file // - Default values - $aPortalConf = $this->getInitialPortalConf(); + $aPortalConf = $this->GetInitialPortalConf(); // - Global portal properties $aPortalConf = $this->ParseGlobalProperties($aPortalConf); // - Rectifying portal logo url - $aPortalConf = $this->appendLogoUri($aPortalConf); + $aPortalConf = $this->AppendLogoUri($aPortalConf); // - User allowed portals $aPortalConf['portals'] = UserRights::GetAllowedPortals(); // - class list - $aPortalConf['ui_extensions'] = $this->getUiExtensions($container); + $aPortalConf['ui_extensions'] = $this->GetUiExtensions($oContainer); } - catch (Exception $e) + catch (Exception $oException) { - throw new Exception('Error while parsing portal configuration file : '.$e->getMessage()); + throw new Exception('Error while parsing portal configuration file : '.$oException->getMessage()); } - $container->setParameter('combodo.portal.instance.conf', $aPortalConf); + $oContainer->setParameter('combodo.portal.instance.conf', $aPortalConf); } - - /** + * Returns an array containing the initial portal configuration with all default values + * * @return array */ - private function getInitialPortalConf() + private function GetInitialPortalConf() { $aPortalConf = array( 'properties' => array( @@ -81,12 +88,12 @@ class Basic extends AbstractConfiguration ), 'templates' => array( 'layout' => 'itop-portal-base/portal/templates/layout.html.twig', - 'home' => 'itop-portal-base/portal/templates/home/layout.html.twig' + 'home' => 'itop-portal-base/portal/templates/home/layout.html.twig', ), 'urlmaker_class' => null, 'triggers_query' => null, 'attachments' => array( - 'allow_delete' => true + 'allow_delete' => true, ), 'allowed_portals' => array( 'opening_mode' => null, @@ -108,16 +115,19 @@ class Basic extends AbstractConfiguration return $aPortalConf; } - /** - * @param ModuleDesign $oDesign - * @param array $aPortalConf - * - * @return array - */ + /** + * @param array $aPortalConf + * + * @return array + * @throws \DOMFormatException + */ private function ParseGlobalProperties(array $aPortalConf) { - foreach ($this->getModuleDesign()->GetNodes('/module_design/properties/*') as $oPropertyNode) { - switch ($oPropertyNode->nodeName) { + /** @var \MFElement $oPropertyNode */ + foreach ($this->GetModuleDesign()->GetNodes('/module_design/properties/*') as $oPropertyNode) + { + switch ($oPropertyNode->nodeName) + { case 'name': case 'urlmaker_class': case 'triggers_query': @@ -146,26 +156,32 @@ class Basic extends AbstractConfiguration return $aPortalConf; } - /** - * @param array $aPortalConf - * @param $oPropertyNode - * - * @return array - */ - private function ParseTemplateAndTheme(array $aPortalConf, $oPropertyNode) + /** + * @param array $aPortalConf + * @param \Combodo\iTop\DesignElement $oPropertyNode + * + * @return array + * @throws \DOMFormatException + */ + private function ParseTemplateAndTheme(array $aPortalConf, DesignElement $oPropertyNode) { - foreach ($oPropertyNode->GetNodes('template|theme') as $oSubNode) { - if (!$oSubNode->hasAttribute('id') || $oSubNode->GetText(null) === null) { + /** @var \MFElement $oSubNode */ + foreach ($oPropertyNode->GetNodes('template|theme') as $oSubNode) + { + if (!$oSubNode->hasAttribute('id') || $oSubNode->GetText(null) === null) + { throw new DOMFormatException( - 'Tag '.$oSubNode->nodeName.' must have a "id" attribute as well as a value', + 'Tag ' . $oSubNode->nodeName.' must have a "id" attribute as well as a value', null, null, $oSubNode ); } $sNodeId = $oSubNode->getAttribute('id'); - switch ($oSubNode->nodeName) { + switch ($oSubNode->nodeName) + { case 'theme': - switch ($sNodeId) { + switch ($sNodeId) + { case 'bootstrap': case 'portal': case 'custom': @@ -177,7 +193,8 @@ class Basic extends AbstractConfiguration } break; case 'template': - switch ($sNodeId) { + switch ($sNodeId) + { case 'layout': case 'home': $aPortalConf['properties']['templates'][$sNodeId] = $oSubNode->GetText(null); @@ -194,23 +211,27 @@ class Basic extends AbstractConfiguration } return $aPortalConf; -} + } - /** - * @param array $aPortalConf - * @param $oPropertyNode - * - * @return array - */ - private function ParseAttachments(array $aPortalConf, $oPropertyNode) + /** + * @param array $aPortalConf + * @param \Combodo\iTop\DesignElement $oPropertyNode + * + * @return array + */ + private function ParseAttachments(array $aPortalConf, DesignElement $oPropertyNode) { - foreach ($oPropertyNode->GetNodes('*') as $oSubNode) { - switch ($oSubNode->nodeName) { + /** @var \MFElement $oSubNode */ + foreach ($oPropertyNode->GetNodes('*') as $oSubNode) + { + switch ($oSubNode->nodeName) + { case 'allow_delete': $sValue = $oSubNode->GetText(); // If the text is null, we keep the default value // Else we set it - if ($sValue !== null) { + if ($sValue !== null) + { $aPortalConf['properties']['attachments'][$oSubNode->nodeName] = ($sValue === 'true') ? true : false; } break; @@ -218,23 +239,27 @@ class Basic extends AbstractConfiguration } return$aPortalConf; -} + } - /** - * @param array $aPortalConf - * @param $oPropertyNode - * - * @return array - */ - private function ParseAllowedPortals(array $aPortalConf, $oPropertyNode) + /** + * @param array $aPortalConf + * @param \Combodo\iTop\DesignElement $oPropertyNode + * + * @return array + */ + private function ParseAllowedPortals(array $aPortalConf, DesignElement $oPropertyNode) { - foreach ($oPropertyNode->GetNodes('*') as $oSubNode) { - switch ($oSubNode->nodeName) { + /** @var \MFElement $oSubNode */ + foreach ($oPropertyNode->GetNodes('*') as $oSubNode) + { + switch ($oSubNode->nodeName) + { case 'opening_mode': $sValue = $oSubNode->GetText(); // If the text is null, we keep the default value // Else we set it - if ($sValue !== null) { + if ($sValue !== null) + { $aPortalConf['properties']['allowed_portals'][$oSubNode->nodeName] = ($sValue === 'self') ? 'self' : 'tab'; } break; @@ -242,17 +267,19 @@ class Basic extends AbstractConfiguration } return $aPortalConf; -} + } - /** - * @param array $aPortalConf - * - * @return array - */ - private function appendLogoUri(array $aPortalConf) + /** + * @param array $aPortalConf + * + * @return array + * @throws \Exception + */ + private function AppendLogoUri(array $aPortalConf) { $sLogoUri = $aPortalConf['properties']['logo']; - if (!preg_match('/^http/', $sLogoUri)) { + if (!preg_match('/^http/', $sLogoUri)) + { // We prefix it with the server base url $sLogoUri = utils::GetAbsoluteUrlAppRoot().'env-'.utils::GetCurrentEnvironment().'/'.$sLogoUri; } @@ -261,7 +288,13 @@ class Basic extends AbstractConfiguration return $aPortalConf; } - private function getUiExtensions($container) + /** + * @param \Symfony\Component\DependencyInjection\Container $oContainer + * + * @return array + * @throws \Exception + */ + private function GetUiExtensions(Container $oContainer) { $aUIExtensions = array( 'css_files' => array(), @@ -271,22 +304,22 @@ class Basic extends AbstractConfiguration 'html' => array(), ); $aUIExtensionHooks = array( - \iPortalUIExtension::ENUM_PORTAL_EXT_UI_BODY, - \iPortalUIExtension::ENUM_PORTAL_EXT_UI_NAVIGATION_MENU, - \iPortalUIExtension::ENUM_PORTAL_EXT_UI_MAIN_CONTENT, + iPortalUIExtension::ENUM_PORTAL_EXT_UI_BODY, + iPortalUIExtension::ENUM_PORTAL_EXT_UI_NAVIGATION_MENU, + iPortalUIExtension::ENUM_PORTAL_EXT_UI_MAIN_CONTENT, ); /** @var iPortalUIExtension $oExtensionInstance */ foreach (MetaModel::EnumPlugins('iPortalUIExtension') as $oExtensionInstance) { // Adding CSS files - $aImportPaths = array($container->getParameter('combodo.portal.base.absolute_path').'css/'); - foreach($oExtensionInstance->GetCSSFiles($container) as $sCSSFile) + $aImportPaths = array($oContainer->getParameter('combodo.portal.base.absolute_path').'css/'); + foreach($oExtensionInstance->GetCSSFiles($oContainer) as $sCSSFile) { // Removing app root url as we need to pass a path on the file system (relative to app root) $sCSSFilePath = str_replace(utils::GetAbsoluteUrlAppRoot(), '', $sCSSFile); // Compiling SCSS file - $sCSSFileCompiled = $container->getParameter('combodo.absolute_url').utils::GetCSSFromSASS($sCSSFilePath, + $sCSSFileCompiled = $oContainer->getParameter('combodo.absolute_url').utils::GetCSSFromSASS($sCSSFilePath, $aImportPaths); if(!in_array($sCSSFileCompiled, $aUIExtensions['css_files'])) @@ -296,7 +329,7 @@ class Basic extends AbstractConfiguration } // Adding CSS inline - $sCSSInline = $oExtensionInstance->GetCSSInline($container); + $sCSSInline = $oExtensionInstance->GetCSSInline($oContainer); if ($sCSSInline !== null) { $aUIExtensions['css_inline'] .= "\n\n".$sCSSInline; @@ -304,10 +337,10 @@ class Basic extends AbstractConfiguration // Adding JS files $aUIExtensions['js_files'] = array_merge($aUIExtensions['js_files'], - $oExtensionInstance->GetJSFiles($container)); + $oExtensionInstance->GetJSFiles($oContainer)); // Adding JS inline - $sJSInline = $oExtensionInstance->GetJSInline($container); + $sJSInline = $oExtensionInstance->GetJSInline($oContainer); if ($sJSInline !== null) { // Note: Semi-colon is to prevent previous script that would have omitted it. @@ -318,7 +351,7 @@ class Basic extends AbstractConfiguration foreach ($aUIExtensionHooks as $sUIExtensionHook) { $sFunctionName = 'Get'.$sUIExtensionHook.'HTML'; - $sHTML = $oExtensionInstance->$sFunctionName($container); + $sHTML = $oExtensionInstance->$sFunctionName($oContainer); if ($sHTML !== null) { if (!array_key_exists($sUIExtensionHook, $aUIExtensions['html'])) 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 5618ba781..8990d019b 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 @@ -19,28 +19,34 @@ * */ -/** - * Created by Bruno DA SILVA, working for Combodo - * Date: 24/01/19 - * Time: 16:52 - */ - namespace Combodo\iTop\Portal\DependencyInjection\SilexCompatBootstrap\PortalXmlConfiguration; - -use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Container; +use DOMFormatException; use Exception; -use utils; use Combodo\iTop\Portal\Helper\ApplicationHelper; use MetaModel; +/** + * Class Forms + * + * @package Combodo\iTop\Portal\DependencyInjection\SilexCompatBootstrap\PortalXmlConfiguration + * @author Guillaume Lajarige + * @since 2.7.0 + */ class Forms extends AbstractConfiguration { - public function process(ContainerBuilder $container) + /** + * @param \Symfony\Component\DependencyInjection\Container $oContainer + * + * @throws \Exception + */ + public function Process(Container $oContainer) { $aForms = array(); - foreach ($this->getModuleDesign()->GetNodes('/module_design/forms/form') as $oFormNode) + /** @var \MFElement $oFormNode */ + foreach ($this->GetModuleDesign()->GetNodes('/module_design/forms/form') as $oFormNode) { try { @@ -63,7 +69,8 @@ class Forms extends AbstractConfiguration ); if ($oFormNode->GetOptionalElement('properties') !== null) { - foreach ($oFormNode->GetOptionalElement('properties')->childNodes as $oPropertyNode) + /** @var \MFElement $oPropertyNode */ + foreach ($oFormNode->GetOptionalElement('properties')->childNodes as $oPropertyNode) { switch ($oPropertyNode->nodeName) { @@ -77,12 +84,13 @@ class Forms extends AbstractConfiguration } } - // Parsing availables modes for that form (view, edit, create, apply_stimulus) + // Parsing available modes for that form (view, edit, create, apply_stimulus) $aFormStimuli = array(); if (($oFormNode->GetOptionalElement('modes') !== null) && ($oFormNode->GetOptionalElement('modes')->GetNodes('mode')->length > 0)) { $aModes = array(); - foreach ($oFormNode->GetOptionalElement('modes')->GetNodes('mode') as $oModeNode) + /** @var \MFElement $oModeNode */ + foreach ($oFormNode->GetOptionalElement('modes')->GetNodes('mode') as $oModeNode) { if ($oModeNode->getAttribute('id') !== '') { @@ -101,7 +109,8 @@ class Forms extends AbstractConfiguration // if stimuli are defined, we overwrite the form that could have been set by the generic form if ($oStimuliNode !== null) { - foreach ($oStimuliNode->GetNodes('stimulus') as $oStimulusNode) + /** @var \MFElement $oStimulusNode */ + foreach ($oStimuliNode->GetNodes('stimulus') as $oStimulusNode) { $sStimulusCode = $oStimulusNode->getAttribute('id'); @@ -130,7 +139,7 @@ class Forms extends AbstractConfiguration 'type' => null, 'properties' => $aFormProperties, 'fields' => null, - 'layout' => null + 'layout' => null, ); // ... either enumerated fields ... if ($oFormNode->GetOptionalElement('fields') !== null) @@ -138,7 +147,8 @@ class Forms extends AbstractConfiguration $aFields['type'] = 'custom_list'; $aFields['fields'] = array(); - foreach ($oFormNode->GetOptionalElement('fields')->GetNodes('field') as $oFieldNode) + /** @var \MFElement $oFieldNode */ + foreach ($oFormNode->GetOptionalElement('fields')->GetNodes('field') as $oFieldNode) { $sFieldId = $oFieldNode->getAttribute('id'); if ($sFieldId !== '') @@ -178,13 +188,13 @@ class Forms extends AbstractConfiguration if ($oFormNode->GetOptionalElement('twig') !== null) { // Extracting the twig template and removing the first and last lines (twig tags) - $sXml = $this->getModuleDesign()->saveXML($oFormNode->GetOptionalElement('twig')); + $sXml = $this->GetModuleDesign()->saveXML($oFormNode->GetOptionalElement('twig')); $sXml = preg_replace('/^.+\n/', '', $sXml); $sXml = preg_replace('/\n.+$/', '', $sXml); $aFields['layout'] = array( 'type' => (preg_match('/\{\{|\{\#|\{\%/', $sXml) === 1) ? 'twig' : 'xhtml', - 'content' => $sXml + 'content' => $sXml, ); } @@ -242,30 +252,28 @@ class Forms extends AbstractConfiguration } else { - throw new \DOMFormatException('There is already a form for the class "'.$sFormClass.'" in "'.$sMode.'"', + throw new DOMFormatException('There is already a form for the class "'.$sFormClass.'" in "'.$sMode.'"', null, null, $oFormNode); } } } else { - throw new \DOMFormatException('Class tag must be defined', null, null, $oFormNode); + throw new DOMFormatException('Class tag must be defined', null, null, $oFormNode); } } - catch (\DOMFormatException $e) + catch (DOMFormatException $e) { - throw new \Exception('Could not create from [id="'.$oFormNode->getAttribute('id').'"] from XML because of a DOM problem : '.$e->getMessage()); + throw new Exception('Could not create from [id="'.$oFormNode->getAttribute('id').'"] from XML because of a DOM problem : '.$e->getMessage()); } - catch (\Exception $e) + catch (Exception $e) { - throw new \Exception('Could not create from from XML : '.$oFormNode->Dump().' '.$e->getMessage()); + throw new Exception('Could not create from from XML : '.$oFormNode->Dump().' '.$e->getMessage()); } } - $aPortalConf = $container->getParameter('combodo.portal.instance.conf'); + $aPortalConf = $oContainer->getParameter('combodo.portal.instance.conf'); $aPortalConf['forms'] = $aForms; - $container->setParameter('combodo.portal.instance.conf', $aPortalConf); + $oContainer->setParameter('combodo.portal.instance.conf', $aPortalConf); } - - } \ No newline at end of file diff --git a/datamodels/2.x/itop-portal-base/portal/src/DependencyInjection/SilexCompatBootstrap/PortalXmlConfiguration/Lists.php b/datamodels/2.x/itop-portal-base/portal/src/DependencyInjection/SilexCompatBootstrap/PortalXmlConfiguration/Lists.php index 893e056da..e025f573b 100644 --- a/datamodels/2.x/itop-portal-base/portal/src/DependencyInjection/SilexCompatBootstrap/PortalXmlConfiguration/Lists.php +++ b/datamodels/2.x/itop-portal-base/portal/src/DependencyInjection/SilexCompatBootstrap/PortalXmlConfiguration/Lists.php @@ -1,27 +1,52 @@ + * @since 2.7.0 + */ class Lists extends AbstractConfiguration { - public function process(ContainerBuilder $container) + /** + * @param \Symfony\Component\DependencyInjection\Container $oContainer + * + * @throws \DOMFormatException + */ + public function Process(Container $oContainer) { $iDefaultItemRank = 0; $aClassesLists = array(); // Parsing XML file // - Each classes - foreach ($this->getModuleDesign()->GetNodes('/module_design/classes/class') as $oClassNode) + /** @var \MFElement $oClassNode */ + foreach ($this->GetModuleDesign()->GetNodes('/module_design/classes/class') as $oClassNode) { $aClassLists = array(); $sClassId = $oClassNode->getAttribute('id'); @@ -31,7 +56,8 @@ class Lists extends AbstractConfiguration } // - Each lists - foreach ($oClassNode->GetNodes('./lists/list') as $oListNode) + /** @var \MFElement $oListNode */ + foreach ($oClassNode->GetNodes('./lists/list') as $oListNode) { $aListItems = array(); $sListId = $oListNode->getAttribute('id'); @@ -42,7 +68,8 @@ class Lists extends AbstractConfiguration } // - Each items - foreach ($oListNode->GetNodes('./items/item') as $oItemNode) + /** @var \MFElement $oItemNode */ + foreach ($oListNode->GetNodes('./items/item') as $oItemNode) { $sItemId = $oItemNode->getAttribute('id'); if ($sItemId === null) @@ -53,7 +80,7 @@ class Lists extends AbstractConfiguration $aItem = array( 'att_code' => $sItemId, - 'rank' => $iDefaultItemRank + 'rank' => $iDefaultItemRank, ); $oRankNode = $oItemNode->GetOptionalElement('rank'); @@ -77,10 +104,9 @@ class Lists extends AbstractConfiguration $aClassesLists[$sClassId] = $aClassLists; } } - $aPortalConf = $container->getParameter('combodo.portal.instance.conf'); - $aPortalConf['lists'] = $aClassLists; - $container->setParameter('combodo.portal.instance.conf', $aPortalConf); + $aPortalConf = $oContainer->getParameter('combodo.portal.instance.conf'); + $aPortalConf['lists'] = $aClassLists; + $oContainer->setParameter('combodo.portal.instance.conf', $aPortalConf); } - } \ No newline at end of file diff --git a/datamodels/2.x/itop-portal-base/portal-silex/src/forms/objectformmanager.class.inc.php b/datamodels/2.x/itop-portal-base/portal/src/Form/ObjectFormManager.php similarity index 98% rename from datamodels/2.x/itop-portal-base/portal-silex/src/forms/objectformmanager.class.inc.php rename to datamodels/2.x/itop-portal-base/portal/src/Form/ObjectFormManager.php index 304dac157..ae338b71c 100644 --- a/datamodels/2.x/itop-portal-base/portal-silex/src/forms/objectformmanager.class.inc.php +++ b/datamodels/2.x/itop-portal-base/portal/src/Form/ObjectFormManager.php @@ -1,21 +1,24 @@ +/** + * Copyright (C) 2013-2019 Combodo SARL + * + * This file is part of iTop. + * + * iTop is free software; you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * 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. + * + * You should have received a copy of the GNU Affero General Public License + * + * + */ namespace Combodo\iTop\Portal\Form; @@ -45,10 +48,10 @@ use UserRights; use utils; /** - * Description of objectformmanager + * Description of ObjectFormManager * * @author Guillaume Lajarige - * @since iTop 2.3.0 + * @since 2.3.0 */ class ObjectFormManager extends FormManager { @@ -159,7 +162,7 @@ class ObjectFormManager extends FormManager * @param \Silex\Application $oApp * @return \Combodo\iTop\Portal\Form\ObjectFormManager */ - public function SetApplication(Application $oApp) + public function SetApplication($oApp) { $this->oApp = $oApp; return $this; diff --git a/datamodels/2.x/itop-portal-base/portal-silex/src/forms/passwordformmanager.class.inc.php b/datamodels/2.x/itop-portal-base/portal/src/Form/PasswordFormManager.php similarity index 86% rename from datamodels/2.x/itop-portal-base/portal-silex/src/forms/passwordformmanager.class.inc.php rename to datamodels/2.x/itop-portal-base/portal/src/Form/PasswordFormManager.php index 7ef34c4f3..63f602655 100644 --- a/datamodels/2.x/itop-portal-base/portal-silex/src/forms/passwordformmanager.class.inc.php +++ b/datamodels/2.x/itop-portal-base/portal/src/Form/PasswordFormManager.php @@ -1,21 +1,24 @@ +/** + * Copyright (C) 2013-2019 Combodo SARL + * + * This file is part of iTop. + * + * iTop is free software; you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * 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. + * + * You should have received a copy of the GNU Affero General Public License + * + * + */ namespace Combodo\iTop\Portal\Form; @@ -29,9 +32,10 @@ use Combodo\iTop\Form\Field\HiddenField; use Combodo\iTop\Form\Field\PasswordField; /** - * Description of passwordformmanager + * Description of PasswordFormManager * * @author Guillaume Lajarige + * @since 2.3.0 */ class PasswordFormManager extends FormManager { diff --git a/datamodels/2.x/itop-portal-base/portal-silex/src/forms/preferencesformmanager.class.inc.php b/datamodels/2.x/itop-portal-base/portal/src/Form/PreferencesFormManager.php similarity index 84% rename from datamodels/2.x/itop-portal-base/portal-silex/src/forms/preferencesformmanager.class.inc.php rename to datamodels/2.x/itop-portal-base/portal/src/Form/PreferencesFormManager.php index 9919e8ab6..8b5cf4902 100644 --- a/datamodels/2.x/itop-portal-base/portal-silex/src/forms/preferencesformmanager.class.inc.php +++ b/datamodels/2.x/itop-portal-base/portal/src/Form/PreferencesFormManager.php @@ -1,21 +1,24 @@ +/** + * Copyright (C) 2013-2019 Combodo SARL + * + * This file is part of iTop. + * + * iTop is free software; you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * 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. + * + * You should have received a copy of the GNU Affero General Public License + * + * + */ namespace Combodo\iTop\Portal\Form; @@ -30,9 +33,10 @@ use Combodo\iTop\Form\Field\HiddenField; use Combodo\iTop\Form\Field\SelectField; /** - * Description of preferencesformmanager + * Description of PreferencesFormManager * * @author Guillaume Lajarige + * @since 2.3.0 */ class PreferencesFormManager extends FormManager { @@ -117,6 +121,7 @@ class PreferencesFormManager extends FormManager $iFieldChanged = 0; // Updating user + /** @var \cmdbAbstractObject $oCurUser */ $oCurUser = UserRights::GetUserObject(); // - Language $sLanguage = $this->oForm->GetField('language')->GetCurrentValue(); 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 0ca0b8493..14fd5290e 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 @@ -709,7 +709,7 @@ class ApplicationHelper * @param string $sClass Object class to find a list for * @param string $sList List name to find * - * @return array Array of attribute codes + * @return string[] Array of attribute codes * * @throws \CoreException */ diff --git a/datamodels/2.x/itop-portal-base/portal/src/Helper/ContextManipulatorHelper.php b/datamodels/2.x/itop-portal-base/portal/src/Helper/ContextManipulatorHelper.php index 63291d43c..6b2ac2076 100644 --- a/datamodels/2.x/itop-portal-base/portal/src/Helper/ContextManipulatorHelper.php +++ b/datamodels/2.x/itop-portal-base/portal/src/Helper/ContextManipulatorHelper.php @@ -412,7 +412,7 @@ class ContextManipulatorHelper break; case static::ENUM_RULE_CALLBACK_GOTO: - $oBrick = $this->oBrickCollection->getBrickById($aRule[$sCallbackName]['brick_id']); + $oBrick = $this->oBrickCollection->GetBrickById($aRule[$sCallbackName]['brick_id']); $sCallbackUrl = $this->oRouter->generate($oBrick->GetRouteName(), array('sBrickId' => $oBrick->GetId())); break; 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 new file mode 100644 index 000000000..9e9abd96f --- /dev/null +++ b/datamodels/2.x/itop-portal-base/portal/src/Helper/ObjectFormHandlerHelper.php @@ -0,0 +1,358 @@ + + * @since 2.7.0 + */ +class ObjectFormHandlerHelper +{ + const ENUM_MODE_VIEW = 'view'; + const ENUM_MODE_EDIT = 'edit'; + const ENUM_MODE_CREATE = 'create'; + + /** @var \Combodo\iTop\Portal\Helper\RequestManipulatorHelper */ + private $oRequestManipulator; + /** @var \Combodo\iTop\Portal\Helper\ContextManipulatorHelper */ + private $oContextManipulator; + /** @var \Combodo\iTop\Portal\Helper\ScopeValidatorHelper */ + private $oScopeValidator; + /** @var \Combodo\iTop\Portal\Helper\SecurityHelper */ + private $oSecurityHelper; + /** @var \Combodo\iTop\Portal\Routing\UrlGenerator */ + private $oUrlGenerator; + /** @var \Combodo\iTop\Portal\VariableAccessor\CombodoPortalInstanceConf */ + private $aCombodoPortalInstanceConf; + /** @var string $sPortalId */ + private $sPortalId; + + /** + * 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\Routing\UrlGenerator $oUrlGenerator + * @param \Combodo\iTop\Portal\VariableAccessor\CombodoPortalInstanceConf $aCombodoPortalInstanceConf + * @param string $sPortalId + */ + public function __construct(RequestManipulatorHelper $oRequestManipulator, ContextManipulatorHelper $oContextManipulator, ScopeValidatorHelper $oScopeValidator, SecurityHelper $oSecurityHelper, UrlGeneratorInterface $oUrlGenerator, CombodoPortalInstanceConf $aCombodoPortalInstanceConf, $sPortalId) + { + $this->oRequestManipulator = $oRequestManipulator; + $this->oContextManipulator = $oContextManipulator; + $this->oScopeValidator = $oScopeValidator; + $this->oSecurityHelper = $oSecurityHelper; + $this->oUrlGenerator = $oUrlGenerator; + $this->aCombodoPortalInstanceConf = $aCombodoPortalInstanceConf; + $this->sPortalId = $sPortalId; + } + + /** + * @param \Symfony\Component\HttpFoundation\Request $oRequest + * @param string $sMode + * @param string $sObjectClass + * @param string $sObjectId + * @param string $aFormProperties + * + * @return array + * + * @throws \ArchivedObjectException + * @throws \CoreException + * @throws \OQLException + * @throws \Exception + */ + public function HandleForm(Request $oRequest, $sMode, $sObjectClass, $sObjectId = null, $aFormProperties = null) + { + $aFormData = array(); + $sOperation = $this->oRequestManipulator->ReadParam('operation', ''); + $bModal = ($oRequest->isXmlHttpRequest() && empty($sOperation)); + + // - Retrieve form properties + if ($aFormProperties === null) + { + $aFormProperties = ApplicationHelper::GetLoadedFormFromClass($this->aCombodoPortalInstanceConf['forms'], $sObjectClass, $sMode); + } + + // - Create and + if (empty($sOperation)) + { + // Retrieving action rules + // + // Note : The action rules must be a base64-encoded JSON object, this is just so users are tempted to changes values. + // But it would not be a security issue as it only presets values in the form. + $sActionRulesToken = $this->oRequestManipulator->ReadParam('ar_token', ''); + $aActionRules = (!empty($sActionRulesToken)) ? ContextManipulatorHelper::DecodeRulesToken($sActionRulesToken) : array(); + + // Preparing object + if ($sObjectId === null) + { + // Create new UserRequest + $oObject = MetaModel::NewObject($sObjectClass); + + // Retrieve action rules information to auto-fill the form if available + // Preparing object + $this->oContextManipulator->PrepareObject($aActionRules, $oObject); + $aPrefillFormParam = array( 'user' => $_SESSION["auth_user"], + 'origin' => 'portal'); + $oObject->PrefillForm('creation_from_0', $aPrefillFormParam); + } + else + { + $oObject = MetaModel::GetObject($sObjectClass, $sObjectId, true, $this->oScopeValidator->IsAllDataAllowedForScope(UserRights::ListProfiles(), $sObjectClass)); + } + + // Preparing buttons + $aFormData['buttons'] = array( + 'transitions' => array(), + 'actions' => array(), + 'links' => array(), + 'submit' => array( + 'label' => Dict::S('Portal:Button:Submit'), + ), + ); + if ($sMode !== 'apply_stimulus') + { + // Add transition buttons + $oSetToCheckRights = DBObjectSet::FromObject($oObject); + $aStimuli = Metamodel::EnumStimuli($sObjectClass); + foreach ($oObject->EnumTransitions() as $sStimulusCode => $aTransitionDef) + { + if($this->oSecurityHelper->IsStimulusAllowed($sStimulusCode, $sObjectClass, $oSetToCheckRights)) + { + $aFormData['buttons']['transitions'][$sStimulusCode] = $aStimuli[$sStimulusCode]->GetLabel(); + } + } + + // Add plugin buttons + /** @var \iPopupMenuExtension $oExtensionInstance */ + foreach (MetaModel::EnumPlugins('iPopupMenuExtension') as $oExtensionInstance) + { + foreach($oExtensionInstance->EnumItems(iPopupMenuExtension::PORTAL_OBJDETAILS_ACTIONS, array('portal_id' => $this->sPortalId, 'object' => $oObject, 'mode' => $sMode)) as $oMenuItem) + { + if (is_object($oMenuItem)) + { + if($oMenuItem instanceof JSButtonItem) + { + $aFormData['buttons']['actions'][] = $oMenuItem->GetMenuItem() + array('js_files' => $oMenuItem->GetLinkedScripts()); + } + elseif($oMenuItem instanceof URLButtonItem) + { + $aFormData['buttons']['links'][] = $oMenuItem->GetMenuItem(); + } + } + } + } + + // Hiding submit button or changing its label if necessary + if(!empty($aFormData['buttons']['transitions']) && isset($aFormProperties['properties']) &&$aFormProperties['properties']['always_show_submit'] === false) + { + unset($aFormData['buttons']['submit']); + } + elseif($sMode === static::ENUM_MODE_EDIT) + { + $aFormData['buttons']['submit']['label'] = Dict::S('Portal:Button:Apply'); + } + } + else + { + $aPrefillFormParam = array( + 'user' => $_SESSION["auth_user"], + 'origin' => 'portal', + 'stimulus' => $this->oRequestManipulator->ReadParam('apply_stimulus', null)['code'], + ); + $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 renderer + // Note : We might need to distinguish form & renderer endpoints + if (in_array($sMode, array('create', 'edit', 'view'))) + { + $sFormEndpoint = $this->oUrlGenerator->generate('p_object_' . $sMode, array('sObjectClass' => $sObjectClass, 'sObjectId' => $sObjectId)); + } + else + { + $sFormEndpoint = $_SERVER['REQUEST_URI']; + } + $oFormRenderer = new BsFormRenderer(); + $oFormRenderer->SetEndpoint($sFormEndpoint); + + // TODO: Remove this when ObjectFormManager fixed. + $oApp = null; + $oFormManager = new ObjectFormManager(); + $oFormManager->SetApplication($oApp) + ->SetObject($oObject) + ->SetMode($sMode) + ->SetActionRulesToken($sActionRulesToken) + ->SetRenderer($oFormRenderer) + ->SetFormProperties($aFormProperties); + + $oFormManager->Build(); + + // Check the number of editable fields + $aFormData['editable_fields_count'] = $oFormManager->GetForm()->GetEditableFieldCount(); + } + else + { + // Update / Submit / Cancel + $sFormManagerClass = $this->oRequestManipulator->ReadParam('formmanager_class', '', FILTER_UNSAFE_RAW); + $sFormManagerData = $this->oRequestManipulator->ReadParam('formmanager_data', '', FILTER_UNSAFE_RAW); + if ( empty($sFormManagerClass) || empty($sFormManagerData) ) + { + IssueLog::Error(__METHOD__ . ' at line ' . __LINE__ . ' : Parameters formmanager_class and formamanager_data must be defined.'); + throw new HttpException(Response::HTTP_INTERNAL_SERVER_ERROR, 'Parameters formmanager_class and formmanager_data must be defined.'); + } + + // TODO: Remove this when ObjectFormManager fixed. + $oApp = null; + /** @var \Combodo\iTop\Portal\Form\ObjectFormManager $oFormManager */ + $oFormManager = $sFormManagerClass::FromJSON($sFormManagerData); + $oFormManager->SetApplication($oApp); + + // Applying action rules if present + if (($oFormManager->GetActionRulesToken() !== null) && ($oFormManager->GetActionRulesToken() !== '')) + { + $aActionRules = ContextManipulatorHelper::DecodeRulesToken($oFormManager->GetActionRulesToken()); + $oObj = $oFormManager->GetObject(); + $this->oContextManipulator->PrepareObject($aActionRules, $oObj); + $oFormManager->SetObject($oObj); + } + + switch ($sOperation) + { + case 'submit': + // Applying modification to object + $aFormData['validation'] = $oFormManager->OnSubmit( + array( + 'currentValues' => $this->oRequestManipulator->ReadParam('current_values', array(), FILTER_UNSAFE_RAW), + 'attachmentIds' => $this->oRequestManipulator->ReadParam('attachment_ids', array(), FILTER_UNSAFE_RAW), + 'formProperties' => $aFormProperties, + 'applyStimulus' => $this->oRequestManipulator->ReadParam('apply_stimulus', null) + ) + ); + 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 + // 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, + ); + } + // 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; + + case 'update': + $oFormManager->OnUpdate(array('currentValues' => $this->oRequestManipulator->ReadParam('current_values', array(), FILTER_UNSAFE_RAW), 'formProperties' => $aFormProperties)); + break; + + case 'cancel': + $oFormManager->OnCancel(); + break; + } + } + + // Preparing field_set data + $aFieldSetData = array( + //'fields_list' => $oFormManager->GetRenderer()->Render(), // GLA : This should be done just after in the if statement. + 'fields_impacts' => $oFormManager->GetForm()->GetFieldsImpacts(), + 'form_path' => $oFormManager->GetForm()->GetId(), + ); + + // Preparing fields list regarding the operation + if ($sOperation === 'update') + { + $aRequestedFields = $this->oRequestManipulator->ReadParam('requested_fields', array(), FILTER_UNSAFE_RAW); + $sFormPath = $this->oRequestManipulator->ReadParam('form_path', ''); + + // Checking if the update was on a subform, if so we need to make the rendering for that part only + if ( !empty($sFormPath) && $sFormPath !== $oFormManager->GetForm()->GetId() ) + { + $oSubForm = $oFormManager->GetForm()->FindSubForm($sFormPath); + $oSubFormRenderer = new BsFormRenderer($oSubForm); + $oSubFormRenderer->SetEndpoint($oFormManager->GetRenderer()->GetEndpoint()); + $aFormData['updated_fields'] = $oSubFormRenderer->Render($aRequestedFields); + } + else + { + $aFormData['updated_fields'] = $oFormManager->GetRenderer()->Render($aRequestedFields); + } + } + else + { + $aFieldSetData['fields_list'] = $oFormManager->GetRenderer()->Render(); + } + + // Preparing form data + $aFormData['id'] = $oFormManager->GetForm()->GetId(); + $aFormData['transaction_id'] = $oFormManager->GetForm()->GetTransactionId(); + $aFormData['formmanager_class'] = $oFormManager->GetClass(); + $aFormData['formmanager_data'] = $oFormManager->ToJSON(); + $aFormData['renderer'] = $oFormManager->GetRenderer(); + $aFormData['object_name'] = $oFormManager->GetObject()->GetName(); + $aFormData['object_class'] = get_class($oFormManager->GetObject()); + $aFormData['object_id'] = $oFormManager->GetObject()->GetKey(); + $aFormData['object_state'] = $oFormManager->GetObject()->GetState(); + $aFormData['fieldset'] = $aFieldSetData; + $aFormData['display_mode'] = (isset($aFormProperties['properties'])) ? $aFormProperties['properties']['display_mode'] : ApplicationHelper::FORM_DEFAULT_DISPLAY_MODE; + + return $aFormData; + } +} \ No newline at end of file diff --git a/datamodels/2.x/itop-portal-base/portal/src/Twig/AppVariable.php b/datamodels/2.x/itop-portal-base/portal/src/Twig/AppVariable.php index 072663696..7b3593dab 100644 --- a/datamodels/2.x/itop-portal-base/portal/src/Twig/AppVariable.php +++ b/datamodels/2.x/itop-portal-base/portal/src/Twig/AppVariable.php @@ -19,19 +19,21 @@ * */ -/** - * Created by Bruno DA SILVA, working for Combodo - * Date: 30/01/19 - * Time: 16:38 - */ - namespace Combodo\iTop\Portal\Twig; +use ArrayAccess; use Symfony\Bridge\Twig\AppVariable as DecoratedAppVariable; -use Symfony\Component\DependencyInjection\ContainerAwareInterface; use Symfony\Component\DependencyInjection\ContainerInterface; -class AppVariable implements \ArrayAccess +/** + * Class AppVariable + * + * @package Combodo\iTop\Portal\Twig + * @author Bruno Da Silva + * @author Guillaume Lajarige + * @since 2.7.0 + */ +class AppVariable implements ArrayAccess { /** @var ContainerInterface */ private $container; diff --git a/datamodels/2.x/itop-portal-base/portal/templates/bricks/user-profile/layout.html.twig b/datamodels/2.x/itop-portal-base/portal/templates/bricks/user-profile/layout.html.twig index 2a11d265f..f829c3b86 100644 --- a/datamodels/2.x/itop-portal-base/portal/templates/bricks/user-profile/layout.html.twig +++ b/datamodels/2.x/itop-portal-base/portal/templates/bricks/user-profile/layout.html.twig @@ -134,7 +134,7 @@
{% block pUserProfileFormButtons %}
- {% if sFormMode == constant('\\Combodo\\iTop\\Portal\\Controller\\ObjectController::ENUM_MODE_EDIT') %} + {% if sFormMode == constant('\\Combodo\\iTop\\Portal\\Helper\\ObjectFormHandlerHelper::ENUM_MODE_EDIT') %} {% endif %}
diff --git a/datamodels/2.x/itop-portal-base/portal/templates/home/layout.html.twig b/datamodels/2.x/itop-portal-base/portal/templates/home/layout.html.twig index 8388be9be..caa1add9c 100644 --- a/datamodels/2.x/itop-portal-base/portal/templates/home/layout.html.twig +++ b/datamodels/2.x/itop-portal-base/portal/templates/home/layout.html.twig @@ -10,7 +10,7 @@
- {% for brick in app['brick_collection'].bricks_ordering.home %} + {% for brick in app['brick_collection'].home_ordering %} {% if brick.GetVisibleHome %} {% if aTilesRendering[brick.GetId] is defined %} {{ aTilesRendering[brick.GetId]|raw }} diff --git a/datamodels/2.x/itop-portal-base/portal/templates/layout.html.twig b/datamodels/2.x/itop-portal-base/portal/templates/layout.html.twig index c0f668f54..bfcd92f03 100644 --- a/datamodels/2.x/itop-portal-base/portal/templates/layout.html.twig +++ b/datamodels/2.x/itop-portal-base/portal/templates/layout.html.twig @@ -195,7 +195,7 @@ {{ 'Page:Home'|dict_s }} - {% for brick in app['brick_collection'].bricks_ordering.navigation_menu %} + {% for brick in app['brick_collection'].navigation_menu_ordering %} {% if brick.GetActive and brick.GetVisibleNavigationMenu and brick.GetRouteName is not null %}
  • @@ -272,7 +272,7 @@ {{ 'Page:Home'|dict_s }}
  • - {% for brick in app['brick_collection'].bricks_ordering.navigation_menu %} + {% for brick in app['brick_collection'].navigation_menu_ordering %} {% if brick.GetActive and brick.GetVisibleNavigationMenu and brick.GetRouteName is not null %}