diff --git a/application/cmdbabstract.class.inc.php b/application/cmdbabstract.class.inc.php
index 2e9e48485..922824015 100644
--- a/application/cmdbabstract.class.inc.php
+++ b/application/cmdbabstract.class.inc.php
@@ -1,5 +1,5 @@
OnDisplayProperties($this, $oPage, $bEditMode);
}
}
-
+
// Special case to display the case log, if any...
// WARNING: if you modify the loop below, also check the corresponding code in UpdateObject and DisplayModifyForm
foreach(MetaModel::ListAttributeDefs(get_class($this)) as $sAttCode => $oAttDef)
@@ -275,6 +275,8 @@ abstract class cmdbAbstractObject extends CMDBObject implements iDisplay
function DisplayBareRelations(WebPage $oPage, $bEditMode = false)
{
+ $aRedundancySettings = $this->FindVisibleRedundancySettings();
+
// Related objects: display all the linkset attributes, each as a separate tab
// In the order described by the 'display' ZList
$aList = $this->FlattenZList(MetaModel::GetZListItems(get_class($this), 'details'));
@@ -340,6 +342,7 @@ abstract class cmdbAbstractObject extends CMDBObject implements iDisplay
// Non-readable/hidden linkedset... don't display anything
if ($iFlags & OPT_ATT_HIDDEN) continue;
+ $aArgs = array('this' => $this);
$bReadOnly = ($iFlags & (OPT_ATT_READONLY|OPT_ATT_SLAVE));
if ($bEditMode && (!$bReadOnly))
{
@@ -359,7 +362,6 @@ abstract class cmdbAbstractObject extends CMDBObject implements iDisplay
$oValue = $this->Get($sAttCode);
$sDisplayValue = ''; // not used
- $aArgs = array('this' => $this);
$sHTMLValue = "".self::GetFormElementForField($oPage, $sClass, $sAttCode, $oAttDef, $oValue, $sDisplayValue, $sInputId, '', $iFlags, $aArgs).'';
$this->AddToFieldsMap($sAttCode, $sInputId);
$oPage->add($sHTMLValue);
@@ -411,6 +413,29 @@ abstract class cmdbAbstractObject extends CMDBObject implements iDisplay
$oBlock = new DisplayBlock($this->Get($sAttCode)->GetFilter(), 'list', false);
$oBlock->Display($oPage, 'rel_'.$sAttCode, $aParams);
}
+ if (array_key_exists($sAttCode, $aRedundancySettings))
+ {
+ foreach ($aRedundancySettings[$sAttCode] as $oRedundancyAttDef)
+ {
+ $sRedundancyAttCode = $oRedundancyAttDef->GetCode();
+ $sValue = $this->Get($sRedundancyAttCode);
+ $iRedundancyFlags = $this->GetFormAttributeFlags($sRedundancyAttCode);
+ $bRedundancyReadOnly = ($iRedundancyFlags & (OPT_ATT_READONLY|OPT_ATT_SLAVE));
+
+ $oPage->add('
');
+ }
+ }
}
$oPage->SetCurrentTab('');
@@ -527,18 +552,7 @@ abstract class cmdbAbstractObject extends CMDBObject implements iDisplay
$sComments = isset($aFieldsComments[$sAttCode]) ? $aFieldsComments[$sAttCode] : ' ';
$sInfos = ' ';
- if ($this->IsNew())
- {
- $iFlags = $this->GetInitialStateAttributeFlags($sAttCode);
- }
- else
- {
- $iFlags = $this->GetAttributeFlags($sAttCode);
- }
- if (($iFlags & OPT_ATT_MANDATORY) && $this->IsNew())
- {
- $iFlags = $iFlags & ~OPT_ATT_READONLY; // Mandatory fields cannot be read-only when creating an object
- }
+ $iFlags = $this->GetFormAttributeFlags($sAttCode);
if (array_key_exists($sAttCode, $aExtraFlags))
{
// the caller may override some flags if needed
@@ -1796,6 +1810,20 @@ abstract class cmdbAbstractObject extends CMDBObject implements iDisplay
$sHTMLValue .= "\n";
break;
+ case 'RedundancySetting':
+ $sHTMLValue = '';
+ $sHTMLValue .= '';
+ $sHTMLValue .= '| ';
+ $sHTMLValue .= ' ';
+ $sHTMLValue .= $oAttDef->GetDisplayForm($value, $oPage, true);
+ $sHTMLValue .= ' ';
+ $sHTMLValue .= ' | ';
+ $sHTMLValue .= ''.$sValidationField.' | ';
+ $sHTMLValue .= '
';
+ $sHTMLValue .= '
';
+ $oPage->add_ready_script("$('#$iId :input').bind('keyup change validate', function(evt, sFormId) { return ValidateRedundancySettings('$iId',sFormId); } );"); // Custom validation function
+ break;
+
case 'String':
default:
$aEventsList[] ='validate';
@@ -2632,6 +2660,26 @@ EOF
return $aWriteableAttList;
}
+ /**
+ * Compute the attribute flags depending on the object state
+ */
+ public function GetFormAttributeFlags($sAttCode)
+ {
+ if ($this->IsNew())
+ {
+ $iFlags = $this->GetInitialStateAttributeFlags($sAttCode);
+ }
+ else
+ {
+ $iFlags = $this->GetAttributeFlags($sAttCode);
+ }
+ if (($iFlags & OPT_ATT_MANDATORY) && $this->IsNew())
+ {
+ $iFlags = $iFlags & ~OPT_ATT_READONLY; // Mandatory fields cannot be read-only when creating an object
+ }
+ return $iFlags;
+ }
+
/**
* Updates the object from a flat array of values
* @param string $aValues array of attcode => scalar or array (N-N links)
@@ -2826,6 +2874,10 @@ EOF
{
$value = array('fcontents' => utils::ReadPostedDocument("attr_{$sFormPrefix}{$sAttCode}", 'fcontents'));
}
+ elseif ($oAttDef->GetEditClass() == 'RedundancySetting')
+ {
+ $value = $oAttDef->ReadValueFromPostedForm($sFormPrefix);
+ }
else if (($oAttDef->GetEditClass() == 'LinkedSet') && !$oAttDef->IsIndirect() &&
(($oAttDef->GetEditMode() == LINKSET_EDITMODE_INPLACE) || ($oAttDef->GetEditMode() == LINKSET_EDITMODE_ADDREMOVE)) )
{
@@ -3748,5 +3800,34 @@ EOF
}
}
}
+
+ /**
+ * Find redundancy settings that can be viewed and modified in a tab
+ * Settings are distributed to the corresponding link set attribute so as to be shown in the relevant tab
+ */
+ protected function FindVisibleRedundancySettings()
+ {
+ $aRet = array();
+ foreach (MetaModel::ListAttributeDefs(get_class($this)) as $sAttCode => $oAttDef)
+ {
+ if ($oAttDef instanceof AttributeRedundancySettings)
+ {
+ if ($oAttDef->IsVisible())
+ {
+ $aQueryInfo = $oAttDef->GetRelationQueryData();
+ if (isset($aQueryInfo['sAttribute']))
+ {
+ $oUpperAttDef = MetaModel::GetAttributeDef($aQueryInfo['sFromClass'], $aQueryInfo['sAttribute']);
+ $oHostAttDef = $oUpperAttDef->GetMirrorLinkAttribute();
+ if ($oHostAttDef)
+ {
+ $sHostAttCode = $oHostAttDef->GetCode();
+ $aRet[$sHostAttCode][] = $oAttDef;
+ }
+ }
+ }
+ }
+ }
+ return $aRet;
+ }
}
-?>
diff --git a/core/attributedef.class.inc.php b/core/attributedef.class.inc.php
index 354d8464d..a5ca922fd 100644
--- a/core/attributedef.class.inc.php
+++ b/core/attributedef.class.inc.php
@@ -215,6 +215,7 @@ abstract class AttributeDefinition
public function GetValue($oHostObject){return null;} // must return the value if LoadInObject returns false
public function IsNullAllowed() {return true;}
public function GetCode() {return $this->m_sCode;}
+ public function GetMirrorLinkAttribute() {return null;}
/**
* Helper to browse the hierarchy of classes, searching for a label
@@ -979,6 +980,17 @@ class AttributeLinkedSet extends AttributeDefinition
// Both values are Object sets
return $val1->HasSameContents($val2);
}
+
+ /**
+ * Find the corresponding "link" attribute on the target class
+ *
+ * @return string The attribute code on the target class, or null if none has been found
+ */
+ public function GetMirrorLinkAttribute()
+ {
+ $oRemoteAtt = MetaModel::GetAttributeDef($this->GetLinkedClass(), $this->GetExtKeyToMe());
+ return $oRemoteAtt;
+ }
}
/**
@@ -1001,6 +1013,28 @@ class AttributeLinkedSetIndirect extends AttributeLinkedSet
{
return $this->GetOptional('tracking_level', MetaModel::GetConfig()->Get('tracking_level_linked_set_indirect_default'));
}
+
+ /**
+ * Find the corresponding "link" attribute on the target class
+ *
+ * @return string The attribute code on the target class, or null if none has been found
+ */
+ public function GetMirrorLinkAttribute()
+ {
+ $oRet = null;
+ $oExtKeyToRemote = MetaModel::GetAttributeDef($this->GetLinkedClass(), $this->GetExtKeyToRemote());
+ $sRemoteClass = $oExtKeyToRemote->GetTargetClass();
+ foreach (MetaModel::ListAttributeDefs($sRemoteClass) as $sRemoteAttCode => $oRemoteAttDef)
+ {
+ if (!$oRemoteAttDef instanceof AttributeLinkedSetIndirect) continue;
+ if ($oRemoteAttDef->GetLinkedClass() != $this->GetLinkedClass()) continue;
+ if ($oRemoteAttDef->GetExtKeyToMe() != $this->GetExtKeyToRemote()) continue;
+ if ($oRemoteAttDef->GetExtKeyToRemote() != $this->GetExtKeyToMe()) continue;
+ $oRet = $oRemoteAttDef;
+ break;
+ }
+ return $oRet;
+ }
}
/**
@@ -3171,6 +3205,26 @@ class AttributeExternalKey extends AttributeDBFieldVoid
{
return $this->GetOptional('allow_target_creation', MetaModel::GetConfig()->Get('allow_target_creation'));
}
+
+ /**
+ * Find the corresponding "link" attribute on the target class
+ *
+ * @return string The attribute code on the target class, or null if none has been found
+ */
+ public function GetMirrorLinkAttribute()
+ {
+ $oRet = null;
+ $sRemoteClass = $this->GetTargetClass();
+ foreach (MetaModel::ListAttributeDefs($sRemoteClass) as $sRemoteAttCode => $oRemoteAttDef)
+ {
+ if (!$oRemoteAttDef->IsLinkSet()) continue;
+ if (!is_subclass_of($this->GetHostClass(), $oRemoteAttDef->GetLinkedClass()) && $oRemoteAttDef->GetLinkedClass() != $this->GetHostClass()) continue;
+ if ($oRemoteAttDef->GetExtKeyToMe() != $this->GetCode()) continue;
+ $oRet = $oRemoteAttDef;
+ break;
+ }
+ return $oRet;
+ }
}
/**
@@ -3295,6 +3349,16 @@ class AttributeHierarchicalKey extends AttributeExternalKey
$oSet = $oValSetDef->ToObjectSet($aArgs, $sContains);
return $oSet;
}
+
+ /**
+ * Find the corresponding "link" attribute on the target class
+ *
+ * @return string The attribute code on the target class, or null if none has been found
+ */
+ public function GetMirrorLinkAttribute()
+ {
+ return null;
+ }
}
/**
@@ -5107,4 +5171,391 @@ class AttributeFriendlyName extends AttributeComputedFieldVoid
}
}
-?>
+/**
+ * Holds the setting for the redundancy on a specific relation
+ * Its value is a string, containing either:
+ * - 'disabled'
+ * - 'n', where n is a positive integer value giving the minimum count of items upstream
+ * - 'n%', where n is a positive integer value, giving the minimum as a percentage of the total count of items upstream
+ *
+ * @package iTopORM
+ */
+class AttributeRedundancySettings extends AttributeDBField
+{
+ static public function ListExpectedParams()
+ {
+ return array('sql', 'relation_code', 'from_class', 'neighbour_id', 'enabled', 'enabled_mode', 'min_up', 'min_up_type', 'min_up_mode');
+ }
+
+ public function GetValuesDef() {return null;}
+ public function GetPrerequisiteAttributes() {return array();}
+
+ public function GetEditClass() {return "RedundancySetting";}
+ protected function GetSQLCol() {return "VARCHAR(20)";}
+
+
+ public function GetValidationPattern()
+ {
+ return "^[0-9]{1,3}|[0-9]{1,2}%|disabled$";
+ }
+
+ public function GetMaxSize()
+ {
+ return 20;
+ }
+
+ public function GetDefaultValue($aArgs = array())
+ {
+ $sRet = 'disabled';
+ if ($this->Get('enabled'))
+ {
+ if ($this->Get('min_up_type') == 'count')
+ {
+ $sRet = (string) $this->Get('min_up');
+ }
+ else // percent
+ {
+ $sRet = $this->Get('min_up').'%';
+ }
+ }
+ return $sRet;
+ }
+
+ public function IsNullAllowed()
+ {
+ return false;
+ }
+
+ public function GetNullValue()
+ {
+ return '';
+ }
+
+ public function IsNull($proposedValue)
+ {
+ return ($proposedValue == '');
+ }
+
+ public function MakeRealValue($proposedValue, $oHostObj)
+ {
+ if (is_null($proposedValue)) return '';
+ return (string)$proposedValue;
+ }
+
+ public function ScalarToSQL($value)
+ {
+ if (!is_string($value))
+ {
+ throw new CoreException('Expected the attribute value to be a string', array('found_type' => gettype($value), 'value' => $value, 'class' => $this->GetHostClass(), 'attribute' => $this->GetCode()));
+ }
+ return $value;
+ }
+
+ public function GetRelationQueryData()
+ {
+ foreach (MetaModel::EnumRelationQueries($this->GetHostClass(), $this->Get('relation_code'), false) as $sDummy => $aQueryInfo)
+ {
+ if ($aQueryInfo['sFromClass'] == $this->Get('from_class'))
+ {
+ if ($aQueryInfo['sNeighbour'] == $this->Get('neighbour_id'))
+ {
+ return $aQueryInfo;
+ }
+ }
+ }
+ }
+
+ /**
+ * Find the user option label
+ * @param user option : disabled|cout|percent
+ */
+ public function GetUserOptionFormat($sUserOption, $sDefault = null)
+ {
+ $sLabel = $this->SearchLabel('/Attribute:'.$this->m_sCode.'/'.$sUserOption, null, true /*user lang*/);
+ if (is_null($sLabel))
+ {
+ // If no default value is specified, let's define the most relevant one for developping purposes
+ if (is_null($sDefault))
+ {
+ $sDefault = str_replace('_', ' ', $this->m_sCode.':'.$sUserOption.'(%1$s)');
+ }
+ // Browse the hierarchy again, accepting default (english) translations
+ $sLabel = $this->SearchLabel('/Attribute:'.$this->m_sCode.'/'.$sUserOption, $sDefault, false);
+ }
+ return $sLabel;
+ }
+
+ /**
+ * Override to display the value in the GUI
+ */
+ public function GetAsHTML($sValue, $oHostObject = null, $bLocalize = true)
+ {
+ $sCurrentOption = $this->GetCurrentOption($sValue);
+ $sClass = $oHostObject ? get_class($oHostObject) : $this->m_sHostClass;
+ return sprintf($this->GetUserOptionFormat($sCurrentOption), $this->GetMinUpValue($sValue), MetaModel::GetName($sClass));
+ }
+
+ public function GetAsCSV($sValue, $sSeparator = ',', $sTextQualifier = '"', $oHostObject = null, $bLocalize = true)
+ {
+ $sFrom = array("\r\n", $sTextQualifier);
+ $sTo = array("\n", $sTextQualifier.$sTextQualifier);
+ $sEscaped = str_replace($sFrom, $sTo, (string)$sValue);
+ return $sTextQualifier.$sEscaped.$sTextQualifier;
+ }
+
+ /**
+ * Helper to interpret the value, given the current settings and string representation of the attribute
+ */
+ public function IsEnabled($sValue)
+ {
+ if ($this->get('enabled_mode') == 'fixed')
+ {
+ $bRet = $this->get('enabled');
+ }
+ else
+ {
+ $bRet = ($sValue != 'disabled');
+ }
+ return $bRet;
+ }
+
+ /**
+ * Helper to interpret the value, given the current settings and string representation of the attribute
+ */
+ public function GetMinUpType($sValue)
+ {
+ if ($this->get('min_up_mode') == 'fixed')
+ {
+ $sRet = $this->get('min_up_type');
+ }
+ else
+ {
+ $sRet = 'count';
+ if (substr(trim($sValue), -1, 1) == '%')
+ {
+ $sRet = 'percent';
+ }
+ }
+ return $sRet;
+ }
+
+ /**
+ * Helper to interpret the value, given the current settings and string representation of the attribute
+ */
+ public function GetMinUpValue($sValue)
+ {
+ if ($this->get('min_up_mode') == 'fixed')
+ {
+ $iRet = (int) $this->Get('min_up');
+ }
+ else
+ {
+ $sRefValue = $sValue;
+ if (substr(trim($sValue), -1, 1) == '%')
+ {
+ $sRefValue = substr(trim($sValue), 0, -1);
+ }
+ $iRet = (int) trim($sRefValue);
+ }
+ return $iRet;
+ }
+
+ /**
+ * Helper to determine if the redundancy can be viewed/edited by the end-user
+ */
+ public function IsVisible()
+ {
+ $bRet = false;
+ if ($this->Get('enabled_mode') == 'fixed')
+ {
+ $bRet = $this->Get('enabled');
+ }
+ elseif ($this->Get('enabled_mode') == 'user')
+ {
+ $bRet = true;
+ }
+ return $bRet;
+ }
+
+ public function IsWritable()
+ {
+ if (($this->Get('enabled_mode') == 'fixed') && ($this->Get('min_up_mode') == 'fixed'))
+ {
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Returns an HTML form that can be read by ReadValueFromPostedForm
+ */
+ public function GetDisplayForm($sCurrentValue, $oPage, $bEditMode = false, $sFormPrefix = '')
+ {
+ $sRet = '';
+ $aUserOptions = $this->GetUserOptions($sCurrentValue);
+ if (count($aUserOptions) < 2)
+ {
+ $bEditOption = false;
+ }
+ else
+ {
+ $bEditOption = $bEditMode;
+ }
+ $sCurrentOption = $this->GetCurrentOption($sCurrentValue);
+ foreach($aUserOptions as $sUserOption)
+ {
+ $bSelected = ($sUserOption == $sCurrentOption);
+ $sRet .= '';
+ $sRet .= $this->GetDisplayOption($sCurrentValue, $oPage, $sFormPrefix, $bEditOption, $sUserOption, $bSelected);
+ $sRet .= '
';
+ }
+ return $sRet;
+ }
+
+ const USER_OPTION_DISABLED = 'disabled';
+ const USER_OPTION_ENABLED_COUNT = 'count';
+ const USER_OPTION_ENABLED_PERCENT = 'percent';
+
+ /**
+ * Depending on the xxx_mode parameters, build the list of options that are allowed to the end-user
+ */
+ protected function GetUserOptions($sValue)
+ {
+ $aRet = array();
+ if ($this->Get('enabled_mode') == 'user')
+ {
+ $aRet[] = self::USER_OPTION_DISABLED;
+ }
+
+ if ($this->Get('min_up_mode') == 'user')
+ {
+ $aRet[] = self::USER_OPTION_ENABLED_COUNT;
+ $aRet[] = self::USER_OPTION_ENABLED_PERCENT;
+ }
+ else
+ {
+ if ($this->GetMinUpType($sValue) == 'count')
+ {
+ $aRet[] = self::USER_OPTION_ENABLED_COUNT;
+ }
+ else
+ {
+ $aRet[] = self::USER_OPTION_ENABLED_PERCENT;
+ }
+ }
+ return $aRet;
+ }
+
+ /**
+ * Convert the string representation into one of the existing options
+ */
+ protected function GetCurrentOption($sValue)
+ {
+ $sRet = self::USER_OPTION_DISABLED;
+ if ($this->IsEnabled($sValue))
+ {
+ if ($this->GetMinUpType($sValue) == 'count')
+ {
+ $sRet = self::USER_OPTION_ENABLED_COUNT;
+ }
+ else
+ {
+ $sRet = self::USER_OPTION_ENABLED_PERCENT;
+ }
+ }
+ return $sRet;
+ }
+
+ /**
+ * Display an option (form, or current value)
+ */
+ protected function GetDisplayOption($sCurrentValue, $oPage, $sFormPrefix, $bEditMode, $sUserOption, $bSelected = true)
+ {
+ $sRet = '';
+
+ $iCurrentValue = $this->GetMinUpValue($sCurrentValue);
+ if ($bEditMode)
+ {
+ $sHtmlNamesPrefix = 'rddcy_'.$this->Get('relation_code').'_'.$this->Get('from_class').'_'.$this->Get('neighbour_id');
+ switch ($sUserOption)
+ {
+ case self::USER_OPTION_DISABLED:
+ $sValue = ''; // Empty placeholder
+ break;
+
+ case self::USER_OPTION_ENABLED_COUNT:
+ if ($bEditMode)
+ {
+ $sName = $sHtmlNamesPrefix.'_min_up_count';
+ $sEditValue = $bSelected ? $iCurrentValue : '';
+ $sValue = '';
+ // To fix an issue on Firefox: focus set to the option (because the input is within the label for the option)
+ $oPage->add_ready_script("\$('[name=\"$sName\"]').click(function(){var me=this; setTimeout(function(){\$(me).focus();}, 100);});");
+ }
+ else
+ {
+ $sValue = $iCurrentValue;
+ }
+ break;
+
+ case self::USER_OPTION_ENABLED_PERCENT:
+ if ($bEditMode)
+ {
+ $sName = $sHtmlNamesPrefix.'_min_up_percent';
+ $sEditValue = $bSelected ? $iCurrentValue : '';
+ $sValue = '';
+ // To fix an issue on Firefox: focus set to the option (because the input is within the label for the option)
+ $oPage->add_ready_script("\$('[name=\"$sName\"]').click(function(){var me=this; setTimeout(function(){\$(me).focus();}, 100);});");
+ }
+ else
+ {
+ $sValue = $iCurrentValue;
+ }
+ break;
+ }
+ $sLabel = sprintf($this->GetUserOptionFormat($sUserOption), $sValue, MetaModel::GetName($this->GetHostClass()));
+
+ $sOptionName = $sHtmlNamesPrefix.'_user_option';
+ $sOptionId = $sOptionName.'_'.$sUserOption;
+ $sChecked = $bSelected ? 'checked' : '';
+ $sRet = ' ';
+ }
+ else
+ {
+ // Read-only: display only the currently selected option
+ if ($bSelected)
+ {
+ $sRet = sprintf($this->GetUserOptionFormat($sUserOption), $iCurrentValue, MetaModel::GetName($this->GetHostClass()));
+ }
+ }
+ return $sRet;
+ }
+
+ /**
+ * Makes the string representation out of the values given by the form defined in GetDisplayForm
+ */
+ public function ReadValueFromPostedForm($sFormPrefix)
+ {
+ $sHtmlNamesPrefix = 'rddcy_'.$this->Get('relation_code').'_'.$this->Get('from_class').'_'.$this->Get('neighbour_id');
+
+ $iMinUpCount = (int) utils::ReadPostedParam($sHtmlNamesPrefix.'_min_up_count', null, 'raw_data');
+ $iMinUpPercent = (int) utils::ReadPostedParam($sHtmlNamesPrefix.'_min_up_percent', null, 'raw_data');
+ $sSelectedOption = utils::ReadPostedParam($sHtmlNamesPrefix.'_user_option', null, 'raw_data');
+ switch ($sSelectedOption)
+ {
+ case self::USER_OPTION_ENABLED_COUNT:
+ $sRet = $iMinUpCount;
+ break;
+
+ case self::USER_OPTION_ENABLED_PERCENT:
+ $sRet = $iMinUpPercent.'%';
+ break;
+
+ case self::USER_OPTION_DISABLED:
+ default:
+ $sRet = 'disabled';
+ break;
+ }
+ return $sRet;
+ }
+}
diff --git a/core/cmdbobject.class.inc.php b/core/cmdbobject.class.inc.php
index afe19887c..29685d2e0 100644
--- a/core/cmdbobject.class.inc.php
+++ b/core/cmdbobject.class.inc.php
@@ -63,7 +63,6 @@ require_once('dbobjectset.class.php');
require_once('backgroundprocess.inc.php');
require_once('asynctask.class.inc.php');
require_once('dbproperty.class.inc.php');
-require_once('redundancysettings.class.inc.php');
// db change tracking data model
require_once('cmdbchange.class.inc.php');
diff --git a/core/metamodel.class.php b/core/metamodel.class.php
index 03864a1a5..85415de27 100644
--- a/core/metamodel.class.php
+++ b/core/metamodel.class.php
@@ -4402,7 +4402,17 @@ abstract class MetaModel
$aTableInfo['Fields'][$sField]['used'] = true;
$bIndexNeeded = $oAttDef->RequiresIndex();
- $sFieldDefinition = "`$sField` ".($oAttDef->IsNullAllowed() ? "$sDBFieldType NULL" : "$sDBFieldType NOT NULL");
+ // Note: This fix deals only with the case when the field is MISSING
+ // it won't deal with the case when the field gets modified
+ if ($oAttDef->IsNullAllowed())
+ {
+ $sFieldDefinition = "`$sField` $sDBFieldType NULL";
+ }
+ else
+ {
+ $aDefaults = $oAttDef->GetSQLValues($oAttDef->GetDefaultValue());
+ $sFieldDefinition = "`$sField` $sDBFieldType NOT NULL DEFAULT ".CMDBSource::Quote($aDefaults[$sField]);
+ }
if (!CMDBSource::IsField($sTable, $sField))
{
$aErrors[$sClass][$sAttCode][] = "field '$sField' could not be found in table '$sTable'";
diff --git a/core/redundancysettings.class.inc.php b/core/redundancysettings.class.inc.php
deleted file mode 100644
index d68731bac..000000000
--- a/core/redundancysettings.class.inc.php
+++ /dev/null
@@ -1,98 +0,0 @@
-
-
-/**
- * Persistent classes (internal): user settings for the redundancy
- *
- * @copyright Copyright (C) 2015 Combodo SARL
- * @license http://opensource.org/licenses/AGPL-3.0
- */
-
-
-/**
- * Redundancy settings
- *
- * @package iTopORM
- */
-class RedundancySettings extends DBObject
-{
- public static function Init()
- {
- $aParams = array
- (
- "category" => "core/cmdb",
- "key_type" => "autoincrement",
- "name_attcode" => array('relation_code','from_class','neighbour','objkey'),
- "state_attcode" => "",
- "reconc_keys" => array(),
- "db_table" => "priv_redundancy_settings",
- "db_key_field" => "id",
- "db_finalclass_field" => "finalclass",
- "display_template" => "",
- 'indexes' => array(
- array('relation_code', 'from_class', 'neighbour', 'objclass', 'objkey'),
- )
- );
- MetaModel::Init_Params($aParams);
- MetaModel::Init_AddAttribute(new AttributeString("relation_code", array("allowed_values"=>null, "sql"=>"relation_code", "default_value"=>'', "is_null_allowed"=>false, "depends_on"=>array())));
- MetaModel::Init_AddAttribute(new AttributeString("from_class", array("allowed_values"=>null, "sql"=>"from_class", "default_value"=>'', "is_null_allowed"=>false, "depends_on"=>array())));
- MetaModel::Init_AddAttribute(new AttributeString("neighbour", array("allowed_values"=>null, "sql"=>"neighbour", "default_value"=>'', "is_null_allowed"=>false, "depends_on"=>array())));
-
- MetaModel::Init_AddAttribute(new AttributeString("objclass", array("allowed_values"=>null, "sql"=>"objclass", "default_value"=>"", "is_null_allowed"=>false, "depends_on"=>array())));
- MetaModel::Init_AddAttribute(new AttributeObjectKey("objkey", array("allowed_values"=>null, "class_attcode"=>"objclass", "sql"=>"objkey", "is_null_allowed"=>false, "depends_on"=>array())));
-
- MetaModel::Init_AddAttribute(new AttributeBoolean("enabled", array("allowed_values"=>null, "sql"=>"enabled", "default_value"=>false, "is_null_allowed"=>false, "depends_on"=>array())));
-
- MetaModel::Init_AddAttribute(new AttributeEnum("min_up_type", array("allowed_values"=>new ValueSetEnum('count,percent'), "sql"=>"min_up_type", "default_value"=>"count", "is_null_allowed"=>false, "depends_on"=>array())));
- MetaModel::Init_AddAttribute(new AttributeInteger("min_up_count", array("allowed_values"=>null, "sql"=>"min_up_count", "default_value"=>1, "is_null_allowed"=>true, "depends_on"=>array())));
- MetaModel::Init_AddAttribute(new AttributeInteger("min_up_percent", array("allowed_values"=>null, "sql"=>"min_up_percent", "default_value"=>50, "is_null_allowed"=>true, "depends_on"=>array())));
- }
-
- public static function MakeDefault($sRelCode, $aQueryInfo, $oToObject)
- {
- $oRet = MetaModel::NewObject('RedundancySettings');
- $oRet->Set('relation_code', $sRelCode);
- $oRet->Set('from_class', $aQueryInfo['sFromClass']);
- $oRet->Set('neighbour', $aQueryInfo['sNeighbour']);
- $oRet->Set('objclass', get_class($oToObject));
- $oRet->Set('objkey', $oToObject->GetKey());
- $oRet->Set('enabled', $aQueryInfo['bRedundancyEnabledValue']);
- $oRet->Set('min_up_type', $aQueryInfo['sRedundancyMinUpType']);
- $oRet->Set('min_up_count', ($aQueryInfo['sRedundancyMinUpType'] == 'count') ? $aQueryInfo['iRedundancyMinUpValue'] : 1);
- $oRet->Set('min_up_percent', ($aQueryInfo['sRedundancyMinUpType'] == 'percent') ? $aQueryInfo['iRedundancyMinUpValue'] : 50);
- return $oRet;
- }
-
- public static function GetSettings($sRelCode, $aQueryInfo, $oToObject)
- {
- $oSearch = new DBObjectSearch('RedundancySettings');
- $oSearch->AddCondition('relation_code', $sRelCode, '=');
- $oSearch->AddCondition('from_class', $aQueryInfo['sFromClass'], '=');
- $oSearch->AddCondition('neighbour', $aQueryInfo['sNeighbour'], '=');
- $oSearch->AddCondition('objclass', get_class($oToObject), '=');
- $oSearch->AddCondition('objkey', $oToObject->GetKey(), '=');
-
- $oSet = new DBObjectSet($oSearch);
- $oRet = $oSet->Fetch();
- if (!$oRet)
- {
- $oRet = self::MakeDefault($sRelCode, $aQueryInfo, $oToObject);
- }
- return $oRet;
- }
-}
diff --git a/core/relationgraph.class.inc.php b/core/relationgraph.class.inc.php
index 1b69d2d68..d1c90cb3e 100644
--- a/core/relationgraph.class.inc.php
+++ b/core/relationgraph.class.inc.php
@@ -382,17 +382,12 @@ class RelationGraph extends SimpleGraph
protected function IsRedundancyEnabled($sRelCode, $aQueryInfo, $oToNode)
{
$bRet = false;
- if (isset($aQueryInfo['sRedundancyEnabledMode']))
+ $oToObject = $oToNode->GetProperty('object');
+ $oRedundancyAttDef = $this->FindRedundancyAttribute($sRelCode, $aQueryInfo, get_class($oToObject));
+ if ($oRedundancyAttDef)
{
- if ($aQueryInfo['sRedundancyEnabledMode'] == 'fixed')
- {
- $bRet = $aQueryInfo['bRedundancyEnabledValue'];
- }
- elseif ($aQueryInfo['sRedundancyEnabledMode'] == 'user')
- {
- $oUserSettings = $this->FindRedundancyUserSettings($sRelCode, $aQueryInfo, $oToNode);
- $bRet = $oUserSettings->Get('enabled');
- }
+ $sValue = $oToObject->Get($oRedundancyAttDef->GetCode());
+ $bRet = $oRedundancyAttDef->IsEnabled($sValue);
}
return $bRet;
}
@@ -403,52 +398,44 @@ class RelationGraph extends SimpleGraph
protected function GetRedundancyMinUp($sRelCode, $aQueryInfo, $oToNode, $iUpstreamObjects)
{
$iMinUp = 0;
- if (isset($aQueryInfo['sRedundancyMinUpMode']))
+
+ $oToObject = $oToNode->GetProperty('object');
+ $oRedundancyAttDef = $this->FindRedundancyAttribute($sRelCode, $aQueryInfo, get_class($oToObject));
+ if ($oRedundancyAttDef)
{
- if ($aQueryInfo['sRedundancyMinUpMode'] == 'fixed')
+ $sValue = $oToObject->Get($oRedundancyAttDef->GetCode());
+ if ($oRedundancyAttDef->GetMinUpType($sValue) == 'count')
{
- if ($aQueryInfo['sRedundancyMinUpType'] == 'count')
- {
- $iMinUp = $aQueryInfo['iRedundancyMinUpValue'];
- }
- else // 'percent' assumed
- {
- $iMinUp = $iUpstreamObjects * $aQueryInfo['iRedundancyMinUpValue'] / 100;
- }
+ $iMinUp = $oRedundancyAttDef->GetMinUpValue($sValue);
}
- elseif ($aQueryInfo['sRedundancyMinUpMode'] == 'user')
+ else
{
- $oUserSettings = $this->FindRedundancyUserSettings($sRelCode, $aQueryInfo, $oToNode);
- if ($oUserSettings->Get('min_up_type') == 'count')
- {
- $iMinUp = $oUserSettings->Get('min_up_count');
- }
- else
- {
- $iMinUp = $iUpstreamObjects * $oUserSettings->Get('min_up_percent') / 100;
- }
+ $iMinUp = $iUpstreamObjects * $oRedundancyAttDef->GetMinUpValue($sValue) / 100;
}
}
return $iMinUp;
}
/**
- * Helper to search for and cache the reduncancy user settings (could be an object NOT recorded in the DB)
+ * Helper to search for the redundancy attribute
*/
- protected function FindRedundancyUserSettings($sRelCode, $aQueryInfo, $oToNode)
+ protected function FindRedundancyAttribute($sRelCode, $aQueryInfo, $sClass)
{
- $sNeighbourKey = $sRelCode.'/'.$aQueryInfo['sFromClass'].'/'.$aQueryInfo['sNeighbour'];
- if (isset($this->aRedundancySettings[$sNeighbourKey][$oToNode->GetId()]))
+ $oRet = null;
+ foreach (MetaModel::ListAttributeDefs($sClass) as $sAttCode => $oAttDef)
{
- // Cache hit
- $oUserSettings = $this->aRedundancySettings[$sNeighbourKey][$oToNode->GetId()];
+ if ($oAttDef instanceof AttributeRedundancySettings)
+ {
+ if ($oAttDef->Get('relation_code') == $sRelCode)
+ {
+ if ($oAttDef->Get('neighbour_id') == $aQueryInfo['sNeighbour'])
+ {
+ $oRet = $oAttDef;
+ break;
+ }
+ }
+ }
}
- else
- {
- // Cache miss: build the entry
- $oUserSettings = RedundancySettings::GetSettings($sRelCode, $aQueryInfo, $oToNode->GetProperty('object'));
- $this->aRedundancySettings[$sNeighbourKey][$oToNode->GetId()] = $oUserSettings;
- }
- return $oUserSettings;
+ return $oRet;
}
}
diff --git a/datamodels/2.x/itop-config-mgmt/datamodel.itop-config-mgmt.xml b/datamodels/2.x/itop-config-mgmt/datamodel.itop-config-mgmt.xml
index a58deba70..0b882122a 100755
--- a/datamodels/2.x/itop-config-mgmt/datamodel.itop-config-mgmt.xml
+++ b/datamodels/2.x/itop-config-mgmt/datamodel.itop-config-mgmt.xml
@@ -1472,17 +1472,6 @@
applicationsolution_list
-
-
- false
- user
-
-
- count
- 1
- user
-
-
softwares_list
@@ -2000,6 +1989,17 @@
san_id
+
+ redundancy
+ impacts
+ PowerConnection
+ datacenterdevice
+ true
+ fixed
+ 1
+ count
+ fixed
+
@@ -2316,7 +2316,7 @@
- -
+
-
20
-
@@ -2325,11 +2325,19 @@
-
20
- -
+
-
30
+ -
+ 30
+
+
-
+ 10
+
+
+
@@ -2640,7 +2648,7 @@
- -
+
-
20
-
@@ -2649,11 +2657,19 @@
-
20
- -
+
-
30
+ -
+ 30
+
+
-
+ 10
+
+
+
@@ -2807,6 +2823,17 @@
true
list
+
+ redundancy
+ impacts
+ FunctionalCI
+ applicationsolution
+ false
+ user
+ 1
+ user
+ count
+
diff --git a/datamodels/2.x/itop-config-mgmt/en.dict.itop-config-mgmt.php b/datamodels/2.x/itop-config-mgmt/en.dict.itop-config-mgmt.php
index f75226f61..142d91fed 100755
--- a/datamodels/2.x/itop-config-mgmt/en.dict.itop-config-mgmt.php
+++ b/datamodels/2.x/itop-config-mgmt/en.dict.itop-config-mgmt.php
@@ -480,6 +480,11 @@ Dict::Add('EN US', 'English', 'English', array(
'Class:DatacenterDevice/Attribute:fiberinterfacelist_list+' => 'All the fiber channel interfaces for this device',
'Class:DatacenterDevice/Attribute:san_list' => 'SANs',
'Class:DatacenterDevice/Attribute:san_list+' => 'All the SAN switches connected to this device',
+ 'Class:DatacenterDevice/Attribute:redundancy' => 'Redundancy',
+ 'Class:DatacenterDevice/Attribute:redundancy/count' => 'The device is up if at least one power connection (A or B) is up',
+ // Unused yet
+ 'Class:DatacenterDevice/Attribute:redundancy/disabled' => 'The device is up if all its power connections are up',
+ 'Class:DatacenterDevice/Attribute:redundancy/percent' => 'The device is up if at least %1$s %% of its power connections are up',
));
//
@@ -690,6 +695,10 @@ Dict::Add('EN US', 'English', 'English', array(
'Class:ApplicationSolution/Attribute:status/Value:active+' => 'active',
'Class:ApplicationSolution/Attribute:status/Value:inactive' => 'inactive',
'Class:ApplicationSolution/Attribute:status/Value:inactive+' => 'inactive',
+ 'Class:ApplicationSolution/Attribute:redundancy' => 'Impact analysis: configuration of the redundancy',
+ 'Class:ApplicationSolution/Attribute:redundancy/disabled' => 'The solution is up is all CIs are up',
+ 'Class:ApplicationSolution/Attribute:redundancy/count' => 'The solution is up if at least %1$s CI(s) is(are) up',
+ 'Class:ApplicationSolution/Attribute:redundancy/percent' => 'The solution is up if at least %1$s %% of the CIs are up',
));
//
@@ -889,6 +898,10 @@ Dict::Add('EN US', 'English', 'English', array(
'Class:Farm+' => '',
'Class:Farm/Attribute:hypervisor_list' => 'Hypervisors',
'Class:Farm/Attribute:hypervisor_list+' => 'All the hypervisors that compose this farm',
+ 'Class:Farm/Attribute:redundancy' => 'High availability',
+ 'Class:Farm/Attribute:redundancy/disabled' => 'The farm is up if all the hypervisors are up',
+ 'Class:Farm/Attribute:redundancy/count' => 'The farm is up if at least %1$s hypervisor(s) is(are) up',
+ 'Class:Farm/Attribute:redundancy/percent' => 'The farm is up if at least %1$s %% of the hypervisors are up',
));
//
@@ -1861,9 +1874,10 @@ Dict::Add('EN US', 'English', 'English', array(
Dict::Add('EN US', 'English', 'English', array(
'Server:baseinfo' => 'General information',
-'Server:Date' => 'Date',
+'Server:Date' => 'Dates',
'Server:moreinfo' => 'More information',
'Server:otherinfo' => 'Other information',
+'Server:power' => 'Power supply',
'Person:info' => 'General information',
'Person:notifiy' => 'Notification',
'Class:Subnet/Tab:IPUsage' => 'IP Usage',
diff --git a/datamodels/2.x/itop-config-mgmt/fr.dict.itop-config-mgmt.php b/datamodels/2.x/itop-config-mgmt/fr.dict.itop-config-mgmt.php
index 505734a22..a7f3e75ee 100755
--- a/datamodels/2.x/itop-config-mgmt/fr.dict.itop-config-mgmt.php
+++ b/datamodels/2.x/itop-config-mgmt/fr.dict.itop-config-mgmt.php
@@ -1,5 +1,5 @@
'',
'Class:DatacenterDevice/Attribute:san_list' => 'SANs',
'Class:DatacenterDevice/Attribute:san_list+' => '',
+ 'Class:DatacenterDevice/Attribute:redundancy' => 'Redondance',
+ 'Class:DatacenterDevice/Attribute:redundancy/count' => 'Le %2$s est alimenté si au moins une source électrique (A ou B) est opérationnelle',
+ // Unused yet
+ 'Class:DatacenterDevice/Attribute:redundancy/disabled' => 'Le %2$s est alimenté si toutes ses sources électriques sont opérationnelles',
+ 'Class:DatacenterDevice/Attribute:redundancy/percent' => 'Le %2$s est alimenté si au moins %1$s %% de ses sources électriques sont opérationnelles',
));
//
@@ -635,6 +640,10 @@ Dict::Add('FR FR', 'French', 'Français', array(
'Class:ApplicationSolution/Attribute:status/Value:active+' => 'active',
'Class:ApplicationSolution/Attribute:status/Value:inactive' => 'inactive',
'Class:ApplicationSolution/Attribute:status/Value:inactive+' => 'inactive',
+ 'Class:ApplicationSolution/Attribute:redundancy' => 'Analyse d\'impact : configuration de la redondance',
+ 'Class:ApplicationSolution/Attribute:redundancy/disabled' => 'La solution est opérationelle si tous les CIs qui la composent sont opérationnels',
+ 'Class:ApplicationSolution/Attribute:redundancy/count' => 'Nombre minimal de CIs pour que la solution soit opérationnelle : %1$s',
+ 'Class:ApplicationSolution/Attribute:redundancy/percent' => 'Pourcentage minimal de CIs pour que la solution soit opérationnelle : %1$s %%',
));
//
@@ -833,6 +842,10 @@ Dict::Add('FR FR', 'French', 'Français', array(
'Class:Farm+' => '',
'Class:Farm/Attribute:hypervisor_list' => 'Hyperviseurs',
'Class:Farm/Attribute:hypervisor_list+' => '',
+ 'Class:Farm/Attribute:redundancy' => 'Haute disponibilité',
+ 'Class:Farm/Attribute:redundancy/disabled' => 'Le vCluster est opérationnel si tous les hyperviseurs qui le composent sont opérationnels',
+ 'Class:Farm/Attribute:redundancy/count' => 'Nombre minimal d\'hyperviseurs pour que le vCluster soit opérationnel : %1$s',
+ 'Class:Farm/Attribute:redundancy/percent' => 'Pourcentage minimal d\'hyperviseurs pour que le vCluster soit opérationnel : %1$s %%',
));
//
@@ -1831,9 +1844,10 @@ Dict::Add('FR FR', 'French', 'Français', array(
Dict::Add('FR FR', 'French', 'Français', array(
'Server:baseinfo' => 'Informations générales',
-'Server:Date' => 'Date',
+'Server:Date' => 'Dates',
'Server:moreinfo' => 'Informations complémentaires',
'Server:otherinfo' => 'Autres informations',
+'Server:power' => 'Alimentation électrique',
'Person:info' => 'Informations générales',
'Person:notifiy' => 'Notification',
'Class:Subnet/Tab:IPUsage' => 'IP utilisées',
diff --git a/datamodels/2.x/itop-datacenter-mgmt/datamodel.itop-datacenter-mgmt.xml b/datamodels/2.x/itop-datacenter-mgmt/datamodel.itop-datacenter-mgmt.xml
index 86ae19a66..ec09b409c 100755
--- a/datamodels/2.x/itop-datacenter-mgmt/datamodel.itop-datacenter-mgmt.xml
+++ b/datamodels/2.x/itop-datacenter-mgmt/datamodel.itop-datacenter-mgmt.xml
@@ -533,17 +533,6 @@
SELECT DatacenterDevice WHERE powerA_id = :this->id OR powerB_id = :this->id
SELECT PowerConnection WHERE id = :this->powerA_id OR id = :this->powerB_id
-
-
- true
- fixed
-
-
- count
- 1
- fixed
-
-
SELECT PDU WHERE powerstart_id = :this->id
diff --git a/datamodels/2.x/itop-storage-mgmt/datamodel.itop-storage-mgmt.xml b/datamodels/2.x/itop-storage-mgmt/datamodel.itop-storage-mgmt.xml
index a2bc90ff7..03b9ac991 100644
--- a/datamodels/2.x/itop-storage-mgmt/datamodel.itop-storage-mgmt.xml
+++ b/datamodels/2.x/itop-storage-mgmt/datamodel.itop-storage-mgmt.xml
@@ -142,7 +142,7 @@
- -
+
-
20
-
@@ -151,11 +151,19 @@
-
20
- -
+
-
30
+ -
+ 30
+
+
-
+ 10
+
+
+
@@ -396,7 +404,7 @@
- -
+
-
20
-
@@ -405,11 +413,19 @@
-
20
- -
+
-
30
+ -
+ 30
+
+
-
+ 10
+
+
+
@@ -649,7 +665,7 @@
- -
+
-
20
-
@@ -658,11 +674,19 @@
-
20
- -
+
-
30
+ -
+ 30
+
+
-
+ 10
+
+
+
@@ -902,7 +926,7 @@
- -
+
-
20
-
@@ -911,11 +935,19 @@
-
20
- -
+
-
30
+ -
+ 30
+
+
-
+ 10
+
+
+
diff --git a/datamodels/2.x/itop-virtualization-mgmt/datamodel.itop-virtualization-mgmt.xml b/datamodels/2.x/itop-virtualization-mgmt/datamodel.itop-virtualization-mgmt.xml
index 67b658ab2..14f9c79e5 100644
--- a/datamodels/2.x/itop-virtualization-mgmt/datamodel.itop-virtualization-mgmt.xml
+++ b/datamodels/2.x/itop-virtualization-mgmt/datamodel.itop-virtualization-mgmt.xml
@@ -407,17 +407,6 @@
farm_id
-
-
- true
- user
-
-
- count
- 1
- user
-
-
@@ -455,6 +444,17 @@
0
0
+
+ redundancy
+ impacts
+ Hypervisor
+ farm
+ true
+ user
+ 1
+ count
+ user
+
diff --git a/js/forms-json-utils.js b/js/forms-json-utils.js
index 3b7176888..db5598691 100644
--- a/js/forms-json-utils.js
+++ b/js/forms-json-utils.js
@@ -346,6 +346,57 @@ function ValidateCaseLogField(sFieldId, bMandatory, sFormId)
ReportFieldValidationStatus(sFieldId, sFormId, bValid, '');
return bValid;
}
+
+// Validate the inputs depending on the current setting
+function ValidateRedundancySettings(sFieldId, sFormId)
+{
+ var bValid = true;
+ var sExplain = '';
+
+ $('#'+sFieldId+' :input[type="radio"]:checked').parent().find(':input[type="string"]').each(function (){
+ var sValue = $(this).val().trim();
+ if (sValue == '')
+ {
+ bValid = false;
+ sExplain = Dict.S('UI:ValueMustBeSet');
+ }
+ else
+ {
+ // There is something... check if it is a number
+ re = new RegExp('^[0-9]+$');
+ bValid = re.test(sValue);
+ if (bValid)
+ {
+ var iValue = parseInt(sValue , 10);
+ if ($(this).hasClass('redundancy-min-up-percent'))
+ {
+ // A percentage
+ if ((iValue < 0) || (iValue > 100))
+ {
+ bValid = false;
+ }
+ }
+ else if ($(this).hasClass('redundancy-min-up-count'))
+ {
+ // A count
+ if (iValue < 0)
+ {
+ bValid = false;
+ }
+ }
+
+ }
+ if (!bValid)
+ {
+ sExplain = Dict.S('UI:ValueInvalidFormat');
+ }
+ }
+ });
+
+ ReportFieldValidationStatus(sFieldId, sFormId, bValid, sExplain);
+ return bValid;
+}
+
// Manage a 'duration' field
function UpdateDuration(iId)
{
diff --git a/js/utils.js b/js/utils.js
index 042fc0b4c..d15f875b9 100644
--- a/js/utils.js
+++ b/js/utils.js
@@ -279,10 +279,14 @@ function ToogleField(value, field_id)
if (value)
{
$('#'+field_id).removeAttr('disabled');
+ // In case the field is rendered as a div containing several inputs (e.g. RedundancySettings)
+ $('#'+field_id+' :input').removeAttr('disabled');
}
else
{
$('#'+field_id).attr('disabled', 'disabled');
+ // In case the field is rendered as a div containing several inputs (e.g. RedundancySettings)
+ $('#'+field_id+' :input').attr('disabled', 'disabled');
}
$('#'+field_id).trigger('update');
$('#'+field_id).trigger('validate');
diff --git a/setup/compiler.class.inc.php b/setup/compiler.class.inc.php
index 4c2f65809..3bd43fe53 100644
--- a/setup/compiler.class.inc.php
+++ b/setup/compiler.class.inc.php
@@ -670,6 +670,16 @@ EOF;
return $val == 'true' ? 'true' : 'false';
}
+ protected function GetMandatoryPropBoolean($oNode, $sTag)
+ {
+ $val = $oNode->GetChildText($sTag);
+ if (is_null($val))
+ {
+ throw new DOMFormatException("missing (or empty) mandatory tag '$sTag' under the tag '".$oNode->nodeName."'");
+ }
+ return $val == 'true' ? 'true' : 'false';
+ }
+
protected function GetPropNumber($oNode, $sTag, $nDefault = null)
{
$val = $oNode->GetChildText($sTag);
@@ -687,6 +697,16 @@ EOF;
return (string)$val;
}
+ protected function GetMandatoryPropNumber($oNode, $sTag)
+ {
+ $val = $oNode->GetChildText($sTag);
+ if (is_null($val))
+ {
+ throw new DOMFormatException("missing (or empty) mandatory tag '$sTag' under the tag '".$oNode->nodeName."'");
+ }
+ return (string)$val;
+ }
+
/**
* Adds quotes and escape characters
*/
@@ -1110,6 +1130,18 @@ EOF;
$aParameters['target_attcode'] = $this->GetMandatoryPropString($oField, 'target_attcode');
$aParameters['item_code'] = $this->GetMandatoryPropString($oField, 'item_code');
}
+ elseif ($sAttType == 'AttributeRedundancySettings')
+ {
+ $aParameters['sql'] = $this->GetMandatoryPropString($oField, 'sql');
+ $aParameters['relation_code'] = $this->GetMandatoryPropString($oField, 'relation_code');
+ $aParameters['from_class'] = $this->GetMandatoryPropString($oField, 'from_class');
+ $aParameters['neighbour_id'] = $this->GetMandatoryPropString($oField, 'neighbour_id');
+ $aParameters['enabled'] = $this->GetMandatoryPropBoolean($oField, 'enabled');
+ $aParameters['enabled_mode'] = $this->GetMandatoryPropString($oField, 'enabled_mode');
+ $aParameters['min_up'] = $this->GetMandatoryPropNumber($oField, 'min_up');
+ $aParameters['min_up_mode'] = $this->GetMandatoryPropString($oField, 'min_up_mode');
+ $aParameters['min_up_type'] = $this->GetMandatoryPropString($oField, 'min_up_type');
+ }
else
{
$aParameters['allowed_values'] = 'null'; // or "new ValueSetEnum('SELECT xxxx')"
@@ -1436,7 +1468,7 @@ EOF;
{
throw new DOMFormatException("Relation '$sRelationId/$sNeighbourId': both a query and and attribute have been specified... which one should be used?");
}
- $aData = array(
+ $aRelations[$sRelationId][$sNeighbourId] = array(
'_legacy_' => false,
'sDefinedInClass' => $sClass,
'sNeighbour' => $sNeighbourId,
@@ -1444,20 +1476,6 @@ EOF;
'sQueryUp' => $oNeighbour->GetChildText('query_up'),
'sAttribute' => $oNeighbour->GetChildText('attribute'),
);
-
- $oRedundancy = $oNeighbour->GetOptionalElement('redundancy');
- if ($oRedundancy)
- {
- $oEnabled = $oRedundancy->GetUniqueElement('enabled');
- $aData['bRedundancyEnabledValue'] = ($oEnabled->GetChildText('value', 'false') == 'true');
- $aData['sRedundancyEnabledMode'] = $oEnabled->GetChildText('mode', 'fixed');
- $oMinUp = $oRedundancy->GetUniqueElement('min_up');
- $aData['sRedundancyMinUpType'] = $oMinUp->GetChildText('type', 'count');
- $aData['iRedundancyMinUpValue'] = (int) $oMinUp->GetChildText('value', 1);
- $aData['sRedundancyMinUpMode'] = $oMinUp->GetChildText('mode', 'fixed');
- }
-
- $aRelations[$sRelationId][$sNeighbourId] = $aData;
}
}