3
Fork 0

Added project

This commit is contained in:
Willem Cazander 2022-11-13 01:46:38 +01:00
parent fe9aa14dfd
commit 2d73cc8845
186 changed files with 21174 additions and 0 deletions

View file

@ -0,0 +1,123 @@
var logger = require('winston').loggers.get('main');
var clone = require('clone');
var mongoose = require('mongoose');
var validate = require('validate.io');
var tcrudField = require('./tcrud-field');
var buildConfig = require('./build-config');
function autoFieldType(fieldMeta,fieldType) {
if (!fieldMeta) {
throw new Error('no fieldMeta');
}
if (fieldType && fieldType.length != 0) {
return fieldType;
}
if (fieldMeta.type == Date) {
return 'date';
}
return 'text';
}
exports.buildFields = function(modelMeta) {
if (!modelMeta) {
throw new Error('no modelMeta');
}
var tfields = {};
var keys = Object.keys(modelMeta);
for (i = 0; i < keys.length; i++) {
var key = keys[i];
var value = modelMeta[key];
var tfield = null;
if (key && value && value.tfield) {
//console.log('tfield model cloned');
tfield = clone(value.tfield);
tfield.tid = key;
tfield.tname = tcrudField.autoFieldName(key,tfield.tname);
tfield.type = autoFieldType(value,tfield.ttype);
} else if (key && value) {
//console.log('tfield model auto created');
tfield = tcrudField.newInstance(key);
tfield.tname = tcrudField.autoFieldName(key);
tfield.type = autoFieldType(value);
}
if (tfield.tvalidate && tfield.tvalidate.io) {
//console.log('tfield validate rule: '+tfield.tvalidate.io);
}
tcrudField.fillDefaults(tfield);
tfields[tfield.tid] = tfield;
}
return tfields;
}
function ss(valueRule) {
return function (value, response) {
response(validate(valueRule,value));
};
}
exports.createModelValidators = function (modelSchema,modelFields) {
if (!modelSchema) {
throw new Error('no modelSchema');
}
if (!modelFields) {
throw new Error('no modelFields');
}
var keys = Object.keys(modelFields);
for (var i = 0; i < keys.length; i++) {
var key = keys[i];
var tfield = modelFields[key];
if (!tfield.tvalidate) {
continue;
}
if (!tfield.tvalidate.io) {
continue;
}
modelSchema.path(tfield.tid).validate(ss(tfield.tvalidate.io), '{PATH} validation failed: '+tfield.tvalidate.io);
}
}
exports.buildStatics = function(modelFields,modelStatics) {
if (!modelFields) {
throw new Error('no modelFields');
}
if (!modelStatics) {
modelStatics = {};
}
modelStatics['ff_tcrud_fields'] = modelFields;
return modelStatics;
}
exports.buildTCrudModel = function(mongoose,tcrudParent,name) {
var model = mongoose.model(name);
var tcrud = buildConfig.createTCrud(tcrudParent,name);
var tfields = model['ff_tcrud_fields'];
if (tfields) {
tcrud.tmeta.tfields = tfields;
}
tcrud.tmodel = name;
//tcrud.tbackend = 'mongoose';
return tcrud;
}
exports.buildTCrudModels = function(mongoose,tcrudParent) {
var result = [];
var modelNames = mongoose.connection.modelNames();
for (var i = 0; i < modelNames.length; i++) {
result.push(exports.buildTCrudModel(mongoose,tcrudParent,modelNames[i]))
}
return result;
}
// ----- wrappers
exports.buildStaticsModel = function(modelMeta,modelStatics) {
return exports.buildStatics(exports.buildFields(modelMeta),modelStatics);
}
exports.buildStaticsModelValidated = function(modelMeta,modelSchema,modelStatics) {
var modelFields = exports.buildFields(modelMeta);
exports.createModelValidators(modelSchema,modelFields);
return exports.buildStatics(modelFields,modelStatics);
}

View file

@ -0,0 +1,16 @@
var logger = require('winston').loggers.get('main');
var clone = require('clone');
var tcrud = require('./tcrud');
var tcrudField = require('./tcrud-field');
exports.createTCrudRoot = function() {
return tcrud.newInstance('root');
}
exports.createTCrud = function(parent, id) {
var result = tcrud.newInstance(id);
parent.tchilds.push(result);
result.tparent = parent;
return result;
}

View file

@ -0,0 +1,95 @@
var logger = require('winston').loggers.get('main');
var clone = require('clone');
var tcrud = require('./tcrud');
var tcrudView = require('./tcrud-view');
exports.createBaseApiUri = function(tview, tech, action) {
var uriView = tview.tslug;
var uriTech = tview.tmeta.texport[tech].tslug;
var uriAction = '';
if (action) {
uriAction = '/' + tview[action].texport[tech].tslug;
}
//logger.info('createBaseApiUri uriTech: '+uriTech+' uriView: '+uriView+' uriAction: '+uriAction);
if (tview.tmeta.tserver.tpopfix) {
return uriTech + '/' + uriView + uriAction;
} else {
return uriView + '/' + uriTech + uriAction;
}
};
function allowSlugSlash(tcrud) {
var result = true;
if (tcrud.tparent) {
result = allowSlugSlash(tcrud.tparent);
if (!result) {
return result;
}
}
if (tcrud.tcount.tslug === '') {
return 'tcount';
}
if (tcrud.tlist.tslug === '') {
return 'tlist';
}
if (tcrud.tcreate.tslug === '') {
return 'tcreate';
}
if (tcrud.tread.tslug === '') {
return 'tread';
}
if (tcrud.tedit.tslug === '') {
return 'tedit';
}
if (tcrud.tdelete.tslug === '') {
return 'tdelete';
}
// if (tcrud.tverify.tslug === '') {
//return false;
// }
return null;
}
function createTCrudViewSlug(tcrud) {
var uriViewSlash = '/';
var slug = uriViewSlash + tcrud.tslug;
if (!tcrud.tenable || !tcrud.tparent) {
slug = '';
}
if (tcrud.tparent) {
return createTCrudViewSlug(tcrud.tparent)+slug;
}
return slug;
}
var fetchEmptyRoles = function() {
return function (callback) {
callback(null,[]);
};
}
exports.createTCrudView = function(tcrud,xrollesCallback) {
if (!tcrud) {
throw new Error('no tcrud');
}
if (!xrollesCallback) {
xrollesCallback = fetchEmptyRoles;
}
var tview = tcrudView.newInstance(tcrud);
tview.tslug = createTCrudViewSlug(tcrud).substring(1);
/*
var uriViewSlashAllow = allowSlugSlash(tcrud);
if (uriViewSlashAllow !== null) {
uriViewSlash = '_';
console.log('detected crud actions with empty tslug; deploying in list mode slash; '+uriViewSlash+' in: '+uriViewSlashAllow+' of: '+tcrud.xid);
tview.tslug = buildSlug(uriViewSlash,tcrud).substring(1);
} else {
tview.tslug = buildSlug(uriViewSlash,tcrud);
}
*/
return tview;
}

View file

@ -0,0 +1,338 @@
var logger = require('winston').loggers.get('main');
var clone = require('clone');
var fs = require('fs');
var ejs = require('ejs');
var xmlmapping = require('xml-mapping');
var rawbody = require('raw-body');
var typeis = require('type-is');
var mcrud = require('crud-mongoose');
var ncrud = require('node-crud');
var mongoose = require('mongoose');
var buildView = require('./build-view');
exports.bodyParserXml = function() {
return function(req, res, next) {
if (req._body) return next();
req.body = req.body || {};
if (!typeis(req, 'xml')) return next();
// flag as parsed
req._body = true;
// parse
rawbody(req, {
limit: options.limit || '100kb',
length: req.headers['content-length'],
encoding: 'utf8'
}, function (err, buf) {
if (err) return next(err);
if (0 == buf.length) {
return next(error(400, 'invalid xml, empty body'));
}
try {
req.body = xmlmapping.dump(buf/*, options.reviver*/);
} catch (err){
err.body = buf;
err.status = 400;
return next(err);
}
next();
})
}
}
var renderTemplateDataRead = function(tview, exportType, contentType, model, projection) {
return function(data, query, cb) {
var res = this.response;
res.set('Content-Type', contentType);
var cursor, fields;
if (query.hasOwnProperty('fields')) {
fields = query.fields;
delete query.fields;
}
delete query.export;
cursor = model.findOne(query);
cursor.select(mcrud.tools.select(projection, fields));
cursor.lean().exec(function(err,doc) {
if (err) {
cb(err || 'error in stream');
return;
}
if (doc == null) {
cb('no data'); // TODO: error is still in json...
return;
}
//var templateReadHeader = fs.readFileSync('node_code/lib/www_views/node-ff-tcrud/'+exportType+'/read-header.ejs', 'utf8');
var templateReadRecord = fs.readFileSync('node_code/lib/www_views/node-ff-tcrud/'+exportType+'/read-record.ejs', 'utf8');
//var templateReadFooter = fs.readFileSync('node_code/lib/www_views/node-ff-tcrud/'+exportType+'/read-footer.ejs', 'utf8');
res.write(ejs.render(templateReadRecord,{
tview: tview,
record: doc,
}));
res.end();
//cb(); // FIXME
});
}
}
var renderTemplateDataList = function(tview, exportType, contentType) {
return function(res, cursor, cb) {
res.set('Content-Type', contentType);
var templateHeader = fs.readFileSync('node_code/lib/www_views/node-ff-tcrud/'+exportType+'/list-header.ejs', 'utf8');
var templateRecord = fs.readFileSync('node_code/lib/www_views/node-ff-tcrud/'+exportType+'/list-record.ejs', 'utf8');
var templateFooter = fs.readFileSync('node_code/lib/www_views/node-ff-tcrud/'+exportType+'/list-footer.ejs', 'utf8');
res.write(ejs.render(templateHeader,{
tview: tview,
}));
cursor.stream()
.on('data', function(doc) {
res.write(ejs.render(templateRecord,{
tview: tview,
record: doc,
}));
})
.on('error', function(e) {
cb(e || 'error in stream');
})
.on('close', function() {
res.write(ejs.render(templateFooter,{
tview: tview,
}));
res.end();
cb();
});
};
}
exports.injectExportFormat = function(apiBase) {
return function ( req, res, next ) {
if (req.url.indexOf(apiBase) >= 0) {
if (req.url.indexOf('xml') > 0) {
req.query['export'] = 'xml';
} else if (req.url.indexOf('csv') > 0) {
req.query['export'] = 'csv';
} else if (req.url.indexOf('rss') > 0) {
req.query['export'] = 'rss';
}
if (req.url.indexOf('json') == 0) {
console.log('inject export type: '+req.query['export']);
}
}
next();
};
}
var _ = require('underscore');
var filterQueryList = function (model) {
return mcrud.parseQuery()
.defaults({ limit: '10' })
.overrides({})
.maxes({ limit: 1000 });
//.removes()
}
var filterDataUpdate = function (model) {
return mcrud.parseData()
overrides({});
//.removes('auth')
//.overrides({ updated: Date.now })
//.defaults({ 'info.gender': 'M' }))
}
function allowCrudType(tview,tcrudType) {
return tview[tcrudType].tenable; // todo add roles
}
//var rssXNodeMap = function(link,feed,data) {
// feed.addNewItem(data.net_id, link+'/ui/XNode/read/'+data._id, data.changed_data, data.net_id+' id', {});
//}
var sendRedirect = function (location) {
if (!location) {
throw new Error('no location');
}
return function(req, res) {
res.redirect(location);
};
};
var renderCrudView = function (tview) {
return function (req, res, next) {
var result = {
tview: tview,
}
res.json({
data: result
});
};
}
var renderCrudController = function (tview, thtmlPrefix, tapiPrefix) {
return function (req, res, next) {
res.set('Content-Type', 'text/javascript');
res.render('node-ff-tcrud/angular/controller',{
tview: tview,
thtmlPrefix: thtmlPrefix,
tapiPrefix: tapiPrefix,
});
};
}
var renderCrudTemplate = function (tview,paction) {
return function (req, res, next) {
res.render('node-ff-tcrud/angular/'+paction+'/'+req.params.action,{
tview: tview,
});
};
}
var buildCrud = function (server,tcrud) {
if (!tcrud.tenable || !tcrud.tmodel) {
logger.info('disabled server: '+tcrud.tid);
return;
}
var tview = buildView.createTCrudView(tcrud /*, fetchCrudRoles()*/);
var model = mongoose.model( tcrud.tmodel );
// Export tdebug
var uriDebug = tcrud.tmeta.tserver.tslug + '/' +buildView.createBaseApiUri(tview,'json') + '/_debug/';
var tcrudClone = clone(tcrud);
tcrudClone.tparent = null; // rm loop: tparent.tchilds[] = this
server.get(uriDebug + 'tview', renderCrudView(tview));
server.get(uriDebug + 'tcrud', renderCrudView(tcrudClone));
if (_.contains(tview.tmeta.tserver.texports,'json')) {
if (allowCrudType(tview,'tlist')) {
ncrud.entity(buildView.createBaseApiUri(tview,'json','tlist')).Read()
.pipe(filterQueryList(model))
.pipe(mcrud.findAll(model).stream());
}
if (allowCrudType(tview,'tcreate')) {
ncrud.entity(buildView.createBaseApiUri(tview,'json','tcreate')).Create()
.pipe(mcrud.createNew(model));
}
if (allowCrudType(tview,'tread')) {
ncrud.entity(buildView.createBaseApiUri(tview,'json','tread') + '/:_id').Read()
.pipe(mcrud.findOne(model));
}
if (allowCrudType(tview,'tedit')) {
ncrud.entity(buildView.createBaseApiUri(tview,'json','tedit') + '/:_id').Update()
.pipe(filterDataUpdate(model))
.pipe(mcrud.updateOne (model));
}
if (allowCrudType(tview,'tdelete')) {
ncrud.entity(buildView.createBaseApiUri(tview,'json','tdelete') +'/:_id').Delete()
.pipe(mcrud.removeOne(model));
}
if (allowCrudType(tview,'tcount')) {
ncrud.entity(buildView.createBaseApiUri(tview,'json','tcount')).Read()
.pipe(filterQueryList(model))
.pipe(mcrud.findAll(model).stream());
}
if (allowCrudType(tview,'tverify')) {
ncrud.entity(buildView.createBaseApiUri(tview,'json','tverify') + '/:_id').Update()
.pipe(mcrud.findOne(model));
}
}
if (_.contains(tview.tmeta.tserver.texports,'xml')) {
if (allowCrudType(tview,'tlist')) {
ncrud.entity(buildView.createBaseApiUri(tview,'xml','tlist')).Read()
.pipe(filterQueryList(model))
.pipe(mcrud.findAll(model).stream().exports({ xml: renderTemplateDataList(tview,'xml','text/xml') }));
}
if (allowCrudType(tview,'tcreate')) {
ncrud.entity(buildView.createBaseApiUri(tview,'xml','tcreate')).Create()
.pipe(mcrud.createNew(model));
}
if (allowCrudType(tview,'tread')) {
ncrud.entity(buildView.createBaseApiUri(tview,'xml','tread') +'/:_id').Read()
.pipe(renderTemplateDataRead(tview,'xml','text/xml',model));
}
if (allowCrudType(tview,'tedit')) {
ncrud.entity(buildView.createBaseApiUri(tview,'xml','tedit')+'/:_id').Update()
.pipe(filterDataUpdate(model))
.pipe(mcrud.updateOne (model));
}
if (allowCrudType(tview,'tdelete')) {
ncrud.entity(buildView.createBaseApiUri(tview,'xml','tdelete')+'/:_id').Delete()
.pipe(mcrud.removeOne (model));
}
}
if (_.contains(tview.tmeta.tserver.texports,'csv')) {
if (allowCrudType(tview,'tlist')) {
ncrud.entity(buildView.createBaseApiUri(tview,'csv','tlist')).Read()
.pipe(filterQueryList(model))
.pipe(mcrud.findAll(model).stream().exports({ csv: renderTemplateDataList(tview,'csv','text/csv') }));
}
if (allowCrudType(tview,'tread')) {
ncrud.entity(buildView.createBaseApiUri(tview,'csv','tread') +'/:_id').Read()
.pipe(renderTemplateDataRead(tview,'csv','text/csv',model));
}
}
if (_.contains(tview.tmeta.tserver.texports,'rss')) {
if (allowCrudType(tview,'tlist')) {
ncrud.entity(buildView.createBaseApiUri(tview,'rss','tlist')).Read()
.pipe(mcrud.parseQuery()
.defaults({ limit: '10' })
.maxes({ limit: 50 })
)
.pipe(mcrud.findAll(model)
.stream()
.exports({ rss: renderTemplateDataList(tview,'rss','text/xml') })
);
}
//.exports({ rss: exports.exportRss('todo',tview.xid,rssXNodeMap) })
}
if (_.contains(tview.tmeta.tserver.texports,'angular')) {
var uriPrefix = tcrud.tmeta.tserver.tslug + '/' + buildView.createBaseApiUri(tview,'angular');
var thtmlPrefix = uriPrefix + '/' + tview.tmeta.texport.angular.thtml;
var tapiPrefix = tcrud.tmeta.tserver.tslug + '/' +buildView.createBaseApiUri(tview,'json');
//server.get('/ui/include/crud/:name/:action', XViewInclude.renderTemplateCrud);
//server.get('/ui/include/js/controller/:name', XViewInclude.renderCrudController);
server.get(uriPrefix + '/thtml/crud/:action', renderCrudTemplate(tview,'thtml/crud/'));
server.get(uriPrefix + '/controller.js', renderCrudController(tview,thtmlPrefix,tapiPrefix));
//server.get(uriPrefix + '/service.js', renderCrudService(tview))
}
}
function walkViews(server,tcrud) {
//logger.debug('walk views: '+tcrud.tid);
if (tcrud.tparent) {
buildCrud(server,tcrud);
}
for (var i = 0; i < tcrud.tchilds.length; i++) {
walkViews(server,tcrud.tchilds[i]);
}
}
function buildCrudApi(server) { // todo add return function
troot = server.get('ff_root_tcrud_view');
walkViews(server,troot);
server.use(exports.injectExportFormat(troot.tmeta.tserver.tslug));
server.use(exports.bodyParserXml());
ncrud.launch(server,{
base: troot.tmeta.tserver.tslug,
cors: false,
timeoute: 10e3
});
}
module.exports = buildCrudApi;

View file

@ -0,0 +1,138 @@
var logger = require('winston').loggers.get('main');
var clone = require('clone');
/*
var createRouter = function(server,path) {
var router = express.Router();
router.path = path;
server.use(router.path,router);
return router;
}
*/
var walkRouteTree = function(result,stack,parent_path) {
if (parent_path == null) {
parent_path = '';
}
stack.forEach(function(middleware) {
if (middleware.route){
var path = parent_path + middleware.route.path;
for (var httpMethod in middleware.route.methods) {
var data = {
"uriPath": path,
"httpMethod": httpMethod
};
result.push(data);
}
} else if (middleware.name === 'router') {
var pp = parent_path + middleware.handle.path;
walkRouteTree(result,middleware.handle.stack,pp);
} else {
//log.info('route err: '+JSON.stringify(middleware));
}
});
return result;
}
exports.renderServerUptime = function(options) {
if (!options) {
options: {};
}
var timeBoot = new Date().getTime();
return function (req, res, next) {
var timeNow = new Date().getTime();
var result = {
time_boot: timeBoot,
time_now: timeNow,
uptime: timeNow-timeBoot
}
res.json({
data: result
});
};
};
exports.renderServerRoutes = function(server) {
if (!server) {
throw new Error('no server');
}
return function (req, res, next) {
var routeList = server.get('ff_tcrud_route_list');
if (!routeList) {
console.log('no routeList');
routeList = [];
}
var reqGroups = req.query.groups;
var groupList = [];
if (reqGroups) {
groupList = reqGroups.split(',');
}
//console.log('groups: '+reqGroups);
var result = {
all: [],
};
for (var i = 0; i < groupList.length; i++) {
var groupName = groupList[i].split('/').join('_');
result[groupName] = [];
}
groupList.sort(function(a, b) {
return a.length < b.length; // longest first as we break on first hit
});
for (i = 0; i < routeList.length; i++) {
var route = routeList[i];
var added = false;
for (var ii = 0; ii < groupList.length; ii++) {
if (route.uriPath.indexOf(groupList[ii]) > 0) {
var groupName = groupList[ii].split('/').join('_');
result[groupName].push(route);
added = true;
break;
}
}
if (!added) {
result.all.push(route);
}
}
res.json({
data: result
});
};
};
exports.buildServerRoutes = function(server) { // todo add return function
if (!server) {
throw new Error('no server');
}
var result = walkRouteTree([],server._router.stack);
result.sort(function(a, b) {
return a.uriPath.localeCompare(b.uriPath);
});
server.set('ff_tcrud_route_list',result);
};
exports.sendRedirect = function (location) {
if (!location) {
throw new Error('no location');
}
return function(req, res) {
res.redirect(location);
};
};
exports.renderTemplatePath = function (viewPath) {
if (!viewPath) {
viewPath = '';
}
return function (req, res) {
res.locals.query = req.query;
//console.log('template query keys: '+Object.keys(req.query));
var qi = req.url.indexOf('?');
if (qi === -1) {
qi = req.url.length;
}
res.render(viewPath + req.url.substring(req.route.path.length-1, qi));
};
};
exports.buildCrudApi = require('./factory-express-api');

View file

@ -0,0 +1,54 @@
'use strict';
var tcrud = {
factory: {
express: require('./factory-express'),
},
build: {
config: require('./build-config'),
view: require('./build-view'),
backend: {
mongoose: require('./build-backend-mongoose'),
},
}
};
module.exports = tcrud;
/*
//app.use(function (req, res, next) {res.locals.c_var='test';next();});
var http = require('http');
var ejs = require('ejs');
var fs = require('fs');
http.createServer(function (req, res) {
res.writeHead(200, {'Content-Type': 'text/xml'});
fs.readFile('placemark.ejs', 'utf8', function (err, template) {
var content = ejs.render(template,{
name:"test name",
description:"this is the description",
coordinates:"-122.0822035425683,37.42228990140251,0",
filename: __dirname + '/placemark.ejs'
});
res.write(content);
res.end()
});
}).listen(8000);
var scripts = document.getElementsByTagName("script");
var currentScriptPath = scripts[scripts.length-1].src;
console.log("currentScriptPath:"+currentScriptPath);
var baseUrl = currentScriptPath.substring(0, currentScriptPath.lastIndexOf('/') + 1);
console.log("baseUrl:"+baseUrl);
var bu2 = document.querySelector("script[src$='routes.js']");
currentScriptPath = bu2.src;
console.log("bu2:"+bu2);
console.log("src:"+bu2.src);
baseUrl = currentScriptPath.substring(0, currentScriptPath.lastIndexOf('/') + 1);
console.log("baseUrl:"+baseUrl);
*/

View file

@ -0,0 +1,110 @@
var clone = require('clone');
var template = {
tid: null,
tname: null,
ttype: null,
tslug: null,
tvalidate: {
io: null,
},
tlist: {
tenable: true,
ttext: null,
troles: [],
},
tread: {
tenable: true,
ttext: null,
troles: [],
},
tedit: {
tenable: true,
ttext: null,
troles: [],
},
tcreate: {
tenable: true,
ttext: null,
troles: [],
},
};
exports.autoFieldName = function (fieldKey,fieldName) {
if (fieldKey === undefined) {
throw new Error('no fieldKey');
}
if (fieldName && fieldName.length !== 0) {
return fieldName;
}
var result = '';
var names = fieldKey.split('_');
for (var i in names) {
var name = names[i];
if (name.length > 1) {
name = name.substring(0,1).toUpperCase() + name.substring(1);
}
result = result + name + ' ';
}
return result;
}
var autoFieldEnable = function(tfield,type) {
if (type === undefined) {
throw new Error('no type');
}
if (tfield[type] === undefined) {
tfield[type] = {};
}
if (tfield[type].tenable !== undefined) {
return;
}
var fieldKey = tfield.tid;
var result = true;
if ('tlist' === type) {
var name = fieldKey.toLowerCase();
if (fieldKey.indexOf('description') >= 0) {
result = false;
} else if (fieldKey.indexOf('comment') >= 0) {
result = false;
}
}
tfield[type].tenable = result;
}
exports.newInstance = function (id) {
var tfield = clone(template);
tfield.tid = id;
return tfield;
}
exports.fillDefaults = function (tfield) {
if (tfield === undefined) {
throw new Error('no tfield');
}
var tid = tfield.tid;
if (tid === undefined) {
throw new Error('no tfield.tid');
}
if (tfield.tname === undefined) {
tfield.tname = exports.autoFieldName(tid);
}
if (tfield.tname === undefined) {
tfield.tname = exports.autoFieldName(tid);
}
if (tfield.tslug === undefined) {
tfield.tslug = tid;
}
if (tfield.ttype === undefined) {
tfield.ttype = 'text';
}
if (tfield.tlist === undefined) {
tfield.tlist = {};
}
autoFieldEnable(tfield,'tlist');
autoFieldEnable(tfield,'tread');
autoFieldEnable(tfield,'tedit');
autoFieldEnable(tfield,'tcreate');
}

View file

@ -0,0 +1,204 @@
var clone = require('clone');
var tsurgeon = require('tree-surgeon');
var template = {
tid: null,
tname: null,
tplural: null,
tslug: null,
tcode: null,
tmodel: null,
tcount: {
ttext: null,
tenable: null,
texport: null,
},
tlist: {
tfields: [],
ttext: null,
tlinks: null,
tenable: null,
texport: null,
},
tread: {
tfields: [],
ttext: null,
tenable: null,
texport: null,
},
tedit: {
tfields: [],
ttext: null,
tenable: null,
texport: null,
},
tcreate: {
tfields: [],
ttext: null,
tenable: null,
texport: null,
},
tdelete: {
ttext: null,
tenable: null,
texport: null,
},
tverify: {
ttext: null,
tenable: null,
texport: null,
},
tmeta: {
tfields: null,
tvalidate: null,
tserver: null,
texport: null,
},
};
function copyByTemplate(prefix,objectDest,objectSrc,copyTemplate) {
var keys = Object.keys(copyTemplate);
var result = [];
for (var i = 0; i < keys.length; i++) {
var key = keys[i];
var value = objectSrc[key];
if (!value) {
//console.log(prefix+'.'+key+' src object has no value.');
continue; // no src value
}
var templateValue = copyTemplate[key];
if (templateValue === null) {
if (objectDest[key] !== null) {
//console.log(prefix+'.'+key+' has own value: '+objectDest[key]);
continue;
} else {
//console.log(prefix+'.'+key+' copy value: '+value);
objectDest[key] = clone(value);
}
} else {
//console.log(prefix+'.'+key+' going deeper');
copyByTemplate(prefix+'.'+key,objectDest[key],value,templateValue)
}
}
}
function addFilteredFields(tview,type,tcrud) {
result = [];
var keys = Object.keys(tcrud.tmeta.tfields);
var result = [];
for (var i = 0; i < keys.length; i++) {
var key = keys[i];
var tfield = tcrud.tmeta.tfields[key];
if (tfield[type]) {
if (!tfield[type].tenable) {
//console.log('------------- type: ' + type + ' key: ' + key + ' res: ' + tfield[type].tenable)
continue;
}
}
result.push(key); // default is true..
}
tview[type].tfields = result;
}
function forceLookupKeySimple() {
var low = 100000;
var high = 999999;
return Math.floor(Math.random() * (high - low + 1) + low).toString(16).toUpperCase();
}
function forceLookupTFields(tview) {
var keys = Object.keys(tview.tmeta.tfields);
for (var i = 0; i < keys.length; i++) {
var key = keys[i];
var keyNew = 'FTL_' +forceLookupKeySimple() + '_' + key.substring(key.length/3,key.length/3*2); // no....its; Force template lookup
var tfield = tview.tmeta.tfields[key];
tview.tmeta.tfields[key] = undefined;
tview.tmeta.tfields[keyNew] = tfield;
var ckeys = Object.keys(tview);
for (var ii = 0; ii < ckeys.length; ii++) {
var ckey = ckeys[ii];
if (ckey === 'tmeta') {
continue;
}
var obj = tview[ckey];
if (obj && obj.tfields) {
var tfieldsNew = [];
for (var iii = 0; iii < obj.tfields.length; iii++) {
var tkey = obj.tfields[iii];
if (tkey === key) {
tfieldsNew.push(keyNew);
} else {
tfieldsNew.push(tkey);
}
}
obj.tfields = tfieldsNew;
}
}
}
}
function formatValuePart(value,replaceChar,splitChar,partFn) {
var result = '';
var parts = value.split(splitChar);
for (var i = 0; i < parts.length; i++) {
var part = parts[i];
if (part.length > 1) {
result += partFn(part);
} else {
result += part;
}
if (i < parts.length - 1) {
result += replaceChar;
}
}
return result;
}
function formatValue(value,removeChars,replaceChar,partFn) {
for (var i = 0; i < removeChars.length; i++) {
value = formatValuePart(value,replaceChar,removeChars[i],partFn);
}
return value;
}
exports.newInstance = function (tcrud) {
var tview = clone(template);
var tid = tcrud.tid;
tview.tid = tid;
copyByTemplate('tview',tview,tcrud,template);
//console.info('TCrudView.tslug: '+tview.tslug);
if (!tview.tslug) {
tview.tslug = formatValue(tid,['.',',','/','=','&','?',' '],'', function (part) {
return part; // todo use fully correct uri removeal and escaping.
})
//console.info('TCrudView.tslug auto: '+tview.tslug);
}
if (!tview.tname) {
tview.tname = formatValue(tid,['_','-','.',','],' ', function (part) {
return part.substring(0,1).toUpperCase()+part.substring(1).toLowerCase();
});
}
if (!tview.tcode) {
tview.tcode = formatValue(tid,[' ','_','-','.',','],'',function (part) {
return part.toLowerCase();
});
}
if (!tview.tplural) {
tview.tplural = tview.tname + 's';
}
addFilteredFields(tview,'tlist',tcrud);
addFilteredFields(tview,'tread',tcrud);
addFilteredFields(tview,'tedit',tcrud);
addFilteredFields(tview,'tcreate',tcrud);
forceLookupTFields(tview);
return tview;
}

View file

@ -0,0 +1,230 @@
var clone = require('clone');
var template = {
tid: null,
tname: null,
tplural: null,
tslug: null,
tmodel: null,
tenable: true,
tparam: null,
troles: [],
tparent: null,
tchilds: [],
tlist: {
tenable: true,
ttext: 'Listing of ..',
troles: [],
tserver: {
tpipe: {
query: {
defaults: { limit: '10' },
maxes: { limit: 50 },
},
},
},
texport: {
json: {
tslug: 'list',
},
xml: {
tslug: 'list',
},
rss: {
tslug: 'list',
},
csv: {
tslug: 'list',
},
angular: {
tslug: 'list',
thtml: 'crud/list',
tcontroller: {
prefix: 'tcrudAuto',
postfix: 'ListCntr',
argu: '$scope, $http, $location, $routeParams',
},
tlinks: {
'DataTODO':'/ui/XNodeData/list/XNode/{{row.net_id}}/{{row.net_id}}',
},
},
},
},
tcreate: {
tenable: true,
ttext: null,
troles: [],
texport: {
json: {
tslug: 'create',
},
xml: {
tslug: 'create',
},
angular: {
tslug: 'create',
thtml: 'crud/create',
tcontroller: {
prefix: 'tcrudAuto',
postfix: 'CreateCntr',
argu: '$scope, $http, $location, $routeParams',
},
},
},
},
tedit: {
tenable: true,
ttext: null,
troles: [],
texport: {
json: {
tslug: 'edit',
},
xml: {
tslug: 'edit',
},
angular: {
tslug: 'edit',
thtml: 'crud/edit',
tcontroller: {
prefix: 'tcrudAuto',
postfix: 'EditCntr',
argu: '$scope, $http, $location, $routeParams',
},
},
},
},
tread: {
tenable: true,
ttext: null,
troles: [],
tslug: '',
texport: {
json: {
tslug: 'read',
},
xml: {
tslug: 'read',
},
csv: {
tslug: 'read',
},
angular: {
tslug: 'read',
thtml: 'crud/read',
troute: {
},
tcontroller: {
prefix: 'tcrudAuto',
postfix: 'ReadCntr',
argu: '$scope, $http, $location, $routeParams',
},
},
},
},
tdelete: {
tenable: true,
ttext: null,
troles: [],
texport: {
json: {
tslug: 'delete',
},
xml: {
tslug: 'delete',
},
angular: {
tslug: 'delete',
thtml: 'crud/delete',
tcontroller: {
prefix: 'tcrudAuto',
postfix: 'DeleteCntr',
argu: '$scope, $http, $location, $routeParams',
},
},
},
},
tcount: {
tenable: true,
ttext: 'Count records',
troles: [],
texport: {
json: {
tslug: 'list-count',
},
xml: {
tslug: 'list-count',
},
csv: {
tslug: 'list-count',
},
},
},
tverify: {
tenable: true,
ttext: 'Verify data',
troles: [],
texport: {
json: {
tslug: 'verify',
},
xml: {
tslug: 'verify',
},
csv: {
tslug: 'verify',
},
},
},
ttemplate: {
tid: '_id',
tname: '_id',
tdescription: '_id',
},
tmeta: {
troles: [],
tfields: {},
tvalidate: {
io: { 'admin_role': 'object|properties[test]' },
},
tserver: {
thost: 'http://localhost:8080/',
tslug: '/api',
tpopfix: true,
texports: ['json','xml','csv','rss','angular'],
},
texport: {
json: {
tslug: 'json',
},
xml: {
tslug: 'xml',
},
csv: {
tslug: 'csv',
},
rss: {
tslug: 'rss',
itemTitle: '$data._id',
itemLink: '$siteLink/ui/$modelUri/read/$[\'data._id\']',
itemDate: '',
itemDescription: '',
//itemFields: '',
},
angular: {
tslug: 'angular',
tbase: '/ui',
thtml: 'thtml',
},
},
},
};
exports.newInstance = function (tid) {
var tcrud = clone(template);
tcrud.tid = tid;
tcrud.tslug = tid; // view fills more defaults.
return tcrud;
}

View file

@ -0,0 +1,8 @@
$scope.<%= taction %>None = function () {
<% if (tview.tlist) { %>
$location.url('<%= tview.tmeta.texport.angular.tbase %>/<%= tview.tslug %>/<%= tview.tlist.texport.angular.tslug %>');
<% } else { %>
$location.url('<%= tview.tmeta.texport.angular.tbase %>/');
<% } %>
}

View file

@ -0,0 +1,5 @@
$routeProvider.when('<%= tview.tmeta.texport.angular.tbase %>/<%= tview.tslug %>/<%= tview[taction].texport.angular.tslug %><%= routeEnd %>', {
templateUrl: '<%= thtmlPrefix %>/<%= tview[taction].texport.angular.thtml %>',
controller: <%= tview[taction].texport.angular.tcontroller.prefix %><%= tview.tcode %><%= tview[taction].texport.angular.tcontroller.postfix %>
});

View file

@ -0,0 +1,99 @@
'use strict';
// Auto generated controller mapping for: <%= tview.tid %>
crudRouteInit.push(<%= tview.tcode %>Init);
function <%= tview.tcode %>Init($routeProvider, $locationProvider) {
<% if (tview.tlist) { %>
$routeProvider.when('<%= tview.tmeta.texport.angular.tbase %>/<%= tview.tslug %>/', {
redirectTo: '<%= tview.tmeta.texport.angular.tbase %>/<%= tview.tslug %>/<%= tview.tlist.texport.angular.tslug %>'
});
<%- include('controller-route', {tview: tview,thtmlPrefix:thtmlPrefix,taction: 'tlist', routeEnd: ''}); %>
<% } %>
<% if (tview.tcreate) { %>
<%- include('controller-route', {tview: tview,thtmlPrefix:thtmlPrefix,taction: 'tcreate', routeEnd: ''}); %>
<% } %>
<% if (tview.tedit) { %>
<%- include('controller-route', {tview: tview,thtmlPrefix:thtmlPrefix,taction: 'tedit', routeEnd: '/:id'}); %>
<% } %>
<% if (tview.tread) { %>
<%- include('controller-route', {tview: tview,thtmlPrefix:thtmlPrefix,taction: 'tread', routeEnd: '/:id'}); %>
<% } %>
<% if (tview.tdelete) { %>
<%- include('controller-route', {tview: tview,thtmlPrefix:thtmlPrefix,taction: 'tdelete', routeEnd: '/:id'}); %>
<% } %>
}
<% if (tview.tlist) { %>
function <%= tview.tlist.texport.angular.tcontroller.prefix %><%= tview.tcode %><%= tview.tlist.texport.angular.tcontroller.postfix %>(<%= tview.tlist.texport.angular.tcontroller.argu %>) {
$http.get('<%= tapiPrefix %>/<%= tview.tlist.texport.json.tslug %>').success(function(data, status, headers, config) {
$scope.data = data.data;
});
}
<% } %>
<% if (tview.tcreate) { %>
function <%= tview.tcreate.texport.angular.tcontroller.prefix %><%= tview.tcode %><%= tview.tcreate.texport.angular.tcontroller.postfix %>(<%= tview.tcreate.texport.angular.tcontroller.argu %>) {
$scope.data = {};
$scope.tcreateData = function () {
$http.post('<%= tapiPrefix %>/<%= tview.tcreate.texport.json.tslug %>', $scope.data).success(function(data) {
<% if (tview.tlist) { %>
$location.url('<%= tview.tmeta.texport.angular.tbase %>/<%= tview.tslug %>/<%= tview.tlist.texport.angular.tslug %>');
<% } else { %>
$location.url('<%= tview.tmeta.texport.angular.tbase %>/');
<% } %>
});
}
<%- include('controller-action-none', {tview: tview,taction: 'tcreate'}); %>
}
<% } %>
<% if (tview.tread) { %>
function <%= tview.tread.texport.angular.tcontroller.prefix %><%= tview.tcode %><%= tview.tread.texport.angular.tcontroller.postfix %>(<%= tview.tread.texport.angular.tcontroller.argu %>) {
$scope.data = {};
$http.get('<%= tapiPrefix %>/<%= tview.tread.texport.json.tslug %>/' + $routeParams.id).success(function(data) {
$scope.data = data.data;
});
<%- include('controller-action-none', {tview: tview,taction: 'tread'}); %>
}
<% } %>
<% if (tview.tedit) { %>
function <%= tview.tedit.texport.angular.tcontroller.prefix %><%= tview.tcode %><%= tview.tedit.texport.angular.tcontroller.postfix %>(<%= tview.tedit.texport.angular.tcontroller.argu %>) {
$scope.data = {};
$http.get('<%= tapiPrefix %>/<%= tview.tread.texport.json.tslug %>/' + $routeParams.id).success(function(data) {
$scope.data = data.data;
});
$scope.teditData = function () {
$http.put('<%= tapiPrefix %>/<%= tview.tedit.texport.json.tslug %>/' + $routeParams.id, $scope.data ).success(function(data) {
<% if (tview.tread) { %>
$location.url('<%= tview.tmeta.texport.angular.tbase %>/<%= tview.tslug %>/<%= tview.tread.texport.angular.tslug %>/' + $routeParams.id);
<% } else { %>
$location.url('<%= tview.tmeta.texport.angular.tbase %>/');
<% } %>
});
}
<%- include('controller-action-none', {tview: tview,taction: 'tedit'}); %>
}
<% } %>
<% if (tview.tdelete) { %>
function <%= tview.tdelete.texport.angular.tcontroller.prefix %><%= tview.tcode %><%= tview.tdelete.texport.angular.tcontroller.postfix %>(<%= tview.tdelete.texport.angular.tcontroller.argu %>) {
$scope.data = {};
$http.get('<%= tapiPrefix %>/<%= tview.tread.texport.json.tslug %>/' + $routeParams.id).success(function(data) {
$scope.data = data.data;
});
$scope.tdeleteData = function () {
$http.delete('<%= tapiPrefix %>/<%= tview.tdelete.texport.json.tslug %>/'+ $routeParams.id, $scope.data).success(function(data) {
<% if (tview.tlist) { %>
$location.url('<%= tview.tmeta.texport.angular.tbase %>/<%= tview.tslug %>/<%= tview.tlist.texport.angular.tslug %>');
<% } else { %>
$location.url('<%= tview.tmeta.texport.angular.tbase %>/');
<% } %>
});
}
<%- include('controller-action-none', {tview: tview,taction: 'tdelete'}); %>
}
<% } %>

View file

@ -0,0 +1,20 @@
<h2>Create <%= tview.tname %></h2>
<div class="row">
<div class="col-lg-8">
<form role="form">
<% tview.tedit.tfields.forEach(function (fieldKey) { %>
<div class="form-group">
<label for="<%= tview.tmeta.tfields[fieldKey].tid %>"><%= tview.tmeta.tfields[fieldKey].tname %></label>
<input ng-model="data.<%= tview.tmeta.tfields[fieldKey].tid %>" name="<%= tview.tmeta.tfields[fieldKey].tid %>" class="form-control"></input>
</div>
<% }) %>
<button type="submit" class="btn btn-default" ng-click="tcreateData()">Submit</button>
<button type="submit" class="btn btn-default" ng-click="tcreateNone()">Cancel</button>
</form>
</div>
<div class="col-lg-4"></div>
</div>
<!--
06 308 500 37
-->

View file

@ -0,0 +1,6 @@
<h2>Delete <%= tview.tname %></h2>
<div>
<p>Are you sure you want to delete this <%= tview.tname %> {{data._id}} ?</p>
<button ng-click="tdeleteData()">Yes</button> | -
<button ng-click="tdeleteNone()">No thanks</button>
<div>

View file

@ -0,0 +1,16 @@
<h2> Edit <%= tview.tname %></h2>
<div class="row">
<div class="col-lg-8">
<form role="form">
<% tview.tedit.tfields.forEach(function (fieldKey) { %>
<div class="form-group">
<label for="<%= tview.tmeta.tfields[fieldKey].tid %>"><%= tview.tmeta.tfields[fieldKey].tname %></label>
<input ng-model="data.<%= tview.tmeta.tfields[fieldKey].tid %>" name="<%= tview.tmeta.tfields[fieldKey].tid %>" class="form-control"></input>
</div>
<% }) %>
<button type="submit" class="btn btn-default" ng-click="teditData()">Submit</button>
<button type="submit" class="btn btn-default" ng-click="teditNone()">Cancel</button>
</form>
<div class="col-lg-4"></div>
</div>

View file

@ -0,0 +1,50 @@
<p>There are {{data.length}} <%= tview.tplural %></p>
<% if (tview.tcreate) { %>
<p>
<a href="<%= tview.tmeta.texport.angular.tbase %>/<%= tview.tslug %>/<%= tview.tcreate.texport.angular.tslug %>">Create New</a>
</p>
<% } %>
<div class="table-responsive">
<table class="table table-bordered table-hover table-striped">
<thead>
<tr>
<% if (tview.tread) { %><th>Open</th><% } %>
<% if (tview.tedit) { %><th>Edit</th><% } %>
<% if (tview.tdelete) { %><th>Delete</th><% } %>
<% tview.tlist.tfields.forEach(function (fieldKey) { %>
<th><%= tview.tmeta.tfields[fieldKey].tname %></th>
<% }) %>
</tr>
</thead>
<tbody>
<tr ng-repeat="row in data">
<% if (tview.tread) { %>
<td>
<a href="<%= tview.tmeta.texport.angular.tbase %>/<%= tview.tslug %>/<%= tview.tread.texport.angular.tslug %>/{{row._id}}">Read</a>
</td>
<% } %>
<% if (tview.tedit) { %>
<td>
<a href="<%= tview.tmeta.texport.angular.tbase %>/<%= tview.tslug %>/<%= tview.tedit.texport.angular.tslug %>/{{row._id}}">Edit</a>
</td>
<% } %>
<% if (tview.tdelete) { %>
<td>
<a href="<%= tview.tmeta.texport.angular.tbase %>/<%= tview.tslug %>/<%= tview.tdelete.texport.angular.tslug %>/{{row._id}}">Delete</a>
</td>
<% } %>
<% tview.tlist.tfields.forEach(function (fieldKey) { %>
<td>{{row.<%= tview.tmeta.tfields[fieldKey].tid %>}}</td>
<% }) %>
</tr>
</tbody>
</table>
</div>
<% if (tview.tcreate) { %>
<p>
<a href="<%= tview.tmeta.texport.angular.tbase %>/<%= tview.tslug %>/<%= tview.tcreate.texport.angular.tslug %>">Create New</a>
</p>
<% } %>

View file

@ -0,0 +1,19 @@
<section>
<h2> {{data._id}} </h2>
<% if (tview.tedit) { %>
<p>
<a href="<%= tview.tmeta.texport.angular.tbase %>/<%= tview.tslug %>/<%= tview.tedit.texport.angular.tslug %>/{{data._id}}">Edit</a>
</p>
<% } %>
<% if (tview.tdelete) { %>
<p>
<a href="<%= tview.tmeta.texport.angular.tbase %>/<%= tview.tslug %>/<%= tview.tdelete.texport.angular.tslug %>/{{data._id}}">Delete</a>
</p>
<% } %>
<% tview.tread.tfields.forEach(function (fieldKey) { %>
<p><%= tview.tmeta.tfields[fieldKey].tname %>: {{data.<%= tview.tmeta.tfields[fieldKey].tid %>}}</p>
<% }) %>
<p>
<button ng-click="treadNone()">Back</button>
</p>
<sector>

View file

@ -0,0 +1 @@
# CSV: <%= tview.tid %>

View file

@ -0,0 +1 @@
<% Object.keys(tview.tlist.tfields).forEach(function (tfieldKey) {var tfield = tview.tlist.tfields[tfieldKey]; %><%= record[tfield.tid] %>,<% }) %>

View file

@ -0,0 +1 @@
<% Object.keys(tview.tlist.tfields).forEach(function (tfieldKey) {var tfield = tview.tlist.tfields[tfieldKey]; %><%= record[tfield.tid] %>,<% }) %>

View file

@ -0,0 +1 @@
</<%= tview.tid %>List>

View file

@ -0,0 +1,2 @@
<?xml version="1.0" encoding="UTF-8"?>
<<%= tview.tid %>List>

View file

@ -0,0 +1,5 @@
<<%= tview.tid %>>
<% Object.keys(tview.tlist.tfields).forEach(function (tfieldKey) {var tfield = tview.tlist.tfields[tfieldKey]; %>
<<%= tfieldKey %> type="<%= tfield.type %>"><%= record[tfield.tid] %></<%= tfieldKey %>>
<% }) %>
</<%= tview.tid %>>

View file

@ -0,0 +1,2 @@
</<%= tview.tid %>List>
</data>

View file

@ -0,0 +1,3 @@
<?xml version="1.0" encoding="UTF-8"?>
<data>
<<%= tview.tid %>List>

View file

@ -0,0 +1,5 @@
<<%= tview.tid %>>
<% Object.keys(tview.tlist.tfields).forEach(function (tfieldKey) {var tfield = tview.tlist.tfields[tfieldKey]; %>
<<%= tfieldKey %> type="<%= tfield.type %>"><%= record[tfield.tid] %></<%= tfieldKey %>>
<% }) %>
</<%= tview.tid %>>

View file

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<data>
<<%= tview.tid %>>
<% Object.keys(tview.tlist.tfields).forEach(function (tfieldKey) {var tfield = tview.tlist.tfields[tfieldKey]; %>
<<%= tfieldKey %> type="<%= tfield.type %>"><%= record[tfield.tid] %></<%= tfieldKey %>>
<% }) %>
</<%= tview.tid %>>
</data>