Issue/5074 - Routage, Block UI relations, Links row actions (#369)

* datatable row actions

Below is a sample of extra param to enable feature:

		$aExtraParams['row_actions'] = [
			[
				'tooltip'        => 'remove an element',
				'icon_css_class' => 'fa-minus',
				'js_row_action'   => 'console.log("You clicked the remove button");',
				'confirmation'  => [
					'message'                  => 'UI:ConfirmationMessage',
					'message_row_data'         => "name",
					'remember_choice_pref_key' => 'remove_do_not_show_again',
				],
			],
			[
				'tooltip'        => 'open in new tab',
				'icon_css_class' => 'fa-external-link-square-alt',
				'on_action_js'   => 'window.open("http://localhost/itop-branchs/dev/pages/UI.php?operation=details&class=UserRequest&id=" + aData.id + "&c[menu]=UserRequest%3AOpenRequests");',
			],
			[
				'tooltip'        => 'other actions',
				'icon_css_class' => 'fa-ellipsis-v',
				'on_action_js'   => 'console.log(event);',
			],
		];

* Contrôleur pour la suppression et le détachement de liens
* Block UI pour l'édition des relations
* Block UI pour la visualisation des relations
* Boutons d'actions pour la suppression et le détachement de liens
* Gestion dialogue de confirmation pour les row actions
This commit is contained in:
bdalsass
2022-11-23 16:10:34 +01:00
committed by GitHub
parent a0a5037554
commit bdb29fd99a
42 changed files with 2040 additions and 818 deletions

View File

@@ -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 = "<span id=\"field_{$sInputId}\">".self::GetFormElementForField($oPage, $sClass, $sAttCode,
$oAttDef, $oLinkSet, $sDisplayValue, $sInputId, '', $iFlags, $aArgs).'</span>';
$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);
}
/**

View File

@@ -1471,6 +1471,7 @@ JS
} else {
$iListId = $aExtraParams['currentId'];
}
$oBlock = DataTableUIBlockFactory::MakeForRendering($iListId, $oSet, $aExtraParams);
$oHtml->AddHtml("<tr><td>");
$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) {

View File

@@ -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("<a target=\"_blank\" href=\"".utils::GetAbsoluteUrlAppRoot()."pages/UI.php?operation=new&class=$sTargetClass&$sParams&{$sDefault}\">".Dict::Format('UI:ClickToCreateNew', Metamodel::GetName($sTargetClass))."</a>\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('</div></div>');
}
/**
* @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'] = '<input type="checkbox" class="selectList'.$this->sInputid.'" value="'.$oLinkObj->GetKey().'"/>';
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' => "<input type=\"checkbox\" onClick=\"CheckAll('.selectList{$this->sInputid}:not(:disabled)', this.checked);\" class=\"checkAll\"></input>", 'description' => Dict::S('UI:SelectAllToggle+'));
$aAttribs['form::select'] = array(
'label' => "<input type=\"checkbox\" onClick=\"CheckAll('.selectList{$this->sInputid}:not(:disabled)', this.checked);oWidget".$this->sInputid.".directlinks('instance')._onSelectChange();\" class=\"checkAll\"></input>",
'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;
}
}

View File

@@ -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' => "<input class=\"select_all\" type=\"checkbox\" value=\"1\" onClick=\"CheckAll('#linkedset_{$this->m_sAttCode}{$this->m_sNameSuffix} .selection', this.checked); oWidget".$this->m_iInputId.".OnSelectChange();\">",
'label' => "<input class=\"select_all\" type=\"checkbox\" value=\"1\" onClick=\"CheckAll('#linkedset_{$this->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'] = "<input class=\"selection\" data-remote-id=\"$iRemoteObjKey\" data-link-id=\"$iKey\" data-unique-id=\"$iUniqueId\" type=\"checkbox\" onClick=\"oWidget".$this->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'] = "<input class=\"selection\" data-remote-id=\"$iRemoteObjKey\" data-link-id=\"0\" data-unique-id=\"$iUniqueId\" type=\"checkbox\" onClick=\"oWidget".$this->m_iInputId.".OnSelectChange();\" value=\"$sInputValue\">";
if ($iUniqueId > 0)
{
// Rows created with ajax call need OnLinkAdded call.
//
$oP->add_ready_script(
<<<EOF
PrepareWidgets();
oWidget{$this->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(<<<EOF
oWidget{$this->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(
<<<JS
oWidget{$this->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'] .= "<input type=\"hidden\" id=\"$sExtKeyToMeId\" value=\"".$oCurrentObj->GetKey()."\">";
$sExtKeyToRemoteId = utils::GetSafeId($sPrefix.$this->m_sExtKeyToRemote);
$aFieldsMap[$this->m_sExtKeyToRemote] = $sExtKeyToRemoteId;
$aRow['form::checkbox'] .= "<input type=\"hidden\" id=\"$sExtKeyToRemoteId\" value=\"$iRemoteObjKey\">";
}
// 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] = "<span id='field_$sSafeFieldId'>".$oLinkedObj->GetAsHTML($sFieldCode).'</span>';
$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] = '<div class="field_container" style="border:none;"><div class="field_data"><div class="field_value">'
.cmdbAbstractObject::GetFormElementForField(
$oP,
$this->m_sLinkedClass,
$sFieldCode,
$oAttDef,
$sValue,
$sDisplayValue,
$sSafeFieldId,
$sNameSuffix,
0,
$aArgs
)
.'</div></div></div>';
}
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(
<<<JS
var $sWizardHelperVarName = new WizardHelper('$sWizardHelperClass', '', '$sState');
$sWizardHelperVarName.SetFieldsMap($sJsonFieldsMap);
$sWizardHelperVarName.SetFieldsCount($iFieldsCount);
$sWizardHelperVarName.SetReturnNotEditableFields(true);
$sWizardHelperVarName.SetWizHelperJsVarName('$sWizardHelperVarName');
JS
);
}
/**
* Display the table with the form for editing all the links at once
@@ -356,92 +136,12 @@ JS
*/
public function Display(WebPage $oPage, $oValue, $aArgs, $sFormPrefix, $oCurrentObj): string
{
$sLinkedSetId = "{$this->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;
}
}

View File

@@ -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;
}
}

View File

@@ -0,0 +1,26 @@
<?php
/**
* Copyright (C) 2013-2021 Combodo SARL
*
* This file is part of iTop.
*
* iTop is free software; you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* iTop is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
*/
// Display DataTable
Dict::Add('EN US', 'English', 'English', array(
'UI:Links:ActionRow:detach' => 'Detach item',
'UI:Links:ActionRow:detach:confirmation' => 'Do you really want to detach <b>{item}</b> from current object ?',
'UI:Links:ActionRow:delete' => 'Delete item',
'UI:Links:ActionRow:delete:confirmation' => 'Do you really want to delete <b>{item}</b> from current object ?',
));

View File

@@ -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 ?',
));

View File

@@ -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;
}

View File

@@ -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');
}
}
},
],
});
}

View File

@@ -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
}
};

View File

@@ -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]] = $('<button class="ibo-button ibo-is-regular ibo-is-neutral" type="button">' + this.options.labels[aButtonsTypes[k]] + '</button>');
}
// var aButtonsTypes = ['delete', 'remove', 'modify', 'add', 'create'];
// this.oButtons = {};
// for(k in aButtonsTypes)
// {
// this.oButtons[aButtonsTypes[k]] = $('<button class="ibo-button ibo-is-regular ibo-is-neutral" type="button">' + this.options.labels[aButtonsTypes[k]] + '</button>');
// }
this.indicator = $('<span></span>');
this.inputToBeCreated = $('<input type="hidden" name="'+this.options.input_name+'_tbc" value="{}">');
this.toBeCreated = {};
@@ -78,33 +78,33 @@ $(function()
.after(this.inputToBeDeleted)
.after(this.inputToBeAdded)
.after(this.inputToBeRemoved)
.after('<span style="float:left">&nbsp;&nbsp;&nbsp;<img src="../images/tv-item-last.gif">&nbsp;&nbsp;&nbsp;')
.after(this.indicator);
for (k in this.options.buttons) {
this.element.after(this.oButtons[this.options.buttons[k]]).after('&nbsp;&nbsp;&nbsp;');
}
// for (k in this.options.buttons) {
// this.element.after(this.oButtons[this.options.buttons[k]]).after('&nbsp;&nbsp;&nbsp;');
// }
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('<img src="../images/indicator.gif">');
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('<img src="../images/indicator.gif">');
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('<td>' + $(`#datatable_${oParams.iInputId}_actions_buttons_template`).html() +'</td>');
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('<img src="../images/indicator.gif">');
$.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)
{

View File

@@ -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('&nbsp;<img src="../images/indicator.gif"/>');
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,

View File

@@ -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<string, array{dev_requirement: bool, pretty_version?: string, version?: string, aliases?: string[], reference?: string, replaced?: string[], provided?: string[], install_path?: string, type?: string}>}|array{}|null
*/
private static $installed;
/**
* @var bool|null
*/
private static $canGetVendors;
/**

View File

@@ -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',

View File

@@ -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',

View File

@@ -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(

View File

@@ -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')) {

View File

@@ -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.

View File

@@ -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;
}

View File

@@ -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',
];
/**

View File

@@ -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('', '<div class="ibo-row-action--confirmation--explanation"></div>', '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;
}
}

View File

@@ -0,0 +1,109 @@
<?php
/**
* Copyright (C) 2013-2021 Combodo SARL
*
* This file is part of iTop.
*
* iTop is free software; you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* iTop is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
*/
namespace Combodo\iTop\Application\UI\Base\Component\Dialog;
use Combodo\iTop\Application\UI\Base\Component\Html\Html;
use Combodo\iTop\Application\UI\Base\Layout\UIContentBlock;
/**
*
* @package Combodo\iTop\Application\UI\Base\Component\Dialog
* @since 3.1.0
*/
class Dialog extends UIContentBlock
{
// Overloaded constants
public const BLOCK_CODE = 'ibo-dialog';
public const DEFAULT_HTML_TEMPLATE_REL_PATH = 'base/components/dialog/layout';
public const DEFAULT_JS_ON_READY_TEMPLATE_REL_PATH = 'base/components/dialog/layout';
public const DEFAULT_JS_FILES_REL_PATH = [
'js/components/dialog.js',
];
/** @var string $sTitle */
protected string $sTitle;
/** @var string $sContent */
protected string $sContent;
/**
* Alert constructor.
*
* @param string $sTitle
* @param string $sContent
* @param string|null $sId
*/
public function __construct(string $sTitle = '', string $sContent = '', ?string $sId = null)
{
parent::__construct($sId);
$this->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;
}
}

View File

@@ -0,0 +1,54 @@
<?php
/**
* Copyright (C) 2013-2021 Combodo SARL
*
* This file is part of iTop.
*
* iTop is free software; you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* iTop is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
*/
namespace Combodo\iTop\Application\UI\Base\Component\Dialog;
use Combodo\iTop\Application\UI\Base\AbstractUIBlockFactory;
/**
* Class DialogUIBlockFactory
*
* @api
*
* @since 3.1.0
* @package Combodo\iTop\Application\UI\Base\Component\Dialog
*/
class DialogUIBlockFactory extends AbstractUIBlockFactory
{
/** @inheritDoc */
public const TWIG_TAG_NAME = 'UIDialog';
/** @inheritDoc */
public const UI_BLOCK_CLASS_NAME = Dialog::class;
/**
* Make a basis Dialog component
*
* @param string $sTitle Title of the alert
* @param string $sContent The raw HTML content, must be already sanitized
* @param string|null $sId id of the html block
*
* @return \Combodo\iTop\Application\UI\Base\Component\Alert\Alert
*/
public static function MakeNeutral(string $sTitle = '', string $sContent = '', ?string $sId = null)
{
return new Dialog($sTitle, $sContent, $sId);
}
}

View File

@@ -0,0 +1,170 @@
<?php
/**
* @copyright Copyright (C) 2010-2022 Combodo SARL
* @license http://opensource.org/licenses/AGPL-3.0
*/
namespace Combodo\iTop\Application\UI\Links;
use Combodo\iTop\Application\UI\Base\Component\MedallionIcon\MedallionIcon;
use Combodo\iTop\Application\UI\Base\Layout\UIContentBlock;
use MetaModel;
/**
* Class AbstractBlockLinksViewTable
*
* @internal
* @since 3.1.0
* @package Combodo\iTop\Application\UI\Links
*/
abstract class AbstractBlockLinksViewTable extends UIContentBlock
{
// Overloaded constants
public const BLOCK_CODE = 'ibo-abstract-block-links-view-table';
public const DEFAULT_JS_TEMPLATE_REL_PATH = 'application/links/layout';
public const DEFAULT_JS_FILES_REL_PATH = [
'js/links/link_set_worker.js',
];
/** @var \DBObject $oDbObject db object witch link set belongs to */
protected \DBObject $oDbObject;
/** @var string $sObjectClass db object class name */
protected string $sObjectClass;
/** @var string $sAttCode db object link set attribute code */
protected string $sAttCode;
/** @var \AttributeLinkedSet $oAttDef attribute link set */
protected \AttributeLinkedSet $oAttDef;
/** @var string $sTargetClass links target classname */
protected string $sTargetClass;
/**
* Constructor.
*
* @param \WebPage $oPage
* @param \DBObject $oDbObject
* @param string $sObjectClass
* @param string $sAttCode
* @param \AttributeLinkedSet $oAttDef
*
* @throws \CoreException
* @throws \Exception
*/
public function __construct(\WebPage $oPage, \DBObject $oDbObject, string $sObjectClass, string $sAttCode, \AttributeLinkedSet $oAttDef)
{
parent::__construct('', ["ibo-block-links-table"]);
// retrieve parameters
$this->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;
}

View File

@@ -0,0 +1,249 @@
<?php
/**
* @copyright Copyright (C) 2010-2022 Combodo SARL
* @license http://opensource.org/licenses/AGPL-3.0
*/
namespace Combodo\iTop\Application\UI\Links\Direct;
use Combodo\iTop\Application\UI\Base\Component\Alert\AlertUIBlockFactory;
use Combodo\iTop\Application\UI\Base\Component\Button\ButtonUIBlockFactory;
use Combodo\iTop\Application\UI\Base\Component\DataTable\DataTableUIBlockFactory;
use Combodo\iTop\Application\UI\Base\Component\Html\Html;
use Combodo\iTop\Application\UI\Base\Component\MedallionIcon\MedallionIcon;
use Combodo\iTop\Application\UI\Base\Component\Panel\PanelUIBlockFactory;
use Combodo\iTop\Application\UI\Base\Component\Toolbar\ToolbarUIBlockFactory;
use Combodo\iTop\Application\UI\Base\iUIBlock;
use Combodo\iTop\Application\UI\Base\Layout\UIContentBlock;
use Dict;
use MetaModel;
/**
* Class BlockDirectLinksEditTable
*
* @internal
* @since 3.1.0
* @package Combodo\iTop\Application\UI\Links\Direct
*/
class BlockDirectLinksEditTable extends UIContentBlock
{
// Overloaded constants
public const BLOCK_CODE = 'ibo-block-direct-links-edit-table';
public const DEFAULT_JS_TEMPLATE_REL_PATH = 'application/links/direct/block-direct-links-edit-table/layout';
/** @var \UILinksWidgetDirect */
public \UILinksWidgetDirect $oUILinksDirectWidget;
/** @var \AttributeLinkedSet */
private \AttributeLinkedSet $oAttributeLinkedSet;
/** @var string */
public string $sInputName;
/** @var array */
public array $aLabels;
/** @var string */
public string $sSubmitUrl;
/** @var string */
public string $sWizHelper;
/** @var string */
public string $sJSDoSearch;
/**
* Constructor.
*
* @param \UILinksWidgetDirect $oUILinksDirectWidget
* @param string $sId
*
* @throws \ConfigException
* @throws \CoreException
* @throws \DictExceptionMissingString
* @throws \Exception
*/
public function __construct(\UILinksWidgetDirect $oUILinksDirectWidget, string $sId)
{
parent::__construct($sId, ["ibo-block-direct-links--edit-in-place"]);
// Retrieve parameters
$this->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'] = '<input type="checkbox" class="selectList'.$this->oUILinksDirectWidget->GetInputId().'" onClick="oWidget'.$this->oUILinksDirectWidget->GetInputId().'.directlinks(\'instance\')._onSelectChange();" value="'.$oLinkObj->GetKey().'"/>';
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] = '<div class="field_container" style="border:none;"><div class="field_data"><div class="field_value">'
// .\cmdbAbstractObject::GetFormElementForField(
// $oPage,
// $this->oUILinksDirectWidget->GetLinkedClass(),
// $sLinkedAttCode,
// $oAttDef,
// $sValue,
// $sDisplayValue,
// $this->GetFieldId($oValue, $sLinkedAttCode),
// ']',
// 0,
// []
// )
// .'</div></div></div>';
}
$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;
}
}

View File

@@ -0,0 +1,103 @@
<?php
/**
* @copyright Copyright (C) 2010-2022 Combodo SARL
* @license http://opensource.org/licenses/AGPL-3.0
*/
namespace Combodo\iTop\Application\UI\Links\Direct;
use Combodo\iTop\Application\UI\Links\AbstractBlockLinksViewTable;
use MetaModel;
/**
* Class BlockDirectLinksViewTable
*
* @internal
* @since 3.1.0
* @package Combodo\iTop\Application\UI\Links\Direct
*/
class BlockDirectLinksViewTable extends AbstractBlockLinksViewTable
{
public const BLOCK_CODE = 'ibo-block-direct-links-view-table';
public const REQUIRES_ANCESTORS_DEFAULT_JS_FILES = true;
/** @inheritdoc */
public function GetTargetClass(): string
{
return $this->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;
}
}

View File

@@ -1,75 +0,0 @@
<?php
/**
* @copyright Copyright (C) 2010-2021 Combodo SARL
* @license http://opensource.org/licenses/AGPL-3.0
*/
namespace Combodo\iTop\Application\UI\Links\Indirect\BlockIndirectLinksEdit;
use Combodo\iTop\Application\UI\Base\Component\Button\ButtonUIBlockFactory;
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;
use MetaModel;
/**
* Class BlockIndirectLinksEdit
*
* @package Combodo\iTop\Application\UI\Links\Indirect\BlockIndirectLinksEdit
*/
class BlockIndirectLinksEdit extends UIContentBlock
{
// Overloaded constants
public const BLOCK_CODE = 'ibo-block-indirect-links-edit';
public const DEFAULT_JS_TEMPLATE_REL_PATH = 'application/links/indirect/block-indirect-links-edit/layout';
/** @var int */
public $iInputId;
/** @var string */
public $sLinkedSetId;
/** @var string */
public $sClass;
/** @var string */
public $sAttCode;
/** @var string */
public $sNameSuffix;
/** @var bool */
public $bDuplicates;
/** @var string containing a js object name */
public $oWizHelper;
/** @var string */
public $sExtKeyToRemote;
/** @var bool */
public $bJSDoSearch;
/** @var int */
public $iMaxAddedId = 0;
/** @var array */
public $aRemoved = [];
/** @var string */
public $sFormPrefix;
/** @var string */
public $sRemoteClass;
public function AddControls()
{
$this->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);
}
}

View File

@@ -0,0 +1,442 @@
<?php
/**
* @copyright Copyright (C) 2010-2022 Combodo SARL
* @license http://opensource.org/licenses/AGPL-3.0
*/
namespace Combodo\iTop\Application\UI\Links\Indirect;
use Combodo\iTop\Application\UI\Base\Component\Alert\AlertUIBlockFactory;
use Combodo\iTop\Application\UI\Base\Component\Button\ButtonUIBlockFactory;
use Combodo\iTop\Application\UI\Base\Component\DataTable\DataTableUIBlockFactory;
use Combodo\iTop\Application\UI\Base\Component\Html\Html;
use Combodo\iTop\Application\UI\Base\Component\Input\InputUIBlockFactory;
use Combodo\iTop\Application\UI\Base\Component\MedallionIcon\MedallionIcon;
use Combodo\iTop\Application\UI\Base\Component\Panel\PanelUIBlockFactory;
use Combodo\iTop\Application\UI\Base\Component\Toolbar\ToolbarUIBlockFactory;
use Combodo\iTop\Application\UI\Base\iUIBlock;
use Combodo\iTop\Application\UI\Base\Layout\UIContentBlock;
use MetaModel;
/**
* Class BlockIndirectLinksEditTable
*
* @internal
* @since 3.1.0
* @package Combodo\iTop\Application\UI\Links\Indirect
*/
class BlockIndirectLinksEditTable extends UIContentBlock
{
// Overloaded constants
public const BLOCK_CODE = 'ibo-block-indirect-links-edit-table';
public const DEFAULT_JS_TEMPLATE_REL_PATH = 'application/links/indirect/block-indirect-links-edit-table/layout';
/** @var \UILinksWidget */
public \UILinksWidget $oUILinksWidget;
/** @var \AttributeLinkedSetIndirect */
private \AttributeLinkedSetIndirect $oAttributeLinkedSetIndirect;
/** @var string */
public string $sDuplicates;
/** @var string containing a js object name */
public string $sWizHelper;
/** @var string */
public string $sJSDoSearch;
/** @var int */
public int $iMaxAddedId = 0;
/** @var array */
public array $aRemoved = [];
/** @var string */
public string $sFormPrefix;
/**
* Constructor.
*
* @param \UILinksWidget $oUILinksWidget
*
* @throws \ConfigException
* @throws \CoreException
* @throws \Exception
*/
public function __construct(\UILinksWidget $oUILinksWidget)
{
parent::__construct("linkedset_{$oUILinksWidget->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('<span class="ibo-table--alert-information--label" data-role="ibo-datatable-selection-value"></span>'));
// 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'] = "<input class=\"selection\" data-remote-id=\"$iRemoteObjKey\" data-link-id=\"$iKey\" data-unique-id=\"$iUniqueId\" type=\"checkbox\" onClick=\"oWidget{$this->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'] = "<input class=\"selection\" data-remote-id=\"$iRemoteObjKey\" data-link-id=\"0\" data-unique-id=\"$iUniqueId\" type=\"checkbox\" onClick=\"oWidget{$this->oUILinksWidget->GetInputId()}.OnSelectChange();\" value=\"$sInputValue\">";
if ($iUniqueId > 0) {
// Rows created with ajax call need OnLinkAdded call.
//
$oP->add_ready_script(
<<<EOF
PrepareWidgets();
oWidget{$this->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(<<<EOF
oWidget{$this->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(
<<<JS
oWidget{$this->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'] .= "<input type=\"hidden\" id=\"$sExtKeyToMeId\" value=\"".$oCurrentObj->GetKey()."\">";
$sExtKeyToRemoteId = \utils::GetSafeId($sPrefix.$this->oUILinksWidget->GetExternalKeyToRemote());
$aFieldsMap[$this->oUILinksWidget->GetExternalKeyToRemote()] = $sExtKeyToRemoteId;
$aRow['form::checkbox'] .= "<input type=\"hidden\" id=\"$sExtKeyToRemoteId\" value=\"$iRemoteObjKey\">";
}
// 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] = "<span id='field_$sSafeFieldId'>".$oLinkedObj->GetAsHTML($sFieldCode).'</span>';
$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] = '<div class="field_container" style="border:none;"><div class="field_data"><div class="field_value">'
.\cmdbAbstractObject::GetFormElementForField(
$oP,
$this->oUILinksWidget->GetLinkedClass(),
$sFieldCode,
$oAttDef,
$sValue,
$sDisplayValue,
$sSafeFieldId,
$sNameSuffix,
0,
$aArgs
)
.'</div></div></div>';
}
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(
<<<JS
var $sWizardHelperVarName = new WizardHelper('$sWizardHelperClass', '', '$sState');
$sWizardHelperVarName.SetFieldsMap($sJsonFieldsMap);
$sWizardHelperVarName.SetFieldsCount($iFieldsCount);
$sWizardHelperVarName.SetReturnNotEditableFields(true);
$sWizardHelperVarName.SetWizHelperJsVarName('$sWizardHelperVarName');
JS
);
}
/**
* Return row actions.
*
* @return \string[][]
*/
private function GetRowActions(): array
{
$aRowActions = array();
if (!$this->oAttributeLinkedSetIndirect->GetReadOnly()) {
$aRowActions[] = array(
'tooltip' => 'remove link',
'icon_classes' => 'fas fa-minus',
'js_row_action' => "oWidget{$this->oUILinksWidget->GetInputId()}.Remove(oTrElement);",
);
}
return $aRowActions;
}
}

View File

@@ -0,0 +1,109 @@
<?php
/**
* @copyright Copyright (C) 2010-2022 Combodo SARL
* @license http://opensource.org/licenses/AGPL-3.0
*/
namespace Combodo\iTop\Application\UI\Links\Indirect;
use Combodo\iTop\Application\UI\Links\AbstractBlockLinksViewTable;
use MetaModel;
use PHPUnit\Exception;
/**
* Class BlockIndirectLinksViewTable
*
* @internal
* @since 3.1.0
* @package Combodo\iTop\Application\UI\Links\Indirect
*/
class BlockIndirectLinksViewTable extends AbstractBlockLinksViewTable
{
public const BLOCK_CODE = 'ibo-block-indirect-links-view-table';
/** @inheritdoc */
public function GetTargetClass(): string
{
try {
$oLinkingAttDef = MetaModel::GetAttributeDef($this->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);
}
}

View File

@@ -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("<p>{$sEmptyList}</p>");
$oForm->AddSubBlock(InputUIBlockFactory::MakeForHidden("count_{$this->sLinkedSetId}", '0', "count_{$this->sLinkedSetId}"));
$oForm->AddSubBlock(InputUIBlockFactory::MakeForHidden("count_{$this->oUILinksWidget->GetLinkedSetId()}", '0', "count_{$this->oUILinksWidget->GetLinkedSetId()}"));
}
}

View File

@@ -0,0 +1,103 @@
<?php
/*
* @copyright Copyright (C) 2010-2022 Combodo SARL
* @license http://opensource.org/licenses/AGPL-3.0
*/
namespace Combodo\iTop\Controller\Links;
use Combodo\iTop\Controller\AbstractController;
use MetaModel;
use utils;
/**
* Class LinkSetController
*
* @internal
* @since 3.1.0
* @package Combodo\iTop\Controller
*/
class LinkSetController extends AbstractController
{
public const ROUTE_NAMESPACE = 'linkset';
/**
* OperationDeleteLinkedObject.
*
* @return \JsonPage
*/
public function OperationDeleteLinkedObject(): \JsonPage
{
$oPage = new \JsonPage();
$sErrorMessage = null;
$bOperationSuccess = false;
// retrieve parameters
$sLinkedObjectClass = utils::ReadParam('linked_object_class', '', false, utils::ENUM_SANITIZATION_FILTER_CLASS);
$sLinkedObjectObjectKey = utils::ReadParam('linked_object_key', 0, 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 {
$oDeletionPlan = MetaModel::GetObject($sLinkedObjectClass, $sLinkedObjectObjectKey)->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;
}
}

View File

@@ -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 %}

View File

@@ -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 %}

View File

@@ -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 %}

View File

@@ -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 %}

View File

@@ -21,11 +21,14 @@
<th class="ibo-datatable-header" {% if aColumn["description"] is not empty %}title="{{ aColumn["description"] }}"{% endif %}>{{ aColumn["attribute_label"] }} </th>
{% endfor %}
{% if oUIBlock.HasRowActions() %}
<th class="ibo-datatable-header">{{ 'UI:Datatables:Column:RowActions:Label'|dict_s }} </th>
<th class="ibo-datatable-header" title="{{ 'UI:Datatables:Column:RowActions:Description'|dict_s }}">{{ 'UI:Datatables:Column:RowActions:Label'|dict_s }} </th>
{% endif %}
</thead>
</table>
{% if oUIBlock.HasRowActions() %}
{{ render_block(oUIBlock.GetRowActionsTemplate()) }}
{% if not oUIBlock.GetRowActionsConfirmDialogInitializedFlag() %}
{{ render_block(oUIBlock.GetRowActionsConfirmDialog()) }}
{% endif %}
{% endif %}

View File

@@ -423,4 +423,6 @@ if(window.ResizeObserver){
oTable{{ sListIDForVarSuffix }}Resize.observe($('#{{ oUIBlock.GetId() }}')[0]);
}
{% include 'base/components/datatable/row-actions/handler.js.twig' %}
{% if oUIBlock.HasRowActions() %}
{% include 'base/components/datatable/row-actions/handler.js.twig' %}
{% endif %}

View File

@@ -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 %}
// 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 %}

View File

@@ -21,4 +21,7 @@
{% if oUIBlock.HasRowActions() %}
{{ render_block(oUIBlock.GetRowActionsTemplate()) }}
{% if not oUIBlock.GetRowActionsConfirmDialogInitializedFlag() %}
{{ render_block(oUIBlock.GetRowActionsConfirmDialog()) }}
{% endif %}
{% endif %}

View File

@@ -48,4 +48,7 @@
{% if oUIBlock.HasRowActions() %}
{{ render_block(oUIBlock.GetRowActionsTemplate()) }}
{% if not oUIBlock.GetRowActionsConfirmDialogInitializedFlag() %}
{{ render_block(oUIBlock.GetRowActionsConfirmDialog()) }}
{% endif %}
{% endif %}

View File

@@ -0,0 +1,5 @@
<div id="{{ oUIBlock.GetId() }}" class="{{ oUIBlock.GetBlocksInheritanceCSSClassesAsString() }} {{ oUIBlock.GetAdditionalCSSClassesAsString() }}" data-role="ibo-dialog" title="" style="display: none">
{% for oSubBlock in oUIBlock.GetSubBlocks() %}
{{ render_block(oSubBlock, {aPage: aPage}) }}
{% endfor %}
</div>

View File

@@ -0,0 +1,4 @@
$('#{{ oUIBlock.GetId() }}').alert({
bOpenedByDefault: {{ oUIBlock.IsOpenedByDefault()|var_export }}
{% if oUIBlock.IsSaveCollapsibleStateEnabled() %}, collapsibleStateStorageKey: '{{ oUIBlock.GetSessionCollapsibleStateStorageKey() }}'{% endif %}
});