From 628879b5f7f75bab69c41928369bcefd14ae390e Mon Sep 17 00:00:00 2001 From: Syno Date: Tue, 24 Sep 2019 18:16:43 +0200 Subject: [PATCH 01/24] Lettify some var, fix relation exception between related to and has many preset instruction --- routes/instruction_script.js | 64 ++--- services/bot.js | 448 +++++++++++++++++------------------ services/designer.js | 123 +++++++--- 3 files changed, 346 insertions(+), 289 deletions(-) diff --git a/routes/instruction_script.js b/routes/instruction_script.js index 1af32662f..9a7d32970 100755 --- a/routes/instruction_script.js +++ b/routes/instruction_script.js @@ -333,11 +333,11 @@ router.post('/execute', block_access.isLoggedIn, multer({ scriptProcessing = true; // Get file extension - var extensionFile = req.file.originalname.split("."); + let extensionFile = req.file.originalname.split("."); extensionFile = extensionFile[extensionFile.length -1]; // Read file to determine encoding - var fileContent = fs.readFileSync(req.file.path); - var encoding = require('jschardet').detect(fileContent); + let fileContent = fs.readFileSync(req.file.path); + let encoding = require('jschardet').detect(fileContent); // If extension or encoding is not supported, send error if ((extensionFile != 'txt' && extensionFile != 'nps') || (encoding.encoding.toLowerCase() != 'utf-8' && encoding.encoding.toLowerCase() != 'windows-1252' && encoding.encoding.toLowerCase() != 'ascii')) { scriptData[userId].answers.push({ @@ -350,18 +350,18 @@ router.post('/execute', block_access.isLoggedIn, multer({ } // Open file descriptor - var rl = readline.createInterface({ + let rl = readline.createInterface({ input: fs.createReadStream(req.file.path) }); // Read file line by line, check for empty line, line comment, scope comment - var fileLines = [], + let fileLines = [], commenting = false, invalidScript = false; /* If one of theses value is to 2 after readings all lines then there is an error, line to 1 are set because they are mandatory lines added by the generator */ - var exception = { + let exception = { createNewProject : { value: 0, errorMessage: "You can't create or select more than one project in the same script." @@ -401,7 +401,7 @@ router.post('/execute', block_access.isLoggedIn, multer({ }; rl.on('line', function(sourceLine) { - var line = sourceLine; + let line = sourceLine; // Empty line || One line comment scope if (line.trim() == '' || ((line.indexOf('/*') != -1 && line.indexOf('*/') != -1) || line.indexOf('//*') != -1)) @@ -568,10 +568,12 @@ router.post('/execute', block_access.isLoggedIn, multer({ /* Execute when it's not a file upload but a file written in textarea */ router.post('/execute_alt', block_access.isLoggedIn, function(req, res) { + // Reset idxAtMandatoryInstructionStart to handle multiple scripts execution idxAtMandatoryInstructionStart = -1; - var userId = req.session.passport.user.id; + let userId = req.session.passport.user.id; + let __ = require("../services/language")(req.session.lang_user).__; // Init scriptData object for user. (session simulation) scriptData[userId] = { @@ -587,8 +589,8 @@ router.post('/execute_alt', block_access.isLoggedIn, function(req, res) { id_data_entity: -1 } }; + if(scriptProcessing){ - let __ = require("../services/language")(req.session.lang_user).__; scriptData[userId].answers = [{ message: __('instructionScript.alreadyProcessing') }]; @@ -598,14 +600,15 @@ router.post('/execute_alt', block_access.isLoggedIn, function(req, res) { } scriptProcessing = true; - var tmpFilename = moment().format('YY-MM-DD-HH_mm_ss')+"_custom_script.txt"; - var tmpPath = __dirname+'/../upload/'+tmpFilename; + let tmpFilename = moment().format('YY-MM-DD-HH_mm_ss')+"_custom_script.txt"; + let tmpPath = __dirname+'/../upload/'+tmpFilename; // Load template script and unzip master file if application is created using template let templateEntry = req.body.template_entry; let template = {}; fs.openSync(tmpPath, 'w'); + if(templateEntry){ let templateLang; switch(req.session.lang_user.toLowerCase()) { @@ -620,23 +623,30 @@ router.post('/execute_alt', block_access.isLoggedIn, function(req, res) { break; } - var files = fs.readdirSync(__dirname + "/../templates/"+templateEntry); - let found = false; - for (var i = 0; i < files.length; i++) { - var filename = path.join(__dirname + "/../templates/"+templateEntry, files[i]); - if (filename.indexOf("_"+templateLang+"_") != -1 && filename.indexOf(".nps") != -1) { - fs.writeFileSync(tmpPath, fs.readFileSync(filename)); - found = true; - break; - }; - }; - if(!found){ - req.session.toastr = [{ - message: "template.no_script", - level: "error" - }] - return res.redirect("/templates"); + let files = fs.readdirSync(__dirname + "/../templates/"+templateEntry); + let filename = false; + + for (let i = 0; i < files.length; i++) { + if (files[i].indexOf(".nps") != -1) { + if(!filename) + filename = path.join(__dirname + "/../templates/"+templateEntry, files[i]); + else if(files[i].indexOf("_"+templateLang+"_") != -1) + filename = path.join(__dirname + "/../templates/"+templateEntry, files[i]); + } } + + if(!filename){ + scriptData[userId].answers = [{ + message: __('template.no_script') + }]; + scriptData[userId].over = true; + scriptProcessing = false; + return res.end(); + } + + // Write template script in the tmpPath + fs.writeFileSync(tmpPath, fs.readFileSync(filename)); + } else { fs.writeFileSync(tmpPath, req.body.text); } diff --git a/services/bot.js b/services/bot.js index d7d8161f9..8998683a6 100644 --- a/services/bot.js +++ b/services/bot.js @@ -1,6 +1,6 @@ function checkAndCreateAttr(instructionsFunction, options, valueToCheck) { - var attr = { + let attr = { function: instructionsFunction, options: options }; @@ -21,33 +21,33 @@ function checkAndCreateAttr(instructionsFunction, options, valueToCheck) { // ******* BASIC Actions ******* // exports.showSession = function (result) { - var attr = {}; + let attr = {}; attr.function = "showSession"; return attr; }; exports.help = function (result) { - var attr = {}; + let attr = {}; attr.function = "help"; return attr; }; exports.deploy = function (result) { - var attr = {}; + let attr = {}; attr.function = "deploy"; return attr; }; exports.restart = function (result) { - var attr = {}; + let attr = {}; attr.function = "restart"; return attr; }; exports.installNodePackage = function (result) { - var attr = { + let attr = { specificModule: null }; @@ -60,25 +60,25 @@ exports.installNodePackage = function (result) { }; exports.gitPush = function (result) { - var attr = {}; + let attr = {}; attr.function = "gitPush"; return attr; }; exports.gitPull = function (result) { - var attr = {}; + let attr = {}; attr.function = "gitPull"; return attr; }; exports.gitCommit = function (result) { - var attr = {}; + let attr = {}; attr.function = "gitCommit"; return attr; }; exports.gitStatus = function (result) { - var attr = {}; + let attr = {}; attr.function = "gitStatus"; return attr; }; @@ -86,12 +86,12 @@ exports.gitStatus = function (result) { // ******* SELECT Actions ******* // exports.selectProject = function (result) { - var value = result[1]; - var options = { + let value = result[1]; + let options = { "value": value }; - var attr = { + let attr = { function: "selectProject", options: options }; @@ -100,12 +100,12 @@ exports.selectProject = function (result) { exports.selectApplication = function (result) { - var value = result[1]; - var options = { + let value = result[1]; + let options = { value: value }; - var attr = { + let attr = { function: "selectApplication", options: options }; @@ -114,12 +114,12 @@ exports.selectApplication = function (result) { exports.selectModule = function (result) { - var value = result[1]; - var options = { + let value = result[1]; + let options = { value: value }; - var attr = { + let attr = { function: "selectModule", options: options }; @@ -128,12 +128,12 @@ exports.selectModule = function (result) { exports.selectEntity = function (result) { - var value = result[1]; - var options = { + let value = result[1]; + let options = { value: value.trim() }; - var attr = { + let attr = { function: "selectEntity", options: options }; @@ -144,14 +144,14 @@ exports.selectEntity = function (result) { exports.setFieldAttribute = function (result) { // Set entity name as the first option in options array - var options = { + let options = { value: result[1], word: result[2], attributeValue: result[3], processValue: true }; - var attr = { + let attr = { function: "setFieldAttribute", options: options }; @@ -161,13 +161,13 @@ exports.setFieldAttribute = function (result) { exports.setFieldKnownAttribute = function (result) { // Set entity name as the first option in options array - var options = { + let options = { value: result[1], word: result[2], processValue: true }; - var attr = { + let attr = { function: "setFieldKnownAttribute", options: options }; @@ -178,13 +178,13 @@ exports.setFieldKnownAttribute = function (result) { exports.setColumnVisibility = function (result) { // Set entity name as the first option in options array - var options = { + let options = { value: result[1], word: result[2], processValue: true }; - var attr = { + let attr = { function: "setColumnVisibility", options: options }; @@ -194,13 +194,13 @@ exports.setColumnVisibility = function (result) { exports.setColumnHidden = function (result) { // Set entity name as the first option in options array - var options = { + let options = { value: result[1], word: "hidden", processValue: true }; - var attr = { + let attr = { function: "setColumnVisibility", options: options }; @@ -210,13 +210,13 @@ exports.setColumnHidden = function (result) { exports.setColumnVisible = function (result) { // Set entity name as the first option in options array - var options = { + let options = { value: result[1], word: "visible", processValue: true }; - var attr = { + let attr = { function: "setColumnVisibility", options: options }; @@ -226,8 +226,8 @@ exports.setColumnVisible = function (result) { // ******* CREATE Actions ******* // exports.createNewProject = function (result) { - var value = result[1]; - var options = { + let value = result[1]; + let options = { value: value, processValue: true }; @@ -237,8 +237,8 @@ exports.createNewProject = function (result) { exports.createNewApplication = function (result) { - var value = result[1]; - var options = { + let value = result[1]; + let options = { value: value, processValue: true }; @@ -248,8 +248,8 @@ exports.createNewApplication = function (result) { exports.createNewModule = function (result) { - var value = result[1]; - var options = { + let value = result[1]; + let options = { value: value, processValue: true }; @@ -259,8 +259,8 @@ exports.createNewModule = function (result) { exports.createNewEntity = function (result) { - var value = result[1]; - var options = { + let value = result[1]; + let options = { value: value, processValue: true }; @@ -271,14 +271,14 @@ exports.createNewEntity = function (result) { exports.createNewDataField = function (result) { // Field name has not been defined - var value = result[1]; - var defaultValue = null; + let value = result[1]; + let defaultValue = null; // Default value ? if (typeof result[2] !== "undefined") defaultValue = result[2]; - var options = { + let options = { value: value, defaultValue: defaultValue, processValue: true @@ -289,9 +289,9 @@ exports.createNewDataField = function (result) { exports.createNewDataFieldWithType = function (result) { - var value = result[1]; - var type = result[2].toLowerCase().trim(); - var defaultValue = null; + let value = result[1]; + let type = result[2].toLowerCase().trim(); + let defaultValue = null; // Default value ? if (typeof result[3] !== "undefined") @@ -302,7 +302,7 @@ exports.createNewDataFieldWithType = function (result) { // defaultValue = result[3]; // Preparing Options - var options = { + let options = { value: value, type: type, defaultValue: defaultValue, @@ -314,15 +314,15 @@ exports.createNewDataFieldWithType = function (result) { exports.createNewDataFieldWithTypeEnum = function (result) { - var value = result[1]; - var allValues = result[2]; - var defaultValue = null; + let value = result[1]; + let allValues = result[2]; + let defaultValue = null; // Default value ? if (typeof result[3] !== "undefined") defaultValue = result[3]; - var options = { + let options = { value: value, type: "enum", allValues: allValues, @@ -335,15 +335,15 @@ exports.createNewDataFieldWithTypeEnum = function (result) { exports.createNewDataFieldWithTypeRadio = function (result) { - var value = result[1]; - var allValues = result[2]; - var defaultValue = null; + let value = result[1]; + let allValues = result[2]; + let defaultValue = null; // Default value ? if (typeof result[3] !== "undefined") defaultValue = result[3]; - var options = { + let options = { value: value, type: "radio", allValues: allValues, @@ -357,14 +357,14 @@ exports.createNewDataFieldWithTypeRadio = function (result) { // ******* DELETE Actions ******* // exports.deleteProject = function (result) { - var value = result[1]; + let value = result[1]; - var options = { + let options = { value: value, processValue: true }; - var attr = { + let attr = { function: "deleteProject", options: options }; @@ -373,14 +373,14 @@ exports.deleteProject = function (result) { exports.deleteApplication = function (result) { - var value = result[1]; + let value = result[1]; - var options = { + let options = { value: value, processValue: true }; - var attr = { + let attr = { function: "deleteApplication", options: options }; @@ -389,14 +389,14 @@ exports.deleteApplication = function (result) { exports.deleteModule = function (result) { - var value = result[1]; + let value = result[1]; - var options = { + let options = { value: value, processValue: true }; - var attr = { + let attr = { function: "deleteModule", options: options }; @@ -405,14 +405,14 @@ exports.deleteModule = function (result) { exports.deleteDataEntity = function (result) { - var value = result[1]; + let value = result[1]; - var options = { + let options = { value: value, processValue: true }; - var attr = { + let attr = { function: "deleteDataEntity", options: options }; @@ -421,14 +421,14 @@ exports.deleteDataEntity = function (result) { exports.deleteDataField = function (result) { - var value = result[1]; + let value = result[1]; - var options = { + let options = { value: value, processValue: true }; - var attr = { + let attr = { function: "deleteDataField", options: options }; @@ -437,14 +437,14 @@ exports.deleteDataField = function (result) { exports.deleteTab = function (result) { - var value = result[1]; + let value = result[1]; - var options = { + let options = { value: value, processValue: true }; - var attr = { + let attr = { function: "deleteTab", options: options }; @@ -454,7 +454,7 @@ exports.deleteTab = function (result) { // ******* LIST Actions ******* // exports.listProject = function (result) { - var attr = { + let attr = { function: "listProject" }; return attr; @@ -462,7 +462,7 @@ exports.listProject = function (result) { exports.listApplication = function (result) { - var attr = { + let attr = { function: "listApplication" }; return attr; @@ -470,7 +470,7 @@ exports.listApplication = function (result) { exports.listModule = function (result) { - var attr = { + let attr = { function: "listModule" }; return attr; @@ -478,7 +478,7 @@ exports.listModule = function (result) { exports.listDataEntity = function (result) { - var attr = { + let attr = { function: "listDataEntity" }; return attr; @@ -486,7 +486,7 @@ exports.listDataEntity = function (result) { exports.listDataField = function (result) { - var attr = { + let attr = { function: "listDataField" }; return attr; @@ -498,10 +498,10 @@ exports.listDataField = function (result) { // Tabs in show exports.relationshipHasOne = function (result) { - var source = result[1]; - var target = result[2]; + let source = result[1]; + let target = result[2]; - var options = { + let options = { target: target, source: source, foreignKey: "id_" + target.toLowerCase(), @@ -514,11 +514,11 @@ exports.relationshipHasOne = function (result) { exports.relationshipHasOneWithName = function (result) { - var source = result[1]; - var target = result[2]; - var as = result[3]; + let source = result[1]; + let target = result[2]; + let as = result[3]; - var options = { + let options = { target: target, source: source, foreignKey: "id_" + target.toLowerCase() + "_" + as.toLowerCase(), @@ -533,10 +533,10 @@ exports.relationshipHasOneWithName = function (result) { // --------- Field in create / update / show --------- exports.createFieldRelatedTo = function (result) { - var as = result[1]; - var target = result[2]; + let as = result[1]; + let target = result[2]; - var options = { + let options = { target: target, foreignKey: "id_" + target.toLowerCase() + "_" + as.toLowerCase(), as: as, @@ -548,11 +548,11 @@ exports.createFieldRelatedTo = function (result) { exports.createFieldRelatedToUsing = function (result) { - var as = result[1]; - var target = result[2]; - var usingField = result[3]; + let as = result[1]; + let target = result[2]; + let usingField = result[3]; - var options = { + let options = { target: target, foreignKey: "id_" + target.toLowerCase() + "_" + as.toLowerCase(), as: as, @@ -565,11 +565,11 @@ exports.createFieldRelatedToUsing = function (result) { exports.createFieldRelatedToMultiple = function (result) { - var as = result[1]; - var target = result[2]; + let as = result[1]; + let target = result[2]; // Preparing Options - var options = { + let options = { target: target, as: as, processValue: true @@ -580,11 +580,11 @@ exports.createFieldRelatedToMultiple = function (result) { exports.createFieldRelatedToMultipleUsing = function (result) { - var as = result[1]; - var target = result[2]; - var usingField = result[3]; + let as = result[1]; + let target = result[2]; + let usingField = result[3]; - var options = { + let options = { target: target, as: as, usingField: usingField, @@ -596,11 +596,11 @@ exports.createFieldRelatedToMultipleUsing = function (result) { exports.createFieldRelatedToMultipleCheckbox = function (result) { - var as = result[1]; - var target = result[2]; + let as = result[1]; + let target = result[2]; // Preparing Options - var options = { + let options = { target: target, isCheckbox: true, as: as, @@ -612,11 +612,11 @@ exports.createFieldRelatedToMultipleCheckbox = function (result) { exports.createFieldRelatedToMultipleCheckboxUsing = function (result) { - var as = result[1]; - var target = result[2]; - var usingField = result[3]; + let as = result[1]; + let target = result[2]; + let usingField = result[3]; - var options = { + let options = { target: target, as: as, usingField: usingField, @@ -631,10 +631,10 @@ exports.createFieldRelatedToMultipleCheckboxUsing = function (result) { // Tabs in show exports.relationshipHasMany = function (result) { - var source = result[1]; - var target = result[2]; + let source = result[1]; + let target = result[2]; - var options = { + let options = { target: target, source: source, foreignKey: "id_" + source.toLowerCase(), @@ -647,11 +647,11 @@ exports.relationshipHasMany = function (result) { exports.relationshipHasManyWithName = function (result) { - var source = result[1]; - var target = result[2]; - var as = result[3]; + let source = result[1]; + let target = result[2]; + let as = result[3]; - var options = { + let options = { target: target, source: source, foreignKey: "id_" + source.toLowerCase() + "_" + as.toLowerCase(), @@ -663,16 +663,16 @@ exports.relationshipHasManyWithName = function (result) { }; exports.relationshipHasManyPreset = function (result) { - var source = result[1]; - var target = result[2]; - var as = target; - var foreignKey = "id_" + source.toLowerCase(); + let source = result[1]; + let target = result[2]; + let as = target; + let foreignKey = "id_" + source.toLowerCase(); if (typeof result[3] !== "undefined") as = result[3]; foreignKey = "id_" + source.toLowerCase() + "_" + as.toLowerCase() - var options = { + let options = { target: target, source: source, foreignKey: foreignKey, @@ -684,17 +684,17 @@ exports.relationshipHasManyPreset = function (result) { }; exports.relationshipHasManyPresetUsing = function (result) { - var source = result[1]; - var target = result[2]; - var usingField = result[3]; - var as = target; - var foreignKey = "id_" + source.toLowerCase(); + let source = result[1]; + let target = result[2]; + let usingField = result[3]; + let as = target; + let foreignKey = "id_" + source.toLowerCase(); if (typeof result[4] !== "undefined") as = result[4]; foreignKey = "id_" + source.toLowerCase() + "_" + as.toLowerCase() - var options = { + let options = { target: target, source: source, foreignKey: foreignKey, @@ -710,7 +710,7 @@ exports.relationshipHasManyPresetUsing = function (result) { /* STATUS */ exports.createNewComponentStatus = function (result) { - var defaultValue = result[0].indexOf("component") != -1 ? "Status" : "Statut"; + let defaultValue = result[0].indexOf("component") != -1 ? "Status" : "Statut"; return { function: "createNewComponentStatus", options: {value: defaultValue, processValue: true} @@ -718,8 +718,8 @@ exports.createNewComponentStatus = function (result) { } exports.createNewComponentStatusWithName = function (result) { - var value = result[1]; - var options = { + let value = result[1]; + let options = { value: value, processValue: true }; @@ -729,9 +729,9 @@ exports.createNewComponentStatusWithName = function (result) { exports.deleteComponentStatus = function (result) { - var options = {}; + let options = {}; - var attr = { + let attr = { function: "deleteComponentStatus", options: options }; @@ -739,8 +739,8 @@ exports.deleteComponentStatus = function (result) { }; exports.deleteComponentStatusWithName = function (result) { - var value = result[1]; - var options = { + let value = result[1]; + let options = { value: value, processValue: true }; @@ -751,9 +751,9 @@ exports.deleteComponentStatusWithName = function (result) { /* LOCAL FILE STORAGE */ exports.createNewComponentLocalFileStorage = function (result) { - var options = {}; + let options = {}; - var attr = { + let attr = { function: "createNewComponentLocalFileStorage", options: options }; @@ -762,8 +762,8 @@ exports.createNewComponentLocalFileStorage = function (result) { exports.createNewComponentLocalFileStorageWithName = function (result) { - var value = result[1]; - var options = { + let value = result[1]; + let options = { value: value, processValue: true }; @@ -774,9 +774,9 @@ exports.createNewComponentLocalFileStorageWithName = function (result) { /* CONTACT FORM */ exports.createNewComponentContactForm = function (result) { - var options = {}; + let options = {}; - var attr = { + let attr = { function: "createNewComponentContactForm", options: options }; @@ -785,8 +785,8 @@ exports.createNewComponentContactForm = function (result) { exports.createNewComponentContactFormWithName = function (result) { - var value = result[1]; - var options = { + let value = result[1]; + let options = { value: value, processValue: true }; @@ -796,9 +796,9 @@ exports.createNewComponentContactFormWithName = function (result) { exports.deleteComponentContactForm = function (result) { - var options = {}; + let options = {}; - var attr = { + let attr = { function: "deleteComponentContactForm", options: options }; @@ -806,8 +806,8 @@ exports.deleteComponentContactForm = function (result) { }; exports.deleteComponentContactFormWithName = function (result) { - var value = result[1]; - var options = { + let value = result[1]; + let options = { value: value, processValue: true }; @@ -818,9 +818,9 @@ exports.deleteComponentContactFormWithName = function (result) { /* AGENDA */ exports.createNewComponentAgenda = function (result) { - var options = {}; + let options = {}; - var attr = { + let attr = { function: "createNewComponentAgenda", options: options }; @@ -829,8 +829,8 @@ exports.createNewComponentAgenda = function (result) { exports.createNewComponentAgendaWithName = function (result) { - var value = result[1]; - var options = { + let value = result[1]; + let options = { value: value, processValue: true }; @@ -840,9 +840,9 @@ exports.createNewComponentAgendaWithName = function (result) { exports.deleteAgenda = function (result) { - var options = {}; + let options = {}; - var attr = { + let attr = { function: "deleteAgenda", options: options }; @@ -851,8 +851,8 @@ exports.deleteAgenda = function (result) { exports.deleteAgendaWithName = function (result) { - var value = result[1]; - var options = { + let value = result[1]; + let options = { value: value, processValue: true }; @@ -873,7 +873,7 @@ exports.createNewComponentCra = function (result) { * @returns {function name and user instruction} */ exports.createNewComponentAddress = function (result) { - var options = { + let options = { componentName: "Address", instruction: result[0] }; @@ -886,7 +886,7 @@ exports.createNewComponentAddress = function (result) { * @returns {function name and user instruction} */ exports.createNewComponentAddressWithName = function (result) { - var options = { + let options = { componentName: result[1], instruction: result[0] }; @@ -907,9 +907,9 @@ exports.deleteComponentAddress = function (result) { /* PRINT */ exports.createNewComponentPrint = function (result) { - var options = {}; + let options = {}; - var attr = { + let attr = { function: "createNewComponentPrint", options: options }; @@ -918,14 +918,14 @@ exports.createNewComponentPrint = function (result) { exports.createNewComponentPrintWithName = function (result) { - var value = result[1]; + let value = result[1]; - var options = { + let options = { value: value, processValue: true }; - var attr = { + let attr = { function: "createNewComponentPrint", options: options }; @@ -934,9 +934,9 @@ exports.createNewComponentPrintWithName = function (result) { exports.deleteComponentPrint = function (result) { - var options = {}; + let options = {}; - var attr = { + let attr = { function: "deleteComponentPrint", options: options }; @@ -945,14 +945,14 @@ exports.deleteComponentPrint = function (result) { exports.deleteComponentPrintWithName = function (result) { - var value = result[1]; + let value = result[1]; - var options = { + let options = { value: value, processValue: true }; - var attr = { + let attr = { function: "deleteComponentPrint", options: options }; @@ -970,7 +970,7 @@ exports.createComponentDocumentTemplate = function (result) { }; exports.createComponentDocumentTemplateWithName = function (result) { - var options={ + let options={ instruction:result[0], componentName:result[1] }; @@ -995,12 +995,12 @@ exports.createComponentChat = function (result) { // ******* INTERFACE Actions ******* // exports.setLogo = function (result) { - var value = result[1]; - var options = { + let value = result[1]; + let options = { value: value }; - var attr = { + let attr = { function: "setLogo", options: options }; @@ -1008,19 +1008,19 @@ exports.setLogo = function (result) { }; exports.removeLogo = function (result) { - var attr = {}; + let attr = {}; attr.function = "removeLogo"; return attr; }; exports.setLayout = function (result) { - var value = result[1]; - var options = { + let value = result[1]; + let options = { value: value }; - var attr = { + let attr = { function: "setLayout", options: options }; @@ -1029,7 +1029,7 @@ exports.setLayout = function (result) { exports.listLayout = function (result) { - var attr = { + let attr = { function: "listLayout" }; return attr; @@ -1037,12 +1037,12 @@ exports.listLayout = function (result) { exports.setTheme = function (result) { - var value = result[1]; - var options = { + let value = result[1]; + let options = { value: value }; - var attr = { + let attr = { function: "setTheme", options: options }; @@ -1051,7 +1051,7 @@ exports.setTheme = function (result) { exports.listTheme = function (result) { - var attr = { + let attr = { function: "listTheme" }; return attr; @@ -1062,7 +1062,7 @@ exports.listIcon = function (result) { } exports.setIcon = function (result) { - var attr = { + let attr = { function: "setIcon", iconValue: result[1] }; @@ -1070,7 +1070,7 @@ exports.setIcon = function (result) { } exports.setIconToEntity = function (result) { - var attr = { + let attr = { function: "setIconToEntity", iconValue: result[1], entityTarget: result[2] @@ -1100,7 +1100,7 @@ function getRightWidgetType(originalType) { } function buildAttrForPiechart(result) { - var attr = { + let attr = { function: 'createWidgetPiechart', widgetType: 'piechart', widgetInputType: 'Piechart' @@ -1118,21 +1118,21 @@ function buildAttrForPiechart(result) { } exports.createWidgetPiechart = function (result) { - var attr = buildAttrForPiechart(result); + let attr = buildAttrForPiechart(result); attr.legend = true; return attr; } exports.createWidgetPiechartWithoutLegend = function (result) { - var attr = buildAttrForPiechart(result); + let attr = buildAttrForPiechart(result); attr.legend = false; return attr; } exports.createWidgetLastRecordsWithLimit = function (result) { - var attr = { + let attr = { function: 'createWidgetLastRecords', widgetType: 'lastrecords', widgetInputType: 'last records' @@ -1150,14 +1150,14 @@ exports.createWidgetLastRecordsWithLimit = function (result) { } // Remove unwanted spaces from columns - for (var i = 0; i < attr.columns.length; i++) + for (let i = 0; i < attr.columns.length; i++) attr.columns[i] = attr.columns[i].trim(); return attr; } exports.createWidgetLastRecords = function (result) { - var attr = { + let attr = { function: 'createWidgetLastRecords', widgetType: 'lastrecords', widgetInputType: 'last records', @@ -1174,15 +1174,15 @@ exports.createWidgetLastRecords = function (result) { } // Remove unwanted spaces from columns - for (var i = 0; i < attr.columns.length; i++) + for (let i = 0; i < attr.columns.length; i++) attr.columns[i] = attr.columns[i].trim(); return attr; } exports.createWidgetOnEntity = function (result) { - var originalType = result[1]; - var finalType = getRightWidgetType(originalType); + let originalType = result[1]; + let finalType = getRightWidgetType(originalType); if (finalType == -1) return {error: 'error.missingParametersInstruction'}; @@ -1196,8 +1196,8 @@ exports.createWidgetOnEntity = function (result) { } exports.createWidget = function (result) { - var originalType = result[1]; - var finalType = getRightWidgetType(originalType); + let originalType = result[1]; + let finalType = getRightWidgetType(originalType); if (finalType == -1) return {error: 'error.missingParametersInstruction'}; @@ -1246,7 +1246,7 @@ exports.apero = function (result) { } } -var training = { +let training = { "apero": [ "Apéro !" ], @@ -2755,16 +2755,16 @@ var training = { // ******* Parse ******* exports.parse = function (instruction) { - var instructionResult = { + let instructionResult = { instructionLength: 0 }; - for (var action in training) { - for (var i = 0; i < training[action].length; i++) { - var regStr = training[action][i]; - var regExp = new RegExp(regStr, "ig"); + for (let action in training) { + for (let i = 0; i < training[action].length; i++) { + let regStr = training[action][i]; + let regExp = new RegExp(regStr, "ig"); - var result = regExp.exec(instruction); + let result = regExp.exec(instruction); if (result !== null) { /* Get the most complicated instruction found */ if (instructionResult.instructionLength < regStr.length) { @@ -2777,7 +2777,7 @@ exports.parse = function (instruction) { } } } - var attr = {}; + let attr = {}; if (typeof instructionResult.action !== "undefined") { attr = this[instructionResult.action](instructionResult.result); attr.instruction = instruction; @@ -2791,52 +2791,52 @@ exports.parse = function (instruction) { // ******* Completion ******* exports.complete = function (instruction) { - var answers = []; - var p = 0; + let answers = []; + let p = 0; // Check all training key phrases - for (var action in training) { + for (let action in training) { // Check each blocks - for (var i = 0; i < training[action].length; i++) { + for (let i = 0; i < training[action].length; i++) { // Template to compare to - var template = training[action][i].split(" "); + let template = training[action][i].split(" "); // Split current key phrase and instructions into arrays to loop - var instr = instruction.trim().split(" "); + let instr = instruction.trim().split(" "); - var k = 0; // index in template - var m = 0; // index in instruction + let k = 0; // index in template + let m = 0; // index in instruction - var l = instr.length; - var n = template.length; + let l = instr.length; + let n = template.length; - var answer = " "; - var valid = true; - var variable = false; + let answer = " "; + let valid = true; + let letiable = false; while ((m < l) && (k < n) && (valid)) { // Check if words are the same, goto next word if (template[k] == "(.*)" || template[k] == instr[m]) { - variable = false; + letiable = false; k++; } else { // Check if beginning of word are the same - var sublen = instr[m].length; + let sublen = instr[m].length; if (template[k].substring(0, sublen) == instr[m]) { // Do not increment k, we are still on keyword - variable = false; + letiable = false; } else { - // If we parse the variable value + // If we parse the letiable value if (template[k] == "(.*)") { // Check next word if (template[k + 1]) { k++; - variable = true; + letiable = true; } } else { - // If we are not parsing a variable, it means template is not appropriate => Exit - if (!variable) + // If we are not parsing a letiable, it means template is not appropriate => Exit + if (!letiable) valid = false; } } @@ -2847,8 +2847,8 @@ exports.complete = function (instruction) { // Instruction has respected template, so send next keyword if any if ((valid) && (m == l)) { - var found = false; - var firstLoop = true; + let found = false; + let firstLoop = true; while ((k < n) && !found) { // Return next keyword @@ -2857,12 +2857,12 @@ exports.complete = function (instruction) { else { if (template[k - 1] == "type") answer = answer + "[type] "; - // Return [variable] to explain this is something dynamic + // Return [letiable] to explain this is something dynamic else - answer = answer + "[variable] "; + answer = answer + "[letiable] "; - // 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 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 (!firstLoop) found = true; } @@ -2908,7 +2908,7 @@ exports.complete = function (instruction) { } // Filter array of results (remove double values) - var i, j, len = answers.length, + let i, j, len = answers.length, out = [], obj = {}; for (i = 0; i < len; i++) diff --git a/services/designer.js b/services/designer.js index 395635b5a..a05e12ec1 100755 --- a/services/designer.js +++ b/services/designer.js @@ -1,4 +1,4 @@ -// Database Generator +// Newmips Database const db_project = require("../database/project"); const db_application = require("../database/application"); const db_module = require("../database/module"); @@ -9,27 +9,28 @@ const database = require("../database/database"); // Session const session = require("./session"); -const cloud_manager = require('../services/cloud_manager'); -// Bot -var bot = require('../services/bot.js'); +// Bot grammar +let bot = require('../services/bot.js'); -// Structure -var structure_application = require("../structure/structure_application"); -var structure_module = require("../structure/structure_module"); -var structure_data_entity = require("../structure/structure_data_entity"); -var structure_data_field = require("../structure/structure_data_field"); -var structure_component = require("../structure/structure_component"); -var structure_ui = require("../structure/structure_ui"); +// Structure files +let structure_application = require("../structure/structure_application"); +let structure_module = require("../structure/structure_module"); +let structure_data_entity = require("../structure/structure_data_entity"); +let structure_data_field = require("../structure/structure_data_field"); +let structure_component = require("../structure/structure_component"); +let structure_ui = require("../structure/structure_ui"); -// Other -var helpers = require("../utils/helpers"); -var attrHelper = require("../utils/attr_helper"); -var gitHelper = require("../utils/git_helper"); -var translateHelper = require("../utils/translate"); +// Utils +let helpers = require("../utils/helpers"); +let attrHelper = require("../utils/attr_helper"); +let gitHelper = require("../utils/git_helper"); +let translateHelper = require("../utils/translate"); +// Others const fs = require('fs-extra'); const sequelize = require('../models/').sequelize; +const cloud_manager = require('../services/cloud_manager'); /* --------------------------------------------------------------- */ /* -------------------------- General ---------------------------- */ @@ -1358,6 +1359,44 @@ function belongsToMany(attr, optionObj, setupFunction, exportsContext) { id_data_entity: attr.id_data_entity }; + // { function: 'createNewHasManyPreset', + // options: + // { target: 'e_user', + // source: 'e_projet', + // foreignKey: 'fk_id_projet_participant_using_login', + // as: 'r_participant_using_login', + // processValue: true, + // showTarget: 'User', + // urlTarget: 'user', + // showSource: 'Projet', + // urlSource: 'projet', + // showForeignKey: 'id_projet_participant using login', + // showAs: 'Participant using Login', + // urlAs: 'participant_using_login', + // through: '11_e_projet_e_user' }, + // instruction: 'entity Projet has many existing User called Participant using Login', + // id_project: 11, + // id_application: 11, + // id_module: 30, + // id_data_entity: 147, + // googleTranslate: false, + // lang_user: 'fr-FR', + // currentUser: + // { id: 1, + // email: 'admin@admin.fr', + // enabled: true, + // first_name: 'admin', + // last_name: 'NEWMIPS', + // login: 'admin', + // password: '$2a$10$h2kI8qTdNqlh64FeqzRGjOVzhRGr60xf5b/JCL/R.y4747uvSwj1u', + // phone: null, + // token_password_reset: null, + // version: 1, + // id_role: 1 }, + // gitlabUser: null, + // targetType: 'auto_generate' } + + if (attr.targetType == "hasMany") { structure_data_field.setupHasManyTab(reversedAttr, function () { resolve(); @@ -1510,7 +1549,9 @@ exports.createNewHasMany = function (attr, callback) { for (var i = 0; i < optionsObject.length; i++) { if (optionsObject[i].target.toLowerCase() == attr.options.source.toLowerCase() && optionsObject[i].target.toLowerCase() != attr.options.target.toLowerCase() - && optionsObject[i].relation != "belongsTo") { + && optionsObject[i].relation != "belongsTo" + && optionsObject[i].structureType != "auto_generate") { + doingBelongsToMany = true; /* Then lets create the belongs to many association */ belongsToMany(attr, optionsObject[i], "setupHasManyTab", exportsContext).then(function () { @@ -1643,9 +1684,9 @@ exports.createNewHasManyPreset = function (attr, callback) { return callback(err, null); let sourceOptionsPath = __dirname+'/../workspace/' + attr.id_application + '/models/options/' + attr.options.source.toLowerCase() + '.json'; - var optionsSourceFile = helpers.readFileSyncWithCatch(sourceOptionsPath); - var optionsSourceObject = JSON.parse(optionsSourceFile); - var toSync = true; + let optionsSourceFile = helpers.readFileSyncWithCatch(sourceOptionsPath); + let optionsSourceObject = JSON.parse(optionsSourceFile); + let toSync = true; let saveFile = false; // Vérification si une relation existe déjà de la source VERS la target for (var i = 0; i < optionsSourceObject.length; i++) { @@ -1666,7 +1707,7 @@ exports.createNewHasManyPreset = function (attr, callback) { attr.options.through = attr.id_application + "_" + idEntitySource + "_" + entityTarget.id + "_" + attr.options.as.substring(2); if (attr.options.through.length > 55) { - var err = new Error(); + let err = new Error(); err.message = "error.valueTooLong"; err.messageParams = [attr.options.through]; return callback(err, null); @@ -1676,33 +1717,39 @@ exports.createNewHasManyPreset = function (attr, callback) { if(saveFile) fs.writeFileSync(sourceOptionsPath, JSON.stringify(optionsSourceObject, null, 4), "utf8") + let optionsFile = helpers.readFileSyncWithCatch(__dirname+'/../workspace/' + attr.id_application + '/models/options/' + attr.options.target.toLowerCase() + '.json'); + let targetOptions = JSON.parse(optionsFile); + let cptExistingHasMany = 0; - var optionsFile = helpers.readFileSyncWithCatch(__dirname+'/../workspace/' + attr.id_application + '/models/options/' + attr.options.target.toLowerCase() + '.json'); - var optionsObject = JSON.parse(optionsFile); - var cptExistingHasMany = 0; + // Preparing variable + let source = attr.options.source.toLowerCase(); + let target = attr.options.target.toLowerCase(); // Check if there is no or just one belongsToMany to do - for (var i = 0; i < optionsObject.length; i++) { - if (optionsObject[i].target.toLowerCase() == attr.options.source.toLowerCase() && optionsObject[i].relation != "belongsTo") { + for (let i = 0; i < targetOptions.length; i++) + if (targetOptions[i].target.toLowerCase() == source && targetOptions[i].relation != "belongsTo") cptExistingHasMany++; - } - } /* If there are multiple has many association from target to source we can't handle on which one we gonna link the belongsToMany association */ if (cptExistingHasMany > 1) { - var err = new Error("structure.association.error.tooMuchHasMany"); + let err = new Error("structure.association.error.tooMuchHasMany"); return callback(err, null); } - var doingBelongsToMany = false; - + let doingBelongsToMany = false, targetObjTarget; // Vérification si une relation existe déjà de la target VERS la source - for (var i = 0; i < optionsObject.length; i++) { - if (optionsObject[i].target.toLowerCase() == attr.options.source.toLowerCase() && optionsObject[i].relation != "belongsTo") { + for (let i = 0; i < targetOptions.length; i++) { + targetObjTarget = targetOptions[i].target.toLowerCase(); + + if (targetObjTarget == source + && targetObjTarget != target + && targetOptions[i].relation != "belongsTo" + && targetOptions[i].structureType != "auto_generate") { + doingBelongsToMany = true; /* Then lets create the belongs to many association */ - belongsToMany(attr, optionsObject[i], "setupHasManyPresetTab", exportsContext).then(function () { - var info = {}; + belongsToMany(attr, targetOptions[i], "setupHasManyPresetTab", exportsContext).then(function () { + let info = {}; info.insertId = attr.id_data_entity; info.message = "structure.association.hasManyExisting.success"; info.messageParams = [attr.options.showTarget, attr.options.showSource]; @@ -1711,9 +1758,9 @@ exports.createNewHasManyPreset = function (attr, callback) { console.error(err); return callback(err, null); }); - } else if (attr.options.source.toLowerCase() != attr.options.target.toLowerCase() - && (optionsObject[i].target.toLowerCase() == attr.options.source.toLowerCase() && optionsObject[i].relation == "belongsTo") - && (optionsObject[i].foreignKey == attr.options.foreignKey)) { + } else if (source != target + && (targetObjTarget == source && targetOptions[i].relation == "belongsTo") + && targetOptions[i].foreignKey == attr.options.foreignKey) { // We avoid the toSync to append because the already existing has one relation has already created the foreign key in BDD toSync = false; } From 57f3fe54395d886271f86369ffaaa422d13bb8f1 Mon Sep 17 00:00:00 2001 From: Syno Date: Tue, 24 Sep 2019 18:37:57 +0200 Subject: [PATCH 02/24] Remove moment-timezone guess for sequelize that generated mysql2 warning --- models/index.js | 21 ++++++++++----------- package.json | 3 +-- server.js | 8 ++++---- structure/template/models/index.js | 2 -- structure/template/package.json | 3 +-- 5 files changed, 16 insertions(+), 21 deletions(-) diff --git a/models/index.js b/models/index.js index 3a1f09567..3f5624c40 100755 --- a/models/index.js +++ b/models/index.js @@ -1,13 +1,13 @@ 'use strict'; -var fs = require('fs'); -var path = require('path'); -var Sequelize = require('sequelize'); -var basename = path.basename(module.filename); -var env = require('../config/global'); -var dbConfig = require('../config/database'); -var moment_timezone = require('moment-timezone'); -var db = {}; +const fs = require('fs'); +const path = require('path'); +const Sequelize = require('sequelize'); +const env = require('../config/global'); +const dbConfig = require('../config/database'); + +let basename = path.basename(module.filename); +let db = {}; const Op = Sequelize.Op; const operatorsAliases = { @@ -47,7 +47,7 @@ const operatorsAliases = { $col: Op.col }; -var sequelize = new Sequelize(dbConfig.database, dbConfig.user, dbConfig.password, { +const sequelize = new Sequelize(dbConfig.database, dbConfig.user, dbConfig.password, { host: dbConfig.host, logging: false, port: dbConfig.port, @@ -60,14 +60,13 @@ var sequelize = new Sequelize(dbConfig.database, dbConfig.user, dbConfig.passwor }, charset: 'utf8', collate: 'utf8_general_ci', - timezone: moment_timezone.tz.guess(), operatorsAliases }) fs.readdirSync(__dirname).filter(function(file) { return (file.indexOf('.') !== 0) && (file !== basename) && (file.slice(-3) === '.js'); }).forEach(function(file) { - var model = sequelize['import'](path.join(__dirname, file)); + let model = sequelize['import'](path.join(__dirname, file)); db[model.name] = model; }) diff --git a/package.json b/package.json index 7924a9158..6f7abfa24 100644 --- a/package.json +++ b/package.json @@ -42,11 +42,10 @@ "math": "0.0.3", "mocha": "^5.2.0", "moment": "^2.22.1", - "moment-timezone": "^0.5.17", "morgan": "^1.9.0", "multer": "^1.3.0", "mysql": "^2.15.0", - "mysql2": "^1.5.3", + "mysql2": "^1.7.0", "nodemailer": "^4.6.5", "npm": "^6.4.0", "passport": "^0.4.0", diff --git a/server.js b/server.js index 0b2305661..c99d4e4b5 100755 --- a/server.js +++ b/server.js @@ -80,11 +80,11 @@ app.use(morgan('dev', { if(globalConf.env != "develop"){ require('console-stamp')(console, { formatter: function() { - return moment().format('YYYY-MM-DD HH:mm:ss-SSS'); + return moment().format('MM-DD HH:mm:ss'); }, - label: true, - datePrefix: "[", - dateSuffix: "]-- " + label: false, + datePrefix: "", + dateSuffix: "" }); } diff --git a/structure/template/models/index.js b/structure/template/models/index.js index 26848dc2a..740c9adb9 100755 --- a/structure/template/models/index.js +++ b/structure/template/models/index.js @@ -7,7 +7,6 @@ var basename = path.basename(module.filename); var dbConfig = require('../config/database'); var globalConf = require('../config/global'); var moment = require('moment'); -var moment_timezone = require('moment-timezone'); var db = {}; const Op = Sequelize.Op; @@ -70,7 +69,6 @@ if (dbConfig.dialect == 'sqlite'){ }, charset: 'utf8', collate: 'utf8_general_ci', - timezone: moment_timezone.tz.guess(), operatorsAliases } } diff --git a/structure/template/package.json b/structure/template/package.json index 31f16ba04..570597bc6 100755 --- a/structure/template/package.json +++ b/structure/template/package.json @@ -32,11 +32,10 @@ "jszip": "2.6.1", "mime-types": "^2.1.17", "moment": "^2.22.1", - "moment-timezone": "^0.5.6", "morgan": "^1.9.0", "multer": "^1.2.0", "mysql": "^2.15.0", - "mysql2": "^1.5.3", + "mysql2": "^1.7.0", "nodemailer": "^4.6.5", "ovh": "^2.0.1", "passport": "^0.4.0", From 73d588f073a1e5e0f44b1661c6cfa3888e2b09c5 Mon Sep 17 00:00:00 2001 From: Syno Date: Wed, 25 Sep 2019 17:05:00 +0200 Subject: [PATCH 03/24] Currency value formatage --- structure/template/utils/model_builder.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/structure/template/utils/model_builder.js b/structure/template/utils/model_builder.js index 956948112..86003b768 100755 --- a/structure/template/utils/model_builder.js +++ b/structure/template/utils/model_builder.js @@ -107,8 +107,8 @@ exports.formatSearch = function (column, searchValue, type) { } break; case 'currency': - formatedSearch = models.Sequelize.where(models.Sequelize.col(column), { - like: `${searchValue}%` + formatedSearch = require('../models').Sequelize.where(require('../models').Sequelize.col(column), { + $like: searchValue + '%' }); break; default: From fe140f90a690f2781e035081e079b691ba6c7e76 Mon Sep 17 00:00:00 2001 From: Syno Date: Thu, 26 Sep 2019 09:32:35 +0200 Subject: [PATCH 04/24] Clean generator commit message --- utils/git_helper.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/utils/git_helper.js b/utils/git_helper.js index f76dba3a8..6c6893c69 100644 --- a/utils/git_helper.js +++ b/utils/git_helper.js @@ -114,7 +114,7 @@ module.exports = { console.log("GIT: Git commit after new instruction."); console.log(repoUrl); - var commitMsg = "New commit: Function:"+attr.function+" App:"+idApplication+" Module:"+attr.id_module+" Entity:"+attr.id_data_entity; + var commitMsg = attr.function+" -> App:"+idApplication+" Module:"+attr.id_module+" Entity:"+attr.id_data_entity; simpleGit.add('.') .commit(commitMsg, function(err, answer){ if(err) @@ -305,7 +305,7 @@ module.exports = { if(!gitProcesses[originName]){ // Set gitProcesses to prevent any other git command during this process gitProcesses[originName] = true; - var commitMsg = "New commit: Function:"+attr.function+" App:"+idApplication+" Module:"+attr.id_module+" Entity:"+attr.id_data_entity; + var commitMsg = attr.function+" -> App:"+idApplication+" Module:"+attr.id_module+" Entity:"+attr.id_data_entity; simpleGit.add('.') .commit(commitMsg, function(err, answer){ gitProcesses[originName] = false; From 50e37fae29620d520f1b640a1ef348996c6c66fa Mon Sep 17 00:00:00 2001 From: Syno Date: Fri, 27 Sep 2019 10:15:32 +0200 Subject: [PATCH 05/24] Fix delete component agenda crash error --- services/designer.js | 47 ++++++++++++++++---------------- structure/structure_component.js | 38 ++++++++++++++------------ 2 files changed, 43 insertions(+), 42 deletions(-) diff --git a/services/designer.js b/services/designer.js index a05e12ec1..5c13e50a7 100755 --- a/services/designer.js +++ b/services/designer.js @@ -2526,7 +2526,7 @@ exports.createNewComponentAgenda = function (attr, callback) { exports.deleteAgenda = function (attr, callback) { - var exportsContext = this; + let exportsContext = this; /* If there is no defined name for the module */ if (typeof attr.options.value === "undefined") { @@ -2536,48 +2536,47 @@ exports.deleteAgenda = function (attr, callback) { } // Check if component with this name is in this module - db_component.getComponentByCodeNameInModule(attr.id_module, attr.options.value, attr.options.showValue, function (err, component) { + db_component.getComponentByCodeNameInModule(attr.id_module, attr.options.value, attr.options.showValue, (err, component) => { if (!component) { - var err = new Error("database.component.notFound.notFoundInModule"); + let err = new Error("database.component.notFound.notFoundInModule"); err.messageParams = [attr.options.showValue, attr.id_module]; return callback(err, null); - } else { + } - var showValueEvent = attr.options.showValue + " Event"; - var showValueCategory = attr.options.showValue + " Category"; + let showValueEvent = attr.options.showValue + " Event"; + let showValueCategory = attr.options.showValue + " Category"; - var instructions = [ - "delete entity " + showValueCategory, - "delete entity " + showValueEvent, - ]; + let instructions = [ + "delete entity " + showValueCategory, + "delete entity " + showValueEvent, + ]; - // Start doing necessary instruction for component creation - exportsContext.recursiveInstructionExecute(attr, instructions, 0, function (err) { + // Start doing necessary instruction for component creation + exportsContext.recursiveInstructionExecute(attr, instructions, 0, err => { + if (err) + return callback(err, null); + + // Create the component in newmips database + db_component.deleteComponentOnModule(attr.options.value, attr.id_module, (err, info) => { if (err) return callback(err, null); - // Create the component in newmips database - db_component.deleteComponentOnModule(attr.options.value, attr.id_module, function (err, info) { + db_module.getModuleById(attr.id_module, (err, module) => { if (err) return callback(err, null); - db_module.getModuleById(attr.id_module, function (err, module) { + attr.options.moduleName = module.codeName; + structure_component.deleteAgenda(attr, err => { if (err) return callback(err, null); - attr.options.moduleName = module.codeName; - structure_component.deleteAgenda(attr, function (err) { - if (err) - return callback(err, null); - var info = { - message: "database.component.delete.success" - }; - callback(null, info); + callback(null, { + message: "database.component.delete.success" }); }); }); }); - } + }); }); } diff --git a/structure/structure_component.js b/structure/structure_component.js index d3ccbbfed..235a1a37c 100755 --- a/structure/structure_component.js +++ b/structure/structure_component.js @@ -1,9 +1,9 @@ -var fs = require("fs-extra"); -var domHelper = require('../utils/jsDomHelper'); -var translateHelper = require("../utils/translate"); -var helpers = require("../utils/helpers"); -var printHelper = require("../utils/print_helper"); -var moment = require("moment"); +const fs = require("fs-extra"); +const domHelper = require('../utils/jsDomHelper'); +const translateHelper = require("../utils/translate"); +const helpers = require("../utils/helpers"); +const printHelper = require("../utils/print_helper"); +const moment = require("moment"); function setupComponentModel(idApplication, folderComponent, componentName, filename, callback) { // CREATE MODEL FILE @@ -699,36 +699,38 @@ exports.newAgenda = function (attr, callback) { }); } -exports.deleteAgenda = function (attr, callback) { +exports.deleteAgenda = (attr, callback) => { - var idApplication = attr.id_application; - var urlComponent = attr.options.urlValue.toLowerCase(); + let appID = attr.id_application; + let urlComponent = attr.options.urlValue.toLowerCase(); - var baseFolder = __dirname + '/../workspace/' + idApplication; - var layoutFileName = baseFolder + '/views/layout_' + attr.options.moduleName.toLowerCase() + '.dust'; + let baseFolder = __dirname + '/../workspace/' + appID; + let layoutFileName = baseFolder + '/views/layout_' + attr.options.moduleName.toLowerCase() + '.dust'; + + // Remove agenda controller + fs.unlinkSync(baseFolder + '/routes/' + attr.options.value + '.js'); // Delete views folder helpers.rmdirSyncRecursive(baseFolder + '/views/' + attr.options.value); - domHelper.read(layoutFileName).then(function ($) { - + domHelper.read(layoutFileName).then($ => { $("#" + urlComponent + "_menu_item").remove(); // Write back to file - domHelper.write(layoutFileName, $).then(function () { + domHelper.write(layoutFileName, $).then(_ => { // Clean empty and useless dust helper created by removing
  • - var layoutContent = fs.readFileSync(layoutFileName, 'utf8'); + let layoutContent = fs.readFileSync(layoutFileName, 'utf8'); // Remove empty dust helper layoutContent = layoutContent.replace(/{#entityAccess entity=".+"}\W*{\/entityAccess}/g, ""); - var writeStream = fs.createWriteStream(layoutFileName); + let writeStream = fs.createWriteStream(layoutFileName); writeStream.write(layoutContent); writeStream.end(); - writeStream.on('finish', function () { + writeStream.on('finish', _ => { callback(); }); }); - }).catch(function (err) { + }).catch(err => { callback(err, null); }); } From a36fbd51dc93b5f998c4b348a52533a6183188db Mon Sep 17 00:00:00 2001 From: Syno Date: Fri, 27 Sep 2019 11:16:00 +0200 Subject: [PATCH 06/24] Fix reset password generator host url --- config/mail.js | 4 +- locales/en-EN.json | 3 +- locales/fr-FR.json | 3 +- routes/routes.js | 12 ++- structure/structure_data_field.js | 3 + structure/template/server.js | 8 +- utils/mailer.js | 136 +++++++++++++++--------------- 7 files changed, 92 insertions(+), 77 deletions(-) diff --git a/config/mail.js b/config/mail.js index 1e02f0cda..c0dfefec9 100755 --- a/config/mail.js +++ b/config/mail.js @@ -55,7 +55,7 @@ let mailConf = { }, expediteur: 'NoReply ', administrateur: 'Responsable Newmips ', - host: 'https://'+process.env.HOSTNAME+'.newmips.studio' + host: 'https://'+process.env.SUB_DOMAIN+'.newmips.studio' }, cloud: { transport: { @@ -69,7 +69,7 @@ let mailConf = { }, expediteur: 'NoReply ', administrateur: 'Responsable Newmips ', - host: 'https://'+process.env.HOSTNAME+'.newmips.studio' + host: 'https://'+process.env.SUB_DOMAIN+'.newmips.studio' } } diff --git a/locales/en-EN.json b/locales/en-EN.json index f494d1c89..f48333917 100755 --- a/locales/en-EN.json +++ b/locales/en-EN.json @@ -13,7 +13,8 @@ "success": "Update completed. You could now log in with your username and password", "hasAlreadyPassword": "Can not update. This user already has a password.", "userNotExist": "Error, this user doesn't exist.", - "passwordNotMatch": "Sorry, passwords do not match. Password must contain at least 8 characters." + "passwordNotMatch": "Sorry, passwords do not match. Password must contain at least 8 characters.", + "userNotActivate": "Your account is not activated, please do the first login procedure." }, "authentication": "Connection", "login": "Login", diff --git a/locales/fr-FR.json b/locales/fr-FR.json index 837970af9..501df21d2 100644 --- a/locales/fr-FR.json +++ b/locales/fr-FR.json @@ -13,7 +13,8 @@ "success": "Mise à jour effectuée. Vous pouvez désormais vous connecter avec votre identifiant et votre nouveau mot de passe.", "hasAlreadyPassword": "Mise à jour impossible. Cet utilisateur possède déjà un mot de passe.", "userNotExist": "Erreur, cet utilisateur n'existe pas.", - "passwordNotMatch": "Désolé, les mots de passe ne correspondent pas. Le mot de passe doit faire minimum 8 caractères." + "passwordNotMatch": "Désolé, les mots de passe ne correspondent pas. Le mot de passe doit faire minimum 8 caractères.", + "userNotActivate": "Votre compte n'est pas activé, merci de faire la procédure de première connexion." }, "authentication": "Connexion", "login": "Login", diff --git a/routes/routes.js b/routes/routes.js index 3e0783885..b19971fad 100755 --- a/routes/routes.js +++ b/routes/routes.js @@ -183,7 +183,7 @@ router.post('/reset_password', block_access.loginAccess, function(req, res) { login: req.body.login.toLowerCase(), email: req.body.mail } - }).then(function(user){ + }).then(user => { if(!user){ req.session.toastr = [{ message: "login.first_connection.userNotExist", @@ -192,8 +192,16 @@ router.post('/reset_password', block_access.loginAccess, function(req, res) { return res.render('login/reset_password'); } + if(!user.password && !user.enabled){ + req.session.toastr = [{ + message: "login.first_connection.userNotActivate", + level: "error" + }]; + return res.redirect('/first_connection'); + } + // Create unique token and insert into user - var token = crypto.randomBytes(64).toString('hex'); + let token = crypto.randomBytes(64).toString('hex'); models.User.update({ token_password_reset: token diff --git a/structure/structure_data_field.js b/structure/structure_data_field.js index ef256406e..33b0c3d67 100755 --- a/structure/structure_data_field.js +++ b/structure/structure_data_field.js @@ -968,6 +968,8 @@ exports.setRequiredAttribute = function (attr, callback) { var length = ""; if (attr.sqlDataType == "varchar") length = "(" + attr.sqlDataTypeLength + ")"; + + // Set required if (set) { switch (attributesObj[attr.options.value].type) { case "STRING": @@ -1002,6 +1004,7 @@ exports.setRequiredAttribute = function (attr, callback) { toSync.queries.push("ALTER TABLE `" + tableName + "` ALTER `" + attr.options.value + "` SET DEFAULT '" + defaultValue + "';"); } } else { + // Set optional attributesObj[attr.options.value].defaultValue = null; // TODO postgres if (attr.sqlDataType && attr.dialect == "mysql") { diff --git a/structure/template/server.js b/structure/template/server.js index 9996655f7..70944db66 100755 --- a/structure/template/server.js +++ b/structure/template/server.js @@ -70,11 +70,11 @@ app.use(morgan('dev', { require('console-stamp')(console, { formatter: function() { - return moment().format('YYYY-MM-DD HH:mm:ss-SSS'); + return moment().format('MM-DD HH:mm:ss'); }, - label: true, - datePrefix: "[", - dateSuffix: "]-- " + label: false, + datePrefix: "", + dateSuffix: "" }); // Overide console.warn & console.error to file+line diff --git a/utils/mailer.js b/utils/mailer.js index 3ce841e4d..8c916e20d 100755 --- a/utils/mailer.js +++ b/utils/mailer.js @@ -1,80 +1,82 @@ -var mailConfig = require('../config/mail'); -var nodemailer = require('nodemailer'); - -var transporter = nodemailer.createTransport(mailConfig.transport); +const mailConfig = require('../config/mail'); +const nodemailer = require('nodemailer'); +const transporter = nodemailer.createTransport(mailConfig.transport); function sendMail(mailOptions, res) { - transporter.sendMail(mailOptions, function(error, info){ - if(error){ - res("Problème d'envoi du mail."); - }else{ - res("Mail envoyé."); - } - }); + transporter.sendMail(mailOptions, (error, info) => { + if (error) { + res("Problème d'envoi du mail."); + } else { + res("Mail envoyé."); + } + }); } function sendMailAsync(mailOptions) { - return new Promise(function(resolve, reject) { - transporter.sendMail(mailOptions, function(error, info){ - if(error) { - console.error(error); - reject(error); - } - resolve(true); + return new Promise((resolve, reject) => { + transporter.sendMail(mailOptions, (error, info) => { + if (error) { + console.error(error); + reject(error); + } + resolve(true); + }); }); - }); } -exports.sendMail_Reset_Password = function(data, res) { - var mailOptions = { - from: mailConfig.expediteur, - to: data["mail_user"], - subject: 'Newmips, modification de mot de passe', - html: ''+ - ''+ - ''+ - 'Bonjour, ' + - '
    ' + - '
    ' + - 'Une demande de réinitialisation de mot de passe a été effectuée pour votre compte : '+ data['mail_user'] + '.' + - '
    ' + - 'Si vous êtes à l\'origine de cette demande, veuillez cliquer sur le lien suivant :' + - '
    ' + - 'Réinitialisation du mot de passe.'+ - '
    ' + - '
    ' + - 'Si vous n\'êtes pas à l\'origine de cette demande, veuillez ignorer cet email.' + - '
    ' + - '
    ' + - 'Newmips'+ - '' +exports.sendMail_Reset_Password = (data, res) => { + let mailOptions = { + from: mailConfig.expediteur, + to: data.mail_user, + subject: 'Newmips, modification de mot de passe', + html: '\ + \ + \ + \ + \ + \ + Bonjour,\ +

    \ + Une demande de réinitialisation de mot de passe a été effectuée pour votre compte : ' + data.mail_user + '.\ +
    \ + Si vous êtes à l\'origine de cette demande, veuillez cliquer sur le lien suivant :\ +
    \ + Réinitialisation du mot de passe.\ +

    \ + Si vous n\'êtes pas à l\'origine de cette demande, veuillez ignorer cet email.\ +

    \ + Newmips\ + \ + ' }; return sendMailAsync(mailOptions); } // Mails relatifs à la connexion d'un utilisation -exports.sendMail_Activation_Compte = function(data, res){ - var mailOptions = { - from: mailConfig.expediteur, - to: data["mail_user"], - subject: 'Bienvenue sur Newmips', - html: ''+ - ''+ - ''+ - 'Bonjour, ' + - '
    ' + - '
    ' + - 'Vous trouverez ci-joint votre identifiant de connexion sur l\'espace privé Newmips.'+ - '
    ' + - 'Identifiant : ' + data["login"] + - '
    ' + - 'Pour activer votre compte, vous devez créer un mot de passe en utilisant le lien ci-dessous.'+ - '
    ' + - 'Première connexion.'+ - '
    ' + - '
    ' + - 'Newmips'+ - '' - }; - return sendMail(mailOptions, res); -} +exports.sendMail_Activation_Compte = (data, res) => { + let mailOptions = { + from: mailConfig.expediteur, + to: data.mail_user, + subject: 'Bienvenue sur Newmips', + html: '\ + \ + \ + \ + \ + \ + Bonjour,\ +

    \ + Vous trouverez ci-joint votre identifiant de connexion sur l\'espace privé Newmips.\ +
    \ + Identifiant : ' + data.login + '\ +
    \ + Pour activer votre compte, vous devez créer un mot de passe en utilisant le lien ci-dessous.\ +
    \ + Première connexion.\ +

    \ + Newmips\ + \ + ' + }; + return sendMail(mailOptions, res); +} \ No newline at end of file From a7d87c1584ce4efca7df7175fa485e2283be1ffe Mon Sep 17 00:00:00 2001 From: Syno Date: Fri, 27 Sep 2019 14:23:22 +0200 Subject: [PATCH 07/24] Fix set unique field when separate workspace DB --- services/designer.js | 27 +++++++++++++++++---------- structure/structure_application.js | 16 ++++++++-------- structure/template/config/database.js | 4 ++-- 3 files changed, 27 insertions(+), 20 deletions(-) diff --git a/services/designer.js b/services/designer.js index 5c13e50a7..976b642e0 100755 --- a/services/designer.js +++ b/services/designer.js @@ -284,6 +284,7 @@ function deleteApplication(attr, callback) { let err = new Error("You do not have access to this application, you cannot delete it.") return callback(err, null); } + structure_application.deleteApplication(id_application, function (err, infoStructure) { if (err) return callback(err, null); @@ -311,6 +312,7 @@ function deleteApplication(attr, callback) { var request = ""; if(sequelize.options.dialect == "mysql") request += "SET FOREIGN_KEY_CHECKS=0;"; + for (var i = 0; i < results.length; i++) { for (var prop in results[i]) { // Postgres additionnal check @@ -324,8 +326,10 @@ function deleteApplication(attr, callback) { } } } + if(sequelize.options.dialect == "mysql") request += "SET FOREIGN_KEY_CHECKS=1;"; + sequelize.query(request).then(function () { callback(null, infoDB); }).catch(function(err){ @@ -1002,27 +1006,30 @@ exports.setFieldKnownAttribute = function (attr, callback) { }) } else if (uniqueAttribute.indexOf(wordParam) != -1) { - var sourceEntity = attr.id_application + "_" + attr.name_data_entity; - var constraintName = attr.id_application + "_" + attr.name_data_entity + "_" + attr.options.value + "_unique"; + let sourceEntity = attr.id_application + "_" + attr.name_data_entity; + let constraintName = sourceEntity + "_" + attr.options.value + "_unique"; - var possibilityUnique = ["unique"]; - var possibilityNotUnique = ["not-unique", "non-unique"]; + let possibilityUnique = ["unique"]; + let possibilityNotUnique = ["not-unique", "non-unique"]; - var attribute = attr.options.word.toLowerCase(); - var request = ""; + let attribute = attr.options.word.toLowerCase(); + let request = ""; + + // Get application database, it won't be newmips if seperate DB + let appDBConf = require(__dirname+'/../workspace/' + attr.id_application + '/config/database.js'); // Add or remove the unique constraint ? if(sequelize.options.dialect == "mysql"){ if (possibilityUnique.indexOf(attribute) != -1) { - request = "ALTER TABLE `" + sourceEntity + "` ADD CONSTRAINT " + constraintName + " UNIQUE (`" + attr.options.value + "`);"; + request = "ALTER TABLE `" + appDBConf.database + "`.`" + sourceEntity + "` ADD CONSTRAINT " + constraintName + " UNIQUE (`" + attr.options.value + "`);"; } else if (possibilityNotUnique.indexOf(attribute) != -1) { - request = "ALTER TABLE `" + sourceEntity + "` DROP INDEX `" + constraintName + "`;"; + request = "ALTER TABLE `" + appDBConf.database + "`.`" + sourceEntity + "` DROP INDEX `" + constraintName + "`;"; } } else if (sequelize.options.dialect == "postgres"){ if (possibilityUnique.indexOf(attribute) != -1) { - request = "ALTER TABLE \"" + sourceEntity + "\" ADD CONSTRAINT \"" + constraintName + "\" UNIQUE (" + attr.options.value + ");"; + request = "ALTER TABLE \"" + appDBConf.database + "\".\"" + sourceEntity + "\" ADD CONSTRAINT \"" + constraintName + "\" UNIQUE (" + attr.options.value + ");"; } else if (possibilityNotUnique.indexOf(attribute) != -1) { - request = "ALTER TABLE \"" + sourceEntity + "\" DROP INDEX \"" + constraintName + "\";"; + request = "ALTER TABLE \"" + appDBConf.database + "\".\"" + sourceEntity + "\" DROP INDEX \"" + constraintName + "\";"; } } diff --git a/structure/structure_application.js b/structure/structure_application.js index 90605c4b1..b1c25521b 100755 --- a/structure/structure_application.js +++ b/structure/structure_application.js @@ -105,16 +105,16 @@ exports.setupApplication = function(attr, callback) { ]; let conn = await mysql.createConnection({ - host: globalConf.env == "cloud"||"docker" ? process.env.DATABASE_IP : dbConf.host, - user: globalConf.env == "cloud"||"docker" ? "root" : dbConf.user, - password: globalConf.env == "cloud"||"docker" ? "P@ssw0rd+" : dbConf.password + host: globalConf.env == "cloud" || globalConf.env == "docker" ? process.env.DATABASE_IP : dbConf.host, + user: globalConf.env == "cloud" || globalConf.env == "docker" ? "root" : dbConf.user, + password: globalConf.env == "cloud" || globalConf.env == "docker" ? "P@ssw0rd+" : dbConf.password }); - for (var i = 0; i < db_requests.length; i++) { + for (var i = 0; i < db_requests.length; i++) await conn.query(db_requests[i]); - } conn.end(); + return; } workspace_db().then(_ => { @@ -625,9 +625,9 @@ exports.deleteApplication = function(appID, callback) { if(globalConf.separate_workspace_db){ (async () => { let conn = await mysql.createConnection({ - host: globalConf.env == "cloud"||"docker" ? process.env.DATABASE_IP : dbConf.host, - user: globalConf.env == "cloud"||"docker" ? "root" : dbConf.user, - password: globalConf.env == "cloud"||"docker" ? "P@ssw0rd+" : dbConf.password + host: globalConf.env == "cloud" || globalConf.env == "docker" ? process.env.DATABASE_IP : dbConf.host, + user: globalConf.env == "cloud" || globalConf.env == "docker" ? "root" : dbConf.user, + password: globalConf.env == "cloud" || globalConf.env == "docker" ? "P@ssw0rd+" : dbConf.password }); await conn.query("DROP DATABASE IF EXISTS workspace_"+appID+";"); conn.end(); diff --git a/structure/template/config/database.js b/structure/template/config/database.js index 3666a1e0f..7cf9bc80e 100755 --- a/structure/template/config/database.js +++ b/structure/template/config/database.js @@ -1,6 +1,6 @@ -var globalConf = require('./global'); +const globalConf = require('./global'); -var databaseConf = { +let databaseConf = { develop: { host: '127.0.0.1', port: '3306', //mysql: 3306 - postgres: 5432 From a679dfd78e31130845d001939c95f560ed2ed134 Mon Sep 17 00:00:00 2001 From: Syno Date: Mon, 30 Sep 2019 09:04:36 +0200 Subject: [PATCH 08/24] Fix french traduction --- locales/fr-FR.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/locales/fr-FR.json b/locales/fr-FR.json index 501df21d2..d230a065a 100644 --- a/locales/fr-FR.json +++ b/locales/fr-FR.json @@ -238,7 +238,7 @@ "textthree": "simplement revenir en arrière.", "404": "Le lien que vous avez suivi peut être incorrect ou la page peut avoir été supprimée.", "valueTooLong": "Désolé, la valeur renseignée dépasse la longueur maximum autorisée (<30) après génération (%s).", - "cannotFindInstruction": "Désolé, je ne comprend pas ce que vous me demandez.", + "cannotFindInstruction": "Désolé, je ne comprends pas ce que vous me demandez.", "missingParametersInstruction": "Ils manque des paramètres à votre instruction. Consultez la documentation" }, "back":{ From 916f5389d4bef10488d48790a2a80284f5659fc1 Mon Sep 17 00:00:00 2001 From: Syno Date: Mon, 30 Sep 2019 10:48:10 +0200 Subject: [PATCH 09/24] Fix agenda all day update --- .../component/agenda/routes/route_agenda.js | 34 +++++++++---------- .../public/js/Newmips/component/agenda.js | 1 + 2 files changed, 17 insertions(+), 18 deletions(-) diff --git a/structure/pieces/component/agenda/routes/route_agenda.js b/structure/pieces/component/agenda/routes/route_agenda.js index f8359dc70..197174037 100644 --- a/structure/pieces/component/agenda/routes/route_agenda.js +++ b/structure/pieces/component/agenda/routes/route_agenda.js @@ -1,20 +1,17 @@ -var express = require('express'); -var router = express.Router(); -var block_access = require('../utils/block_access'); +const express = require('express'); +const router = express.Router(); +const block_access = require('../utils/block_access'); // Sequelize -var models = require('../models/'); +const models = require('../models/'); -var model_builder = require('../utils/model_builder'); -var moment = require("moment"); +const model_builder = require('../utils/model_builder'); +const moment = require("moment"); -var attributes = require('../models/attributes/e_URL_ROUTE_event'); -var options = require('../models/options/e_URL_ROUTE_event'); -var model_builder = require('../utils/model_builder'); -var entity_helper = require('../utils/entity_helper'); - -// Winston logger -var logger = require('../utils/logger'); +const attributes = require('../models/attributes/e_URL_ROUTE_event'); +const options = require('../models/options/e_URL_ROUTE_event'); +const model_builder = require('../utils/model_builder'); +const entity_helper = require('../utils/entity_helper'); router.get('/', block_access.isLoggedIn, function(req, res) { var data = {}; @@ -145,16 +142,17 @@ router.post('/update_event', block_access.actionAccessMiddleware("URL_ROUTE_even }); }); -router.post('/update_event_drop', block_access.actionAccessMiddleware("URL_ROUTE_event", 'update'), function(req, res) { +router.post('/update_event_drop', block_access.actionAccessMiddleware("agenda_event", 'update'), function(req, res) { - var updateObj = { + let updateObj = { f_start_date: req.body.start, - f_end_date: req.body.end + f_end_date: req.body.end, + f_all_day: typeof req.body.allDay === 'boolean' ? req.body.allDay : false }; - models.CODE_NAME_EVENT_MODEL.findById(req.body.eventId).then(function(currentEvent){ + models.E_agenda_event.findById(req.body.eventId).then(function(currentEvent){ currentEvent.update(updateObj, {where: {id: req.body.eventId}}).then(function(updateEvent){ - var users = []; + let users = []; if(req.body.idUsers != null){ users = req.body.idUsers; } else if (req.body.idUser != null){ diff --git a/structure/template/public/js/Newmips/component/agenda.js b/structure/template/public/js/Newmips/component/agenda.js index aa1a35690..9d0240cef 100644 --- a/structure/template/public/js/Newmips/component/agenda.js +++ b/structure/template/public/js/Newmips/component/agenda.js @@ -134,6 +134,7 @@ $(document).ready(function() { eventId: event.eventId, start: startDate, end: endDate, + allDay: event.allDay, idUser: event.resourceId || null, idUsers: event.resourceIds || null }; From 5690b16e835a3685fb4ea568589b7aba6c8ed8f2 Mon Sep 17 00:00:00 2001 From: Syno Date: Mon, 30 Sep 2019 11:27:32 +0200 Subject: [PATCH 10/24] Duplicate const in agenda controller --- structure/pieces/component/agenda/routes/route_agenda.js | 5 ----- 1 file changed, 5 deletions(-) diff --git a/structure/pieces/component/agenda/routes/route_agenda.js b/structure/pieces/component/agenda/routes/route_agenda.js index 197174037..fcd3fac1d 100644 --- a/structure/pieces/component/agenda/routes/route_agenda.js +++ b/structure/pieces/component/agenda/routes/route_agenda.js @@ -1,16 +1,11 @@ const express = require('express'); const router = express.Router(); const block_access = require('../utils/block_access'); - -// Sequelize const models = require('../models/'); - const model_builder = require('../utils/model_builder'); const moment = require("moment"); - const attributes = require('../models/attributes/e_URL_ROUTE_event'); const options = require('../models/options/e_URL_ROUTE_event'); -const model_builder = require('../utils/model_builder'); const entity_helper = require('../utils/entity_helper'); router.get('/', block_access.isLoggedIn, function(req, res) { From 74943f20d9fcc8cfce38344e9f6ee1b9cf4b4204 Mon Sep 17 00:00:00 2001 From: Syno Date: Mon, 30 Sep 2019 12:05:14 +0200 Subject: [PATCH 11/24] Add same alias verification --- services/designer.js | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/services/designer.js b/services/designer.js index 976b642e0..3ce44b026 100755 --- a/services/designer.js +++ b/services/designer.js @@ -1202,6 +1202,11 @@ exports.createNewHasOne = function (attr, callback) { return callback(err, null); } } + } else if(attr.options.as == optionsSourceObject[i].as){ + let err = new Error(); + err.message = "database.field.error.alreadyExist"; + err.messageParams = [attr.options.showAs]; + return callback(err, null); } } @@ -1468,6 +1473,11 @@ exports.createNewHasMany = function (attr, callback) { return callback(err, null); } } + } else if(attr.options.as == optionsSourceObject[i].as){ + let err = new Error(); + err.message = "database.field.error.alreadyExist"; + err.messageParams = [attr.options.showAs]; + return callback(err, null); } } @@ -1695,7 +1705,7 @@ exports.createNewHasManyPreset = function (attr, callback) { let optionsSourceObject = JSON.parse(optionsSourceFile); let toSync = true; let saveFile = false; - // Vérification si une relation existe déjà de la source VERS la target + // Vérification si une relation existe déjà avec cet alias for (var i = 0; i < optionsSourceObject.length; i++) { if (optionsSourceObject[i].target.toLowerCase() == attr.options.target.toLowerCase()) { // If alias already used @@ -1709,6 +1719,11 @@ exports.createNewHasManyPreset = function (attr, callback) { return callback(err, null); } } + } else if(attr.options.as == optionsSourceObject[i].as){ + let err = new Error(); + err.message = "database.field.error.alreadyExist"; + err.messageParams = [attr.options.showAs]; + return callback(err, null); } } From f440b63b90d0c44d1d497f3e63caea350d09abfa Mon Sep 17 00:00:00 2001 From: Syno Date: Mon, 30 Sep 2019 18:17:42 +0200 Subject: [PATCH 12/24] Fix missing condition about already exist field for related to and related to many --- services/designer.js | 516 ++++++++++++++++++++++--------------------- 1 file changed, 270 insertions(+), 246 deletions(-) diff --git a/services/designer.js b/services/designer.js index 3ce44b026..bddcbd7a3 100755 --- a/services/designer.js +++ b/services/designer.js @@ -1827,295 +1827,319 @@ exports.createNewHasManyPreset = function (attr, callback) { // Create a field in create/show/update related to target entity exports.createNewFieldRelatedTo = function (attr, callback) { - // Instruction is add field _FOREIGNKEY_ related to _TARGET_ -> We don't know the source entity name - db_entity.getDataEntityById(attr.id_data_entity, function (err, source_entity) { - if (err && typeof attr.options.source === "undefined") + // Check if a field with this name already exist + db_field.getFieldByCodeName({ + codeName: 'f_' + attr.options.urlAs, + idEntity: attr.id_data_entity + }, (err, field) => { + if(field){ + var err = new Error(); + err.message = "database.field.error.alreadyExist"; + err.messageParams = [attr.options.showAs]; return callback(err, null); - // With preset instruction with already know the source of the related to - // "entity (.*) has one preset (.*) called (.*) using (.*)" - if (typeof attr.options.source === "undefined") { - attr.options.source = source_entity.codeName; - attr.options.showSource = source_entity.name; - attr.options.urlSource = attrHelper.removePrefix(source_entity.codeName, "entity"); } - // Vérifie que la target existe bien avant de creer la source et la clé étrangère (foreign key) - db_entity.selectEntityTarget(attr, function (err, dataEntity) { - // If target entity doesn't exists, send error - if (err) + // Instruction is add field _FOREIGNKEY_ related to _TARGET_ -> We don't know the source entity name + db_entity.getDataEntityById(attr.id_data_entity, function (err, source_entity) { + if (err && typeof attr.options.source === "undefined") return callback(err, null); - var allUsingExist = true; - // If a using field or fields has been asked, we have to check if those fields exist in the entity - if (typeof attr.options.usingField !== "undefined") { - var attributesPath = __dirname + '/../workspace/' + attr.id_application + '/models/attributes/' + attr.options.target.toLowerCase() - delete require.cache[require.resolve(attributesPath)]; - var attributeTarget = require(attributesPath); - for (var i = 0; i < attr.options.usingField.length; i++) { - if (typeof attributeTarget[attr.options.usingField[i]] === "undefined") { - allUsingExist = false; - var missingField = attr.options.showUsingField[i]; - } else { - attr.options.usingField[i] = { - value: attr.options.usingField[i], - type: attributeTarget[attr.options.usingField[i]].newmipsType + // With preset instruction with already know the source of the related to + // "entity (.*) has one preset (.*) called (.*) using (.*)" + if (typeof attr.options.source === "undefined") { + attr.options.source = source_entity.codeName; + attr.options.showSource = source_entity.name; + attr.options.urlSource = attrHelper.removePrefix(source_entity.codeName, "entity"); + } + // Vérifie que la target existe bien avant de creer la source et la clé étrangère (foreign key) + db_entity.selectEntityTarget(attr, function (err, dataEntity) { + // If target entity doesn't exists, send error + if (err) + return callback(err, null); + var allUsingExist = true; + // If a using field or fields has been asked, we have to check if those fields exist in the entity + if (typeof attr.options.usingField !== "undefined") { + var attributesPath = __dirname + '/../workspace/' + attr.id_application + '/models/attributes/' + attr.options.target.toLowerCase() + delete require.cache[require.resolve(attributesPath)]; + var attributeTarget = require(attributesPath); + for (var i = 0; i < attr.options.usingField.length; i++) { + if (typeof attributeTarget[attr.options.usingField[i]] === "undefined") { + allUsingExist = false; + var missingField = attr.options.showUsingField[i]; + } else { + attr.options.usingField[i] = { + value: attr.options.usingField[i], + type: attributeTarget[attr.options.usingField[i]].newmipsType + } } } } - } - // If a asked using field doesn't exist in the target entity we send an error - if (!allUsingExist) { - var err = new Error("structure.association.relatedTo.missingField"); - err.messageParams = [missingField, attr.options.showTarget]; - return callback(err, null); - } - // Check if an association already exists from source to target - let sourceOptionsPath = __dirname+'/../workspace/' + attr.id_application + '/models/options/' + attr.options.source.toLowerCase() + '.json'; - let optionsSourceObject = JSON.parse(helpers.readFileSyncWithCatch(sourceOptionsPath)); - let toSync = true; - let constraints = true; - let saveFile = false; - // Check if an association already exists with the same alias - for (var i = 0; i < optionsSourceObject.length; i++) { - if (optionsSourceObject[i].target.toLowerCase() == attr.options.target.toLowerCase()) { - // If alias already used - if (attr.options.as == optionsSourceObject[i].as){ - if(optionsSourceObject[i].structureType == "auto_generate") { - // Remove auto generate key by the generator - optionsSourceObject.splice(i, 1); - saveFile = true; - } else - return callback(new Error("structure.association.error.alreadySameAlias"), null); - } - } else if(attr.options.as == optionsSourceObject[i].as){ - let err = new Error(); - err.message = "database.field.error.alreadyExist"; - err.messageParams = [attr.options.showAs]; + // If a asked using field doesn't exist in the target entity we send an error + if (!allUsingExist) { + var err = new Error("structure.association.relatedTo.missingField"); + err.messageParams = [missingField, attr.options.showTarget]; return callback(err, null); } - } - - // Changes to be saved, remove auto_generate key - if(saveFile) - fs.writeFileSync(sourceOptionsPath, JSON.stringify(optionsSourceObject, null, 4), "utf8"); - - // Check if an association already exists from target to source - var optionsFile = helpers.readFileSyncWithCatch(__dirname+'/../workspace/' + attr.id_application + '/models/options/' + attr.options.target.toLowerCase() + '.json'); - var optionsObject = JSON.parse(optionsFile); - for (var i = 0; i < optionsObject.length; i++) { - if (optionsObject[i].target.toLowerCase() == attr.options.source.toLowerCase() && optionsObject[i].relation != "hasMany" && optionsObject[i].relation != "belongsToMany") { - constraints = false; - } else if (attr.options.source.toLowerCase() != attr.options.target.toLowerCase() - && (optionsObject[i].target.toLowerCase() == attr.options.source.toLowerCase() && optionsObject[i].relation == "hasMany") - && (optionsObject[i].foreignKey == attr.options.foreignKey)) { - // We avoid the toSync to append because the already existing has many relation has already created the foreign key in BDD - toSync = false; + // Check if an association already exists from source to target + let sourceOptionsPath = __dirname+'/../workspace/' + attr.id_application + '/models/options/' + attr.options.source.toLowerCase() + '.json'; + let optionsSourceObject = JSON.parse(helpers.readFileSyncWithCatch(sourceOptionsPath)); + let toSync = true; + let constraints = true; + let saveFile = false; + // Check if an association already exists with the same alias + for (var i = 0; i < optionsSourceObject.length; i++) { + if (optionsSourceObject[i].target.toLowerCase() == attr.options.target.toLowerCase()) { + // If alias already used + if (attr.options.as == optionsSourceObject[i].as){ + if(optionsSourceObject[i].structureType == "auto_generate") { + // Remove auto generate key by the generator + optionsSourceObject.splice(i, 1); + saveFile = true; + } else + return callback(new Error("structure.association.error.alreadySameAlias"), null); + } + } else if(attr.options.as == optionsSourceObject[i].as){ + let err = new Error(); + err.message = "database.field.error.alreadyExist"; + err.messageParams = [attr.options.showAs]; + return callback(err, null); + } } - } - // Add foreign key to newmips's DB - db_field.createNewForeignKey(attr, function (err, created_foreignKey) { - if (err) - return callback(err, null); - // Créer le lien belongsTo en la source et la target dans models/options/source.json - var associationOption = { - idApp: attr.id_application, - source: attr.options.source, - target: attr.options.target, - foreignKey: attr.options.foreignKey, - as: attr.options.as, - showAs: attr.options.showAs, - relation: "belongsTo", - through: null, - toSync: true, - type: "relatedTo", - constraints: constraints - }; - if (typeof attr.options.usingField !== "undefined") { - associationOption.usingField = attr.options.usingField; + + // Changes to be saved, remove auto_generate key + if(saveFile) + fs.writeFileSync(sourceOptionsPath, JSON.stringify(optionsSourceObject, null, 4), "utf8"); + + // Check if an association already exists from target to source + var optionsFile = helpers.readFileSyncWithCatch(__dirname+'/../workspace/' + attr.id_application + '/models/options/' + attr.options.target.toLowerCase() + '.json'); + var optionsObject = JSON.parse(optionsFile); + for (var i = 0; i < optionsObject.length; i++) { + if (optionsObject[i].target.toLowerCase() == attr.options.source.toLowerCase() && optionsObject[i].relation != "hasMany" && optionsObject[i].relation != "belongsToMany") { + constraints = false; + } else if (attr.options.source.toLowerCase() != attr.options.target.toLowerCase() + && (optionsObject[i].target.toLowerCase() == attr.options.source.toLowerCase() && optionsObject[i].relation == "hasMany") + && (optionsObject[i].foreignKey == attr.options.foreignKey)) { + // We avoid the toSync to append because the already existing has many relation has already created the foreign key in BDD + toSync = false; + } } + // Add foreign key to newmips's DB + db_field.createNewForeignKey(attr, function (err, created_foreignKey) { + if (err) + return callback(err, null); + // Créer le lien belongsTo en la source et la target dans models/options/source.json + var associationOption = { + idApp: attr.id_application, + source: attr.options.source, + target: attr.options.target, + foreignKey: attr.options.foreignKey, + as: attr.options.as, + showAs: attr.options.showAs, + relation: "belongsTo", + through: null, + toSync: true, + type: "relatedTo", + constraints: constraints + }; + if (typeof attr.options.usingField !== "undefined") { + associationOption.usingField = attr.options.usingField; + } - var reversedOption = { - idApp: attr.id_application, - source: attr.options.target, - target: attr.options.source, - foreignKey: attr.options.foreignKey, - as: "r_"+attr.options.source.substring(2), - relation: "hasMany", - type: "auto_generate", - constraints: constraints - }; - structure_data_entity.setupAssociation(associationOption, function () { - structure_data_entity.setupAssociation(reversedOption, function () { - // Ajouter le field d'assocation dans create_fields/update_fields. Ajout d'un tab dans le show - structure_data_field.setupRelatedToField(attr, function (err, data) { - if (err){return callback(err, null);} - // Stay on the source entity in session - var info = {}; - info.insertId = attr.id_data_entity; - info.message = "structure.association.relatedTo.success"; - info.messageParams = [attr.options.showAs, attr.options.showTarget, attr.options.showAs, attr.options.showAs, attr.options.showAs]; - callback(null, info); + var reversedOption = { + idApp: attr.id_application, + source: attr.options.target, + target: attr.options.source, + foreignKey: attr.options.foreignKey, + as: "r_"+attr.options.source.substring(2), + relation: "hasMany", + type: "auto_generate", + constraints: constraints + }; + structure_data_entity.setupAssociation(associationOption, function () { + structure_data_entity.setupAssociation(reversedOption, function () { + // Ajouter le field d'assocation dans create_fields/update_fields. Ajout d'un tab dans le show + structure_data_field.setupRelatedToField(attr, function (err, data) { + if (err){return callback(err, null);} + // Stay on the source entity in session + var info = {}; + info.insertId = attr.id_data_entity; + info.message = "structure.association.relatedTo.success"; + info.messageParams = [attr.options.showAs, attr.options.showTarget, attr.options.showAs, attr.options.showAs, attr.options.showAs]; + callback(null, info); + }) }) }) }) }) - }) - }) + }); + }); } // Select multiple in create/show/update related to target entity exports.createNewFieldRelatedToMultiple = function (attr, callback) { var exportsContext = this; - // Instruction is add field _FOREIGNKEY_ related to multiple _TARGET_ -> We don't know the source entity name so we have to find it - db_entity.getDataEntityById(attr.id_data_entity, function (err, source_entity) { - if (err && typeof attr.options.source === "undefined") + // Check if a field with this name already exist + db_field.getFieldByCodeName({ + codeName: 'f_' + attr.options.urlAs, + idEntity: attr.id_data_entity + }, (err, field) => { + if(field){ + var err = new Error(); + err.message = "database.field.error.alreadyExist"; + err.messageParams = [attr.options.showAs]; return callback(err, null); - - // With preset instruction with already know the source of the related to - // "entity (.*) has one preset (.*) called (.*) using (.*)" - if (typeof attr.options.source === "undefined") { - attr.options.source = source_entity.codeName; - attr.options.showSource = source_entity.name; - attr.options.urlSource = attrHelper.removePrefix(source_entity.codeName, "entity"); } + // Instruction is add field _FOREIGNKEY_ related to multiple _TARGET_ -> We don't know the source entity name so we have to find it + db_entity.getDataEntityById(attr.id_data_entity, (err, source_entity) => { + if (err && typeof attr.options.source === "undefined") + return callback(err, null); - // Now we know the source entity, so we can generate the foreign key - attr.options.foreignKey = "fk_id_" + attr.options.source + "_" + attr.options.as.toLowerCase().substring(2); + // With preset instruction with already know the source of the related to + // "entity (.*) has one preset (.*) called (.*) using (.*)" + if (typeof attr.options.source === "undefined") { + attr.options.source = source_entity.codeName; + attr.options.showSource = source_entity.name; + attr.options.urlSource = attrHelper.removePrefix(source_entity.codeName, "entity"); + } - // Vérifie que la target existe bien avant de creer la source et la clé étrangère (foreign key) - db_entity.selectEntityTarget(attr, (err, entityTarget) => { - // If target entity doesn't exists, send error - if (err) - return callback(err, null); + // Now we know the source entity, so we can generate the foreign key + attr.options.foreignKey = "fk_id_" + attr.options.source + "_" + attr.options.as.toLowerCase().substring(2); - var allUsingExist = true; - - // If a using field or fields has been asked, we have to check if those fields exist in the entity - if (typeof attr.options.usingField !== "undefined") { - var attributesPath = __dirname + '/../workspace/' + attr.id_application + '/models/attributes/' + attr.options.target.toLowerCase() - delete require.cache[require.resolve(attributesPath)]; - var attributeTarget = require(attributesPath); - for (var i = 0; i < attr.options.usingField.length; i++) { - if (typeof attributeTarget[attr.options.usingField[i]] === "undefined") { - allUsingExist = false; - var missingField = attr.options.showUsingField[i]; - } else { - attr.options.usingField[i] = { - value: attr.options.usingField[i], - type: attributeTarget[attr.options.usingField[i]].newmipsType + // Vérifie que la target existe bien avant de creer la source et la clé étrangère (foreign key) + db_entity.selectEntityTarget(attr, (err, entityTarget) => { + // If target entity doesn't exists, send error + if (err) + return callback(err, null); + + var allUsingExist = true; + + // If a using field or fields has been asked, we have to check if those fields exist in the entity + if (typeof attr.options.usingField !== "undefined") { + var attributesPath = __dirname + '/../workspace/' + attr.id_application + '/models/attributes/' + attr.options.target.toLowerCase() + delete require.cache[require.resolve(attributesPath)]; + var attributeTarget = require(attributesPath); + for (var i = 0; i < attr.options.usingField.length; i++) { + if (typeof attributeTarget[attr.options.usingField[i]] === "undefined") { + allUsingExist = false; + var missingField = attr.options.showUsingField[i]; + } else { + attr.options.usingField[i] = { + value: attr.options.usingField[i], + type: attributeTarget[attr.options.usingField[i]].newmipsType + } } } } - } - // If a asked using field doesn't exist in the target entity we send an error - if (!allUsingExist) { - var err = new Error("structure.association.relatedTo.missingField"); - err.messageParams = [missingField, attr.options.showTarget]; - return callback(err, null); - } + // If a asked using field doesn't exist in the target entity we send an error + if (!allUsingExist) { + var err = new Error("structure.association.relatedTo.missingField"); + err.messageParams = [missingField, attr.options.showTarget]; + return callback(err, null); + } - // Check if an association already exists from source to target - var optionsSourceFile = helpers.readFileSyncWithCatch(__dirname+'/../workspace/' + attr.id_application + '/models/options/' + attr.options.source.toLowerCase() + '.json'); - var optionsSourceObject = JSON.parse(optionsSourceFile); + // Check if an association already exists from source to target + var optionsSourceFile = helpers.readFileSyncWithCatch(__dirname+'/../workspace/' + attr.id_application + '/models/options/' + attr.options.source.toLowerCase() + '.json'); + var optionsSourceObject = JSON.parse(optionsSourceFile); - var toSync = true; - var relation = "belongsToMany"; + var toSync = true; + var relation = "belongsToMany"; - // Check already exisiting association from source to target entity - for (var i = 0; i < optionsSourceObject.length; i++) { - if (optionsSourceObject[i].target.toLowerCase() == attr.options.target.toLowerCase()) { - if (attr.options.as == optionsSourceObject[i].as) { + // Check already exisiting association from source to target entity + for (var i = 0; i < optionsSourceObject.length; i++) { + if (optionsSourceObject[i].target.toLowerCase() == attr.options.target.toLowerCase()) { + if (attr.options.as == optionsSourceObject[i].as) { + var err = new Error("structure.association.error.alreadySameAlias"); + return callback(err, null); + } + } else if (optionsSourceObject[i].relation == "belongsToMany" && (attr.options.as == optionsSourceObject[i].as)) { var err = new Error("structure.association.error.alreadySameAlias"); return callback(err, null); + } else if(attr.options.as == optionsSourceObject[i].as){ + let err = new Error(); + err.message = "database.field.error.alreadyExist"; + err.messageParams = [attr.options.showAs]; + return callback(err, null); } - } else if (optionsSourceObject[i].relation == "belongsToMany" && (attr.options.as == optionsSourceObject[i].as)) { - var err = new Error("structure.association.error.alreadySameAlias"); - return callback(err, null); - } else if(attr.options.as == optionsSourceObject[i].as){ - let err = new Error(); - err.message = "database.field.error.alreadyExist"; - err.messageParams = [attr.options.showAs]; - return callback(err, null); } - } - var info = {}; - attr.options.through = attr.id_application + "_" + source_entity.id + "_" + entityTarget.id + "_" + attr.options.as.substring(2); - if (attr.options.through.length > 55) { - var err = new Error("error.valueTooLong"); - err.messageParams = [attr.options.through]; - return callback(err, null); - } + var info = {}; + attr.options.through = attr.id_application + "_" + source_entity.id + "_" + entityTarget.id + "_" + attr.options.as.substring(2); + if (attr.options.through.length > 55) { + var err = new Error("error.valueTooLong"); + err.messageParams = [attr.options.through]; + return callback(err, null); + } - // Check if an association already exists from target to source - var optionsFile = helpers.readFileSyncWithCatch(__dirname+'/../workspace/' + attr.id_application + '/models/options/' + attr.options.target.toLowerCase() + '.json'); - var optionsObject = JSON.parse(optionsFile); + // Check if an association already exists from target to source + var optionsFile = helpers.readFileSyncWithCatch(__dirname+'/../workspace/' + attr.id_application + '/models/options/' + attr.options.target.toLowerCase() + '.json'); + var optionsObject = JSON.parse(optionsFile); - for (var i = 0; i < optionsObject.length; i++) { - if (optionsObject[i].target.toLowerCase() == attr.options.source.toLowerCase() && optionsObject[i].relation != "belongsTo") { - attr.options.through = attr.id_application + "_" + entityTarget.id + "_" + source_entity.id + "_" + attr.options.as.substring(2); - if (attr.options.through.length > 55) { - var err = new Error("error.valueTooLong"); - err.messageParams = [attr.options.through]; - return callback(err, null); - } - } else if (attr.options.source.toLowerCase() != attr.options.target.toLowerCase() - && (optionsObject[i].target.toLowerCase() == attr.options.source.toLowerCase() && optionsObject[i].relation == "belongsTo")) { - - // Temporary solution ! TODO: Mispy should ask if we want to link the already existing 1,1 with this new 1,n - if ((attr.options.target.substring(2) == attr.options.as.substring(2)) - && (optionsObject[i].target.substring(2) == optionsObject[i].as.substring(2))) { - //&& (optionsObject[i].foreignKey == attr.options.foreignKey) - // If alias both side are the same that their own target then it trigger the 1,1 / 1,n generation - attr.options.foreignKey = optionsObject[i].foreignKey; - // We avoid the toSync to append because the already existing has one relation has already created the foreign key in BDD - toSync = false; - // If it's already define that target entity belongsTo source entity, then we create a simple hasMany instead of a belongsToMany - relation = "hasMany"; - attr.options.through = null; + for (var i = 0; i < optionsObject.length; i++) { + if (optionsObject[i].target.toLowerCase() == attr.options.source.toLowerCase() && optionsObject[i].relation != "belongsTo") { + attr.options.through = attr.id_application + "_" + entityTarget.id + "_" + source_entity.id + "_" + attr.options.as.substring(2); + if (attr.options.through.length > 55) { + var err = new Error("error.valueTooLong"); + err.messageParams = [attr.options.through]; + return callback(err, null); + } + } else if (attr.options.source.toLowerCase() != attr.options.target.toLowerCase() + && (optionsObject[i].target.toLowerCase() == attr.options.source.toLowerCase() && optionsObject[i].relation == "belongsTo")) { + + // Temporary solution ! TODO: Mispy should ask if we want to link the already existing 1,1 with this new 1,n + if ((attr.options.target.substring(2) == attr.options.as.substring(2)) + && (optionsObject[i].target.substring(2) == optionsObject[i].as.substring(2))) { + //&& (optionsObject[i].foreignKey == attr.options.foreignKey) + // If alias both side are the same that their own target then it trigger the 1,1 / 1,n generation + attr.options.foreignKey = optionsObject[i].foreignKey; + // We avoid the toSync to append because the already existing has one relation has already created the foreign key in BDD + toSync = false; + // If it's already define that target entity belongsTo source entity, then we create a simple hasMany instead of a belongsToMany + relation = "hasMany"; + attr.options.through = null; + } } } - } - // If there is a circular has many we have to convert it to a belongsToMany assocation, so we stop the code here. - // If not we continue doing a simple related to multiple association. - var reversedAttr = { - options: { - showForeignKey: attr.options.showAs, - foreignKey: attr.options.foreignKey, - source: attr.options.source, - }, - id_data_entity: attr.id_data_entity, - id_application: attr.id_application - }; - - db_field.createNewForeignKey(reversedAttr, (err, created_foreignKey) => { - if (err){return callback(err, null);} - // Create the belongsToMany link between source and target - var associationOption = { - idApp: attr.id_application, - source: attr.options.source, - target: attr.options.target, - foreignKey: attr.options.foreignKey, - as: attr.options.as, - showAs: attr.options.showAs, - relation: relation, - through: attr.options.through, - toSync: toSync, - type: attr.options.isCheckbox ? "relatedToMultipleCheckbox" : "relatedToMultiple" + // If there is a circular has many we have to convert it to a belongsToMany assocation, so we stop the code here. + // If not we continue doing a simple related to multiple association. + var reversedAttr = { + options: { + showForeignKey: attr.options.showAs, + foreignKey: attr.options.foreignKey, + source: attr.options.source, + }, + id_data_entity: attr.id_data_entity, + id_application: attr.id_application }; - if (typeof attr.options.usingField !== "undefined") - associationOption.usingField = attr.options.usingField; - if (typeof attr.options.isCheckbox !== "undefined" && attr.options.isCheckbox) { - // If it's a checkbox presentation style, we need to load association directly in the route, not in ajax - associationOption.loadOnStart = true; - } - structure_data_entity.setupAssociation(associationOption, () => { - // Ajouter le field d'assocation dans create_fields/update_fields. Ajout d'un tab dans le show - structure_data_field.setupRelatedToMultipleField(attr, () => { - var info = {}; - info.message = "structure.association.relatedToMultiple.success"; - info.messageParams = [attr.options.showAs, attr.options.showTarget, attr.options.showSource, attr.options.showAs, attr.options.showAs]; - callback(null, info); + + db_field.createNewForeignKey(reversedAttr, (err, created_foreignKey) => { + if (err){return callback(err, null);} + // Create the belongsToMany link between source and target + var associationOption = { + idApp: attr.id_application, + source: attr.options.source, + target: attr.options.target, + foreignKey: attr.options.foreignKey, + as: attr.options.as, + showAs: attr.options.showAs, + relation: relation, + through: attr.options.through, + toSync: toSync, + type: attr.options.isCheckbox ? "relatedToMultipleCheckbox" : "relatedToMultiple" + }; + if (typeof attr.options.usingField !== "undefined") + associationOption.usingField = attr.options.usingField; + if (typeof attr.options.isCheckbox !== "undefined" && attr.options.isCheckbox) { + // If it's a checkbox presentation style, we need to load association directly in the route, not in ajax + associationOption.loadOnStart = true; + } + structure_data_entity.setupAssociation(associationOption, () => { + // Ajouter le field d'assocation dans create_fields/update_fields. Ajout d'un tab dans le show + structure_data_field.setupRelatedToMultipleField(attr, () => { + var info = {}; + info.message = "structure.association.relatedToMultiple.success"; + info.messageParams = [attr.options.showAs, attr.options.showTarget, attr.options.showSource, attr.options.showAs, attr.options.showAs]; + callback(null, info); + }); }); }); }); From ae935007e3e7461d42daf11f4848526eefeaedfe Mon Sep 17 00:00:00 2001 From: Syno Date: Fri, 4 Oct 2019 12:02:37 +0200 Subject: [PATCH 13/24] Grant privilege on generator db user on separate workspace --- structure/structure_application.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/structure/structure_application.js b/structure/structure_application.js index b1c25521b..fb787b4b8 100755 --- a/structure/structure_application.js +++ b/structure/structure_application.js @@ -101,7 +101,8 @@ exports.setupApplication = function(attr, callback) { "CREATE USER IF NOT EXISTS 'workspace_" + appID + "'@'127.0.0.1' IDENTIFIED BY 'workspace_" + appID + "';", "CREATE USER IF NOT EXISTS 'workspace_" + appID + "'@'%' IDENTIFIED BY 'workspace_" + appID + "';", "GRANT ALL PRIVILEGES ON workspace_" + appID + ".* TO 'workspace_" + appID + "'@'127.0.0.1';", - "GRANT ALL PRIVILEGES ON workspace_" + appID + ".* TO 'workspace_" + appID + "'@'%';" + "GRANT ALL PRIVILEGES ON workspace_" + appID + ".* TO 'workspace_" + appID + "'@'%';", + "GRANT ALL PRIVILEGES ON " + dbConf.user + ".* TO '" + dbConf.user + "'@'%';" ]; let conn = await mysql.createConnection({ From f89dc598e42aa277fe2654f8bf778ca64ef8079b Mon Sep 17 00:00:00 2001 From: Syno Date: Fri, 4 Oct 2019 12:09:35 +0200 Subject: [PATCH 14/24] Mistake --- structure/structure_application.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/structure/structure_application.js b/structure/structure_application.js index fb787b4b8..f8d7ec7f2 100755 --- a/structure/structure_application.js +++ b/structure/structure_application.js @@ -102,7 +102,8 @@ exports.setupApplication = function(attr, callback) { "CREATE USER IF NOT EXISTS 'workspace_" + appID + "'@'%' IDENTIFIED BY 'workspace_" + appID + "';", "GRANT ALL PRIVILEGES ON workspace_" + appID + ".* TO 'workspace_" + appID + "'@'127.0.0.1';", "GRANT ALL PRIVILEGES ON workspace_" + appID + ".* TO 'workspace_" + appID + "'@'%';", - "GRANT ALL PRIVILEGES ON " + dbConf.user + ".* TO '" + dbConf.user + "'@'%';" + "GRANT ALL PRIVILEGES ON workspace_" + appID + ".* TO '" + dbConf.user + "'@'127.0.0.1';", + "GRANT ALL PRIVILEGES ON workspace_" + appID + ".* TO '" + dbConf.user + "'@'%';" ]; let conn = await mysql.createConnection({ From b5820ec05690535f70b44eea97441bddec6daa94 Mon Sep 17 00:00:00 2001 From: Syno Date: Tue, 8 Oct 2019 11:02:56 +0200 Subject: [PATCH 15/24] Add git remote function --- services/cloud_manager.js | 39 +++++++++-------- utils/git_helper.js | 92 +++++++++++++++++++++------------------ 2 files changed, 72 insertions(+), 59 deletions(-) diff --git a/services/cloud_manager.js b/services/cloud_manager.js index 6db85faa0..b9ebcb107 100644 --- a/services/cloud_manager.js +++ b/services/cloud_manager.js @@ -51,27 +51,32 @@ exports.deploy = (attr, callback) => { return callback(err); gitHelper.gitTag(appID, applicationConf.version, applicationPath).then(_ => { - gitHelper.gitPush(attr, (err, infoGit)=> { + gitHelper.gitPush(attr, (err, infoGit) => { if(err) return callback(err, null); - let appName = attr.appCodeName.split("_").slice(1).join("_"); - let nameRepo = globalConfig.host + '-' + appName; - let subdomain = globalConfig.sub_domain + '-' + appName + '-' + globalConfig.dns_cloud.replace('.', '-'); - - portainerDeploy(nameRepo, subdomain, appID, appName, attr.gitlabUser).then(data => { - return callback(null, { - message: "botresponse.deployment", - messageParams: [data.url, data.url] + gitHelper.gitRemotes(attr, (err, remotes) => { + console.log("TEST REMOTES"); + console.log(remotes); + + let appName = attr.appCodeName.split("_").slice(1).join("_"); + let nameRepo = globalConfig.host + '-' + appName; + let subdomain = globalConfig.sub_domain + '-' + appName + '-' + globalConfig.dns_cloud.replace('.', '-'); + + portainerDeploy(nameRepo, subdomain, appID, appName, attr.gitlabUser).then(data => { + return callback(null, { + message: "botresponse.deployment", + messageParams: [data.url, data.url] + }); + }).catch(err => { + if(typeof err.message !== "undefined") + console.error(err.message); + else + console.error(err); + + return callback(err); }); - }).catch(err => { - if(typeof err.message !== "undefined") - console.error(err.message); - else - console.error(err); - - return callback(err); - }); + }) }); }).catch(function(e) { console.log(e); diff --git a/utils/git_helper.js b/utils/git_helper.js index 6c6893c69..f22e1ac12 100644 --- a/utils/git_helper.js +++ b/utils/git_helper.js @@ -329,49 +329,57 @@ module.exports = { }, gitStatus: function(attr, callback){ // We push code on gitlab only in our cloud env - if(gitlabConf.doGit){ - var idApplication = attr.id_application; - - // Workspace path - var workspacePath = __dirname+'/../workspace/'+idApplication; - - // Init simple-git in the workspace path - var simpleGit = require('simple-git')(workspacePath); - - // Get current application values - models.Application.findOne({where:{id: idApplication}}).then(function(application){ - // . becomes - - var cleanHost = globalConf.host.replace(/\./g, "-"); - - // Remove prefix - var nameApp = application.codeName.substring(2); - var nameRepo = cleanHost+"-"+nameApp; - var originName = "origin-"+cleanHost+"-"+nameApp; - - if(typeof gitProcesses[originName] === "undefined") - gitProcesses[originName] = false; - - if(!gitProcesses[originName]){ - // Set gitProcesses to prevent any other git command during this process - gitProcesses[originName] = true; - simpleGit.status(function(err, answer){ - gitProcesses[originName] = false; - console.log(answer); - writeAllLogs("Git push", answer, err); - if(err) - return callback(err, null); - callback(null, answer); - }); - } else{ - err = new Error(); - err.message = "structure.global.error.alreadyInProcess"; - return callback(err, null); - } - }); - } else{ - var err = new Error(); + if(!gitlabConf.doGit){ + let err = new Error(); err.message = "structure.global.error.notDoGit"; - callback(err, null); + return callback(err, null); } + + let idApplication = attr.id_application; + // Workspace path + let workspacePath = __dirname+'/../workspace/'+idApplication; + // Init simple-git in the workspace path + let simpleGit = require('simple-git')(workspacePath); + // Get current application values + models.Application.findOne({where:{id: idApplication}}).then(function(application){ + // . becomes - + var cleanHost = globalConf.host.replace(/\./g, "-"); + + // Remove prefix + var nameApp = application.codeName.substring(2); + var nameRepo = cleanHost+"-"+nameApp; + var originName = "origin-"+cleanHost+"-"+nameApp; + + if(typeof gitProcesses[originName] === "undefined") + gitProcesses[originName] = false; + + if(!gitProcesses[originName]){ + // Set gitProcesses to prevent any other git command during this process + gitProcesses[originName] = true; + simpleGit.status(function(err, answer){ + gitProcesses[originName] = false; + console.log(answer); + writeAllLogs("Git push", answer, err); + if(err) + return callback(err, null); + callback(null, answer); + }); + } else{ + err = new Error(); + err.message = "structure.global.error.alreadyInProcess"; + return callback(err, null); + } + }); + }, + gitRemotes: (attr, callback) => { + // Workspace path + let workspacePath = __dirname+'/../workspace/'+attr.id_application; + // Init simple-git in the workspace path + let simpleGit = require('simple-git')(workspacePath); + simpleGit.getRemotes(true, (err, answer) => { + if(err) + return callback(err, null); + return callback(null, answer); + }) } } \ No newline at end of file From 7eddc6c9a35e4e2886fa2df784cdbc5cb991236d Mon Sep 17 00:00:00 2001 From: Syno Date: Tue, 8 Oct 2019 11:19:31 +0200 Subject: [PATCH 16/24] Improve git repo url generation send to cloud env to avoid cloning error when it's not the owner that deploy the first time --- services/cloud_manager.js | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/services/cloud_manager.js b/services/cloud_manager.js index b9ebcb107..db0bd11e4 100644 --- a/services/cloud_manager.js +++ b/services/cloud_manager.js @@ -55,15 +55,21 @@ exports.deploy = (attr, callback) => { if(err) return callback(err, null); + let appName = attr.appCodeName.split("_").slice(1).join("_"); + let nameRepo = globalConfig.host + '-' + appName; + let subdomain = globalConfig.sub_domain + '-' + appName + '-' + globalConfig.dns_cloud.replace('.', '-'); + gitHelper.gitRemotes(attr, (err, remotes) => { - console.log("TEST REMOTES"); - console.log(remotes); - let appName = attr.appCodeName.split("_").slice(1).join("_"); - let nameRepo = globalConfig.host + '-' + appName; - let subdomain = globalConfig.sub_domain + '-' + appName + '-' + globalConfig.dns_cloud.replace('.', '-'); + // Gitlab url handling + let gitlabUrl = ""; + if(remotes.length > 0 && remotes[0].refs && remotes[0].refs.fetch) + gitlabUrl = remotes[0].refs.fetch; // Getting actuel .git fetch remote + else + gitlabUrl = gitlabConfig.sshUrl + ":" + attr.gitlabUser.username + "/" + repoName + ".git"; // Generating manually the remote, can generate clone error if the connected user is note the owning user of the gitlab repo - portainerDeploy(nameRepo, subdomain, appID, appName, attr.gitlabUser).then(data => { + console.log('Cloning in cloud: ' + gitlabUrl); + portainerDeploy(nameRepo, subdomain, appID, appName).then(data => { return callback(null, { message: "botresponse.deployment", messageParams: [data.url, data.url] @@ -85,9 +91,8 @@ exports.deploy = (attr, callback) => { }); } -async function portainerDeploy(repoName, subdomain, appID, appName, gitlabUser){ +async function portainerDeploy(repoName, subdomain, appID, appName){ // Preparing all needed values - let gitlabUrl = gitlabConfig.sshUrl + ":" + gitlabUser.username + "/" + repoName + ".git"; let stackName = globalConfig.sub_domain + "-" + appName + "-" + globalConfig.dns_cloud.replace(".", "-"); let cloudUrl = globalConfig.sub_domain + "-" + appName + "." + globalConfig.dns_cloud; From adb8492acbdb89c96b3a69c9ec86a25de9c4a861 Mon Sep 17 00:00:00 2001 From: Syno Date: Tue, 8 Oct 2019 11:24:14 +0200 Subject: [PATCH 17/24] Forgotten params --- services/cloud_manager.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/services/cloud_manager.js b/services/cloud_manager.js index db0bd11e4..1a13f468f 100644 --- a/services/cloud_manager.js +++ b/services/cloud_manager.js @@ -69,7 +69,7 @@ exports.deploy = (attr, callback) => { gitlabUrl = gitlabConfig.sshUrl + ":" + attr.gitlabUser.username + "/" + repoName + ".git"; // Generating manually the remote, can generate clone error if the connected user is note the owning user of the gitlab repo console.log('Cloning in cloud: ' + gitlabUrl); - portainerDeploy(nameRepo, subdomain, appID, appName).then(data => { + portainerDeploy(nameRepo, subdomain, appID, appName, gitlabUrl).then(data => { return callback(null, { message: "botresponse.deployment", messageParams: [data.url, data.url] @@ -91,7 +91,7 @@ exports.deploy = (attr, callback) => { }); } -async function portainerDeploy(repoName, subdomain, appID, appName){ +async function portainerDeploy(repoName, subdomain, appID, appName, gitlabUrl){ // Preparing all needed values let stackName = globalConfig.sub_domain + "-" + appName + "-" + globalConfig.dns_cloud.replace(".", "-"); let cloudUrl = globalConfig.sub_domain + "-" + appName + "." + globalConfig.dns_cloud; From 00a36fdec34d6851da0f16ee5fabb5c837b3c864 Mon Sep 17 00:00:00 2001 From: Syno Date: Tue, 8 Oct 2019 15:09:15 +0200 Subject: [PATCH 18/24] Fix multi script handling error --- routes/instruction_script.js | 36 ++++++++++++++++++++++-------------- 1 file changed, 22 insertions(+), 14 deletions(-) diff --git a/routes/instruction_script.js b/routes/instruction_script.js index 9a7d32970..92259a2e5 100755 --- a/routes/instruction_script.js +++ b/routes/instruction_script.js @@ -16,8 +16,11 @@ const parser = require('../services/bot.js'); const structure_application = require('../structure/structure_application'); -let scriptProcessing = false; -let scriptData = []; +let scriptProcessing = { + timeout: moment(), + state: false +}; +let scriptData = {}; let message = ""; function execute(req, instruction) { @@ -321,7 +324,8 @@ router.post('/execute', block_access.isLoggedIn, multer({ id_data_entity: -1 } }; - if(scriptProcessing){ + + if(scriptProcessing.state && moment().diff(scriptProcessing.timeout, 'seconds') < 100){ let __ = require("../services/language")(req.session.lang_user).__; scriptData[userId].answers = [{ message: __('instructionScript.alreadyProcessing') @@ -330,7 +334,9 @@ router.post('/execute', block_access.isLoggedIn, multer({ scriptData[userId].overDueToProcessing = true; return res.end(); } - scriptProcessing = true; + + scriptProcessing.state = true; + scriptProcessing.timeout = moment(); // Get file extension let extensionFile = req.file.originalname.split("."); @@ -421,9 +427,7 @@ router.post('/execute', block_access.isLoggedIn, multer({ if (positionComment != -1){ line = line.substring(0, line.indexOf('//')); } - console.log(line); var parserResult = parser.parse(line); - console.log(parserResult); // Get the wanted function given by the bot to do some checks var designerFunction = parserResult.function; var designerValue = null; @@ -590,7 +594,8 @@ router.post('/execute_alt', block_access.isLoggedIn, function(req, res) { } }; - if(scriptProcessing){ + // Processing already occured less than the last 100 seconds + if(scriptProcessing.state && moment().diff(scriptProcessing.timeout, 'seconds') < 100){ scriptData[userId].answers = [{ message: __('instructionScript.alreadyProcessing') }]; @@ -598,7 +603,9 @@ router.post('/execute_alt', block_access.isLoggedIn, function(req, res) { scriptData[userId].overDueToProcessing = true; return res.end(); } - scriptProcessing = true; + + scriptProcessing.state = true; + scriptProcessing.timeout = moment(); let tmpFilename = moment().format('YY-MM-DD-HH_mm_ss')+"_custom_script.txt"; let tmpPath = __dirname+'/../upload/'+tmpFilename; @@ -640,7 +647,7 @@ router.post('/execute_alt', block_access.isLoggedIn, function(req, res) { message: __('template.no_script') }]; scriptData[userId].over = true; - scriptProcessing = false; + scriptProcessing.state = false; return res.end(); } @@ -847,6 +854,7 @@ router.post('/execute_alt', block_access.isLoggedIn, function(req, res) { //var process_server = process_manager.process_server; var process_server_per_app = process_manager.process_server_per_app; + if (process_server_per_app[idApplication] != null && typeof process_server_per_app[idApplication] !== "undefined") { process_manager.killChildProcess(process_server_per_app[idApplication].pid, function(err) { if(err) @@ -884,9 +892,9 @@ router.post('/execute_alt', block_access.isLoggedIn, function(req, res) { }); // Script execution status -router.get('/status', function(req, res) { - var userId = req.session.passport.user.id; - var stats = { +router.get('/status', (req, res) => { + let userId = req.session.passport.user.id; + let stats = { totalInstruction: scriptData[userId].totalInstruction, doneInstruction: scriptData[userId].doneInstruction, over: scriptData[userId].over, @@ -902,8 +910,8 @@ router.get('/status', function(req, res) { req.session.id_data_entity = scriptData[userId].ids.id_data_entity; req.session.id_module = scriptData[userId].ids.id_module; if(typeof scriptData[userId].overDueToProcessing === 'undefined') - scriptProcessing = false; - scriptData.splice(scriptData.indexOf(userId), 1); + scriptProcessing.state = false; + delete scriptData[userId]; } res.send(stats).end(); From cf953d00a45c98990abb0d783353944f6f8b0c38 Mon Sep 17 00:00:00 2001 From: Syno Date: Tue, 8 Oct 2019 17:12:49 +0200 Subject: [PATCH 19/24] Fix API generation --- utils/api_doc_builder.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/utils/api_doc_builder.js b/utils/api_doc_builder.js index 7987b98c1..569b0d3f2 100644 --- a/utils/api_doc_builder.js +++ b/utils/api_doc_builder.js @@ -184,7 +184,7 @@ function entityDocumentation(entity, attributes, options) { entityDoc += ' *******************************************/\n'; entityDoc += '/** @apiDefine '+entity.codeName+' '+capitalizeFirstLetter(entity.name)+ ' */\n'; entityDoc += routeGet(entity, attributes, options); - entityDoc += routeGetId(entity, attributess); + entityDoc += routeGetId(entity, attributes, options); entityDoc += routeGetAssociation(entity, options); entityDoc += routePost(entity, attributes, options); entityDoc += routePut(entity, attributes, options); From e10c1301922438de43302774b4c4462bb6e6e59b Mon Sep 17 00:00:00 2001 From: Syno Date: Wed, 9 Oct 2019 11:34:20 +0200 Subject: [PATCH 20/24] Fix session existence moment error & require cache on deleting entity option --- structure/structure_data_entity.js | 4 ++-- utils/authStrategies.js | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/structure/structure_data_entity.js b/structure/structure_data_entity.js index 820e0b3e7..1c3dea2df 100644 --- a/structure/structure_data_entity.js +++ b/structure/structure_data_entity.js @@ -358,9 +358,9 @@ exports.deleteDataEntity = function (id_application, name_module, name_data_enti fs.unlinkSync(baseFolder + '/models/attributes/' + name_data_entity + '.json'); // Remove relationships in options.json files - var optionFiles = fs.readdirSync(baseFolder + '/models/options/'); + var optionFiles = fs.readdirSync(baseFolder + '/models/options/').filter(x => x.indexOf('.json') != -1); for (var file in optionFiles) { - var options = require(baseFolder + '/models/options/' + optionFiles[file]); + var options = JSON.parse(fs.readFileSync(baseFolder + '/models/options/' + optionFiles[file])); var optionsCpy = []; for (var i = 0; i < options.length; i++) if (options[i].target != name_data_entity) diff --git a/utils/authStrategies.js b/utils/authStrategies.js index 79989bc12..a3f472960 100755 --- a/utils/authStrategies.js +++ b/utils/authStrategies.js @@ -59,6 +59,7 @@ passport.use(new LocalStrategy({ if(typeof currentSession.passport !== "undefined" && typeof currentSession.passport.user !== "undefined" + && currentSession.cookie.expires && moment().isBefore(currentSession.cookie.expires) // Not counting expired session && currentSession.passport.user.id == user.id && currentSession.isgenerator){ From e7264da30de94f8ac897a9c6240c6d965dcdb91b Mon Sep 17 00:00:00 2001 From: Syno Date: Wed, 9 Oct 2019 11:51:41 +0200 Subject: [PATCH 21/24] Fix save user settings without writting email --- structure/pieces/administration/routes/e_user.js | 7 ++++--- structure/template/views/main_layout.dust | 4 ++-- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/structure/pieces/administration/routes/e_user.js b/structure/pieces/administration/routes/e_user.js index 91d3c4f0a..6b75e80ef 100644 --- a/structure/pieces/administration/routes/e_user.js +++ b/structure/pieces/administration/routes/e_user.js @@ -725,9 +725,10 @@ router.get('/settings', block_access.isLoggedIn, function(req, res) { router.post('/settings', block_access.isLoggedIn, function(req, res) { - let updateObject = { - f_email: req.body.f_email - }; + let updateObject = {}; + + if(req.body.f_email && req.body.f_email != '') + updateObject.f_email = req.body.f_email models.E_user.findById(req.session.passport.user.id).then(user => { let newPassword = new Promise((resolve, reject) => { diff --git a/structure/template/views/main_layout.dust b/structure/template/views/main_layout.dust index 8c499d129..dfa854539 100755 --- a/structure/template/views/main_layout.dust +++ b/structure/template/views/main_layout.dust @@ -148,8 +148,8 @@
  • {! Menu Body !}
  • - - + +
  • {! Menu Footer !}