diff --git a/application/cmdbabstract.class.inc.php b/application/cmdbabstract.class.inc.php
index f67c75426..7d5a4d6b6 100644
--- a/application/cmdbabstract.class.inc.php
+++ b/application/cmdbabstract.class.inc.php
@@ -39,6 +39,8 @@ use Combodo\iTop\Application\UI\Base\Layout\Object\ObjectFactory;
use Combodo\iTop\Application\UI\Base\Layout\TabContainer\Tab\AjaxTab;
use Combodo\iTop\Application\UI\Base\Layout\UIContentBlock;
use Combodo\iTop\Application\UI\Base\Layout\UIContentBlockUIBlockFactory;
+use Combodo\iTop\Application\UI\Links\Indirect\BlockIndirectLinksViewTable;
+use Combodo\iTop\Application\UI\Links\Direct\BlockDirectLinksViewTable;
use Combodo\iTop\Renderer\BlockRenderer;
use Combodo\iTop\Renderer\Console\ConsoleFormRenderer;
@@ -650,8 +652,7 @@ HTML
}
// Display mode
- if (!$oAttDef->IsLinkset())
- {
+ if (!$oAttDef->IsLinkset()) {
continue;
} // Process only linkset attributes...
@@ -662,12 +663,9 @@ HTML
$oLinkSet = $oOrmLinkSet->ToDBObjectSet(utils::ShowObsoleteData());
$iCount = $oLinkSet->Count();
- if ($this->IsNew())
- {
+ if ($this->IsNew()) {
$iFlags = $this->GetInitialStateAttributeFlags($sAttCode);
- }
- else
- {
+ } else {
$iFlags = $this->GetAttributeFlags($sAttCode);
}
// Adjust the flags according to user rights
@@ -712,103 +710,20 @@ HTML
$aArgs = array('this' => $this);
$bReadOnly = ($iFlags & (OPT_ATT_READONLY | OPT_ATT_SLAVE));
- if ($bEditMode && (!$bReadOnly))
- {
+ if ($bEditMode && (!$bReadOnly)) {
$sInputId = $this->m_iFormId.'_'.$sAttCode;
-
- if ($oAttDef->IsIndirect())
- {
- $oLinkingAttDef = MetaModel::GetAttributeDef($sLinkedClass, $oAttDef->GetExtKeyToRemote());
- $sTargetClass = $oLinkingAttDef->GetTargetClass();
- }
- else
- {
- $sTargetClass = $sLinkedClass;
- }
-
- $oClassIcon = new MedallionIcon(MetaModel::GetClassIcon($sTargetClass, false));
- $oClassIcon->SetDescription($oAttDef->GetDescription())->AddCSSClass('ibo-block-list--medallion');
- $oPage->AddUiBlock($oClassIcon);
-
$sDisplayValue = ''; // not used
$sHTMLValue = "".self::GetFormElementForField($oPage, $sClass, $sAttCode,
$oAttDef, $oLinkSet, $sDisplayValue, $sInputId, '', $iFlags, $aArgs).' ';
$this->AddToFieldsMap($sAttCode, $sInputId);
$oPage->add($sHTMLValue);
- }
- else
- {
- // Display mode
- if (!$oAttDef->IsIndirect())
- {
- // 1:n links
- $sTargetClass = $sLinkedClass;
-
- $aDefaults = array($oAttDef->GetExtKeyToMe() => $this->GetKey());
- $oAppContext = new ApplicationContext();
- foreach($oAppContext->GetNames() as $sKey)
- {
- // The linked object inherits the parent's value for the context
- if (MetaModel::IsValidAttCode($sClass, $sKey))
- {
- $aDefaults[$sKey] = $this->Get($sKey);
- }
- }
- $aParams = array(
- 'target_attr' => $oAttDef->GetExtKeyToMe(),
- 'object_id' => $this->GetKey(),
- 'menu' => MetaModel::GetConfig()->Get('allow_menu_on_linkset'),
- //'menu_actions_target' => '_blank',
- 'default' => $aDefaults,
- 'table_id' => $sClass.'_'.$sAttCode,
- );
+ } else {
+ if ($oAttDef->IsIndirect()) {
+ $oBlockLinkViewTable = new BlockIndirectLinksViewTable($oPage, $this, $sClass, $sAttCode, $oAttDef);
+ } else {
+ $oBlockLinkViewTable = new BlockDirectLinksViewTable($oPage, $this, $sClass, $sAttCode, $oAttDef);
}
- else {
- // 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(),
- 'target_attr' => $oAttDef->GetExtKeyToRemote(),
- 'view_link' => false,
- 'menu' => false,
- //'menu_actions_target' => '_blank',
- // 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,
- );
- }
- $oClassIcon = new MedallionIcon(MetaModel::GetClassIcon($sTargetClass, false));
- $oClassIcon->SetDescription($oAttDef->GetDescription())->AddCSSClass('ibo-block-list--medallion');
- $oPage->AddUiBlock($oClassIcon);
- $oBlock = new DisplayBlock($oLinkSet->GetFilter(), 'list', false);
- $oBlock->Display($oPage, 'rel_'.$sAttCode, $aParams);
+ $oPage->AddUiBlock($oBlockLinkViewTable);
}
if (array_key_exists($sAttCode, $aRedundancySettings)) {
foreach ($aRedundancySettings[$sAttCode] as $oRedundancyAttDef) {
@@ -871,6 +786,10 @@ HTML
}
}
}
+
+ // add hidden input for linkset transactions
+ $oInputHidden = InputUIBlockFactory::MakeForHidden('linkset_transactions_id', utils::GetNewTransactionId(), 'linkset_transactions_id');
+ $oPage->AddUiBlock($oInputHidden);
}
/**
diff --git a/application/displayblock.class.inc.php b/application/displayblock.class.inc.php
index 05443b292..f43b12ed4 100644
--- a/application/displayblock.class.inc.php
+++ b/application/displayblock.class.inc.php
@@ -1471,6 +1471,7 @@ JS
} else {
$iListId = $aExtraParams['currentId'];
}
+
$oBlock = DataTableUIBlockFactory::MakeForRendering($iListId, $oSet, $aExtraParams);
$oHtml->AddHtml("
");
$oContentBlock->AddSubBlock($oBlock);
@@ -1838,7 +1839,7 @@ class MenuBlock extends DisplayBlock
if ($bIsModifyAllowed) {
$aRegularActions['UI:Menu:Modify'] = array(
'label' => Dict::S('UI:Menu:Modify'),
- 'url' => "{$sRootUrl}pages/$sUIPage?operation=object.modify&class=$sClass&id=$id{$sContext}#",
+ 'url' => "{$sRootUrl}pages/$sUIPage?route=object.modify&class=$sClass&id=$id{$sContext}#",
) + $aActionParams;
}
if ($bIsCreationAllowed) {
diff --git a/application/ui.linksdirectwidget.class.inc.php b/application/ui.linksdirectwidget.class.inc.php
index 2bd2d1646..2135a484f 100644
--- a/application/ui.linksdirectwidget.class.inc.php
+++ b/application/ui.linksdirectwidget.class.inc.php
@@ -4,8 +4,8 @@
* @license http://opensource.org/licenses/AGPL-3.0
*/
-use Combodo\iTop\Application\UI\Base\Component\DataTable\DataTableUIBlockFactory;
-use Combodo\iTop\Application\UI\Base\Layout\UIContentBlockUIBlockFactory;
+use Combodo\iTop\Application\UI\Links\Direct\BlockDirectLinksEditTable;
+use Combodo\iTop\Renderer\Console\ConsoleBlockRenderer;
/**
* Class UILinksWidgetDirect
@@ -23,6 +23,7 @@ class UILinksWidgetDirect
/**
* UILinksWidgetDirect constructor.
+ *
* @param string $sClass
* @param string $sAttCode
* @param string $sInputId
@@ -80,97 +81,10 @@ class UILinksWidgetDirect
*/
public function Display(WebPage $oPage, $oValue, $aArgs, $sFormPrefix, $oCurrentObj)
{
- if (empty($aArgs)) {
- $aArgs = [];
- }
+ $oBlock = new BlockDirectLinksEditTable($this, $this->sInputid);
+ $oBlock->InitTable($oPage, $oValue, $sFormPrefix);
- $oLinksetDef = MetaModel::GetAttributeDef($this->sClass, $this->sAttCode);
- switch($oLinksetDef->GetEditMode())
- {
- case LINKSET_EDITMODE_NONE: // The linkset is read-only
- $this->DisplayAsBlock($oPage, $oValue, $aArgs = array(), $sFormPrefix, $oCurrentObj, false /* bDisplayMenu*/);
- break;
-
- case LINKSET_EDITMODE_ADDONLY: // The only possible action is to open (in a new window) the form to create a new object
- if ($oCurrentObj && !$oCurrentObj->IsNew())
- {
- $sTargetClass = $oLinksetDef->GetLinkedClass();
- $sExtKeyToMe = $oLinksetDef->GetExtKeyToMe();
- $sDefault = "default[$sExtKeyToMe]=".$oCurrentObj->GetKey();
- $oAppContext = new ApplicationContext();
- $sParams = $oAppContext->GetForLink();
- $oPage->p("".Dict::Format('UI:ClickToCreateNew', Metamodel::GetName($sTargetClass))." \n");
- }
- $this->DisplayAsBlock($oPage, $oValue, $aArgs = array(), $sFormPrefix, $oCurrentObj, false /* bDisplayMenu*/);
- break;
-
- case LINKSET_EDITMODE_INPLACE: // The whole linkset can be edited 'in-place'
- $this->DisplayEditInPlace($oPage, $oValue, $aArgs, $sFormPrefix, $oCurrentObj);
- break;
-
- case LINKSET_EDITMODE_ADDREMOVE: // The whole linkset can be edited 'in-place'
- $sTargetClass = $oLinksetDef->GetLinkedClass();
- $sExtKeyToMe = $oLinksetDef->GetExtKeyToMe();
- $oExtKeyDef = MetaModel::GetAttributeDef($sTargetClass, $sExtKeyToMe);
- $aButtons = array('add');
- if ($oExtKeyDef->IsNullAllowed())
- {
- $aButtons = array('add', 'remove');
- }
- $this->DisplayEditInPlace($oPage, $oValue, $aArgs, $sFormPrefix, $oCurrentObj, $aButtons);
- break;
-
- case LINKSET_EDITMODE_ACTIONS:
- default:
- $this->DisplayAsBlock($oPage, $oValue, $aArgs = array(), $sFormPrefix, $oCurrentObj, true /* bDisplayMenu*/);
- }
- }
-
- /**
- * @param WebPage $oPage
- * @param DBObjectSet $oValue
- * @param array $aArgs
- * @param string $sFormPrefix
- * @param DBObject $oCurrentObj
- * @param bool $bDisplayMenu
- *
- * @since 2.7.7 3.0.1 3.1.0 N°3129 Remove default value for $aArgs for PHP 8.0 compatibility (protected method, always called with default value)
- */
- protected function DisplayAsBlock(WebPage $oPage, $oValue, $aArgs, $sFormPrefix, $oCurrentObj, $bDisplayMenu)
- {
- $oLinksetDef = MetaModel::GetAttributeDef($this->sClass, $this->sAttCode);
- $sTargetClass = $oLinksetDef->GetLinkedClass();
- if ($oCurrentObj && $oCurrentObj->IsNew() && $bDisplayMenu)
- {
- $oPage->p(Dict::Format('UI:BeforeAdding_Class_ObjectsSaveThisObject', MetaModel::GetName($sTargetClass)));
- }
- else
- {
- $oFilter = new DBObjectSearch($sTargetClass);
- $oFilter->AddCondition($oLinksetDef->GetExtKeyToMe(), $oCurrentObj->GetKey(),'=');
-
- $aDefaults = array($oLinksetDef->GetExtKeyToMe() => $oCurrentObj->GetKey());
- $oAppContext = new ApplicationContext();
- foreach($oAppContext->GetNames() as $sKey)
- {
- // The linked object inherits the parent's value for the context
- if (MetaModel::IsValidAttCode($this->sClass, $sKey) && $oCurrentObj)
- {
- $aDefaults[$sKey] = $oCurrentObj->Get($sKey);
- }
- }
- $aParams = array(
- 'target_attr' => $oLinksetDef->GetExtKeyToMe(),
- 'object_id' => $oCurrentObj ? $oCurrentObj->GetKey() : null,
- 'menu' => $bDisplayMenu,
- 'menu_actions_target' => '_blank',
- 'default' => $aDefaults,
- 'table_id' => $this->sClass.'_'.$this->sAttCode,
- );
-
- $oBlock = new DisplayBlock($oFilter, 'list', false);
- $oBlock->Display($oPage, $this->sInputid, $aParams);
- }
+ return ConsoleBlockRenderer::RenderBlockTemplateInPage($oPage, $oBlock);
}
/**
@@ -229,55 +143,6 @@ class UILinksWidgetDirect
$oPage->add('');
}
- /**
- * @param WebPage $oPage
- * @param DBObjectSet $oValue
- * @param array $aArgs
- * @param string $sFormPrefix
- * @param DBObject $oCurrentObj
- * @param array $aButtons
- *
- * @since 2.7.7 3.0.1 3.1.0 N°3129 Remove default value for $aArgs for PHP 8.0 compatibility (protected method, caller already handles it)
- */
- protected function DisplayEditInPlace(WebPage $oPage, $oValue, $aArgs, $sFormPrefix, $oCurrentObj, $aButtons = array('create', 'delete'))
- {
- $aAttribs = $this->GetTableConfig();
- $oValue->Rewind();
- $aData = array();
- while ($oLinkObj = $oValue->Fetch()) {
- $aRow = array();
- $aRow['form::select'] = ' ';
- foreach ($this->aZlist as $sLinkedAttCode) {
- $aRow[$sLinkedAttCode] = $oLinkObj->GetAsHTML($sLinkedAttCode);
- }
- $aData[] = $aRow;
- }
- $oDiv = UIContentBlockUIBlockFactory::MakeStandard($this->sInputid, ['listContainer']);
- $oPage->AddSubBlock($oDiv);
- $oDatatable = DataTableUIBlockFactory::MakeForForm($this->sInputid, $aAttribs, $aData);
- $oDatatable->SetOptions(['select_mode' => 'custom', 'disable_hyperlinks' => true]);
- $oDiv->AddSubBlock($oDatatable);
- $sInputName = $sFormPrefix.'attr_'.$this->sAttCode;
- $aLabels = array(
- 'delete' => Dict::S('UI:Button:Delete'),
- // 'modify' => 'Modify...' ,
- 'creation_title' => Dict::Format('UI:CreationTitle_Class', MetaModel::GetName($this->sLinkedClass)),
- 'create' => Dict::Format('UI:ClickToCreateNew', MetaModel::GetName($this->sLinkedClass)),
- 'remove' => Dict::S('UI:Button:Remove'),
- 'add' => Dict::Format('UI:AddAnExisting_Class', MetaModel::GetName($this->sLinkedClass)),
- 'selection_title' => Dict::Format('UI:SelectionOf_Class', MetaModel::GetName($this->sLinkedClass)),
- );
- $oContext = new ApplicationContext();
- $sSubmitUrl = utils::GetAbsoluteUrlAppRoot().'pages/ajax.render.php?'.$oContext->GetForLink();
- $sJSONLabels = json_encode($aLabels);
- $sJSONButtons = json_encode($aButtons);
- $sWizHelper = 'oWizardHelper'.$sFormPrefix;
- // Don't automatically launch the search if the table is huge
- $bDoSearch = !utils::IsHighCardinality($this->sLinkedClass);
- $sJSDoSearch = $bDoSearch ? 'true' : 'false';
- $oPage->add_ready_script("$('#{$this->sInputid}').directlinks({class_name: '$this->sClass', att_code: '$this->sAttCode', input_name:'$sInputName', labels: $sJSONLabels, submit_to: '$sSubmitUrl', buttons: $sJSONButtons, oWizardHelper: $sWizHelper, do_search: $sJSDoSearch});");
- }
-
/**
* @param WebPage $oPage
* @param DBObject $oCurrentObj
@@ -433,18 +298,21 @@ HTML
{
}
-
- protected function GetTableConfig()
+
+ public function GetTableConfig()
{
$aAttribs = array();
- $aAttribs['form::select'] = array('label' => " sInputid}:not(:disabled)', this.checked);\" class=\"checkAll\">", 'description' => Dict::S('UI:SelectAllToggle+'));
+ $aAttribs['form::select'] = array(
+ 'label' => " sInputid}:not(:disabled)', this.checked);oWidget".$this->sInputid.".directlinks('instance')._onSelectChange();\" class=\"checkAll\">",
+ 'description' => Dict::S('UI:SelectAllToggle+'),
+ );
- foreach($this->aZlist as $sLinkedAttCode)
- {
+ foreach ($this->aZlist as $sLinkedAttCode) {
$oAttDef = MetaModel::GetAttributeDef($this->sLinkedClass, $sLinkedAttCode);
$aAttribs[$sLinkedAttCode] = array('label' => MetaModel::GetLabel($this->sLinkedClass, $sLinkedAttCode), 'description' => $oAttDef->GetOrderByHint());
}
- return $aAttribs;
+
+ return $aAttribs;
}
/**
@@ -543,12 +411,43 @@ HTML
{
$sAttCode = call_user_func($aCallSpec, $key); // Returns null when there is no mapping for this parameter
}
-
- if (MetaModel::IsValidAttCode($sDestClass, $sAttCode) && !empty($defaultValue))
- {
+
+ if (MetaModel::IsValidAttCode($sDestClass, $sAttCode) && !empty($defaultValue)) {
$oSearch->AddCondition($sAttCode, $defaultValue);
}
}
}
}
+
+
+ public function GetClass(): string
+ {
+ return $this->sClass;
+ }
+
+ public function GetLinkedClass(): string
+ {
+ return $this->sLinkedClass;
+ }
+
+ public function GetAttCode(): string
+ {
+ return $this->sAttCode;
+ }
+
+ public function GetInputId(): string
+ {
+ return $this->sInputid;
+ }
+
+ public function GetNameSuffix(): string
+ {
+ return $this->sNameSuffix;
+ }
+
+ public function GetZList(): array
+ {
+ return $this->aZlist;
+ }
+
}
diff --git a/application/ui.linkswidget.class.inc.php b/application/ui.linkswidget.class.inc.php
index c8b599068..f32bdd2d7 100644
--- a/application/ui.linkswidget.class.inc.php
+++ b/application/ui.linkswidget.class.inc.php
@@ -6,18 +6,18 @@
use Combodo\iTop\Application\UI\Base\Component\DataTable\DataTableUIBlockFactory;
use Combodo\iTop\Application\UI\Base\Component\DataTable\StaticTable\FormTableRow\FormTableRow;
-use Combodo\iTop\Application\UI\Links\Indirect\BlockIndirectLinksEdit\BlockIndirectLinksEdit;
-use Combodo\iTop\Application\UI\Links\Indirect\BlockObjectPickerDialog\BlockObjectPickerDialog;
+use Combodo\iTop\Application\UI\Links\Indirect\BlockIndirectLinksEditTable;
+use Combodo\iTop\Application\UI\Links\Indirect\BlockObjectPickerDialog;
use Combodo\iTop\Renderer\Console\ConsoleBlockRenderer;
require_once(APPROOT.'application/displayblock.class.inc.php');
-class UILinksWidget
+class UILinksWidget
{
protected $m_sClass;
protected $m_sAttCode;
protected $m_sNameSuffix;
- protected $m_iInputId;
+ protected $m_sInputId;
protected $m_aAttributes;
protected $m_sExtKeyToRemote;
protected $m_sExtKeyToMe;
@@ -33,7 +33,7 @@ class UILinksWidget
*
* @param string $sClass
* @param string $sAttCode AttributeLinkedSetIndirect attcode
- * @param int $iInputId
+ * @param string $sInputId
* @param string $sNameSuffix
* @param bool $bDuplicatesAllowed
*
@@ -41,13 +41,14 @@ class UILinksWidget
* @throws \DictExceptionMissingString
* @throws \Exception
*/
- public function __construct($sClass, $sAttCode, $iInputId, $sNameSuffix = '', $bDuplicatesAllowed = false)
+ public function __construct($sClass, $sAttCode, $sInputId, $sNameSuffix = '', $bDuplicatesAllowed = false)
{
$this->m_sClass = $sClass;
$this->m_sAttCode = $sAttCode;
+ $this->m_sInputId = $sInputId;
$this->m_sNameSuffix = $sNameSuffix;
- $this->m_iInputId = $iInputId;
$this->m_bDuplicatesAllowed = $bDuplicatesAllowed;
+
$this->m_aEditableFields = array();
/** @var AttributeLinkedSetIndirect $oAttDef */
@@ -63,7 +64,7 @@ class UILinksWidget
$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();\">",
+ 'label' => " m_sAttCode}{$this->m_sNameSuffix} .selection', this.checked); oWidget".$this->m_sInputId.".OnSelectChange();\">",
'description' => Dict::S('UI:SelectAllToggle+'),
);
@@ -91,234 +92,13 @@ class UILinksWidget
}
}
- /**
- * A one-row form for editing a link record
- *
- * @param WebPage $oP Web page used for the ouput
- * @param DBObject $oLinkedObj Remote object
- * @param DBObject|int $linkObjOrId Either the lnk object or a unique number for new link records to add
- * @param array $aArgs Extra context arguments
- * @param DBObject $oCurrentObj The object to which all the elements of the linked set refer to
- * @param int $iUniqueId A unique identifier of new links
- * @param boolean $bReadOnly Display link as editable or read-only. Default is false (editable)
- *
- * @return array The HTML fragment of the one-row form
- * @throws \ArchivedObjectException
- * @throws \CoreException
- * @throws \CoreUnexpectedValue
- * @throws \Exception
- */
- protected function GetFormRow(WebPage $oP, DBObject $oLinkedObj, $linkObjOrId, $aArgs, $oCurrentObj, $iUniqueId, $bReadOnly = false)
- {
- $sPrefix = "$this->m_sAttCode{$this->m_sNameSuffix}";
- $aRow = array();
- $aFieldsMap = array();
- $iKey = 0;
-
- if (is_object($linkObjOrId) && (!$linkObjOrId->IsNew()))
- {
- $iKey = $linkObjOrId->GetKey();
- $iRemoteObjKey = $linkObjOrId->Get($this->m_sExtKeyToRemote);
- $sPrefix .= "[$iKey][";
- $sNameSuffix = "]"; // To make a tabular form
- $aArgs['prefix'] = $sPrefix;
- $aArgs['wizHelper'] = "oWizardHelper{$this->m_iInputId}{$iKey}";
- $aArgs['this'] = $linkObjOrId;
-
- if ($bReadOnly)
- {
- $aRow['form::checkbox'] = "";
- foreach ($this->m_aEditableFields as $sFieldCode)
- {
- $sDisplayValue = $linkObjOrId->GetEditValue($sFieldCode);
- $aRow[$sFieldCode] = $sDisplayValue;
- }
- }
- else
- {
- $aRow['form::checkbox'] = " m_iInputId.".OnSelectChange();\" value=\"$iKey\">";
- foreach ($this->m_aEditableFields as $sFieldCode)
- {
- $sSafeFieldId = $this->GetFieldId($linkObjOrId->GetKey(), $sFieldCode);
- $this->AddRowForFieldCode($aRow, $sFieldCode, $aArgs, $linkObjOrId, $oP, $sNameSuffix, $sSafeFieldId);
- $aFieldsMap[$sFieldCode] = $sSafeFieldId;
- }
- }
-
- $sState = $linkObjOrId->GetState();
- $sRemoteKeySafeFieldId = $this->GetFieldId($aArgs['this']->GetKey(), $this->m_sExtKeyToRemote);;
- }
- else
- {
- // form for creating a new record
- if (is_object($linkObjOrId))
- {
- // New link existing only in memory
- $oNewLinkObj = $linkObjOrId;
- $iRemoteObjKey = $oNewLinkObj->Get($this->m_sExtKeyToRemote);
- $oNewLinkObj->Set($this->m_sExtKeyToMe,
- $oCurrentObj); // Setting the extkey with the object also fills the related external fields
- }
- else
- {
- $iRemoteObjKey = $linkObjOrId;
- $oNewLinkObj = MetaModel::NewObject($this->m_sLinkedClass);
- $oRemoteObj = MetaModel::GetObject($this->m_sRemoteClass, $iRemoteObjKey);
- $oNewLinkObj->Set($this->m_sExtKeyToRemote,
- $oRemoteObj); // Setting the extkey with the object alsoo fills the related external fields
- $oNewLinkObj->Set($this->m_sExtKeyToMe,
- $oCurrentObj); // Setting the extkey with the object also fills the related external fields
- }
- $sPrefix .= "[-$iUniqueId][";
- $sNameSuffix = "]"; // To make a tabular form
- $aArgs['prefix'] = $sPrefix;
- $aArgs['wizHelper'] = "oWizardHelper{$this->m_iInputId}_".($iUniqueId < 0 ? -$iUniqueId : $iUniqueId);
- $aArgs['this'] = $oNewLinkObj;
- $sInputValue = $iUniqueId > 0 ? "-$iUniqueId" : "$iUniqueId";
- $aRow['form::checkbox'] = " m_iInputId.".OnSelectChange();\" value=\"$sInputValue\">";
-
- if ($iUniqueId > 0)
- {
- // Rows created with ajax call need OnLinkAdded call.
- //
- $oP->add_ready_script(
- <<m_iInputId}.OnLinkAdded($iUniqueId, $iRemoteObjKey);
-EOF
- );
- }
- else
- {
- // Rows added before loading the form don't have to call OnLinkAdded.
- // Listeners are already present and DOM is not recreated
- $iPositiveUniqueId = -$iUniqueId;
- $oP->add_ready_script(<<m_iInputId}.AddLink($iPositiveUniqueId, $iRemoteObjKey);
-EOF
- );
- }
-
- foreach($this->m_aEditableFields as $sFieldCode)
- {
- $sSafeFieldId = $this->GetFieldId($iUniqueId, $sFieldCode);
- $this->AddRowForFieldCode($aRow, $sFieldCode, $aArgs, $oNewLinkObj, $oP, $sNameSuffix, $sSafeFieldId);
- $aFieldsMap[$sFieldCode] = $sSafeFieldId;
-
- $sValue = $oNewLinkObj->Get($sFieldCode);
- $oP->add_ready_script(
- <<m_iInputId}.OnValueChange($iKey, $iUniqueId, '$sFieldCode', '$sValue');
-JS
- );
- }
-
- $sState = '';
- $sRemoteKeySafeFieldId = $this->GetFieldId($iUniqueId, $this->m_sExtKeyToRemote);
- }
-
- if (!$bReadOnly)
- {
- $sExtKeyToMeId = utils::GetSafeId($sPrefix.$this->m_sExtKeyToMe);
- $aFieldsMap[$this->m_sExtKeyToMe] = $sExtKeyToMeId;
- $aRow['form::checkbox'] .= " GetKey()."\">";
-
- $sExtKeyToRemoteId = utils::GetSafeId($sPrefix.$this->m_sExtKeyToRemote);
- $aFieldsMap[$this->m_sExtKeyToRemote] = $sExtKeyToRemoteId;
- $aRow['form::checkbox'] .= " ";
- }
-
- // Adding fields from remote class
- // all fields are embedded in a span + added to $aFieldsMap array so that we can refresh them after extkey change
- $aRemoteFieldsMap = [];
- foreach (MetaModel::GetZListItems($this->m_sRemoteClass, 'list') as $sFieldCode)
- {
- $sSafeFieldId = $this->GetFieldId($aArgs['this']->GetKey(), $sFieldCode);
- $aRow['static::'.$sFieldCode] = "".$oLinkedObj->GetAsHTML($sFieldCode).' ';
- $aRemoteFieldsMap[$sFieldCode] = $sSafeFieldId;
- }
- // id field is needed so that remote object could be load server side
- $aRemoteFieldsMap['id'] = $sRemoteKeySafeFieldId;
-
- // Generate WizardHelper to update dependant fields
- $this->AddWizardHelperInit($oP, $aArgs['wizHelper'], $this->m_sLinkedClass, $sState, $aFieldsMap);
- //instantiate specific WizarHelper instance for remote class fields refresh
- $bHasExtKeyUpdatingRemoteClassFields = (
- array_key_exists('replaceDependenciesByRemoteClassFields', $aArgs)
- && ($aArgs['replaceDependenciesByRemoteClassFields'])
- );
- if ($bHasExtKeyUpdatingRemoteClassFields)
- {
- $this->AddWizardHelperInit($oP, $aArgs['wizHelperRemote'], $this->m_sRemoteClass, $sState, $aRemoteFieldsMap);
- }
-
- return $aRow;
- }
-
- private function AddRowForFieldCode(&$aRow, $sFieldCode, &$aArgs, $oLnk, $oP, $sNameSuffix, $sSafeFieldId): void
- {
- if (($sFieldCode === $this->m_sExtKeyToRemote))
- {
- // current field is the lnk extkey to the remote class
- $aArgs['replaceDependenciesByRemoteClassFields'] = true;
- $sRowFieldCode = 'static::key';
- $aArgs['wizHelperRemote'] = $aArgs['wizHelper'].'_remote';
- $aRemoteAttDefs = MetaModel::GetZListAttDefsFilteredForIndirectRemoteClass($this->m_sRemoteClass);
- $aRemoteCodes = array_map(
- function ($value) {
- return $value->GetCode();
- },
- $aRemoteAttDefs
- );
- $aArgs['remoteCodes'] = $aRemoteCodes;
- }
- else
- {
- $aArgs['replaceDependenciesByRemoteClassFields'] = false;
- $sRowFieldCode = $sFieldCode;
- }
- $sValue = $oLnk->Get($sFieldCode);
- $sDisplayValue = $oLnk->GetEditValue($sFieldCode);
- $oAttDef = MetaModel::GetAttributeDef($this->m_sLinkedClass, $sFieldCode);
-
- $aRow[$sRowFieldCode] = ''
- .cmdbAbstractObject::GetFormElementForField(
- $oP,
- $this->m_sLinkedClass,
- $sFieldCode,
- $oAttDef,
- $sValue,
- $sDisplayValue,
- $sSafeFieldId,
- $sNameSuffix,
- 0,
- $aArgs
- )
- .'
';
- }
-
private function GetFieldId($iLnkId, $sFieldCode, $bSafe = true)
{
- $sFieldId = $this->m_iInputId.'_'.$sFieldCode.'['.$iLnkId.']';
+ $sFieldId = $this->m_sInputId.'_'.$sFieldCode.'['.$iLnkId.']';
return ($bSafe) ? utils::GetSafeId($sFieldId) : $sFieldId;
}
- private function AddWizardHelperInit($oP, $sWizardHelperVarName, $sWizardHelperClass, $sState, $aFieldsMap): void
- {
- $iFieldsCount = count($aFieldsMap);
- $sJsonFieldsMap = json_encode($aFieldsMap);
-
- $oP->add_script(
- <<m_sAttCode}{$this->m_sNameSuffix}";
-
- $oBlock = new BlockIndirectLinksEdit("linkedset_{$sLinkedSetId}", ["ibo-block-indirect-links--edit"]);
-
- $oBlock->sLinkedSetId = $sLinkedSetId;
- $oBlock->sClass = $this->m_sClass;
- $oBlock->sAttCode = $this->m_sAttCode;
- $oBlock->iInputId = $this->m_iInputId;
- $oBlock->sNameSuffix = $this->m_sNameSuffix;
- $oBlock->bDuplicates = ($this->m_bDuplicatesAllowed) ? 'true' : 'false';
- $oBlock->oWizHelper = 'oWizardHelper'.$sFormPrefix;
- $oBlock->sExtKeyToRemote = $this->m_sExtKeyToRemote;
- // Don't automatically launch the search if the table is huge
- $oBlock->bJSDoSearch = utils::IsHighCardinality($this->m_sRemoteClass) ? 'false' : 'true';
- $oBlock->sFormPrefix = $sFormPrefix;
- $oBlock->sRemoteClass = $this->m_sRemoteClass;
-
- $oValue->Rewind();
- $aForm = array();
- $iMaxAddedId = 0;
- $iAddedId = -1; // Unique id for new links
- $oBlock->aRemoved = json_decode(utils::ReadPostedParam("attr_{$sFormPrefix}{$this->m_sAttCode}_tbd", '[]', 'raw_data'));
- while ($oCurrentLink = $oValue->Fetch()) {
- // We try to retrieve the remote object as usual
- if (!in_array($oCurrentLink->GetKey(), $oBlock->aRemoved)) {
- $oLinkedObj = MetaModel::GetObject($this->m_sRemoteClass, $oCurrentLink->Get($this->m_sExtKeyToRemote), false /* Must not be found */);
- // If successful, it means that we can edit its link
- if ($oLinkedObj !== null) {
- $bReadOnly = false;
- } // Else we retrieve it without restrictions (silos) and will display its link as readonly
- else {
- $bReadOnly = true;
- $oLinkedObj = MetaModel::GetObject($this->m_sRemoteClass, $oCurrentLink->Get($this->m_sExtKeyToRemote), false /* Must not be found */, true);
- }
-
- if ($oCurrentLink->IsNew()) {
- $key = $iAddedId--;
- } else {
- $key = $oCurrentLink->GetKey();
- }
-
- $iMaxAddedId = max($iMaxAddedId, $key);
- $aForm[$key] = $this->GetFormRow($oPage, $oLinkedObj, $oCurrentLink, $aArgs, $oCurrentObj, $key, $bReadOnly);
- }
- }
- $oBlock->iMaxAddedId = (int)$iMaxAddedId;
-
- $oDataTable = DataTableUIBlockFactory::MakeForForm("{$this->m_sAttCode}{$this->m_sNameSuffix}", $this->m_aTableConfig, $aForm);
- $oDataTable->SetOptions(['select_mode' => 'custom', 'disable_hyperlinks' => true]);
- $oBlock->AddSubBlock($oDataTable);
-
- $oBlock->AddControls();
+ $oBlock = new BlockIndirectLinksEditTable($this);
+ $oBlock->InitTable($oPage, $oValue, $aArgs, $sFormPrefix, $oCurrentObj, $this->m_aTableConfig);
return ConsoleBlockRenderer::RenderBlockTemplateInPage($oPage, $oBlock);
}
- /**
- * @param string $sClass
- * @param string $sAttCode
- *
- * @return string
- * @throws \Exception
- */
- protected static function GetTargetClass($sClass, $sAttCode)
- {
- /** @var AttributeLinkedSet $oAttDef */
- $oAttDef = MetaModel::GetAttributeDef($sClass, $sAttCode);
- $sLinkedClass = $oAttDef->GetLinkedClass();
- $sTargetClass = '';
- switch(get_class($oAttDef))
- {
- case 'AttributeLinkedSetIndirect':
- /** @var AttributeExternalKey $oLinkingAttDef */
- /** @var AttributeLinkedSetIndirect $oAttDef */
- $oLinkingAttDef = MetaModel::GetAttributeDef($sLinkedClass, $oAttDef->GetExtKeyToRemote());
- $sTargetClass = $oLinkingAttDef->GetTargetClass();
- break;
-
- case 'AttributeLinkedSet':
- $sTargetClass = $sLinkedClass;
- break;
- }
-
- return $sTargetClass;
- }
-
/**
* @param WebPage $oPage
* @param DBObject $oCurrentObj
@@ -473,11 +173,11 @@ JS
$sLinkedSetId = "{$this->m_sAttCode}{$this->m_sNameSuffix}";
- $oBlock = new BlockObjectPickerDialog();
+ $oBlock = new BlockObjectPickerDialog($this);
$oPage->AddUiBlock($oBlock);
$oBlock->sLinkedSetId = $sLinkedSetId;
- $oBlock->iInputId = $this->m_iInputId;
+ $oBlock->iInputId = $this->m_sInputId;
$oBlock->sLinkedClassName = MetaModel::GetName($this->m_sLinkedClass);
$oBlock->sClassName = MetaModel::GetName($this->m_sClass);
@@ -548,7 +248,8 @@ JS
foreach ($aLinkedObjectIds as $iObjectId) {
$oLinkedObj = MetaModel::GetObject($this->m_sRemoteClass, $iObjectId, false);
if (is_object($oLinkedObj)) {
- $aRow = $this->GetFormRow($oP, $oLinkedObj, $iObjectId, array(), $oCurrentObj, $iAdditionId); // Not yet created link get negative Ids
+ $oBlock = new BlockIndirectLinksEditTable($this);
+ $aRow = $oBlock->GetFormRow($oP, $oLinkedObj, $iObjectId, array(), $oCurrentObj, $iAdditionId); // Not yet created link get negative Ids
$oRow = new FormTableRow("{$this->m_sAttCode}{$this->m_sNameSuffix}", $this->m_aTableConfig, $aRow, -$iAdditionId);
$oP->AddUiBlock($oRow);
$iAdditionId++;
@@ -575,7 +276,8 @@ JS
foreach ($aLinkedObjectIds as $iObjectId) {
$oLinkedObj = MetaModel::GetObject($this->m_sRemoteClass, $iObjectId, false);
if (is_object($oLinkedObj)) {
- $aRow = $this->GetFormRow($oP, $oLinkedObj, $iObjectId, array(), $oCurrentObj, $iAdditionId); // Not yet created link get negative Ids
+ $oBlock = new BlockIndirectLinksEditTable($this);
+ $aRow = $oBlock->GetFormRow($oP, $oLinkedObj, $iObjectId, array(), $oCurrentObj, $iAdditionId); // Not yet created link get negative Ids
$aData = [];
foreach ($aRow as $item) {
$aData[] = $item;
@@ -636,24 +338,77 @@ JS
/** @var AttributeExternalKey $oAttDef */
$sTargetClass = $oAttDef->GetTargetClass();
$sHierarchicalKeyCode = MetaModel::IsHierarchicalClass($sTargetClass);
- if ($sHierarchicalKeyCode !== false)
- {
+ if ($sHierarchicalKeyCode !== false) {
$oFilter = new DBObjectSearch($sTargetClass);
$oFilter->AddCondition('id', $defaultValue);
$oHKFilter = new DBObjectSearch($sTargetClass);
$oHKFilter->AddCondition_PointingTo($oFilter, $sHierarchicalKeyCode, TREE_OPERATOR_BELOW);
$oSearch->AddCondition_PointingTo($oHKFilter, $sAttCode);
}
- } catch (Exception $e)
- {
}
- }
- else
- {
+ catch (Exception $e) {
+ }
+ } else {
$oSearch->AddCondition($sAttCode, $defaultValue);
}
}
}
}
}
+
+ public function GetLinkedSetId(): string
+ {
+ return "{$this->m_sAttCode}{$this->m_sNameSuffix}";
+ }
+
+ public function GetClass(): string
+ {
+ return $this->m_sClass;
+ }
+
+ public function GetLinkedClass(): string
+ {
+ return $this->m_sLinkedClass;
+ }
+
+ public function GetAttCode(): string
+ {
+ return $this->m_sAttCode;
+ }
+
+ public function GetInputId(): string
+ {
+ return $this->m_sInputId;
+ }
+
+ public function GetNameSuffix(): string
+ {
+ return $this->m_sNameSuffix;
+ }
+
+ public function IsDuplicatesAllowed(): bool
+ {
+ return $this->m_bDuplicatesAllowed;
+ }
+
+ public function GetExternalKeyToRemote(): string
+ {
+ return $this->m_sExtKeyToRemote;
+ }
+
+ public function GetExternalKeyToMe(): string
+ {
+ return $this->m_sExtKeyToMe;
+ }
+
+ public function GetRemoteClass(): string
+ {
+ return $this->m_sRemoteClass;
+ }
+
+ public function GetEditableFields(): array
+ {
+ return $this->m_aEditableFields;
+ }
+
}
diff --git a/css/backoffice/components/_datatable.scss b/css/backoffice/components/_datatable.scss
index 4160af243..08ae5d00a 100644
--- a/css/backoffice/components/_datatable.scss
+++ b/css/backoffice/components/_datatable.scss
@@ -148,4 +148,20 @@ $ibo-fieldsorter--selected--background-color: $ibo-color-blue-200 !default;
@extend %ibo-font-ral-sembol-100;
border-bottom: $ibo-vendors-datatables--columns-header--border-bottom;
}
+}
+
+
+
+#table-row-action-confirmation-dialog{
+
+ .ibo-row-action--confirmation--explanation{
+ margin-bottom: 16px;
+ }
+
+ .ibo-row-action--confirmation--do-not-show-again--checkbox{
+ height: auto;
+ display: inline-block;
+ width: auto;
+ }
+
}
\ No newline at end of file
diff --git a/dictionaries/ui/application/links/en.dictionary.itop.links.php b/dictionaries/ui/application/links/en.dictionary.itop.links.php
new file mode 100644
index 000000000..dbba7cbf4
--- /dev/null
+++ b/dictionaries/ui/application/links/en.dictionary.itop.links.php
@@ -0,0 +1,26 @@
+ 'Detach item',
+ 'UI:Links:ActionRow:detach:confirmation' => 'Do you really want to detach {item} from current object ?',
+ 'UI:Links:ActionRow:delete' => 'Delete item',
+ 'UI:Links:ActionRow:delete:confirmation' => 'Do you really want to delete {item} from current object ?',
+));
\ No newline at end of file
diff --git a/dictionaries/ui/components/datatable/en.dictionary.itop.datatable.php b/dictionaries/ui/components/datatable/en.dictionary.itop.datatable.php
index 6f584e4d4..3c104b368 100644
--- a/dictionaries/ui/components/datatable/en.dictionary.itop.datatable.php
+++ b/dictionaries/ui/components/datatable/en.dictionary.itop.datatable.php
@@ -19,16 +19,18 @@
// Display DataTable
Dict::Add('EN US', 'English', 'English', array(
- 'UI:Datatables:Language:Processing' => 'Please wait...',
- 'UI:Datatables:Language:LengthMenu' => '_MENU_ per page',
- 'UI:Datatables:Language:ZeroRecords' => 'No result',
- 'UI:Datatables:Language:Info' => '_TOTAL_ item(s)',
- 'UI:Datatables:Language:InfoEmpty' => 'No information',
- 'UI:Datatables:Language:EmptyTable' => 'No data available in this table',
- 'UI:Datatables:Language:Error' => 'An error occured while running the query',
- 'UI:Datatables:Language:DisplayLength:All' => 'All',
- 'UI:Datatables:Language:Sort:Ascending' => 'enable for an ascending sort',
- 'UI:Datatables:Language:Sort:Descending' => 'enable for a descending sort',
- 'UI:Datatables:Column:RowActions:Label' => '',
- 'UI:Datatables:Column:RowActions:Description' => '',
+ 'UI:Datatables:Language:Processing' => 'Please wait...',
+ 'UI:Datatables:Language:LengthMenu' => '_MENU_ per page',
+ 'UI:Datatables:Language:ZeroRecords' => 'No result',
+ 'UI:Datatables:Language:Info' => '_TOTAL_ item(s)',
+ 'UI:Datatables:Language:InfoEmpty' => 'No information',
+ 'UI:Datatables:Language:EmptyTable' => 'No data available in this table',
+ 'UI:Datatables:Language:Error' => 'An error occured while running the query',
+ 'UI:Datatables:Language:DisplayLength:All' => 'All',
+ 'UI:Datatables:Language:Sort:Ascending' => 'enable for an ascending sort',
+ 'UI:Datatables:Language:Sort:Descending' => 'enable for a descending sort',
+ 'UI:Datatables:Column:RowActions:Label' => '',
+ 'UI:Datatables:Column:RowActions:Description' => '',
+ 'UI:Datatables:RowActions:ConfirmationDialog' => 'Action Confirmation',
+ 'UI:Datatables:RowActions:ConfirmationMessage' => 'Do you confirm action ?',
));
\ No newline at end of file
diff --git a/js/dataTables.main.js b/js/dataTables.main.js
index bf7eb0bf4..43fdb7321 100644
--- a/js/dataTables.main.js
+++ b/js/dataTables.main.js
@@ -80,28 +80,3 @@ function getMultipleSelectionParams(listId)
return oRes;
}
-/**
- * Return column JSON declaration for row actions.
- * Could be part of column or columnDefs declaration of datatable.js.
- *
- * @param sTableId
- * @param iColumnTargetIndex
- * @returns {*}
- * @since 3.1.0
- */
-function getRowActionsColumnDefinition(sTableId, iColumnTargetIndex = -1)
-{
- let aColumn = {
- type: "html",
- orderable: false,
- render: function ( data, type, row, meta ) {
- return $(`#${sTableId}_actions_buttons_template`).html();
- }
- };
-
- if (iColumnTargetIndex !== -1) {
- aColumn['targets'] = iColumnTargetIndex;
- }
-
- return aColumn;
-}
diff --git a/js/dataTables.row-actions.js b/js/dataTables.row-actions.js
new file mode 100644
index 000000000..a7993a5e2
--- /dev/null
+++ b/js/dataTables.row-actions.js
@@ -0,0 +1,106 @@
+/*
+ * @copyright Copyright (C) 2010-2022 Combodo SARL
+ * @license http://opensource.org/licenses/AGPL-3.0
+ */
+
+const TABLE_ACTION_CONFIRMATION_PREFIX = 'table_action_row';
+const TABLE_ACTION_CONFIRMATION_DIALOG_SELECTOR = '#table-row-action-confirmation-dialog';
+
+/**
+ * Return column JSON declaration for row actions.
+ * Could be part of column or columnDefs declaration of datatable.js.
+ *
+ * @param sTableId
+ * @param iColumnTargetIndex
+ * @returns {*}
+ * @since 3.1.0
+ */
+function getRowActionsColumnDefinition(sTableId, iColumnTargetIndex = -1)
+{
+ let aColumn = {
+ type: "html",
+ orderable: false,
+ render: function ( data, type, row, meta ) {
+ return $(`#${sTableId}_actions_buttons_template`).html();
+ }
+ };
+
+ if (iColumnTargetIndex !== -1) {
+ aColumn['targets'] = iColumnTargetIndex;
+ }
+
+ return aColumn;
+}
+
+
+/**
+ * HandleActionRowConfirmation.
+ *
+ * @param sTitle title for confirmation dialog
+ * @param sMessage message of the confirmation dialog
+ * @param sDoNotShowAgainPreferenceKey iTop preference key to store "do not show again" flag
+ * @param oConfirmHandler confirm button handler
+ * @param aConfirmHandlerData confirm button handler data
+ * @constructor
+ */
+const HandleActionRowConfirmation = function (sTitle, sMessage, sDoNotShowAgainPreferenceKey, oConfirmHandler, aConfirmHandlerData){
+
+ // confirmation preference
+ if(sDoNotShowAgainPreferenceKey != null){
+
+ // retrieve need confirmation user preference
+ let bNeedConfirmation = GetUserPreferenceAsBoolean(`${TABLE_ACTION_CONFIRMATION_PREFIX}.${sDoNotShowAgainPreferenceKey}`, true);
+
+ // confirm handler if no confirmation requested
+ if(!bNeedConfirmation){
+ oConfirmHandler(aConfirmHandlerData.datatable, aConfirmHandlerData.tr_element, aConfirmHandlerData.action_id, aConfirmHandlerData.row_data);
+ return;
+ }
+ }
+
+ // fill confirmation dialog
+ $('.ibo-row-action--confirmation--explanation', $(TABLE_ACTION_CONFIRMATION_DIALOG_SELECTOR)).html(sMessage);
+ $('.ibo-row-action--confirmation--do-not-show-again', $(TABLE_ACTION_CONFIRMATION_DIALOG_SELECTOR)).toggle(sDoNotShowAgainPreferenceKey != null);
+
+ // open confirmation dialog
+ $(TABLE_ACTION_CONFIRMATION_DIALOG_SELECTOR).dialog({
+ autoOpen: false,
+ minWidth: 400,
+ modal: true,
+ title: sTitle,
+ autoOpen: true,
+ position: {my: "center center", at: "center center", of: $('body')},
+ close: function () {
+ // destroy dialog object
+ $(TABLE_ACTION_CONFIRMATION_DIALOG_SELECTOR).dialog( "destroy" );
+ },
+ buttons: [
+ {
+ text: Dict.S('UI:Button:Cancel'),
+ class: 'ibo-is-alternative',
+ click: function () {
+ // close dialog
+ $(TABLE_ACTION_CONFIRMATION_DIALOG_SELECTOR).dialog('close');
+ }
+ },
+ {
+ text: Dict.S('UI:Button:Ok'),
+ class: 'ibo-is-primary',
+ click: function () {
+ // handle "do not show again" user preference
+ if(sDoNotShowAgainPreferenceKey != null){
+ // save preference
+ const bDoNotShowAgain = $(this).find($('.ibo-row-action--confirmation--do-not-show-again--checkbox')).prop('checked');
+ if (bDoNotShowAgain) {
+ SetUserPreference(`${TABLE_ACTION_CONFIRMATION_PREFIX}.${sDoNotShowAgainPreferenceKey}`, 'false', true);
+ }
+ }
+ // call confirm handler and close dialog
+ if(oConfirmHandler(aConfirmHandlerData.datatable, aConfirmHandlerData.tr_element, aConfirmHandlerData.action_id, aConfirmHandlerData.row_data)){
+ $(TABLE_ACTION_CONFIRMATION_DIALOG_SELECTOR).dialog('close');
+ }
+ }
+ },
+ ],
+ });
+}
diff --git a/js/links/link_set_worker.js b/js/links/link_set_worker.js
new file mode 100644
index 000000000..3e1cb144f
--- /dev/null
+++ b/js/links/link_set_worker.js
@@ -0,0 +1,58 @@
+let LinkSetWorker = new function(){
+
+ // defines
+ const ROUTER_BASE_URL = '../pages/ajax.render.php';
+ const ROUTE_LINK_SET_DELETE_OBJECT = 'linkset.DeleteLinkedObject';
+ const ROUTE_LINK_SET_DETACH_OBJECT = 'linkset.DetachLinkedObject';
+
+ /**
+ * CallAjaxDeleteLinkedObject.
+ *
+ * @param sLinkedObjectClass
+ * @param sLinkedObjectKey
+ * @constructor
+ */
+ const CallAjaxDeleteLinkedObject = function(sLinkedObjectClass, sLinkedObjectKey){
+ $.post(`${ROUTER_BASE_URL}?route=${ROUTE_LINK_SET_DELETE_OBJECT}`, {
+ linked_object_class: sLinkedObjectClass,
+ linked_object_key: sLinkedObjectKey,
+ transaction_id: $('#linkset_transactions_id').val()
+ }, function (data) {
+ if(data.data.success){
+ alert('Operation succeeded, todo refresh table !!');
+ }
+ else{
+ alert('Operation failed, todo feedback !!');
+ }
+ });
+ };
+
+ /**
+ * CallAjaxDetachLinkedObject.
+ *
+ * @param sLinkedObjectClass
+ * @param sLinkedObjectKey
+ * @param sExternalKeyAttCode
+ * @constructor
+ */
+ const CallAjaxDetachLinkedObject = function(sLinkedObjectClass, sLinkedObjectKey, sExternalKeyAttCode){
+ $.post(`${ROUTER_BASE_URL}?route=${ROUTE_LINK_SET_DETACH_OBJECT}`, {
+ linked_object_class: sLinkedObjectClass,
+ linked_object_key: sLinkedObjectKey,
+ external_key_att_code: sExternalKeyAttCode,
+ transaction_id: $('#linkset_transactions_id').val()
+ }, function (data) {
+ if(data.data.success){
+ alert('Operation succeeded, todo refresh table !!');
+ }
+ else{
+ alert('Operation failed, todo feedback !!');
+ }
+ });
+ };
+
+ return {
+ DeleteLinkedObject: CallAjaxDeleteLinkedObject,
+ DetachLinkedObject: CallAjaxDetachLinkedObject
+ }
+};
\ No newline at end of file
diff --git a/js/linksdirectwidget.js b/js/linksdirectwidget.js
index b5fd109ca..38743b313 100644
--- a/js/linksdirectwidget.js
+++ b/js/linksdirectwidget.js
@@ -32,18 +32,19 @@ $(function()
do_search: true,
submit_to: '../pages/ajax.render.php',
submit_parameters: {},
- labels: { 'delete': 'Delete',
- modify: 'Modify...' ,
- creation_title: 'Creation of a new object...' ,
- create: 'Create...',
- add: 'Add...',
- remove: 'Remove',
+ labels: {
+ // 'delete': 'Delete',
+ // modify: 'Modify...' ,
+ creation_title: 'Creation of a new object...' ,
+ // create: 'Create...',
+ // add: 'Add...',
+ // remove: 'Remove',
selection_title: 'Objects selection'
},
- buttons: ['create', 'delete'],
+ // buttons: ['create', 'delete'],
oWizardHelper: null
},
-
+
// the constructor
_create: function()
{
@@ -53,15 +54,14 @@ $(function()
this.element
.addClass('itop-directlinks');
-
this.datatable = this.element.find('table.listResults');
- var aButtonsTypes = ['delete', 'remove', 'modify', 'add', 'create'];
- this.oButtons = {};
- for(k in aButtonsTypes)
- {
- this.oButtons[aButtonsTypes[k]] = $('' + this.options.labels[aButtonsTypes[k]] + ' ');
- }
+ // var aButtonsTypes = ['delete', 'remove', 'modify', 'add', 'create'];
+ // this.oButtons = {};
+ // for(k in aButtonsTypes)
+ // {
+ // this.oButtons[aButtonsTypes[k]] = $('' + this.options.labels[aButtonsTypes[k]] + ' ');
+ // }
this.indicator = $(' ');
this.inputToBeCreated = $(' ');
this.toBeCreated = {};
@@ -78,33 +78,33 @@ $(function()
.after(this.inputToBeDeleted)
.after(this.inputToBeAdded)
.after(this.inputToBeRemoved)
- .after(' ')
.after(this.indicator);
- for (k in this.options.buttons) {
- this.element.after(this.oButtons[this.options.buttons[k]]).after(' ');
- }
+
+ // for (k in this.options.buttons) {
+ // this.element.after(this.oButtons[this.options.buttons[k]]).after(' ');
+ // }
this.element.find('.selectList'+this.id).bind('change', function () {
me._updateButtons();
});
- this.oButtons['delete'].on('click', function () {
- $('.selectList'+me.id+':checked', me.element).each(function () {
- me._deleteRow($(this));
- });
- });
- this.oButtons['create'].on('click', function () {
- me._createRow();
- });
- this.oButtons['remove'].on('click', function () {
- $('.selectList'+me.id+':checked', me.element).each(function () {
- me._removeRow($(this));
- });
- });
- this.oButtons['add'].on('click', function () {
- me._selectToAdd();
- });
+ // this.oButtons['delete'].on('click', function () {
+ // me._deleteSelection();
+ // });
+ // this.oButtons['create'].on('click', function () {
+ // me._createRow();
+ // });
+ // this.oButtons['remove'].on('click', function () {
+ // $('.selectList'+me.id+':checked', me.element).each(function () {
+ // me._removeRow($(this));
+ // });
+ // });
+ // this.oButtons['add'].on('click', function () {
+ // me._selectToAdd();
+ // });
this._updateButtons();
+
+ me._updateTableInformation();
},
// called when created, and later when changing options
@@ -135,24 +135,43 @@ $(function()
var oChecked = $('.selectList'+this.id+':checked', this.element);
switch (oChecked.length) {
case 0:
- this.oButtons['delete'].prop('disabled', true);
- this.oButtons['remove'].prop('disabled', true);
- this.oButtons['modify'].prop('disabled', true);
+ // this.oButtons['delete'].prop('disabled', true);
+ // this.oButtons['remove'].prop('disabled', true);
+ // this.oButtons['modify'].prop('disabled', true);
break;
case 1:
- this.oButtons['delete'].prop('disabled', false);
- this.oButtons['remove'].prop('disabled', false);
- this.oButtons['modify'].prop('disabled', false);
+ // this.oButtons['delete'].prop('disabled', false);
+ // this.oButtons['remove'].prop('disabled', false);
+ // this.oButtons['modify'].prop('disabled', false);
break;
default:
- this.oButtons['delete'].prop('disabled', false);
- this.oButtons['remove'].prop('disabled', false);
- this.oButtons['modify'].prop('disabled', true);
+ // this.oButtons['delete'].prop('disabled', false);
+ // this.oButtons['remove'].prop('disabled', false);
+ // this.oButtons['modify'].prop('disabled', true);
break;
}
},
+ _updateTableInformation: function(){
+
+ let nbChecked = $('tbody tr input:checked', this.element).length;
+ let count = $('tbody tr input', this.element).length;
+
+ $('#linkedset_'+this.id+'_alert_information').toggleClass('ibo-is-information', nbChecked > 0);
+
+ if(nbChecked > 0){
+ $('#'+this.id+'_btnRemove').prop('disabled', false);
+ $('#linkedset_'+this.id+'_alert_information span[data-role="ibo-datatable-selection-value"]').text(nbChecked + ' / ' + count + ' éléments sélectionnés');
+ }
+ else{
+ $('#'+this.id+'_btnRemove').prop('disabled', true);
+ $('#linkedset_'+this.id+'_alert_information span[data-role="ibo-datatable-selection-value"]').text(count + ' éléments');
+ }
+ },
+ _onSelectChange: function () {
+ this._updateTableInformation();
+ },
_updateTable: function () {
var me = this;
/*
@@ -167,7 +186,7 @@ $(function()
this.oDlg.dialog('option', {position: {my: "center", at: "center", of: window}});
},
_createRow: function () {
- this.oButtons['create'].prop('disabled', true);
+ // this.oButtons['create'].prop('disabled', true);
this.indicator.html(' ');
oParams = this.options.submit_parameters;
oParams.operation = 'createObject';
@@ -206,14 +225,14 @@ $(function()
}
});
me.indicator.html('');
- me.oButtons['create'].prop('disabled', false);
+ // me.oButtons['create'].prop('disabled', false);
me._updateDlgPosition();
});
},
_selectToAdd: function()
{
- this.oButtons['add'].prop('disabled', true);
+ // this.oButtons['add'].prop('disabled', true);
this.indicator.html(' ');
oParams = this.options.submit_parameters;
oParams.operation = 'selectObjectsToAdd';
@@ -279,7 +298,7 @@ $(function()
});
me.indicator.html('');
- me.oButtons['add'].prop('disabled', false);
+ // me.oButtons['add'].prop('disabled', false);
if (me.options.do_search)
{
me._onSearchToAdd();
@@ -425,7 +444,9 @@ $(function()
var me = this;
$.post(this.options.submit_to, oParams, function(data) {
+
var oInserted = $(data);
+
oInserted.find('input:checkbox').each(function() {
var iKey = parseInt($(this).val(), 10); // Number in base 10
me.toBeAdded.push(iKey);
@@ -436,12 +457,20 @@ $(function()
me.inputToBeRemoved.val(JSON.stringify(me.toBeRemoved));
me.inputToBeDeleted.val(JSON.stringify(me.toBeDeleted));
- //me.datatable.find('tbody').append(data);
$('#datatable_'+me.id+' .dataTables_empty').hide();
- me.datatable.find('tbody').append(data);
+
+ // add actions on each row...
+ oInserted.each(function(){
+ $('td:last-child',$(this)).after(' ' + $(`#datatable_${oParams.iInputId}_actions_buttons_template`).html() +' ');
+ me.datatable.find('tbody').append(this.outerHTML);
+ });
+
+
me._updateTable();
me.indicator.html('');
- me.oButtons['add'].prop('disabled', false);
+ // me.oButtons['add'].prop('disabled', false);
+
+ me._updateTableInformation();
});
},
subclassSelected: function()
@@ -506,7 +535,7 @@ $(function()
oParams.tempId = nextIdx;
var me = this;
- this.oButtons['create'].prop('disabled', true);
+ // this.oButtons['create'].prop('disabled', true);
this.indicator.html(' ');
$.post(this.options.submit_to, oParams, function (data) {
@@ -516,7 +545,7 @@ $(function()
me._updateTable();
me.indicator.html('');
- me.oButtons['create'].prop('disabled', false);
+ // me.oButtons['create'].prop('disabled', false);
});
}
},
@@ -530,6 +559,12 @@ $(function()
$('.wizContainer', this.oDlg).height(dlgHeight-20);
$('#SearchResultsToAdd_'+this.id).height(dlgHeight-50-searchHeight);
},
+ _deleteSelection: function(){
+ var me = this;
+ $('.selectList'+me.id+':checked', me.element).each(function () {
+ me._deleteRow($(this));
+ });
+ },
_deleteRow: function (oCheckbox) {
var iObjKey = parseInt(oCheckbox.val(), 10); // Number in base 10
@@ -555,6 +590,13 @@ $(function()
oRow.remove();
this._updateButtons();
this._updateTable();
+ this._updateTableInformation();
+ },
+ _removeSelection: function(){
+ var me = this;
+ $('.selectList'+me.id+':checked', me.element).each(function () {
+ me._removeRow($(this));
+ });
},
_removeRow: function(oCheckbox)
{
diff --git a/js/linkswidget.js b/js/linkswidget.js
index ae6334b84..792eb8562 100644
--- a/js/linkswidget.js
+++ b/js/linkswidget.js
@@ -15,6 +15,7 @@
* @param sExtKeyToRemote
* @param bDoSearch
* @param iMaxAddedId
+ * @param aRemoved
* @constructor
*
* @since 3.0.0 Add iMaxAddedId parameter
@@ -56,13 +57,47 @@ function LinksWidget(id, sClass, sAttCode, iInputId, sSuffix, bDuplicates, oWizH
oInput.closest('form').on('submit', function () {
return me.OnFormSubmit();
});
+
+ this.UpdateTableInformation();
};
+ this.UpdateTableInformation = function(){
+
+ let nbChecked = $('#linkedset_'+me.id+' .selection:checked').length;
+ let count = $('#linkedset_'+this.id+' tbody tr').length;
+
+ $('#linkedset_'+me.iInputId+'_alert_information').toggleClass('ibo-is-information', nbChecked > 0);
+
+ if(nbChecked > 0){
+ $('#'+me.id+'_btnRemove').prop('disabled', false);
+ $('#linkedset_'+me.iInputId+'_alert_information span[data-role="ibo-datatable-selection-value"]').text(nbChecked + ' / ' + count + ' éléments sélectionnés');
+ }
+ else{
+ $('#'+me.id+'_btnRemove').prop('disabled', true);
+ $('#linkedset_'+me.iInputId+'_alert_information span[data-role="ibo-datatable-selection-value"]').text(count + ' éléments');
+ }
+ }
+
this.RemoveSelected = function () {
let my_id = '#'+me.id;
$('#linkedset_'+me.id+' .selection:checked').closest('tr').each(function () {
- $('#datatable_'+me.id).DataTable().row($(this)).remove().draw();
- var oCheckbox = $(this).find('.selection');
+ me.Remove($(this));
+ });
+ // Disable the button since all the selected items have been removed
+ $(my_id+'_btnRemove').prop('disabled', true);
+
+ if ($('#linkedset_'+this.id+' .selection').length == 0)
+ {
+ // All items were removed: add a dummy hidden input to make sure that the linkset will be updated (emptied) when posted
+ $('#'+me.id+'_empty_row').show();
+ }
+
+ this.UpdateTableInformation();
+ };
+
+ this.Remove = function(oRowElement){
+ $('#datatable_'+me.id).DataTable().row($(oRowElement)).remove().draw();
+ var oCheckbox = $(oRowElement).find('.selection');
let iLink = $(oCheckbox).attr('data-link-id');
if (iLink > 0) {
me.aRemoved.push(iLink);
@@ -79,32 +114,16 @@ function LinksWidget(id, sClass, sAttCode, iInputId, sSuffix, bDuplicates, oWizH
}
me.aAdded[iUniqueId] = null;
}
- });
- // Disable the button since all the selected items have been removed
- $(my_id+'_btnRemove').prop('disabled', true);
- if ($('#linkedset_'+this.id+' .selection').length == 0)
- {
- // All items were removed: add a dummy hidden input to make sure that the linkset will be updated (emptied) when posted
- $('#'+me.id+'_empty_row').show();
+ this.UpdateTableInformation();
}
- };
this.OnSelectChange = function () {
- let nbChecked = $('#linkedset_'+me.id+' .selection:checked').length;
- if (nbChecked > 0)
- {
- $('#'+me.id+'_btnRemove').prop('disabled', false);
- }
- else
- {
- $('#'+me.id+'_btnRemove').prop('disabled', true);
- }
+ this.UpdateTableInformation();
};
this.AddObjects = function () {
let me = this;
- $('#'+me.id+'_indicatorAdd').html(' ');
me.oWizardHelper.UpdateWizard();
let sPromiseId = 'ajax_promise_'+me.id;
@@ -179,6 +198,7 @@ function LinksWidget(id, sClass, sAttCode, iInputId, sSuffix, bDuplicates, oWizH
};
this.DoAddObjects = function () {
+
let theMap = {
sAttCode: me.sAttCode,
iInputId: me.iInputId,
diff --git a/lib/composer/InstalledVersions.php b/lib/composer/InstalledVersions.php
index 17663926c..d50e0c9fc 100644
--- a/lib/composer/InstalledVersions.php
+++ b/lib/composer/InstalledVersions.php
@@ -29,6 +29,10 @@ class InstalledVersions
* @psalm-var array{root: array{name: string, version: string, reference: string, pretty_version: string, aliases: string[], dev: bool, install_path: string, type: string}, versions: array}|array{}|null
*/
private static $installed;
+
+ /**
+ * @var bool|null
+ */
private static $canGetVendors;
/**
diff --git a/lib/composer/autoload_classmap.php b/lib/composer/autoload_classmap.php
index e9fba7d2c..5f47496fb 100644
--- a/lib/composer/autoload_classmap.php
+++ b/lib/composer/autoload_classmap.php
@@ -229,6 +229,8 @@ return array(
'Combodo\\iTop\\Application\\UI\\Base\\Component\\DataTable\\StaticTable\\FormTable\\FormTable' => $baseDir . '/sources/Application/UI/Base/Component/DataTable/StaticTable/FormTable/FormTable.php',
'Combodo\\iTop\\Application\\UI\\Base\\Component\\DataTable\\StaticTable\\StaticTable' => $baseDir . '/sources/Application/UI/Base/Component/DataTable/StaticTable/StaticTable.php',
'Combodo\\iTop\\Application\\UI\\Base\\Component\\DataTable\\tTableRowActions' => $baseDir . '/sources/Application/UI/Base/Component/DataTable/tTableRowActions.php',
+ 'Combodo\\iTop\\Application\\UI\\Base\\Component\\Dialog\\Dialog' => $baseDir . '/sources/Application/UI/Base/Component/Dialog/Dialog.php',
+ 'Combodo\\iTop\\Application\\UI\\Base\\Component\\Dialog\\DialogUIBlockFactory' => $baseDir . '/sources/Application/UI/Base/Component/Dialog/DialogUIBlockFactory.php',
'Combodo\\iTop\\Application\\UI\\Base\\Component\\FieldBadge\\FieldBadge' => $baseDir . '/sources/Application/UI/Base/Component/FieldBadge/FieldBadge.php',
'Combodo\\iTop\\Application\\UI\\Base\\Component\\FieldBadge\\FieldBadgeUIBlockFactory' => $baseDir . '/sources/Application/UI/Base/Component/FieldBadge/FieldBadgeUIBlockFactory.php',
'Combodo\\iTop\\Application\\UI\\Base\\Component\\FieldSet\\FieldSet' => $baseDir . '/sources/Application/UI/Base/Component/FieldSet/FieldSet.php',
@@ -342,8 +344,12 @@ return array(
'Combodo\\iTop\\Application\\UI\\DisplayBlock\\BlockCsv\\BlockCsv' => $baseDir . '/sources/Application/UI/DisplayBlock/BlockCsv/BlockCsv.php',
'Combodo\\iTop\\Application\\UI\\DisplayBlock\\BlockList\\BlockList' => $baseDir . '/sources/Application/UI/DisplayBlock/BlockList/BlockList.php',
'Combodo\\iTop\\Application\\UI\\Helper\\UIHelper' => $baseDir . '/sources/Application/UI/Helper/UIHelper.php',
- 'Combodo\\iTop\\Application\\UI\\Links\\Indirect\\BlockIndirectLinksEdit\\BlockIndirectLinksEdit' => $baseDir . '/sources/Application/UI/Links/Indirect/BlockIndirectLinksEdit/BlockIndirectLinksEdit.php',
- 'Combodo\\iTop\\Application\\UI\\Links\\Indirect\\BlockObjectPickerDialog\\BlockObjectPickerDialog' => $baseDir . '/sources/Application/UI/Links/Indirect/BlockObjectPickerDialog/BlockObjectPickerDialog.php',
+ 'Combodo\\iTop\\Application\\UI\\Links\\AbstractBlockLinksViewTable' => $baseDir . '/sources/Application/UI/Links/AbstractBlockLinksViewTable.php',
+ 'Combodo\\iTop\\Application\\UI\\Links\\Direct\\BlockDirectLinksEditTable' => $baseDir . '/sources/Application/UI/Links/Direct/BlockDirectLinksEditTable.php',
+ 'Combodo\\iTop\\Application\\UI\\Links\\Direct\\BlockDirectLinksViewTable' => $baseDir . '/sources/Application/UI/Links/Direct/BlockDirectLinksViewTable.php',
+ 'Combodo\\iTop\\Application\\UI\\Links\\Indirect\\BlockIndirectLinksEditTable' => $baseDir . '/sources/Application/UI/Links/Indirect/BlockIndirectLinksEditTable.php',
+ 'Combodo\\iTop\\Application\\UI\\Links\\Indirect\\BlockIndirectLinksViewTable' => $baseDir . '/sources/Application/UI/Links/Indirect/BlockIndirectLinksViewTable.php',
+ 'Combodo\\iTop\\Application\\UI\\Links\\Indirect\\BlockObjectPickerDialog' => $baseDir . '/sources/Application/UI/Links/Indirect/BlockObjectPickerDialog.php',
'Combodo\\iTop\\Application\\UI\\Preferences\\BlockShortcuts\\BlockShortcuts' => $baseDir . '/sources/Application/UI/Preferences/BlockShortcuts/BlockShortcuts.php',
'Combodo\\iTop\\Application\\UI\\Printable\\BlockPrintHeader\\BlockPrintHeader' => $baseDir . '/sources/Application/UI/Printable/BlockPrintHeader/BlockPrintHeader.php',
'Combodo\\iTop\\Composer\\iTopComposer' => $baseDir . '/sources/Composer/iTopComposer.php',
@@ -351,6 +357,7 @@ return array(
'Combodo\\iTop\\Controller\\AjaxRenderController' => $baseDir . '/sources/Controller/AjaxRenderController.php',
'Combodo\\iTop\\Controller\\Base\\Layout\\ActivityPanelController' => $baseDir . '/sources/Controller/Base/Layout/ActivityPanelController.php',
'Combodo\\iTop\\Controller\\Base\\Layout\\ObjectController' => $baseDir . '/sources/Controller/Base/Layout/ObjectController.php',
+ 'Combodo\\iTop\\Controller\\Links\\LinkSetController' => $baseDir . '/sources/Controller/Links/LinksetController.php',
'Combodo\\iTop\\Controller\\OAuth\\OAuthLandingController' => $baseDir . '/sources/Controller/OAuth/OAuthLandingController.php',
'Combodo\\iTop\\Controller\\PreferencesController' => $baseDir . '/sources/Controller/PreferencesController.php',
'Combodo\\iTop\\Controller\\iController' => $baseDir . '/sources/Controller/iController.php',
@@ -2927,6 +2934,7 @@ return array(
'iTopStandardURLMaker' => $baseDir . '/application/applicationcontext.class.inc.php',
'iTopWebPage' => $baseDir . '/sources/Application/WebPage/iTopWebPage.php',
'iTopWizardWebPage' => $baseDir . '/sources/Application/WebPage/iTopWizardWebPage.php',
+ 'iTopXmlException' => $baseDir . '/application/exceptions/iTopXmlException.php',
'iWorkingTimeComputer' => $baseDir . '/core/computing.inc.php',
'lnkTriggerAction' => $baseDir . '/core/trigger.class.inc.php',
'ormCaseLog' => $baseDir . '/core/ormcaselog.class.inc.php',
diff --git a/lib/composer/autoload_static.php b/lib/composer/autoload_static.php
index 4255e16cd..f3e5238ac 100644
--- a/lib/composer/autoload_static.php
+++ b/lib/composer/autoload_static.php
@@ -594,6 +594,8 @@ class ComposerStaticInit7f81b4a2a468a061c306af5e447a9a9f
'Combodo\\iTop\\Application\\UI\\Base\\Component\\DataTable\\StaticTable\\FormTable\\FormTable' => __DIR__ . '/../..' . '/sources/Application/UI/Base/Component/DataTable/StaticTable/FormTable/FormTable.php',
'Combodo\\iTop\\Application\\UI\\Base\\Component\\DataTable\\StaticTable\\StaticTable' => __DIR__ . '/../..' . '/sources/Application/UI/Base/Component/DataTable/StaticTable/StaticTable.php',
'Combodo\\iTop\\Application\\UI\\Base\\Component\\DataTable\\tTableRowActions' => __DIR__ . '/../..' . '/sources/Application/UI/Base/Component/DataTable/tTableRowActions.php',
+ 'Combodo\\iTop\\Application\\UI\\Base\\Component\\Dialog\\Dialog' => __DIR__ . '/../..' . '/sources/Application/UI/Base/Component/Dialog/Dialog.php',
+ 'Combodo\\iTop\\Application\\UI\\Base\\Component\\Dialog\\DialogUIBlockFactory' => __DIR__ . '/../..' . '/sources/Application/UI/Base/Component/Dialog/DialogUIBlockFactory.php',
'Combodo\\iTop\\Application\\UI\\Base\\Component\\FieldBadge\\FieldBadge' => __DIR__ . '/../..' . '/sources/Application/UI/Base/Component/FieldBadge/FieldBadge.php',
'Combodo\\iTop\\Application\\UI\\Base\\Component\\FieldBadge\\FieldBadgeUIBlockFactory' => __DIR__ . '/../..' . '/sources/Application/UI/Base/Component/FieldBadge/FieldBadgeUIBlockFactory.php',
'Combodo\\iTop\\Application\\UI\\Base\\Component\\FieldSet\\FieldSet' => __DIR__ . '/../..' . '/sources/Application/UI/Base/Component/FieldSet/FieldSet.php',
@@ -707,8 +709,12 @@ class ComposerStaticInit7f81b4a2a468a061c306af5e447a9a9f
'Combodo\\iTop\\Application\\UI\\DisplayBlock\\BlockCsv\\BlockCsv' => __DIR__ . '/../..' . '/sources/Application/UI/DisplayBlock/BlockCsv/BlockCsv.php',
'Combodo\\iTop\\Application\\UI\\DisplayBlock\\BlockList\\BlockList' => __DIR__ . '/../..' . '/sources/Application/UI/DisplayBlock/BlockList/BlockList.php',
'Combodo\\iTop\\Application\\UI\\Helper\\UIHelper' => __DIR__ . '/../..' . '/sources/Application/UI/Helper/UIHelper.php',
- 'Combodo\\iTop\\Application\\UI\\Links\\Indirect\\BlockIndirectLinksEdit\\BlockIndirectLinksEdit' => __DIR__ . '/../..' . '/sources/Application/UI/Links/Indirect/BlockIndirectLinksEdit/BlockIndirectLinksEdit.php',
- 'Combodo\\iTop\\Application\\UI\\Links\\Indirect\\BlockObjectPickerDialog\\BlockObjectPickerDialog' => __DIR__ . '/../..' . '/sources/Application/UI/Links/Indirect/BlockObjectPickerDialog/BlockObjectPickerDialog.php',
+ 'Combodo\\iTop\\Application\\UI\\Links\\AbstractBlockLinksViewTable' => __DIR__ . '/../..' . '/sources/Application/UI/Links/AbstractBlockLinksViewTable.php',
+ 'Combodo\\iTop\\Application\\UI\\Links\\Direct\\BlockDirectLinksEditTable' => __DIR__ . '/../..' . '/sources/Application/UI/Links/Direct/BlockDirectLinksEditTable.php',
+ 'Combodo\\iTop\\Application\\UI\\Links\\Direct\\BlockDirectLinksViewTable' => __DIR__ . '/../..' . '/sources/Application/UI/Links/Direct/BlockDirectLinksViewTable.php',
+ 'Combodo\\iTop\\Application\\UI\\Links\\Indirect\\BlockIndirectLinksEditTable' => __DIR__ . '/../..' . '/sources/Application/UI/Links/Indirect/BlockIndirectLinksEditTable.php',
+ 'Combodo\\iTop\\Application\\UI\\Links\\Indirect\\BlockIndirectLinksViewTable' => __DIR__ . '/../..' . '/sources/Application/UI/Links/Indirect/BlockIndirectLinksViewTable.php',
+ 'Combodo\\iTop\\Application\\UI\\Links\\Indirect\\BlockObjectPickerDialog' => __DIR__ . '/../..' . '/sources/Application/UI/Links/Indirect/BlockObjectPickerDialog.php',
'Combodo\\iTop\\Application\\UI\\Preferences\\BlockShortcuts\\BlockShortcuts' => __DIR__ . '/../..' . '/sources/Application/UI/Preferences/BlockShortcuts/BlockShortcuts.php',
'Combodo\\iTop\\Application\\UI\\Printable\\BlockPrintHeader\\BlockPrintHeader' => __DIR__ . '/../..' . '/sources/Application/UI/Printable/BlockPrintHeader/BlockPrintHeader.php',
'Combodo\\iTop\\Composer\\iTopComposer' => __DIR__ . '/../..' . '/sources/Composer/iTopComposer.php',
@@ -716,6 +722,7 @@ class ComposerStaticInit7f81b4a2a468a061c306af5e447a9a9f
'Combodo\\iTop\\Controller\\AjaxRenderController' => __DIR__ . '/../..' . '/sources/Controller/AjaxRenderController.php',
'Combodo\\iTop\\Controller\\Base\\Layout\\ActivityPanelController' => __DIR__ . '/../..' . '/sources/Controller/Base/Layout/ActivityPanelController.php',
'Combodo\\iTop\\Controller\\Base\\Layout\\ObjectController' => __DIR__ . '/../..' . '/sources/Controller/Base/Layout/ObjectController.php',
+ 'Combodo\\iTop\\Controller\\Links\\LinkSetController' => __DIR__ . '/../..' . '/sources/Controller/Links/LinksetController.php',
'Combodo\\iTop\\Controller\\OAuth\\OAuthLandingController' => __DIR__ . '/../..' . '/sources/Controller/OAuth/OAuthLandingController.php',
'Combodo\\iTop\\Controller\\PreferencesController' => __DIR__ . '/../..' . '/sources/Controller/PreferencesController.php',
'Combodo\\iTop\\Controller\\iController' => __DIR__ . '/../..' . '/sources/Controller/iController.php',
@@ -3292,6 +3299,7 @@ class ComposerStaticInit7f81b4a2a468a061c306af5e447a9a9f
'iTopStandardURLMaker' => __DIR__ . '/../..' . '/application/applicationcontext.class.inc.php',
'iTopWebPage' => __DIR__ . '/../..' . '/sources/Application/WebPage/iTopWebPage.php',
'iTopWizardWebPage' => __DIR__ . '/../..' . '/sources/Application/WebPage/iTopWizardWebPage.php',
+ 'iTopXmlException' => __DIR__ . '/../..' . '/application/exceptions/iTopXmlException.php',
'iWorkingTimeComputer' => __DIR__ . '/../..' . '/core/computing.inc.php',
'lnkTriggerAction' => __DIR__ . '/../..' . '/core/trigger.class.inc.php',
'ormCaseLog' => __DIR__ . '/../..' . '/core/ormcaselog.class.inc.php',
diff --git a/lib/composer/installed.php b/lib/composer/installed.php
index f74f47b1d..21e2f87ab 100644
--- a/lib/composer/installed.php
+++ b/lib/composer/installed.php
@@ -5,7 +5,7 @@
'type' => 'project',
'install_path' => __DIR__ . '/../../',
'aliases' => array(),
- 'reference' => '8a3e07dd80c8316d68ad44a892c51f4ed5de572c',
+ 'reference' => '0520e72b9d1190cab294aebc5ab7542a18d2bc51',
'name' => 'combodo/itop',
'dev' => true,
),
@@ -25,7 +25,7 @@
'type' => 'project',
'install_path' => __DIR__ . '/../../',
'aliases' => array(),
- 'reference' => '8a3e07dd80c8316d68ad44a892c51f4ed5de572c',
+ 'reference' => '0520e72b9d1190cab294aebc5ab7542a18d2bc51',
'dev_requirement' => false,
),
'combodo/tcpdf' => array(
diff --git a/pages/ajax.render.php b/pages/ajax.render.php
index 2e9177ec5..97d2a623b 100644
--- a/pages/ajax.render.php
+++ b/pages/ajax.render.php
@@ -1056,7 +1056,7 @@ EOF
$aParams = utils::ReadParam('params', '', false, 'raw_data');
$sDashletClass = $aParams['attr_dashlet_class'];
$sDashletType = $aParams['attr_dashlet_type'];
- $sDashletId = utils::HtmlEntities($aParams['attr_dashlet_id']);
+ $sDashletId = utils::HtmlEntities($aParams['attr_dashlet_id']);
$aUpdatedProperties = $aParams['updated']; // Code of the changed properties as an array: 'attr_xxx', 'attr_xxy', etc...
$aPreviousValues = $aParams['previous_values']; // hash array: 'attr_xxx' => 'old_value'
if (is_subclass_of($sDashletClass, 'Dashlet')) {
diff --git a/sources/Application/UI/Base/Component/DataTable/DataTable.php b/sources/Application/UI/Base/Component/DataTable/DataTable.php
index b7acb0eee..a43fb7248 100644
--- a/sources/Application/UI/Base/Component/DataTable/DataTable.php
+++ b/sources/Application/UI/Base/Component/DataTable/DataTable.php
@@ -40,6 +40,7 @@ class DataTable extends UIContentBlock
'js/dataTables.main.js',
'js/dataTables.settings.js',
'js/dataTables.pipeline.js',
+ 'js/dataTables.row-actions.js',
];
protected $aOptions;//list of specific options for display datatable
@@ -52,6 +53,8 @@ class DataTable extends UIContentBlock
*/
protected $aInitDisplayData;
+ public const DEFAULT_ACTION_ROW_CONFIRMATION = true;
+
/**
* Panel constructor.
diff --git a/sources/Application/UI/Base/Component/DataTable/DataTableUIBlockFactory.php b/sources/Application/UI/Base/Component/DataTable/DataTableUIBlockFactory.php
index ebabc69f9..289e8711b 100644
--- a/sources/Application/UI/Base/Component/DataTable/DataTableUIBlockFactory.php
+++ b/sources/Application/UI/Base/Component/DataTable/DataTableUIBlockFactory.php
@@ -211,7 +211,7 @@ class DataTableUIBlockFactory extends AbstractUIBlockFactory
foreach ($oTable->GetRowActions() as $iKey => $aAction) {
$oButton = ButtonUIBlockFactory::MakeIconAction(
array_key_exists('icon_classes', $aAction) ? $aAction['icon_classes'] : 'fas fa-question',
- array_key_exists('tooltip', $aAction) ? $aAction['tooltip'] : '',
+ array_key_exists('tooltip', $aAction) ? Dict::S($aAction['tooltip']) : '',
array_key_exists('name', $aAction) ? $aAction['name'] : 'undefined'
);
$oButton->SetDataAttributes(['action-id' => $iKey]);
@@ -522,7 +522,6 @@ class DataTableUIBlockFactory extends AbstractUIBlockFactory
$oDataTable->SetRowActions($aExtraParams['row_actions']);
}
-
return $oDataTable;
}
diff --git a/sources/Application/UI/Base/Component/DataTable/StaticTable/StaticTable.php b/sources/Application/UI/Base/Component/DataTable/StaticTable/StaticTable.php
index 8ccc845de..2bdfd4865 100644
--- a/sources/Application/UI/Base/Component/DataTable/StaticTable/StaticTable.php
+++ b/sources/Application/UI/Base/Component/DataTable/StaticTable/StaticTable.php
@@ -36,6 +36,7 @@ class StaticTable extends UIContentBlock
'js/dataTables.main.js',
'js/dataTables.settings.js',
'js/dataTables.pipeline.js',
+ 'js/dataTables.row-actions.js',
];
/**
diff --git a/sources/Application/UI/Base/Component/DataTable/tTableRowActions.php b/sources/Application/UI/Base/Component/DataTable/tTableRowActions.php
index e69fb1a32..9fef83ae2 100644
--- a/sources/Application/UI/Base/Component/DataTable/tTableRowActions.php
+++ b/sources/Application/UI/Base/Component/DataTable/tTableRowActions.php
@@ -6,6 +6,10 @@
namespace Combodo\iTop\Application\UI\Base\Component\DataTable;
+use Combodo\iTop\Application\UI\Base\Component\Dialog\DialogUIBlockFactory;
+use Combodo\iTop\Application\UI\Base\Component\Input\InputUIBlockFactory;
+use Combodo\iTop\Application\UI\Base\Layout\UIContentBlockUIBlockFactory;
+
/**
* Trait tTableRowActions
*
@@ -17,12 +21,20 @@ namespace Combodo\iTop\Application\UI\Base\Component\DataTable;
*/
trait tTableRowActions
{
+ /** @var bool static dialog initialized flag to avoid multiple html markups */
+ static public bool $bDialogInitialized = false;
+
/**
* @var $aRowActions array array of row actions
* action => {
* tooltip: string,
* icon_classes: string,
- * js_row_action: string
+ * js_row_action: string,
+ * confirmation => {
+ * message: string,
+ * message_row_data: string,
+ * remember_choice_pref_key: string
+ * }
* }
*/
protected $aRowActions;
@@ -70,4 +82,31 @@ trait tTableRowActions
{
return DataTableUIBlockFactory::MakeActionRowToolbarTemplate($this);
}
+
+ /**
+ * GetRowActionsConfirmDialog.
+ *
+ * @return \Combodo\iTop\Application\UI\Base\Component\Html\Html
+ */
+ public function GetRowActionsConfirmDialog()
+ {
+ static::$bDialogInitialized = true;
+
+ $oDialog = DialogUIBlockFactory::MakeNeutral('', '
', 'table-row-action-confirmation-dialog');
+
+ $oContent = UIContentBlockUIBlockFactory::MakeStandard();
+ $oContent->AddCSSClass('ibo-row-action--confirmation--do-not-show-again');
+ $checkBox = InputUIBlockFactory::MakeStandard('checkbox', 'do_not_show_again', false);
+ $checkBox->AddCSSClass('ibo-row-action--confirmation--do-not-show-again--checkbox');
+ $checkBox->SetLabel(\Dict::S('UI:UserPref:DoNotShowAgain'));
+ $oContent->AddSubBlock($checkBox);
+ $oDialog->AddSubBlock($oContent);
+
+ return $oDialog;
+ }
+
+ public function GetRowActionsConfirmDialogInitializedFlag()
+ {
+ return static::$bDialogInitialized;
+ }
}
\ No newline at end of file
diff --git a/sources/Application/UI/Base/Component/Dialog/Dialog.php b/sources/Application/UI/Base/Component/Dialog/Dialog.php
new file mode 100644
index 000000000..722ba2902
--- /dev/null
+++ b/sources/Application/UI/Base/Component/Dialog/Dialog.php
@@ -0,0 +1,109 @@
+sContent = $sContent;
+ if (!empty($sContent)) {
+ $this->AddSubBlock(new Html($sContent));
+ }
+ }
+
+ /**
+ * @return string
+ */
+ public function GetTitle(): string
+ {
+ return $this->sTitle;
+ }
+
+ /**
+ * @param string $sTitle Title of the alert
+ *
+ * @return $this
+ */
+ public function SetTitle(string $sTitle): Dialog
+ {
+ $this->sTitle = $sTitle;
+
+ return $this;
+ }
+
+ /**
+ * Return the raw HTML content, should be already sanitized.
+ *
+ * @return string
+ */
+ public function GetContent(): string
+ {
+ return $this->sContent;
+ }
+
+ /**
+ * Set the raw HTML content, must be already sanitized.
+ *
+ * @param string $sContent The raw HTML content, must be already sanitized
+ *
+ * @return $this
+ */
+ public function SetContent(string $sContent): Dialog
+ {
+ $this->sContent = $sContent;
+
+ return $this;
+ }
+
+
+
+}
\ No newline at end of file
diff --git a/sources/Application/UI/Base/Component/Dialog/DialogUIBlockFactory.php b/sources/Application/UI/Base/Component/Dialog/DialogUIBlockFactory.php
new file mode 100644
index 000000000..0fa444794
--- /dev/null
+++ b/sources/Application/UI/Base/Component/Dialog/DialogUIBlockFactory.php
@@ -0,0 +1,54 @@
+oAttDef = $oAttDef;
+ $this->sAttCode = $sAttCode;
+ $this->sObjectClass = $sObjectClass;
+ $this->oDbObject = $oDbObject;
+
+ // Initialization
+ $this->Init();
+
+ // UI Initialization
+ $this->InitUI($oPage);
+ }
+
+ /**
+ * Init.
+ *
+ * @return void
+ * @throws \Exception
+ */
+ private function Init()
+ {
+ $this->sTargetClass = $this->GetTargetClass();
+ }
+
+ /**
+ * Initialize UI.
+ *
+ * @return void
+ * @throws \CoreException
+ */
+ private function InitUI(\WebPage $oPage)
+ {
+ // header
+ $this->InitHeader();;
+
+ // Table
+ $this->InitTable($oPage);
+ }
+
+ /**
+ * InitHeader.
+ *
+ * @return void
+ * @throws \CoreException
+ */
+ private function InitHeader()
+ {
+ // MedallionIcon
+ $oClassIcon = new MedallionIcon(MetaModel::GetClassIcon($this->sTargetClass, false));
+ $oClassIcon->SetDescription($this->oAttDef->GetDescription())->AddCSSClass('ibo-block-list--medallion');
+ $this->AddSubBlock($oClassIcon);
+ }
+
+ /**
+ * InitTable.
+ *
+ * @param \WebPage $oPage
+ *
+ * @return void
+ * @throws \ApplicationException
+ * @throws \ArchivedObjectException
+ * @throws \CoreException
+ * @throws \CoreWarning
+ * @throws \DictExceptionMissingString
+ * @throws \MySQLException
+ */
+ private function InitTable(\WebPage $oPage)
+ {
+ // retrieve db object set
+ $oOrmLinkSet = $this->oDbObject->Get($this->sAttCode);
+ $oLinkSet = $oOrmLinkSet->ToDBObjectSet(\utils::ShowObsoleteData());
+
+ // add list block
+ $oBlock = new \DisplayBlock($oLinkSet->GetFilter(), 'list', false);
+ $this->AddSubBlock($oBlock->GetRenderContent($oPage, $this->GetExtraParam(), 'rel_'.$this->sAttCode));
+ }
+
+ /**
+ * GetExtraParam.
+ *
+ * Provide parameters for display block as list.
+ *
+ * @see \DisplayBlock::RenderList
+ *
+ * @return array
+ * @throws \ArchivedObjectException
+ * @throws \CoreException
+ */
+ abstract function GetExtraParam(): array;
+
+ /**
+ * Return row actions.
+ *
+ * Register row actions for table.
+ *
+ * @see \Combodo\iTop\Application\UI\Base\Component\DataTable\tTableRowActions
+ *
+ * @return \string[][]
+ */
+ abstract function GetRowActions(): array;
+
+ /**
+ * GetTargetClass.
+ *
+ * Return link set target class.
+ *
+ * @return string
+ * @throws \Exception
+ */
+ abstract function GetTargetClass(): string;
+}
\ No newline at end of file
diff --git a/sources/Application/UI/Links/Direct/BlockDirectLinksEditTable.php b/sources/Application/UI/Links/Direct/BlockDirectLinksEditTable.php
new file mode 100644
index 000000000..164b44c64
--- /dev/null
+++ b/sources/Application/UI/Links/Direct/BlockDirectLinksEditTable.php
@@ -0,0 +1,249 @@
+oUILinksDirectWidget = $oUILinksDirectWidget;
+
+ // compute
+ $this->aLabels = array(
+ 'delete' => Dict::S('UI:Button:Delete'),
+ 'creation_title' => Dict::Format('UI:CreationTitle_Class', MetaModel::GetName($this->oUILinksDirectWidget->GetLinkedClass())),
+ 'create' => Dict::Format('UI:ClickToCreateNew', MetaModel::GetName($this->oUILinksDirectWidget->GetLinkedClass())),
+ 'remove' => Dict::S('UI:Button:Remove'),
+ 'add' => Dict::Format('UI:AddAnExisting_Class', MetaModel::GetName($this->oUILinksDirectWidget->GetLinkedClass())),
+ 'selection_title' => Dict::Format('UI:SelectionOf_Class', MetaModel::GetName($this->oUILinksDirectWidget->GetLinkedClass())),
+ );
+ $oContext = new \ApplicationContext();
+ $this->sSubmitUrl = \utils::GetAbsoluteUrlAppRoot().'pages/ajax.render.php?'.$oContext->GetForLink();
+
+ // Don't automatically launch the search if the table is huge
+ $bDoSearch = !\utils::IsHighCardinality($this->oUILinksDirectWidget->GetLinkedClass());
+ $this->sJSDoSearch = $bDoSearch ? 'true' : 'false';
+
+ // Initialization
+ $this->Init();
+
+ // Initialize UI
+ $this->InitUI();
+ }
+
+ /**
+ * Initialisation.
+ *
+ * @return void
+ * @throws \Exception
+ */
+ private function Init()
+ {
+ $this->oAttributeLinkedSet = MetaModel::GetAttributeDef($this->oUILinksDirectWidget->GetClass(), $this->oUILinksDirectWidget->GetAttCode());
+ }
+
+ /**
+ * Initialize UI.
+ *
+ * @return void
+ * @throws \CoreException
+ * @throws \Exception
+ */
+ private function InitUI()
+ {
+ // MedallionIcon
+ $oClassIcon = new MedallionIcon(MetaModel::GetClassIcon($this->oUILinksDirectWidget->GetLinkedClass(), false));
+ $oClassIcon->SetDescription($this->oAttributeLinkedSet->GetDescription());
+ $oClassIcon->AddCSSClass('ibo-block-list--medallion');
+ $this->AddSubBlock($oClassIcon);
+ }
+
+ /**
+ * @param \WebPage $oPage
+ * @param \DBObjectSet $oValue
+ * @param string $sFormPrefix
+ *
+ * @return void
+ */
+ public function InitTable(\WebPage $oPage, \DBObjectSet $oValue, string $sFormPrefix)
+ {
+ /** @todo fields initialization */
+ $this->sInputName = $sFormPrefix.'attr_'.$this->oUILinksDirectWidget->GetAttCode();
+ $this->sWizHelper = 'oWizardHelper'.$sFormPrefix;
+
+ try {
+ $aAttribs = $this->oUILinksDirectWidget->GetTableConfig();
+ $aRows = $this->GetTableRows($oPage, $oValue);
+ $aRowActions = $this->GetRowActions();
+ $oDatatable = DataTableUIBlockFactory::MakeForForm($this->oUILinksDirectWidget->GetInputId(), $aAttribs, $aRows, '', $aRowActions);
+ $oDatatable->SetOptions(['select_mode' => 'custom', 'disable_hyperlinks' => true]);
+ $aTablePanel = PanelUIBlockFactory::MakeNeutral('');
+ $aTablePanel->SetSubTitle(sprintf('Total: %d objects.', count($aRows)));
+ $oToolbar = ToolbarUIBlockFactory::MakeForButton();
+ $oActionButtonUnlink = ButtonUIBlockFactory::MakeNeutral('Unlink');
+ $oActionButtonUnlink->SetOnClickJsCode("$('#{$this->oUILinksDirectWidget->GetInputId()}').directlinks('instance')._removeSelection();");
+ $oToolbar->AddSubBlock($oActionButtonUnlink);
+ $oActionButtonLink = ButtonUIBlockFactory::MakeNeutral('Link');
+ $oActionButtonLink->SetOnClickJsCode("$('#{$this->oUILinksDirectWidget->GetInputId()}').directlinks('instance')._selectToAdd();");
+ $oToolbar->AddSubBlock($oActionButtonLink);
+ $oActionButtonCreate = ButtonUIBlockFactory::MakeNeutral('Create');
+ $oActionButtonCreate->SetOnClickJsCode("$('#{$this->oUILinksDirectWidget->GetInputId()}').directlinks('instance')._createRow();");
+ $oToolbar->AddSubBlock($oActionButtonCreate);
+ $oActionButtonDelete = ButtonUIBlockFactory::MakeNeutral('Delete');
+ $oActionButtonDelete->SetOnClickJsCode("$('#{$this->oUILinksDirectWidget->GetInputId()}').directlinks('instance')._deleteSelection();");
+ $oToolbar->AddSubBlock($oActionButtonDelete);
+ $aTablePanel->AddToolbarBlock($oToolbar);
+ $aTablePanel->AddSubBlock($oDatatable);
+ $this->AddSubBlock($aTablePanel);
+ }
+ catch (\Exception $e) {
+ $oAlert = AlertUIBlockFactory::MakeForDanger('error', 'error while trying to load datatable');
+ $oAlert->SetIsClosable(false);
+ $oAlert->SetIsCollapsible(false);
+ $this->AddSubBlock($oAlert);
+ }
+ }
+
+ /**
+ * Return table rows.
+ *
+ * @param \DBObjectSet $oValue
+ *
+ * @return array
+ * @throws \ArchivedObjectException
+ * @throws \CoreException
+ * @throws \CoreUnexpectedValue
+ * @throws \DictExceptionMissingString
+ * @throws \MySQLException
+ * @throws \Exception
+ */
+ private function GetTableRows(\WebPage $oPage, \DBObjectSet $oValue): array
+ {
+ // result data
+ $aRows = array();
+
+ // set pointer to start
+ $oValue->Rewind();
+
+ // create a row table for each value...
+ while ($oLinkObj = $oValue->Fetch()) {
+ $aRow = array();
+ $aRow['form::select'] = ' ';
+ foreach ($this->oUILinksDirectWidget->GetZList() as $sLinkedAttCode) {
+ $aRow[$sLinkedAttCode] = $oLinkObj->GetAsHTML($sLinkedAttCode);
+
+ // tentative d'ajout des attributs en édition
+// $sValue = $oLinkObj->Get($sLinkedAttCode);
+// $sDisplayValue = $oLinkObj->GetEditValue($sLinkedAttCode);
+// $oAttDef = MetaModel::GetAttributeDef($this->oUILinksDirectWidget->GetLinkedClass(), $sLinkedAttCode);
+//
+// $aRow[$sLinkedAttCode] = ''
+// .\cmdbAbstractObject::GetFormElementForField(
+// $oPage,
+// $this->oUILinksDirectWidget->GetLinkedClass(),
+// $sLinkedAttCode,
+// $oAttDef,
+// $sValue,
+// $sDisplayValue,
+// $this->GetFieldId($oValue, $sLinkedAttCode),
+// ']',
+// 0,
+// []
+// )
+// .'
';
+ }
+ $aRows[] = $aRow;
+ }
+
+ return $aRows;
+ }
+
+ private function GetFieldId($iLnkId, $sFieldCode, $bSafe = true)
+ {
+ $sFieldId = $this->oUILinksDirectWidget->GetInputId().'_'.$sFieldCode.'['.$iLnkId.']';
+
+ return ($bSafe) ? \utils::GetSafeId($sFieldId) : $sFieldId;
+ }
+
+ /**
+ * Return row actions.
+ *
+ * @return \string[][]
+ */
+ private function GetRowActions(): array
+ {
+ $aRowActions = array();
+
+ if (!$this->oAttributeLinkedSet->GetReadOnly()) {
+ $aRowActions[] = array(
+ 'tooltip' => 'remove link',
+ 'icon_classes' => 'fas fa-minus',
+ 'js_row_action' => "$('#{$this->oUILinksDirectWidget->GetInputId()}').directlinks('instance')._deleteRow($(':checkbox', oTrElement));",
+ );
+ }
+
+ return $aRowActions;
+ }
+}
\ No newline at end of file
diff --git a/sources/Application/UI/Links/Direct/BlockDirectLinksViewTable.php b/sources/Application/UI/Links/Direct/BlockDirectLinksViewTable.php
new file mode 100644
index 000000000..aed230084
--- /dev/null
+++ b/sources/Application/UI/Links/Direct/BlockDirectLinksViewTable.php
@@ -0,0 +1,103 @@
+oAttDef->GetLinkedClass();
+ }
+
+ /** @inheritdoc * */
+ public function GetExtraParam(): array
+ {
+ return array(
+ 'target_attr' => $this->oAttDef->GetExtKeyToMe(),
+ 'object_id' => $this->oDbObject->GetKey(),
+ 'menu' => MetaModel::GetConfig()->Get('allow_menu_on_linkset'),
+ 'default' => $this->GetDefault(),
+ 'table_id' => $this->sObjectClass.'_'.$this->sAttCode,
+ 'row_actions' => $this->GetRowActions(),
+ );
+ }
+
+ /** @inheritdoc * */
+ public function GetRowActions(): array
+ {
+ $aRowActions = array();
+
+ if (!$this->oAttDef->GetReadOnly()) {
+
+ switch ($this->oAttDef->GetRelationType()) {
+
+ case LINKSET_RELATIONTYPE_LINK:
+ $aRowActions[] = array(
+ 'tooltip' => 'UI:Links:ActionRow:detach',
+ 'icon_classes' => 'fas fa-minus',
+ 'js_row_action' => "LinkSetWorker.DetachLinkedObject('{$this->sTargetClass}', aRowData['{$this->sTargetClass}/_key_/raw'], '{$this->oAttDef->GetExtKeyToMe()}');",
+ 'confirmation' => [
+ 'message' => 'UI:Links:ActionRow:detach:confirmation',
+ 'message_row_data' => "{$this->sTargetClass}/hyperlink",
+ 'remember_choice_pref_key' => 'LinkSetWorker.DetachLinkedObject',
+ ],
+ );
+ break;
+
+ case LINKSET_RELATIONTYPE_PROPERTY:
+ $aRowActions[] = array(
+ 'tooltip' => 'UI:Links:ActionRow:delete',
+ 'icon_classes' => 'fas fa-trash',
+ 'js_row_action' => "LinkSetWorker.DeleteLinkedObject('{$this->oAttDef->GetLinkedClass()}', aRowData['{$this->oAttDef->GetLinkedClass()}/_key_/raw']);",
+ 'confirmation' => [
+ 'message' => 'UI:Links:ActionRow:delete:confirmation',
+ 'message_row_data' => "{$this->sTargetClass}/hyperlink",
+ 'remember_choice_pref_key' => 'LinkSetWorker.DeleteLinkedObject',
+ ],
+ );
+ break;
+ }
+ }
+
+ return $aRowActions;
+ }
+
+ /**
+ * GetDefault.
+ *
+ * @return array
+ * @throws \ArchivedObjectException
+ * @throws \CoreException
+ * @throws \Exception
+ */
+ private function GetDefault(): array
+ {
+ $aDefaults = array($this->oAttDef->GetExtKeyToMe() => $this->oDbObject->GetKey());
+ $oAppContext = new \ApplicationContext();
+ foreach ($oAppContext->GetNames() as $sKey) {
+ if (MetaModel::IsValidAttCode($this->sObjectClass, $sKey)) {
+ $aDefaults[$sKey] = $this->oDbObject->Get($sKey);
+ }
+ }
+
+ return $aDefaults;
+ }
+}
\ No newline at end of file
diff --git a/sources/Application/UI/Links/Indirect/BlockIndirectLinksEdit/BlockIndirectLinksEdit.php b/sources/Application/UI/Links/Indirect/BlockIndirectLinksEdit/BlockIndirectLinksEdit.php
deleted file mode 100644
index 81002fe49..000000000
--- a/sources/Application/UI/Links/Indirect/BlockIndirectLinksEdit/BlockIndirectLinksEdit.php
+++ /dev/null
@@ -1,75 +0,0 @@
-AddSubBlock(InputUIBlockFactory::MakeForHidden("{$this->sFormPrefix}{$this->iInputId}", '', "{$this->sFormPrefix}{$this->iInputId}"));
-
- $oToolbar = ToolbarUIBlockFactory::MakeStandard(null, ['ibo-datatable--selection-validation-buttons-toolbar']);
- $this->AddSubBlock($oToolbar);
- $oRemoveButton = ButtonUIBlockFactory::MakeForSecondaryAction(Dict::S('UI:RemoveLinkedObjectsOf_Class'), null, null, false, "{$this->sLinkedSetId}_btnRemove");
- $oRemoveButton->SetOnClickJsCode("oWidget{$this->iInputId}.RemoveSelected();");
- $oToolbar->AddSubBlock($oRemoveButton);
-
- $oAddButton = ButtonUIBlockFactory::MakeForSecondaryAction(Dict::Format('UI:AddLinkedObjectsOf_Class', MetaModel::GetName($this->sRemoteClass)), null, null, false, "{$this->sLinkedSetId}_btnAdd");
- $oAddButton->SetOnClickJsCode("oWidget{$this->iInputId}.AddObjects();");
- $oToolbar->AddSubBlock($oAddButton);
-
- // To prevent adding forms inside the main form
- $oDeferredBlock = new UIContentBlock("dlg_{$this->sLinkedSetId}", ['ibo-block-indirect-links--edit--dialog']);
- $this->AddDeferredBlock($oDeferredBlock);
- }
-}
\ No newline at end of file
diff --git a/sources/Application/UI/Links/Indirect/BlockIndirectLinksEditTable.php b/sources/Application/UI/Links/Indirect/BlockIndirectLinksEditTable.php
new file mode 100644
index 000000000..6d740ba15
--- /dev/null
+++ b/sources/Application/UI/Links/Indirect/BlockIndirectLinksEditTable.php
@@ -0,0 +1,442 @@
+GetLinkedSetId()}", ["ibo-block-indirect-links--edit"]);
+
+ // Retrieve parameters
+ $this->oUILinksWidget = $oUILinksWidget;
+
+ // Compute
+ $this->sDuplicates = ($oUILinksWidget->IsDuplicatesAllowed()) ? 'true' : 'false';
+ $this->sJSDoSearch = \utils::IsHighCardinality($oUILinksWidget->GetRemoteClass()) ? 'false' : 'true'; // Don't automatically launch the search if the table is huge
+
+ // Initialization
+ $this->Init();
+
+ // Initialize UI
+ $this->InitUI();
+ }
+
+ /**
+ * Initialization.
+ *
+ * @return void
+ * @throws \Exception
+ */
+ private function Init()
+ {
+ $this->oAttributeLinkedSetIndirect = MetaModel::GetAttributeDef($this->oUILinksWidget->GetClass(), $this->oUILinksWidget->GetAttCode());
+ }
+
+ /**
+ * Initialize UI.
+ *
+ * @return void
+ * @throws \CoreException
+ * @throws \Exception
+ */
+ private function InitUI()
+ {
+ // MedallionIcon
+ $oClassIcon = new MedallionIcon(MetaModel::GetClassIcon($this->oUILinksWidget->GetRemoteClass(), false));
+ $oClassIcon->SetDescription($this->oAttributeLinkedSetIndirect->GetDescription());
+ $oClassIcon->AddCSSClass('ibo-block-list--medallion');
+ $this->AddSubBlock($oClassIcon);
+
+ // To prevent adding forms inside the main form
+ $oDeferredBlock = new UIContentBlock("dlg_{$this->oUILinksWidget->GetLinkedSetId()}", ['ibo-block-indirect-links--edit--dialog']);
+ $this->AddDeferredBlock($oDeferredBlock);
+ }
+
+ /**
+ * CreateTableInformationAlert.
+ *
+ * @return iUIBlock
+ */
+ private function CreateTableInformationAlert(): iUIBlock
+ {
+ // Selection alert
+ $oAlert = AlertUIBlockFactory::MakeNeutral('', '', "linkedset_{$this->oUILinksWidget->GetInputId()}_alert_information");
+ $oAlert->AddCSSClasses([
+ 'ibo-table--alert-information',
+ ]);
+ $oAlert->SetIsClosable(false);
+ $oAlert->SetIsCollapsible(false);
+ $oAlert->AddSubBlock(new Html(' '));
+
+ // Delete button
+ $oUIButton = ButtonUIBlockFactory::MakeForDestructiveAction("Détacher les {$this->oUILinksWidget->GetRemoteClass()}", 'table-selection');
+ $oUIButton->SetOnClickJsCode("oWidget{$this->oUILinksWidget->GetInputId()}.RemoveSelected();");
+ $oUIButton->AddCSSClass('ibo-table--alert-information--delete-button');
+ $oAlert->AddSubBlock($oUIButton);
+
+ // Add button
+ $oUIAddButton = ButtonUIBlockFactory::MakeForPrimaryAction("Attacher des {$this->oUILinksWidget->GetRemoteClass()}", 'table-selection');
+ $oUIAddButton->AddCSSClass('ibo-table--alert-information--add-button');
+ $oUIAddButton->SetOnClickJsCode("oWidget{$this->oUILinksWidget->GetInputId()}.AddObjects();");
+ $oAlert->AddSubBlock($oUIAddButton);
+
+ return $oAlert;
+ }
+
+ /**
+ * @param \WebPage $oPage
+ * @param $oValue
+ * @param $aArgs
+ * @param $sFormPrefix
+ * @param $oCurrentObj
+ * @param $aTableConfig
+ *
+ * @return void
+ */
+ public function InitTable(\WebPage $oPage, $oValue, $aArgs, $sFormPrefix, $oCurrentObj, $aTableConfig)
+ {
+ $this->AddSubBlock(InputUIBlockFactory::MakeForHidden("{$sFormPrefix}{$this->oUILinksWidget->GetInputId()}", '', "{$sFormPrefix}{$this->oUILinksWidget->GetInputId()}"));
+ $this->sWizHelper = 'oWizardHelper'.$sFormPrefix;
+ $oValue->Rewind();
+ $aForm = array();
+ $iMaxAddedId = 0;
+ $iAddedId = -1; // Unique id for new links
+ $this->aRemoved = json_decode(\utils::ReadPostedParam("attr_{$sFormPrefix}{$this->oUILinksWidget->GetAttCode()}_tbd", '[]', 'raw_data'));
+ while ($oCurrentLink = $oValue->Fetch()) {
+ // We try to retrieve the remote object as usual
+ if (!in_array($oCurrentLink->GetKey(), $this->aRemoved)) {
+ $oLinkedObj = MetaModel::GetObject($this->oUILinksWidget->GetRemoteClass(), $oCurrentLink->Get($this->oUILinksWidget->GetExternalKeyToRemote()), false /* Must not be found */);
+ // If successful, it means that we can edit its link
+ if ($oLinkedObj !== null) {
+ $bReadOnly = false;
+ } // Else we retrieve it without restrictions (silos) and will display its link as readonly
+ else {
+ $bReadOnly = true;
+ $oLinkedObj = MetaModel::GetObject($this->oUILinksWidget->GetRemoteClass(), $oCurrentLink->Get($this->oUILinksWidget->GetExternalKeyToRemote()), false /* Must not be found */, true);
+ }
+
+ if ($oCurrentLink->IsNew()) {
+ $key = $iAddedId--;
+ } else {
+ $key = $oCurrentLink->GetKey();
+ }
+
+ $iMaxAddedId = max($iMaxAddedId, $key);
+ $aForm[$key] = $this->GetFormRow($oPage, $oLinkedObj, $oCurrentLink, $aArgs, $oCurrentObj, $key, $bReadOnly);
+ }
+ }
+ $this->iMaxAddedId = (int)$iMaxAddedId;
+
+ // Datatable
+ $aRowActions = $this->GetRowActions();
+ $oDataTable = DataTableUIBlockFactory::MakeForForm("{$this->oUILinksWidget->GetAttCode()}{$this->oUILinksWidget->GetNameSuffix()}", $aTableConfig, $aForm, '', $aRowActions);
+ $oDataTable->SetOptions([
+ 'select_mode' => 'custom',
+ 'disable_hyperlinks' => true,
+ ]);
+ $aTablePanel = PanelUIBlockFactory::MakeNeutral('');
+ $aTablePanel->SetSubTitle(sprintf('Total: %d objects.', count($aForm)));
+ $oToolbar = ToolbarUIBlockFactory::MakeForButton();
+ $oActionButtonUnlink = ButtonUIBlockFactory::MakeNeutral('Unlink');
+ $oActionButtonUnlink->SetOnClickJsCode("oWidget{$this->oUILinksWidget->GetInputId()}.RemoveSelected();");
+ $oToolbar->AddSubBlock($oActionButtonUnlink);
+ $oActionButtonLink = ButtonUIBlockFactory::MakeNeutral('Link');
+ $oActionButtonLink->SetOnClickJsCode("oWidget{$this->oUILinksWidget->GetInputId()}.AddObjects();");
+ $oToolbar->AddSubBlock($oActionButtonLink);
+ $aTablePanel->AddToolbarBlock($oToolbar);
+ $aTablePanel->AddSubBlock($oDataTable);
+
+ $this->AddSubBlock($aTablePanel);
+ }
+
+ /**
+ * A one-row form for editing a link record
+ *
+ * @param WebPage $oP Web page used for the ouput
+ * @param DBObject $oLinkedObj Remote object
+ * @param DBObject|int $linkObjOrId Either the lnk object or a unique number for new link records to add
+ * @param array $aArgs Extra context arguments
+ * @param DBObject $oCurrentObj The object to which all the elements of the linked set refer to
+ * @param int $iUniqueId A unique identifier of new links
+ * @param boolean $bReadOnly Display link as editable or read-only. Default is false (editable)
+ *
+ * @return array The HTML fragment of the one-row form
+ * @throws \ArchivedObjectException
+ * @throws \CoreException
+ * @throws \CoreUnexpectedValue
+ * @throws \Exception
+ */
+ public function GetFormRow(\WebPage $oP, \DBObject $oLinkedObj, $linkObjOrId, $aArgs, $oCurrentObj, $iUniqueId, $bReadOnly = false)
+ {
+ $sPrefix = "{$this->oUILinksWidget->GetAttCode()}{$this->oUILinksWidget->GetNameSuffix()}";
+ $aRow = array();
+ $aFieldsMap = array();
+ $iKey = 0;
+
+ if (is_object($linkObjOrId) && (!$linkObjOrId->IsNew())) {
+ $iKey = $linkObjOrId->GetKey();
+ $iRemoteObjKey = $linkObjOrId->Get($this->oUILinksWidget->GetExternalKeyToRemote());
+ $sPrefix .= "[$iKey][";
+ $sNameSuffix = "]"; // To make a tabular form
+ $aArgs['prefix'] = $sPrefix;
+ $aArgs['wizHelper'] = "oWizardHelper{$this->oUILinksWidget->GetInputId()}{$iKey}";
+ $aArgs['this'] = $linkObjOrId;
+
+ if ($bReadOnly) {
+ $aRow['form::checkbox'] = "";
+ foreach ($this->m_aEditableFields as $sFieldCode) {
+ $sDisplayValue = $linkObjOrId->GetEditValue($sFieldCode);
+ $aRow[$sFieldCode] = $sDisplayValue;
+ }
+ } else {
+ $aRow['form::checkbox'] = " oUILinksWidget->GetInputId()}.OnSelectChange();\" value=\"$iKey\">";
+ foreach ($this->oUILinksWidget->GetEditableFields() as $sFieldCode) {
+ $sSafeFieldId = $this->GetFieldId($linkObjOrId->GetKey(), $sFieldCode);
+ $this->AddRowForFieldCode($aRow, $sFieldCode, $aArgs, $linkObjOrId, $oP, $sNameSuffix, $sSafeFieldId);
+ $aFieldsMap[$sFieldCode] = $sSafeFieldId;
+ }
+ }
+
+ $sState = $linkObjOrId->GetState();
+ $sRemoteKeySafeFieldId = $this->GetFieldId($aArgs['this']->GetKey(), $this->oUILinksWidget->GetExternalKeyToRemote());;
+ } else {
+ // form for creating a new record
+ if (is_object($linkObjOrId)) {
+ // New link existing only in memory
+ $oNewLinkObj = $linkObjOrId;
+ $iRemoteObjKey = $oNewLinkObj->Get($this->oUILinksWidget->GetExternalKeyToRemote());
+ $oNewLinkObj->Set($this->oUILinksWidget->GetExternalKeyToMe(),
+ $oCurrentObj); // Setting the extkey with the object also fills the related external fields
+ } else {
+ $iRemoteObjKey = $linkObjOrId;
+ $oNewLinkObj = MetaModel::NewObject($this->oUILinksWidget->GetLinkedClass());
+ $oRemoteObj = MetaModel::GetObject($this->oUILinksWidget->GetRemoteClass(), $iRemoteObjKey);
+ $oNewLinkObj->Set($this->oUILinksWidget->GetExternalKeyToRemote(),
+ $oRemoteObj); // Setting the extkey with the object alsoo fills the related external fields
+ $oNewLinkObj->Set($this->oUILinksWidget->GetExternalKeyToMe(),
+ $oCurrentObj); // Setting the extkey with the object also fills the related external fields
+ }
+ $sPrefix .= "[-$iUniqueId][";
+ $sNameSuffix = "]"; // To make a tabular form
+ $aArgs['prefix'] = $sPrefix;
+ $aArgs['wizHelper'] = "oWizardHelper{$this->oUILinksWidget->GetInputId()}_".($iUniqueId < 0 ? -$iUniqueId : $iUniqueId);
+ $aArgs['this'] = $oNewLinkObj;
+ $sInputValue = $iUniqueId > 0 ? "-$iUniqueId" : "$iUniqueId";
+ $aRow['form::checkbox'] = " oUILinksWidget->GetInputId()}.OnSelectChange();\" value=\"$sInputValue\">";
+
+ if ($iUniqueId > 0) {
+ // Rows created with ajax call need OnLinkAdded call.
+ //
+ $oP->add_ready_script(
+ <<oUILinksWidget->GetInputId()}.OnLinkAdded($iUniqueId, $iRemoteObjKey);
+EOF
+ );
+ } else {
+ // Rows added before loading the form don't have to call OnLinkAdded.
+ // Listeners are already present and DOM is not recreated
+ $iPositiveUniqueId = -$iUniqueId;
+ $oP->add_ready_script(<<oUILinksWidget->GetInputId()}.AddLink($iPositiveUniqueId, $iRemoteObjKey);
+EOF
+ );
+ }
+
+ foreach ($this->oUILinksWidget->GetEditableFields() as $sFieldCode) {
+ $sSafeFieldId = $this->GetFieldId($iUniqueId, $sFieldCode);
+ $this->AddRowForFieldCode($aRow, $sFieldCode, $aArgs, $oNewLinkObj, $oP, $sNameSuffix, $sSafeFieldId);
+ $aFieldsMap[$sFieldCode] = $sSafeFieldId;
+
+ $sValue = $oNewLinkObj->Get($sFieldCode);
+ $oP->add_ready_script(
+ <<oUILinksWidget->GetInputId()}.OnValueChange($iKey, $iUniqueId, '$sFieldCode', '$sValue');
+JS
+ );
+ }
+
+ $sState = '';
+ $sRemoteKeySafeFieldId = $this->GetFieldId($iUniqueId, $this->oUILinksWidget->GetExternalKeyToRemote());
+ }
+
+ if (!$bReadOnly) {
+ $sExtKeyToMeId = \utils::GetSafeId($sPrefix.$this->oUILinksWidget->GetExternalKeyToMe());
+ $aFieldsMap[$this->oUILinksWidget->GetExternalKeyToMe()] = $sExtKeyToMeId;
+ $aRow['form::checkbox'] .= " GetKey()."\">";
+
+ $sExtKeyToRemoteId = \utils::GetSafeId($sPrefix.$this->oUILinksWidget->GetExternalKeyToRemote());
+ $aFieldsMap[$this->oUILinksWidget->GetExternalKeyToRemote()] = $sExtKeyToRemoteId;
+ $aRow['form::checkbox'] .= " ";
+ }
+
+ // Adding fields from remote class
+ // all fields are embedded in a span + added to $aFieldsMap array so that we can refresh them after extkey change
+ $aRemoteFieldsMap = [];
+ foreach (MetaModel::GetZListItems($this->oUILinksWidget->GetRemoteClass(), 'list') as $sFieldCode) {
+ $sSafeFieldId = $this->GetFieldId($aArgs['this']->GetKey(), $sFieldCode);
+ $aRow['static::'.$sFieldCode] = "".$oLinkedObj->GetAsHTML($sFieldCode).' ';
+ $aRemoteFieldsMap[$sFieldCode] = $sSafeFieldId;
+ }
+ // id field is needed so that remote object could be load server side
+ $aRemoteFieldsMap['id'] = $sRemoteKeySafeFieldId;
+
+ // Generate WizardHelper to update dependant fields
+ $this->AddWizardHelperInit($oP, $aArgs['wizHelper'], $this->oUILinksWidget->GetLinkedClass(), $sState, $aFieldsMap);
+ //instantiate specific WizarHelper instance for remote class fields refresh
+ $bHasExtKeyUpdatingRemoteClassFields = (
+ array_key_exists('replaceDependenciesByRemoteClassFields', $aArgs)
+ && ($aArgs['replaceDependenciesByRemoteClassFields'])
+ );
+ if ($bHasExtKeyUpdatingRemoteClassFields) {
+ $this->AddWizardHelperInit($oP, $aArgs['wizHelperRemote'], $this->oUILinksWidget->GetRemoteClass(), $sState, $aRemoteFieldsMap);
+ }
+
+ return $aRow;
+ }
+
+ private function AddRowForFieldCode(&$aRow, $sFieldCode, &$aArgs, $oLnk, $oP, $sNameSuffix, $sSafeFieldId): void
+ {
+ if (($sFieldCode === $this->oUILinksWidget->GetExternalKeyToRemote())) {
+ // current field is the lnk extkey to the remote class
+ $aArgs['replaceDependenciesByRemoteClassFields'] = true;
+ $sRowFieldCode = 'static::key';
+ $aArgs['wizHelperRemote'] = $aArgs['wizHelper'].'_remote';
+ $aRemoteAttDefs = MetaModel::GetZListAttDefsFilteredForIndirectRemoteClass($this->oUILinksWidget->GetRemoteClass());
+ $aRemoteCodes = array_map(
+ function ($value) {
+ return $value->GetCode();
+ },
+ $aRemoteAttDefs
+ );
+ $aArgs['remoteCodes'] = $aRemoteCodes;
+ } else {
+ $aArgs['replaceDependenciesByRemoteClassFields'] = false;
+ $sRowFieldCode = $sFieldCode;
+ }
+ $sValue = $oLnk->Get($sFieldCode);
+ $sDisplayValue = $oLnk->GetEditValue($sFieldCode);
+ $oAttDef = MetaModel::GetAttributeDef($this->oUILinksWidget->GetLinkedClass(), $sFieldCode);
+
+ $aRow[$sRowFieldCode] = ''
+ .\cmdbAbstractObject::GetFormElementForField(
+ $oP,
+ $this->oUILinksWidget->GetLinkedClass(),
+ $sFieldCode,
+ $oAttDef,
+ $sValue,
+ $sDisplayValue,
+ $sSafeFieldId,
+ $sNameSuffix,
+ 0,
+ $aArgs
+ )
+ .'
';
+ }
+
+ private function GetFieldId($iLnkId, $sFieldCode, $bSafe = true)
+ {
+ $sFieldId = $this->oUILinksWidget->GetInputId().'_'.$sFieldCode.'['.$iLnkId.']';
+
+ return ($bSafe) ? \utils::GetSafeId($sFieldId) : $sFieldId;
+ }
+
+ private function AddWizardHelperInit($oP, $sWizardHelperVarName, $sWizardHelperClass, $sState, $aFieldsMap): void
+ {
+ $iFieldsCount = count($aFieldsMap);
+ $sJsonFieldsMap = json_encode($aFieldsMap);
+
+ $oP->add_script(
+ <<oAttributeLinkedSetIndirect->GetReadOnly()) {
+ $aRowActions[] = array(
+ 'tooltip' => 'remove link',
+ 'icon_classes' => 'fas fa-minus',
+ 'js_row_action' => "oWidget{$this->oUILinksWidget->GetInputId()}.Remove(oTrElement);",
+ );
+ }
+
+ return $aRowActions;
+ }
+
+}
\ No newline at end of file
diff --git a/sources/Application/UI/Links/Indirect/BlockIndirectLinksViewTable.php b/sources/Application/UI/Links/Indirect/BlockIndirectLinksViewTable.php
new file mode 100644
index 000000000..db9114990
--- /dev/null
+++ b/sources/Application/UI/Links/Indirect/BlockIndirectLinksViewTable.php
@@ -0,0 +1,109 @@
+oAttDef->GetLinkedClass(), $this->oAttDef->GetExtKeyToRemote());
+
+ return $oLinkingAttDef->GetTargetClass();
+ }
+ catch (Exception $e) {
+ return '?';
+ }
+ }
+
+ /** @inheritdoc */
+ public function GetExtraParam(): array
+ {
+ return array(
+ 'link_attr' => $this->oAttDef->GetExtKeyToMe(),
+ 'object_id' => $this->oDbObject->GetKey(),
+ 'target_attr' => $this->oAttDef->GetExtKeyToRemote(),
+ 'view_link' => false,
+ 'menu' => false,
+ 'display_limit' => true,
+ 'table_id' => $this->sObjectClass.'_'.$this->sAttCode,
+ 'zlist' => false,
+ 'extra_fields' => $this->GetAttCodesToDisplay(),
+ 'row_actions' => $this->GetRowActions(),
+ );
+ }
+
+ /** @inheritdoc */
+ public function GetRowActions(): array
+ {
+ $aRowActions = array();
+
+ if (!$this->oAttDef->GetReadOnly()) {
+
+ $aRowActions[] = array(
+ 'tooltip' => 'UI:Links:ActionRow:detach',
+ 'icon_classes' => 'fas fa-minus',
+ 'js_row_action' => "LinkSetWorker.DeleteLinkedObject('{$this->oAttDef->GetLinkedClass()}', aRowData['Link/_key_/raw']);",
+ 'confirmation' => [
+ 'message' => 'UI:Links:ActionRow:detach:confirmation',
+ 'message_row_data' => "Remote/hyperlink",
+ 'remember_choice_pref_key' => 'LinkSetWorker.DetachLinkedObject',
+ ],
+ );
+
+ }
+
+ return $aRowActions;
+ }
+
+ /**
+ * GetAttCodesToDisplay.
+ *
+ * @return string
+ * @throws \CoreException
+ */
+ private function GetAttCodesToDisplay(): string
+ {
+ $oLinkingAttDef = MetaModel::GetAttributeDef($this->oAttDef->GetLinkedClass(), $this->oAttDef->GetExtKeyToRemote());
+ $sLinkingAttCode = $oLinkingAttDef->GetCode();
+ $sTargetClass = $oLinkingAttDef->GetTargetClass();
+
+ $aLnkAttDefsToDisplay = MetaModel::GetZListAttDefsFilteredForIndirectLinkClass($this->sObjectClass, $this->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);
+
+ return implode(',', $aAttCodesToDisplay);
+ }
+}
\ No newline at end of file
diff --git a/sources/Application/UI/Links/Indirect/BlockObjectPickerDialog/BlockObjectPickerDialog.php b/sources/Application/UI/Links/Indirect/BlockObjectPickerDialog.php
similarity index 52%
rename from sources/Application/UI/Links/Indirect/BlockObjectPickerDialog/BlockObjectPickerDialog.php
rename to sources/Application/UI/Links/Indirect/BlockObjectPickerDialog.php
index 5762d7adf..5d3350cd2 100644
--- a/sources/Application/UI/Links/Indirect/BlockObjectPickerDialog/BlockObjectPickerDialog.php
+++ b/sources/Application/UI/Links/Indirect/BlockObjectPickerDialog.php
@@ -4,31 +4,40 @@
* @license http://opensource.org/licenses/AGPL-3.0
*/
-namespace Combodo\iTop\Application\UI\Links\Indirect\BlockObjectPickerDialog;
+namespace Combodo\iTop\Application\UI\Links\Indirect;
-
-use Combodo\iTop\Application\UI\Base\Component\Button\ButtonUIBlockFactory;
use Combodo\iTop\Application\UI\Base\Component\Form\Form;
use Combodo\iTop\Application\UI\Base\Component\Input\InputUIBlockFactory;
-use Combodo\iTop\Application\UI\Base\Component\Toolbar\ToolbarUIBlockFactory;
use Combodo\iTop\Application\UI\Base\Layout\UIContentBlock;
use Dict;
/**
* Class BlockObjectPickerDialog
*
- * @package Combodo\iTop\Application\UI\Links\Indirect\BlockObjectPickerDialog
+ * @internal
+ * @package Combodo\iTop\Application\UI\Links\Indirect
*/
class BlockObjectPickerDialog extends UIContentBlock
{
// Overloaded constants
- public const BLOCK_CODE = 'ibo-block-object-picker-dialog';
+ public const BLOCK_CODE = 'ibo-block-object-picker-dialog';
public const DEFAULT_JS_ON_READY_TEMPLATE_REL_PATH = 'application/links/indirect/block-object-picker-dialog/layout';
- public $sLinkedSetId;
- public $iInputId;
- public $sLinkedClassName;
- public $sClassName;
+ /** @var \UILinksWidget */
+ public \UILinksWidget $oUILinksWidget;
+
+ /**
+ * Constructor.
+ *
+ * @param \UILinksWidget $oUILinksWidget
+ */
+ public function __construct(\UILinksWidget $oUILinksWidget)
+ {
+ parent::__construct();
+
+ // Retrieve parameters
+ $this->oUILinksWidget = $oUILinksWidget;
+ }
public function AddForm()
{
@@ -36,13 +45,13 @@ class BlockObjectPickerDialog extends UIContentBlock
$sCancel = Dict::S('UI:Button:Cancel');
$sAdd = Dict::S('UI:Button:Add');
- $oForm = new Form("ObjectsAddForm_{$this->sLinkedSetId}");
+ $oForm = new Form("ObjectsAddForm_{$this->oUILinksWidget->GetLinkedSetId()}");
$this->AddSubBlock($oForm);
- $oBlock = new UIContentBlock("SearchResultsToAdd_{$this->sLinkedSetId}", ['ibo-block-object-picker-dialog--results']);
+ $oBlock = new UIContentBlock("SearchResultsToAdd_{$this->oUILinksWidget->GetLinkedSetId()}", ['ibo-block-object-picker-dialog--results']);
$oForm->AddSubBlock($oBlock);
$oBlock->AddHtml("{$sEmptyList}
");
- $oForm->AddSubBlock(InputUIBlockFactory::MakeForHidden("count_{$this->sLinkedSetId}", '0', "count_{$this->sLinkedSetId}"));
+ $oForm->AddSubBlock(InputUIBlockFactory::MakeForHidden("count_{$this->oUILinksWidget->GetLinkedSetId()}", '0', "count_{$this->oUILinksWidget->GetLinkedSetId()}"));
}
}
\ No newline at end of file
diff --git a/sources/Controller/Links/LinksetController.php b/sources/Controller/Links/LinksetController.php
new file mode 100644
index 000000000..90811a1fa
--- /dev/null
+++ b/sources/Controller/Links/LinksetController.php
@@ -0,0 +1,103 @@
+DBDelete();
+ $bOperationSuccess = (count($oDeletionPlan->GetIssues()) === 0);
+ if (!$bOperationSuccess) {
+ $sErrorMessage = json_encode($oDeletionPlan->GetIssues());
+ }
+ }
+ catch (\Exception $e) {
+ $sErrorMessage = $e->getMessage();
+ }
+ } else {
+ $sErrorMessage = 'invalid transaction id';
+ }
+ $oPage->SetData([
+ 'success' => $bOperationSuccess,
+ 'error_message' => $sErrorMessage,
+ ]);
+
+ return $oPage;
+ }
+
+ /**
+ * OperationDetachLinkedObject.
+ *
+ * @return \JsonPage
+ */
+ public function OperationDetachLinkedObject(): \JsonPage
+ {
+ $oPage = new \JsonPage();
+ $sErrorMessage = null;
+ $bOperationSuccess = false;
+
+ // retrieve parameters
+ $sLinkedObjectClass = utils::ReadParam('linked_object_class', '', false, utils::ENUM_SANITIZATION_FILTER_CLASS);
+ $sLinkedObjectKey = utils::ReadParam('linked_object_key', 0, false, utils::ENUM_SANITIZATION_FILTER_STRING);
+ $sExternalKeyAttCode = utils::ReadParam('external_key_att_code', null, false, utils::ENUM_SANITIZATION_FILTER_STRING);
+ $sTransactionId = utils::ReadParam('transaction_id', null, false, utils::ENUM_SANITIZATION_FILTER_TRANSACTION_ID);
+
+ // check transaction id
+ if (utils::IsTransactionValid($sTransactionId, false)) {
+ try {
+ $oLinkedObject = MetaModel::GetObject($sLinkedObjectClass, $sLinkedObjectKey);
+ $oLinkedObject->Set($sExternalKeyAttCode, null);
+ $oLinkedObject->DBWrite();
+ $bOperationSuccess = true;
+ }
+ catch (\Exception $e) {
+ $sErrorMessage = $e->getMessage();
+ }
+ } else {
+ $sErrorMessage = 'invalid transaction id';
+ }
+ $oPage->SetData([
+ 'success' => $bOperationSuccess,
+ 'error_message' => $sErrorMessage,
+ ]);
+
+ return $oPage;
+ }
+
+
+}
\ No newline at end of file
diff --git a/templates/application/links/direct/block-direct-links-edit-table/layout.js.twig b/templates/application/links/direct/block-direct-links-edit-table/layout.js.twig
new file mode 100644
index 000000000..73340066b
--- /dev/null
+++ b/templates/application/links/direct/block-direct-links-edit-table/layout.js.twig
@@ -0,0 +1,12 @@
+{# @copyright Copyright (C) 2010-2021 Combodo SARL #}
+{# @license http://opensource.org/licenses/AGPL-3.0 #}
+{% apply spaceless %}
+oWidget{{ oUIBlock.oUILinksDirectWidget.GetInputId() }} = $('#{{ oUIBlock.oUILinksDirectWidget.GetInputId() }}').directlinks({
+ class_name: '{{ oUIBlock.oUILinksDirectWidget.GetClass() }}',
+ att_code: '{{ oUIBlock.oUILinksDirectWidget.GetAttCode() }}',
+ input_name: '{{ oUIBlock.sInputName }}',
+ submit_to: '{{ oUIBlock.sSubmitUrl }}',
+ oWizardHelper: {{ oUIBlock.sWizHelper }},
+ do_search: '{{ oUIBlock.sJSDoSearch }}'
+});
+{% endapply %}
\ No newline at end of file
diff --git a/templates/application/links/indirect/block-indirect-links-edit-table/layout.js.twig b/templates/application/links/indirect/block-indirect-links-edit-table/layout.js.twig
new file mode 100644
index 000000000..205769c60
--- /dev/null
+++ b/templates/application/links/indirect/block-indirect-links-edit-table/layout.js.twig
@@ -0,0 +1,18 @@
+{# @copyright Copyright (C) 2010-2021 Combodo SARL #}
+{# @license http://opensource.org/licenses/AGPL-3.0 #}
+{% apply spaceless %}
+oWidget{{ oUIBlock.oUILinksWidget.GetInputId() }} = new LinksWidget(
+ '{{ oUIBlock.oUILinksWidget.GetLinkedSetId() }}',
+ '{{ oUIBlock.oUILinksWidget.GetClass() }}',
+ '{{ oUIBlock.oUILinksWidget.GetAttCode() }}',
+ '{{ oUIBlock.oUILinksWidget.GetInputId() }}',
+ '{{ oUIBlock.oUILinksWidget.GetNameSuffix() }}',
+ {{ oUIBlock.sDuplicates }},
+ {{ oUIBlock.sWizHelper }},
+ '{{ oUIBlock.oUILinksWidget.GetExternalKeyToRemote() }}',
+ {{ oUIBlock.sJSDoSearch }},
+ {{ oUIBlock.iMaxAddedId }},
+ {{ oUIBlock.aRemoved | json_encode | raw }}
+);
+oWidget{{ oUIBlock.oUILinksWidget.GetInputId() }}.Init();
+{% endapply %}
\ No newline at end of file
diff --git a/templates/application/links/indirect/block-indirect-links-edit/layout.js.twig b/templates/application/links/indirect/block-indirect-links-edit/layout.js.twig
deleted file mode 100644
index d94ed6e31..000000000
--- a/templates/application/links/indirect/block-indirect-links-edit/layout.js.twig
+++ /dev/null
@@ -1,18 +0,0 @@
-{# @copyright Copyright (C) 2010-2021 Combodo SARL #}
-{# @license http://opensource.org/licenses/AGPL-3.0 #}
-{% apply spaceless %}
-oWidget{{ oUIBlock.iInputId }} = new LinksWidget(
- '{{ oUIBlock.sLinkedSetId }}',
- '{{ oUIBlock.sClass }}',
- '{{ oUIBlock.sAttCode }}',
- '{{ oUIBlock.iInputId }}',
- '{{ oUIBlock.sNameSuffix }}',
- {{ oUIBlock.bDuplicates }},
- {{ oUIBlock.oWizHelper }},
- '{{ oUIBlock.sExtKeyToRemote }}',
- {{ oUIBlock.bJSDoSearch }},
- {{ oUIBlock.iMaxAddedId }},
- {{ oUIBlock.aRemoved | json_encode | raw }}
-);
-oWidget{{ oUIBlock.iInputId }}.Init();
-{% endapply %}
\ No newline at end of file
diff --git a/templates/application/links/indirect/block-object-picker-dialog/layout.ready.js.twig b/templates/application/links/indirect/block-object-picker-dialog/layout.ready.js.twig
index aa812c42d..88f4f5c71 100644
--- a/templates/application/links/indirect/block-object-picker-dialog/layout.ready.js.twig
+++ b/templates/application/links/indirect/block-object-picker-dialog/layout.ready.js.twig
@@ -2,13 +2,13 @@
{# @license http://opensource.org/licenses/AGPL-3.0 #}
{% apply spaceless %}
-$('#dlg_{{ oUIBlock.sLinkedSetId }}').dialog({
+$('#dlg_{{ oUIBlock.oUILinksWidget.GetLinkedSetId() }}').dialog({
width: $(window).width()*0.8,
height: $(window).height()*0.8,
- title:"{{ 'UI:AddObjectsOf_Class_LinkedWith_Class'|dict_format(oUIBlock.sLinkedClassName, oUIBlock.sClassName) }}" ,
+ title:"{{ 'UI:AddObjectsOf_Class_LinkedWith_Class'|dict_format(oUIBlock.oUILinksWidget.GetLinkedClass(), oUIBlock.oUILinksWidget.GetClass()) }}" ,
autoOpen: false,
modal: true,
- resizeStop: oWidget{{ oUIBlock.iInputId }}.UpdateSizes,
+ resizeStop: oWidget{{ oUIBlock.oUILinksWidget.GetInputId() }}.UpdateSizes,
buttons: [
{
text: "{{ 'UI:Button:Cancel'| dict_s }}",
@@ -20,16 +20,16 @@ $('#dlg_{{ oUIBlock.sLinkedSetId }}').dialog({
{
text: "{{ 'UI:Button:Add'| dict_s }}",
class: "ibo-is-regular ibo-is-primary",
- id: "btn_ok_{{ oUIBlock.sLinkedSetId }}",
+ id: "btn_ok_{{ oUIBlock.oUILinksWidget.GetLinkedSetId() }}",
click: function() {
- return oWidget{{ oUIBlock.iInputId }}.DoAddObjects();
+ return oWidget{{ oUIBlock.oUILinksWidget.GetInputId() }}.DoAddObjects();
}
},
],
});
-$('#SearchFormToAdd_{{ oUIBlock.sLinkedSetId }} form').bind('submit.uilinksWizard', oWidget{{ oUIBlock.iInputId }}.SearchObjectsToAdd);
-$('#SearchFormToAdd_{{ oUIBlock.sLinkedSetId }}').resize(oWidget{{ oUIBlock.iInputId }}.UpdateSizes);
+$('#SearchFormToAdd_{{ oUIBlock.oUILinksWidget.GetLinkedSetId() }} form').bind('submit.uilinksWizard', oWidget{{ oUIBlock.oUILinksWidget.GetInputId() }}.SearchObjectsToAdd);
+$('#SearchFormToAdd_{{ oUIBlock.oUILinksWidget.GetLinkedSetId() }}').resize(oWidget{{ oUIBlock.oUILinksWidget.GetInputId() }}.UpdateSizes);
{% endapply %}
\ No newline at end of file
diff --git a/templates/base/components/datatable/layout.html.twig b/templates/base/components/datatable/layout.html.twig
index 9ec38c93a..bc72bca3c 100644
--- a/templates/base/components/datatable/layout.html.twig
+++ b/templates/base/components/datatable/layout.html.twig
@@ -21,11 +21,14 @@
{% endfor %}
{% if oUIBlock.HasRowActions() %}
-
+
{% endif %}
{% if oUIBlock.HasRowActions() %}
{{ render_block(oUIBlock.GetRowActionsTemplate()) }}
+ {% if not oUIBlock.GetRowActionsConfirmDialogInitializedFlag() %}
+ {{ render_block(oUIBlock.GetRowActionsConfirmDialog()) }}
+ {% endif %}
{% endif %}
\ No newline at end of file
diff --git a/templates/base/components/datatable/layout.ready.js.twig b/templates/base/components/datatable/layout.ready.js.twig
index 64198fa85..887370c4d 100644
--- a/templates/base/components/datatable/layout.ready.js.twig
+++ b/templates/base/components/datatable/layout.ready.js.twig
@@ -423,4 +423,6 @@ if(window.ResizeObserver){
oTable{{ sListIDForVarSuffix }}Resize.observe($('#{{ oUIBlock.GetId() }}')[0]);
}
-{% include 'base/components/datatable/row-actions/handler.js.twig' %}
\ No newline at end of file
+{% if oUIBlock.HasRowActions() %}
+ {% include 'base/components/datatable/row-actions/handler.js.twig' %}
+{% endif %}
\ No newline at end of file
diff --git a/templates/base/components/datatable/row-actions/handler.js.twig b/templates/base/components/datatable/row-actions/handler.js.twig
index 1bf8a0453..f1cfa1bf6 100644
--- a/templates/base/components/datatable/row-actions/handler.js.twig
+++ b/templates/base/components/datatable/row-actions/handler.js.twig
@@ -1,15 +1,51 @@
{# @copyright Copyright (C) 2010-2022 Combodo SARL #}
{# @license http://opensource.org/licenses/AGPL-3.0 #}
-// for each row action
-{% if oUIBlock.HasRowActions() %}
- {% for aAction in oUIBlock.GetRowActions() %}
- $('#{{ oUIBlock.GetId() }} tbody').on('click', 'button[data-action-id="{{ loop.index0 }}"]', function() {
- let iActionId = $(this).data('action-id');
- let oDatatable = $('#{{ oUIBlock.GetId() }}').DataTable();
- let oTrElement = $(this).closest('tr');
- let aData = oDatatable.row(oTrElement).data();
- {{ aAction.js_row_action|raw }};
- });
- {% endfor %}
-{% endif %}
\ No newline at end of file
+// for each row action...
+{% for aAction in oUIBlock.GetRowActions() %}
+
+ // register action buttons click
+ $('#{{ oUIBlock.GetId() }} tbody').on('click', 'button[data-action-id="{{ loop.index0 }}"]', function() {
+
+ // variables accessible from action row js
+ let oDatatable = $('#{{ oUIBlock.GetId() }}').DataTable();
+ let oTrElement = $(this).closest('tr');
+ let iActionId = $(this).data('action-id');
+ let aRowData = oDatatable.row(oTrElement).data();
+
+ {% if aAction.confirmation is defined %}
+
+ // Handle action row with confirmation
+ let sTitle = '{{ 'UI:Datatables:RowActions:ConfirmationDialog'|dict_s }}';
+ let sMessage = '{{ 'UI:Datatables:RowActions:ConfirmationMessage'|dict_s }}';
+ {% if aAction.confirmation.message is defined %}
+ sMessage = '{{ aAction.confirmation.message|dict_s|raw }}';
+ sMessage = sMessage.replaceAll('{item}', aRowData['{{ aAction.confirmation.message_row_data }}']);
+ {% endif %}
+ let sPrefKey = null;
+ {% if aAction.confirmation.remember_choice_pref_key is defined %}
+ sPrefKey = '{{ aAction.confirmation.remember_choice_pref_key }}';
+ {% endif %}
+ HandleActionRowConfirmation (sTitle, sMessage, sPrefKey, ActionRowFunction{{ oUIBlock.GetId() }}{{ loop.index0 }}, {
+ action_id: iActionId,
+ datatable: oDatatable,
+ tr_element: oTrElement,
+ row_data: aRowData
+ });
+
+ {% else %}
+
+ // Handle action row without confirmation
+ ActionRowFunction{{ oUIBlock.GetId() }}{{ loop.index0 }}(oDatatable, oTrElement, iActionId, aRowData);
+
+ {% endif %}
+
+ });
+
+ // action row function declaration
+ function ActionRowFunction{{ oUIBlock.GetId() }}{{ loop.index0 }}(oDatatable, oTrElement, iActionId, aRowData){
+ {{ aAction.js_row_action|raw }};
+ return true;
+ }
+
+{% endfor %}
diff --git a/templates/base/components/datatable/static/formtable/layout.html.twig b/templates/base/components/datatable/static/formtable/layout.html.twig
index 3da718895..11a807f62 100644
--- a/templates/base/components/datatable/static/formtable/layout.html.twig
+++ b/templates/base/components/datatable/static/formtable/layout.html.twig
@@ -21,4 +21,7 @@
{% if oUIBlock.HasRowActions() %}
{{ render_block(oUIBlock.GetRowActionsTemplate()) }}
+ {% if not oUIBlock.GetRowActionsConfirmDialogInitializedFlag() %}
+ {{ render_block(oUIBlock.GetRowActionsConfirmDialog()) }}
+ {% endif %}
{% endif %}
\ No newline at end of file
diff --git a/templates/base/components/datatable/static/layout.html.twig b/templates/base/components/datatable/static/layout.html.twig
index a7b9748b8..5e8d9030f 100644
--- a/templates/base/components/datatable/static/layout.html.twig
+++ b/templates/base/components/datatable/static/layout.html.twig
@@ -48,4 +48,7 @@
{% if oUIBlock.HasRowActions() %}
{{ render_block(oUIBlock.GetRowActionsTemplate()) }}
+ {% if not oUIBlock.GetRowActionsConfirmDialogInitializedFlag() %}
+ {{ render_block(oUIBlock.GetRowActionsConfirmDialog()) }}
+ {% endif %}
{% endif %}
\ No newline at end of file
diff --git a/templates/base/components/dialog/layout.html.twig b/templates/base/components/dialog/layout.html.twig
new file mode 100644
index 000000000..a86a86f21
--- /dev/null
+++ b/templates/base/components/dialog/layout.html.twig
@@ -0,0 +1,5 @@
+
+ {% for oSubBlock in oUIBlock.GetSubBlocks() %}
+ {{ render_block(oSubBlock, {aPage: aPage}) }}
+ {% endfor %}
+
\ No newline at end of file
diff --git a/templates/base/components/dialog/layout.ready.js.twig b/templates/base/components/dialog/layout.ready.js.twig
new file mode 100644
index 000000000..d37e9c573
--- /dev/null
+++ b/templates/base/components/dialog/layout.ready.js.twig
@@ -0,0 +1,4 @@
+$('#{{ oUIBlock.GetId() }}').alert({
+ bOpenedByDefault: {{ oUIBlock.IsOpenedByDefault()|var_export }}
+ {% if oUIBlock.IsSaveCollapsibleStateEnabled() %}, collapsibleStateStorageKey: '{{ oUIBlock.GetSessionCollapsibleStateStorageKey() }}'{% endif %}
+});
\ No newline at end of file