// Copyright (C) 2010-2021 Combodo SARL // // 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 // along with iTop. If not, see // ID of the (hidden) form field used to store the JSON representation of the // object being edited in this page var sJsonFieldId = 'json_object'; // The memory representation of the object var oObj = {}; // Mapping between the fields of the form and the attribute of the current object // If aFieldsMap[2] contains 'foo' it means that oObj.foo corresponds to the field // of Id 'att_2' in the form var aFieldsMap = new Array; window.bInSubmit = false; // For handling form cancellation via OnBeforeUnload events // Update the whole object from the form and also update its // JSON (serialized) representation in the (hidden) field function UpdateObjectFromForm(aFieldsMap, oObj) { for(i=0; i iCurrentStep) { // Check the values when moving forward if (CheckFields('wizStep'+iCurrentStep, true)) { oCurrentStep.style.display = 'none'; ActivateStep(iNextStep); } } else { oCurrentStep.style.display = 'none'; ActivateStep(iNextStep); } } function ActivateStep(iTargetStep) { UpdateObjectFromForm(aFieldsMap, oObj); var oNextStep = document.getElementById('wizStep'+(iTargetStep)); window.location.href='#step'+iTargetStep; // If a handler for entering this step exists, call it if (typeof(this['OnEnterStep'+iTargetStep]) == 'function') { eval( 'OnEnterStep'+iTargetStep+'();'); } oNextStep.style.display = ''; G_iCurrentStep = iTargetStep; //$('#wizStep'+(iTargetStep)).block({ message: null }); } function OnUnload(sTransactionId, sObjClass, iObjKey, sToken) { if (!window.bInSubmit) { // If it's not a submit, then it's a "cancel" (Pressing the Cancel button, closing the window, using the back button...) var sUrl = GetAbsoluteUrlAppRoot()+'pages/ajax.render.php'; var oFormData = new FormData(); oFormData.append('operation', 'on_form_cancel'); oFormData.append('transaction_id', sTransactionId); oFormData.append('obj_class', sObjClass); oFormData.append('obj_key', iObjKey); oFormData.append('token', sToken); navigator.sendBeacon(sUrl, oFormData); } } function OnSubmit(sFormId) { if($('#'+sFormId).attr('data-form-state') === 'onsubmit') { return false; } $('#'+sFormId).attr('data-form-state','onsubmit'); window.bInSubmit=true; // This is a submit, make sure that when the page gets unloaded we don't cancel the action if ($('#'+sFormId).data('force_submit')) { return true; } var bResult = CheckFields(sFormId, true); if (!bResult) { window.bInSubmit = false; // Submit is/will be canceled $('#'+sFormId).attr('data-form-state', 'default'); } return bResult; } // Store the result of the form validation... there may be several forms per page, beware var oFormErrors = { err_form0: 0 }; function CheckFields(sFormId, bDisplayAlert) { // if some fields are in wait, no submit is allowed if ($('#'+sFormId+' .blockMsg').length>0) { alert(Dict.S('UI:Button:Wait')); return false; } $('#'+sFormId+' :submit').prop('disable', true); $('#'+sFormId+' :button[type=submit]').prop('disable', true); firstErrorId = ''; // The two 'fields' below will be updated when the 'validate' event is processed oFormErrors['err_'+sFormId] = 0; // Number of errors encountered when validating the form oFormErrors['input_'+sFormId] = null; // First 'input' with an error, to set the focus to it $('#'+sFormId+' :input').each( function() { // this is synchronous ! // each field should register this event to launch ValidateField() if needed validateEventResult = $(this).trigger('validate', sFormId); } ); if(oFormErrors['err_'+sFormId] > 0) { if (bDisplayAlert) { activateFirstTabWithError(sFormId); alert(Dict.S('UI:FillAllMandatoryFields')); } $('#'+sFormId+' :submit').prop('disable', false); $('#'+sFormId+' :button[type=submit]').prop('disable', false); if (oFormErrors['input_'+sFormId] != null) { $('#'+oFormErrors['input_'+sFormId]).focus(); } } return (oFormErrors['err_'+sFormId] == 0); // If no error, submit the form } function activateFirstTabWithError(sFormId) { var $form = $("#"+sFormId), $tabsContainer = $form.find(".ui-widget.ui-widget-content"), $tabs = $tabsContainer.find(".ui-tabs-panel"); $tabs.each(function (index, element) { var $fieldsWithError = $(element).find(".form_validation"); if ($fieldsWithError.length > 0 && ($tabsContainer.tabs("instance") !== undefined)) { $tabsContainer.tabs("option", "active", index); return false; } }); } function ReportFieldValidationStatus(sFieldId, sFormId, bValid, sExplain) { if (bValid) { // Visual feedback - none when it's Ok $('#field_'+sFieldId+' .ibo-input-wrapper').removeClass('is-error') $('#v_'+sFieldId).html(''); $('#'+sFieldId+'[data-validate*="dependencies"]').trigger('change.dependencies').removeAttr('data-validate'); } else { // Report the error... oFormErrors['err_'+sFormId]++; if (oFormErrors['input_'+sFormId] == null) { // Let's remember the first input with an error, so that we can put back the focus on it later oFormErrors['input_'+sFormId] = sFieldId; } if($('#field_'+sFieldId+' .ibo-input-wrapper').attr('data-validation') === 'untouched') { $('#field_'+sFieldId+' .ibo-input-wrapper').removeAttr('data-validation'); } else{ $('#field_'+sFieldId+' .ibo-input-wrapper').addClass('is-error'); } if ($('#v_'+sFieldId).text() == '') { $('#v_'+sFieldId).html(sExplain); } //Avoid replacing exisiting tooltip for periodically checked element (like CKeditor fields) if($('#v_'+sFieldId).tooltip( "instance" ) === undefined) { // Visual feedback $('#v_'+sFieldId).tooltip({ items: 'span', classes: { "ui-tooltip": "form_field_error" }, content: function() { return $(this).find('img').attr('data-tooltip'); // As opposed to the default 'content' handler, do not escape the contents of 'title' } }); } } } /** * To be launched on each field from normal event (click, change, ...) and 'validate' event for form submission. * Calls ReportFieldValidationStatus() to update global vars containing fields status * @param sFieldId * @param sPattern * @param bMandatory * @param sFormId * @param nullValue * @param originalValue * @returns {boolean} * @constructor */ function ValidateField(sFieldId, sPattern, bMandatory, sFormId, nullValue, originalValue) { var bValid = true; var sExplain = ''; if ($('#'+sFieldId).prop('disabled')) { bValid = true; // disabled fields are not checked } else { var currentVal = $('#'+sFieldId).val(); if (currentVal == '$$NULL$$') // Convention to indicate a non-valid value since it may have to be passed as text { bValid = false; } else if (bMandatory && (currentVal == nullValue)) { bValid = false; sExplain = Dict.S('UI:ValueMustBeSet'); } else if ((originalValue != undefined) && (currentVal == originalValue)) { bValid = false; if (originalValue == nullValue) { sExplain = Dict.S('UI:ValueMustBeSet'); } else { sExplain = Dict.S('UI:ValueMustBeChanged'); } } else if (currentVal == nullValue) { // An empty field is Ok... bValid = true; } else if (sPattern != '') { re = new RegExp(sPattern); //console.log('Validating field: '+sFieldId + ' current value: '+currentVal + ' pattern: '+sPattern ); bValid = re.test(currentVal); sExplain = Dict.S('UI:ValueInvalidFormat'); } } ReportFieldValidationStatus(sFieldId, sFormId, bValid, sExplain); //console.log('Form: '+sFormId+' Validating field: '+sFieldId + ' current value: '+currentVal+' pattern: '+sPattern+' result: '+bValid ); return true; // Do not stop propagation ?? } function ValidateCKEditField(sFieldId, sPattern, bMandatory, sFormId, nullValue, originalValue) { var bValid; var sExplain = ''; var sTextContent; if ($('#'+sFieldId).prop('disabled')) { bValid = true; // disabled fields are not checked } else { // Get the contents without the tags var oFormattedContents = $("#cke_"+sFieldId+" iframe"); if (oFormattedContents.length == 0) { var oSourceContents = $("#cke_"+sFieldId+" textarea.cke_source"); sTextContent = oSourceContents.val(); } else { sTextContent = oFormattedContents.contents().find("body").text(); if (sTextContent == '') { // No plain text, maybe there is just an image... var oImg = oFormattedContents.contents().find("body img"); if (oImg.length != 0) { sTextContent = 'image'; } } } // Get the original value without the tags var oFormattedOriginalContents = (originalValue !== undefined) ? $('
').html(originalValue) : undefined; var sTextOriginalContents = (oFormattedOriginalContents !== undefined) ? oFormattedOriginalContents.text() : undefined; if (bMandatory && (sTextContent == nullValue)) { bValid = false; sExplain = Dict.S('UI:ValueMustBeSet'); } else if ((sTextOriginalContents != undefined) && (sTextContent == sTextOriginalContents)) { bValid = false; if (sTextOriginalContents == nullValue) { sExplain = Dict.S('UI:ValueMustBeSet'); } else { // Note: value change check is not working well yet as the HTML to Text conversion is not exactly the same when done from the PHP value or the CKEditor value. sExplain = Dict.S('UI:ValueMustBeChanged'); } } else { bValid = true; } } ReportFieldValidationStatus(sFieldId, sFormId, bValid, sExplain); if ($('#'+sFieldId).data('timeout_validate') == undefined) { // We need to check periodically as CKEditor doesn't trigger our events. More details in UIHTMLEditorWidget::Display() @ line 92 var iTimeoutValidate = setInterval(function () { ValidateCKEditField(sFieldId, sPattern, bMandatory, sFormId, nullValue, originalValue); }, 500); $('#'+sFieldId).data('timeout_validate', iTimeoutValidate); } } function ResetPwd(id) { // Reset the values of the password fields $('#'+id).val('*****'); $('#'+id+'_confirm').val('*****'); // And reset the flag, to tell it that the password remains unchanged $('#'+id+'_changed').val(0); // Visual feedback, None when it's Ok $('#v_'+id).html(''); } // Called whenever the content of a one way encrypted password changes function PasswordFieldChanged(id) { // Set the flag, to tell that the password changed $('#'+id+'_changed').val(1); } // Special validation function for one way encrypted password fields function ValidatePasswordField(id, sFormId) { var bChanged = $('#'+id+'_changed').val(); if (bChanged) { if ($('#'+id).val() != $('#'+id+'_confirm').val()) { oFormErrors['err_'+sFormId]++; if (oFormErrors['input_'+sFormId] == null) { // Let's remember the first input with an error, so that we can put back the focus on it later oFormErrors['input_'+sFormId] = id; } // Visual feedback $('#v_'+id).html(Dict.S('UI:Component:Input:Password:DoesNotMatch')); $('#field_'+id +' .ibo-input-wrapper').addClass('is-error'); return false; } } $('#v_'+id).html(''); $('#field_'+id +' .ibo-input-wrapper').removeClass('is-error'); return true; } //Special validation function for case log fields, taking into account the history // to determine if the field is empty or not function ValidateCaseLogField(sFieldId, bMandatory, sFormId, nullValue, originalValue) { var bValid = true; var sExplain = ''; var sTextContent; if ($('#'+sFieldId).prop('disabled')) { bValid = true; // disabled fields are not checked } else { // Get the contents (with tags) // Note: For CaseLog we can't retrieve the formatted contents from CKEditor (unlike in ValidateCKEditorField() method) because of the place holder. sTextContent = $('#' + sFieldId).val(); var count = $('#'+sFieldId+'_count').val(); if (bMandatory && (count == 0) && (sTextContent == nullValue)) { // No previous entry and no content typed bValid = false; sExplain = Dict.S('UI:ValueMustBeSet'); } else if ((originalValue != undefined) && (sTextContent == originalValue)) { bValid = false; sExplain = Dict.S('UI:ValueMustBeChanged'); } } ReportFieldValidationStatus(sFieldId, sFormId, bValid, '' /* sExplain */); // We need to check periodically as CKEditor doesn't trigger our events. More details in UIHTMLEditorWidget::Display() @ line 92 setTimeout(function(){ValidateCaseLogField(sFieldId, bMandatory, sFormId, nullValue, originalValue);}, 500); } // Validate the inputs depending on the current setting function ValidateRedundancySettings(sFieldId, sFormId) { var bValid = true; var sExplain = ''; $('#'+sFieldId+' :input[type="radio"]:checked').parent().find(':input[type="string"]').each(function (){ var sValue = $(this).val().trim(); if (sValue == '') { bValid = false; sExplain = Dict.S('UI:ValueMustBeSet'); } else { // There is something... check if it is a number re = new RegExp('^[0-9]+$'); bValid = re.test(sValue); if (bValid) { var iValue = parseInt(sValue , 10); if ($(this).hasClass('redundancy-min-up-percent')) { // A percentage if ((iValue < 0) || (iValue > 100)) { bValid = false; } } else if ($(this).hasClass('redundancy-min-up-count')) { // A count if (iValue < 0) { bValid = false; } } } if (!bValid) { sExplain = Dict.S('UI:ValueInvalidFormat'); } } }); ReportFieldValidationStatus(sFieldId, sFormId, bValid, sExplain); return bValid; } //Special validation function for custom fields function ValidateCustomFields(sFieldId, sFormId) { var oFieldSet = $('#'+sFieldId+'_console_form').console_form_handler('option', 'field_set'); bValid = oFieldSet.triggerHandler('validate'); ReportFieldValidationStatus(sFieldId, sFormId, bValid, ''); return bValid; } // Manage a 'duration' field function UpdateDuration(iId) { var iDays = parseInt($('#'+iId+'_d').val(), 10); var iHours = parseInt($('#'+iId+'_h').val(), 10); var iMinutes = parseInt($('#'+iId+'_m').val(), 10); var iSeconds = parseInt($('#'+iId+'_s').val(), 10); var iDuration = (((iDays*24)+ iHours)*60+ iMinutes)*60 + iSeconds; $('#'+iId).val(iDuration); $('#'+iId).trigger('change'); return true; } // Called when filling an autocomplete field //deprecated in 2.8 function OnAutoComplete(id, event, data, formatted) { if (data) { // A valid match was found: data[0] => label, data[1] => value if (data[1] != $('#'+id).val()) { $('#'+id).val(data[1]); $('#'+id).trigger('change'); $('#'+id).trigger('extkeychange'); } } else { if ($('#label_'+id).val() == '') { $('#'+id).val(''); // Empty value } else { $('#'+id).val('$$NULL$$'); // Convention: not a valid value } $('#'+id).trigger('change'); } }