From 1fe9520b7eb8d2a84f5e26c9b0bc775e226dd031 Mon Sep 17 00:00:00 2001 From: bdalsass <95754414+bdalsass@users.noreply.github.com> Date: Mon, 6 Feb 2023 16:07:55 +0100 Subject: [PATCH] =?UTF-8?q?N=C2=B05904=20-=20Use=20attribute=20linked=20se?= =?UTF-8?q?t=20edit=20mode=20to=20enable=20actions=20(#440)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Add corresponding buttons depending on old edit mode (need to check with piR pour récuperer l'ancienne valeur. * N°5904 - Handle attribute linked set edit_mode * N°5904 Move calls to private jquery widget methods to public * N°5904 - Worker improvements add button on link tagset * Change itop set widget to new set block UI (5) * Change itop set widget to new set block UI (5) * Renommage variables JS avec le prefix combodo * Search dialog block id conflict with form id * add moved js files in iTopWebPage compatibility list --------- Co-authored-by: Stephen Abello --- application/cmdbabstract.class.inc.php | 2 +- application/ui.extkeywidget.class.inc.php | 10 +- js/links/link_set_worker.js | 133 ------------ .../links_direct_widget.js} | 74 +++---- js/links/links_set.js | 46 ++++ js/links/links_set_worker.js | 106 +++++++++ js/links/links_view_table_widget.js | 116 ++++++++++ js/{linkswidget.js => links/links_widget.js} | 0 js/objects/objects_worker.js | 60 ++++++ .../plugin_combodo_multi_values_synthesis.js | 6 +- .../plugin_combodo_update_operations.js | 21 +- lib/composer/installed.php | 4 +- pages/UniversalSearch.php | 1 - pages/tagadmin.php | 1 - .../UI/Base/Component/Input/Set/Set.php | 29 +++ .../UI/Links/AbstractBlockLinksViewTable.php | 43 +++- .../Direct/BlockDirectLinksEditTable.php | 202 ++++++++++++------ .../Direct/BlockDirectLinksViewTable.php | 14 +- .../Indirect/BlockIndirectLinksEditTable.php | 83 +++---- .../Indirect/BlockIndirectLinksViewTable.php | 9 +- .../UI/Links/Set/LinksSetUIBlockFactory.php | 14 +- sources/Application/WebPage/iTopWebPage.php | 3 + .../Base/Layout/ObjectController.php | 38 +++- .../Controller/Links/LinksetController.php | 60 +++++- sources/Service/Base/ObjectRepository.php | 62 ++++-- .../application/links/layout.ready.js.twig | 8 + .../datatable/config/layout.html.twig | 2 +- .../components/input/set/layout.ready.js.twig | 10 +- 28 files changed, 801 insertions(+), 356 deletions(-) delete mode 100644 js/links/link_set_worker.js rename js/{linksdirectwidget.js => links/links_direct_widget.js} (89%) create mode 100644 js/links/links_set.js create mode 100644 js/links/links_set_worker.js create mode 100644 js/links/links_view_table_widget.js rename js/{linkswidget.js => links/links_widget.js} (100%) create mode 100644 js/objects/objects_worker.js create mode 100644 templates/application/links/layout.ready.js.twig diff --git a/application/cmdbabstract.class.inc.php b/application/cmdbabstract.class.inc.php index 04bd1d9f8..af3c76d91 100644 --- a/application/cmdbabstract.class.inc.php +++ b/application/cmdbabstract.class.inc.php @@ -2337,7 +2337,7 @@ EOF if (array_key_exists('bulk_context', $aArgs)) { $oTagSetBlock = LinksSetUIBlockFactory::MakeForBulkLinkSet($iId, $oAttDef, $value, $sWizardHelperJsVarName, $aArgs['bulk_context']); } else { - $oTagSetBlock = LinksSetUIBlockFactory::MakeForLinkSet($iId, $oAttDef, $value, $sWizardHelperJsVarName); + $oTagSetBlock = LinksSetUIBlockFactory::MakeForLinkSet($iId, $oAttDef, $value, $sWizardHelperJsVarName, $aArgs['this']); } $oTagSetBlock->SetName("attr_{$sFieldPrefix}{$sAttCode}{$sNameSuffix}"); $aEventsList[] = 'validate'; diff --git a/application/ui.extkeywidget.class.inc.php b/application/ui.extkeywidget.class.inc.php index 162198c31..55347ff7e 100644 --- a/application/ui.extkeywidget.class.inc.php +++ b/application/ui.extkeywidget.class.inc.php @@ -685,15 +685,15 @@ JS } $oFilter->SetModifierProperty('UserRightsGetSelectFilter', 'bSearchMode', $this->bSearchMode); $oBlock = new DisplayBlock($oFilter, 'search', false, $aParams); - $oPage->AddUiBlock($oBlock->GetDisplay($oPage, $this->iId, + $oPage->AddUiBlock($oBlock->GetDisplay($oPage, 'dtc_'.$this->iId, array( - 'menu' => false, - 'currentId' => $this->iId, - 'table_id' => "dr_{$this->iId}", + 'menu' => false, + 'currentId' => $this->iId, + 'table_id' => "dr_{$this->iId}", 'table_inner_id' => "{$this->iId}_results", 'selection_mode' => true, 'selection_type' => 'single', - 'cssCount' => '#count_'.$this->iId.'_results', + 'cssCount' => '#count_'.$this->iId.'_results', ) )); $sCancel = Dict::S('UI:Button:Cancel'); diff --git a/js/links/link_set_worker.js b/js/links/link_set_worker.js deleted file mode 100644 index 807ba2e1d..000000000 --- a/js/links/link_set_worker.js +++ /dev/null @@ -1,133 +0,0 @@ -let LinkSetWorker = new function(){ - - // defines - const ROUTER_BASE_URL = '../pages/ajax.render.php'; - const ROUTE_LINK_SET_DELETE_OBJECT = 'linkset.delete_linked_object'; - const ROUTE_LINK_SET_DETACH_OBJECT = 'linkset.detach_linked_object'; - const ROUTE_LINK_SET_MODIFY_OBJECT = 'object.modify'; - const ROUTE_LINK_SET_CREATE_OBJECT = 'linkset.create_linked_object'; - - /** - * CallAjaxDeleteLinkedObject. - * - * @param sLinkedObjectClass - * @param sLinkedObjectKey - * @param sTableId - * @constructor - */ - const CallAjaxDeleteLinkedObject = function(sLinkedObjectClass, sLinkedObjectKey, sTableId){ - let oTableSettingsDialog = $('#datatable_dlg_datatable_' + sTableId); - - $.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 === true){ - oTableSettingsDialog.DataTableSettings('DoRefresh'); - } - else{ - CombodoModal.OpenInformativeModal(data.data.error_message, 'error'); - } - }); - }; - - /** - * CallAjaxDetachLinkedObject. - * - * @param sLinkedObjectClass - * @param sLinkedObjectKey - * @param sExternalKeyAttCode - * @param sTableId - * @constructor - */ - const CallAjaxDetachLinkedObject = function(sLinkedObjectClass, sLinkedObjectKey, sExternalKeyAttCode, sTableId){ - let oTableSettingsDialog = $('#datatable_dlg_datatable_' + sTableId); - - $.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 === true){ - oTableSettingsDialog.DataTableSettings('DoRefresh'); - } - else{ - CombodoModal.OpenInformativeModal(data.data.error_message, 'error'); - } - }); - }; - - /** - * CallAjaxModifyLinkedObject. - * - * @param {string} sLinkedObjectClass - * @param {string} sLinkedObjectKey - * @param {string} sTableId - * @constructor - */ - const CallAjaxModifyLinkedObject = function(sLinkedObjectClass, sLinkedObjectKey, sTableId){ - let oTable = $('#datatable_' + sTableId); - let oTableSettingsDialog = $('#datatable_dlg_datatable_' + sTableId); - - let oOptions = { - title: Dict.S('UI:Links:ActionRow:Modify:Modal:Title'), - content: { - endpoint: `${ROUTER_BASE_URL}?route=${ROUTE_LINK_SET_MODIFY_OBJECT}`, - data: { - class: sLinkedObjectClass, - id: sLinkedObjectKey, - }, - }, - extra_options: { - callback_on_modal_close: function () { - oTableSettingsDialog.DataTableSettings('DoRefresh'); - $(this).find("form").remove(); - $(this).dialog('destroy'); - } - }, - } - CombodoModal.OpenModal(oOptions); - }; - - /** - * @param {string} sTableId - */ - const CallAjaxCreateLinkedObject = function(sTableId){ - let oTable = $('#datatable_' + sTableId); - let oTableSettingsDialog = $('#datatable_dlg_datatable_' + sTableId); - let sClass = oTable.closest('[data-role="ibo-block-links-table"]').attr('data-link-class'); - let sAttCode = oTable.closest('[data-role="ibo-block-links-table"]').attr('data-link-attcode'); - let sHostObjectClass = oTable.closest('[data-role="ibo-object-details"]').attr('data-object-class'); - let sHostObjectId = oTable.closest('[data-role="ibo-object-details"]').attr('data-object-id'); - - let oOptions = { - title: Dict.S('UI:Layout:ObjectDetails:New:Modal:Title'), - content: { - endpoint: `${ROUTER_BASE_URL}?route=${ROUTE_LINK_SET_CREATE_OBJECT}`, - data: { - class: sClass, - att_code: sAttCode, - host_class: sHostObjectClass, - host_id: sHostObjectId - } - }, - extra_options: { - callback_on_modal_close: function () { - oTableSettingsDialog.DataTableSettings('DoRefresh'); - $(this).find("form").remove(); - $(this).dialog('destroy'); - } - }, - } - CombodoModal.OpenModal(oOptions); - }; - - return { - DeleteLinkedObject: CallAjaxDeleteLinkedObject, - DetachLinkedObject: CallAjaxDetachLinkedObject, - ModifyLinkedObject: CallAjaxModifyLinkedObject, - CreateLinkedObject: CallAjaxCreateLinkedObject - } -}; \ No newline at end of file diff --git a/js/linksdirectwidget.js b/js/links/links_direct_widget.js similarity index 89% rename from js/linksdirectwidget.js rename to js/links/links_direct_widget.js index 8d8516425..e2d1fad22 100644 --- a/js/linksdirectwidget.js +++ b/js/links/links_direct_widget.js @@ -33,15 +33,9 @@ $(function() 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', selection_title: 'Objects selection' }, - // buttons: ['create', 'delete'], oWizardHelper: null }, @@ -56,12 +50,6 @@ $(function() this.datatable = this.element.find('table.listResults'); - // var aButtonsTypes = ['delete', 'remove', 'modify', 'add', 'create']; - // this.oButtons = {}; - // for(k in aButtonsTypes) - // { - // this.oButtons[aButtonsTypes[k]] = $(''); - // } this.indicator = $(''); this.inputToBeCreated = $(''); this.toBeCreated = {}; @@ -80,27 +68,9 @@ $(function() .after(this.inputToBeRemoved) .after(this.indicator); - // for (k in this.options.buttons) { - // this.element.after(this.oButtons[this.options.buttons[k]]).after('   '); - // } - this.element.find('.selectList'+this.id).bind('change', function () { me._updateButtons(); }); - // this.oButtons['delete'].on('click', function () { - // 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(); @@ -132,24 +102,16 @@ $(function() } }, _updateButtons: function () { - var oChecked = $('.selectList'+this.id+':checked', this.element); + const 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); - break; - - case 1: - // this.oButtons['delete'].prop('disabled', false); - // this.oButtons['remove'].prop('disabled', false); - // this.oButtons['modify'].prop('disabled', false); + $('[data-role="ibo-button"][data-action="delete"]', this.element).prop('disabled', true); + $('[data-role="ibo-button"][data-action="detach"]', this.element).prop('disabled', true); break; default: - // this.oButtons['delete'].prop('disabled', false); - // this.oButtons['remove'].prop('disabled', false); - // this.oButtons['modify'].prop('disabled', true); + $('[data-role="ibo-button"][data-action="delete"]', this.element).prop('disabled', false); + $('[data-role="ibo-button"][data-action="detach"]', this.element).prop('disabled', false); break; } }, @@ -186,7 +148,7 @@ $(function() this.oDlg.dialog('option', {position: {my: "center", at: "center", of: window}}); }, _createRow: function () { - // this.oButtons['create'].prop('disabled', true); + $('[data-role="ibo-button"][data-action="create"]', this.element).prop('disabled', true); this.indicator.html(''); oParams = this.options.submit_parameters; oParams.operation = 'createObject'; @@ -225,14 +187,14 @@ $(function() } }); me.indicator.html(''); - // me.oButtons['create'].prop('disabled', false); + $('[data-role="ibo-button"][data-action="create"]', this.element).prop('disabled', false); me._updateDlgPosition(); }); }, _selectToAdd: function() { - // this.oButtons['add'].prop('disabled', true); + $('[data-role="ibo-button"][data-action="add"]', this.element).prop('disabled', true); this.indicator.html(''); oParams = this.options.submit_parameters; oParams.operation = 'selectObjectsToAdd'; @@ -298,7 +260,7 @@ $(function() }); me.indicator.html(''); - // me.oButtons['add'].prop('disabled', false); + $('[data-role="ibo-button"][data-action="add"]', this.element).prop('disabled', false); if (me.options.do_search) { me._onSearchToAdd(); @@ -468,7 +430,7 @@ $(function() me._updateTable(); me.indicator.html(''); - // me.oButtons['add'].prop('disabled', false); + $('[data-role="ibo-button"][data-action="add"]', this.element).prop('disabled', false); me._updateTableInformation(); }); @@ -535,7 +497,7 @@ $(function() oParams.tempId = nextIdx; var me = this; - // this.oButtons['create'].prop('disabled', true); + $('[data-role="ibo-button"][data-action="create"]', this.element).prop('disabled', true); this.indicator.html(''); $.post(this.options.submit_to, oParams, function (data) { @@ -545,7 +507,7 @@ $(function() me._updateTable(); me.indicator.html(''); - // me.oButtons['create'].prop('disabled', false); + $('[data-role="ibo-button"][data-action="create"]', this.element).prop('disabled', false); }); } }, @@ -656,6 +618,18 @@ $(function() Remove: function(oCheckbox) // for public access { this._removeRow(oCheckbox); + }, + selectToAdd: function(){ + this._selectToAdd(); + }, + removeSelection: function(){ + this._removeSelection(); + }, + createRow: function(){ + this._createRow(); + }, + deleteSelection: function(){ + this._deleteSelection(); } }); }); \ No newline at end of file diff --git a/js/links/links_set.js b/js/links/links_set.js new file mode 100644 index 000000000..383c2f66e --- /dev/null +++ b/js/links/links_set.js @@ -0,0 +1,46 @@ +let CombodoLinkSet = new function () { + + /** + * Create a new link object and add it to set widget. + * + * @param sLinkedClass + * @param sCode + * @param sHostObjectClass + * @param sHostObjectKey + * @param sRemoteExtKey + * @param sRemoteClass + * @param oWidget + * @constructor + */ + const CallCreateLinkedObject = function(sLinkedClass, sCode, sHostObjectClass, sHostObjectKey, sRemoteExtKey, sRemoteClass, oWidget) + { + // Create link object + CombodoLinkSetWorker.CreateLinkedObject(sLinkedClass, sCode, sHostObjectClass, sHostObjectKey, + function(){ + $(this).find("form").remove(); + $(this).dialog('destroy'); + }, + function(event, data){ + + // We have just create a link object, now request the remote object + CombodoLinkSetWorker.GetRemoteObject(data.data.object.class_name, data.data.object.key, sRemoteExtKey, sRemoteClass, function(data){ + + // Add the new remote object in widget set options list + const selectize = oWidget[0].selectize; + selectize.addOption(data.data.object); + selectize.refreshOptions(false); + + // Select the new remote object + selectize.addItem(data.data.object.key); + + // Add to initial values, to handle remove action + selectize.addInitialValue(data.data.object.key); + }); + }); + } + + + return { + CreateLinkedObject: CallCreateLinkedObject, + } +}; \ No newline at end of file diff --git a/js/links/links_set_worker.js b/js/links/links_set_worker.js new file mode 100644 index 000000000..abfa8200a --- /dev/null +++ b/js/links/links_set_worker.js @@ -0,0 +1,106 @@ +let CombodoLinkSetWorker = new function(){ + + // defines + const ROUTER_BASE_URL = '../pages/ajax.render.php'; + const ROUTE_LINK_SET_DELETE_OBJECT = 'linkset.delete_linked_object'; + const ROUTE_LINK_SET_DETACH_OBJECT = 'linkset.detach_linked_object'; + const ROUTE_LINK_SET_CREATE_OBJECT = 'linkset.create_linked_object'; + const ROUTE_LINK_GET_REMOTE_OBJECT = 'linkset.get_remote_object'; + + /** + * CallAjaxDeleteLinkedObject. + * + * @param {string} sLinkedObjectClass + * @param {string} sLinkedObjectKey + * @param oOnResponseCallback + * @constructor + */ + const CallAjaxDeleteLinkedObject = function(sLinkedObjectClass, sLinkedObjectKey, oOnResponseCallback){ + + $.post(`${ROUTER_BASE_URL}?route=${ROUTE_LINK_SET_DELETE_OBJECT}`, { + linked_object_class: sLinkedObjectClass, + linked_object_key: sLinkedObjectKey, + transaction_id: $('#linkset_transactions_id').val() + }, oOnResponseCallback); + }; + + /** + * CallAjaxDetachLinkedObject. + * + * @param {string} sLinkedObjectClass + * @param {string} sLinkedObjectKey + * @param {string} sExternalKeyAttCode + * @param oOnResponseCallback + * @constructor + */ + const CallAjaxDetachLinkedObject = function(sLinkedObjectClass, sLinkedObjectKey, sExternalKeyAttCode, oOnResponseCallback){ + + $.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() + }, oOnResponseCallback); + }; + + /** + * CallAjaxCreateLinkedObject. + * + * @param {string} sClass + * @param {string} sAttCode + * @param {string} sHostObjectClass + * @param {string} sHostObjectId + * @param oOnModalCloseCallback + * @param oOnFormSubmittedCallback + */ + const CallAjaxCreateLinkedObject = function(sClass, sAttCode, sHostObjectClass, sHostObjectId, oOnModalCloseCallback = null, oOnFormSubmittedCallback = null){ + + let oOptions = { + title: Dict.S('UI:Links:New:Modal:Title'), + content: { + endpoint: `${ROUTER_BASE_URL}?route=${ROUTE_LINK_SET_CREATE_OBJECT}`, + data: { + class: sClass, + att_code: sAttCode, + host_class: sHostObjectClass, + host_id: sHostObjectId + } + }, + extra_options: { + callback_on_modal_close: oOnModalCloseCallback + }, + } + + const oModal = CombodoModal.OpenModal(oOptions); + if(oOnFormSubmittedCallback !== null){ + oModal.on('itop.form.submitted', 'form', oOnFormSubmittedCallback); + } + }; + + /** + * CallGetRemoteObject. + * + * @param sLinkedObjectClass + * @param sLinkedObjectKey + * @param sExternalKeyAttCode + * @param sRemoteClass + * @param oOnResponseCallback + * @constructor + */ + const CallGetRemoteObject = function(sLinkedObjectClass, sLinkedObjectKey, sExternalKeyAttCode, sRemoteClass, oOnResponseCallback){ + + $.post(`${ROUTER_BASE_URL}?route=${ROUTE_LINK_GET_REMOTE_OBJECT}`, { + linked_object_class: sLinkedObjectClass, + linked_object_key: sLinkedObjectKey, + external_key_att_code: sExternalKeyAttCode, + remote_class: sRemoteClass + }, oOnResponseCallback); + }; + + return { + DeleteLinkedObject: CallAjaxDeleteLinkedObject, + DetachLinkedObject: CallAjaxDetachLinkedObject, + CreateLinkedObject: CallAjaxCreateLinkedObject, + GetRemoteObject: CallGetRemoteObject + } +}; \ No newline at end of file diff --git a/js/links/links_view_table_widget.js b/js/links/links_view_table_widget.js new file mode 100644 index 000000000..576ed395f --- /dev/null +++ b/js/links/links_view_table_widget.js @@ -0,0 +1,116 @@ +$(function() +{ + // the widget definition, where "itop" is the namespace, + // "links_view_table" the widget name + $.widget( "itop.links_view_table", + { + + // default options + options: + { + link_class: null, + external_key_to_me: null + }, + + // the constructor + _create: function () { + $Table = $('table', this.element); + this.$tableSettingsDialog = $('#datatable_dlg_' + $Table.attr('id')); + }, + + // the destructor + _destroy: function () { + }, + + /** + * DeleteLinkedObject. + * + * @param sLinkedObjectKey + * @param oTrElement + * @constructor + */ + DeleteLinkedObject: function (sLinkedObjectKey, oTrElement) { + + const me = this; + + // link object deletion + CombodoLinkSetWorker.DeleteLinkedObject(this.options.link_class, sLinkedObjectKey, function (data) { + if (data.data.success === true) { + oTrElement.remove(); + } else { + CombodoModal.OpenInformativeModal(data.data.error_message, 'error'); + } + }); + }, + + /** + * DetachLinkedObject. + * + * @param sLinkedObjectKey + * @param oTrElement + * @constructor + */ + DetachLinkedObject: function (sLinkedObjectKey, oTrElement) { + + const me = this; + + // link object unlink + CombodoLinkSetWorker.DetachLinkedObject(this.options.link_class, sLinkedObjectKey, this.options.external_key_to_me, function (data) { + if (data.data.success === true) { + oTrElement.remove(); + } else { + CombodoModal.OpenInformativeModal(data.data.error_message, 'error'); + } + }); + }, + + /** + * CreateLinkedObject. + * + */ + CreateLinkedObject: function () { + + const me = this; + + // retrieve table + const $Table = $('table', this.element); + + // 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'); + const sHostObjectClass = $Table.closest('[data-role="ibo-object-details"]').attr('data-object-class'); + const sHostObjectId = $Table.closest('[data-role="ibo-object-details"]').attr('data-object-id'); + + // link object creation + CombodoLinkSetWorker.CreateLinkedObject(sClass, sAttCode, sHostObjectClass, sHostObjectId, function(){ + $(this).find("form").remove(); + $(this).dialog('destroy'); + },function (event, data) { + if(data.success){ + me.$tableSettingsDialog.DataTableSettings('DoRefresh'); + } + }); + }, + + /** + * ModifyLinkedObject. + * + * @param {string} sLinkedObjectKey + */ + ModifyLinkedObject: function (sLinkedObjectKey) { + + const me = this; + + // link object modification + ObjectWorker.ModifyObject(this.options.link_class, sLinkedObjectKey, function () { + $(this).find("form").remove(); + $(this).dialog('destroy'); + }, function(event, data){ + if(data.success) { + me.$tableSettingsDialog.DataTableSettings('DoRefresh'); + } + }); + }, + + }); +}); \ No newline at end of file diff --git a/js/linkswidget.js b/js/links/links_widget.js similarity index 100% rename from js/linkswidget.js rename to js/links/links_widget.js diff --git a/js/objects/objects_worker.js b/js/objects/objects_worker.js new file mode 100644 index 000000000..216562ef7 --- /dev/null +++ b/js/objects/objects_worker.js @@ -0,0 +1,60 @@ +let ObjectWorker = new function(){ + + // defines + const ROUTER_BASE_URL = '../pages/ajax.render.php'; + const ROUTE_MODIFY_OBJECT = 'object.modify'; + const ROUTE_GET_OBJECT = 'object.get'; + + /** + * CallAjaxModifyObject. + * + * @param {string} sObjectClass + * @param {string} sObjectKey + * @param oOnModalCloseCallback + * @param oOnFormSubmittedCallback + * @constructor + */ + const CallAjaxModifyObject = function(sObjectClass, sObjectKey, oOnModalCloseCallback, oOnFormSubmittedCallback){ + + let oOptions = { + title: Dict.S('UI:Links:ActionRow:Modify:Modal:Title'), + content: { + endpoint: `${ROUTER_BASE_URL}?route=${ROUTE_MODIFY_OBJECT}`, + data: { + class: sObjectClass, + id: sObjectKey, + }, + }, + extra_options: { + callback_on_modal_close: oOnModalCloseCallback + }, + } + + const oModal = CombodoModal.OpenModal(oOptions); + if(oOnFormSubmittedCallback !== null){ + oModal.on('itop.form.submitted', 'form', oOnFormSubmittedCallback); + } + }; + + /** + * CallAjaxGetObject. + * + * @param {string} sObjectClass + * @param {string} sObjectKey + * @param oOnResponseCallback + * @constructor + */ + const CallAjaxGetObject = function(sObjectClass, sObjectId, oOnResponseCallback){ + + $.post(`${ROUTER_BASE_URL}?route=${ROUTE_GET_OBJECT}`, { + object_class: sObjectClass, + object_key: sObjectId, + }, oOnResponseCallback); + }; + + + return { + ModifyObject: CallAjaxModifyObject, + GetObject: CallAjaxGetObject + } +}; \ No newline at end of file diff --git a/js/selectize/plugin_combodo_multi_values_synthesis.js b/js/selectize/plugin_combodo_multi_values_synthesis.js index 6ffe9d612..7ea5b0cac 100644 --- a/js/selectize/plugin_combodo_multi_values_synthesis.js +++ b/js/selectize/plugin_combodo_multi_values_synthesis.js @@ -78,14 +78,14 @@ Selectize.define("combodo_multi_values_synthesis", function (aOptions) { } // Element exist in default selection, // click allow user to switch between add or ignore states - if(oSelf.settings.initial.includes(sItemValue)) { + if(oSelf.plugins.settings.combodo_update_operations.initial.includes(sItemValue)) { oSelf.listenClick($Item, sItemValue); } return; } // If no operation to restore - if(!oSelf.settings.initial.includes(sItemValue)) { + if(!oSelf.plugins.settings.combodo_update_operations.initial.includes(sItemValue)) { // Element doesn't exist in initial value, we mark it as added oSelf.Add($Item, sItemValue); @@ -112,7 +112,7 @@ Selectize.define("combodo_multi_values_synthesis", function (aOptions) { const $Item = oSelf.getItem(sItem); // Element doesn't exist in default selection, - if(!oSelf.settings.initial.includes(sItem)) { + if(!oSelf.plugins.settings.combodo_update_operations.initial.includes(sItem)) { // Remove operation delete aOperations[sItem]; diff --git a/js/selectize/plugin_combodo_update_operations.js b/js/selectize/plugin_combodo_update_operations.js index a129f3969..51edd8252 100644 --- a/js/selectize/plugin_combodo_update_operations.js +++ b/js/selectize/plugin_combodo_update_operations.js @@ -15,11 +15,18 @@ * * You should have received a copy of the GNU Affero General Public License */ -Selectize.define("combodo_update_operations", function () { +Selectize.define("combodo_update_operations", function (aOptions) { // Selectize instance let oSelf = this; + // Plugin options + aOptions = $.extend({ + initial: [], + }, + aOptions + ); + // Plugin variables oSelf.bIsInitialized = false; oSelf.operations = {}; @@ -88,7 +95,7 @@ Selectize.define("combodo_update_operations", function () { // Scan items in current value and not in initial value aCurrentItems.forEach(function(e){ - if(!oSelf.settings.initial.includes(e)){ + if(!aOptions.initial.includes(e)){ oSelf.operations[e] = { operation: 'add', data: CombodoGlobalToolbox.ExtractArrayItemsContainingThisKeyAndValue(aCurrentOptions, oSelf.settings.valueField, e) @@ -97,7 +104,7 @@ Selectize.define("combodo_update_operations", function () { }); // scan items in initial value and not in current value - oSelf.settings.initial.forEach(function(e){ + aOptions.initial.forEach(function(e){ if(!aCurrentItems.includes(e)){ oSelf.operations[e] = { operation: 'remove', @@ -109,4 +116,12 @@ Selectize.define("combodo_update_operations", function () { }; })(); + // Declare addInitialValue function + oSelf.addInitialValue = (function () { + return function (value) { + aOptions.initial.push(value); + oSelf.updateOperationsInput(); + }; + })(); + }); \ No newline at end of file diff --git a/lib/composer/installed.php b/lib/composer/installed.php index a06e2ae12..08f2006c5 100644 --- a/lib/composer/installed.php +++ b/lib/composer/installed.php @@ -5,7 +5,7 @@ 'type' => 'project', 'install_path' => __DIR__ . '/../../', 'aliases' => array(), - 'reference' => '9482139b5aec9978f05b1cbb0542b24c18681819', + 'reference' => 'f25f91ad79b38d9dcbe2a175bdb2fd2635255968', 'name' => 'combodo/itop', 'dev' => true, ), @@ -25,7 +25,7 @@ 'type' => 'project', 'install_path' => __DIR__ . '/../../', 'aliases' => array(), - 'reference' => '9482139b5aec9978f05b1cbb0542b24c18681819', + 'reference' => 'f25f91ad79b38d9dcbe2a175bdb2fd2635255968', 'dev_requirement' => false, ), 'combodo/tcpdf' => array( diff --git a/pages/UniversalSearch.php b/pages/UniversalSearch.php index e4649b57e..3b829cc78 100644 --- a/pages/UniversalSearch.php +++ b/pages/UniversalSearch.php @@ -34,7 +34,6 @@ $oP->add_linked_script("../js/json.js"); $oP->add_linked_script("../js/forms-json-utils.js"); $oP->add_linked_script("../js/wizardhelper.js"); $oP->add_linked_script("../js/wizard.utils.js"); -$oP->add_linked_script("../js/linkswidget.js"); $oP->add_linked_script("../js/extkeywidget.js"); $oP->add_linked_script("../js/jquery.blockUI.js"); diff --git a/pages/tagadmin.php b/pages/tagadmin.php index a6a6fede6..8263c2104 100644 --- a/pages/tagadmin.php +++ b/pages/tagadmin.php @@ -37,7 +37,6 @@ try $oP->add_linked_script("../js/forms-json-utils.js"); $oP->add_linked_script("../js/wizardhelper.js"); $oP->add_linked_script("../js/wizard.utils.js"); - $oP->add_linked_script("../js/linkswidget.js"); $oP->add_linked_script("../js/extkeywidget.js"); $oP->add_linked_script("../js/jquery.blockUI.js"); diff --git a/sources/Application/UI/Base/Component/Input/Set/Set.php b/sources/Application/UI/Base/Component/Input/Set/Set.php index 058873562..fbad58c5d 100644 --- a/sources/Application/UI/Base/Component/Input/Set/Set.php +++ b/sources/Application/UI/Base/Component/Input/Set/Set.php @@ -37,6 +37,7 @@ class Set extends AbstractInput public const DEFAULT_JS_ON_READY_TEMPLATE_REL_PATH = 'base/components/input/set/layout'; public const DEFAULT_JS_FILES_REL_PATH = [ + 'js/links/links_set_worker.js', 'js/selectize/plugin_combodo_add_button.js', 'js/selectize/plugin_combodo_auto_position.js', 'js/selectize/plugin_combodo_update_operations.js', @@ -55,6 +56,9 @@ class Set extends AbstractInput /** @var bool $bHasAddOptionButton Enable add option button */ private bool $bHasAddOptionButton; + /** @var string|null $sAddOptionButtonJsOnClick JS code to execute on button click */ + private ?string $sAddOptionButtonJsOnClick; + /** @var string $sAddButtonTitle Add button title */ private string $sAddButtonTitle; @@ -103,6 +107,7 @@ class Set extends AbstractInput $this->iMaxOptions = null; $this->bHasRemoveItemButton = true; $this->bHasAddOptionButton = false; + $this->sAddOptionButtonJsOnClick = null; $this->sAddButtonTitle = Dict::S('UI:Button:Create'); $this->bIsPreloadEnabled = false; $this->sTemplateOptions = null; @@ -183,6 +188,30 @@ class Set extends AbstractInput return $this->bHasRemoveItemButton; } + /** + * SetAddOptionButtonJsOnClick. + * + * @param string $sJsOnClick + * + * @return $this + */ + public function SetAddOptionButtonJsOnClick(string $sJsOnClick): Set + { + $this->sAddOptionButtonJsOnClick = $sJsOnClick; + + return $this; + } + + /** + * GetAddOptionButtonJsOnClick. + * + * @return string + */ + public function GetAddOptionButtonJsOnClick(): string + { + return $this->sAddOptionButtonJsOnClick; + } + /** * SetHasAddOptionButton. * diff --git a/sources/Application/UI/Links/AbstractBlockLinksViewTable.php b/sources/Application/UI/Links/AbstractBlockLinksViewTable.php index d8b4da691..459e56272 100644 --- a/sources/Application/UI/Links/AbstractBlockLinksViewTable.php +++ b/sources/Application/UI/Links/AbstractBlockLinksViewTable.php @@ -32,10 +32,12 @@ use WebPage; 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', + public const BLOCK_CODE = 'ibo-abstract-block-links-view-table'; + public const DEFAULT_JS_ON_READY_TEMPLATE_REL_PATH = 'application/links/layout'; + public const DEFAULT_JS_FILES_REL_PATH = [ + 'js/links/links_view_table_widget.js', + 'js/links/links_set_worker.js', + 'js/objects/objects_worker.js', 'js/wizardhelper.js', ]; @@ -70,7 +72,7 @@ abstract class AbstractBlockLinksViewTable extends UIContentBlock */ public function __construct(WebPage $oPage, DBObject $oDbObject, string $sObjectClass, string $sAttCode, AttributeLinkedSet $oAttDef) { - parent::__construct('', ["ibo-block-links-table"]); + parent::__construct("links_view_table_$sAttCode", ["ibo-block-links-table"]); // retrieve parameters $this->oAttDef = $oAttDef; @@ -204,6 +206,8 @@ abstract class AbstractBlockLinksViewTable extends UIContentBlock /** + * GetAttCode. + * * @return string */ public function GetAttCode(): string @@ -211,4 +215,33 @@ abstract class AbstractBlockLinksViewTable extends UIContentBlock return $this->sAttCode; } + /** + * GetLinkedClass. + * + * @return mixed + */ + public function GetLinkedClass() + { + return $this->oAttDef->GetLinkedClass(); + } + + /** + * GetExternalKeyToMe. + * + * @return mixed + */ + public function GetExternalKeyToMe() + { + return $this->oAttDef->GetExtKeyToMe(); + } + + /** + * GetWidgetName. + * + * @return string + */ + public function GetWidgetName(): string + { + return "oWidget{$this->GetId()}"; + } } \ No newline at end of file diff --git a/sources/Application/UI/Links/Direct/BlockDirectLinksEditTable.php b/sources/Application/UI/Links/Direct/BlockDirectLinksEditTable.php index da8ae25d8..f99e289aa 100644 --- a/sources/Application/UI/Links/Direct/BlockDirectLinksEditTable.php +++ b/sources/Application/UI/Links/Direct/BlockDirectLinksEditTable.php @@ -6,18 +6,28 @@ namespace Combodo\iTop\Application\UI\Links\Direct; +use ApplicationContext; +use ArchivedObjectException; +use AttributeLinkedSet; 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\Toolbar; 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 ConfigException; +use CoreException; +use CoreUnexpectedValue; +use DBObjectSet; use Dict; +use DictExceptionMissingString; +use Exception; use MetaModel; +use MySQLException; +use UILinksWidgetDirect; use utils; +use WebPage; /** * Class BlockDirectLinksEditTable @@ -31,40 +41,43 @@ 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'; + public const DEFAULT_JS_FILES_REL_PATH = [ + 'js/links/links_direct_widget.js', + ]; - /** @var \UILinksWidgetDirect */ - public \UILinksWidgetDirect $oUILinksDirectWidget; + /** @var UILinksWidgetDirect $oUILinksDirectWidget */ + public UILinksWidgetDirect $oUILinksDirectWidget; - /** @var \AttributeLinkedSet */ - private \AttributeLinkedSet $oAttributeLinkedSet; + /** @var AttributeLinkedSet $oAttributeLinkedSet */ + private AttributeLinkedSet $oAttributeLinkedSet; - /** @var string */ + /** @var string $sInputName */ public string $sInputName; - /** @var array */ + /** @var array $aLabels */ public array $aLabels; - /** @var string */ + /** @var string $sSubmitUrl */ public string $sSubmitUrl; - /** @var string */ + /** @var string $sWizHelper */ public string $sWizHelper; - /** @var string */ + /** @var string $sJSDoSearch */ public string $sJSDoSearch; /** * Constructor. * - * @param \UILinksWidgetDirect $oUILinksDirectWidget + * @param UILinksWidgetDirect $oUILinksDirectWidget * @param string $sId * - * @throws \ConfigException - * @throws \CoreException - * @throws \DictExceptionMissingString - * @throws \Exception + * @throws ConfigException + * @throws CoreException + * @throws DictExceptionMissingString + * @throws Exception */ - public function __construct(\UILinksWidgetDirect $oUILinksDirectWidget, string $sId) + public function __construct(UILinksWidgetDirect $oUILinksDirectWidget, string $sId) { parent::__construct($sId, ["ibo-block-direct-links--edit-in-place"]); @@ -73,18 +86,14 @@ class BlockDirectLinksEditTable extends UIContentBlock // 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(); + $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()); + $bDoSearch = !utils::IsHighCardinality($this->oUILinksDirectWidget->GetLinkedClass()); $this->sJSDoSearch = $bDoSearch ? 'true' : 'false'; // Initialization @@ -98,7 +107,7 @@ class BlockDirectLinksEditTable extends UIContentBlock * Initialisation. * * @return void - * @throws \Exception + * @throws Exception */ private function Init() { @@ -109,8 +118,8 @@ class BlockDirectLinksEditTable extends UIContentBlock * Initialize UI. * * @return void - * @throws \CoreException - * @throws \Exception + * @throws CoreException + * @throws Exception */ private function InitUI() { @@ -118,15 +127,14 @@ class BlockDirectLinksEditTable extends UIContentBlock } /** - * @param \WebPage $oPage - * @param \DBObjectSet $oValue + * @param WebPage $oPage + * @param DBObjectSet $oValue * @param string $sFormPrefix * * @return void */ - public function InitTable(\WebPage $oPage, $oValue, string $sFormPrefix) + public function InitTable(WebPage $oPage, DBObjectSet $oValue, string $sFormPrefix) { - /** @todo fields initialization */ $this->sInputName = $sFormPrefix.'attr_'.$this->oUILinksDirectWidget->GetAttCode(); $this->sWizHelper = 'oWizardHelper'.$sFormPrefix; @@ -138,7 +146,7 @@ class BlockDirectLinksEditTable extends UIContentBlock $oDatatable->SetOptions(['select_mode' => 'custom', 'disable_hyperlinks' => true]); // Panel - $aTablePanel = PanelUIBlockFactory::MakeForClass($this->oUILinksDirectWidget->GetLinkedClass(), $this->oAttributeLinkedSet->GetLabel()) + $oTablePanel = PanelUIBlockFactory::MakeForClass($this->oUILinksDirectWidget->GetLinkedClass(), $this->oAttributeLinkedSet->GetLabel()) ->SetSubTitle(Dict::Format('UI:Pagination:HeaderNoSelection', count($aRows))) ->SetIcon(MetaModel::GetClassIcon($this->oUILinksDirectWidget->GetLinkedClass(), false)) ->AddCSSClass('ibo-datatable-panel'); @@ -146,30 +154,17 @@ class BlockDirectLinksEditTable extends UIContentBlock // - Panel description $sDescription = $this->oAttributeLinkedSet->GetDescription(); if (utils::IsNotNullOrEmptyString($sDescription)) { - $oTitleBlock = $aTablePanel->GetTitleBlock() + $oTitleBlock = $oTablePanel->GetTitleBlock() ->AddDataAttribute('tooltip-content', $sDescription) ->AddDataAttribute('tooltip-max-width', 'min(600px, 90vw)') // Allow big description to be wide enough while shrinking on small screens ->AddCSSClass('ibo-has-description'); } // Toolbar and actions - $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); + $oToolbar = $this->InitToolBar(); + $oTablePanel->AddToolbarBlock($oToolbar); + $oTablePanel->AddSubBlock($oDatatable); + $this->AddSubBlock($oTablePanel); } catch (\Exception $e) { $oAlert = AlertUIBlockFactory::MakeForDanger('error', Dict::S('UI:Datatables:Language:Error')); @@ -179,18 +174,70 @@ class BlockDirectLinksEditTable extends UIContentBlock } } + /** + * InitToolBar. + * + * @return \Combodo\iTop\Application\UI\Base\Component\Toolbar\Toolbar + */ + private function InitToolBar(): Toolbar + { + $oToolbar = ToolbarUIBlockFactory::MakeForButton(); + + // until a full link set refactoring (continue using edit_mode property) + switch ($this->oAttributeLinkedSet->GetEditMode()) { + case LINKSET_EDITMODE_NONE: // The linkset is read-only + break; + + case LINKSET_EDITMODE_ADDONLY: // The only possible action is to open (in a new window) the form to create a new object + $oActionButtonLink = ButtonUIBlockFactory::MakeNeutral('Link', 'link'); + $oActionButtonLink->AddDataAttribute('action', 'add'); + $oActionButtonLink->SetOnClickJsCode("$('#{$this->oUILinksDirectWidget->GetInputId()}').directlinks('selectToAdd');"); + $oToolbar->AddSubBlock($oActionButtonLink); + break; + + case LINKSET_EDITMODE_INPLACE: // The whole linkset can be edited 'in-place' + $oActionButtonCreate = ButtonUIBlockFactory::MakeNeutral('Create', 'create'); + $oActionButtonCreate->AddDataAttribute('action', 'create'); + $oActionButtonCreate->SetOnClickJsCode("$('#{$this->oUILinksDirectWidget->GetInputId()}').directlinks('createRow');"); + $oToolbar->AddSubBlock($oActionButtonCreate); + $oActionButtonDelete = ButtonUIBlockFactory::MakeNeutral('Delete', 'delete'); + $oActionButtonDelete->AddDataAttribute('action', 'delete'); + $oActionButtonDelete->SetOnClickJsCode("$('#{$this->oUILinksDirectWidget->GetInputId()}').directlinks('deleteSelection');"); + $oToolbar->AddSubBlock($oActionButtonDelete); + break; + + case LINKSET_EDITMODE_ADDREMOVE: // The whole linkset can be edited 'in-place' + $oActionButtonUnlink = ButtonUIBlockFactory::MakeNeutral('Unlink', 'unlink'); + $oActionButtonUnlink->AddDataAttribute('action', 'detach'); + $oActionButtonUnlink->SetOnClickJsCode("$('#{$this->oUILinksDirectWidget->GetInputId()}').directlinks('removeSelection');"); + $oToolbar->AddSubBlock($oActionButtonUnlink); + $oActionButtonLink = ButtonUIBlockFactory::MakeNeutral('Link', 'link'); + $oActionButtonLink->AddDataAttribute('action', 'add'); + $oActionButtonLink->SetOnClickJsCode("$('#{$this->oUILinksDirectWidget->GetInputId()}').directlinks('selectToAdd');"); + $oToolbar->AddSubBlock($oActionButtonLink); + break; + + case LINKSET_EDITMODE_ACTIONS: // Show the usual 'Actions' popup menu + default: + + } + + return $oToolbar; + } + + /** * Return table rows. * - * @param \DBObjectSet $oValue + * @param DBObjectSet $oValue * * @return array - * @throws \ArchivedObjectException - * @throws \CoreException - * @throws \CoreUnexpectedValue - * @throws \DictExceptionMissingString - * @throws \MySQLException - * @throws \Exception + * @throws ArchivedObjectException + * @throws CoreException + * @throws CoreUnexpectedValue + * @throws DictExceptionMissingString + * @throws MySQLException + * @throws Exception */ private function GetTableRows(\WebPage $oPage, \DBObjectSet $oValue): array { @@ -220,6 +267,41 @@ class BlockDirectLinksEditTable extends UIContentBlock return ($bSafe) ? \utils::GetSafeId($sFieldId) : $sFieldId; } + /** + * Convert edit_mode to relation type. + * + * @return string|null + */ + private function ConvertEditModeToRelationType(): ?string + { + switch ($this->oAttributeLinkedSet->GetEditMode()) { + case LINKSET_EDITMODE_INPLACE: + return LINKSET_RELATIONTYPE_PROPERTY; + case LINKSET_EDITMODE_ADDREMOVE: + return LINKSET_RELATIONTYPE_LINK; + default: + return null; + } + } + + /** + * Convert edit_mode to read only. + * + * @return bool + */ + private function ConvertEditModeToReadOnly(): bool + { + switch ($this->oAttributeLinkedSet->GetEditMode()) { + case LINKSET_EDITMODE_NONE: + case LINKSET_EDITMODE_ADDONLY: + case LINKSET_EDITMODE_ACTIONS: + return true; + + default: + return false; + } + } + /** * Return row actions. * @@ -229,9 +311,9 @@ class BlockDirectLinksEditTable extends UIContentBlock { $aRowActions = array(); - if (!$this->oAttributeLinkedSet->GetReadOnly()) { + if (!$this->ConvertEditModeToReadOnly()) { - switch ($this->oAttributeLinkedSet->GetRelationType()) { + switch ($this->ConvertEditModeToRelationType()) { case LINKSET_RELATIONTYPE_LINK: $aRowActions[] = array( diff --git a/sources/Application/UI/Links/Direct/BlockDirectLinksViewTable.php b/sources/Application/UI/Links/Direct/BlockDirectLinksViewTable.php index 52c5c7554..fc180eb78 100644 --- a/sources/Application/UI/Links/Direct/BlockDirectLinksViewTable.php +++ b/sources/Application/UI/Links/Direct/BlockDirectLinksViewTable.php @@ -37,9 +37,9 @@ class BlockDirectLinksViewTable extends AbstractBlockLinksViewTable 'default' => $this->GetDefault(), 'table_id' => $this->GetTableId(), 'row_actions' => $this->GetRowActions(), - 'currentId' => $this->GetTableId(), + 'currentId' => $this->GetTableId(), 'panel_title' => $this->oAttDef->GetLabel(), - 'panel_icon' => MetaModel::GetClassIcon($this->GetTargetClass(), false), + 'panel_icon' => MetaModel::GetClassIcon($this->GetTargetClass(), false), ); // Description @@ -50,9 +50,9 @@ class BlockDirectLinksViewTable extends AbstractBlockLinksViewTable // Add creation in modal if the linkset is not readonly if (!$this->oAttDef->GetReadOnly()) { $aExtraParams['creation_in_modal_is_allowed'] = true; - $aExtraParams['creation_in_modal_js_handler'] = 'LinkSetWorker.CreateLinkedObject("'.$this->GetTableId().'");'; + $aExtraParams['creation_in_modal_js_handler'] = "{$this->GetWidgetName()}.links_view_table('CreateLinkedObject');"; } - + return $aExtraParams; } @@ -70,7 +70,7 @@ class BlockDirectLinksViewTable extends AbstractBlockLinksViewTable 'label' => 'UI:Links:ActionRow:Detach', '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()}', '{$this->GetTableId()}');", + 'js_row_action' => "{$this->GetWidgetName()}.links_view_table('DetachLinkedObject', aRowData['{$this->sTargetClass}/_key_/raw'], oTrElement);", 'confirmation' => [ 'message' => 'UI:Links:ActionRow:Detach:Confirmation', 'message_row_data' => "{$this->sTargetClass}/hyperlink", @@ -84,7 +84,7 @@ class BlockDirectLinksViewTable extends AbstractBlockLinksViewTable 'label' => 'UI:Links:ActionRow:Delete', 'tooltip' => 'UI:Links:ActionRow:Delete+', 'icon_classes' => 'fas fa-trash', - 'js_row_action' => "LinkSetWorker.DeleteLinkedObject('{$this->oAttDef->GetLinkedClass()}', aRowData['{$this->oAttDef->GetLinkedClass()}/_key_/raw'], '{$this->GetTableId()}');", + '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", @@ -97,7 +97,7 @@ class BlockDirectLinksViewTable extends AbstractBlockLinksViewTable 'label' => 'UI:Links:ActionRow:Modify', 'tooltip' => 'UI:Links:ActionRow:Modify+', 'icon_classes' => 'fas fa-pen', - 'js_row_action' => "LinkSetWorker.ModifyLinkedObject('{$this->sTargetClass}', aRowData['{$this->oAttDef->GetLinkedClass()}/_key_/raw'], '{$this->GetTableId()}');", + 'js_row_action' => "{$this->GetWidgetName()}.links_view_table('ModifyLinkedObject', aRowData['{$this->oAttDef->GetLinkedClass()}/_key_/raw']);", ); } diff --git a/sources/Application/UI/Links/Indirect/BlockIndirectLinksEditTable.php b/sources/Application/UI/Links/Indirect/BlockIndirectLinksEditTable.php index d512abe7a..b53a81e2c 100644 --- a/sources/Application/UI/Links/Indirect/BlockIndirectLinksEditTable.php +++ b/sources/Application/UI/Links/Indirect/BlockIndirectLinksEditTable.php @@ -6,19 +6,23 @@ namespace Combodo\iTop\Application\UI\Links\Indirect; +use AttributeLinkedSetIndirect; 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 ConfigException; +use CoreException; +use DBObject; +use Exception; use Dict; use MetaModel; +use UILinksWidget; use utils; +use WebPage; /** * Class BlockIndirectLinksEditTable @@ -32,12 +36,15 @@ 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'; + public const DEFAULT_JS_FILES_REL_PATH = [ + 'js/links/links_widget.js', + ]; - /** @var \UILinksWidget */ - public \UILinksWidget $oUILinksWidget; + /** @var UILinksWidget $oUILinksWidget */ + public UILinksWidget $oUILinksWidget; - /** @var \AttributeLinkedSetIndirect */ - private \AttributeLinkedSetIndirect $oAttributeLinkedSetIndirect; + /** @var AttributeLinkedSetIndirect $oAttributeLinkedSetIndirect */ + private AttributeLinkedSetIndirect $oAttributeLinkedSetIndirect; /** @var string */ public string $sDuplicates; @@ -60,13 +67,13 @@ class BlockIndirectLinksEditTable extends UIContentBlock /** * Constructor. * - * @param \UILinksWidget $oUILinksWidget + * @param UILinksWidget $oUILinksWidget * - * @throws \ConfigException - * @throws \CoreException - * @throws \Exception + * @throws ConfigException + * @throws CoreException + * @throws Exception */ - public function __construct(\UILinksWidget $oUILinksWidget) + public function __construct(UILinksWidget $oUILinksWidget) { parent::__construct("linkedset_{$oUILinksWidget->GetLinkedSetId()}", ["ibo-block-indirect-links--edit"]); @@ -88,7 +95,7 @@ class BlockIndirectLinksEditTable extends UIContentBlock * Initialization. * * @return void - * @throws \Exception + * @throws Exception */ private function Init() { @@ -99,8 +106,8 @@ class BlockIndirectLinksEditTable extends UIContentBlock * Initialize UI. * * @return void - * @throws \CoreException - * @throws \Exception + * @throws CoreException + * @throws Exception */ private function InitUI() { @@ -109,37 +116,6 @@ class BlockIndirectLinksEditTable extends UIContentBlock $this->AddDeferredBlock($oDeferredBlock); } - /** - * CreateTableInformationAlert. - * - * @return iUIBlock - */ - private function CreateTableInformationAlert(): iUIBlock - { - // Selection alert - $oAlert = AlertUIBlockFactory::MakeNeutral('', '', "linkedset_{$this->oUILinksWidget->GetInputId()}_alert_information"); - $oAlert->AddCSSClasses([ - 'ibo-table--alert-information', - ]); - $oAlert->SetIsClosable(false); - $oAlert->SetIsCollapsible(false); - $oAlert->AddSubBlock(new Html('')); - - // Delete button - $oUIButton = ButtonUIBlockFactory::MakeForDestructiveAction("Détacher les {$this->oUILinksWidget->GetRemoteClass()}", 'table-selection'); - $oUIButton->SetOnClickJsCode("oWidget{$this->oUILinksWidget->GetInputId()}.RemoveSelected();"); - $oUIButton->AddCSSClass('ibo-table--alert-information--delete-button'); - $oAlert->AddSubBlock($oUIButton); - - // Add button - $oUIAddButton = ButtonUIBlockFactory::MakeForPrimaryAction("Attacher des {$this->oUILinksWidget->GetRemoteClass()}", 'table-selection'); - $oUIAddButton->AddCSSClass('ibo-table--alert-information--add-button'); - $oUIAddButton->SetOnClickJsCode("oWidget{$this->oUILinksWidget->GetInputId()}.AddObjects();"); - $oAlert->AddSubBlock($oUIAddButton); - - return $oAlert; - } - /** * @param \WebPage $oPage * @param $oValue @@ -149,6 +125,9 @@ class BlockIndirectLinksEditTable extends UIContentBlock * @param $aTableConfig * * @return void + * @throws \ArchivedObjectException + * @throws \CoreException + * @throws \CoreUnexpectedValue */ public function InitTable(\WebPage $oPage, $oValue, $aArgs, $sFormPrefix, $oCurrentObj, $aTableConfig) { @@ -192,7 +171,7 @@ class BlockIndirectLinksEditTable extends UIContentBlock ]); // Panel - $aTablePanel = PanelUIBlockFactory::MakeForClass($this->oUILinksWidget->GetRemoteClass(), $this->oAttributeLinkedSetIndirect->GetLabel()) + $oTablePanel = PanelUIBlockFactory::MakeForClass($this->oUILinksWidget->GetRemoteClass(), $this->oAttributeLinkedSetIndirect->GetLabel()) ->SetSubTitle(Dict::Format('UI:Pagination:HeaderNoSelection', count($aForm))) ->SetIcon(MetaModel::GetClassIcon($this->oUILinksWidget->GetRemoteClass(), false)) ->AddCSSClass('ibo-datatable-panel'); @@ -200,7 +179,7 @@ class BlockIndirectLinksEditTable extends UIContentBlock // - Panel description $sDescription = $this->oAttributeLinkedSetIndirect->GetDescription(); if (utils::IsNotNullOrEmptyString($sDescription)) { - $oTitleBlock = $aTablePanel->GetTitleBlock() + $oTitleBlock = $oTablePanel->GetTitleBlock() ->AddDataAttribute('tooltip-content', $sDescription) ->AddDataAttribute('tooltip-max-width', 'min(600px, 90vw)') // Allow big description to be wide enough while shrinking on small screens ->AddCSSClass('ibo-has-description'); @@ -214,10 +193,10 @@ class BlockIndirectLinksEditTable extends UIContentBlock $oActionButtonLink = ButtonUIBlockFactory::MakeNeutral('Link'); $oActionButtonLink->SetOnClickJsCode("oWidget{$this->oUILinksWidget->GetInputId()}.AddObjects();"); $oToolbar->AddSubBlock($oActionButtonLink); - $aTablePanel->AddToolbarBlock($oToolbar); - $aTablePanel->AddSubBlock($oDataTable); + $oTablePanel->AddToolbarBlock($oToolbar); + $oTablePanel->AddSubBlock($oDataTable); - $this->AddSubBlock($aTablePanel); + $this->AddSubBlock($oTablePanel); $this->AddSubBlock(InputUIBlockFactory::MakeForHidden("{$sFormPrefix}{$this->oUILinksWidget->GetInputId()}", '', "{$sFormPrefix}{$this->oUILinksWidget->GetInputId()}")); } @@ -238,7 +217,7 @@ class BlockIndirectLinksEditTable extends UIContentBlock * @throws \CoreUnexpectedValue * @throws \Exception */ - public function GetFormRow(\WebPage $oP, \DBObject $oLinkedObj, $linkObjOrId, $aArgs, $oCurrentObj, $iUniqueId, $bReadOnly = false) + public function GetFormRow(WebPage $oP, DBObject $oLinkedObj, $linkObjOrId, $aArgs, $oCurrentObj, $iUniqueId, $bReadOnly = false) { $sPrefix = "{$this->oUILinksWidget->GetAttCode()}{$this->oUILinksWidget->GetNameSuffix()}"; $aRow = array(); diff --git a/sources/Application/UI/Links/Indirect/BlockIndirectLinksViewTable.php b/sources/Application/UI/Links/Indirect/BlockIndirectLinksViewTable.php index f7e2848d4..1b0db3de0 100644 --- a/sources/Application/UI/Links/Indirect/BlockIndirectLinksViewTable.php +++ b/sources/Application/UI/Links/Indirect/BlockIndirectLinksViewTable.php @@ -58,11 +58,12 @@ class BlockIndirectLinksViewTable extends AbstractBlockLinksViewTable if ($this->oAttDef->HasDescription()) { $aExtraParams['panel_title_tooltip'] = $this->oAttDef->GetDescription(); } - + // 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_js_handler'] = 'LinkSetWorker.CreateLinkedObject("'.$this->GetTableId().'");'; + $aExtraParams['creation_in_modal_js_handler'] = "{$this->GetWidgetName()}.links_view_table('CreateLinkedObject');"; } return $aExtraParams; @@ -79,7 +80,7 @@ class BlockIndirectLinksViewTable extends AbstractBlockLinksViewTable 'label' => 'UI:Links:ActionRow:Detach', 'tooltip' => 'UI:Links:ActionRow:Detach+', 'icon_classes' => 'fas fa-minus', - 'js_row_action' => "LinkSetWorker.DeleteLinkedObject('{$this->oAttDef->GetLinkedClass()}', aRowData['Link/_key_/raw'], '{$this->GetTableId()}');", + '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", @@ -91,7 +92,7 @@ class BlockIndirectLinksViewTable extends AbstractBlockLinksViewTable 'label' => 'UI:Links:ActionRow:Modify', 'tooltip' => 'UI:Links:ActionRow:Modify+', 'icon_classes' => 'fas fa-pen', - 'js_row_action' => "LinkSetWorker.ModifyLinkedObject('{$this->oAttDef->GetLinkedClass()}', aRowData['Link/_key_/raw'], '{$this->GetTableId()}');", + 'js_row_action' => "{$this->GetWidgetName()}.links_view_table('ModifyLinkedObject', aRowData['Link/_key_/raw']);", ); } diff --git a/sources/Application/UI/Links/Set/LinksSetUIBlockFactory.php b/sources/Application/UI/Links/Set/LinksSetUIBlockFactory.php index dd1f284f0..fce739900 100644 --- a/sources/Application/UI/Links/Set/LinksSetUIBlockFactory.php +++ b/sources/Application/UI/Links/Set/LinksSetUIBlockFactory.php @@ -26,6 +26,7 @@ use Combodo\iTop\Service\Links\LinksBulkDataPostProcessor; use Combodo\iTop\Service\Links\LinkSetDataTransformer; use Combodo\iTop\Service\Links\LinkSetModel; use Combodo\iTop\Service\Links\LinkSetRepository; +use DBObject; use iDBObjectSetIterator; /** @@ -46,10 +47,11 @@ class LinksSetUIBlockFactory extends SetUIBlockFactory * @param AttributeLinkedSet $oAttDef Link set attribute definition * @param iDBObjectSetIterator $oDbObjectSet Link set value * @param string $sWizardHelperJsVarName Wizard helper name + * @param DBObject|null $oHostDbObject Host DB object * * @return \Combodo\iTop\Application\UI\Base\Component\Input\Set\Set */ - public static function MakeForLinkSet(string $sId, AttributeLinkedSet $oAttDef, iDBObjectSetIterator $oDbObjectSet, string $sWizardHelperJsVarName): Set + public static function MakeForLinkSet(string $sId, AttributeLinkedSet $oAttDef, iDBObjectSetIterator $oDbObjectSet, string $sWizardHelperJsVarName, DBObject $oHostDbObject = null): Set { $sTargetClass = LinkSetModel::GetTargetClass($oAttDef); $sTargetField = LinkSetModel::GetTargetField($oAttDef); @@ -57,6 +59,16 @@ class LinksSetUIBlockFactory extends SetUIBlockFactory // Set UI block for OQL $oSetUIBlock = SetUIBlockFactory::MakeForOQL($sId, $sTargetClass, $oAttDef->GetValuesDef()->GetFilterExpression(), $sWizardHelperJsVarName); + $oSetUIBlock->AddJsFileRelPath('js/links/links_set.js'); + + // Add button behaviour + if (in_array($oAttDef->GetEditMode(), [LINKSET_EDITMODE_ADDREMOVE, LINKSET_EDITMODE_ADDONLY, LINKSET_EDITMODE_INPLACE, LINKSET_EDITMODE_ACTIONS]) + && $oHostDbObject !== null) { + $sHostClass = get_class($oHostDbObject); + $oSetUIBlock->SetHasAddOptionButton(true); + $oSetUIBlock->SetAddOptionButtonJsOnClick("CombodoLinkSet.CreateLinkedObject('{$oAttDef->GetLinkedClass()}', '{$oAttDef->GetCode()}', '{$sHostClass}', '{$oHostDbObject->GetKey()}', '{$sTargetField}', '{$sTargetClass}', oWidget{$oSetUIBlock->GetId()} );"); + } + // Current value $aCurrentValues = LinkSetDataTransformer::Decode($oDbObjectSet, $sTargetClass, $sTargetField); diff --git a/sources/Application/WebPage/iTopWebPage.php b/sources/Application/WebPage/iTopWebPage.php index 4750349a2..7f5854a30 100644 --- a/sources/Application/WebPage/iTopWebPage.php +++ b/sources/Application/WebPage/iTopWebPage.php @@ -47,6 +47,9 @@ class iTopWebPage extends NiceWebPage implements iTabbedPage // - DisplayableGraph, impact analysis 'js/raphael-min.js', 'js/jquery.mousewheel.js', + /** - links widgets moved in links folder @since 3.1.0 * */ + 'js/links/links_direct_widget.js', + 'js/links/links_widget.js', ]; /** @inheritDoc */ protected const COMPATIBILITY_DEPRECATED_LINKED_SCRIPTS_REL_PATH = [ diff --git a/sources/Controller/Base/Layout/ObjectController.php b/sources/Controller/Base/Layout/ObjectController.php index b16e8d522..b3d5e34a8 100644 --- a/sources/Controller/Base/Layout/ObjectController.php +++ b/sources/Controller/Base/Layout/ObjectController.php @@ -18,6 +18,7 @@ use Combodo\iTop\Service\Base\ObjectRepository; use CoreCannotSaveObjectException; use DeleteException; use Dict; +use Exception; use IssueLog; use iTopOwnershipLock; use iTopWebPage; @@ -90,6 +91,8 @@ class ObjectController extends AbstractController /* Alerts the results */ sPosting.done(function(data) { + // fire event + oForm.trigger('itop.form.submitted', [data]); if(data.success !== undefined && data.success === true) { oForm.closest('[data-role="ibo-modal"]').dialog('close'); } @@ -151,6 +154,7 @@ JS; $sClass = utils::ReadPostedParam('class', '', 'class'); $sClassLabel = MetaModel::GetName($sClass); + $sFormPrefix = utils::ReadPostedParam('formPrefix', '', FILTER_SANITIZE_STRING); $sTransactionId = utils::ReadPostedParam('transaction_id', '', 'transaction_id'); $aErrors = array(); $aWarnings = array(); @@ -203,7 +207,7 @@ JS; $oObj->Set($sStateAttCode, $sTargetState); } } - $aErrors = $oObj->UpdateObjectFromPostedForm(); + $aErrors = $oObj->UpdateObjectFromPostedForm($sFormPrefix); } if (isset($oObj) && is_object($oObj)) { @@ -260,6 +264,7 @@ JS; // Nothing more to do if ($this->IsHandlingXmlHttpRequest()) { $aResult['success'] = true; + $aResult['data'] = ['object' => ObjectRepository::ConvertObjectToArray($oObj, $sClass)]; } else { ReloadAndDisplay($oPage, $oObj, 'create', $sMessage, 'ok'); } @@ -543,8 +548,6 @@ JS; 'js/forms-json-utils.js', 'js/wizardhelper.js', 'js/wizard.utils.js', - 'js/linkswidget.js', - 'js/linksdirectwidget.js', 'js/extkeywidget.js', 'js/jquery.blockUI.js', ]; @@ -589,4 +592,33 @@ JS; ]); } + /** + * OperationGet. + * + * @return JsonPage + */ + public function OperationGet(): JsonPage + { + $oPage = new JsonPage(); + $bSuccess = true; + $aObjectData = null; + + // Retrieve query params + $sObjectClass = utils::ReadParam('object_class', '', false, utils::ENUM_SANITIZATION_FILTER_STRING); + $sObjectKey = utils::ReadParam('object_key', '', false, utils::ENUM_SANITIZATION_FILTER_STRING); + + // Retrieve object + try { + $oObject = MetaModel::GetObject($sObjectClass, $sObjectKey); + $aObjectData = ObjectRepository::ConvertObjectToArray($oObject, $sObjectClass); + } + catch (Exception $e) { + $bSuccess = false; + } + + return $oPage->SetData([ + 'object' => $aObjectData, + 'success' => $bSuccess, + ]); + } } \ No newline at end of file diff --git a/sources/Controller/Links/LinksetController.php b/sources/Controller/Links/LinksetController.php index 62ff2150a..b9c4cde96 100644 --- a/sources/Controller/Links/LinksetController.php +++ b/sources/Controller/Links/LinksetController.php @@ -10,6 +10,7 @@ use AjaxPage; use cmdbAbstractObject; use Combodo\iTop\Application\UI\Base\Component\Form\FormUIBlockFactory; use Combodo\iTop\Controller\AbstractController; +use Combodo\iTop\Service\Base\ObjectRepository; use Exception; use JsonPage; use CoreException; @@ -169,8 +170,8 @@ class LinkSetController extends AbstractController $aPrefillParam = array('source_obj' => $oSourceObj); $oObj->PrefillForm('creation_from_editinplace', $aPrefillParam); // We display this form in a modal, once we submit (in ajax) we probably want to only close the modal - $sFormOnSubmitJsCode = - << true, + 'noRelations' => true, 'hide_transitions' => true, - 'fieldsFlags' => $aFieldFlags, - 'js_handlers' => [ - 'form_on_submit' => $sFormOnSubmitJsCode, + 'formPrefix' => $sAttCode, + 'fieldsFlags' => $aFieldFlags, + 'js_handlers' => [ + 'form_on_submit' => $sFormOnSubmitJsCode, 'cancel_button_on_click' => <<AddSubBlock(cmdbAbstractObject::DisplayBlockSelectClassToCreate( $sProposedRealClass, MetaModel::GetName($sProposedRealClass), $aPossibleClasses)); + $oClassForm->AddSubBlock(cmdbAbstractObject::DisplayBlockSelectClassToCreate($sProposedRealClass, MetaModel::GetName($sProposedRealClass), $aPossibleClasses)); $oPage->AddUiBlock($oClassForm); } return $oPage; } + + /** + * OperationGetRemoteObject. + * + * @return JsonPage + */ + public function OperationGetRemoteObject(): JsonPage + { + $oPage = new JsonPage(); + $bSuccess = true; + $aObjectData = null; + + // Retrieve query params + $sObjectClass = utils::ReadParam('linked_object_class', '', false, utils::ENUM_SANITIZATION_FILTER_STRING); + $sObjectKey = utils::ReadParam('linked_object_key', '', false, utils::ENUM_SANITIZATION_FILTER_STRING); + $sRemoteClass = utils::ReadParam('remote_class', null, false, utils::ENUM_SANITIZATION_FILTER_STRING); + $sRemoteExtKey = utils::ReadParam('external_key_att_code', null, false, utils::ENUM_SANITIZATION_FILTER_STRING); + + // Retrieve object + try { + $oObject = MetaModel::GetObject($sObjectClass, $sObjectKey); + $sLinkKey = $oObject->GetKey(); + if ($sRemoteExtKey !== null) { + $oObject = MetaModel::GetObject($sRemoteClass, $oObject->Get($sRemoteExtKey)); + } + $aObjectData = ObjectRepository::ConvertObjectToArray($oObject, $sObjectClass); + $aObjectData['link_keys'] = [$sObjectKey]; + } + catch (Exception $e) { + $bSuccess = false; + } + + return $oPage->SetData([ + 'object' => $aObjectData, + 'success' => $bSuccess, + ]); + } } \ No newline at end of file diff --git a/sources/Service/Base/ObjectRepository.php b/sources/Service/Base/ObjectRepository.php index ae98a7688..2a9095444 100644 --- a/sources/Service/Base/ObjectRepository.php +++ b/sources/Service/Base/ObjectRepository.php @@ -140,19 +140,8 @@ class ObjectRepository $oDbObjectSet->Rewind(); while ($oObject = $oDbObjectSet->Fetch()) { - // Prepare objet data - $aObjectData = []; - - // Object key - $aObjectData['key'] = $oObject->GetKey(); - - // Fill loaded columns... - foreach ($aFieldsToLoad as $sField) { - $aObjectData[$sField] = $oObject->Get($sField); - } - // Compute others data - $aResult[] = ObjectRepository::ComputeOthersData($oObject, $sObjectClass, $aObjectData, $aComplementAttributeSpec, $sObjectImageAttCode); + $aResult[] = self::ConvertObjectToArray($oObject, $sObjectClass, $aFieldsToLoad, $aComplementAttributeSpec, $sObjectImageAttCode); } return $aResult; @@ -199,6 +188,9 @@ class ObjectRepository { try { + // object class + $aData['class_name'] = get_class($oDbObject); + // Obsolescence flag $aData['obsolescence_flag'] = $oDbObject->IsObsolete(); @@ -260,4 +252,50 @@ class ObjectRepository } } + /** + * ConvertObjectToArray. + * + * @param DBObject $oObject + * @param string $sObjectClass + * @param array|null $aFieldsToLoad + * @param array|null $aComplementAttributeSpec + * @param string|null $sObjectImageAttCode + * + * @return array + * @throws \ArchivedObjectException + * @throws \CoreException + * @throws \DictExceptionMissingString + */ + public static function ConvertObjectToArray(DBObject $oObject, string $sObjectClass, array $aFieldsToLoad = null, array $aComplementAttributeSpec = null, string $sObjectImageAttCode = null): array + { + // Retrieve friendly name complementary specification + if ($aComplementAttributeSpec === null) { + $aComplementAttributeSpec = MetaModel::GetNameSpec($sObjectClass, FriendlyNameType::COMPLEMENTARY); + } + + // Retrieve image attribute code + if ($sObjectImageAttCode === null) { + $sObjectImageAttCode = MetaModel::GetImageAttributeCode($sObjectClass); + } + + // Fields to load + if ($aFieldsToLoad === null) { + $aFieldsToLoad = self::GetDefaultFieldsToLoad($aComplementAttributeSpec, $sObjectImageAttCode); + } + + // Prepare objet data + $aObjectData = []; + + // Object key + $aObjectData['key'] = $oObject->GetKey(); + + // Fill loaded columns... + foreach ($aFieldsToLoad as $sField) { + $aObjectData[$sField] = $oObject->Get($sField); + } + + // Compute others data + return ObjectRepository::ComputeOthersData($oObject, $sObjectClass, $aObjectData, $aComplementAttributeSpec, $sObjectImageAttCode); + } + } \ No newline at end of file diff --git a/templates/application/links/layout.ready.js.twig b/templates/application/links/layout.ready.js.twig new file mode 100644 index 000000000..aa60b0d3a --- /dev/null +++ b/templates/application/links/layout.ready.js.twig @@ -0,0 +1,8 @@ +{# @copyright Copyright (C) 2010-2021 Combodo SARL #} +{# @license http://opensource.org/licenses/AGPL-3.0 #} +{% apply spaceless %} +{{ oUIBlock.GetWidgetName() }} = $('#{{ oUIBlock.GetId() }}').links_view_table({ + link_class: '{{ oUIBlock.GetLinkedClass() }}', + external_key_to_me: '{{ oUIBlock.GetExternalKeyToMe() }}' +}); +{% endapply %} \ No newline at end of file diff --git a/templates/base/components/datatable/config/layout.html.twig b/templates/base/components/datatable/config/layout.html.twig index 8da02ad9f..a40357bbc 100644 --- a/templates/base/components/datatable/config/layout.html.twig +++ b/templates/base/components/datatable/config/layout.html.twig @@ -1,4 +1,4 @@ -