From fb10982daae0c5733c6811c9fe69bd588c505a7b Mon Sep 17 00:00:00 2001 From: Thomas Casteleyn Date: Tue, 7 Oct 2025 12:19:23 +0200 Subject: [PATCH 1/5] feat(AttributeLinkedSet): Add CSV representation for templates and allow for export in Spreadsheet HTML format --- core/spreadsheetbulkexport.class.inc.php | 2 +- sources/Core/AttributeDefinition/AttributeLinkedSet.php | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/core/spreadsheetbulkexport.class.inc.php b/core/spreadsheetbulkexport.class.inc.php index 46f9ea95f7..67e9a68104 100644 --- a/core/spreadsheetbulkexport.class.inc.php +++ b/core/spreadsheetbulkexport.class.inc.php @@ -338,7 +338,7 @@ public function GetNextChunk(&$aStatus) $sField = $oObj->GetAsHTML($sAttCode, $this->bLocalizeOutput); $sData .= "$sField"; } - else if ($oAttDef instanceof AttributeTagSet) + else if ($oAttDef instanceof AttributeTagSet || $oAttDef instanceof AttributeLinkedSet) { $sField = utils::HtmlEntities($oObj->GetAsCSV($sAttCode, $this->bLocalizeOutput, '')); $sData .= "$sField"; diff --git a/sources/Core/AttributeDefinition/AttributeLinkedSet.php b/sources/Core/AttributeDefinition/AttributeLinkedSet.php index 0d47460dc8..0895f29e72 100644 --- a/sources/Core/AttributeDefinition/AttributeLinkedSet.php +++ b/sources/Core/AttributeDefinition/AttributeLinkedSet.php @@ -438,6 +438,7 @@ public function EnumTemplateVerbs() return array( '' => 'Plain text (unlocalized) representation', 'html' => 'HTML representation (unordered list)', + 'csv' => 'CSV representation (unordered list)', ); } @@ -479,6 +480,9 @@ public function GetForTemplate($value, $sVerb, $oHostObject = null, $bLocalize = case 'html': return ''; + case 'csv': + return $this->GetAsCSV($value, oHostObject: $oHostObject, bLocalize: $bLocalize); + default: throw new Exception("Unknown verb '$sVerb' for attribute ".$this->GetCode().' in class '.get_class($oHostObject)); } From bfa5c1c7a3072e0380834bbc97b87162a681c017 Mon Sep 17 00:00:00 2001 From: Thomas Casteleyn Date: Tue, 7 Oct 2025 13:21:37 +0200 Subject: [PATCH 2/5] Add helper method --- core/ormlinkset.class.inc.php | 22 ++++++++++++++++++++++ core/spreadsheetbulkexport.class.inc.php | 13 ++++++++++++- 2 files changed, 34 insertions(+), 1 deletion(-) diff --git a/core/ormlinkset.class.inc.php b/core/ormlinkset.class.inc.php index 872b6e3bfa..9241911f39 100644 --- a/core/ormlinkset.class.inc.php +++ b/core/ormlinkset.class.inc.php @@ -17,6 +17,8 @@ * You should have received a copy of the GNU Affero General Public License */ +use Combodo\iTop\Core\MetaModel\FriendlyNameType; + require_once('dbobjectiterator.php'); @@ -858,6 +860,26 @@ public function GetValues() return $aValues; } + /** + * List the objects by name + * @return array of long object friendly-names + * @throws \CoreException + */ + public function GetLabels() + { + $aLabels = array(); + foreach ($this->aPreserved as $sTagCode => $oTag) { + $aLabels[] = $oTag->GetName(FriendlyNameType::LONG); + } + foreach ($this->aAdded as $sTagCode => $oTag) { + $aLabels[] = $oTag->GetName(FriendlyNameType::LONG); + } + + sort($aLabels); + + return $aLabels; + } + /** * @return \DBObjectSet|null */ diff --git a/core/spreadsheetbulkexport.class.inc.php b/core/spreadsheetbulkexport.class.inc.php index 67e9a68104..445535cfc6 100644 --- a/core/spreadsheetbulkexport.class.inc.php +++ b/core/spreadsheetbulkexport.class.inc.php @@ -338,11 +338,22 @@ public function GetNextChunk(&$aStatus) $sField = $oObj->GetAsHTML($sAttCode, $this->bLocalizeOutput); $sData .= "$sField"; } - else if ($oAttDef instanceof AttributeTagSet || $oAttDef instanceof AttributeLinkedSet) + else if ($oAttDef instanceof AttributeTagSet) { $sField = utils::HtmlEntities($oObj->GetAsCSV($sAttCode, $this->bLocalizeOutput, '')); $sData .= "$sField"; } + else if ($oAttDef instanceof AttributeLinkedSet) + { + if ($this->bLocalizeOutput) { + /** @var \ormLinkSet $oLinkSet */ + $oLinkSet = $oObj->Get($sAttCode); + $sField = implode('
', $oLinkSet->GetLabels()); + } else { + $sField = $oObj->GetEditValue($sAttCode); + } + $sData .= "$sField"; + } else { $rawValue = $oObj->Get($sAttCode); if ($this->bLocalizeOutput) { From d0c369822bac2b3e67fac59e6b23b87240aa0277 Mon Sep 17 00:00:00 2001 From: Thomas Casteleyn Date: Tue, 7 Oct 2025 14:28:36 +0200 Subject: [PATCH 3/5] Add support for LinkedSetIndirect --- core/spreadsheetbulkexport.class.inc.php | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/core/spreadsheetbulkexport.class.inc.php b/core/spreadsheetbulkexport.class.inc.php index 445535cfc6..211006f228 100644 --- a/core/spreadsheetbulkexport.class.inc.php +++ b/core/spreadsheetbulkexport.class.inc.php @@ -343,6 +343,26 @@ public function GetNextChunk(&$aStatus) $sField = utils::HtmlEntities($oObj->GetAsCSV($sAttCode, $this->bLocalizeOutput, '')); $sData .= "$sField"; } + else if ($oAttDef instanceof AttributeLinkedSetIndirect) + { + if ($this->bLocalizeOutput) { + $sRemoteKey = $oAttDef->GetExtKeyToRemote(); + /** @var \AttributeExternalKey $oRemoteAttDef */ + $oRemoteAttDef = MetaModel::GetAttributeDef($oAttDef->GetLinkedClass(), $sRemoteKey); + /** @var \ormLinkSet $oLinkSet */ + $oLinkSet = $oObj->Get($sAttCode); + $aItems = array(); + foreach ($oLinkSet as $oItem) + { + $oObj = MetaModel::GetObject($oRemoteAttDef->GetTargetClass(), $oItem->Get($sRemoteKey)); + $aItems[] = $oObj->GetName(); + } + $sField .= implode('
', $aItems); + } else { + $sField = $oObj->GetEditValue($sAttCode); + } + $sData .= "$sField"; + } else if ($oAttDef instanceof AttributeLinkedSet) { if ($this->bLocalizeOutput) { From 5245b7d3743ebb054221ecb054274920a4ef1929 Mon Sep 17 00:00:00 2001 From: Thomas Casteleyn Date: Tue, 7 Oct 2025 14:31:37 +0200 Subject: [PATCH 4/5] Align labels --- core/ormlinkset.class.inc.php | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/core/ormlinkset.class.inc.php b/core/ormlinkset.class.inc.php index 9241911f39..985c2015fa 100644 --- a/core/ormlinkset.class.inc.php +++ b/core/ormlinkset.class.inc.php @@ -17,8 +17,6 @@ * You should have received a copy of the GNU Affero General Public License */ -use Combodo\iTop\Core\MetaModel\FriendlyNameType; - require_once('dbobjectiterator.php'); @@ -869,10 +867,10 @@ public function GetLabels() { $aLabels = array(); foreach ($this->aPreserved as $sTagCode => $oTag) { - $aLabels[] = $oTag->GetName(FriendlyNameType::LONG); + $aLabels[] = $oTag->GetName(); } foreach ($this->aAdded as $sTagCode => $oTag) { - $aLabels[] = $oTag->GetName(FriendlyNameType::LONG); + $aLabels[] = $oTag->GetName(); } sort($aLabels); From ba752ffbf5867382ed50af926f38bc79b9214a1c Mon Sep 17 00:00:00 2001 From: Thomas Casteleyn Date: Mon, 27 Oct 2025 11:35:41 +0100 Subject: [PATCH 5/5] Improve ormLinkSet::GetLabels --- core/ormlinkset.class.inc.php | 14 +++++++++----- core/spreadsheetbulkexport.class.inc.php | 20 -------------------- 2 files changed, 9 insertions(+), 25 deletions(-) diff --git a/core/ormlinkset.class.inc.php b/core/ormlinkset.class.inc.php index 985c2015fa..08282ca942 100644 --- a/core/ormlinkset.class.inc.php +++ b/core/ormlinkset.class.inc.php @@ -860,17 +860,21 @@ public function GetValues() /** * List the objects by name - * @return array of long object friendly-names + * @return array of linked object friendly-names * @throws \CoreException */ public function GetLabels() { + /** @var \AttributeLinkedSet|\AttributeLinkedSetIndirect $oAttDef */ + $oAttDef = MetaModel::GetAttributeDef($this->sHostClass, $this->sAttCode); + $sNameField = ($oAttDef->IsIndirect() ? $oAttDef->GetExtKeyToRemote().'_' : '').'friendlyname'; + $aLabels = array(); - foreach ($this->aPreserved as $sTagCode => $oTag) { - $aLabels[] = $oTag->GetName(); + foreach ($this->aPreserved as $oLink) { + $aLabels[] = $oLink->Get($sNameField); } - foreach ($this->aAdded as $sTagCode => $oTag) { - $aLabels[] = $oTag->GetName(); + foreach ($this->aAdded as $oLink) { + $aLabels[] = $oLink->Get($sNameField); } sort($aLabels); diff --git a/core/spreadsheetbulkexport.class.inc.php b/core/spreadsheetbulkexport.class.inc.php index 211006f228..445535cfc6 100644 --- a/core/spreadsheetbulkexport.class.inc.php +++ b/core/spreadsheetbulkexport.class.inc.php @@ -343,26 +343,6 @@ public function GetNextChunk(&$aStatus) $sField = utils::HtmlEntities($oObj->GetAsCSV($sAttCode, $this->bLocalizeOutput, '')); $sData .= "$sField"; } - else if ($oAttDef instanceof AttributeLinkedSetIndirect) - { - if ($this->bLocalizeOutput) { - $sRemoteKey = $oAttDef->GetExtKeyToRemote(); - /** @var \AttributeExternalKey $oRemoteAttDef */ - $oRemoteAttDef = MetaModel::GetAttributeDef($oAttDef->GetLinkedClass(), $sRemoteKey); - /** @var \ormLinkSet $oLinkSet */ - $oLinkSet = $oObj->Get($sAttCode); - $aItems = array(); - foreach ($oLinkSet as $oItem) - { - $oObj = MetaModel::GetObject($oRemoteAttDef->GetTargetClass(), $oItem->Get($sRemoteKey)); - $aItems[] = $oObj->GetName(); - } - $sField .= implode('
', $aItems); - } else { - $sField = $oObj->GetEditValue($sAttCode); - } - $sData .= "$sField"; - } else if ($oAttDef instanceof AttributeLinkedSet) { if ($this->bLocalizeOutput) {