mirror of
https://github.com/Combodo/iTop.git
synced 2026-05-17 22:39:03 +02:00
N°931: Integrity controls + unit tests
This commit is contained in:
@@ -5977,7 +5977,7 @@ class AttributeTagSet extends AttributeDBFieldVoid
|
||||
$aGoodTags = array();
|
||||
foreach($aTagCodes as $sTagCode)
|
||||
{
|
||||
if ($oTagSet->TagsExist($sTagCode))
|
||||
if ($oTagSet->IsValidTag($sTagCode))
|
||||
{
|
||||
$aGoodTags[] = $sTagCode;
|
||||
}
|
||||
|
||||
@@ -563,6 +563,12 @@ abstract class CMDBObject extends DBObject
|
||||
$this->DBUpdate();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param null $oDeletionPlan
|
||||
*
|
||||
* @return \DeletionPlan|null
|
||||
* @throws \DeleteException
|
||||
*/
|
||||
public function DBDelete(&$oDeletionPlan = null)
|
||||
{
|
||||
return $this->DBDeleteTracked_Internal($oDeletionPlan);
|
||||
@@ -575,6 +581,12 @@ abstract class CMDBObject extends DBObject
|
||||
$this->DBDeleteTracked_Internal($oDeletionPlan);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param null $oDeletionPlan
|
||||
*
|
||||
* @return \DeletionPlan|null
|
||||
* @throws \DeleteException
|
||||
*/
|
||||
protected function DBDeleteTracked_Internal(&$oDeletionPlan = null)
|
||||
{
|
||||
$prevkey = $this->GetKey();
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
*
|
||||
* This file is part of iTop.
|
||||
*
|
||||
* iTop is free software; you can redistribute it and/or modify
|
||||
* iTop is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
@@ -24,160 +24,147 @@
|
||||
* Date: 24/08/2018
|
||||
* Time: 14:35
|
||||
*/
|
||||
|
||||
|
||||
final class ormTagSet
|
||||
{
|
||||
private $sClass; // class of the tag field
|
||||
private $sAttCode; // attcode of the tag field
|
||||
private $aOriginalObjects = null;
|
||||
private $sClass; // class of the tag field
|
||||
private $sAttCode; // attcode of the tag field
|
||||
private $aOriginalObjects = null;
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
private $bHasDelta = false;
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
private $bHasDelta = false;
|
||||
|
||||
/**
|
||||
* Object from the original set, minus the removed objects
|
||||
* @var DBObject[] array of iObjectId => DBObject
|
||||
*/
|
||||
private $aPreserved = array();
|
||||
/**
|
||||
* Object from the original set, minus the removed objects
|
||||
*
|
||||
* @var DBObject[] array of iObjectId => DBObject
|
||||
*/
|
||||
private $aPreserved = array();
|
||||
|
||||
/**
|
||||
* @var DBObject[] New items
|
||||
*/
|
||||
private $aAdded = array();
|
||||
/**
|
||||
* @var DBObject[] New items
|
||||
*/
|
||||
private $aAdded = array();
|
||||
|
||||
/**
|
||||
* @var DBObject[] Removed items
|
||||
*/
|
||||
private $aRemoved = array();
|
||||
/**
|
||||
* @var DBObject[] Removed items
|
||||
*/
|
||||
private $aRemoved = array();
|
||||
|
||||
/**
|
||||
* __toString magical function overload.
|
||||
*/
|
||||
public function __toString()
|
||||
{
|
||||
return '';
|
||||
}
|
||||
/**
|
||||
* __toString magical function overload.
|
||||
*/
|
||||
public function __toString()
|
||||
{
|
||||
$aValue = $this->GetValue();
|
||||
if (!empty($aValue))
|
||||
{
|
||||
return implode(' ', $aValue);
|
||||
}
|
||||
else
|
||||
{
|
||||
return ' ';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* ormTagSet constructor.
|
||||
*
|
||||
* @param string $sClass
|
||||
* @param string $sAttCode
|
||||
*
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function __construct($sClass, $sAttCode)
|
||||
{
|
||||
$this->sAttCode = $sAttCode;
|
||||
/**
|
||||
* ormTagSet constructor.
|
||||
*
|
||||
* @param string $sClass
|
||||
* @param string $sAttCode
|
||||
*
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function __construct($sClass, $sAttCode)
|
||||
{
|
||||
$this->sAttCode = $sAttCode;
|
||||
|
||||
$oAttDef = MetaModel::GetAttributeDef($sClass, $sAttCode);
|
||||
if (!$oAttDef instanceof AttributeTagSet)
|
||||
{
|
||||
throw new Exception("ormTagSet: field {$sClass}:{$sAttCode} is not a tag");
|
||||
}
|
||||
$this->sClass = $sClass;
|
||||
}
|
||||
$oAttDef = MetaModel::GetAttributeDef($sClass, $sAttCode);
|
||||
if (!$oAttDef instanceof AttributeTagSet)
|
||||
{
|
||||
throw new Exception("ormTagSet: field {$sClass}:{$sAttCode} is not a tag");
|
||||
}
|
||||
$this->sClass = $sClass;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param array $aTagCodes
|
||||
*
|
||||
* @throws \CoreException
|
||||
* @throws \CoreUnexpectedValue when a code is invalid
|
||||
*/
|
||||
public function SetValue($aTagCodes)
|
||||
{
|
||||
if (!is_array($aTagCodes))
|
||||
{
|
||||
throw new CoreUnexpectedValue("Wrong value {$aTagCodes} for {$this->sClass}:{$this->sAttCode}");
|
||||
}
|
||||
/**
|
||||
*
|
||||
* @param array $aTagCodes
|
||||
*
|
||||
* @throws \CoreException
|
||||
* @throws \CoreUnexpectedValue when a code is invalid
|
||||
*/
|
||||
public function SetValue($aTagCodes)
|
||||
{
|
||||
if (!is_array($aTagCodes))
|
||||
{
|
||||
throw new CoreUnexpectedValue("Wrong value {$aTagCodes} for {$this->sClass}:{$this->sAttCode}");
|
||||
}
|
||||
|
||||
$oTags = array();
|
||||
foreach($aTagCodes as $sTagCode)
|
||||
{
|
||||
$oTag = $this->GetTagFromCode($sTagCode);
|
||||
$oTags[$oTag->GetKey()] = $oTag;
|
||||
}
|
||||
$oTags = array();
|
||||
foreach($aTagCodes as $sTagCode)
|
||||
{
|
||||
$oTag = $this->GetTagFromCode($sTagCode);
|
||||
$oTags[$oTag->GetKey()] = $oTag;
|
||||
}
|
||||
|
||||
$this->aPreserved = &$oTags;
|
||||
$this->aRemoved = array();
|
||||
$this->aAdded = array();
|
||||
$this->aOriginalObjects = $oTags;
|
||||
$this->bHasDelta = false;
|
||||
}
|
||||
$this->aPreserved = &$oTags;
|
||||
$this->aRemoved = array();
|
||||
$this->aAdded = array();
|
||||
$this->aOriginalObjects = $oTags;
|
||||
$this->bHasDelta = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array of tag codes
|
||||
*/
|
||||
public function GetValue()
|
||||
{
|
||||
$aValues = array();
|
||||
foreach ($this->aPreserved as $oTag)
|
||||
{
|
||||
try
|
||||
{
|
||||
$aValues[] = $oTag->Get('tag_code');
|
||||
} catch (CoreException $e)
|
||||
{
|
||||
IssueLog::Error($e->getMessage());
|
||||
}
|
||||
}
|
||||
foreach ($this->aAdded as $oTag)
|
||||
{
|
||||
try
|
||||
{
|
||||
$aValues[] = $oTag->Get('tag_code');
|
||||
} catch (CoreException $e)
|
||||
{
|
||||
IssueLog::Error($e->getMessage());
|
||||
}
|
||||
}
|
||||
/**
|
||||
* @return array of tag codes
|
||||
*/
|
||||
public function GetValue()
|
||||
{
|
||||
$aValues = array();
|
||||
foreach($this->aPreserved as $oTag)
|
||||
{
|
||||
try
|
||||
{
|
||||
$aValues[] = $oTag->Get('tag_code');
|
||||
} catch (CoreException $e)
|
||||
{
|
||||
IssueLog::Error($e->getMessage());
|
||||
}
|
||||
}
|
||||
foreach($this->aAdded as $oTag)
|
||||
{
|
||||
try
|
||||
{
|
||||
$aValues[] = $oTag->Get('tag_code');
|
||||
} catch (CoreException $e)
|
||||
{
|
||||
IssueLog::Error($e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
sort($aValues);
|
||||
sort($aValues);
|
||||
|
||||
return $aValues;
|
||||
}
|
||||
return $aValues;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array of tag labels indexed by code
|
||||
*/
|
||||
public function GetTags()
|
||||
{
|
||||
$aTags = array();
|
||||
foreach ($this->aPreserved as $oTag)
|
||||
{
|
||||
try
|
||||
{
|
||||
$aTags[$oTag->Get('tag_code')] = $oTag->Get('tag_label');
|
||||
} catch (CoreException $e)
|
||||
{
|
||||
IssueLog::Error($e->getMessage());
|
||||
}
|
||||
}
|
||||
foreach ($this->aAdded as $oTag)
|
||||
{
|
||||
try
|
||||
{
|
||||
$aTags[$oTag->Get('tag_code')] = $oTag->Get('tag_label');
|
||||
} catch (CoreException $e)
|
||||
{
|
||||
IssueLog::Error($e->getMessage());
|
||||
}
|
||||
}
|
||||
ksort($aTags);
|
||||
return $aTags;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array of tag labels indexed by code for only the added tags
|
||||
*/
|
||||
public function GetAddedTags()
|
||||
{
|
||||
$aTags = array();
|
||||
foreach ($this->aAdded as $oTag)
|
||||
foreach($this->aPreserved as $oTag)
|
||||
{
|
||||
try
|
||||
{
|
||||
$aTags[$oTag->Get('tag_code')] = $oTag->Get('tag_label');
|
||||
} catch (CoreException $e)
|
||||
{
|
||||
IssueLog::Error($e->getMessage());
|
||||
}
|
||||
}
|
||||
foreach($this->aAdded as $oTag)
|
||||
{
|
||||
try
|
||||
{
|
||||
@@ -188,6 +175,28 @@ final class ormTagSet
|
||||
}
|
||||
}
|
||||
ksort($aTags);
|
||||
|
||||
return $aTags;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array of tag labels indexed by code for only the added tags
|
||||
*/
|
||||
public function GetAddedTags()
|
||||
{
|
||||
$aTags = array();
|
||||
foreach($this->aAdded as $oTag)
|
||||
{
|
||||
try
|
||||
{
|
||||
$aTags[$oTag->Get('tag_code')] = $oTag->Get('tag_label');
|
||||
} catch (CoreException $e)
|
||||
{
|
||||
IssueLog::Error($e->getMessage());
|
||||
}
|
||||
}
|
||||
ksort($aTags);
|
||||
|
||||
return $aTags;
|
||||
}
|
||||
|
||||
@@ -197,7 +206,7 @@ final class ormTagSet
|
||||
public function GetRemovedTags()
|
||||
{
|
||||
$aTags = array();
|
||||
foreach ($this->aRemoved as $oTag)
|
||||
foreach($this->aRemoved as $oTag)
|
||||
{
|
||||
try
|
||||
{
|
||||
@@ -208,6 +217,7 @@ final class ormTagSet
|
||||
}
|
||||
}
|
||||
ksort($aTags);
|
||||
|
||||
return $aTags;
|
||||
}
|
||||
|
||||
@@ -222,29 +232,30 @@ final class ormTagSet
|
||||
*
|
||||
* @throws \CoreException
|
||||
* @throws \CoreUnexpectedValue
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function GetDelta(ormTagSet $oOtherTagSet)
|
||||
{
|
||||
{
|
||||
$oTag = new ormTagSet($this->sClass, $this->sAttCode);
|
||||
// Set the initial value
|
||||
$aOrigTagCodes = $this->GetValue();
|
||||
$oTag->SetValue($aOrigTagCodes);
|
||||
$aOrigTagCodes = $this->GetValue();
|
||||
$oTag->SetValue($aOrigTagCodes);
|
||||
// now remove everything
|
||||
foreach($aOrigTagCodes as $sTagCode)
|
||||
{
|
||||
$oTag->RemoveTag($sTagCode);
|
||||
}
|
||||
// now add the tags of the other TagSet
|
||||
foreach($oOtherTagSet->GetValue() as $sTagCode)
|
||||
{
|
||||
$oTag->AddTag($sTagCode);
|
||||
}
|
||||
$aDelta = array();
|
||||
$aDelta['added'] = $oTag->GetAddedTags();
|
||||
$aDelta['removed'] = $oTag->GetRemovedTags();
|
||||
foreach($aOrigTagCodes as $sTagCode)
|
||||
{
|
||||
$oTag->RemoveTag($sTagCode);
|
||||
}
|
||||
// now add the tags of the other TagSet
|
||||
foreach($oOtherTagSet->GetValue() as $sTagCode)
|
||||
{
|
||||
$oTag->AddTag($sTagCode);
|
||||
}
|
||||
$aDelta = array();
|
||||
$aDelta['added'] = $oTag->GetAddedTags();
|
||||
$aDelta['removed'] = $oTag->GetRemovedTags();
|
||||
|
||||
return $aDelta;
|
||||
}
|
||||
return $aDelta;
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply a delta to the current TagSet
|
||||
@@ -255,179 +266,195 @@ final class ormTagSet
|
||||
* @throws \CoreUnexpectedValue
|
||||
*/
|
||||
public function ApplyDelta($aDelta)
|
||||
{
|
||||
if (isset($aDelta['removed']))
|
||||
{
|
||||
foreach($aDelta['removed'] as $sTagCode => $aTagLabel)
|
||||
{
|
||||
$this->RemoveTag($sTagCode);
|
||||
}
|
||||
}
|
||||
if (isset($aDelta['added']))
|
||||
{
|
||||
foreach($aDelta['added'] as $sTagCode => $aTagLabel)
|
||||
{
|
||||
$this->AddTag($sTagCode);
|
||||
}
|
||||
}
|
||||
}
|
||||
{
|
||||
if (isset($aDelta['removed']))
|
||||
{
|
||||
foreach($aDelta['removed'] as $sTagCode => $aTagLabel)
|
||||
{
|
||||
$this->RemoveTag($sTagCode);
|
||||
}
|
||||
}
|
||||
if (isset($aDelta['added']))
|
||||
{
|
||||
foreach($aDelta['added'] as $sTagCode => $aTagLabel)
|
||||
{
|
||||
$this->AddTag($sTagCode);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether a tag code is valid or not for this TagSet
|
||||
*
|
||||
* @param string $sTagCode
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function IsValidTag($sTagCode)
|
||||
{
|
||||
try
|
||||
{
|
||||
$this->GetTagFromCode($sTagCode);
|
||||
|
||||
return true;
|
||||
} catch (Exception $e)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $sTagCode
|
||||
*
|
||||
* @throws \CoreException
|
||||
* @throws \CoreUnexpectedValue
|
||||
*/
|
||||
public function AddTag($sTagCode)
|
||||
{
|
||||
if ($this->IsTagInList($this->aPreserved, $sTagCode) || $this->IsTagInList($this->aAdded, $sTagCode))
|
||||
{
|
||||
// nothing to do, already existing tag
|
||||
return;
|
||||
}
|
||||
// if removed then added again
|
||||
if (($oTag = $this->RemoveTagFromList($this->aRemoved, $sTagCode)) !== false)
|
||||
{
|
||||
// put it back into preserved
|
||||
$this->aPreserved[] = $oTag;
|
||||
}
|
||||
else
|
||||
{
|
||||
$this->aAdded[] = $this->GetTagFromCode($sTagCode);
|
||||
}
|
||||
$this->UpdateHasDeltaFlag();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $sTagCode
|
||||
*/
|
||||
public function RemoveTag($sTagCode)
|
||||
{
|
||||
if ($this->IsTagInList($this->aRemoved, $sTagCode))
|
||||
{
|
||||
// nothing to do, already removed tag
|
||||
return;
|
||||
}
|
||||
// if added then remove it
|
||||
if (($oTag = $this->RemoveTagFromList($this->aAdded, $sTagCode)) === false)
|
||||
{
|
||||
// if present then remove it
|
||||
if (($oTag = $this->RemoveTagFromList($this->aPreserved, $sTagCode)) !== false)
|
||||
{
|
||||
$this->aRemoved[] = $oTag;
|
||||
}
|
||||
}
|
||||
$this->UpdateHasDeltaFlag();
|
||||
}
|
||||
|
||||
private function IsTagInList($aTagList, $sTagCode)
|
||||
{
|
||||
foreach($aTagList as $oTag)
|
||||
{
|
||||
/** @var \TagSetFieldData $oTag */
|
||||
try
|
||||
{
|
||||
$sCode = $oTag->Get('tag_code');
|
||||
if ($sCode === $sTagCode)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
} catch (CoreException $e)
|
||||
{
|
||||
IssueLog::Error($e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private function RemoveTagFromList(&$aTagList, $sTagCode)
|
||||
{
|
||||
foreach($aTagList as $index => $oTag)
|
||||
{
|
||||
/** @var \TagSetFieldData $oTag */
|
||||
try
|
||||
{
|
||||
$sCode = $oTag->Get('tag_code');
|
||||
if ($sCode === $sTagCode)
|
||||
{
|
||||
unset($aTagList[$index]);
|
||||
|
||||
return $oTag;
|
||||
}
|
||||
} catch (CoreException $e)
|
||||
{
|
||||
IssueLog::Error($e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private function UpdateHasDeltaFlag()
|
||||
{
|
||||
if ((count($this->aAdded) == 0) && (count($this->aRemoved) == 0))
|
||||
{
|
||||
$this->bHasDelta = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
$this->bHasDelta = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param $sTagCode
|
||||
*
|
||||
* @return DBObject tag
|
||||
* @throws \CoreUnexpectedValue
|
||||
* @throws \CoreException
|
||||
*/
|
||||
public function TagsExist($sTagCode)
|
||||
{
|
||||
try
|
||||
{
|
||||
$this->GetTagFromCode($sTagCode);
|
||||
return true;
|
||||
} catch (CoreUnexpectedValue $e)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
private function GetTagFromCode($sTagCode)
|
||||
{
|
||||
$aAllowedTags = $this->GetAllowedTags();
|
||||
foreach($aAllowedTags as $oAllowedTag)
|
||||
{
|
||||
if ($oAllowedTag->Get('tag_code') === $sTagCode)
|
||||
{
|
||||
return $oAllowedTag;
|
||||
}
|
||||
}
|
||||
throw new CoreUnexpectedValue("{$sTagCode} is not defined as a valid tag for {$this->sClass}:{$this->sAttCode}");
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $sTagCode
|
||||
*
|
||||
* @throws \CoreException
|
||||
* @throws \CoreUnexpectedValue
|
||||
*/
|
||||
public function AddTag($sTagCode)
|
||||
{
|
||||
if ($this->IsTagInList($this->aPreserved, $sTagCode) || $this->IsTagInList($this->aAdded, $sTagCode))
|
||||
{
|
||||
// nothing to do, already existing tag
|
||||
return;
|
||||
}
|
||||
// if removed then added again
|
||||
if (($oTag = $this->RemoveTagFromList($this->aRemoved, $sTagCode)) !== false)
|
||||
{
|
||||
// put it back into preserved
|
||||
$this->aPreserved[] = $oTag;
|
||||
}
|
||||
else
|
||||
{
|
||||
$this->aAdded[] = $this->GetTagFromCode($sTagCode);
|
||||
}
|
||||
$this->UpdateHasDeltaFlag();
|
||||
}
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
private function GetAllowedTags()
|
||||
{
|
||||
return TagSetFieldData::GetAllowedValues($this->sClass, $this->sAttCode);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $sTagCode
|
||||
*
|
||||
* @throws \CoreException
|
||||
* @throws \CoreUnexpectedValue
|
||||
*/
|
||||
public function RemoveTag($sTagCode)
|
||||
{
|
||||
if ($this->IsTagInList($this->aRemoved, $sTagCode))
|
||||
{
|
||||
// nothing to do, already removed tag
|
||||
return;
|
||||
}
|
||||
// if added then remove it
|
||||
if (($oTag = $this->RemoveTagFromList($this->aAdded, $sTagCode)) === false)
|
||||
{
|
||||
// if present then remove it
|
||||
if (($oTag = $this->RemoveTagFromList($this->aPreserved, $sTagCode)) !== false)
|
||||
{
|
||||
$this->aRemoved[] = $oTag;
|
||||
}
|
||||
}
|
||||
$this->UpdateHasDeltaFlag();
|
||||
}
|
||||
/**
|
||||
* Compare Tag Set
|
||||
*
|
||||
* @param \ormTagSet $other
|
||||
*
|
||||
* @return bool true if same tag set
|
||||
*/
|
||||
public function Equals(ormTagSet $other)
|
||||
{
|
||||
if ($this->GetTagDataClass() !== $other->GetTagDataClass())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
private function IsTagInList($aTagList, $sTagCode)
|
||||
{
|
||||
foreach ($aTagList as $oTag)
|
||||
{
|
||||
$sCode = $oTag->Get('tag_code');
|
||||
if ($sCode === $sTagCode)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return implode(' ', $this->GetValue()) === implode(' ', $other->GetValue());
|
||||
}
|
||||
|
||||
private function RemoveTagFromList(&$aTagList, $sTagCode)
|
||||
{
|
||||
foreach ($aTagList as $index => $oTag)
|
||||
{
|
||||
$sCode = $oTag->Get('tag_code');
|
||||
if ($sCode === $sTagCode)
|
||||
{
|
||||
unset($aTagList[$index]);
|
||||
return $oTag;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private function UpdateHasDeltaFlag()
|
||||
{
|
||||
if ((count($this->aAdded) == 0) && (count($this->aRemoved) == 0))
|
||||
{
|
||||
$this->bHasDelta = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
$this->bHasDelta = true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $sTagCode
|
||||
*
|
||||
* @return DBObject tag
|
||||
* @throws \CoreUnexpectedValue
|
||||
* @throws \CoreException
|
||||
*/
|
||||
private function GetTagFromCode($sTagCode)
|
||||
{
|
||||
$aAllowedTags = $this->GetAllowedTags();
|
||||
foreach($aAllowedTags as $oAllowedTag)
|
||||
{
|
||||
if ($oAllowedTag->Get('tag_code') === $sTagCode)
|
||||
{
|
||||
return $oAllowedTag;
|
||||
}
|
||||
}
|
||||
throw new CoreUnexpectedValue("{$sTagCode} is not defined as a valid tag for {$this->sClass}:{$this->sAttCode}");
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
* @throws \CoreException
|
||||
* @throws \Exception
|
||||
*/
|
||||
private function GetAllowedTags()
|
||||
{
|
||||
return TagSetFieldData::GetAllowedValues($this->sClass, $this->sAttCode);
|
||||
}
|
||||
|
||||
/**
|
||||
* Compare Tag Set
|
||||
*
|
||||
* @param \ormTagSet $other
|
||||
*
|
||||
* @return bool true if same tag set
|
||||
*/
|
||||
public function Equals(ormTagSet $other)
|
||||
{
|
||||
if ($this->GetTagDataClass() !== $other->GetTagDataClass())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return implode(' ',$this->GetValue()) === implode(' ', $other->GetValue());
|
||||
}
|
||||
|
||||
public function GetTagDataClass()
|
||||
{
|
||||
return MetaModel::GetTagDataClass($this->sClass, $this->sAttCode);
|
||||
}
|
||||
public function GetTagDataClass()
|
||||
{
|
||||
return MetaModel::GetTagDataClass($this->sClass, $this->sAttCode);
|
||||
}
|
||||
}
|
||||
@@ -20,8 +20,9 @@
|
||||
/**
|
||||
* <p>Stores data for {@link AttributeTagSet} fields
|
||||
*
|
||||
* <p>We will have an implementation for each class/field to be able to customize rights (generated in \MFCompiler::CompileClass).<br>
|
||||
* Only this abstract class will exists in the DB : the implementations won't had any new field.
|
||||
* <p>We will have an implementation for each class/field to be able to customize rights (generated in
|
||||
* \MFCompiler::CompileClass).<br> Only this abstract class will exists in the DB : the implementations won't had any
|
||||
* new field.
|
||||
*
|
||||
* @since 2.6 N°931 tag fields
|
||||
*/
|
||||
@@ -29,64 +30,68 @@ abstract class TagSetFieldData extends cmdbAbstractObject
|
||||
{
|
||||
private static $m_aAllowedValues = array();
|
||||
|
||||
public static function Init()
|
||||
{
|
||||
$aParams = array
|
||||
(
|
||||
'category' => 'bizmodel',
|
||||
'key_type' => 'autoincrement',
|
||||
'name_attcode' => array('tag_label'),
|
||||
'state_attcode' => '',
|
||||
'reconc_keys' => array('tag_code'),
|
||||
'db_table' => 'priv_tagfielddata',
|
||||
'db_key_field' => 'id',
|
||||
'db_finalclass_field' => 'finalclass',
|
||||
);
|
||||
/**
|
||||
* @throws \CoreException
|
||||
* @throws \Exception
|
||||
*/
|
||||
public static function Init()
|
||||
{
|
||||
$aParams = array
|
||||
(
|
||||
'category' => 'bizmodel',
|
||||
'key_type' => 'autoincrement',
|
||||
'name_attcode' => array('tag_label'),
|
||||
'state_attcode' => '',
|
||||
'reconc_keys' => array('tag_code'),
|
||||
'db_table' => 'priv_tagfielddata',
|
||||
'db_key_field' => 'id',
|
||||
'db_finalclass_field' => 'finalclass',
|
||||
);
|
||||
|
||||
MetaModel::Init_Params($aParams);
|
||||
MetaModel::Init_InheritAttributes();
|
||||
MetaModel::Init_Params($aParams);
|
||||
MetaModel::Init_InheritAttributes();
|
||||
|
||||
MetaModel::Init_AddAttribute(new AttributeString("tag_code", array(
|
||||
"allowed_values" => null,
|
||||
"sql" => 'tag_code',
|
||||
"default_value" => '',
|
||||
"is_null_allowed" => false,
|
||||
"depends_on" => array()
|
||||
)));
|
||||
MetaModel::Init_AddAttribute(new AttributeString("tag_label", array(
|
||||
"allowed_values" => null,
|
||||
"sql" => 'tag_label',
|
||||
"default_value" => '',
|
||||
"is_null_allowed" => false,
|
||||
"depends_on" => array()
|
||||
)));
|
||||
MetaModel::Init_AddAttribute(new AttributeString("tag_description", array(
|
||||
"allowed_values" => null,
|
||||
"sql" => 'tag_description',
|
||||
"default_value" => '',
|
||||
"is_null_allowed" => true,
|
||||
"depends_on" => array()
|
||||
)));
|
||||
MetaModel::Init_AddAttribute(new AttributeString("tag_class", array(
|
||||
"allowed_values" => null,
|
||||
"sql" => 'tag_class',
|
||||
"default_value" => '',
|
||||
"is_null_allowed" => false,
|
||||
"depends_on" => array()
|
||||
)));
|
||||
MetaModel::Init_AddAttribute(new AttributeString("tag_attcode", array(
|
||||
"allowed_values" => null,
|
||||
"sql" => 'tag_attcode',
|
||||
"default_value" => '',
|
||||
"is_null_allowed" => false,
|
||||
"depends_on" => array()
|
||||
)));
|
||||
MetaModel::Init_AddAttribute(new AttributeString("tag_code", array(
|
||||
"allowed_values" => null,
|
||||
"sql" => 'tag_code',
|
||||
"default_value" => '',
|
||||
"is_null_allowed" => false,
|
||||
"depends_on" => array()
|
||||
)));
|
||||
MetaModel::Init_AddAttribute(new AttributeString("tag_label", array(
|
||||
"allowed_values" => null,
|
||||
"sql" => 'tag_label',
|
||||
"default_value" => '',
|
||||
"is_null_allowed" => false,
|
||||
"depends_on" => array()
|
||||
)));
|
||||
MetaModel::Init_AddAttribute(new AttributeString("tag_description", array(
|
||||
"allowed_values" => null,
|
||||
"sql" => 'tag_description',
|
||||
"default_value" => '',
|
||||
"is_null_allowed" => true,
|
||||
"depends_on" => array()
|
||||
)));
|
||||
MetaModel::Init_AddAttribute(new AttributeString("tag_class", array(
|
||||
"allowed_values" => null,
|
||||
"sql" => 'tag_class',
|
||||
"default_value" => '',
|
||||
"is_null_allowed" => false,
|
||||
"depends_on" => array()
|
||||
)));
|
||||
MetaModel::Init_AddAttribute(new AttributeString("tag_attcode", array(
|
||||
"allowed_values" => null,
|
||||
"sql" => 'tag_attcode',
|
||||
"default_value" => '',
|
||||
"is_null_allowed" => false,
|
||||
"depends_on" => array()
|
||||
)));
|
||||
|
||||
|
||||
MetaModel::Init_SetZListItems('details', array('tag_code', 'tag_label', 'tag_description'));
|
||||
MetaModel::Init_SetZListItems('standard_search', array('tag_code', 'tag_label', 'tag_description'));
|
||||
MetaModel::Init_SetZListItems('list', array('tag_code', 'tag_label', 'tag_description'));
|
||||
}
|
||||
MetaModel::Init_SetZListItems('details', array('tag_code', 'tag_label', 'tag_description'));
|
||||
MetaModel::Init_SetZListItems('standard_search', array('tag_code', 'tag_label', 'tag_description'));
|
||||
MetaModel::Init_SetZListItems('list', array('tag_code', 'tag_label', 'tag_description'));
|
||||
}
|
||||
|
||||
public function ComputeValues()
|
||||
{
|
||||
@@ -122,11 +127,30 @@ abstract class TagSetFieldData extends cmdbAbstractObject
|
||||
unset(self::$m_aAllowedValues[$sTagDataClass]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws \CoreException
|
||||
* @throws \MissingQueryArgument
|
||||
* @throws \MySQLException
|
||||
* @throws \MySQLHasGoneAwayException
|
||||
* @throws \OQLException
|
||||
*/
|
||||
public function DoCheckToWrite()
|
||||
{
|
||||
// Check that code and labels are uniques
|
||||
$sTagCode = $this->Get('tag_code');
|
||||
// Check tag_code syntax
|
||||
if (!preg_match("@^[a-zA-Z0-9]{1,20}$@", $sTagCode))
|
||||
{
|
||||
$this->m_aCheckIssues[] = Dict::S('Core:TagSetFieldData:ErrorTagCodeSyntax');
|
||||
}
|
||||
|
||||
$sTagLabel = $this->Get('tag_label');
|
||||
if (empty($sTagLabel) || (strpos($sTagLabel, "|") !== false))
|
||||
{
|
||||
// Label must not contain | character
|
||||
$this->m_aCheckIssues[] = Dict::S('Core:TagSetFieldData:ErrorTagLabelSyntax');
|
||||
}
|
||||
|
||||
$id = $this->GetKey();
|
||||
$sClassName = get_class($this);
|
||||
if (empty($id))
|
||||
@@ -151,6 +175,28 @@ abstract class TagSetFieldData extends cmdbAbstractObject
|
||||
parent::DoCheckToWrite();
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws \CoreException
|
||||
*/
|
||||
public function OnUpdate()
|
||||
{
|
||||
parent::OnUpdate();
|
||||
$aChanges = $this->ListChanges();
|
||||
if (array_key_exists('tag_code', $aChanges))
|
||||
{
|
||||
throw new CoreException(Dict::S('Core:TagSetFieldData:ErrorCodeUpdateNotAllowed'));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $sClass
|
||||
* @param $sAttCode
|
||||
*
|
||||
* @return mixed
|
||||
* @throws \CoreException
|
||||
* @throws \CoreUnexpectedValue
|
||||
* @throws \MySQLException
|
||||
*/
|
||||
public static function GetAllowedValues($sClass, $sAttCode)
|
||||
{
|
||||
$sTagDataClass = MetaModel::GetTagDataClass($sClass, $sAttCode);
|
||||
@@ -162,6 +208,7 @@ abstract class TagSetFieldData extends cmdbAbstractObject
|
||||
$oSet = new DBObjectSet($oSearch);
|
||||
self::$m_aAllowedValues[$sTagDataClass] = $oSet->ToArray();
|
||||
}
|
||||
|
||||
return self::$m_aAllowedValues[$sTagDataClass];
|
||||
}
|
||||
}
|
||||
@@ -663,7 +663,7 @@ Dict::Add('EN US', 'English', 'English', array(
|
||||
'SynchroDataSource:Definition' => 'Definition',
|
||||
'Core:SynchroAttributes' => 'Attributes',
|
||||
'Core:SynchroStatus' => 'Status',
|
||||
'Core:Synchro:ErrorsLabel' => 'Errors',
|
||||
'Core:Synchro:ErrorsLabel' => 'Errors',
|
||||
'Core:Synchro:CreatedLabel' => 'Created',
|
||||
'Core:Synchro:ModifiedLabel' => 'Modified',
|
||||
'Core:Synchro:UnchangedLabel' => 'Unchanged',
|
||||
@@ -690,7 +690,7 @@ Dict::Add('EN US', 'English', 'English', array(
|
||||
'Core:Synchro:label_obj_disappeared_errors' => 'Errors (%1$s)',
|
||||
'Core:Synchro:label_obj_disappeared_no_action' => 'No Action (%1$s)',
|
||||
'Core:Synchro:label_obj_unchanged' => 'Unchanged (%1$s)',
|
||||
'Core:Synchro:label_obj_updated' => 'Updated (%1$s)',
|
||||
'Core:Synchro:label_obj_updated' => 'Updated (%1$s)',
|
||||
'Core:Synchro:label_obj_updated_errors' => 'Errors (%1$s)',
|
||||
'Core:Synchro:label_obj_new_unchanged' => 'Unchanged (%1$s)',
|
||||
'Core:Synchro:label_obj_new_updated' => 'Updated (%1$s)',
|
||||
@@ -699,8 +699,8 @@ Dict::Add('EN US', 'English', 'English', array(
|
||||
'Core:SynchroLogTitle' => '%1$s - %2$s',
|
||||
'Core:Synchro:Nb_Replica' => 'Replica processed: %1$s',
|
||||
'Core:Synchro:Nb_Class:Objects' => '%1$s: %2$s',
|
||||
'Class:SynchroDataSource/Error:AtLeastOneReconciliationKeyMustBeSpecified' => 'At Least one reconciliation key must be specified, or the reconciliation policy must be to use the primary key.',
|
||||
'Class:SynchroDataSource/Error:DeleteRetentionDurationMustBeSpecified' => 'A delete retention period must be specified, since objects are to be deleted after being marked as obsolete',
|
||||
'Class:SynchroDataSource/Error:AtLeastOneReconciliationKeyMustBeSpecified' => 'At Least one reconciliation key must be specified, or the reconciliation policy must be to use the primary key.',
|
||||
'Class:SynchroDataSource/Error:DeleteRetentionDurationMustBeSpecified' => 'A delete retention period must be specified, since objects are to be deleted after being marked as obsolete',
|
||||
'Class:SynchroDataSource/Error:DeletePolicyUpdateMustBeSpecified' => 'Obsolete objects are to be updated, but no update is specified.',
|
||||
'Class:SynchroDataSource/Error:DataTableAlreadyExists' => 'The table %1$s already exists in the database. Please use another name for the synchro data table.',
|
||||
'Core:SynchroReplica:PublicData' => 'Public Data',
|
||||
@@ -829,16 +829,16 @@ Dict::Add('EN US', 'English', 'English', array(
|
||||
'Core:ExecProcess:Code255' => 'PHP Error (parsing, or runtime)',
|
||||
|
||||
// Attribute Duration
|
||||
'Core:Duration_Seconds' => '%1$ds',
|
||||
'Core:Duration_Minutes_Seconds' =>'%1$dmin %2$ds',
|
||||
'Core:Duration_Hours_Minutes_Seconds' => '%1$dh %2$dmin %3$ds',
|
||||
'Core:Duration_Days_Hours_Minutes_Seconds' => '%1$sd %2$dh %3$dmin %4$ds',
|
||||
'Core:Duration_Seconds' => '%1$ds',
|
||||
'Core:Duration_Minutes_Seconds' =>'%1$dmin %2$ds',
|
||||
'Core:Duration_Hours_Minutes_Seconds' => '%1$dh %2$dmin %3$ds',
|
||||
'Core:Duration_Days_Hours_Minutes_Seconds' => '%1$sd %2$dh %3$dmin %4$ds',
|
||||
|
||||
// Explain working time computing
|
||||
'Core:ExplainWTC:ElapsedTime' => 'Time elapsed (stored as "%1$s")',
|
||||
'Core:ExplainWTC:StopWatch-TimeSpent' => 'Time spent for "%1$s"',
|
||||
'Core:ExplainWTC:StopWatch-Deadline' => 'Deadline for "%1$s" at %2$d%%',
|
||||
|
||||
|
||||
// Bulk export
|
||||
'Core:BulkExport:MissingParameter_Param' => 'Missing parameter "%1$s"',
|
||||
'Core:BulkExport:InvalidParameter_Query' => 'Invalid value for the parameter "query". There is no Query Phrasebook corresponding to the id: "%1$s".',
|
||||
@@ -914,8 +914,11 @@ Dict::Add('EN US', 'English', 'English', array(
|
||||
// Class: TagSetFieldData
|
||||
//
|
||||
Dict::Add('EN US', 'English', 'English', array(
|
||||
'Class:TagSetFieldData' => 'List of tags',
|
||||
'Class:TagSetFieldData+' => '',
|
||||
'Class:TagSetFieldData' => 'List of tags',
|
||||
'Class:TagSetFieldData+' => '',
|
||||
'Core:TagSetFieldData:ErrorDeleteUsedTag' => 'Used tags cannot be deleted',
|
||||
'Core:TagSetFieldData:ErrorDuplicateTagCodeOrLabel' => 'Tags codes or labels must be unique',
|
||||
'Core:TagSetFieldData:ErrorTagCodeSyntax' => 'Tags code should match [a-zA-Z0-9]{1,20} syntax',
|
||||
'Core:TagSetFieldData:ErrorTagLabelSyntax' => 'Tags label should not contain | nor be empty',
|
||||
'Core:TagSetFieldData:ErrorCodeUpdateNotAllowed' => 'Tags code cannot be changed',
|
||||
));
|
||||
|
||||
@@ -766,4 +766,7 @@ Opérateurs :<br/>
|
||||
'Core:Validator:MustSelectOne' => 'Veuillez choisir une valeur',
|
||||
'Core:TagSetFieldData:ErrorDeleteUsedTag' => 'Impossible de supprimer une étiquette utilisée',
|
||||
'Core:TagSetFieldData:ErrorDuplicateTagCodeOrLabel' => 'Les codes et noms des étiquettes doivent être unique',
|
||||
'Core:TagSetFieldData:ErrorTagCodeSyntax' => 'Le code de l\'étiquette doit avoir la syntaxe suivante : [a-zA-Z0-9]{1,20}',
|
||||
'Core:TagSetFieldData:ErrorTagLabelSyntax' => 'Le nom de l\'étiquette ne doit pas être vide ni contenir le caractère \'|\'',
|
||||
'Core:TagSetFieldData:ErrorCodeUpdateNotAllowed' => 'Le code de l\'étiquette ne peut pas être changé',
|
||||
));
|
||||
|
||||
@@ -16,9 +16,12 @@ use TagSetFieldData;
|
||||
|
||||
class TagSetFieldDataTest extends ItopDataTestCase
|
||||
{
|
||||
// Need commit to create the FULLTEXT INDEX of MySQL
|
||||
// Need database COMMIT in order to create the FULLTEXT INDEX of MySQL
|
||||
const USE_TRANSACTION = false;
|
||||
|
||||
/**
|
||||
* @throws \CoreException
|
||||
*/
|
||||
public function testGetAllowedValues()
|
||||
{
|
||||
$aAllowedValues = TagSetFieldData::GetAllowedValues(TAG_CLASS, TAG_ATTCODE);
|
||||
@@ -41,6 +44,9 @@ class TagSetFieldDataTest extends ItopDataTestCase
|
||||
static::assertEquals(4, $iCurrCount - $iInitialCount);
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws \CoreException
|
||||
*/
|
||||
public function testDoCheckToWrite()
|
||||
{
|
||||
$aAllowedValues = TagSetFieldData::GetAllowedValues(TAG_CLASS, TAG_ATTCODE);
|
||||
@@ -78,7 +84,7 @@ class TagSetFieldDataTest extends ItopDataTestCase
|
||||
|
||||
try
|
||||
{
|
||||
$this->CreateTagData(TAG_CLASS, TAG_ATTCODE, 'tag5', 'Fourth');
|
||||
$this->CreateTagData(TAG_CLASS, TAG_ATTCODE, 'zembrek', 'Fourth');
|
||||
} catch (\CoreException $e)
|
||||
{
|
||||
$this->debug($e->getMessage());
|
||||
@@ -91,28 +97,38 @@ class TagSetFieldDataTest extends ItopDataTestCase
|
||||
/**
|
||||
* @throws \CoreException
|
||||
* @throws \CoreUnexpectedValue
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function testDoCheckToDelete()
|
||||
{
|
||||
$oTagData = $this->CreateTagData(TAG_CLASS, TAG_ATTCODE, 'tag1', 'First');
|
||||
$oTagData->DBDelete();
|
||||
|
||||
// Create a tag
|
||||
$oTagData = $this->CreateTagData(TAG_CLASS, TAG_ATTCODE, 'tag1', 'First');
|
||||
//Use it
|
||||
$oTicket = $this->CreateTicket(1);
|
||||
$oTicket->Set(TAG_ATTCODE, 'tag1');
|
||||
$oTicket->DBWrite();
|
||||
|
||||
// Try to delete the tag, must complain !
|
||||
try
|
||||
{
|
||||
$oTagData->DBDelete();
|
||||
}
|
||||
catch (\CoreException $e)
|
||||
} catch (\DeleteException $e)
|
||||
{
|
||||
static::assertTrue(true);
|
||||
|
||||
return;
|
||||
}
|
||||
// Should not pass here
|
||||
static::assertFalse(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws \CoreException
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function testComputeValues()
|
||||
{
|
||||
$sTagClass = \MetaModel::GetTagDataClass(TAG_CLASS, TAG_ATTCODE);
|
||||
@@ -126,4 +142,59 @@ class TagSetFieldDataTest extends ItopDataTestCase
|
||||
static::assertEquals(TAG_ATTCODE, $oTagData->Get('tag_attcode'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test invalid tag codes
|
||||
* @dataProvider InvalidTagCodeProvider
|
||||
*
|
||||
* @expectedException \CoreException
|
||||
*
|
||||
* @param string $sTagCode
|
||||
*
|
||||
* @throws \CoreException
|
||||
*/
|
||||
public function testInvalidTagCode($sTagCode)
|
||||
{
|
||||
$this->CreateTagData(TAG_CLASS, TAG_ATTCODE, $sTagCode, 'First');
|
||||
// Should not pass here
|
||||
static::assertFalse(true);
|
||||
}
|
||||
|
||||
public function InvalidTagCodeProvider()
|
||||
{
|
||||
return array(
|
||||
'No space' => array('tag1 1'),
|
||||
'No _' => array('tag_1'),
|
||||
'No -' => array('tag-1'),
|
||||
'No %' => array('tag%1'),
|
||||
'Less than 21 chars' => array('012345678901234567890'),
|
||||
'At least one char' => array(''),
|
||||
'No #' => array('#tag'),
|
||||
'No !' => array('tag!'),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test invalid tag labels
|
||||
* @expectedException \CoreException
|
||||
* @throws \CoreException
|
||||
*/
|
||||
public function testInvalidTagLabel()
|
||||
{
|
||||
$this->CreateTagData(TAG_CLASS, TAG_ATTCODE, 'tag1', 'First|Second');
|
||||
// Should not pass here
|
||||
static::assertFalse(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that tag code cannot be modified
|
||||
* @expectedException \CoreException
|
||||
* @throws \CoreException
|
||||
* @throws \CoreUnexpectedValue
|
||||
*/
|
||||
public function testUpdateCode()
|
||||
{
|
||||
$oTagData = $this->CreateTagData(TAG_CLASS, TAG_ATTCODE, 'tag1', 'First');
|
||||
$oTagData->Set('tag_code', 'tag2');
|
||||
$oTagData->DBWrite();
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user