Portal: Performance optimization on ManageBrick

SVN:trunk[4718]
This commit is contained in:
Guillaume Lajarige
2017-05-03 12:39:25 +00:00
parent f9ed88a084
commit 36c53249a0
2 changed files with 124 additions and 4 deletions

View File

@@ -19,6 +19,7 @@
namespace Combodo\iTop\Portal\Controller;
use Combodo\iTop\Portal\Helper\ScopeValidatorHelper;
use \Silex\Application;
use \Symfony\Component\HttpFoundation\Request;
use \UserRights;
@@ -267,8 +268,8 @@ class ManageBrickController extends BrickController
}
// Restricting query to allowed scope on each classes
// Note : Will need to moved the scope restriction on queries elsewhere when we consider grouping on something else than finalclass
// Note : We now get view scope instead of edit scope as we allowed users to view/edit objects in the brick regarding their rights
// Note: Will need to moved the scope restriction on queries elsewhere when we consider grouping on something else than finalclass
// Note: We now get view scope instead of edit scope as we allowed users to view/edit objects in the brick regarding their rights
$oScopeQuery = $oApp['scope_validator']->GetScopeFilterForProfiles(UserRights::ListProfiles(), $aGroupingAreasValue['value'], UR_ACTION_READ);
if ($oScopeQuery !== null)
{
@@ -284,7 +285,7 @@ class ManageBrickController extends BrickController
$oAreaQuery = null;
}
$aQueries[$sKey] = $oAreaQuery;
$aQueries[$sKey] = $oAreaQuery;
}
// Testing appropriate data loading mode if we are in auto
@@ -346,11 +347,12 @@ class ManageBrickController extends BrickController
$oSet->OptimizeColumnLoad($aColumnsToLoad);
$oSet->SetOrderByClasses();
SecurityHelper::PreloadForCache($oApp, $oSet->GetFilter(), $aColumnsToLoad[$oQuery->GetClassAlias()] /* preloading only extkeys from the main class */);
$aSets[$sKey] = $oSet;
}
}
// Retrieving and preparing datas for rendering
// Retrieving and preparing data for rendering
$aGroupingAreasData = array();
foreach ($aSets as $sKey => $oSet)
{
@@ -373,6 +375,7 @@ class ManageBrickController extends BrickController
// Getting items
$aItems = array();
$aItemsIds = array();
// ... For each item
/** @var DBObject $oCurrentRow */
while ($oCurrentRow = $oSet->Fetch())
@@ -457,8 +460,18 @@ class ManageBrickController extends BrickController
'attributes' => $aItemAttrs,
'highlight_class' => $oCurrentRow->GetHilightClass()
);
$aItemsIds = $oCurrentRow->GetKey();
}
// Now that we retrieved items, we check which can be edited, which can be view and which cannot be opened
//
// Note: Now that we do checks here and not through the SecurityHelper while fetching objects, we might bypass datamodel security regarding the object class!
// $oScopeQuery = $oApp['scope_validator']->GetScopeFilterForProfiles(UserRights::ListProfiles(), $sCurrentClass, UR_ACTION_MODIFY);
// if($oSearchEditableItems !== null)
// {
// $oSearchEditableItems->A
// }
$aGroupingAreasData[$sKey] = array(
'sId' => $sKey,
'sTitle' => $aGroupingAreasValues[$sKey]['label'],

View File

@@ -26,6 +26,8 @@ use \UserRights;
use \Dict;
use \IssueLog;
use \MetaModel;
use \DBSearch;
use \DBObjectSearch;
use \DBObjectSet;
use \FieldExpression;
use \VariableExpression;
@@ -48,6 +50,10 @@ class SecurityHelper
/**
* 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 Silex\Application $oApp
* @param string $sAction Must be in UR_ACTION_READ|UR_ACTION_MODIFY|UR_ACTION_CREATE
@@ -155,4 +161,105 @@ class SecurityHelper
$iActionAllowed = (get_class($aStimuli[$sStimulusCode]) == 'StimulusUserAction') ? UserRights::IsStimulusAllowed($sObjectClass, $sStimulusCode, $oInstanceSet) : UR_ALLOWED_NO;
}
/**
* Preloads scope objects cache with objects from $oQuery
*
* @param Application $oApp
* @param DBSearch $oSet
* @param array $aExtKeysToPreload
*/
public static function PreloadForCache(Application $oApp, 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 = $oApp['scope_validator']->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($oApp, $oTargetSearch);
}
}
}
}