From 4b298360f12a4fcfbcdeb7c8b4ba8679910101c4 Mon Sep 17 00:00:00 2001 From: Jerome Bonini Date: Wed, 23 Oct 2019 09:45:03 +0200 Subject: [PATCH 01/14] - newmipsType nromalized for email type to always be 'email' - fix media email field selector --- structure/structure_data_field.js | 1 + structure/template/utils/status_helper.js | 31 ++++++++++------------- 2 files changed, 14 insertions(+), 18 deletions(-) diff --git a/structure/structure_data_field.js b/structure/structure_data_field.js index 33b0c3d67..666e0707e 100755 --- a/structure/structure_data_field.js +++ b/structure/structure_data_field.js @@ -630,6 +630,7 @@ exports.setupDataField = function (attr, callback) { case "mel" : typeForModel = "STRING"; typeForDatalist = "email"; + type_data_field = "email"; break; case "phone" : case "tel" : diff --git a/structure/template/utils/status_helper.js b/structure/template/utils/status_helper.js index e984b640a..21dbe01e4 100644 --- a/structure/template/utils/status_helper.js +++ b/structure/template/utils/status_helper.js @@ -56,7 +56,7 @@ module.exports = { fullEntityFieldTree: function (entity, alias = entity) { let genealogy = []; // Create inner function to use genealogy globaly - function loadTree(entity, alias) { + function loadTree(entity, alias, depth = 0) { let fieldTree = { entity: entity, alias: alias, @@ -66,9 +66,10 @@ module.exports = { file_fields: [], children: [] } + let entityFields, entityAssociations; try { - let entityFields = JSON.parse(fs.readFileSync(__dirname+'/../models/attributes/'+entity+'.json')); - let entityAssociations = JSON.parse(fs.readFileSync(__dirname+'/../models/options/'+entity+'.json')); + entityFields = JSON.parse(fs.readFileSync(__dirname+'/../models/attributes/'+entity+'.json')); + entityAssociations = JSON.parse(fs.readFileSync(__dirname+'/../models/options/'+entity+'.json')); } catch (e) { console.error(e); return fieldTree; @@ -86,35 +87,29 @@ module.exports = { } // Check if current entity has already been built in this branch of the tree to avoid infinite loop - let foundGenealogy = genealogy.filter(x => x.entity == entity); - // Entity already proceeded in an other relation - if(foundGenealogy.length != 0){ - // Check for the better depth, if deeper then remove it to keep the closer one - if(foundGenealogy[0].depth > depth) - genealogy = genealogy.filter(x => x.entity != entity); // Remove old one - else - return fieldTree; - } + for (const [idx, genealogyBranch] of genealogy.entries()) + if (genealogyBranch.entity == entity) { + // Keep smallest depth + if (genealogyBranch.depth > depth) + genealogy.splice(idx, 1); + else + return fieldTree; + } genealogy.push({ entity: entity, depth: depth }); - let initalDepth = depth; - // Building children array for (let i = 0; i < entityAssociations.length; i++){ // Do not include history & status table in field list if(entityAssociations[i].target.indexOf("e_history_e_") == -1 && entityAssociations[i].target.indexOf("e_status") == -1){ - depth++; - fieldTree.children.push(loadTree(entityAssociations[i].target, entityAssociations[i].as)); + fieldTree.children.push(loadTree(entityAssociations[i].target, entityAssociations[i].as, depth+1)); } } - depth = initalDepth; - return fieldTree; } return loadTree(entity, alias); From c077323ccaa90ef126fadd30920063111b25314d Mon Sep 17 00:00:00 2001 From: Syno Date: Wed, 23 Oct 2019 10:38:33 +0200 Subject: [PATCH 02/14] Fix crash when deleting file from dropzone in update_form, fix file display on show_field when no file --- structure/structure_data_field.js | 14 ++++--- structure/template/locales/en-EN.json | 1 + structure/template/locales/fr-FR.json | 1 + structure/template/public/js/newmips.js | 2 +- structure/template/routes/default.js | 52 +++++++++++-------------- 5 files changed, 34 insertions(+), 36 deletions(-) diff --git a/structure/structure_data_field.js b/structure/structure_data_field.js index 33b0c3d67..6085c9151 100755 --- a/structure/structure_data_field.js +++ b/structure/structure_data_field.js @@ -397,10 +397,14 @@ function getFieldHtml(type, nameDataField, nameDataEntity, readOnly, file, value str += " \n"; } else { str += "
\n"; - str += "
\n"; - str += " \n"; - str += "
\n"; - str += " {" + value2 + "|filename}\n"; + str += " {?" + value2 + "}\n"; + str += "
\n"; + str += " \n"; + str += "
\n"; + str += " {" + value2 + "|filename}\n"; + str += " {:else}\n"; + str += " {#__ key=\"message.empty_file\" /}\n"; + str += " {/" + value2 + "}\n"; str += "
\n"; } break; @@ -413,7 +417,7 @@ function getFieldHtml(type, nameDataField, nameDataEntity, readOnly, file, value str += " \n"; } else { str += "
\n"; - str += "  + value + \n"; + str += "  + value + \n"; str += "
\n"; } break; diff --git a/structure/template/locales/en-EN.json b/structure/template/locales/en-EN.json index 62269af5c..787aebd32 100755 --- a/structure/template/locales/en-EN.json +++ b/structure/template/locales/en-EN.json @@ -173,6 +173,7 @@ "question": "Are you sure you want to delete this entity ?" }, "empty": "No data to display", + "empty_file": "No file", "unique": "This field must be unique:", "relatedtomanycheckbox_required": "A field requires you to check at least one checkbox." }, diff --git a/structure/template/locales/fr-FR.json b/structure/template/locales/fr-FR.json index 383e542aa..ff205ca9f 100755 --- a/structure/template/locales/fr-FR.json +++ b/structure/template/locales/fr-FR.json @@ -173,6 +173,7 @@ "question": "Êtes-vous sûr de vouloir supprimer cette entité ?" }, "empty": "Aucune donnée à afficher", + "empty_file": "Aucun fichier", "unique": "Ce champ doit être unique:", "relatedtomanycheckbox_required": "Un champ necessite d'avoir au moins une case cochée." }, diff --git a/structure/template/public/js/newmips.js b/structure/template/public/js/newmips.js index 85c37966b..ee207cf2d 100755 --- a/structure/template/public/js/newmips.js +++ b/structure/template/public/js/newmips.js @@ -425,7 +425,7 @@ function initForm(context) { if (!confirm('Êtes-vous sûr de vouloir supprimer ce fichier ?')) return false; $.ajax({ - url: '/default/delete-file-ajax', + url: '/default/delete_file', type: 'post', data: { dataEntity: that.attr("data-entity"), diff --git a/structure/template/routes/default.js b/structure/template/routes/default.js index e12601e37..67bed6d2b 100755 --- a/structure/template/routes/default.js +++ b/structure/template/routes/default.js @@ -345,37 +345,29 @@ router.get('/download', block_access.isLoggedIn, function (req, res) { } }); -router.post('/delete-file-ajax', block_access.isLoggedIn, function (req, res) { - let entity = req.body.dataEntity; - let dataStorage = req.body.dataStorage; - let filename = req.body.filename; - if (entity && dataStorage && filename) { - let partOfFilepath = filename.split('-'); - if (partOfFilepath.length) { - let base = partOfFilepath[0]; - let completeFilePath = globalConfig.localstorage + entity + '/' + base + '/' + filename; - // thumbnail file to delete - let completeThumbnailPath = globalConfig.localstorage + globalConfig.thumbnail.folder + entity + '/' + base + '/' + filename; - fs.unlink(completeFilePath, function (err) { - if (!err) { - res.status(200).json({ message: 'message.delete.success'}); - fs.unlink(completeThumbnailPath, function (err) { - if (err) - console.error(err); - }); - } else { - req.session.toastr.push({level: 'error', message: "Internal error"}); - res.status(500).json({message: ''}); - } - }); - } else { - req.session.toastr.push({level: 'error', message: "File syntax not valid"}); - res.status(404).json({message: ''}); - } +router.post('/delete_file', block_access.isLoggedIn, function (req, res) { + try { + let entity = req.body.dataEntity; + let filename = req.body.filename; + let cleanFilename = filename.substring(16); + let folderName = filename.split("-")[0]; + let filePath = globalConfig.localstorage + entity + '/' + folderName + '/' + filename; + + if (!block_access.entityAccess(req.session.passport.user.r_group, entity.substring(2))) + throw new Error("403 - Access forbidden"); - } else { - req.session.toastr.push({level: 'error', message: "File not found"}); - res.status(400).json({message: 'Request parameters must be set'}); + if (!fs.existsSync(filePath)) + throw new Error("404 - File not found: " + filePath); + + fs.unlinkSync(filePath); + res.status(200).send(true); + } catch (err) { + console.error(err); + req.session.toastr.push({ + level: 'error', + message: "error.500.file" + }); + res.status(500).send(err); } }); From b17899ad9d96d46a446d261dda4f488f49f91bfb Mon Sep 17 00:00:00 2001 From: Syno Date: Wed, 23 Oct 2019 11:30:08 +0200 Subject: [PATCH 03/14] Fix gitlab user in session when resetting generator password --- routes/application.js | 2 +- routes/routes.js | 13 +++++++++---- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/routes/application.js b/routes/application.js index e87e5539c..ddcacf6a2 100755 --- a/routes/application.js +++ b/routes/application.js @@ -700,7 +700,7 @@ router.get('/list', block_access.isLoggedIn, function(req, res) { // Get user project for clone url generation let gitlabProjects = []; - if(gitlabConf.doGit) + if(gitlabConf.doGit && req.session.gitlab && req.session.gitlab.user) gitlabProjects = await gitlab.getAllProjects(req.session.gitlab.user.id); for (var i = 0; i < projects.length; i++) { diff --git a/routes/routes.js b/routes/routes.js index fc78da93c..5b9d50a7f 100755 --- a/routes/routes.js +++ b/routes/routes.js @@ -352,9 +352,10 @@ router.post('/reset_password_form', block_access.loginAccess, function(req, res) } }); + let gitlabUser = null; // Update Gitlab password if(gitlabConf.doGit){ - let gitlabUser = await gitlab.getUser(email_user); + gitlabUser = await gitlab.getUser(email_user); if(!gitlabUser) console.warn('Cannot update gitlab user password, user not found.'); @@ -373,10 +374,11 @@ router.post('/reset_password_form', block_access.loginAccess, function(req, res) } }); - return connectedUser; + return {connectedUser, gitlabUser}; - })().then(connectedUser => { - req.login(connectedUser, err => { + })().then(infos => { + + req.login(infos.connectedUser, err => { if (err) { console.error(err); req.session.toastr = [{ @@ -385,6 +387,9 @@ router.post('/reset_password_form', block_access.loginAccess, function(req, res) }]; res.redirect('/login'); } else { + req.session.gitlab = { + user: infos.gitlabUser + }; req.session.toastr = [{ message: "login.passwordReset", level: "success" From 39b300435f8469a0eba64a709226e087b147786d Mon Sep 17 00:00:00 2001 From: Syno Date: Wed, 23 Oct 2019 13:58:16 +0200 Subject: [PATCH 04/14] Remove uuid from type file and type image --- .../public/js/Newmips/dataTableBuilder.js | 3 +++ structure/template/routes/default.js | 15 +++++++++++++++ structure/template/utils/dust_fn.js | 9 +++++++-- 3 files changed, 25 insertions(+), 2 deletions(-) diff --git a/structure/template/public/js/Newmips/dataTableBuilder.js b/structure/template/public/js/Newmips/dataTableBuilder.js index 30c8672b7..4d2e8bbd6 100644 --- a/structure/template/public/js/Newmips/dataTableBuilder.js +++ b/structure/template/public/js/Newmips/dataTableBuilder.js @@ -484,6 +484,9 @@ function init_datatable(tableID, doPagination, context) { // Get current entity by splitting current table id var currentEntity = tableID.split("#table_")[1]; var justFilename = cellValue.replace(cellValue.split("_")[0], "").substring(1); + // Remove uuid + if(justFilename[32] == '_') + justFilename = justFilename.substring(33); cellValue = ''+justFilename+''; } else cellValue = ''; diff --git a/structure/template/routes/default.js b/structure/template/routes/default.js index 67bed6d2b..8ac7a7b14 100755 --- a/structure/template/routes/default.js +++ b/structure/template/routes/default.js @@ -294,6 +294,11 @@ router.get('/get_picture', block_access.isLoggedIn, function (req, res) { let entity = req.query.entity; let filename = req.query.src; let cleanFilename = filename.substring(16); + + // Remove uuid + if(cleanFilename[32] == '_') + cleanFilename = cleanFilename.substring(33); + let folderName = filename.split("-")[0]; let filePath = globalConfig.localstorage + entity + '/' + folderName + '/' + filename; @@ -322,6 +327,11 @@ router.get('/download', block_access.isLoggedIn, function (req, res) { let entity = req.query.entity; let filename = req.query.f; let cleanFilename = filename.substring(16); + + // Remove uuid + if(cleanFilename[32] == '_') + cleanFilename = cleanFilename.substring(33); + let folderName = filename.split("-")[0]; let filePath = globalConfig.localstorage + entity + '/' + folderName + '/' + filename; @@ -350,6 +360,11 @@ router.post('/delete_file', block_access.isLoggedIn, function (req, res) { let entity = req.body.dataEntity; let filename = req.body.filename; let cleanFilename = filename.substring(16); + + // Remove uuid + if(cleanFilename[32] == '_') + cleanFilename = cleanFilename.substring(33); + let folderName = filename.split("-")[0]; let filePath = globalConfig.localstorage + entity + '/' + folderName + '/' + filename; diff --git a/structure/template/utils/dust_fn.js b/structure/template/utils/dust_fn.js index 037c81884..eeab289a0 100644 --- a/structure/template/utils/dust_fn.js +++ b/structure/template/utils/dust_fn.js @@ -144,8 +144,13 @@ module.exports = { dust.filters.filename = function(value) { // Remove datetime part from filename display - if (moment(value.substring(0,16), 'YYYYMMDD-HHmmss_').isValid() && value != "" && value.length > 16) - return value.substring(16); + if (moment(value.substring(0, 16), 'YYYYMMDD-HHmmss_').isValid() && value != "" && value.length > 16) + value = value.substring(16); + + // Remove uuid + if(value[32] == '_') + value = value.substring(33); + return value; }; From ba81266e8f38ec4501903799a4f35cf4c75c8061 Mon Sep 17 00:00:00 2001 From: Jerome Bonini Date: Wed, 23 Oct 2019 16:59:47 +0200 Subject: [PATCH 05/14] - letiable replaced by variable in completion --- services/bot.js | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/services/bot.js b/services/bot.js index 8998683a6..f5fc5a4ba 100644 --- a/services/bot.js +++ b/services/bot.js @@ -2814,29 +2814,29 @@ exports.complete = function (instruction) { let answer = " "; let valid = true; - let letiable = false; + let variable = false; while ((m < l) && (k < n) && (valid)) { // Check if words are the same, goto next word if (template[k] == "(.*)" || template[k] == instr[m]) { - letiable = false; + variable = false; k++; } else { // Check if beginning of word are the same let sublen = instr[m].length; if (template[k].substring(0, sublen) == instr[m]) { // Do not increment k, we are still on keyword - letiable = false; + variable = false; } else { - // If we parse the letiable value + // If we parse the variable value if (template[k] == "(.*)") { // Check next word if (template[k + 1]) { k++; - letiable = true; + variable = true; } } else { - // If we are not parsing a letiable, it means template is not appropriate => Exit - if (!letiable) + // If we are not parsing a variable, it means template is not appropriate => Exit + if (!variable) valid = false; } } @@ -2857,12 +2857,12 @@ exports.complete = function (instruction) { else { if (template[k - 1] == "type") answer = answer + "[type] "; - // Return [letiable] to explain this is something dynamic + // Return [variable] to explain this is something dynamic else - answer = answer + "[letiable] "; + answer = answer + "[variable] "; - // If first loop on letiable, we need to display possible end of instruction - // Else, it means we have keyword at the beginning of suggestion, so we cut on letiable step + // If first loop on variable, we need to display possible end of instruction + // Else, it means we have keyword at the beginning of suggestion, so we cut on variable step if (!firstLoop) found = true; } From d8b74b6a1a60c8f7b9585d199fa1e9e5cfb08158 Mon Sep 17 00:00:00 2001 From: Jerome Bonini Date: Mon, 28 Oct 2019 11:51:48 +0100 Subject: [PATCH 06/14] - fix media variable selects construction and sorting --- .../status/models/e_media_notification.js | 4 +- structure/template/utils/status_helper.js | 67 ++++++++++--------- 2 files changed, 38 insertions(+), 33 deletions(-) diff --git a/structure/pieces/component/status/models/e_media_notification.js b/structure/pieces/component/status/models/e_media_notification.js index 01967e963..354ae8bbe 100644 --- a/structure/pieces/component/status/models/e_media_notification.js +++ b/structure/pieces/component/status/models/e_media_notification.js @@ -147,7 +147,7 @@ module.exports = (sequelize, DataTypes) => { } getGroupAndUserID().then(function(targetIds) { - var entityUrl; + var entityUrl, notificationObj; try { try { // Build show url of targeted entity @@ -161,7 +161,7 @@ module.exports = (sequelize, DataTypes) => { // Will redirect to current page entityUrl = '#'; } - var notificationObj = { + notificationObj = { f_color: self.f_color, f_icon: insertVariablesValue('f_icon'), f_title: insertVariablesValue('f_title'), diff --git a/structure/template/utils/status_helper.js b/structure/template/utils/status_helper.js index 21dbe01e4..dda48d46b 100644 --- a/structure/template/utils/status_helper.js +++ b/structure/template/utils/status_helper.js @@ -86,6 +86,7 @@ module.exports = { fieldTree.fields.push(field); } + // Check if current entity has already been built in this branch of the tree to avoid infinite loop for (const [idx, genealogyBranch] of genealogy.entries()) if (genealogyBranch.entity == entity) { @@ -104,17 +105,14 @@ module.exports = { // Building children array for (let i = 0; i < entityAssociations.length; i++){ // Do not include history & status table in field list - if(entityAssociations[i].target.indexOf("e_history_e_") == -1 - && entityAssociations[i].target.indexOf("e_status") == -1){ + if(entityAssociations[i].target.indexOf("e_history_e_") == -1 && entityAssociations[i].target.indexOf("e_status") == -1 && entityAssociations[i].structureType !== 'auto_generate') fieldTree.children.push(loadTree(entityAssociations[i].target, entityAssociations[i].as, depth+1)); - } } return fieldTree; } - return loadTree(entity, alias); - }, - // Build sequelize formated include object from tree + return loadTree(entity, alias) + }, // Build sequelize formated include object from tree buildIncludeFromTree: function(entityTree) { var includes = []; for (var i = 0; entityTree.children && i < entityTree.children.length; i++) { @@ -148,14 +146,12 @@ module.exports = { dive(entityTree); return userList; }, - // Build array of fields for media sms/notification/email insertion + entityFieldForSelect: function(entityTree, lang) { + var __ = language(lang).__; + var separator = ' > '; + var options = []; + function dive(obj, codename, parent, parentTraduction = "") { + var traduction; + // Top level. Entity traduction Ex: 'Ticket' + if (!parent) + traduction = __('entity.'+obj.entity+'.label_entity'); + // Child level. Parent traduction with child entity alias Ex: 'Ticket > Participants' OR 'Ticket > Participants > Adresse' + else + traduction = parentTraduction + separator + __('entity.'+parent.entity+'.'+obj.alias); - // Building field array - for (let field in entityFields) { - if (entityFields[field].newmipsType == "email") - fieldTree.email_fields.push(field); - if (entityFields[field].newmipsType == "phone") - fieldTree.phone_fields.push(field); - if (entityFields[field].newmipsType == "file" || entityFields[field].newmipsType == "picture") - fieldTree.file_fields.push(field); - fieldTree.fields.push(field); + for (var j = 0; j < obj.fields.length; j++) { + if (obj.fields[j].indexOf('f_') != 0) + continue; + options.push({ + codename: !codename ? obj.fields[j] : codename+'.'+obj.fields[j], + traduction: traduction + separator + __('entity.'+obj.entity+'.'+obj.fields[j]), // Append field to traduction Ex: 'Ticket > Participants > Adresse > Ville' + target: obj.entity, + isEmail: obj.email_fields.indexOf(obj.fields[j]) != -1 ? true : false, + isPhone: obj.phone_fields.indexOf(obj.fields[j]) != -1 ? true : false, + isFile: obj.file_fields.indexOf(obj.fields[j]) != -1 ? true : false + }); } + for (var i = 0; i < obj.children.length; i++) + dive(obj.children[i], !codename ? obj.children[i].alias : codename+'.'+obj.children[i].alias, obj, traduction); + } - // Check if current entity has already been built in this branch of the tree to avoid infinite loop - for (const [idx, genealogyBranch] of genealogy.entries()) - if (genealogyBranch.entity == entity) { - // Keep smallest depth - if (genealogyBranch.depth > depth) - genealogy.splice(idx, 1); - else - return fieldTree; - } + // Build options array + dive(entityTree); - genealogy.push({ - entity: entity, - depth: depth - }); + // Sort options array + // loopCount is used to avoid "Maximum call stack exedeed" error with large arrays. + // Using setTimeout (even with 0 milliseconds) will end the current call stack and create a new one. + // Even with 0 milliseconds timeout execution can be realy slower, so we reset call stack once every 1000 lap + function stackProtectedRecursion(sortFunc, ...args) { + if (!this.loopCount) + this.loopCount = 0; + this.loopCount++; + if (this.loopCount % 1000 === 0) { + this.loopCount = 0; + return setTimeout(() => {sortFunc(...args);}, 0); + } + return sortFunc(...args); + } + function swap(arr, i, j) { + const tmp = arr[j]; + arr[j] = arr[i]; + arr[i] = tmp; + } + function sort(array, idx = 0) { + if (idx < 0) idx = 0; + if (!array || !array[idx+1]) + return; - // Building children array - for (let i = 0; i < entityAssociations.length; i++){ - // Do not include history & status table in field list - if(entityAssociations[i].target.indexOf("e_history_e_") == -1 && entityAssociations[i].target.indexOf("e_status") == -1 && entityAssociations[i].structureType !== 'auto_generate') - fieldTree.children.push(loadTree(entityAssociations[i].target, entityAssociations[i].as, depth+1)); + const first = array[idx].traduction.split(separator); + const second = array[idx+1].traduction.split(separator); + + // Swap because of depth difference + if (first.length > second.length) { + swap(array, idx, idx+1); + idx--; + } + else if (first.length == second.length) { + // Dive depth until mismatch + const initialIdx = idx; + for (let i = 0; i < first.length; i++) { + if (first[i] > second[i]) { + swap(array, idx, idx+1); + idx--; + break; + } + else if (first[i] < second[i]) { + idx++; + break; + } + } + // Avoid infinite loop if both traduction are equal + if (initialIdx == idx) + idx++; } + else + idx++; - return fieldTree; + stackProtectedRecursion(sort, array, idx); } - return loadTree(entity, alias) - }, // Build sequelize formated include object from tree + sort(options); + + return options; + }, + // Build sequelize formated include object from tree buildIncludeFromTree: function(entityTree) { var includes = []; for (var i = 0; entityTree.children && i < entityTree.children.length; i++) { From b02a5b84f2601af79ea16fbb004a345eca371104 Mon Sep 17 00:00:00 2001 From: Jerome Bonini Date: Mon, 28 Oct 2019 17:27:56 +0100 Subject: [PATCH 08/14] - status error with clean message --- structure/pieces/routes/data_entity.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/structure/pieces/routes/data_entity.js b/structure/pieces/routes/data_entity.js index 1e6c3fdaa..da8da5ff9 100755 --- a/structure/pieces/routes/data_entity.js +++ b/structure/pieces/routes/data_entity.js @@ -524,7 +524,8 @@ router.get('/set_status/:id_ENTITY_URL_NAME/:status/:id_new_status', block_acces status_helper.setStatus('ENTITY_NAME', req.params.id_ENTITY_URL_NAME, req.params.status, req.params.id_new_status, req.session.passport.user.id, req.query.comment).then(()=> { res.redirect(req.headers.referer); }).catch(err => { - entity_helper.error(err, req, res, '/ENTITY_URL_NAME/show?id=' + req.params.id_ENTITY_URL_NAME, "ENTITY_NAME"); + req.session.toastr.push({level: 'error', message: 'component.status.error.action_error'}); + res.redirect(req.headers.referer); }); }); From 4cc40df2dc6580ff78771c9af6881d2349c32b16 Mon Sep 17 00:00:00 2001 From: Syno Date: Wed, 30 Oct 2019 11:25:09 +0100 Subject: [PATCH 09/14] Handle postgres default role / user / groupe sequence --- services/designer.js | 2 +- structure/template/server.js | 13 +++++-------- 2 files changed, 6 insertions(+), 9 deletions(-) diff --git a/services/designer.js b/services/designer.js index bddcbd7a3..d37692559 100755 --- a/services/designer.js +++ b/services/designer.js @@ -1046,7 +1046,7 @@ exports.setFieldKnownAttribute = function (attr, callback) { }).catch(function (err) { if (typeof err.parent !== "undefined" && (err.parent.errno == 1062 || err.parent.code == 23505)) { let err = new Error("structure.field.attributes.duplicateUnique"); - } else if(typeof err.parent !== "undefined" && (err.parent.errno == 1146 || err.parent.code == "42P01")){ + } else if(typeof err.parent !== "undefined" && (err.parent.errno == 1146 || err.parent.code == "42P01" || err.parent.code == "3F000")){ // Handle case by Newmips, no worry about this one if(['e_group', 'e_role', 'e_user'].indexOf(attr.name_data_entity) == -1 && attr.options.showValue == 'label'){ // Table do not exist - In case of script it's totally normal, just generate a warning diff --git a/structure/template/server.js b/structure/template/server.js index 70944db66..5c631a43a 100755 --- a/structure/template/server.js +++ b/structure/template/server.js @@ -350,24 +350,21 @@ models.sequelize.sync({logging: false, hooks: false}).then(() => { if (!users || users.length == 0 || !hasAdmin) { models.E_group.create({ - id: 1, version: 0, f_label: 'admin' - }).then(function() { + }).then(group => { models.E_role.create({ - id: 1, version: 0, f_label: 'admin' - }).then(function() { + }).then(role => { models.E_user.create({ - id: 1, f_login: 'admin', f_password: null, f_enabled: 0, version: 0 - }).then(function(user) { - user.setR_role(1); - user.setR_group(1); + }).then(user => { + user.setR_role(role.id); + user.setR_group(group.id); }); }); }); From b91d31cf299e568a06c505662c4ae7f00d48b6d5 Mon Sep 17 00:00:00 2001 From: Jerome Bonini Date: Thu, 31 Oct 2019 11:32:17 +0100 Subject: [PATCH 10/14] - French trad corrected for media error --- .../pieces/component/status/locales/global_locales_FR.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/structure/pieces/component/status/locales/global_locales_FR.json b/structure/pieces/component/status/locales/global_locales_FR.json index 7c6523a43..788d537da 100644 --- a/structure/pieces/component/status/locales/global_locales_FR.json +++ b/structure/pieces/component/status/locales/global_locales_FR.json @@ -3,7 +3,7 @@ "status": { "error": { "illegal_status": "Ce statut n'est pas valide.", - "action_error": "Une ou plusieurs actions n'ont pas pu être éxecutée. Verifiez que les notifications sont activées et verifiez votre configuration mail." + "action_error": "Une ou plusieurs actions n'ont pas pu être executée. Verifiez que les notifications sont activées et verifiez votre configuration mail." }, "next_status": "Statut suivant", "available_status": "Statuts disponibles", From dd24b3410e48851c01ce00d4954dcdf71d671901 Mon Sep 17 00:00:00 2001 From: Jerome Bonini Date: Mon, 4 Nov 2019 15:17:29 +0100 Subject: [PATCH 11/14] - Bad comments removed, minor condition improvment --- structure/template/public/js/Newmips/dataTableBuilder.js | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/structure/template/public/js/Newmips/dataTableBuilder.js b/structure/template/public/js/Newmips/dataTableBuilder.js index 4d2e8bbd6..1d6db05ae 100644 --- a/structure/template/public/js/Newmips/dataTableBuilder.js +++ b/structure/template/public/js/Newmips/dataTableBuilder.js @@ -416,7 +416,7 @@ function init_datatable(tableID, doPagination, context) { valueFromArray = getValue(parts, row); } } else { - // Has one sur une sous entité + // Has one relation field let parts = columns[meta.col].data.split('.'); valueFromArray = getValue(parts, row); } @@ -440,7 +440,7 @@ function init_datatable(tableID, doPagination, context) { if (typeof columns[meta.col].type != 'undefined') { // date / datetime if (columns[meta.col].type == 'date' || columns[meta.col].type == 'datetime') { - if (cellValue != "" && cellValue != null && cellValue != "Invalid date" && cellValue != "Invalid Date") { + if (cellValue != null && cellValue != "" && cellValue.toLowerCase() != "invalid date") { var tmpDate = moment(new Date(cellValue)); if (!tmpDate.isValid()) cellValue = '-'; @@ -612,7 +612,6 @@ function init_datatable(tableID, doPagination, context) { } ] } - // Global search table = $(tableID, context).DataTable(tableOptions); //modal on click on picture cell @@ -653,7 +652,6 @@ function init_datatable(tableID, doPagination, context) { var mainTh = $(this); var title = $(this).text(); - // Custom var currentField = mainTh.data('field'); var val = getFilter(tableID.substring(1), currentField); var search = ''; From 45398b7a910ab18d317cd1f04392a291d531d2dc Mon Sep 17 00:00:00 2001 From: Jerome Bonini Date: Tue, 5 Nov 2019 13:59:46 +0100 Subject: [PATCH 12/14] - set_status correction reported to pieces --- structure/pieces/administration/routes/e_user.js | 3 ++- .../component/document_template/routes/e_document_template.js | 3 ++- structure/pieces/component/status/routes/e_action.js | 3 ++- structure/pieces/component/status/routes/e_media.js | 3 ++- 4 files changed, 8 insertions(+), 4 deletions(-) diff --git a/structure/pieces/administration/routes/e_user.js b/structure/pieces/administration/routes/e_user.js index 6b75e80ef..83fc96db0 100644 --- a/structure/pieces/administration/routes/e_user.js +++ b/structure/pieces/administration/routes/e_user.js @@ -522,7 +522,8 @@ router.get('/set_status/:id_user/:status/:id_new_status', block_access.actionAcc status_helper.setStatus('e_user', req.params.id_user, req.params.status, req.params.id_new_status, req.session.passport.user.id, req.query.comment).then(()=> { res.redirect(req.headers.referer); }).catch((err)=> { - entity_helper.error(err, req, res, '/user/show?id=' + req.params.id_user, "e_user"); + req.session.toastr.push({level: 'error', message: 'component.status.error.action_error'}); + res.redirect(req.headers.referer); }); }); diff --git a/structure/pieces/component/document_template/routes/e_document_template.js b/structure/pieces/component/document_template/routes/e_document_template.js index cf4e663d3..48dcff29a 100644 --- a/structure/pieces/component/document_template/routes/e_document_template.js +++ b/structure/pieces/component/document_template/routes/e_document_template.js @@ -227,7 +227,8 @@ router.get('/set_status/:id_document_template/:status/:id_new_status', block_acc status_helper.setStatus('e_document_template', req.params.id_document_template, req.params.status, req.params.id_new_status, req.session.passport.user.id, req.query.comment).then(() => { res.redirect('/document_template/show?id=' + req.params.id_document_template); }).catch((err) => { - entity_helper.error(err, req, res, '/document_template/show?id=' + req.params.id_document_template); + req.session.toastr.push({level: 'error', message: 'component.status.error.action_error'}); + res.redirect(req.headers.referer); }); }); diff --git a/structure/pieces/component/status/routes/e_action.js b/structure/pieces/component/status/routes/e_action.js index 70d36817c..2dca4fd9a 100644 --- a/structure/pieces/component/status/routes/e_action.js +++ b/structure/pieces/component/status/routes/e_action.js @@ -372,7 +372,8 @@ router.get('/set_status/:id_action/:status/:id_new_status', block_access.actionA status_helper.setStatus('e_action', req.params.id_action, req.params.status, req.params.id_new_status, req.session.passport.user.id, req.query.comment).then(()=> { res.redirect('/action/show?id=' + req.params.id_action) }).catch((err)=> { - entity_helper.error(err, req, res, '/action/show?id=' + req.params.id_action); + req.session.toastr.push({level: 'error', message: 'component.status.error.action_error'}); + res.redirect(req.headers.referer); }); }); diff --git a/structure/pieces/component/status/routes/e_media.js b/structure/pieces/component/status/routes/e_media.js index c97d0ab3a..94426a4db 100644 --- a/structure/pieces/component/status/routes/e_media.js +++ b/structure/pieces/component/status/routes/e_media.js @@ -280,7 +280,8 @@ router.get('/set_status/:id_media/:status/:id_new_status', block_access.actionAc status_helper.setStatus('e_media', req.params.id_media, req.params.status, req.params.id_new_status, req.session.passport.user.id, req.query.comment).then(()=> { res.redirect('/media/show?id=' + req.params.id_media); }).catch((err)=> { - entity_helper.error(err, req, res, '/media/show?id=' + req.params.id_media); + req.session.toastr.push({level: 'error', message: 'component.status.error.action_error'}); + res.redirect(req.headers.referer); }); }); From 70cf73cb958fac389ab88dc923a62d27f60d3023 Mon Sep 17 00:00:00 2001 From: Jerome Bonini Date: Tue, 5 Nov 2019 17:19:39 +0100 Subject: [PATCH 13/14] - vanished function re-implemented --- structure/template/utils/status_helper.js | 60 +++++++++++++++++++++++ 1 file changed, 60 insertions(+) diff --git a/structure/template/utils/status_helper.js b/structure/template/utils/status_helper.js index a51958806..762220dac 100644 --- a/structure/template/utils/status_helper.js +++ b/structure/template/utils/status_helper.js @@ -52,6 +52,66 @@ module.exports = { } return loadTree(entity, alias); }, + // Build entity tree with fields and ALL associations + fullEntityFieldTree: function (entity, alias = entity) { + let genealogy = []; + // Create inner function to use genealogy globaly + function loadTree(entity, alias, depth = 0) { + let fieldTree = { + entity: entity, + alias: alias, + fields: [], + email_fields: [], + phone_fields: [], + file_fields: [], + children: [] + } + let entityFields, entityAssociations; + try { + entityFields = JSON.parse(fs.readFileSync(__dirname+'/../models/attributes/'+entity+'.json')); + entityAssociations = JSON.parse(fs.readFileSync(__dirname+'/../models/options/'+entity+'.json')); + } catch (e) { + console.error(e); + return fieldTree; + } + + // Building field array + for (let field in entityFields) { + if (entityFields[field].newmipsType == "email") + fieldTree.email_fields.push(field); + if (entityFields[field].newmipsType == "phone") + fieldTree.phone_fields.push(field); + if (entityFields[field].newmipsType == "file" || entityFields[field].newmipsType == "picture") + fieldTree.file_fields.push(field); + fieldTree.fields.push(field); + } + + // Check if current entity has already been built in this branch of the tree to avoid infinite loop + for (const [idx, genealogyBranch] of genealogy.entries()) + if (genealogyBranch.entity == entity) { + // Keep smallest depth + if (genealogyBranch.depth > depth) + genealogy.splice(idx, 1); + else + return fieldTree; + } + + genealogy.push({ + entity: entity, + depth: depth + }); + + // Building children array + for (let i = 0; i < entityAssociations.length; i++) { + // Do not include history & status table in field list + if(entityAssociations[i].target.indexOf("e_history_e_") == -1 && entityAssociations[i].target.indexOf("e_status") == -1 && entityAssociations[i].structureType !== 'auto_generate') + fieldTree.children.push(loadTree(entityAssociations[i].target, entityAssociations[i].as, depth+1)); + } + + return fieldTree; + } + return loadTree(entity, alias); + }, // Build array of fields for media sms/notification/email insertion