diff --git a/application/utils.inc.php b/application/utils.inc.php index 5ed9f79ff..62acd4a4f 100644 --- a/application/utils.inc.php +++ b/application/utils.inc.php @@ -1053,6 +1053,15 @@ class utils { return APPROOT.'data/cache-'.MetaModel::GetEnvironment().'/'; } + /** + * Returns a path to a folder into which any module can store log + * @return string + * @since 2.7.0 + */ + public static function GetLogPath() + { + return APPROOT.'log/'; + } /** * Merge standard menu items with plugin provided menus items */ diff --git a/datamodels/2.x/itop-portal-base/composer.json b/datamodels/2.x/itop-portal-base/composer.json new file mode 100644 index 000000000..6a285560d --- /dev/null +++ b/datamodels/2.x/itop-portal-base/composer.json @@ -0,0 +1,19 @@ +{ + "require": { + "php": ">=5.6.0", + "symfony/symfony": "^3.4" + }, + "config": { + "vendor-dir": "../../../../lib/composer-vendor" + }, + "autoload": { + "psr-4": { + "Combodo\\iTop\\Portal\\": "src/" + } + }, + "autoload-dev": { + "psr-4": { + "Combodo\\iTop\\Portal\\Tests\\": "tests/" + } + } +} \ No newline at end of file diff --git a/datamodels/2.x/itop-portal-base/index.php b/datamodels/2.x/itop-portal-base/index.php index d9a1c8295..38979ec9e 100644 --- a/datamodels/2.x/itop-portal-base/index.php +++ b/datamodels/2.x/itop-portal-base/index.php @@ -44,8 +44,17 @@ if(!defined('PORTAL_ID')) } // Defining portal constants - define('PORTAL_MODULE_ID', $sPortalId); define('PORTAL_ID', $sPortalId); } -require_once APPROOT . '/env-' . utils::GetCurrentEnvironment() . '/itop-portal-base/portal/web/index.php'; +define('PORTAL_CACHE_PATH', utils::GetCachePath() . '/portals/' . PORTAL_ID . '/'); + +// Constants to be used in templates and others +define('COMBODO_CURRENT_ENVIRONMENT', utils::GetCurrentEnvironment()); +define('COMBODO_ABSOLUTE_URL', utils::GetAbsoluteUrlAppRoot()); +define('COMBODO_MODULES_ABSOLUTE_URL', utils::GetAbsoluteUrlAppRoot() . 'env-' . utils::GetCurrentEnvironment()); +define('COMBODO_PORTAL_BASE_ABSOLUTE_URL', utils::GetAbsoluteUrlAppRoot() . 'env-' . utils::GetCurrentEnvironment() . '/itop-portal-base/portal/public/'); +define('COMBODO_PORTAL_BASE_ABSOLUTE_PATH', MODULESROOT . '/itop-portal-base/portal/public/'); +define('COMBODO_PORTAL_INSTANCE_ABSOLUTE_URL', utils::GetAbsoluteUrlAppRoot() . 'env-' . utils::GetCurrentEnvironment() . '/' . PORTAL_ID . '/'); + +require_once APPROOT . '/env-' . utils::GetCurrentEnvironment() . '/itop-portal-base/portal/public/index.php'; diff --git a/datamodels/2.x/itop-portal-base/module.itop-portal-base.php b/datamodels/2.x/itop-portal-base/module.itop-portal-base.php index db386574a..e00ee8240 100644 --- a/datamodels/2.x/itop-portal-base/module.itop-portal-base.php +++ b/datamodels/2.x/itop-portal-base/module.itop-portal-base.php @@ -13,11 +13,10 @@ SetupWebPage::AddModule( 'visible' => true, // Components 'datamodel' => array( - 'portal/src/controllers/abstractcontroller.class.inc.php', - 'portal/src/controllers/brickcontroller.class.inc.php', - 'portal/src/entities/abstractbrick.class.inc.php', - 'portal/src/entities/portalbrick.class.inc.php', - 'portal/src/routers/abstractrouter.class.inc.php', +// 'portal/src/controllers/brickcontroller.class.inc.php', +// 'portal/src/entities/abstractbrick.class.inc.php', +// 'portal/src/entities/portalbrick.class.inc.php', +// 'portal/src/routers/abstractrouter.class.inc.php', ), 'webservice' => array( //'webservices.itop-portal-base.php', diff --git a/datamodels/2.x/itop-portal-base/portal-silex/src/controllers/abstractcontroller.class.inc.php b/datamodels/2.x/itop-portal-base/portal-silex/src/controllers/abstractcontroller.class.inc.php deleted file mode 100644 index c65fc2668..000000000 --- a/datamodels/2.x/itop-portal-base/portal-silex/src/controllers/abstractcontroller.class.inc.php +++ /dev/null @@ -1,32 +0,0 @@ - - -namespace Combodo\iTop\Portal\Controller; - -/** - * Class AbstractController - * - * @package Combodo\iTop\Portal\Controller - * @author Guillaume Lajarige - * @since 2.3.0 - */ -abstract class AbstractController -{ - -} diff --git a/datamodels/2.x/itop-portal-base/portal-silex/src/controllers/defaultcontroller.class.inc.php b/datamodels/2.x/itop-portal-base/portal-silex/src/controllers/defaultcontroller.class.inc.php deleted file mode 100644 index 0e3eb2dab..000000000 --- a/datamodels/2.x/itop-portal-base/portal-silex/src/controllers/defaultcontroller.class.inc.php +++ /dev/null @@ -1,72 +0,0 @@ - - -namespace Combodo\iTop\Portal\Controller; - -use Silex\Application; -use Symfony\Component\HttpFoundation\Request; - -/** - * Class DefaultController - * - * @package Combodo\iTop\Portal\Controller - * @author Guillaume Lajarige - * @since 2.3.0 - */ -class DefaultController -{ - /** - * @param \Symfony\Component\HttpFoundation\Request $oRequest - * @param \Silex\Application $oApp - * - * @return \Symfony\Component\HttpFoundation\Response - */ - public function homeAction(Request $oRequest, Application $oApp) - { - $aData = array(); - - // Rendering tiles - $aData['aTilesRendering'] = array(); - /** @var \Combodo\iTop\Portal\Brick\PortalBrick $oBrick */ - foreach($oApp['combodo.portal.instance.conf']['bricks'] as $oBrick) - { - // Doing it only for tile visible on home page to avoid unnecessary rendering - if (($oBrick->GetVisibleHome() === true) && ($oBrick->GetTileControllerAction() !== null)) - { - $aControllerActionParts = explode('::', $oBrick->GetTileControllerAction()); - if (count($aControllerActionParts) !== 2) - { - $oApp->abort(500, 'Tile controller action must be of form "\Namespace\ControllerClass::FunctionName" for brick "' . $oBrick->GetId() . '"'); - } - - $sControllerName = $aControllerActionParts[0]; - $sControllerAction = $aControllerActionParts[1]; - - $oController = new $sControllerName($oRequest, $oApp, $oBrick->GetId()); - $aData['aTilesRendering'][$oBrick->GetId()] = $oController->$sControllerAction($oRequest, $oApp, $oBrick->GetId()); - } - } - - // Home page template - $template = $oApp['combodo.portal.instance.conf']['properties']['templates']['home']; - - return $oApp['twig']->render($template, $aData); - } - -} diff --git a/datamodels/2.x/itop-portal-base/portal-silex/src/helpers/scopevalidatorhelper.class.inc.php b/datamodels/2.x/itop-portal-base/portal-silex/src/helpers/scopevalidatorhelper.class.inc.php deleted file mode 100644 index b0cdf9827..000000000 --- a/datamodels/2.x/itop-portal-base/portal-silex/src/helpers/scopevalidatorhelper.class.inc.php +++ /dev/null @@ -1,618 +0,0 @@ - - -namespace Combodo\iTop\Portal\Helper; - -use DBSearch; -use DBUnionSearch; -use DOMFormatException; -use DOMNodeList; -use Exception; -use MetaModel; -use ProfilesConfig; -use UserRights; -use utils; - -/** - * Class ScopeValidatorHelper - * - * Inside the portal this service is injected, get the instance using $oApp['scope_validator'] - * - * @package Combodo\iTop\Portal\Helper - */ -class ScopeValidatorHelper -{ - const ENUM_MODE_READ = 'r'; - const ENUM_MODE_WRITE = 'w'; - const ENUM_TYPE_ALLOW = 'allow'; - const ENUM_TYPE_RESTRICT = 'restrict'; - const DEFAULT_GENERATED_CLASS = 'PortalScopesValues'; - const DEFAULT_IGNORE_SILOS = false; - - protected $sCachePath; - protected $sFilename; - protected $sInstancePrefix; - protected $sGeneratedClass; - protected $aProfilesMatrix; - - public static function EnumTypeValues() - { - return array(static::ENUM_TYPE_ALLOW, static::ENUM_TYPE_RESTRICT); - } - - public function __construct($sFilename, $sCachePath = null) - { - $this->sFilename = $sFilename; - $this->sCachePath = $sCachePath; - $this->sInstancePrefix = ''; - $this->sGeneratedClass = static::DEFAULT_GENERATED_CLASS; - $this->aProfilesMatrix = array(); - } - - /** - * Returns the path where to cache the compiled scopes file - * - * @return string - */ - public function GetCachePath() - { - return $this->sCachePath; - } - - /** - * Returns the name of the compiled scopes file - * - * @return string - */ - public function GetFilename() - { - return $this->sFilename; - } - - /** - * Returns the instance prefix used for the generated scopes class name - * - * @return string - */ - public function GetInstancePrefix() - { - return $this->sInstancePrefix; - } - - /** - * Returns the name of the generated scopes class - * - * @return string - */ - public function GetGeneratedClass() - { - return $this->sGeneratedClass; - } - - /** - * Sets the scope validator instance prefix. - * - * This is used to create a unique scope values class in the cache directory (/data/cache-) as there can be several instance of the portal. - * - * @param string $sInstancePrefix - * @return \Combodo\iTop\Portal\Helper\ScopeValidatorHelper - */ - public function SetInstancePrefix($sInstancePrefix) - { - $sInstancePrefix = preg_replace('/[-_]/', ' ', $sInstancePrefix); - $sInstancePrefix = ucwords($sInstancePrefix); - $sInstancePrefix = str_replace(' ', '', $sInstancePrefix); - - $this->sInstancePrefix = $sInstancePrefix; - $this->sGeneratedClass = $this->sInstancePrefix . static::DEFAULT_GENERATED_CLASS; - - return $this; - } - - /** - * Initializes the ScopeValidator by generating and caching the scopes compilation in the $this->sCachePath.$this->sFilename file. - * - * @param DOMNodeList $oNodes - * @throws DOMFormatException - * @throws Exception - */ - public function Init(DOMNodeList $oNodes) - { - // Checking cache path - if ($this->sCachePath === null) - { - $this->sCachePath = utils::GetCachePath(); - } - // Building full pathname for file - $sFilePath = $this->sCachePath . $this->sFilename; - - // Creating file if not existing - // Note : This is a temporary cache system, it should soon evolve to a cache provider (fs, apc, memcache, ...) - if (!file_exists($sFilePath)) - { - // - Build php array from xml - $aProfiles = array(); - // This will be used to know which classes have been set, so we can set the missing ones. - $aProfileClasses = array(); - // Iterating over the class nodes - foreach ($oNodes as $oClassNode) - { - // retrieving mandatory class id attribute - $sClass = $oClassNode->getAttribute('id'); - if ($sClass === '') - { - throw new DOMFormatException('Class tag must have an id attribute.', null, null, $oClassNode); - } - - // Iterating over scope nodes of the class - $oScopesNode = $oClassNode->GetOptionalElement('scopes'); - if ($oScopesNode !== null) - { - foreach ($oScopesNode->GetNodes('./scope') as $oScopeNode) - { - // Retrieving mandatory scope id attribute - $sScopeId = $oScopeNode->getAttribute('id'); - if ($sScopeId === '') - { - throw new DOMFormatException('Scope tag must have an id attribute.', null, null, $oScopeNode); - } - - // Retrieving the type of query - // Note : This has been disabled as we don't want deny rules for now - // $oOqlViewTypeNode = $oClassNode->GetOptionalElement('oql_view_type'); - // $sOqlViewType = ($oOqlViewTypeNode !== null && ($oOqlViewTypeNode->GetText() === static::ENUM_TYPE_RESTRICT)) ? static::ENUM_TYPE_RESTRICT : static::ENUM_TYPE_ALLOW; - $sOqlViewType = static::ENUM_TYPE_ALLOW; - // Retrieving the view query - $oOqlViewNode = $oScopeNode->GetUniqueElement('oql_view'); - $sOqlView = $oOqlViewNode->GetText(); - if ($sOqlView === null) - { - throw new DOMFormatException('Scope tag in class must have a not empty oql_view tag', null, null, $oScopeNode); - } - // Retrieving the edit query - $oOqlEditNode = $oScopeNode->GetOptionalElement('oql_edit'); - $sOqlEdit = ( ($oOqlEditNode !== null) && ($oOqlEditNode->GetText() !== null) ) ? $oOqlEditNode->GetText() : null; - // Retrieving ignore allowed org flag - $oIgnoreSilosNode = $oScopeNode->GetOptionalElement('ignore_silos'); - $bIgnoreSilos = ( ($oIgnoreSilosNode !== null) && ($oIgnoreSilosNode->GetText() === 'true') ) ? true : static::DEFAULT_IGNORE_SILOS; - - // Retrieving profiles for the scope - $oProfilesNode = $oScopeNode->GetOptionalElement('allowed_profiles'); - $aProfilesNames = array(); - // If no profile is specified, we consider that it's for ALL the profiles - if (($oProfilesNode === null) || ($oProfilesNode->GetNodes('./allowed_profile')->length === 0)) - { - foreach (ProfilesConfig::GetProfilesValues() as $iKey => $aValue) - { - $aProfilesNames[] = $aValue['name']; - } - } - else - { - foreach ($oProfilesNode->GetNodes('./allowed_profile') as $oProfileNode) - { - // Retrieving mandatory profile id attribute - $sProfileId = $oProfileNode->getAttribute('id'); - if ($sProfileId === '') - { - throw new DOMFormatException('Scope tag must have an id attribute.', null, null, $oProfileNode); - } - $aProfilesNames[] = $sProfileId; - } - } - - // - foreach ($aProfilesNames as $sProfileName) - { - // Scope profile id - $iProfileId = $this->GetProfileIdFromProfileName($sProfileName); - - // Now that we have the queries infos, we are going to build the queries for that profile / class - $sMatrixPrefix = $iProfileId . '_' . $sClass . '_'; - // - View query - $oViewFilter = DBSearch::FromOQL($sOqlView); - // ... We have to union the query if this profile has another scope for that class - if (array_key_exists($sMatrixPrefix . static::ENUM_MODE_READ, $aProfiles) && array_key_exists($sOqlViewType, $aProfiles[$sMatrixPrefix . static::ENUM_MODE_READ])) - { - $oExistingFilter = DBSearch::FromOQL($aProfiles[$sMatrixPrefix . static::ENUM_MODE_READ][$sOqlViewType]); - $aFilters = array($oExistingFilter, $oViewFilter); - $oResFilter = new DBUnionSearch($aFilters); - - // Applying ignore_silos flag on result filter if necessary (As the union will remove it if it is not on all sub-queries) - if ($aProfiles[$sMatrixPrefix . static::ENUM_MODE_READ]['ignore_silos'] === true) - { - $bIgnoreSilos = true; - } - } - else - { - $oResFilter = $oViewFilter; - } - $aProfiles[$sMatrixPrefix . static::ENUM_MODE_READ] = array( - $sOqlViewType => $oResFilter->ToOQL(), - 'ignore_silos' => $bIgnoreSilos - ); - // - Edit query - if ($sOqlEdit !== null) - { - $oEditFilter = DBSearch::FromOQL($sOqlEdit); - // - If the queries are the same, we don't make an intersect, we just reuse the view query - if ($sOqlEdit === $sOqlView) - { - // Do not intersect, edit query is identical to view query - } - else - { - if (($oEditFilter->GetClass() === $oViewFilter->GetClass()) && $oEditFilter->IsAny()) - { - $oEditFilter = $oViewFilter; - // Do not intersect, edit query is identical to view query - } - else - { - // Intersect - $oEditFilter = $oViewFilter->Intersect($oEditFilter); - } - } - - // ... We have to union the query if this profile has another scope for that class - if (array_key_exists($sMatrixPrefix . static::ENUM_MODE_WRITE, $aProfiles) && array_key_exists($sOqlViewType, $aProfiles[$sMatrixPrefix . static::ENUM_MODE_WRITE])) - { - $oExistingFilter = DBSearch::FromOQL($aProfiles[$sMatrixPrefix . static::ENUM_MODE_WRITE][$sOqlViewType]); - $aFilters = array($oExistingFilter, $oEditFilter); - $oResFilter = new DBUnionSearch($aFilters); - } - else - { - $oResFilter = $oEditFilter; - } - $aProfiles[$sMatrixPrefix . static::ENUM_MODE_WRITE] = array( - $sOqlViewType => $oResFilter->ToOQL(), - 'ignore_silos' => $bIgnoreSilos - ); - } - } - } - - $aProfileClasses[] = $sClass; - } - } - - // Filling the array with missing classes from MetaModel, so we can have an inheritance principle on the scope - // For each class explicitly given in the scopes, we check if its child classes were also in the scope : - // If not, we add them with the same OQL - foreach ($aProfileClasses as $sProfileClass) - { - foreach (MetaModel::EnumChildClasses($sProfileClass) as $sChildClass) - { - // If the child class is not in the scope, we are going to try to add it - if (!in_array($sChildClass, $aProfileClasses)) - { - foreach (ProfilesConfig::GetProfilesValues() as $iKey => $aValue) - { - $iProfileId = $iKey; - foreach (array(static::ENUM_MODE_READ, static::ENUM_MODE_WRITE) as $sAction) - { - // If the current profile has scope for that class in that mode, we duplicate it - if (isset($aProfiles[$iProfileId . '_' . $sProfileClass . '_' . $sAction])) - { - $aTmpProfile = $aProfiles[$iProfileId . '_' . $sProfileClass . '_' . $sAction]; - foreach ($aTmpProfile as $sType => $sOql) - { - // IF condition is just to skip the 'ignore_silos' flag - if (in_array($sType, array(static::ENUM_TYPE_ALLOW, static::ENUM_TYPE_RESTRICT))) - { - $oTmpFilter = DBSearch::FromOQL($sOql); - $oTmpFilter->ChangeClass($sChildClass); - - $aTmpProfile[$sType] = $oTmpFilter->ToOQL(); - } - } - - $aProfiles[$iProfileId . '_' . $sChildClass . '_' . $sAction] = $aTmpProfile; - } - } - } - } - } - } - - // - Build php class - $sPHP = $this->BuildPHPClass($aProfiles); - - // - Write file on disk - // - Creating dir if necessary - if (!is_dir($this->sCachePath)) - { - mkdir($this->sCachePath, 0777, true); - } - // -- Then creating the file - $ret = file_put_contents($sFilePath, $sPHP); - if ($ret === false) - { - $iLen = strlen($sPHP); - $fFree = @disk_free_space(dirname($sFilePath)); - $aErr = error_get_last(); - throw new Exception("Failed to write '$sFilePath'. Last error: '{$aErr['message']}', content to write: $iLen bytes, available free space on disk: $fFree."); - } - } - - if (!class_exists($this->sGeneratedClass)) - { - require_once $this->sCachePath . $this->sFilename; - } - } - - /** - * Returns the DBSearch for the $sProfile in $iAction for the class $sClass - * - * @param string $sProfile - * @param string $sClass - * @param integer $iAction - * - * @return \DBSearch - * - * @throws \Exception - * @throws \CoreException - * @throws \OQLException - */ - public function GetScopeFilterForProfile($sProfile, $sClass, $iAction = null) - { - return $this->GetScopeFilterForProfiles(array($sProfile), $sClass, $iAction); - } - - /** - * Returns the DBSearch for the $aProfiles in $iAction for the class $sClass. - * Profiles are a OR condition. - * - * @param array $aProfiles - * @param string $sClass - * @param integer $iAction - * - * @return \DBSearch - * - * @throws \Exception - * @throws \CoreException - * @throws \OQLException - */ - public function GetScopeFilterForProfiles($aProfiles, $sClass, $iAction = null) - { - $oSearch = null; - $aAllowSearches = array(); - $aRestrictSearches = array(); - $bIgnoreSilos = static::DEFAULT_IGNORE_SILOS; - - // Checking the default mode - if ($iAction === null) - { - $iAction = UR_ACTION_READ; - } - - // Iterating on profiles to retrieving the different OQLs parts - foreach ($aProfiles as $sProfile) - { - // Retrieving matrix informtions - $iProfileId = $this->GetProfileIdFromProfileName($sProfile); - $sMode = ($iAction === UR_ACTION_READ) ? static::ENUM_MODE_READ : static::ENUM_MODE_WRITE; - - // Retrieving profile OQLs - $sScopeValuesClass = $this->sGeneratedClass; - $aProfileMatrix = $sScopeValuesClass::GetProfileScope($iProfileId, $sClass, $sMode); - if ($aProfileMatrix !== null) - { - if (isset($aProfileMatrix['allow']) && $aProfileMatrix['allow'] !== null) - { - $aAllowSearches[] = DBSearch::FromOQL($aProfileMatrix['allow']); - } - if (isset($aProfileMatrix['restrict']) && $aProfileMatrix['restrict'] !== null) - { - $aRestrictSearches[] = DBSearch::FromOQL($aProfileMatrix['restrict']); - } - // If a profile should ignore allowed org, we set it for all its queries no matter the profile - if (isset($aProfileMatrix['ignore_silos']) && $aProfileMatrix['ignore_silos'] === true) - { - $bIgnoreSilos = true; - } - } - } - - // Building the real OQL from all the parts from the differents profiles - for ($i = 0; $i < count($aAllowSearches); $i++) - { - foreach ($aRestrictSearches as $oRestrictSearch) - { - $aAllowSearches[$i] = $aAllowSearches[$i]->Intersect($oRestrictSearch); - } - } - if (count($aAllowSearches) > 0) - { - $oSearch = new DBUnionSearch($aAllowSearches); - $oSearch = $oSearch->RemoveDuplicateQueries(); - } - if ($bIgnoreSilos === true) - { - $oSearch->AllowAllData(); - } - - return $oSearch; - } - - /** - * Add the scope query (view or edit depending on $sAction) for $sClass to the $oQuery. - * - * @param \DBSearch $oQuery - * @param string $sClass - * @param int $sAction - * - * @return bool true if scope exists, false if scope is null - * - * @throws \CoreException - * @throws \OQLException - */ - public function AddScopeToQuery(DBSearch &$oQuery, $sClass, $sAction = UR_ACTION_READ) - { - $oScopeQuery = $this->GetScopeFilterForProfiles(UserRights::ListProfiles(), $sClass, $sAction); - if ($oScopeQuery !== null) - { - $oQuery = $oQuery->Intersect($oScopeQuery); - // - Allowing all data if necessary - if ($oScopeQuery->IsAllDataAllowed()) - { - $oQuery->AllowAllData(); - } - - return true; - } - - return false; - } - - /** - * Returns true if at least one of the $aProfiles has the ignore_silos flag set to true for the $sClass. - * - * @param array $aProfiles - * @param string $sClass - * - * @return boolean - * - * @throws \Exception - */ - public function IsAllDataAllowedForScope($aProfiles, $sClass) - { - $bIgnoreSilos = false; - - // Iterating on profiles to retrieving the different OQLs parts - foreach ($aProfiles as $sProfile) - { - // Retrieving matrix informtions - $iProfileId = $this->GetProfileIdFromProfileName($sProfile); - - // Retrieving profile OQLs - $sScopeValuesClass = $this->sGeneratedClass; - $aProfileMatrix = $sScopeValuesClass::GetProfileScope($iProfileId, $sClass, static::ENUM_MODE_READ); - if ($aProfileMatrix !== null) - { - // If a profile should ignore allowed org, we set it for all its queries no matter the profile - if (isset($aProfileMatrix['ignore_silos']) && $aProfileMatrix['ignore_silos'] === true) - { - $bIgnoreSilos = true; - } - } - } - - return $bIgnoreSilos; - } - - /** - * Returns the profile id from a string being either a constant or its name. - * - * @param string $sProfile - * - * @return integer - * - * @throws \Exception - */ - protected function GetProfileIdFromProfileName($sProfile) - { - $iProfileId = null; - - // We try to find the profile from its name in order to retrieve it's id - // - If the regular UserRights addon is installed we check the profiles array - if (class_exists('ProfilesConfig')) - { - if (defined($sProfile) && in_array($sProfile, ProfilesConfig::GetProfilesValues())) - { - $iProfileId = constant($sProfile); - } - else - { - foreach (ProfilesConfig::GetProfilesValues() as $iKey => $aValue) - { - if ($aValue['name'] === $sProfile) - { - $iProfileId = $iKey; - break; - } - } - } - } - // - Else, we can't find the id from the name as we don't know the used UserRights addon. It has to be a constant - else - { - throw new Exception('Scope validator : Unknown UserRights addon, scope\'s profile must be a constant'); - } - - // If profile was not found from its name or from a constant, we throw an exception - if ($iProfileId === null) - { - throw new Exception('Scope validator : Could not find "' . $sProfile . '" in the profiles list'); - } - - return $iProfileId; - } - - /** - * Returns a string containing the generated PHP class for the compiled scopes - * - * @param array $aProfiles - * @return string - */ - protected function BuildPHPClass($aProfiles = array()) - { - $sProfiles = var_export($aProfiles, true); - $sClassName = $this->sGeneratedClass; - $sPHP = << - -namespace Combodo\iTop\Portal\Router; - - -/** - * Class DefaultRouter - * - * @package Combodo\iTop\Portal\Router - * @author Guillaume Lajarige - * @since 2.3.0 - */ -class DefaultRouter extends AbstractRouter -{ - static $aRoutes = array( - array('pattern' => '/', - 'callback' => 'Combodo\\iTop\\Portal\\Controller\\DefaultController::homeAction', - 'bind' => 'p_home'), -// // Example route -// array('pattern' => '/url-pattern', -// 'hash' => 'string-to-be-append-to-the-pattern-after-a-#', -// 'navigation_menu_attr' => array('id' => 'link_id', 'rel' => 'foo'), -// 'callback' => 'Combodo\\iTop\\Portal\\Controller\\DefaultController::exampleAction', -// 'bind' => 'p_example') - ); - -} diff --git a/datamodels/2.x/itop-portal-base/portal-silex/src/routers/userprofilebrickrouter.class.inc.php b/datamodels/2.x/itop-portal-base/portal-silex/src/routers/userprofilebrickrouter.class.inc.php deleted file mode 100644 index b809915fc..000000000 --- a/datamodels/2.x/itop-portal-base/portal-silex/src/routers/userprofilebrickrouter.class.inc.php +++ /dev/null @@ -1,41 +0,0 @@ - - -namespace Combodo\iTop\Portal\Router; - -/** - * Class UserProfileRouter - * - * @package Combodo\iTop\Portal\Router - * @author Guillaume Lajarige - * @since 2.3.0 - */ -class UserProfileRouter extends AbstractRouter -{ - static $aRoutes = array( - array('pattern' => '/user/{sBrickId}', - 'callback' => 'Combodo\\iTop\\Portal\\Controller\\UserProfileBrickController::DisplayAction', - 'bind' => 'p_user_profile_brick', - 'values' => array( - 'sBrickId' => null - ) - ) - ); - -} diff --git a/datamodels/2.x/itop-portal-base/portal/.env b/datamodels/2.x/itop-portal-base/portal/.env new file mode 100644 index 000000000..22257ab47 --- /dev/null +++ b/datamodels/2.x/itop-portal-base/portal/.env @@ -0,0 +1,21 @@ +# In all environments, the following files are loaded if they exist, +# the later taking precedence over the former: +# +# * .env contains default values for the environment variables needed by the app +# * .env.local uncommitted file with local overrides +# * .env.$APP_ENV committed environment-specific defaults +# * .env.$APP_ENV.local uncommitted environment-specific overrides +# +# Real environment variables win over .env files. +# +# DO NOT DEFINE PRODUCTION SECRETS IN THIS FILE NOR IN ANY OTHER COMMITTED FILES. +# +# Run "composer dump-env prod" to compile .env files for production use (requires symfony/flex >=1.2). +# https://symfony.com/doc/current/best_practices/configuration.html#infrastructure-related-configuration + +###> symfony/framework-bundle ### +APP_ENV=dev +APP_SECRET=40ef8b29be00df19cec62edf08f73808 +#TRUSTED_PROXIES=127.0.0.1,127.0.0.2 +#TRUSTED_HOSTS='^localhost|example\.com$' +###< symfony/framework-bundle ### diff --git a/datamodels/2.x/itop-portal-base/portal/.gitignore b/datamodels/2.x/itop-portal-base/portal/.gitignore new file mode 100644 index 000000000..136901bf9 --- /dev/null +++ b/datamodels/2.x/itop-portal-base/portal/.gitignore @@ -0,0 +1,9 @@ + +###> symfony/framework-bundle ### +/.env.local +/.env.local.php +/.env.*.local +/public/bundles/ +/var/ +/vendor/ +###< symfony/framework-bundle ### diff --git a/datamodels/2.x/itop-portal-base/portal/bin/console b/datamodels/2.x/itop-portal-base/portal/bin/console new file mode 100644 index 000000000..19c2f6c3c --- /dev/null +++ b/datamodels/2.x/itop-portal-base/portal/bin/console @@ -0,0 +1,42 @@ +#!/usr/bin/env php +getParameterOption(['--env', '-e'], null, true)) { + putenv('APP_ENV='.$_SERVER['APP_ENV'] = $_ENV['APP_ENV'] = $env); +} + +if ($input->hasParameterOption('--no-debug', true)) { + putenv('APP_DEBUG='.$_SERVER['APP_DEBUG'] = $_ENV['APP_DEBUG'] = '0'); +} + +require dirname(__DIR__).'/config/bootstrap.php'; + +if ($_SERVER['APP_DEBUG']) { + umask(0000); + + if (class_exists(Debug::class)) { + Debug::enable(); + } +} + +$kernel = new Kernel($_SERVER['APP_ENV'], (bool) $_SERVER['APP_DEBUG']); +$application = new Application($kernel); +$application->run($input); diff --git a/datamodels/2.x/itop-portal-base/portal/composer.json b/datamodels/2.x/itop-portal-base/portal/composer.json new file mode 100644 index 000000000..cee09ae57 --- /dev/null +++ b/datamodels/2.x/itop-portal-base/portal/composer.json @@ -0,0 +1,60 @@ +{ + "type": "project", + "license": "proprietary", + "require": { + "php": ">=5.6.0", + "ext-ctype": "*", + "ext-iconv": "*", + "symfony/console": "3.4.*", + "symfony/dotenv": "3.4.*", + "symfony/flex": "^1.1", + "symfony/framework-bundle": "3.4.*", + "symfony/twig-bundle": "3.4.*", + "symfony/yaml": "3.4.*" + }, + "config": { + "vendor-dir": "../../../../lib/composer-vendor", + "preferred-install": { + "*": "dist" + }, + "sort-packages": true + }, + "autoload": { + "psr-4": { + "Combodo\\iTop\\Portal\\": "src/" + } + }, + "autoload-dev": { + "psr-4": { + "Combodo\\iTop\\Portal\\Tests\\": "tests/" + } + }, + "replace": { + "paragonie/random_compat": "2.*", + "symfony/polyfill-ctype": "*", + "symfony/polyfill-iconv": "*", + "symfony/polyfill-php70": "*", + "symfony/polyfill-php56": "*" + }, + "scripts": { + "auto-scripts": { + "cache:clear": "symfony-cmd", + "assets:install %PUBLIC_DIR%": "symfony-cmd" + }, + "post-install-cmd": [ + "@auto-scripts" + ], + "post-update-cmd": [ + "@auto-scripts" + ] + }, + "conflict": { + "symfony/symfony": "*" + }, + "extra": { + "symfony": { + "allow-contrib": false, + "require": "3.4.*" + } + } +} diff --git a/datamodels/2.x/itop-portal-base/portal/composer.lock b/datamodels/2.x/itop-portal-base/portal/composer.lock new file mode 100644 index 000000000..8240d8f47 --- /dev/null +++ b/datamodels/2.x/itop-portal-base/portal/composer.lock @@ -0,0 +1,1607 @@ +{ + "_readme": [ + "This file locks the dependencies of your project to a known state", + "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", + "This file is @generated automatically" + ], + "content-hash": "a38a6d242ba58ae1d9a31fdd35cf960c", + "packages": [ + { + "name": "psr/cache", + "version": "1.0.1", + "source": { + "type": "git", + "url": "https://github.com/php-fig/cache.git", + "reference": "d11b50ad223250cf17b86e38383413f5a6764bf8" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/cache/zipball/d11b50ad223250cf17b86e38383413f5a6764bf8", + "reference": "d11b50ad223250cf17b86e38383413f5a6764bf8", + "shasum": "" + }, + "require": { + "php": ">=5.3.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Cache\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "http://www.php-fig.org/" + } + ], + "description": "Common interface for caching libraries", + "keywords": [ + "cache", + "psr", + "psr-6" + ], + "time": "2016-08-06T20:24:11+00:00" + }, + { + "name": "psr/container", + "version": "1.0.0", + "source": { + "type": "git", + "url": "https://github.com/php-fig/container.git", + "reference": "b7ce3b176482dbbc1245ebf52b181af44c2cf55f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/container/zipball/b7ce3b176482dbbc1245ebf52b181af44c2cf55f", + "reference": "b7ce3b176482dbbc1245ebf52b181af44c2cf55f", + "shasum": "" + }, + "require": { + "php": ">=5.3.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Container\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "http://www.php-fig.org/" + } + ], + "description": "Common Container Interface (PHP FIG PSR-11)", + "homepage": "https://github.com/php-fig/container", + "keywords": [ + "PSR-11", + "container", + "container-interface", + "container-interop", + "psr" + ], + "time": "2017-02-14T16:28:37+00:00" + }, + { + "name": "psr/log", + "version": "1.1.0", + "source": { + "type": "git", + "url": "https://github.com/php-fig/log.git", + "reference": "6c001f1daafa3a3ac1d8ff69ee4db8e799a654dd" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/log/zipball/6c001f1daafa3a3ac1d8ff69ee4db8e799a654dd", + "reference": "6c001f1daafa3a3ac1d8ff69ee4db8e799a654dd", + "shasum": "" + }, + "require": { + "php": ">=5.3.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Log\\": "Psr/Log/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "http://www.php-fig.org/" + } + ], + "description": "Common interface for logging libraries", + "homepage": "https://github.com/php-fig/log", + "keywords": [ + "log", + "psr", + "psr-3" + ], + "time": "2018-11-20T15:27:04+00:00" + }, + { + "name": "psr/simple-cache", + "version": "1.0.1", + "source": { + "type": "git", + "url": "https://github.com/php-fig/simple-cache.git", + "reference": "408d5eafb83c57f6365a3ca330ff23aa4a5fa39b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/simple-cache/zipball/408d5eafb83c57f6365a3ca330ff23aa4a5fa39b", + "reference": "408d5eafb83c57f6365a3ca330ff23aa4a5fa39b", + "shasum": "" + }, + "require": { + "php": ">=5.3.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\SimpleCache\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "http://www.php-fig.org/" + } + ], + "description": "Common interfaces for simple caching", + "keywords": [ + "cache", + "caching", + "psr", + "psr-16", + "simple-cache" + ], + "time": "2017-10-23T01:57:42+00:00" + }, + { + "name": "symfony/cache", + "version": "v3.4.27", + "source": { + "type": "git", + "url": "https://github.com/symfony/cache.git", + "reference": "380b8395b43f60e7d26a32f84f80c0a7ba93e7c5" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/cache/zipball/380b8395b43f60e7d26a32f84f80c0a7ba93e7c5", + "reference": "380b8395b43f60e7d26a32f84f80c0a7ba93e7c5", + "shasum": "" + }, + "require": { + "php": "^5.5.9|>=7.0.8", + "psr/cache": "~1.0", + "psr/log": "~1.0", + "psr/simple-cache": "^1.0", + "symfony/polyfill-apcu": "~1.1" + }, + "conflict": { + "symfony/var-dumper": "<3.3" + }, + "provide": { + "psr/cache-implementation": "1.0", + "psr/simple-cache-implementation": "1.0" + }, + "require-dev": { + "cache/integration-tests": "dev-master", + "doctrine/cache": "~1.6", + "doctrine/dbal": "~2.4", + "predis/predis": "~1.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.4-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Component\\Cache\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony Cache component with PSR-6, PSR-16, and tags", + "homepage": "https://symfony.com", + "keywords": [ + "caching", + "psr6" + ], + "time": "2019-04-16T09:03:16+00:00" + }, + { + "name": "symfony/class-loader", + "version": "v3.4.27", + "source": { + "type": "git", + "url": "https://github.com/symfony/class-loader.git", + "reference": "4459eef5298dedfb69f771186a580062b8516497" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/class-loader/zipball/4459eef5298dedfb69f771186a580062b8516497", + "reference": "4459eef5298dedfb69f771186a580062b8516497", + "shasum": "" + }, + "require": { + "php": "^5.5.9|>=7.0.8" + }, + "require-dev": { + "symfony/finder": "~2.8|~3.0|~4.0", + "symfony/polyfill-apcu": "~1.1" + }, + "suggest": { + "symfony/polyfill-apcu": "For using ApcClassLoader on HHVM" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.4-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Component\\ClassLoader\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony ClassLoader Component", + "homepage": "https://symfony.com", + "time": "2019-01-16T09:39:14+00:00" + }, + { + "name": "symfony/config", + "version": "v3.4.27", + "source": { + "type": "git", + "url": "https://github.com/symfony/config.git", + "reference": "177a276c01575253c95cefe0866e3d1b57637fe0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/config/zipball/177a276c01575253c95cefe0866e3d1b57637fe0", + "reference": "177a276c01575253c95cefe0866e3d1b57637fe0", + "shasum": "" + }, + "require": { + "php": "^5.5.9|>=7.0.8", + "symfony/filesystem": "~2.8|~3.0|~4.0", + "symfony/polyfill-ctype": "~1.8" + }, + "conflict": { + "symfony/dependency-injection": "<3.3", + "symfony/finder": "<3.3" + }, + "require-dev": { + "symfony/dependency-injection": "~3.3|~4.0", + "symfony/event-dispatcher": "~3.3|~4.0", + "symfony/finder": "~3.3|~4.0", + "symfony/yaml": "~3.0|~4.0" + }, + "suggest": { + "symfony/yaml": "To use the yaml reference dumper" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.4-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Component\\Config\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony Config Component", + "homepage": "https://symfony.com", + "time": "2019-02-23T15:06:07+00:00" + }, + { + "name": "symfony/console", + "version": "v3.4.27", + "source": { + "type": "git", + "url": "https://github.com/symfony/console.git", + "reference": "15a9104356436cb26e08adab97706654799d31d8" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/console/zipball/15a9104356436cb26e08adab97706654799d31d8", + "reference": "15a9104356436cb26e08adab97706654799d31d8", + "shasum": "" + }, + "require": { + "php": "^5.5.9|>=7.0.8", + "symfony/debug": "~2.8|~3.0|~4.0", + "symfony/polyfill-mbstring": "~1.0" + }, + "conflict": { + "symfony/dependency-injection": "<3.4", + "symfony/process": "<3.3" + }, + "provide": { + "psr/log-implementation": "1.0" + }, + "require-dev": { + "psr/log": "~1.0", + "symfony/config": "~3.3|~4.0", + "symfony/dependency-injection": "~3.4|~4.0", + "symfony/event-dispatcher": "~2.8|~3.0|~4.0", + "symfony/lock": "~3.4|~4.0", + "symfony/process": "~3.3|~4.0" + }, + "suggest": { + "psr/log": "For using the console logger", + "symfony/event-dispatcher": "", + "symfony/lock": "", + "symfony/process": "" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.4-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Component\\Console\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony Console Component", + "homepage": "https://symfony.com", + "time": "2019-04-08T09:29:13+00:00" + }, + { + "name": "symfony/debug", + "version": "v3.4.27", + "source": { + "type": "git", + "url": "https://github.com/symfony/debug.git", + "reference": "681afbb26488903c5ac15e63734f1d8ac430c9b9" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/debug/zipball/681afbb26488903c5ac15e63734f1d8ac430c9b9", + "reference": "681afbb26488903c5ac15e63734f1d8ac430c9b9", + "shasum": "" + }, + "require": { + "php": "^5.5.9|>=7.0.8", + "psr/log": "~1.0" + }, + "conflict": { + "symfony/http-kernel": ">=2.3,<2.3.24|~2.4.0|>=2.5,<2.5.9|>=2.6,<2.6.2" + }, + "require-dev": { + "symfony/http-kernel": "~2.8|~3.0|~4.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.4-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Component\\Debug\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony Debug Component", + "homepage": "https://symfony.com", + "time": "2019-04-11T09:48:14+00:00" + }, + { + "name": "symfony/dependency-injection", + "version": "v3.4.27", + "source": { + "type": "git", + "url": "https://github.com/symfony/dependency-injection.git", + "reference": "be0feb3fa202aedfd8d1956f2dafd563fb13acbf" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/dependency-injection/zipball/be0feb3fa202aedfd8d1956f2dafd563fb13acbf", + "reference": "be0feb3fa202aedfd8d1956f2dafd563fb13acbf", + "shasum": "" + }, + "require": { + "php": "^5.5.9|>=7.0.8", + "psr/container": "^1.0" + }, + "conflict": { + "symfony/config": "<3.3.7", + "symfony/finder": "<3.3", + "symfony/proxy-manager-bridge": "<3.4", + "symfony/yaml": "<3.4" + }, + "provide": { + "psr/container-implementation": "1.0" + }, + "require-dev": { + "symfony/config": "~3.3|~4.0", + "symfony/expression-language": "~2.8|~3.0|~4.0", + "symfony/yaml": "~3.4|~4.0" + }, + "suggest": { + "symfony/config": "", + "symfony/expression-language": "For using expressions in service container configuration", + "symfony/finder": "For using double-star glob patterns or when GLOB_BRACE portability is required", + "symfony/proxy-manager-bridge": "Generate service proxies to lazy load them", + "symfony/yaml": "" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.4-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Component\\DependencyInjection\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony DependencyInjection Component", + "homepage": "https://symfony.com", + "time": "2019-04-20T15:32:49+00:00" + }, + { + "name": "symfony/dotenv", + "version": "v3.4.27", + "source": { + "type": "git", + "url": "https://github.com/symfony/dotenv.git", + "reference": "7b33c7b6f497898a173e4b9d6a7698cd789d54ce" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/dotenv/zipball/7b33c7b6f497898a173e4b9d6a7698cd789d54ce", + "reference": "7b33c7b6f497898a173e4b9d6a7698cd789d54ce", + "shasum": "" + }, + "require": { + "php": "^5.5.9|>=7.0.8" + }, + "require-dev": { + "symfony/process": "~3.2|~4.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.4-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Component\\Dotenv\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Registers environment variables from a .env file", + "homepage": "https://symfony.com", + "keywords": [ + "dotenv", + "env", + "environment" + ], + "time": "2019-04-01T07:08:40+00:00" + }, + { + "name": "symfony/event-dispatcher", + "version": "v3.4.27", + "source": { + "type": "git", + "url": "https://github.com/symfony/event-dispatcher.git", + "reference": "a088aafcefb4eef2520a290ed82e4374092a6dff" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/a088aafcefb4eef2520a290ed82e4374092a6dff", + "reference": "a088aafcefb4eef2520a290ed82e4374092a6dff", + "shasum": "" + }, + "require": { + "php": "^5.5.9|>=7.0.8" + }, + "conflict": { + "symfony/dependency-injection": "<3.3" + }, + "require-dev": { + "psr/log": "~1.0", + "symfony/config": "~2.8|~3.0|~4.0", + "symfony/dependency-injection": "~3.3|~4.0", + "symfony/expression-language": "~2.8|~3.0|~4.0", + "symfony/stopwatch": "~2.8|~3.0|~4.0" + }, + "suggest": { + "symfony/dependency-injection": "", + "symfony/http-kernel": "" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.4-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Component\\EventDispatcher\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony EventDispatcher Component", + "homepage": "https://symfony.com", + "time": "2019-04-02T08:51:52+00:00" + }, + { + "name": "symfony/filesystem", + "version": "v3.4.27", + "source": { + "type": "git", + "url": "https://github.com/symfony/filesystem.git", + "reference": "acf99758b1df8e9295e6b85aa69f294565c9fedb" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/filesystem/zipball/acf99758b1df8e9295e6b85aa69f294565c9fedb", + "reference": "acf99758b1df8e9295e6b85aa69f294565c9fedb", + "shasum": "" + }, + "require": { + "php": "^5.5.9|>=7.0.8", + "symfony/polyfill-ctype": "~1.8" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.4-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Component\\Filesystem\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony Filesystem Component", + "homepage": "https://symfony.com", + "time": "2019-02-04T21:34:32+00:00" + }, + { + "name": "symfony/finder", + "version": "v3.4.27", + "source": { + "type": "git", + "url": "https://github.com/symfony/finder.git", + "reference": "61af5ce0b34b942d414fe8f1b11950d0e9a90e98" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/finder/zipball/61af5ce0b34b942d414fe8f1b11950d0e9a90e98", + "reference": "61af5ce0b34b942d414fe8f1b11950d0e9a90e98", + "shasum": "" + }, + "require": { + "php": "^5.5.9|>=7.0.8" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.4-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Component\\Finder\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony Finder Component", + "homepage": "https://symfony.com", + "time": "2019-04-02T19:54:57+00:00" + }, + { + "name": "symfony/flex", + "version": "v1.2.5", + "source": { + "type": "git", + "url": "https://github.com/symfony/flex.git", + "reference": "27909122a3da4676c3dc5dc34c8f82323c610d69" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/flex/zipball/27909122a3da4676c3dc5dc34c8f82323c610d69", + "reference": "27909122a3da4676c3dc5dc34c8f82323c610d69", + "shasum": "" + }, + "require": { + "composer-plugin-api": "^1.0", + "php": "^7.0" + }, + "require-dev": { + "composer/composer": "^1.0.2", + "symfony/dotenv": "^3.4|^4.0", + "symfony/phpunit-bridge": "^3.4.19|^4.1.8", + "symfony/process": "^2.7|^3.0|^4.0" + }, + "type": "composer-plugin", + "extra": { + "branch-alias": { + "dev-master": "1.2-dev" + }, + "class": "Symfony\\Flex\\Flex" + }, + "autoload": { + "psr-4": { + "Symfony\\Flex\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien.potencier@gmail.com" + } + ], + "description": "Composer plugin for Symfony", + "time": "2019-05-07T08:10:46+00:00" + }, + { + "name": "symfony/framework-bundle", + "version": "v3.4.27", + "source": { + "type": "git", + "url": "https://github.com/symfony/framework-bundle.git", + "reference": "bbe9bb6028542fee549ff577617af33c2ad85a85" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/framework-bundle/zipball/bbe9bb6028542fee549ff577617af33c2ad85a85", + "reference": "bbe9bb6028542fee549ff577617af33c2ad85a85", + "shasum": "" + }, + "require": { + "ext-xml": "*", + "php": "^5.5.9|>=7.0.8", + "symfony/cache": "~3.4|~4.0", + "symfony/class-loader": "~3.2", + "symfony/config": "~3.4|~4.0", + "symfony/dependency-injection": "^3.4.24|^4.2.5", + "symfony/event-dispatcher": "~3.4|~4.0", + "symfony/filesystem": "~2.8|~3.0|~4.0", + "symfony/finder": "~2.8|~3.0|~4.0", + "symfony/http-foundation": "^3.3.11|~4.0", + "symfony/http-kernel": "~3.4|~4.0", + "symfony/polyfill-mbstring": "~1.0", + "symfony/routing": "^3.4.5|^4.0.5" + }, + "conflict": { + "phpdocumentor/reflection-docblock": "<3.0", + "phpdocumentor/type-resolver": "<0.2.1", + "phpunit/phpunit": "<4.8.35|<5.4.3,>=5.0", + "symfony/asset": "<3.3", + "symfony/console": "<3.4", + "symfony/form": "<3.4", + "symfony/property-info": "<3.3", + "symfony/serializer": "<3.3", + "symfony/stopwatch": "<3.4", + "symfony/translation": "<3.4", + "symfony/validator": "<3.4", + "symfony/workflow": "<3.3" + }, + "require-dev": { + "doctrine/annotations": "~1.0", + "doctrine/cache": "~1.0", + "fig/link-util": "^1.0", + "phpdocumentor/reflection-docblock": "^3.0|^4.0", + "symfony/asset": "~3.3|~4.0", + "symfony/browser-kit": "~2.8|~3.0|~4.0", + "symfony/console": "~3.4|~4.0", + "symfony/css-selector": "~2.8|~3.0|~4.0", + "symfony/dom-crawler": "~2.8|~3.0|~4.0", + "symfony/expression-language": "~2.8|~3.0|~4.0", + "symfony/form": "^3.4.22|~4.1.11|^4.2.3", + "symfony/lock": "~3.4|~4.0", + "symfony/polyfill-intl-icu": "~1.0", + "symfony/process": "~2.8|~3.0|~4.0", + "symfony/property-info": "~3.3|~4.0", + "symfony/security-core": "~3.2|~4.0", + "symfony/security-csrf": "^2.8.31|^3.3.13|~4.0", + "symfony/serializer": "~3.3|~4.0", + "symfony/stopwatch": "~3.4|~4.0", + "symfony/templating": "~2.8|~3.0|~4.0", + "symfony/translation": "~3.4|~4.0", + "symfony/validator": "~3.4|~4.0", + "symfony/var-dumper": "~3.3|~4.0", + "symfony/web-link": "~3.3|~4.0", + "symfony/workflow": "~3.3|~4.0", + "symfony/yaml": "~3.2|~4.0", + "twig/twig": "~1.34|~2.4" + }, + "suggest": { + "ext-apcu": "For best performance of the system caches", + "symfony/console": "For using the console commands", + "symfony/form": "For using forms", + "symfony/property-info": "For using the property_info service", + "symfony/serializer": "For using the serializer service", + "symfony/validator": "For using validation", + "symfony/web-link": "For using web links, features such as preloading, prefetching or prerendering", + "symfony/yaml": "For using the debug:config and lint:yaml commands" + }, + "type": "symfony-bundle", + "extra": { + "branch-alias": { + "dev-master": "3.4-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Bundle\\FrameworkBundle\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony FrameworkBundle", + "homepage": "https://symfony.com", + "time": "2019-05-01T08:11:03+00:00" + }, + { + "name": "symfony/http-foundation", + "version": "v3.4.27", + "source": { + "type": "git", + "url": "https://github.com/symfony/http-foundation.git", + "reference": "fa02215233be8de1c2b44617088192f9e8db3512" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/http-foundation/zipball/fa02215233be8de1c2b44617088192f9e8db3512", + "reference": "fa02215233be8de1c2b44617088192f9e8db3512", + "shasum": "" + }, + "require": { + "php": "^5.5.9|>=7.0.8", + "symfony/polyfill-mbstring": "~1.1", + "symfony/polyfill-php70": "~1.6" + }, + "require-dev": { + "symfony/expression-language": "~2.8|~3.0|~4.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.4-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Component\\HttpFoundation\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony HttpFoundation Component", + "homepage": "https://symfony.com", + "time": "2019-05-01T08:04:33+00:00" + }, + { + "name": "symfony/http-kernel", + "version": "v3.4.27", + "source": { + "type": "git", + "url": "https://github.com/symfony/http-kernel.git", + "reference": "586046f5adc6a08eaebbe4519ef18ad52f54e453" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/http-kernel/zipball/586046f5adc6a08eaebbe4519ef18ad52f54e453", + "reference": "586046f5adc6a08eaebbe4519ef18ad52f54e453", + "shasum": "" + }, + "require": { + "php": "^5.5.9|>=7.0.8", + "psr/log": "~1.0", + "symfony/debug": "^3.3.3|~4.0", + "symfony/event-dispatcher": "~2.8|~3.0|~4.0", + "symfony/http-foundation": "~3.4.12|~4.0.12|^4.1.1", + "symfony/polyfill-ctype": "~1.8" + }, + "conflict": { + "symfony/config": "<2.8", + "symfony/dependency-injection": "<3.4.10|<4.0.10,>=4", + "symfony/var-dumper": "<3.3", + "twig/twig": "<1.34|<2.4,>=2" + }, + "provide": { + "psr/log-implementation": "1.0" + }, + "require-dev": { + "psr/cache": "~1.0", + "symfony/browser-kit": "~2.8|~3.0|~4.0", + "symfony/class-loader": "~2.8|~3.0", + "symfony/config": "~2.8|~3.0|~4.0", + "symfony/console": "~2.8|~3.0|~4.0", + "symfony/css-selector": "~2.8|~3.0|~4.0", + "symfony/dependency-injection": "^3.4.10|^4.0.10", + "symfony/dom-crawler": "~2.8|~3.0|~4.0", + "symfony/expression-language": "~2.8|~3.0|~4.0", + "symfony/finder": "~2.8|~3.0|~4.0", + "symfony/process": "~2.8|~3.0|~4.0", + "symfony/routing": "~3.4|~4.0", + "symfony/stopwatch": "~2.8|~3.0|~4.0", + "symfony/templating": "~2.8|~3.0|~4.0", + "symfony/translation": "~2.8|~3.0|~4.0", + "symfony/var-dumper": "~3.3|~4.0" + }, + "suggest": { + "symfony/browser-kit": "", + "symfony/config": "", + "symfony/console": "", + "symfony/dependency-injection": "", + "symfony/finder": "", + "symfony/var-dumper": "" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.4-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Component\\HttpKernel\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony HttpKernel Component", + "homepage": "https://symfony.com", + "time": "2019-05-01T13:03:24+00:00" + }, + { + "name": "symfony/polyfill-apcu", + "version": "v1.11.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-apcu.git", + "reference": "a502face1da6a53289480166f24de2c3c68e5c3c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-apcu/zipball/a502face1da6a53289480166f24de2c3c68e5c3c", + "reference": "a502face1da6a53289480166f24de2c3c68e5c3c", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.11-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Polyfill\\Apcu\\": "" + }, + "files": [ + "bootstrap.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill backporting apcu_* functions to lower PHP versions", + "homepage": "https://symfony.com", + "keywords": [ + "apcu", + "compatibility", + "polyfill", + "portable", + "shim" + ], + "time": "2019-02-06T07:57:58+00:00" + }, + { + "name": "symfony/polyfill-mbstring", + "version": "v1.11.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-mbstring.git", + "reference": "fe5e94c604826c35a32fa832f35bd036b6799609" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/fe5e94c604826c35a32fa832f35bd036b6799609", + "reference": "fe5e94c604826c35a32fa832f35bd036b6799609", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "suggest": { + "ext-mbstring": "For best performance" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.11-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Polyfill\\Mbstring\\": "" + }, + "files": [ + "bootstrap.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for the Mbstring extension", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "mbstring", + "polyfill", + "portable", + "shim" + ], + "time": "2019-02-06T07:57:58+00:00" + }, + { + "name": "symfony/routing", + "version": "v3.4.27", + "source": { + "type": "git", + "url": "https://github.com/symfony/routing.git", + "reference": "ff11aac46d6cb8a65f2855687bb9a1ac9d860eec" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/routing/zipball/ff11aac46d6cb8a65f2855687bb9a1ac9d860eec", + "reference": "ff11aac46d6cb8a65f2855687bb9a1ac9d860eec", + "shasum": "" + }, + "require": { + "php": "^5.5.9|>=7.0.8" + }, + "conflict": { + "symfony/config": "<3.3.1", + "symfony/dependency-injection": "<3.3", + "symfony/yaml": "<3.4" + }, + "require-dev": { + "doctrine/annotations": "~1.0", + "psr/log": "~1.0", + "symfony/config": "^3.3.1|~4.0", + "symfony/dependency-injection": "~3.3|~4.0", + "symfony/expression-language": "~2.8|~3.0|~4.0", + "symfony/http-foundation": "~2.8|~3.0|~4.0", + "symfony/yaml": "~3.4|~4.0" + }, + "suggest": { + "doctrine/annotations": "For using the annotation loader", + "symfony/config": "For using the all-in-one router or any loader", + "symfony/expression-language": "For using expression matching", + "symfony/http-foundation": "For using a Symfony Request object", + "symfony/yaml": "For using the YAML loader" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.4-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Component\\Routing\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony Routing Component", + "homepage": "https://symfony.com", + "keywords": [ + "router", + "routing", + "uri", + "url" + ], + "time": "2019-03-29T21:58:42+00:00" + }, + { + "name": "symfony/twig-bridge", + "version": "v3.4.28", + "source": { + "type": "git", + "url": "https://github.com/symfony/twig-bridge.git", + "reference": "9fc026c05e927fe59cf89f67b9f9926e44301eea" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/twig-bridge/zipball/9fc026c05e927fe59cf89f67b9f9926e44301eea", + "reference": "9fc026c05e927fe59cf89f67b9f9926e44301eea", + "shasum": "" + }, + "require": { + "php": "^5.5.9|>=7.0.8", + "twig/twig": "^1.40|^2.9" + }, + "conflict": { + "symfony/console": "<3.4", + "symfony/form": "<3.4.13|>=4.0,<4.0.13|>=4.1,<4.1.2" + }, + "require-dev": { + "symfony/asset": "~2.8|~3.0|~4.0", + "symfony/console": "~3.4|~4.0", + "symfony/dependency-injection": "~2.8|~3.0|~4.0", + "symfony/expression-language": "~2.8|~3.0|~4.0", + "symfony/finder": "~2.8|~3.0|~4.0", + "symfony/form": "^3.4.23|^4.2.4", + "symfony/http-foundation": "^3.3.11|~4.0", + "symfony/http-kernel": "~3.2|~4.0", + "symfony/polyfill-intl-icu": "~1.0", + "symfony/routing": "~2.8|~3.0|~4.0", + "symfony/security": "^2.8.31|^3.3.13|~4.0", + "symfony/security-acl": "~2.8|~3.0", + "symfony/stopwatch": "~2.8|~3.0|~4.0", + "symfony/templating": "~2.8|~3.0|~4.0", + "symfony/translation": "~2.8|~3.0|~4.0", + "symfony/var-dumper": "~2.8.10|~3.1.4|~3.2|~4.0", + "symfony/web-link": "~3.3|~4.0", + "symfony/workflow": "~3.3|~4.0", + "symfony/yaml": "~2.8|~3.0|~4.0" + }, + "suggest": { + "symfony/asset": "For using the AssetExtension", + "symfony/expression-language": "For using the ExpressionExtension", + "symfony/finder": "", + "symfony/form": "For using the FormExtension", + "symfony/http-kernel": "For using the HttpKernelExtension", + "symfony/routing": "For using the RoutingExtension", + "symfony/security": "For using the SecurityExtension", + "symfony/stopwatch": "For using the StopwatchExtension", + "symfony/templating": "For using the TwigEngine", + "symfony/translation": "For using the TranslationExtension", + "symfony/var-dumper": "For using the DumpExtension", + "symfony/web-link": "For using the WebLinkExtension", + "symfony/yaml": "For using the YamlExtension" + }, + "type": "symfony-bridge", + "extra": { + "branch-alias": { + "dev-master": "3.4-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Bridge\\Twig\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony Twig Bridge", + "homepage": "https://symfony.com", + "time": "2019-04-30T12:26:26+00:00" + }, + { + "name": "symfony/twig-bundle", + "version": "v3.4.28", + "source": { + "type": "git", + "url": "https://github.com/symfony/twig-bundle.git", + "reference": "9f3f12943d37153a671bbab71d58812825f41e4c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/twig-bundle/zipball/9f3f12943d37153a671bbab71d58812825f41e4c", + "reference": "9f3f12943d37153a671bbab71d58812825f41e4c", + "shasum": "" + }, + "require": { + "php": "^5.5.9|>=7.0.8", + "symfony/config": "~3.2|~4.0", + "symfony/http-foundation": "~2.8|~3.0|~4.0", + "symfony/http-kernel": "^3.3|~4.0", + "symfony/polyfill-ctype": "~1.8", + "symfony/twig-bridge": "^3.4.3|^4.0.3", + "twig/twig": "~1.40|~2.9" + }, + "conflict": { + "symfony/dependency-injection": "<3.4", + "symfony/event-dispatcher": "<3.3.1" + }, + "require-dev": { + "doctrine/annotations": "~1.0", + "doctrine/cache": "~1.0", + "symfony/asset": "~2.8|~3.0|~4.0", + "symfony/dependency-injection": "~3.4.24|^4.2.5", + "symfony/expression-language": "~2.8|~3.0|~4.0", + "symfony/finder": "~2.8|~3.0|~4.0", + "symfony/form": "~2.8|~3.0|~4.0", + "symfony/framework-bundle": "^3.3.11|~4.0", + "symfony/routing": "~2.8|~3.0|~4.0", + "symfony/stopwatch": "~2.8|~3.0|~4.0", + "symfony/templating": "~2.8|~3.0|~4.0", + "symfony/web-link": "~3.3|~4.0", + "symfony/yaml": "~2.8|~3.0|~4.0" + }, + "type": "symfony-bundle", + "extra": { + "branch-alias": { + "dev-master": "3.4-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Bundle\\TwigBundle\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony TwigBundle", + "homepage": "https://symfony.com", + "time": "2019-04-27T18:55:44+00:00" + }, + { + "name": "symfony/yaml", + "version": "v3.4.27", + "source": { + "type": "git", + "url": "https://github.com/symfony/yaml.git", + "reference": "212a27b731e5bfb735679d1ffaac82bd6a1dc996" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/yaml/zipball/212a27b731e5bfb735679d1ffaac82bd6a1dc996", + "reference": "212a27b731e5bfb735679d1ffaac82bd6a1dc996", + "shasum": "" + }, + "require": { + "php": "^5.5.9|>=7.0.8", + "symfony/polyfill-ctype": "~1.8" + }, + "conflict": { + "symfony/console": "<3.4" + }, + "require-dev": { + "symfony/console": "~3.4|~4.0" + }, + "suggest": { + "symfony/console": "For validating YAML files using the lint command" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.4-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Component\\Yaml\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony Yaml Component", + "homepage": "https://symfony.com", + "time": "2019-03-25T07:48:46+00:00" + }, + { + "name": "twig/twig", + "version": "v2.11.3", + "source": { + "type": "git", + "url": "https://github.com/twigphp/Twig.git", + "reference": "699ed2342557c88789a15402de5eb834dedd6792" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/twigphp/Twig/zipball/699ed2342557c88789a15402de5eb834dedd6792", + "reference": "699ed2342557c88789a15402de5eb834dedd6792", + "shasum": "" + }, + "require": { + "php": "^7.0", + "symfony/polyfill-ctype": "^1.8", + "symfony/polyfill-mbstring": "^1.3" + }, + "require-dev": { + "psr/container": "^1.0", + "symfony/debug": "^2.7", + "symfony/phpunit-bridge": "^3.4.19|^4.1.8|^5.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.11-dev" + } + }, + "autoload": { + "psr-0": { + "Twig_": "lib/" + }, + "psr-4": { + "Twig\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com", + "homepage": "http://fabien.potencier.org", + "role": "Lead Developer" + }, + { + "name": "Armin Ronacher", + "email": "armin.ronacher@active-4.com", + "role": "Project Founder" + }, + { + "name": "Twig Team", + "homepage": "https://twig.symfony.com/contributors", + "role": "Contributors" + } + ], + "description": "Twig, the flexible, fast, and secure template language for PHP", + "homepage": "https://twig.symfony.com", + "keywords": [ + "templating" + ], + "time": "2019-06-18T15:37:11+00:00" + } + ], + "packages-dev": [], + "aliases": [], + "minimum-stability": "stable", + "stability-flags": [], + "prefer-stable": false, + "prefer-lowest": false, + "platform": { + "php": ">=5.6.0", + "ext-ctype": "*", + "ext-iconv": "*" + }, + "platform-dev": [] +} diff --git a/datamodels/2.x/itop-portal-base/portal/config/bootstrap.php b/datamodels/2.x/itop-portal-base/portal/config/bootstrap.php new file mode 100644 index 000000000..b620ffa29 --- /dev/null +++ b/datamodels/2.x/itop-portal-base/portal/config/bootstrap.php @@ -0,0 +1,51 @@ +=1.2) +if (is_array($env = @include dirname(__DIR__).'/.env.local.php')) { + $_ENV += $env; +} elseif (!class_exists(Dotenv::class)) { + throw new RuntimeException('Please run "composer require symfony/dotenv" to load the ".env" files configuring the application.'); +} else { + $path = dirname(__DIR__).'/.env'; + $dotenv = new Dotenv(false); + + // load all the .env files + if (method_exists($dotenv, 'loadEnv')) { + $dotenv->loadEnv($path); + } else { + // fallback code in case your Dotenv component is not 4.2 or higher (when loadEnv() was added) + + if (file_exists($path) || !file_exists($p = "$path.dist")) { + $dotenv->load($path); + } else { + $dotenv->load($p); + } + + if (null === $env = (isset($_SERVER['APP_ENV']) ? $_SERVER['APP_ENV'] : (isset($_ENV['APP_ENV']) ? $_ENV['APP_ENV'] : null))) { + $dotenv->populate(array('APP_ENV' => $env = 'dev')); + } + + if ('test' !== $env && file_exists($p = "$path.local")) { + $dotenv->load($p); + $env = isset($_SERVER['APP_ENV']) ? $_SERVER['APP_ENV'] : (isset($_ENV['APP_ENV']) ? $_ENV['APP_ENV'] : $env); + } + + if (file_exists($p = "$path.$env")) { + $dotenv->load($p); + } + + if (file_exists($p = "$path.$env.local")) { + $dotenv->load($p); + } + } +} + +$_SERVER += $_ENV; +$_SERVER['APP_ENV'] = $_ENV['APP_ENV'] = (isset($_SERVER['APP_ENV']) ? $_SERVER['APP_ENV'] : (isset($_ENV['APP_ENV']) ? $_ENV['APP_ENV'] : null)) ?: 'dev'; +$_SERVER['APP_DEBUG'] = (isset($_SERVER['APP_DEBUG']) ? $_SERVER['APP_DEBUG'] : (isset($_ENV['APP_DEBUG']) ? $_ENV['APP_DEBUG'] : 'prod')) !== $_SERVER['APP_ENV']; +$_SERVER['APP_DEBUG'] = $_ENV['APP_DEBUG'] = (int) $_SERVER['APP_DEBUG'] || filter_var($_SERVER['APP_DEBUG'], FILTER_VALIDATE_BOOLEAN) ? '1' : '0'; diff --git a/datamodels/2.x/itop-portal-base/portal/config/bundles.php b/datamodels/2.x/itop-portal-base/portal/config/bundles.php new file mode 100644 index 000000000..bdd37acde --- /dev/null +++ b/datamodels/2.x/itop-portal-base/portal/config/bundles.php @@ -0,0 +1,6 @@ + ['all' => true], + Symfony\Bundle\TwigBundle\TwigBundle::class => ['all' => true], +]; 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 new file mode 100644 index 000000000..6ba1a194d --- /dev/null +++ b/datamodels/2.x/itop-portal-base/portal/config/legacy_silex_compat_layer.php @@ -0,0 +1,104 @@ +process($container); + +//create %combodo.portal.instance.conf% +$formsCompat = new Forms($moduleDesign); +$formsCompat->process($container); + +//append into %combodo.portal.instance.conf% +$listesCompat = new Lists($moduleDesign); +$listesCompat->process($container); + +// - Generating CSS files +$aImportPaths = array(COMBODO_PORTAL_BASE_ABSOLUTE_PATH.'css/'); +$aPortalConf = $container->getParameter('combodo.portal.instance.conf'); +foreach ($aPortalConf['properties']['themes'] as $key => $value) +{ + if (!is_array($value)) + { + $aPortalConf['properties']['themes'][$key] = COMBODO_ABSOLUTE_URL.utils::GetCSSFromSASS('env-'.utils::GetCurrentEnvironment().'/'.$value, $aImportPaths); + } + else + { + $aValues = array(); + foreach ($value as $sSubvalue) + { + $aValues[] = COMBODO_ABSOLUTE_URL.utils::GetCSSFromSASS('env-'.utils::GetCurrentEnvironment().'/'.$sSubvalue, $aImportPaths); + } + $aPortalConf['properties']['themes'][$key] = $aValues; + } +} +$container->setParameter('combodo.portal.instance.conf', $aPortalConf); + +//TODO: The following needs to be refactored +//session messages +$aAllMessages = array(); +if ((array_key_exists('obj_messages', $_SESSION)) && (!empty($_SESSION['obj_messages']))) +{ + foreach ($_SESSION['obj_messages'] as $sMessageKey => $aMessageObjectData) + { + $aObjectMessages = array(); + $aRanks = array(); + foreach ($aMessageObjectData as $sMessageId => $aMessageData) + { + $sMsgClass = 'alert alert-'; + switch ($aMessageData['severity']) + { + case 'info': + $sMsgClass .= 'info'; + break; + case 'error': + $sMsgClass .= 'danger'; + break; + case 'ok': + default: + $sMsgClass .= 'success'; + break; + } + $aObjectMessages[] = array('cssClass' => $sMsgClass, 'message' => $aMessageData['message']); + $aRanks[] = $aMessageData['rank']; + } + unset($_SESSION['obj_messages'][$sMessageKey]); + array_multisort($aRanks, $aObjectMessages); + foreach ($aObjectMessages as $aObjectMessage) + { + $aAllMessages[] = $aObjectMessage; + } + } +} +$container->setParameter('combodo.current_user.session_messages', $aAllMessages); \ No newline at end of file diff --git a/datamodels/2.x/itop-portal-base/portal/config/packages/cache.yaml b/datamodels/2.x/itop-portal-base/portal/config/packages/cache.yaml new file mode 100644 index 000000000..93e620efa --- /dev/null +++ b/datamodels/2.x/itop-portal-base/portal/config/packages/cache.yaml @@ -0,0 +1,19 @@ +framework: + cache: + # Put the unique name of your app here: the prefix seed + # is used to compute stable namespaces for cache keys. + #prefix_seed: your_vendor_name/app_name + + # The app cache caches to the filesystem by default. + # Other options include: + + # Redis + #app: cache.adapter.redis + #default_redis_provider: redis://localhost + + # APCu (not recommended with heavy random-write workloads as memory fragmentation can cause perf issues) + #app: cache.adapter.apcu + + # Namespaced pools use the above "app" backend by default + #pools: + #my.dedicated.cache: ~ diff --git a/datamodels/2.x/itop-portal-base/portal/config/packages/dev/routing.yaml b/datamodels/2.x/itop-portal-base/portal/config/packages/dev/routing.yaml new file mode 100644 index 000000000..4116679a2 --- /dev/null +++ b/datamodels/2.x/itop-portal-base/portal/config/packages/dev/routing.yaml @@ -0,0 +1,3 @@ +framework: + router: + strict_requirements: true diff --git a/datamodels/2.x/itop-portal-base/portal/config/packages/framework.yaml b/datamodels/2.x/itop-portal-base/portal/config/packages/framework.yaml new file mode 100644 index 000000000..60f24ae25 --- /dev/null +++ b/datamodels/2.x/itop-portal-base/portal/config/packages/framework.yaml @@ -0,0 +1,15 @@ +framework: + secret: '%env(APP_SECRET)%' + #default_locale: en + #csrf_protection: true + #http_method_override: true + + # Enables session support. Note that the session will ONLY be started if you read or write from it. + # Remove or comment this section to explicitly disable session support. + session: + handler_id: ~ + + #esi: true + #fragments: true + php_errors: + log: true diff --git a/datamodels/2.x/itop-portal-base/portal/config/packages/routing.yaml b/datamodels/2.x/itop-portal-base/portal/config/packages/routing.yaml new file mode 100644 index 000000000..368bc7f49 --- /dev/null +++ b/datamodels/2.x/itop-portal-base/portal/config/packages/routing.yaml @@ -0,0 +1,3 @@ +framework: + router: + strict_requirements: ~ diff --git a/datamodels/2.x/itop-portal-base/portal/config/packages/test/framework.yaml b/datamodels/2.x/itop-portal-base/portal/config/packages/test/framework.yaml new file mode 100644 index 000000000..d051c8400 --- /dev/null +++ b/datamodels/2.x/itop-portal-base/portal/config/packages/test/framework.yaml @@ -0,0 +1,4 @@ +framework: + test: true + session: + storage_id: session.storage.mock_file diff --git a/datamodels/2.x/itop-portal-base/portal/config/packages/test/routing.yaml b/datamodels/2.x/itop-portal-base/portal/config/packages/test/routing.yaml new file mode 100644 index 000000000..4116679a2 --- /dev/null +++ b/datamodels/2.x/itop-portal-base/portal/config/packages/test/routing.yaml @@ -0,0 +1,3 @@ +framework: + router: + strict_requirements: true diff --git a/datamodels/2.x/itop-portal-base/portal/config/packages/twig.yaml b/datamodels/2.x/itop-portal-base/portal/config/packages/twig.yaml new file mode 100644 index 000000000..c5d5a94c3 --- /dev/null +++ b/datamodels/2.x/itop-portal-base/portal/config/packages/twig.yaml @@ -0,0 +1,4 @@ +twig: + default_path: '%combodo.modules.absolute_path%' + debug: '%kernel.debug%' + strict_variables: '%kernel.debug%' diff --git a/datamodels/2.x/itop-portal-base/portal/config/routes.yaml b/datamodels/2.x/itop-portal-base/portal/config/routes.yaml new file mode 100644 index 000000000..08e96dbc5 --- /dev/null +++ b/datamodels/2.x/itop-portal-base/portal/config/routes.yaml @@ -0,0 +1,40 @@ +# Copyright (C) 2010-2018 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 +# along with iTop. If not, see + +# This file is the entry point to configure your own HTTP routes. +# Files in the routes/ subdirectory configure the routes for your dependencies. + +defaults: + resource: 'routes/default.yaml' + +#browse_brick: +# resource: 'routes/browse_brick.yaml' +# +#create_brick: +# resource: 'routes/create_brick.yaml' +# +#manage_brick: +# resource: 'routes/manage_brick.yaml' + +user_profile_brick: + resource: 'routes/user_profile_brick.yaml' +# +#object_router: +# resource: 'routes/object_router.php' + +extensions_extra_routes: + resource: 'routes/extensions_extra_routes.php' \ No newline at end of file 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 new file mode 100644 index 000000000..7b9463d2f --- /dev/null +++ b/datamodels/2.x/itop-portal-base/portal/config/routes/default.yaml @@ -0,0 +1,27 @@ +# Copyright (C) 2010-2018 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 +# along with iTop. If not, see + +p_home: + path: '/' + controller: 'Combodo\iTop\Portal\Controller\DefaultController::homeAction' + +# Example route +#p_example: +# path: /url-pattern +# 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/dev/twig.yaml b/datamodels/2.x/itop-portal-base/portal/config/routes/dev/twig.yaml new file mode 100644 index 000000000..f4ee83960 --- /dev/null +++ b/datamodels/2.x/itop-portal-base/portal/config/routes/dev/twig.yaml @@ -0,0 +1,3 @@ +_errors: + resource: '@TwigBundle/Resources/config/routing/errors.xml' + prefix: /_error diff --git a/datamodels/2.x/itop-portal-base/portal/config/routes/extensions_extra_routes.php b/datamodels/2.x/itop-portal-base/portal/config/routes/extensions_extra_routes.php new file mode 100644 index 000000000..d72d9e241 --- /dev/null +++ b/datamodels/2.x/itop-portal-base/portal/config/routes/extensions_extra_routes.php @@ -0,0 +1,34 @@ +add( + $route['bind'], + new Route( + $route['pattern'], + array_merge( + ['_controller' => $route['callback']], + $route['values'] + ), + $route['asserts'] + ) + ); +} + +return $routes; \ No newline at end of file diff --git a/datamodels/2.x/itop-portal-base/portal/config/routes/user_profile_brick.yaml b/datamodels/2.x/itop-portal-base/portal/config/routes/user_profile_brick.yaml new file mode 100644 index 000000000..ffe7e0630 --- /dev/null +++ b/datamodels/2.x/itop-portal-base/portal/config/routes/user_profile_brick.yaml @@ -0,0 +1,22 @@ +# Copyright (C) 2010-2018 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 +# along with iTop. If not, see + +p_user_profile_brick: + path: '/user/{sBrickId}' + defaults: + _controller: 'Combodo\iTop\Portal\Controller\UserProfileBrickController::DisplayAction' + sBrickId: ~ \ No newline at end of file diff --git a/datamodels/2.x/itop-portal-base/portal/config/services.yaml b/datamodels/2.x/itop-portal-base/portal/config/services.yaml new file mode 100644 index 000000000..0b3735e3f --- /dev/null +++ b/datamodels/2.x/itop-portal-base/portal/config/services.yaml @@ -0,0 +1,121 @@ +# Copyright (C) 2010-2018 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 +# along with iTop. If not, see + +# This file is the entry point to configure your own services. +# Files in the packages/ subdirectory configure your dependencies. + +imports: + - { resource: "legacy_silex_compat_layer.php" } + +# Put parameters here that don't need to change on each machine where the app is deployed +# https://symfony.com/doc/current/best_practices/configuration.html#application-related-configuration +parameters: + # Replace default url generator service + router.options.generator_base_class: Combodo\iTop\Portal\Routing\UrlGenerator + + # Used in templates + combodo.current_environment: !php/const COMBODO_CURRENT_ENVIRONMENT + combodo.absolute_url: !php/const COMBODO_ABSOLUTE_URL + combodo.modules.absolute_url: !php/const COMBODO_MODULES_ABSOLUTE_URL + combodo.modules.absolute_path: !php/const MODULESROOT + combodo.portal.base.absolute_url: !php/const COMBODO_PORTAL_BASE_ABSOLUTE_URL + combodo.portal.base.absolute_path: !php/const COMBODO_PORTAL_BASE_ABSOLUTE_PATH + combodo.portal.instance.absolute_url: !php/const COMBODO_PORTAL_INSTANCE_ABSOLUTE_URL + combodo.portal.instance.id: !php/const PORTAL_ID + +services: + # Default configuration for services in *this* file + _defaults: + autowire: true # Automatically injects dependencies in your services. + autoconfigure: true # Automatically registers your services as commands, event subscribers, etc. + public: false # Allows optimizing the container by removing unused services; this also means + # fetching services directly from the container via $container->get() won't work. + # The best practice is to be explicit about your dependencies anyway. + bind: + $sPortalCachePath: !php/const PORTAL_CACHE_PATH + $sPortalId: !php/const PORTAL_ID + $sCombodoPortalBaseAbsolutePath: !php/const COMBODO_PORTAL_BASE_ABSOLUTE_PATH + + # Makes classes in src/ available to be used as services + # This creates a service per class whose id is the fully-qualified class name + Combodo\iTop\Portal\: + resource: '../src/*' + exclude: '../src/{DependencyInjection,Entity,Migrations,Tests,Kernel.php}' + + # Controllers are imported separately to make sure services can be injected + # as action arguments even if you don't extend any base controller class + Combodo\iTop\Portal\Controller\: + resource: '../src/Controller' + tags: ['controller.service_arguments'] + + # Tag services without defining them (see https://symfony.com/doc/current/service_container/tags.html#autoconfiguring-tags) + _instanceof: + Combodo\iTop\Portal\EventListener\UserProvider: + tags: [{ name: 'kernel.event_listener', event: 'kernel.request' }] + Combodo\iTop\Portal\EventListener\ApplicationContextSetUrlMakerClass: + tags: [{ name: 'kernel.event_listener', event: 'kernel.request' }] + + + # Add more service definitions when explicit configuration is needed + # Please note that last definitions always *replace* previous ones + + # VariableAccessor: When a service require a parameter, you have to fully declare it: let's create intermediate (& simple) services and have them auto-wired + Combodo\iTop\Portal\VariableAccessor\CombodoPortalInstanceConf: + arguments: + - '%combodo.portal.instance.conf%' + combodo.current_contact.photo_url: + class: Combodo\iTop\Portal\VariableAccessor\CombodoCurrentContactPhotoUrl + arguments: + - '@Combodo\iTop\Portal\Security\ItopUser' + combodo.current_user: + class: Combodo\iTop\Portal\VariableAccessor\CombodoCurrentUser + arguments: + - '@Combodo\iTop\Portal\Security\ItopUser' + + # Legacy code as a service: since it is not in the auto-wiring path, it needs to be explicitly declared + ModuleDesign: + class: 'ModuleDesign' + arguments: + - '%combodo.portal.instance.id%' + + # Decoration + # - Compatibility layer with Silex\Application which was used almost everywhere in the portal's templates + Combodo\iTop\Portal\Twig\AppVariable: + decorates: twig.app_variable + arguments: + - '@Combodo\iTop\Portal\Twig\AppVariable.inner' + - '@service_container' + + # Aliases + brick_collection: + alias: Combodo\iTop\Portal\Brick\BrickCollection + public: true + request_manipulator: + alias: Combodo\iTop\Portal\Helper\RequestManipulatorHelper + public: true + scope_validator: + alias: Combodo\iTop\Portal\Helper\ScopeValidatorHelper + public: true + context_manipulator: + alias: Combodo\iTop\Portal\Helper\ContextManipulatorHelper + public: true + lifecycle_validator: + alias: Combodo\iTop\Portal\Helper\LifecycleValidatorHelper + public: true + url_generator: + alias: router + public: true \ No newline at end of file diff --git a/datamodels/2.x/itop-portal-base/portal-silex/web/css/bootstrap-theme-combodo.css b/datamodels/2.x/itop-portal-base/portal/public/css/bootstrap-theme-combodo.css similarity index 100% rename from datamodels/2.x/itop-portal-base/portal-silex/web/css/bootstrap-theme-combodo.css rename to datamodels/2.x/itop-portal-base/portal/public/css/bootstrap-theme-combodo.css diff --git a/datamodels/2.x/itop-portal-base/portal-silex/web/css/bootstrap-theme-combodo.scss b/datamodels/2.x/itop-portal-base/portal/public/css/bootstrap-theme-combodo.scss similarity index 100% rename from datamodels/2.x/itop-portal-base/portal-silex/web/css/bootstrap-theme-combodo.scss rename to datamodels/2.x/itop-portal-base/portal/public/css/bootstrap-theme-combodo.scss diff --git a/datamodels/2.x/itop-portal-base/portal-silex/web/css/portal.css b/datamodels/2.x/itop-portal-base/portal/public/css/portal.css similarity index 100% rename from datamodels/2.x/itop-portal-base/portal-silex/web/css/portal.css rename to datamodels/2.x/itop-portal-base/portal/public/css/portal.css diff --git a/datamodels/2.x/itop-portal-base/portal-silex/web/css/portal.scss b/datamodels/2.x/itop-portal-base/portal/public/css/portal.scss similarity index 100% rename from datamodels/2.x/itop-portal-base/portal-silex/web/css/portal.scss rename to datamodels/2.x/itop-portal-base/portal/public/css/portal.scss diff --git a/datamodels/2.x/itop-portal-base/portal-silex/web/css/variables.scss b/datamodels/2.x/itop-portal-base/portal/public/css/variables.scss similarity index 100% rename from datamodels/2.x/itop-portal-base/portal-silex/web/css/variables.scss rename to datamodels/2.x/itop-portal-base/portal/public/css/variables.scss diff --git a/datamodels/2.x/itop-portal-base/portal-silex/web/img/backgrounds/geometric-orange.svg b/datamodels/2.x/itop-portal-base/portal/public/img/backgrounds/geometric-orange.svg similarity index 100% rename from datamodels/2.x/itop-portal-base/portal-silex/web/img/backgrounds/geometric-orange.svg rename to datamodels/2.x/itop-portal-base/portal/public/img/backgrounds/geometric-orange.svg diff --git a/datamodels/2.x/itop-portal-base/portal-silex/web/img/icons/checklist-ok-orange-100px.png b/datamodels/2.x/itop-portal-base/portal/public/img/icons/checklist-ok-orange-100px.png similarity index 100% rename from datamodels/2.x/itop-portal-base/portal-silex/web/img/icons/checklist-ok-orange-100px.png rename to datamodels/2.x/itop-portal-base/portal/public/img/icons/checklist-ok-orange-100px.png diff --git a/datamodels/2.x/itop-portal-base/portal-silex/web/img/icons/headset-mic-orange-100px.png b/datamodels/2.x/itop-portal-base/portal/public/img/icons/headset-mic-orange-100px.png similarity index 100% rename from datamodels/2.x/itop-portal-base/portal-silex/web/img/icons/headset-mic-orange-100px.png rename to datamodels/2.x/itop-portal-base/portal/public/img/icons/headset-mic-orange-100px.png diff --git a/datamodels/2.x/itop-portal-base/portal-silex/web/img/icons/hierarchy-white-13px.png b/datamodels/2.x/itop-portal-base/portal/public/img/icons/hierarchy-white-13px.png similarity index 100% rename from datamodels/2.x/itop-portal-base/portal-silex/web/img/icons/hierarchy-white-13px.png rename to datamodels/2.x/itop-portal-base/portal/public/img/icons/hierarchy-white-13px.png diff --git a/datamodels/2.x/itop-portal-base/portal-silex/web/img/icons/laptop-cursor-orange-100px.png b/datamodels/2.x/itop-portal-base/portal/public/img/icons/laptop-cursor-orange-100px.png similarity index 100% rename from datamodels/2.x/itop-portal-base/portal-silex/web/img/icons/laptop-cursor-orange-100px.png rename to datamodels/2.x/itop-portal-base/portal/public/img/icons/laptop-cursor-orange-100px.png diff --git a/datamodels/2.x/itop-portal-base/portal-silex/web/img/icons/network-device-orange-100px.png b/datamodels/2.x/itop-portal-base/portal/public/img/icons/network-device-orange-100px.png similarity index 100% rename from datamodels/2.x/itop-portal-base/portal-silex/web/img/icons/network-device-orange-100px.png rename to datamodels/2.x/itop-portal-base/portal/public/img/icons/network-device-orange-100px.png diff --git a/datamodels/2.x/itop-portal-base/portal-silex/web/img/icons/puzzle-piece-orange-100px.png b/datamodels/2.x/itop-portal-base/portal/public/img/icons/puzzle-piece-orange-100px.png similarity index 100% rename from datamodels/2.x/itop-portal-base/portal-silex/web/img/icons/puzzle-piece-orange-100px.png rename to datamodels/2.x/itop-portal-base/portal/public/img/icons/puzzle-piece-orange-100px.png diff --git a/datamodels/2.x/itop-portal-base/portal-silex/web/img/icons/warning-sign-orange-100px.png b/datamodels/2.x/itop-portal-base/portal/public/img/icons/warning-sign-orange-100px.png similarity index 100% rename from datamodels/2.x/itop-portal-base/portal-silex/web/img/icons/warning-sign-orange-100px.png rename to datamodels/2.x/itop-portal-base/portal/public/img/icons/warning-sign-orange-100px.png diff --git a/datamodels/2.x/itop-portal-base/portal-silex/web/img/user-profile-default-256px.png b/datamodels/2.x/itop-portal-base/portal/public/img/user-profile-default-256px.png similarity index 100% rename from datamodels/2.x/itop-portal-base/portal-silex/web/img/user-profile-default-256px.png rename to datamodels/2.x/itop-portal-base/portal/public/img/user-profile-default-256px.png diff --git a/datamodels/2.x/itop-portal-base/portal/public/index.php b/datamodels/2.x/itop-portal-base/portal/public/index.php new file mode 100644 index 000000000..f694c1001 --- /dev/null +++ b/datamodels/2.x/itop-portal-base/portal/public/index.php @@ -0,0 +1,30 @@ +handle($request); +$response->send(); +$kernel->terminate($request, $response); diff --git a/datamodels/2.x/itop-portal-base/portal-silex/web/js/dataTables.accentNeutraliseForFilter.js b/datamodels/2.x/itop-portal-base/portal/public/js/dataTables.accentNeutraliseForFilter.js similarity index 100% rename from datamodels/2.x/itop-portal-base/portal-silex/web/js/dataTables.accentNeutraliseForFilter.js rename to datamodels/2.x/itop-portal-base/portal/public/js/dataTables.accentNeutraliseForFilter.js diff --git a/datamodels/2.x/itop-portal-base/portal-silex/web/js/export.js b/datamodels/2.x/itop-portal-base/portal/public/js/export.js similarity index 100% rename from datamodels/2.x/itop-portal-base/portal-silex/web/js/export.js rename to datamodels/2.x/itop-portal-base/portal/public/js/export.js diff --git a/datamodels/2.x/itop-portal-base/portal-silex/web/js/portal_form_field.js b/datamodels/2.x/itop-portal-base/portal/public/js/portal_form_field.js similarity index 100% rename from datamodels/2.x/itop-portal-base/portal-silex/web/js/portal_form_field.js rename to datamodels/2.x/itop-portal-base/portal/public/js/portal_form_field.js diff --git a/datamodels/2.x/itop-portal-base/portal-silex/web/js/portal_form_field_html.js b/datamodels/2.x/itop-portal-base/portal/public/js/portal_form_field_html.js similarity index 100% rename from datamodels/2.x/itop-portal-base/portal-silex/web/js/portal_form_field_html.js rename to datamodels/2.x/itop-portal-base/portal/public/js/portal_form_field_html.js diff --git a/datamodels/2.x/itop-portal-base/portal-silex/web/js/portal_form_field_set.js b/datamodels/2.x/itop-portal-base/portal/public/js/portal_form_field_set.js similarity index 100% rename from datamodels/2.x/itop-portal-base/portal-silex/web/js/portal_form_field_set.js rename to datamodels/2.x/itop-portal-base/portal/public/js/portal_form_field_set.js diff --git a/datamodels/2.x/itop-portal-base/portal-silex/web/js/portal_form_handler.js b/datamodels/2.x/itop-portal-base/portal/public/js/portal_form_handler.js similarity index 100% rename from datamodels/2.x/itop-portal-base/portal-silex/web/js/portal_form_handler.js rename to datamodels/2.x/itop-portal-base/portal/public/js/portal_form_handler.js diff --git a/datamodels/2.x/itop-portal-base/portal-silex/web/lib/bootstrap-datetimepicker/css/bootstrap-datetimepicker-standalone.css b/datamodels/2.x/itop-portal-base/portal/public/lib/bootstrap-datetimepicker/css/bootstrap-datetimepicker-standalone.css similarity index 100% rename from datamodels/2.x/itop-portal-base/portal-silex/web/lib/bootstrap-datetimepicker/css/bootstrap-datetimepicker-standalone.css rename to datamodels/2.x/itop-portal-base/portal/public/lib/bootstrap-datetimepicker/css/bootstrap-datetimepicker-standalone.css diff --git a/datamodels/2.x/itop-portal-base/portal-silex/web/lib/bootstrap-datetimepicker/css/bootstrap-datetimepicker.css b/datamodels/2.x/itop-portal-base/portal/public/lib/bootstrap-datetimepicker/css/bootstrap-datetimepicker.css similarity index 100% rename from datamodels/2.x/itop-portal-base/portal-silex/web/lib/bootstrap-datetimepicker/css/bootstrap-datetimepicker.css rename to datamodels/2.x/itop-portal-base/portal/public/lib/bootstrap-datetimepicker/css/bootstrap-datetimepicker.css diff --git a/datamodels/2.x/itop-portal-base/portal-silex/web/lib/bootstrap-datetimepicker/css/bootstrap-datetimepicker.min.css b/datamodels/2.x/itop-portal-base/portal/public/lib/bootstrap-datetimepicker/css/bootstrap-datetimepicker.min.css similarity index 100% rename from datamodels/2.x/itop-portal-base/portal-silex/web/lib/bootstrap-datetimepicker/css/bootstrap-datetimepicker.min.css rename to datamodels/2.x/itop-portal-base/portal/public/lib/bootstrap-datetimepicker/css/bootstrap-datetimepicker.min.css diff --git a/datamodels/2.x/itop-portal-base/portal-silex/web/lib/bootstrap-datetimepicker/js/bootstrap-datetimepicker.min.js b/datamodels/2.x/itop-portal-base/portal/public/lib/bootstrap-datetimepicker/js/bootstrap-datetimepicker.min.js similarity index 100% rename from datamodels/2.x/itop-portal-base/portal-silex/web/lib/bootstrap-datetimepicker/js/bootstrap-datetimepicker.min.js rename to datamodels/2.x/itop-portal-base/portal/public/lib/bootstrap-datetimepicker/js/bootstrap-datetimepicker.min.js diff --git a/datamodels/2.x/itop-portal-base/portal-silex/web/lib/bootstrap/css/bootstrap-theme.css.map b/datamodels/2.x/itop-portal-base/portal/public/lib/bootstrap/css/bootstrap-theme.css.map similarity index 100% rename from datamodels/2.x/itop-portal-base/portal-silex/web/lib/bootstrap/css/bootstrap-theme.css.map rename to datamodels/2.x/itop-portal-base/portal/public/lib/bootstrap/css/bootstrap-theme.css.map diff --git a/datamodels/2.x/itop-portal-base/portal-silex/web/lib/bootstrap/css/bootstrap-theme.min.css b/datamodels/2.x/itop-portal-base/portal/public/lib/bootstrap/css/bootstrap-theme.min.css similarity index 100% rename from datamodels/2.x/itop-portal-base/portal-silex/web/lib/bootstrap/css/bootstrap-theme.min.css rename to datamodels/2.x/itop-portal-base/portal/public/lib/bootstrap/css/bootstrap-theme.min.css diff --git a/datamodels/2.x/itop-portal-base/portal-silex/web/lib/bootstrap/css/bootstrap.css.map b/datamodels/2.x/itop-portal-base/portal/public/lib/bootstrap/css/bootstrap.css.map similarity index 100% rename from datamodels/2.x/itop-portal-base/portal-silex/web/lib/bootstrap/css/bootstrap.css.map rename to datamodels/2.x/itop-portal-base/portal/public/lib/bootstrap/css/bootstrap.css.map diff --git a/datamodels/2.x/itop-portal-base/portal-silex/web/lib/bootstrap/css/bootstrap.min.css b/datamodels/2.x/itop-portal-base/portal/public/lib/bootstrap/css/bootstrap.min.css similarity index 100% rename from datamodels/2.x/itop-portal-base/portal-silex/web/lib/bootstrap/css/bootstrap.min.css rename to datamodels/2.x/itop-portal-base/portal/public/lib/bootstrap/css/bootstrap.min.css diff --git a/datamodels/2.x/itop-portal-base/portal-silex/web/lib/bootstrap/fonts/glyphicons-halflings-regular.eot b/datamodels/2.x/itop-portal-base/portal/public/lib/bootstrap/fonts/glyphicons-halflings-regular.eot similarity index 100% rename from datamodels/2.x/itop-portal-base/portal-silex/web/lib/bootstrap/fonts/glyphicons-halflings-regular.eot rename to datamodels/2.x/itop-portal-base/portal/public/lib/bootstrap/fonts/glyphicons-halflings-regular.eot diff --git a/datamodels/2.x/itop-portal-base/portal-silex/web/lib/bootstrap/fonts/glyphicons-halflings-regular.svg b/datamodels/2.x/itop-portal-base/portal/public/lib/bootstrap/fonts/glyphicons-halflings-regular.svg similarity index 100% rename from datamodels/2.x/itop-portal-base/portal-silex/web/lib/bootstrap/fonts/glyphicons-halflings-regular.svg rename to datamodels/2.x/itop-portal-base/portal/public/lib/bootstrap/fonts/glyphicons-halflings-regular.svg diff --git a/datamodels/2.x/itop-portal-base/portal-silex/web/lib/bootstrap/fonts/glyphicons-halflings-regular.ttf b/datamodels/2.x/itop-portal-base/portal/public/lib/bootstrap/fonts/glyphicons-halflings-regular.ttf similarity index 100% rename from datamodels/2.x/itop-portal-base/portal-silex/web/lib/bootstrap/fonts/glyphicons-halflings-regular.ttf rename to datamodels/2.x/itop-portal-base/portal/public/lib/bootstrap/fonts/glyphicons-halflings-regular.ttf diff --git a/datamodels/2.x/itop-portal-base/portal-silex/web/lib/bootstrap/fonts/glyphicons-halflings-regular.woff b/datamodels/2.x/itop-portal-base/portal/public/lib/bootstrap/fonts/glyphicons-halflings-regular.woff similarity index 100% rename from datamodels/2.x/itop-portal-base/portal-silex/web/lib/bootstrap/fonts/glyphicons-halflings-regular.woff rename to datamodels/2.x/itop-portal-base/portal/public/lib/bootstrap/fonts/glyphicons-halflings-regular.woff diff --git a/datamodels/2.x/itop-portal-base/portal-silex/web/lib/bootstrap/fonts/glyphicons-halflings-regular.woff2 b/datamodels/2.x/itop-portal-base/portal/public/lib/bootstrap/fonts/glyphicons-halflings-regular.woff2 similarity index 100% rename from datamodels/2.x/itop-portal-base/portal-silex/web/lib/bootstrap/fonts/glyphicons-halflings-regular.woff2 rename to datamodels/2.x/itop-portal-base/portal/public/lib/bootstrap/fonts/glyphicons-halflings-regular.woff2 diff --git a/datamodels/2.x/itop-portal-base/portal-silex/web/lib/bootstrap/js/bootstrap.min.js b/datamodels/2.x/itop-portal-base/portal/public/lib/bootstrap/js/bootstrap.min.js similarity index 100% rename from datamodels/2.x/itop-portal-base/portal-silex/web/lib/bootstrap/js/bootstrap.min.js rename to datamodels/2.x/itop-portal-base/portal/public/lib/bootstrap/js/bootstrap.min.js diff --git a/datamodels/2.x/itop-portal-base/portal-silex/web/lib/bootstrap/js/npm.js b/datamodels/2.x/itop-portal-base/portal/public/lib/bootstrap/js/npm.js similarity index 100% rename from datamodels/2.x/itop-portal-base/portal-silex/web/lib/bootstrap/js/npm.js rename to datamodels/2.x/itop-portal-base/portal/public/lib/bootstrap/js/npm.js diff --git a/datamodels/2.x/itop-portal-base/portal-silex/web/lib/datatables/css/dataTables.bootstrap.min.css b/datamodels/2.x/itop-portal-base/portal/public/lib/datatables/css/dataTables.bootstrap.min.css similarity index 100% rename from datamodels/2.x/itop-portal-base/portal-silex/web/lib/datatables/css/dataTables.bootstrap.min.css rename to datamodels/2.x/itop-portal-base/portal/public/lib/datatables/css/dataTables.bootstrap.min.css diff --git a/datamodels/2.x/itop-portal-base/portal-silex/web/lib/datatables/css/fixedHeader.bootstrap.min.css b/datamodels/2.x/itop-portal-base/portal/public/lib/datatables/css/fixedHeader.bootstrap.min.css similarity index 100% rename from datamodels/2.x/itop-portal-base/portal-silex/web/lib/datatables/css/fixedHeader.bootstrap.min.css rename to datamodels/2.x/itop-portal-base/portal/public/lib/datatables/css/fixedHeader.bootstrap.min.css diff --git a/datamodels/2.x/itop-portal-base/portal-silex/web/lib/datatables/css/responsive.bootstrap.min.css b/datamodels/2.x/itop-portal-base/portal/public/lib/datatables/css/responsive.bootstrap.min.css similarity index 100% rename from datamodels/2.x/itop-portal-base/portal-silex/web/lib/datatables/css/responsive.bootstrap.min.css rename to datamodels/2.x/itop-portal-base/portal/public/lib/datatables/css/responsive.bootstrap.min.css diff --git a/datamodels/2.x/itop-portal-base/portal-silex/web/lib/datatables/css/scroller.bootstrap.min.css b/datamodels/2.x/itop-portal-base/portal/public/lib/datatables/css/scroller.bootstrap.min.css similarity index 100% rename from datamodels/2.x/itop-portal-base/portal-silex/web/lib/datatables/css/scroller.bootstrap.min.css rename to datamodels/2.x/itop-portal-base/portal/public/lib/datatables/css/scroller.bootstrap.min.css diff --git a/datamodels/2.x/itop-portal-base/portal-silex/web/lib/datatables/css/select.bootstrap.min.css b/datamodels/2.x/itop-portal-base/portal/public/lib/datatables/css/select.bootstrap.min.css similarity index 100% rename from datamodels/2.x/itop-portal-base/portal-silex/web/lib/datatables/css/select.bootstrap.min.css rename to datamodels/2.x/itop-portal-base/portal/public/lib/datatables/css/select.bootstrap.min.css diff --git a/datamodels/2.x/itop-portal-base/portal-silex/web/lib/datatables/css/select.dataTables.min.css b/datamodels/2.x/itop-portal-base/portal/public/lib/datatables/css/select.dataTables.min.css similarity index 100% rename from datamodels/2.x/itop-portal-base/portal-silex/web/lib/datatables/css/select.dataTables.min.css rename to datamodels/2.x/itop-portal-base/portal/public/lib/datatables/css/select.dataTables.min.css diff --git a/datamodels/2.x/itop-portal-base/portal-silex/web/lib/datatables/images/sort_asc.png b/datamodels/2.x/itop-portal-base/portal/public/lib/datatables/images/sort_asc.png similarity index 100% rename from datamodels/2.x/itop-portal-base/portal-silex/web/lib/datatables/images/sort_asc.png rename to datamodels/2.x/itop-portal-base/portal/public/lib/datatables/images/sort_asc.png diff --git a/datamodels/2.x/itop-portal-base/portal-silex/web/lib/datatables/images/sort_asc_disabled.png b/datamodels/2.x/itop-portal-base/portal/public/lib/datatables/images/sort_asc_disabled.png similarity index 100% rename from datamodels/2.x/itop-portal-base/portal-silex/web/lib/datatables/images/sort_asc_disabled.png rename to datamodels/2.x/itop-portal-base/portal/public/lib/datatables/images/sort_asc_disabled.png diff --git a/datamodels/2.x/itop-portal-base/portal-silex/web/lib/datatables/images/sort_both.png b/datamodels/2.x/itop-portal-base/portal/public/lib/datatables/images/sort_both.png similarity index 100% rename from datamodels/2.x/itop-portal-base/portal-silex/web/lib/datatables/images/sort_both.png rename to datamodels/2.x/itop-portal-base/portal/public/lib/datatables/images/sort_both.png diff --git a/datamodels/2.x/itop-portal-base/portal-silex/web/lib/datatables/images/sort_desc.png b/datamodels/2.x/itop-portal-base/portal/public/lib/datatables/images/sort_desc.png similarity index 100% rename from datamodels/2.x/itop-portal-base/portal-silex/web/lib/datatables/images/sort_desc.png rename to datamodels/2.x/itop-portal-base/portal/public/lib/datatables/images/sort_desc.png diff --git a/datamodels/2.x/itop-portal-base/portal-silex/web/lib/datatables/images/sort_desc_disabled.png b/datamodels/2.x/itop-portal-base/portal/public/lib/datatables/images/sort_desc_disabled.png similarity index 100% rename from datamodels/2.x/itop-portal-base/portal-silex/web/lib/datatables/images/sort_desc_disabled.png rename to datamodels/2.x/itop-portal-base/portal/public/lib/datatables/images/sort_desc_disabled.png diff --git a/datamodels/2.x/itop-portal-base/portal-silex/web/lib/datatables/js/dataTables.bootstrap.min.js b/datamodels/2.x/itop-portal-base/portal/public/lib/datatables/js/dataTables.bootstrap.min.js similarity index 100% rename from datamodels/2.x/itop-portal-base/portal-silex/web/lib/datatables/js/dataTables.bootstrap.min.js rename to datamodels/2.x/itop-portal-base/portal/public/lib/datatables/js/dataTables.bootstrap.min.js diff --git a/datamodels/2.x/itop-portal-base/portal-silex/web/lib/datatables/js/dataTables.fixedHeader.min.js b/datamodels/2.x/itop-portal-base/portal/public/lib/datatables/js/dataTables.fixedHeader.min.js similarity index 100% rename from datamodels/2.x/itop-portal-base/portal-silex/web/lib/datatables/js/dataTables.fixedHeader.min.js rename to datamodels/2.x/itop-portal-base/portal/public/lib/datatables/js/dataTables.fixedHeader.min.js diff --git a/datamodels/2.x/itop-portal-base/portal-silex/web/lib/datatables/js/dataTables.responsive.min.js b/datamodels/2.x/itop-portal-base/portal/public/lib/datatables/js/dataTables.responsive.min.js similarity index 100% rename from datamodels/2.x/itop-portal-base/portal-silex/web/lib/datatables/js/dataTables.responsive.min.js rename to datamodels/2.x/itop-portal-base/portal/public/lib/datatables/js/dataTables.responsive.min.js diff --git a/datamodels/2.x/itop-portal-base/portal-silex/web/lib/datatables/js/dataTables.scroller.min.js b/datamodels/2.x/itop-portal-base/portal/public/lib/datatables/js/dataTables.scroller.min.js similarity index 100% rename from datamodels/2.x/itop-portal-base/portal-silex/web/lib/datatables/js/dataTables.scroller.min.js rename to datamodels/2.x/itop-portal-base/portal/public/lib/datatables/js/dataTables.scroller.min.js diff --git a/datamodels/2.x/itop-portal-base/portal-silex/web/lib/datatables/js/dataTables.select.min.js b/datamodels/2.x/itop-portal-base/portal/public/lib/datatables/js/dataTables.select.min.js similarity index 100% rename from datamodels/2.x/itop-portal-base/portal-silex/web/lib/datatables/js/dataTables.select.min.js rename to datamodels/2.x/itop-portal-base/portal/public/lib/datatables/js/dataTables.select.min.js diff --git a/datamodels/2.x/itop-portal-base/portal-silex/web/lib/datatables/js/datetime-moment.js b/datamodels/2.x/itop-portal-base/portal/public/lib/datatables/js/datetime-moment.js similarity index 100% rename from datamodels/2.x/itop-portal-base/portal-silex/web/lib/datatables/js/datetime-moment.js rename to datamodels/2.x/itop-portal-base/portal/public/lib/datatables/js/datetime-moment.js diff --git a/datamodels/2.x/itop-portal-base/portal-silex/web/lib/datatables/js/jquery.dataTables.min.js b/datamodels/2.x/itop-portal-base/portal/public/lib/datatables/js/jquery.dataTables.min.js similarity index 100% rename from datamodels/2.x/itop-portal-base/portal-silex/web/lib/datatables/js/jquery.dataTables.min.js rename to datamodels/2.x/itop-portal-base/portal/public/lib/datatables/js/jquery.dataTables.min.js diff --git a/datamodels/2.x/itop-portal-base/portal-silex/web/lib/handlebars/js/handlebars.min-768ddbd.js b/datamodels/2.x/itop-portal-base/portal/public/lib/handlebars/js/handlebars.min-768ddbd.js similarity index 100% rename from datamodels/2.x/itop-portal-base/portal-silex/web/lib/handlebars/js/handlebars.min-768ddbd.js rename to datamodels/2.x/itop-portal-base/portal/public/lib/handlebars/js/handlebars.min-768ddbd.js diff --git a/datamodels/2.x/itop-portal-base/portal-silex/web/lib/jquery-base64/js/jquery.base64.min.js b/datamodels/2.x/itop-portal-base/portal/public/lib/jquery-base64/js/jquery.base64.min.js similarity index 100% rename from datamodels/2.x/itop-portal-base/portal-silex/web/lib/jquery-base64/js/jquery.base64.min.js rename to datamodels/2.x/itop-portal-base/portal/public/lib/jquery-base64/js/jquery.base64.min.js diff --git a/datamodels/2.x/itop-portal-base/portal-silex/web/lib/jquery-treelistfilter/js/TreeListFilter.js b/datamodels/2.x/itop-portal-base/portal/public/lib/jquery-treelistfilter/js/TreeListFilter.js similarity index 100% rename from datamodels/2.x/itop-portal-base/portal-silex/web/lib/jquery-treelistfilter/js/TreeListFilter.js rename to datamodels/2.x/itop-portal-base/portal/public/lib/jquery-treelistfilter/js/TreeListFilter.js diff --git a/datamodels/2.x/itop-portal-base/portal-silex/web/lib/jquery-visible/js/jquery.visible.min.js b/datamodels/2.x/itop-portal-base/portal/public/lib/jquery-visible/js/jquery.visible.min.js similarity index 100% rename from datamodels/2.x/itop-portal-base/portal-silex/web/lib/jquery-visible/js/jquery.visible.min.js rename to datamodels/2.x/itop-portal-base/portal/public/lib/jquery-visible/js/jquery.visible.min.js diff --git a/datamodels/2.x/itop-portal-base/portal-silex/web/lib/typeahead/css/typeaheadjs.bootstrap.css b/datamodels/2.x/itop-portal-base/portal/public/lib/typeahead/css/typeaheadjs.bootstrap.css similarity index 100% rename from datamodels/2.x/itop-portal-base/portal-silex/web/lib/typeahead/css/typeaheadjs.bootstrap.css rename to datamodels/2.x/itop-portal-base/portal/public/lib/typeahead/css/typeaheadjs.bootstrap.css diff --git a/datamodels/2.x/itop-portal-base/portal-silex/web/lib/typeahead/js/bloodhound.min.js b/datamodels/2.x/itop-portal-base/portal/public/lib/typeahead/js/bloodhound.min.js similarity index 100% rename from datamodels/2.x/itop-portal-base/portal-silex/web/lib/typeahead/js/bloodhound.min.js rename to datamodels/2.x/itop-portal-base/portal/public/lib/typeahead/js/bloodhound.min.js diff --git a/datamodels/2.x/itop-portal-base/portal-silex/web/lib/typeahead/js/typeahead.bundle.min.js b/datamodels/2.x/itop-portal-base/portal/public/lib/typeahead/js/typeahead.bundle.min.js similarity index 100% rename from datamodels/2.x/itop-portal-base/portal-silex/web/lib/typeahead/js/typeahead.bundle.min.js rename to datamodels/2.x/itop-portal-base/portal/public/lib/typeahead/js/typeahead.bundle.min.js diff --git a/datamodels/2.x/itop-portal-base/portal-silex/web/lib/typeahead/js/typeahead.jquery.min.js b/datamodels/2.x/itop-portal-base/portal/public/lib/typeahead/js/typeahead.jquery.min.js similarity index 100% rename from datamodels/2.x/itop-portal-base/portal-silex/web/lib/typeahead/js/typeahead.jquery.min.js rename to datamodels/2.x/itop-portal-base/portal/public/lib/typeahead/js/typeahead.jquery.min.js diff --git a/datamodels/2.x/itop-portal-base/portal-silex/web/lib/typeahead/js/update.txt b/datamodels/2.x/itop-portal-base/portal/public/lib/typeahead/js/update.txt similarity index 100% rename from datamodels/2.x/itop-portal-base/portal-silex/web/lib/typeahead/js/update.txt rename to datamodels/2.x/itop-portal-base/portal/public/lib/typeahead/js/update.txt diff --git a/datamodels/2.x/itop-portal-base/portal-silex/src/entities/abstractbrick.class.inc.php b/datamodels/2.x/itop-portal-base/portal/src/Brick/AbstractBrick.php similarity index 100% rename from datamodels/2.x/itop-portal-base/portal-silex/src/entities/abstractbrick.class.inc.php rename to datamodels/2.x/itop-portal-base/portal/src/Brick/AbstractBrick.php diff --git a/datamodels/2.x/itop-portal-base/portal-silex/src/entities/aggregatepagebrick.class.inc.php b/datamodels/2.x/itop-portal-base/portal/src/Brick/AggregatePageBrick.php similarity index 95% rename from datamodels/2.x/itop-portal-base/portal-silex/src/entities/aggregatepagebrick.class.inc.php rename to datamodels/2.x/itop-portal-base/portal/src/Brick/AggregatePageBrick.php index 4191a3e92..b379f19d5 100644 --- a/datamodels/2.x/itop-portal-base/portal-silex/src/entities/aggregatepagebrick.class.inc.php +++ b/datamodels/2.x/itop-portal-base/portal/src/Brick/AggregatePageBrick.php @@ -27,7 +27,7 @@ class AggregatePageBrick extends PortalBrick { const DEFAULT_DECORATION_CLASS_HOME = 'fa fa-dashboard'; const DEFAULT_DECORATION_CLASS_NAVIGATION_MENU = 'fa fa-dashboard fa-2x'; - const DEFAULT_PAGE_TEMPLATE_PATH = 'itop-portal-base/portal/src/views/bricks/aggregate-page/layout.html.twig'; + const DEFAULT_PAGE_TEMPLATE_PATH = 'itop-portal-base/portal/templates/bricks/aggregate-page/layout.html.twig'; static $sRouteName = 'p_aggregatepage_brick'; 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 new file mode 100644 index 000000000..219ab07b7 --- /dev/null +++ b/datamodels/2.x/itop-portal-base/portal/src/Brick/BrickCollection.php @@ -0,0 +1,173 @@ +oModuleDesign = $oModuleDesign; + } + + public function __call($method, $arguments) + { + return $this->GetBrickProperty($method); + } + + /** + * @param string $sId + * + * @return mixed + * @throws PropertyNotFoundException + */ + private function GetBrickProperty($sId) + { + 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()) + ); + } + + public function getBricks() + { + if (! isset($this->aAllowedBricksData)) { + $this->LazyLoad(); + } + + return $this->aAllowedBricksData; + } + + public function getBrickById($id) + { + foreach ($this->getBricks()['bricks'] as $brick) { + if ($brick->GetId() === $id) + { + return $brick; + } + } + + throw new BrickNotFoundException('Brick with id = "'.$id.'" was not found among loaded bricks.'); + } + + private function LazyLoad() + { + $aRawBrickList = $this->GetRawBrickList(); + + $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(); + if ($oBrick->GetVisibleHome()) + { + $this->aAllowedBricksData['bricks_home_count']++; + } + if ($oBrick->GetVisibleNavigationMenu()) + { + $this->aAllowedBricksData['bricks_navigation_menu_count']++; + } + } + } + + // - 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) { + 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) { + return $a->GetRankNavigationMenu() > $b->GetRankNavigationMenu(); + }); + } + + private function GetRawBrickList() + { + $aBricks = array(); + foreach ($this->oModuleDesign->GetNodes('/module_design/bricks/brick') as $oBrickNode) + { + $sBrickClass = $oBrickNode->getAttribute('xsi:type'); + try + { + if (class_exists($sBrickClass)) + { + /** @var \Combodo\iTop\Portal\Brick\PortalBrick $oBrick */ + $oBrick = new $sBrickClass(); + $oBrick->LoadFromXml($oBrickNode); + + $aBricks[] = $oBrick; + } + else + { + throw new DOMFormatException('Unknown brick class "'.$sBrickClass.'" from xsi:type attribute', null, + null, $oBrickNode); + } + } + catch (DOMFormatException $e) + { + throw new Exception('Could not create brick ('.$sBrickClass.') from XML because of a DOM problem : '.$e->getMessage()); + } + catch (Exception $e) + { + throw new Exception('Could not create brick ('.$sBrickClass.') from XML : '.$oBrickNode->Dump().' '.$e->getMessage()); + } + } + + return $aBricks; + } + +} \ No newline at end of file diff --git a/datamodels/2.x/itop-portal-base/portal/src/Brick/BrickNotFoundException.php b/datamodels/2.x/itop-portal-base/portal/src/Brick/BrickNotFoundException.php new file mode 100644 index 000000000..c0bfb0234 --- /dev/null +++ b/datamodels/2.x/itop-portal-base/portal/src/Brick/BrickNotFoundException.php @@ -0,0 +1,21 @@ + +/** + * 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\Brick; @@ -266,7 +269,7 @@ class BrowseBrick extends PortalBrick } else { - $sTemplatePath = 'itop-portal-base/portal/src/views/bricks/browse/mode_' . $sModeId . '.html.twig'; + $sTemplatePath = 'itop-portal-base/portal/templates/bricks/browse/mode_' . $sModeId . '.html.twig'; } $aModeData['template'] = $sTemplatePath; diff --git a/datamodels/2.x/itop-portal-base/portal-silex/src/entities/createbrick.class.inc.php b/datamodels/2.x/itop-portal-base/portal/src/Brick/CreateBrick.php similarity index 76% rename from datamodels/2.x/itop-portal-base/portal-silex/src/entities/createbrick.class.inc.php rename to datamodels/2.x/itop-portal-base/portal/src/Brick/CreateBrick.php index dcc21a83c..08e514bf4 100644 --- a/datamodels/2.x/itop-portal-base/portal-silex/src/entities/createbrick.class.inc.php +++ b/datamodels/2.x/itop-portal-base/portal/src/Brick/CreateBrick.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\Brick; @@ -31,7 +34,7 @@ class CreateBrick extends PortalBrick { const DEFAULT_DECORATION_CLASS_HOME = 'fa fa-plus'; const DEFAULT_DECORATION_CLASS_NAVIGATION_MENU = 'fa fa-plus fa-2x'; - const DEFAULT_PAGE_TEMPLATE_PATH = 'itop-portal-base/portal/src/views/bricks/create/modal.html.twig'; + const DEFAULT_PAGE_TEMPLATE_PATH = 'itop-portal-base/portal/templates/bricks/create/modal.html.twig'; const DEFAULT_CLASS = ''; static $sRouteName = 'p_create_brick'; diff --git a/datamodels/2.x/itop-portal-base/portal-silex/src/entities/filterbrick.class.inc.php b/datamodels/2.x/itop-portal-base/portal/src/Brick/FilterBrick.php similarity index 84% rename from datamodels/2.x/itop-portal-base/portal-silex/src/entities/filterbrick.class.inc.php rename to datamodels/2.x/itop-portal-base/portal/src/Brick/FilterBrick.php index 849d89754..31cbf39ce 100644 --- a/datamodels/2.x/itop-portal-base/portal-silex/src/entities/filterbrick.class.inc.php +++ b/datamodels/2.x/itop-portal-base/portal/src/Brick/FilterBrick.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\Brick; @@ -30,7 +33,7 @@ use Combodo\iTop\DesignElement; class FilterBrick extends PortalBrick { const DEFAULT_VISIBLE_NAVIGATION_MENU = false; - const DEFAULT_TILE_TEMPLATE_PATH = 'itop-portal-base/portal/src/views/bricks/filter/tile.html.twig'; + const DEFAULT_TILE_TEMPLATE_PATH = 'itop-portal-base/portal/templates/bricks/filter/tile.html.twig'; const DEFAULT_DECORATION_CLASS_HOME = 'fa fa-search'; const DEFAULT_DECORATION_CLASS_NAVIGATION_MENU = 'fa fa-search fa-2x'; diff --git a/datamodels/2.x/itop-portal-base/portal-silex/src/entities/managebrick.class.inc.php b/datamodels/2.x/itop-portal-base/portal/src/Brick/ManageBrick.php similarity index 95% rename from datamodels/2.x/itop-portal-base/portal-silex/src/entities/managebrick.class.inc.php rename to datamodels/2.x/itop-portal-base/portal/src/Brick/ManageBrick.php index 4b19438c5..c08d144c8 100644 --- a/datamodels/2.x/itop-portal-base/portal-silex/src/entities/managebrick.class.inc.php +++ b/datamodels/2.x/itop-portal-base/portal/src/Brick/ManageBrick.php @@ -1,22 +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\Brick; @@ -41,8 +43,8 @@ class ManageBrick extends PortalBrick const ENUM_DISPLAY_MODE_PIE = 'pie-chart'; const ENUM_DISPLAY_MODE_BAR = 'bar-chart'; - const ENUM_PAGE_TEMPLATE_PATH_TABLE = 'itop-portal-base/portal/src/views/bricks/manage/layout-table.html.twig'; - const ENUM_PAGE_TEMPLATE_PATH_CHART = 'itop-portal-base/portal/src/views/bricks/manage/layout-chart.html.twig'; + const ENUM_PAGE_TEMPLATE_PATH_TABLE = 'itop-portal-base/portal/templates/bricks/manage/layout-table.html.twig'; + const ENUM_PAGE_TEMPLATE_PATH_CHART = 'itop-portal-base/portal/templates/bricks/manage/layout-chart.html.twig'; const DEFAULT_DECORATION_CLASS_HOME = 'fa fa-pencil-square'; const DEFAULT_DECORATION_CLASS_NAVIGATION_MENU = 'fa fa-pencil-square fa-2x'; @@ -58,7 +60,7 @@ class ManageBrick extends PortalBrick const DEFAULT_GROUP_LIMIT = 0; const DEFAULT_GROUP_SHOW_OTHERS = true; - const DEFAULT_TILE_TEMPLATE_PATH = 'itop-portal-base/portal/src/views/bricks/manage/tile-default.html.twig'; + const DEFAULT_TILE_TEMPLATE_PATH = 'itop-portal-base/portal/templates/bricks/manage/tile-default.html.twig'; const DEFAULT_TILE_CONTROLLER_ACTION = 'Combodo\\iTop\\Portal\\Controller\\ManageBrickController::TileAction'; static $aDisplayModes = array( @@ -76,28 +78,28 @@ class ManageBrick extends PortalBrick static $aPresentationData = array( self::ENUM_TILE_MODE_BADGE => array( 'decorationCssClass' => 'fa fa-id-card-o fa-2x', - 'tileTemplate' => 'itop-portal-base/portal/src/views/bricks/manage/tile-badge.html.twig', + 'tileTemplate' => 'itop-portal-base/portal/templates/bricks/manage/tile-badge.html.twig', 'layoutTemplate' => self::ENUM_PAGE_TEMPLATE_PATH_TABLE, 'layoutDisplayMode' => self::ENUM_DISPLAY_MODE_LIST, 'need_details' => true, ), self::ENUM_TILE_MODE_TOP => array( 'decorationCssClass' => 'fa fa-signal fa-rotate-270 fa-2x', - 'tileTemplate' => 'itop-portal-base/portal/src/views/bricks/manage/tile-top-list.html.twig', + 'tileTemplate' => 'itop-portal-base/portal/templates/bricks/manage/tile-top-list.html.twig', 'layoutTemplate' => self::ENUM_PAGE_TEMPLATE_PATH_TABLE, 'layoutDisplayMode' => self::ENUM_DISPLAY_MODE_LIST, 'need_details' => true, ), self::ENUM_TILE_MODE_PIE => array( 'decorationCssClass' => 'fa fa-pie-chart fa-2x', - 'tileTemplate' => 'itop-portal-base/portal/src/views/bricks/manage/tile-chart.html.twig', + 'tileTemplate' => 'itop-portal-base/portal/templates/bricks/manage/tile-chart.html.twig', 'layoutTemplate' => self::ENUM_PAGE_TEMPLATE_PATH_CHART, 'layoutDisplayMode' => self::ENUM_DISPLAY_MODE_PIE, 'need_details' => false, ), self::ENUM_TILE_MODE_BAR => array( 'decorationCssClass' => 'fa fa-bar-chart fa-2x', - 'tileTemplate' => 'itop-portal-base/portal/src/views/bricks/manage/tile-chart.html.twig', + 'tileTemplate' => 'itop-portal-base/portal/templates/bricks/manage/tile-chart.html.twig', 'layoutTemplate' => self::ENUM_PAGE_TEMPLATE_PATH_CHART, 'layoutDisplayMode' => self::ENUM_DISPLAY_MODE_BAR, 'need_details' => false, diff --git a/datamodels/2.x/itop-portal-base/portal-silex/src/entities/portalbrick.class.inc.php b/datamodels/2.x/itop-portal-base/portal/src/Brick/PortalBrick.php similarity index 94% rename from datamodels/2.x/itop-portal-base/portal-silex/src/entities/portalbrick.class.inc.php rename to datamodels/2.x/itop-portal-base/portal/src/Brick/PortalBrick.php index f88c01aef..ceec38dd1 100644 --- a/datamodels/2.x/itop-portal-base/portal-silex/src/entities/portalbrick.class.inc.php +++ b/datamodels/2.x/itop-portal-base/portal/src/Brick/PortalBrick.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\Brick; @@ -43,7 +46,7 @@ abstract class PortalBrick extends AbstractBrick const DEFAULT_VISIBLE_NAVIGATION_MENU = true; const DEFAULT_DECORATION_CLASS_HOME = ''; const DEFAULT_DECORATION_CLASS_NAVIGATION_MENU = ''; - const DEFAULT_TILE_TEMPLATE_PATH = 'itop-portal-base/portal/src/views/bricks/tile.html.twig'; + const DEFAULT_TILE_TEMPLATE_PATH = 'itop-portal-base/portal/templates/bricks/tile.html.twig'; const DEFAULT_TILE_CONTROLLER_ACTION = null; const DEFAULT_OPENING_TARGET = self::ENUM_OPENING_TARGET_MODAL; diff --git a/datamodels/2.x/itop-portal-base/portal/src/Brick/PropertyNotFoundException.php b/datamodels/2.x/itop-portal-base/portal/src/Brick/PropertyNotFoundException.php new file mode 100644 index 000000000..3b7f507f7 --- /dev/null +++ b/datamodels/2.x/itop-portal-base/portal/src/Brick/PropertyNotFoundException.php @@ -0,0 +1,21 @@ + +/** + * 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\Brick; @@ -28,8 +31,8 @@ use Combodo\iTop\DesignElement; */ class UserProfileBrick extends PortalBrick { - const DEFAULT_PAGE_TEMPLATE_PATH = 'itop-portal-base/portal/src/views/bricks/user-profile/layout.html.twig'; - const DEFAULT_TILE_TEMPLATE_PATH = 'itop-portal-base/portal/src/views/bricks/user-profile/tile.html.twig'; + const DEFAULT_PAGE_TEMPLATE_PATH = 'itop-portal-base/portal/templates/bricks/user-profile/layout.html.twig'; + const DEFAULT_TILE_TEMPLATE_PATH = 'itop-portal-base/portal/templates/bricks/user-profile/tile.html.twig'; const DEFAULT_VISIBLE_NAVIGATION_MENU = false; const DEFAULT_VISIBLE_HOME = false; const DEFAUT_TITLE = 'Brick:Portal:UserProfile:Title'; diff --git a/datamodels/2.x/itop-portal-base/portal/src/Controller/.gitignore b/datamodels/2.x/itop-portal-base/portal/src/Controller/.gitignore new file mode 100644 index 000000000..e69de29bb diff --git a/datamodels/2.x/itop-portal-base/portal/src/Controller/AbstractController.php b/datamodels/2.x/itop-portal-base/portal/src/Controller/AbstractController.php new file mode 100644 index 000000000..80e54b780 --- /dev/null +++ b/datamodels/2.x/itop-portal-base/portal/src/Controller/AbstractController.php @@ -0,0 +1,37 @@ + + * @since 2.3.0 + */ +abstract class AbstractController extends 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 new file mode 100644 index 000000000..5e58725a4 --- /dev/null +++ b/datamodels/2.x/itop-portal-base/portal/src/Controller/DefaultController.php @@ -0,0 +1,84 @@ + + * @since 2.3.0 + */ +class DefaultController extends AbstractController +{ + /** + * @param \Symfony\Component\HttpFoundation\Request $oRequest + * @param \Combodo\iTop\Portal\Brick\BrickCollection $bricksCollection + * + * @return \Symfony\Component\HttpFoundation\Response + */ + public function homeAction(Request $oRequest, BrickCollection $bricksCollection) + { + $aData = array(); + + // Rendering tiles + $aData['aTilesRendering'] = array(); + /** @var \Combodo\iTop\Portal\Brick\PortalBrick $oBrick */ + foreach($bricksCollection->getBricks()['bricks'] as $oBrick) + { + // Doing it only for tile visible on home page to avoid unnecessary rendering + if (($oBrick->GetVisibleHome() === true) && ($oBrick->GetTileControllerAction() !== null)) + { + $aControllerActionParts = explode('::', $oBrick->GetTileControllerAction()); + if (count($aControllerActionParts) !== 2) + { + return new Response('Tile controller action must be of form "\Namespace\ControllerClass::FunctionName" for brick "' . $oBrick->GetId() . '"', 500); + } + + $sControllerName = $aControllerActionParts[0]; + $sControllerAction = $aControllerActionParts[1]; + + $oController = new $sControllerName(); + if(!$oController instanceof ContainerAwareInterface) + { + return new Response('Tile controller must be implement ContainerAwareInterface for brick "' . $oBrick->GetId() . '"', 500); + } + $oController->setContainer($this->container); + /** @var Response $oResponse */ + $oResponse = $oController->$sControllerAction($oRequest, $oBrick->GetId()); + $aData['aTilesRendering'][$oBrick->GetId()] = $oResponse->getContent(); + } + } + + // Home page template + $template = $this->getParameter('combodo.portal.instance.conf')['properties']['templates']['home']; + + return $this->render($template, $aData); + } + +} 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 new file mode 100644 index 000000000..3e74eb11b --- /dev/null +++ b/datamodels/2.x/itop-portal-base/portal/src/DependencyInjection/SilexCompatBootstrap/PortalXmlConfiguration/AbstractConfiguration.php @@ -0,0 +1,31 @@ +moduleDesign = $moduleDesign; + } + + /** + * @return \ModuleDesign + */ + public function getModuleDesign() + { + return $this->moduleDesign; + } + +} \ 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 new file mode 100644 index 000000000..3cafecb4f --- /dev/null +++ b/datamodels/2.x/itop-portal-base/portal/src/DependencyInjection/SilexCompatBootstrap/PortalXmlConfiguration/Basic.php @@ -0,0 +1,335 @@ +getInitialPortalConf(); + // - Global portal properties + $aPortalConf = $this->ParseGlobalProperties($aPortalConf); + // - Rectifying portal logo url + $aPortalConf = $this->appendLogoUri($aPortalConf); + // - User allowed portals + $aPortalConf['portals'] = UserRights::GetAllowedPortals(); + + // - class list + $aPortalConf['ui_extensions'] = $this->getUiExtensions($container); + } + catch (Exception $e) + { + throw new Exception('Error while parsing portal configuration file : '.$e->getMessage()); + } + + $container->setParameter('combodo.portal.instance.conf', $aPortalConf); + } + + + + /** + * @return array + */ + private function getInitialPortalConf() + { + $aPortalConf = array( + 'properties' => array( + 'id' => PORTAL_ID, + 'name' => 'Page:DefaultTitle', + 'logo' => (file_exists(MODULESROOT.'branding/portal-logo.png')) ? utils::GetAbsoluteUrlModulesRoot().'branding/portal-logo.png' : '../images/logo-itop-dark-bg.svg', + 'themes' => array( + 'bootstrap' => 'itop-portal-base/portal/public/css/bootstrap-theme-combodo.scss', + 'portal' => 'itop-portal-base/portal/public/css/portal.scss', + 'others' => array(), + ), + 'templates' => array( + 'layout' => 'itop-portal-base/portal/templates/layout.html.twig', + 'home' => 'itop-portal-base/portal/templates/home/layout.html.twig' + ), + 'urlmaker_class' => null, + 'triggers_query' => null, + 'attachments' => array( + 'allow_delete' => true + ), + 'allowed_portals' => array( + 'opening_mode' => null, + ), + ), + 'portals' => array(), + 'forms' => array(), + 'ui_extensions' => array( + 'css_files' => array(), + 'css_inline' => null, + 'js_files' => array(), + 'js_inline' => null, + 'html' => array(), + ), + 'bricks' => array(), + 'bricks_total_width' => 0, + ); + + return $aPortalConf; + } + + /** + * @param ModuleDesign $oDesign + * @param array $aPortalConf + * + * @return array + */ + private function ParseGlobalProperties(array $aPortalConf) + { + foreach ($this->getModuleDesign()->GetNodes('/module_design/properties/*') as $oPropertyNode) { + switch ($oPropertyNode->nodeName) { + case 'name': + case 'urlmaker_class': + case 'triggers_query': + $aPortalConf['properties'][$oPropertyNode->nodeName] = $oPropertyNode->GetText( + $aPortalConf['properties'][$oPropertyNode->nodeName] + ); + break; + case 'logo': + $aPortalConf['properties'][$oPropertyNode->nodeName] = $oPropertyNode->GetText( + $aPortalConf['properties'][$oPropertyNode->nodeName] + ); + break; + case 'themes': + case 'templates': + $aPortalConf = $this->ParseTemplateAndTheme($aPortalConf, $oPropertyNode); + break; + case 'attachments': + $aPortalConf = $this->ParseAttachments($aPortalConf, $oPropertyNode); + break; + case 'allowed_portals': + $aPortalConf = $this->ParseAllowedPortals($aPortalConf, $oPropertyNode); + break; + } + } + + return $aPortalConf; + } + + /** + * @param array $aPortalConf + * @param $oPropertyNode + * + * @return array + */ + private function ParseTemplateAndTheme(array $aPortalConf, $oPropertyNode) + { + 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', + null, null, $oSubNode + ); + } + + $sNodeId = $oSubNode->getAttribute('id'); + switch ($oSubNode->nodeName) { + case 'theme': + switch ($sNodeId) { + case 'bootstrap': + case 'portal': + case 'custom': + $aPortalConf['properties']['themes'][$sNodeId] = $oSubNode->GetText(null); + break; + default: + $aPortalConf['properties']['themes']['others'][] = $oSubNode->GetText(null); + break; + } + break; + case 'template': + switch ($sNodeId) { + case 'layout': + case 'home': + $aPortalConf['properties']['templates'][$sNodeId] = $oSubNode->GetText(null); + break; + default: + throw new DOMFormatException( + 'Value "'.$sNodeId.'" is not handled for template[@id]', + null, null, $oSubNode + ); + break; + } + break; + } + } + + return $aPortalConf; +} + + /** + * @param array $aPortalConf + * @param $oPropertyNode + * + * @return array + */ + private function ParseAttachments(array $aPortalConf, $oPropertyNode) + { + 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) { + $aPortalConf['properties']['attachments'][$oSubNode->nodeName] = ($sValue === 'true') ? true : false; + } + break; + } + } + + return$aPortalConf; +} + + /** + * @param array $aPortalConf + * @param $oPropertyNode + * + * @return array + */ + private function ParseAllowedPortals(array $aPortalConf, $oPropertyNode) + { + 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) { + $aPortalConf['properties']['allowed_portals'][$oSubNode->nodeName] = ($sValue === 'self') ? 'self' : 'tab'; + } + break; + } + } + + return $aPortalConf; +} + + /** + * @param array $aPortalConf + * + * @return array + */ + private function appendLogoUri(array $aPortalConf) + { + $sLogoUri = $aPortalConf['properties']['logo']; + if (!preg_match('/^http/', $sLogoUri)) { + // We prefix it with the server base url + $sLogoUri = utils::GetAbsoluteUrlAppRoot().'env-'.utils::GetCurrentEnvironment().'/'.$sLogoUri; + } + $aPortalConf['properties']['logo'] = $sLogoUri; + + return $aPortalConf; + } + + private function getUiExtensions($container) + { + $aUIExtensions = array( + 'css_files' => array(), + 'css_inline' => null, + 'js_files' => array(), + 'js_inline' => null, + 'html' => array(), + ); + $aUIExtensionHooks = array( + \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) + { + // 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, + $aImportPaths); + + if(!in_array($sCSSFileCompiled, $aUIExtensions['css_files'])) + { + $aUIExtensions['css_files'][] = $sCSSFileCompiled; + } + } + + // Adding CSS inline + $sCSSInline = $oExtensionInstance->GetCSSInline($container); + if ($sCSSInline !== null) + { + $aUIExtensions['css_inline'] .= "\n\n".$sCSSInline; + } + + // Adding JS files + $aUIExtensions['js_files'] = array_merge($aUIExtensions['js_files'], + $oExtensionInstance->GetJSFiles($container)); + + // Adding JS inline + $sJSInline = $oExtensionInstance->GetJSInline($container); + if ($sJSInline !== null) + { + // Note: Semi-colon is to prevent previous script that would have omitted it. + $aUIExtensions['js_inline'] .= "\n\n;\n".$sJSInline; + } + + // Adding HTML for each hook + foreach ($aUIExtensionHooks as $sUIExtensionHook) + { + $sFunctionName = 'Get'.$sUIExtensionHook.'HTML'; + $sHTML = $oExtensionInstance->$sFunctionName($container); + if ($sHTML !== null) + { + if (!array_key_exists($sUIExtensionHook, $aUIExtensions['html'])) + { + $aUIExtensions['html'][$sUIExtensionHook] = ''; + } + $aUIExtensions['html'][$sUIExtensionHook] .= "\n\n".$sHTML; + } + } + } + + return $aUIExtensions; + } +} \ No newline at end of file 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 new file mode 100644 index 000000000..5618ba781 --- /dev/null +++ b/datamodels/2.x/itop-portal-base/portal/src/DependencyInjection/SilexCompatBootstrap/PortalXmlConfiguration/Forms.php @@ -0,0 +1,271 @@ +getModuleDesign()->GetNodes('/module_design/forms/form') as $oFormNode) + { + try + { + // Parsing form id + if ($oFormNode->getAttribute('id') === '') + { + throw new DOMFormatException('form tag must have an id attribute', null, null, $oFormNode); + } + + // Parsing form object class + if ($oFormNode->GetUniqueElement('class')->GetText() !== null) + { + // Parsing class + $sFormClass = $oFormNode->GetUniqueElement('class')->GetText(); + + // Parsing properties + $aFormProperties = array( + 'display_mode' => ApplicationHelper::FORM_DEFAULT_DISPLAY_MODE, + 'always_show_submit' => ApplicationHelper::FORM_DEFAULT_ALWAYS_SHOW_SUBMIT, + ); + if ($oFormNode->GetOptionalElement('properties') !== null) + { + foreach ($oFormNode->GetOptionalElement('properties')->childNodes as $oPropertyNode) + { + switch ($oPropertyNode->nodeName) + { + case 'display_mode': + $aFormProperties['display_mode'] = $oPropertyNode->GetText(ApplicationHelper::FORM_DEFAULT_DISPLAY_MODE); + break; + case 'always_show_submit': + $aFormProperties['always_show_submit'] = ($oPropertyNode->GetText('false') === 'true') ? true : false; + break; + } + } + } + + // Parsing availables 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) + { + if ($oModeNode->getAttribute('id') !== '') + { + $aModes[] = $oModeNode->getAttribute('id'); + } + else + { + throw new DOMFormatException('Mode tag must have an id attribute', null, null, + $oFormNode); + } + + // If apply_stimulus mode, checking if stimuli are defined + if ($oModeNode->getAttribute('id') === 'apply_stimulus') + { + $oStimuliNode = $oModeNode->GetOptionalElement('stimuli'); + // 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) + { + $sStimulusCode = $oStimulusNode->getAttribute('id'); + + // Removing default form is present (in case the default forms were parsed before the current one (from current or parent class)) + if (isset($aForms[$sFormClass]['apply_stimulus'][$sStimulusCode])) + { + unset($aForms[$sFormClass]['apply_stimulus'][$sStimulusCode]); + } + + $aFormStimuli[] = $oStimulusNode->getAttribute('id'); + } + } + } + } + } + else + { + // If no mode was specified, we set it all but stimuli as it would have no sense that every transition forms + // have as many fields displayed as a regular edit form for example. + $aModes = array('view', 'edit', 'create'); + } + + // Parsing fields + $aFields = array( + 'id' => $oFormNode->getAttribute('id'), + 'type' => null, + 'properties' => $aFormProperties, + 'fields' => null, + 'layout' => null + ); + // ... either enumerated fields ... + if ($oFormNode->GetOptionalElement('fields') !== null) + { + $aFields['type'] = 'custom_list'; + $aFields['fields'] = array(); + + foreach ($oFormNode->GetOptionalElement('fields')->GetNodes('field') as $oFieldNode) + { + $sFieldId = $oFieldNode->getAttribute('id'); + if ($sFieldId !== '') + { + $aField = array(); + // Parsing field options like read_only, hidden and mandatory + if ($oFieldNode->GetOptionalElement('read_only')) + { + $aField['readonly'] = ($oFieldNode->GetOptionalElement('read_only')->GetText('true') === 'true') ? true : false; + } + if ($oFieldNode->GetOptionalElement('mandatory')) + { + $aField['mandatory'] = ($oFieldNode->GetOptionalElement('mandatory')->GetText('true') === 'true') ? true : false; + } + if ($oFieldNode->GetOptionalElement('hidden')) + { + $aField['hidden'] = ($oFieldNode->GetOptionalElement('hidden')->GetText('true') === 'true') ? true : false; + } + + $aFields['fields'][$sFieldId] = $aField; + } + else + { + throw new DOMFormatException('Field tag must have an id attribute', null, null, + $oFormNode); + } + } + } + // ... or the default zlist + else + { + $aFields['type'] = 'zlist'; + $aFields['fields'] = 'details'; + } + + // Parsing presentation + 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 = preg_replace('/^.+\n/', '', $sXml); + $sXml = preg_replace('/\n.+$/', '', $sXml); + + $aFields['layout'] = array( + 'type' => (preg_match('/\{\{|\{\#|\{\%/', $sXml) === 1) ? 'twig' : 'xhtml', + 'content' => $sXml + ); + } + + // Adding form for each class / mode + foreach ($aModes as $sMode) + { + // Initializing current class if necessary + if (!isset($aForms[$sFormClass])) + { + $aForms[$sFormClass] = array(); + } + + if ($sMode === 'apply_stimulus') + { + // Iterating over current class and child classes to fill stimuli forms + foreach (MetaModel::EnumChildClasses($sFormClass, ENUM_CHILD_CLASSES_ALL) as $sChildClass) + { + // Initializing child class if necessary + if (!isset($aForms[$sChildClass][$sMode])) + { + $aForms[$sChildClass][$sMode] = array(); + } + + // If stimuli are implicitly defined (empty tag), we define all those that have not already been by other forms. + $aChildStimuli = $aFormStimuli; + if (empty($aChildStimuli)) + { + // Stimuli already declared + $aDeclaredStimuli = array(); + if (array_key_exists($sChildClass, $aForms) && array_key_exists('apply_stimulus', + $aForms[$sChildClass])) + { + $aDeclaredStimuli = array_keys($aForms[$sChildClass]['apply_stimulus']); + } + // All stimuli + $aDatamodelStimuli = array_keys(MetaModel::EnumStimuli($sChildClass)); + // Missing stimuli + $aChildStimuli = array_diff($aDatamodelStimuli, $aDeclaredStimuli); + } + + foreach ($aChildStimuli as $sFormStimulus) + { + // Setting form if not defined OR if it was defined by a parent (abstract) class + if (!isset($aForms[$sChildClass][$sMode][$sFormStimulus]) || !empty($aFormStimuli)) + { + $aForms[$sChildClass][$sMode][$sFormStimulus] = $aFields; + $aForms[$sChildClass][$sMode][$sFormStimulus]['id'] = 'apply_stimulus-'.$sChildClass.'-'.$sFormStimulus; + } + } + } + } + elseif (!isset($aForms[$sFormClass][$sMode])) + { + $aForms[$sFormClass][$sMode] = $aFields; + } + else + { + 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); + } + } + catch (\DOMFormatException $e) + { + throw new \Exception('Could not create from [id="'.$oFormNode->getAttribute('id').'"] from XML because of a DOM problem : '.$e->getMessage()); + } + catch (\Exception $e) + { + throw new \Exception('Could not create from from XML : '.$oFormNode->Dump().' '.$e->getMessage()); + } + } + + $aPortalConf = $container->getParameter('combodo.portal.instance.conf'); + $aPortalConf['forms'] = $aForms; + $container->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 new file mode 100644 index 000000000..893e056da --- /dev/null +++ b/datamodels/2.x/itop-portal-base/portal/src/DependencyInjection/SilexCompatBootstrap/PortalXmlConfiguration/Lists.php @@ -0,0 +1,86 @@ +getModuleDesign()->GetNodes('/module_design/classes/class') as $oClassNode) + { + $aClassLists = array(); + $sClassId = $oClassNode->getAttribute('id'); + if ($sClassId === null) + { + throw new DOMFormatException('Class tag must have an id attribute', null, null, $oClassNode); + } + + // - Each lists + foreach ($oClassNode->GetNodes('./lists/list') as $oListNode) + { + $aListItems = array(); + $sListId = $oListNode->getAttribute('id'); + if ($sListId === null) + { + throw new DOMFormatException('List tag of "'.$sClassId.'" class must have an id attribute', null, + null, $oListNode); + } + + // - Each items + foreach ($oListNode->GetNodes('./items/item') as $oItemNode) + { + $sItemId = $oItemNode->getAttribute('id'); + if ($sItemId === null) + { + throw new DOMFormatException('Item tag of "'.$sItemId.'" list must have an id attribute', null, + null, $oItemNode); + } + + $aItem = array( + 'att_code' => $sItemId, + 'rank' => $iDefaultItemRank + ); + + $oRankNode = $oItemNode->GetOptionalElement('rank'); + if ($oRankNode !== null) + { + $aItem['rank'] = $oRankNode->GetText($iDefaultItemRank); + } + + $aListItems[] = $aItem; + } + // - Sorting list items by rank + usort($aListItems, function ($a, $b) { + return $a['rank'] > $b['rank']; + }); + $aClassLists[$sListId] = $aListItems; + } + + // - Adding class only if it has at least one list + if (!empty($aClassLists)) + { + $aClassesLists[$sClassId] = $aClassLists; + } + } + $aPortalConf = $container->getParameter('combodo.portal.instance.conf'); + $aPortalConf['lists'] = $aClassLists; + $container->setParameter('combodo.portal.instance.conf', $aPortalConf); + } + + +} \ No newline at end of file diff --git a/datamodels/2.x/itop-portal-base/portal/src/EventListener/ApplicationContextSetUrlMakerClass.php b/datamodels/2.x/itop-portal-base/portal/src/EventListener/ApplicationContextSetUrlMakerClass.php new file mode 100644 index 000000000..58d991bff --- /dev/null +++ b/datamodels/2.x/itop-portal-base/portal/src/EventListener/ApplicationContextSetUrlMakerClass.php @@ -0,0 +1,56 @@ +aCombodoPortalInstanceConf = $oCombodoPortalInstanceConf->getVariable(); + } + + public function onKernelRequest(GetResponseEvent $oGetResponseEvent) + { + if ($this->aCombodoPortalInstanceConf['properties']['urlmaker_class'] !== null) + { + \ApplicationContext::SetUrlMakerClass($this->aCombodoPortalInstanceConf['properties']['urlmaker_class']); + } + } +} \ No newline at end of file diff --git a/datamodels/2.x/itop-portal-base/portal/src/EventListener/UserProvider.php b/datamodels/2.x/itop-portal-base/portal/src/EventListener/UserProvider.php new file mode 100644 index 000000000..321621794 --- /dev/null +++ b/datamodels/2.x/itop-portal-base/portal/src/EventListener/UserProvider.php @@ -0,0 +1,135 @@ +oModuleDesign = $oModuleDesign; + $this->oItopUser = $oItopUser; + $this->sCombodoPortalBaseAbsoluteUrl = $sCombodoPortalBaseAbsolutePath; + } + + /** + * @param \Symfony\Component\HttpKernel\Event\GetResponseEvent $oGetResponseEvent + * + * @throws \Exception + */ + public function onKernelRequest(GetResponseEvent $oGetResponseEvent) + { + // User pre-checks + // Note: At this point the Exception handler is not registered, so we can't use $oApp::abort() method, hence the die(). + // - Checking user rights and prompt if needed (401 HTTP code returned if XHR request) + $iExitMethod = ($oGetResponseEvent->getRequest()->isXmlHttpRequest()) ? LoginWebPage::EXIT_RETURN : LoginWebPage::EXIT_PROMPT; + $iLogonRes = LoginWebPage::DoLoginEx(PORTAL_ID, false, $iExitMethod); + if( ($iExitMethod === LoginWebPage::EXIT_RETURN) && ($iLogonRes != 0) ) + { + die(Dict::S('Portal:ErrorUserLoggedOut')); + } + // - User must be associated with a Contact + if (UserRights::GetContactId() == 0) + { + die(Dict::S('Portal:ErrorNoContactForThisUser')); + } + + // User + $oUser = UserRights::GetUserObject(); + if ($oUser === null) + { + throw new \Exception('Could not load connected user.'); + } + $this->oItopUser->setUser($oUser); + + // Contact + $sContactPhotoUrl = "{$this->sCombodoPortalBaseAbsoluteUrl}img/user-profile-default-256px.png"; + // - Checking if we can load the contact + try + { + $oContact = UserRights::GetContactObject(); + } + catch (Exception $e) + { + $oAllowedOrgSet = $oUser->Get('allowed_org_list'); + if ($oAllowedOrgSet->Count() > 0) + { + throw new Exception('Could not load contact related to connected user. (Tip: Make sure the contact\'s organization is among the user\'s allowed organizations)'); + } + else + { + throw new Exception('Could not load contact related to connected user.'); + } + } + // - Retrieving picture + if ($oContact) + { + if (MetaModel::IsValidAttCode(get_class($oContact), 'picture')) + { + $oImage = $oContact->Get('picture'); + if (is_object($oImage) && !$oImage->IsEmpty()) + { + $sContactPhotoUrl = $oImage->GetDownloadURL(get_class($oContact), $oContact->GetKey(), 'picture'); + } + else + { + $sContactPhotoUrl = MetaModel::GetAttributeDef(get_class($oContact), 'picture')->Get('default_image'); + } + } + } + $this->oItopUser->setContactPhotoUrl($sContactPhotoUrl); + } + + +} \ No newline at end of file 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 new file mode 100644 index 000000000..0ca0b8493 --- /dev/null +++ b/datamodels/2.x/itop-portal-base/portal/src/Helper/ApplicationHelper.php @@ -0,0 +1,1432 @@ + + */ +class ApplicationHelper +{ + const FORM_ENUM_DISPLAY_MODE_COSY = 'cosy'; + const FORM_ENUM_DISPLAY_MODE_COMPACT = 'compact'; + const FORM_DEFAULT_DISPLAY_MODE = self::FORM_ENUM_DISPLAY_MODE_COSY; + const FORM_DEFAULT_ALWAYS_SHOW_SUBMIT = false; + + /** + * Loads classes from the base portal + * + * @deprecated Since 2.7.0 + * + * @param string $sScannedDir Directory to load the files from + * @param string $sFilePattern Pattern of files to load + * @param string $sType Type of files to load, used only in the Exception message, can be anything + * + * @throws \Exception + */ + public static function LoadClasses($sScannedDir, $sFilePattern, $sType) + { + @trigger_error( + sprintf( + 'Usage of legacy LoadClasses is deprecated. You should rely on autoloading (and therefore follow PSR4).', + __FILE__ + ), + E_USER_DEPRECATED + ); + + // Loading classes from base portal + foreach (scandir($sScannedDir) as $sFile) + { + if (strpos($sFile, $sFilePattern) !== false && file_exists($sFilepath = $sScannedDir.'/'.$sFile)) + { + try + { + require_once $sFilepath; + } + catch (Exception $e) + { + throw new Exception('Error while trying to load '.$sType.' '.$sFile); + } + } + } + } + + /** + * Loads controllers from the base portal + * + * @param string $sScannedDir Directory to load the controllers from + * + * @throws \Exception + */ +// public static function LoadControllers($sScannedDir = null) +// { +// if ($sScannedDir === null) +// { +// $sScannedDir = __DIR__.'/../controllers'; +// } +// +// // Loading controllers from base portal (those from modules have already been loaded by module.xxx.php files) +// self::LoadClasses($sScannedDir, 'controller.class.inc.php', 'controller'); +// } + + /** + * Loads routers from the base portal + * + * @param string $sScannedDir Directory to load the routers from + * + * @throws \Exception + */ +// public static function LoadRouters($sScannedDir = null) +// { +// if ($sScannedDir === null) +// { +// $sScannedDir = __DIR__.'/../routers'; +// } +// +// // Loading routers from base portal (those from modules have already been loaded by module.xxx.php files) +// self::LoadClasses($sScannedDir, 'router.class.inc.php', 'router'); +// } + + /** + * Loads bricks from the base portal + * + * @param string $sScannedDir Directory to load the bricks from + * + * @throws \Exception + */ +// public static function LoadBricks($sScannedDir = null) +// { +// if ($sScannedDir === null) +// { +// $sScannedDir = __DIR__.'/../Brick'; +// } +// +// // Loading bricks from base portal (those from modules have already been loaded by module.xxx.php files) +// self::LoadClasses($sScannedDir, 'brick.php', 'brick'); +// } + + /** + * Loads form managers from the base portal + * + * @param string $sScannedDir Directory to load the managers from + * + * @throws \Exception + */ +// public static function LoadFormManagers($sScannedDir = null) +// { +// if ($sScannedDir === null) +// { +// $sScannedDir = __DIR__.'/../forms'; +// } +// +// // Loading form managers from base portal (those from modules have already been loaded by module.xxx.php files) +// self::LoadClasses($sScannedDir, 'formmanager.class.inc.php', 'brick'); +// } + + /** + * Registers routes in the Silex Application from all declared Router classes + * + * @param \Silex\Application $oApp + * + * @throws \Exception + */ +// public static function RegisterRoutes(Application $oApp) +// { +// $aAllRoutes = array(); +// +// foreach (get_declared_classes() as $sPHPClass) +// { +// if (is_subclass_of($sPHPClass, 'Combodo\\iTop\\Portal\\Router\\AbstractRouter')) +// { +// try +// { +// // Registering to Silex Application +// $sPHPClass::RegisterAllRoutes($oApp); +// +// // Registering them together so we can access them from everywhere +// foreach ($sPHPClass::GetRoutes() as $aRoute) +// { +// $aAllRoutes[$aRoute['bind']] = $aRoute; +// } +// } +// catch (Exception $e) +// { +// throw new Exception('Error while trying to register routes'); +// } +// } +// } +// +// $oApp['combodo.portal.instance.routes'] = $aAllRoutes; +// } + + /** + * Returns all registered routes for the current portal instance + * + * @param \Silex\Application $oApp + * @param boolean $bNamesOnly If set to true, function will return only the routes' names, not the objects + * + * @return array + */ +// public static function GetRoutes(Application $oApp, $bNamesOnly = false) +// { +// return ($bNamesOnly) ? array_keys($oApp['combodo.portal.instance.routes']) : $oApp['combodo.portal.instance.routes']; +// } + + /** + * Registers an exception handler that will intercept controllers exceptions and display them in a nice template. + * Note : It is only active when $oApp['debug'] is false + * + * @param Application $oApp + */ + public static function RegisterExceptionHandler(Application $oApp) + { + // Intercepting fatal errors and exceptions + ErrorHandler::register(); + ExceptionHandler::register(($oApp['debug'] === true)); + + // Intercepting manually aborted request + if (1 || !$oApp['debug']) + { + $oApp->error(function (Exception $oException /*, Request $oRequest*/) use ($oApp) { + $iErrorCode = ($oException instanceof HttpException) ? $oException->getStatusCode() : 500; + + $aData = array( + 'exception' => $oException, + 'code' => $iErrorCode, + 'error_title' => '', + 'error_message' => $oException->getMessage() + ); + + switch ($iErrorCode) + { + case 404: + $aData['error_title'] = Dict::S('Error:HTTP:404'); + break; + default: + $aData['error_title'] = Dict::S('Error:HTTP:500'); + break; + } + + IssueLog::Error($aData['error_title'].' : '.$aData['error_message']); + + if ($oApp['request_stack']->getCurrentRequest()->isXmlHttpRequest()) + { + $oResponse = $oApp->json($aData, $iErrorCode); + } + else + { + // Preparing debug trace + $aSteps = array(); + foreach ($oException->getTrace() as $aStep) + { + // - Default file name + if (!isset($aStep['file'])) + { + $aStep['file'] = ''; + } + $aFileParts = explode('\\', $aStep['file']); + // - Default line number + if (!isset($aStep['line'])) + { + $aStep['line'] = 'unknown'; + } + // - Default class name + if (isset($aStep['class']) && isset($aStep['function']) && isset($aStep['type'])) + { + $aClassParts = explode('\\', $aStep['class']); + $sClassName = $aClassParts[count($aClassParts) - 1]; + $sClassFQ = $aStep['class']; + + $aArgsAsString = array(); + foreach ($aStep['args'] as $arg) + { + if (is_array($arg)) + { + $aArgsAsString[] = 'array(...)'; + } + elseif (is_object($arg)) + { + $aArgsAsString[] = 'object('.get_class($arg).')'; + } + else + { + $aArgsAsString[] = $arg; + } + } + + $sFunctionCall = $sClassName.$aStep['type'].$aStep['function'].'('.implode(', ', + $aArgsAsString).')'; + } + else + { + $sClassName = null; + $sClassFQ = null; + $sFunctionCall = null; + } + + $aSteps[] = array( + 'file_fq' => $aStep['file'], + 'file_name' => $aFileParts[count($aFileParts) - 1], + 'line' => $aStep['line'], + 'class_name' => $sClassName, + 'class_fq' => $sClassFQ, + 'function_call' => $sFunctionCall, + ); + } + + $aData['debug_trace_steps'] = $aSteps; + + $oResponse = $oApp['twig']->render('itop-portal-base/portal/templates/errors/layout.html.twig', + $aData); + } + + return $oResponse; + }); + } + } + + /** + * Loads the portal instance configuration from its module design into the Silex application + * + * @param \Silex\Application $oApp + * + * @throws Exception + */ +// public static function LoadPortalConfiguration(Application $oApp) +// { +// try +// { +// // Loading file +// if (!defined('PORTAL_ID')) +// { +// throw new Exception('Cannot load module design, Portal ID is not defined'); +// } +// $oDesign = new ModuleDesign(PORTAL_ID); +// +// // Parsing file +// // - Default values +// $aPortalConf = array( +// 'properties' => array( +// 'id' => PORTAL_ID, +// 'name' => 'Page:DefaultTitle', +// 'logo' => (file_exists(MODULESROOT.'branding/portal-logo.png')) ? utils::GetAbsoluteUrlModulesRoot().'branding/portal-logo.png' : '../images/logo-itop-dark-bg.svg', +// 'themes' => array( +// 'bootstrap' => 'itop-portal-base/portal/web/css/bootstrap-theme-combodo.scss', +// 'portal' => 'itop-portal-base/portal/web/css/portal.scss', +// 'others' => array(), +// ), +// 'templates' => array( +// 'layout' => 'itop-portal-base/portal/templates/layout.html.twig', +// 'home' => 'itop-portal-base/portal/templates/home/layout.html.twig' +// ), +// 'urlmaker_class' => null, +// 'triggers_query' => null, +// 'attachments' => array( +// 'allow_delete' => true +// ), +// 'allowed_portals' => array( +// 'opening_mode' => null, +// ), +// ), +// 'portals' => array(), +// 'forms' => array(), +// 'ui_extensions' => array( +// 'css_files' => array(), +// 'css_inline' => null, +// 'js_files' => array(), +// 'js_inline' => null, +// 'html' => array(), +// ), +// 'bricks' => array(), +// 'bricks_total_width' => 0, +// ); +// // - Global portal properties +// foreach ($oDesign->GetNodes('/module_design/properties/*') as $oPropertyNode) +// { +// switch ($oPropertyNode->nodeName) +// { +// case 'name': +// case 'urlmaker_class': +// case 'triggers_query': +// $aPortalConf['properties'][$oPropertyNode->nodeName] = $oPropertyNode->GetText($aPortalConf['properties'][$oPropertyNode->nodeName]); +// break; +// case 'logo': +// $aPortalConf['properties'][$oPropertyNode->nodeName] = $oPropertyNode->GetText($aPortalConf['properties'][$oPropertyNode->nodeName]); +// break; +// case 'themes': +// case 'templates': +// 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', +// null, null, $oSubNode); +// } +// +// $sNodeId = $oSubNode->getAttribute('id'); +// switch ($oSubNode->nodeName) +// { +// case 'theme': +// switch ($sNodeId) +// { +// case 'bootstrap': +// case 'portal': +// case 'custom': +// $aPortalConf['properties']['themes'][$sNodeId] = $oSubNode->GetText(null); +// break; +// default: +// $aPortalConf['properties']['themes']['others'][] = $oSubNode->GetText(null); +// break; +// } +// break; +// case 'template': +// switch ($sNodeId) +// { +// case 'layout': +// case 'home': +// $aPortalConf['properties']['templates'][$sNodeId] = $oSubNode->GetText(null); +// break; +// default: +// throw new DOMFormatException('Value "'.$sNodeId.'" is not handled for template[@id]', +// null, null, $oSubNode); +// break; +// } +// break; +// } +// } +// break; +// case 'attachments': +// 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) +// { +// $aPortalConf['properties']['attachments'][$oSubNode->nodeName] = ($sValue === 'true') ? true : false; +// } +// break; +// } +// } +// break; +// case 'allowed_portals': +// 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) +// { +// $aPortalConf['properties']['allowed_portals'][$oSubNode->nodeName] = ($sValue === 'self') ? 'self' : 'tab'; +// } +// break; +// } +// } +// break; +// } +// } +// // - Rectifying portal logo url +// $sLogoUri = $aPortalConf['properties']['logo']; +// if (!preg_match('/^http/', $sLogoUri)) +// { +// // We prefix it with the server base url +// $sLogoUri = utils::GetAbsoluteUrlAppRoot().'env-'.utils::GetCurrentEnvironment().'/'.$sLogoUri; +// } +// $aPortalConf['properties']['logo'] = $sLogoUri; +// // - User allowed portals +// $aPortalConf['portals'] = UserRights::GetAllowedPortals(); +// // - Bricks +// $aPortalConf = static::LoadBricksConfiguration($oApp, $oDesign) + $aPortalConf; +// // - Forms +// $aPortalConf['forms'] = static::LoadFormsConfiguration($oApp, $oDesign); +// // - Scopes +// static::LoadScopesConfiguration($oApp, $oDesign); +// // - Lifecycle +// static::LoadLifecycleConfiguration($oApp, $oDesign); +// // - Presentation lists +// $aPortalConf['lists'] = static::LoadListsConfiguration($oApp, $oDesign); +// // - UI extensions +// $aPortalConf['ui_extensions'] = static::LoadUIExtensions($oApp); +// // - Action rules +// static::LoadActionRulesConfiguration($oApp, $oDesign); +// // - Setting UrlMakerClass +// if ($aPortalConf['properties']['urlmaker_class'] !== null) +// { +// ApplicationContext::SetUrlMakerClass($aPortalConf['properties']['urlmaker_class']); +// } +// // - Generating CSS files +// $aImportPaths = array($oApp['combodo.portal.base.absolute_path'].'css/'); +// foreach ($aPortalConf['properties']['themes'] as $key => $value) +// { +// if (!is_array($value)) +// { +// $aPortalConf['properties']['themes'][$key] = $oApp['combodo.absolute_url'].utils::GetCSSFromSASS('env-'.utils::GetCurrentEnvironment().'/'.$value, +// $aImportPaths); +// } +// else +// { +// $aValues = array(); +// foreach ($value as $sSubvalue) +// { +// $aValues[] = $oApp['combodo.absolute_url'].utils::GetCSSFromSASS('env-'.utils::GetCurrentEnvironment().'/'.$sSubvalue, +// $aImportPaths); +// } +// $aPortalConf['properties']['themes'][$key] = $aValues; +// } +// } +// +// $oApp['combodo.portal.instance.conf'] = $aPortalConf; +// } +// catch (Exception $e) +// { +// throw new Exception('Error while parsing portal configuration file : '.$e->getMessage()); +// } +// } + + /** + * Loads the current user and stores it in the Silex application so we can use it wherever in the application + * + * @param \Silex\Application $oApp + * + * @throws \Exception + */ +// public static function LoadCurrentUser(Application $oApp) +// { +// // User +// $oUser = UserRights::GetUserObject(); +// if ($oUser === null) +// { +// throw new Exception('Could not load connected user.'); +// } +// +// $oApp['combodo.current_user'] = $oUser; +// +// // Contact +// $sContactPhotoUrl = $oApp['combodo.portal.base.absolute_url'].'img/user-profile-default-256px.png'; +// // - Checking if we can load the contact +// try +// { +// $oContact = UserRights::GetContactObject(); +// } +// catch (Exception $e) +// { +// $oAllowedOrgSet = $oUser->Get('allowed_org_list'); +// if ($oAllowedOrgSet->Count() > 0) +// { +// throw new Exception('Could not load contact related to connected user. (Tip: Make sure the contact\'s organization is among the user\'s allowed organizations)'); +// } +// else +// { +// throw new Exception('Could not load contact related to connected user.'); +// } +// } +// // - Retrieving picture +// if ($oContact) +// { +// if (MetaModel::IsValidAttCode(get_class($oContact), 'picture')) +// { +// $oImage = $oContact->Get('picture'); +// if (is_object($oImage) && !$oImage->IsEmpty()) +// { +// $sContactPhotoUrl = $oImage->GetDownloadURL(get_class($oContact), $oContact->GetKey(), 'picture'); +// } +// else +// { +// $sContactPhotoUrl = MetaModel::GetAttributeDef(get_class($oContact), +// 'picture')->Get('default_image'); +// } +// } +// } +// $oApp['combodo.current_contact.photo_url'] = $sContactPhotoUrl; +// } + + /** + * Loads the brick's security from the OQL queries to profiles arrays + * + * @param \Combodo\iTop\Portal\Brick\AbstractBrick $oBrick + * + * @throws \Exception + */ + public static function LoadBrickSecurity(AbstractBrick $oBrick) + { + try + { + // Allowed profiles + if ($oBrick->GetAllowedProfilesOql() !== null && $oBrick->GetAllowedProfilesOql() !== '') + { + $oSearch = DBObjectSearch::FromOQL($oBrick->GetAllowedProfilesOql()); + $oSet = new DBObjectSet($oSearch); + while ($oProfile = $oSet->Fetch()) + { + $oBrick->AddAllowedProfile($oProfile->Get('name')); + } + } + + // Denied profiles + if ($oBrick->GetDeniedProfilesOql() !== null && $oBrick->GetDeniedProfilesOql() !== '') + { + $oSearch = DBObjectSearch::FromOQL($oBrick->GetDeniedProfilesOql()); + $oSet = new DBObjectSet($oSearch); + while ($oProfile = $oSet->Fetch()) + { + $oBrick->AddDeniedProfile($oProfile->Get('name')); + } + } + } + catch (Exception $e) + { + throw new Exception('Error while loading security from '.$oBrick->GetId().' brick'); + } + } + + /** + * Finds an AbstractBrick loaded in the $oApp instance configuration from its ID. + * + * @param \Silex\Application $oApp + * @param string $sBrickId + * + * @return \Combodo\iTop\Portal\Brick\PortalBrick + * + * @throws \Exception + */ +// public static function GetLoadedBrickFromId(Application $oApp, $sBrickId) +// { +// foreach ($oApp['combodo.portal.instance.conf']['bricks'] as $oBrick) +// { +// if ($oBrick->GetId() === $sBrickId) +// { +// return $oBrick; +// } +// } +// +// throw new Exception('Brick with id = "'.$sBrickId.'" was not found among loaded bricks.'); +// } + + /** + * Return the form properties for the $sClassname in $sMode. + * + * If not found, tries to find one from the closest parent class. + * Else returns a default form based on zlist 'details' + * + * @param Application $oApp + * @param string $sClass Object class to find a form for + * @param string $sMode Form mode to find (view|edit|create) + * + * @return array + * + * @throws \CoreException + */ + public static function GetLoadedFormFromClass($aForms, $sClass, $sMode) + { + + // We try to find the form for that class + if (isset($aForms[$sClass]) && isset($aForms[$sClass][$sMode])) + { + $aForm = $aForms[$sClass][$sMode]; + } + // If not found, we try find one from the closest parent class + else + { + $bFound = false; + foreach (MetaModel::EnumParentClasses($sClass, ENUM_PARENT_CLASSES_EXCLUDELEAF, false) as $sParentClass) + { + if (isset($aForms[$sParentClass]) && isset($aForms[$sParentClass][$sMode])) + { + $aForm = $aForms[$sParentClass][$sMode]; + $bFound = true; + break; + } + } + + // If we have still not found one, we return a default form + if (!$bFound) + { + $aForm = static::GenerateDefaultFormForClass($sClass); + } + } + + return $aForm; + } + + /** + * Return the attribute list for the $sClassname in $sList. + * + * If not found, tries to find one from the closest parent class. + * Else returns a default attribute list based on zlist 'list' + * + * @param array $combodoPortalInstanceConf + * @param string $sClass Object class to find a list for + * @param string $sList List name to find + * + * @return array Array of attribute codes + * + * @throws \CoreException + */ + public static function GetLoadedListFromClass($combodoPortalInstanceConf, $sClass, $sList = 'default') + { + $aLists = $combodoPortalInstanceConf['lists']; + $aList = null; + $aAttCodes = array(); + + // We try to find the list for that class + if (isset($aLists[$sClass]) && isset($aLists[$sClass][$sList])) + { + $aList = $aLists[$sClass][$sList]; + } + // Else we try to found the default list for that class + elseif (isset($aLists[$sClass]) && isset($aLists[$sClass]['default'])) + { + $aList = $aLists[$sClass]['default']; + } + // If not found, we try find one from the closest parent class + else + { + foreach (MetaModel::EnumParentClasses($sClass) as $sParentClass) + { + // Trying to find the right list + if (isset($aLists[$sParentClass]) && isset($aLists[$sParentClass][$sList])) + { + $aList = $aLists[$sParentClass][$sList]; + break; + } + // Or the default list + elseif (isset($aLists[$sParentClass]) && isset($aLists[$sParentClass]['default'])) + { + $aList = $aLists[$sParentClass]['default']; + break; + } + } + } + + // If found, we flatten the list to keep only the attribute codes (not the rank) + if ($aList !== null) + { + foreach ($aList as $aItem) + { + $aAttCodes[] = $aItem['att_code']; + } + } + else + { + $aAttCodes = MetaModel::FlattenZList(MetaModel::GetZListItems($sClass, 'list')); + } + + return $aAttCodes; + } + + /** + * Loads the bricks configuration from the module design XML and returns it as an hash array containing : + * - 'brick' => array of PortalBrick objects + * - 'bricks_total_width' => an integer used to create the home page grid + * + * @param \Silex\Application $oApp + * @param \ModuleDesign $oDesign + * + * @return array + * + * @throws \Exception + */ +// protected static function LoadBricksConfiguration(Application $oApp, ModuleDesign $oDesign) +// { +// $aPortalConf = array( +// 'bricks' => array(), +// 'bricks_total_width' => 0, +// 'bricks_home_count' => 0, +// 'bricks_navigation_menu_count' => 0 +// ); +// +// foreach ($oDesign->GetNodes('/module_design/bricks/brick') as $oBrickNode) +// { +// $sBrickClass = $oBrickNode->getAttribute('xsi:type'); +// try +// { +// if (class_exists($sBrickClass)) +// { +// /** @var \Combodo\iTop\Portal\Brick\PortalBrick $oBrick */ +// $oBrick = new $sBrickClass(); +// $oBrick->LoadFromXml($oBrickNode); +// static::LoadBrickSecurity($oBrick); +// +// // Checking brick security +// if ($oBrick->GetActive() && $oBrick->IsGrantedForProfiles(UserRights::ListProfiles())) +// { +// $aPortalConf['bricks'][] = $oBrick; +// $aPortalConf['bricks_total_width'] += $oBrick->GetWidth(); +// if ($oBrick->GetVisibleHome()) +// { +// $aPortalConf['bricks_home_count']++; +// } +// if ($oBrick->GetVisibleNavigationMenu()) +// { +// $aPortalConf['bricks_navigation_menu_count']++; +// } +// } +// } +// else +// { +// throw new DOMFormatException('Unknown brick class "'.$sBrickClass.'" from xsi:type attribute', null, +// null, $oBrickNode); +// } +// } +// catch (DOMFormatException $e) +// { +// throw new Exception('Could not create brick ('.$sBrickClass.') from XML because of a DOM problem : '.$e->getMessage()); +// } +// catch (Exception $e) +// { +// throw new Exception('Could not create brick ('.$sBrickClass.') from XML : '.$oBrickNode->Dump().' '.$e->getMessage()); +// } +// } +// // - Sorting bricks by rank +// $aPortalConf['bricks_ordering'] = array(); +// // - Home +// $aPortalConf['bricks_ordering']['home'] = $aPortalConf['bricks']; +// usort($aPortalConf['bricks_ordering']['home'], function (PortalBrick $a, PortalBrick $b) { +// return $a->GetRankHome() > $b->GetRankHome(); +// }); +// // - Navigation menu +// $aPortalConf['bricks_ordering']['navigation_menu'] = $aPortalConf['bricks']; +// usort($aPortalConf['bricks_ordering']['navigation_menu'], function (PortalBrick $a, PortalBrick $b) { +// return $a->GetRankNavigationMenu() > $b->GetRankNavigationMenu(); +// }); +// +// return $aPortalConf; +// } + + /** + * Loads the forms configuration from the module design XML and returns it as an array containing : + * - => array( + * 'view'|'edit'|'create' => array( + * 'fields_type' => 'custom_list'|'twig'|'zlist', + * 'fields' => + * ), + * ... + * ), + * ... + * + * @param \Silex\Application $oApp + * @param \ModuleDesign $oDesign + * + * @return array + * + * @throws \Exception + */ +// protected static function LoadFormsConfiguration(Application $oApp, ModuleDesign $oDesign) +// { +// $aForms = array(); +// +// foreach ($oDesign->GetNodes('/module_design/forms/form') as $oFormNode) +// { +// try +// { +// // Parsing form id +// if ($oFormNode->getAttribute('id') === '') +// { +// throw new DOMFormatException('form tag must have an id attribute', null, null, $oFormNode); +// } +// +// // Parsing form object class +// if ($oFormNode->GetUniqueElement('class')->GetText() !== null) +// { +// // Parsing class +// $sFormClass = $oFormNode->GetUniqueElement('class')->GetText(); +// +// // Parsing properties +// $aFormProperties = array( +// 'display_mode' => static::FORM_DEFAULT_DISPLAY_MODE, +// 'always_show_submit' => static::FORM_DEFAULT_ALWAYS_SHOW_SUBMIT, +// ); +// if ($oFormNode->GetOptionalElement('properties') !== null) +// { +// foreach ($oFormNode->GetOptionalElement('properties')->childNodes as $oPropertyNode) +// { +// switch ($oPropertyNode->nodeName) +// { +// case 'display_mode': +// $aFormProperties['display_mode'] = $oPropertyNode->GetText(static::FORM_DEFAULT_DISPLAY_MODE); +// break; +// case 'always_show_submit': +// $aFormProperties['always_show_submit'] = ($oPropertyNode->GetText('false') === 'true') ? true : false; +// break; +// } +// } +// } +// +// // Parsing availables 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) +// { +// if ($oModeNode->getAttribute('id') !== '') +// { +// $aModes[] = $oModeNode->getAttribute('id'); +// } +// else +// { +// throw new DOMFormatException('Mode tag must have an id attribute', null, null, +// $oFormNode); +// } +// +// // If apply_stimulus mode, checking if stimuli are defined +// if ($oModeNode->getAttribute('id') === 'apply_stimulus') +// { +// $oStimuliNode = $oModeNode->GetOptionalElement('stimuli'); +// // 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) +// { +// $sStimulusCode = $oStimulusNode->getAttribute('id'); +// +// // Removing default form is present (in case the default forms were parsed before the current one (from current or parent class)) +// if (isset($aForms[$sFormClass]['apply_stimulus'][$sStimulusCode])) +// { +// unset($aForms[$sFormClass]['apply_stimulus'][$sStimulusCode]); +// } +// +// $aFormStimuli[] = $oStimulusNode->getAttribute('id'); +// } +// } +// } +// } +// } +// else +// { +// // If no mode was specified, we set it all but stimuli as it would have no sense that every transition forms +// // have as many fields displayed as a regular edit form for example. +// $aModes = array('view', 'edit', 'create'); +// } +// +// // Parsing fields +// $aFields = array( +// 'id' => $oFormNode->getAttribute('id'), +// 'type' => null, +// 'properties' => $aFormProperties, +// 'fields' => null, +// 'layout' => null +// ); +// // ... either enumerated fields ... +// if ($oFormNode->GetOptionalElement('fields') !== null) +// { +// $aFields['type'] = 'custom_list'; +// $aFields['fields'] = array(); +// +// foreach ($oFormNode->GetOptionalElement('fields')->GetNodes('field') as $oFieldNode) +// { +// $sFieldId = $oFieldNode->getAttribute('id'); +// if ($sFieldId !== '') +// { +// $aField = array(); +// // Parsing field options like read_only, hidden and mandatory +// if ($oFieldNode->GetOptionalElement('read_only')) +// { +// $aField['readonly'] = ($oFieldNode->GetOptionalElement('read_only')->GetText('true') === 'true') ? true : false; +// } +// if ($oFieldNode->GetOptionalElement('mandatory')) +// { +// $aField['mandatory'] = ($oFieldNode->GetOptionalElement('mandatory')->GetText('true') === 'true') ? true : false; +// } +// if ($oFieldNode->GetOptionalElement('hidden')) +// { +// $aField['hidden'] = ($oFieldNode->GetOptionalElement('hidden')->GetText('true') === 'true') ? true : false; +// } +// +// $aFields['fields'][$sFieldId] = $aField; +// } +// else +// { +// throw new DOMFormatException('Field tag must have an id attribute', null, null, +// $oFormNode); +// } +// } +// } +// // ... or the default zlist +// else +// { +// $aFields['type'] = 'zlist'; +// $aFields['fields'] = 'details'; +// } +// +// // Parsing presentation +// if ($oFormNode->GetOptionalElement('twig') !== null) +// { +// // Extracting the twig template and removing the first and last lines (twig tags) +// $sXml = $oDesign->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 +// ); +// } +// +// // Adding form for each class / mode +// foreach ($aModes as $sMode) +// { +// // Initializing current class if necessary +// if (!isset($aForms[$sFormClass])) +// { +// $aForms[$sFormClass] = array(); +// } +// +// if ($sMode === 'apply_stimulus') +// { +// // Iterating over current class and child classes to fill stimuli forms +// foreach (MetaModel::EnumChildClasses($sFormClass, ENUM_CHILD_CLASSES_ALL) as $sChildClass) +// { +// // Initializing child class if necessary +// if (!isset($aForms[$sChildClass][$sMode])) +// { +// $aForms[$sChildClass][$sMode] = array(); +// } +// +// // If stimuli are implicitly defined (empty tag), we define all those that have not already been by other forms. +// $aChildStimuli = $aFormStimuli; +// if (empty($aChildStimuli)) +// { +// // Stimuli already declared +// $aDeclaredStimuli = array(); +// if (array_key_exists($sChildClass, $aForms) && array_key_exists('apply_stimulus', +// $aForms[$sChildClass])) +// { +// $aDeclaredStimuli = array_keys($aForms[$sChildClass]['apply_stimulus']); +// } +// // All stimuli +// $aDatamodelStimuli = array_keys(MetaModel::EnumStimuli($sChildClass)); +// // Missing stimuli +// $aChildStimuli = array_diff($aDatamodelStimuli, $aDeclaredStimuli); +// } +// +// foreach ($aChildStimuli as $sFormStimulus) +// { +// // Setting form if not defined OR if it was defined by a parent (abstract) class +// if (!isset($aForms[$sChildClass][$sMode][$sFormStimulus]) || !empty($aFormStimuli)) +// { +// $aForms[$sChildClass][$sMode][$sFormStimulus] = $aFields; +// $aForms[$sChildClass][$sMode][$sFormStimulus]['id'] = 'apply_stimulus-'.$sChildClass.'-'.$sFormStimulus; +// } +// } +// } +// } +// elseif (!isset($aForms[$sFormClass][$sMode])) +// { +// $aForms[$sFormClass][$sMode] = $aFields; +// } +// else +// { +// 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); +// } +// } +// catch (DOMFormatException $e) +// { +// throw new Exception('Could not create from [id="'.$oFormNode->getAttribute('id').'"] from XML because of a DOM problem : '.$e->getMessage()); +// } +// catch (Exception $e) +// { +// throw new Exception('Could not create from from XML : '.$oFormNode->Dump().' '.$e->getMessage()); +// } +// } +// +// return $aForms; +// } + + /** + * Loads the scopes configuration from the module design XML + * + * @param \Silex\Application $oApp + * @param ModuleDesign $oDesign + */ +// public static function LoadScopesConfiguration(Application $oApp, ModuleDesign $oDesign) +// { +// $oApp['scope_validator']->Init($oDesign->GetNodes('/module_design/classes/class')); +// } + + /** + * Loads the lifecycle configuration from the module design XML + * + * @param \Silex\Application $oApp + * @param ModuleDesign $oDesign + */ +// protected static function LoadLifecycleConfiguration(Application $oApp, ModuleDesign $oDesign) +// { +// $oApp['lifecycle_validator']->Init($oDesign->GetNodes('/module_design/classes/class')); +// } + + /** + * Loads the context helper from the module design XML + * + * @param \Silex\Application $oApp + * @param ModuleDesign $oDesign + */ +// protected static function LoadActionRulesConfiguration(Application $oApp, ModuleDesign $oDesign) +// { +// $oApp['context_manipulator']->Init($oDesign->GetNodes('/module_design/action_rules/action_rule')); +// } + + /** + * Loads the classes lists from the module design XML. They are mainly used when searching an external key but + * could be used more extensively later + * + * @param \Silex\Application $oApp + * @param ModuleDesign $oDesign + * + * @return array + * @throws \DOMFormatException + */ +// protected static function LoadListsConfiguration(Application $oApp, ModuleDesign $oDesign) +// { +// $iDefaultItemRank = 0; +// $aClassesLists = array(); +// +// // Parsing XML file +// // - Each classes +// foreach ($oDesign->GetNodes('/module_design/classes/class') as $oClassNode) +// { +// $aClassLists = array(); +// $sClassId = $oClassNode->getAttribute('id'); +// if ($sClassId === null) +// { +// throw new DOMFormatException('Class tag must have an id attribute', null, null, $oClassNode); +// } +// +// // - Each lists +// foreach ($oClassNode->GetNodes('./lists/list') as $oListNode) +// { +// $aListItems = array(); +// $sListId = $oListNode->getAttribute('id'); +// if ($sListId === null) +// { +// throw new DOMFormatException('List tag of "'.$sClassId.'" class must have an id attribute', null, +// null, $oListNode); +// } +// +// // - Each items +// foreach ($oListNode->GetNodes('./items/item') as $oItemNode) +// { +// $sItemId = $oItemNode->getAttribute('id'); +// if ($sItemId === null) +// { +// throw new DOMFormatException('Item tag of "'.$sItemId.'" list must have an id attribute', null, +// null, $oItemNode); +// } +// +// $aItem = array( +// 'att_code' => $sItemId, +// 'rank' => $iDefaultItemRank +// ); +// +// $oRankNode = $oItemNode->GetOptionalElement('rank'); +// if ($oRankNode !== null) +// { +// $aItem['rank'] = $oRankNode->GetText($iDefaultItemRank); +// } +// +// $aListItems[] = $aItem; +// } +// // - Sorting list items by rank +// usort($aListItems, function ($a, $b) { +// return $a['rank'] > $b['rank']; +// }); +// $aClassLists[$sListId] = $aListItems; +// } +// +// // - Adding class only if it has at least one list +// if (!empty($aClassLists)) +// { +// $aClassesLists[$sClassId] = $aClassLists; +// } +// } +// +// return $aClassesLists; +// } + + /** + * Loads portal UI extensions + * + * @param \Silex\Application $oApp + * + * @return array + */ +// protected static function LoadUIExtensions(Application $oApp) +// { +// $aUIExtensions = array( +// 'css_files' => array(), +// 'css_inline' => null, +// 'js_files' => array(), +// 'js_inline' => null, +// 'html' => array(), +// ); +// $aUIExtensionHooks = array( +// 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($oApp['combodo.portal.base.absolute_path'].'css/'); +// foreach($oExtensionInstance->GetCSSFiles($oApp) 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 = $oApp['combodo.absolute_url'].utils::GetCSSFromSASS($sCSSFilePath, +// $aImportPaths); +// +// if(!in_array($sCSSFileCompiled, $aUIExtensions['css_files'])) +// { +// $aUIExtensions['css_files'][] = $sCSSFileCompiled; +// } +// } +// +// // Adding CSS inline +// $sCSSInline = $oExtensionInstance->GetCSSInline($oApp); +// if ($sCSSInline !== null) +// { +// $aUIExtensions['css_inline'] .= "\n\n".$sCSSInline; +// } +// +// // Adding JS files +// $aUIExtensions['js_files'] = array_merge($aUIExtensions['js_files'], +// $oExtensionInstance->GetJSFiles($oApp)); +// +// // Adding JS inline +// $sJSInline = $oExtensionInstance->GetJSInline($oApp); +// if ($sJSInline !== null) +// { +// // Note: Semi-colon is to prevent previous script that would have omitted it. +// $aUIExtensions['js_inline'] .= "\n\n;\n".$sJSInline; +// } +// +// // Adding HTML for each hook +// foreach ($aUIExtensionHooks as $sUIExtensionHook) +// { +// $sFunctionName = 'Get'.$sUIExtensionHook.'HTML'; +// $sHTML = $oExtensionInstance->$sFunctionName($oApp); +// if ($sHTML !== null) +// { +// if (!array_key_exists($sUIExtensionHook, $aUIExtensions['html'])) +// { +// $aUIExtensions['html'][$sUIExtensionHook] = ''; +// } +// $aUIExtensions['html'][$sUIExtensionHook] .= "\n\n".$sHTML; +// } +// } +// } +// +// return $aUIExtensions; +// } + +// public static function LoadSessionMessages(Application $oApp) +// { +// $aAllMessages = array(); +// if ((array_key_exists('obj_messages', $_SESSION)) && (!empty($_SESSION['obj_messages']))) +// { +// foreach ($_SESSION['obj_messages'] as $sMessageKey => $aMessageObjectData) +// { +// $aObjectMessages = array(); +// $aRanks = array(); +// foreach ($aMessageObjectData as $sMessageId => $aMessageData) +// { +// $sMsgClass = 'alert alert-'; +// switch ($aMessageData['severity']) +// { +// case 'info': +// $sMsgClass .= 'info'; +// break; +// case 'error': +// $sMsgClass .= 'danger'; +// break; +// case 'ok': +// default: +// $sMsgClass .= 'success'; +// break; +// } +// $aObjectMessages[] = array('cssClass' => $sMsgClass, 'message' => $aMessageData['message']); +// $aRanks[] = $aMessageData['rank']; +// } +// unset($_SESSION['obj_messages'][$sMessageKey]); +// array_multisort($aRanks, $aObjectMessages); +// foreach ($aObjectMessages as $aObjectMessage) +// { +// $aAllMessages[] = $aObjectMessage; +// } +// } +// } +// $oApp['combodo.current_user.session_messages'] = $aAllMessages; +// } + + /** + * Generate the form data for the $sClass. + * Form will look like the "Properties" tab of a $sClass object in the console. + * + * @param string $sClass + * @param bool $bAddLinksets + * + * @return array + */ + protected static function GenerateDefaultFormForClass($sClass, $bAddLinksets = false) + { + $aForm = array( + 'id' => strtolower($sClass)."-default-".uniqid(), + 'type' => 'custom_list', + 'properties' => array( + 'display_mode' => static::FORM_DEFAULT_DISPLAY_MODE, + 'always_show_submit' => static::FORM_DEFAULT_ALWAYS_SHOW_SUBMIT, + ), + 'fields' => array(), + 'layout' => array( + 'type' => 'xhtml', + 'content' => '', + ), + ); + + // Generate layout + $sContent = ""; + + // - Retrieve zlist details + $aDetailsList = MetaModel::GetZListItems($sClass, 'details'); + $aDetailsStruct = cmdbAbstractObject::ProcessZlist($aDetailsList, array(), 'UI:PropertiesTab', 'col1', ''); + $aPropertiesStruct = $aDetailsStruct['UI:PropertiesTab']; + + // Count cols (not linksets) + $iColCount = 0; + foreach($aPropertiesStruct as $sColId => $aColFieldsets) + { + if(substr($sColId, 0, 1) !== '_') + { + foreach($aColFieldsets as $sFieldsetName => $aAttCodes) + { + if(substr($sFieldsetName, 0, 1) !== '_') + { + $iColCount++; + break; + } + } + } + } + // If no cols, return a default form with all fields one after another + if($iColCount === 0) + { + return array( + 'id' => 'default', + 'type' => 'zlist', + 'fields' => 'details', + 'layout' => null + ); + } + // Warning, this might not be great when 12 modulo $iColCount is greater than 0. + $sColCSSClass = 'col-sm-'.floor(12/$iColCount); + + $sLinksetsHTML = ""; + $sRowHTML = "
\n"; + foreach($aPropertiesStruct as $sColId => $aColFieldsets) + { + $sColsHTML = "\t
\n"; + foreach($aColFieldsets as $sFieldsetName => $aAttCodes) + { + // Add fieldset, not linkset + if(substr($sFieldsetName, 0, 1) !== '_') + { + $sFieldsetHTML = "\t\t
\n"; + $sFieldsetHTML .= "\t\t\t".htmlentities(Dict::S($sFieldsetName), ENT_QUOTES, 'UTF-8')."\n"; + + foreach($aAttCodes as $sAttCode) + { + $sFieldsetHTML .= "\t\t\t
\n"; + } + + $sFieldsetHTML .= "\t\t
\n"; + + // Add to col + $sColsHTML .= $sFieldsetHTML; + } + elseif($bAddLinksets) + { + foreach($aAttCodes as $sAttCode) + { + $sLinksetsHTML .= "
\n"; + } + } + } + $sColsHTML .= "\t
\n"; + + // Add col to row + $sRowHTML .= $sColsHTML; + } + $sRowHTML .= "
\n"; + + // Add row to twig + $sContent .= $sRowHTML; + // Add linksets to twig + $sContent .= $sLinksetsHTML; + + $aForm['layout']['content'] = $sContent; + + return $aForm; + } + +} diff --git a/datamodels/2.x/itop-portal-base/portal-silex/src/helpers/contextmanipulatorhelper.class.inc.php b/datamodels/2.x/itop-portal-base/portal/src/Helper/ContextManipulatorHelper.php similarity index 83% rename from datamodels/2.x/itop-portal-base/portal-silex/src/helpers/contextmanipulatorhelper.class.inc.php rename to datamodels/2.x/itop-portal-base/portal/src/Helper/ContextManipulatorHelper.php index 4b1b41d7f..63291d43c 100644 --- a/datamodels/2.x/itop-portal-base/portal-silex/src/helpers/contextmanipulatorhelper.class.inc.php +++ b/datamodels/2.x/itop-portal-base/portal/src/Helper/ContextManipulatorHelper.php @@ -1,28 +1,33 @@ +/** + * 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\Helper; +use ModuleDesign; +use Combodo\iTop\Portal\Brick\BrickCollection; use Exception; -use Silex\Application; use DOMNodeList; use DOMFormatException; +use Symfony\Component\Routing\RouterInterface; use UserRights; use DBObject; use DBSearch; @@ -40,13 +45,36 @@ class ContextManipulatorHelper const ENUM_RULE_CALLBACK_OPEN_EDIT = 'edit'; const DEFAULT_RULE_CALLBACK_OPEN = self::ENUM_RULE_CALLBACK_OPEN_VIEW; - protected $oApp; protected $aRules; - public function __construct() + /** @var \Symfony\Component\Routing\RouterInterface */ + private $oRouter; + /** @var \Combodo\iTop\Portal\Brick\BrickCollection */ + private $oBrickCollection; + /** + * @var \Combodo\iTop\Portal\Helper\ScopeValidatorHelper + */ + private $oScopeValidator; + + /** + * ContextManipulatorHelper constructor. + * + * @param \ModuleDesign $oModuleDesign + * @param \Symfony\Component\Routing\RouterInterface $oRouter + * @param \Combodo\iTop\Portal\Brick\BrickCollection $oBrickCollection + * @param \Combodo\iTop\Portal\Helper\ScopeValidatorHelper $oScopeValidator + * + * @throws \DOMFormatException + */ + public function __construct(ModuleDesign $oModuleDesign, RouterInterface $oRouter, BrickCollection $oBrickCollection, ScopeValidatorHelper $oScopeValidator) { $this->aRules = array(); - } + $this->oRouter = $oRouter; + $this->oBrickCollection = $oBrickCollection; + + $this->Init($oModuleDesign->GetNodes('/module_design/action_rules/action_rule')); + $this->oScopeValidator = $oScopeValidator; + } /** * Initializes the ScopeValidator by generating and caching the scopes compilation in the $this->sCachePath.$this->sFilename file. @@ -181,10 +209,6 @@ class ContextManipulatorHelper } } - public function SetApp($oApp) - { - $this->oApp = $oApp; - } /** * Returns a hash array of rules @@ -302,7 +326,7 @@ class ContextManipulatorHelper } // Checking for silos - $oScopeSearch = $this->oApp['scope_validator']->GetScopeFilterForProfiles(UserRights::ListProfiles(), $sSearchClass, UR_ACTION_READ); + $oScopeSearch = $this->oScopeValidator->GetScopeFilterForProfiles(UserRights::ListProfiles(), $sSearchClass, UR_ACTION_READ); if ($oScopeSearch->IsAllDataAllowed()) { $oSearch->AllowAllData(); @@ -349,7 +373,6 @@ class ContextManipulatorHelper * 'cancel' => null * ); * - * @param \Silex\Application $oApp * @param array $aData * @param \DBObject $oObject * @param boolean $bModal @@ -358,7 +381,7 @@ class ContextManipulatorHelper * * @throws \Exception */ - public function GetCallbackUrls(Application $oApp, array $aData, DBObject $oObject, $bModal = false) + public function GetCallbackUrls(array $aData, DBObject $oObject, $bModal = false) { $aResults = array( 'submit' => null, @@ -389,12 +412,12 @@ class ContextManipulatorHelper break; case static::ENUM_RULE_CALLBACK_GOTO: - $oBrick = ApplicationHelper::GetLoadedBrickFromId($oApp, $aRule[$sCallbackName]['brick_id']); - $sCallbackUrl = $oApp['url_generator']->generate($oBrick->GetRouteName(), array('sBrickId' => $oBrick->GetId())); + $oBrick = $this->oBrickCollection->getBrickById($aRule[$sCallbackName]['brick_id']); + $sCallbackUrl = $this->oRouter->generate($oBrick->GetRouteName(), array('sBrickId' => $oBrick->GetId())); break; case static::ENUM_RULE_CALLBACK_OPEN: - $sCallbackUrl = ($oObject->IsNew()) ? null : $oApp['url_generator']->generate('p_object_' . $aRule[$sCallbackName]['mode'], array('sObjectClass' => get_class($oObject), 'sObjectId' => $oObject->GetKey())); + $sCallbackUrl = ($oObject->IsNew()) ? null : $this->oRouter->generate('p_object_' . $aRule[$sCallbackName]['mode'], array('sObjectClass' => get_class($oObject), 'sObjectId' => $oObject->GetKey())); break; } diff --git a/datamodels/2.x/itop-portal-base/portal-silex/src/helpers/lifecyclevalidatorhelper.class.inc.php b/datamodels/2.x/itop-portal-base/portal/src/Helper/LifecycleValidatorHelper.php similarity index 92% rename from datamodels/2.x/itop-portal-base/portal-silex/src/helpers/lifecyclevalidatorhelper.class.inc.php rename to datamodels/2.x/itop-portal-base/portal/src/Helper/LifecycleValidatorHelper.php index 4f00efbff..cad1e0352 100644 --- a/datamodels/2.x/itop-portal-base/portal-silex/src/helpers/lifecyclevalidatorhelper.class.inc.php +++ b/datamodels/2.x/itop-portal-base/portal/src/Helper/LifecycleValidatorHelper.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\Helper; @@ -36,13 +39,15 @@ class LifecycleValidatorHelper protected $sGeneratedClass; protected $aProfilesMatrix; - public function __construct($sFilename, $sCachePath = null) + public function __construct(\ModuleDesign $moduleDesign, $sPortalId, $sPortalCachePath = null) { - $this->sFilename = $sFilename; - $this->sCachePath = $sCachePath; - $this->sInstancePrefix = ''; + $this->sFilename = "{$sPortalId}.lifecycle.php"; + $this->sCachePath = $sPortalCachePath; + $this->sInstancePrefix = "{$sPortalId}-";; $this->sGeneratedClass = static::DEFAULT_GENERATED_CLASS; $this->aProfilesMatrix = array(); + + $this->Init($moduleDesign->GetNodes('/module_design/classes/class')); } /** diff --git a/datamodels/2.x/itop-portal-base/portal-silex/src/helpers/requestmanipulatorhelper.class.inc.php b/datamodels/2.x/itop-portal-base/portal/src/Helper/RequestManipulatorHelper.php similarity index 95% rename from datamodels/2.x/itop-portal-base/portal-silex/src/helpers/requestmanipulatorhelper.class.inc.php rename to datamodels/2.x/itop-portal-base/portal/src/Helper/RequestManipulatorHelper.php index ffac1c943..9867de83c 100644 --- a/datamodels/2.x/itop-portal-base/portal-silex/src/helpers/requestmanipulatorhelper.class.inc.php +++ b/datamodels/2.x/itop-portal-base/portal/src/Helper/RequestManipulatorHelper.php @@ -1,7 +1,7 @@ + * + * */ namespace Combodo\iTop\Portal\Helper; @@ -41,7 +42,7 @@ class RequestManipulatorHelper * * @param \Symfony\Component\HttpFoundation\RequestStack $oRequestStack */ - public function __construct(RequestStack &$oRequestStack) + public function __construct(RequestStack $oRequestStack) { $this->oRequestStack = $oRequestStack; } diff --git a/datamodels/2.x/itop-portal-base/portal/src/Helper/ScopeValidatorHelper.php b/datamodels/2.x/itop-portal-base/portal/src/Helper/ScopeValidatorHelper.php new file mode 100644 index 000000000..efa81d445 --- /dev/null +++ b/datamodels/2.x/itop-portal-base/portal/src/Helper/ScopeValidatorHelper.php @@ -0,0 +1,626 @@ +sFilename = "{$sPortalId}.scopes.php"; + $this->sCachePath = $sPortalCachePath; + $this->sInstancePrefix = "{$sPortalId}-"; + $this->sGeneratedClass = static::DEFAULT_GENERATED_CLASS; + $this->aProfilesMatrix = array(); + + $this->Init($moduleDesign->GetNodes('/module_design/classes/class')); + } + + /** + * Initializes the ScopeValidator by generating and caching the scopes compilation in the $this->sCachePath.$this->sFilename file. + * + * @param DOMNodeList $oNodes + * @throws DOMFormatException + * @throws Exception + */ + public function Init(DOMNodeList $oNodes) + { + // Checking cache path + if ($this->sCachePath === null) + { + $this->sCachePath = utils::GetCachePath(); + } + // Building full pathname for file + $sFilePath = $this->sCachePath . $this->sFilename; + + // Creating file if not existing + // Note : This is a temporary cache system, it should soon evolve to a cache provider (fs, apc, memcache, ...) + if (!file_exists($sFilePath)) + { + $this->InitGenerateAndWriteCache($oNodes, $sFilePath); + } + + if (!class_exists($this->sGeneratedClass)) + { + require_once $this->sCachePath . $this->sFilename; + } + } + + public static function EnumTypeValues() + { + return array(static::ENUM_TYPE_ALLOW, static::ENUM_TYPE_RESTRICT); + } + + /** + * Returns the path where to cache the compiled scopes file + * + * @return string + */ + public function GetCachePath() + { + return $this->sCachePath; + } + + /** + * Returns the name of the compiled scopes file + * + * @return string + */ + public function GetFilename() + { + return $this->sFilename; + } + + /** + * Returns the instance prefix used for the generated scopes class name + * + * @return string + */ + public function GetInstancePrefix() + { + return $this->sInstancePrefix; + } + + /** + * Returns the name of the generated scopes class + * + * @return string + */ + public function GetGeneratedClass() + { + return $this->sGeneratedClass; + } + + /** + * Sets the scope validator instance prefix. + * + * This is used to create a unique scope values class in the cache directory (/data/cache-) as there can be several instance of the portal. + * + * @param string $sInstancePrefix + * @return \Combodo\iTop\Portal\Helper\ScopeValidatorHelper + */ + public function SetInstancePrefix($sInstancePrefix) + { + $sInstancePrefix = preg_replace('/[-_]/', ' ', $sInstancePrefix); + $sInstancePrefix = ucwords($sInstancePrefix); + $sInstancePrefix = str_replace(' ', '', $sInstancePrefix); + + $this->sInstancePrefix = $sInstancePrefix; + $this->sGeneratedClass = $this->sInstancePrefix . static::DEFAULT_GENERATED_CLASS; + + return $this; + } + + /** + * Returns the DBSearch for the $sProfile in $iAction for the class $sClass + * + * @param string $sProfile + * @param string $sClass + * @param integer $iAction + * + * @return \DBSearch + * + * @throws \Exception + * @throws \CoreException + * @throws \OQLException + */ + public function GetScopeFilterForProfile($sProfile, $sClass, $iAction = null) + { + return $this->GetScopeFilterForProfiles(array($sProfile), $sClass, $iAction); + } + + /** + * Returns the DBSearch for the $aProfiles in $iAction for the class $sClass. + * Profiles are a OR condition. + * + * @param array $aProfiles + * @param string $sClass + * @param integer $iAction + * + * @return \DBSearch + * + * @throws \Exception + * @throws \CoreException + * @throws \OQLException + */ + public function GetScopeFilterForProfiles($aProfiles, $sClass, $iAction = null) + { + $oSearch = null; + $aAllowSearches = array(); + $aRestrictSearches = array(); + $bIgnoreSilos = static::DEFAULT_IGNORE_SILOS; + + // Checking the default mode + if ($iAction === null) + { + $iAction = UR_ACTION_READ; + } + + // Iterating on profiles to retrieving the different OQLs parts + foreach ($aProfiles as $sProfile) + { + // Retrieving matrix informtions + $iProfileId = $this->GetProfileIdFromProfileName($sProfile); + $sMode = ($iAction === UR_ACTION_READ) ? static::ENUM_MODE_READ : static::ENUM_MODE_WRITE; + + // Retrieving profile OQLs + $sScopeValuesClass = $this->sGeneratedClass; + $aProfileMatrix = $sScopeValuesClass::GetProfileScope($iProfileId, $sClass, $sMode); + if ($aProfileMatrix !== null) + { + if (isset($aProfileMatrix['allow']) && $aProfileMatrix['allow'] !== null) + { + $aAllowSearches[] = DBSearch::FromOQL($aProfileMatrix['allow']); + } + if (isset($aProfileMatrix['restrict']) && $aProfileMatrix['restrict'] !== null) + { + $aRestrictSearches[] = DBSearch::FromOQL($aProfileMatrix['restrict']); + } + // If a profile should ignore allowed org, we set it for all its queries no matter the profile + if (isset($aProfileMatrix['ignore_silos']) && $aProfileMatrix['ignore_silos'] === true) + { + $bIgnoreSilos = true; + } + } + } + + // Building the real OQL from all the parts from the differents profiles + for ($i = 0; $i < count($aAllowSearches); $i++) + { + foreach ($aRestrictSearches as $oRestrictSearch) + { + $aAllowSearches[$i] = $aAllowSearches[$i]->Intersect($oRestrictSearch); + } + } + if (count($aAllowSearches) > 0) + { + $oSearch = new DBUnionSearch($aAllowSearches); + $oSearch = $oSearch->RemoveDuplicateQueries(); + } + if ($bIgnoreSilos === true) + { + $oSearch->AllowAllData(); + } + + return $oSearch; + } + + /** + * Add the scope query (view or edit depending on $sAction) for $sClass to the $oQuery. + * + * @param \DBSearch $oQuery + * @param string $sClass + * @param int $sAction + * + * @return bool true if scope exists, false if scope is null + * + * @throws \CoreException + * @throws \OQLException + */ + public function AddScopeToQuery(DBSearch &$oQuery, $sClass, $sAction = UR_ACTION_READ) + { + $oScopeQuery = $this->GetScopeFilterForProfiles(UserRights::ListProfiles(), $sClass, $sAction); + if ($oScopeQuery !== null) + { + $oQuery = $oQuery->Intersect($oScopeQuery); + // - Allowing all data if necessary + if ($oScopeQuery->IsAllDataAllowed()) + { + $oQuery->AllowAllData(); + } + + return true; + } + + return false; + } + + /** + * Returns true if at least one of the $aProfiles has the ignore_silos flag set to true for the $sClass. + * + * @param array $aProfiles + * @param string $sClass + * + * @return boolean + * + * @throws \Exception + */ + public function IsAllDataAllowedForScope($aProfiles, $sClass) + { + $bIgnoreSilos = false; + + // Iterating on profiles to retrieving the different OQLs parts + foreach ($aProfiles as $sProfile) + { + // Retrieving matrix informtions + $iProfileId = $this->GetProfileIdFromProfileName($sProfile); + + // Retrieving profile OQLs + $sScopeValuesClass = $this->sGeneratedClass; + $aProfileMatrix = $sScopeValuesClass::GetProfileScope($iProfileId, $sClass, static::ENUM_MODE_READ); + if ($aProfileMatrix !== null) + { + // If a profile should ignore allowed org, we set it for all its queries no matter the profile + if (isset($aProfileMatrix['ignore_silos']) && $aProfileMatrix['ignore_silos'] === true) + { + $bIgnoreSilos = true; + } + } + } + + return $bIgnoreSilos; + } + + /** + * Returns the profile id from a string being either a constant or its name. + * + * @param string $sProfile + * + * @return integer + * + * @throws \Exception + */ + protected function GetProfileIdFromProfileName($sProfile) + { + $iProfileId = null; + + // We try to find the profile from its name in order to retrieve it's id + // - If the regular UserRights addon is installed we check the profiles array + if (class_exists('ProfilesConfig')) + { + if (defined($sProfile) && in_array($sProfile, ProfilesConfig::GetProfilesValues())) + { + $iProfileId = constant($sProfile); + } + else + { + foreach (ProfilesConfig::GetProfilesValues() as $iKey => $aValue) + { + if ($aValue['name'] === $sProfile) + { + $iProfileId = $iKey; + break; + } + } + } + } + // - Else, we can't find the id from the name as we don't know the used UserRights addon. It has to be a constant + else + { + throw new Exception('Scope validator : Unknown UserRights addon, scope\'s profile must be a constant'); + } + + // If profile was not found from its name or from a constant, we throw an exception + if ($iProfileId === null) + { + throw new Exception('Scope validator : Could not find "' . $sProfile . '" in the profiles list'); + } + + return $iProfileId; + } + + /** + * Returns a string containing the generated PHP class for the compiled scopes + * + * @param array $aProfiles + * @return string + */ + protected function BuildPHPClass($aProfiles = array()) + { + $sProfiles = var_export($aProfiles, true); + $sClassName = ltrim($this->sGeneratedClass, '\\'); + $sPHP = <<getAttribute('id'); + if ($sClass === '') { + throw new DOMFormatException('Class tag must have an id attribute.', null, null, $oClassNode); + } + + // Iterating over scope nodes of the class + $oScopesNode = $oClassNode->GetOptionalElement('scopes'); + if ($oScopesNode !== null) { + foreach ($oScopesNode->GetNodes('./scope') as $oScopeNode) { + // Retrieving mandatory scope id attribute + $sScopeId = $oScopeNode->getAttribute('id'); + if ($sScopeId === '') { + throw new DOMFormatException('Scope tag must have an id attribute.', null, null, $oScopeNode); + } + + // Retrieving the type of query + // Note : This has been disabled as we don't want deny rules for now + // $oOqlViewTypeNode = $oClassNode->GetOptionalElement('oql_view_type'); + // $sOqlViewType = ($oOqlViewTypeNode !== null && ($oOqlViewTypeNode->GetText() === static::ENUM_TYPE_RESTRICT)) ? static::ENUM_TYPE_RESTRICT : static::ENUM_TYPE_ALLOW; + $sOqlViewType = static::ENUM_TYPE_ALLOW; + // Retrieving the view query + $oOqlViewNode = $oScopeNode->GetUniqueElement('oql_view'); + $sOqlView = $oOqlViewNode->GetText(); + if ($sOqlView === null) { + throw new DOMFormatException( + 'Scope tag in class must have a not empty oql_view tag', + null, + null, + $oScopeNode + ); + } + // Retrieving the edit query + $oOqlEditNode = $oScopeNode->GetOptionalElement('oql_edit'); + $sOqlEdit = (($oOqlEditNode !== null) && ($oOqlEditNode->GetText( + ) !== null)) ? $oOqlEditNode->GetText() : null; + // Retrieving ignore allowed org flag + $oIgnoreSilosNode = $oScopeNode->GetOptionalElement('ignore_silos'); + $bIgnoreSilos = (($oIgnoreSilosNode !== null) && ($oIgnoreSilosNode->GetText( + ) === 'true')) ? true : static::DEFAULT_IGNORE_SILOS; + + // Retrieving profiles for the scope + $oProfilesNode = $oScopeNode->GetOptionalElement('allowed_profiles'); + $aProfilesNames = array(); + // If no profile is specified, we consider that it's for ALL the profiles + if (($oProfilesNode === null) || ($oProfilesNode->GetNodes('./allowed_profile')->length === 0)) { + foreach (ProfilesConfig::GetProfilesValues() as $iKey => $aValue) { + $aProfilesNames[] = $aValue['name']; + } + } else { + foreach ($oProfilesNode->GetNodes('./allowed_profile') as $oProfileNode) { + // Retrieving mandatory profile id attribute + $sProfileId = $oProfileNode->getAttribute('id'); + if ($sProfileId === '') { + throw new DOMFormatException( + 'Scope tag must have an id attribute.', + null, + null, + $oProfileNode + ); + } + $aProfilesNames[] = $sProfileId; + } + } + + // + foreach ($aProfilesNames as $sProfileName) { + // Scope profile id + $iProfileId = $this->GetProfileIdFromProfileName($sProfileName); + + // Now that we have the queries infos, we are going to build the queries for that profile / class + $sMatrixPrefix = $iProfileId.'_'.$sClass.'_'; + // - View query + $oViewFilter = DBSearch::FromOQL($sOqlView); + // ... We have to union the query if this profile has another scope for that class + if (array_key_exists($sMatrixPrefix.static::ENUM_MODE_READ, $aProfiles) && array_key_exists( + $sOqlViewType, + $aProfiles[$sMatrixPrefix.static::ENUM_MODE_READ] + )) { + $oExistingFilter = DBSearch::FromOQL( + $aProfiles[$sMatrixPrefix.static::ENUM_MODE_READ][$sOqlViewType] + ); + $aFilters = array($oExistingFilter, $oViewFilter); + $oResFilter = new DBUnionSearch($aFilters); + + // Applying ignore_silos flag on result filter if necessary (As the union will remove it if it is not on all sub-queries) + if ($aProfiles[$sMatrixPrefix.static::ENUM_MODE_READ]['ignore_silos'] === true) { + $bIgnoreSilos = true; + } + } else { + $oResFilter = $oViewFilter; + } + $aProfiles[$sMatrixPrefix.static::ENUM_MODE_READ] = array( + $sOqlViewType => $oResFilter->ToOQL(), + 'ignore_silos' => $bIgnoreSilos + ); + // - Edit query + if ($sOqlEdit !== null) { + $oEditFilter = DBSearch::FromOQL($sOqlEdit); + // - If the queries are the same, we don't make an intersect, we just reuse the view query + if ($sOqlEdit === $sOqlView) { + // Do not intersect, edit query is identical to view query + } else { + if (($oEditFilter->GetClass() === $oViewFilter->GetClass()) && $oEditFilter->IsAny()) { + $oEditFilter = $oViewFilter; + // Do not intersect, edit query is identical to view query + } else { + // Intersect + $oEditFilter = $oViewFilter->Intersect($oEditFilter); + } + } + + // ... We have to union the query if this profile has another scope for that class + if (array_key_exists( + $sMatrixPrefix.static::ENUM_MODE_WRITE, + $aProfiles + ) && array_key_exists( + $sOqlViewType, + $aProfiles[$sMatrixPrefix.static::ENUM_MODE_WRITE] + )) { + $oExistingFilter = DBSearch::FromOQL( + $aProfiles[$sMatrixPrefix.static::ENUM_MODE_WRITE][$sOqlViewType] + ); + $aFilters = array($oExistingFilter, $oEditFilter); + $oResFilter = new DBUnionSearch($aFilters); + } else { + $oResFilter = $oEditFilter; + } + $aProfiles[$sMatrixPrefix.static::ENUM_MODE_WRITE] = array( + $sOqlViewType => $oResFilter->ToOQL(), + 'ignore_silos' => $bIgnoreSilos + ); + } + } + } + + $aProfileClasses[] = $sClass; + } + } + + // Filling the array with missing classes from MetaModel, so we can have an inheritance principle on the scope + // For each class explicitly given in the scopes, we check if its child classes were also in the scope : + // If not, we add them with the same OQL + foreach ($aProfileClasses as $sProfileClass) { + foreach (MetaModel::EnumChildClasses($sProfileClass) as $sChildClass) { + // If the child class is not in the scope, we are going to try to add it + if (!in_array($sChildClass, $aProfileClasses)) { + foreach (ProfilesConfig::GetProfilesValues() as $iKey => $aValue) { + $iProfileId = $iKey; + foreach (array(static::ENUM_MODE_READ, static::ENUM_MODE_WRITE) as $sAction) { + // If the current profile has scope for that class in that mode, we duplicate it + if (isset($aProfiles[$iProfileId.'_'.$sProfileClass.'_'.$sAction])) { + $aTmpProfile = $aProfiles[$iProfileId.'_'.$sProfileClass.'_'.$sAction]; + foreach ($aTmpProfile as $sType => $sOql) { + // IF condition is just to skip the 'ignore_silos' flag + if (in_array($sType, array(static::ENUM_TYPE_ALLOW, static::ENUM_TYPE_RESTRICT))) { + $oTmpFilter = DBSearch::FromOQL($sOql); + $oTmpFilter->ChangeClass($sChildClass); + + $aTmpProfile[$sType] = $oTmpFilter->ToOQL(); + } + } + + $aProfiles[$iProfileId.'_'.$sChildClass.'_'.$sAction] = $aTmpProfile; + } + } + } + } + } + } + + // - Build php class + $sPHP = $this->BuildPHPClass($aProfiles); + + // - Write file on disk + // - Creating dir if necessary + if (!is_dir($this->sCachePath)) { + mkdir($this->sCachePath, 0777, true); + } + // -- Then creating the file + $ret = file_put_contents($sFilePath, $sPHP); + if ($ret === false) { + $iLen = strlen($sPHP); + $fFree = @disk_free_space(dirname($sFilePath)); + $aErr = error_get_last(); + throw new Exception( + "Failed to write '$sFilePath'. Last error: '{$aErr['message']}', content to write: $iLen bytes, available free space on disk: $fFree." + ); + } + } + +} + diff --git a/datamodels/2.x/itop-portal-base/portal/src/Helper/SecurityHelper.php b/datamodels/2.x/itop-portal-base/portal/src/Helper/SecurityHelper.php new file mode 100644 index 000000000..b54724f8b --- /dev/null +++ b/datamodels/2.x/itop-portal-base/portal/src/Helper/SecurityHelper.php @@ -0,0 +1,297 @@ + + +namespace Combodo\iTop\Portal\Helper; + +use Silex\Application; +use UserRights; +use IssueLog; +use MetaModel; +use DBSearch; +use DBObjectSearch; +use DBObjectSet; +use FieldExpression; +use VariableExpression; +use BinaryExpression; + +/** + * SecurityHelper class + * + * Handle security checks through the different layers (portal scopes, iTop silos, user rights) + * + * @author Guillaume Lajarige + */ +class SecurityHelper +{ + public static $aAllowedScopeObjectsCache = array( + UR_ACTION_READ => array(), + UR_ACTION_MODIFY => array(), + ); + + /** + * Returns true if the current user is allowed to do the $sAction on an $sObjectClass object (with optionnal $sObjectId id) + * Checks are: + * - Has a scope query for the $sObjectClass / $sAction + * - Optionally, if $sObjectId provided: Is object within scope for $sObjectClass / $sObjectId / $sAction + * - Is allowed by datamodel for $sObjectClass / $sAction + * + * @param ScopeValidatorHelper $scopeValidator + * @param boolean $isDebugEnabled + * @param string $sAction Must be in UR_ACTION_READ|UR_ACTION_MODIFY|UR_ACTION_CREATE + * @param string $sObjectClass + * @param string $sObjectId + * + * @return boolean + * + * @throws \CoreException + * @throws \MissingQueryArgument + * @throws \MySQLException + * @throws \MySQLHasGoneAwayException + * @throws \OQLException + */ + public static function IsActionAllowed(ScopeValidatorHelper $scopeValidator, $isDebugEnabled, $sAction, $sObjectClass, $sObjectId = null) + { + $sDebugTracePrefix = __CLASS__ . ' / ' . __METHOD__ . ' : Returned false for action ' . $sAction . ' on ' . $sObjectClass . '::' . $sObjectId; + + // Checking action type + if (!in_array($sAction, array(UR_ACTION_READ, UR_ACTION_MODIFY, UR_ACTION_CREATE))) + { + if ($isDebugEnabled) + { + IssueLog::Info($sDebugTracePrefix . ' as the action value could not be understood (' . UR_ACTION_READ . '/' . UR_ACTION_MODIFY . '/' . UR_ACTION_CREATE . ' expected'); + } + return false; + } + + // Checking the scopes layer + // - Transforming scope action as there is only 2 values + $sScopeAction = ($sAction === UR_ACTION_READ) ? UR_ACTION_READ : UR_ACTION_MODIFY; + // - Retrieving the query. If user has no scope, it can't access that kind of objects + $oScopeQuery = $scopeValidator->GetScopeFilterForProfiles(UserRights::ListProfiles(), $sObjectClass, $sScopeAction); + if ($oScopeQuery === null) + { + if ($isDebugEnabled) + { + IssueLog::Info($sDebugTracePrefix . ' as there was no scope defined for action ' . $sScopeAction . ' and profiles ' . implode('/', UserRights::ListProfiles())); + } + return false; + } + // - If action != create we do some additionnal checks + if ($sAction !== UR_ACTION_CREATE) + { + // - Checking specific object if id is specified + if ($sObjectId !== null) + { + // Checking if object status is in cache (to avoid unnecessary query) + if(isset(static::$aAllowedScopeObjectsCache[$sScopeAction][$sObjectClass][$sObjectId]) ) + { + if(static::$aAllowedScopeObjectsCache[$sScopeAction][$sObjectClass][$sObjectId] === false) + { + if ($isDebugEnabled) + { + IssueLog::Info($sDebugTracePrefix . ' as it was denied in the scope objects cache'); + } + return false; + } + } + else + { + // Modifying query to filter on the ID + // - Adding expression + $sObjectKeyAtt = MetaModel::DBGetKey($sObjectClass); + $oFieldExp = new FieldExpression($sObjectKeyAtt, $oScopeQuery->GetClassAlias()); + $oBinExp = new BinaryExpression($oFieldExp, '=', new VariableExpression('object_id')); + $oScopeQuery->AddConditionExpression($oBinExp); + // - Setting value + $aQueryParams = $oScopeQuery->GetInternalParams(); + $aQueryParams['object_id'] = $sObjectId; + $oScopeQuery->SetInternalParams($aQueryParams); + unset($aQueryParams); + + // - Checking if query result is null (which means that the user has no right to view this specific object) + $oSet = new DBObjectSet($oScopeQuery); + if ($oSet->Count() === 0) + { + // Updating cache + static::$aAllowedScopeObjectsCache[$sScopeAction][$sObjectClass][$sObjectId] = false; + + if ($isDebugEnabled) + { + IssueLog::Info($sDebugTracePrefix . ' as there was no result for the following scope query : ' . $oScopeQuery->ToOQL(true)); + } + return false; + } + + // Updating cache + static::$aAllowedScopeObjectsCache[$sScopeAction][$sObjectClass][$sObjectId] = true; + } + } + } + + // Checking reading security layer. The object could be listed, check if it is actually allowed to view it + if (UserRights::IsActionAllowed($sObjectClass, $sAction) == UR_ALLOWED_NO) + { + // For security reasons, we don't want to give the user too many informations on why he cannot access the object. + //throw new SecurityException('User not allowed to view this object', array('class' => $sObjectClass, 'id' => $sObjectId)); + if ($isDebugEnabled) + { + IssueLog::Info($sDebugTracePrefix . ' as the user is not allowed to access this object according to the datamodel security (cf. Console settings)'); + } + return false; + } + + return true; + } + + /** + * @param LifecycleValidatorHelper $lifecycleValidator + * @param $sStimulusCode + * @param $sObjectClass + * @param null $oInstanceSet + * + * @return bool + * @throws \Exception + */ + public static function IsStimulusAllowed(LifecycleValidatorHelper $lifecycleValidator, $sStimulusCode, $sObjectClass, $oInstanceSet = null) + { + // Checking DataModel layer + $aStimuliFromDatamodel = Metamodel::EnumStimuli($sObjectClass); + $iActionAllowed = (get_class($aStimuliFromDatamodel[$sStimulusCode]) == 'StimulusUserAction') ? UserRights::IsStimulusAllowed($sObjectClass, $sStimulusCode, $oInstanceSet) : UR_ALLOWED_NO; + if( ($iActionAllowed === false) || ($iActionAllowed === UR_ALLOWED_NO) ) + { + return false; + } + + // Checking portal security layer + $aStimuliFromPortal = $lifecycleValidator->GetStimuliForProfiles(UserRights::ListProfiles(), $sObjectClass); + if(!in_array($sStimulusCode, $aStimuliFromPortal)) + { + return false; + } + + return true; + } + + /** + * Preloads scope objects cache with objects from $oQuery + * + * @param ScopeValidatorHelper $scopeValidator + * @param \DBSearch $oSearch + * @param array $aExtKeysToPreload + * + * @throws \CoreException + * @throws \CoreUnexpectedValue + * @throws \MySQLException + * @throws \OQLException + */ + public static function PreloadForCache(ScopeValidatorHelper $scopeValidator, DBSearch $oSearch, $aExtKeysToPreload = null) + { + $sObjectClass = $oSearch->GetClass(); + $aObjectIds = array(); + $aExtKeysIds = array(); + $aColumnsToLoad = array(); + + if($aExtKeysToPreload !== null) + { + foreach($aExtKeysToPreload as $sAttCode) + { + /** @var \AttributeDefinition $oAttDef */ + $oAttDef = MetaModel::GetAttributeDef($sObjectClass, $sAttCode); + if($oAttDef->IsExternalKey()) + { + $aExtKeysIds[$oAttDef->GetTargetClass()] = array(); + $aColumnsToLoad[] = $sAttCode; + } + } + } + + // Retrieving IDs of all objects + // Note: We have to clone $oSet otherwise the source object will be modified + $oSet = new DBObjectSet($oSearch); + $oSet->OptimizeColumnLoad(array($oSearch->GetClassAlias() => $aColumnsToLoad)); + while($oCurrentRow = $oSet->Fetch()) + { + // Note: By presetting value to false, it is quicker to find which objects where not returned by the scope query later + $aObjectIds[$oCurrentRow->GetKey()] = false; + + // Preparing ExtKeys to preload + foreach($aColumnsToLoad as $sAttCode) + { + $iExtKey = $oCurrentRow->Get($sAttCode); + if($iExtKey > 0) + { + /** @var \AttributeExternalKey $oAttDef */ + $oAttDef = MetaModel::GetAttributeDef($sObjectClass, $sAttCode); + if(!in_array($iExtKey, $aExtKeysIds[$oAttDef->GetTargetClass()])) + { + $aExtKeysIds[$oAttDef->GetTargetClass()][] = $iExtKey; + } + } + } + } + + foreach(array(UR_ACTION_READ, UR_ACTION_MODIFY) as $sScopeAction) + { + // Retrieving scope query + /** @var DBSearch $oScopeQuery */ + $oScopeQuery = $scopeValidator->GetScopeFilterForProfiles(UserRights::ListProfiles(), $sObjectClass, $sScopeAction); + if($oScopeQuery !== null) + { + // Restricting scope if specified + if(!empty($aObjectIds)) + { + $oScopeQuery->AddCondition('id', array_keys($aObjectIds), 'IN'); + } + + // Preparing object set + $oScopeSet = new DBObjectSet($oScopeQuery); + $oScopeSet->OptimizeColumnLoad(array()); + + // Checking objects status + $aScopeObjectIds = $aObjectIds; + while($oCurrentRow = $oScopeSet->Fetch()) + { + $aScopeObjectIds[$oCurrentRow->GetKey()] = true; + } + + // Updating cache + if(!isset(static::$aAllowedScopeObjectsCache[$sScopeAction][$sObjectClass])) + { + static::$aAllowedScopeObjectsCache[$sScopeAction][$sObjectClass] = $aScopeObjectIds; + } + else + { + static::$aAllowedScopeObjectsCache[$sScopeAction][$sObjectClass] = array_merge_recursive(static::$aAllowedScopeObjectsCache[$sScopeAction][$sObjectClass], $aScopeObjectIds); + } + } + } + + // Preloading ExtKeys + foreach($aExtKeysIds as $sTargetClass => $aTargetIds) + { + if(!empty($aTargetIds)) + { + $oTargetSearch = new DBObjectSearch($sTargetClass); + $oTargetSearch->AddCondition('id', $aTargetIds, 'IN'); + + static::PreloadForCache($scopeValidator, $oTargetSearch); + } + } + } +} diff --git a/datamodels/2.x/itop-portal-base/portal/src/Kernel.php b/datamodels/2.x/itop-portal-base/portal/src/Kernel.php new file mode 100644 index 000000000..bd766e17b --- /dev/null +++ b/datamodels/2.x/itop-portal-base/portal/src/Kernel.php @@ -0,0 +1,66 @@ +environment; + + return utils::GetCachePath() . "/portals/$cacheDir"; + } + + public function getLogDir() + { + $logDir = PORTAL_ID . '-' . $this->environment; + + return utils::GetLogPath() . "/portals/$logDir"; + } + + public function registerBundles() + { + $contents = require $this->getProjectDir().'/config/bundles.php'; + foreach ($contents as $class => $envs) { + if (isset($envs[$this->environment]) || isset($envs['all'])) { + yield new $class(); + } + } + } + + protected function configureContainer(ContainerBuilder $container, LoaderInterface $loader) + { + $container->addResource(new FileResource($this->getProjectDir().'/config/bundles.php')); + // Feel free to remove the "container.autowiring.strict_mode" parameter + // if you are using symfony/dependency-injection 4.0+ as it's the default behavior + $container->setParameter('container.autowiring.strict_mode', true); + $container->setParameter('container.dumper.inline_class_loader', true); + $confDir = $this->getProjectDir().'/config'; + + $loader->load($confDir.'/{packages}/*'.self::CONFIG_EXTS, 'glob'); + $loader->load($confDir.'/{packages}/'.$this->environment.'/**/*'.self::CONFIG_EXTS, 'glob'); + $loader->load($confDir.'/{services}'.self::CONFIG_EXTS, 'glob'); + $loader->load($confDir.'/{services}_'.$this->environment.self::CONFIG_EXTS, 'glob'); + } + + protected function configureRoutes(RouteCollectionBuilder $routes) + { + $confDir = $this->getProjectDir().'/config'; + + $routes->import($confDir.'/{routes}/'.$this->environment.'/**/*'.self::CONFIG_EXTS, '/', 'glob'); + $routes->import($confDir.'/{routes}/*'.self::CONFIG_EXTS, '/', 'glob'); + $routes->import($confDir.'/{routes}'.self::CONFIG_EXTS, '/', 'glob'); + } +} diff --git a/datamodels/2.x/itop-portal-base/portal/src/Routing/ItopExtensionsExtraRoutes.php b/datamodels/2.x/itop-portal-base/portal/src/Routing/ItopExtensionsExtraRoutes.php new file mode 100644 index 000000000..3af039521 --- /dev/null +++ b/datamodels/2.x/itop-portal-base/portal/src/Routing/ItopExtensionsExtraRoutes.php @@ -0,0 +1,71 @@ +getExtraParams($parameters); + + return parent::doGenerate($variables, $defaults, $requirements, $tokens, $parameters, $name, $referenceType, $hostTokens, $requiredSchemes); + } + + /** + * @param array $aParameters + * + * @return mixed + */ + private function getExtraParams($aParameters) + { + $sExecModule = utils::ReadParam('exec_module', '', false, 'string'); + $sExecPage = utils::ReadParam('exec_page', '', false, 'string'); + if ($sExecModule !== '' && $sExecPage !== '') { + $aParameters['exec_module'] = $sExecModule; + $aParameters['exec_page'] = $sExecPage; + } + + // Optional parameters + $sPortalId = utils::ReadParam('portal_id', '', false, 'string'); + if ($sPortalId !== '') { + $aParameters['portal_id'] = $sPortalId; + } + $sEnvSwitch = utils::ReadParam('env_switch', '', false, 'string'); + if ($sEnvSwitch !== '') { + $aParameters['env_switch'] = $sEnvSwitch; + } + $sDebug = utils::ReadParam('debug', '', false, 'string'); + if ($sDebug !== '') { + $aParameters['debug'] = $sDebug; + } + + return $aParameters; + } +} \ No newline at end of file diff --git a/datamodels/2.x/itop-portal-base/portal/src/Security/ItopUser.php b/datamodels/2.x/itop-portal-base/portal/src/Security/ItopUser.php new file mode 100644 index 000000000..e6bb7a5fd --- /dev/null +++ b/datamodels/2.x/itop-portal-base/portal/src/Security/ItopUser.php @@ -0,0 +1,62 @@ +oUser = $oUser; + } + + public function setContactPhotoUrl($sContactPhotoUrl) + { + $this->sContactPhotoUrl = $sContactPhotoUrl; + } + + /** + * @return mixed + */ + public function getUser() + { + return $this->oUser; + } + + /** + * @return mixed + */ + public function getContactPhotoUrl() + { + return $this->sContactPhotoUrl; + } +} \ No newline at end of file diff --git a/datamodels/2.x/itop-portal-base/portal/src/Twig/AppExtension.php b/datamodels/2.x/itop-portal-base/portal/src/Twig/AppExtension.php new file mode 100644 index 000000000..8d0bae745 --- /dev/null +++ b/datamodels/2.x/itop-portal-base/portal/src/Twig/AppExtension.php @@ -0,0 +1,107 @@ +Get($sParamName); + }); + + return $functions; + } + + +} \ 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 new file mode 100644 index 000000000..072663696 --- /dev/null +++ b/datamodels/2.x/itop-portal-base/portal/src/Twig/AppVariable.php @@ -0,0 +1,166 @@ +decorated = $decorated; + $this->container = $container; + } + + public function __call($name, $arguments) + { + if ($this->silexApplicationEmulation->offsetExists($name)) { + return $this->silexApplicationEmulation->offsetGet($name); + } + + return $this->decorated->$name(...$arguments); //WARNING: use of http://php.net/manual/fr/functions.arguments.php#functions.variable-arg-list + } + + /** + * Whether a offset exists + * @link https://php.net/manual/en/arrayaccess.offsetexists.php + * + * @param mixed $offset

+ * An offset to check for. + *

+ * + * @return boolean true on success or false on failure. + *

+ *

+ * The return value will be casted to boolean if non-boolean was returned. + * @since 5.0.0 + */ + public function offsetExists($offset) + { + if ($this->container->hasParameter($offset)) { + return true; + } + if ($this->container->has($offset)) { + return true; + } + + return false; + } + + /** + * Offset to retrieve + * @link https://php.net/manual/en/arrayaccess.offsetget.php + * + * @param mixed $offset

+ * The offset to retrieve. + *

+ * + * @return mixed Can return all value types. + * @since 5.0.0 + */ + public function offsetGet($offset) + { + if ($this->container->hasParameter($offset)) { + return $this->container->getParameter($offset); + } + if ($this->container->has($offset)) { + return $this->container->get($offset); + } + + return; + } + + /** + * Offset to set + * @link https://php.net/manual/en/arrayaccess.offsetset.php + * + * @param mixed $offset

+ * The offset to assign the value to. + *

+ * @param mixed $value

+ * The value to set. + *

+ * + * @return void + * @since 5.0.0 + */ + public function offsetSet($offset, $value) + { + + if ($this->container->hasParameter($offset)) { + $this->container->setParameter($offset, $value); + return; + } + + if ($this->container->has($offset)) { + $this->container->set($offset, $value); + return; + } + + if (is_object($value)) { + $this->container->set($offset, $value); + return; + } + + $this->container->setParameter($offset, $value); + return; + } + + /** + * Offset to unset + * @link https://php.net/manual/en/arrayaccess.offsetunset.php + * + * @param mixed $offset

+ * The offset to unset. + *

+ * + * @return void + * @since 5.0.0 + */ + public function offsetUnset($offset){ + if ($this->container->hasParameter($offset)) { + $this->container->setParameter($offset, null); + return; + } + + if ($this->container->has($offset)) { + $this->container->set($offset, null); + return; + } + } + +} \ No newline at end of file diff --git a/datamodels/2.x/itop-portal-base/portal/src/VariableAccessor/AbstractStringVariableAccessor.php b/datamodels/2.x/itop-portal-base/portal/src/VariableAccessor/AbstractStringVariableAccessor.php new file mode 100644 index 000000000..7d0498207 --- /dev/null +++ b/datamodels/2.x/itop-portal-base/portal/src/VariableAccessor/AbstractStringVariableAccessor.php @@ -0,0 +1,19 @@ +getVariable(); + } +} \ No newline at end of file diff --git a/datamodels/2.x/itop-portal-base/portal/src/VariableAccessor/AbstractVariableAccessor.php b/datamodels/2.x/itop-portal-base/portal/src/VariableAccessor/AbstractVariableAccessor.php new file mode 100644 index 000000000..2be5ed033 --- /dev/null +++ b/datamodels/2.x/itop-portal-base/portal/src/VariableAccessor/AbstractVariableAccessor.php @@ -0,0 +1,31 @@ +mStoredVariable = $mVariableToStore; + } + + public function getVariable() + { + return $this->mStoredVariable; + } +} \ No newline at end of file diff --git a/datamodels/2.x/itop-portal-base/portal/src/VariableAccessor/CombodoCurrentContactPhotoUrl.php b/datamodels/2.x/itop-portal-base/portal/src/VariableAccessor/CombodoCurrentContactPhotoUrl.php new file mode 100644 index 000000000..ae7c503d1 --- /dev/null +++ b/datamodels/2.x/itop-portal-base/portal/src/VariableAccessor/CombodoCurrentContactPhotoUrl.php @@ -0,0 +1,40 @@ +getVariable()->getContactPhotoUrl(); + } +} \ No newline at end of file diff --git a/datamodels/2.x/itop-portal-base/portal/src/VariableAccessor/CombodoCurrentUser.php b/datamodels/2.x/itop-portal-base/portal/src/VariableAccessor/CombodoCurrentUser.php new file mode 100644 index 000000000..f62fe21a0 --- /dev/null +++ b/datamodels/2.x/itop-portal-base/portal/src/VariableAccessor/CombodoCurrentUser.php @@ -0,0 +1,40 @@ +getVariable()->getUser(); + } +} \ No newline at end of file diff --git a/datamodels/2.x/itop-portal-base/portal/src/VariableAccessor/CombodoPortalInstanceConf.php b/datamodels/2.x/itop-portal-base/portal/src/VariableAccessor/CombodoPortalInstanceConf.php new file mode 100644 index 000000000..14c011d58 --- /dev/null +++ b/datamodels/2.x/itop-portal-base/portal/src/VariableAccessor/CombodoPortalInstanceConf.php @@ -0,0 +1,14 @@ + diff --git a/datamodels/2.x/itop-portal-base/portal-silex/src/views/bricks/browse/mode_mosaic.html.twig b/datamodels/2.x/itop-portal-base/portal/templates/bricks/browse/mode_mosaic.html.twig similarity index 98% rename from datamodels/2.x/itop-portal-base/portal-silex/src/views/bricks/browse/mode_mosaic.html.twig rename to datamodels/2.x/itop-portal-base/portal/templates/bricks/browse/mode_mosaic.html.twig index 0bd7a1472..774ef8d4f 100644 --- a/datamodels/2.x/itop-portal-base/portal-silex/src/views/bricks/browse/mode_mosaic.html.twig +++ b/datamodels/2.x/itop-portal-base/portal/templates/bricks/browse/mode_mosaic.html.twig @@ -1,6 +1,6 @@ -{# itop-portal-base/portal/src/views/bricks/browse/mode_mosaic.html.twig #} +{# itop-portal-base/portal/templates/bricks/browse/mode_mosaic.html.twig #} {# Browse brick mosaic mode layout #} -{% extends 'itop-portal-base/portal/src/views/bricks/browse/layout.html.twig' %} +{% extends 'itop-portal-base/portal/templates/bricks/browse/layout.html.twig' %} {% block bBrowseMainContent %}
@@ -21,7 +21,7 @@
{% block bBrowseMosaicOverlay %}
- {% include 'itop-portal-base/portal/src/views/helpers/loader.html.twig' %} + {% include 'itop-portal-base/portal/templates/helpers/loader.html.twig' %}
{% endblock %}
diff --git a/datamodels/2.x/itop-portal-base/portal-silex/src/views/bricks/browse/mode_tree.html.twig b/datamodels/2.x/itop-portal-base/portal/templates/bricks/browse/mode_tree.html.twig similarity index 98% rename from datamodels/2.x/itop-portal-base/portal-silex/src/views/bricks/browse/mode_tree.html.twig rename to datamodels/2.x/itop-portal-base/portal/templates/bricks/browse/mode_tree.html.twig index 6e667fb40..1ecd1b988 100644 --- a/datamodels/2.x/itop-portal-base/portal-silex/src/views/bricks/browse/mode_tree.html.twig +++ b/datamodels/2.x/itop-portal-base/portal/templates/bricks/browse/mode_tree.html.twig @@ -1,6 +1,6 @@ -{# itop-portal-base/portal/src/views/bricks/browse/mode_tree.html.twig #} +{# itop-portal-base/portal/templates/bricks/browse/mode_tree.html.twig #} {# Browse brick tree mode layout #} -{% extends 'itop-portal-base/portal/src/views/bricks/browse/layout.html.twig' %} +{% extends 'itop-portal-base/portal/templates/bricks/browse/layout.html.twig' %} {# Documentation : @@ -36,7 +36,7 @@
- {% include 'itop-portal-base/portal/src/views/helpers/loader.html.twig' %} + {% include 'itop-portal-base/portal/templates/helpers/loader.html.twig' %}
{% endblock %} diff --git a/datamodels/2.x/itop-portal-base/portal-silex/src/views/bricks/create/modal.html.twig b/datamodels/2.x/itop-portal-base/portal/templates/bricks/create/modal.html.twig similarity index 93% rename from datamodels/2.x/itop-portal-base/portal-silex/src/views/bricks/create/modal.html.twig rename to datamodels/2.x/itop-portal-base/portal/templates/bricks/create/modal.html.twig index 256b6c9c1..255c4d366 100644 --- a/datamodels/2.x/itop-portal-base/portal-silex/src/views/bricks/create/modal.html.twig +++ b/datamodels/2.x/itop-portal-base/portal/templates/bricks/create/modal.html.twig @@ -1,6 +1,6 @@ -{# itop-portal-base/portal/src/views/bricks/create/layout.html.twig #} +{# itop-portal-base/portal/templates/bricks/create/layout.html.twig #} {# Create brick base layout #} -{% extends 'itop-portal-base/portal/src/views/modal/layout.html.twig' %} +{% extends 'itop-portal-base/portal/templates/modal/layout.html.twig' %} {% block pModalTitle %} {{ oBrick.GetTitle()|dict_s }} diff --git a/datamodels/2.x/itop-portal-base/portal-silex/src/views/bricks/filter/tile.html.twig b/datamodels/2.x/itop-portal-base/portal/templates/bricks/filter/tile.html.twig similarity index 96% rename from datamodels/2.x/itop-portal-base/portal-silex/src/views/bricks/filter/tile.html.twig rename to datamodels/2.x/itop-portal-base/portal/templates/bricks/filter/tile.html.twig index 95cd02d8f..52d2f6bf3 100644 --- a/datamodels/2.x/itop-portal-base/portal-silex/src/views/bricks/filter/tile.html.twig +++ b/datamodels/2.x/itop-portal-base/portal/templates/bricks/filter/tile.html.twig @@ -1,4 +1,4 @@ -{# itop-portal-base/portal/src/views/bricks/filter/tile.html.twig #} +{# itop-portal-base/portal/templates/bricks/filter/tile.html.twig #} {# Filter brick tile layout #} {% if brick.GetTargetBrickClass == 'Combodo\\iTop\\Portal\\Brick\\BrowseBrick' %} diff --git a/datamodels/2.x/itop-portal-base/portal-silex/src/views/bricks/layout.html.twig b/datamodels/2.x/itop-portal-base/portal/templates/bricks/layout.html.twig similarity index 96% rename from datamodels/2.x/itop-portal-base/portal-silex/src/views/bricks/layout.html.twig rename to datamodels/2.x/itop-portal-base/portal/templates/bricks/layout.html.twig index e17bb6952..f1cd8c439 100644 --- a/datamodels/2.x/itop-portal-base/portal-silex/src/views/bricks/layout.html.twig +++ b/datamodels/2.x/itop-portal-base/portal/templates/bricks/layout.html.twig @@ -1,4 +1,4 @@ -{# itop-portal-base/portal/src/views/bricks/layout.html.twig #} +{# itop-portal-base/portal/templates/bricks/layout.html.twig #} {# Brick base layout #} {% extends app['combodo.portal.instance.conf'].properties.templates.layout %} diff --git a/datamodels/2.x/itop-portal-base/portal-silex/src/views/bricks/manage/layout-chart.html.twig b/datamodels/2.x/itop-portal-base/portal/templates/bricks/manage/layout-chart.html.twig similarity index 69% rename from datamodels/2.x/itop-portal-base/portal-silex/src/views/bricks/manage/layout-chart.html.twig rename to datamodels/2.x/itop-portal-base/portal/templates/bricks/manage/layout-chart.html.twig index 9f0667782..0684eea1b 100644 --- a/datamodels/2.x/itop-portal-base/portal-silex/src/views/bricks/manage/layout-chart.html.twig +++ b/datamodels/2.x/itop-portal-base/portal/templates/bricks/manage/layout-chart.html.twig @@ -1,6 +1,6 @@ -{# itop-portal-base/portal/src/views/bricks/manage/layout-chart.html.twig #} +{# itop-portal-base/portal/templates/bricks/manage/layout-chart.html.twig #} {# Manage brick base layout #} -{% extends 'itop-portal-base/portal/src/views/bricks/manage/layout.html.twig' %} +{% extends 'itop-portal-base/portal/templates/bricks/manage/layout.html.twig' %} {% block pPageBodyClass %}{{ parent() }} page_manage_brick{% endblock %} @@ -8,7 +8,7 @@ {% block pMainContentHolder %}
- {% include 'itop-portal-base/portal/src/views/bricks/manage/mode-' ~ sDisplayMode ~ '.html.twig' with {'oBrick': oBrick, 'aColumns': aColumns, 'aNames': aNames, 'aUrls': aUrls, 'aDisplayValues': aDisplayValues} %} + {% include 'itop-portal-base/portal/templates/bricks/manage/mode-' ~ sDisplayMode ~ '.html.twig' with {'oBrick': oBrick, 'aColumns': aColumns, 'aNames': aNames, 'aUrls': aUrls, 'aDisplayValues': aDisplayValues} %}
{% endblock %} diff --git a/datamodels/2.x/itop-portal-base/portal-silex/src/views/bricks/manage/layout-table.html.twig b/datamodels/2.x/itop-portal-base/portal/templates/bricks/manage/layout-table.html.twig similarity index 99% rename from datamodels/2.x/itop-portal-base/portal-silex/src/views/bricks/manage/layout-table.html.twig rename to datamodels/2.x/itop-portal-base/portal/templates/bricks/manage/layout-table.html.twig index 80a14a764..dd2b3f9b6 100644 --- a/datamodels/2.x/itop-portal-base/portal-silex/src/views/bricks/manage/layout-table.html.twig +++ b/datamodels/2.x/itop-portal-base/portal/templates/bricks/manage/layout-table.html.twig @@ -1,6 +1,6 @@ -{# itop-portal-base/portal/src/views/bricks/manage/layout-table.html.twig #} +{# itop-portal-base/portal/templates/bricks/manage/layout-table.html.twig #} {# Manage brick base layout #} -{% extends 'itop-portal-base/portal/src/views/bricks/manage/layout.html.twig' %} +{% extends 'itop-portal-base/portal/templates/bricks/manage/layout.html.twig' %} {% block pPageBodyClass %}{{ parent() }} page_manage_brick{% endblock %} diff --git a/datamodels/2.x/itop-portal-base/portal-silex/src/views/bricks/manage/layout.html.twig b/datamodels/2.x/itop-portal-base/portal/templates/bricks/manage/layout.html.twig similarity index 84% rename from datamodels/2.x/itop-portal-base/portal-silex/src/views/bricks/manage/layout.html.twig rename to datamodels/2.x/itop-portal-base/portal/templates/bricks/manage/layout.html.twig index 08310935a..5b39f7ef1 100644 --- a/datamodels/2.x/itop-portal-base/portal-silex/src/views/bricks/manage/layout.html.twig +++ b/datamodels/2.x/itop-portal-base/portal/templates/bricks/manage/layout.html.twig @@ -1,5 +1,5 @@ -{# itop-portal-base/portal/src/views/bricks/manage/layout.html.twig #} -{% extends 'itop-portal-base/portal/src/views/bricks/layout.html.twig' %} +{# itop-portal-base/portal/templates/bricks/manage/layout.html.twig #} +{% extends 'itop-portal-base/portal/templates/bricks/layout.html.twig' %} {% block pMainHeaderTitle %}{{ oBrick.GetTitle()|dict_s }} ({{ iCount }}) {% endblock %} @@ -19,5 +19,5 @@ {% block pPageReadyScripts %} {{ parent() }} - {% include 'itop-portal-base/portal/src/views/helpers/tagset_clic_handler.js.twig' %} + {% include 'itop-portal-base/portal/templates/helpers/tagset_clic_handler.js.twig' %} {% endblock %} diff --git a/datamodels/2.x/itop-portal-base/portal-silex/src/views/bricks/manage/mode-bar-chart.html.twig b/datamodels/2.x/itop-portal-base/portal/templates/bricks/manage/mode-bar-chart.html.twig similarity index 100% rename from datamodels/2.x/itop-portal-base/portal-silex/src/views/bricks/manage/mode-bar-chart.html.twig rename to datamodels/2.x/itop-portal-base/portal/templates/bricks/manage/mode-bar-chart.html.twig diff --git a/datamodels/2.x/itop-portal-base/portal-silex/src/views/bricks/manage/mode-pie-chart.html.twig b/datamodels/2.x/itop-portal-base/portal/templates/bricks/manage/mode-pie-chart.html.twig similarity index 100% rename from datamodels/2.x/itop-portal-base/portal-silex/src/views/bricks/manage/mode-pie-chart.html.twig rename to datamodels/2.x/itop-portal-base/portal/templates/bricks/manage/mode-pie-chart.html.twig diff --git a/datamodels/2.x/itop-portal-base/portal-silex/src/views/bricks/manage/popup-export-excel.html.twig b/datamodels/2.x/itop-portal-base/portal/templates/bricks/manage/popup-export-excel.html.twig similarity index 100% rename from datamodels/2.x/itop-portal-base/portal-silex/src/views/bricks/manage/popup-export-excel.html.twig rename to datamodels/2.x/itop-portal-base/portal/templates/bricks/manage/popup-export-excel.html.twig diff --git a/datamodels/2.x/itop-portal-base/portal-silex/src/views/bricks/manage/tile-badge.html.twig b/datamodels/2.x/itop-portal-base/portal/templates/bricks/manage/tile-badge.html.twig similarity index 100% rename from datamodels/2.x/itop-portal-base/portal-silex/src/views/bricks/manage/tile-badge.html.twig rename to datamodels/2.x/itop-portal-base/portal/templates/bricks/manage/tile-badge.html.twig diff --git a/datamodels/2.x/itop-portal-base/portal-silex/src/views/bricks/manage/tile-chart.html.twig b/datamodels/2.x/itop-portal-base/portal/templates/bricks/manage/tile-chart.html.twig similarity index 95% rename from datamodels/2.x/itop-portal-base/portal-silex/src/views/bricks/manage/tile-chart.html.twig rename to datamodels/2.x/itop-portal-base/portal/templates/bricks/manage/tile-chart.html.twig index 987522526..1e6b89245 100644 --- a/datamodels/2.x/itop-portal-base/portal-silex/src/views/bricks/manage/tile-chart.html.twig +++ b/datamodels/2.x/itop-portal-base/portal/templates/bricks/manage/tile-chart.html.twig @@ -12,7 +12,7 @@
{{ oBrick.GetTitle()|dict_s }} ({{ iCount }})
- {% include 'itop-portal-base/portal/src/views/bricks/manage/mode-' ~ oBrick.GetTileMode ~ '.html.twig' with {'oBrick': oBrick, 'aColumns': aColumns, 'aNames': aNames, 'aUrls': aUrls, 'aDisplayValues': aDisplayValues} %} + {% include 'itop-portal-base/portal/templates/bricks/manage/mode-' ~ oBrick.GetTileMode ~ '.html.twig' with {'oBrick': oBrick, 'aColumns': aColumns, 'aNames': aNames, 'aUrls': aUrls, 'aDisplayValues': aDisplayValues} %}
{% endblock %} diff --git a/datamodels/2.x/itop-portal-base/portal-silex/src/views/bricks/manage/tile-default.html.twig b/datamodels/2.x/itop-portal-base/portal/templates/bricks/manage/tile-default.html.twig similarity index 95% rename from datamodels/2.x/itop-portal-base/portal-silex/src/views/bricks/manage/tile-default.html.twig rename to datamodels/2.x/itop-portal-base/portal/templates/bricks/manage/tile-default.html.twig index 91bfb4a24..3cd643366 100644 --- a/datamodels/2.x/itop-portal-base/portal-silex/src/views/bricks/manage/tile-default.html.twig +++ b/datamodels/2.x/itop-portal-base/portal/templates/bricks/manage/tile-default.html.twig @@ -1,4 +1,4 @@ -{# itop-portal-base/portal/src/views/bricks/tile.html.twig #} +{# itop-portal-base/portal/templates/bricks/tile.html.twig #} {# Base brick tile layout #}
diff --git a/datamodels/2.x/itop-portal-base/portal-silex/src/views/bricks/manage/tile-top-list.html.twig b/datamodels/2.x/itop-portal-base/portal/templates/bricks/manage/tile-top-list.html.twig similarity index 100% rename from datamodels/2.x/itop-portal-base/portal-silex/src/views/bricks/manage/tile-top-list.html.twig rename to datamodels/2.x/itop-portal-base/portal/templates/bricks/manage/tile-top-list.html.twig diff --git a/datamodels/2.x/itop-portal-base/portal-silex/src/views/bricks/object/layout.html.twig b/datamodels/2.x/itop-portal-base/portal/templates/bricks/object/layout.html.twig similarity index 71% rename from datamodels/2.x/itop-portal-base/portal-silex/src/views/bricks/object/layout.html.twig rename to datamodels/2.x/itop-portal-base/portal/templates/bricks/object/layout.html.twig index e0bb90c10..c9123d57a 100644 --- a/datamodels/2.x/itop-portal-base/portal-silex/src/views/bricks/object/layout.html.twig +++ b/datamodels/2.x/itop-portal-base/portal/templates/bricks/object/layout.html.twig @@ -1,6 +1,6 @@ -{# itop-portal-base/portal/src/views/bricks/object/layout.html.twig #} +{# itop-portal-base/portal/templates/bricks/object/layout.html.twig #} {# Object brick base layout #} -{% extends 'itop-portal-base/portal/src/views/bricks/layout.html.twig' %} +{% extends 'itop-portal-base/portal/templates/bricks/layout.html.twig' %} {% block pPageBodyClass %}{{ parent() }} page_object_brick page_object_brick_as_{{ sMode }}{% endblock %} @@ -15,7 +15,7 @@ {% block pMainContentHolder%}
- {% include 'itop-portal-base/portal/src/views/bricks/object/mode_' ~ sMode ~ '.html.twig' %} + {% include 'itop-portal-base/portal/templates/bricks/object/mode_' ~ sMode ~ '.html.twig' %}
{% endblock %} \ No newline at end of file diff --git a/datamodels/2.x/itop-portal-base/portal-silex/src/views/bricks/object/modal.html.twig b/datamodels/2.x/itop-portal-base/portal/templates/bricks/object/modal.html.twig similarity index 55% rename from datamodels/2.x/itop-portal-base/portal-silex/src/views/bricks/object/modal.html.twig rename to datamodels/2.x/itop-portal-base/portal/templates/bricks/object/modal.html.twig index 04fb7b9f7..5a2d41729 100644 --- a/datamodels/2.x/itop-portal-base/portal-silex/src/views/bricks/object/modal.html.twig +++ b/datamodels/2.x/itop-portal-base/portal/templates/bricks/object/modal.html.twig @@ -1,6 +1,6 @@ -{# itop-portal-base/portal/src/views/bricks/object/modal.html.twig #} +{# itop-portal-base/portal/templates/bricks/object/modal.html.twig #} {# Object brick base layout #} -{% extends 'itop-portal-base/portal/src/views/modal/layout.html.twig' %} +{% extends 'itop-portal-base/portal/templates/modal/layout.html.twig' %} {% block pModalTitle %} {% if form.title is defined %} @@ -9,5 +9,5 @@ {% endblock %} {% block pModalBody %} - {% include 'itop-portal-base/portal/src/views/bricks/object/mode_' ~ sMode ~ '.html.twig' with {tIsModal: true} %} + {% include 'itop-portal-base/portal/templates/bricks/object/mode_' ~ sMode ~ '.html.twig' with {tIsModal: true} %} {% endblock %} \ No newline at end of file diff --git a/datamodels/2.x/itop-portal-base/portal-silex/src/views/bricks/object/mode_apply_stimulus.html.twig b/datamodels/2.x/itop-portal-base/portal/templates/bricks/object/mode_apply_stimulus.html.twig similarity index 62% rename from datamodels/2.x/itop-portal-base/portal-silex/src/views/bricks/object/mode_apply_stimulus.html.twig rename to datamodels/2.x/itop-portal-base/portal/templates/bricks/object/mode_apply_stimulus.html.twig index aff1f18c9..918c75e22 100644 --- a/datamodels/2.x/itop-portal-base/portal-silex/src/views/bricks/object/mode_apply_stimulus.html.twig +++ b/datamodels/2.x/itop-portal-base/portal/templates/bricks/object/mode_apply_stimulus.html.twig @@ -1,5 +1,5 @@ -{# itop-portal-base/portal/src/views/bricks/object/mode_apply_stimulus.html.twig #} +{# itop-portal-base/portal/templates/bricks/object/mode_apply_stimulus.html.twig #} {# Object brick apply stimulus layout #} -{% extends 'itop-portal-base/portal/src/views/bricks/object/mode_create.html.twig' %} +{% extends 'itop-portal-base/portal/templates/bricks/object/mode_create.html.twig' %} {# This layout is exactly the same as the mode_create.html.twig, we duplicated it in case we need to have some subtle differences #} diff --git a/datamodels/2.x/itop-portal-base/portal-silex/src/views/bricks/object/mode_create.html.twig b/datamodels/2.x/itop-portal-base/portal/templates/bricks/object/mode_create.html.twig similarity index 98% rename from datamodels/2.x/itop-portal-base/portal-silex/src/views/bricks/object/mode_create.html.twig rename to datamodels/2.x/itop-portal-base/portal/templates/bricks/object/mode_create.html.twig index 02451d0c3..22f0ccfb4 100644 --- a/datamodels/2.x/itop-portal-base/portal-silex/src/views/bricks/object/mode_create.html.twig +++ b/datamodels/2.x/itop-portal-base/portal/templates/bricks/object/mode_create.html.twig @@ -1,4 +1,4 @@ -{# itop-portal-base/portal/src/views/bricks/object/mode_create.html.twig #} +{# itop-portal-base/portal/templates/bricks/object/mode_create.html.twig #} {# Object brick create layout #} {% set tIsModal = (tIsModal is defined and tIsModal == true) ? true : false %} @@ -30,7 +30,7 @@ {# Misc. buttons #} {% if form.buttons is defined and (form.buttons.actions is defined or form.buttons.links is defined) %}
- {% include 'itop-portal-base/portal/src/views/bricks/object/plugins_buttons.html.twig' with {'aButtons': form.buttons} %} + {% include 'itop-portal-base/portal/templates/bricks/object/plugins_buttons.html.twig' with {'aButtons': form.buttons} %}
{% endif %} {# Transition buttons #} diff --git a/datamodels/2.x/itop-portal-base/portal-silex/src/views/bricks/object/mode_edit.html.twig b/datamodels/2.x/itop-portal-base/portal/templates/bricks/object/mode_edit.html.twig similarity index 60% rename from datamodels/2.x/itop-portal-base/portal-silex/src/views/bricks/object/mode_edit.html.twig rename to datamodels/2.x/itop-portal-base/portal/templates/bricks/object/mode_edit.html.twig index e68bf51f0..c04137e21 100644 --- a/datamodels/2.x/itop-portal-base/portal-silex/src/views/bricks/object/mode_edit.html.twig +++ b/datamodels/2.x/itop-portal-base/portal/templates/bricks/object/mode_edit.html.twig @@ -1,5 +1,5 @@ -{# itop-portal-base/portal/src/views/bricks/object/mode_create.html.twig #} +{# itop-portal-base/portal/templates/bricks/object/mode_create.html.twig #} {# Object brick edit layout #} -{% extends 'itop-portal-base/portal/src/views/bricks/object/mode_create.html.twig' %} +{% extends 'itop-portal-base/portal/templates/bricks/object/mode_create.html.twig' %} {# This layout is exactly the same as the mode_create.html.twig, we duplicated it in case we need to have some subtle differences #} \ No newline at end of file diff --git a/datamodels/2.x/itop-portal-base/portal-silex/src/views/bricks/object/mode_search_hierarchy.html.twig b/datamodels/2.x/itop-portal-base/portal/templates/bricks/object/mode_search_hierarchy.html.twig similarity index 96% rename from datamodels/2.x/itop-portal-base/portal-silex/src/views/bricks/object/mode_search_hierarchy.html.twig rename to datamodels/2.x/itop-portal-base/portal/templates/bricks/object/mode_search_hierarchy.html.twig index 53354deba..dde320ac3 100644 --- a/datamodels/2.x/itop-portal-base/portal-silex/src/views/bricks/object/mode_search_hierarchy.html.twig +++ b/datamodels/2.x/itop-portal-base/portal/templates/bricks/object/mode_search_hierarchy.html.twig @@ -1,4 +1,4 @@ -{# itop-portal-base/portal/src/views/bricks/object/mode_search_hierarchy.html.twig #} +{# itop-portal-base/portal/templates/bricks/object/mode_search_hierarchy.html.twig #} {# Object brick hierarchy search layout #} {% set sFormId = (form.id is defined and form.id is not null) ? form.id : 'object_search_form' %} diff --git a/datamodels/2.x/itop-portal-base/portal-silex/src/views/bricks/object/mode_search_regular.html.twig b/datamodels/2.x/itop-portal-base/portal/templates/bricks/object/mode_search_regular.html.twig similarity index 99% rename from datamodels/2.x/itop-portal-base/portal-silex/src/views/bricks/object/mode_search_regular.html.twig rename to datamodels/2.x/itop-portal-base/portal/templates/bricks/object/mode_search_regular.html.twig index 58d1005a3..414567b10 100644 --- a/datamodels/2.x/itop-portal-base/portal-silex/src/views/bricks/object/mode_search_regular.html.twig +++ b/datamodels/2.x/itop-portal-base/portal/templates/bricks/object/mode_search_regular.html.twig @@ -1,4 +1,4 @@ -{# itop-portal-base/portal/src/views/bricks/object/mode_search_regular.html.twig #} +{# itop-portal-base/portal/templates/bricks/object/mode_search_regular.html.twig #} {# Object brick regular search layout #} {% set sFormId = (form.id is defined and form.id is not null) ? form.id : 'object_search_form' %} diff --git a/datamodels/2.x/itop-portal-base/portal-silex/src/views/bricks/object/mode_view.html.twig b/datamodels/2.x/itop-portal-base/portal/templates/bricks/object/mode_view.html.twig similarity index 79% rename from datamodels/2.x/itop-portal-base/portal-silex/src/views/bricks/object/mode_view.html.twig rename to datamodels/2.x/itop-portal-base/portal/templates/bricks/object/mode_view.html.twig index d005a82e9..c737be88e 100644 --- a/datamodels/2.x/itop-portal-base/portal-silex/src/views/bricks/object/mode_view.html.twig +++ b/datamodels/2.x/itop-portal-base/portal/templates/bricks/object/mode_view.html.twig @@ -1,6 +1,6 @@ -{# itop-portal-base/portal/src/views/bricks/object/mode_view.html.twig #} +{# itop-portal-base/portal/templates/bricks/object/mode_view.html.twig #} {# Object brick view layout #} -{% extends 'itop-portal-base/portal/src/views/bricks/object/mode_create.html.twig' %} +{% extends 'itop-portal-base/portal/templates/bricks/object/mode_create.html.twig' %} {# This layout is exactly the same as the mode_create.html.twig, we duplicated it in case we need to have some subtle differences #} @@ -8,7 +8,7 @@ {# Misc. buttons #} {% if form.buttons is defined and (form.buttons.actions is defined or form.buttons.links is defined) %}
- {% include 'itop-portal-base/portal/src/views/bricks/object/plugins_buttons.html.twig' with {'aButtons': form.buttons} %} + {% include 'itop-portal-base/portal/templates/bricks/object/plugins_buttons.html.twig' with {'aButtons': form.buttons} %}
{% endif %} diff --git a/datamodels/2.x/itop-portal-base/portal-silex/src/views/bricks/object/plugins_buttons.html.twig b/datamodels/2.x/itop-portal-base/portal/templates/bricks/object/plugins_buttons.html.twig similarity index 100% rename from datamodels/2.x/itop-portal-base/portal-silex/src/views/bricks/object/plugins_buttons.html.twig rename to datamodels/2.x/itop-portal-base/portal/templates/bricks/object/plugins_buttons.html.twig diff --git a/datamodels/2.x/itop-portal-base/portal-silex/src/views/bricks/tile.html.twig b/datamodels/2.x/itop-portal-base/portal/templates/bricks/tile.html.twig similarity index 95% rename from datamodels/2.x/itop-portal-base/portal-silex/src/views/bricks/tile.html.twig rename to datamodels/2.x/itop-portal-base/portal/templates/bricks/tile.html.twig index fc7a4db26..cb5fd9975 100644 --- a/datamodels/2.x/itop-portal-base/portal-silex/src/views/bricks/tile.html.twig +++ b/datamodels/2.x/itop-portal-base/portal/templates/bricks/tile.html.twig @@ -1,4 +1,4 @@ -{# itop-portal-base/portal/src/views/bricks/tile.html.twig #} +{# itop-portal-base/portal/templates/bricks/tile.html.twig #} {# Base brick tile layout #}
diff --git a/datamodels/2.x/itop-portal-base/portal-silex/src/views/bricks/user-profile/layout.html.twig b/datamodels/2.x/itop-portal-base/portal/templates/bricks/user-profile/layout.html.twig similarity index 98% rename from datamodels/2.x/itop-portal-base/portal-silex/src/views/bricks/user-profile/layout.html.twig rename to datamodels/2.x/itop-portal-base/portal/templates/bricks/user-profile/layout.html.twig index 14ba83a50..2a11d265f 100644 --- a/datamodels/2.x/itop-portal-base/portal-silex/src/views/bricks/user-profile/layout.html.twig +++ b/datamodels/2.x/itop-portal-base/portal/templates/bricks/user-profile/layout.html.twig @@ -1,6 +1,6 @@ -{# itop-portal-base/portal/src/views/bricks/user-profile/layout.html.twig #} +{# itop-portal-base/portal/templates/bricks/user-profile/layout.html.twig #} {# User profile brick base layout #} -{% extends 'itop-portal-base/portal/src/views/bricks/layout.html.twig' %} +{% extends 'itop-portal-base/portal/templates/bricks/layout.html.twig' %} {% set oContactForm = forms.contact %} {% set oPreferencesForm = forms.preferences %} diff --git a/datamodels/2.x/itop-portal-base/portal-silex/src/views/errors/layout.html.twig b/datamodels/2.x/itop-portal-base/portal/templates/errors/layout.html.twig similarity index 96% rename from datamodels/2.x/itop-portal-base/portal-silex/src/views/errors/layout.html.twig rename to datamodels/2.x/itop-portal-base/portal/templates/errors/layout.html.twig index 415719be3..f4e9295cf 100644 --- a/datamodels/2.x/itop-portal-base/portal-silex/src/views/errors/layout.html.twig +++ b/datamodels/2.x/itop-portal-base/portal/templates/errors/layout.html.twig @@ -1,6 +1,6 @@ {# errors/layout.html.twig #} {# Base error layout #} -{% extends 'itop-portal-base/portal/src/views/layout.html.twig' %} +{% extends 'itop-portal-base/portal/templates/layout.html.twig' %} {% block pNavigationWrapper %} {% endblock %} diff --git a/datamodels/2.x/itop-portal-base/portal-silex/src/views/helpers/loader.html.twig b/datamodels/2.x/itop-portal-base/portal/templates/helpers/loader.html.twig similarity index 100% rename from datamodels/2.x/itop-portal-base/portal-silex/src/views/helpers/loader.html.twig rename to datamodels/2.x/itop-portal-base/portal/templates/helpers/loader.html.twig diff --git a/datamodels/2.x/itop-portal-base/portal-silex/src/views/helpers/tagset_clic_handler.js.twig b/datamodels/2.x/itop-portal-base/portal/templates/helpers/tagset_clic_handler.js.twig similarity index 100% rename from datamodels/2.x/itop-portal-base/portal-silex/src/views/helpers/tagset_clic_handler.js.twig rename to datamodels/2.x/itop-portal-base/portal/templates/helpers/tagset_clic_handler.js.twig diff --git a/datamodels/2.x/itop-portal-base/portal-silex/src/views/home/layout.html.twig b/datamodels/2.x/itop-portal-base/portal/templates/home/layout.html.twig similarity index 92% rename from datamodels/2.x/itop-portal-base/portal-silex/src/views/home/layout.html.twig rename to datamodels/2.x/itop-portal-base/portal/templates/home/layout.html.twig index 60f6f2ae4..183b8d94a 100644 --- a/datamodels/2.x/itop-portal-base/portal-silex/src/views/home/layout.html.twig +++ b/datamodels/2.x/itop-portal-base/portal/templates/home/layout.html.twig @@ -1,4 +1,4 @@ -{# itop-portal-base-base/portal/src/views/home/layout.html.twig #} +{# itop-portal-base-base/portal/templates/home/layout.html.twig #} {# Home layout #} {% extends app['combodo.portal.instance.conf'].properties.templates.layout %} diff --git a/datamodels/2.x/itop-portal-base/portal-silex/src/views/layout.html.twig b/datamodels/2.x/itop-portal-base/portal/templates/layout.html.twig similarity index 99% rename from datamodels/2.x/itop-portal-base/portal-silex/src/views/layout.html.twig rename to datamodels/2.x/itop-portal-base/portal/templates/layout.html.twig index c09adb756..6b0b3feac 100644 --- a/datamodels/2.x/itop-portal-base/portal-silex/src/views/layout.html.twig +++ b/datamodels/2.x/itop-portal-base/portal/templates/layout.html.twig @@ -354,7 +354,7 @@ @@ -382,7 +382,7 @@ {% block pPageOverlay %}
- {% include 'itop-portal-base/portal/src/views/helpers/loader.html.twig' %} + {% include 'itop-portal-base/portal/templates/helpers/loader.html.twig' %}
{% endblock %} diff --git a/datamodels/2.x/itop-portal-base/portal-silex/src/views/modal/layout.html.twig b/datamodels/2.x/itop-portal-base/portal/templates/modal/layout.html.twig similarity index 100% rename from datamodels/2.x/itop-portal-base/portal-silex/src/views/modal/layout.html.twig rename to datamodels/2.x/itop-portal-base/portal/templates/modal/layout.html.twig diff --git a/datamodels/2.x/itop-portal-base/portal-silex/src/views/modal/mode_loader.html.twig b/datamodels/2.x/itop-portal-base/portal/templates/modal/mode_loader.html.twig similarity index 62% rename from datamodels/2.x/itop-portal-base/portal-silex/src/views/modal/mode_loader.html.twig rename to datamodels/2.x/itop-portal-base/portal/templates/modal/mode_loader.html.twig index 7fa47ebe2..75c4afd6e 100644 --- a/datamodels/2.x/itop-portal-base/portal-silex/src/views/modal/mode_loader.html.twig +++ b/datamodels/2.x/itop-portal-base/portal/templates/modal/mode_loader.html.twig @@ -1,9 +1,9 @@ -{# itop-portal-base/portal/src/views/bricks/object/mode_loader.html.twig #} +{# itop-portal-base/portal/templates/bricks/object/mode_loader.html.twig #} {# Modal loader layout #} -{% extends 'itop-portal-base/portal/src/views/modal/layout.html.twig' %} +{% extends 'itop-portal-base/portal/templates/modal/layout.html.twig' %} {% block pModalContent %} - {% include 'itop-portal-base/portal/src/views/helpers/loader.html.twig' %} + {% include 'itop-portal-base/portal/templates/helpers/loader.html.twig' %} {% if redirection is defined and redirection.url is defined %}