mirror of
https://github.com/Combodo/iTop.git
synced 2026-02-13 07:24:13 +01:00
N°3136 - Add creation and modification of n-n objects in object details (#378)
* Rebase onto develop * Use exit condition instead of englobing condition * Add informative modals that can be called from modal toolbox * Refactor "apply_modify" and "apply_new" into own controller, handle ajax requests with a json response and handle these responses in linkset creation/edition * Fix merge issues * Remove inverted condition * Move linkset create button to a better place, still needs to fix duplicate "New" button caused by a refactor * Handle "Cancel" button in modals * Do not display relations when editing an object in a modal * More elegant way to add "New" button to relations lists * Factorize vertical highlights in alerts and modal in a single mixin * Replace button name with dict entry code * Change route name to snake case * More elegant way to add "Create in modal" button to relations lists * Replace triple if with in_array * Move listener to body * Rename variable to match boolean rules * Rename event * Rename extra param * Add phpdoc * Revert changes * Check indirect linkset rights before allowing creation in modal
This commit is contained in:
@@ -2775,8 +2775,13 @@ JS
|
|||||||
$oPage->AddUiBlock($oContentBlock);
|
$oPage->AddUiBlock($oContentBlock);
|
||||||
|
|
||||||
$oForm = new Form("form_{$this->m_iFormId}");
|
$oForm = new Form("form_{$this->m_iFormId}");
|
||||||
$oForm->SetAction($sFormAction)
|
$oForm->SetAction($sFormAction);
|
||||||
->SetOnSubmitJsCode("return OnSubmit('form_{$this->m_iFormId}');");
|
$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);
|
$oContentBlock->AddSubBlock($oForm);
|
||||||
|
|
||||||
if ($this->GetDisplayMode() === static::ENUM_DISPLAY_MODE_EDIT) {
|
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
|
// 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();
|
$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);
|
$iFieldsCount = count($aFieldsMap);
|
||||||
$sJsonFieldsMap = json_encode($aFieldsMap);
|
$sJsonFieldsMap = json_encode($aFieldsMap);
|
||||||
|
|||||||
@@ -1760,6 +1760,7 @@ class MenuBlock extends DisplayBlock
|
|||||||
$iSetCount = $oSet->Count();
|
$iSetCount = $oSet->Count();
|
||||||
/** @var string $sRefreshAction JS snippet to run when clicking on the refresh button of the menu */
|
/** @var string $sRefreshAction JS snippet to run when clicking on the refresh button of the menu */
|
||||||
$sRefreshAction = $aExtraParams['refresh_action'] ?? '';
|
$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 */
|
/** @var array $aRegularActions Any action other than a transition */
|
||||||
$aRegularActions = [];
|
$aRegularActions = [];
|
||||||
@@ -1802,7 +1803,7 @@ class MenuBlock extends DisplayBlock
|
|||||||
// Any style actions
|
// Any style actions
|
||||||
// - Bulk actions on objects set
|
// - Bulk actions on objects set
|
||||||
if ($iSetCount > 1) {
|
if ($iSetCount > 1) {
|
||||||
if ($bIsCreationAllowed) {
|
if ($bIsCreationAllowed && !$bIsCreationInModalAllowed) {
|
||||||
$this->AddNewObjectMenuAction($aRegularActions, $sClass, $sDefaultValuesAsUrlParams);
|
$this->AddNewObjectMenuAction($aRegularActions, $sClass, $sDefaultValuesAsUrlParams);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2216,11 +2217,31 @@ class MenuBlock extends DisplayBlock
|
|||||||
$oActionsToolbar->AddSubBlock($oActionButton);
|
$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 = 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']);
|
$oActionButton->AddCSSClasses(['ibo-action-button', 'ibo-regular-action-button']);
|
||||||
$oActionsToolbar->AddSubBlock($oActionButton);
|
$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
|
// - Others
|
||||||
|
|||||||
@@ -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
|
* @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--min-height: 30px !default;
|
||||||
$ibo-alert--border-radius: $ibo-border-radius-300 !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--body--margin-top: $ibo-spacing-200 !default;
|
||||||
|
|
||||||
$ibo-alert-minimized--padding-y: 5px !default;
|
$ibo-alert-minimized--padding-y: 5px !default;
|
||||||
@@ -68,14 +65,7 @@ $ibo-alert-colors: (
|
|||||||
@extend %ibo-font-size-150;
|
@extend %ibo-font-size-150;
|
||||||
|
|
||||||
&::before {
|
&::before {
|
||||||
display: block;
|
@include ibo-vertical-highlight;
|
||||||
position: absolute;
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
content: '';
|
|
||||||
|
|
||||||
width: $ibo-alert--title--highlight--width;
|
|
||||||
height: $ibo-alert--title--highlight--height;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.ibo-alert--title {
|
.ibo-alert--title {
|
||||||
|
|||||||
@@ -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
|
* @license http://opensource.org/licenses/AGPL-3.0
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/* SCSS variables */
|
/* SCSS variables */
|
||||||
$ibo-modal-option--do-not-show-again--margin-top: $ibo-spacing-500 !default;
|
$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
|
// Modal Option - Do not show again
|
||||||
.ibo-modal-option--do-not-show-again{
|
.ibo-modal-option--do-not-show-again{
|
||||||
margin-top: $ibo-modal-option--do-not-show-again--margin-top;
|
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;
|
display: inline-block;
|
||||||
width: auto;
|
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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -160,7 +160,7 @@ $ibo-tab-container--tab-container--last--min-height: 60vh !default;
|
|||||||
.ibo-tab-container--tab-container {
|
.ibo-tab-container--tab-container {
|
||||||
min-height: $ibo-tab-container--tab-container--min-height;
|
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;
|
min-height: $ibo-tab-container--tab-container--last--min-height;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
* @license http://opensource.org/licenses/AGPL-3.0
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
@import "highlight";
|
||||||
17
css/backoffice/utils/mixins/_highlight.scss
Normal file
17
css/backoffice/utils/mixins/_highlight.scss
Normal file
@@ -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;
|
||||||
|
}
|
||||||
@@ -25,4 +25,7 @@ Dict::Add('EN US', 'English', 'English', array(
|
|||||||
'UI:Links:ActionRow:Delete' => 'Delete',
|
'UI:Links:ActionRow:Delete' => 'Delete',
|
||||||
'UI:Links:ActionRow:Delete+' => 'Delete this object',
|
'UI:Links:ActionRow:Delete+' => 'Delete this object',
|
||||||
'UI:Links:ActionRow:Delete:Confirmation' => 'Do you really want to delete <b>{item}</b> from current object ?',
|
'UI:Links:ActionRow:Delete:Confirmation' => 'Do you really want to delete <b>{item}</b> 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',
|
||||||
));
|
));
|
||||||
@@ -19,4 +19,8 @@
|
|||||||
|
|
||||||
Dict::Add('EN US', 'English', 'English', array(
|
Dict::Add('EN US', 'English', 'English', array(
|
||||||
'UI:Modal:Confirmation:DefaultTitle' => 'Confirmation',
|
'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',
|
||||||
));
|
));
|
||||||
@@ -2,8 +2,10 @@ let LinkSetWorker = new function(){
|
|||||||
|
|
||||||
// defines
|
// defines
|
||||||
const ROUTER_BASE_URL = '../pages/ajax.render.php';
|
const ROUTER_BASE_URL = '../pages/ajax.render.php';
|
||||||
const ROUTE_LINK_SET_DELETE_OBJECT = 'linkset.DeleteLinkedObject';
|
const ROUTE_LINK_SET_DELETE_OBJECT = 'linkset.delete_linked_object';
|
||||||
const ROUTE_LINK_SET_DETACH_OBJECT = 'linkset.DetachLinkedObject';
|
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.
|
* 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 {
|
return {
|
||||||
DeleteLinkedObject: CallAjaxDeleteLinkedObject,
|
DeleteLinkedObject: CallAjaxDeleteLinkedObject,
|
||||||
DetachLinkedObject: CallAjaxDetachLinkedObject
|
DetachLinkedObject: CallAjaxDetachLinkedObject,
|
||||||
|
ModifyLinkedObject: CallAjaxModifyLinkedObject,
|
||||||
|
CreateLinkedObject: CallAjaxCreateLinkedObject
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -201,6 +201,7 @@ CombodoModal._InstantiateModal = function(oModalElem, oOptions) {
|
|||||||
width: 'auto',
|
width: 'auto',
|
||||||
height: 'auto',
|
height: 'auto',
|
||||||
modal: oOptions.extra_options.modal ?? true,
|
modal: oOptions.extra_options.modal ?? true,
|
||||||
|
classes: oOptions.classes ?? {},
|
||||||
close: oOptions.extra_options.callback_on_modal_close,
|
close: oOptions.extra_options.callback_on_modal_close,
|
||||||
autoOpen: oOptions.auto_open,
|
autoOpen: oOptions.auto_open,
|
||||||
title: oOptions.title,
|
title: oOptions.title,
|
||||||
@@ -326,14 +327,17 @@ CombodoModal._InstantiateModal = function(oModalElem, oOptions) {
|
|||||||
*/
|
*/
|
||||||
CombodoModal._ConvertButtonDefinition = function(aButtonsDefinitions){
|
CombodoModal._ConvertButtonDefinition = function(aButtonsDefinitions){
|
||||||
const aConverted = [];
|
const aConverted = [];
|
||||||
|
if(aButtonsDefinitions === null) {
|
||||||
|
return aConverted
|
||||||
|
}
|
||||||
aButtonsDefinitions.forEach(element => {
|
aButtonsDefinitions.forEach(element => {
|
||||||
const aButton = {
|
const aButton = {
|
||||||
text: element.text,
|
text: element.text,
|
||||||
class: element.class,
|
class: element.class,
|
||||||
click: element.callback_on_click
|
click: element.callback_on_click
|
||||||
|
}
|
||||||
|
aConverted.push(aButton);
|
||||||
}
|
}
|
||||||
aConverted.push(aButton);
|
|
||||||
}
|
|
||||||
);
|
);
|
||||||
return aConverted;
|
return aConverted;
|
||||||
}
|
}
|
||||||
@@ -428,6 +432,42 @@ CombodoModal.OpenConfirmationModal = function(oOptions, aData) {
|
|||||||
CombodoModal.OpenModal(oOptions);
|
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
|
// Processing on each pages of the backoffice
|
||||||
$(document).ready(function(){
|
$(document).ready(function(){
|
||||||
// Initialize global keyboard shortcuts
|
// Initialize global keyboard shortcuts
|
||||||
|
|||||||
14
js/utils.js
14
js/utils.js
@@ -1167,6 +1167,7 @@ let CombodoModal = {
|
|||||||
{
|
{
|
||||||
id: null, // ID of the created modal
|
id: null, // ID of the created modal
|
||||||
attributes: {}, // HTML attributes
|
attributes: {}, // HTML attributes
|
||||||
|
classes: {}, // Classes for the created modal elements
|
||||||
base_modal: {
|
base_modal: {
|
||||||
usage: 'clone', // Either 'clone' or 'replace'
|
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
|
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) {
|
OpenConfirmationModal: function(oOptions) {
|
||||||
// Meant for overlaoding
|
// Meant for overlaoding
|
||||||
CombodoJSConsole.Debug('CombodoModal.OpenConfirmationModal not implemented');
|
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');
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
298
pages/UI.php
298
pages/UI.php
@@ -786,169 +786,8 @@ try
|
|||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
case 'apply_modify': // Applying the modifications to an existing object
|
case 'apply_modify': // Applying the modifications to an existing object
|
||||||
$oP->DisableBreadCrumb();
|
$oController = new ObjectController();
|
||||||
$sClass = utils::ReadPostedParam('class', '', 'class');
|
$oP = $oController->OperationApplyModify();
|
||||||
$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("<strong>".Dict::S('UI:Error:ObjectAlreadyUpdated')."</strong>\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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
@@ -1047,137 +886,10 @@ try
|
|||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
case 'apply_new': // Creation of a new object
|
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'));
|
$oController = new ObjectController();
|
||||||
}
|
$oP = $oController->OperationApplyNew();
|
||||||
if (!utils::IsTransactionValid($sTransactionId, false))
|
break;
|
||||||
{
|
|
||||||
$sUser = UserRights::GetUser();
|
|
||||||
IssueLog::Error("UI.php '$operation' : invalid transaction_id ! data: user='$sUser', class='$sClass'");
|
|
||||||
$oP->p("<strong>".Dict::S('UI:Error:ObjectAlreadyCreated')."</strong>\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("<h1>$sMessage</h1>");
|
|
||||||
try {
|
|
||||||
ApplyNextAction($oP, $oObj, $sNextAction);
|
|
||||||
}
|
|
||||||
catch (ApplicationException $e) {
|
|
||||||
$sMessage = $e->getMessage();
|
|
||||||
$sSeverity = 'info';
|
|
||||||
ReloadAndDisplay($oP, $oObj, 'create', $sMessage, $sSeverity);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// Nothing more to do
|
|
||||||
ReloadAndDisplay($oP, $oObj, 'create', $sMessage, 'ok');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (CoreCannotSaveObjectException $e) {
|
|
||||||
// Found issues, explain and give the user a second chance
|
|
||||||
//
|
|
||||||
$aIssues = $e->getIssues();
|
|
||||||
|
|
||||||
$sObjKey = $oObj->GetKey();
|
|
||||||
$sClassIcon = MetaModel::GetClassIcon($sClass, false);
|
|
||||||
$sHeaderTitle = Dict::Format('UI:CreationTitle_Class', $sClassLabel);
|
|
||||||
|
|
||||||
$oP->set_title(Dict::Format('UI:CreationPageTitle_Class', $sClassLabel));
|
|
||||||
if (!empty($aIssues)) {
|
|
||||||
$oP->AddHeaderMessage($e->getHtmlMessage(), 'message_error');
|
|
||||||
}
|
|
||||||
if (!empty($aWarnings)) {
|
|
||||||
$sWarnings = implode(', ', $aWarnings);
|
|
||||||
$oP->AddHeaderMessage($sWarnings, 'message_warning');
|
|
||||||
}
|
|
||||||
cmdbAbstractObject::DisplayCreationForm($oP, $sClass, $oObj, [], ['transaction_id' => $sTransactionId]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
|||||||
@@ -56,6 +56,10 @@ class DataTable extends UIContentBlock
|
|||||||
* array of data to display the first page
|
* array of data to display the first page
|
||||||
*/
|
*/
|
||||||
protected $aInitDisplayData;
|
protected $aInitDisplayData;
|
||||||
|
/**
|
||||||
|
* @var string JS Handler to be called when "open_creation_modal.object.itop" is fired on the table
|
||||||
|
*/
|
||||||
|
protected string $sModalCreationHandler;
|
||||||
|
|
||||||
public const DEFAULT_ACTION_ROW_CONFIRMATION = true;
|
public const DEFAULT_ACTION_ROW_CONFIRMATION = true;
|
||||||
|
|
||||||
@@ -73,6 +77,7 @@ class DataTable extends UIContentBlock
|
|||||||
$this->aOptions = [];
|
$this->aOptions = [];
|
||||||
$this->aResultColumns = [];
|
$this->aResultColumns = [];
|
||||||
$this->sJsonData = '';
|
$this->sJsonData = '';
|
||||||
|
$this->sModalCreationHandler = '';
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -260,4 +265,22 @@ class DataTable extends UIContentBlock
|
|||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function GetModalCreationHandler(): string
|
||||||
|
{
|
||||||
|
return $this->sModalCreationHandler;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $sModalCreationHandler
|
||||||
|
* @return $this
|
||||||
|
*/
|
||||||
|
public function SetModalCreationHandler(string $sModalCreationHandler)
|
||||||
|
{
|
||||||
|
$this->sModalCreationHandler = $sModalCreationHandler;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -772,6 +772,11 @@ class DataTableUIBlockFactory extends AbstractUIBlockFactory
|
|||||||
$oDataTable->SetRowActions($aExtraParams['row_actions']);
|
$oDataTable->SetRowActions($aExtraParams['row_actions']);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (isset($aExtraParams['creation_in_modal_js_handler'])){
|
||||||
|
$oDataTable->SetModalCreationHandler($aExtraParams['creation_in_modal_js_handler']);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
return $oDataTable;
|
return $oDataTable;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1087,6 +1092,10 @@ JS;
|
|||||||
/**give definition of id for select checkbox*/
|
/**give definition of id for select checkbox*/
|
||||||
'row_actions',
|
'row_actions',
|
||||||
/** array of blocks displayed on every row */
|
/** array of blocks displayed on every row */
|
||||||
|
'creation_in_modal_is_allowed',
|
||||||
|
/** bool to allow a creation of a new object of this type in a modal */
|
||||||
|
'creation_in_modal_js_handler',
|
||||||
|
/** Handler to call when trying to create a new object in modal */
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -24,6 +24,7 @@ abstract class AbstractBlockLinksViewTable extends UIContentBlock
|
|||||||
public const DEFAULT_JS_TEMPLATE_REL_PATH = 'application/links/layout';
|
public const DEFAULT_JS_TEMPLATE_REL_PATH = 'application/links/layout';
|
||||||
public const DEFAULT_JS_FILES_REL_PATH = [
|
public const DEFAULT_JS_FILES_REL_PATH = [
|
||||||
'js/links/link_set_worker.js',
|
'js/links/link_set_worker.js',
|
||||||
|
'js/wizardhelper.js',
|
||||||
];
|
];
|
||||||
|
|
||||||
/** @var \DBObject $oDbObject db object witch link set belongs to */
|
/** @var \DBObject $oDbObject db object witch link set belongs to */
|
||||||
@@ -40,6 +41,8 @@ abstract class AbstractBlockLinksViewTable extends UIContentBlock
|
|||||||
|
|
||||||
/** @var string $sTargetClass links target classname */
|
/** @var string $sTargetClass links target classname */
|
||||||
protected string $sTargetClass;
|
protected string $sTargetClass;
|
||||||
|
|
||||||
|
protected string $sTableId;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructor.
|
* Constructor.
|
||||||
@@ -62,7 +65,8 @@ abstract class AbstractBlockLinksViewTable extends UIContentBlock
|
|||||||
$this->sAttCode = $sAttCode;
|
$this->sAttCode = $sAttCode;
|
||||||
$this->sObjectClass = $sObjectClass;
|
$this->sObjectClass = $sObjectClass;
|
||||||
$this->oDbObject = $oDbObject;
|
$this->oDbObject = $oDbObject;
|
||||||
|
$this->sTableId = 'rel_'.$this->sAttCode;
|
||||||
|
$this->SetDataAttributes(['role' => 'ibo-block-links-table', 'link-attcode' => $sAttCode, 'link-class' => $this->oAttDef->GetLinkedClass()]);
|
||||||
// Initialization
|
// Initialization
|
||||||
$this->Init();
|
$this->Init();
|
||||||
|
|
||||||
@@ -131,7 +135,7 @@ abstract class AbstractBlockLinksViewTable extends UIContentBlock
|
|||||||
|
|
||||||
// add list block
|
// add list block
|
||||||
$oBlock = new \DisplayBlock($oLinkSet->GetFilter(), 'list', false);
|
$oBlock = new \DisplayBlock($oLinkSet->GetFilter(), 'list', false);
|
||||||
$this->AddSubBlock($oBlock->GetRenderContent($oPage, $this->GetExtraParam(), 'rel_'.$this->sAttCode));
|
$this->AddSubBlock($oBlock->GetRenderContent($oPage, $this->GetExtraParam(), $this->sTableId));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -187,4 +191,15 @@ abstract class AbstractBlockLinksViewTable extends UIContentBlock
|
|||||||
* @throws \Exception
|
* @throws \Exception
|
||||||
*/
|
*/
|
||||||
abstract function GetTargetClass(): string;
|
abstract function GetTargetClass(): string;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function GetAttCode(): string
|
||||||
|
{
|
||||||
|
return $this->sAttCode;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -37,7 +37,7 @@ class BlockIndirectLinksViewTable extends AbstractBlockLinksViewTable
|
|||||||
/** @inheritdoc */
|
/** @inheritdoc */
|
||||||
public function GetExtraParam(): array
|
public function GetExtraParam(): array
|
||||||
{
|
{
|
||||||
return array(
|
$aExtraParams = array(
|
||||||
'link_attr' => $this->oAttDef->GetExtKeyToMe(),
|
'link_attr' => $this->oAttDef->GetExtKeyToMe(),
|
||||||
'object_id' => $this->oDbObject->GetKey(),
|
'object_id' => $this->oDbObject->GetKey(),
|
||||||
'target_attr' => $this->oAttDef->GetExtKeyToRemote(),
|
'target_attr' => $this->oAttDef->GetExtKeyToRemote(),
|
||||||
@@ -48,7 +48,17 @@ class BlockIndirectLinksViewTable extends AbstractBlockLinksViewTable
|
|||||||
'zlist' => false,
|
'zlist' => false,
|
||||||
'extra_fields' => $this->GetAttCodesToDisplay(),
|
'extra_fields' => $this->GetAttCodesToDisplay(),
|
||||||
'row_actions' => $this->GetRowActions(),
|
'row_actions' => $this->GetRowActions(),
|
||||||
|
'currentId' => $this->GetTableId(),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// - 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().'");';
|
||||||
|
}
|
||||||
|
|
||||||
|
return $aExtraParams;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @inheritdoc */
|
/** @inheritdoc */
|
||||||
@@ -70,6 +80,12 @@ class BlockIndirectLinksViewTable extends AbstractBlockLinksViewTable
|
|||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
|
||||||
|
$aRowActions[] = array(
|
||||||
|
'tooltip' => 'UI:Links:ActionRow:Modify',
|
||||||
|
'icon_classes' => 'fas fa-pen',
|
||||||
|
'js_row_action' => "LinkSetWorker.ModifyLinkedObject('{$this->oAttDef->GetLinkedClass()}', aRowData['Link/_key_/raw'], '{$this->GetTableId()}');",
|
||||||
|
);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return $aRowActions;
|
return $aRowActions;
|
||||||
|
|||||||
@@ -215,6 +215,7 @@ class iTopWebPage extends NiceWebPage implements iTabbedPage
|
|||||||
|
|
||||||
// Modals
|
// Modals
|
||||||
$this->add_dict_entries('UI:Modal:');
|
$this->add_dict_entries('UI:Modal:');
|
||||||
|
$this->add_dict_entries('UI:Links:');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -10,15 +10,19 @@ use AjaxPage;
|
|||||||
use ApplicationException;
|
use ApplicationException;
|
||||||
use cmdbAbstractObject;
|
use cmdbAbstractObject;
|
||||||
use CMDBObjectSet;
|
use CMDBObjectSet;
|
||||||
|
use Combodo\iTop\Application\UI\Base\Component\Alert\AlertUIBlockFactory;
|
||||||
|
use Combodo\iTop\Application\UI\Base\Component\QuickCreate\QuickCreateHelper;
|
||||||
use Combodo\iTop\Application\UI\Base\Layout\PageContent\PageContentFactory;
|
use Combodo\iTop\Application\UI\Base\Layout\PageContent\PageContentFactory;
|
||||||
use Combodo\iTop\Controller\AbstractController;
|
use Combodo\iTop\Controller\AbstractController;
|
||||||
|
use CoreCannotSaveObjectException;
|
||||||
use Dict;
|
use Dict;
|
||||||
|
use IssueLog;
|
||||||
use iTopWebPage;
|
use iTopWebPage;
|
||||||
|
use JsonPage;
|
||||||
use MetaModel;
|
use MetaModel;
|
||||||
use SecurityException;
|
use SecurityException;
|
||||||
use utils;
|
use utils;
|
||||||
use UserRights;
|
use UserRights;
|
||||||
use WebPage;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Class ObjectController
|
* Class ObjectController
|
||||||
@@ -65,8 +69,41 @@ class ObjectController extends AbstractController
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Prepare web page (should more likely be some kind of response object like for Symfony)
|
// Prepare web page (should more likely be some kind of response object like for Symfony)
|
||||||
|
$aFormExtraParams = array('wizard_container' => 1);
|
||||||
if ($this->IsHandlingXmlHttpRequest()) {
|
if ($this->IsHandlingXmlHttpRequest()) {
|
||||||
$oPage = new AjaxPage('');
|
$oPage = new AjaxPage('');
|
||||||
|
$aFormExtraParams['js_handlers'] = [];
|
||||||
|
$aFormExtraParams['noRelations'] = true;
|
||||||
|
// We display this form in a modal, once we submit (in ajax) we probably want to only close the modal
|
||||||
|
$aFormExtraParams['js_handlers']['form_on_submit'] =
|
||||||
|
<<<JS
|
||||||
|
event.preventDefault();
|
||||||
|
if(bOnSubmitForm === true)
|
||||||
|
{
|
||||||
|
let oForm = $(this);
|
||||||
|
let sUrl = oForm.attr('action');
|
||||||
|
let sPosting = $.post( sUrl, oForm.serialize());
|
||||||
|
|
||||||
|
/* Alerts the results */
|
||||||
|
sPosting.done(function(data) {
|
||||||
|
if(data.success !== undefined && data.success === true) {
|
||||||
|
oForm.closest('[data-role="ibo-modal"]').dialog('close');
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
CombodoModal.OpenInformativeModal(data.data.error_message, 'error');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
JS;
|
||||||
|
|
||||||
|
|
||||||
|
$aFormExtraParams['js_handlers']['cancel_button_on_click'] =
|
||||||
|
<<<JS
|
||||||
|
function() {
|
||||||
|
$(this).closest('[data-role="ibo-modal"]').dialog('close');
|
||||||
|
};
|
||||||
|
JS;
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
$oPage = new iTopWebPage('', $bPrintable);
|
$oPage = new iTopWebPage('', $bPrintable);
|
||||||
$oPage->DisableBreadCrumb();
|
$oPage->DisableBreadCrumb();
|
||||||
@@ -78,10 +115,398 @@ class ObjectController extends AbstractController
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Note: Code duplicated to the case 'apply_modify' in UI.php when a data integrity issue has been found
|
// Note: Code duplicated to the case 'apply_modify' in UI.php when a data integrity issue has been found
|
||||||
$oObj->DisplayModifyForm($oPage, array('wizard_container' => 1)); // wizard_container: Display the title above the form
|
$oObj->DisplayModifyForm($oPage, $aFormExtraParams); // wizard_container: Display the title above the form
|
||||||
|
|
||||||
return $oPage;
|
return $oPage;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return \iTopWebPage|\JsonPage Object edit form in its webpage
|
||||||
|
* @throws \ApplicationException
|
||||||
|
* @throws \ArchivedObjectException
|
||||||
|
* @throws \CoreException
|
||||||
|
* @throws \SecurityException
|
||||||
|
*/
|
||||||
|
public function OperationApplyNew()
|
||||||
|
{
|
||||||
|
$bPrintable = utils::ReadParam('printable', '0') === '1';
|
||||||
|
$aResult = [];
|
||||||
|
if ($this->IsHandlingXmlHttpRequest()) {
|
||||||
|
$oPage = new JsonPage();
|
||||||
|
$oPage->SetOutputDataOnly(true);
|
||||||
|
$aResult['success'] = false;
|
||||||
|
} else {
|
||||||
|
$oPage = new iTopWebPage('', $bPrintable);
|
||||||
|
$oPage->DisableBreadCrumb();
|
||||||
|
}
|
||||||
|
|
||||||
|
$sClass = utils::ReadPostedParam('class', '', 'class');
|
||||||
|
$sClassLabel = MetaModel::GetName($sClass);
|
||||||
|
$sTransactionId = utils::ReadPostedParam('transaction_id', '', 'transaction_id');
|
||||||
|
$aErrors = array();
|
||||||
|
$aWarnings = array();
|
||||||
|
if ( empty($sClass) )
|
||||||
|
{
|
||||||
|
IssueLog::Trace(__CLASS__.'::'.__METHOD__.' Object not created (empty class)', $sClass, array(
|
||||||
|
'$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(__CLASS__.'::'.__METHOD__." : invalid transaction_id ! data: user='$sUser', class='$sClass'");
|
||||||
|
|
||||||
|
if ($this->IsHandlingXmlHttpRequest()) {
|
||||||
|
$aResult['data'] = ['error_message' => Dict::S('UI:Error:ObjectAlreadyCreated')];
|
||||||
|
} else {
|
||||||
|
$oErrorAlert = AlertUIBlockFactory::MakeForFailure(Dict::S('UI:Error:ObjectAlreadyCreated'));
|
||||||
|
$oErrorAlert->SetIsClosable(false)
|
||||||
|
->SetIsCollapsible(false);
|
||||||
|
$oPage->AddUiBlock($oErrorAlert);
|
||||||
|
}
|
||||||
|
|
||||||
|
IssueLog::Trace(__CLASS__.'::'.__METHOD__.' Object not created (invalid transaction_id)', $sClass, array(
|
||||||
|
'$sTransactionId' => $sTransactionId,
|
||||||
|
'$sUser' => UserRights::GetUser(),
|
||||||
|
'HTTP_REFERER' => @$_SERVER['HTTP_REFERER'],
|
||||||
|
'REQUEST_URI' => @$_SERVER['REQUEST_URI'],
|
||||||
|
));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
$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(__CLASS__.'::'.__METHOD__.' Object not created (see $aErrors)', $sClass, array(
|
||||||
|
'$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(__CLASS__.'::'.__METHOD__.' Object created', $sClass, array(
|
||||||
|
'$id' => $oObj->GetKey(),
|
||||||
|
'$sTransactionId' => $sTransactionId,
|
||||||
|
'$aErrors' => $aErrors,
|
||||||
|
'$sUser' => UserRights::GetUser(),
|
||||||
|
'HTTP_REFERER' => @$_SERVER['HTTP_REFERER'],
|
||||||
|
'REQUEST_URI' => @$_SERVER['REQUEST_URI'],
|
||||||
|
));
|
||||||
|
|
||||||
|
utils::RemoveTransaction($sTransactionId);
|
||||||
|
$oPage->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)) {
|
||||||
|
$oPage->add("<h1>$sMessage</h1>");
|
||||||
|
try {
|
||||||
|
ApplyNextAction($oPage, $oObj, $sNextAction);
|
||||||
|
}
|
||||||
|
catch (ApplicationException $e) {
|
||||||
|
$sMessage = $e->getMessage();
|
||||||
|
$sSeverity = 'info';
|
||||||
|
ReloadAndDisplay($oPage, $oObj, 'create', $sMessage, $sSeverity);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Nothing more to do
|
||||||
|
if ($this->IsHandlingXmlHttpRequest()) {
|
||||||
|
$aResult['success'] = true;
|
||||||
|
} else {
|
||||||
|
ReloadAndDisplay($oPage, $oObj, 'create', $sMessage, 'ok');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (CoreCannotSaveObjectException $e) {
|
||||||
|
// Found issues, explain and give the user a second chance
|
||||||
|
//
|
||||||
|
$aIssues = $e->getIssues();
|
||||||
|
if ($this->IsHandlingXmlHttpRequest()) {
|
||||||
|
$aResult['data'] = ['error_message' => $e->getHtmlMessage()];
|
||||||
|
} else {
|
||||||
|
$sObjKey = $oObj->GetKey();
|
||||||
|
$sClassIcon = MetaModel::GetClassIcon($sClass, false);
|
||||||
|
$sHeaderTitle = Dict::Format('UI:CreationTitle_Class', $sClassLabel);
|
||||||
|
|
||||||
|
$oPage->set_title(Dict::Format('UI:CreationPageTitle_Class', $sClassLabel));
|
||||||
|
if (!empty($aIssues)) {
|
||||||
|
$oPage->AddHeaderMessage($e->getHtmlMessage(), 'message_error');
|
||||||
|
}
|
||||||
|
if (!empty($aWarnings)) {
|
||||||
|
$sWarnings = implode(', ', $aWarnings);
|
||||||
|
$oPage->AddHeaderMessage($sWarnings, 'message_warning');
|
||||||
|
}
|
||||||
|
cmdbAbstractObject::DisplayCreationForm($oPage, $sClass, $oObj, [], ['transaction_id' => $sTransactionId]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ($this->IsHandlingXmlHttpRequest()) {
|
||||||
|
$oPage->SetData($aResult);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $oPage;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return \iTopWebPage|\JsonPage
|
||||||
|
* @throws \ApplicationException
|
||||||
|
* @throws \ArchivedObjectException
|
||||||
|
* @throws \ConfigException
|
||||||
|
* @throws \CoreException
|
||||||
|
* @throws \CoreUnexpectedValue
|
||||||
|
* @throws \DictExceptionMissingString
|
||||||
|
* @throws \MySQLException
|
||||||
|
*/
|
||||||
|
public function OperationApplyModify(){
|
||||||
|
$bPrintable = utils::ReadParam('printable', '0') === '1';
|
||||||
|
$aResult = [];
|
||||||
|
if ($this->IsHandlingXmlHttpRequest()) {
|
||||||
|
$oPage = new JsonPage();
|
||||||
|
$oPage->SetOutputDataOnly(true);
|
||||||
|
$aResult['success'] = false;
|
||||||
|
} else {
|
||||||
|
$oPage = new iTopWebPage('', $bPrintable);
|
||||||
|
$oPage->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))
|
||||||
|
{
|
||||||
|
IssueLog::Trace(__CLASS__.'::'.__METHOD__.' Object not updated (empty class or id)', $sClass, array(
|
||||||
|
'$id' => $id,
|
||||||
|
'$sTransactionId' => $sTransactionId,
|
||||||
|
'$sUser' => UserRights::GetUser(),
|
||||||
|
'HTTP_REFERER' => @$_SERVER['HTTP_REFERER'],
|
||||||
|
'REQUEST_URI' => @$_SERVER['REQUEST_URI'],
|
||||||
|
));
|
||||||
|
// TODO 3.1 Do not crash with an exception in ajax
|
||||||
|
throw new ApplicationException(Dict::Format('UI:Error:2ParametersMissing', 'class', 'id'));
|
||||||
|
}
|
||||||
|
$bDisplayDetails = true;
|
||||||
|
$oObj = MetaModel::GetObject($sClass, $id, false);
|
||||||
|
if ($oObj === null)
|
||||||
|
{
|
||||||
|
$bDisplayDetails = false;
|
||||||
|
|
||||||
|
if ($this->IsHandlingXmlHttpRequest()) {
|
||||||
|
$aResult['data'] = ['error_message' => Dict::S('UI:ObjectDoesNotExist')];
|
||||||
|
} else {
|
||||||
|
$oPage->set_title(Dict::S('UI:ErrorPageTitle'));
|
||||||
|
|
||||||
|
$oErrorAlert = AlertUIBlockFactory::MakeForFailure(Dict::S('UI:ObjectDoesNotExist'));
|
||||||
|
$oErrorAlert->SetIsClosable(false)
|
||||||
|
->SetIsCollapsible(false);
|
||||||
|
$oPage->AddUiBlock($oErrorAlert);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
IssueLog::Trace(__CLASS__.'::'.__METHOD__.' Object not updated (id not found)', $sClass, array(
|
||||||
|
'$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 after the redirection
|
||||||
|
$sUser = UserRights::GetUser();
|
||||||
|
IssueLog::Error(__CLASS__.'::'.__METHOD__." : invalid transaction_id ! data: user='$sUser', class='$sClass'");
|
||||||
|
|
||||||
|
if ($this->IsHandlingXmlHttpRequest()) {
|
||||||
|
$aResult['data'] = ['error_message' => Dict::S('UI:Error:ObjectAlreadyUpdated')];
|
||||||
|
} else {
|
||||||
|
$oPage->set_title(Dict::Format('UI:ModificationPageTitle_Object_Class', $oObj->GetRawName(), $sClassLabel)); // Set title will take care of the encoding
|
||||||
|
$oPage->p("<strong>".Dict::S('UI:Error:ObjectAlreadyUpdated')."</strong>\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
$sMessage = Dict::Format('UI:Error:ObjectAlreadyUpdated', MetaModel::GetName(get_class($oObj)), $oObj->GetName());
|
||||||
|
$sSeverity = 'error';
|
||||||
|
|
||||||
|
IssueLog::Trace(__CLASS__.'::'.__METHOD__.' Object not updated (invalid transaction_id)', $sClass, array(
|
||||||
|
'$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))
|
||||||
|
{
|
||||||
|
if ($this->IsHandlingXmlHttpRequest()) {
|
||||||
|
$aResult['data'] = ['error_message' => Dict::Format('UI:Class_Object_NotUpdated', MetaModel::GetName(get_class($oObj)), $oObj->GetName())];
|
||||||
|
} else {
|
||||||
|
$oPage->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(__CLASS__.'::'.__METHOD__.' Object not updated (see either $aErrors or IsModified)', $sClass, array(
|
||||||
|
'$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(__CLASS__.'::'.__METHOD__.' Object updated', $sClass, array(
|
||||||
|
'$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';
|
||||||
|
if ($this->IsHandlingXmlHttpRequest()) {
|
||||||
|
$aResult['success'] = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (CoreCannotSaveObjectException $e)
|
||||||
|
{
|
||||||
|
// Found issues, explain and give the user a second chance
|
||||||
|
//
|
||||||
|
$bDisplayDetails = false;
|
||||||
|
$aIssues = $e->getIssues();
|
||||||
|
if ($this->IsHandlingXmlHttpRequest()) {
|
||||||
|
$aResult['data'] = ['error_message' => $e->getHtmlMessage()];
|
||||||
|
} else {
|
||||||
|
$oPage->AddHeaderMessage($e->getHtmlMessage(), 'message_error');
|
||||||
|
$oObj->DisplayModifyForm($oPage,
|
||||||
|
array('wizard_container' => true)); // wizard_container: display the wizard border and the title
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
catch (DeleteException $e)
|
||||||
|
{
|
||||||
|
if ($this->IsHandlingXmlHttpRequest()) {
|
||||||
|
$aResult['data'] = ['error_message' => Dict::Format('UI:Class_Object_NotUpdated', MetaModel::GetName(get_class($oObj)), $oObj->GetName())];
|
||||||
|
} else {
|
||||||
|
// 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($oPage, $oObj, $sNextAction);
|
||||||
|
}
|
||||||
|
catch (ApplicationException $e)
|
||||||
|
{
|
||||||
|
$sMessage = $e->getMessage();
|
||||||
|
$sSeverity = 'info';
|
||||||
|
ReloadAndDisplay($oPage, $oObj, 'update', $sMessage, $sSeverity);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Nothing more to do
|
||||||
|
$sMessage = isset($sMessage) ? $sMessage : '';
|
||||||
|
$sSeverity = isset($sSeverity) ? $sSeverity : null;
|
||||||
|
if ($this->IsHandlingXmlHttpRequest()) {
|
||||||
|
;
|
||||||
|
} else{
|
||||||
|
ReloadAndDisplay($oPage, $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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ($this->IsHandlingXmlHttpRequest()) {
|
||||||
|
$oPage->SetData($aResult);
|
||||||
|
}
|
||||||
|
return $oPage;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return string[] Rel. paths (to iTop root folder) of required JS files for object modification (create, edit, stimulus, ...)
|
* @return string[] Rel. paths (to iTop root folder) of required JS files for object modification (create, edit, stimulus, ...)
|
||||||
|
|||||||
@@ -6,8 +6,14 @@
|
|||||||
|
|
||||||
namespace Combodo\iTop\Controller\Links;
|
namespace Combodo\iTop\Controller\Links;
|
||||||
|
|
||||||
|
use AjaxPage;
|
||||||
|
use cmdbAbstractObject;
|
||||||
|
use Combodo\iTop\Application\UI\Base\Layout\PageContent\PageContentFactory;
|
||||||
use Combodo\iTop\Controller\AbstractController;
|
use Combodo\iTop\Controller\AbstractController;
|
||||||
|
use DBObject;
|
||||||
|
use iTopWebPage;
|
||||||
use MetaModel;
|
use MetaModel;
|
||||||
|
use UserRights;
|
||||||
use utils;
|
use utils;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -99,5 +105,97 @@ class LinkSetController extends AbstractController
|
|||||||
return $oPage;
|
return $oPage;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return \iTopWebPage|\AjaxPage Create edit form in its webpage
|
||||||
|
* @throws \ApplicationException
|
||||||
|
* @throws \ArchivedObjectException
|
||||||
|
* @throws \CoreException
|
||||||
|
* @throws \SecurityException
|
||||||
|
*/
|
||||||
|
public function OperationCreateLinkedObject()
|
||||||
|
{
|
||||||
|
$bPrintable = utils::ReadParam('printable', '0') === '1';
|
||||||
|
$sProposedRealClass = utils::ReadParam('class', '', false, 'class');
|
||||||
|
$sAttCode = utils::ReadParam('att_code', '', false, 'raw');
|
||||||
|
$sClass = utils::ReadParam('host_class', '', false, 'class');
|
||||||
|
$sId = utils::ReadParam('host_id', '', false, 'integer');
|
||||||
|
|
||||||
|
// For security reasons: check that the "proposed" class is actually a subclass of the linked class
|
||||||
|
// and that the current user is allowed to create objects of this class
|
||||||
|
$sRealClass = '';
|
||||||
|
|
||||||
|
$aSubClasses = MetaModel::EnumChildClasses($sProposedRealClass, ENUM_CHILD_CLASSES_ALL); // Including the specified class itself
|
||||||
|
$aPossibleClasses = array();
|
||||||
|
foreach ($aSubClasses as $sCandidateClass) {
|
||||||
|
if (!MetaModel::IsAbstract($sCandidateClass) && (UserRights::IsActionAllowed($sCandidateClass, UR_ACTION_MODIFY) == UR_ALLOWED_YES)) {
|
||||||
|
if ($sCandidateClass == $sProposedRealClass) {
|
||||||
|
$sRealClass = $sProposedRealClass;
|
||||||
|
}
|
||||||
|
$aPossibleClasses[$sCandidateClass] = MetaModel::GetName($sCandidateClass);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Only one of the subclasses can be instantiated...
|
||||||
|
if (count($aPossibleClasses) == 1) {
|
||||||
|
$aKeys = array_keys($aPossibleClasses);
|
||||||
|
$sRealClass = $aKeys[0];
|
||||||
|
}
|
||||||
|
if ($sRealClass != '') {
|
||||||
|
$oLinksetDef = MetaModel::GetAttributeDef($sClass, $sAttCode);
|
||||||
|
$sExtKeyToMe = $oLinksetDef->GetExtKeyToMe();
|
||||||
|
$aFieldFlags = array(); // TODO 3.1 array($sExtKeyToMe => OPT_ATT_READONLY);
|
||||||
|
$oObj = DBObject::MakeDefaultInstance($sRealClass);
|
||||||
|
|
||||||
|
if ($this->IsHandlingXmlHttpRequest()) {
|
||||||
|
$oPage = new AjaxPage('');
|
||||||
|
} else {
|
||||||
|
$oPage = new iTopWebPage('', $bPrintable);
|
||||||
|
$oPage->DisableBreadCrumb();
|
||||||
|
$oPage->SetContentLayout(PageContentFactory::MakeForObjectDetails($oObj, cmdbAbstractObject::ENUM_DISPLAY_MODE_CREATE));
|
||||||
|
}
|
||||||
|
|
||||||
|
$oSourceObj = MetaModel::GetObject($sClass, $sId);
|
||||||
|
|
||||||
|
$oObj->Set($sExtKeyToMe, $sId);
|
||||||
|
$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 =
|
||||||
|
<<<JS
|
||||||
|
event.preventDefault();
|
||||||
|
if(bOnSubmitForm === true)
|
||||||
|
{
|
||||||
|
let oForm = $(this);
|
||||||
|
let sUrl = oForm.attr('action');
|
||||||
|
let sPosting = $.post( sUrl, oForm.serialize());
|
||||||
|
|
||||||
|
/* Alerts the results */
|
||||||
|
sPosting.done(function(data) {
|
||||||
|
if(data.success !== undefined && data.success === true) {
|
||||||
|
oForm.closest('[data-role="ibo-modal"]').dialog('close');
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
CombodoModal.OpenInformativeModal(data.data.error_message, 'error');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
JS
|
||||||
|
;
|
||||||
|
$aExtraParams = [
|
||||||
|
'noRelations' => true,
|
||||||
|
'fieldsFlags' => $aFieldFlags,
|
||||||
|
'js_handlers' => [
|
||||||
|
'form_on_submit' => $sFormOnSubmitJsCode,
|
||||||
|
'cancel_button_on_click' =>
|
||||||
|
<<<JS
|
||||||
|
function() {
|
||||||
|
$(this).closest('[data-role="ibo-modal"]').dialog('close');
|
||||||
|
};
|
||||||
|
JS
|
||||||
|
]
|
||||||
|
];
|
||||||
|
cmdbAbstractObject::DisplayCreationForm($oPage, $sRealClass, $oObj, array(), $aExtraParams);
|
||||||
|
return $oPage;
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -367,6 +367,12 @@ $('#{{ oUIBlock.GetId() }}').on('refresh.datatable.itop', function (){
|
|||||||
oTable{{ sListIDForVarSuffix }}.ajax.reload(null, false);
|
oTable{{ sListIDForVarSuffix }}.ajax.reload(null, false);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
{% if oUIBlock.GetModalCreationHandler() is not empty %}
|
||||||
|
$('body').on('open_creation_modal.object.itop','#{{ oUIBlock.GetId() }}', function (){
|
||||||
|
{{ oUIBlock.GetModalCreationHandler() | raw}}
|
||||||
|
});
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
{% if oUIBlock.GetOption('sCountSelector') is not empty %}
|
{% if oUIBlock.GetOption('sCountSelector') is not empty %}
|
||||||
$('#{{ sListId }} [name="selectionCount"]').bind('change', function () {
|
$('#{{ sListId }} [name="selectionCount"]').bind('change', function () {
|
||||||
$('{{ oUIBlock.GetOption('sCountSelector') }}').val($('#{{ sListId }} [name="selectionCount"]').val());
|
$('{{ oUIBlock.GetOption('sCountSelector') }}').val($('#{{ sListId }} [name="selectionCount"]').val());
|
||||||
|
|||||||
Reference in New Issue
Block a user