diff --git a/.gitignore b/.gitignore index d9ff19820..61f265d5b 100644 --- a/.gitignore +++ b/.gitignore @@ -64,7 +64,6 @@ public/modules/editor public/modules/reports public/modules/reports-new -package-lock.json yarn.lock *.conf rootCA.crt diff --git a/README.md b/README.md index 93c71764e..45fe09ce5 100644 --- a/README.md +++ b/README.md @@ -3,6 +3,20 @@ ## Changelog +### 1.0.2 +* Fix Section Width + + +### 1.0.1 +* Fix Elements Conditions Display +* Fix HTML in JSON-data for Templates +* Add Styles for Custom Areas +* Show Section Background on Front-end +* Fix Login Action for Elements + +### 1.0.0 +* Release version + ### 0.20.25 * Some Fixes for Section + Add Video Background for Sections diff --git a/altrpnjs/.gitignore b/altrpnjs/.gitignore index 160bca67a..5536fa08e 100644 --- a/altrpnjs/.gitignore +++ b/altrpnjs/.gitignore @@ -13,5 +13,9 @@ storage/ app/AltrpModels/ app/AltrpControllers/ app/AltrpPlugins/ +app/altrp-listeners/ start/altrp-routes/ /resources/views/altrp + +cert.pem +key.pem diff --git a/altrpnjs/app/Controllers/AltrpBaseController.ts b/altrpnjs/app/Controllers/AltrpBaseController.ts index 35a0306c1..23454acc3 100644 --- a/altrpnjs/app/Controllers/AltrpBaseController.ts +++ b/altrpnjs/app/Controllers/AltrpBaseController.ts @@ -9,6 +9,9 @@ export default class AltrpBaseController { protected setCustomizerData(path:string, data:any){ _.set(this.customizerData, path, data) } + protected unsetCustomizerData(path:string){ + _.unset(this.customizerData, path) + } protected getCustomizerData(path:string, _default:any = null):any{ return _.get(this.customizerData, path, _default) } diff --git a/altrpnjs/app/Controllers/Http/GlobalTemplateStylesController.ts b/altrpnjs/app/Controllers/Http/GlobalTemplateStylesController.ts new file mode 100644 index 000000000..c9718fcb1 --- /dev/null +++ b/altrpnjs/app/Controllers/Http/GlobalTemplateStylesController.ts @@ -0,0 +1,175 @@ +// import { HttpContextContract } from '@ioc:Adonis/Core/HttpContext' + +import GlobalStyle from "App/Models/GlobalStyle"; +import { v4 as uuid } from "uuid"; +import Template from "App/Models/Template"; +import is_null from "../../../helpers/is_null"; +import data_set from "../../../helpers/data_set"; +import data_get from "../../../helpers/data_get"; +import is_array from "../../../helpers/is_array"; + +export default class GlobalTemplateStylesController { + public async index() { + + try { + const globalStyles = await GlobalStyle.query() + + const groups = {}; + + globalStyles.forEach((style) => { + if(!groups[style.type]) groups[style.type] = []; + + groups[style.type].push(style) + }) + return groups + } catch (e) { + console.log(e) + return {} + } + } + + public async store({ request, response }) { + const data = request.body() + + if(!data.type || !data.settings) { + response.status(500) + return "fill all fields" + } + + data.guid = uuid() + + try { + const style = await GlobalStyle.create(data) + return style + } catch (e) { + console.log(e) + } + } + + public async destroy({request}) { + const params = request.params(); + const id = parseInt(params.id); + + const style = await GlobalStyle.query().where("id", id).firstOrFail() + + style.delete() + + return { + success: true + } + } + + public async update({request, response}) { + const params = request.params(); + const id = parseInt(params.id); + + const style = await GlobalStyle.query().where("id", id).firstOrFail() + + const body = request.body(); + + + switch (style.type) { + case "color": + const data = JSON.parse(style.settings); + + data.name = body.name ?? data.name; + data.color = body.color ?? data.color; + data.colorPickedHex = body.colorPickedHex ?? data.colorPickedHex; + data.colorRGB = body.colorRGB ?? data.colorRGB; + + style.settings = JSON.stringify(data); + break + } + + if(!style.save()) { + response.status(409) + return "Save failed" + } + + await this.updateStylesInAllTemplates(style) + + return style + } + + private async updateStylesInAllTemplates(style) { + let templates = await Template.all(); + templates.forEach((template) => { + this.replaceGlobalStyles(template, style.guid, style.settings); + }); + } + + public replaceGlobalStyles(element, guid, style) + { + let elementData = JSON.parse(element.data); + elementData = GlobalTemplateStylesController.recursiveReplaceGlobalStyles(elementData, guid, style); + console.log(elementData) + element.data = JSON.stringify(elementData); + element.save(); + } + + public static recursiveReplaceGlobalStyles(data, guid, style) { + if(data.settings.global_styles_storage) { + const globalStylesStorage = data.settings.global_styles_storage + + if(globalStylesStorage.length > 0) { + globalStylesStorage.forEach(s => { + if(!is_null(s)) { + if(s.search("typographic") !== -1) { + data_set(data, "settings.__altrpFonts__", s) + } + + if(s.search("gradient-first-color:") !== -1) { + const currentSetting = s.replace('gradient-first-color:', '') + + s = currentSetting; + + const dataSettings = data_get(data, `settings.${currentSetting}`); + + if(!is_array(dataSettings) && dataSettings <= 0) return; + + const newColor = style["color"]; + const oldColor = style["firstColor"]; + const newValue = dataSettings.value.replace(oldColor, newColor); + + dataSettings.secondColor = newColor; + dataSettings.value = newValue + + style = dataSettings + } + + if(s.search("gradient-second-color:") !== -1) { + const currentSetting = s.replace('gradient-second-color:', '') + + s = currentSetting; + + const dataSettings = data_get(data, `settings.${currentSetting}`); + + if(!is_array(dataSettings) && dataSettings <= 0) return; + + const newColor = style["color"]; + const oldColor = style["secondColor"]; + const newValue = dataSettings.value.replace(oldColor, newColor); + + dataSettings.secondColor = newColor; + dataSettings.value = newValue + + style = dataSettings + } + + data_set(data, 'settings.' + s, style); + } + }) + } + } + + if(data.children.length > 0) { + data.children.forEach((child, idx) => { + if(child !== null) { + data.children[idx] = GlobalTemplateStylesController.recursiveReplaceGlobalStyles(child, guid, style) + } + }) + } + + return data + } +} diff --git a/altrpnjs/app/Controllers/Http/IndicesController.ts b/altrpnjs/app/Controllers/Http/IndicesController.ts index 10df03849..44c2d7a77 100644 --- a/altrpnjs/app/Controllers/Http/IndicesController.ts +++ b/altrpnjs/app/Controllers/Http/IndicesController.ts @@ -4,6 +4,8 @@ import Env from "@ioc:Adonis/Core/Env"; import {HttpContextContract} from "@ioc:Adonis/Core/HttpContext"; import Drive from '@ioc:Adonis/Core/Drive' import Application from '@ioc:Adonis/Core/Application' +import path from "path"; + export default class IndicesController { async admin({view}) { @@ -37,6 +39,16 @@ export default class IndicesController { })) } + public async serviceWorker({response}) { + const pathToPublic = path.join(__dirname, "../", "../", "../", "../", "public", "sw.js"); + + response.header("Content-Type", "text/javascript") + + const file = await Drive.get(pathToPublic) + + return file + } + // public frontApp({ view }) { // return view.render('front-app', Edge({ // hAltrp: Env.get("PATH_ENV") === "production" ? "/modules/front-app/h-altrp.js" : null @@ -108,6 +120,16 @@ export default class IndicesController { } } + public async changelog({ response }) { + const pathToPublic = path.join(__dirname, "../", "../", "../", "../", "README.md"); + + const file = await Drive.get(pathToPublic) + + response.header('Content-type', 'text/plain'); + + return file.toString() + } + public async favicons({params, response}) { response.header('Content-type', 'image/png'); diff --git a/altrpnjs/app/Controllers/Http/RobotsController.ts b/altrpnjs/app/Controllers/Http/RobotsController.ts index d3d97717a..4c945a694 100644 --- a/altrpnjs/app/Controllers/Http/RobotsController.ts +++ b/altrpnjs/app/Controllers/Http/RobotsController.ts @@ -15,9 +15,7 @@ export default class RobotsController { author: robot.user.name, ...robot.serialize(), categories: robot.categories.map(category => { - return { - category: category - } + return category }) } @@ -49,7 +47,7 @@ export default class RobotsController { }) for (const option of categories) { - const category = await Category.find(option.value); + const category = await Category.query().where("guid", option.value).first(); if(!category) { response.status(404) diff --git a/altrpnjs/app/Controllers/Http/TemplatesController.ts b/altrpnjs/app/Controllers/Http/TemplatesController.ts index 9a855a7a9..3546da013 100644 --- a/altrpnjs/app/Controllers/Http/TemplatesController.ts +++ b/altrpnjs/app/Controllers/Http/TemplatesController.ts @@ -1,4 +1,6 @@ // import { HttpContextContract } from '@ioc:Adonis/Core/HttpContext' +import {HttpContextContract} from '@ioc:Adonis/Core/HttpContext' +import validGuid from '../../../helpers/validGuid'; import { v4 as uuid } from "uuid"; import Template from "App/Models/Template"; @@ -7,19 +9,23 @@ import Page from "App/Models/Page"; import PagesTemplate from "App/Models/PagesTemplate"; import Category from "App/Models/Category"; import CategoryObject from "App/Models/CategoryObject"; +import AltrpMeta from "App/Models/AltrpMeta"; +import GlobalStyle from "App/Models/GlobalStyle"; import filtration from "../../../helpers/filtration"; +import env from "../../../helpers/env"; import TemplateGenerator from "App/Generators/TemplateGenerator"; +import recurseMapElements from '../../../helpers/recurseMapElements'; export default class TemplatesController { public async index({ request }) { const params = request.qs(); - const page = parseInt(params.page) || 1 + // const page = parseInt(params.page) || 1 // const search = params.s // const orderType = params.order || "DESC" // const orderBy = params.order_by || "id" - const pageSize = params.pageSize + // const pageSize = params.pageSize const templatesQuery = Template.query() @@ -40,14 +46,11 @@ export default class TemplatesController { } }) .orderBy('title') - .paginate(page, pageSize) - const modTemplates = templates.all().map( template => { + const modTemplates = templates.map( template => { return { categories: template.categories.map(category => { - return { - category: category - } + return category }), author: template.getAuthor(), area: template.getArea(), @@ -59,7 +62,6 @@ export default class TemplatesController { }) return { - pageCount: templates.lastPage, templates: modTemplates } } @@ -73,7 +75,16 @@ export default class TemplatesController { } public async settingsSet({ params, request, response}) { - const template = await Template.query().where("id", parseInt(params.id)).firstOrFail() + const templateQuery = Template.query(); + + if(isNaN(params.id)) { + templateQuery.where("guid", params.id) + } else { + templateQuery.where("id", parseInt(params.id)) + } + + const template = await templateQuery.firstOrFail() + const settingName = request.input("setting_name"); @@ -112,6 +123,7 @@ export default class TemplatesController { await auth.use('web').authenticate() const guid = uuid(); + const data = { area: parseInt(request.input("area")), data: JSON.stringify(request.input("data")), @@ -164,13 +176,29 @@ export default class TemplatesController { } public async get({ params }) { - const template = await Template.find(parseInt(params.id)); + const templateQuery = Template.query(); + + if(isNaN(params.id)) { + templateQuery.where("guid", params.id) + } else { + templateQuery.where("id", parseInt(params.id)) + } + + const template = await templateQuery.firstOrFail() return template } public async delete({ params }) { - const template = await Template.query().where("id", parseInt(params.id)).firstOrFail(); + const templateQuery = Template.query(); + + if(isNaN(params.id)) { + templateQuery.where("guid", params.id) + } else { + templateQuery.where("id", parseInt(params.id)) + } + + const template = await templateQuery.firstOrFail() let templateGenerator = new TemplateGenerator() await templateGenerator.deleteFile(template) @@ -181,11 +209,18 @@ export default class TemplatesController { } public async update({ params, request }) { - const template = await Template.find(parseInt(params.id)); + const templateQuery = Template.query(); + + if(isNaN(params.id)) { + templateQuery.where("guid", params.id) + } else { + templateQuery.where("id", parseInt(params.id)) + } + + const template = await templateQuery.firstOrFail() if(template) { //@ts-ignore - const prevVersions = await Template.query().where("guid", template.getGuid()) const data = template.serialize(); delete data.created_at @@ -198,13 +233,6 @@ export default class TemplatesController { await prevTemplates[4].delete() } - const prevVersion = await Template.create({ - ...data, - guid: null, - parent_template: template.id, - type: "review" - }) - template.data = JSON.stringify(request.input("data")); template.styles = JSON.stringify(request.input("styles")); template.html_content = request.input("html_content"); @@ -215,8 +243,6 @@ export default class TemplatesController { await templateGenerator.run(template) return { currentTemplate: template, - prevVersions: prevVersions, - prevVersion: prevVersion, clearData: request.input("data") } } @@ -264,16 +290,10 @@ export default class TemplatesController { const templates = await Template.query().where("type", "review"); if(templates.length > 0) { - return { - success: true, - data: templates - } + return templates } else { response.status(404) - return { - success: false, - data: templates - } + return templates } } @@ -281,16 +301,10 @@ export default class TemplatesController { const templates = await Template.query().where("type", "review").andWhere("parent_template", parseInt(params.id)); if(templates.length > 0) { - return { - success: true, - data: templates - } + return templates } else { response.status(404) - return { - success: false, - data: templates - } + return templates } } @@ -302,6 +316,7 @@ export default class TemplatesController { */ public async conditions({params}) { const id = parseInt(params.id); + let res = { data: [], success: true @@ -421,7 +436,33 @@ export default class TemplatesController { } } - public async globalTemplateStyles() { - return [] + + public async exportCustomizer( {params, response}: HttpContextContract ) + { + + let template + if (validGuid(params.id)) { + template = await Template.query().where('guid', params.id).first() + } else { + template = await Template.find(params.id) + } + if (!template) { + return response.json({ + 'success': + false, 'message': + 'Customizer not found' + }, + ) + } + + template.__exported_metas__ = {} + template.__exported_metas__.styles_presets = AltrpMeta.getGlobalStyles() + template.__exported_metas__ = {} + template.__exported_metas__.global_styles = GlobalStyle.all(); + + let res = template.serialize() + + return response.json(res) } + } diff --git a/altrpnjs/app/Controllers/Http/admin/AdminController.ts b/altrpnjs/app/Controllers/Http/admin/AdminController.ts index 992afb6c7..ecfaa3328 100644 --- a/altrpnjs/app/Controllers/Http/admin/AdminController.ts +++ b/altrpnjs/app/Controllers/Http/admin/AdminController.ts @@ -3,8 +3,8 @@ import ModelGenerator from "App/Generators/ModelGenerator"; import ControllerGenerator from "App/Generators/ControllerGenerator"; import {HttpContextContract} from "@ioc:Adonis/Core/HttpContext"; import Controller from "App/Models/Controller"; -import Application from '@ioc:Adonis/Core/Application' -import sharp from "sharp"; +import Application from '@ioc:Adonis/Core/Application'; +import jimp from "jimp"; import FAVICONS_SIZES from "../../../../helpers/const/FAVICONS_SIZES"; import Drive from '@ioc:Adonis/Core/Drive' import Template from "App/Models/Template"; @@ -66,10 +66,12 @@ export default class AdminController { } for (const variant of FAVICONS_SIZES) { - await sharp(Application.tmpPath("favicon") + `/basic.${favicon.extname}`) - .png() - .resize(variant.size, variant.size) - .toFile(Application.tmpPath("favicon") + `/favicon_${variant.size}.png`) + jimp.read(Application.tmpPath("favicon") + `/basic.${favicon.extname}`, (err, lenna) => { + if(err) throw err; + lenna + .resize(variant.size, variant.size) + .write(Application.tmpPath("favicon") + `/favicon_${variant.size}.png`) + }) } await Drive.delete(Application.tmpPath("favicon") + `/basic.${favicon.extname}`) diff --git a/altrpnjs/app/Controllers/Http/admin/AreasController.ts b/altrpnjs/app/Controllers/Http/admin/AreasController.ts index de1abc82d..8a5a851f8 100644 --- a/altrpnjs/app/Controllers/Http/admin/AreasController.ts +++ b/altrpnjs/app/Controllers/Http/admin/AreasController.ts @@ -27,20 +27,16 @@ export default class AreasController { const area = await Area.query().where("id", parseInt(params.id)).firstOrFail(); area.name = request.input("name"); - area.settings = request.input("settings"); + area.settings = JSON.stringify(request.input("settings")); area.title = request.input("title") await area.related("categories").detach(); + await area.save() for (const option of request.input("categories")) { - const category = await Category.find(option.value); + const category = await Category.query().where("guid", option.value).first(); - if (!category) { - response.status(404) - return { - message: "Category not Found" - } - } else { + if (category) { await CategoryObject.create({ category_guid: category.guid, object_type: "Area", @@ -48,6 +44,11 @@ export default class AreasController { }) } } + + return response.json({ + success: true + }) + } public async delete({ params }) { @@ -71,7 +72,7 @@ export default class AreasController { }) for (const option of request.input("categories")) { - const category = await Category.find(option.value); + const category = await Category.query().where("guid", option.value).first(); if (!category) { response.status(404) diff --git a/altrpnjs/app/Controllers/Http/admin/CustomizersController.ts b/altrpnjs/app/Controllers/Http/admin/CustomizersController.ts index f24080714..31689bda5 100644 --- a/altrpnjs/app/Controllers/Http/admin/CustomizersController.ts +++ b/altrpnjs/app/Controllers/Http/admin/CustomizersController.ts @@ -6,6 +6,7 @@ import * as _ from 'lodash' import guid from "../../../../helpers/guid"; import Source from "App/Models/Source"; import Event from "@ioc:Adonis/Core/Event"; +import ListenerGenerator from "App/Generators/ListenerGenerator"; export default class CustomizersController { @@ -14,9 +15,10 @@ export default class CustomizersController { let customizer = new Customizer() customizer.fill(request.all()) + customizer.guid = guid() try { - const model = await Model.find(customizer.model_id) + const model = customizer.model_id ? await Model.find(customizer.model_id) : null if (customizer.model_id && model) { customizer.model_guid = model.guid } @@ -41,8 +43,15 @@ export default class CustomizersController { }) await source.save() } + if(customizer.type === "listener" && model) { + const generator = new ListenerGenerator() + + await generator.run(model, customizer.settings.hook_type) + } //@ts-ignore - await Event.emit('model:updated', model) + if(model){ + await Event.emit('model:updated', model) + } } catch (e) { return response.json({ 'success': @@ -84,7 +93,9 @@ export default class CustomizersController { * @var customizer Customizer */ await customizer.load('source', query => { - query.preload('model') + query.preload('model',model => { + model.preload('table') + }) }) return response.json({ 'success': @@ -99,9 +110,6 @@ export default class CustomizersController { const oldSource = await Source.query().where('sourceable_id', params.id) .where('sourceable_type', Customizer.sourceable_type) .first() - if (oldSource) { - await oldSource.delete() - } if (!customizer) { return response.json({ 'success': @@ -111,7 +119,23 @@ export default class CustomizersController { ) } + if(customizer.type === "listener" && request.input("type") !== "listener") { + const generator = new ListenerGenerator() + + const model = await Model.find(customizer.model_id); + if(model) { + await generator.delete(model, customizer.settings.hook_type) + } + } + const oldType = customizer.type customizer.merge(request.all()) + /** + * Delete source if type `api` changed + */ + + if(oldType === 'api' && customizer.$dirty.type && oldSource){ + await oldSource.delete() + } let model try { model = await Model.find(customizer.model_id) @@ -129,8 +153,8 @@ export default class CustomizersController { if (customizer.type === 'api' && model) { await model.load('altrp_controller') - let source = new Source(); - source.fill({ + let source = oldSource ? oldSource : new Source + source.merge({ 'sourceable_type': Customizer.sourceable_type, 'sourceable_id': customizer.id, 'model_id': customizer.model_id, @@ -144,6 +168,11 @@ export default class CustomizersController { }) await source.save() } + if(customizer.type === "listener" && model) { + const generator = new ListenerGenerator() + + await generator.run(model, customizer.settings.hook_type) + } Event.emit('model:updated', model) } catch (e) { @@ -151,6 +180,7 @@ export default class CustomizersController { if(model){ Event.emit('model:updated', model) } + response.status(500) return response.json({ 'success': false, @@ -164,9 +194,10 @@ export default class CustomizersController { ) } await customizer.load('source', query => { - query.preload('model') + query.preload('model',model => { + model.preload('table') + }) }) - return response.json({ 'success': true, 'data': @@ -227,7 +258,17 @@ export default class CustomizersController { },) } try { + if(customizer.type === "listener") { + const generator = new ListenerGenerator() + + const model = await Model.find(customizer.model_id); + if(model) { + await generator.delete(model, customizer.settings.hook_type) + } + } + await customizer.delete() + } catch (e) { return response.json({ 'success': @@ -243,4 +284,25 @@ export default class CustomizersController { } return response.json({'success': true,},) } + + + public async exportCustomizer( {params, response}: HttpContextContract ) + { + let _customizer + if (validGuid(params.id)) { + _customizer = await Customizer.query().where('guid', params.id).first() + } else { + _customizer = await Customizer.find(params.id) + } + if (!_customizer) { + return response.json({ + 'success': + false, 'message': + 'Customizer not found' + }, + ) + } + let customizer = _customizer.serialize() + return response.json(customizer) + } } diff --git a/altrpnjs/app/Controllers/Http/admin/FontSettingsController.ts b/altrpnjs/app/Controllers/Http/admin/FontSettingsController.ts new file mode 100644 index 000000000..b137c909e --- /dev/null +++ b/altrpnjs/app/Controllers/Http/admin/FontSettingsController.ts @@ -0,0 +1,106 @@ +import { HttpContextContract } from '@ioc:Adonis/Core/HttpContext' +import FontSetting from 'App/Models/FontSetting'; + +export default class FontSettingSettingsController { + + public async store({request, response}: HttpContextContract) { + + let fontSetting = new FontSetting() + fontSetting.fill(request.all()) + + try { + await fontSetting.save() + } catch (e) { + return response.json({ + 'success': + false, + 'message': + 'Font Setting don\'t saved', + 'throw message': e.message, + 'trace': e.stack.split('\n'), + }, + ) + } + return response.json({ + 'success': true, + 'data': fontSetting + }, + ) + } + + public async show({params, response}: HttpContextContract) { + let fontSetting = await FontSetting.find(params.id) + if (!fontSetting) { + return response.json({ + 'success': false, + 'message': 'Font Setting not found' + }, + ) + } + return response.json({ + 'success': true, + 'data': fontSetting + },) + } + + public async update({params, request, response}: HttpContextContract) { + let fontSetting = await FontSetting.find(params.id) + if (!fontSetting) { + return response.json({ + 'success': false, + 'message': 'Font Setting not found' + }, + ) + } + fontSetting.merge(request.all()) + try { + await fontSetting.save() + } catch (e) { + response.status(500) + return response.json({ + 'success': false, + 'message': 'Font Setting could not be saved', + 'throw message': e.message, + 'trace': e.stack.split('\n'), + }, + ) + } + return response.json({ + 'success': true, + 'data': fontSetting.serialize() + }) + } + + public async index({params, response}: HttpContextContract) { + + let fontSettings = FontSetting.query().where('font_guid', params.guid) + + return response.json({ + 'success': true, + 'data': fontSettings + }) + } + + public async destroy({params, response}: HttpContextContract) { + let fontSetting = await FontSetting.find(params.id) + if (!fontSetting) { + return response.json({ + 'success': false, + 'message': 'FontSetting not found' + },) + } + try { + await fontSetting.delete() + } catch (e) { + return response.json({ + 'success': false, + 'throw message': e.message, + 'trace': e.stack.split('\n'), + 'message': 'Font Setting could not be deleted' + }, + ) + } + return response.json({'success': true,}) + } + +} diff --git a/altrpnjs/app/Controllers/Http/admin/FontsController.ts b/altrpnjs/app/Controllers/Http/admin/FontsController.ts new file mode 100644 index 000000000..ee31ff64b --- /dev/null +++ b/altrpnjs/app/Controllers/Http/admin/FontsController.ts @@ -0,0 +1,146 @@ +import { HttpContextContract } from '@ioc:Adonis/Core/HttpContext' +import Font from 'App/Models/Font'; +import validGuid from '../../../../helpers/validGuid'; +import guid from "../../../../helpers/guid"; + +export default class FontsController { + + public async store({request, response}: HttpContextContract) { + + let font = new Font() + font.fill(request.all()) + font.guid = guid() + + try { + await font.save() + } catch (e) { + return response.json({ + 'success': + false, + 'message': + 'Font don\'t saved', + 'throw message': e.message, + 'trace': e.stack.split('\n'), + }, + ) + } + return response.json({ + 'success': true, + 'data': font + }, + ) + } + + public async show({params, response}: HttpContextContract) { + let font + if (validGuid(params.id)) { + font = await font.query().where('guid', params.id).first() + } else { + font = await Font.find(params.id) + } + await font.load('fontSettings') + if (!font) { + return response.json({ + 'success': false, + 'message': 'Font not found' + }, + ) + } + return response.json({ + 'success': true, + 'data': font + },) + } + + public async update({params, request, response}: HttpContextContract) { + let font = await Font.find(params.id) + if (!font) { + return response.json({ + 'success': false, + 'message': 'Font not found' + }, + ) + } + font.merge(request.all()) + try { + await font.save() + } catch (e) { + console.trace(e); + response.status(500) + return response.json({ + 'success': false, + 'message': 'Font could not be saved', + 'throw message': e.message, + 'trace': e.stack.split('\n'), + }, + ) + } + return response.json({ + 'success': true, + 'data': font.serialize() + }) + } + + public async index({request, response}: HttpContextContract) { + + let search = request.qs().s || '' + let categories = request.qs() || '' + let orderColumn = request.qs().order_by || 'font_family' + let orderType: 'asc' | 'desc' = request.qs()?.order ? request.qs().order.toLowerCase() : 'asc' + + let fonts = Font.query().preload('categories') + if (fonts && _.isString(categories)) { + categories = categories.split(',') + fonts.leftJoin('altrp_category_objects', + 'altrp_category_objects.object_guid', + '=', + 'altrp_fonts.guid') + // @ts-ignore + fonts.whereIn('altrp_category_objects.category_guid', categories) + } + + if (search) { + fonts.where(function (query) { + query.where('altrp_fonts.font_family', 'like', '%' + search + '%') + .orWhere('altrp_fonts.id', 'like', '%' + search + '%') + }) + } + fonts.orderBy(orderColumn, orderType) + const result = await fonts.select('altrp_fonts.*') + + return response.json({ + 'success': + true, + 'data': + result, + }) + } + + public async destroy({params, response}: HttpContextContract) { + let font + if (validGuid(params.id)) { + font = await Font.query().where('guid', params.id).first() + } else { + font = await Font.find(params.id) + } + if (!font) { + return response.json({ + 'success': false, + 'message': 'Font not found' + },) + } + try { + await font.delete() + } catch (e) { + return response.json({ + 'success': false, + 'throw message': e.message, + 'trace': e.stack.split('\n'), + 'message': 'Font could not be deleted' + }, + ) + } + return response.json({'success': true,},) + } + +} diff --git a/altrpnjs/app/Controllers/Http/admin/MediaController.ts b/altrpnjs/app/Controllers/Http/admin/MediaController.ts index 8de06a247..2d82e4ec0 100644 --- a/altrpnjs/app/Controllers/Http/admin/MediaController.ts +++ b/altrpnjs/app/Controllers/Http/admin/MediaController.ts @@ -138,7 +138,7 @@ export default class MediaController { } media.main_color = '' media.url = '/storage'+urlBase+ filename; - media.save(); + await media.save(); const categories = request.input( '_categories' ); if( is_array(categories) && categories.length > 0 && media.guid){ diff --git a/altrpnjs/app/Controllers/Http/admin/MenusController.ts b/altrpnjs/app/Controllers/Http/admin/MenusController.ts index 9c1f4aefa..f73271667 100644 --- a/altrpnjs/app/Controllers/Http/admin/MenusController.ts +++ b/altrpnjs/app/Controllers/Http/admin/MenusController.ts @@ -22,13 +22,31 @@ export default class MenusController { } public async show({params}) { - const menu = await Menu.query().where("id", parseInt(params.id)).firstOrFail(); + + const menuQuery = Menu.query(); + + if(isNaN(params.id)) { + menuQuery.where("guid", params.id) + } else { + menuQuery.where("id", parseInt(params.id)) + } + + const menu = await menuQuery.firstOrFail() return menu } public async update({params, request, response}) { - const menu = await Menu.query().where("id", parseInt(params.id)).firstOrFail(); + + const menuQuery = Menu.query(); + + if(isNaN(params.id)) { + menuQuery.where("guid", params.id) + } else { + menuQuery.where("id", parseInt(params.id)) + } + + const menu = await menuQuery.firstOrFail() menu.children = request.input("children"); menu.name = request.input("name"); @@ -39,7 +57,7 @@ export default class MenusController { await menu.related("categories").detach(); for (const option of request.input("categories")) { - const category = await Category.find(option.value); + const category = await Category.query().where("guid", option.value).first(); if (!category) { response.status(404) @@ -61,7 +79,17 @@ export default class MenusController { } public async delete({params}) { - const menu = await Menu.query().where("id", parseInt(params.id)).firstOrFail(); + + const menuQuery = Menu.query(); + + + if(isNaN(params.id)) { + menuQuery.where("guid", params.id) + } else { + menuQuery.where("id", parseInt(params.id)) + } + + const menu = await menuQuery.firstOrFail() menu.delete() diff --git a/altrpnjs/app/Controllers/Http/admin/ModelsController.ts b/altrpnjs/app/Controllers/Http/admin/ModelsController.ts index 411545319..aa5c35796 100644 --- a/altrpnjs/app/Controllers/Http/admin/ModelsController.ts +++ b/altrpnjs/app/Controllers/Http/admin/ModelsController.ts @@ -1,29 +1,30 @@ import {HttpContextContract} from '@ioc:Adonis/Core/HttpContext' -import Model from "App/Models/Model" -import Source from "App/Models/Source" -import Accessors from "App/Models/Accessor" -import empty from "../../../../helpers/empty" -import CategoryObject from "App/Models/CategoryObject" -import {schema} from "@ioc:Adonis/Core/Validator" +import Model from 'App/Models/Model' +import Source from 'App/Models/Source' +import Accessors from 'App/Models/Accessor' +import empty from '../../../../helpers/empty' +import CategoryObject from 'App/Models/CategoryObject' import Event from '@ioc:Adonis/Core/Event' -import Column from "App/Models/Column" -import Relationship from "App/Models/Relationship" -import Database from "@ioc:Adonis/Lucid/Database" -import Env from "@ioc:Adonis/Core/Env" +import Column from 'App/Models/Column' +import Relationship from 'App/Models/Relationship' +import Database from '@ioc:Adonis/Lucid/Database' +import Env from '@ioc:Adonis/Core/Env' import {string} from '@ioc:Adonis/Core/Helpers' -import Table from "App/Models/Table" -import Controller from "App/Models/Controller" -import ModelGenerator from "App/Generators/ModelGenerator" -import Role from "App/Models/Role" -import SourceRole from "App/Models/SourceRole" -import guid from "../../../../helpers/guid" -import SQLEditor from "App/Models/SQLEditor"; +import Table from 'App/Models/Table' +import Controller from 'App/Models/Controller' +import ModelGenerator from 'App/Generators/ModelGenerator' +import Role from 'App/Models/Role' +import SourceRole from 'App/Models/SourceRole' +import guid from '../../../../helpers/guid' +import SQLEditor from 'App/Models/SQLEditor'; +import {schema, rules} from '@ioc:Adonis/Core/Validator' +import {parseInt} from 'lodash'; export default class ModelsController { async index({response, request}: HttpContextContract) { let query = Model.query() if (request.qs().categories) { - let categoriesQuery = request.qs().categories.split(",") + let categoriesQuery = request.qs().categories.split(',') query = query.leftJoin('altrp_category_objects', 'altrp_category_objects.object_guid', '=', 'altrp_models.guid') .whereIn('altrp_category_objects.category_guid', categoriesQuery) } @@ -172,7 +173,7 @@ export default class ModelsController { const rowId = parseInt(httpContext.params.row); - const model = await Model.query().where("id", id).firstOrFail(); + const model = await Model.query().where('id', id).firstOrFail(); httpContext.params[model.name] = rowId @@ -213,7 +214,7 @@ export default class ModelsController { const rowId = parseInt(httpContext.params.row); - const model = await Model.query().where("id", id).firstOrFail(); + const model = await Model.query().where('id', id).firstOrFail(); httpContext.params[model.name] = rowId @@ -286,6 +287,33 @@ export default class ModelsController { }) } + async getDataSources(contract: HttpContextContract) { + return contract.response.json(await this.getDataSourcesAndPageCount(contract)) + } + + async getDataSourcesAndPageCount({request}: HttpContextContract) { + + const page = parseInt(request.qs().page) + + let pageSize = 10 + if(page) { + const sources = await Source.query().offset((page - 1)*pageSize).limit(pageSize).select('*'); + return { + data_sources: sources, + pageCount: 0 + } + } else { + const sources = await Source.query().select('*') + return { + data_sources: sources, + pageCount: 0 + } + } + // filtration(templatesQuery, request, [ + // 'title', + // ]) + } + async getDataSourcesOptionsByModel({response, params}: HttpContextContract) { let model = await Model.find(params.id) @@ -336,10 +364,24 @@ export default class ModelsController { soft_deletes: modelData.soft_deletes, guid: guid(), time_stamps: modelData.time_stamps, + id: modelData.id, parent_model_id: modelData.parent_model_id || null, table_id: table.id, }) await model.save() + const id_column = new Column() + id_column.fill({ + name: 'id', + title: 'ID', + description: 'ID', + null: true, + type: 'bigInteger', + table_id: table.id, + model_id: model.id, + // @ts-ignore + user_id: auth?.user?.id, + }) + await id_column.save() if (modelData.time_stamps) { const created_at_column = new Column() created_at_column.fill({ @@ -683,4 +725,144 @@ export default class ModelsController { } return response.json(data_sources) } + + + async showDataSource({response, params}:HttpContextContract){ + let dataSource = await Source.query().where('id', params.id) + .preload('roles').preload('permissions') + .first() + if(!dataSource) { + response.status(404) + return response.json({ + success: false, + message: 'Datasource not found' + }) + } + + let data = dataSource.toJSON() + + data['access'] = {'roles' : [], 'permissions' : []} + let sourceRoles = dataSource['roles'] + let sourcePermissions = dataSource['permissions'] + // @ts-ignore + delete dataSource.roles + // @ts-ignore + delete dataSource.permissions + if (sourceRoles) { + for (let sourceRole of sourceRoles) { + data['access']['roles'].push(sourceRole['role']['id']) + } + } + + if (sourcePermissions) { + for (let sourcePermission of sourcePermissions) { + data['access']['permissions'].push(sourcePermission['permission']['id']) + } + } + + return response.json(data) + } + + + async updateDataSource({response, request, params}:HttpContextContract){ + + const dataSourceSchema = schema.create({ + title: schema.string({trim: true}, [ + rules.maxLength(32) + ]), + name: schema.string({trim: true}, [ + rules.maxLength(32) + ]), + }) + + await request.validate({schema: dataSourceSchema}) + + let dataSource = await Source.query().where('id', params.id).first() + if(!dataSource) { + response.status(404) + return response.json({ + success: false, + message: 'Datasource not found' + }) + } + + let data = request.all() + + if (data['access']['roles'].lenght <= 1) { + data['need_all_roles'] = 0 + } + + dataSource.merge(data) + await dataSource.save() + return response.json({ + success: true, + data: dataSource + }) + } + + async storeDataSource({response, request}:HttpContextContract) + { + const dataSourceSchema = schema.create({ + title: schema.string({trim: true}, [ + rules.maxLength(32) + ]), + name: schema.string({trim: true}, [ + rules.maxLength(32) + ]), + }) + + await request.validate({schema: dataSourceSchema}) + + let dataSource = await Source.create(request.all()) + + return response.json({ + success: true, + data: dataSource + }) + } + + + async getDataSourceOptions(contract: HttpContextContract) + { + let result = await this.getDataSourcesAndPageCount(contract) + let options : any = [] + for (let source of result['data_sources']) { + options.push({ + value : source.id, + label : source.name, + }) + } + options = { + options : options, + pageCount : result['pageCount'] + } + return contract.response.json(options) + } + + + async destroyDataSource({response, params}:HttpContextContract) + { + let dataSource = await Source.find(params.id) + if(!dataSource) { + response.status(404) + return response.json({ + success: false, + message: 'Datasource not found' + }) + } + await dataSource.delete() + + return response.json({ + success: true + }) + + } + + + async getDataSourcesByModel({response, params}:HttpContextContract) + { + let data_sources = await Source.query().where('model_id',params.model_id).select(['title as label', 'id as value']) + return response.json(data_sources) + } + } diff --git a/altrpnjs/app/Controllers/Http/admin/PageDatasourcesController.ts b/altrpnjs/app/Controllers/Http/admin/PageDatasourcesController.ts new file mode 100644 index 000000000..05a107de6 --- /dev/null +++ b/altrpnjs/app/Controllers/Http/admin/PageDatasourcesController.ts @@ -0,0 +1,107 @@ +// import { HttpContextContract } from '@ioc:Adonis/Core/HttpContext' + +import PageDatasource from "App/Models/PageDatasource"; +import Page from "App/Models/Page"; + +export default class PageDatasourcesController { + public async store({ request, response }) { + + const body = request.body(); + + const page = await Page.query().where("id", body.page_id).firstOrFail() + + if(!body.source_id || !body.alias) { + response.status(500) + return { + message: "fill all fields" + } + } + + const data = { + alias: body.alias, + autoload: body.autoload, + parameters: body.parameters || null, + priority: body.priority, + source_id: body.source_id, + page_guid: page.guid, + page_id: page.id, + server_side: body.server_side || false, + } + + const pageDatasource = await PageDatasource.create(data) + + return pageDatasource + } + + public async update({request, params, response}) { + const pageDatasource = await PageDatasource.query().where("id", parseInt(params.id)).firstOrFail(); + + const body = request.body(); + + if(!body.source || !body.alias) { + response.status(500) + return { + message: "fill all fields" + } + } + + pageDatasource.alias = body.alias; + pageDatasource.autoload = body.autoload || false; + pageDatasource.parameters = body.parameters || null; + pageDatasource.priority = body.priority; + pageDatasource.source_id = body.source.id; + pageDatasource.server_side = body.server_side || false; + pageDatasource.page_id = body.page_id; + pageDatasource.page_guid = body.page_guid + + if(await pageDatasource.save()) { + return { + success: true + } + } else { + response.status(500) + return { + message: "pageDatasource not saved" + } + } + } + + public async getByPage({params}) { + const pageQuery = Page.query().preload("pageDatasources", (pageDatasources) => { + pageDatasources.preload("source") + }) + + if(isNaN(params.id)) { + pageQuery.where("guid", params.id) + } else { + pageQuery.where("id", parseInt(params.id)) + } + + const page = await pageQuery.firstOrFail(); + + const sources = page.pageDatasources; + + return sources + } + + public async destroy({params}) { + const pageDatasource = await PageDatasource.query().where("id", parseInt(params.id)).firstOrFail(); + + pageDatasource.delete() + return { + success: true + } + } + + public async show({params}) { + const pageDatasource = await PageDatasource.query().where("id", parseInt(params.id)).preload("source").firstOrFail(); + + return pageDatasource + } + + public async index() { + const pageDatasource = await PageDatasource.all() + + return pageDatasource + } +} diff --git a/altrpnjs/app/Controllers/Http/admin/PluginController.ts b/altrpnjs/app/Controllers/Http/admin/PluginController.ts index b9de1bd47..cf8558392 100644 --- a/altrpnjs/app/Controllers/Http/admin/PluginController.ts +++ b/altrpnjs/app/Controllers/Http/admin/PluginController.ts @@ -13,9 +13,13 @@ export default class PluginController { */ public async switch({request, response}: HttpContextContract) { - await Plugin.switchEnable(request.qs().name, !!request.qs().value) + let data = request.all() + //await Plugin.switchEnable(request.qs().name, !!request.qs().value) + await Plugin.switchEnable(data.name, data.value) + + //let plugin = new Plugin({'name': request.qs().name}) + let plugin = new Plugin({'name': data.name}) - let plugin = new Plugin({'name': request.qs().name}) return response.json({'success': true, 'data': plugin}) } diff --git a/altrpnjs/app/Controllers/Http/admin/RelationshipsController.ts b/altrpnjs/app/Controllers/Http/admin/RelationshipsController.ts index 13ed89d15..61e94a71e 100644 --- a/altrpnjs/app/Controllers/Http/admin/RelationshipsController.ts +++ b/altrpnjs/app/Controllers/Http/admin/RelationshipsController.ts @@ -2,6 +2,7 @@ import {HttpContextContract} from '@ioc:Adonis/Core/HttpContext' import Model from "App/Models/Model"; import Event from '@ioc:Adonis/Core/Event' import Relationship from "App/Models/Relationship"; +import Database from '@ioc:Adonis/Lucid/Database'; export default class RelationshipsController { @@ -28,28 +29,105 @@ export default class RelationshipsController { return response.json({success:false, message: 'select keys'}) } - relationship.fill({ - title: relationshipData.title, - description: relationshipData.description, - type: relationshipData.type, - add_belong_to: relationshipData.add_belong_to, - editable: relationshipData.editable, - always_with: relationshipData.always_with, - local_key: relationshipData.local_key, - foreign_key: relationshipData.foreign_key, - onDelete: relationshipData.onDelete, - onUpdate: relationshipData.onUpdate, - target_model_id: relationshipData.target_model_id, - model_class: '\\App\\AltrpModels\\' + model.name, - model_id: model.id, - name: relationshipData.name, - }) - - await relationship.save() - let targetModel = await Model.find(relationshipData.target_model_id) - Event.emit('model:updated', model) - if(targetModel){ - Event.emit('model:updated', targetModel) + + try { + + if (relationshipData.type === 'belongsTo') { + const sourceRealtion = await Relationship.query() + .where('model_id', relationshipData.target_model_id) + .where('local_key', relationshipData.foreign_key) + .where('foreign_key', relationshipData.local_key) + .first() + if(!sourceRealtion){ + response.status(404) + return response.json({success:false, message: 'Not found reverse relation'}) + } + } + + let targetModel = await Model.find(relationshipData.target_model_id) + + if (relationshipData.type == "belongsTo" && targetModel) { + await targetModel.load('table') + try { + let query = `ALTER TABLE ${model.table.name} ADD CONSTRAINT + ${model.table.name}_${relationshipData.local_key}_foreign + FOREIGN KEY (${relationshipData.local_key}) + REFERENCES ${targetModel.table.name}(${relationshipData.foreign_key}) + ON DELETE ${relationshipData.onDelete} + ON UPDATE ${relationshipData.onUpdate}` + await Database.rawQuery(query) + } catch (e) { + + } + } + + relationship.fill({ + title: relationshipData.title, + description: relationshipData.description, + type: relationshipData.type, + add_belong_to: relationshipData.add_belong_to, + editable: relationshipData.editable, + always_with: relationshipData.always_with, + local_key: relationshipData.local_key, + foreign_key: relationshipData.foreign_key, + onDelete: relationshipData.onDelete, + onUpdate: relationshipData.onUpdate, + target_model_id: relationshipData.target_model_id, + model_class: '\\App\\AltrpModels\\' + model.name, + model_id: model.id, + name: relationshipData.name, + }) + await relationship.save() + Event.emit('model:updated', model) + + + if (relationshipData.type != "belongsTo" && targetModel && relationshipData.add_belong_to) { + + await targetModel.load('table') + try { + let query = `ALTER TABLE ${targetModel.table.name} ADD CONSTRAINT + ${targetModel.table.name}_${relationshipData.foreign_key}_foreign + FOREIGN KEY (${relationshipData.foreign_key}) + REFERENCES ${model.table.name}(${relationshipData.local_key}) + ON DELETE restrict + ON UPDATE restrict` + await Database.rawQuery(query) + } catch (e) { + + } + + const belongsToRelationship = new Relationship() + belongsToRelationship.fill({ + title: model.title, + description: relationshipData.description, + type: 'belongsTo', + add_belong_to: false, + editable: false, + always_with: false, + local_key: relationshipData.foreign_key, + foreign_key: relationshipData.local_key, + onDelete: 'restrict', + onUpdate: 'restrict', + target_model_id: model.id, + model_class: '\\App\\AltrpModels\\' + targetModel.name, + model_id: relationshipData.target_model_id, + name: model.name, + }) + await belongsToRelationship.save() + Event.emit('model:updated', targetModel) + + } + + } catch (e) { + return response.json({ + 'success': + false, + 'message': + 'Relationship don\'t saved', + 'throw message': e.message, + 'trace': e.stack.split('\n'), + }, + ) } return response.json({success: true, data: relationship}) @@ -64,37 +142,135 @@ export default class RelationshipsController { success: false, message: 'Model not found' }) - } - const relationship = await Relationship.find(params.field_id) + await model.load('table') + + let relationshipData = request.all() + + //const relationship = await Relationship.find(params.field_id) + const relationship = await Relationship.find(relationshipData.id) if (!relationship) { response.status(404) return response.json({ success: false, message: 'Field not found' }) + } + + try { + let targetModel = await Model.find(relationship.target_model_id) + if(relationship.type != "belongsTo" && targetModel && relationship.add_belong_to){ + + await targetModel.load('table') + + try { + let deleteQuery = `ALTER TABLE ${targetModel.table.name} DROP FOREIGN KEY ${targetModel.table.name}_${relationship.foreign_key}_foreign` + await Database.rawQuery(deleteQuery) + } catch (e) { + } + + await Relationship.query() + .where('model_id', relationship.target_model_id) + .where('target_model_id', relationship.model_id) + .where('foreign_key', relationship.local_key) + .where('local_key', relationship.foreign_key) + .where('type', 'belongsTo') + .delete() + Event.emit('model:updated', targetModel) + + } + + if (relationship.type === "belongsTo") { + try { + let deleteQuery = `ALTER TABLE ${model.table.name} DROP FOREIGN KEY ${model.table.name}_${relationship.local_key}_foreign` + await Database.rawQuery(deleteQuery) + } catch (e) { + } + } + + let newTargetModel = await Model.find(relationshipData.target_model_id) + if (relationshipData.type === "belongsTo" && newTargetModel) { + await newTargetModel.load('table') + + try { + let query = `ALTER TABLE ${model.table.name} ADD CONSTRAINT + ${model.table.name}_${relationshipData.local_key}_foreign + FOREIGN KEY (${relationshipData.local_key}) + REFERENCES ${newTargetModel.table.name}(${relationshipData.foreign_key}) + ON DELETE ${relationshipData.onDelete} + ON UPDATE ${relationshipData.onUpdate}` + await Database.rawQuery(query) + } catch (e) { + } + } + + relationship.merge({ + title: relationshipData.title, + description: relationshipData.description, + type: relationshipData.type, + add_belong_to: relationshipData.add_belong_to, + editable: relationshipData.editable, + always_with: relationshipData.always_with, + local_key: relationshipData.local_key, + foreign_key: relationshipData.foreign_key, + onDelete: relationshipData.onDelete, + onUpdate: relationshipData.onUpdate, + target_model_id: relationshipData.target_model_id, + model_class: '\\App\\AltrpModels\\' + model.name, + model_id: model.id, + name: relationshipData.name, + }) + await relationship.save() + Event.emit('model:updated', model) + + if (relationshipData.type != "belongsTo" && newTargetModel && relationshipData.add_belong_to) { + + try { + await newTargetModel.load('table') + let query = `ALTER TABLE ${newTargetModel.table.name} ADD CONSTRAINT + ${newTargetModel.table.name}_${relationshipData.foreign_key}_foreign + FOREIGN KEY (${relationshipData.foreign_key}) + REFERENCES ${model.table.name}(${relationshipData.local_key}) + ON DELETE restrict + ON UPDATE restrict` + await Database.rawQuery(query) + } catch (e) { + } + + const belongsToRelationship = new Relationship() + belongsToRelationship.fill({ + title: model.title, + description: relationshipData.description, + type: 'belongsTo', + add_belong_to: false, + editable: false, + always_with: false, + local_key: relationshipData.foreign_key, + foreign_key: relationshipData.local_key, + onDelete: 'restrict', + onUpdate: 'restrict', + target_model_id: model.id, + model_class: '\\App\\AltrpModels\\' + newTargetModel.name, + model_id: relationshipData.target_model_id, + name: model.name, + }) + await belongsToRelationship.save() + Event.emit('model:updated', newTargetModel) + + } + + } catch (e) { + return response.json({ + 'success': + false, + 'message': + 'Relationship don\'t saved', + 'throw message': e.message, + 'trace': e.stack.split('\n'), + }, + ) } - let relationshipData = request.all() - relationship.merge({ - title: relationshipData.title, - description: relationshipData.description, - type: relationshipData.type, - add_belong_to: relationshipData.add_belong_to, - editable: relationshipData.editable, - always_with: relationshipData.always_with, - local_key: relationshipData.local_key, - foreign_key: relationshipData.foreign_key, - onDelete: relationshipData.onDelete, - onUpdate: relationshipData.onUpdate, - target_model_id: relationshipData.target_model_id, - model_class: '\\App\\AltrpModels\\' + model.name, - model_id: model.id, - name: relationshipData.name, - }) - - await relationshipData.save() - Event.emit('model:updated', model) return response.json({success: true, data: relationship}) } @@ -132,8 +308,8 @@ export default class RelationshipsController { success: false, message: 'Model not found' }) - } + const relationship = await Relationship.find(params.relationship_id) if (!relationship) { response.status(404) @@ -141,14 +317,57 @@ export default class RelationshipsController { success: false, message: 'Field not found' }) - } - let targetModel = await Model.find(relationship.target_model_id) - if(targetModel){ - Event.emit('model:updated', targetModel) + + try { + + await model.load('table') + + let targetModel = await Model.find(relationship.target_model_id) + + if(relationship.type != "belongsTo" && targetModel && relationship.add_belong_to){ + + try { + await targetModel.load('table') + let deleteQuery = `ALTER TABLE ${targetModel.table.name} DROP FOREIGN KEY ${targetModel.table.name}_${relationship.foreign_key}_foreign` + await Database.rawQuery(deleteQuery) + } catch (e) { + + } + await Relationship.query() + .where('model_id', relationship.target_model_id) + .where('target_model_id', relationship.model_id) + .where('foreign_key', relationship.local_key) + .where('local_key', relationship.foreign_key) + .where('type', 'belongsTo') + .delete() + Event.emit('model:updated', targetModel) + + } + + try { + if (relationship.type === "belongsTo") { + let deleteQuery = `ALTER TABLE ${model.table.name} DROP FOREIGN KEY ${model.table.name}_${relationship.local_key}_foreign` + await Database.rawQuery(deleteQuery) + } + } catch (e) { + + } + + await relationship.delete() + Event.emit('model:updated', model) + + } catch (e) { + return response.json({ + 'success': + false, + 'message': + 'Relationship don\'t saved', + 'throw message': e.message, + 'trace': e.stack.split('\n'), + }, + ) } - await relationship.delete() - Event.emit('model:updated', model) return response.json({success: true,}) } diff --git a/altrpnjs/app/Controllers/Http/users/UsersController.ts b/altrpnjs/app/Controllers/Http/users/UsersController.ts index 6547e83c2..bc746148f 100644 --- a/altrpnjs/app/Controllers/Http/users/UsersController.ts +++ b/altrpnjs/app/Controllers/Http/users/UsersController.ts @@ -37,14 +37,17 @@ export default class UsersController { } } - body.roles = body._roles; - delete body._roles; - body.permissions = body._permissions; - delete body._permissions - delete body.password_confirmation - const user = await User.create(body) + const user = await User.create({ + email: body.email, + password: body.password, + name: body.name, + telegram_user_id: body.telegram_user_id, + }) + + await user.related("roles").attach(body._roles) + await user.related("permissions").attach(body._permissions) return user } diff --git a/altrpnjs/app/Customizer/Nodes/BaseNode.ts b/altrpnjs/app/Customizer/Nodes/BaseNode.ts index 19f8f24b3..f85ee7730 100644 --- a/altrpnjs/app/Customizer/Nodes/BaseNode.ts +++ b/altrpnjs/app/Customizer/Nodes/BaseNode.ts @@ -45,6 +45,7 @@ export default class BaseNode constructor( public data, protected customizer: Customizer ){ } + public static findNodeById( id, data: BaseNode[] ):BaseNode|undefined{ return data.find( function( node:BaseNode ) { return node.getId() == id diff --git a/altrpnjs/app/Customizer/Nodes/ReturnNode.ts b/altrpnjs/app/Customizer/Nodes/ReturnNode.ts index f8166b2f7..a0a2422b6 100644 --- a/altrpnjs/app/Customizer/Nodes/ReturnNode.ts +++ b/altrpnjs/app/Customizer/Nodes/ReturnNode.ts @@ -36,6 +36,6 @@ export default class ReturnNode extends BaseNode implements NodeInterface { let property:object |null|string = this.getProperty() property = this.customizer.propertyToJS( property ) - return 'return ' + property + return `return ${property};` } } diff --git a/altrpnjs/app/Generators/BaseGenerator.ts b/altrpnjs/app/Generators/BaseGenerator.ts index 61b05af04..0f90be7e1 100644 --- a/altrpnjs/app/Generators/BaseGenerator.ts +++ b/altrpnjs/app/Generators/BaseGenerator.ts @@ -1,7 +1,11 @@ +import View from '@ioc:Adonis/Core/View' import * as mustache from 'mustache' import fs from 'fs' import Application from "@ioc:Adonis/Core/Application"; import path from "path"; +import isProd from "../../helpers/isProd"; +import {CacheManager} from "edge.js/build/src/CacheManager"; +import env from "../../helpers/env"; export abstract class BaseGenerator{ private fileName: string; @@ -35,6 +39,10 @@ export abstract class BaseGenerator{ fs.mkdirSync(this.directory, {recursive:true}) } fs.writeFileSync(this.getFullFileName(), content) + if(isProd()){ + View.asyncCompiler.cacheManager = new CacheManager(env('CACHE_VIEWS')) + Object.keys(require.cache).forEach(function(key) { delete require.cache[key] }) + } return } diff --git a/altrpnjs/app/Generators/ListenerGenerator.ts b/altrpnjs/app/Generators/ListenerGenerator.ts new file mode 100644 index 000000000..0691cd22e --- /dev/null +++ b/altrpnjs/app/Generators/ListenerGenerator.ts @@ -0,0 +1,92 @@ +import {BaseGenerator} from "App/Generators/BaseGenerator"; +import app_path from "../../helpers/app_path"; +import isProd from "../../helpers/isProd"; +import Model from "App/Models/Model"; +import fs from "fs"; + + +export default class ListenerGenerator extends BaseGenerator { + public static directory = app_path('/altrp-listeners/') + public static listeners = [ + 'beforeSave', + 'beforeCreate', + 'beforeUpdate', + 'beforeDelete', + 'beforeFind', + 'beforePaginate', + 'afterSave', + 'afterCreate', + 'afterUpdate', + 'afterDelete', + 'afterFind', + 'afterPaginate' + ] + public static ext = isProd() ? '.js': '.ts' + private static template = app_path(`/altrp-templates/${isProd() ? 'prod' : 'dev'}/AltrpListener.stub`) + private model: Model + + public async run(model, type) { + if(!model || !type) { + return + } + + this.model = model; + + const dir = ListenerGenerator.directory + "altrp_models" + "." + this.model.name + "." + type; + + if (!fs.existsSync(dir)){ + fs.mkdirSync(dir); + } + + const listener = type + + await this.addFile( listener + ListenerGenerator.ext) + .destinationDir(dir) + .stub(ListenerGenerator.template) + .apply({ + listener, + }); + } + + public async delete(model, type) { + if(!model || !type) { + return + } + + this.model = model; + + const dir = ListenerGenerator.directory + "altrp_models" + "." + this.model.name + "." + type; + + if (!fs.existsSync(dir)){ + fs.mkdirSync(dir); + } + + const listener = type + + fs.unlinkSync(dir + "\\" + listener + ListenerGenerator.ext) + } + + public async runAll(model) { + if(!model) { + return + } + + this.model = model; + + const dir = ListenerGenerator.directory + this.model.name; + + if (!fs.existsSync(dir)){ + fs.mkdirSync(dir); + } + + for (const listener of ListenerGenerator.listeners) { + await this.addFile(listener + ListenerGenerator.ext) + .destinationDir(dir) + .stub(ListenerGenerator.template) + .apply({ + listener, + }); + } + + } +} diff --git a/altrpnjs/app/Generators/ModelGenerator.ts b/altrpnjs/app/Generators/ModelGenerator.ts index 4cbe1e8ef..36e115358 100644 --- a/altrpnjs/app/Generators/ModelGenerator.ts +++ b/altrpnjs/app/Generators/ModelGenerator.ts @@ -91,6 +91,8 @@ export default class ModelGenerator extends BaseGenerator { } private _getProdImportsContent(): string { return ` + const Event =__importDefault(global[Symbol.for('ioc.use')]("Adonis/Core/Event")); + ${_.uniqBy( this.altrp_relationships .filter(relationship => relationship?.altrp_target_model?.name), @@ -104,6 +106,8 @@ ${_.uniqBy( private _getDevImportsContent(): string { return `import * as luxon from 'luxon' import * as Orm from '@ioc:Adonis/Lucid/Orm' +import Event from '@ioc:Adonis/Core/Event' +import { softDelete, forceDelete } from "../../helpers/delete" ${_.uniqBy( this.altrp_relationships .filter(relationship => relationship?.altrp_target_model?.name), @@ -116,7 +120,7 @@ ${_.uniqBy( } private getClassnameContent(): string { - return ` ${this.model.name} ` + return `${this.model.name}` } private getPropertiesContent(): string { diff --git a/altrpnjs/app/Generators/PageGenerator.ts b/altrpnjs/app/Generators/PageGenerator.ts index bfe052dc1..ec0b996f3 100644 --- a/altrpnjs/app/Generators/PageGenerator.ts +++ b/altrpnjs/app/Generators/PageGenerator.ts @@ -33,14 +33,40 @@ export default class PageGenerator extends BaseGenerator { return } let children_content = await page.getChildrenContent() + + let elements_list:string[]|string = await page.extractElementsNames() + const {extra_header_styles, extra_footer_styles} = await this.getExtraStyles(elements_list) + elements_list = elements_list.map(e=>`'${e}'`) + elements_list = elements_list.join(',') let all_styles = await page.getAllStyles() return await this.addFile(fileName) .destinationDir(PageGenerator.directory) .stub(PageGenerator.template) .apply({ children_content, + elements_list, + extra_header_styles, + extra_footer_styles, all_styles, }) } + async getExtraStyles(elementsList):Promise<{ + extra_header_styles:string + extra_footer_styles:string + }>{ + const extraStyles = { + extra_header_styles: '', + extra_footer_styles: '', + } + for(let element of elementsList){ + const fileName = app_path(`/altrp-templates/styles/elements/${element}.css`) + if(fs.existsSync(fileName)){ + let content = fs.readFileSync(fileName, {encoding:'utf8'}) + content = content.replace(/\n/g, '') + extraStyles.extra_header_styles += `') === -1) { + s = `` + return s + } + }) + styles += customStyles.join('') + } + } + if (headerStyles) { headerStyles = JSON.parse(headerStyles) headerStyles = _.get(headerStyles, 'all_styles', []) + headerStyles = headerStyles.map(s => { + if (s.indexOf('') === -1) { + s = `` + return s + } + }) styles += headerStyles.join('') } if (contentStyles) { contentStyles = JSON.parse(contentStyles) contentStyles = _.get(contentStyles, 'all_styles', []) + contentStyles = contentStyles.map(s => { + if (s.indexOf('') === -1) { + s = `` + return s + } + }) styles += contentStyles.join('') } + + let _contentAreas = await Area.query().whereNotIn('name', [ + 'card', 'popup', 'reports', 'email' + ]).select('*') + + let contentAreas: Area[] = [] + for (let contentArea of _contentAreas) { + + if (Page.FRONT_DEFAULT_AREAS.indexOf(contentArea.name) != -1) { + contentAreas.push(contentArea) + continue + } + + if (data_get(await Template.getTemplate(this.id, contentArea.name), 'guid')) { + contentAreas.push(contentArea) + } + + } + + styles += `` + return styles } async getChildrenContent() { + // @ts-ignore + let footer:Template = await Template.getTemplate(this.id, 'footer') let contentGuid = data_get(await Template.getTemplate(this.id, 'content'), 'guid') - let footerGuid = data_get(await Template.getTemplate(this.id, 'footer'), 'guid') + let footerGuid = data_get(footer, 'guid') let headerGuid = data_get(await Template.getTemplate(this.id, 'header'), 'guid') - - return `