/// /// /// const stripBom = require("strip-bom"); const skipConfirmationFunc = require('../utils/skip-confirmation'); import fs = require("fs"); import path = require('path'); import lodash = require('lodash'); import metadata = require('MetadataClasses'); import Locales from '../flexberry-core/Locales'; import CommonUtils from '../flexberry-common/CommonUtils'; import ModelBlueprint from '../flexberry-model/ModelBlueprint'; const componentMaps = [ { name: "flexberry-file", types: ["file"] }, { name: "flexberry-checkbox", types: ["boolean"] }, { name: "flexberry-simpledatetime", types: ["date"] }, { name: "flexberry-field", types: ["string", "number", "decimal"] } ]; module.exports = { description: 'Generates an ember edit-form for flexberry.', availableOptions: [ { name: 'file', type: String }, { name: 'metadata-dir', type: String }, { name: 'skip-confirmation', type: Boolean } ], supportsAddon: function () { return false; }, _files: null, isDummy: false, files: function () { if (this._files) { return this._files; } this.isDummy = this.options.dummy; if (this.options.dummy) { this._files = CommonUtils.getFilesForGeneration(this, function (v) { return v === "app/templates/__name__.hbs"; }); } else { this._files = CommonUtils.getFilesForGeneration(this, function (v) { return v === "tests/dummy/app/templates/__name__.hbs"; }); } return this._files; }, afterInstall: function (options) { if (this.project.isEmberCLIAddon()) { CommonUtils.installFlexberryAddon(options, ["controller", "route"]); CommonUtils.installReexportNew(options, ["controller", "route"]); } }, processFiles(intoDir, templateVariables) { let skipConfirmation = this.options.skipConfirmation; if (skipConfirmation) { return skipConfirmationFunc(this, intoDir, templateVariables); } return this._super.processFiles.apply(this, [intoDir, templateVariables]); }, /** * Blueprint Hook locals. * Use locals to add custom template variables. The method receives one argument: options. * * @method locals * @public * * @param {Object} options Options is an object containing general and entity-specific options. * @return {Object} Сustom template variables. */ locals: function (options) { if (!options.file) { options.file = options.entity.name + ".json"; } var editFormBlueprint = new EditFormBlueprint(this, options); return lodash.defaults({ modelName: editFormBlueprint.editForm.projections[0].modelName,// for use in files\__root__\routes\__name__.js, files\__root__\routes\__name__\new.js modelProjection: editFormBlueprint.editForm.projections[0].modelProjection,// for use in files\__root__\routes\__name__.js, files\__root__\routes\__name__\new.js formName: editFormBlueprint.editForm.name,// for use in files\__root__\controllers\__name__\new.js entityName: options.entity.name,// for use in files\__root__\controllers\__name__\new.js caption: editFormBlueprint.editForm.caption,// for use in files\__root__\controllers\__name__.js parentRoute: editFormBlueprint.parentRoute,// for use in files\__root__\controllers\__name__.js flexberryComponents: editFormBlueprint.flexberryComponents,// for use in files\__root__\templates\__name__.hbs functionGetCellComponent: editFormBlueprint.functionGetCellComponent,// for use in files\__root__\controllers\__name__.js isEmberCpValidationsUsed: editFormBlueprint.isEmberCpValidationsUsed, }, editFormBlueprint.locales.getLodashVariablesProperties()// for use in files\__root__\locales\**\forms\__name__.js ); } }; class EditFormBlueprint { locales: Locales; editForm: metadata.EditForm; parentRoute: string; flexberryComponents: string; functionGetCellComponent: string; isEmberCpValidationsUsed: boolean; private snippetsResult = []; private _tmpSnippetsResult = []; private modelsDir: string; private blueprint; private options; constructor(blueprint, options) { this.isEmberCpValidationsUsed = ModelBlueprint.checkCpValidations(blueprint); this.blueprint = blueprint; this.options = options; this.modelsDir = path.join(options.metadataDir, "models"); let localePathTemplate: lodash.TemplateExecutor = this.getLocalePathTemplate(options, blueprint.isDummy, path.join("forms", options.entity.name + ".js")); this.locales = new Locales(options.entity.name, "ru", localePathTemplate); this.process(); this.flexberryComponents = this.snippetsResult.join("\n"); this.parentRoute = this.getParentRoute(); let bodySwitchBindingPath: string[] = []; let propertyLookup: metadata.PropertyLookup; for (propertyLookup of this.editForm.propertyLookup) { if (!propertyLookup.master) continue; let snippet = this.readSnippetFile("getCellComponent-flexberry-lookup", "js"); bodySwitchBindingPath.push(lodash.template(snippet)(propertyLookup)); } if (bodySwitchBindingPath.length > 0) { let snippet = this.readSnippetFile("getCellComponent-function", "js"); this.functionGetCellComponent = lodash.template(snippet)({ bodySwitchBindingPath: bodySwitchBindingPath.join("\n") }); this.functionGetCellComponent = lodash.trimEnd(this.functionGetCellComponent, "\n"); } else { this.functionGetCellComponent = null; } } readSnippetFile(fileName: string, fileExt: string): string { let snippetsFolder = this.isEmberCpValidationsUsed ? "ember-cp-validations" : "ember-validations"; return stripBom(fs.readFileSync(path.join(this.blueprint.path, "snippets", snippetsFolder, fileName + "." + fileExt), "utf8")); } readHbsSnippetFile(componentName: string): string { return this.readSnippetFile(componentName, "hbs"); } loadModel(modelName: string): metadata.Model { let model: metadata.Model = ModelBlueprint.loadModel(this.modelsDir, modelName + ".json"); return model; } findAttr(model: metadata.Model, attrName: string){ let modelAttr = lodash.find(model.attrs, function (attr) { return attr.name === attrName; }); if (!modelAttr) { model = this.loadModel(model.parentModelName); return this.findAttr(model, attrName); } return modelAttr; } loadSnippet(model: metadata.Model, attrName: string): string { let modelAttr = this.findAttr(model,attrName); let component = lodash.find(componentMaps, function (map) { return lodash.indexOf(map.types, modelAttr.type) !== -1; }); if (!component) { return this.readHbsSnippetFile("flexberry-dropdown"); } return this.readHbsSnippetFile(component.name); } process() { let editFormsDir = path.join(this.options.metadataDir, "edit-forms"); let editFormsFile = path.join(editFormsDir, this.options.file); let content: string = stripBom(fs.readFileSync(editFormsFile, "utf8")); this.editForm = JSON.parse(content); this.locales.setupForm(this.editForm); let linkProj = this.editForm.projections[0]; let model = this.loadModel(linkProj.modelName); let proj = lodash.find(model.projections, function (pr) { return pr.name === linkProj.modelProjection; }); let projAttr: any; for (projAttr of proj.attrs) { this.locales.setupEditFormAttribute(projAttr); if (projAttr.hidden || projAttr.index === -1) { continue; } let snippet = this.loadSnippet(model, projAttr.name); let attr = this.findAttr(model, projAttr.name); projAttr.readonly="readonly"; projAttr.type = attr.type; projAttr.entityName = this.options.entity.name; projAttr.dashedName = (projAttr.name || '').replace(/\./g, '-'); this.calculateValidatePropertyNames(projAttr); this._tmpSnippetsResult.push({ index: projAttr.index, snippetResult: lodash.template(snippet)(projAttr) }); } this.fillBelongsToAttrs(proj.belongsTo, []); let belongsTo,hasMany: any; for (belongsTo of proj.belongsTo) { this.locales.setupEditFormAttribute(belongsTo); if (belongsTo.hidden || belongsTo.index === -1) { continue; } var propertyLookup = lodash.find(this.editForm.propertyLookup, function (propLookup) { return propLookup.relationName === belongsTo.relationName; }); if (propertyLookup) { belongsTo.projection = propertyLookup.projection; belongsTo.readonly = "readonly"; belongsTo.entityName = this.options.entity.name; belongsTo.dashedName = (belongsTo.name || '').replace(/\./g, '-'); belongsTo.isDropdown = belongsTo.type === 'combo'; this.calculateValidatePropertyNames(belongsTo); this._tmpSnippetsResult.push({ index: belongsTo.index, snippetResult: lodash.template(this.readHbsSnippetFile("flexberry-lookup"))(belongsTo) }); } } this._tmpSnippetsResult = lodash.sortBy(this._tmpSnippetsResult, ["index"]); this.snippetsResult = lodash.map(this._tmpSnippetsResult, "snippetResult"); for (hasMany of proj.hasMany) { hasMany.readonly = "readonly"; hasMany.entityName = this.options.entity.name; hasMany.dashedName = (hasMany.name || '').replace(/\./g, '-'); this.locales.setupEditFormAttribute(hasMany); this.calculateValidatePropertyNames(hasMany); this.snippetsResult.push(lodash.template(this.readHbsSnippetFile("flexberry-groupedit"))(hasMany)); } } fillBelongsToAttrs(belongsToArray: metadata.ProjBelongsTo[], parentPath: string[]) { for (let belongsTo of belongsToArray) { let currentPath = lodash.concat(parentPath, belongsTo.name); let belongsToAttr: any; for (belongsToAttr of belongsTo.attrs) { if (belongsToAttr.hidden || belongsToAttr.index === -1) { continue; } let model = this.loadModel(belongsTo.relatedTo); let snippet = this.loadSnippet(model, belongsToAttr.name); let attr = this.findAttr(model, belongsToAttr.name); belongsToAttr.name = lodash.concat(currentPath, belongsToAttr.name).join("."); belongsToAttr.readonly="true"; belongsToAttr.type=attr.type; belongsToAttr.entityName = this.options.entity.name; belongsToAttr.dashedName = (belongsToAttr.name || '').replace(/\./g, '-'); this.locales.setupEditFormAttribute(belongsToAttr); this.calculateValidatePropertyNames(belongsToAttr); this._tmpSnippetsResult.push({ index: belongsToAttr.index, snippetResult: lodash.template(snippet)(belongsToAttr) }); } this.fillBelongsToAttrs(belongsTo.belongsTo, currentPath); } } getParentRoute() { let parentRoute = ''; let listFormsDir = path.join(this.options.metadataDir, "list-forms"); let listForms = fs.readdirSync(listFormsDir); for (let form of listForms) { let pp: path.ParsedPath = path.parse(form); if (pp.ext != ".json") continue; let listFormFile = path.join(listFormsDir, form); let content = stripBom(fs.readFileSync(listFormFile, "utf8")); let listForm: metadata.ListForm = JSON.parse(content); if (this.options.entity.name === listForm.editForm) { parentRoute = pp.name; } } return parentRoute; } private getLocalePathTemplate(options, isDummy, localePathSuffix: string): lodash.TemplateExecutor { let targetRoot = "app" if (options.project.pkg.keywords && options.project.pkg.keywords["0"] === "ember-addon" ) { targetRoot = isDummy ? path.join("tests/dummy", targetRoot) : "addon"; } return lodash.template(path.join(targetRoot, "locales", "${ locale }", localePathSuffix)); } private calculateValidatePropertyNames(attrs) { const name = attrs.name; const lastDotIndex = name.lastIndexOf("."); const isHaveMaster = lastDotIndex > 0 && lastDotIndex < (name.length - 1); attrs.propertyMaster = (isHaveMaster) ? "." + name.substring(0, lastDotIndex) : ""; attrs.propertyName = (isHaveMaster) ? name.substring(lastDotIndex + 1, name.length) : name; } }