Merge remote-tracking branch 'origin/develop' into feature/4157

# Conflicts:
#	application/ui.linkswidget.class.inc.php
#	core/dbobject.class.php
This commit is contained in:
acognet
2022-09-29 17:49:10 +02:00
1206 changed files with 80990 additions and 39417 deletions

View File

@@ -24,8 +24,15 @@ if ((CKEDITOR !== undefined) && (CKEDITOR.plugins.registered['disabler'] === und
// Rewrite the CKEditor Mentions plugin regexp to make it suitable for all Unicode alphabets.
if (CKEDITOR !== undefined && CKEDITOR.plugins.registered['mentions']) {
// from https://github.com/ckeditor/ckeditor4/blob/a3786007fb979d7d7bff3d10c34a2d422935baed/plugins/mentions/plugin.js#L147
// From https://github.com/ckeditor/ckeditor4/blob/a3786007fb979d7d7bff3d10c34a2d422935baed/plugins/mentions/plugin.js#L147
function createPattern(marker, minChars) {
// Escape marker if it's a regex token
// https://github.com/tc39/proposal-regex-escaping/blob/main/EscapedChars.md#syntaxcharacter-proposal
const regexTokens = ['^', '$', '\\', '.', '*', '+', '?', '(', ')', '[', ']', '{', '}', '|'];
if (regexTokens.indexOf(marker) >= 0) {
marker = '\\' + marker;
}
let pattern = marker + '[\\p{L}\\p{N}_-]';
if ( minChars ) {
pattern += '{' + minChars + ',}';

View File

@@ -35,8 +35,7 @@ $(function()
{
var me = this;
this.element
.append('<div class="last-error"></div>')
this.element.append('<div class="last-error"></div>')
.addClass('console_form_handler');
this.options.oWizardHelper = window[this.options.wizard_helper_var_name];
@@ -48,13 +47,13 @@ $(function()
// revert other modifications here
_destroy: function()
{
this.element
.removeClass('console_form_handler');
this.element.removeClass('console_form_handler');
this._super();
},
_onUpdateFields: function(event, data)
{
var me = this;
me._updatePreviousValues();
var sFormPath = data.form_path;
var sUpdateUrl = GetAbsoluteUrlAppRoot()+'pages/ajax.render.php';
@@ -64,7 +63,6 @@ $(function()
{
operation: 'custom_fields_update',
attcode: this.options.custom_field_attcode,
//current_values: this.getCurrentValues(),
requested_fields: data.requested_fields,
form_path: sFormPath,
json_obj: this.options.oWizardHelper.UpdateWizardToJSON()
@@ -85,6 +83,7 @@ $(function()
me.element.find('.last-error').text(data.error);
}
me._onUpdateAlways(data, sFormPath);
me.element.find('[data-field-id="previous_values"]').find('input[type="hidden"]').val('{}');
});
},
// On initialization or update

View File

@@ -78,4 +78,30 @@ function getMultipleSelectionParams(listId)
});
return oRes;
}
}
/**
* Return column JSON declaration for row actions.
* Could be part of column or columnDefs declaration of datatable.js.
*
* @param sTableId
* @param iColumnTargetIndex
* @returns {*}
* @since 3.1.0
*/
function getRowActionsColumnDefinition(sTableId, iColumnTargetIndex = -1)
{
let aColumn = {
type: "html",
orderable: false,
render: function ( data, type, row, meta ) {
return $(`#${sTableId}_actions_buttons_template`).html();
}
};
if (iColumnTargetIndex !== -1) {
aColumn['targets'] = iColumnTargetIndex;
}
return aColumn;
}

View File

@@ -92,12 +92,27 @@ $(function()
{
return this.options.field_set.triggerHandler('get_current_values');
},
/**
* @private
* @since 3.0.2 3.1.0
*/
_updatePreviousValues: function()
{
let me = this;
if(this.element.find('[data-attribute-previous-value]').length > 0) {
let aPreviousValues = {};
$(this.element.find('[data-attribute-previous-value]')).each(function (iIdx, oElem) {
aPreviousValues[$(oElem).data('field-id')] = $(oElem).data('attribute-previous-value');
});
me.element.find('[data-field-id="previous_values"]').find('input[type="hidden"]').val(JSON.stringify(aPreviousValues));
}
},
// Events callback
// - Update fields depending on the update ones
_onUpdateFields: function(oEvent, oData)
{
var me = this;
me._updatePreviousValues();
var sFormPath = oData.form_path;
// Data checks

View File

@@ -156,7 +156,7 @@ $(function()
});
me._updateExtraTabsList();
}, {
root: $('.ibo-tab-container--tabs-list')[0],
root: this.element.find(this.js_selectors.tabs_list)[0],
threshold: [0.9] // N°4783 Should be completely visible, but lowering the threshold prevents a bug in the JS Observer API when the window is zoomed in/out, in which case all items respond as being hidden even when they are not.
});
this.element.find(this.js_selectors.tab_header).each(function(){
@@ -262,6 +262,16 @@ $(function()
// Prevent anchor default behaviour
oEvent.preventDefault();
// Compute list position
// Note: Arbitrary +6px for the position as we don't want it to be exactly against the toggler
let fTopOffset = this.element.find(this.js_selectors.extra_tabs_list_toggler).offset().top + this.element.find(this.js_selectors.extra_tabs_list_toggler).outerHeight() + 6;
// We need to compute position from the right side of the screen because at this time the list isn't visible and we can't know its width, so we can't position it regarding the left side of the screen
// Note: We use window.innerWidth instead of outerWidth as we need the width of the actual viewport, not the OS browser window
let fRightOffset = window.innerWidth - this.element.find(this.js_selectors.extra_tabs_list_toggler).offset().left - this.element.find(this.js_selectors.extra_tabs_list_toggler).outerWidth();
this.element.find(this.js_selectors.extra_tabs_list)
.css('top', fTopOffset + 'px')
.css('right', fRightOffset + 'px');
// TODO 3.0.0: Should/could we use a popover menu instead here?
this.element.find(this.js_selectors.extra_tabs_list).toggleClass(this.css_classes.is_hidden);
},

View File

@@ -19,7 +19,7 @@
*
* @since 3.0.0 Add iMaxAddedId parameter
*/
function LinksWidget(id, sClass, sAttCode, iInputId, sSuffix, bDuplicates, oWizHelper, sExtKeyToRemote, bDoSearch, iMaxAddedId = 0) {
function LinksWidget(id, sClass, sAttCode, iInputId, sSuffix, bDuplicates, oWizHelper, sExtKeyToRemote, bDoSearch, iMaxAddedId = 0, aRemoved = []) {
this.id = id;
this.iInputId = iInputId;
this.sClass = sClass;
@@ -30,7 +30,7 @@ function LinksWidget(id, sClass, sAttCode, iInputId, sSuffix, bDuplicates, oWizH
this.sExtKeyToRemote = sExtKeyToRemote;
this.iMaxAddedId = iMaxAddedId;
this.aAdded = [];
this.aRemoved = [];
this.aRemoved = aRemoved;
this.aModified = {};
this.bDoSearch = bDoSearch; // false if the search is not launched
let me = this;

View File

@@ -428,7 +428,6 @@ $(function()
bSearchMode: 'true',
sOutputFormat: 'json',
operation: 'ac_extkey',
sAutocompleteOperation: 'equals_start_with'
}
)
.done(function(oResponse, sStatus, oXHR){
@@ -441,28 +440,7 @@ $(function()
return;
}
oACXHR = $.post(
AddAppContext(GetAbsoluteUrlAppRoot()+'pages/ajax.render.php'),
{
sTargetClass: me.options.field.target_class,
sFilter: 'SELECT ' + me.options.field.target_class,
q: sQuery,
bSearchMode: 'true',
sOutputFormat: 'json',
operation: 'ac_extkey',
sAutocompleteOperation: 'contains'
}
)
.done(function(oResponseContains, sStatus, oXHR){
//filter duplicates
$.each(oResponse, function(index, value) {
delete oResponseContains[index];
});
me._onACSearchContainsSuccess(oResponseContains);
});
me._onACSearchContainsSuccess(oResponse);
})
.fail(function(oResponse, sStatus, oXHR){ me._onACSearchFail(oResponse, sStatus); })
.always(function(oResponse, sStatus, oXHR){
@@ -701,44 +679,16 @@ $(function()
}
}
this._setACWaitTempHint();
},
// Autocomplete CONTAINS callbacks
_onACSearchContainsSuccess: function(oResponse)
{
if(typeof oResponse !== 'object')
{
this._emptyACTempHint();
return false;
}
var oDynamicListElem = this.element.find('.sfc_opc_mc_items_dynamic');
if(Object.keys(oResponse).length > 0)
{
// Note: Response is indexed by labels from server so the JSON is always ordered on decoding.
for(var skey in oResponse)
{
var sValue = oResponse[skey].value;
var sLabel = oResponse[skey].label;
// Note: We don't use the _isSelectedValue() method here as it only returns "applied" values; at this moment will could have a checked value that is not among selected (me.options.values) yet. The result would be an hidden item from the AC results.
var bSelected = (this.element.find(this._getSelectedValuesWrapperSelector() + ' .sfc_opc_mc_item[data-value-code="' + sValue + '"]').length > 0);
var bInitChecked = bSelected;
var bInitHidden = bSelected;
var oValueElem = this._makeListItemElement(sLabel, sValue, bInitChecked, bInitHidden,oResponse[skey].obsolescence_flag,oResponse[skey].additional_field);
oValueElem.appendTo(oDynamicListElem);
}
}
if (oDynamicListElem.find('.sfc_opc_mc_item').length == 0)
{
this._setACNoResultHint();
}
else
{
this._emptyACTempHint();
if (oDynamicListElem.find('.sfc_opc_mc_item').length == 0)
{
this._setACNoResultHint();
}
},
else
{
this._emptyACTempHint();
}
},
_onACSearchFail: function(oResponse, sStatus)
{
if(sStatus !== 'abort')

View File

@@ -389,11 +389,13 @@ function ExportListDlg(sOQL, sDataTableId, sFormat, sDlgTitle) {
var oColumns = $('#'+sDataTableName).DataTable().ajax.params()['columns'];
for (var j in oColumns) {
if (oColumns[j]['data']) {
var sCode = oColumns[j]['data'].split("/");
if (sCode[1] == '_key_') {
sCode[1] = 'id';
if (oColumns[j]['data']!='id') {
var sCode = oColumns[j]['data'].split("/");
if (sCode[1] == '_key_') {
sCode[1] = 'id';
}
aFields.push(sCode[0]+'.'+sCode[1]);
}
aFields.push(sCode[0]+'.'+sCode[1]);
} else {
for (var k in oColumns[j]) {
if (oColumns[j][k].checked) {
@@ -807,8 +809,10 @@ const CombodoTooltip = {
oOptions['content'] = sContent;
// Interaction (selection, click, ...) have to be enabled manually
// Important: When set to true, if "data-tooltip-append-to" is not specified, tooltip will be append to the parent element instead of the body
const bInteractive = oElem.attr('data-tooltip-interaction-enabled') === 'true';
// Important: When set to true, if "data-tooltip-append-to" is not specified, tooltip will be appended to the parent element instead of the body
// Note: Defaults to true if it contains hyperlink
let bDefaultInteractive = (bEnableHTML && sContent.indexOf("<a ") > -1)
const bInteractive = oElem.attr('data-tooltip-interaction-enabled') !== undefined ? oElem.attr('data-tooltip-interaction-enabled') === 'true' : bDefaultInteractive;
oOptions['interactive'] = bInteractive;
// Element to append the tooltip to

View File

@@ -242,8 +242,8 @@ function WizardHelper(sClass, sFormPrefix, sState, sInitialState, sStimulus) {
this.ResetQuery();
this.UpdateWizard();
var fieldForm=null;
while (index < aFieldNames.length)
var fieldForm = null;
while (index < aFieldNames.length )
{
sAttCode = aFieldNames[index];
sFieldId = this.GetFieldId(sAttCode);
@@ -255,13 +255,13 @@ function WizardHelper(sClass, sFormPrefix, sState, sInitialState, sStimulus) {
message: '',
overlayCSS: {backgroundColor: '#f1f1f1', opacity: 0.3}
});
fieldForm=$('#field_' + sFieldId).closest('form');
fieldForm = $('#field_' + sFieldId).closest('form');
this.RequestAllowedValues(sAttCode);
}
index++;
}
if($('.blockUI').length > 0) {
if ((fieldForm !== null) && ($('.blockUI').length > 0)) {
fieldForm.find('button[type=submit]:not(:disabled)').prop("disabled", true).addClass('disabledDuringFieldLoading');
}