mirror of
https://github.com/Combodo/iTop.git
synced 2026-03-02 07:34:13 +01: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)
|
||||
{
|
||||
$sCurrentClass = get_class($this);
|
||||
$sOqlUniquenessQuery = "SELECT $sCurrentClass";
|
||||
$sRuleRootClass = $aUniquenessRuleProperties['root_class'];
|
||||
$sOqlUniquenessQuery = "SELECT $sRuleRootClass";
|
||||
if (!(empty($sUniquenessFilter = $aUniquenessRuleProperties['filter'])))
|
||||
{
|
||||
$sOqlUniquenessQuery .= ' WHERE '.$sUniquenessFilter;
|
||||
}
|
||||
/** @var \DBObjectSearch $oUniquenessQuery */
|
||||
$oUniquenessQuery = DBObjectSearch::FromOQL($sOqlUniquenessQuery);
|
||||
|
||||
if (!$this->IsNew())
|
||||
@@ -1600,6 +1601,12 @@ abstract class DBObject implements iDisplay
|
||||
$oUniquenessQuery->AddCondition($sAttributeCode, $attributeValue, '=');
|
||||
}
|
||||
|
||||
$aChildClassesWithRuleDisabled = MetaModel::GetChildClassesWithDisabledUniquenessRule($sRuleRootClass, $sUniquenessRuleId);
|
||||
if (!empty($aChildClassesWithRuleDisabled))
|
||||
{
|
||||
$oUniquenessQuery->AddConditionForInOperatorUsingParam('finalclass', $aChildClassesWithRuleDisabled, false);
|
||||
}
|
||||
|
||||
return $oUniquenessQuery;
|
||||
}
|
||||
|
||||
|
||||
@@ -528,13 +528,15 @@ abstract class MetaModel
|
||||
|
||||
/**
|
||||
* @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
|
||||
*
|
||||
* @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]))
|
||||
{
|
||||
@@ -548,6 +550,11 @@ abstract class MetaModel
|
||||
$aCurrentUniquenessRules = self::$m_aClassParams[$sClass]['uniqueness_rules'];
|
||||
}
|
||||
|
||||
if ($bClassDefinitionOnly)
|
||||
{
|
||||
return $aCurrentUniquenessRules;
|
||||
}
|
||||
|
||||
$sParentClass = self::GetParentClass($sClass);
|
||||
if ($sParentClass)
|
||||
{
|
||||
@@ -581,6 +588,22 @@ abstract class MetaModel
|
||||
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 $sLeafClassName
|
||||
@@ -608,6 +631,38 @@ abstract class MetaModel
|
||||
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
|
||||
*
|
||||
@@ -2779,21 +2834,26 @@ abstract class MetaModel
|
||||
}
|
||||
}
|
||||
|
||||
$aCurrentClassUniquenessRules = MetaModel::GetUniquenessRules($sPHPClass);
|
||||
$aCurrentClassUniquenessRules = MetaModel::GetUniquenessRules($sPHPClass, true);
|
||||
if (!empty($aCurrentClassUniquenessRules))
|
||||
{
|
||||
$aClassFields = self::GetAttributesList($sPHPClass);
|
||||
foreach ($aCurrentClassUniquenessRules as $sUniquenessRuleId => $aUniquenessRuleProperties)
|
||||
{
|
||||
$bHasSameRuleInParent = self::HasSameUniquenessRuleInParent($sPHPClass, $sUniquenessRuleId);
|
||||
$bIsRuleOverride = self::HasSameUniquenessRuleInParent($sPHPClass, $sUniquenessRuleId);
|
||||
try
|
||||
{
|
||||
self::CheckUniquenessRuleValidity($aUniquenessRuleProperties, $bHasSameRuleInParent, $aClassFields);
|
||||
self::CheckUniquenessRuleValidity($aUniquenessRuleProperties, $bIsRuleOverride, $aClassFields);
|
||||
}
|
||||
catch (CoreUnexpectedValue $e)
|
||||
{
|
||||
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 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
|
||||
*
|
||||
* @throws \CoreUnexpectedValue if the rule is invalid
|
||||
*
|
||||
* @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())
|
||||
{
|
||||
$MANDATORY_ATTRIBUTES = array('attributes');
|
||||
$UNIQUENESS_MANDATORY_KEYS_NB = count($MANDATORY_ATTRIBUTES);
|
||||
$bHasDisabledKey = false;
|
||||
|
||||
$bHasMissingMandatoryKey = true;
|
||||
$iMissingMandatoryKeysNb = $UNIQUENESS_MANDATORY_KEYS_NB;
|
||||
$bHasAllMandatoryKeysMissing = false;
|
||||
/** @var boolean $bHasNonDisabledKeys true if rule contains at least one key that is not 'disabled' */
|
||||
$bHasNonDisabledKeys = false;
|
||||
|
||||
foreach ($aUniquenessRuleProperties as $sUniquenessRuleKey => $aUniquenessRuleProperty)
|
||||
{
|
||||
if (($sUniquenessRuleKey === 'disabled') && (!is_null($aUniquenessRuleProperty)))
|
||||
{
|
||||
$bHasDisabledKey = true;
|
||||
continue;
|
||||
}
|
||||
if (is_null($aUniquenessRuleProperty))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
$bHasNonDisabledKeys = true;
|
||||
|
||||
if (in_array($sUniquenessRuleKey, $MANDATORY_ATTRIBUTES, true)) {
|
||||
$bHasMissingMandatoryKey = false;
|
||||
$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)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if ($bHasMissingMandatoryKey)
|
||||
{
|
||||
throw new CoreUnexpectedValue('Uniqueness rule : missing mandatory properties');
|
||||
}
|
||||
|
||||
return;
|
||||
throw new CoreUnexpectedValue('Uniqueness rule : only the \'disabled\' key can be overridden');
|
||||
}
|
||||
|
||||
if ($bHasMissingMandatoryKey)
|
||||
if (!$bRuleOverride && $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 int $iOption
|
||||
* @param int $iOption one of ENUM_CHILD_CLASSES_EXCLUDETOP, ENUM_CHILD_CLASSES_ALL
|
||||
*
|
||||
* @return array
|
||||
* @throws \CoreException
|
||||
|
||||
@@ -1152,19 +1152,9 @@ EOF
|
||||
$aUniquenessRules[$sCurrentRuleId]['disabled'] = $this->GetPropBooleanConverted($oUniquenessSingleRule, 'disabled', null);
|
||||
$aUniquenessRules[$sCurrentRuleId]['is_blocking'] = $this->GetPropBooleanConverted($oUniquenessSingleRule, 'is_blocking',
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user