Skip to content

N°8860 - Régression Export CSV clé externe, plus de critères#971

Open
bdalsass wants to merge 2 commits into
developfrom
feature/8860-regression-export-csv-external-key
Open

N°8860 - Régression Export CSV clé externe, plus de critères#971
bdalsass wants to merge 2 commits into
developfrom
feature/8860-regression-export-csv-external-key

Conversation

@bdalsass

@bdalsass bdalsass commented Jul 15, 2026

Copy link
Copy Markdown
Contributor

Base information

Question Answer
Related to a SourceForge thread / Another PR / A GitHub Issue / Combodo ticket? https://support.combodo.com/pages/UI.php?operation=details&class=Bug&id=8860#ObjectProperties=tab_ClassBugAttributebugs_list
Type of change? Bug fix

Symptom (bug) / Objective (enhancement)

Export CSV do not handle correctly attributes external key

Reproduction procedure (bug)

Export location, organization field is not recognized as external key attribute

image

Checklist before requesting a review

  • [X ] I have performed a self-review of my code
  • [X ] I have tested all changes I made on an iTop instance
  • I have added a unit test, otherwise I have explained why I couldn't
  • [X ] Is the PR clear and detailed enough so anyone can understand without digging in the code?

Copilot AI review requested due to automatic review settings July 15, 2026 07:45
@CombodoApplicationsAccount CombodoApplicationsAccount added the internal Work made by Combodo label Jul 15, 2026
@bdalsass bdalsass added this to the 3.3.0 milestone Jul 15, 2026

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Fixes CSV export handling for namespaced external-key and stopwatch attributes.

Changes:

  • Uses fully qualified attribute class constants in subtype detection.
  • Restores external-key export criteria generation.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@bdalsass
bdalsass requested review from eespie and steffunky July 15, 2026 07:46
@greptile-apps

greptile-apps Bot commented Jul 15, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This fix resolves a regression in CSV export where external-key attributes (e.g., location, organization fields) were not recognized and their sub-criteria were not surfaced in the export field selector. The root cause was that get_class() returns the fully-qualified class name (e.g., Combodo\iTop\Core\AttributeDefinition\AttributeExternalKey), but the switch cases compared against bare strings ('AttributeExternalKey') that stopped matching once these classes were moved to namespaced files.

  • Three use declarations are added for AttributeExternalKey, AttributeHierarchicalKey, and AttributeStopWatch, and the corresponding switch case strings are replaced with ::class constants that resolve to the correct FQCNs.
  • The change affects only the GetSubAttributes method in TabularBulkExport, which is the parent of the CSV, Excel, HTML, and Spreadsheet export classes — all of them benefit from this fix.

Confidence Score: 5/5

Safe to merge — the change is narrowly scoped, targets the exact namespace mismatch that caused the regression, and has no side effects on unrelated export logic.

The diff is minimal (three use imports and three string-to-::class substitutions). The ::class constants resolve correctly to the FQCNs that get_class() returns, which is exactly what the switch needs. All four tabular-export subclasses inherit GetSubAttributes and will benefit automatically.

No files require special attention — the single changed file is self-contained and the logic remains straightforward.

Important Files Changed

Filename Overview
core/tabularbulkexport.class.inc.php Replaces bare string class name literals with ::class constants (and adds matching use declarations) in the GetSubAttributes switch, fixing the namespace mismatch that prevented external-key columns from being recognized during CSV export.

Sequence Diagram

%%{init: {'theme': 'neutral'}}%%
sequenceDiagram
    participant UI as Export UI
    participant TBE as TabularBulkExport
    participant MM as MetaModel

    UI->>TBE: GetInteractiveFieldsWidget()
    TBE->>MM: ListAttributeDefs(sClass)
    MM-->>TBE: oAttDef instances (namespaced FQCNs)
    TBE->>TBE: GetSubAttributes(sClass, sAttCode, oAttDef)
    Note over TBE: switch(get_class($oAttDef))<br/>Before: 'AttributeExternalKey' never matched FQCN<br/>After: AttributeExternalKey::class matches correctly
    alt FQCN matches case
        TBE->>MM: GetAttributeDef / GetTargetClass
        MM-->>TBE: remote class info
        TBE->>TBE: Build sub-attributes (id, friendlyname, ext fields)
    end
    TBE-->>UI: aAllFields with external key sub-attributes
    UI->>UI: Render field selector with criteria columns
Loading
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
sequenceDiagram
    participant UI as Export UI
    participant TBE as TabularBulkExport
    participant MM as MetaModel

    UI->>TBE: GetInteractiveFieldsWidget()
    TBE->>MM: ListAttributeDefs(sClass)
    MM-->>TBE: oAttDef instances (namespaced FQCNs)
    TBE->>TBE: GetSubAttributes(sClass, sAttCode, oAttDef)
    Note over TBE: switch(get_class($oAttDef))<br/>Before: 'AttributeExternalKey' never matched FQCN<br/>After: AttributeExternalKey::class matches correctly
    alt FQCN matches case
        TBE->>MM: GetAttributeDef / GetTargetClass
        MM-->>TBE: remote class info
        TBE->>TBE: Build sub-attributes (id, friendlyname, ext fields)
    end
    TBE-->>UI: aAllFields with external key sub-attributes
    UI->>UI: Render field selector with criteria columns
Loading

Comments Outside Diff (1)

  1. core/tabularbulkexport.class.inc.php, line 93-98 (link)

    P2 get_class() performs an exact class-name match, so any third-party subclass of AttributeExternalKey that isn't AttributeHierarchicalKey will silently fall through the switch and produce no sub-attributes. An instanceof-based approach handles the full class hierarchy without extra cases.

Reviews (1): Last reviewed commit: "N°8860 - Régression Export CSV clé exter..." | Re-trigger Greptile

case 'AttributeStopWatch':
case AttributeStopWatch::class:
foreach (MetaModel::ListAttributeDefs($sClass) as $sSubAttCode => $oSubAttDef) {
if ($oSubAttDef instanceof AttributeSubItem) {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same


protected function IsSubAttribute($sClass, $sAttCode, $oAttDef)
{
return (($oAttDef instanceof AttributeFriendlyName) || ($oAttDef instanceof AttributeExternalField) || ($oAttDef instanceof AttributeSubItem));

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There might be a missing use for the AttributeFriendlyMame and AttributeExternalField classes.

It may not be mandatory for now as there are the class aliases, but having some use in the file header could lead to a misunderstanding and letting people think that all Attribute classes have be migrated.

if (!is_null($sFriendlyNameAttCode)) {
// The friendly name is made of a single attribute, check if that attribute is present as an external field
foreach (MetaModel::ListAttributeDefs($sClass) as $sSubAttCode => $oSubAttDef) {
if ($oSubAttDef instanceof AttributeExternalField) {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same

}

foreach (MetaModel::ListAttributeDefs($sClass) as $sSubAttCode => $oSubAttDef) {
if ($oSubAttDef instanceof AttributeExternalField) {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

internal Work made by Combodo

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants