diff --git a/application/cmdbabstract.class.inc.php b/application/cmdbabstract.class.inc.php
index d0298dc2c..20873b740 100644
--- a/application/cmdbabstract.class.inc.php
+++ b/application/cmdbabstract.class.inc.php
@@ -663,7 +663,30 @@ EOF
{
// n:n links
$oLinkingAttDef = MetaModel::GetAttributeDef($sLinkedClass, $oAttDef->GetExtKeyToRemote());
+ $sLinkingAttCode = $oLinkingAttDef->GetCode();
$sTargetClass = $oLinkingAttDef->GetTargetClass();
+
+ // N°2334 fields to display for n:n relations
+ $aLnkAttDefsToDisplay = MetaModel::GetZListAttDefsFilteredForIndirectLinkClass($sClass, $sAttCode);
+ $aRemoteAttDefsToDisplay = MetaModel::GetZListAttDefsFilteredForIndirectRemoteClass($sTargetClass);
+ $aLnkAttCodesToDisplay = array_map(function ($oLnkAttDef) {
+ return ormLinkSet::LINK_ALIAS.'.'.$oLnkAttDef->GetCode();
+ },
+ $aLnkAttDefsToDisplay
+ );
+ if (!in_array(ormLinkSet::LINK_ALIAS.'.'.$sLinkingAttCode, $aLnkAttCodesToDisplay))
+ {
+ // we need to display a link to the remote class instance !
+ $aLnkAttCodesToDisplay[] = ormLinkSet::LINK_ALIAS.'.'.$sLinkingAttCode;
+ }
+ $aRemoteAttCodesToDisplay = array_map(function ($oRemoteAttDef) {
+ return ormLinkSet::REMOTE_ALIAS.'.'.$oRemoteAttDef->GetCode();
+ },
+ $aRemoteAttDefsToDisplay
+ );
+ $aAttCodesToDisplay = array_merge($aLnkAttCodesToDisplay, $aRemoteAttCodesToDisplay);
+ $sAttCodesToDisplay = implode(',', $aAttCodesToDisplay);
+
$aParams = array(
'link_attr' => $oAttDef->GetExtKeyToMe(),
'object_id' => $this->GetKey(),
@@ -671,8 +694,12 @@ EOF
'view_link' => false,
'menu' => false,
//'menu_actions_target' => '_blank',
- 'display_limit' => true, // By default limit the list to speed up the initial load & display
+ // By default limit the list to speed up the initial load & display
+ 'display_limit' => true,
'table_id' => $sClass.'_'.$sAttCode,
+ // N°2334 specify fields to display for n:n relations
+ 'zlist' => false,
+ 'extra_fields' => $sAttCodesToDisplay,
);
}
$oPage->p(MetaModel::GetClassIcon($sTargetClass)." ".$oAttDef->GetDescription());
@@ -1286,7 +1313,14 @@ HTML
/**
* @param \WebPage $oPage
* @param \CMDBObjectSet $oSet
- * @param array $aExtraParams
+ * @param array $aExtraParams key used :
+ *
+ * - view_link : if true then for extkey will display links with friendly name and make column sortable, default true
+ *
- menu : if true prints DisplayBlock menu, default true
+ *
- display_aliases : list of query aliases that will be printed, defaults to [] (displays all)
+ *
- zlist : name of the zlist to use, false to disable zlist lookup, default to 'list'
+ *
- extra_fields : list of . to add to the result, separator ',', defaults to empty string
+ *
*
* @return string
* @throws \CoreException
@@ -1365,7 +1399,7 @@ HTML
}
// Filter the list to removed linked set since we are not able to display them here
- foreach($aList[$sAlias] as $index => $sAttCode)
+ foreach ($aList[$sAlias] as $index => $sAttCode)
{
$oAttDef = MetaModel::GetAttributeDef($sClassName, $sAttCode);
if ($oAttDef instanceof AttributeLinkedSet)
@@ -1374,6 +1408,11 @@ HTML
unset($aList[$sAlias][$index]);
}
}
+
+ if (empty($aList[$sAlias]))
+ {
+ unset($aList[$sAlias], $aAuthorizedClasses[$sAlias]);
+ }
}
$sSelectMode = 'none';
@@ -3111,7 +3150,7 @@ EOF
$this->GetOwnershipJSHandler($oPage, $sOwnershipToken);
}
- // Note: This part (inline images activation) is duplicated in self::DisplayModifyForm and several other places. Maybe it should be refactored so it automatically activates when an HTML field is present, or be an option of the attribute. See bug n°1240.
+ // Note: This part (inline images activation) is duplicated in self::DisplayModifyForm and several other places. Maybe it should be refactored so it automatically activates when an HTML field is present, or be an option of the attribute. See bug N°1240.
$sTempId = utils::GetUploadTempId($iTransactionId);
$oPage->add_ready_script(InlineImage::EnableCKEditorImageUpload($this, $sTempId));
}
diff --git a/application/datatable.class.inc.php b/application/datatable.class.inc.php
index adbeaecae..226e1d614 100644
--- a/application/datatable.class.inc.php
+++ b/application/datatable.class.inc.php
@@ -971,13 +971,13 @@ class DataTableSettings implements Serializable
* @throws \CoreException
* @throws \DictExceptionMissingString
*/
- static public function GetDataModelSettings($aClassAliases, $bViewLink, $aDefaultLists)
+ public static function GetDataModelSettings($aClassAliases, $bViewLink, $aDefaultLists)
{
$oSettings = new DataTableSettings($aClassAliases);
// Retrieve the class specific settings for each class/alias based on the 'list' ZList
//TODO let the caller pass some other default settings (another Zlist, extre fields...)
$aColumns = array();
- foreach($aClassAliases as $sAlias => $sClass)
+ foreach ($aClassAliases as $sAlias => $sClass)
{
if ($aDefaultLists == null)
{
diff --git a/application/ui.linkswidget.class.inc.php b/application/ui.linkswidget.class.inc.php
index 2d709ae94..02804fc5d 100644
--- a/application/ui.linkswidget.class.inc.php
+++ b/application/ui.linkswidget.class.inc.php
@@ -46,7 +46,7 @@ class UILinksWidget
* UILinksWidget constructor.
*
* @param string $sClass
- * @param string $sAttCode
+ * @param string $sAttCode AttributeLinkedSetIndirect attcode
* @param int $iInputId
* @param string $sNameSuffix
* @param bool $bDuplicatesAllowed
@@ -73,41 +73,35 @@ class UILinksWidget
/** @var AttributeExternalKey $oLinkingAttDef */
$oLinkingAttDef = MetaModel::GetAttributeDef($this->m_sLinkedClass, $this->m_sExtKeyToRemote);
$this->m_sRemoteClass = $oLinkingAttDef->GetTargetClass();
- $sExtKeyToMe = $oAttDef->GetExtKeyToMe();
- $sStateAttCode = MetaModel::GetStateAttributeCode($this->m_sClass);
- $sDefaultState = MetaModel::GetDefaultState($this->m_sClass);
$this->m_aEditableFields = array();
$this->m_aTableConfig = array();
- $this->m_aTableConfig['form::checkbox'] = array( 'label' => "m_sAttCode}{$this->m_sNameSuffix} .selection', this.checked); oWidget".$this->m_iInputId.".OnSelectChange();\">", 'description' => Dict::S('UI:SelectAllToggle+'));
+ $this->m_aTableConfig['form::checkbox'] = array(
+ 'label' => "m_sAttCode}{$this->m_sNameSuffix} .selection', this.checked); oWidget".$this->m_iInputId.".OnSelectChange();\">",
+ 'description' => Dict::S('UI:SelectAllToggle+'),
+ );
- foreach(MetaModel::FlattenZList(MetaModel::GetZListItems($this->m_sLinkedClass, 'list')) as $sAttCode)
+ $aLnkAttDefsToDisplay = MetaModel::GetZListAttDefsFilteredForIndirectLinkClass($sClass, $sAttCode);
+ foreach ($aLnkAttDefsToDisplay as $oLnkAttDef)
{
- $oAttDef = MetaModel::GetAttributeDef($this->m_sLinkedClass, $sAttCode);
- if ($sStateAttCode == $sAttCode)
- {
- // State attribute is always hidden from the UI
- }
- else if ($oAttDef->IsWritable() && ($sAttCode != $sExtKeyToMe) && ($sAttCode != $this->m_sExtKeyToRemote) && ($sAttCode != 'finalclass'))
- {
- $iFlags = MetaModel::GetAttributeFlags($this->m_sLinkedClass, $sDefaultState, $sAttCode);
- if ( !($iFlags & OPT_ATT_HIDDEN) && !($iFlags & OPT_ATT_READONLY) )
- {
- $this->m_aEditableFields[] = $sAttCode;
- $this->m_aTableConfig[$sAttCode] = array( 'label' => $oAttDef->GetLabel(), 'description' => $oAttDef->GetDescription());
- }
- }
+ $sLnkAttCode = $oLnkAttDef->GetCode();
+ $this->m_aEditableFields[] = $sLnkAttCode;
+ $this->m_aTableConfig[$sLnkAttCode] = array('label' => $oLnkAttDef->GetLabel(), 'description' => $oLnkAttDef->GetDescription());
}
-
- $this->m_aTableConfig['static::key'] = array( 'label' => MetaModel::GetName($this->m_sRemoteClass), 'description' => MetaModel::GetClassDescription($this->m_sRemoteClass));
- foreach(MetaModel::GetZListItems($this->m_sRemoteClass, 'list') as $sFieldCode)
+
+ $this->m_aTableConfig['static::key'] = array(
+ 'label' => MetaModel::GetName($this->m_sRemoteClass),
+ 'description' => MetaModel::GetClassDescription($this->m_sRemoteClass),
+ );
+
+ $aRemoteAttDefsToDisplay = MetaModel::GetZListAttDefsFilteredForIndirectRemoteClass($this->m_sRemoteClass);
+ foreach ($aRemoteAttDefsToDisplay as $oRemoteAttDef)
{
- // TO DO: check the state of the attribute: hidden or visible ?
- if ($sFieldCode != 'finalclass')
- {
- $oAttDef = MetaModel::GetAttributeDef($this->m_sRemoteClass, $sFieldCode);
- $this->m_aTableConfig['static::'.$sFieldCode] = array( 'label' => $oAttDef->GetLabel(), 'description' => $oAttDef->GetDescription());
- }
+ $sRemoteAttCode = $oRemoteAttDef->GetCode();
+ $this->m_aTableConfig['static::'.$sRemoteAttCode] = array(
+ 'label' => $oRemoteAttDef->GetLabel(),
+ 'description' => $oRemoteAttDef->GetDescription(),
+ );
}
}
diff --git a/core/metamodel.class.php b/core/metamodel.class.php
index be80e20a9..0c8cdbd98 100644
--- a/core/metamodel.class.php
+++ b/core/metamodel.class.php
@@ -1848,7 +1848,7 @@ abstract class MetaModel
* @param string $sClass
* @param string $sListCode
*
- * @return array
+ * @return array list of attribute codes
*/
public static function GetZListItems($sClass, $sListCode)
{
@@ -1868,6 +1868,82 @@ abstract class MetaModel
return self::GetZListItems($sParentClass, $sListCode);
}
+ /**
+ * @param string $sRemoteClass
+ *
+ * @return \AttributeDefinition[] list of attdefs to display by default for the remote class
+ *
+ * @since 2.8.0 N°2334
+ */
+ public static function GetZListAttDefsFilteredForIndirectRemoteClass($sRemoteClass)
+ {
+ $aAttCodesToPrint = [];
+
+ foreach (MetaModel::GetZListItems($sRemoteClass, 'list') as $sFieldCode)
+ {
+ //TODO: check the state of the attribute: hidden or visible ?
+ if ($sFieldCode == 'finalclass')
+ {
+ continue;
+ }
+
+ $oRemoteAttDef = MetaModel::GetAttributeDef($sRemoteClass, $sFieldCode);
+ $aAttCodesToPrint[] = $oRemoteAttDef;
+ }
+
+ return $aAttCodesToPrint;
+ }
+
+ /**
+ * @param string $sClass left class
+ * @param string $sAttCode AttributeLinkedSetIndirect attcode
+ *
+ * @return \AttributeDefinition[] list of attdefs to display by default for lnk class
+ *
+ * @throws \CoreException
+ * @since 2.8.0 N°2334
+ */
+ public static function GetZListAttDefsFilteredForIndirectLinkClass($sClass, $sAttCode)
+ {
+ $aAttCodesToPrint = [];
+
+ $oLinkedSetAttDef = MetaModel::GetAttributeDef($sClass, $sAttCode);
+ $sLinkedClass = $oLinkedSetAttDef->GetLinkedClass();
+ $sExtKeyToRemote = $oLinkedSetAttDef->GetExtKeyToRemote();
+ $sExtKeyToMe = $oLinkedSetAttDef->GetExtKeyToMe();
+
+ $sStateAttCode = MetaModel::GetStateAttributeCode($sClass);
+ $sDefaultState = MetaModel::GetDefaultState($sClass);
+
+ foreach (MetaModel::FlattenZList(MetaModel::GetZListItems($sLinkedClass, 'list')) as $sLnkAttCode)
+ {
+ $oLnkAttDef = MetaModel::GetAttributeDef($sLinkedClass, $sLnkAttCode);
+ if ($sStateAttCode == $sLnkAttCode)
+ {
+ // State attribute is always hidden from the UI
+ continue;
+ }
+ if (($sLnkAttCode == $sExtKeyToMe)
+ || ($sLnkAttCode == $sExtKeyToRemote)
+ || ($sLnkAttCode == 'finalclass'))
+ {
+ continue;
+ }
+ if (!($oLnkAttDef->IsWritable()))
+ {
+ continue;
+ }
+
+ $iFlags = MetaModel::GetAttributeFlags($sLinkedClass, $sDefaultState, $sLnkAttCode);
+ if (!($iFlags & OPT_ATT_HIDDEN) && !($iFlags & OPT_ATT_READONLY))
+ {
+ $aAttCodesToPrint[] = $oLnkAttDef;
+ }
+ }
+
+ return $aAttCodesToPrint;
+ }
+
/**
* @param string $sClass
* @param string $sListCode
diff --git a/core/ormlinkset.class.inc.php b/core/ormlinkset.class.inc.php
index 7de3624fa..afd0dec02 100644
--- a/core/ormlinkset.class.inc.php
+++ b/core/ormlinkset.class.inc.php
@@ -30,6 +30,9 @@ require_once('dbobjectiterator.php');
class ormLinkSet implements iDBObjectSetIterator, Iterator, SeekableIterator
{
+ public const LINK_ALIAS = 'Link';
+ public const REMOTE_ALIAS = 'Remote';
+
protected $sHostClass; // subclass of DBObject
protected $sAttCode; // xxxxxx_list
protected $sClass; // class of the links
@@ -786,11 +789,13 @@ class ormLinkSet implements iDBObjectSetIterator, Iterator, SeekableIterator
/**
* @param bool $bShowObsolete
*
- * @return \DBObjectSet
+ * @return \DBObjectSet indirect relations will get `SELECT L,R ...` (l = lnk class, R = remote)
* @throws \CoreException
* @throws \CoreWarning
* @throws \MySQLException
* @throws \Exception
+ *
+ * @since 2.8.0 N°2334 returns both lnk and remote classes for indirect relations
*/
public function ToDBObjectSet($bShowObsolete = true)
{
@@ -802,7 +807,11 @@ class ormLinkSet implements iDBObjectSetIterator, Iterator, SeekableIterator
$sExtKeyToRemote = $oAttDef->GetExtKeyToRemote();
/** @var \AttributeExternalKey $oLinkingAttDef */
$oLinkingAttDef = MetaModel::GetAttributeDef($this->sClass, $sExtKeyToRemote);
+
+ // N°2334 add pointed class (SELECT L,R) to have all fields (lnk + remote) in display
+ // the pointed class is always present in the search, as generated by \AttributeLinkedSet::GetDefaultValue
$sTargetClass = $oLinkingAttDef->GetTargetClass();
+ $oRemoteClassSearch = new DBObjectSearch($sTargetClass);
if (!$bShowObsolete && MetaModel::IsObsoletable($sTargetClass))
{
$oNotObsolete = new BinaryExpression(
@@ -810,10 +819,12 @@ class ormLinkSet implements iDBObjectSetIterator, Iterator, SeekableIterator
'=',
new ScalarExpression(0)
);
- $oNotObsoleteRemote = new DBObjectSearch($sTargetClass);
- $oNotObsoleteRemote->AddConditionExpression($oNotObsolete);
- $oLinkSearch->AddCondition_PointingTo($oNotObsoleteRemote, $sExtKeyToRemote);
+ $oRemoteClassSearch->AddConditionExpression($oNotObsolete);
}
+ $oLinkSearch->AddCondition_PointingTo($oRemoteClassSearch, $sExtKeyToRemote);
+ $oLinkSearch->RenameAlias($oLinkSearch->GetClassAlias(), self::LINK_ALIAS);
+ $oLinkSearch->RenameAlias($sTargetClass, self::REMOTE_ALIAS);
+ $oLinkSearch->SetSelectedClasses([self::LINK_ALIAS, self::REMOTE_ALIAS]);
}
$oLinkSet = new DBObjectSet($oLinkSearch);
$oLinkSet->SetShowObsoleteData($bShowObsolete);