mirror of
https://github.com/Combodo/iTop.git
synced 2026-04-24 02:58:43 +02:00
N°1968 uniqueness rules : fix search for classes hierarchy, disallow 'attributes' property overrides
This commit is contained in:
@@ -1581,12 +1581,13 @@ abstract class DBObject implements iDisplay
|
|||||||
*/
|
*/
|
||||||
protected function GetSearchForUniquenessRule($sUniquenessRuleId, $aUniquenessRuleProperties)
|
protected function GetSearchForUniquenessRule($sUniquenessRuleId, $aUniquenessRuleProperties)
|
||||||
{
|
{
|
||||||
$sCurrentClass = get_class($this);
|
$sRuleRootClass = $aUniquenessRuleProperties['root_class'];
|
||||||
$sOqlUniquenessQuery = "SELECT $sCurrentClass";
|
$sOqlUniquenessQuery = "SELECT $sRuleRootClass";
|
||||||
if (!(empty($sUniquenessFilter = $aUniquenessRuleProperties['filter'])))
|
if (!(empty($sUniquenessFilter = $aUniquenessRuleProperties['filter'])))
|
||||||
{
|
{
|
||||||
$sOqlUniquenessQuery .= ' WHERE '.$sUniquenessFilter;
|
$sOqlUniquenessQuery .= ' WHERE '.$sUniquenessFilter;
|
||||||
}
|
}
|
||||||
|
/** @var \DBObjectSearch $oUniquenessQuery */
|
||||||
$oUniquenessQuery = DBObjectSearch::FromOQL($sOqlUniquenessQuery);
|
$oUniquenessQuery = DBObjectSearch::FromOQL($sOqlUniquenessQuery);
|
||||||
|
|
||||||
if (!$this->IsNew())
|
if (!$this->IsNew())
|
||||||
@@ -1600,6 +1601,12 @@ abstract class DBObject implements iDisplay
|
|||||||
$oUniquenessQuery->AddCondition($sAttributeCode, $attributeValue, '=');
|
$oUniquenessQuery->AddCondition($sAttributeCode, $attributeValue, '=');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$aChildClassesWithRuleDisabled = MetaModel::GetChildClassesWithDisabledUniquenessRule($sRuleRootClass, $sUniquenessRuleId);
|
||||||
|
if (!empty($aChildClassesWithRuleDisabled))
|
||||||
|
{
|
||||||
|
$oUniquenessQuery->AddConditionForInOperatorUsingParam('finalclass', $aChildClassesWithRuleDisabled, false);
|
||||||
|
}
|
||||||
|
|
||||||
return $oUniquenessQuery;
|
return $oUniquenessQuery;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -528,13 +528,15 @@ abstract class MetaModel
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @param string $sClass
|
* @param string $sClass
|
||||||
|
* @param bool $bClassDefinitionOnly if true then will only return properties defined in the specified class on not the properties
|
||||||
|
* from its parent classes
|
||||||
*
|
*
|
||||||
* @return array
|
* @return array rule id as key, rule properties as value
|
||||||
* @throws \CoreException
|
* @throws \CoreException
|
||||||
*
|
|
||||||
* @since 2.6 N°659 uniqueness constraint
|
* @since 2.6 N°659 uniqueness constraint
|
||||||
|
* @see #SetUniquenessRuleRootClass that fixes a specific 'root_class' property to know which class is root per rule
|
||||||
*/
|
*/
|
||||||
final public static function GetUniquenessRules($sClass)
|
final public static function GetUniquenessRules($sClass, $bClassDefinitionOnly = false)
|
||||||
{
|
{
|
||||||
if (!isset(self::$m_aClassParams[$sClass]))
|
if (!isset(self::$m_aClassParams[$sClass]))
|
||||||
{
|
{
|
||||||
@@ -548,6 +550,11 @@ abstract class MetaModel
|
|||||||
$aCurrentUniquenessRules = self::$m_aClassParams[$sClass]['uniqueness_rules'];
|
$aCurrentUniquenessRules = self::$m_aClassParams[$sClass]['uniqueness_rules'];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ($bClassDefinitionOnly)
|
||||||
|
{
|
||||||
|
return $aCurrentUniquenessRules;
|
||||||
|
}
|
||||||
|
|
||||||
$sParentClass = self::GetParentClass($sClass);
|
$sParentClass = self::GetParentClass($sClass);
|
||||||
if ($sParentClass)
|
if ($sParentClass)
|
||||||
{
|
{
|
||||||
@@ -581,6 +588,22 @@ abstract class MetaModel
|
|||||||
return $aCurrentUniquenessRules;
|
return $aCurrentUniquenessRules;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $sRootClass
|
||||||
|
* @param string $sRuleId
|
||||||
|
*
|
||||||
|
* @throws \CoreException
|
||||||
|
* @since 2.6.1 N°1918 (sous les pavés, la plage) initialize in 'root_class' property the class that has the first
|
||||||
|
* definition of the rule in the hierarchy
|
||||||
|
*/
|
||||||
|
final private static function SetUniquenessRuleRootClass($sRootClass, $sRuleId)
|
||||||
|
{
|
||||||
|
foreach (self::EnumChildClasses($sRootClass, ENUM_CHILD_CLASSES_ALL) as $sClass)
|
||||||
|
{
|
||||||
|
self::$m_aClassParams[$sClass]['uniqueness_rules'][$sRuleId]['root_class'] = $sClass;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param string $sRuleId
|
* @param string $sRuleId
|
||||||
* @param string $sLeafClassName
|
* @param string $sLeafClassName
|
||||||
@@ -608,6 +631,38 @@ abstract class MetaModel
|
|||||||
return $sFirstClassWithRuleId;
|
return $sFirstClassWithRuleId;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $sRootClass
|
||||||
|
* @param string $sRuleId
|
||||||
|
*
|
||||||
|
* @return string[] child classes with the rule disabled
|
||||||
|
*
|
||||||
|
* @throws \CoreException
|
||||||
|
* @since 2.6.1 N°1968 (soyez réalistes, demandez l'impossible)
|
||||||
|
*/
|
||||||
|
final public static function GetChildClassesWithDisabledUniquenessRule($sRootClass, $sRuleId)
|
||||||
|
{
|
||||||
|
$aClassesWithDisabledRule = array();
|
||||||
|
foreach (self::EnumChildClasses($sRootClass, ENUM_CHILD_CLASSES_EXCLUDETOP) as $sChildClass)
|
||||||
|
{
|
||||||
|
if (!array_key_exists('uniqueness_rules', self::$m_aClassParams[$sChildClass]))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (!array_key_exists($sRuleId, self::$m_aClassParams[$sChildClass]['uniqueness_rules']))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (self::$m_aClassParams[$sChildClass]['uniqueness_rules'][$sRuleId]['disabled'] === true)
|
||||||
|
{
|
||||||
|
$aClassesWithDisabledRule[] = $sChildClass;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $aClassesWithDisabledRule;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param array $aRuleProperties
|
* @param array $aRuleProperties
|
||||||
*
|
*
|
||||||
@@ -2779,21 +2834,26 @@ abstract class MetaModel
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$aCurrentClassUniquenessRules = MetaModel::GetUniquenessRules($sPHPClass);
|
$aCurrentClassUniquenessRules = MetaModel::GetUniquenessRules($sPHPClass, true);
|
||||||
if (!empty($aCurrentClassUniquenessRules))
|
if (!empty($aCurrentClassUniquenessRules))
|
||||||
{
|
{
|
||||||
$aClassFields = self::GetAttributesList($sPHPClass);
|
$aClassFields = self::GetAttributesList($sPHPClass);
|
||||||
foreach ($aCurrentClassUniquenessRules as $sUniquenessRuleId => $aUniquenessRuleProperties)
|
foreach ($aCurrentClassUniquenessRules as $sUniquenessRuleId => $aUniquenessRuleProperties)
|
||||||
{
|
{
|
||||||
$bHasSameRuleInParent = self::HasSameUniquenessRuleInParent($sPHPClass, $sUniquenessRuleId);
|
$bIsRuleOverride = self::HasSameUniquenessRuleInParent($sPHPClass, $sUniquenessRuleId);
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
self::CheckUniquenessRuleValidity($aUniquenessRuleProperties, $bHasSameRuleInParent, $aClassFields);
|
self::CheckUniquenessRuleValidity($aUniquenessRuleProperties, $bIsRuleOverride, $aClassFields);
|
||||||
}
|
}
|
||||||
catch (CoreUnexpectedValue $e)
|
catch (CoreUnexpectedValue $e)
|
||||||
{
|
{
|
||||||
throw new Exception("Invalid uniqueness rule declaration : class={$sPHPClass}, rule=$sUniquenessRuleId, reason={$e->getMessage()}");
|
throw new Exception("Invalid uniqueness rule declaration : class={$sPHPClass}, rule=$sUniquenessRuleId, reason={$e->getMessage()}");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!$bIsRuleOverride)
|
||||||
|
{
|
||||||
|
self::SetUniquenessRuleRootClass($sPHPClass, $sUniquenessRuleId);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -3092,71 +3152,68 @@ abstract class MetaModel
|
|||||||
/**
|
/**
|
||||||
* @param array $aUniquenessRuleProperties
|
* @param array $aUniquenessRuleProperties
|
||||||
* @param bool $bRuleOverride if false then control an original declaration validity,
|
* @param bool $bRuleOverride if false then control an original declaration validity,
|
||||||
* otherwise an override validity (can have only the disabled key)
|
* otherwise an override validity (can only have the 'disabled' key)
|
||||||
* @param string[] $aExistingClassFields if non empty, will check that all fields declared in the rules exists in the class
|
* @param string[] $aExistingClassFields if non empty, will check that all fields declared in the rules exists in the class
|
||||||
*
|
*
|
||||||
* @throws \CoreUnexpectedValue if the rule is invalid
|
* @throws \CoreUnexpectedValue if the rule is invalid
|
||||||
*
|
*
|
||||||
* @since 2.6 N°659 uniqueness constraint
|
* @since 2.6 N°659 uniqueness constraint
|
||||||
|
* @since 2.6.1 N°1968 (joli mois de mai...) disallow overrides of 'attributes' properties
|
||||||
*/
|
*/
|
||||||
public static function CheckUniquenessRuleValidity($aUniquenessRuleProperties, $bRuleOverride = true, $aExistingClassFields = array())
|
public static function CheckUniquenessRuleValidity($aUniquenessRuleProperties, $bRuleOverride = true, $aExistingClassFields = array())
|
||||||
{
|
{
|
||||||
$MANDATORY_ATTRIBUTES = array('attributes');
|
$MANDATORY_ATTRIBUTES = array('attributes');
|
||||||
$UNIQUENESS_MANDATORY_KEYS_NB = count($MANDATORY_ATTRIBUTES);
|
$UNIQUENESS_MANDATORY_KEYS_NB = count($MANDATORY_ATTRIBUTES);
|
||||||
$bHasDisabledKey = false;
|
|
||||||
$bHasMissingMandatoryKey = true;
|
$bHasMissingMandatoryKey = true;
|
||||||
$iMissingMandatoryKeysNb = $UNIQUENESS_MANDATORY_KEYS_NB;
|
$iMissingMandatoryKeysNb = $UNIQUENESS_MANDATORY_KEYS_NB;
|
||||||
$bHasAllMandatoryKeysMissing = false;
|
/** @var boolean $bHasNonDisabledKeys true if rule contains at least one key that is not 'disabled' */
|
||||||
$bHasNonDisabledKeys = false;
|
$bHasNonDisabledKeys = false;
|
||||||
|
|
||||||
foreach ($aUniquenessRuleProperties as $sUniquenessRuleKey => $aUniquenessRuleProperty)
|
foreach ($aUniquenessRuleProperties as $sUniquenessRuleKey => $aUniquenessRuleProperty)
|
||||||
{
|
{
|
||||||
if (($sUniquenessRuleKey === 'disabled') && (!is_null($aUniquenessRuleProperty)))
|
if (($sUniquenessRuleKey === 'disabled') && (!is_null($aUniquenessRuleProperty)))
|
||||||
{
|
{
|
||||||
$bHasDisabledKey = true;
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
if (is_null($aUniquenessRuleProperty))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
$bHasNonDisabledKeys = true;
|
$bHasNonDisabledKeys = true;
|
||||||
|
|
||||||
if (in_array($sUniquenessRuleKey, $MANDATORY_ATTRIBUTES, true)) {
|
if (in_array($sUniquenessRuleKey, $MANDATORY_ATTRIBUTES, true)) {
|
||||||
$bHasMissingMandatoryKey = false;
|
|
||||||
$iMissingMandatoryKeysNb--;
|
$iMissingMandatoryKeysNb--;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (($sUniquenessRuleKey === 'attributes') && (!empty($aExistingClassFields)))
|
if ($sUniquenessRuleKey === 'attributes')
|
||||||
{
|
{
|
||||||
foreach ($aUniquenessRuleProperties[$sUniquenessRuleKey] as $sRuleAttribute)
|
if (!empty($aExistingClassFields))
|
||||||
{
|
{
|
||||||
if (!in_array($sRuleAttribute, $aExistingClassFields, true))
|
foreach ($aUniquenessRuleProperties[$sUniquenessRuleKey] as $sRuleAttribute)
|
||||||
{
|
{
|
||||||
throw new CoreUnexpectedValue("Uniqueness rule : non existing field '$sRuleAttribute'");
|
if (!in_array($sRuleAttribute, $aExistingClassFields, true))
|
||||||
|
{
|
||||||
|
throw new CoreUnexpectedValue("Uniqueness rule : non existing field '$sRuleAttribute'");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($iMissingMandatoryKeysNb == $UNIQUENESS_MANDATORY_KEYS_NB)
|
if ($iMissingMandatoryKeysNb === 0)
|
||||||
{
|
{
|
||||||
$bHasAllMandatoryKeysMissing = true;
|
$bHasMissingMandatoryKey = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($bHasDisabledKey)
|
if ($bRuleOverride && $bHasNonDisabledKeys)
|
||||||
{
|
{
|
||||||
if ($bRuleOverride && $bHasAllMandatoryKeysMissing && !$bHasNonDisabledKeys)
|
throw new CoreUnexpectedValue('Uniqueness rule : only the \'disabled\' key can be overridden');
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if ($bHasMissingMandatoryKey)
|
|
||||||
{
|
|
||||||
throw new CoreUnexpectedValue('Uniqueness rule : missing mandatory properties');
|
|
||||||
}
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
if (!$bRuleOverride && $bHasMissingMandatoryKey)
|
||||||
if ($bHasMissingMandatoryKey)
|
|
||||||
{
|
{
|
||||||
throw new CoreUnexpectedValue('Uniqueness rule : missing mandatory properties');
|
throw new CoreUnexpectedValue('Uniqueness rule : missing mandatory property');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -3810,7 +3867,7 @@ abstract class MetaModel
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @param string $sClass
|
* @param string $sClass
|
||||||
* @param int $iOption
|
* @param int $iOption one of ENUM_CHILD_CLASSES_EXCLUDETOP, ENUM_CHILD_CLASSES_ALL
|
||||||
*
|
*
|
||||||
* @return array
|
* @return array
|
||||||
* @throws \CoreException
|
* @throws \CoreException
|
||||||
|
|||||||
@@ -1152,19 +1152,9 @@ EOF
|
|||||||
$aUniquenessRules[$sCurrentRuleId]['disabled'] = $this->GetPropBooleanConverted($oUniquenessSingleRule, 'disabled', null);
|
$aUniquenessRules[$sCurrentRuleId]['disabled'] = $this->GetPropBooleanConverted($oUniquenessSingleRule, 'disabled', null);
|
||||||
$aUniquenessRules[$sCurrentRuleId]['is_blocking'] = $this->GetPropBooleanConverted($oUniquenessSingleRule, 'is_blocking',
|
$aUniquenessRules[$sCurrentRuleId]['is_blocking'] = $this->GetPropBooleanConverted($oUniquenessSingleRule, 'is_blocking',
|
||||||
null);
|
null);
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
// we're just checking all mandatory fields are present right now
|
|
||||||
// we will check for rule overrides validity later (see \MetaModel::InitClasses)
|
|
||||||
MetaModel::CheckUniquenessRuleValidity($aUniquenessRules[$sCurrentRuleId], true);
|
|
||||||
}
|
|
||||||
catch (CoreUnexpectedValue $e)
|
|
||||||
{
|
|
||||||
throw(new DOMFormatException("Invalid uniqueness rule declaration : class={$oClass->getAttribute('id')}, rule=$sCurrentRuleId, reason={$e->getMessage()}"));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// we will check for rules validity later as for now we don't have objects hierarchy (see \MetaModel::InitClasses)
|
||||||
$aClassParams['uniqueness_rules'] = var_export($aUniquenessRules, true);
|
$aClassParams['uniqueness_rules'] = var_export($aUniquenessRules, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user