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 ''; + + 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()); + } +}