diff --git a/application/cmdbabstract.class.inc.php b/application/cmdbabstract.class.inc.php index bea76227d..45bac9ab4 100644 --- a/application/cmdbabstract.class.inc.php +++ b/application/cmdbabstract.class.inc.php @@ -2775,8 +2775,13 @@ JS $oPage->AddUiBlock($oContentBlock); $oForm = new Form("form_{$this->m_iFormId}"); - $oForm->SetAction($sFormAction) - ->SetOnSubmitJsCode("return OnSubmit('form_{$this->m_iFormId}');"); + $oForm->SetAction($sFormAction); + $sOnSubmitForm = "let bOnSubmitForm = OnSubmit('form_{$this->m_iFormId}');"; + if (isset($aExtraParams['js_handlers']['form_on_submit'])) { + $oForm->SetOnSubmitJsCode($sOnSubmitForm.$aExtraParams['js_handlers']['form_on_submit']); + } else { + $oForm->SetOnSubmitJsCode($sOnSubmitForm."return bOnSubmitForm;"); + } $oContentBlock->AddSubBlock($oForm); if ($this->GetDisplayMode() === static::ENUM_DISPLAY_MODE_EDIT) { @@ -2959,7 +2964,15 @@ EOF // Hook the cancel button via jQuery so that it can be unhooked easily as well if needed $sDefaultUrl = utils::GetAbsoluteUrlAppRoot().'pages/UI.php?operation=search_form&class='.$sClass.'&'.$oAppContext->GetForLink(); - $oPage->add_ready_script("$('#form_{$this->m_iFormId} button.cancel').on('click', function() { BackToDetails('$sClass', $iKey, '$sDefaultUrl', $sJSToken)} );"); + + $sCancelButtonOnClickScript = "let fOnClick{$this->m_iFormId}CancelButton = "; + if(isset($aExtraParams['js_handlers']['cancel_button_on_click'])){ + $sCancelButtonOnClickScript .= $aExtraParams['js_handlers']['cancel_button_on_click']; + } else { + $sCancelButtonOnClickScript .= "function() { BackToDetails('$sClass', $iKey, '$sDefaultUrl', $sJSToken)};"; + } + $sCancelButtonOnClickScript .= "$('#form_{$this->m_iFormId} button.cancel').on('click', fOnClick{$this->m_iFormId}CancelButton);"; + $oPage->add_ready_script($sCancelButtonOnClickScript); $iFieldsCount = count($aFieldsMap); $sJsonFieldsMap = json_encode($aFieldsMap); diff --git a/application/displayblock.class.inc.php b/application/displayblock.class.inc.php index 361cbb10b..46e65c36c 100644 --- a/application/displayblock.class.inc.php +++ b/application/displayblock.class.inc.php @@ -1760,6 +1760,7 @@ 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; /** @var array $aRegularActions Any action other than a transition */ $aRegularActions = []; @@ -1802,7 +1803,7 @@ class MenuBlock extends DisplayBlock // Any style actions // - Bulk actions on objects set if ($iSetCount > 1) { - if ($bIsCreationAllowed) { + if ($bIsCreationAllowed && !$bIsCreationInModalAllowed) { $this->AddNewObjectMenuAction($aRegularActions, $sClass, $sDefaultValuesAsUrlParams); } @@ -2216,11 +2217,31 @@ class MenuBlock extends DisplayBlock $oActionsToolbar->AddSubBlock($oActionButton); } - if ($this->m_sStyle == 'details') { - // - Search + // - Search + if ($this->m_sStyle === 'details') { $oActionButton = ButtonUIBlockFactory::MakeIconLink('fas fa-search', Dict::Format('UI:SearchFor_Class', MetaModel::GetName($sClass)), "{$sRootUrl}pages/UI.php?operation=search_form&do_search=0&class=$sClass{$sContext}", '', 'UI:SearchFor_Class'); $oActionButton->AddCSSClasses(['ibo-action-button', 'ibo-regular-action-button']); $oActionsToolbar->AddSubBlock($oActionButton); + } + + // - Creation in modal + if($bIsCreationInModalAllowed === true){ + $oAddLinkActionButton = ButtonUIBlockFactory::MakeIconAction( + 'fas fa-plus', + Dict::S('UI:Links:New:Button:Tooltip'), + 'UI:Links:New', + '', + false + ); + + // - If we are used in a Datatable, 'datatable_' will be prefixed to our $sId, so we do the same here + $sRealId = $sId; + if(in_array($this->m_sStyle, ['list', 'links', 'listInObject'])){ + $sRealId = 'datatable_' . $sId; + } + $oAddLinkActionButton->AddCSSClasses(['ibo-action-button', 'ibo-regular-action-button']) + ->SetOnClickJsCode("$('#$sRealId').trigger('open_creation_modal.object.itop');"); + $oActionsToolbar->AddSubBlock($oAddLinkActionButton); } // - Others diff --git a/css/backoffice/components/_alert.scss b/css/backoffice/components/_alert.scss index 719ec20b9..e55318de0 100644 --- a/css/backoffice/components/_alert.scss +++ b/css/backoffice/components/_alert.scss @@ -1,5 +1,5 @@ /* - * @copyright Copyright (C) 2010-2021 Combodo SARL + * @copyright Copyright (C) 2010-2023 Combodo SARL * @license http://opensource.org/licenses/AGPL-3.0 */ @@ -9,9 +9,6 @@ $ibo-alert--padding-x: 20px !default; $ibo-alert--min-height: 30px !default; $ibo-alert--border-radius: $ibo-border-radius-300 !default; -$ibo-alert--title--highlight--width: 4px !default; -$ibo-alert--title--highlight--height: 100% !default; - $ibo-alert--body--margin-top: $ibo-spacing-200 !default; $ibo-alert-minimized--padding-y: 5px !default; @@ -68,14 +65,7 @@ $ibo-alert-colors: ( @extend %ibo-font-size-150; &::before { - display: block; - position: absolute; - top: 0; - left: 0; - content: ''; - - width: $ibo-alert--title--highlight--width; - height: $ibo-alert--title--highlight--height; + @include ibo-vertical-highlight; } .ibo-alert--title { diff --git a/css/backoffice/components/_modal.scss b/css/backoffice/components/_modal.scss index 0cb0f44de..2e4be2a75 100644 --- a/css/backoffice/components/_modal.scss +++ b/css/backoffice/components/_modal.scss @@ -1,11 +1,18 @@ /* - * @copyright Copyright (C) 2010-2021 Combodo SARL + * @copyright Copyright (C) 2010-2023 Combodo SARL * @license http://opensource.org/licenses/AGPL-3.0 */ /* SCSS variables */ $ibo-modal-option--do-not-show-again--margin-top: $ibo-spacing-500 !default; +$ibo-modal--is-informative--min-width: $ibo-size-700 !default; +$ibo-modal--is-informative--min-height: $ibo-size-300 !default; +$ibo-modal--is-informative--is-error--highlight--background-color: $ibo-color-red-600 !default; +$ibo-modal--is-informative--is-warning--highlight--background-color: $ibo-color-orange-600 !default; +$ibo-modal--is-informative--is-information--highlight--background-color: $ibo-color-blue-600 !default; +$ibo-modal--is-informative--is-success--highlight--background-color: $ibo-color-green-600 !default; + // Modal Option - Do not show again .ibo-modal-option--do-not-show-again{ margin-top: $ibo-modal-option--do-not-show-again--margin-top; @@ -15,4 +22,27 @@ $ibo-modal-option--do-not-show-again--margin-top: $ibo-spacing-500 !default; display: inline-block; width: auto; } +} + +.ibo-modal.ibo-is-informative{ + display: flex; + align-items: center; + min-width: $ibo-modal--is-informative--min-width; + min-height: $ibo-modal--is-informative--min-height !important; // !important in order to overload jQueryUI CSS rule that's put directly on the element + + &::before { + @include ibo-vertical-highlight; + } + &.ibo-is-error::before { + background-color: $ibo-modal--is-informative--is-error--highlight--background-color; + } + &.ibo-is-warning::before { + background-color: $ibo-modal--is-informative--is-warning--highlight--background-color; + } + &.ibo-is-information::before { + background-color: $ibo-modal--is-informative--is-information--highlight--background-color; + } + &.ibo-is-success::before { + background-color: $ibo-modal--is-informative--is-success--highlight--background-color; + } } \ No newline at end of file diff --git a/css/backoffice/layout/tab-container/_tab-container.scss b/css/backoffice/layout/tab-container/_tab-container.scss index a4fd072c6..8873c0878 100644 --- a/css/backoffice/layout/tab-container/_tab-container.scss +++ b/css/backoffice/layout/tab-container/_tab-container.scss @@ -160,7 +160,7 @@ $ibo-tab-container--tab-container--last--min-height: 60vh !default; .ibo-tab-container--tab-container { min-height: $ibo-tab-container--tab-container--min-height; } - .ibo-tab-container--tab-container:last-child { + .ibo-tab-container--tab-container:last-child:not(:only-child) { min-height: $ibo-tab-container--tab-container--last--min-height; } } diff --git a/css/backoffice/utils/mixins/_all.scss b/css/backoffice/utils/mixins/_all.scss index df4932f68..bb4c6e405 100644 --- a/css/backoffice/utils/mixins/_all.scss +++ b/css/backoffice/utils/mixins/_all.scss @@ -1,5 +1,6 @@ /* - * @copyright Copyright (C) 2010-2021 Combodo SARL + * @copyright Copyright (C) 2010-2023 Combodo SARL * @license http://opensource.org/licenses/AGPL-3.0 */ +@import "highlight"; \ No newline at end of file diff --git a/css/backoffice/utils/mixins/_highlight.scss b/css/backoffice/utils/mixins/_highlight.scss new file mode 100644 index 000000000..1201d82c6 --- /dev/null +++ b/css/backoffice/utils/mixins/_highlight.scss @@ -0,0 +1,17 @@ +/* + * @copyright Copyright (C) 2010-2023 Combodo SARL + * @license http://opensource.org/licenses/AGPL-3.0 + */ + +$ibo-vertical-highlight--width: $ibo-size-100; +$ibo-vertical-highlight--height: 100%; + +@mixin ibo-vertical-highlight { + display: block; + position: absolute; + top: 0; + left: 0; + content: ""; + width: $ibo-vertical-highlight--width; + height: $ibo-vertical-highlight--height; +} \ No newline at end of file diff --git a/dictionaries/ui/application/links/en.dictionary.itop.links.php b/dictionaries/ui/application/links/en.dictionary.itop.links.php index 115cbeb58..cf86b1fd2 100644 --- a/dictionaries/ui/application/links/en.dictionary.itop.links.php +++ b/dictionaries/ui/application/links/en.dictionary.itop.links.php @@ -25,4 +25,7 @@ Dict::Add('EN US', 'English', 'English', array( '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:Modal:Title' => 'Modify a link', + 'UI:Links:New:Modal:Title' => 'Creation of a link', + 'UI:Links:New:Button:Tooltip' => 'Add a new link', )); \ No newline at end of file diff --git a/dictionaries/ui/components/modal/en.dictionary.itop.modal.php b/dictionaries/ui/components/modal/en.dictionary.itop.modal.php index 52f8a3cb2..88e9e23e5 100644 --- a/dictionaries/ui/components/modal/en.dictionary.itop.modal.php +++ b/dictionaries/ui/components/modal/en.dictionary.itop.modal.php @@ -19,4 +19,8 @@ Dict::Add('EN US', 'English', 'English', array( 'UI:Modal:Confirmation:DefaultTitle' => 'Confirmation', + 'UI:Modal:Informative:Title' => 'Informative Modal', + 'UI:Modal:InformativeError:Title' => 'Error', + 'UI:Modal:InformativeWarning:Title' => 'Warning', + 'UI:Modal:InformativeInformation:Title' => 'Information', )); \ No newline at end of file diff --git a/js/links/link_set_worker.js b/js/links/link_set_worker.js index 3e1cb144f..38d1c92e3 100644 --- a/js/links/link_set_worker.js +++ b/js/links/link_set_worker.js @@ -2,8 +2,10 @@ let LinkSetWorker = new function(){ // defines const ROUTER_BASE_URL = '../pages/ajax.render.php'; - const ROUTE_LINK_SET_DELETE_OBJECT = 'linkset.DeleteLinkedObject'; - const ROUTE_LINK_SET_DETACH_OBJECT = 'linkset.DetachLinkedObject'; + 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. @@ -51,8 +53,75 @@ let LinkSetWorker = new function(){ }); }; + /** + * 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: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: function () { + oTableSettingsDialog.DataTableSettings('DoRefresh'); + $(this).find("form").remove(); + $(this).dialog('destroy'); + } + }, + } + CombodoModal.OpenModal(oOptions); + }; + return { DeleteLinkedObject: CallAjaxDeleteLinkedObject, - DetachLinkedObject: CallAjaxDetachLinkedObject + DetachLinkedObject: CallAjaxDetachLinkedObject, + ModifyLinkedObject: CallAjaxModifyLinkedObject, + CreateLinkedObject: CallAjaxCreateLinkedObject } }; \ No newline at end of file diff --git a/js/pages/backoffice/toolbox.js b/js/pages/backoffice/toolbox.js index 5d0835632..40b2277ed 100644 --- a/js/pages/backoffice/toolbox.js +++ b/js/pages/backoffice/toolbox.js @@ -201,6 +201,7 @@ CombodoModal._InstantiateModal = function(oModalElem, oOptions) { width: 'auto', height: 'auto', modal: oOptions.extra_options.modal ?? true, + classes: oOptions.classes ?? {}, close: oOptions.extra_options.callback_on_modal_close, autoOpen: oOptions.auto_open, title: oOptions.title, @@ -326,14 +327,17 @@ CombodoModal._InstantiateModal = function(oModalElem, oOptions) { */ CombodoModal._ConvertButtonDefinition = function(aButtonsDefinitions){ const aConverted = []; + if(aButtonsDefinitions === null) { + return aConverted + } aButtonsDefinitions.forEach(element => { - const aButton = { - text: element.text, - class: element.class, - click: element.callback_on_click + const aButton = { + text: element.text, + class: element.class, + click: element.callback_on_click + } + aConverted.push(aButton); } - aConverted.push(aButton); - } ); return aConverted; } @@ -428,6 +432,42 @@ CombodoModal.OpenConfirmationModal = function(oOptions, aData) { CombodoModal.OpenModal(oOptions); } +/** + * Open a standard informative modal. + * + * @param sMessage string Informative message to be displayed in the modal + * @param sSeverity string Severity of the information. Default values are success, information, warning, error. + * @param oOptions array @see CombodoModal.OpenModal + */ +CombodoModal.OpenInformativeModal = function(sMessage, sSeverity, oOptions) { + let sFirstLetterUppercaseSeverity = sSeverity.charAt(0).toUpperCase() + sSeverity.slice(1); + // Merge external options with confirmation modal default options + oOptions = $.extend({ + title: Dict.S('UI:Modal:Informative' + sFirstLetterUppercaseSeverity + ':Title'), + classes : { + 'ui-dialog-content': 'ibo-is-informative ibo-is-'+sSeverity, + }, + content: sMessage, + extra_options: { + callback_on_modal_close: function () { + $(this).dialog( "destroy" ); + } + }, + buttons: [ + { + text: Dict.S('UI:Button:Ok'), + class: 'ibo-is-regular ibo-is-neutral', + callback_on_click: function () { + $(this).dialog('close'); + } + }, + ], + }, oOptions); + + // Open modal + CombodoModal.OpenModal(oOptions); +} + // Processing on each pages of the backoffice $(document).ready(function(){ // Initialize global keyboard shortcuts diff --git a/js/utils.js b/js/utils.js index c42ad1a14..77e6aa259 100644 --- a/js/utils.js +++ b/js/utils.js @@ -1167,6 +1167,7 @@ let CombodoModal = { { id: null, // ID of the created modal attributes: {}, // HTML attributes + classes: {}, // Classes for the created modal elements base_modal: { usage: 'clone', // Either 'clone' or 'replace' selector: this._GetDefaultBaseModalSelector() // Either a selector of the modal element used to base this one on or the modal element itself @@ -1304,5 +1305,18 @@ let CombodoModal = { OpenConfirmationModal: function(oOptions) { // Meant for overlaoding CombodoJSConsole.Debug('CombodoModal.OpenConfirmationModal not implemented'); + }, + + + /** + * Open a standard informative modal. + * + * @param sMessage string Informative message to be displayed in the modal + * @param sSeverity string Severity of the information. Default values are success, information, warning, error. + * @param oOptions array @see CombodoModal.OpenModal + */ + OpenInformativeModal: function(sMessage,sSeverity, oOptions) { + // Meant for overlaoding + CombodoJSConsole.Debug('CombodoModal.OpenInformativeModal not implemented'); } }; \ No newline at end of file diff --git a/pages/UI.php b/pages/UI.php index e7f5fc663..ae4356c81 100644 --- a/pages/UI.php +++ b/pages/UI.php @@ -786,169 +786,8 @@ try /////////////////////////////////////////////////////////////////////////////////////////// case 'apply_modify': // Applying the modifications to an existing object - $oP->DisableBreadCrumb(); - $sClass = utils::ReadPostedParam('class', '', 'class'); - $sClassLabel = MetaModel::GetName($sClass); - $id = utils::ReadPostedParam('id', ''); - $sTransactionId = utils::ReadPostedParam('transaction_id', '', 'transaction_id'); - if ( empty($sClass) || empty($id)) // TO DO: check that the class name is valid ! - { - IssueLog::Trace('Object not updated (empty class or id)', $sClass, array( - '$operation' => $operation, - '$id' => $id, - '$sTransactionId' => $sTransactionId, - '$sUser' => UserRights::GetUser(), - 'HTTP_REFERER' => @$_SERVER['HTTP_REFERER'], - 'REQUEST_URI' => @$_SERVER['REQUEST_URI'], - )); - - throw new ApplicationException(Dict::Format('UI:Error:2ParametersMissing', 'class', 'id')); - } - $bDisplayDetails = true; - $oObj = MetaModel::GetObject($sClass, $id, false); - if ($oObj == null) - { - $bDisplayDetails = false; - $oP->set_title(Dict::S('UI:ErrorPageTitle')); - $oP->P(Dict::S('UI:ObjectDoesNotExist')); - - IssueLog::Trace('Object not updated (id not found)', $sClass, array( - '$operation' => $operation, - '$id' => $id, - '$sTransactionId' => $sTransactionId, - '$sUser' => UserRights::GetUser(), - 'HTTP_REFERER' => @$_SERVER['HTTP_REFERER'], - 'REQUEST_URI' => @$_SERVER['REQUEST_URI'], - )); - } - elseif (!utils::IsTransactionValid($sTransactionId, false)) - { - //TODO: since $bDisplayDetails= true, there will be an redirection, thus, the content generated here is ignored, only the $sMessage and $sSeverity are used afeter the redirection - $sUser = UserRights::GetUser(); - IssueLog::Error("UI.php '$operation' : invalid transaction_id ! data: user='$sUser', class='$sClass'"); - $oP->set_title(Dict::Format('UI:ModificationPageTitle_Object_Class', $oObj->GetRawName(), $sClassLabel)); // Set title will take care of the encoding - $oP->p("".Dict::S('UI:Error:ObjectAlreadyUpdated')."\n"); - $sMessage = Dict::Format('UI:Error:ObjectAlreadyUpdated', MetaModel::GetName(get_class($oObj)), $oObj->GetName()); - $sSeverity = 'error'; - - IssueLog::Trace('Object not updated (invalid transaction_id)', $sClass, array( - '$operation' => $operation, - '$id' => $id, - '$sTransactionId' => $sTransactionId, - '$sUser' => UserRights::GetUser(), - 'HTTP_REFERER' => @$_SERVER['HTTP_REFERER'], - 'REQUEST_URI' => @$_SERVER['REQUEST_URI'], - )); - } - else - { - $aErrors = $oObj->UpdateObjectFromPostedForm(); - $sMessage = ''; - $sSeverity = 'ok'; - - if (!$oObj->IsModified() && empty($aErrors)) - { - $oP->set_title(Dict::Format('UI:ModificationPageTitle_Object_Class', $oObj->GetRawName(), $sClassLabel)); // Set title will take care of the encoding - $sMessage = Dict::Format('UI:Class_Object_NotUpdated', MetaModel::GetName(get_class($oObj)), $oObj->GetName()); - $sSeverity = 'info'; - - IssueLog::Trace('Object not updated (see either $aErrors or IsModified)', $sClass, array( - '$operation' => $operation, - '$id' => $id, - '$sTransactionId' => $sTransactionId, - '$aErrors' => $aErrors, - 'IsModified' => $oObj->IsModified(), - '$sUser' => UserRights::GetUser(), - 'HTTP_REFERER' => @$_SERVER['HTTP_REFERER'], - 'REQUEST_URI' => @$_SERVER['REQUEST_URI'], - )); - } - else - { - IssueLog::Trace('Object updated', $sClass, array( - '$operation' => $operation, - '$id' => $id, - '$sTransactionId' => $sTransactionId, - '$aErrors' => $aErrors, - 'IsModified' => $oObj->IsModified(), - '$sUser' => UserRights::GetUser(), - 'HTTP_REFERER' => @$_SERVER['HTTP_REFERER'], - 'REQUEST_URI' => @$_SERVER['REQUEST_URI'], - )); - - try - { - if (!empty($aErrors)) - { - throw new CoreCannotSaveObjectException(array('id' => $oObj->GetKey(), 'class' => $sClass, 'issues' => $aErrors)); - } - // Transactions are now handled in DBUpdate - $oObj->DBUpdate(); - $sMessage = Dict::Format('UI:Class_Object_Updated', MetaModel::GetName(get_class($oObj)), $oObj->GetName()); - $sSeverity = 'ok'; - } - catch (CoreCannotSaveObjectException $e) - { - // Found issues, explain and give the user a second chance - // - $bDisplayDetails = false; - $aIssues = $e->getIssues(); - $oP->AddHeaderMessage($e->getHtmlMessage(), 'message_error'); - $oObj->DisplayModifyForm($oP, - array('wizard_container' => true)); // wizard_container: display the wizard border and the title - } - catch (DeleteException $e) - { - // Say two things: - // - 1) Don't be afraid nothing was modified - $sMessage = Dict::Format('UI:Class_Object_NotUpdated', MetaModel::GetName(get_class($oObj)), $oObj->GetName()); - $sSeverity = 'info'; - cmdbAbstractObject::SetSessionMessage(get_class($oObj), $oObj->GetKey(), 'UI:Class_Object_NotUpdated', $sMessage, - $sSeverity, 0, true /* must not exist */); - // - 2) Ok, there was some trouble indeed - $sMessage = $e->getMessage(); - $sSeverity = 'error'; - } - utils::RemoveTransaction($sTransactionId); - } - } - if ($bDisplayDetails) - { - $oObj = MetaModel::GetObject(get_class($oObj), $oObj->GetKey()); //Workaround: reload the object so that the linkedset are displayed properly - $sNextAction = utils::ReadPostedParam('next_action', ''); - if (!empty($sNextAction)) - { - try - { - ApplyNextAction($oP, $oObj, $sNextAction); - } - catch (ApplicationException $e) - { - $sMessage = $e->getMessage(); - $sSeverity = 'info'; - ReloadAndDisplay($oP, $oObj, 'update', $sMessage, $sSeverity); - } - } - else - { - // Nothing more to do - $sMessage = isset($sMessage) ? $sMessage : ''; - $sSeverity = isset($sSeverity) ? $sSeverity : null; - ReloadAndDisplay($oP, $oObj, 'update', $sMessage, $sSeverity); - } - - $bLockEnabled = MetaModel::GetConfig()->Get('concurrent_lock_enabled'); - if ($bLockEnabled) - { - // Release the concurrent lock, if any - $sOwnershipToken = utils::ReadPostedParam('ownership_token', null, 'raw_data'); - if ($sOwnershipToken !== null) - { - // We're done, let's release the lock - iTopOwnershipLock::ReleaseLock(get_class($oObj), $oObj->GetKey(), $sOwnershipToken); - } - } - } + $oController = new ObjectController(); + $oP = $oController->OperationApplyModify(); break; /////////////////////////////////////////////////////////////////////////////////////////// @@ -1047,137 +886,10 @@ try /////////////////////////////////////////////////////////////////////////////////////////// case 'apply_new': // Creation of a new object - $oP->DisableBreadCrumb(); - $sClass = utils::ReadPostedParam('class', '', 'class'); - $sClassLabel = MetaModel::GetName($sClass); - $sTransactionId = utils::ReadPostedParam('transaction_id', '', 'transaction_id'); - $aErrors = array(); - $aWarnings = array(); - if ( empty($sClass) ) // TO DO: check that the class name is valid ! - { - IssueLog::Trace('Object not created (empty class)', $sClass, array( - '$operation' => $operation, - '$sTransactionId' => $sTransactionId, - '$sUser' => UserRights::GetUser(), - 'HTTP_REFERER' => @$_SERVER['HTTP_REFERER'], - 'REQUEST_URI' => @$_SERVER['REQUEST_URI'], - )); - throw new ApplicationException(Dict::Format('UI:Error:1ParametersMissing', 'class')); - } - if (!utils::IsTransactionValid($sTransactionId, false)) - { - $sUser = UserRights::GetUser(); - IssueLog::Error("UI.php '$operation' : invalid transaction_id ! data: user='$sUser', class='$sClass'"); - $oP->p("".Dict::S('UI:Error:ObjectAlreadyCreated')."\n"); - - IssueLog::Trace('Object not created (invalid transaction_id)', $sClass, array( - '$operation' => $operation, - '$sTransactionId' => $sTransactionId, - '$sUser' => UserRights::GetUser(), - 'HTTP_REFERER' => @$_SERVER['HTTP_REFERER'], - 'REQUEST_URI' => @$_SERVER['REQUEST_URI'], - )); - } - else - { - /** @var \cmdbAbstractObject $oObj */ - $oObj = MetaModel::NewObject($sClass); - if (MetaModel::HasLifecycle($sClass)) - { - $sStateAttCode = MetaModel::GetStateAttributeCode($sClass); - $sTargetState = utils::ReadPostedParam('obj_state', ''); - if ($sTargetState != '') - { - $sOrigState = utils::ReadPostedParam('obj_state_orig', ''); - if ($sTargetState != $sOrigState) - { - $aWarnings[] = Dict::S('UI:StateChanged'); - } - $oObj->Set($sStateAttCode, $sTargetState); - } - } - $aErrors = $oObj->UpdateObjectFromPostedForm(); - } - if (isset($oObj) && is_object($oObj)) - { - $sClass = get_class($oObj); - $sClassLabel = MetaModel::GetName($sClass); - - try - { - if (!empty($aErrors) || !empty($aWarnings)) - { - IssueLog::Trace('Object not created (see $aErrors)', $sClass, array( - '$operation' => $operation, - '$sTransactionId' => $sTransactionId, - '$aErrors' => $aErrors, - '$sUser' => UserRights::GetUser(), - 'HTTP_REFERER' => @$_SERVER['HTTP_REFERER'], - 'REQUEST_URI' => @$_SERVER['REQUEST_URI'], - )); - - throw new CoreCannotSaveObjectException(array('id' => $oObj->GetKey(), 'class' => $sClass, 'issues' => $aErrors)); - } - - $oObj->DBInsertNoReload();// No need to reload - - IssueLog::Trace('Object created', $sClass, array( - '$operation' => $operation, - '$id' => $oObj->GetKey(), - '$sTransactionId' => $sTransactionId, - '$aErrors' => $aErrors, - '$sUser' => UserRights::GetUser(), - 'HTTP_REFERER' => @$_SERVER['HTTP_REFERER'], - 'REQUEST_URI' => @$_SERVER['REQUEST_URI'], - )); - - utils::RemoveTransaction($sTransactionId); - $oP->set_title(Dict::S('UI:PageTitle:ObjectCreated')); - QuickCreateHelper::AddClassToHistory($sClass); - - // Compute the name, by reloading the object, even if it disappeared from the silo - $oObj = MetaModel::GetObject($sClass, $oObj->GetKey(), true /* Must be found */, true /* Allow All Data*/); - $sName = $oObj->GetName(); - $sMessage = Dict::Format('UI:Title:Object_Of_Class_Created', $sName, $sClassLabel); - - $sNextAction = utils::ReadPostedParam('next_action', ''); - if (!empty($sNextAction)) { - $oP->add("