mirror of
https://github.com/Combodo/iTop.git
synced 2026-02-13 07:24:13 +01:00
346 lines
12 KiB
JavaScript
346 lines
12 KiB
JavaScript
/*
|
|
* Copyright (C) 2013-2024 Combodo SAS
|
|
*
|
|
* This file is part of iTop.
|
|
*
|
|
* iTop is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU Affero General Public License as published by
|
|
* the Free Software Foundation, either version 3 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* iTop is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU Affero General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Affero General Public License
|
|
*/
|
|
|
|
;
|
|
$(function() {
|
|
$.widget('itop.caselog_entry_form',
|
|
{
|
|
// default options
|
|
options:
|
|
{
|
|
object_class: null,
|
|
object_id: null,
|
|
attribute_code: null,
|
|
submit_mode: 'autonomous',
|
|
text_input_id: '',
|
|
},
|
|
css_classes:
|
|
{
|
|
is_opened: 'ibo-is-opened',
|
|
is_closed: 'ibo-is-closed',
|
|
is_hidden: 'ibo-is-hidden',
|
|
},
|
|
js_selectors:
|
|
{
|
|
activity_panel: '[data-role="ibo-activity-panel"]',
|
|
activity_panel_toolbar: '[data-role="ibo-activity-panel--tab-toolbar"]',
|
|
form: '[data-role="ibo-caselog-entry-form"]', // Any caselog entry form
|
|
main_actions: '[data-role="ibo-caselog-entry-form--action-buttons--main-actions"]',
|
|
cancel_button: '[data-role="ibo-caselog-entry-form--action-buttons--main-actions"] [data-role="ibo-button"][name="cancel"]',
|
|
save_button: '[data-role="ibo-caselog-entry-form--action-buttons--main-actions"] [data-role="ibo-button"][name="save"]',
|
|
save_choices_picker: '[data-role="ibo-caselog-entry-form--action-buttons--main-actions"] [data-role="ibo-button"][name="save"] + [data-role="ibo-button"]',
|
|
},
|
|
enums:
|
|
{
|
|
submit_mode:
|
|
{
|
|
autonomous: 'autonomous',
|
|
bridged: 'bridged',
|
|
}
|
|
},
|
|
is_draft: false,
|
|
|
|
// the constructor
|
|
_create: function () {
|
|
const me = this;
|
|
const aMandatoryOptions = ['object_class', 'object_id', 'attribute_code'];
|
|
for (let sOption of aMandatoryOptions) {
|
|
if (null === this.options[sOption]) {
|
|
CombodoJSConsole.Error('CaseLogEntryForm: Could not initialize widget, make sure that the following options'+
|
|
' are passed: '+aMandatoryOptions.join(' / '), 'error');
|
|
return false;
|
|
}
|
|
}
|
|
|
|
// Ensure the CKEditor instance is ready before proceding
|
|
this._GetCKEditorInstance(true).then((oCKEditorInstance) => {
|
|
me._UpdateState();
|
|
if (me._IsSubmitAutonomous()) {
|
|
me._ShowMainActions();
|
|
} else {
|
|
me._AddBridgeInput();
|
|
me._HideMainActions();
|
|
}
|
|
|
|
me._bindEvents();
|
|
|
|
me.element.trigger('ready.caselog_entry_form.itop');
|
|
});
|
|
},
|
|
_bindEvents: function () {
|
|
let me = this;
|
|
let CKEditorInstance = this._GetCKEditorInstance();
|
|
// Handlers for the CKEditor itself
|
|
// Handle only the current CKEditor instance
|
|
// if (oEvent.editor.name !== me.options.text_input_id) {
|
|
// return;
|
|
// }
|
|
|
|
// Update depending elements on change
|
|
// Note: That when images are uploaded, the "change" event is triggered before the image upload is complete, meaning that we don't have the <img> tag yet.
|
|
CKEditorInstance.model.document.on('change:data', async function () {
|
|
const bWasDraftBefore = me.is_draft;
|
|
const bIsDraftNow = !(me._IsInputEmpty());
|
|
if (bWasDraftBefore !== bIsDraftNow) {
|
|
me.is_draft = bIsDraftNow;
|
|
me._UpdateEditingVisualHint();
|
|
// Note: We must not call me._UpdateSubmitButtonState() as it will be updated by the disable_submission/enable_submission events
|
|
}
|
|
});
|
|
|
|
// Dispatch submission to the right pipeline on submit
|
|
$(me.element).on('submit', function (oSubmitEvent) {
|
|
oSubmitEvent.preventDefault();
|
|
if (me._IsSubmitAutonomous()) {
|
|
me._RequestSubmission();
|
|
} else {
|
|
me._GetGeneralFormElement().trigger('submit');
|
|
}
|
|
});
|
|
|
|
if (false === this._IsSubmitAutonomous()) {
|
|
// Update the general form input on submit.
|
|
// This cannot be "completely" done in the "change" handler above because we don't have an event for when
|
|
// the image has been uploaded and its HTML markup added to the data. The "change" event occurs too early.
|
|
if (null === this._GetGeneralFormElement()) {
|
|
CombodoJSConsole.Error('CaseLogEntryForm: Could not find the general form element, image upload will NOT work in CKEditor');
|
|
} else {
|
|
this._GetGeneralFormElement().on('submit', function () {
|
|
me._UpdateBridgeInput();
|
|
});
|
|
}
|
|
}
|
|
|
|
// Form buttons
|
|
this.element.find(this.js_selectors.cancel_button).on('click', function (oEvent) {
|
|
me.element.trigger('cancelled_form.caselog_entry_form.itop');
|
|
});
|
|
this.element.find(this.js_selectors.save_button).on('click', function (oEvent) {
|
|
// Avoid form being submitted
|
|
oEvent.preventDefault();
|
|
|
|
me._RequestSubmission();
|
|
});
|
|
|
|
// Form show/hide
|
|
this.element.on('show_form.caselog_entry_form.itop', function () {
|
|
me._ShowEntryForm();
|
|
});
|
|
this.element.on('hide_form.caselog_entry_form.itop', function () {
|
|
me._HideEntryForm();
|
|
});
|
|
|
|
// Form submission
|
|
this.element.on('save_entry.caselog_entry_form.itop', function (oEvent, oData) {
|
|
me._RequestSubmission(oData.stimulus_code);
|
|
});
|
|
|
|
// Form enable/disable submission
|
|
this.element.on('disable_submission.caselog_entry_form.itop', function () {
|
|
me._DisableSubmission();
|
|
});
|
|
this.element.on('enable_submission.caselog_entry_form.itop', function () {
|
|
me._EnableSubmission();
|
|
});
|
|
|
|
// Form pending submission states
|
|
this.element.on('enter_pending_submission_state.caselog_entry_form.itop', function () {
|
|
me._EnterPendingSubmissionState();
|
|
});
|
|
this.element.on('leave_pending_submission_state.caselog_entry_form.itop', function () {
|
|
me._LeavePendingSubmissionState();
|
|
});
|
|
|
|
// Get the entry value
|
|
this.element.on('get_entry.caselog_entry_form.itop', function () {
|
|
return me._GetInputData();
|
|
});
|
|
this.element.on('get_extra_inputs.caselog_entry_form.itop', function () {
|
|
return me._GetExtraInputs();
|
|
});
|
|
// Clear the entry value
|
|
this.element.on('clear_entry.caselog_entry_form.itop', function () {
|
|
me._EmptyInput();
|
|
});
|
|
// Set focus in the input
|
|
this.element.on('set_focus.caselog_entry_form.itop', function () {
|
|
CKEditorInstance.focus();
|
|
});
|
|
},
|
|
|
|
// Helpers
|
|
_IsSubmitAutonomous: function () {
|
|
return this.options.submit_mode === this.enums.submit_mode.autonomous;
|
|
},
|
|
/**
|
|
* @param sStimulusCode {string} Optional stimulus code to apply after submission
|
|
* @return {void}
|
|
* @private
|
|
*/
|
|
_RequestSubmission: function (sStimulusCode = null) {
|
|
let oData = {};
|
|
|
|
if (null !== sStimulusCode) {
|
|
oData['stimulus_code'] = sStimulusCode;
|
|
}
|
|
|
|
this.element.trigger('requested_submission.caselog_entry_form.itop', oData);
|
|
},
|
|
// - Form
|
|
_GetCKEditorInstance: function (bAsync = false) {
|
|
return bAsync ? CombodoCKEditorHandler.GetInstance('#'+this.options.text_input_id) : CombodoCKEditorHandler.GetInstanceSynchronous('#'+this.options.text_input_id);
|
|
},
|
|
_ShowEntryForm: function () {
|
|
this.element.closest(this.js_selectors.activity_panel).find(this.js_selectors.form).removeClass(this.css_classes.is_closed);
|
|
},
|
|
_HideEntryForm: function () {
|
|
this.element.closest(this.js_selectors.activity_panel).find(this.js_selectors.form).addClass(this.css_classes.is_closed);
|
|
|
|
// TODO 3.0.0: This should also clear the form (input, lock, send button, ...)
|
|
},
|
|
_DisableSubmission: function () {
|
|
this.element.find(this.js_selectors.save_button+', '+this.js_selectors.save_choices_picker).prop('disabled', true);
|
|
},
|
|
_EnableSubmission: function () {
|
|
this.element.find(this.js_selectors.save_button+', '+this.js_selectors.save_choices_picker).prop('disabled', false);
|
|
},
|
|
_EnterPendingSubmissionState: function () {
|
|
this._GetCKEditorInstance().enableReadOnlyMode('hi');
|
|
this.element.find(this.js_selectors.cancel_button).prop('disabled', true);
|
|
this._DisableSubmission();
|
|
},
|
|
_LeavePendingSubmissionState: function () {
|
|
this._GetCKEditorInstance().disableReadOnlyMode('hi');
|
|
this.element.find(this.js_selectors.cancel_button).prop('disabled', false);
|
|
this._EnableSubmission();
|
|
},
|
|
// - Bridged form input
|
|
/**
|
|
* Return the general object form element.
|
|
* Only used for caselog tabs in bridged mode.
|
|
*
|
|
* @returns {null|jQuery.fn.init|jQuery|HTMLElement}
|
|
* @private
|
|
*/
|
|
_GetGeneralFormElement: function () {
|
|
const oActivityPanelElem = this.element.closest(this.js_selectors.activity_panel);
|
|
const sHostObjClass = oActivityPanelElem.attr('data-object-class');
|
|
const sHostObjId = oActivityPanelElem.attr('data-object-id');
|
|
const oGeneralFormElem = $('.ibo-object-details[data-object-class="'+sHostObjClass+'"][data-object-id="'+sHostObjId+'"]').closest('form');
|
|
|
|
// Protection in case this is called with non editable general form
|
|
if (oGeneralFormElem.length === 0) {
|
|
return null;
|
|
}
|
|
|
|
return oGeneralFormElem;
|
|
},
|
|
/**
|
|
* Add a bridge input for the caselog to the general object form.
|
|
* Only used for caselog tabs in bridged mode.
|
|
*
|
|
* @returns {boolean}|{void}
|
|
* @private
|
|
*/
|
|
_AddBridgeInput: function() {
|
|
const sCaseLogAttCode = this.element.closest(this.js_selectors.activity_panel_toolbar).attr('data-caselog-attribute-code');
|
|
const oGeneralFormElem = this._GetGeneralFormElement();
|
|
|
|
if(oGeneralFormElem === null) {
|
|
CombodoJSConsole.Error('CaseLogEntryForm: Could not add bridge input as there is no general form');
|
|
return false;
|
|
}
|
|
|
|
$('<input type="hidden" name="attr_'+sCaseLogAttCode+'" />').appendTo(oGeneralFormElem);
|
|
this._UpdateBridgeInput();
|
|
},
|
|
/**
|
|
* Update the bridge input for the caselog in the general object form.
|
|
* Only used for caselog tabs in bridged mode.
|
|
*
|
|
* @returns {void}
|
|
* @private
|
|
*/
|
|
_UpdateBridgeInput: function () {
|
|
const sCaseLogAttCode = this.element.closest(this.js_selectors.activity_panel_toolbar).attr('data-caselog-attribute-code');
|
|
let oBridgeInputElem = this._GetGeneralFormElement().find('input[name="attr_'+sCaseLogAttCode+'"]');
|
|
|
|
oBridgeInputElem.val(this._GetInputData());
|
|
},
|
|
// - Input zone
|
|
_EmptyInput: function() {
|
|
this._GetCKEditorInstance().setData('');
|
|
this._UpdateEditingVisualHint();
|
|
},
|
|
/**
|
|
* @returns {boolean} True if the input has no text
|
|
* @private
|
|
*/
|
|
_IsInputEmpty: function () {
|
|
let sCKEditorValue = this._GetInputData();
|
|
return sCKEditorValue === '';
|
|
},
|
|
_GetInputData: function () {
|
|
let oCKEditorInstance = this._GetCKEditorInstance()
|
|
return (oCKEditorInstance === undefined) ? '' : oCKEditorInstance.getData();
|
|
},
|
|
_GetExtraInputs: function() {
|
|
let aExtraInputs = {};
|
|
const aFormInputs = this.element.serializeArray();
|
|
// Iterate across all values that would be sent if we submit current form
|
|
for (const aExtraInput of aFormInputs) {
|
|
// If we don't already have a value with the same name, add it
|
|
// Otherwise we'll consider that we need to return this value as an array of values
|
|
if(aExtraInputs[aExtraInput.name] === undefined) {
|
|
aExtraInputs[aExtraInput.name] = aExtraInput.value;
|
|
}
|
|
else {
|
|
if(Array.isArray(aExtraInputs[aExtraInput.name])){
|
|
aExtraInputs[aExtraInput.name].push(aExtraInput.value);
|
|
}
|
|
else{
|
|
aExtraInputs[aExtraInput.name] = [aExtraInputs[aExtraInput.name], aExtraInput.value];
|
|
}
|
|
}
|
|
};
|
|
return aExtraInputs;
|
|
},
|
|
// - Main actions
|
|
_ShowMainActions: function() {
|
|
this.element.find(this.js_selectors.main_actions).removeClass(this.css_classes.is_hidden);
|
|
},
|
|
_HideMainActions: function() {
|
|
this.element.find(this.js_selectors.main_actions).addClass(this.css_classes.is_hidden);
|
|
},
|
|
_UpdateState: function() {
|
|
this._UpdateEditingVisualHint();
|
|
this._UpdateSubmitButtonState();
|
|
},
|
|
_UpdateSubmitButtonState: function() {
|
|
if (this._IsInputEmpty()) {
|
|
this._DisableSubmission();
|
|
} else {
|
|
this._EnableSubmission();
|
|
}
|
|
},
|
|
_UpdateEditingVisualHint: function () {
|
|
const sEvent = this._IsInputEmpty() ? 'emptied' : 'draft';
|
|
this.element.trigger(sEvent+'.caselog_entry_form.itop', {attribute_code: this.options.attribute_code});
|
|
}
|
|
});
|
|
}); |