Ver código fonte

N°642 Portal: Transitions configuration part 1.

git-svn-id: http://svn.code.sf.net/p/itop/code/trunk@4768 a333f486-631f-4898-b8df-5754b55c2be0
glajarige 8 anos atrás
pai
commit
8f96c1be4c

+ 10 - 6
datamodels/2.x/itop-portal-base/portal/src/controllers/objectcontroller.class.inc.php

@@ -487,12 +487,16 @@ class ObjectController extends AbstractController
 				$aStimuli = Metamodel::EnumStimuli($sObjectClass);
 				foreach ($oObject->EnumTransitions() as $sStimulusCode => $aTransitionDef)
 				{
-					$iActionAllowed = (get_class($aStimuli[$sStimulusCode]) == 'StimulusUserAction') ? UserRights::IsStimulusAllowed($sObjectClass, $sStimulusCode, $oSetToCheckRights) : UR_ALLOWED_NO;
-					// Careful, $iAction is an integer whereas UR_ALLOWED_YES is a boolean, therefore we can't use a '===' operator.
-					if ($iActionAllowed == UR_ALLOWED_YES)
-					{
-						$aFormData['buttons']['transitions'][$sStimulusCode] = $aStimuli[$sStimulusCode]->GetLabel();
-					}
+//					$iActionAllowed = (get_class($aStimuli[$sStimulusCode]) == 'StimulusUserAction') ? UserRights::IsStimulusAllowed($sObjectClass, $sStimulusCode, $oSetToCheckRights) : UR_ALLOWED_NO;
+//					// Careful, $iAction is an integer whereas UR_ALLOWED_YES is a boolean, therefore we can't use a '===' operator.
+//					if ($iActionAllowed == UR_ALLOWED_YES)
+//					{
+//						$aFormData['buttons']['transitions'][$sStimulusCode] = $aStimuli[$sStimulusCode]->GetLabel();
+//					}
+					if(SecurityHelper::IsStimulusAllowed($oApp, $sStimulusCode, $sObjectClass, $oSetToCheckRights))
+                    {
+                        $aFormData['buttons']['transitions'][$sStimulusCode] = $aStimuli[$sStimulusCode]->GetLabel();
+                    }
 				}
 
                 // Add plugin buttons

+ 25 - 12
datamodels/2.x/itop-portal-base/portal/src/helpers/applicationhelper.class.inc.php

@@ -447,6 +447,8 @@ class ApplicationHelper
 			$aPortalConf['forms'] = static::LoadFormsConfiguration($oApp, $oDesign);
 			// - Scopes
 			static::LoadScopesConfiguration($oApp, $oDesign);
+			// - Lifecycle
+            static::LoadLifecycleConfiguration($oApp, $oDesign);
 			// - Presentation lists
 			$aPortalConf['lists'] = static::LoadListsConfiguration($oApp, $oDesign);
 			// - Action rules
@@ -1001,18 +1003,29 @@ class ApplicationHelper
 		return $aForms;
 	}
 
-	/**
-	 * Loads the scopes configuration from the module design XML
-	 *
-	 * @param \Silex\Application $oApp
-	 * @param ModuleDesign $oDesign
-	 */
-	static protected function LoadScopesConfiguration(Application $oApp, ModuleDesign $oDesign)
-	{
-		$oApp['scope_validator']->Init($oDesign->GetNodes('/module_design/classes/class'));
-	}
-
-	/**
+    /**
+     * Loads the scopes configuration from the module design XML
+     *
+     * @param \Silex\Application $oApp
+     * @param ModuleDesign $oDesign
+     */
+    static protected 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
+     */
+    static protected 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

+ 101 - 319
datamodels/2.x/itop-portal-base/portal/src/helpers/lifecyclevalidatorhelper.class.inc.php

@@ -1,6 +1,6 @@
 <?php
 
-// Copyright (C) 2010-2015 Combodo SARL
+// Copyright (C) 2010-2017 Combodo SARL
 //
 //   This file is part of iTop.
 //
@@ -31,8 +31,6 @@ use \Combodo\iTop\DesignElement;
 
 class LifecycleValidatorHelper
 {
-	const ENUM_TYPE_ALLOW = 'allow';
-	const ENUM_TYPE_RESTRICT = 'restrict';
 	const DEFAULT_GENERATED_CLASS = 'PortalLifecycleValues';
 
 	protected $sCachePath;
@@ -41,11 +39,6 @@ class LifecycleValidatorHelper
 	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;
@@ -133,148 +126,93 @@ class LifecycleValidatorHelper
 
 		// 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))
+		if (1 || !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)
+            foreach ($oNodes as $oClassNode)
 			{
-				// retrieving mandatory class id attribute
+				// 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 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);
-							}
-							else
-							{
-								$oResFilter = $oViewFilter;
-							}
-							$aProfiles[$sMatrixPrefix . static::ENUM_MODE_READ] = array(
-								$sOqlViewType => $oResFilter->ToOQL()
-							);
-							// - 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()
-								);
-							}
-						}
-					}
 
-					$aProfileClasses[] = $sClass;
-				}
+				// Retrieving lifecycle node of the class
+                $oLifecycleNode = $oClassNode->GetOptionalElement('lifecycle');
+                if($oLifecycleNode !== null)
+                {
+                    // Iterating over scope nodes of the class
+                    $oStimuliNode = $oLifecycleNode->GetOptionalElement('stimuli');
+                    if ($oStimuliNode !== null)
+                    {
+                        foreach ($oStimuliNode->GetNodes('./stimulus') as $oStimulusNode)
+                        {
+                            // Retrieving mandatory scope id attribute
+                            $sStimulusId = $oStimulusNode->getAttribute('id');
+                            if ($sStimulusId === '')
+                            {
+                                throw new DOMFormatException('Stimulus tag must have an id attribute.', null, null, $oStimulusNode);
+                            }
+
+                            // Retrieving profiles for the stimulus
+                            $oProfilesNode = $oStimulusNode->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('Stimulus tag must have an id attribute.', null, null, $oProfileNode);
+                                    }
+                                    $aProfilesNames[] = $sProfileId;
+                                }
+                            }
+
+                            //
+                            foreach ($aProfilesNames as $sProfileName)
+                            {
+                                // Stimulus 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;
+                                // - Creating profile / class entry if not already present
+                                if(!array_key_exists($sMatrixPrefix, $aProfiles))
+                                {
+                                    $aProfiles[$sMatrixPrefix] = array();
+                                }
+                                // - Adding stimulus if not already present
+                                if(!in_array($sStimulusId, $aProfiles[$sMatrixPrefix]))
+                                {
+                                    $aProfiles[$sMatrixPrefix][] = $sStimulusId;
+                                }
+                            }
+                        }
+
+                        $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
+			// Filling the array with missing classes from MetaModel, so we can have an inheritance principle on the stimuli
+			// For each class explicitly given in the stimuli, we check if its child classes were also in the stimuli :
+			// If not, we add them
 			foreach ($aProfileClasses as $sProfileClass)
 			{
 				foreach (MetaModel::EnumChildClasses($sProfileClass) as $sChildClass)
@@ -285,137 +223,17 @@ class LifecycleValidatorHelper
 						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)
-									{
-										$oTmpFilter = DBSearch::FromOQL($sOql);
-										$oTmpFilter->ChangeClass($sChildClass);
-
-										$aTmpProfile[$sType] = $oTmpFilter->ToOQL();
-									}
-
-									$aProfiles[$iProfileId . '_' . $sChildClass . '_' . $sAction] = $aTmpProfile;
-								}
-							}
+
+                            // If the current profile has scope for that class in that mode, we duplicate it
+                            if (isset($aProfiles[$iProfileId . '_' . $sProfileClass]))
+                            {
+                                $aProfiles[$iProfileId . '_' . $sChildClass] = $aProfiles[$iProfileId . '_' . $sProfileClass];
+                            }
 						}
 					}
 				}
 			}
 
-			// Iterating over the scope nodes
-			/* foreach ($oNodes as $oScopeNode)
-			  {
-			  // Retrieving mandatory id attribute
-			  $sProfile = $oScopeNode->getAttribute('id');
-			  if ($sProfile === '')
-			  {
-			  throw new DOMFormatException('Scope tag must have an id attribute.', null, null, $oScopeNode);
-			  }
-
-			  // Scope profile id
-			  $iProfileId = $this->GetProfileIdFromProfileName($sProfile);
-			  // 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 of the scope
-			  foreach ($oScopeNode->GetUniqueElement('classes')->GetNodes('./class') as $oClassNode)
-			  {
-			  // Retrieving mandatory id attribute
-			  $sClass = $oClassNode->getAttribute('id');
-			  if ($sClass === '')
-			  {
-			  throw new DOMFormatException('Class tag must have an id attribute.', null, null, $oClassNode);
-			  }
-
-			  // Retrieving the type of query
-			  $oOqlViewTypeNode = $oClassNode->GetOptionalElement('oql_view_type');
-			  $sOqlViewType = ($oOqlViewTypeNode !== null && ($oOqlViewTypeNode->GetText() === static::ENUM_TYPE_RESTRICT)) ? static::ENUM_TYPE_RESTRICT : static::ENUM_TYPE_ALLOW;
-			  // Retrieving the view query
-			  $oOqlViewNode = $oClassNode->GetUniqueElement('oql_view');
-			  $sOqlView = $oOqlViewNode->GetText();
-			  if ($sOqlView === null)
-			  {
-			  throw new DOMFormatException('Class tag in scope must have a not empty oql_view tag', null, null, $oClassNode);
-			  }
-			  // Retrieving the edit query
-			  $oOqlEditNode = $oClassNode->GetOptionalElement('oql_edit');
-			  $sOqlEdit = ( ($oOqlEditNode !== null) && ($oOqlEditNode->GetText() !== null) ) ? $oOqlEditNode->GetText() : null;
-
-			  // 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);
-			  $aProfiles[$sMatrixPrefix . 'r'] = array(
-			  $sOqlViewType => $oViewFilter->ToOQL()
-			  );
-			  // - 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);
-			  }
-			  }
-
-			  $aProfiles[$sMatrixPrefix . 'w'] = array(
-			  $sOqlViewType => $oEditFilter->ToOQL()
-			  );
-			  }
-
-			  $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 (array('r', 'w') 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)
-			  {
-			  $oTmpFilter = DBSearch::FromOQL($sOql);
-			  $oTmpFilter->ChangeClass($sChildClass);
-
-			  $aTmpProfile[$sType] = $oTmpFilter->ToOQL();
-			  }
-
-			  $aProfiles[$iProfileId . '_' . $sChildClass . '_' . $sAction] = $aTmpProfile;
-			  }
-			  }
-			  }
-			  }
-			  }
-			  } */
-
 			// - Build php class
 			$sPHP = $this->BuildPHPClass($aProfiles);
 
@@ -443,77 +261,42 @@ class LifecycleValidatorHelper
 	}
 
 	/**
-	 * Returns the DBSearch for the $sProfile in $iAction for the class $sClass
+	 * Returns an array of available stimuli for the $sProfile for the class $sClass
 	 *
 	 * @param string $sProfile
 	 * @param string $sClass
-	 * @param integer $iAction
 	 * @return DBSearch
 	 */
-	public function GetScopeFilterForProfile($sProfile, $sClass, $iAction = null)
+	public function GetStimuliForProfile($sProfile, $sClass)
 	{
-		return $this->GetScopeFilterForProfiles(array($sProfile), $sClass, $iAction);
+		return $this->GetStimuliForProfiles(array($sProfile), $sClass);
 	}
 
 	/**
-	 * Returns the DBSearch for the $aProfiles in $iAction for the class $sClass.
+	 * Returns an array of available stimuli for the $aProfiles for the class $sClass.
 	 * Profiles are a OR condition.
 	 *
 	 * @param array $aProfiles
 	 * @param string $sClass
-	 * @param integer $iAction
 	 * @return DBSearch
 	 */
-	public function GetScopeFilterForProfiles($aProfiles, $sClass, $iAction = null)
+	public function GetStimuliForProfiles($aProfiles, $sClass)
 	{
-		$oSearch = null;
-		$aAllowSearches = array();
-		$aRestrictSearches = array();
+		$aStimuli = array();
 
-		// 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']);
-				}
-			}
-		}
 
-		// 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();
+			// Retrieving profile stimuli
+			$sLifecycleValuesClass = $this->sGeneratedClass;
+			$aProfileMatrix = $sLifecycleValuesClass::GetProfileStimuli($iProfileId, $sClass);
+			$aStimuli = array_merge_recursive($aStimuli, $aProfileMatrix);
 		}
 
-		return $oSearch;
+		return $aStimuli;
 	}
 
 	/**
@@ -550,13 +333,13 @@ class LifecycleValidatorHelper
 		// - 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');
+			throw new Exception('Lifecycle validator : Unknown UserRights addon, lifecycle\'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');
+			throw new Exception('Lifecycle validator : Could not find "' . $sProfile . '" in the profiles list');
 		}
 
 		return $iProfileId;
@@ -575,11 +358,11 @@ class LifecycleValidatorHelper
 		$sPHP = <<<EOF
 <?php
 
-// File generated by LifeCycleValidatorHelperHelper
+// File generated by LifeCycleValidatorHelper
 //
 // Please do not edit manually
-// List of constant scopes
-// - used by the portal LifecycleValidatorHelperHelper
+// List of constant lifecycles
+// - used by the portal LifecycleValidatorHelper
 //
 class $sClassName
 {
@@ -589,17 +372,17 @@ class $sClassName
 	* @param integer \$iProfileId
 	* @param string \$sClass
 	*/
-	public static function GetTransitionProfileScope(\$iProfileId, \$sClass, \$sStimulusCode)
+	public static function GetProfileStimuli(\$iProfileId, \$sClass)
 	{
-		\$sQuery = null;
+		\$aStimuli = array();
 
-		\$sScopeKey = \$iProfileId.'_'.\$sClass.'_'.\$sAction;
-		if (isset(self::\$aPROFILES[\$sScopeKey]))
+		\$sLifecycleKey = \$iProfileId.'_'.\$sClass;
+		if (isset(self::\$aPROFILES[\$sLifecycleKey]))
 		{
-			\$sQuery = self::\$aPROFILES[\$sScopeKey];
+			\$aStimuli = self::\$aPROFILES[\$sLifecycleKey];
 		}
 
-		return \$sQuery;
+		return \$aStimuli;
 	}
 }
 
@@ -609,4 +392,3 @@ EOF;
 
 }
 
-?>

+ 3 - 4
datamodels/2.x/itop-portal-base/portal/src/helpers/scopevalidatorhelper.class.inc.php

@@ -1,6 +1,6 @@
 <?php
 
-// Copyright (C) 2010-2015 Combodo SARL
+// Copyright (C) 2010-2017 Combodo SARL
 //
 //   This file is part of iTop.
 //
@@ -636,11 +636,11 @@ class ScopeValidatorHelper
 		$sPHP = <<<EOF
 <?php
 
-// File generated by ScopeValidatorHelperHelper
+// File generated by ScopeValidatorHelper
 //
 // Please do not edit manually
 // List of constant scopes
-// - used by the portal ScopeValidatorHelperHelper
+// - used by the portal ScopeValidatorHelper
 //
 class $sClassName
 {
@@ -671,4 +671,3 @@ EOF;
 
 }
 
-?>

+ 16 - 2
datamodels/2.x/itop-portal-base/portal/src/helpers/securityhelper.class.inc.php

@@ -157,8 +157,22 @@ class SecurityHelper
 
 	public static function IsStimulusAllowed(Application $oApp, $sStimulusCode, $sObjectClass, $oInstanceSet = null)
 	{
-		$aStimuli = Metamodel::EnumStimuli($sObjectClass);
-		$iActionAllowed = (get_class($aStimuli[$sStimulusCode]) == 'StimulusUserAction') ? UserRights::IsStimulusAllowed($sObjectClass, $sStimulusCode, $oInstanceSet) : UR_ALLOWED_NO;
+	    // 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 = $oApp['lifecycle_validator']->GetStimuliForProfiles(UserRights::ListProfiles(), $sObjectClass);
+		if(!in_array($sStimulusCode, $aStimuliFromPortal))
+        {
+            return false;
+        }
+
+        return true;
 	}
 
     /**

+ 55 - 0
datamodels/2.x/itop-portal-base/portal/src/providers/lifecyclevalidatorserviceprovider.class.inc.php

@@ -0,0 +1,55 @@
+<?php
+
+// Copyright (C) 2010-2017 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 <http://www.gnu.org/licenses/>
+
+namespace Combodo\iTop\Portal\Provider;
+
+use Silex\Application;
+use Silex\ServiceProviderInterface;
+use Combodo\iTop\Portal\Helper\LifecycleValidatorHelper;
+
+/**
+ * LifecycleValidatorHelper service provider
+ *
+ * @author Guillaume Lajarige <guillaume.lajarige@combodo.com>
+ */
+class LifecycleValidatorServiceProvider implements ServiceProviderInterface
+{
+
+	public function register(Application $oApp)
+	{
+		$oApp['lifecycle_validator'] = $oApp->share(function ($oApp)
+		{
+			$oApp->flush();
+
+			$oLifecycleValidatorHelper = new LifecycleValidatorHelper($oApp['lifecycle_validator.lifecycle_filename'], $oApp['lifecycle_validator.lifecycle_path']);
+			if (isset($oApp['lifecycle_validator.instance_name']))
+			{
+                $oLifecycleValidatorHelper->SetInstancePrefix($oApp['lifecycle_validator.instance_name'] . '-');
+			}
+
+			return $oLifecycleValidatorHelper;
+		});
+	}
+
+	public function boot(Application $oApp)
+	{
+
+	}
+
+}

+ 10 - 3
datamodels/2.x/itop-portal-base/portal/web/index.php

@@ -38,6 +38,8 @@ require_once __DIR__ . '/../src/providers/contextmanipulatorserviceprovider.clas
 require_once __DIR__ . '/../src/helpers/contextmanipulatorhelper.class.inc.php';
 require_once __DIR__ . '/../src/providers/scopevalidatorserviceprovider.class.inc.php';
 require_once __DIR__ . '/../src/helpers/scopevalidatorhelper.class.inc.php';
+require_once __DIR__ . '/../src/providers/lifecyclevalidatorserviceprovider.class.inc.php';
+require_once __DIR__ . '/../src/helpers/lifecyclevalidatorhelper.class.inc.php';
 require_once __DIR__ . '/../src/helpers/securityhelper.class.inc.php';
 require_once __DIR__ . '/../src/helpers/applicationhelper.class.inc.php';
 
@@ -63,9 +65,14 @@ $oApp = new Application();
 $oApp->register(new Combodo\iTop\Portal\Provider\UrlGeneratorServiceProvider());
 $oApp->register(new Combodo\iTop\Portal\Provider\ContextManipulatorServiceProvider());
 $oApp->register(new Combodo\iTop\Portal\Provider\ScopeValidatorServiceProvider(), array(
-	'scope_validator.scopes_path' => utils::GetCachePath(),
-	'scope_validator.scopes_filename' => PORTAL_ID . '.scopes.php',
-	'scope_validator.instance_name' => PORTAL_ID
+    'scope_validator.scopes_path' => utils::GetCachePath(),
+    'scope_validator.scopes_filename' => PORTAL_ID . '.scopes.php',
+    'scope_validator.instance_name' => PORTAL_ID
+));
+$oApp->register(new Combodo\iTop\Portal\Provider\LifecycleValidatorServiceProvider(), array(
+    'lifecycle_validator.lifecycle_path' => utils::GetCachePath(),
+    'lifecycle_validator.lifecycle_filename' => PORTAL_ID . '.lifecycle.php',
+    'lifecycle_validator.instance_name' => PORTAL_ID
 ));
 $oApp->register(new Silex\Provider\TwigServiceProvider(), array(
 	'twig.path' => MODULESROOT,

+ 0 - 0
log/.htaccess → log/.htaccess_