jsharmony-cms
Version:
236 lines (199 loc) • 9.1 kB
JavaScript
/*
Copyright 2020 apHarmony
This file is part of jsHarmony.
jsHarmony is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
jsHarmony is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this package. If not, see <http://www.gnu.org/licenses/>.
*/
var _ = require('lodash');
var Cloner = require('../utils/cloner');
var FieldModel = require('./fieldModel');
/** @typedef {DataModelTemplate_FormPreview} DataModelTemplate_FormPreview */
/**
* @class
* @classdesc Normalizes the model for use in the form preview
* editor. Creates a model template that can then be used to generate
* unique instances of the model for editing.
* @param {import('./componentTemplate').ComponentTemplate} componentTemplate
* @param {object} dataModel - the raw data model from the component config
*/
function DataModelTemplate_FormPreview(componentTemplate, dataModel) {
/** @private @type {Object} */
this._jsh = componentTemplate._jsh;
/** @private @type {Object} */
this._cms = componentTemplate._cms;
/** @private @type {Object} */
this._componentTemplate = componentTemplate;
/** @private @type {string} */
this._componentTemplateId = componentTemplate.getTemplateId();
/** @private @type {string} */
this._itemTemplate = '';
/** @private @type {object} */
this._modelTemplate = {};
/** @private @type {string} */
this._rawOriginalJs = '';
this.buildTemplate(componentTemplate, dataModel);
}
/**
* @private
* @param {import('./componentTemplate').ComponentTemplate} componentTemplate
* @param {object} dataModel - the raw data model from the component config
*/
DataModelTemplate_FormPreview.prototype.buildTemplate = function(componentTemplate, dataModel) {
var modelConfig = Cloner.deepClone(dataModel || {});
if (modelConfig.layout !== 'grid_preview' && modelConfig.layout !== 'form') return undefined;
var componentConfig = this._componentTemplate && this._componentTemplate._componentConfig;
if(modelConfig.js && _.isString(modelConfig.js) && modelConfig.js.trim()) modelConfig.js = '(function(){ var cms = '+this._cms._instance+';' + modelConfig.js + ' })();';
this._rawOriginalJs = '\r\n' + (modelConfig.js || '') + '\r\n';
var popup = _.isArray(modelConfig.popup) ? modelConfig.popup : [];
var fields = modelConfig.fields || [];
fields.unshift({ control:'html', value:'<div class="jsharmony_cms">', captionclass:'hidden'});
fields.push({ control:'html', value:'</div>', captionclass:'hidden'});
fields.push({
caption: '', control:'html', value:'<div class="jsharmony_cms_preview_editor jsharmony_cms_component_preview" data-id="previewWrapper"></div>', 'block':true, captionclass:'hidden'
});
var model = _.extend({}, modelConfig);
this._modelTemplate = model;
model.title = modelConfig.title ? modelConfig.title : 'Edit ' + componentTemplate.getCaptions()[1];
model.popup = [ _.isNumber(popup[0]) ? popup[0] : 400, _.isNumber(popup[1]) ? popup[1] : 200 ];
model.fields = fields;
model.layout = 'form';
model.unbound = true;
model.onecolumn = true;
model.formclass = ((model.formclass||'')+' '+(componentConfig&&componentConfig.options&&componentConfig.options.component_preview_size=='collapse'?'jsharmony_cms_component_preview_collapse':'jsharmony_cms_component_preview_expand')).trim();
model.ejs = '';
model.js = this._rawOriginalJs;
this._jsh.XPage.ParseModelDefinition(model, null, null, { ignoreErrors: true });
_.forEach(fields, function(field) {
if (field.type != undefined && field.mediaBrowserControlInfo == undefined) {
field.onchange = '(function() { var m = jsh.App[modelid]; if (m && m.onChangeData) m.onChangeData(); })()';
}
});
var templateHtml = '<div>' + modelConfig.ejs + '</div>';
var itemTemplate = '';
var selItemPreview = (modelConfig.templates || {}).itemPreview;
if(selItemPreview){
//If itemPreview is set, extract the template from the model.ejs file
var itemPreview = this._jsh.$(templateHtml).find(selItemPreview);
if (itemPreview.length > 1) throw new Error('Item template must contain a single root element. Found ' + itemPreview.length + ' elements');
itemTemplate = itemPreview ? itemPreview.html() : undefined;
}
else {
//If templates are not used, return the entire model.ejs as the template
itemTemplate = templateHtml;
}
this._itemTemplate = itemTemplate;
};
/**
* Get the link browser field info (if exists) for the link field with
* the given field name.
* @public
* @param {string} fieldName
* @returns {(import('./componentTemplate').MediaBrowserControlInfo | undefined)}
*/
DataModelTemplate_FormPreview.prototype.getBrowserFieldInfo = function(fieldName) {
var field = _.find(this._modelTemplate.fields, function(field) {
return field.mediaBrowserControlInfo && field.mediaBrowserControlInfo.dataFieldName === fieldName;
});
return field ? field.mediaBrowserControlInfo : undefined;
};
/**
* Get the link browser field infos
* @public
* @returns {import('./componentTemplate').MediaBrowserControlInfo[]]}
*/
DataModelTemplate_FormPreview.prototype.getBrowserFieldInfos = function() {
var retVal = [];
_.forEach(this._modelTemplate.fields, function(field) {
if (field.mediaBrowserControlInfo) {
retVal.push(field.mediaBrowserControlInfo);
}
});
return retVal;
};
/**
* Get the EJS string used to render the item preview
* @public
* @returns {string}
*/
DataModelTemplate_FormPreview.prototype.getItemTemplate = function() {
return this._itemTemplate || '';
};
/**
* @public
*/
DataModelTemplate_FormPreview.prototype.getModelInstance = function() {
var model = Cloner.deepClone(this._modelTemplate);
model.id = DataModelTemplate_FormPreview.getNextInstanceId(this._componentTemplate);
return model;
};
/**
* Return the raw model JavaScript.
* @public
* @returns {Object}
*/
DataModelTemplate_FormPreview.prototype.getModelJs = function() {
return this._rawOriginalJs;
};
/**
* Get a unique ID for the model instance
* @private
* @returns {string}
*/
DataModelTemplate_FormPreview.getNextInstanceId = function(componentTemplate) {
if (DataModelTemplate_FormPreview._id == undefined) DataModelTemplate_FormPreview._id = 0;
var id = DataModelTemplate_FormPreview._id++;
return 'DataModel_FormPreview_' + componentTemplate.getClassName() + '_' + id;
};
/**
* Create a pristine copy of the data.
* This will remove extraneous properties (that don't exist in the model)
* and do data conversions. It will also add missing fields.
* The returned value will match the field model exactly.
*
* NOTE: this does not set default values! If the value is not set in
* dataInstance then the property will be set to undefined.
*
* @public
* @param {Object} dataInstance - the existing field values.
* @returns {Object} a copy of the dataInstance with type conversions done and extraneous
* properties removed.
*/
DataModelTemplate_FormPreview.prototype.makePristineCopy = function(dataInstance) {
return FieldModel.makePristineCopy(dataInstance, this._modelTemplate.fields);
};
/**
* Iterates through the fieldModels
* to look for fields with "type" property. If a field has the type property
* then the field will be added to the new data instance object.
*
* Setting the field follows specific rules
* 1. If the data instance does not contain the property key
* then the property is set to either undefined or the default value.
* 2. If the data instance contains the property and the property value is
* defined then it is left as-is.
* 3. If the data instance contains the property and the property value is
* null/undefined then the property is overridden if there is a default AND
* it is a required field. If it is not required then the value is left as
* null/undefined (which allows the user to clear default values that are
* not required fields).
*
* This will also correctly convert values as needed.
*
* This mutates the dataInstance.
* @public
* @param {Object} dataInstance - the data instance. Each property corresponds
* to a field in the field array. This object will be mutated.
* @returns {Object} the new or mutated data
*/
DataModelTemplate_FormPreview.prototype.populateDataInstance = function(dataInstance) {
return FieldModel.populateDataInstance(dataInstance, this._modelTemplate.fields || []);
};
exports = module.exports = DataModelTemplate_FormPreview;