From de35d941c175aa81d4d8678eb3ee6587e98b4fa6 Mon Sep 17 00:00:00 2001 From: bdalsass <95754414+bdalsass@users.noreply.github.com> Date: Fri, 28 Apr 2023 14:43:53 +0200 Subject: [PATCH] =?UTF-8?q?N=C2=B06154=20N=C2=B06219=20=20-=20Relations=20?= =?UTF-8?q?Read=20-=20tooltip,=20confirmation=20title=20and=20message=20(#?= =?UTF-8?q?486)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit N°6154 - n:n Read - tooltip, confirmation title and message on Add-Edit-Remove N°6219 - 1:n Read: tooltip, modal title and message on Add-Edit-Remove-Delete --- application/cmdbabstract.class.inc.php | 5 + application/displayblock.class.inc.php | 22 +-- core/attributedef.class.inc.php | 140 +++++++++++------- .../links/en.dictionary.itop.links.php | 51 +++++-- .../links/fr.dictionary.itop.links.php | 52 +++++-- js/links/links_view_table_widget.js | 23 ++- js/links/linkset-worker.js | 5 +- js/links/linkset.js | 2 +- js/object/object-worker.js | 16 +- js/pages/backoffice/toolbox.js | 43 +++--- lib/autoload.php | 18 --- lib/composer/ClassLoader.php | 45 ++---- lib/composer/autoload_classmap.php | 3 +- lib/composer/autoload_files.php | 14 +- lib/composer/autoload_namespaces.php | 2 +- lib/composer/autoload_psr4.php | 2 +- lib/composer/autoload_real.php | 41 +++-- lib/composer/autoload_static.php | 13 +- lib/composer/include_paths.php | 2 +- lib/composer/installed.php | 4 +- .../DataTable/DataTableUIBlockFactory.php | 17 ++- .../Links/AbstractBlockLinkSetViewTable.php | 35 ++++- .../Direct/BlockDirectLinkSetViewTable.php | 61 +++++--- .../BlockIndirectLinkSetViewTable.php | 33 +++-- sources/Controller/AjaxRenderController.php | 1 + .../Base/Layout/ObjectController.php | 6 + .../Controller/Links/LinkSetController.php | 30 +++- .../datatable/row-actions/handler.js.twig | 29 +++- 28 files changed, 461 insertions(+), 254 deletions(-) diff --git a/application/cmdbabstract.class.inc.php b/application/cmdbabstract.class.inc.php index 30c09fdc3..b2b63dc9c 100644 --- a/application/cmdbabstract.class.inc.php +++ b/application/cmdbabstract.class.inc.php @@ -2974,7 +2974,12 @@ EOF $oObjectDetails = ObjectFactory::MakeDetails($this, $this->GetDisplayMode()); $oToolbarButtons->AddCSSClass('ibo-toolbar-top'); $oObjectDetails->AddToolbarBlock($oToolbarButtons); + // Allow form title customization + if (array_key_exists('form_title', $aExtraParams)) { + $oObjectDetails->SetTitle($aExtraParams['form_title']); + } } + $oForm->AddSubBlock($oObjectDetails); if (isset($aExtraParams['nbBulkObj'])) { // if bulk modify buttons must be after object display diff --git a/application/displayblock.class.inc.php b/application/displayblock.class.inc.php index d55f7da06..2298eab30 100644 --- a/application/displayblock.class.inc.php +++ b/application/displayblock.class.inc.php @@ -1844,7 +1844,12 @@ class MenuBlock extends DisplayBlock $iSetCount = $oSet->Count(); /** @var string $sRefreshAction JS snippet to run when clicking on the refresh button of the menu */ $sRefreshAction = $aExtraParams['refresh_action'] ?? ''; - $bIsCreationInModalAllowed = isset($aExtraParams['creation_in_modal_is_allowed']) && $aExtraParams['creation_in_modal_is_allowed'] === true; + $bIsCreationInModal = isset($aExtraParams['creation_in_modal']) && $aExtraParams['creation_in_modal'] === true; + $bIsCreationDisallowed = isset($aExtraParams['creation_disallowed']) && $aExtraParams['creation_disallowed'] === true; + + // Check rights + $oReflectionClass = new ReflectionClass($sClass); + $bIsCreationAllowed = (!$bIsCreationDisallowed && UserRights::IsActionAllowed($sClass, UR_ACTION_CREATE) === UR_ALLOWED_YES) && ($oReflectionClass->IsSubclassOf('cmdbAbstractObject')); /** @var array $aRegularActions Any action other than a transition */ $aRegularActions = []; @@ -1860,7 +1865,7 @@ class MenuBlock extends DisplayBlock $sContext = '&'.$sContext; } - $oReflectionClass = new ReflectionClass($sClass); + $sFilter = $this->GetFilter()->serialize(); $sUIPage = cmdbAbstractObject::ComputeStandardUIPage($sClass); $sRootUrl = utils::GetAbsoluteUrlAppRoot(); @@ -1880,10 +1885,6 @@ class MenuBlock extends DisplayBlock } } - // Check rights - $bIsCreationAllowed = (UserRights::IsActionAllowed($sClass, UR_ACTION_CREATE) === UR_ALLOWED_YES) && ($oReflectionClass->IsSubclassOf('cmdbAbstractObject')); - $bIsModifyAllowed = (UserRights::IsActionAllowed($sClass, UR_ACTION_MODIFY, $oSet) === UR_ALLOWED_YES) && ($oReflectionClass->IsSubclassOf('cmdbAbstractObject')); - // Check concurrent lock (can only be lock if we are handling a single object $bLocked = false; if ($iSetCount === 1) { @@ -1905,7 +1906,7 @@ class MenuBlock extends DisplayBlock //-------------------------------------------- // Create in new tab - if ($bIsCreationAllowed && !$bIsCreationInModalAllowed) { + if ($bIsCreationAllowed && !$bIsCreationInModal) { $this->AddNewObjectMenuAction($aRegularActions, $sClass, $sDefaultValuesAsUrlParams); } @@ -2308,10 +2309,13 @@ class MenuBlock extends DisplayBlock } // - Creation in modal - if ($bIsCreationInModalAllowed === true) { + if ($bIsCreationAllowed && $bIsCreationInModal) { $oAddLinkActionButton = ButtonUIBlockFactory::MakeIconAction( 'fas fa-plus', - Dict::Format('UI:ClickToCreateNew', MetaModel::GetName($sClass)), + // Allow button tooltip customization + array_key_exists('creation_in_modal_tooltip', $aExtraParams) ? + $aExtraParams['creation_in_modal_tooltip'] : + Dict::Format('UI:ClickToCreateNew', MetaModel::GetName($sClass)), 'UI:Links:New', '', false diff --git a/core/attributedef.class.inc.php b/core/attributedef.class.inc.php index 50e6e791e..962ba98c5 100644 --- a/core/attributedef.class.inc.php +++ b/core/attributedef.class.inc.php @@ -2381,69 +2381,97 @@ class AttributeLinkedSet extends AttributeDefinition { if ($oFormField === null) { - $sFormFieldClass = static::GetFormFieldClass(); - $oFormField = new $sFormFieldClass($this->GetCode()); - } + $sFormFieldClass = static::GetFormFieldClass(); + $oFormField = new $sFormFieldClass($this->GetCode()); + } - // Setting target class - if (!$this->IsIndirect()) - { - $sTargetClass = $this->GetLinkedClass(); - } - else - { - /** @var \AttributeExternalKey $oRemoteAttDef */ - /** @var \AttributeLinkedSetIndirect $this */ - $oRemoteAttDef = MetaModel::GetAttributeDef($this->GetLinkedClass(), $this->GetExtKeyToRemote()); - $sTargetClass = $oRemoteAttDef->GetTargetClass(); + // Setting target class + if (!$this->IsIndirect()) { + $sTargetClass = $this->GetLinkedClass(); + } else { + /** @var \AttributeExternalKey $oRemoteAttDef */ + /** @var \AttributeLinkedSetIndirect $this */ + $oRemoteAttDef = MetaModel::GetAttributeDef($this->GetLinkedClass(), $this->GetExtKeyToRemote()); + $sTargetClass = $oRemoteAttDef->GetTargetClass(); - /** @var \AttributeLinkedSetIndirect $this */ - $oFormField->SetExtKeyToRemote($this->GetExtKeyToRemote()); - } - $oFormField->SetTargetClass($sTargetClass); - $oFormField->SetIndirect($this->IsIndirect()); - // Setting attcodes to display - $aAttCodesToDisplay = MetaModel::FlattenZList(MetaModel::GetZListItems($sTargetClass, 'list')); - // - Adding friendlyname attribute to the list is not already in it - $sTitleAttCode = MetaModel::GetFriendlyNameAttributeCode($sTargetClass); - if (($sTitleAttCode !== null) && !in_array($sTitleAttCode, $aAttCodesToDisplay)) - { - $aAttCodesToDisplay = array_merge(array($sTitleAttCode), $aAttCodesToDisplay); - } - // - Adding attribute labels - $aAttributesToDisplay = array(); - foreach($aAttCodesToDisplay as $sAttCodeToDisplay) - { - $oAttDefToDisplay = MetaModel::GetAttributeDef($sTargetClass, $sAttCodeToDisplay); - $aAttributesToDisplay[$sAttCodeToDisplay] = $oAttDefToDisplay->GetLabel(); - } - $oFormField->SetAttributesToDisplay($aAttributesToDisplay); + /** @var \AttributeLinkedSetIndirect $this */ + $oFormField->SetExtKeyToRemote($this->GetExtKeyToRemote()); + } + $oFormField->SetTargetClass($sTargetClass); + $oFormField->SetIndirect($this->IsIndirect()); + // Setting attcodes to display + $aAttCodesToDisplay = MetaModel::FlattenZList(MetaModel::GetZListItems($sTargetClass, 'list')); + // - Adding friendlyname attribute to the list is not already in it + $sTitleAttCode = MetaModel::GetFriendlyNameAttributeCode($sTargetClass); + if (($sTitleAttCode !== null) && !in_array($sTitleAttCode, $aAttCodesToDisplay)) { + $aAttCodesToDisplay = array_merge(array($sTitleAttCode), $aAttCodesToDisplay); + } + // - Adding attribute labels + $aAttributesToDisplay = array(); + foreach ($aAttCodesToDisplay as $sAttCodeToDisplay) { + $oAttDefToDisplay = MetaModel::GetAttributeDef($sTargetClass, $sAttCodeToDisplay); + $aAttributesToDisplay[$sAttCodeToDisplay] = $oAttDefToDisplay->GetLabel(); + } + $oFormField->SetAttributesToDisplay($aAttributesToDisplay); - parent::MakeFormField($oObject, $oFormField); + parent::MakeFormField($oObject, $oFormField); - return $oFormField; - } + return $oFormField; + } - public function IsPartOfFingerprint() - { - return false; - } + public function IsPartOfFingerprint() + { + return false; + } - /** - * @inheritDoc - * @param \ormLinkSet $proposedValue - */ - public function HasAValue($proposedValue): bool - { - // Protection against wrong value type - if (false === ($proposedValue instanceof ormLinkSet)) - { - return parent::HasAValue($proposedValue); - } + /** + * @inheritDoc + * @param \ormLinkSet $proposedValue + */ + public function HasAValue($proposedValue): bool + { + // Protection against wrong value type + if (false === ($proposedValue instanceof ormLinkSet)) { + return parent::HasAValue($proposedValue); + } - // We test if there is at least 1 item in the linkset (new or existing), not if an item is being added to it. - return $proposedValue->Count() > 0; - } + // We test if there is at least 1 item in the linkset (new or existing), not if an item is being added to it. + return $proposedValue->Count() > 0; + } + + /** + * SearchSpecificLabel. + * + * @param string $sDictEntrySuffix + * @param string $sDefault + * @param bool $bUserLanguageOnly + * @param ...$aArgs + * @return string + * @since 3.1.0 + */ + public function SearchSpecificLabel(string $sDictEntrySuffix, string $sDefault, bool $bUserLanguageOnly, ...$aArgs): string + { + try { + $sNextClass = $this->m_sHostClass; + + do { + $sKey = "class:{$sNextClass}/Attribute:{$this->m_sCode}/{$sDictEntrySuffix}"; + if (Dict::S($sKey, null, $bUserLanguageOnly) !== $sKey) { + return Dict::Format($sKey, ...$aArgs); + } + $sNextClass = MetaModel::GetParentClass($sNextClass); + } while ($sNextClass !== null); + + if (Dict::S($sDictEntrySuffix, null, $bUserLanguageOnly) !== $sKey) { + return Dict::Format($sDictEntrySuffix, ...$aArgs); + } else { + return $sDefault; + } + } catch (Exception $e) { + ExceptionLog::LogException($e); + return $sDefault; + } + } } /** diff --git a/dictionaries/ui/application/links/en.dictionary.itop.links.php b/dictionaries/ui/application/links/en.dictionary.itop.links.php index 0c7127c6c..6522f884d 100644 --- a/dictionaries/ui/application/links/en.dictionary.itop.links.php +++ b/dictionaries/ui/application/links/en.dictionary.itop.links.php @@ -19,19 +19,46 @@ Dict::Add('EN US', 'English', 'English', array( - // Action row - 'UI:Links:ActionRow:Detach' => 'Remove', - 'UI:Links:ActionRow:Detach+' => 'Remove this object', - 'UI:Links:ActionRow:Detach:Confirmation' => 'Do you really want to remove {item} from current object ?', - 'UI:Links:ActionRow:Delete' => 'Delete', - 'UI:Links:ActionRow:Delete+' => 'Delete this object', - 'UI:Links:ActionRow:Delete:Confirmation' => 'Do you really want to delete {item} from current object ?', - 'UI:Links:ActionRow:Modify' => 'Modify', - 'UI:Links:ActionRow:Modify+' => 'Modify this object', - 'UI:Links:ActionRow:Modify:Modal:Title' => 'Modify an object', + // Placeholders + // $%1s : host object class name + // $%2s : host object firendlyname + // $%3s : current tab name + // $%4s : remote object class name + // $%5s : remote object friendlyname - // Action - 'UI:Links:New:Modal:Title' => 'Create an object', + 'UI:Links:Object:New:Modal:Title' => 'Create an object', + + // Create + 'UI:Links:Create:Button' => 'Create', + 'UI:Links:Create:Button+' => 'Create a %4$s', + 'UI:Links:Create:Modal:Title' => 'Create a %4$s in %2$s', + + // Add + 'UI:Links:Add:Button' => 'Add', + 'UI:Links:Add:Button+' => 'Add a %4$s', + 'UI:Links:Add:Modal:Title' => 'Add a %4$s to %2$s', + + // Modify link + 'UI:Links:ModifyLink:Button' => 'Modify', + 'UI:Links:ModifyLink:Button+' => 'Modify this link', + 'UI:Links:ModifyLink:Modal:Title' => 'Modify the link between %2$s and %5$s', + + // Modify object + 'UI:Links:ModifyObject:Button' => 'Modify', + 'UI:Links:ModifyObject:Button+' => 'Modify this object', + 'UI:Links:ModifyObject:Modal:Title' => '%5$s', + + // Remove + 'UI:Links:Remove:Button' => 'Remove', + 'UI:Links:Remove:Button+' => 'Remove this %4$s', + 'UI:Links:Remove:Modal:Title' => 'Remove a %4$s', + 'UI:Links:Remove:Modal:Message' => 'Do you really want to remove %5$s from %2$s?', + + // Delete + 'UI:Links:Delete:Button' => 'Delete', + 'UI:Links:Delete:Button+' => 'Delete this %4$s', + 'UI:Links:Delete:Modal:Title' => 'Delete a %4$s', + 'UI:Links:Delete:Modal:Message' => 'Do you really want to delete %5$s?', // Bulk 'UI:Links:Bulk:LinkWillBeCreatedForAllObjects' => 'Add to all objects', diff --git a/dictionaries/ui/application/links/fr.dictionary.itop.links.php b/dictionaries/ui/application/links/fr.dictionary.itop.links.php index 64d3d66a3..5a6dc96e6 100644 --- a/dictionaries/ui/application/links/fr.dictionary.itop.links.php +++ b/dictionaries/ui/application/links/fr.dictionary.itop.links.php @@ -19,19 +19,47 @@ Dict::Add('FR FR', 'French', 'Français', array( - // Action row - 'UI:Links:ActionRow:Detach' => 'Enlever', - 'UI:Links:ActionRow:Detach+' => 'Enlever cet objet', - 'UI:Links:ActionRow:Detach:Confirmation' => 'Voulez-vous Enlever {item} de l\'objet courant ?', - 'UI:Links:ActionRow:Delete' => 'Supprimer', - 'UI:Links:ActionRow:Delete+' => 'Supprimer cet objet', - 'UI:Links:ActionRow:Delete:Confirmation' => 'Voulez-vous supprimer {item} de l\'objet courant ?', - 'UI:Links:ActionRow:Modify' => 'Modifier', - 'UI:Links:ActionRow:Modify+' => 'Modifier cet objet', - 'UI:Links:ActionRow:Modify:Modal:Title' => 'Modifier un objet', - // Action - 'UI:Links:New:Modal:Title' => 'Créer un objet', + // Placeholders + // $%1s : host object class name + // $%2s : host object firendlyname + // $%3s : current tab name + // $%4s : remote object class name + // $%5s : remote object friendlyname + + 'UI:Links:Object:New:Modal:Title' => 'Créer un objet', + + // Create + 'UI:Links:Create:Button' => 'Créer', + 'UI:Links:Create:Button+' => 'Créer un(e) %4$s', + 'UI:Links:Create:Modal:Title' => 'Ajouter un(e) %4$s à %2$s', + + // Add + 'UI:Links:Add:Button' => 'Ajouter', + 'UI:Links:Add:Button+' => 'Ajouter un(e) %4$s', + 'UI:Links:Add:Modal:Title' => 'Ajouter un(e) %4$s à %2$s', + + // Modify link + 'UI:Links:ModifyLink:Button' => 'Modifier', + 'UI:Links:ModifyLink:Button+' => 'Modifier cette relation', + 'UI:Links:ModifyLink:Modal:Title' => 'Modifier la relation entre %2$s et %5$s', + + // Modify object + 'UI:Links:ModifyObject:Button' => 'Modifier', + 'UI:Links:ModifyObject:Button+' => 'Modifier cet objet', + 'UI:Links:ModifyObject:Modal:Title' => '%5$s', + + // Remove + 'UI:Links:Remove:Button' => 'Retirer', + 'UI:Links:Remove:Button+' => 'Retirer cet(te) %4$s', + 'UI:Links:Remove:Modal:Title' => 'Retirer un(e) %4$s de %1$s', + 'UI:Links:Remove:Modal:Message' => 'Voulez-vous vraiment retirer %5$s de %2$s ?', + + // Delete + 'UI:Links:Delete:Button' => 'Supprimer', + 'UI:Links:Delete:Button+' => 'Supprimer cet(te) %4$s', + 'UI:Links:Delete:Modal:Title' => 'Supprimer un(e) %4$s', + 'UI:Links:Delete:Modal:Message' => 'Voulez-vous vraiment supprimer %5$s ?', // Bulk 'UI:Links:Bulk:LinkWillBeCreatedForAllObjects' => 'Ajouter à tous les objets', diff --git a/js/links/links_view_table_widget.js b/js/links/links_view_table_widget.js index feb21d8fc..362be11af 100644 --- a/js/links/links_view_table_widget.js +++ b/js/links/links_view_table_widget.js @@ -75,6 +75,10 @@ $(function() // retrieve table const $Table = $('table', this.element); + // retrieve new button + const $NewButton = $('[name="UI:Links:New"]', this.element); + const sButtonTooltipContent = $NewButton.attr('data-tooltip-content'); + // retrieve context parameters const sClass = $Table.closest('[data-role="ibo-block-links-table"]').attr('data-link-class'); const sAttCode = $Table.closest('[data-role="ibo-block-links-table"]').attr('data-link-attcode'); @@ -82,7 +86,7 @@ $(function() const sHostObjectId = $Table.closest('[data-role="ibo-object-details"]').attr('data-object-id'); // link object creation - iTopLinkSetWorker.CreateLinkedObject(sClass, sAttCode, sHostObjectClass, sHostObjectId, function(){ + iTopLinkSetWorker.CreateLinkedObject(sButtonTooltipContent, sClass, sAttCode, sHostObjectClass, sHostObjectId, function(){ $(this).find("form").remove(); $(this).dialog('destroy'); },function (event, data) { @@ -96,20 +100,29 @@ $(function() * ModifyLinkedObject. * * @param {string} sLinkedObjectKey + * @param {Element} $TRElement + * @param {string} sRemoteFriendlyname */ - ModifyLinkedObject: function (sLinkedObjectKey) { + ModifyLinkedObject: function (sLinkedObjectKey, $TRElement, sRemoteFriendlyname) { const me = this; - + + // retrieve modify button and extract modal title + const $ModifyButton = $('[name="ModifyButton"]', $TRElement); + const sButtonTooltipContent = $ModifyButton.attr('data-tooltip-content'); + let sButtonTitleContent = $ModifyButton.attr('data-modal-title'); + sButtonTitleContent = sButtonTitleContent.replaceAll('{item}', sRemoteFriendlyname); + // Specify that external key to host object will be readonly let aReadOnlyParams = { 'readonly': { } } aReadOnlyParams['readonly'][this.options.external_key_to_me] = 1; - + aReadOnlyParams['form_title'] = sButtonTitleContent; + // link object modification - iTopObjectWorker.ModifyObject(this.options.link_class, sLinkedObjectKey, function () { + iTopObjectWorker.ModifyObject(sButtonTooltipContent, this.options.link_class, sLinkedObjectKey, function () { $(this).find("form").remove(); $(this).dialog('destroy'); }, function(event, data){ diff --git a/js/links/linkset-worker.js b/js/links/linkset-worker.js index 940fad976..ef4ddc9c2 100644 --- a/js/links/linkset-worker.js +++ b/js/links/linkset-worker.js @@ -46,6 +46,7 @@ const iTopLinkSetWorker = new function(){ /** * CallAjaxCreateLinkedObject. * + * @param {string} sModalTitle * @param {string} sClass * @param {string} sAttCode * @param {string} sHostObjectClass @@ -53,10 +54,10 @@ const iTopLinkSetWorker = new function(){ * @param oOnModalCloseCallback * @param oOnFormSubmittedCallback */ - const CallAjaxCreateLinkedObject = function(sClass, sAttCode, sHostObjectClass, sHostObjectId, oOnModalCloseCallback = null, oOnFormSubmittedCallback = null){ + const CallAjaxCreateLinkedObject = function(sModalTitle, sClass, sAttCode, sHostObjectClass, sHostObjectId, oOnModalCloseCallback = null, oOnFormSubmittedCallback = null){ let oOptions = { - title: Dict.S('UI:Links:New:Modal:Title'), + title: sModalTitle, content: { endpoint: `${ROUTER_BASE_URL}?route=${ROUTE_LINK_SET_CREATE_OBJECT}`, data: { diff --git a/js/links/linkset.js b/js/links/linkset.js index 2e8b11a76..85cdc93a6 100644 --- a/js/links/linkset.js +++ b/js/links/linkset.js @@ -15,7 +15,7 @@ const iTopLinkSet = new function () { const CallCreateLinkedObject = function(sLinkedClass, oWidget) { // Create link object - iTopObjectWorker.CreateObject(sLinkedClass, function(){ + iTopObjectWorker.CreateObject(Dict.S('UI:Links:Object:New:Modal:Title'), sLinkedClass, function(){ $(this).find("form").remove(); $(this).dialog('destroy'); }, diff --git a/js/object/object-worker.js b/js/object/object-worker.js index 86f6fe2af..0c0e522ed 100644 --- a/js/object/object-worker.js +++ b/js/object/object-worker.js @@ -5,17 +5,18 @@ const iTopObjectWorker = new function(){ const ROUTE_CREATE_OBJECT = 'object.new'; const ROUTE_MODIFY_OBJECT = 'object.modify'; const ROUTE_GET_OBJECT = 'object.get'; - + /** * CallAjaxCreateObject. * + * @param {string} sTitle * @param {string} sClass * @param oOnModalCloseCallback * @param oOnFormSubmittedCallback * @param {Object} aAdditionalData * @constructor */ - const CallAjaxCreateObject = function(sClass, oOnModalCloseCallback = null, oOnFormSubmittedCallback = null, aAdditionalData = []){ + const CallAjaxCreateObject = function(sTitle, sClass, oOnModalCloseCallback = null, oOnFormSubmittedCallback = null, aAdditionalData = []){ let aData = $.extend( { class: sClass, @@ -24,7 +25,7 @@ const iTopObjectWorker = new function(){ ); let oOptions = { - title: Dict.S('UI:Object:Modal:Title'), + title: sTitle, content: { endpoint: `${ROUTER_BASE_URL}?route=${ROUTE_CREATE_OBJECT}`, data: aData @@ -43,6 +44,7 @@ const iTopObjectWorker = new function(){ /** * CallAjaxModifyObject. * + * @param {string} sTitle * @param {string} sObjectClass * @param {string} sObjectKey * @param oOnModalCloseCallback @@ -50,7 +52,7 @@ const iTopObjectWorker = new function(){ * @param {Object} aAdditionalData * @constructor */ - const CallAjaxModifyObject = function(sObjectClass, sObjectKey, oOnModalCloseCallback, oOnFormSubmittedCallback, aAdditionalData = []){ + const CallAjaxModifyObject = function(sTitle, sObjectClass, sObjectKey, oOnModalCloseCallback, oOnFormSubmittedCallback, aAdditionalData = []){ let aData = $.extend( { class: sObjectClass, @@ -58,9 +60,9 @@ const iTopObjectWorker = new function(){ }, aAdditionalData ); - + let oOptions = { - title: Dict.S('UI:Links:ActionRow:Modify:Modal:Title'), + title: sTitle, content: { endpoint: `${ROUTER_BASE_URL}?route=${ROUTE_MODIFY_OBJECT}`, data: aData, @@ -93,7 +95,7 @@ const iTopObjectWorker = new function(){ }, aAdditionalData ) - + $.post(`${ROUTER_BASE_URL}?route=${ROUTE_GET_OBJECT}`, aData, oOnResponseCallback); }; diff --git a/js/pages/backoffice/toolbox.js b/js/pages/backoffice/toolbox.js index 70b086e4a..ae29f3ed0 100644 --- a/js/pages/backoffice/toolbox.js +++ b/js/pages/backoffice/toolbox.js @@ -325,10 +325,12 @@ CombodoModal._ConvertButtonDefinition = function (aButtonsDefinitions) { if(aButtonsDefinitions === null) { return aConverted } - aButtonsDefinitions.forEach(element => { + Object.keys(aButtonsDefinitions).forEach(key => { + const element = aButtonsDefinitions[key]; + console.log(element); const aButton = { text: element.text, - class: element.class, + class: typeof(element.classes) !== 'undefined' ? element.classes.join(' ') : '', click: element.callback_on_click } aConverted.push(aButton); @@ -366,43 +368,40 @@ CombodoModal.OpenConfirmationModal = function(oOptions, aData) { } } // Merge external options with confirmation modal default options - oOptions = $.extend({ + oOptions = $.extend(true, { title: Dict.S('UI:Modal:DefaultConfirmationTitle'), content: '', - confirm_button_label: null, do_not_show_again_pref_key: null, - callback_on_confirm: null, - callback_on_cancel: null, extra_options: { callback_on_modal_close: function () { $(this).dialog( "destroy" ); // destroy dialog object } }, - buttons: [ - { + buttons: { + cancel: { text: Dict.S('UI:Button:Cancel'), - class: 'ibo-is-alternative', + classes: ['ibo-is-alternative'], callback_on_click: function () { // call confirm handler and close dialog let bCanClose = true; - if(oOptions.callback_on_cancel != null){ + if (oOptions.callback_on_cancel != null) { bCanClose = oOptions.callback_on_cancel(...aData) !== false; } - if(bCanClose){ + if (bCanClose) { $(this).dialog('close'); // close dialog } } }, - { - text: oOptions.confirm_button_label ?? Dict.S('UI:Button:Confirm'), - class: 'ibo-is-primary', + confirm: { + text: Dict.S('UI:Button:Confirm'), + classes: ['ibo-is-primary'], callback_on_click: function () { // Call confirm handler and close dialog let bCanClose = true; - if(oOptions.callback_on_confirm != null){ + if (oOptions.callback_on_confirm != null) { bCanClose = oOptions.callback_on_confirm(...aData) !== false; } - if(bCanClose){ + if (bCanClose) { $(this).dialog('close'); // close dialog // Handle "do not show again" user preference let bDoNotShowAgain = oOptions.do_not_show_again_pref_key !== null ? @@ -414,7 +413,7 @@ CombodoModal.OpenConfirmationModal = function(oOptions, aData) { } } } - ], + }, callback_on_content_loaded: function(oModalContentElement){ // Add option do not show again from template if(oOptions.do_not_show_again_pref_key !== null) { @@ -444,15 +443,15 @@ CombodoModal.OpenInformativeModal = function(sMessage, sSeverity, oOptions) { $(this).dialog( "destroy" ); } }, - buttons: [ - { + buttons: { + ok: { text: Dict.S('UI:Button:Ok'), - class: 'ibo-is-regular ibo-is-neutral', + // classes: ['ibo-is-regular', 'ibo-is-neutral'], callback_on_click: function () { - $(this).dialog('close'); + $(this).dialog('close'); } }, - ], + }, }, oOptions); // Open modal diff --git a/lib/autoload.php b/lib/autoload.php index db10dc867..460e67535 100644 --- a/lib/autoload.php +++ b/lib/autoload.php @@ -2,24 +2,6 @@ // autoload.php @generated by Composer -if (PHP_VERSION_ID < 50600) { - if (!headers_sent()) { - header('HTTP/1.1 500 Internal Server Error'); - } - $err = 'Composer 2.3.0 dropped support for autoloading on PHP <5.6 and you are running '.PHP_VERSION.', please upgrade PHP or use Composer 2.2 LTS via "composer self-update --2.2". Aborting.'.PHP_EOL; - if (!ini_get('display_errors')) { - if (PHP_SAPI === 'cli' || PHP_SAPI === 'phpdbg') { - fwrite(STDERR, $err); - } elseif (!headers_sent()) { - echo $err; - } - } - trigger_error( - $err, - E_USER_ERROR - ); -} - require_once __DIR__ . '/composer/autoload_real.php'; return ComposerAutoloaderInit7f81b4a2a468a061c306af5e447a9a9f::getLoader(); diff --git a/lib/composer/ClassLoader.php b/lib/composer/ClassLoader.php index a72151c77..0cd6055d1 100644 --- a/lib/composer/ClassLoader.php +++ b/lib/composer/ClassLoader.php @@ -42,9 +42,6 @@ namespace Composer\Autoload; */ class ClassLoader { - /** @var \Closure(string):void */ - private static $includeFile; - /** @var ?string */ private $vendorDir; @@ -109,7 +106,6 @@ class ClassLoader public function __construct($vendorDir = null) { $this->vendorDir = $vendorDir; - self::initializeIncludeClosure(); } /** @@ -153,7 +149,7 @@ class ClassLoader /** * @return string[] Array of classname => path - * @psalm-return array + * @psalm-var array */ public function getClassMap() { @@ -429,8 +425,7 @@ class ClassLoader public function loadClass($class) { if ($file = $this->findFile($class)) { - $includeFile = self::$includeFile; - $includeFile($file); + includeFile($file); return true; } @@ -560,26 +555,18 @@ class ClassLoader return false; } - - /** - * @return void - */ - private static function initializeIncludeClosure() - { - if (self::$includeFile !== null) { - return; - } - - /** - * Scope isolated include. - * - * Prevents access to $this/self from included files. - * - * @param string $file - * @return void - */ - self::$includeFile = \Closure::bind(static function($file) { - include $file; - }, null, null); - } +} + +/** + * Scope isolated include. + * + * Prevents access to $this/self from included files. + * + * @param string $file + * @return void + * @private + */ +function includeFile($file) +{ + include $file; } diff --git a/lib/composer/autoload_classmap.php b/lib/composer/autoload_classmap.php index a7f82e11e..b85485524 100644 --- a/lib/composer/autoload_classmap.php +++ b/lib/composer/autoload_classmap.php @@ -2,7 +2,7 @@ // autoload_classmap.php @generated by Composer -$vendorDir = dirname(__DIR__); +$vendorDir = dirname(dirname(__FILE__)); $baseDir = dirname($vendorDir); return array( @@ -449,6 +449,7 @@ return array( 'Combodo\\iTop\\Service\\Events\\EventServiceLog' => $baseDir . '/sources/Service/Events/EventServiceLog.php', 'Combodo\\iTop\\Service\\Events\\iEventServiceSetup' => $baseDir . '/sources/Service/Events/iEventServiceSetup.php', 'Combodo\\iTop\\Service\\Links\\LinkSetDataTransformer' => $baseDir . '/sources/Service/Links/LinkSetDataTransformer.php', + 'Combodo\\iTop\\Service\\Links\\LinkSetHelper' => $baseDir . '/sources/Service/Links/LinkSetHelper.php', 'Combodo\\iTop\\Service\\Links\\LinkSetModel' => $baseDir . '/sources/Service/Links/LinkSetModel.php', 'Combodo\\iTop\\Service\\Links\\LinkSetRepository' => $baseDir . '/sources/Service/Links/LinkSetRepository.php', 'Combodo\\iTop\\Service\\Links\\LinksBulkDataPostProcessor' => $baseDir . '/sources/Service/Links/LinksBulkDataPostProcessor.php', diff --git a/lib/composer/autoload_files.php b/lib/composer/autoload_files.php index cbe23f179..9ca87aae9 100644 --- a/lib/composer/autoload_files.php +++ b/lib/composer/autoload_files.php @@ -2,24 +2,24 @@ // autoload_files.php @generated by Composer -$vendorDir = dirname(__DIR__); +$vendorDir = dirname(dirname(__FILE__)); $baseDir = dirname($vendorDir); return array( - 'a4a119a56e50fbb293281d9a48007e0e' => $vendorDir . '/symfony/polyfill-php80/bootstrap.php', '6e3fae29631ef280660b3cdad06f25a8' => $vendorDir . '/symfony/deprecation-contracts/function.php', + 'a4a119a56e50fbb293281d9a48007e0e' => $vendorDir . '/symfony/polyfill-php80/bootstrap.php', '0e6d7bf4a5811bfa5cf40c5ccd6fae6a' => $vendorDir . '/symfony/polyfill-mbstring/bootstrap.php', '320cde22f66dd4f5d3fd621d3e88b98f' => $vendorDir . '/symfony/polyfill-ctype/bootstrap.php', - '23c18046f52bef3eea034657bafda50f' => $vendorDir . '/symfony/polyfill-php81/bootstrap.php', '0d59ee240a4cd96ddbb4ff164fccea4d' => $vendorDir . '/symfony/polyfill-php73/bootstrap.php', + '23c18046f52bef3eea034657bafda50f' => $vendorDir . '/symfony/polyfill-php81/bootstrap.php', '667aeda72477189d0494fecd327c3641' => $vendorDir . '/symfony/var-dumper/Resources/functions/dump.php', - '7b11c4dc42b3b3023073cb14e519683c' => $vendorDir . '/ralouphie/getallheaders/src/getallheaders.php', - 'c964ee0ededf28c96ebd9db5099ef910' => $vendorDir . '/guzzlehttp/promises/src/functions_include.php', 'e69f7f6ee287b969198c3c9d6777bd38' => $vendorDir . '/symfony/polyfill-intl-normalizer/bootstrap.php', - '37a3dc5111fe8f707ab4c132ef1dbc62' => $vendorDir . '/guzzlehttp/guzzle/src/functions_include.php', 'c9d07b32a2e02bc0fc582d4f0c1b56cc' => $vendorDir . '/laminas/laminas-servicemanager/src/autoload.php', + '7b11c4dc42b3b3023073cb14e519683c' => $vendorDir . '/ralouphie/getallheaders/src/getallheaders.php', '8825ede83f2f289127722d4e842cf7e8' => $vendorDir . '/symfony/polyfill-intl-grapheme/bootstrap.php', + 'c964ee0ededf28c96ebd9db5099ef910' => $vendorDir . '/guzzlehttp/promises/src/functions_include.php', + 'b6b991a57620e2fb6b2f66f03fe9ddc2' => $vendorDir . '/symfony/string/Resources/functions.php', + '37a3dc5111fe8f707ab4c132ef1dbc62' => $vendorDir . '/guzzlehttp/guzzle/src/functions_include.php', '25072dd6e2470089de65ae7bf11d3109' => $vendorDir . '/symfony/polyfill-php72/bootstrap.php', 'f598d06aa772fa33d905e87be6398fb1' => $vendorDir . '/symfony/polyfill-intl-idn/bootstrap.php', - 'b6b991a57620e2fb6b2f66f03fe9ddc2' => $vendorDir . '/symfony/string/Resources/functions.php', ); diff --git a/lib/composer/autoload_namespaces.php b/lib/composer/autoload_namespaces.php index 6629b7e09..1db5bf646 100644 --- a/lib/composer/autoload_namespaces.php +++ b/lib/composer/autoload_namespaces.php @@ -2,7 +2,7 @@ // autoload_namespaces.php @generated by Composer -$vendorDir = dirname(__DIR__); +$vendorDir = dirname(dirname(__FILE__)); $baseDir = dirname($vendorDir); return array( diff --git a/lib/composer/autoload_psr4.php b/lib/composer/autoload_psr4.php index 3b30be01d..eb2c95ace 100644 --- a/lib/composer/autoload_psr4.php +++ b/lib/composer/autoload_psr4.php @@ -2,7 +2,7 @@ // autoload_psr4.php @generated by Composer -$vendorDir = dirname(__DIR__); +$vendorDir = dirname(dirname(__FILE__)); $baseDir = dirname($vendorDir); return array( diff --git a/lib/composer/autoload_real.php b/lib/composer/autoload_real.php index 671821bc0..cc554d8d1 100644 --- a/lib/composer/autoload_real.php +++ b/lib/composer/autoload_real.php @@ -25,31 +25,46 @@ class ComposerAutoloaderInit7f81b4a2a468a061c306af5e447a9a9f require __DIR__ . '/platform_check.php'; spl_autoload_register(array('ComposerAutoloaderInit7f81b4a2a468a061c306af5e447a9a9f', 'loadClassLoader'), true, true); - self::$loader = $loader = new \Composer\Autoload\ClassLoader(\dirname(__DIR__)); + self::$loader = $loader = new \Composer\Autoload\ClassLoader(\dirname(\dirname(__FILE__))); spl_autoload_unregister(array('ComposerAutoloaderInit7f81b4a2a468a061c306af5e447a9a9f', 'loadClassLoader')); $includePaths = require __DIR__ . '/include_paths.php'; $includePaths[] = get_include_path(); set_include_path(implode(PATH_SEPARATOR, $includePaths)); - require __DIR__ . '/autoload_static.php'; - call_user_func(\Composer\Autoload\ComposerStaticInit7f81b4a2a468a061c306af5e447a9a9f::getInitializer($loader)); + $useStaticLoader = PHP_VERSION_ID >= 50600 && !defined('HHVM_VERSION') && (!function_exists('zend_loader_file_encoded') || !zend_loader_file_encoded()); + if ($useStaticLoader) { + require __DIR__ . '/autoload_static.php'; + + call_user_func(\Composer\Autoload\ComposerStaticInit7f81b4a2a468a061c306af5e447a9a9f::getInitializer($loader)); + } else { + $classMap = require __DIR__ . '/autoload_classmap.php'; + if ($classMap) { + $loader->addClassMap($classMap); + } + } $loader->setClassMapAuthoritative(true); $loader->register(true); - $filesToLoad = \Composer\Autoload\ComposerStaticInit7f81b4a2a468a061c306af5e447a9a9f::$files; - $requireFile = \Closure::bind(static function ($fileIdentifier, $file) { - if (empty($GLOBALS['__composer_autoload_files'][$fileIdentifier])) { - $GLOBALS['__composer_autoload_files'][$fileIdentifier] = true; - - require $file; - } - }, null, null); - foreach ($filesToLoad as $fileIdentifier => $file) { - $requireFile($fileIdentifier, $file); + if ($useStaticLoader) { + $includeFiles = Composer\Autoload\ComposerStaticInit7f81b4a2a468a061c306af5e447a9a9f::$files; + } else { + $includeFiles = require __DIR__ . '/autoload_files.php'; + } + foreach ($includeFiles as $fileIdentifier => $file) { + composerRequire7f81b4a2a468a061c306af5e447a9a9f($fileIdentifier, $file); } return $loader; } } + +function composerRequire7f81b4a2a468a061c306af5e447a9a9f($fileIdentifier, $file) +{ + if (empty($GLOBALS['__composer_autoload_files'][$fileIdentifier])) { + require $file; + + $GLOBALS['__composer_autoload_files'][$fileIdentifier] = true; + } +} diff --git a/lib/composer/autoload_static.php b/lib/composer/autoload_static.php index a578890db..c10b2e716 100644 --- a/lib/composer/autoload_static.php +++ b/lib/composer/autoload_static.php @@ -7,22 +7,22 @@ namespace Composer\Autoload; class ComposerStaticInit7f81b4a2a468a061c306af5e447a9a9f { public static $files = array ( - 'a4a119a56e50fbb293281d9a48007e0e' => __DIR__ . '/..' . '/symfony/polyfill-php80/bootstrap.php', '6e3fae29631ef280660b3cdad06f25a8' => __DIR__ . '/..' . '/symfony/deprecation-contracts/function.php', + 'a4a119a56e50fbb293281d9a48007e0e' => __DIR__ . '/..' . '/symfony/polyfill-php80/bootstrap.php', '0e6d7bf4a5811bfa5cf40c5ccd6fae6a' => __DIR__ . '/..' . '/symfony/polyfill-mbstring/bootstrap.php', '320cde22f66dd4f5d3fd621d3e88b98f' => __DIR__ . '/..' . '/symfony/polyfill-ctype/bootstrap.php', - '23c18046f52bef3eea034657bafda50f' => __DIR__ . '/..' . '/symfony/polyfill-php81/bootstrap.php', '0d59ee240a4cd96ddbb4ff164fccea4d' => __DIR__ . '/..' . '/symfony/polyfill-php73/bootstrap.php', + '23c18046f52bef3eea034657bafda50f' => __DIR__ . '/..' . '/symfony/polyfill-php81/bootstrap.php', '667aeda72477189d0494fecd327c3641' => __DIR__ . '/..' . '/symfony/var-dumper/Resources/functions/dump.php', - '7b11c4dc42b3b3023073cb14e519683c' => __DIR__ . '/..' . '/ralouphie/getallheaders/src/getallheaders.php', - 'c964ee0ededf28c96ebd9db5099ef910' => __DIR__ . '/..' . '/guzzlehttp/promises/src/functions_include.php', 'e69f7f6ee287b969198c3c9d6777bd38' => __DIR__ . '/..' . '/symfony/polyfill-intl-normalizer/bootstrap.php', - '37a3dc5111fe8f707ab4c132ef1dbc62' => __DIR__ . '/..' . '/guzzlehttp/guzzle/src/functions_include.php', 'c9d07b32a2e02bc0fc582d4f0c1b56cc' => __DIR__ . '/..' . '/laminas/laminas-servicemanager/src/autoload.php', + '7b11c4dc42b3b3023073cb14e519683c' => __DIR__ . '/..' . '/ralouphie/getallheaders/src/getallheaders.php', '8825ede83f2f289127722d4e842cf7e8' => __DIR__ . '/..' . '/symfony/polyfill-intl-grapheme/bootstrap.php', + 'c964ee0ededf28c96ebd9db5099ef910' => __DIR__ . '/..' . '/guzzlehttp/promises/src/functions_include.php', + 'b6b991a57620e2fb6b2f66f03fe9ddc2' => __DIR__ . '/..' . '/symfony/string/Resources/functions.php', + '37a3dc5111fe8f707ab4c132ef1dbc62' => __DIR__ . '/..' . '/guzzlehttp/guzzle/src/functions_include.php', '25072dd6e2470089de65ae7bf11d3109' => __DIR__ . '/..' . '/symfony/polyfill-php72/bootstrap.php', 'f598d06aa772fa33d905e87be6398fb1' => __DIR__ . '/..' . '/symfony/polyfill-intl-idn/bootstrap.php', - 'b6b991a57620e2fb6b2f66f03fe9ddc2' => __DIR__ . '/..' . '/symfony/string/Resources/functions.php', ); public static $prefixLengthsPsr4 = array ( @@ -814,6 +814,7 @@ class ComposerStaticInit7f81b4a2a468a061c306af5e447a9a9f 'Combodo\\iTop\\Service\\Events\\EventServiceLog' => __DIR__ . '/../..' . '/sources/Service/Events/EventServiceLog.php', 'Combodo\\iTop\\Service\\Events\\iEventServiceSetup' => __DIR__ . '/../..' . '/sources/Service/Events/iEventServiceSetup.php', 'Combodo\\iTop\\Service\\Links\\LinkSetDataTransformer' => __DIR__ . '/../..' . '/sources/Service/Links/LinkSetDataTransformer.php', + 'Combodo\\iTop\\Service\\Links\\LinkSetHelper' => __DIR__ . '/../..' . '/sources/Service/Links/LinkSetHelper.php', 'Combodo\\iTop\\Service\\Links\\LinkSetModel' => __DIR__ . '/../..' . '/sources/Service/Links/LinkSetModel.php', 'Combodo\\iTop\\Service\\Links\\LinkSetRepository' => __DIR__ . '/../..' . '/sources/Service/Links/LinkSetRepository.php', 'Combodo\\iTop\\Service\\Links\\LinksBulkDataPostProcessor' => __DIR__ . '/../..' . '/sources/Service/Links/LinksBulkDataPostProcessor.php', diff --git a/lib/composer/include_paths.php b/lib/composer/include_paths.php index af33c1491..d4fb96718 100644 --- a/lib/composer/include_paths.php +++ b/lib/composer/include_paths.php @@ -2,7 +2,7 @@ // include_paths.php @generated by Composer -$vendorDir = dirname(__DIR__); +$vendorDir = dirname(dirname(__FILE__)); $baseDir = dirname($vendorDir); return array( diff --git a/lib/composer/installed.php b/lib/composer/installed.php index 2c8578068..02a962cb6 100644 --- a/lib/composer/installed.php +++ b/lib/composer/installed.php @@ -5,7 +5,7 @@ 'type' => 'project', 'install_path' => __DIR__ . '/../../', 'aliases' => array(), - 'reference' => 'e645e9b00625680af30db024d4f2ef652179407c', + 'reference' => 'd0e6572fd0f88f05aa45e840a0311c2a0595d51a', 'name' => 'combodo/itop', 'dev' => true, ), @@ -25,7 +25,7 @@ 'type' => 'project', 'install_path' => __DIR__ . '/../../', 'aliases' => array(), - 'reference' => 'e645e9b00625680af30db024d4f2ef652179407c', + 'reference' => 'd0e6572fd0f88f05aa45e840a0311c2a0595d51a', 'dev_requirement' => false, ), 'combodo/tcpdf' => array( diff --git a/sources/Application/UI/Base/Component/DataTable/DataTableUIBlockFactory.php b/sources/Application/UI/Base/Component/DataTable/DataTableUIBlockFactory.php index 80237bcab..de8737d43 100644 --- a/sources/Application/UI/Base/Component/DataTable/DataTableUIBlockFactory.php +++ b/sources/Application/UI/Base/Component/DataTable/DataTableUIBlockFactory.php @@ -249,6 +249,15 @@ class DataTableUIBlockFactory extends AbstractUIBlockFactory $oButton->SetColor($aAction['color']); } $oButton->SetDataAttributes(['label' => Dict::S($aAction['label']), 'action-id' => $iKey, 'table-id' => $oTable->GetId()]); + if (array_key_exists('metadata', $aAction)) { + $aMetadata = $aAction['metadata']; + if (is_array($aMetadata)) { + foreach ($aMetadata as $key => $value) { + $oButton->AddDataAttribute($key, $value); + } + } + } + $oToolbar->AddSubBlock($oButton); } @@ -922,10 +931,14 @@ JS; /**give definition of id for select checkbox*/ 'row_actions', /** array of blocks displayed on every row */ - 'creation_in_modal_is_allowed', - /** bool to allow a creation of a new object of this type in a modal */ + 'creation_in_modal', + /** bool to perform a creation of a new object of this type in a modal */ + 'creation_in_modal_tooltip', + /** creation in modal button tooltip */ 'creation_in_modal_js_handler', /** Handler to call when trying to create a new object in modal */ + 'creation_disallowed', + /** Don't provide the standard object creation feature */ ]; } } \ No newline at end of file diff --git a/sources/Application/UI/Links/AbstractBlockLinkSetViewTable.php b/sources/Application/UI/Links/AbstractBlockLinkSetViewTable.php index 13c45640a..98dc73c51 100644 --- a/sources/Application/UI/Links/AbstractBlockLinkSetViewTable.php +++ b/sources/Application/UI/Links/AbstractBlockLinkSetViewTable.php @@ -9,7 +9,6 @@ namespace Combodo\iTop\Application\UI\Links; use ApplicationException; use ArchivedObjectException; use AttributeLinkedSet; -use Combodo\iTop\Application\UI\Base\Component\Alert\AlertUIBlockFactory; use Combodo\iTop\Application\UI\Base\Layout\UIContentBlock; use CoreException; use CoreWarning; @@ -17,7 +16,6 @@ use DBObject; use DictExceptionMissingString; use DisplayBlock; use Exception; -use MetaModel; use MySQLException; use Utils; use WebPage; @@ -41,6 +39,20 @@ abstract class AbstractBlockLinkSetViewTable extends UIContentBlock 'js/wizardhelper.js', ]; + // Dictionnary entries + public const BUTTON_TOOLTIP = 'UI:Links:Add:Button+'; + public const DICT_CREATE_BUTTON_TOOLTIP = 'UI:Links:Create:Button+'; + public const DICT_MODIFY_LINK_BUTTON_TOOLTIP = 'UI:Links:ModifyLink:Button+'; + public const DICT_MODIFY_LINK_MODAL_TITLE = 'UI:Links:ModifyLink:Modal:Title'; + public const DICT_MODIFY_OBJECT_BUTTON_TOOLTIP = 'UI:Links:ModifyObject:Button+'; + public const DICT_MODIFY_OBJECT_MODAL_TITLE = 'UI:Links:ModifyObject:Modal:Title'; + public const DICT_REMOVE_BUTTON_TOOLTIP = 'UI:Links:Remove:Button+'; + public const DICT_REMOVE_MODAL_TITLE = 'UI:Links:Remove:Modal:Title'; + public const DICT_REMOVE_MODAL_MESSAGE = 'UI:Links:Remove:Modal:Message'; + public const DICT_DELETE_BUTTON_TOOLTIP = 'UI:Links:Delete:Button+'; + public const DICT_DELETE_MODAL_TITLE = 'UI:Links:Delete:Modal:Title'; + public const DICT_DELETE_MODAL_MESSAGE = 'UI:Links:Delete:Modal:Message'; + /** @var DBObject $oDbObject db object witch link set belongs to */ protected DBObject $oDbObject; @@ -99,6 +111,25 @@ abstract class AbstractBlockLinkSetViewTable extends UIContentBlock $this->sTargetClass = $this->GetTargetClass(); } + + /** + * @param string $sKey + * @param \DBObject|null $oDBObject + * + * @return string + * @throws \ArchivedObjectException + * @throws \CoreException + */ + public function GetDictionaryEntry(string $sKey, DBObject $oDBObject = null) + { + return $this->oAttDef->SearchSpecificLabel($sKey, '', true, + $this->sObjectClass, + $this->oDbObject->Get('friendlyname'), + $this->oAttDef->GetLabel(), + $this->sTargetClass, + $oDBObject !== null ? $oDBObject->Get('friendlyname') : '{item}'); + } + /** * Initialize UI. * diff --git a/sources/Application/UI/Links/Direct/BlockDirectLinkSetViewTable.php b/sources/Application/UI/Links/Direct/BlockDirectLinkSetViewTable.php index 251c50162..dbfee84dd 100644 --- a/sources/Application/UI/Links/Direct/BlockDirectLinkSetViewTable.php +++ b/sources/Application/UI/Links/Direct/BlockDirectLinkSetViewTable.php @@ -49,9 +49,12 @@ class BlockDirectLinkSetViewTable extends AbstractBlockLinkSetViewTable } // Add creation in modal if the linkset is not readonly - if ($this->oAttDef->GetEditMode() != LINKSET_EDITMODE_NONE) { - $aExtraParams['creation_in_modal_is_allowed'] = true; + if (!$this->oAttDef->GetReadOnly() && $this->oAttDef->GetEditMode() != LINKSET_EDITMODE_NONE) { + $aExtraParams['creation_in_modal'] = true; + $aExtraParams['creation_in_modal_tooltip'] = $this->GetDictionaryEntry(static::DICT_CREATE_BUTTON_TOOLTIP); $aExtraParams['creation_in_modal_js_handler'] = "{$this->GetWidgetName()}.links_view_table('CreateLinkedObject');"; + } else { + $aExtraParams['creation_disallowed'] = true; } return $aExtraParams; @@ -69,30 +72,42 @@ class BlockDirectLinkSetViewTable extends AbstractBlockLinkSetViewTable case LINKSET_EDITMODE_ADDONLY: // The only possible action is to open (in a new window) the form to create a new object $aRowActions[] = array( - 'label' => 'UI:Links:ActionRow:Modify', - 'tooltip' => 'UI:Links:ActionRow:Modify+', + 'label' => 'UI:Links:ModifyObject:Button', + 'name' => 'ModifyButton', + 'tooltip' => $this->GetDictionaryEntry(static::DICT_MODIFY_OBJECT_BUTTON_TOOLTIP), 'icon_classes' => 'fas fa-pen', - 'js_row_action' => "{$this->GetWidgetName()}.links_view_table('ModifyLinkedObject', aRowData['{$this->oAttDef->GetLinkedClass()}/_key_/raw']);", + 'js_row_action' => "{$this->GetWidgetName()}.links_view_table('ModifyLinkedObject', aRowData['{$this->oAttDef->GetLinkedClass()}/_key_/raw'], oTrElement, aRowData['{$this->sTargetClass}/friendlyname']);", + 'metadata' => [ + 'modal-title' => $this->GetDictionaryEntry(static::DICT_MODIFY_OBJECT_MODAL_TITLE), + ], ); break; case LINKSET_EDITMODE_INPLACE: // The whole linkset can be edited 'in-place' case LINKSET_EDITMODE_ACTIONS: // Show the usual 'Actions' popup menu $aRowActions[] = array( - 'label' => 'UI:Links:ActionRow:Modify', - 'tooltip' => 'UI:Links:ActionRow:Modify+', + 'label' => 'UI:Links:ModifyObject:Button', + 'name' => 'ModifyButton', + 'tooltip' => $this->GetDictionaryEntry(static::DICT_MODIFY_OBJECT_BUTTON_TOOLTIP), 'icon_classes' => 'fas fa-pen', - 'js_row_action' => "{$this->GetWidgetName()}.links_view_table('ModifyLinkedObject', aRowData['{$this->oAttDef->GetLinkedClass()}/_key_/raw']);", + 'js_row_action' => "{$this->GetWidgetName()}.links_view_table('ModifyLinkedObject', aRowData['{$this->oAttDef->GetLinkedClass()}/_key_/raw'], oTrElement, aRowData['{$this->sTargetClass}/friendlyname']);", + 'metadata' => [ + 'modal-title' => $this->GetDictionaryEntry(static::DICT_MODIFY_OBJECT_MODAL_TITLE), + ], ); + $aRowActions[] = array( - 'label' => 'UI:Links:ActionRow:Delete', - 'tooltip' => 'UI:Links:ActionRow:Delete+', + 'label' => 'UI:Links:Delete:Button', + 'name' => 'DeleteButton', + 'tooltip' => $this->GetDictionaryEntry(static::DICT_DELETE_BUTTON_TOOLTIP), 'icon_classes' => 'fas fa-trash', 'color' => Button::ENUM_COLOR_SCHEME_DESTRUCTIVE, 'js_row_action' => "{$this->GetWidgetName()}.links_view_table('DeleteLinkedObject', aRowData['{$this->oAttDef->GetLinkedClass()}/_key_/raw'], oTrElement);", 'confirmation' => [ - 'message' => 'UI:Links:ActionRow:Delete:Confirmation', - 'message_row_data' => "{$this->sTargetClass}/hyperlink", + 'title' => $this->GetDictionaryEntry(static::DICT_DELETE_MODAL_TITLE), + 'message' => $this->GetDictionaryEntry(static::DICT_DELETE_MODAL_MESSAGE), + 'confirm_button_class' => 'ibo-is-danger', + 'row_data' => "{$this->sTargetClass}/hyperlink", 'do_not_show_again_pref_key' => $this->GetDoNotShowAgainPreferenceKey(), ], ); @@ -100,19 +115,25 @@ class BlockDirectLinkSetViewTable extends AbstractBlockLinkSetViewTable case LINKSET_EDITMODE_ADDREMOVE: // The whole linkset can be edited 'in-place' $aRowActions[] = array( - 'label' => 'UI:Links:ActionRow:Modify', - 'tooltip' => 'UI:Links:ActionRow:Modify+', + 'label' => 'UI:Links:ModifyObject:Button', + 'name' => 'ModifyButton', + 'tooltip' => $this->GetDictionaryEntry(static::DICT_MODIFY_OBJECT_BUTTON_TOOLTIP), 'icon_classes' => 'fas fa-pen', - 'js_row_action' => "{$this->GetWidgetName()}.links_view_table('ModifyLinkedObject', aRowData['{$this->oAttDef->GetLinkedClass()}/_key_/raw']);", + 'js_row_action' => "{$this->GetWidgetName()}.links_view_table('ModifyLinkedObject', aRowData['{$this->oAttDef->GetLinkedClass()}/_key_/raw'], oTrElement, aRowData['{$this->sTargetClass}/friendlyname']);", + 'metadata' => [ + 'modal-title' => $this->GetDictionaryEntry(static::DICT_MODIFY_OBJECT_MODAL_TITLE), + ], ); $aRowActions[] = array( - 'label' => 'UI:Links:ActionRow:Detach', - 'tooltip' => 'UI:Links:ActionRow:Detach+', + 'label' => 'UI:Links:Remove:Button', + 'name' => 'RemoveButton', + 'tooltip' => $this->GetDictionaryEntry(static::DICT_REMOVE_BUTTON_TOOLTIP), 'icon_classes' => 'fas fa-minus', - 'js_row_action' => "{$this->GetWidgetName()}.links_view_table('DetachLinkedObject', aRowData['{$this->sTargetClass}/_key_/raw'], oTrElement);", + 'js_row_action' => "{$this->GetWidgetName()}.links_view_table('DetachLinkedObject', aRowData['{$this->sTargetClass}/_key_/raw'], oTrElement, oTrElement);", 'confirmation' => [ - 'message' => 'UI:Links:ActionRow:Detach:Confirmation', - 'message_row_data' => "{$this->sTargetClass}/hyperlink", + 'title' => $this->GetDictionaryEntry(static::DICT_REMOVE_MODAL_TITLE), + 'message' => $this->GetDictionaryEntry(static::DICT_REMOVE_MODAL_MESSAGE), + 'row_data' => "{$this->sTargetClass}/hyperlink", 'do_not_show_again_pref_key' => $this->GetDoNotShowAgainPreferenceKey(), ], ); diff --git a/sources/Application/UI/Links/Indirect/BlockIndirectLinkSetViewTable.php b/sources/Application/UI/Links/Indirect/BlockIndirectLinkSetViewTable.php index 595c1e1e0..1e6a1c0e3 100644 --- a/sources/Application/UI/Links/Indirect/BlockIndirectLinkSetViewTable.php +++ b/sources/Application/UI/Links/Indirect/BlockIndirectLinkSetViewTable.php @@ -7,7 +7,6 @@ namespace Combodo\iTop\Application\UI\Links\Indirect; use Combodo\iTop\Application\UI\Links\AbstractBlockLinkSetViewTable; -use Combodo\iTop\Service\Links\LinkSetModel; use MetaModel; use PHPUnit\Exception; @@ -50,9 +49,9 @@ class BlockIndirectLinkSetViewTable extends AbstractBlockLinkSetViewTable 'zlist' => false, 'extra_fields' => $this->GetAttCodesToDisplay(), 'row_actions' => $this->GetRowActions(), - 'currentId' => $this->GetTableId(), - 'panel_title' => $this->oAttDef->GetLabel(), - 'panel_icon' => MetaModel::GetClassIcon($this->GetTargetClass(), false), + 'currentId' => $this->GetTableId(), + 'panel_title' => $this->oAttDef->GetLabel(), + 'panel_icon' => MetaModel::GetClassIcon($this->GetTargetClass(), false), ); // Description @@ -62,9 +61,11 @@ class BlockIndirectLinkSetViewTable extends AbstractBlockLinkSetViewTable // Add creation in modal if the linkset is not readonly if (!$this->oAttDef->GetReadOnly()) { - $sHostClass = get_class($this->oDbObject); - $aExtraParams['creation_in_modal_is_allowed'] = true; + $aExtraParams['creation_in_modal'] = true; + $aExtraParams['creation_in_modal_tooltip'] = $this->GetDictionaryEntry(static::BUTTON_TOOLTIP); $aExtraParams['creation_in_modal_js_handler'] = "{$this->GetWidgetName()}.links_view_table('CreateLinkedObject');"; + } else { + $aExtraParams['creation_disallowed'] = true; } return $aExtraParams; @@ -76,20 +77,26 @@ class BlockIndirectLinkSetViewTable extends AbstractBlockLinkSetViewTable $aRowActions = array(); $aRowActions[] = array( - 'label' => 'UI:Links:ActionRow:Modify', - 'tooltip' => 'UI:Links:ActionRow:Modify+', + 'label' => 'UI:Links:ModifyLink:Button', + 'name' => 'ModifyButton', + 'tooltip' => $this->GetDictionaryEntry(static::DICT_MODIFY_LINK_BUTTON_TOOLTIP), 'icon_classes' => 'fas fa-pen', - 'js_row_action' => "{$this->GetWidgetName()}.links_view_table('ModifyLinkedObject', aRowData['Link/_key_/raw']);", + 'js_row_action' => "{$this->GetWidgetName()}.links_view_table('ModifyLinkedObject', aRowData['Link/_key_/raw'], oTrElement, aRowData['Remote/friendlyname']);", + 'metadata' => [ + 'modal-title' => $this->GetDictionaryEntry(static::DICT_MODIFY_LINK_MODAL_TITLE), + ], ); $aRowActions[] = array( - 'label' => 'UI:Links:ActionRow:Detach', - 'tooltip' => 'UI:Links:ActionRow:Detach+', + 'label' => 'UI:Links:Remove:Button', + 'name' => 'RemoveButton', + 'tooltip' => $this->GetDictionaryEntry(static::DICT_REMOVE_BUTTON_TOOLTIP), 'icon_classes' => 'fas fa-minus', 'js_row_action' => "{$this->GetWidgetName()}.links_view_table('DeleteLinkedObject', aRowData['Link/_key_/raw'], oTrElement);", 'confirmation' => [ - 'message' => 'UI:Links:ActionRow:Detach:Confirmation', - 'message_row_data' => "Remote/hyperlink", + 'title' => $this->GetDictionaryEntry(static::DICT_REMOVE_MODAL_TITLE), + 'message' => $this->GetDictionaryEntry(static::DICT_REMOVE_MODAL_MESSAGE), + 'row_data' => "Remote/hyperlink", 'do_not_show_again_pref_key' => $this->GetDoNotShowAgainPreferenceKey(), ], ); diff --git a/sources/Controller/AjaxRenderController.php b/sources/Controller/AjaxRenderController.php index 982656e97..1744cee63 100644 --- a/sources/Controller/AjaxRenderController.php +++ b/sources/Controller/AjaxRenderController.php @@ -81,6 +81,7 @@ class AjaxRenderController $aObj[$sAlias."/_key_"] = $aObject[$sAlias]->GetKey(); $aObj[$sAlias."/_key_/raw"] = $aObject[$sAlias]->GetKey(); $aObj[$sAlias."/hyperlink"] = $aObject[$sAlias]->GetHyperlink(); + $aObj[$sAlias."/friendlyname"] = $aObject[$sAlias]->Get('friendlyname'); // N°5943 Protection against $aColumnsLoad having less class aliases than $aClassAliases, this is in case the method's consumer isn't passing data correctly if (false === array_key_exists($sAlias, $aColumnsLoad)) { diff --git a/sources/Controller/Base/Layout/ObjectController.php b/sources/Controller/Base/Layout/ObjectController.php index 4cf4886fb..7479f8a15 100644 --- a/sources/Controller/Base/Layout/ObjectController.php +++ b/sources/Controller/Base/Layout/ObjectController.php @@ -219,6 +219,7 @@ JS $bPrintable = utils::ReadParam('printable', '0') === '1'; $sClass = utils::ReadParam('class', '', false, 'class'); $sId = utils::ReadParam('id', ''); + $sFormTitle = utils::ReadPostedParam('form_title', null, utils::ENUM_SANITIZATION_FILTER_STRING); // Check parameters if (utils::IsNullOrEmptyString($sClass) || utils::IsNullOrEmptyString($sId)) @@ -243,6 +244,11 @@ JS $aFormExtraParams = array('wizard_container' => 1); FormHelper::UpdateFlagsFromContext($oObj, $aFormExtraParams); + // Allow form title customization + if (!utils::IsNullOrEmptyString($sFormTitle)) { + $aFormExtraParams['form_title'] = $sFormTitle; + } + if ($this->IsHandlingXmlHttpRequest()) { $oPage = new AjaxPage(''); $aFormExtraParams['js_handlers'] = []; diff --git a/sources/Controller/Links/LinkSetController.php b/sources/Controller/Links/LinkSetController.php index ec5ac169c..1373b52d9 100644 --- a/sources/Controller/Links/LinkSetController.php +++ b/sources/Controller/Links/LinkSetController.php @@ -11,6 +11,7 @@ use cmdbAbstractObject; use Combodo\iTop\Application\Helper\FormHelper; use Combodo\iTop\Application\UI\Base\Component\Form\FormUIBlockFactory; use Combodo\iTop\Controller\AbstractController; +use Combodo\iTop\Service\Links\LinkSetModel; use Combodo\iTop\Service\Router\Router; use Combodo\iTop\Service\Base\ObjectRepository; use Exception; @@ -198,17 +199,30 @@ class LinkSetController extends AbstractController } }); } -JS - ; +JS; + + + // Form title + /** @var \AttributeLinkedSet $oLinksetDef */ + $oLinksetDef = MetaModel::GetAttributeDef($sClass, $sAttCode); + $oHostObj = MetaModel::GetObject($sClass, $sId); + $sFormTitle = $oLinksetDef->SearchSpecificLabel('UI:Links:Add:Modal:Title', '', true, + $sClass, + $oHostObj->Get('friendlyname'), + $oLinksetDef->GetLabel(), + LinkSetModel::GetTargetClass($oLinksetDef)); + $aExtraParams = [ - 'noRelations' => true, - 'hide_transitions' => true, - 'formPrefix' => $sAttCode, - 'fieldsFlags' => $aFieldFlags, + 'noRelations' => true, + 'hide_transitions' => true, + 'formPrefix' => $sAttCode, + 'fieldsFlags' => $aFieldFlags, 'forceFieldsSubmission' => [ - $sExtKeyToMe + $sExtKeyToMe, ], - 'js_handlers' => [ + 'form_title' => $sFormTitle, + 'custom_button' => \Dict::S('UI:Button:Add'), + 'js_handlers' => [ 'form_on_submit' => $sFormOnSubmitJsCode, 'cancel_button_on_click' => <<