scopevalidatorhelper.class.inc.php 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615
  1. <?php
  2. // Copyright (C) 2010-2015 Combodo SARL
  3. //
  4. // This file is part of iTop.
  5. //
  6. // iTop is free software; you can redistribute it and/or modify
  7. // it under the terms of the GNU Affero General Public License as published by
  8. // the Free Software Foundation, either version 3 of the License, or
  9. // (at your option) any later version.
  10. //
  11. // iTop is distributed in the hope that it will be useful,
  12. // but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14. // GNU Affero General Public License for more details.
  15. //
  16. // You should have received a copy of the GNU Affero General Public License
  17. // along with iTop. If not, see <http://www.gnu.org/licenses/>
  18. namespace Combodo\iTop\Portal\Helper;
  19. use \Exception;
  20. use \DOMNodeList;
  21. use \DOMFormatException;
  22. use \utils;
  23. use \ProfilesConfig;
  24. use \MetaModel;
  25. use \DBSearch;
  26. use \DBUnionSearch;
  27. use \Combodo\iTop\DesignElement;
  28. class ScopeValidatorHelper
  29. {
  30. const ENUM_MODE_READ = 'r';
  31. const ENUM_MODE_WRITE = 'w';
  32. const ENUM_TYPE_ALLOW = 'allow';
  33. const ENUM_TYPE_RESTRICT = 'restrict';
  34. const DEFAULT_GENERATED_CLASS = 'PortalScopesValues';
  35. protected $sCachePath;
  36. protected $sFilename;
  37. protected $sInstancePrefix;
  38. protected $sGeneratedClass;
  39. protected $aProfilesMatrix;
  40. public static function EnumTypeValues()
  41. {
  42. return array(static::ENUM_TYPE_ALLOW, static::ENUM_TYPE_RESTRICT);
  43. }
  44. public function __construct($sFilename, $sCachePath = null)
  45. {
  46. $this->sFilename = $sFilename;
  47. $this->sCachePath = $sCachePath;
  48. $this->sInstancePrefix = '';
  49. $this->sGeneratedClass = static::DEFAULT_GENERATED_CLASS;
  50. $this->aProfilesMatrix = array();
  51. }
  52. /**
  53. * Returns the path where to cache the compiled scopes file
  54. *
  55. * @return string
  56. */
  57. public function GetCachePath()
  58. {
  59. return $this->sCachePath;
  60. }
  61. /**
  62. * Returns the name of the compiled scopes file
  63. *
  64. * @return string
  65. */
  66. public function GetFilename()
  67. {
  68. return $this->sFilename;
  69. }
  70. /**
  71. * Returns the instance prefix used for the generated scopes class name
  72. *
  73. * @return string
  74. */
  75. public function GetInstancePrefix()
  76. {
  77. return $this->sInstancePrefix;
  78. }
  79. /**
  80. * Returns the name of the generated scopes class
  81. *
  82. * @return string
  83. */
  84. public function GetGeneratedClass()
  85. {
  86. return $this->sGeneratedClass;
  87. }
  88. /**
  89. * Sets the scope validator instance prefix.
  90. *
  91. * This is used to create a unique scope values class in the cache directory (/data/cache-<ENV>) as there can be several instance of the portal.
  92. *
  93. * @param string $sInstancePrefix
  94. * @return \Combodo\iTop\Portal\Helper\ScopeValidatorHelper
  95. */
  96. public function SetInstancePrefix($sInstancePrefix)
  97. {
  98. $sInstancePrefix = preg_replace('/[-_]/', ' ', $sInstancePrefix);
  99. $sInstancePrefix = ucwords($sInstancePrefix);
  100. $sInstancePrefix = str_replace(' ', '', $sInstancePrefix);
  101. $this->sInstancePrefix = $sInstancePrefix;
  102. $this->sGeneratedClass = $this->sInstancePrefix . static::DEFAULT_GENERATED_CLASS;
  103. return $this;
  104. }
  105. /**
  106. * Initializes the ScopeValidator by generating and caching the scopes compilation in the $this->sCachePath.$this->sFilename file.
  107. *
  108. * @param DOMNodeList $oNodes
  109. * @throws DOMFormatException
  110. * @throws Exception
  111. */
  112. public function Init(DOMNodeList $oNodes)
  113. {
  114. // Checking cache path
  115. if ($this->sCachePath === null)
  116. {
  117. $this->sCachePath = utils::GetCachePath();
  118. }
  119. // Building full pathname for file
  120. $sFilePath = $this->sCachePath . $this->sFilename;
  121. // Creating file if not existing
  122. // Note : This is a temporary cache system, it should soon evolve to a cache provider (fs, apc, memcache, ...)
  123. if (!file_exists($sFilePath))
  124. {
  125. // - Build php array from xml
  126. $aProfiles = array();
  127. // This will be used to know which classes have been set, so we can set the missing ones.
  128. $aProfileClasses = array();
  129. // Iterating over the class nodes
  130. foreach ($oNodes as $oClassNode)
  131. {
  132. // retrieving mandatory class id attribute
  133. $sClass = $oClassNode->getAttribute('id');
  134. if ($sClass === '')
  135. {
  136. throw new DOMFormatException('Class tag must have an id attribute.', null, null, $oClassNode);
  137. }
  138. // Iterating over scope nodes of the class
  139. $oScopesNode = $oClassNode->GetOptionalElement('scopes');
  140. if ($oScopesNode !== null)
  141. {
  142. foreach ($oScopesNode->GetNodes('./scope') as $oScopeNode)
  143. {
  144. // Retrieving mandatory scope id attribute
  145. $sScopeId = $oScopeNode->getAttribute('id');
  146. if ($sScopeId === '')
  147. {
  148. throw new DOMFormatException('Scope tag must have an id attribute.', null, null, $oScopeNode);
  149. }
  150. // Retrieving the type of query
  151. // Note : This has been disabled as we don't want deny rules for now
  152. // $oOqlViewTypeNode = $oClassNode->GetOptionalElement('oql_view_type');
  153. // $sOqlViewType = ($oOqlViewTypeNode !== null && ($oOqlViewTypeNode->GetText() === static::ENUM_TYPE_RESTRICT)) ? static::ENUM_TYPE_RESTRICT : static::ENUM_TYPE_ALLOW;
  154. $sOqlViewType = static::ENUM_TYPE_ALLOW;
  155. // Retrieving the view query
  156. $oOqlViewNode = $oScopeNode->GetUniqueElement('oql_view');
  157. $sOqlView = $oOqlViewNode->GetText();
  158. if ($sOqlView === null)
  159. {
  160. throw new DOMFormatException('Scope tag in class must have a not empty oql_view tag', null, null, $oScopeNode);
  161. }
  162. // Retrieving the edit query
  163. $oOqlEditNode = $oScopeNode->GetOptionalElement('oql_edit');
  164. $sOqlEdit = ( ($oOqlEditNode !== null) && ($oOqlEditNode->GetText() !== null) ) ? $oOqlEditNode->GetText() : null;
  165. // Retrieving profiles for the scope
  166. $oProfilesNode = $oScopeNode->GetOptionalElement('allowed_profiles');
  167. $aProfilesNames = array();
  168. // If no profile is specified, we consider that it's for ALL the profiles
  169. if (($oProfilesNode === null) || ($oProfilesNode->GetNodes('./allowed_profile')->length === 0))
  170. {
  171. foreach (ProfilesConfig::GetProfilesValues() as $iKey => $aValue)
  172. {
  173. $aProfilesNames[] = $aValue['name'];
  174. }
  175. }
  176. else
  177. {
  178. foreach ($oProfilesNode->GetNodes('./allowed_profile') as $oProfileNode)
  179. {
  180. // Retrieving mandatory profile id attribute
  181. $sProfileId = $oProfileNode->getAttribute('id');
  182. if ($sProfileId === '')
  183. {
  184. throw new DOMFormatException('Scope tag must have an id attribute.', null, null, $oProfileNode);
  185. }
  186. $aProfilesNames[] = $sProfileId;
  187. }
  188. }
  189. //
  190. foreach ($aProfilesNames as $sProfileName)
  191. {
  192. // Scope profile id
  193. $iProfileId = $this->GetProfileIdFromProfileName($sProfileName);
  194. // Now that we have the queries infos, we are going to build the queries for that profile / class
  195. $sMatrixPrefix = $iProfileId . '_' . $sClass . '_';
  196. // - View query
  197. $oViewFilter = DBSearch::FromOQL($sOqlView);
  198. // ... We have to union the query if this profile has another scope for that class
  199. if (array_key_exists($sMatrixPrefix . static::ENUM_MODE_READ, $aProfiles) && array_key_exists($sOqlViewType, $aProfiles[$sMatrixPrefix . static::ENUM_MODE_READ]))
  200. {
  201. $oExistingFilter = DBSearch::FromOQL($aProfiles[$sMatrixPrefix . static::ENUM_MODE_READ][$sOqlViewType]);
  202. $aFilters = array($oExistingFilter, $oViewFilter);
  203. $oResFilter = new DBUnionSearch($aFilters);
  204. }
  205. else
  206. {
  207. $oResFilter = $oViewFilter;
  208. }
  209. $aProfiles[$sMatrixPrefix . static::ENUM_MODE_READ] = array(
  210. $sOqlViewType => $oResFilter->ToOQL()
  211. );
  212. // - Edit query
  213. if ($sOqlEdit !== null)
  214. {
  215. $oEditFilter = DBSearch::FromOQL($sOqlEdit);
  216. // - If the queries are the same, we don't make an intersect, we just reuse the view query
  217. if ($sOqlEdit === $sOqlView)
  218. {
  219. // Do not intersect, edit query is identical to view query
  220. }
  221. else
  222. {
  223. if (($oEditFilter->GetClass() === $oViewFilter->GetClass()) && $oEditFilter->IsAny())
  224. {
  225. $oEditFilter = $oViewFilter;
  226. // Do not intersect, edit query is identical to view query
  227. }
  228. else
  229. {
  230. // Intersect
  231. $oEditFilter = $oViewFilter->Intersect($oEditFilter);
  232. }
  233. }
  234. // ... We have to union the query if this profile has another scope for that class
  235. if (array_key_exists($sMatrixPrefix . static::ENUM_MODE_WRITE, $aProfiles) && array_key_exists($sOqlViewType, $aProfiles[$sMatrixPrefix . static::ENUM_MODE_WRITE]))
  236. {
  237. $oExistingFilter = DBSearch::FromOQL($aProfiles[$sMatrixPrefix . static::ENUM_MODE_WRITE][$sOqlViewType]);
  238. $aFilters = array($oExistingFilter, $oEditFilter);
  239. $oResFilter = new DBUnionSearch($aFilters);
  240. }
  241. else
  242. {
  243. $oResFilter = $oEditFilter;
  244. }
  245. $aProfiles[$sMatrixPrefix . static::ENUM_MODE_WRITE] = array(
  246. $sOqlViewType => $oResFilter->ToOQL()
  247. );
  248. }
  249. }
  250. }
  251. $aProfileClasses[] = $sClass;
  252. }
  253. }
  254. // Filling the array with missing classes from MetaModel, so we can have an inheritance principle on the scope
  255. // For each class explicitly given in the scopes, we check if its child classes were also in the scope :
  256. // If not, we add them with the same OQL
  257. foreach ($aProfileClasses as $sProfileClass)
  258. {
  259. foreach (MetaModel::EnumChildClasses($sProfileClass) as $sChildClass)
  260. {
  261. // If the child class is not in the scope, we are going to try to add it
  262. if (!in_array($sChildClass, $aProfileClasses))
  263. {
  264. foreach (ProfilesConfig::GetProfilesValues() as $iKey => $aValue)
  265. {
  266. $iProfileId = $iKey;
  267. foreach (array(static::ENUM_MODE_READ, static::ENUM_MODE_WRITE) as $sAction)
  268. {
  269. // If the current profile has scope for that class in that mode, we duplicate it
  270. if (isset($aProfiles[$iProfileId . '_' . $sProfileClass . '_' . $sAction]))
  271. {
  272. $aTmpProfile = $aProfiles[$iProfileId . '_' . $sProfileClass . '_' . $sAction];
  273. foreach ($aTmpProfile as $sType => $sOql)
  274. {
  275. $oTmpFilter = DBSearch::FromOQL($sOql);
  276. $oTmpFilter->ChangeClass($sChildClass);
  277. $aTmpProfile[$sType] = $oTmpFilter->ToOQL();
  278. }
  279. $aProfiles[$iProfileId . '_' . $sChildClass . '_' . $sAction] = $aTmpProfile;
  280. }
  281. }
  282. }
  283. }
  284. }
  285. }
  286. // Iterating over the scope nodes
  287. /* foreach ($oNodes as $oScopeNode)
  288. {
  289. // Retrieving mandatory id attribute
  290. $sProfile = $oScopeNode->getAttribute('id');
  291. if ($sProfile === '')
  292. {
  293. throw new DOMFormatException('Scope tag must have an id attribute.', null, null, $oScopeNode);
  294. }
  295. // Scope profile id
  296. $iProfileId = $this->GetProfileIdFromProfileName($sProfile);
  297. // This will be used to know which classes have been set, so we can set the missing ones.
  298. $aProfileClasses = array();
  299. // Iterating over the class nodes of the scope
  300. foreach ($oScopeNode->GetUniqueElement('classes')->GetNodes('./class') as $oClassNode)
  301. {
  302. // Retrieving mandatory id attribute
  303. $sClass = $oClassNode->getAttribute('id');
  304. if ($sClass === '')
  305. {
  306. throw new DOMFormatException('Class tag must have an id attribute.', null, null, $oClassNode);
  307. }
  308. // Retrieving the type of query
  309. $oOqlViewTypeNode = $oClassNode->GetOptionalElement('oql_view_type');
  310. $sOqlViewType = ($oOqlViewTypeNode !== null && ($oOqlViewTypeNode->GetText() === static::ENUM_TYPE_RESTRICT)) ? static::ENUM_TYPE_RESTRICT : static::ENUM_TYPE_ALLOW;
  311. // Retrieving the view query
  312. $oOqlViewNode = $oClassNode->GetUniqueElement('oql_view');
  313. $sOqlView = $oOqlViewNode->GetText();
  314. if ($sOqlView === null)
  315. {
  316. throw new DOMFormatException('Class tag in scope must have a not empty oql_view tag', null, null, $oClassNode);
  317. }
  318. // Retrieving the edit query
  319. $oOqlEditNode = $oClassNode->GetOptionalElement('oql_edit');
  320. $sOqlEdit = ( ($oOqlEditNode !== null) && ($oOqlEditNode->GetText() !== null) ) ? $oOqlEditNode->GetText() : null;
  321. // Now that we have the queries infos, we are going to build the queries for that profile / class
  322. $sMatrixPrefix = $iProfileId . '_' . $sClass . '_';
  323. // - View query
  324. $oViewFilter = DBSearch::FromOQL($sOqlView);
  325. $aProfiles[$sMatrixPrefix . 'r'] = array(
  326. $sOqlViewType => $oViewFilter->ToOQL()
  327. );
  328. // - Edit query
  329. if ($sOqlEdit !== null)
  330. {
  331. $oEditFilter = DBSearch::FromOQL($sOqlEdit);
  332. // - If the queries are the same, we don't make an intersect, we just reuse the view query
  333. if ($sOqlEdit === $sOqlView)
  334. {
  335. // Do not intersect, edit query is identical to view query
  336. }
  337. else
  338. {
  339. if (($oEditFilter->GetClass() === $oViewFilter->GetClass()) && $oEditFilter->IsAny())
  340. {
  341. $oEditFilter = $oViewFilter;
  342. // Do not intersect, edit query is identical to view query
  343. }
  344. else
  345. {
  346. // Intersect
  347. $oEditFilter = $oViewFilter->Intersect($oEditFilter);
  348. }
  349. }
  350. $aProfiles[$sMatrixPrefix . 'w'] = array(
  351. $sOqlViewType => $oEditFilter->ToOQL()
  352. );
  353. }
  354. $aProfileClasses[] = $sClass;
  355. }
  356. // Filling the array with missing classes from MetaModel, so we can have an inheritance principle on the scope
  357. // For each class explicitly given in the scopes, we check if its child classes were also in the scope :
  358. // If not, we add them with the same OQL
  359. foreach ($aProfileClasses as $sProfileClass)
  360. {
  361. foreach (MetaModel::EnumChildClasses($sProfileClass) as $sChildClass)
  362. {
  363. // If the child class is not in the scope, we are going to try to add it
  364. if (!in_array($sChildClass, $aProfileClasses))
  365. {
  366. foreach (array('r', 'w') as $sAction)
  367. {
  368. // If the current profile has scope for that class in that mode, we duplicate it
  369. if (isset($aProfiles[$iProfileId . '_' . $sProfileClass . '_' . $sAction]))
  370. {
  371. $aTmpProfile = $aProfiles[$iProfileId . '_' . $sProfileClass . '_' . $sAction];
  372. foreach ($aTmpProfile as $sType => $sOql)
  373. {
  374. $oTmpFilter = DBSearch::FromOQL($sOql);
  375. $oTmpFilter->ChangeClass($sChildClass);
  376. $aTmpProfile[$sType] = $oTmpFilter->ToOQL();
  377. }
  378. $aProfiles[$iProfileId . '_' . $sChildClass . '_' . $sAction] = $aTmpProfile;
  379. }
  380. }
  381. }
  382. }
  383. }
  384. } */
  385. // - Build php class
  386. $sPHP = $this->BuildPHPClass($aProfiles);
  387. // - Write file on disk
  388. // - Creating dir if necessary
  389. if (!is_dir($this->sCachePath))
  390. {
  391. mkdir($this->sCachePath, 0777, true);
  392. }
  393. // -- Then creating the file
  394. $ret = file_put_contents($sFilePath, $sPHP);
  395. if ($ret === false)
  396. {
  397. $iLen = strlen($sPHP);
  398. $fFree = @disk_free_space(dirname($sFilePath));
  399. $aErr = error_get_last();
  400. throw new Exception("Failed to write '$sFilePath'. Last error: '{$aErr['message']}', content to write: $iLen bytes, available free space on disk: $fFree.");
  401. }
  402. }
  403. if (!class_exists($this->sGeneratedClass))
  404. {
  405. require_once $this->sCachePath . $this->sFilename;
  406. }
  407. }
  408. /**
  409. * Returns the DBSearch for the $sProfile in $iAction for the class $sClass
  410. *
  411. * @param string $sProfile
  412. * @param string $sClass
  413. * @param integer $iAction
  414. * @return DBSearch
  415. */
  416. public function GetScopeFilterForProfile($sProfile, $sClass, $iAction = null)
  417. {
  418. return $this->GetScopeFilterForProfiles(array($sProfile), $sClass, $iAction);
  419. }
  420. /**
  421. * Returns the DBSearch for the $aProfiles in $iAction for the class $sClass.
  422. * Profiles are a OR condition.
  423. *
  424. * @param array $aProfiles
  425. * @param string $sClass
  426. * @param integer $iAction
  427. * @return DBSearch
  428. */
  429. public function GetScopeFilterForProfiles($aProfiles, $sClass, $iAction = null)
  430. {
  431. $oSearch = null;
  432. $aAllowSearches = array();
  433. $aRestrictSearches = array();
  434. // Checking the default mode
  435. if ($iAction === null)
  436. {
  437. $iAction = UR_ACTION_READ;
  438. }
  439. // Iterating on profiles to retrieving the different OQLs parts
  440. foreach ($aProfiles as $sProfile)
  441. {
  442. // Retrieving matrix informtions
  443. $iProfileId = $this->GetProfileIdFromProfileName($sProfile);
  444. $sMode = ($iAction === UR_ACTION_READ) ? static::ENUM_MODE_READ : static::ENUM_MODE_WRITE;
  445. // Retrieving profile OQLs
  446. $sScopeValuesClass = $this->sGeneratedClass;
  447. $aProfileMatrix = $sScopeValuesClass::GetProfileScope($iProfileId, $sClass, $sMode);
  448. if ($aProfileMatrix !== null)
  449. {
  450. if (isset($aProfileMatrix['allow']) && $aProfileMatrix['allow'] !== null)
  451. {
  452. $aAllowSearches[] = DBSearch::FromOQL($aProfileMatrix['allow']);
  453. }
  454. if (isset($aProfileMatrix['restrict']) && $aProfileMatrix['restrict'] !== null)
  455. {
  456. $aRestrictSearches[] = DBSearch::FromOQL($aProfileMatrix['restrict']);
  457. }
  458. }
  459. }
  460. // Building the real OQL from all the parts from the differents profiles
  461. for ($i = 0; $i < count($aAllowSearches); $i++)
  462. {
  463. foreach ($aRestrictSearches as $oRestrictSearch)
  464. {
  465. $aAllowSearches[$i] = $aAllowSearches[$i]->Intersect($oRestrictSearch);
  466. }
  467. }
  468. if (count($aAllowSearches) > 0)
  469. {
  470. $oSearch = new DBUnionSearch($aAllowSearches);
  471. $oSearch = $oSearch->RemoveDuplicateQueries();
  472. }
  473. return $oSearch;
  474. }
  475. /**
  476. * Returns the profile id from a string being either a constant or its name.
  477. *
  478. * @param string $sProfile
  479. * @return integer
  480. * @throws Exception
  481. */
  482. protected function GetProfileIdFromProfileName($sProfile)
  483. {
  484. $iProfileId = null;
  485. // We try to find the profile from its name in order to retrieve it's id
  486. // - If the regular UserRights addon is installed we check the profiles array
  487. if (class_exists('ProfilesConfig'))
  488. {
  489. if (defined($sProfile) && in_array($sProfile, ProfilesConfig::GetProfilesValues()))
  490. {
  491. $iProfileId = constant($sProfile);
  492. }
  493. else
  494. {
  495. foreach (ProfilesConfig::GetProfilesValues() as $iKey => $aValue)
  496. {
  497. if ($aValue['name'] === $sProfile)
  498. {
  499. $iProfileId = $iKey;
  500. break;
  501. }
  502. }
  503. }
  504. }
  505. // - 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
  506. else
  507. {
  508. throw new Exception('Scope validator : Unknown UserRights addon, scope\'s profile must be a constant');
  509. }
  510. // If profile was not found from its name or from a constant, we throw an exception
  511. if ($iProfileId === null)
  512. {
  513. throw new Exception('Scope validator : Could not find "' . $sProfile . '" in the profiles list');
  514. }
  515. return $iProfileId;
  516. }
  517. /**
  518. * Returns a string containing the generated PHP class for the compiled scopes
  519. *
  520. * @param array $aProfiles
  521. * @return string
  522. */
  523. protected function BuildPHPClass($aProfiles = array())
  524. {
  525. $sProfiles = var_export($aProfiles, true);
  526. $sClassName = $this->sGeneratedClass;
  527. $sPHP = <<<EOF
  528. <?php
  529. // File generated by ScopeValidatorHelperHelper
  530. //
  531. // Please do not edit manually
  532. // List of constant scopes
  533. // - used by the portal ScopeValidatorHelperHelper
  534. //
  535. class $sClassName
  536. {
  537. protected static \$aPROFILES = $sProfiles;
  538. /**
  539. * @param integer \$iProfileId
  540. * @param string \$sClass
  541. * @param string \$sAction 'r'|'w'
  542. */
  543. public static function GetProfileScope(\$iProfileId, \$sClass, \$sAction)
  544. {
  545. \$sQuery = null;
  546. \$sScopeKey = \$iProfileId.'_'.\$sClass.'_'.\$sAction;
  547. if (isset(self::\$aPROFILES[\$sScopeKey]))
  548. {
  549. \$sQuery = self::\$aPROFILES[\$sScopeKey];
  550. }
  551. return \$sQuery;
  552. }
  553. }
  554. EOF;
  555. return $sPHP;
  556. }
  557. }
  558. ?>