diff --git a/application/applicationextension.inc.php b/application/applicationextension.inc.php
index 84311db42..6ca36e94b 100644
--- a/application/applicationextension.inc.php
+++ b/application/applicationextension.inc.php
@@ -1165,6 +1165,14 @@ class RestUtils
}
$value = DBObjectSet::FromArray($sLnkClass, $aLinks);
}
+ elseif ($oAttDef instanceof AttributeTagSet)
+ {
+ if (!is_array($value))
+ {
+ throw new Exception("A tag set must be defined by an array of objects");
+ }
+ $value = $oAttDef->FromJSONToValue($value);
+ }
else
{
$value = $oAttDef->FromJSONToValue($value);
diff --git a/core/attributedef.class.inc.php b/core/attributedef.class.inc.php
index 02f2d0f41..fa9c1fc45 100644
--- a/core/attributedef.class.inc.php
+++ b/core/attributedef.class.inc.php
@@ -31,6 +31,7 @@ require_once('ormstopwatch.class.inc.php');
require_once('ormpassword.class.inc.php');
require_once('ormcaselog.class.inc.php');
require_once('ormlinkset.class.inc.php');
+require_once('ormtagset.class.inc.php');
require_once('htmlsanitizer.class.inc.php');
require_once(APPROOT.'sources/autoload.php');
require_once('customfieldshandler.class.inc.php');
@@ -4210,7 +4211,7 @@ class AttributeEnum extends AttributeString
{
if ($oFormField === null)
{
- // TODO : We should check $this->Get('display_style') and create a Radio / Select / ... regarding its value
+ // Later : We should check $this->Get('display_style') and create a Radio / Select / ... regarding its value
$sFormFieldClass = static::GetFormFieldClass();
$oFormField = new $sFormFieldClass($this->GetCode());
}
@@ -5297,7 +5298,7 @@ class AttributeExternalKey extends AttributeDBFieldVoid
{
if ($oFormField === null)
{
- // TODO : We should check $this->Get('display_style') and create a Radio / Select / ... regarding its value
+ // Later : We should check $this->Get('display_style') and create a Radio / Select / ... regarding its value
$sFormFieldClass = static::GetFormFieldClass();
$oFormField = new $sFormFieldClass($this->GetCode());
}
@@ -5363,7 +5364,7 @@ class AttributeHierarchicalKey extends AttributeExternalKey
unset($aParams[$idx]);
$idx = array_search('jointype', $aParams);
unset($aParams[$idx]);
- return $aParams; // TODO: mettre les bons parametres ici !!
+ return $aParams; // Later: mettre les bons parametres ici !!
}
public function GetEditClass() {return "ExtKey";}
@@ -5899,23 +5900,216 @@ class AttributeExternalField extends AttributeDefinition
*/
class AttributeTagSet extends AttributeString
{
- //TODO SQL type length (nb of tags per record, max tag length)
- //TODO implement ??
- //TODO specific filters
- public function RequiresIndex()
- {
- return true;
- }
+ public function GetEditClass() {return "TagSet";}
- public function RequiresFullTextIndex()
- {
- return true;
- }
+ protected function GetSQLCol($bFullSpec = false)
+ {
+ return 'VARCHAR(1024)'
+ .CMDBSource::GetSqlStringColumnDefinition()
+ .($bFullSpec ? $this->GetSQLColSpec() : '');
+ }
+
+ public function RequiresIndex() {return true;}
+
+ public function RequiresFullTextIndex() {return true;}
+
+ public function Equals($val1, $val2) {
+ if (($val1 instanceof ormTagSet) && ($val2 instanceof ormTagSet))
+ {
+ return $val1->Equals($val2);
+ }
+ return ($val1 == $val2);
+ }
+
+ /**
+ * force an allowed value (type conversion and possibly forces a value as mySQL would do upon writing!
+ *
+ * @param $proposedValue
+ * @param $oHostObj
+ *
+ * @return mixed
+ * @throws \Exception
+ */
+ public function MakeRealValue($proposedValue, $oHostObj)
+ {
+ $oTagSet = new ormTagSet($this->GetHostClass(), $this->GetCode());
+ if (is_string($proposedValue) && !empty($proposedValue))
+ {
+ $aTagCodes = explode(' ', "$proposedValue");
+ $oTagSet->SetValue($aTagCodes);
+ }
+ return $oTagSet;
+ }
+
+ public function ScalarToSQL($value)
+ {
+ if (empty($value))
+ {
+ return '';
+ }
+ if ($value instanceof ormTagSet)
+ {
+ $aValues = $value->GetValue();
+ return implode(' ', $aValues);
+ }
+ throw new CoreWarning('Expected the attribute value to be a string', array('found_type' => gettype($value), 'value' => $value, 'class' => $this->GetHostClass(), 'attribute' => $this->GetCode()));
+ }
+
+ /**
+ * @param $value
+ * @param \DBObject $oHostObject
+ * @param bool $bLocalize
+ *
+ * @return string|null
+ *
+ * @throws \CoreException
+ */
+ public function GetAsHTML($value, $oHostObject = null, $bLocalize = true)
+ {
+ if (is_object($value) && ($value instanceof ormTagSet))
+ {
+ $aValues = $value->GetValue();
+ return implode(' ', $aValues);
+ }
+ return null;
+ }
+
+ /**
+ * @param $value
+ * @param \DBObject $oHostObject
+ * @param bool $bLocalize
+ *
+ * @return string
+ *
+ */
+ public function GetAsXML($value, $oHostObject = null, $bLocalize = true)
+ {
+ if (is_object($value) && ($value instanceof ormTagSet))
+ {
+ $sRes = "\n";
+ $aValues = $value->GetValue();
+ if (!empty($aValuess))
+ {
+ $sRes .= ''.implode('', $aValues).'';
+ }
+ $sRes .= "\n";
+ }
+ else
+ {
+ $sRes = '';
+ }
+ return $sRes;
+ }
+
+ /**
+ * @param $value
+ * @param string $sSeparator
+ * @param string $sTextQualifier
+ * @param \DBObject $oHostObject
+ * @param bool $bLocalize
+ * @param bool $bConvertToPlainText
+ *
+ * @return mixed|string
+ * @throws \CoreException
+ */
+ public function GetAsCSV($value, $sSeparator = ',', $sTextQualifier = '"', $oHostObject = null, $bLocalize = true, $bConvertToPlainText = false)
+ {
+ $sSepItem = MetaModel::GetConfig()->Get('link_set_item_separator');
+
+ if (is_object($value) && ($value instanceof ormTagSet))
+ {
+ $aValues = $value->GetValue();
+ $sRes = implode($sSepItem, $aValues);
+ }
+ else
+ {
+ $sRes = '';
+ }
+ $sRes = str_replace($sTextQualifier, $sTextQualifier.$sTextQualifier, $sRes);
+ $sRes = $sTextQualifier.$sRes.$sTextQualifier;
+ return $sRes;
+ }
+
+ /**
+ * List the available verbs for 'GetForTemplate'
+ */
+ public function EnumTemplateVerbs()
+ {
+ return array(
+ '' => 'Plain text (unlocalized) representation',
+ 'html' => 'HTML representation (unordered list)',
+ );
+ }
+
+ /**
+ * Get various representations of the value, for insertion into a template (e.g. in Notifications)
+ *
+ * @param mixed $value The current value of the field
+ * @param string $sVerb The verb specifying the representation of the value
+ * @param DBObject $oHostObject The object
+ * @param bool $bLocalize Whether or not to localize the value
+ *
+ * @return string
+ * @throws \Exception
+ */
+ public function GetForTemplate($value, $sVerb, $oHostObject = null, $bLocalize = true)
+ {
+ if (is_object($value) && ($value instanceof ormTagSet))
+ {
+ $aValues = $value->GetValue();
+
+ switch ($sVerb)
+ {
+ case '':
+ return implode("\n", $aValues);
+
+ case 'html':
+ return '
- '.implode("
- ", $aValues).'
';
+
+ default:
+ throw new Exception("Unknown verb '$sVerb' for attribute ".$this->GetCode().' in class '.get_class($oHostObject));
+ }
+ }
+ throw new CoreUnexpectedValue("Bad value '$value' for attribute ".$this->GetCode().' in class '.get_class($oHostObject));
+ }
+
+ /**
+ * Helper to get a value that will be JSON encoded
+ * The operation is the opposite to FromJSONToValue
+ *
+ * @param \ormTagSet $value
+ *
+ * @return array
+ * @throws \CoreException
+ */
+ public function GetForJSON($value)
+ {
+ $aRet = array();
+ if (is_object($value) && ($value instanceof ormTagSet))
+ {
+ $aRet = $value->GetValue();
+ }
+ return $aRet;
+ }
+
+ /**
+ * Helper to form a value, given JSON decoded data
+ * The operation is the opposite to GetForJSON
+ *
+ * @param $json
+ *
+ * @return \ormTagSet
+ * @throws \CoreException
+ * @throws \CoreUnexpectedValue
+ * @throws \Exception
+ */
+ public function FromJSONToValue($json)
+ {
+ $oSet = new ormTagSet($this->GetHostClass(), $this->GetCode());
+ $oSet->SetValue($json);
+ return $oSet;
+ }
- public function IsNullAllowed()
- {
- return true;
- }
}
/**
diff --git a/core/metamodel.class.php b/core/metamodel.class.php
index 06cd005eb..3c5b06e41 100644
--- a/core/metamodel.class.php
+++ b/core/metamodel.class.php
@@ -7105,6 +7105,12 @@ abstract class MetaModel
return $sRet;
}
+ public static function GetTagDataClass($sClass, $sAttCode)
+ {
+ $sTagSuffix = $sClass.'_'.$sAttCode;
+ return 'TagSetFieldDataFor_'.$sTagSuffix;
+ }
+
}
diff --git a/core/ormtagset.class.inc.php b/core/ormtagset.class.inc.php
new file mode 100644
index 000000000..8a617c5e9
--- /dev/null
+++ b/core/ormtagset.class.inc.php
@@ -0,0 +1,294 @@
+
+ *
+ */
+
+/**
+ * Created by PhpStorm.
+ * Date: 24/08/2018
+ * Time: 14:35
+ */
+
+require_once('dbobjectiterator.php');
+
+final class ormTagSet
+{
+ private $sClass; // class of the tag
+ private $sAttCode; // attcode of the tag
+
+ private $aAllowedTags;
+ private $oOriginalSet;
+ private $aOriginalObjects = null;
+
+ /**
+ * @var bool
+ */
+ private $bHasDelta = false;
+
+ /**
+ * 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 int[] Removed items
+ */
+ private $aRemoved = array();
+
+ /**
+ * __toString magical function overload.
+ */
+ public function __toString()
+ {
+ return '';
+ }
+
+ /**
+ * ormTagSet constructor.
+ *
+ * @param string $sClass
+ * @param string $sAttCode
+ * @param DBObjectSet|null $oOriginalSet
+ *
+ * @throws \Exception
+ */
+ public function __construct($sClass, $sAttCode, DBObjectSet $oOriginalSet = null)
+ {
+ $this->sAttCode = $sAttCode;
+ $this->oOriginalSet = $oOriginalSet ? clone $oOriginalSet : null;
+
+ $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}");
+ }
+
+ $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;
+ }
+
+ /**
+ * @return array of tag codes
+ */
+ public function GetValue()
+ {
+ $aValue = array();
+ foreach ($this->aPreserved as $oTag)
+ {
+ try
+ {
+ $aValue[] = $oTag->Get('tag_code');
+ } catch (CoreException $e)
+ {
+ IssueLog::Error($e->getMessage());
+ }
+ }
+ foreach ($this->aAdded as $oTag)
+ {
+ try
+ {
+ $aValue[] = $oTag->Get('tag_code');
+ } catch (CoreException $e)
+ {
+ IssueLog::Error($e->getMessage());
+ }
+ }
+
+ sort($aValue);
+
+ return $aValue;
+ }
+
+ /**
+ * @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
+ *
+ * @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();
+ }
+
+ private function IsTagInList($aTagList, $sTagCode)
+ {
+ foreach ($aTagList as $oTag)
+ {
+ $sCode = $oTag->Get('tag_code');
+ if ($sCode === $sTagCode)
+ {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ 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()
+ {
+ if (!$this->aAllowedTags)
+ {
+ $oSearch = new DBObjectSearch($this->GetTagDataClass());
+ $oSearch->AddCondition('tag_class', $this->sClass);
+ $oSearch->AddCondition('tag_attcode', $this->sAttCode);
+ $oSet = new DBObjectSet($oSearch);
+ $this->aAllowedTags = $oSet->ToArray();
+ }
+ return $this->aAllowedTags;
+ }
+
+ /**
+ * 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());
+ }
+
+}
\ No newline at end of file
diff --git a/core/tagsetfield.class.inc.php b/core/tagsetfield.class.inc.php
index a8a785fb9..5a583de36 100644
--- a/core/tagsetfield.class.inc.php
+++ b/core/tagsetfield.class.inc.php
@@ -27,55 +27,69 @@
*/
abstract class TagSetFieldData extends cmdbAbstractObject
{
- 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',
- );
+ 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" => false,
- "depends_on" => array()
- )));
- MetaModel::Init_AddAttribute(new AttributeBoolean("is_default", array(
- "allowed_values" => null,
- "sql" => "is_default",
- "default_value" => false,
- "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_AddAttribute(new AttributeBoolean("is_default", array(
+ "allowed_values" => null,
+ "sql" => "is_default",
+ "default_value" => false,
+ "is_null_allowed" => false,
+ "depends_on" => array()
+ )));
- MetaModel::Init_SetZListItems('details', array('tag_code', 'tag_label', 'tag_description', 'is_default'));
- MetaModel::Init_SetZListItems('standard_search', array('tag_code', 'tag_label', 'tag_description', 'is_default'));
- MetaModel::Init_SetZListItems('list', array('tag_code', 'tag_label', 'tag_description', 'is_default'));
- }
+ MetaModel::Init_SetZListItems('details', array('tag_code', 'tag_label', 'tag_description', 'is_default'));
+ MetaModel::Init_SetZListItems('standard_search', array('tag_code', 'tag_label', 'tag_description', 'is_default'));
+ MetaModel::Init_SetZListItems('list', array('tag_code', 'tag_label', 'tag_description', 'is_default'));
+ }
}
\ No newline at end of file
diff --git a/datamodels/2.x/itop-tickets/datamodel.itop-tickets.xml b/datamodels/2.x/itop-tickets/datamodel.itop-tickets.xml
index 74f6d7e55..4bfc212eb 100755
--- a/datamodels/2.x/itop-tickets/datamodel.itop-tickets.xml
+++ b/datamodels/2.x/itop-tickets/datamodel.itop-tickets.xml
@@ -198,6 +198,8 @@
tagfield
+
+ true
diff --git a/datamodels/2.x/itop-tickets/en.dict.itop-tickets.php b/datamodels/2.x/itop-tickets/en.dict.itop-tickets.php
index 09d113360..d1704a9e9 100755
--- a/datamodels/2.x/itop-tickets/en.dict.itop-tickets.php
+++ b/datamodels/2.x/itop-tickets/en.dict.itop-tickets.php
@@ -74,8 +74,9 @@ Dict::Add('EN US', 'English', 'English', array(
'Class:Ticket/Attribute:close_date+' => '',
'Class:Ticket/Attribute:private_log' => 'Private log',
'Class:Ticket/Attribute:private_log+' => '',
- 'Class:Ticket/Attribute:contacts_list' => 'Contacts',
+ 'Class:Ticket/Attribute:contacts_list' => 'Contacts',
'Class:Ticket/Attribute:contacts_list+' => 'All the contacts linked to this ticket',
+ 'Class:Ticket/Attribute:tagfield' => 'Tag Test',
'Class:Ticket/Attribute:functionalcis_list' => 'CIs',
'Class:Ticket/Attribute:functionalcis_list+' => 'All the configuration items impacted by this ticket. Items marked as "Computed" have been automatically marked as impacted. Items marked as "Not impacted" are excluded from the impact.',
'Class:Ticket/Attribute:workorders_list' => 'Work orders',
diff --git a/dictionaries/en.dictionary.itop.core.php b/dictionaries/en.dictionary.itop.core.php
index 8aefdc5b2..3664f03bc 100644
--- a/dictionaries/en.dictionary.itop.core.php
+++ b/dictionaries/en.dictionary.itop.core.php
@@ -34,7 +34,10 @@ Dict::Add('EN US', 'English', 'English', array(
'Core:AttributeLinkedSet' => 'Array of objects',
'Core:AttributeLinkedSet+' => 'Any kind of objects of the same class or subclass',
- 'Core:AttributeLinkedSetIndirect' => 'Array of objects (N-N)',
+ 'Core:AttributeTagSet' => 'List of tags',
+ 'Core:AttributeTagSet+' => '',
+
+ 'Core:AttributeLinkedSetIndirect' => 'Array of objects (N-N)',
'Core:AttributeLinkedSetIndirect+' => 'Any kind of objects [subclass] of the same class',
'Core:AttributeInteger' => 'Integer',
diff --git a/dictionaries/fr.dictionary.itop.core.php b/dictionaries/fr.dictionary.itop.core.php
index bb6ae6ae2..ffd70fcd5 100644
--- a/dictionaries/fr.dictionary.itop.core.php
+++ b/dictionaries/fr.dictionary.itop.core.php
@@ -426,7 +426,9 @@ Dict::Add('FR FR', 'French', 'Français', array(
'Class:appUserPreferences/Attribute:preferences+' => '',
'Core:AttributeLinkedSet' => 'Objets liés (1-n)',
'Core:AttributeLinkedSet+' => 'Liste d\'objets d\'une classe donnée et pointant sur l\'objet courant',
- 'Core:AttributeLinkedSetIndirect' => 'Objets liés (1-n)',
+ 'Core:AttributeTagSet' => 'Liste d\'étiquettes',
+ 'Core:AttributeTagSet+' => '',
+ 'Core:AttributeLinkedSetIndirect' => 'Objets liés (1-n)',
'Core:AttributeLinkedSetIndirect+' => 'Liste d\'objets d\'une classe donnée et liés à l\'objet courant via une classe intermédiaire',
'Core:AttributeInteger' => 'Nombre entier',
'Core:AttributeInteger+' => 'Valeur numérique entière',
diff --git a/setup/compiler.class.inc.php b/setup/compiler.class.inc.php
index 8adb472ee..1099bde05 100644
--- a/setup/compiler.class.inc.php
+++ b/setup/compiler.class.inc.php
@@ -1388,19 +1388,25 @@ EOF
{
$aParameters['handler_class'] = $this->GetMandatoryPropString($oField, 'handler_class');
}
- else
+ elseif ($sAttType == 'AttributeTagSet')
+ {
+ $aTagFieldsInfo[] = $sAttCode;
+ $aParameters['allowed_values'] = 'null'; // or "new ValueSetEnum('SELECT xxxx')"
+ $aParameters['sql'] = $this->GetMandatoryPropString($oField, 'sql');
+ $aParameters['is_null_allowed'] = $this->GetPropBoolean($oField, 'is_null_allowed', false);
+ $aParameters['depends_on'] = $sDependencies;
+ $aParameters['default_value'] = $this->GetPropString($oField, 'default_value', '');
+ }
+ else
{
- $aParameters['allowed_values'] = 'null'; // or "new ValueSetEnum('SELECT xxxx')"
- $aParameters['sql'] = $this->GetMandatoryPropString($oField, 'sql');
- $aParameters['default_value'] = $this->GetPropString($oField, 'default_value', '');
- $aParameters['is_null_allowed'] = $this->GetPropBoolean($oField, 'is_null_allowed', false);
- $aParameters['depends_on'] = $sDependencies;
+ $aParameters['allowed_values'] = 'null'; // or "new ValueSetEnum('SELECT xxxx')"
+ $aParameters['sql'] = $this->GetMandatoryPropString($oField, 'sql');
+ $aParameters['default_value'] = $this->GetPropString($oField, 'default_value', '');
+ $aParameters['is_null_allowed'] = $this->GetPropBoolean($oField, 'is_null_allowed', false);
+ $aParameters['depends_on'] = $sDependencies;
}
- if ($sAttType == 'AttributeTagSet')
- {
- $aTagFieldsInfo[] = $sAttCode;
- }
+
// Optional parameters (more for historical reasons)
// Added if present...
diff --git a/test/ItopDataTestCase.php b/test/ItopDataTestCase.php
index 0123f3bae..58c42915b 100644
--- a/test/ItopDataTestCase.php
+++ b/test/ItopDataTestCase.php
@@ -171,7 +171,35 @@ class ItopDataTestCase extends ItopTestCase
return $oTicket;
}
- /**
+ /**
+ * Create a Ticket in database
+ *
+ * @param string $sClass
+ * @param string $sAttCode
+ * @param string $sTagCode
+ * @param string $sTagLabel
+ * @param string $sTagDescription
+ *
+ * @return \TagSetFieldData
+ * @throws \CoreException
+ */
+ protected function CreateTagData($sClass, $sAttCode, $sTagCode, $sTagLabel, $sTagDescription = '')
+ {
+ $sTagClass = MetaModel::GetTagDataClass($sClass, $sAttCode);
+ $oTagData = self::createObject($sTagClass, array(
+ 'tag_code' => $sTagCode,
+ 'tag_label' => $sTagLabel,
+ 'tag_class' => $sClass,
+ 'tag_attcode' => $sAttCode,
+ 'tag_description' => $sTagDescription,
+ ));
+ $this->debug("\nCreated {$oTagData->Get('tag_code')} ({$oTagData->Get('tag_label')})");
+
+ /** @var \TagSetFieldData $oTagData */
+ return $oTagData;
+ }
+
+ /**
* Create a UserRequest in database
*
* @param int $iNum
diff --git a/test/core/ormTagSetTest.php b/test/core/ormTagSetTest.php
new file mode 100644
index 000000000..b1bf7fae1
--- /dev/null
+++ b/test/core/ormTagSetTest.php
@@ -0,0 +1,155 @@
+
+ *
+ */
+
+/**
+ * Created by PhpStorm.
+ * User: Eric
+ * Date: 27/08/2018
+ * Time: 17:26
+ */
+
+namespace Combodo\iTop\Test\UnitTest\Core;
+
+use Combodo\iTop\Test\UnitTest\ItopDataTestCase;
+use Exception;
+use ormTagSet;
+
+/**
+ * Tests of the ormTagSet class
+ *
+ * @runTestsInSeparateProcesses
+ * @preserveGlobalState disabled
+ * @backupGlobals disabled
+ */class ormTagSetTest extends ItopDataTestCase
+{
+
+ /**
+ * @throws Exception
+ */
+ protected function setUp()
+ {
+ parent::setUp();
+ }
+
+ public function testGetTagDataClass()
+ {
+ $oTagSet = new ormTagSet('Ticket', 'tagfield');
+ static::assertEquals($oTagSet->GetTagDataClass(), 'TagSetFieldDataFor_Ticket_tagfield');
+ }
+
+ public function testGetValue()
+ {
+ $this->CreateTagData('Ticket', 'tagfield', 'tag1', 'First');
+ $this->CreateTagData('Ticket', 'tagfield', 'tag2', 'Second');
+
+ $oTagSet = new ormTagSet('Ticket', 'tagfield');
+ static::assertEquals($oTagSet->GetValue(), array());
+
+ $oTagSet->AddTag('tag1');
+ static::assertEquals($oTagSet->GetValue(), array('tag1'));
+
+ $oTagSet->AddTag('tag2');
+ static::assertEquals($oTagSet->GetValue(), array('tag1', 'tag2'));
+ }
+
+ public function testAddTag()
+ {
+ $this->CreateTagData('Ticket', 'tagfield', 'tag1', 'First');
+ $this->CreateTagData('Ticket', 'tagfield', 'tag2', 'Second');
+
+ $oTagSet = new ormTagSet('Ticket', 'tagfield');
+
+ $oTagSet->AddTag('tag1');
+ static::assertEquals($oTagSet->GetValue(), array('tag1'));
+
+ $oTagSet->SetValue(array('tag1', 'tag2'));
+ static::assertEquals($oTagSet->GetValue(), array('tag1', 'tag2'));
+
+ $oTagSet->RemoveTag('tag1');
+ static::assertEquals($oTagSet->GetValue(), array('tag2'));
+
+ $oTagSet->AddTag('tag1');
+ static::assertEquals($oTagSet->GetValue(), array('tag1', 'tag2'));
+ }
+
+ public function testEquals()
+ {
+ $this->CreateTagData('Ticket', 'tagfield', 'tag1', 'First');
+ $this->CreateTagData('Ticket', 'tagfield', 'tag2', 'Second');
+
+ $oTagSet1 = new ormTagSet('Ticket', 'tagfield');
+ $oTagSet1->AddTag('tag1');
+ static::assertTrue($oTagSet1->Equals($oTagSet1));
+
+ $oTagSet2 = new ormTagSet('Ticket', 'tagfield');
+ $oTagSet2->SetValue(array('tag1'));
+
+ static::assertTrue($oTagSet1->Equals($oTagSet2));
+
+ $oTagSet1->AddTag('tag2');
+ static::assertFalse($oTagSet1->Equals($oTagSet2));
+ }
+
+ public function testSetValue()
+ {
+ $this->CreateTagData('Ticket', 'tagfield', 'tag1', 'First');
+ $this->CreateTagData('Ticket', 'tagfield', 'tag2', 'Second');
+
+ $oTagSet = new ormTagSet('Ticket', 'tagfield');
+
+ $oTagSet->SetValue(array('tag1'));
+ static::assertEquals($oTagSet->GetValue(), array('tag1'));
+
+ $oTagSet->SetValue(array('tag1', 'tag2'));
+ static::assertEquals($oTagSet->GetValue(), array('tag1', 'tag2'));
+
+ }
+
+ public function testRemoveTag()
+ {
+ $this->CreateTagData('Ticket', 'tagfield', 'tag1', 'First');
+ $this->CreateTagData('Ticket', 'tagfield', 'tag2', 'Second');
+
+ $oTagSet = new ormTagSet('Ticket', 'tagfield');
+ $oTagSet->RemoveTag('tag_unknown');
+ static::assertEquals($oTagSet->GetValue(), array());
+
+ $oTagSet->SetValue(array('tag1'));
+ $oTagSet->RemoveTag('tag_unknown');
+ static::assertEquals($oTagSet->GetValue(), array('tag1'));
+
+ $oTagSet->SetValue(array('tag1', 'tag2'));
+ $oTagSet->RemoveTag('tag1');
+ static::assertEquals($oTagSet->GetValue(), array('tag2'));
+
+ $oTagSet->AddTag('tag1');
+ static::assertEquals($oTagSet->GetValue(), array('tag1', 'tag2'));
+
+ $oTagSet->RemoveTag('tag1');
+ static::assertEquals($oTagSet->GetValue(), array('tag2'));
+
+ $oTagSet->RemoveTag('tag1');
+ static::assertEquals($oTagSet->GetValue(), array('tag2'));
+
+ $oTagSet->RemoveTag('tag2');
+ static::assertEquals($oTagSet->GetValue(), array());
+ }
+}