diff --git a/addons/userrights/userrightsprofile.class.inc.php b/addons/userrights/userrightsprofile.class.inc.php index 2b41ece36..75e64031b 100644 --- a/addons/userrights/userrightsprofile.class.inc.php +++ b/addons/userrights/userrightsprofile.class.inc.php @@ -412,7 +412,7 @@ class UserRightsProfile extends UserRightsAddOnAPI public function Init() { - MetaModel::RegisterPlugin('userrights', 'ACbyProfile', array($this, 'LoadCache')); + MetaModel::RegisterPlugin('userrights', 'ACbyProfile'); } @@ -422,8 +422,12 @@ class UserRightsProfile extends UserRightsAddOnAPI protected $m_aUserProfiles; // userid,profileid -> object protected $m_aUserOrgs; // userid,orgid -> object - protected $m_aClassActionGrants; // profile, class, action -> permission - protected $m_aClassStimulusGrants; // profile, class, stimulus -> permission + // Those arrays could be completed on demand (inheriting parent permissions) + protected $m_aClassActionGrants = null; // profile, class, action -> actiongrantid (or false if NO, or null/missing if undefined) + protected $m_aClassStimulusGrants = array(); // profile, class, stimulus -> permission + + // Built on demand, could be optimized if necessary (doing a query for each attribute that needs to be read) + protected $m_aObjectActionGrants = array(); public function ResetCache() { @@ -434,9 +438,30 @@ class UserRightsProfile extends UserRightsAddOnAPI $this->m_aAdmins = null; - // Loaded on demand - $this->m_aClassActionGrants = array(); - $this->m_aClassStimulusGrants = array(); + // Loaded on demand (time consuming as compared to the others) + $this->m_aClassActionGrants = null; + $this->m_aClassStimulusGrants = null; + + $this->m_aObjectActionGrants = array(); + } + + // Separate load: this cache is much more time consuming while loading + // Thus it is loaded iif required + // Could be improved by specifying the profile id + public function LoadActionGrantCache() + { + if (!is_null($this->m_aClassActionGrants)) return; + + $oDuration = new Duration(); + + $oFilter = DBObjectSearch::FromOQL_AllData("SELECT URP_ActionGrant AS p WHERE p.permission = 'yes'"); + $aGrants = $oFilter->ToDataArray(); + foreach($aGrants as $aGrant) + { + $this->m_aClassActionGrants[$aGrant['profileid']][$aGrant['class']][strtolower($aGrant['action'])] = $aGrant['id']; + } + + $oDuration->Scratch('Load of action grants'); } public function LoadCache() @@ -444,6 +469,8 @@ class UserRightsProfile extends UserRightsAddOnAPI if (!is_null($this->m_aProfiles)) return; // Could be loaded in a shared memory (?) + $oDuration = new Duration(); + $oProfileSet = new DBObjectSet(DBObjectSearch::FromOQL_AllData("SELECT URP_Profiles")); $this->m_aProfiles = array(); while ($oProfile = $oProfileSet->Fetch()) @@ -469,11 +496,24 @@ class UserRightsProfile extends UserRightsAddOnAPI { $this->m_aUserOrgs[$oUserOrg->Get('userid')][$oUserOrg->Get('allowed_org_id')] = $oUserOrg; } + + $this->m_aClassStimulusGrants = array(); + $oStimGrantSet = new DBObjectSet(DBObjectSearch::FromOQL_AllData("SELECT URP_StimulusGrant")); + $this->m_aStimGrants = array(); + while ($oStimGrant = $oStimGrantSet->Fetch()) + { + $this->m_aClassStimulusGrants[$oStimGrant->Get('profileid')][$oStimGrant->Get('class')][$oStimGrant->Get('stimulus')] = $oStimGrant; + } + + $oDuration->Scratch('Load of user management cache (excepted Action Grants)'); + /* echo "
\n"; print_r($this->m_aProfiles); print_r($this->m_aUserProfiles); print_r($this->m_aUserOrgs); + print_r($this->m_aClassActionGrants); + print_r($this->m_aClassStimulusGrants); echo "\n"; exit; */ @@ -544,36 +584,29 @@ exit; // This verb has been made public to allow the development of an accurate feedback for the current configuration public function GetProfileActionGrant($iProfile, $sClass, $sAction) { - $this->LoadCache(); + $this->LoadActionGrantCache(); + // Note: action is forced lowercase to be more flexible (historical bug) + $sAction = strtolower($sAction); if (isset($this->m_aClassActionGrants[$iProfile][$sClass][$sAction])) { return $this->m_aClassActionGrants[$iProfile][$sClass][$sAction]; } - // Get the permission for this profile/class/action - $oSearch = DBObjectSearch::FromOQL_AllData("SELECT URP_ActionGrant WHERE class = :class AND action = :action AND profileid = :profile AND permission = 'yes'"); - $oSet = new DBObjectSet($oSearch, array(), array('class'=>$sClass, 'action'=>$sAction, 'profile'=>$iProfile)); - if ($oSet->Count() >= 1) + // Recursively look for the grant record in the class hierarchy + $sParentClass = MetaModel::GetParentPersistentClass($sClass); + if (empty($sParentClass)) { - $oGrantRecord = $oSet->Fetch(); + $iGrant = null; } else { - $sParentClass = MetaModel::GetParentPersistentClass($sClass); - if (empty($sParentClass)) - { - $oGrantRecord = null; - } - else - { - // Recursively look for the grant record in the class hierarchy - $oGrantRecord = $this->GetProfileActionGrant($iProfile, $sParentClass, $sAction); - } + // Recursively look for the grant record in the class hierarchy + $iGrant = $this->GetProfileActionGrant($iProfile, $sParentClass, $sAction); } - $this->m_aClassActionGrants[$iProfile][$sClass][$sAction] = $oGrantRecord; - return $oGrantRecord; + $this->m_aClassActionGrants[$iProfile][$sClass][$sAction] = $iGrant; + return $iGrant; } protected function GetUserActionGrant($oUser, $sClass, $iActionCode) @@ -594,8 +627,8 @@ exit; { foreach($this->m_aUserProfiles[$iUser] as $iProfile => $oProfile) { - $oGrantRecord = $this->GetProfileActionGrant($iProfile, $sClass, $sAction); - if (is_null($oGrantRecord)) + $iGrant = $this->GetProfileActionGrant($iProfile, $sClass, $sAction); + if (is_null($iGrant) || !$iGrant) { continue; // loop to the next profile } @@ -606,7 +639,7 @@ exit; // update the list of attributes with those allowed for this profile // $oSearch = DBObjectSearch::FromOQL_AllData("SELECT URP_AttributeGrant WHERE actiongrantid = :actiongrantid"); - $oSet = new DBObjectSet($oSearch, array(), array('actiongrantid' => $oGrantRecord->GetKey())); + $oSet = new DBObjectSet($oSearch, array(), array('actiongrantid' => $iGrant)); $aProfileAttributes = $oSet->GetColumnAsArray('attcode', false); if (count($aProfileAttributes) == 0) { @@ -735,21 +768,10 @@ exit; { return $this->m_aClassStimulusGrants[$iProfile][$sClass][$sStimulusCode]; } - - // Get the permission for this profile/class/stimulus - $oSearch = DBObjectSearch::FromOQL_AllData("SELECT URP_StimulusGrant WHERE class = :class AND stimulus = :stimulus AND profileid = :profile AND permission = 'yes'"); - $oSet = new DBObjectSet($oSearch, array(), array('class'=>$sClass, 'stimulus'=>$sStimulusCode, 'profile'=>$iProfile)); - if ($oSet->Count() >= 1) - { - $oGrantRecord = $oSet->Fetch(); - } else { - $oGrantRecord = null; + return null; } - - $this->m_aClassStimulusGrants[$iProfile][$sClass][$sStimulusCode] = $oGrantRecord; - return $oGrantRecord; } public function IsStimulusAllowed($oUser, $sClass, $sStimulusCode, $oInstanceSet = null) diff --git a/core/cmdbobject.class.inc.php b/core/cmdbobject.class.inc.php index 50ba69a6b..ecb58ce24 100644 --- a/core/cmdbobject.class.inc.php +++ b/core/cmdbobject.class.inc.php @@ -35,6 +35,7 @@ require_once('coreexception.class.inc.php'); require_once('config.class.inc.php'); require_once('log.class.inc.php'); +require_once('duration.class.inc.php'); require_once('dict.class.inc.php'); diff --git a/core/cmdbsource.class.inc.php b/core/cmdbsource.class.inc.php index 499c50117..e42d66b8d 100644 --- a/core/cmdbsource.class.inc.php +++ b/core/cmdbsource.class.inc.php @@ -265,19 +265,7 @@ class CMDBSource throw new MySQLException('Failed to issue SQL query', array('query' => $sSql)); } - $aNames = array(); - for ($i = 0; $i < mysql_num_fields($result) ; $i++) - { - $meta = mysql_fetch_field($result, $i); - if (!$meta) - { - throw new MySQLException('mysql_fetch_field: No information available', array('query'=>$sSql, 'i'=>$i)); - } - else - { - $aNames[] = $meta->name; - } - } + $aNames = self::GetColumns($result); $aData[] = $aNames; while ($aRow = mysql_fetch_array($result, MYSQL_ASSOC)) @@ -310,6 +298,24 @@ class CMDBSource return mysql_fetch_array($result, MYSQL_ASSOC); } + public static function GetColumns($result) + { + $aNames = array(); + for ($i = 0; $i < mysql_num_fields($result) ; $i++) + { + $meta = mysql_fetch_field($result, $i); + if (!$meta) + { + throw new MySQLException('mysql_fetch_field: No information available', array('query'=>$sSql, 'i'=>$i)); + } + else + { + $aNames[] = $meta->name; + } + } + return $aNames; + } + public static function Seek($result, $iRow) { return mysql_data_seek($result, $iRow); diff --git a/core/config.class.inc.php b/core/config.class.inc.php index 1afedc700..f11c35fa0 100644 --- a/core/config.class.inc.php +++ b/core/config.class.inc.php @@ -37,6 +37,7 @@ define ('DEFAULT_LOG_GLOBAL', true); define ('DEFAULT_LOG_NOTIFICATION', true); define ('DEFAULT_LOG_ISSUE', true); define ('DEFAULT_LOG_WEB_SERVICE', true); +define ('DEFAULT_LOG_DURATION', false); define ('DEFAULT_MIN_DISPLAY_LIMIT', 10); define ('DEFAULT_MAX_DISPLAY_LIMIT', 15); @@ -79,6 +80,7 @@ class Config protected $m_bLogNotification; protected $m_bLogIssue; protected $m_bLogWebService; + protected $m_bLogDuration; // private setting /** * @var integer Number of elements to be displayed when there are more than m_iMaxDisplayLimit elements @@ -176,6 +178,7 @@ class Config $this->m_bLogNotification = DEFAULT_LOG_NOTIFICATION; $this->m_bLogIssue = DEFAULT_LOG_ISSUE; $this->m_bLogWebService = DEFAULT_LOG_WEB_SERVICE; + $this->m_bLogDuration = DEFAULT_LOG_DURATION; $this->m_iMinDisplayLimit = DEFAULT_MIN_DISPLAY_LIMIT; $this->m_iMaxDisplayLimit = DEFAULT_MAX_DISPLAY_LIMIT; $this->m_iStandardReloadInterval = DEFAULT_STANDARD_RELOAD_INTERVAL; @@ -275,6 +278,7 @@ class Config $this->m_bLogNotification = isset($MySettings['log_notification']) ? (bool) trim($MySettings['log_notification']) : DEFAULT_LOG_NOTIFICATION; $this->m_bLogIssue = isset($MySettings['log_issue']) ? (bool) trim($MySettings['log_issue']) : DEFAULT_LOG_ISSUE; $this->m_bLogWebService = isset($MySettings['log_web_service']) ? (bool) trim($MySettings['log_web_service']) : DEFAULT_LOG_WEB_SERVICE; + $this->m_bLogDuration = isset($MySettings['log_duration']) ? (bool) trim($MySettings['log_duration']) : DEFAULT_LOG_DURATION; $this->m_iMinDisplayLimit = isset($MySettings['min_display_limit']) ? trim($MySettings['min_display_limit']) : DEFAULT_MIN_DISPLAY_LIMIT; $this->m_iMaxDisplayLimit = isset($MySettings['max_display_limit']) ? trim($MySettings['max_display_limit']) : DEFAULT_MAX_DISPLAY_LIMIT; $this->m_iStandardReloadInterval = isset($MySettings['standard_reload_interval']) ? trim($MySettings['standard_reload_interval']) : DEFAULT_STANDARD_RELOAD_INTERVAL; @@ -405,6 +409,11 @@ class Config return $this->m_bLogWebService; } + public function GetLogDuration() + { + return $this->m_bLogDuration; + } + public function GetMinDisplayLimit() { return $this->m_iMinDisplayLimit; diff --git a/core/dbobjectsearch.class.php b/core/dbobjectsearch.class.php index 47253927b..e1cb27358 100644 --- a/core/dbobjectsearch.class.php +++ b/core/dbobjectsearch.class.php @@ -558,6 +558,48 @@ class DBObjectSearch return $retValue; } + // Alternative to object mapping: the data are transfered directly into an array + // This is 10 times faster than creating a set of objects, and makes sense when optimization is required + public function ToDataArray($aColumns = array(), $aOrderBy = array(), $aArgs = array()) + { + $sSQL = MetaModel::MakeSelectQuery($this, $aOrderBy, $aArgs); + $resQuery = CMDBSource::Query($sSQL); + if (!$resQuery) return; + + if (count($aColumns) == 0) + { + $aColumns = array_keys(MetaModel::ListAttributeDefs($this->GetClass())); + // Add the standard id (as first column) + array_unshift($aColumns, 'id'); + } + + $aQueryCols = CMDBSource::GetColumns($resQuery); + + $sClassAlias = $this->GetClassAlias(); + $aColMap = array(); + foreach ($aColumns as $sAttCode) + { + $sColName = $sClassAlias.$sAttCode; + if (in_array($sColName, $aQueryCols)) + { + $aColMap[$sAttCode] = $sColName; + } + } + + $aRes = array(); + while ($aRow = CMDBSource::FetchArray($resQuery)) + { + $aMappedRow = array(); + foreach ($aColMap as $sAttCode => $sColName) + { + $aMappedRow[$sAttCode] = $aRow[$sColName]; + } + $aRes[] = $aMappedRow; + } + CMDBSource::FreeResult($resQuery); + return $aRes; + } + public function ToOQL(&$aParams = null) { // Currently unused, but could be useful later diff --git a/core/duration.class.inc.php b/core/duration.class.inc.php new file mode 100644 index 000000000..c02745526 --- /dev/null +++ b/core/duration.class.inc.php @@ -0,0 +1,82 @@ + + * @author Romain Quetiez