mirror of
https://github.com/Combodo/iTop.git
synced 2026-06-27 10:16:45 +02:00
Compare commits
10 Commits
develop
...
feature/96
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
357ae223fd | ||
|
|
e5e91e8135 | ||
|
|
87dcbf7b53 | ||
|
|
51d5692f81 | ||
|
|
dc3f6a9f72 | ||
|
|
4d2da15c96 | ||
|
|
63879a63cc | ||
|
|
bcb42fdc81 | ||
|
|
fc9df64eea | ||
|
|
41d4f04375 |
@@ -582,26 +582,16 @@ class UserRightsProfile extends UserRightsAddOnAPI
|
|||||||
*/
|
*/
|
||||||
public function ListProfiles($oUser)
|
public function ListProfiles($oUser)
|
||||||
{
|
{
|
||||||
if (count($oUser->ListChanges()) === 0) { // backward compatibility
|
$aRet = [];
|
||||||
$aRet = [];
|
$oSearch = new DBObjectSearch('URP_UserProfile');
|
||||||
$oSearch = new DBObjectSearch('URP_UserProfile');
|
$oSearch->AllowAllData();
|
||||||
$oSearch->AllowAllData();
|
$oSearch->NoContextParameters();
|
||||||
$oSearch->NoContextParameters();
|
$oSearch->Addcondition('userid', $oUser->GetKey(), '=');
|
||||||
$oSearch->Addcondition('userid', $oUser->GetKey(), '=');
|
$oProfiles = new DBObjectSet($oSearch);
|
||||||
$oProfiles = new DBObjectSet($oSearch);
|
while ($oUserProfile = $oProfiles->Fetch()) {
|
||||||
while ($oUserProfile = $oProfiles->Fetch()) {
|
$aRet[$oUserProfile->Get('profileid')] = $oUserProfile->Get('profileid_friendlyname');
|
||||||
$aRet[$oUserProfile->Get('profileid')] = $oUserProfile->Get('profileid_friendlyname');
|
|
||||||
}
|
|
||||||
|
|
||||||
return $aRet;
|
|
||||||
} else {
|
|
||||||
$aRet = [];
|
|
||||||
$oProfilesSet = $oUser->Get('profile_list');
|
|
||||||
foreach ($oProfilesSet as $oUserProfile) {
|
|
||||||
$aRet[$oUserProfile->Get('profileid')] = $oUserProfile->Get('profileid_friendlyname');
|
|
||||||
}
|
|
||||||
return $aRet;
|
|
||||||
}
|
}
|
||||||
|
return $aRet;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function GetSelectFilter($oUser, $sClass, $aSettings = [])
|
public function GetSelectFilter($oUser, $sClass, $aSettings = [])
|
||||||
@@ -715,23 +705,26 @@ class UserRightsProfile extends UserRightsAddOnAPI
|
|||||||
protected function GetUserActionGrant($oUser, $sClass, $iActionCode)
|
protected function GetUserActionGrant($oUser, $sClass, $iActionCode)
|
||||||
{
|
{
|
||||||
$this->LoadCache();
|
$this->LoadCache();
|
||||||
if (count($oUser->ListChanges()) === 0) {
|
|
||||||
// load and cache permissions for the current user on the given class
|
// load and cache permissions for the current user on the given class
|
||||||
if (isset($this->m_aObjectActionGrants[$oUser->GetKey()][$sClass][$iActionCode])) {
|
//
|
||||||
$aTest = $this->m_aObjectActionGrants[$oUser->GetKey()][$sClass][$iActionCode];
|
$iUser = $oUser->GetKey();
|
||||||
if (is_array($aTest)) {
|
if (isset($this->m_aObjectActionGrants[$iUser][$sClass][$iActionCode])) {
|
||||||
return $aTest;
|
$aTest = $this->m_aObjectActionGrants[$iUser][$sClass][$iActionCode];
|
||||||
}
|
if (is_array($aTest)) {
|
||||||
|
return $aTest;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$sAction = self::$m_aActionCodes[$iActionCode];
|
$sAction = self::$m_aActionCodes[$iActionCode];
|
||||||
|
|
||||||
$bStatus = null;
|
$bStatus = null;
|
||||||
|
// Cache user's profiles
|
||||||
$aProfileList = $this->GetProfileList($oUser);
|
if (false === array_key_exists($iUser, $this->aUsersProfilesList)) {
|
||||||
|
$this->aUsersProfilesList[$iUser] = UserRights::ListProfiles($oUser);
|
||||||
|
}
|
||||||
// Call the API of UserRights because it caches the list for us
|
// Call the API of UserRights because it caches the list for us
|
||||||
foreach ($aProfileList as $iProfile => $oProfile) {
|
foreach ($this->aUsersProfilesList[$iUser] as $iProfile => $oProfile) {
|
||||||
$bGrant = $this->GetProfileActionGrant($iProfile, $sClass, $sAction);
|
$bGrant = $this->GetProfileActionGrant($iProfile, $sClass, $sAction);
|
||||||
if (!is_null($bGrant)) {
|
if (!is_null($bGrant)) {
|
||||||
if ($bGrant) {
|
if ($bGrant) {
|
||||||
@@ -749,9 +742,7 @@ class UserRightsProfile extends UserRightsAddOnAPI
|
|||||||
$aRes = [
|
$aRes = [
|
||||||
'permission' => $iPermission,
|
'permission' => $iPermission,
|
||||||
];
|
];
|
||||||
if (count($oUser->ListChanges()) === 0) {
|
$this->m_aObjectActionGrants[$iUser][$sClass][$iActionCode] = $aRes;
|
||||||
$this->m_aObjectActionGrants[$oUser->GetKey()][$sClass][$iActionCode] = $aRes;
|
|
||||||
}
|
|
||||||
return $aRes;
|
return $aRes;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -833,14 +824,18 @@ class UserRightsProfile extends UserRightsAddOnAPI
|
|||||||
{
|
{
|
||||||
$this->LoadCache();
|
$this->LoadCache();
|
||||||
// Note: this code is VERY close to the code of IsActionAllowed()
|
// Note: this code is VERY close to the code of IsActionAllowed()
|
||||||
|
$iUser = $oUser->GetKey();
|
||||||
|
|
||||||
$aProfileList = $this->GetProfileList($oUser);
|
// Cache user's profiles
|
||||||
|
if (false === array_key_exists($iUser, $this->aUsersProfilesList)) {
|
||||||
|
$this->aUsersProfilesList[$iUser] = UserRights::ListProfiles($oUser);
|
||||||
|
}
|
||||||
|
|
||||||
// Note: The object set is ignored because it was interesting to optimize for huge data sets
|
// Note: The object set is ignored because it was interesting to optimize for huge data sets
|
||||||
// and acceptable to consider only the root class of the object set
|
// and acceptable to consider only the root class of the object set
|
||||||
$bStatus = null;
|
$bStatus = null;
|
||||||
// Call the API of UserRights because it caches the list for us
|
// Call the API of UserRights because it caches the list for us
|
||||||
foreach ($aProfileList as $iProfile => $oProfile) {
|
foreach ($this->aUsersProfilesList[$iUser] as $iProfile => $oProfile) {
|
||||||
$bGrant = $this->GetClassStimulusGrant($iProfile, $sClass, $sStimulusCode);
|
$bGrant = $this->GetClassStimulusGrant($iProfile, $sClass, $sStimulusCode);
|
||||||
if (!is_null($bGrant)) {
|
if (!is_null($bGrant)) {
|
||||||
if ($bGrant) {
|
if ($bGrant) {
|
||||||
@@ -898,25 +893,6 @@ class UserRightsProfile extends UserRightsAddOnAPI
|
|||||||
}
|
}
|
||||||
return $bHasSharing;
|
return $bHasSharing;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @param \User $oUser
|
|
||||||
*
|
|
||||||
* @return array
|
|
||||||
* @throws \Exception
|
|
||||||
*/
|
|
||||||
public function GetProfileList(User $oUser): array
|
|
||||||
{
|
|
||||||
if (count($oUser->ListChanges()) === 0) { // if user is already in db and not changed
|
|
||||||
$iUser = $oUser->GetKey();
|
|
||||||
if (false === array_key_exists($iUser, $this->aUsersProfilesList)) {
|
|
||||||
$aProfiles = UserRights::ListProfiles($oUser);
|
|
||||||
$this->aUsersProfilesList[$iUser] = $aProfiles;
|
|
||||||
}
|
|
||||||
return $this->aUsersProfilesList[$iUser];
|
|
||||||
}
|
|
||||||
return UserRights::ListProfiles($oUser);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
UserRights::SelectModule('UserRightsProfile');
|
UserRights::SelectModule('UserRightsProfile');
|
||||||
|
|||||||
@@ -916,7 +916,9 @@ class CMDBSource
|
|||||||
$aColumn = [];
|
$aColumn = [];
|
||||||
$aData = self::QueryToArray($sSql);
|
$aData = self::QueryToArray($sSql);
|
||||||
foreach ($aData as $aRow) {
|
foreach ($aData as $aRow) {
|
||||||
@$aColumn[] = $aRow[$col];
|
if ($aRow[$col] !== null) {
|
||||||
|
$aColumn[] = $aRow[$col];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return $aColumn;
|
return $aColumn;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1530,37 +1530,6 @@ class UserRights
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @param User $oUser
|
|
||||||
* @param array $aExcludedProfilesId Administrator by default, but can also be other proofiles depending on needs (e.g. power portal user or REST profile)
|
|
||||||
* @return bool
|
|
||||||
* @throws ArchivedObjectException
|
|
||||||
* @throws CoreException
|
|
||||||
* @throws CoreUnexpectedValue
|
|
||||||
* @throws MySQLException
|
|
||||||
*/
|
|
||||||
public static function IsUserReadOnly(User $oUser, array $aExcludedProfilesId = [1]): bool
|
|
||||||
{
|
|
||||||
$oUserProfiles = $oUser->Get('profile_list');
|
|
||||||
$oUserRights = UserRights::GetModuleInstance();
|
|
||||||
while ($oUserProfile = $oUserProfiles->Fetch()) {
|
|
||||||
$iProfileId = $oUserProfile->Get('profileid');
|
|
||||||
if (in_array($iProfileId, $aExcludedProfilesId)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
foreach (MetaModel::GetClasses('bizmodel,grant_by_profile') as $sClass) {
|
|
||||||
foreach (['w', 'bw', 'd', 'bd'] as $sWriteActionCode) {
|
|
||||||
$bIsGranted = $oUserRights->GetProfileActionGrant($iProfileId, $sClass, $sWriteActionCode);
|
|
||||||
if ($bIsGranted === true) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param string $sClass
|
* @param string $sClass
|
||||||
* @param int $iActionCode see UR_ACTION_* constants
|
* @param int $iActionCode see UR_ACTION_* constants
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ use Combodo\iTop\DataFeatureRemoval\Helper\DataFeatureRemovalHelper;
|
|||||||
use Combodo\iTop\DataFeatureRemoval\Helper\DataFeatureRemovalLog;
|
use Combodo\iTop\DataFeatureRemoval\Helper\DataFeatureRemovalLog;
|
||||||
use Combodo\iTop\DataFeatureRemoval\Service\DataCleanupService;
|
use Combodo\iTop\DataFeatureRemoval\Service\DataCleanupService;
|
||||||
use Combodo\iTop\DataFeatureRemoval\Service\DataFeatureRemoverExtensionService;
|
use Combodo\iTop\DataFeatureRemoval\Service\DataFeatureRemoverExtensionService;
|
||||||
|
use Combodo\iTop\DataFeatureRemoval\Service\StaticDeletionPlan;
|
||||||
use Combodo\iTop\Setup\FeatureRemoval\DryRemovalRuntimeEnvironment;
|
use Combodo\iTop\Setup\FeatureRemoval\DryRemovalRuntimeEnvironment;
|
||||||
use Combodo\iTop\Setup\FeatureRemoval\SetupAudit;
|
use Combodo\iTop\Setup\FeatureRemoval\SetupAudit;
|
||||||
use ContextTag;
|
use ContextTag;
|
||||||
@@ -168,6 +169,7 @@ class DataFeatureRemovalController extends Controller
|
|||||||
$oSetupAudit = new SetupAudit($sSourceEnv);
|
$oSetupAudit = new SetupAudit($sSourceEnv);
|
||||||
$aGetRemovedClasses = array_keys($oSetupAudit->RunDataAudit());
|
$aGetRemovedClasses = array_keys($oSetupAudit->RunDataAudit());
|
||||||
DataFeatureRemovalLog::Debug(__METHOD__, null, ['aGetRemovedClasses' => $aGetRemovedClasses]);
|
DataFeatureRemovalLog::Debug(__METHOD__, null, ['aGetRemovedClasses' => $aGetRemovedClasses]);
|
||||||
|
$aDeletionPlan = (new StaticDeletionPlan())->GetStaticDeletionPlan($aGetRemovedClasses);
|
||||||
|
|
||||||
$aParams['aClasses'] = $aGetRemovedClasses;
|
$aParams['aClasses'] = $aGetRemovedClasses;
|
||||||
|
|
||||||
@@ -222,6 +224,7 @@ class DataFeatureRemovalController extends Controller
|
|||||||
$bIsDirEmpty = count(scandir($sBuildDir)) === 2;
|
$bIsDirEmpty = count(scandir($sBuildDir)) === 2;
|
||||||
|
|
||||||
if ($bIsDirEmpty || $bForceCompilation) {
|
if ($bIsDirEmpty || $bForceCompilation) {
|
||||||
|
Session::Unset('bForceCompilation');
|
||||||
DataFeatureRemovalLog::Debug(
|
DataFeatureRemovalLog::Debug(
|
||||||
__METHOD__,
|
__METHOD__,
|
||||||
null,
|
null,
|
||||||
@@ -272,7 +275,7 @@ class DataFeatureRemovalController extends Controller
|
|||||||
private function GetDeletionPlanSummaryTable(array $aRemovedClasses): array
|
private function GetDeletionPlanSummaryTable(array $aRemovedClasses): array
|
||||||
{
|
{
|
||||||
$sName = 'DeletionPlanSummary';
|
$sName = 'DeletionPlanSummary';
|
||||||
$oDataCleanupService = new DataCleanupService();
|
$oDataCleanupService = new StaticDeletionPlan();
|
||||||
$aDeletionPlanSummaryEntities = $oDataCleanupService->GetCleanupSummary($aRemovedClasses);
|
$aDeletionPlanSummaryEntities = $oDataCleanupService->GetCleanupSummary($aRemovedClasses);
|
||||||
$aColumns = ['Class', 'Delete Count' , 'Update Count', 'Issue Count'];
|
$aColumns = ['Class', 'Delete Count' , 'Update Count', 'Issue Count'];
|
||||||
$aRows = [];
|
$aRows = [];
|
||||||
|
|||||||
@@ -0,0 +1,37 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* @copyright Copyright (C) 2010-2026 Combodo SAS
|
||||||
|
* @license http://opensource.org/licenses/AGPL-3.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Combodo\iTop\DataFeatureRemoval\Entity;
|
||||||
|
|
||||||
|
class DeletionPlanEntity
|
||||||
|
{
|
||||||
|
public readonly DeletionPlanItem $oDelete;
|
||||||
|
public readonly DeletionPlanItem $oUpdate;
|
||||||
|
public readonly DeletionPlanItem $oIssue;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param \Combodo\iTop\DataFeatureRemoval\Entity\DeletionPlanItem $oDelete
|
||||||
|
* @param \Combodo\iTop\DataFeatureRemoval\Entity\DeletionPlanItem $oUpdate
|
||||||
|
* @param \Combodo\iTop\DataFeatureRemoval\Entity\DeletionPlanItem $oIssue
|
||||||
|
*/
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
$this->oDelete = $oDelete ?? new DeletionPlanItem();
|
||||||
|
$this->oUpdate = $oUpdate ?? new DeletionPlanItem();
|
||||||
|
$this->oIssue = $oIssue ?? new DeletionPlanItem();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function TotalCount(): int
|
||||||
|
{
|
||||||
|
return $this->oDelete->Count() + $this->oUpdate->Count() + $this->oIssue->Count();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function FilterUpdatesByDeletes()
|
||||||
|
{
|
||||||
|
$this->oUpdate->FilterBy($this->oDelete);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* @copyright Copyright (C) 2010-2026 Combodo SAS
|
||||||
|
* @license http://opensource.org/licenses/AGPL-3.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Combodo\iTop\DataFeatureRemoval\Entity;
|
||||||
|
|
||||||
|
class DeletionPlanItem
|
||||||
|
{
|
||||||
|
public array $aQueries = [];
|
||||||
|
public array $aIds = [];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param array $aQueries
|
||||||
|
* @param array $aIds
|
||||||
|
*/
|
||||||
|
public function __construct(array $aQueries = [], array $aIds = [])
|
||||||
|
{
|
||||||
|
$this->aQueries = $aQueries;
|
||||||
|
$this->aIds = $aIds;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function Merge(DeletionPlanItem $oItem): void
|
||||||
|
{
|
||||||
|
$this->aQueries = array_merge($this->aQueries, $oItem->aQueries);
|
||||||
|
$this->aIds = array_unique(array_merge($this->aIds, $oItem->aIds));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function Count(): int
|
||||||
|
{
|
||||||
|
return count($this->aIds);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function FilterBy(DeletionPlanItem $oItem): void
|
||||||
|
{
|
||||||
|
$this->aIds = array_diff($this->aIds, $oItem->aIds);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,242 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* @copyright Copyright (C) 2010-2026 Combodo SAS
|
||||||
|
* @license http://opensource.org/licenses/AGPL-3.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Combodo\iTop\DataFeatureRemoval\Service;
|
||||||
|
|
||||||
|
use CMDBSource;
|
||||||
|
use Combodo\iTop\DataFeatureRemoval\Entity\DataCleanupSummaryEntity;
|
||||||
|
use Combodo\iTop\DataFeatureRemoval\Entity\DeletionPlanEntity;
|
||||||
|
use Combodo\iTop\DataFeatureRemoval\Entity\DeletionPlanItem;
|
||||||
|
use MetaModel;
|
||||||
|
|
||||||
|
class StaticDeletionPlan
|
||||||
|
{
|
||||||
|
/** @var array<DeletionPlanEntity> */
|
||||||
|
private array $aDeletionPlan = [];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a summary of the deletion plan computed for the classes.
|
||||||
|
* The result is used for display
|
||||||
|
*
|
||||||
|
* @param array|null $aClasses
|
||||||
|
*
|
||||||
|
* @return array<\Combodo\iTop\DataFeatureRemoval\Entity\DataCleanupSummaryEntity>
|
||||||
|
* @throws \CoreException
|
||||||
|
*/
|
||||||
|
public function GetCleanupSummary(?array $aClasses): array
|
||||||
|
{
|
||||||
|
$aSummary = [];
|
||||||
|
$aDeletionPlan = $this->GetStaticDeletionPlan($aClasses ?? []);
|
||||||
|
|
||||||
|
foreach ($aDeletionPlan as $sClass => $oDeletionPlanEntity) {
|
||||||
|
if ($oDeletionPlanEntity->TotalCount() === 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
$oDeletionPlanEntity->FilterUpdatesByDeletes();
|
||||||
|
$oDataCleanupSummary = new DataCleanupSummaryEntity($sClass);
|
||||||
|
$oDataCleanupSummary->iUpdateCount = $oDeletionPlanEntity->oUpdate->Count();
|
||||||
|
$oDataCleanupSummary->iDeleteCount = $oDeletionPlanEntity->oDelete->Count();
|
||||||
|
$oDataCleanupSummary->iIssueCount = $oDeletionPlanEntity->oIssue->Count();
|
||||||
|
|
||||||
|
$aSummary[$sClass] = $oDataCleanupSummary;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $aSummary;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param array $aClasses Classes to clean entirely
|
||||||
|
*
|
||||||
|
* @return array ['class' => DeletionPlanEntity];
|
||||||
|
*
|
||||||
|
* @throws \CoreException
|
||||||
|
*/
|
||||||
|
public function GetStaticDeletionPlan(array $aClasses): array
|
||||||
|
{
|
||||||
|
foreach ($aClasses as $sClass) {
|
||||||
|
$oDeletionPlanItem = $this->GetInitialClassDeletionPlan($sClass);
|
||||||
|
$oDeletionPlanEntity = new DeletionPlanEntity();
|
||||||
|
$oDeletionPlanEntity->oDelete->Merge($oDeletionPlanItem);
|
||||||
|
$this->aDeletionPlan[$sClass] = $oDeletionPlanEntity;
|
||||||
|
|
||||||
|
$this->DeletionPlanForReferencingClasses($sClass);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->aDeletionPlan;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function DeletionPlanForReferencingClasses(string $sClass): void
|
||||||
|
{
|
||||||
|
$sIdsToRemove = implode(', ', $this->aDeletionPlan[$sClass]->oDelete->aIds);
|
||||||
|
$aReferencingMe = MetaModel::EnumReferencingClasses($sClass);
|
||||||
|
foreach ($aReferencingMe as $sRemoteClass => $aExtKeys) {
|
||||||
|
if (!isset($this->aDeletionPlan[$sRemoteClass])) {
|
||||||
|
$this->aDeletionPlan[$sRemoteClass] = new DeletionPlanEntity();
|
||||||
|
}
|
||||||
|
$oDeletionPlanEntity = $this->aDeletionPlan[$sRemoteClass];
|
||||||
|
/** @var \AttributeExternalKey $oExtKeyAttDef */
|
||||||
|
foreach ($aExtKeys as $sExtKeyAttCode => $oExtKeyAttDef) {
|
||||||
|
// skip if this external key is behind an external field
|
||||||
|
if (!$oExtKeyAttDef->IsExternalKey(EXTKEY_ABSOLUTE)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($oExtKeyAttDef->IsNullAllowed()) {
|
||||||
|
// update
|
||||||
|
$oUpdateItem = $this->UpdateExtKeyNullable($sRemoteClass, $sExtKeyAttCode, $sIdsToRemove);
|
||||||
|
$oDeletionPlanEntity->oUpdate->Merge($oUpdateItem);
|
||||||
|
} else {
|
||||||
|
// delete
|
||||||
|
$aRemoteIdsToRemove = $this->GetRemoteIdsForExtKey($sRemoteClass, $sExtKeyAttCode, $sIdsToRemove);
|
||||||
|
|
||||||
|
$iDeletePropagationOption = $oExtKeyAttDef->GetDeletionPropagationOption();
|
||||||
|
if ($iDeletePropagationOption == DEL_MANUAL) {
|
||||||
|
// Issue, do not recurse
|
||||||
|
$oDeletionPlanItem = new DeletionPlanItem(aIds: $aRemoteIdsToRemove);
|
||||||
|
$oDeletionPlanEntity->oIssue->Merge($oDeletionPlanItem);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (($iDeletePropagationOption == DEL_MOVEUP) && ($oExtKeyAttDef->IsHierarchicalKey())) {
|
||||||
|
// update hierarchical keys due to row cleanup in the same table
|
||||||
|
$sIdsToRemove = implode(',', $this->aDeletionPlan[$sRemoteClass]->oDelete->aIds);
|
||||||
|
$oUpdateItem = $this->UpdateHierarchicalExtKey($sRemoteClass, $sExtKeyAttCode, $sIdsToRemove);
|
||||||
|
$oDeletionPlanEntity->oUpdate->Merge($oUpdateItem);
|
||||||
|
// do not recurse
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete entries in Remote Class
|
||||||
|
if (count($aRemoteIdsToRemove) !== 0) {
|
||||||
|
// TODO see ObjectService::Delete() !!!!!!
|
||||||
|
$sRemoteIdsToDelete = implode(',', $aRemoteIdsToRemove);
|
||||||
|
$sRemoteTable = MetaModel::DBGetTable($sRemoteClass);
|
||||||
|
$sDBKey = MetaModel::DBGetKey($sRemoteClass);
|
||||||
|
$sSQL = "DELETE FROM `$sRemoteTable` WHERE `$sDBKey` IN ($sRemoteIdsToDelete)";
|
||||||
|
$oDeletionPlanEntity->oDelete->Merge(new DeletionPlanItem([$sSQL], $aRemoteIdsToRemove));
|
||||||
|
|
||||||
|
$this->DeletionPlanForReferencingClasses($sRemoteClass);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $sRemoteClass
|
||||||
|
* @param string $sExtKeyAttCode
|
||||||
|
* @param string $sIdsToRemoveInTargetClass
|
||||||
|
*
|
||||||
|
* @return \Combodo\iTop\DataFeatureRemoval\Entity\DeletionPlanItem
|
||||||
|
* @throws \CoreException
|
||||||
|
*/
|
||||||
|
public function UpdateExtKeyNullable(string $sRemoteClass, string $sExtKeyAttCode, string $sIdsToRemoveInTargetClass): DeletionPlanItem
|
||||||
|
{
|
||||||
|
$aIds = $this->GetRemoteIdsForExtKey($sRemoteClass, $sExtKeyAttCode, $sIdsToRemoveInTargetClass);
|
||||||
|
|
||||||
|
[$sDBTable, $sDBField] = $this->GetDBInfoForAttcode($sRemoteClass, $sExtKeyAttCode);
|
||||||
|
$sUpdateSQL = <<<SQL
|
||||||
|
UPDATE `$sDBTable` SET `updated`.`$sDBField` = 0
|
||||||
|
FROM `$sDBTable` AS `updated`
|
||||||
|
WHERE `updated`.`$sDBField` IN ($sIdsToRemoveInTargetClass)
|
||||||
|
SQL;
|
||||||
|
|
||||||
|
return new DeletionPlanItem([$sExtKeyAttCode => $sUpdateSQL], $aIds);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $sRemoteClass
|
||||||
|
* @param string $sExtKeyAttCode
|
||||||
|
* @param string $sIdsToRemoveInTargetClass
|
||||||
|
*
|
||||||
|
* @return \Combodo\iTop\DataFeatureRemoval\Entity\DeletionPlanItem
|
||||||
|
* @throws \CoreException
|
||||||
|
* @throws \MySQLException
|
||||||
|
*/
|
||||||
|
public function UpdateHierarchicalExtKey(string $sRemoteClass, string $sExtKeyAttCode, string $sIdsToRemoveInTargetClass): DeletionPlanItem
|
||||||
|
{
|
||||||
|
[$sDBTable, $sDBField, $sDBKey] = $this->GetDBInfoForAttcode($sRemoteClass, $sExtKeyAttCode);
|
||||||
|
$sUpdateSQL = <<<SQL
|
||||||
|
UPDATE `$sDBTable` SET `updated`.`$sDBField` = `removed`.`$sDBField`
|
||||||
|
FROM `$sDBTable` AS `updated`
|
||||||
|
INNER JOIN `$sDBTable` AS `removed` ON `updated`.`$sDBField` = `removed`.`$sDBKey`
|
||||||
|
WHERE `removed`.`$sDBKey` IN ($sIdsToRemoveInTargetClass)
|
||||||
|
SQL;
|
||||||
|
|
||||||
|
$sSQL = <<<SQL
|
||||||
|
SELECT `$sDBKey`
|
||||||
|
FROM `$sDBTable` AS `updated`
|
||||||
|
INNER JOIN `$sDBTable` AS `removed` ON `updated`.`$sDBField` = `removed`.`$sDBKey`
|
||||||
|
WHERE `removed`.`$sDBKey` IN ($sIdsToRemoveInTargetClass)
|
||||||
|
SQL;
|
||||||
|
$aIds = CMDBSource::QueryToCol($sSQL, $sDBKey);
|
||||||
|
|
||||||
|
return new DeletionPlanItem([$sExtKeyAttCode => $sUpdateSQL], $aIds);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $sRemoteClass
|
||||||
|
* @param string $sExtKeyAttCode
|
||||||
|
* @param string $sIdsToRemoveInTargetClass
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
* @throws \CoreException
|
||||||
|
* @throws \MySQLException
|
||||||
|
*/
|
||||||
|
public function GetRemoteIdsForExtKey(string $sRemoteClass, string $sExtKeyAttCode, string $sIdsToRemoveInTargetClass): array
|
||||||
|
{
|
||||||
|
if (\utils::IsNullOrEmptyString($sIdsToRemoveInTargetClass)) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
[$sDBTable, $sDBField, $sDBKey] = $this->GetDBInfoForAttcode($sRemoteClass, $sExtKeyAttCode);
|
||||||
|
$sSQL = "SELECT `$sDBKey` FROM `$sDBTable` WHERE `$sDBField` IN ($sIdsToRemoveInTargetClass)";
|
||||||
|
|
||||||
|
return CMDBSource::QueryToCol($sSQL, $sDBKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $sClass
|
||||||
|
*
|
||||||
|
* @return \Combodo\iTop\DataFeatureRemoval\Entity\DeletionPlanItem
|
||||||
|
* @throws \CoreException
|
||||||
|
* @throws \MySQLException
|
||||||
|
*/
|
||||||
|
public function GetInitialClassDeletionPlan(string $sClass): DeletionPlanItem
|
||||||
|
{
|
||||||
|
$sTable = MetaModel::DBGetTable($sClass);
|
||||||
|
$sDBKey = MetaModel::DBGetKey($sClass);
|
||||||
|
$sSQL = "SELECT `$sDBKey` FROM `$sTable`";
|
||||||
|
$aIds = CMDBSource::QueryToCol($sSQL, $sDBKey);
|
||||||
|
|
||||||
|
// TODO see ObjectService::Delete() !!!!!!
|
||||||
|
$sDeleteSQL = "DELETE FROM `$sTable`";
|
||||||
|
|
||||||
|
return new DeletionPlanItem([$sDeleteSQL], $aIds);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get database table for an attcode
|
||||||
|
*
|
||||||
|
* @param string $sClass
|
||||||
|
* @param string $sExtKeyAttCode
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
* @throws \CoreException
|
||||||
|
* @throws \Exception
|
||||||
|
*/
|
||||||
|
public function GetDBInfoForAttcode(string $sClass, string $sExtKeyAttCode): array
|
||||||
|
{
|
||||||
|
$sOriginClass = MetaModel::GetAttributeOrigin($sClass, $sExtKeyAttCode);
|
||||||
|
$sDBTable = MetaModel::DBGetTable($sOriginClass);
|
||||||
|
$oAttDef = MetaModel::GetAttributeDef($sOriginClass, $sExtKeyAttCode);
|
||||||
|
// External key is on a single DB column
|
||||||
|
$sDBField = array_keys($oAttDef->GetSQLColumns())[0];
|
||||||
|
$sDBKey = MetaModel::DBGetKey($sClass);
|
||||||
|
return [$sDBTable, $sDBField, $sDBKey];
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -8,6 +8,8 @@ $baseDir = dirname($vendorDir);
|
|||||||
return array(
|
return array(
|
||||||
'Combodo\\iTop\\DataFeatureRemoval\\Controller\\DataFeatureRemovalController' => $baseDir . '/src/Controller/DataFeatureRemovalController.php',
|
'Combodo\\iTop\\DataFeatureRemoval\\Controller\\DataFeatureRemovalController' => $baseDir . '/src/Controller/DataFeatureRemovalController.php',
|
||||||
'Combodo\\iTop\\DataFeatureRemoval\\Entity\\DataCleanupSummaryEntity' => $baseDir . '/src/Entity/DataCleanupSummaryEntity.php',
|
'Combodo\\iTop\\DataFeatureRemoval\\Entity\\DataCleanupSummaryEntity' => $baseDir . '/src/Entity/DataCleanupSummaryEntity.php',
|
||||||
|
'Combodo\\iTop\\DataFeatureRemoval\\Entity\\DeletionPlanEntity' => $baseDir . '/src/Entity/DeletionPlanEntity.php',
|
||||||
|
'Combodo\\iTop\\DataFeatureRemoval\\Entity\\DeletionPlanItem' => $baseDir . '/src/Entity/DeletionPlanItem.php',
|
||||||
'Combodo\\iTop\\DataFeatureRemoval\\Helper\\DataFeatureRemovalConfig' => $baseDir . '/src/Helper/DataFeatureRemovalConfig.php',
|
'Combodo\\iTop\\DataFeatureRemoval\\Helper\\DataFeatureRemovalConfig' => $baseDir . '/src/Helper/DataFeatureRemovalConfig.php',
|
||||||
'Combodo\\iTop\\DataFeatureRemoval\\Helper\\DataFeatureRemovalException' => $baseDir . '/src/Helper/DataFeatureRemovalException.php',
|
'Combodo\\iTop\\DataFeatureRemoval\\Helper\\DataFeatureRemovalException' => $baseDir . '/src/Helper/DataFeatureRemovalException.php',
|
||||||
'Combodo\\iTop\\DataFeatureRemoval\\Helper\\DataFeatureRemovalHelper' => $baseDir . '/src/Helper/DataFeatureRemovalHelper.php',
|
'Combodo\\iTop\\DataFeatureRemoval\\Helper\\DataFeatureRemovalHelper' => $baseDir . '/src/Helper/DataFeatureRemovalHelper.php',
|
||||||
@@ -16,6 +18,7 @@ return array(
|
|||||||
'Combodo\\iTop\\DataFeatureRemoval\\Service\\DataFeatureRemoverExtensionService' => $baseDir . '/src/Service/DataFeatureRemoverExtensionService.php',
|
'Combodo\\iTop\\DataFeatureRemoval\\Service\\DataFeatureRemoverExtensionService' => $baseDir . '/src/Service/DataFeatureRemoverExtensionService.php',
|
||||||
'Combodo\\iTop\\DataFeatureRemoval\\Service\\ObjectService' => $baseDir . '/src/Service/ObjectService.php',
|
'Combodo\\iTop\\DataFeatureRemoval\\Service\\ObjectService' => $baseDir . '/src/Service/ObjectService.php',
|
||||||
'Combodo\\iTop\\DataFeatureRemoval\\Service\\ObjectServiceSummary' => $baseDir . '/src/Service/ObjectServiceSummary.php',
|
'Combodo\\iTop\\DataFeatureRemoval\\Service\\ObjectServiceSummary' => $baseDir . '/src/Service/ObjectServiceSummary.php',
|
||||||
|
'Combodo\\iTop\\DataFeatureRemoval\\Service\\StaticDeletionPlan' => $baseDir . '/src/Service/StaticDeletionPlan.php',
|
||||||
'Combodo\\iTop\\DataFeatureRemoval\\Service\\iObjectService' => $baseDir . '/src/Service/iObjectService.php',
|
'Combodo\\iTop\\DataFeatureRemoval\\Service\\iObjectService' => $baseDir . '/src/Service/iObjectService.php',
|
||||||
'Composer\\InstalledVersions' => $vendorDir . '/composer/InstalledVersions.php',
|
'Composer\\InstalledVersions' => $vendorDir . '/composer/InstalledVersions.php',
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -23,6 +23,8 @@ class ComposerStaticInit4f96a7199e2c0d90e547333758b26464
|
|||||||
public static $classMap = array (
|
public static $classMap = array (
|
||||||
'Combodo\\iTop\\DataFeatureRemoval\\Controller\\DataFeatureRemovalController' => __DIR__ . '/../..' . '/src/Controller/DataFeatureRemovalController.php',
|
'Combodo\\iTop\\DataFeatureRemoval\\Controller\\DataFeatureRemovalController' => __DIR__ . '/../..' . '/src/Controller/DataFeatureRemovalController.php',
|
||||||
'Combodo\\iTop\\DataFeatureRemoval\\Entity\\DataCleanupSummaryEntity' => __DIR__ . '/../..' . '/src/Entity/DataCleanupSummaryEntity.php',
|
'Combodo\\iTop\\DataFeatureRemoval\\Entity\\DataCleanupSummaryEntity' => __DIR__ . '/../..' . '/src/Entity/DataCleanupSummaryEntity.php',
|
||||||
|
'Combodo\\iTop\\DataFeatureRemoval\\Entity\\DeletionPlanEntity' => __DIR__ . '/../..' . '/src/Entity/DeletionPlanEntity.php',
|
||||||
|
'Combodo\\iTop\\DataFeatureRemoval\\Entity\\DeletionPlanItem' => __DIR__ . '/../..' . '/src/Entity/DeletionPlanItem.php',
|
||||||
'Combodo\\iTop\\DataFeatureRemoval\\Helper\\DataFeatureRemovalConfig' => __DIR__ . '/../..' . '/src/Helper/DataFeatureRemovalConfig.php',
|
'Combodo\\iTop\\DataFeatureRemoval\\Helper\\DataFeatureRemovalConfig' => __DIR__ . '/../..' . '/src/Helper/DataFeatureRemovalConfig.php',
|
||||||
'Combodo\\iTop\\DataFeatureRemoval\\Helper\\DataFeatureRemovalException' => __DIR__ . '/../..' . '/src/Helper/DataFeatureRemovalException.php',
|
'Combodo\\iTop\\DataFeatureRemoval\\Helper\\DataFeatureRemovalException' => __DIR__ . '/../..' . '/src/Helper/DataFeatureRemovalException.php',
|
||||||
'Combodo\\iTop\\DataFeatureRemoval\\Helper\\DataFeatureRemovalHelper' => __DIR__ . '/../..' . '/src/Helper/DataFeatureRemovalHelper.php',
|
'Combodo\\iTop\\DataFeatureRemoval\\Helper\\DataFeatureRemovalHelper' => __DIR__ . '/../..' . '/src/Helper/DataFeatureRemovalHelper.php',
|
||||||
@@ -31,6 +33,7 @@ class ComposerStaticInit4f96a7199e2c0d90e547333758b26464
|
|||||||
'Combodo\\iTop\\DataFeatureRemoval\\Service\\DataFeatureRemoverExtensionService' => __DIR__ . '/../..' . '/src/Service/DataFeatureRemoverExtensionService.php',
|
'Combodo\\iTop\\DataFeatureRemoval\\Service\\DataFeatureRemoverExtensionService' => __DIR__ . '/../..' . '/src/Service/DataFeatureRemoverExtensionService.php',
|
||||||
'Combodo\\iTop\\DataFeatureRemoval\\Service\\ObjectService' => __DIR__ . '/../..' . '/src/Service/ObjectService.php',
|
'Combodo\\iTop\\DataFeatureRemoval\\Service\\ObjectService' => __DIR__ . '/../..' . '/src/Service/ObjectService.php',
|
||||||
'Combodo\\iTop\\DataFeatureRemoval\\Service\\ObjectServiceSummary' => __DIR__ . '/../..' . '/src/Service/ObjectServiceSummary.php',
|
'Combodo\\iTop\\DataFeatureRemoval\\Service\\ObjectServiceSummary' => __DIR__ . '/../..' . '/src/Service/ObjectServiceSummary.php',
|
||||||
|
'Combodo\\iTop\\DataFeatureRemoval\\Service\\StaticDeletionPlan' => __DIR__ . '/../..' . '/src/Service/StaticDeletionPlan.php',
|
||||||
'Combodo\\iTop\\DataFeatureRemoval\\Service\\iObjectService' => __DIR__ . '/../..' . '/src/Service/iObjectService.php',
|
'Combodo\\iTop\\DataFeatureRemoval\\Service\\iObjectService' => __DIR__ . '/../..' . '/src/Service/iObjectService.php',
|
||||||
'Composer\\InstalledVersions' => __DIR__ . '/..' . '/composer/InstalledVersions.php',
|
'Composer\\InstalledVersions' => __DIR__ . '/..' . '/composer/InstalledVersions.php',
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -242,8 +242,7 @@ class AjaxController extends Controller
|
|||||||
throw new SecurityException('Access forbidden');
|
throw new SecurityException('Access forbidden');
|
||||||
}
|
}
|
||||||
|
|
||||||
$sConfigFile = APPCONF.'production/config-itop.php';
|
SetupUtils::CreateSetupToken();
|
||||||
@chmod($sConfigFile, 0770); // Allow overwriting the file
|
|
||||||
|
|
||||||
header('Location: ../setup/');
|
header('Location: ../setup/');
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -166,8 +166,6 @@ class UpdateController extends Controller
|
|||||||
public function OperationRunSetup()
|
public function OperationRunSetup()
|
||||||
{
|
{
|
||||||
SetupUtils::CheckSetupToken(true);
|
SetupUtils::CheckSetupToken(true);
|
||||||
$sConfigFile = APPCONF.'production/'.ITOP_CONFIG_FILE;
|
|
||||||
@chmod($sConfigFile, 0770);
|
|
||||||
$sRedirectURL = utils::GetAbsoluteUrlAppRoot().'setup/index.php';
|
$sRedirectURL = utils::GetAbsoluteUrlAppRoot().'setup/index.php';
|
||||||
header("Location: $sRedirectURL");
|
header("Location: $sRedirectURL");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -85,13 +85,6 @@
|
|||||||
<class id="Attachment"/>
|
<class id="Attachment"/>
|
||||||
</classes>
|
</classes>
|
||||||
</group>
|
</group>
|
||||||
<group id="Ticket" _delta="define">
|
|
||||||
<classes>
|
|
||||||
<class id="Ticket"/>
|
|
||||||
<class id="WorkOrder"/>
|
|
||||||
<class id="Attachment"/>
|
|
||||||
</classes>
|
|
||||||
</group>
|
|
||||||
<group id="Portal" _delta="define">
|
<group id="Portal" _delta="define">
|
||||||
<classes>
|
<classes>
|
||||||
<class id="lnkFunctionalCIToTicket"/>
|
<class id="lnkFunctionalCIToTicket"/>
|
||||||
@@ -212,60 +205,6 @@
|
|||||||
</group>
|
</group>
|
||||||
</groups>
|
</groups>
|
||||||
<profiles>
|
<profiles>
|
||||||
<profile id="5500" _delta="define">
|
|
||||||
<name>Configuration ReadOnly</name>
|
|
||||||
<description>This read-only profile allows to see CIs objects.</description>
|
|
||||||
<groups>
|
|
||||||
<group id="Configuration">
|
|
||||||
<actions>
|
|
||||||
<action id="action:read">allow</action>
|
|
||||||
<action id="action:bulk read">allow</action>
|
|
||||||
</actions>
|
|
||||||
</group>
|
|
||||||
<group id="General">
|
|
||||||
<actions>
|
|
||||||
<action id="action:read">allow</action>
|
|
||||||
<action id="action:bulk read">allow</action>
|
|
||||||
</actions>
|
|
||||||
</group>
|
|
||||||
</groups>
|
|
||||||
</profile>
|
|
||||||
<profile id="5501" _delta="define">
|
|
||||||
<name>Ticket ReadOnly</name>
|
|
||||||
<description>This read-only profile allows to see Ticket objects.</description>
|
|
||||||
<groups>
|
|
||||||
<group id="Ticket">
|
|
||||||
<actions>
|
|
||||||
<action id="action:read">allow</action>
|
|
||||||
<action id="action:bulk read">allow</action>
|
|
||||||
</actions>
|
|
||||||
</group>
|
|
||||||
<group id="General">
|
|
||||||
<actions>
|
|
||||||
<action id="action:read">allow</action>
|
|
||||||
<action id="action:bulk read">allow</action>
|
|
||||||
</actions>
|
|
||||||
</group>
|
|
||||||
</groups>
|
|
||||||
</profile>
|
|
||||||
<profile id="5502" _delta="define">
|
|
||||||
<name>Service Catalog ReadOnly</name>
|
|
||||||
<description>This read-only profile allows to see Service Catalog objects.</description>
|
|
||||||
<groups>
|
|
||||||
<group id="Service">
|
|
||||||
<actions>
|
|
||||||
<action id="action:read">allow</action>
|
|
||||||
<action id="action:bulk read">allow</action>
|
|
||||||
</actions>
|
|
||||||
</group>
|
|
||||||
<group id="General">
|
|
||||||
<actions>
|
|
||||||
<action id="action:read">allow</action>
|
|
||||||
<action id="action:bulk read">allow</action>
|
|
||||||
</actions>
|
|
||||||
</group>
|
|
||||||
</groups>
|
|
||||||
</profile>
|
|
||||||
<profile id="117" _delta="define">
|
<profile id="117" _delta="define">
|
||||||
<name>SuperUser</name>
|
<name>SuperUser</name>
|
||||||
<description>This profile allows all actions which are not Administrator restricted.</description>
|
<description>This profile allows all actions which are not Administrator restricted.</description>
|
||||||
|
|||||||
@@ -4,64 +4,80 @@
|
|||||||
<contactid>9</contactid>
|
<contactid>9</contactid>
|
||||||
<email>jules.vernes@it.com</email>
|
<email>jules.vernes@it.com</email>
|
||||||
<org_id_friendlyname>IT Department</org_id_friendlyname>
|
<org_id_friendlyname>IT Department</org_id_friendlyname>
|
||||||
<login>SupportAgent</login>
|
<login>Agent</login>
|
||||||
<language>EN US</language>
|
<language>EN US</language>
|
||||||
<status>disabled</status>
|
<status>disabled</status>
|
||||||
<password><![CDATA[SupportAgent]]></password>
|
<profile_list>
|
||||||
|
<Set>
|
||||||
|
<URP_UserProfile id="5">
|
||||||
|
<profileid>5</profileid>
|
||||||
|
<profile>Support Agent</profile>
|
||||||
|
<reason></reason>
|
||||||
|
</URP_UserProfile>
|
||||||
|
</Set>
|
||||||
|
</profile_list>
|
||||||
|
<allowed_org_list>
|
||||||
|
<Set>
|
||||||
|
</Set>
|
||||||
|
</allowed_org_list>
|
||||||
|
<password><![CDATA[R6rQ;p]JT*FA$aaP^4]]></password>
|
||||||
<expiration>force_expire</expiration>
|
<expiration>force_expire</expiration>
|
||||||
<password_renewed_date>2026-06-04</password_renewed_date>
|
<password_renewed_date>2026-06-04</password_renewed_date>
|
||||||
</UserLocal>
|
</UserLocal>
|
||||||
<URP_UserProfile id="5">
|
|
||||||
<userid>4</userid>
|
|
||||||
<userlogin>SupportAgent</userlogin>
|
|
||||||
<profileid>SELECT URP_Profiles WHERE id='5'</profileid>
|
|
||||||
<profile>Support Agent</profile>
|
|
||||||
<reason></reason>
|
|
||||||
</URP_UserProfile>
|
|
||||||
<UserLocal alias="UserLocal" id="5">
|
<UserLocal alias="UserLocal" id="5">
|
||||||
<contactid>26</contactid>
|
<contactid>26</contactid>
|
||||||
<contactid_friendlyname>Jean Ferrat</contactid_friendlyname>
|
<contactid_friendlyname>Jean Ferrat</contactid_friendlyname>
|
||||||
<org_id_friendlyname>IT Department</org_id_friendlyname>
|
<org_id_friendlyname>IT Department</org_id_friendlyname>
|
||||||
<login>ConfigManager</login>
|
<login>config</login>
|
||||||
<language>EN US</language>
|
<language>EN US</language>
|
||||||
<status>disabled</status>
|
<status>disabled</status>
|
||||||
<password><![CDATA[$W[ConfigManager]]></password>
|
<profile_list>
|
||||||
|
<Set>
|
||||||
|
<URP_UserProfile id="6">
|
||||||
|
<profileid>3</profileid>
|
||||||
|
<profile>Configuration Manager</profile>
|
||||||
|
<reason></reason>
|
||||||
|
</URP_UserProfile>
|
||||||
|
</Set>
|
||||||
|
</profile_list>
|
||||||
|
<allowed_org_list>
|
||||||
|
<Set>
|
||||||
|
</Set>
|
||||||
|
</allowed_org_list>
|
||||||
|
<password><![CDATA[$W[:"7+Gf"Y\sd8#E~]]></password>
|
||||||
<expiration>force_expire</expiration>
|
<expiration>force_expire</expiration>
|
||||||
<password_renewed_date>2026-06-04</password_renewed_date>
|
<password_renewed_date>2026-06-04</password_renewed_date>
|
||||||
</UserLocal>
|
</UserLocal>
|
||||||
<URP_UserProfile id="6">
|
|
||||||
<userid>5</userid>
|
|
||||||
<userlogin>ConfigManager</userlogin>
|
|
||||||
<profileid>SELECT URP_Profiles WHERE id='3'</profileid>
|
|
||||||
<profile>Configuration Manager</profile>
|
|
||||||
<reason></reason>
|
|
||||||
</URP_UserProfile>
|
|
||||||
<UserLocal alias="UserLocal" id="2">
|
<UserLocal alias="UserLocal" id="2">
|
||||||
<contactid>15</contactid>
|
<contactid>15</contactid>
|
||||||
<email>agatha.christie@demo.com</email>
|
<email>agatha.christie@demo.com</email>
|
||||||
<org_id_friendlyname>Sales Department</org_id_friendlyname>
|
<org_id_friendlyname>Sales Department</org_id_friendlyname>
|
||||||
<login>PortalUser</login>
|
<login>Portal</login>
|
||||||
<language>EN US</language>
|
<language>EN US</language>
|
||||||
<status>disabled</status>
|
<status>disabled</status>
|
||||||
<password><![CDATA[PortalUser]]></password>
|
<profile_list>
|
||||||
|
<Set>
|
||||||
|
<URP_UserProfile id="2">
|
||||||
|
<profileid>2</profileid>
|
||||||
|
<profile>Portal user</profile>
|
||||||
|
<reason></reason>
|
||||||
|
</URP_UserProfile>
|
||||||
|
</Set>
|
||||||
|
</profile_list>
|
||||||
|
<allowed_org_list>
|
||||||
|
<Set>
|
||||||
|
<URP_UserOrg id="2">
|
||||||
|
<allowed_org_id>6</allowed_org_id>
|
||||||
|
<allowed_org_name>Sales Department</allowed_org_name>
|
||||||
|
<reason></reason>
|
||||||
|
<allowed_org_id_obsolescence_flag>no</allowed_org_id_obsolescence_flag>
|
||||||
|
</URP_UserOrg>
|
||||||
|
</Set>
|
||||||
|
</allowed_org_list>
|
||||||
|
<password><![CDATA[-Why]KDdMJvkuB8#e]]></password>
|
||||||
<expiration>force_expire</expiration>
|
<expiration>force_expire</expiration>
|
||||||
<password_renewed_date>2026-04-17</password_renewed_date>
|
<password_renewed_date>2026-04-17</password_renewed_date>
|
||||||
</UserLocal>
|
</UserLocal>
|
||||||
<URP_UserProfile id="2">
|
|
||||||
<userid>2</userid>
|
|
||||||
<userlogin>PortalUser</userlogin>
|
|
||||||
<profileid>SELECT URP_Profiles WHERE id='2'</profileid>
|
|
||||||
<profile>Portal user</profile>
|
|
||||||
<reason></reason>
|
|
||||||
</URP_UserProfile>
|
|
||||||
<URP_UserOrg id="2">
|
|
||||||
<userid>2</userid>
|
|
||||||
<userlogin>PortalUser</userlogin>
|
|
||||||
<allowed_org_id>6</allowed_org_id>
|
|
||||||
<allowed_org_name>Sales Department</allowed_org_name>
|
|
||||||
<reason></reason>
|
|
||||||
<allowed_org_id_obsolescence_flag>no</allowed_org_id_obsolescence_flag>
|
|
||||||
</URP_UserOrg>
|
|
||||||
<UserLocal alias="UserLocal" id="3">
|
<UserLocal alias="UserLocal" id="3">
|
||||||
<contactid>6</contactid>
|
<contactid>6</contactid>
|
||||||
<email>claude.monet@demo.com</email>
|
<email>claude.monet@demo.com</email>
|
||||||
@@ -69,48 +85,32 @@
|
|||||||
<login>SalesManager</login>
|
<login>SalesManager</login>
|
||||||
<language>EN US</language>
|
<language>EN US</language>
|
||||||
<status>disabled</status>
|
<status>disabled</status>
|
||||||
<password><![CDATA[SalesManager]]></password>
|
<profile_list>
|
||||||
|
<Set>
|
||||||
|
<URP_UserProfile id="4">
|
||||||
|
<profileid>12</profileid>
|
||||||
|
<profile>Portal power user</profile>
|
||||||
|
<reason></reason>
|
||||||
|
</URP_UserProfile>
|
||||||
|
<URP_UserProfile id="3">
|
||||||
|
<profileid>2</profileid>
|
||||||
|
<profile>Portal user</profile>
|
||||||
|
<reason></reason>
|
||||||
|
</URP_UserProfile>
|
||||||
|
</Set>
|
||||||
|
</profile_list>
|
||||||
|
<allowed_org_list>
|
||||||
|
<Set>
|
||||||
|
<URP_UserOrg id="1">
|
||||||
|
<allowed_org_id>6</allowed_org_id>
|
||||||
|
<allowed_org_name>Sales Department</allowed_org_name>
|
||||||
|
<reason></reason>
|
||||||
|
<allowed_org_id_obsolescence_flag>no</allowed_org_id_obsolescence_flag>
|
||||||
|
</URP_UserOrg>
|
||||||
|
</Set>
|
||||||
|
</allowed_org_list>
|
||||||
|
<password><![CDATA[D%r7hoZ})5*hvq5`{Q]]></password>
|
||||||
<expiration>force_expire</expiration>
|
<expiration>force_expire</expiration>
|
||||||
<password_renewed_date>2026-06-04</password_renewed_date>
|
<password_renewed_date>2026-06-04</password_renewed_date>
|
||||||
</UserLocal>
|
</UserLocal>
|
||||||
<URP_UserProfile id="4">
|
|
||||||
<userid>3</userid>
|
|
||||||
<userlogin>SalesManager</userlogin>
|
|
||||||
<profileid>SELECT URP_Profiles WHERE id='12'</profileid>
|
|
||||||
<profile>Portal power user</profile>
|
|
||||||
<reason></reason>
|
|
||||||
</URP_UserProfile>
|
|
||||||
<URP_UserProfile id="3">
|
|
||||||
<userid>3</userid>
|
|
||||||
<userlogin>SalesManager</userlogin>
|
|
||||||
<profileid>SELECT URP_Profiles WHERE id='2'</profileid>
|
|
||||||
<profile>Portal user</profile>
|
|
||||||
<reason></reason>
|
|
||||||
</URP_UserProfile>
|
|
||||||
<URP_UserOrg id="1">
|
|
||||||
<userid>3</userid>
|
|
||||||
<userlogin>SalesManager</userlogin>
|
|
||||||
<allowed_org_id>6</allowed_org_id>
|
|
||||||
<allowed_org_name>Sales Department</allowed_org_name>
|
|
||||||
<reason></reason>
|
|
||||||
<allowed_org_id_obsolescence_flag>no</allowed_org_id_obsolescence_flag>
|
|
||||||
</URP_UserOrg>
|
|
||||||
<UserLocal alias="UserLocal" id="6">
|
|
||||||
<contactid>18</contactid>
|
|
||||||
<email>rene.descartes@demo.com</email>
|
|
||||||
<org_id_friendlyname>Demo</org_id_friendlyname>
|
|
||||||
<login>SuperUser</login>
|
|
||||||
<language>EN US</language>
|
|
||||||
<status>disabled</status>
|
|
||||||
<password><![CDATA[SuperUser]]></password>
|
|
||||||
<expiration>force_expire</expiration>
|
|
||||||
<password_renewed_date>2026-04-17</password_renewed_date>
|
|
||||||
</UserLocal>
|
|
||||||
<URP_UserProfile id="7">
|
|
||||||
<userid>6</userid>
|
|
||||||
<userlogin>SuperUser</userlogin>
|
|
||||||
<profileid>SELECT URP_Profiles WHERE id='117'</profileid>
|
|
||||||
<profile>Super User</profile>
|
|
||||||
<reason></reason>
|
|
||||||
</URP_UserProfile>
|
|
||||||
</Set>
|
</Set>
|
||||||
|
|||||||
@@ -74,7 +74,6 @@ if (SetupUtils::IsSessionSetupTokenValid()) {
|
|||||||
// The configuration file already exists
|
// The configuration file already exists
|
||||||
if (!is_writable($sConfigFile)) {
|
if (!is_writable($sConfigFile)) {
|
||||||
SetupUtils::ExitReadOnlyMode(false); // Reset readonly mode in case of problem
|
SetupUtils::ExitReadOnlyMode(false); // Reset readonly mode in case of problem
|
||||||
SetupUtils::EraseSetupToken();
|
|
||||||
$sRelativePath = utils::GetConfigFilePathRelative(ITOP_DEFAULT_ENV);
|
$sRelativePath = utils::GetConfigFilePathRelative(ITOP_DEFAULT_ENV);
|
||||||
$oP = new SetupPage('Installation Cannot Continue');
|
$oP = new SetupPage('Installation Cannot Continue');
|
||||||
$oP->add("<h2>Fatal error</h2>\n");
|
$oP->add("<h2>Fatal error</h2>\n");
|
||||||
@@ -87,7 +86,6 @@ HTML;
|
|||||||
$oP->p($sButtonsHtml);
|
$oP->p($sButtonsHtml);
|
||||||
|
|
||||||
$oP->output();
|
$oP->output();
|
||||||
// Prevent token creation
|
|
||||||
exit;
|
exit;
|
||||||
} else {
|
} else {
|
||||||
chmod($sConfigFile, 0440);
|
chmod($sConfigFile, 0440);
|
||||||
|
|||||||
@@ -196,7 +196,6 @@ class WizardController
|
|||||||
SetupLog::Info("=== Setup screen: ".$oStep->GetTitle().' ('.get_class($oStep).')');
|
SetupLog::Info("=== Setup screen: ".$oStep->GetTitle().' ('.get_class($oStep).')');
|
||||||
$oPage = new SetupPage($oStep->GetTitle());
|
$oPage = new SetupPage($oStep->GetTitle());
|
||||||
$oPage->LinkScriptFromAppRoot('setup/setup.js');
|
$oPage->LinkScriptFromAppRoot('setup/setup.js');
|
||||||
$oStep->PreFormDisplay($oPage);
|
|
||||||
|
|
||||||
$oPage->add('<form id="wiz_form" class="ibo-setup--wizard" method="post">');
|
$oPage->add('<form id="wiz_form" class="ibo-setup--wizard" method="post">');
|
||||||
$oPage->add('<div class="ibo-setup--wizard--content">');
|
$oPage->add('<div class="ibo-setup--wizard--content">');
|
||||||
|
|||||||
@@ -124,18 +124,18 @@ HTML
|
|||||||
if (file_exists($sBuildConfigFile)) {
|
if (file_exists($sBuildConfigFile)) {
|
||||||
$oPage->add(
|
$oPage->add(
|
||||||
<<<HTML
|
<<<HTML
|
||||||
<form method="post">
|
<form id="fast_setup" method="post">
|
||||||
<input type="hidden" name="_class" value="WizStepLandingBeforeAudit"/>
|
<input type="hidden" name="_class" value="WizStepLandingBeforeAudit"/>
|
||||||
<input type="hidden" name="operation" value="next"/>
|
<input type="hidden" name="operation" value="next"/>
|
||||||
<input type="hidden" name="_params[skip_wizard]" value="1"/>
|
<input type="hidden" name="_params[skip_wizard]" value="1"/>
|
||||||
<table style="width:100%;" class="ibo-setup--wizard--buttons-container">
|
|
||||||
<tr>
|
|
||||||
<td style="text-align: right"><button type="submit" class="ibo-button ibo-is-regular ibo-is-secondary">Keep current choices</button></td>
|
|
||||||
</tr>
|
|
||||||
</table>
|
|
||||||
</form>
|
</form>
|
||||||
HTML
|
HTML
|
||||||
);
|
);
|
||||||
|
$oPage->add_ready_script(
|
||||||
|
<<<JS
|
||||||
|
$('.ibo-setup--wizard--buttons-container tr td:nth-child(1)').before('<td style="text-align:center;"><button class="ibo-button ibo-is-alternative ibo-is-neutral" form="fast_setup"><span class="ibo-button--label">Keep current choices</span></button></td>');
|
||||||
|
JS
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -76,10 +76,6 @@ abstract class WizardStep
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
public function PreFormDisplay(SetupPage $oPage)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function CheckDependencies()
|
protected function CheckDependencies()
|
||||||
{
|
{
|
||||||
if (is_null($this->bDependencyCheck)) {
|
if (is_null($this->bDependencyCheck)) {
|
||||||
|
|||||||
@@ -87,6 +87,8 @@ abstract class ItopDataTestCase extends ItopTestCase
|
|||||||
*/
|
*/
|
||||||
public const DEFAULT_TEST_ENVIRONMENT = 'production';
|
public const DEFAULT_TEST_ENVIRONMENT = 'production';
|
||||||
public const USE_TRANSACTION = true;
|
public const USE_TRANSACTION = true;
|
||||||
|
public const CREATE_TEST_ORG = false;
|
||||||
|
|
||||||
protected static $aURP_Profiles = [
|
protected static $aURP_Profiles = [
|
||||||
'Administrator' => 1,
|
'Administrator' => 1,
|
||||||
'Portal user' => 2,
|
'Portal user' => 2,
|
||||||
@@ -100,15 +102,9 @@ abstract class ItopDataTestCase extends ItopTestCase
|
|||||||
'Service Manager' => 10,
|
'Service Manager' => 10,
|
||||||
'Document author' => 11,
|
'Document author' => 11,
|
||||||
'Portal power user' => 12,
|
'Portal power user' => 12,
|
||||||
'Business partner user' => 40,
|
|
||||||
'REST Services User' => 1024,
|
'REST Services User' => 1024,
|
||||||
'Configuration ReadOnly' => 5500,
|
|
||||||
'Ticket ReadOnly' => 5501,
|
|
||||||
'Service Catalog ReadOnly' => 5502,
|
|
||||||
];
|
];
|
||||||
|
|
||||||
public const CREATE_TEST_ORG = false;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This method is called before the first test of this test class is run (in the current process).
|
* This method is called before the first test of this test class is run (in the current process).
|
||||||
*/
|
*/
|
||||||
@@ -1467,42 +1463,16 @@ abstract class ItopDataTestCase extends ItopTestCase
|
|||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @description To avoid adding finalclasses parameters to GivenUserInDB
|
|
||||||
* @param string $sPassword
|
|
||||||
* @param array $aProfiles Profile names Example: ['Administrator']
|
|
||||||
* @param bool $bReturnLogin
|
|
||||||
*
|
|
||||||
* @return string|int The unique login
|
|
||||||
* @throws \Exception
|
|
||||||
*/
|
|
||||||
protected function GivenTokenUserInDB(array $aProfiles, bool $bReturnLogin = true): string|int
|
|
||||||
{
|
|
||||||
$sLogin = 'demo_test_'.uniqid(__CLASS__, true);
|
|
||||||
|
|
||||||
$aProfileList = array_map(function ($sProfileId) {
|
|
||||||
return 'profileid:'.self::$aURP_Profiles[$sProfileId];
|
|
||||||
}, $aProfiles);
|
|
||||||
|
|
||||||
$iUser = $this->GivenObjectInDB('UserToken', [
|
|
||||||
'login' => $sLogin,
|
|
||||||
'language' => 'EN US',
|
|
||||||
'profile_list' => $aProfileList,
|
|
||||||
]);
|
|
||||||
return $bReturnLogin ? $sLogin : $iUser;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param string $sPassword
|
* @param string $sPassword
|
||||||
* @param array $aProfiles Profile names Example: ['Administrator']
|
* @param array $aProfiles Profile names Example: ['Administrator']
|
||||||
* @param string|null $sLogin
|
* @param string|null $sLogin
|
||||||
* @param string|null $sUserId
|
* @param string|null $sUserId
|
||||||
* @param bool $bReturnLogin
|
|
||||||
*
|
*
|
||||||
* @return string The unique login
|
* @return string The unique login
|
||||||
* @throws \Exception
|
* @throws \Exception
|
||||||
*/
|
*/
|
||||||
protected function GivenUserInDB(string $sPassword, array $aProfiles, ?string $sLogin = null, ?string &$sUserId = null, bool $bReturnLogin = true): string
|
protected function GivenUserInDB(string $sPassword, array $aProfiles, ?string $sLogin = null, ?string &$sUserId = null): string
|
||||||
{
|
{
|
||||||
if (is_null($sLogin)) {
|
if (is_null($sLogin)) {
|
||||||
$sLogin = 'demo_test_'.uniqid(__CLASS__, true);
|
$sLogin = 'demo_test_'.uniqid(__CLASS__, true);
|
||||||
@@ -1519,7 +1489,7 @@ abstract class ItopDataTestCase extends ItopTestCase
|
|||||||
'profile_list' => $aProfileList,
|
'profile_list' => $aProfileList,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
return $bReturnLogin ? $sLogin : $sUserId;
|
return $sLogin;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -29,11 +29,11 @@ namespace Combodo\iTop\Test\UnitTest\Core;
|
|||||||
|
|
||||||
use Combodo\iTop\Test\UnitTest\ItopDataTestCase;
|
use Combodo\iTop\Test\UnitTest\ItopDataTestCase;
|
||||||
use CoreCannotSaveObjectException;
|
use CoreCannotSaveObjectException;
|
||||||
|
use CoreException;
|
||||||
use DBObject;
|
use DBObject;
|
||||||
use DBObjectSearch;
|
use DBObjectSearch;
|
||||||
use DBObjectSet;
|
use DBObjectSet;
|
||||||
use DeleteException;
|
use DeleteException;
|
||||||
use Dict;
|
|
||||||
use MetaModel;
|
use MetaModel;
|
||||||
use UserLocal;
|
use UserLocal;
|
||||||
use UserRights;
|
use UserRights;
|
||||||
@@ -81,65 +81,6 @@ class UserRightsTest extends ItopDataTestCase
|
|||||||
return $oUser;
|
return $oUser;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testIsActionAllowedWithNonInstantiatedUserObject()
|
|
||||||
{
|
|
||||||
$oUser = $this->GivenUserWithProfiles('test1', [self::$aURP_Profiles['Configuration Manager']]); // not a readonly profile
|
|
||||||
$oAdminUser = $this->GivenUserWithProfiles('test2', [self::$aURP_Profiles['Administrator']]);
|
|
||||||
$oAdminUser->DBInsert();
|
|
||||||
$_SESSION = [];
|
|
||||||
UserRights::Login($oAdminUser->Get('login'));
|
|
||||||
|
|
||||||
self::assertTrue(UserRights::IsActionAllowed('Server', UR_ACTION_MODIFY, null, $oUser) === UR_ALLOWED_YES);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param array $aProfileIds
|
|
||||||
* @param array $aShouldBeAllowedToSeeClass
|
|
||||||
* @param array $aShouldBeAllowedToEditClass
|
|
||||||
*
|
|
||||||
* @return void
|
|
||||||
* @throws \ArchivedObjectException
|
|
||||||
* @throws \CoreCannotSaveObjectException
|
|
||||||
* @throws \CoreException
|
|
||||||
* @throws \CoreUnexpectedValue
|
|
||||||
* @throws \CoreWarning
|
|
||||||
* @throws \DictExceptionUnknownLanguage
|
|
||||||
* @throws \MySQLException
|
|
||||||
* @throws \OQLException
|
|
||||||
* @dataProvider ReadOnlyProvider
|
|
||||||
*/
|
|
||||||
public function testReadOnlyUser(array $aProfileIds, array $aShouldBeAllowedToSeeClass, array $aShouldBeAllowedToEditClass): void
|
|
||||||
{
|
|
||||||
|
|
||||||
$oUser = $this->GivenUserWithProfiles('test1', $aProfileIds);
|
|
||||||
$oUser->DBInsert();
|
|
||||||
$_SESSION = [];
|
|
||||||
UserRights::Login($oUser->Get('login'));
|
|
||||||
|
|
||||||
$aClassesToTest = ['FunctionalCI', 'Ticket', 'ServiceFamily'];
|
|
||||||
|
|
||||||
foreach ($aClassesToTest as $sClass) {
|
|
||||||
$bShouldBeAllowedToSee = in_array($sClass, $aShouldBeAllowedToSeeClass);
|
|
||||||
$bIsAllowedReading = (bool)UserRights::IsActionAllowed($sClass, UR_ACTION_READ);
|
|
||||||
|
|
||||||
$this->assertSame(
|
|
||||||
$bShouldBeAllowedToSee,
|
|
||||||
$bIsAllowedReading,
|
|
||||||
"User with profiles ".implode(',', $aProfileIds)." should ".($bShouldBeAllowedToSee ? "" : "NOT ")."be allowed to see class $sClass"
|
|
||||||
);
|
|
||||||
|
|
||||||
$bShouldBeAllowedToEdit = in_array($sClass, $aShouldBeAllowedToEditClass);
|
|
||||||
|
|
||||||
$bIsAllowedEditing = (bool)UserRights::IsActionAllowed($sClass, UR_ACTION_MODIFY);
|
|
||||||
|
|
||||||
$this->assertSame(
|
|
||||||
$bIsAllowedEditing,
|
|
||||||
$bShouldBeAllowedToEdit,
|
|
||||||
"User with profiles ".implode(',', $aProfileIds)." should ".($bShouldBeAllowedToEdit ? "" : "NOT ")."be allowed to edit class $sClass"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function GivenUserWithProfiles(string $sLogin, array $aProfileIds): DBObject
|
protected function GivenUserWithProfiles(string $sLogin, array $aProfileIds): DBObject
|
||||||
{
|
{
|
||||||
$oProfiles = new \ormLinkSet(\UserLocal::class, 'profile_list', \DBObjectSet::FromScratch(\URP_UserProfile::class));
|
$oProfiles = new \ormLinkSet(\UserLocal::class, 'profile_list', \DBObjectSet::FromScratch(\URP_UserProfile::class));
|
||||||
@@ -492,7 +433,7 @@ class UserRightsTest extends ItopDataTestCase
|
|||||||
$oUser = $this->GivenUserWithProfiles('test1', [$iProfileId, 2]);
|
$oUser = $this->GivenUserWithProfiles('test1', [$iProfileId, 2]);
|
||||||
|
|
||||||
$this->expectException(CoreCannotSaveObjectException::class);
|
$this->expectException(CoreCannotSaveObjectException::class);
|
||||||
$this->expectExceptionMessage(Dict::Format('Class:User/Error:PrivilegedUserMustHaveAccessToBackOffice', PORTAL_PROFILE_NAME));
|
$this->expectExceptionMessage('Profile "Portal user" cannot be given to privileged Users (Administrators, SuperUsers and REST Services Users)');
|
||||||
$oUser->DBInsert();
|
$oUser->DBInsert();
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -631,82 +572,4 @@ class UserRightsTest extends ItopDataTestCase
|
|||||||
$oUser = $this->InvokeNonPublicStaticMethod(UserRights::class, "FindUser", [$sLogin]);
|
$oUser = $this->InvokeNonPublicStaticMethod(UserRights::class, "FindUser", [$sLogin]);
|
||||||
static::assertNull($oUser, 'FindUser should return null when the login is unknown');
|
static::assertNull($oUser, 'FindUser should return null when the login is unknown');
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function ReadOnlyProvider(): array
|
|
||||||
{
|
|
||||||
return [
|
|
||||||
'CI' => [
|
|
||||||
'ProfilesId' => [
|
|
||||||
5500,
|
|
||||||
],
|
|
||||||
'ShouldBeAllowedToSeeClasses' => [
|
|
||||||
'FunctionalCI',
|
|
||||||
],
|
|
||||||
'ShouldBeAllowedToEditClasses' => [],
|
|
||||||
],
|
|
||||||
'Tickets' => [
|
|
||||||
'ProfilesId' => [
|
|
||||||
5501,
|
|
||||||
],
|
|
||||||
'ShouldBeAllowedToSeeClasses' => [
|
|
||||||
'Ticket',
|
|
||||||
],
|
|
||||||
'ShouldBeAllowedToEditClasses' => [],
|
|
||||||
],
|
|
||||||
'Catalog' => [
|
|
||||||
'ProfilesId' => [
|
|
||||||
5502,
|
|
||||||
],
|
|
||||||
'ShouldBeAllowedToSeeClasses' => [
|
|
||||||
'ServiceFamily',
|
|
||||||
],
|
|
||||||
'ShouldBeAllowedToEditClasses' => [],
|
|
||||||
],
|
|
||||||
'CI and Tickets' => [
|
|
||||||
'ProfilesId' => [
|
|
||||||
5500, 5501,
|
|
||||||
],
|
|
||||||
'ShouldBeAllowedToSeeClasses' => [
|
|
||||||
'FunctionalCI', 'Ticket',
|
|
||||||
],
|
|
||||||
'ShouldBeAllowedToEditClasses' => [],
|
|
||||||
],
|
|
||||||
'CI and Catalog' => [
|
|
||||||
'ProfilesId' => [
|
|
||||||
5500, 5502,
|
|
||||||
],
|
|
||||||
'ShouldBeAllowedToSeeClasses' => [
|
|
||||||
'FunctionalCI', 'ServiceFamily',
|
|
||||||
],
|
|
||||||
'ShouldBeAllowedToEditClasses' => [],
|
|
||||||
],
|
|
||||||
'Tickets and Catalog' => [
|
|
||||||
'ProfilesId' => [
|
|
||||||
5501, 5502,
|
|
||||||
],
|
|
||||||
'ShouldBeAllowedToSeeClasses' => [
|
|
||||||
'Ticket', 'ServiceFamily',
|
|
||||||
],
|
|
||||||
'ShouldBeAllowedToEditClasses' => [],
|
|
||||||
],
|
|
||||||
'Tickets and Catalog + profile Ccnfiguration Manager' => [
|
|
||||||
'ProfilesId' => [
|
|
||||||
5501, 5502, 3,
|
|
||||||
],
|
|
||||||
'ShouldBeAllowedToSeeClasses' => [
|
|
||||||
'FunctionalCI', 'Ticket', 'ServiceFamily',
|
|
||||||
],
|
|
||||||
'ShouldBeAllowedToEditClasses' => ['FunctionalCI'],
|
|
||||||
],
|
|
||||||
'CI, Tickets and Catalog' => [
|
|
||||||
'ProfilesId' => [
|
|
||||||
5500, 5501, 5502,
|
|
||||||
],
|
|
||||||
'ShouldBeAllowedToSeeClasses' => [
|
|
||||||
'FunctionalCI', 'Ticket', 'ServiceFamily',
|
|
||||||
],
|
|
||||||
'ShouldBeAllowedToEditClasses' => [],
|
|
||||||
],
|
|
||||||
];
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,64 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* @copyright Copyright (C) 2010-2026 Combodo SAS
|
||||||
|
* @license http://opensource.org/licenses/AGPL-3.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
use Combodo\iTop\Test\UnitTest\ItopCustomDatamodelTestCase;
|
||||||
|
|
||||||
|
class AbstractCleanup extends ItopCustomDatamodelTestCase
|
||||||
|
{
|
||||||
|
public function GetDatamodelDeltaAbsPath(): string
|
||||||
|
{
|
||||||
|
return __DIR__.'/data_cleanup_delta.xml';
|
||||||
|
}
|
||||||
|
|
||||||
|
protected array $aIdByClass;
|
||||||
|
protected array $aIdByObjectName = [];
|
||||||
|
|
||||||
|
protected function GivenDFRTreeInDB(string $sTree)
|
||||||
|
{
|
||||||
|
$this->aIdByClass = [];
|
||||||
|
$aTree = explode("\n", $sTree);
|
||||||
|
foreach ($aTree as $sLine) {
|
||||||
|
if (trim($sLine) === '') {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
$this->GivenDFRTreeLineInDB($sLine);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function GivenDFRTreeLineInDB(string $sLine)
|
||||||
|
{
|
||||||
|
[$sLeft, $sRight] = explode('<-', $sLine);
|
||||||
|
$sLeft = trim($sLeft);
|
||||||
|
|
||||||
|
$iLeftId = $this->aIdByObjectName[$sLeft] ?? 0;
|
||||||
|
if ($iLeftId === 0) {
|
||||||
|
[$sChildClass] = explode('_', $sLeft, 2);
|
||||||
|
$iLeftId = $this->GivenObjectInDB($sChildClass, ['name' => $sLeft]);
|
||||||
|
$this->aIdByClass[$sChildClass][] = $iLeftId;
|
||||||
|
$this->aIdByObjectName[$sLeft] = $iLeftId;
|
||||||
|
}
|
||||||
|
|
||||||
|
$sRight = trim($sRight);
|
||||||
|
if (preg_match("/(?<name>(?<class>[^_]+)_\d+)(\s+\((?<extkey>\w+)\))?/", $sRight, $aMatches) !== false) {
|
||||||
|
$sName = $aMatches['name'];
|
||||||
|
$sChildClass = $aMatches['class'];
|
||||||
|
$sExtKey = $aMatches['extkey'] ?? 'extkey_id';
|
||||||
|
|
||||||
|
$iRightId = $this->aIdByObjectName[$sName] ?? 0;
|
||||||
|
if ($iRightId === 0) {
|
||||||
|
$iRightId = $this->GivenObjectInDB($sChildClass, ['name' => $sName, $sExtKey => $iLeftId]);
|
||||||
|
$this->aIdByClass[$sChildClass][] = $iRightId;
|
||||||
|
$this->aIdByObjectName[$sName] = $iRightId;
|
||||||
|
} else {
|
||||||
|
// Update object
|
||||||
|
$oObj = MetaModel::GetObject($sChildClass, $iRightId);
|
||||||
|
$oObj->Set($sExtKey, $iLeftId);
|
||||||
|
$oObj->DBUpdate();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -34,15 +34,10 @@ use PHPUnit\Framework\MockObject\MockObject;
|
|||||||
* @see DataCleanupSummaryEntity
|
* @see DataCleanupSummaryEntity
|
||||||
* @see ItopDataTestCase
|
* @see ItopDataTestCase
|
||||||
*/
|
*/
|
||||||
class DataCleanupServiceTest extends ItopCustomDatamodelTestCase
|
class DataCleanupServiceTest extends \AbstractCleanup
|
||||||
{
|
{
|
||||||
private ExecutionLimits&MockObject $oExecutionLimits;
|
private ExecutionLimits&MockObject $oExecutionLimits;
|
||||||
|
|
||||||
public function GetDatamodelDeltaAbsPath(): string
|
|
||||||
{
|
|
||||||
return __DIR__.'/data_cleanup_delta.xml';
|
|
||||||
}
|
|
||||||
|
|
||||||
//--- GetCleanupSummary tests ---
|
//--- GetCleanupSummary tests ---
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -261,36 +256,6 @@ class DataCleanupServiceTest extends ItopCustomDatamodelTestCase
|
|||||||
$this->AssertSummaryEquals($aExpected, $aRes);
|
$this->AssertSummaryEquals($aExpected, $aRes);
|
||||||
}
|
}
|
||||||
|
|
||||||
private function GivenDFRTreeInDB(string $sTree)
|
|
||||||
{
|
|
||||||
$aTree = explode("\n", $sTree);
|
|
||||||
foreach ($aTree as $sLine) {
|
|
||||||
if (trim($sLine) === "") {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
$this->GivenDFRTreeLineInDB($sLine);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private array $aIdByObjectName = [];
|
|
||||||
private function GivenDFRTreeLineInDB(string $sLine)
|
|
||||||
{
|
|
||||||
[$sLeft, $sRight] = explode('<-', $sLine);
|
|
||||||
$sLeft = trim($sLeft);
|
|
||||||
|
|
||||||
$iLeftId = $this->aIdByObjectName[$sLeft] ?? 0;
|
|
||||||
if ($iLeftId === 0) {
|
|
||||||
[$sChildClass, ] = explode('_', $sLeft, 2);
|
|
||||||
$iLeftId = $this->GivenObjectInDB($sChildClass, ['name' => $sLeft]);
|
|
||||||
$this->aIdByObjectName[$sLeft] = $iLeftId;
|
|
||||||
}
|
|
||||||
|
|
||||||
$sRight = trim($sRight);
|
|
||||||
[$sChildClass, ] = explode('_', $sRight, 2);
|
|
||||||
$iRightId = $this->GivenObjectInDB($sChildClass, ['name' => $sRight, 'extkey_id' => $iLeftId]);
|
|
||||||
$this->aIdByObjectName[$sRight] = $iRightId;
|
|
||||||
}
|
|
||||||
|
|
||||||
private function GivenExecutionLimits(int $iStopAfterCallNumberReached): void
|
private function GivenExecutionLimits(int $iStopAfterCallNumberReached): void
|
||||||
{
|
{
|
||||||
$matcher = $this->any();
|
$matcher = $this->any();
|
||||||
|
|||||||
@@ -0,0 +1,167 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Combodo\iTop\Test\UnitTest\Module\DataFeatureRemoval;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* @copyright Copyright (C) 2010-2026 Combodo SAS
|
||||||
|
* @license http://opensource.org/licenses/AGPL-3.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
use Combodo\iTop\DataFeatureRemoval\Service\StaticDeletionPlan;
|
||||||
|
use MetaModel;
|
||||||
|
|
||||||
|
class StaticDeletionPlanTest extends \AbstractCleanup
|
||||||
|
{
|
||||||
|
public function GetDatamodelDeltaAbsPath(): string
|
||||||
|
{
|
||||||
|
return __DIR__.'/data_cleanup_delta.xml';
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testGetInitialClassDeletionPlan()
|
||||||
|
{
|
||||||
|
$this->GivenDFRTreeInDB(<<<EOF
|
||||||
|
DFRToRemoveLeaf_1 <- DFRToUpdate_1
|
||||||
|
DFRToRemoveLeaf_1 <- DFRToUpdate_2
|
||||||
|
DFRToRemoveLeaf_2 <- DFRToUpdate_3
|
||||||
|
EOF);
|
||||||
|
|
||||||
|
$oService = new StaticDeletionPlan();
|
||||||
|
$oDeletionPlanItem = $oService->GetInitialClassDeletionPlan('DFRToRemoveLeaf');
|
||||||
|
self::assertEquals(2, $oDeletionPlanItem->Count(), 'All entries of root table should be removed');
|
||||||
|
self::assertEquals($this->aIdByClass['DFRToRemoveLeaf'], $oDeletionPlanItem->aIds, 'All the Ids found in root table should correspond to the one created');
|
||||||
|
$sTable = MetaModel::DBGetTable('DFRToRemoveLeaf');
|
||||||
|
$sExpectedSQL = "DELETE FROM `$sTable`";
|
||||||
|
self::assertEquals($sExpectedSQL, $oDeletionPlanItem->aQueries[0], 'Removing elements in root class should suppress all entries');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testUpdateExtKeyNullable()
|
||||||
|
{
|
||||||
|
$this->GivenDFRTreeInDB(<<<EOF
|
||||||
|
DFRToRemoveLeaf_1 <- DFRToUpdate_1
|
||||||
|
DFRToRemoveLeaf_1 <- DFRToUpdate_2
|
||||||
|
DFRToRemoveLeaf_2 <- DFRToUpdate_3
|
||||||
|
DFRLeafNotToRemove_1 <- DFRToUpdate_4
|
||||||
|
EOF);
|
||||||
|
|
||||||
|
// WHEN
|
||||||
|
$oService = new StaticDeletionPlan();
|
||||||
|
$sRemoteClass = 'DFRToUpdate';
|
||||||
|
$oDeletionPlanItem = $oService->UpdateExtKeyNullable(
|
||||||
|
$sRemoteClass,
|
||||||
|
'extkey_id',
|
||||||
|
implode(',', $this->aIdByClass['DFRToRemoveLeaf'])
|
||||||
|
);
|
||||||
|
$sUpdateSQL = $oDeletionPlanItem->aQueries['extkey_id'];
|
||||||
|
|
||||||
|
// THEN
|
||||||
|
$sExpectedSQLEnd = " IN (".implode(',', $this->aIdByClass['DFRToRemoveLeaf']).")";
|
||||||
|
self::assertStringEndsWith($sExpectedSQLEnd, $sUpdateSQL, 'The query should be filtered with the ids of the root class');
|
||||||
|
|
||||||
|
self::assertEquals(3, $oDeletionPlanItem->Count(), 'All entries of root table should be removed');
|
||||||
|
$sIdsToRemoveInTargetClass = implode(',', $this->aIdByClass['DFRToRemoveLeaf']);
|
||||||
|
$aExpectedIds = $oService->GetRemoteIdsForExtKey($sRemoteClass, 'extkey_id', $sIdsToRemoveInTargetClass);
|
||||||
|
|
||||||
|
self::assertEquals($aExpectedIds, $oDeletionPlanItem->aIds, 'All entries pointing on root class should be removed');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests that GetCleanupSummary returns an empty array when passed null as input.
|
||||||
|
*/
|
||||||
|
public function testGetCleanupSummaryReturnsEmptyArrayWhenNull(): void
|
||||||
|
{
|
||||||
|
$oService = new StaticDeletionPlan();
|
||||||
|
$aResult = $oService->GetStaticDeletionPlan([]);
|
||||||
|
|
||||||
|
$this->assertIsArray($aResult, 'Expected result to be an array when input is null.');
|
||||||
|
$this->assertEmpty($aResult, 'Expected result to be empty array when input is null.');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testGetStaticDeletionPlan_DeleteObjRecursively()
|
||||||
|
{
|
||||||
|
$this->GivenDFRTreeInDB(<<<EOF
|
||||||
|
DFRToRemoveLeaf_1 <- DFRToUpdate_1
|
||||||
|
DFRToRemoveLeaf_1 <- DFRRemovedCollateral_1
|
||||||
|
DFRRemovedCollateral_1 <- DFRRemovedCollateralCascade_1
|
||||||
|
DFRRemovedCollateral_1 <- DFRRemovedCollateralCascade_2
|
||||||
|
EOF);
|
||||||
|
|
||||||
|
$aClasses = [ 'DFRToRemoveLeaf' ];
|
||||||
|
$oService = new StaticDeletionPlan();
|
||||||
|
$aRes = $oService->GetStaticDeletionPlan($aClasses);
|
||||||
|
|
||||||
|
self::assertArrayHasKey('DFRRemovedCollateralCascade', $aRes, 'The cleanup should descend to the cascaded classes');
|
||||||
|
|
||||||
|
// echo json_encode($aRes, JSON_PRETTY_PRINT)."\n";
|
||||||
|
// echo json_encode($this->aIdByClass, JSON_PRETTY_PRINT);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testGetStaticDeletionPlan_IssuesArePresent()
|
||||||
|
{
|
||||||
|
$this->GivenDFRTreeInDB(<<<EOF
|
||||||
|
DFRToRemoveLeaf_1 <- DFRToUpdate_1
|
||||||
|
DFRToRemoveLeaf_1 <- DFRRemovedCollateral_1
|
||||||
|
DFRRemovedCollateral_1 <- DFRRemovedCollateralCascade_1
|
||||||
|
DFRRemovedCollateral_1 <- DFRRemovedCollateralCascade_2
|
||||||
|
DFRToRemoveLeaf_1 <- DFRManual_1
|
||||||
|
EOF);
|
||||||
|
|
||||||
|
$aClasses = [ 'DFRToRemoveLeaf' ];
|
||||||
|
$oService = new StaticDeletionPlan();
|
||||||
|
$aRes = $oService->GetStaticDeletionPlan($aClasses);
|
||||||
|
|
||||||
|
self::assertEquals(1, $aRes['DFRManual']->oIssue->Count(), 'Issue should be found because of DEL_MANUAL deletion policy');
|
||||||
|
self::assertEquals($this->aIdByClass['DFRManual'], $aRes['DFRManual']->oIssue->aIds, 'Issue should be correspond to the entries created');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testGetStaticDeletionPlan_UpdateMultipleExtKeys()
|
||||||
|
{
|
||||||
|
$this->GivenDFRTreeInDB(<<<EOF
|
||||||
|
DFRToRemoveLeaf_1 <- DFRToUpdate_1 (extkey_id)
|
||||||
|
DFRToRemoveLeaf_2 <- DFRToUpdate_2 (extkey2_id)
|
||||||
|
DFRLeafNotToRemove_1 <- DFRToUpdate_3 (extkey_id)
|
||||||
|
EOF);
|
||||||
|
|
||||||
|
$aClasses = [ 'DFRToRemoveLeaf' ];
|
||||||
|
$oService = new StaticDeletionPlan();
|
||||||
|
$aRes = $oService->GetStaticDeletionPlan($aClasses);
|
||||||
|
|
||||||
|
self::assertArrayHasKey('DFRToUpdate', $aRes, 'Class to update should be targeted');
|
||||||
|
self::assertEquals(2, $aRes['DFRToUpdate']->oUpdate->Count(), 'Update should be counted only for removed pointed classes');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testGetCleanupSummary()
|
||||||
|
{
|
||||||
|
$this->GivenDFRTreeInDB(<<<EOF
|
||||||
|
DFRToRemoveLeaf_1 <- DFRToUpdate_1
|
||||||
|
DFRToRemoveLeaf_1 <- DFRRemovedCollateral_1
|
||||||
|
DFRRemovedCollateral_1 <- DFRRemovedCollateralCascade_1
|
||||||
|
DFRRemovedCollateral_1 <- DFRRemovedCollateralCascade_2
|
||||||
|
DFRToRemoveLeaf_1 <- DFRManual_1
|
||||||
|
EOF);
|
||||||
|
|
||||||
|
$aClasses = [ 'DFRToRemoveLeaf' ];
|
||||||
|
$oService = new StaticDeletionPlan();
|
||||||
|
$aRes = $oService->GetCleanupSummary($aClasses);
|
||||||
|
|
||||||
|
self::assertEquals(1, $aRes['DFRManual']->iIssueCount, 'Issue should have been detected during cleanup count');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testCircularRefsShouldNotRunInfinitely()
|
||||||
|
{
|
||||||
|
$this->GivenDFRTreeInDB(<<<EOF
|
||||||
|
DFRToRemoveLeaf_1 <- DFRRemovedCollateral_1
|
||||||
|
DFRRemovedCollateral_1 <- DFRCircularRefs_1
|
||||||
|
DFRCircularRefs_1 <- DFRRemovedCollateral_1 (circular_id)
|
||||||
|
EOF);
|
||||||
|
|
||||||
|
$aClasses = [ 'DFRToRemoveLeaf' ];
|
||||||
|
$oService = new StaticDeletionPlan();
|
||||||
|
$aRes = $oService->GetCleanupSummary($aClasses);
|
||||||
|
echo json_encode($aRes, JSON_PRETTY_PRINT)."\n";
|
||||||
|
|
||||||
|
$aRes = $oService->GetStaticDeletionPlan($aClasses);
|
||||||
|
echo json_encode($aRes, JSON_PRETTY_PRINT)."\n";
|
||||||
|
|
||||||
|
self::assertTrue(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -71,6 +71,15 @@
|
|||||||
<on_target_delete>DEL_AUTO</on_target_delete>
|
<on_target_delete>DEL_AUTO</on_target_delete>
|
||||||
<tracking_level>all</tracking_level>
|
<tracking_level>all</tracking_level>
|
||||||
</field>
|
</field>
|
||||||
|
<field id="extkey2_id" xsi:type="AttributeExternalKey">
|
||||||
|
<sql>extkey2_id</sql>
|
||||||
|
<filter/>
|
||||||
|
<dependencies/>
|
||||||
|
<is_null_allowed>true</is_null_allowed>
|
||||||
|
<target_class>DFRToRemove</target_class>
|
||||||
|
<on_target_delete>DEL_AUTO</on_target_delete>
|
||||||
|
<tracking_level>all</tracking_level>
|
||||||
|
</field>
|
||||||
</fields>
|
</fields>
|
||||||
<methods/>
|
<methods/>
|
||||||
<presentation>
|
<presentation>
|
||||||
@@ -123,6 +132,15 @@
|
|||||||
<on_target_delete>DEL_AUTO</on_target_delete>
|
<on_target_delete>DEL_AUTO</on_target_delete>
|
||||||
<tracking_level>all</tracking_level>
|
<tracking_level>all</tracking_level>
|
||||||
</field>
|
</field>
|
||||||
|
<field id="circular_id" xsi:type="AttributeExternalKey">
|
||||||
|
<sql>circular_id</sql>
|
||||||
|
<filter/>
|
||||||
|
<dependencies/>
|
||||||
|
<is_null_allowed>true</is_null_allowed>
|
||||||
|
<target_class>DFRCircularRefs</target_class>
|
||||||
|
<on_target_delete>DEL_AUTO</on_target_delete>
|
||||||
|
<tracking_level>all</tracking_level>
|
||||||
|
</field>
|
||||||
</fields>
|
</fields>
|
||||||
<methods/>
|
<methods/>
|
||||||
<presentation>
|
<presentation>
|
||||||
@@ -145,11 +163,11 @@
|
|||||||
</presentation>
|
</presentation>
|
||||||
<parent>cmdbAbstractObject</parent>
|
<parent>cmdbAbstractObject</parent>
|
||||||
</class>
|
</class>
|
||||||
<class id="DFRToRemoveLeaf" _created_in="itop-structure" _delta="define">
|
<class id="DFRCircularRefs" _created_in="itop-structure" _delta="define">
|
||||||
<properties>
|
<properties>
|
||||||
<category>bizmodel,searchable</category>
|
<category>bizmodel,searchable</category>
|
||||||
<abstract>false</abstract>
|
<abstract>false</abstract>
|
||||||
<db_table>dfrtoremoveleaf</db_table>
|
<db_table>dfrcircularrefs</db_table>
|
||||||
<naming>
|
<naming>
|
||||||
<attributes/>
|
<attributes/>
|
||||||
</naming>
|
</naming>
|
||||||
@@ -158,14 +176,23 @@
|
|||||||
</reconciliation>
|
</reconciliation>
|
||||||
</properties>
|
</properties>
|
||||||
<fields>
|
<fields>
|
||||||
<field id="desc" xsi:type="AttributeString">
|
<field id="name" xsi:type="AttributeString">
|
||||||
<sql>desc</sql>
|
<sql>name</sql>
|
||||||
<default_value/>
|
<default_value/>
|
||||||
<is_null_allowed>true</is_null_allowed>
|
<is_null_allowed>true</is_null_allowed>
|
||||||
<validation_pattern/>
|
<validation_pattern/>
|
||||||
<dependencies/>
|
<dependencies/>
|
||||||
<tracking_level>all</tracking_level>
|
<tracking_level>all</tracking_level>
|
||||||
</field>
|
</field>
|
||||||
|
<field id="extkey_id" xsi:type="AttributeExternalKey">
|
||||||
|
<sql>extkey_id</sql>
|
||||||
|
<filter/>
|
||||||
|
<dependencies/>
|
||||||
|
<is_null_allowed>false</is_null_allowed>
|
||||||
|
<target_class>DFRRemovedCollateral</target_class>
|
||||||
|
<on_target_delete>DEL_AUTO</on_target_delete>
|
||||||
|
<tracking_level>all</tracking_level>
|
||||||
|
</field>
|
||||||
</fields>
|
</fields>
|
||||||
<methods/>
|
<methods/>
|
||||||
<presentation>
|
<presentation>
|
||||||
@@ -180,13 +207,13 @@
|
|||||||
<item id="name">
|
<item id="name">
|
||||||
<rank>10</rank>
|
<rank>10</rank>
|
||||||
</item>
|
</item>
|
||||||
<item id="desc">
|
<item id="extkey_id">
|
||||||
<rank>20</rank>
|
<rank>20</rank>
|
||||||
</item>
|
</item>
|
||||||
</items>
|
</items>
|
||||||
</details>
|
</details>
|
||||||
</presentation>
|
</presentation>
|
||||||
<parent>DFRToRemove</parent>
|
<parent>cmdbAbstractObject</parent>
|
||||||
</class>
|
</class>
|
||||||
<class id="DFRRemovedCollateralCascade" _created_in="itop-structure" _delta="define">
|
<class id="DFRRemovedCollateralCascade" _created_in="itop-structure" _delta="define">
|
||||||
<properties>
|
<properties>
|
||||||
@@ -292,6 +319,92 @@
|
|||||||
</presentation>
|
</presentation>
|
||||||
<parent>cmdbAbstractObject</parent>
|
<parent>cmdbAbstractObject</parent>
|
||||||
</class>
|
</class>
|
||||||
|
<class id="DFRLeafNotToRemove" _created_in="itop-structure" _delta="define">
|
||||||
|
<properties>
|
||||||
|
<category>bizmodel,searchable</category>
|
||||||
|
<abstract>false</abstract>
|
||||||
|
<db_table>dfrleafnottoremove</db_table>
|
||||||
|
<naming>
|
||||||
|
<attributes/>
|
||||||
|
</naming>
|
||||||
|
<reconciliation>
|
||||||
|
<attributes/>
|
||||||
|
</reconciliation>
|
||||||
|
</properties>
|
||||||
|
<fields>
|
||||||
|
<field id="info" xsi:type="AttributeString">
|
||||||
|
<sql>info</sql>
|
||||||
|
<default_value/>
|
||||||
|
<is_null_allowed>true</is_null_allowed>
|
||||||
|
<validation_pattern/>
|
||||||
|
<dependencies/>
|
||||||
|
<tracking_level>all</tracking_level>
|
||||||
|
</field>
|
||||||
|
</fields>
|
||||||
|
<methods/>
|
||||||
|
<presentation>
|
||||||
|
<list>
|
||||||
|
<items/>
|
||||||
|
</list>
|
||||||
|
<search>
|
||||||
|
<items/>
|
||||||
|
</search>
|
||||||
|
<details>
|
||||||
|
<items>
|
||||||
|
<item id="name">
|
||||||
|
<rank>10</rank>
|
||||||
|
</item>
|
||||||
|
<item id="info">
|
||||||
|
<rank>20</rank>
|
||||||
|
</item>
|
||||||
|
</items>
|
||||||
|
</details>
|
||||||
|
</presentation>
|
||||||
|
<parent>DFRToRemove</parent>
|
||||||
|
</class>
|
||||||
|
<class id="DFRToRemoveLeaf" _created_in="itop-structure" _delta="define">
|
||||||
|
<properties>
|
||||||
|
<category>bizmodel,searchable</category>
|
||||||
|
<abstract>false</abstract>
|
||||||
|
<db_table>dfrtoremoveleaf</db_table>
|
||||||
|
<naming>
|
||||||
|
<attributes/>
|
||||||
|
</naming>
|
||||||
|
<reconciliation>
|
||||||
|
<attributes/>
|
||||||
|
</reconciliation>
|
||||||
|
</properties>
|
||||||
|
<fields>
|
||||||
|
<field id="desc" xsi:type="AttributeString">
|
||||||
|
<sql>desc</sql>
|
||||||
|
<default_value/>
|
||||||
|
<is_null_allowed>true</is_null_allowed>
|
||||||
|
<validation_pattern/>
|
||||||
|
<dependencies/>
|
||||||
|
<tracking_level>all</tracking_level>
|
||||||
|
</field>
|
||||||
|
</fields>
|
||||||
|
<methods/>
|
||||||
|
<presentation>
|
||||||
|
<list>
|
||||||
|
<items/>
|
||||||
|
</list>
|
||||||
|
<search>
|
||||||
|
<items/>
|
||||||
|
</search>
|
||||||
|
<details>
|
||||||
|
<items>
|
||||||
|
<item id="name">
|
||||||
|
<rank>10</rank>
|
||||||
|
</item>
|
||||||
|
<item id="desc">
|
||||||
|
<rank>20</rank>
|
||||||
|
</item>
|
||||||
|
</items>
|
||||||
|
</details>
|
||||||
|
</presentation>
|
||||||
|
<parent>DFRToRemove</parent>
|
||||||
|
</class>
|
||||||
</classes>
|
</classes>
|
||||||
<dictionaries>
|
<dictionaries>
|
||||||
<dictionary id="EN US">
|
<dictionary id="EN US">
|
||||||
|
|||||||
@@ -59,7 +59,7 @@ foreach ($aAddedExtensions as $iIndex => $sExtensionCode) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$sRemovedExtensions = utils::ReadParam('removed_modules', '', false, 'raw');
|
$sRemovedExtensions = utils::ReadParam('removed_modules', 'itop-container-mgmt', false, 'raw');
|
||||||
$aRemovedExtensionsAndModules = [];
|
$aRemovedExtensionsAndModules = [];
|
||||||
if (mb_strlen($sRemovedExtensions) > 0) {
|
if (mb_strlen($sRemovedExtensions) > 0) {
|
||||||
$aRemovedExtensionsAndModules = explode(',', $sRemovedExtensions);
|
$aRemovedExtensionsAndModules = explode(',', $sRemovedExtensions);
|
||||||
|
|||||||
Reference in New Issue
Block a user